io.ultreia.java4all.jaxx.widgets.list.DoubleListHandler Maven / Gradle / Ivy
package io.ultreia.java4all.jaxx.widgets.list;
/*
* #%L
* JAXX :: Widgets
* %%
* Copyright (C) 2008 - 2024 Code Lutin, Ultreia.io
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Lesser Public License for more details.
*
* You should have received a copy of the GNU General Lesser Public
* License along with this program. If not, see
* .
* #L%
*/
import io.ultreia.java4all.decoration.Decorated;
import io.ultreia.java4all.decoration.Decorator;
import io.ultreia.java4all.jaxx.widgets.BeanUIUtil;
import io.ultreia.java4all.jaxx.widgets.list.actions.DoubleListRemoveAction;
import io.ultreia.java4all.lang.Setters;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.nuiton.jaxx.runtime.spi.UIHandler;
import org.nuiton.jaxx.runtime.swing.JAXXButtonGroup;
import org.nuiton.jaxx.runtime.swing.SwingUtil;
import org.nuiton.jaxx.runtime.swing.model.JaxxDefaultListModel;
import org.nuiton.jaxx.runtime.swing.model.JaxxFilterableListModel;
import org.nuiton.jaxx.runtime.swing.renderer.DecoratorListCellRenderer;
import org.nuiton.jaxx.runtime.swing.renderer.FilteredDecoratorListCellRenderer;
import org.nuiton.jaxx.widgets.MutateOnConditionalPropertyChangeListener;
import javax.swing.AbstractButton;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPopupMenu;
import javax.swing.ListSelectionModel;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import java.awt.event.FocusEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.Predicate;
/**
* Created on 11/28/14.
*
* @author Tony Chemit - [email protected]
* @since 3.0
*/
@SuppressWarnings("unused")
public class DoubleListHandler implements UIHandler> {
private static final Logger log = LogManager.getLogger(DoubleListHandler.class);
private DoubleList ui;
private final BeanUIUtil.PopupHandler popupHandler = new BeanUIUtil.PopupHandler() {
@Override
public JPopupMenu getPopup() {
return ui.getPopup();
}
@Override
public JComponent getInvoker() {
return ui.getDisplayDecorator();
}
};
/**
* the decorator of data
*/
private Decorator decorator;
@Override
public void beforeInit(DoubleList ui) {
this.ui = ui;
DoubleListConfig config = new DoubleListConfig<>();
DoubleListModel model = new DoubleListModel<>(config);
ui.setContextValue(model);
}
@Override
public void afterInit(DoubleList ui) {
DoubleListRemoveAction.init(ui, ui.getSelectedListRemoveAction(), new DoubleListRemoveAction<>());
SwingUtil.changeFontSize(ui.getAddAction(), 10f);
SwingUtil.changeFontSize(ui.getRemoveAction(), 10f);
// nothing to do here, everything is done in init method
}
//------------------------------------------------------------------------//
//-- Filter methods ------------------------------------------------------//
//------------------------------------------------------------------------//
public void addFilter(Predicate filter) {
ui.getModel().getUniverseModel().addFilter(filter);
}
public void removeFilter(Predicate filter) {
ui.getModel().getUniverseModel().removeFilter(filter);
}
public void clearFilters() {
ui.getModel().getUniverseModel().clearFilters();
}
public void refreshFilteredElements() {
ui.getModel().getUniverseModel().refreshFilteredElements();
}
//------------------------------------------------------------------------//
//-- Public methods ------------------------------------------------------//
//------------------------------------------------------------------------//
/**
* When universe list was double clicked, move selected items to selected list.
*
* @param event mouse event
*/
public void onUniverseListClicked(MouseEvent event) {
if (!ui.isEnabled()) {
return;
}
JList universeList = ui.getUniverseList();
if (event.getClickCount() == 2) {
int index = universeList.locationToIndex(event.getPoint());
if (index < 0) {
return;
}
O item = ui.getModel().getUniverseModel().getElementAt(index);
List items = new ArrayList<>();
items.add(item);
addToSelected(items);
}
}
public void onUniverseListFocusGained(FocusEvent event) {
if (!ui.isEnabled()) {
return;
}
JList list = ui.getUniverseList();
if (list.isSelectionEmpty() && list.getModel().getSize() > 0) {
list.setSelectedIndex(0);
}
}
public void addToSelected(List items) {
ui.getModel().addToSelected(items);
if (ui.getModel().getConfig().isAutoSortSelectedList()) {
sortSelectedList();
}
}
public void removeFromSelected(List items) {
ui.getModel().removeFromSelected(items);
sortData();
}
/**
* When selected list was double clicked, move selected items to universe list.
*
* @param event mouse event
*/
public void onSelectedListClicked(MouseEvent event) {
if (!ui.isEnabled()) {
return;
}
JList selectedList = ui.getSelectedList();
if (event.getClickCount() == 2) {
int index = selectedList.locationToIndex(event.getPoint());
if (index < 0) {
return;
}
List items = new ArrayList<>();
items.add(ui.getModel().getSelectedModel().getElementAt(index));
removeFromSelected(items);
}
}
public void onSelectedListFocusGained(FocusEvent event) {
if (!ui.isEnabled()) {
return;
}
JList list = ui.getSelectedList();
if (list.isSelectionEmpty() && list.getModel().getSize() > 0) {
list.setSelectedIndex(0);
}
}
public void onKeyPressedOnUniverseList(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
int lastIndice = ui.getUniverseList().getSelectionModel().getLeadSelectionIndex();
ui.getAddAction().doClick();
JList> source = (JList>) e.getSource();
if (source.getModel().getSize() > 0) {
source.setSelectedIndex(Math.min(lastIndice, source.getModel().getSize() - 1));
}
}
}
public void onKeyPressedOnSelectedList(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_DELETE) {
int lastIndice = ui.getSelectedList().getSelectionModel().getLeadSelectionIndex();
ui.getRemoveAction().doClick();
JList> source = (JList>) e.getSource();
if (source.getModel().getSize() > 0) {
source.setSelectedIndex(Math.min(lastIndice, source.getModel().getSize() - 1));
}
}
}
public void selectFirstRowIfNoSelection(FocusEvent e) {
JList> source = (JList>) e.getSource();
if (source.isSelectionEmpty()) {
source.setSelectedIndex(0);
}
}
public JPopupMenu getSelectedListPopup(boolean showIt) {
JPopupMenu result;
if (showIt) {
result = ui.getSelectedListPopup();
} else {
result = null;
}
return result;
}
/**
* Toggle the popup visible state.
*/
public void togglePopup() {
popupHandler.togglePopup();
}
//------------------------------------------------------------------------//
//-- Init methods --------------------------------------------------------//
//------------------------------------------------------------------------//
/**
* Initializes the handler of the UI
*
* @param decorator the decorator to use to display the data nicely
* @param universe the list of all the available items
* @param selected the list of selected items
*/
public void init(Decorator decorator, List universe, List selected) {
if (decorator == null) {
throw new NullPointerException("decorator can not be null (for type " + ui.getBeanType() + ")");
}
this.decorator = decorator.copy();
DoubleListModel model = ui.getModel();
ui.addPropertyChangeListener("enabled", e -> {
boolean newValue = (boolean) e.getNewValue();
if (!newValue) {
ui.getUniverseList().getSelectionModel().clearSelection();
ui.getSelectedList().getSelectionModel().clearSelection();
}
});
DoubleListConfig config = model.getConfig();
Object bean = model.getBean();
if (bean != null) {
String property = config.getProperty();
if (property != null) {
Method mutator = Setters.getMutator(bean, property);
// check mutator exists
Objects.requireNonNull(mutator, "could not find mutator for " + property);
// When selected list changed, let's push it back in bean
model.addPropertyChangeListener(
DoubleListModel.PROPERTY_SELECTED,
new MutateOnConditionalPropertyChangeListener<>(model, mutator, DoubleListModel -> true));
}
}
if (config.isShowListLabel()) {
ui.getUniverseHeaderLabel().setText(config.getUniverseLabel());
ui.getSelectedHeaderLabel().setText(config.getSelectedLabel());
JLabel label = new JLabel(config.getLabel());
ui.getBeforeFilterPanel().add(label);
}
{
// Init universe list
JList universeList = ui.getUniverseList();
FilteredDecoratorListCellRenderer super O> universeListCellRenderer = new FilteredDecoratorListCellRenderer<>(this.decorator);
universeList.setCellRenderer(universeListCellRenderer);
// When universe list selection model changed, update the add button enabled property
universeList.getSelectionModel().addListSelectionListener(e -> {
ListSelectionModel source = (ListSelectionModel) e.getSource();
ui.getModel().setAddEnabled(!source.isSelectionEmpty());
});
JaxxFilterableListModel filterModel = ui.getModel().getUniverseModel();
filterModel.setDecorator(this.decorator);
ui.getFilterField().getDocument().addDocumentListener(new DocumentListener() {
@Override
public void insertUpdate(DocumentEvent e) {
String text = ui.getFilterField().getText();
universeListCellRenderer.setFilterText(text);
filterModel.setFilterText(text);
}
@Override
public void removeUpdate(DocumentEvent e) {
String text = ui.getFilterField().getText();
universeListCellRenderer.setFilterText(text);
filterModel.setFilterText(text);
}
@Override
public void changedUpdate(DocumentEvent e) {
String text = ui.getFilterField().getText();
universeListCellRenderer.setFilterText(text);
filterModel.setFilterText(text);
}
});
}
{
// Init selected list
JList selectedList = ui.getSelectedList();
selectedList.setCellRenderer(new DecoratorListCellRenderer<>(this.decorator));
// When selected list selection model changed, update the add button enabled property
selectedList.getSelectionModel().addListSelectionListener(e -> {
ListSelectionModel source = (ListSelectionModel) e.getSource();
DoubleListModel model1 = ui.getModel();
if (!e.getValueIsAdjusting()) {
boolean removeEnabled = !source.isSelectionEmpty();
model1.setRemoveEnabled(removeEnabled);
int minSelectionIndex = source.getMinSelectionIndex();
int maxSelectionIndex = source.getMaxSelectionIndex();
boolean oneSelection = minSelectionIndex == maxSelectionIndex;
boolean upEnabled = removeEnabled &&
oneSelection &&
minSelectionIndex > 0;
model1.setSelectedUpEnabled(upEnabled);
boolean downEnabled = removeEnabled &&
oneSelection &&
minSelectionIndex + 1 < model1.getSelectedListSize();
model1.setSelectedDownEnabled(downEnabled);
}
});
}
{
// Init decorator ui
JAXXButtonGroup indexes = ui.getIndexes();
// build popup
popupHandler.preparePopup(null,
null,
config.getI18nPrefix(),
config.getLabel(),
indexes,
ui.getPopupSeparator(),
ui.getPopupLabel(),
ui.getSortUp(),
ui.getSortDown(),
this.decorator);
// select sort button
indexes.setSelectedButton(ui.getIndex());
}
setUniverse(universe);
setSelected(selected);
{
// Listen ui changes
ui.addPropertyChangeListener(DoubleList.PROPERTY_INDEX, evt -> {
// decorator index has changed, force reload of data in ui
setIndex((Integer) evt.getOldValue(),
(Integer) evt.getNewValue());
});
ui.addPropertyChangeListener(DoubleList.PROPERTY_REVERSE_SORT, evt -> {
// sort order has changed, force reload of data in ui
setSortOrder((Boolean) evt.getOldValue(),
(Boolean) evt.getNewValue());
});
ui.addPropertyChangeListener(DoubleList.PROPERTY_HIGHLIGHT_FILTER_TEXT, evt -> setHighlightFilterText((Boolean) evt.getNewValue()));
}
// force to reload the showSelectPopup binding
ui.processDataBinding(DoubleList.BINDING_SELECTED_LIST_COMPONENT_POPUP_MENU);
}
protected void setSelected(List selected) {
DoubleListModel model = ui.getModel();
if (selected != null) {
if (decorator != null) {
selected.forEach(datum -> ((Decorated) datum).registerDecorator(decorator));
}
}
model.setSelected(selected);
if (model.getConfig().isAutoSortSelectedList()) {
sortSelectedList();
}
}
//------------------------------------------------------------------------//
//-- Internal methods ----------------------------------------------------//
//------------------------------------------------------------------------//
public void sortData() {
// just update UI should do the math of this
updateUI(ui.getIndex(), ui.isReverseSort());
}
/**
* Modifie l'index du décorateur
*
* @param oldValue l'ancienne valeur
* @param newValue la nouvelle valeur
*/
protected void setIndex(Integer oldValue, Integer newValue) {
if (newValue == null || newValue.equals(oldValue)) {
return;
}
if (log.isDebugEnabled()) {
log.debug("check state : <" + oldValue + " to " + newValue + ">");
}
AbstractButton button = ui.getIndexes().getButton(newValue);
if (button != null) {
button.setSelected(true);
}
updateUI(newValue, ui.isReverseSort());
if (ui.getModel().getConfig().isAutoSortSelectedList()) {
sortSelectedList();
}
}
/**
* Modifie l'index du décorateur
*
* @param oldValue l'ancienne valeur
* @param newValue la nouvelle valeur
*/
protected void setSortOrder(Boolean oldValue, Boolean newValue) {
if (newValue == null || newValue.equals(oldValue)) {
return;
}
log.debug("check state : <" + oldValue + " to " + newValue + ">");
updateUI(ui.getIndex(), newValue);
if (ui.getModel().getConfig().isAutoSortSelectedList()) {
sortSelectedList();
}
}
protected void setHighlightFilterText(Boolean newValue) {
@SuppressWarnings({"unchecked", "rawtypes"}) FilteredDecoratorListCellRenderer super O> universeListCellRenderer =
(FilteredDecoratorListCellRenderer) ui.getUniverseList().getCellRenderer();
universeListCellRenderer.setHighlightFilterText(newValue);
ui.getUniverseList().repaint();
}
protected void updateUI(int index, boolean reverseSort) {
if (decorator == null) {
// can't come here right now...
return;
}
// change decorator context
decorator.setIndex(index);
List data = ui.getModel().getUniverseModel().toList();
try {
// Sort data with the decorator jxpath tokens.
decorator.sort(data, index, reverseSort);
} catch (Exception eee) {
log.warn(eee.getMessage(), eee);
}
// reload the model
SwingUtil.fillList(ui.getUniverseList(), data, null);
}
protected void sortSelectedList() {
if (decorator == null) {
// can't come here right now...
return;
}
JaxxDefaultListModel selectedModel = ui.getModel().getSelectedModel();
List data = selectedModel.toList();
try {
decorator.sort(data, ui.getIndex(), ui.isReverseSort());
} catch (Exception eee) {
log.warn(eee.getMessage(), eee);
}
boolean valueIsAdjusting = selectedModel.isValueIsAdjusting();
// We need to adjust the model (but nothing is really changed on it)
// See https://gitlab.com/ultreiaio/jaxx/-/issues/839
selectedModel.setValueIsAdjusting(true);
try {
// reload the model
SwingUtil.fillList(ui.getSelectedList(), data, null);
} finally {
if (!valueIsAdjusting) {
selectedModel.setValueIsAdjusting(false);
}
}
}
public Decorator getDecorator() {
return decorator;
}
public void setUniverse(List universe) {
if (decorator != null) {
universe.forEach(datum -> ((Decorated) datum).registerDecorator(decorator));
}
DoubleListModel model = ui.getModel();
sort(universe);
model.setUniverse(universe);
}
public void sort(List data) {
Decorator decorator = ui.getModel().getUniverseModel().getDecorator();
Integer index = ui.getIndex();
Boolean reverseSort = ui.isReverseSort();
decorator.sort(data, index, reverseSort);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy