All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.ultreia.java4all.jaxx.widgets.list.DoubleListHandler Maven / Gradle / Ivy

There is a newer version: 3.1.5
Show newest version
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 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 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