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

jaxx.runtime.swing.editor.bean.BeanComboBoxHandler Maven / Gradle / Ivy

There is a newer version: 3.0-alpha-1
Show newest version
/*
 * #%L
 * JAXX :: Widgets
 * %%
 * Copyright (C) 2008 - 2014 Code Lutin, Tony Chemit
 * %%
 * 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%
 */

package jaxx.runtime.swing.editor.bean;

import jaxx.runtime.SwingUtil;
import jaxx.runtime.swing.JAXXButtonGroup;
import jaxx.runtime.swing.renderer.DecoratorListCellRenderer;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jdesktop.swingx.autocomplete.ObjectToStringConverter;
import org.nuiton.decorator.DecoratorUtil;
import org.nuiton.decorator.JXPathDecorator;
import org.nuiton.decorator.MultiJXPathDecorator;
import org.nuiton.util.beans.BeanUtil;

import javax.swing.*;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import javax.swing.text.Document;
import javax.swing.text.JTextComponent;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

/**
 * Le handler d'un {@link BeanComboBox}.
 * 

* Note: ce handler n'est pas stateless et n'est donc pas partageable entre plusieurs ui. * * @param le type des objet contenus dans le modèle du composant. * @author Tony Chemit - [email protected] * @see BeanComboBox */ public class BeanComboBoxHandler implements PropertyChangeListener { public static final Log log = LogFactory.getLog(BeanComboBoxHandler.class); /** ui if the handler */ protected BeanComboBox ui; /** the mutator method on the property of boxed bean in the ui */ protected Method mutator; /** the original document of the combbo box editor (keep it to make possible undecorate) */ protected Document originalDocument; /** the convertor used to auto-complete */ protected ObjectToStringConverter convertor; /** the decorator of data */ protected MultiJXPathDecorator decorator; protected boolean init; public BeanComboBoxHandler(BeanComboBox ui) { this.ui = ui; } protected final FocusListener EDITOR_TEXT_COMP0NENT_FOCUSLISTENER = new FocusListener() { @Override public void focusGained(FocusEvent e) { if (log.isDebugEnabled()) { log.debug("close popup from " + e); } ui.getPopup().setVisible(false); } @Override public void focusLost(FocusEvent e) { } }; private final BeanUIUtil.PopupHandler popupHandler = new BeanUIUtil.PopupHandler() { @Override public JPopupMenu getPopup() { return ui.getPopup(); } @Override public JComponent getInvoker() { return ui.getChangeDecorator(); } }; /** * Initialise le handler de l'ui * * @param decorator le decorateur a utiliser * @param data la liste des données a gérer */ public void init(JXPathDecorator decorator, List data) { if (init) { throw new IllegalStateException("can not init the handler twice"); } init = true; if (decorator == null) { throw new NullPointerException("decorator can not be null (for type " + ui.getBeanType() + ")"); } JAXXButtonGroup indexes = ui.getIndexes(); this.decorator = BeanUIUtil.createDecorator(decorator); final JComboBox combobox = ui.getCombobox(); // init combobox renderer base on given decorator combobox.setRenderer(new DecoratorListCellRenderer(this.decorator)); combobox.addPopupMenuListener(new PopupMenuListener() { private O selectedItem; boolean canceled = false; public void popupMenuWillBecomeVisible(PopupMenuEvent e) { selectedItem = (O) combobox.getSelectedItem(); } public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { O newSelectedItem = (O) combobox.getSelectedItem(); log.debug(String.format("old ui value: %s, new value: %s :: %s", ui.getSelectedItem(), newSelectedItem, Objects.equals(ui.getSelectedItem(), newSelectedItem))); log.debug(String.format("old value: %s, new value: %s :: %s", selectedItem, newSelectedItem, Objects.equals(selectedItem, newSelectedItem))); if (canceled) { ui.setSelectedItem(null); } else { if (ui.isForce()) { ui.setSelectedItem(null); } setSelectedItem(selectedItem, newSelectedItem); } selectedItem = null; canceled = false; } public void popupMenuCanceled(PopupMenuEvent e) { canceled = true; } }); convertor = BeanUIUtil.newDecoratedObjectToStringConverter(this.decorator); // keep a trace of original document (to make possible reverse autom-complete) JTextComponent editorComponent = (JTextComponent) combobox.getEditor().getEditorComponent(); originalDocument = editorComponent.getDocument(); // build popup popupHandler.preparePopup(ui.getSelectedToolTipText(), ui.getNotSelectedToolTipText(), ui.getI18nPrefix(), ui.getPopupTitleText(), indexes, ui.getPopupSeparator(), ui.getPopupLabel(), ui.getSortUp(), ui.getSortDown(), this.decorator); ui.autoComplete = true; ui.addPropertyChangeListener(this); // set datas ui.setData(data); // select sort button indexes.setSelectedButton(ui.getIndex()); } /** Toggle the popup visible state. */ public void togglePopup() { popupHandler.togglePopup(); } /** * @return {@code true} if there is no data in comboBox, * {@code false} otherwise. * @since 2.5.9 */ public boolean isEmpty() { return CollectionUtils.isEmpty(ui.getData()); } /** * Add the given items into the comboBox. *

* Note: The item will be inserted at his correct following * the selected ordering. * * @param items items to add in comboBox. * @since 2.5.28 */ public void addItems(Iterable items) { List data = ui.getData(); boolean wasEmpty = CollectionUtils.isEmpty(data); for (O item : items) { data.add(item); } updateUI(ui.getIndex(), ui.isReverseSort()); fireEmpty(wasEmpty); } /** * Remove the given items from the comboBox model. *

* Note: If this item was selected, then selection will be * cleared. * * @param items items to remove from the comboBox model * @since 2.5.28 */ public void removeItems(Iterable items) { List data = ui.getData(); boolean needUpdate = false; for (O item : items) { boolean remove = data.remove(item); if (remove) { // item was found in data Object selectedItem = ui.getSelectedItem(); if (item == selectedItem) { // item was selected item, reset selected item then ui.setSelectedItem(null); } needUpdate = true; } } if (needUpdate) { updateUI(ui.getIndex(), ui.isReverseSort()); fireEmpty(false); } } /** * Add the given item into the comboBox. *

* Note: The item will be inserted at his correct following * the selected ordering. * * @param item item to add in comboBox. * @since 2.5.9 */ public void addItem(O item) { addItems(Collections.singleton(item)); } /** * Remove the given item from the comboBox model. *

* Note: If this item was selected, then selection will be * cleared. * * @param item the item to remove from the comboBox model * @since 2.5.9 */ public void removeItem(O item) { removeItems(Collections.singleton(item)); } /** * Sort data of the model. * * @since 2.5.10 */ public void sortData() { // just update UI should do the math of this updateUI(ui.getIndex(), ui.isReverseSort()); } /** * Focus combo only if autoFocus ui property is on. * * @since 2.8.5 */ public void focusCombo() { if (ui.isAutoFocus()) { ui.combobox.requestFocusInWindow(); } } /** * Modifie l'état autoComplete de l'ui. * * @param oldValue l'ancienne valeur * @param newValue la nouvelle valeur */ protected void setAutoComplete(Boolean oldValue, Boolean newValue) { oldValue = oldValue != null && oldValue; newValue = newValue != null && newValue; if (oldValue.equals(newValue)) { return; } if (log.isDebugEnabled()) { log.debug("autocomplete state : <" + oldValue + " to " + newValue + ">"); } if (!newValue) { JTextComponent editorComponent = (JTextComponent) ui.getCombobox().getEditor().getEditorComponent(); editorComponent.removeFocusListener(EDITOR_TEXT_COMP0NENT_FOCUSLISTENER); BeanUIUtil.undecorate(ui.getCombobox(), originalDocument); } else { BeanUIUtil.decorate(ui.getCombobox(), convertor); JTextComponent editorComponent = (JTextComponent) ui.getCombobox().getEditor().getEditorComponent(); editorComponent.addFocusListener(EDITOR_TEXT_COMP0NENT_FOCUSLISTENER); } } /** * 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 + ">"); } updateUI(newValue, ui.isReverseSort()); } /** * 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; } if (log.isDebugEnabled()) { log.debug("check state : <" + oldValue + " to " + newValue + ">"); } updateUI(ui.getIndex(), newValue); } protected void updateUI(int index, boolean reversesort) { // change decorator context decorator.setContextIndex(index); // keep selected item Object previousSelectedItem = ui.getSelectedItem(); Boolean wasAutoComplete = ui.isAutoComplete(); if (wasAutoComplete) { ui.setAutoComplete(false); } // remove autocomplete if (previousSelectedItem != null) { ui.getCombobox().setSelectedItem(null); ui.selectedItem = null; } List data = ui.getData(); if (ui.isSortable()) { try { // Sort data with the decorator jxpath tokens. DecoratorUtil.sort(decorator, data, index, reversesort); } catch (Exception eee) { log.warn(eee.getMessage(), eee); } } // reload the model SwingUtil.fillComboBox(ui.getCombobox(), data, null); if (wasAutoComplete) { ui.setAutoComplete(true); } if (previousSelectedItem != null) { ui.setSelectedItem(previousSelectedItem); } ui.getCombobox().requestFocus(); } /** * Modifie la valeur sélectionnée dans la liste déroulante. * * @param oldValue l'ancienne valeur * @param newValue la nouvelle valeur */ protected void setSelectedItem(O oldValue, O newValue) { log.debug(String.format("old value: %s, new value: %s", oldValue, newValue)); if (ui.getBean() == null) { return; } if (newValue == null) { if (ui.getCombobox().getSelectedItem() == null) { return; } ui.getCombobox().setSelectedItem(null); if (ui.isAutoComplete()) { ui.setAutoComplete(false); ui.setAutoComplete(true); } if (oldValue == null) { return; } } if (log.isDebugEnabled()) { log.debug(ui.getProperty() + " on " + getBeanType() + " :: " + oldValue + " to " + newValue); } BeanUIUtil.invokeMethod(getMutator(), ui.getBean(), (O) newValue); } /** @return le document de l'éditeur avant complétion. */ public Document getOriginalDocument() { return originalDocument; } public MultiJXPathDecorator getDecorator() { return decorator; } /** * @return get the type of objects contained in the comboBox model. * @since 2.5.9 */ public Class getBeanType() { Class result = ui.getBeanType(); if (result == null) { result = decorator == null ? null : decorator.getType(); } return result; } /** * Obtain the type of objects contained in the comboBox using the model mutator. * * @return get the type of objects contained in the comboBox model. * @deprecated since 2.5.9 (use now method {@link #getBeanType()}) */ @Deprecated public Class getTargetClass() { Method m = getMutator(); return m == null ? null : m.getParameterTypes()[0]; } /** @return le mutateur a utiliser pour modifier le bean associé. */ protected Method getMutator() { if (mutator == null && ui.getBean() != null && ui.getProperty() != null) { mutator = BeanUtil.getMutator(ui.getBean(), ui.getProperty()); } return mutator; } @Override public void propertyChange(PropertyChangeEvent evt) { String propertyName = evt.getPropertyName(); if (BeanComboBox.PROPERTY_SELECTED_ITEM.equals(propertyName)) { setSelectedItem((O) evt.getOldValue(), (O) evt.getNewValue()); return; } if (BeanComboBox.PROPERTY_AUTO_COMPLETE.equals(propertyName)) { setAutoComplete((Boolean) evt.getOldValue(), (Boolean) evt.getNewValue()); return; } if (BeanListHeader.PROPERTY_INDEX.equals(propertyName)) { // decorator index has changed, force reload of data in ui setIndex((Integer) evt.getOldValue(), (Integer) evt.getNewValue()); return; } if (BeanListHeader.PROPERTY_REVERSE_SORT.equals(propertyName)) { // sort order has changed, force reload of data in ui setSortOrder((Boolean) evt.getOldValue(), (Boolean) evt.getNewValue()); return; } if (BeanListHeader.PROPERTY_DATA.equals(propertyName)) { // list has changed, force reload of index setIndex(-1, ui.getIndex()); // list has changed, fire empty property List list = (List) evt.getOldValue(); fireEmpty(CollectionUtils.isEmpty(list)); } } protected void fireEmpty(boolean wasEmpty) { ui.firePropertyChange(BeanComboBox.PROPERTY_EMPTY, wasEmpty, isEmpty()); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy