Author: tchemit Date: 2010-09-07 12:45:13 +0200 (Tue, 07 Sep 2010) New Revision: 2071 Url: http://nuiton.org/repositories/revision/jaxx/2071 Log: improve bean editors + add missing icons Added: trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing/editor/bean/BeanUIUtil.java trunk/jaxx-widgets/src/main/resources/icons/action-combobox-reset.png trunk/jaxx-widgets/src/main/resources/icons/action-combobox-sort.png Modified: trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing/editor/bean/BeanComboBox.jaxx trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing/editor/bean/BeanComboBoxHandler.java trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing/editor/bean/BeanListHeader.jaxx trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing/editor/bean/BeanListHeaderHandler.java Modified: trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing/editor/bean/BeanComboBox.jaxx =================================================================== --- trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing/editor/bean/BeanComboBox.jaxx 2010-09-07 10:44:52 UTC (rev 2070) +++ trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing/editor/bean/BeanComboBox.jaxx 2010-09-07 10:45:13 UTC (rev 2071) @@ -27,6 +27,11 @@ onFocusGained='combobox.requestFocus()' onFocusLost='hidePopup()'> + <import> + static org.nuiton.i18n.I18n.n_ + jaxx.runtime.decorator.JXPathDecorator + </import> + <!-- auto complete property --> <Boolean id='autoComplete' javaBean='false'/> @@ -97,15 +102,6 @@ </JPopupMenu> <script><![CDATA[ -import static org.nuiton.i18n.I18n.n_; -import jaxx.runtime.decorator.JXPathDecorator; - -public static final String DEFAULT_POPUP_LABEL = n_("bean.popup.label"); - -public static final String DEFAULT_SELECTED_TOOLTIP = n_("bean.sort.on"); - -public static final String DEFAULT_NOT_SELECTED_TOOLTIP = n_("bean.sort.off"); - public void init(JXPathDecorator<O> decorator, java.util.List<O> data) { handler.init(decorator, data); } Modified: trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing/editor/bean/BeanComboBoxHandler.java =================================================================== --- trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing/editor/bean/BeanComboBoxHandler.java 2010-09-07 10:44:52 UTC (rev 2070) +++ trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing/editor/bean/BeanComboBoxHandler.java 2010-09-07 10:45:13 UTC (rev 2071) @@ -26,7 +26,6 @@ package jaxx.runtime.swing.editor.bean; import jaxx.runtime.SwingUtil; -import jaxx.runtime.decorator.Decorator; import jaxx.runtime.decorator.DecoratorUtils; import jaxx.runtime.decorator.JXPathDecorator; import jaxx.runtime.decorator.MultiJXPathDecorator; @@ -35,32 +34,20 @@ import org.apache.commons.beanutils.PropertyUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.jdesktop.swingx.autocomplete.AutoCompleteDecorator; -import org.jdesktop.swingx.autocomplete.AutoCompletePropertyChangeListener; import org.jdesktop.swingx.autocomplete.ObjectToStringConverter; -import javax.swing.JComboBox; +import javax.swing.JComponent; import javax.swing.JPopupMenu; -import javax.swing.JRadioButtonMenuItem; -import javax.swing.JToggleButton; -import javax.swing.SwingUtilities; import javax.swing.text.Document; import javax.swing.text.JTextComponent; -import java.awt.Component; -import java.awt.Dimension; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; -import java.beans.Introspector; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.beans.PropertyDescriptor; import java.lang.reflect.Method; -import java.util.Date; import java.util.List; -import static org.nuiton.i18n.I18n._; -import static org.nuiton.i18n.I18n.n_; - /** * Le handler d'un {@link BeanComboBox}. * <p/> @@ -74,14 +61,6 @@ public static final Log log = LogFactory.getLog(BeanComboBoxHandler.class); - public static final String SELECTED_ITEM_PROPERTY = "selectedItem"; - - public static final String INDEX_PROPERTY = "index"; - - public static final String AUTO_COMPLETE_PROPERTY = "autoComplete"; - - public static final String DATA_PROPERTY = "data"; - /** ui if the handler */ protected BeanComboBox<O> ui; @@ -118,6 +97,19 @@ } }; + 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 * @@ -130,36 +122,30 @@ throw new IllegalStateException("can not init the handler twice"); } init = true; - if (decorator == null) { - throw new NullPointerException("can not have a null decorator as parameter"); - } JAXXButtonGroup indexes = ui.getIndexes(); - MultiJXPathDecorator<O> d; - if (decorator instanceof MultiJXPathDecorator<?>) { - // should clone decorator ? - d = (MultiJXPathDecorator<O>) decorator; - } else { - d = DecoratorUtils.newMultiJXPathDecorator( - decorator.getInternalClass(), - decorator.getInitialExpression(), - "??" + new Date().getTime(), - " - "); - } - this.decorator = d; + this.decorator = BeanUIUtil.createDecorator(decorator); // init combobox renderer base on given decorator - ui.getCombobox().setRenderer(new DecoratorListCellRenderer(d)); + ui.getCombobox().setRenderer(new DecoratorListCellRenderer(this.decorator)); - convertor = newDecoratedObjectToStringConverter(d); + convertor = BeanUIUtil.newDecoratedObjectToStringConverter(this.decorator); // keep a trace of original document (to make possible reverse autom-complete) JTextComponent editorComponent = (JTextComponent) ui.getCombobox().getEditor().getEditorComponent(); originalDocument = editorComponent.getDocument(); // build popup - preparePopup(d); + popupHandler.preparePopup(ui.getSelectedToolTipText(), + ui.getNotSelectedToolTipText(), + ui.getI18nPrefix(), + ui.getPopupTitleText(), + indexes, + ui.getPopupLabel(), + ui.getSortUp(), + ui.getSortDown(), + this.decorator); ui.autoComplete = true; @@ -174,43 +160,9 @@ /** Toggle the popup visible state. */ public void togglePopup() { - boolean newValue = !ui.getPopup().isVisible(); - - if (log.isTraceEnabled()) { - log.trace(newValue); - } - - if (!newValue) { - if (ui.getPopup() != null) { - ui.getPopup().setVisible(false); - } - return; - } - SwingUtilities.invokeLater(new Runnable() { - - @Override - public void run() { - - updatePopup(); - - Dimension dim = ui.getPopup().getPreferredSize(); - JToggleButton invoker = ui.getChangeDecorator(); - ui.getPopup().show( - invoker, - (int) (invoker.getPreferredSize().getWidth() - dim.getWidth()), - invoker.getHeight() - ); - } - }); + popupHandler.togglePopup(); } - - protected void updatePopup() { - JPopupMenu popup = ui.getPopup(); - popup.pack(); - - } - /** * Modifie l'état autoComplete de l'ui. * @@ -220,7 +172,7 @@ protected void setAutoComplete(Boolean oldValue, Boolean newValue) { oldValue = oldValue != null && oldValue; newValue = newValue != null && newValue; - if (oldValue == newValue) { + if (oldValue.equals(newValue)) { return; } if (log.isDebugEnabled()) { @@ -229,9 +181,9 @@ if (!newValue) { JTextComponent editorComponent = (JTextComponent) ui.getCombobox().getEditor().getEditorComponent(); editorComponent.removeFocusListener(EDITOR_TEXT_COMP0NENT_FOCUSLISTENER); - undecorate(ui.getCombobox(), originalDocument); + BeanUIUtil.undecorate(ui.getCombobox(), originalDocument); } else { - decorate(ui.getCombobox(), convertor); + BeanUIUtil.decorate(ui.getCombobox(), convertor); JTextComponent editorComponent = (JTextComponent) ui.getCombobox().getEditor().getEditorComponent(); editorComponent.addFocusListener(EDITOR_TEXT_COMP0NENT_FOCUSLISTENER); } @@ -250,7 +202,7 @@ if (log.isDebugEnabled()) { log.debug("check state : <" + oldValue + " to " + newValue + ">"); } - updateComboBox(newValue, ui.isReverseSort()); + updateUI(newValue, ui.isReverseSort()); } /** @@ -269,11 +221,11 @@ log.debug("check state : <" + oldValue + " to " + newValue + ">"); } - updateComboBox(ui.getIndex(), newValue); + updateUI(ui.getIndex(), newValue); } @SuppressWarnings({"unchecked"}) - protected void updateComboBox(int index, boolean reversesort) { + protected void updateUI(int index, boolean reversesort) { // change decorator context decorator.setContextIndex(index); @@ -301,10 +253,8 @@ reversesort ); - } catch (Exception e) { - log.warn(e.getMessage(), e); - //System.out.println("newValue :: "+decorator+" : "+newValue); - //System.out.println("datas :: "+ui.getData()); + } catch (Exception eee) { + log.warn(eee.getMessage(), eee); } // reload the model @@ -370,73 +320,6 @@ return decorator; } - /** - * Creation de l'ui pour modifier le décorateur. - * - * @param decorator le decorateur a utiliser - */ - protected void preparePopup(MultiJXPathDecorator<?> decorator) { - String selectedTip = ui.getSelectedToolTipText(); - if (selectedTip == null) { - // use default selected tip text - selectedTip = BeanComboBox.DEFAULT_SELECTED_TOOLTIP; - } - String notSelectedTip = ui.getNotSelectedToolTipText(); - if (notSelectedTip == null) { - // use default selected tip text - notSelectedTip = BeanComboBox.DEFAULT_NOT_SELECTED_TOOLTIP; - } - JPopupMenu popup = ui.getPopup(); - - //Container container = ui.getIndexesContainer(); - for (int i = 0, max = decorator.getNbContext(); i < max; i++) { - String property = ui.getI18nPrefix() + decorator.getProperty(i); - String propertyI18n = _(property); - JRadioButtonMenuItem button = new JRadioButtonMenuItem(propertyI18n); - button.putClientProperty(JAXXButtonGroup.BUTTON8GROUP_CLIENT_PROPERTY, ui.getIndexes()); - button.putClientProperty(JAXXButtonGroup.VALUE_CLIENT_PROPERTY, i); - popup.add(button); - if (selectedTip != null) { - button.putClientProperty(JAXXButtonGroup.SELECTED_TIP_CLIENT_PROPERTY, _(selectedTip, propertyI18n)); - } - if (notSelectedTip != null) { - button.putClientProperty(JAXXButtonGroup.NOT_SELECTED_TIP_CLIENT_PROPERTY, _(notSelectedTip, propertyI18n)); - } - button.setSelected(false); - ui.getIndexes().add(button); - } - String title = ui.getPopupTitleText(); - if (title == null) { - // use default popup title - title = BeanComboBox.DEFAULT_POPUP_LABEL; - - Class<?> internalClass = decorator.getInternalClass(); - String beanI18nKey; - if (internalClass == null) { - beanI18nKey = n_("bean.unknown.type"); - } else { - beanI18nKey = ui.getI18nPrefix() + Introspector.decapitalize(internalClass.getSimpleName()); - } - String beanI18n = _(beanI18nKey); - title = _(title, beanI18n); - } else { - title = _(title); - } - - - ui.getSortDown().putClientProperty(JAXXButtonGroup.SELECTED_TIP_CLIENT_PROPERTY, _("bean.sort.down.tip")); - ui.getSortDown().putClientProperty(JAXXButtonGroup.NOT_SELECTED_TIP_CLIENT_PROPERTY, _("bean.sort.down.toSelect.tip")); - - ui.getSortUp().putClientProperty(JAXXButtonGroup.SELECTED_TIP_CLIENT_PROPERTY, _("bean.sort.up.tip")); - ui.getSortUp().putClientProperty(JAXXButtonGroup.NOT_SELECTED_TIP_CLIENT_PROPERTY, _("bean.sort.up.toSelect.tip")); - - ui.getPopupLabel().setText(title); - ui.getPopup().setLabel(title); - ui.getPopup().invalidate(); - - - } - public Class<?> getTargetClass() { Method m = getMutator(); return m == null ? null : m.getParameterTypes()[0]; @@ -466,61 +349,6 @@ return mutator; } - /** - * Encapsule un {@link Decorator} dans un {@link ObjectToStringConverter}. - * - * @param decorator le decorateur a encapsuler. - * @return le converter encapsule dans un {@link ObjectToStringConverter} - */ - public static ObjectToStringConverter newDecoratedObjectToStringConverter(final Decorator<?> decorator) { - - return new ObjectToStringConverter() { - - @Override - public String getPreferredStringForItem(Object item) { - return item instanceof String ? (String) item : item == null ? "" : decorator.toString(item); - } - }; - } - - /** - * Ajout l'auto-complétion sur une liste déroulante, en utilisant le - * converteur donné pour afficher les données. - * - * @param combo la combo à décorer - * @param convertor le converter utilisé pour afficher les données. - */ - public static void decorate(JComboBox combo, ObjectToStringConverter convertor) { - - AutoCompleteDecorator.decorate(combo, convertor); - } - - /** - * Désactive l'aut-complétion sur une liste déroulante, en y repositionnant - * le modèle du document d'édition d'avant auto-complétion. - * - * @param combo la liste déroulante à décorer - * @param originalDocument le document original de l'édtieur de la - * liste déroulante. - */ - public static void undecorate(JComboBox combo, Document originalDocument) { - - // has not to be editable - combo.setEditable(false); - - // configure the text component=editor component - Component c = combo.getEditor().getEditorComponent(); - JTextComponent editorComponent = (JTextComponent) c; - editorComponent.setDocument(originalDocument); - editorComponent.setText(null); - //remove old property change listener - for (PropertyChangeListener l : c.getPropertyChangeListeners("editor")) { - if (l instanceof AutoCompletePropertyChangeListener) { - c.removePropertyChangeListener("editor", l); - } - } - } - @Override public void propertyChange(PropertyChangeEvent evt) { String propertyName = evt.getPropertyName(); Modified: trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing/editor/bean/BeanListHeader.jaxx =================================================================== --- trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing/editor/bean/BeanListHeader.jaxx 2010-09-07 10:44:52 UTC (rev 2070) +++ trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing/editor/bean/BeanListHeader.jaxx 2010-09-07 10:45:13 UTC (rev 2071) @@ -99,12 +99,6 @@ <script><![CDATA[ -public static final String DEFAULT_POPUP_LABEL = n_("bean.popup.label"); - -public static final String DEFAULT_SELECTED_TOOLTIP = n_("bean.sort.on"); - -public static final String DEFAULT_NOT_SELECTED_TOOLTIP = n_("bean.sort.off"); - public void init(JXPathDecorator<O> decorator, java.util.List<O> data) { handler.init(decorator, data); } Modified: trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing/editor/bean/BeanListHeaderHandler.java =================================================================== --- trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing/editor/bean/BeanListHeaderHandler.java 2010-09-07 10:44:52 UTC (rev 2070) +++ trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing/editor/bean/BeanListHeaderHandler.java 2010-09-07 10:45:13 UTC (rev 2071) @@ -35,20 +35,12 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import javax.swing.JComponent; import javax.swing.JPopupMenu; -import javax.swing.JRadioButtonMenuItem; -import javax.swing.JToggleButton; -import javax.swing.SwingUtilities; -import java.awt.Dimension; -import java.beans.Introspector; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; -import java.util.Date; import java.util.List; -import static org.nuiton.i18n.I18n._; -import static org.nuiton.i18n.I18n.n_; - /** * Le handler d'un {@link BeanListHeader}. * <p/> @@ -72,6 +64,19 @@ protected boolean init; + private final BeanUIUtil.PopupHandler popupHandler = new BeanUIUtil.PopupHandler() { + + @Override + public JPopupMenu getPopup() { + return ui.getPopup(); + } + + @Override + public JComponent getInvoker() { + return ui.getChangeDecorator(); + } + }; + public BeanListHeaderHandler(BeanListHeader<O> ui) { this.ui = ui; } @@ -88,33 +93,24 @@ throw new IllegalStateException("can not init the handler twice"); } init = true; - if (decorator == null) { - throw new NullPointerException( - "can not have a null decorator as parameter"); - } JAXXButtonGroup indexes = ui.getIndexes(); - MultiJXPathDecorator<O> d; - if (decorator instanceof MultiJXPathDecorator<?>) { + this.decorator = BeanUIUtil.createDecorator(decorator); - // should clone decorator ? - d = (MultiJXPathDecorator<O>) decorator; - } else { - d = DecoratorUtils.newMultiJXPathDecorator( - decorator.getInternalClass(), - decorator.getInitialExpression(), - "??" + new Date().getTime(), - " - " - ); - } - this.decorator = d; - // init combobox renderer base on given decorator - ui.getList().setCellRenderer(new DecoratorListCellRenderer(d)); + ui.getList().setCellRenderer(new DecoratorListCellRenderer(this.decorator)); // build popup - preparePopup(d); + popupHandler.preparePopup(ui.getSelectedToolTipText(), + ui.getNotSelectedToolTipText(), + ui.getI18nPrefix(), + ui.getPopupTitleText(), + indexes, + ui.getPopupLabel(), + ui.getSortUp(), + ui.getSortDown(), + this.decorator); ui.addPropertyChangeListener(this); @@ -127,42 +123,9 @@ /** Toggle the popup visible state. */ public void togglePopup() { - boolean newValue = !ui.getPopup().isVisible(); - - if (log.isTraceEnabled()) { - log.trace(newValue); - } - - if (!newValue) { - if (ui.getPopup() != null) { - ui.getPopup().setVisible(false); - } - return; - } - SwingUtilities.invokeLater(new Runnable() { - - @Override - public void run() { - - updatePopup(); - - Dimension dim = ui.getPopup().getPreferredSize(); - JToggleButton invoker = ui.getChangeDecorator(); - ui.getPopup().show( - invoker, - (int) (invoker.getPreferredSize().getWidth() - dim.getWidth()), - invoker.getHeight() - ); - } - }); + popupHandler.togglePopup(); } - protected void updatePopup() { - JPopupMenu popup = ui.getPopup(); - popup.pack(); - - } - /** * Modifie l'index du décorateur * @@ -176,7 +139,7 @@ if (log.isDebugEnabled()) { log.debug("check state : <" + oldValue + " to " + newValue + ">"); } - updateComboBox(newValue, ui.isReverseSort()); + updateUI(newValue, ui.isReverseSort()); } /** @@ -195,17 +158,20 @@ log.debug("check state : <" + oldValue + " to " + newValue + ">"); } - updateComboBox(ui.getIndex(), newValue); + updateUI(ui.getIndex(), newValue); } @SuppressWarnings({"unchecked"}) - protected void updateComboBox(int index, boolean reverseSort) { + protected void updateUI(int index, boolean reverseSort) { // change decorator context decorator.setContextIndex(index); String expression = decorator.getExpression(); - log.info("will use expression (index = " + index + ") : " + expression); + if (log.isDebugEnabled()) { + log.debug("will use expression (index = " + index + ") : " + + expression); + } // get the current selection in list Object[] selection = ui.getList().getSelectedValues(); @@ -254,71 +220,6 @@ return decorator; } - /** - * Creation de l'ui pour modifier le décorateur. - * - * @param decorator le decorateur a utiliser - */ - protected void preparePopup(MultiJXPathDecorator<?> decorator) { - String selectedTip = ui.getSelectedToolTipText(); - if (selectedTip == null) { - // use default selected tip text - selectedTip = BeanListHeader.DEFAULT_SELECTED_TOOLTIP; - } - String notSelectedTip = ui.getNotSelectedToolTipText(); - if (notSelectedTip == null) { - // use default selected tip text - notSelectedTip = BeanListHeader.DEFAULT_NOT_SELECTED_TOOLTIP; - } - JPopupMenu popup = ui.getPopup(); - - //Container container = ui.getIndexesContainer(); - for (int i = 0, max = decorator.getNbContext(); i < max; i++) { - String property = ui.getI18nPrefix() + decorator.getProperty(i); - String propertyI18n = _(property); - JRadioButtonMenuItem button = new JRadioButtonMenuItem(propertyI18n); - button.putClientProperty(JAXXButtonGroup.BUTTON8GROUP_CLIENT_PROPERTY, ui.getIndexes()); - button.putClientProperty(JAXXButtonGroup.VALUE_CLIENT_PROPERTY, i); - popup.add(button); - if (selectedTip != null) { - button.putClientProperty(JAXXButtonGroup.SELECTED_TIP_CLIENT_PROPERTY, _(selectedTip, propertyI18n)); - } - if (notSelectedTip != null) { - button.putClientProperty(JAXXButtonGroup.NOT_SELECTED_TIP_CLIENT_PROPERTY, _(notSelectedTip, propertyI18n)); - } - button.setSelected(false); - ui.getIndexes().add(button); - } - String title = ui.getPopupTitleText(); - if (title == null) { - // use default popup title - title = BeanListHeader.DEFAULT_POPUP_LABEL; - - Class<O> internalClass = ui.getBeanType(); - String beanI18nKey; - if (internalClass == null) { - beanI18nKey = n_("bean.unknown.type"); - } else { - beanI18nKey = ui.getI18nPrefix() + - Introspector.decapitalize(internalClass.getSimpleName()); - } - String beanI18n = _(beanI18nKey); - title = _(title, beanI18n); - } else { - title = _(title); - } - - ui.getSortDown().putClientProperty(JAXXButtonGroup.SELECTED_TIP_CLIENT_PROPERTY, _("bean.sort.down.tip")); - ui.getSortDown().putClientProperty(JAXXButtonGroup.NOT_SELECTED_TIP_CLIENT_PROPERTY, _("bean.sort.down.toSelect.tip")); - - ui.getSortUp().putClientProperty(JAXXButtonGroup.SELECTED_TIP_CLIENT_PROPERTY, _("bean.sort.up.tip")); - ui.getSortUp().putClientProperty(JAXXButtonGroup.NOT_SELECTED_TIP_CLIENT_PROPERTY, _("bean.sort.up.toSelect.tip")); - - ui.getPopupLabel().setText(title); - ui.getPopup().setLabel(title); - ui.getPopup().invalidate(); - } - @Override public void propertyChange(PropertyChangeEvent evt) { String propertyName = evt.getPropertyName(); Added: trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing/editor/bean/BeanUIUtil.java =================================================================== --- trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing/editor/bean/BeanUIUtil.java (rev 0) +++ trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing/editor/bean/BeanUIUtil.java 2010-09-07 10:45:13 UTC (rev 2071) @@ -0,0 +1,266 @@ +/* + * #%L + * JAXX :: Widgets + * + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2008 - 2010 CodeLutin + * %% + * 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 + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package jaxx.runtime.swing.editor.bean; + +import jaxx.runtime.decorator.Decorator; +import jaxx.runtime.decorator.DecoratorUtils; +import jaxx.runtime.decorator.JXPathDecorator; +import jaxx.runtime.decorator.MultiJXPathDecorator; +import jaxx.runtime.swing.JAXXButtonGroup; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jdesktop.swingx.autocomplete.AutoCompleteDecorator; +import org.jdesktop.swingx.autocomplete.AutoCompletePropertyChangeListener; +import org.jdesktop.swingx.autocomplete.ObjectToStringConverter; + +import javax.swing.AbstractButton; +import javax.swing.ButtonGroup; +import javax.swing.JComboBox; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPopupMenu; +import javax.swing.JRadioButtonMenuItem; +import javax.swing.SwingUtilities; +import javax.swing.text.Document; +import javax.swing.text.JTextComponent; +import java.awt.Component; +import java.awt.Dimension; +import java.beans.Introspector; +import java.beans.PropertyChangeListener; +import java.util.Date; + +import static org.nuiton.i18n.I18n._; +import static org.nuiton.i18n.I18n.n_; + +/** + * Class with usefull methods used in bean uis. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.2 + */ +public class BeanUIUtil { + + public static final String DEFAULT_POPUP_LABEL = n_("bean.popup.label"); + + public static final String DEFAULT_SELECTED_TOOLTIP = n_("bean.sort.on"); + + public static final String DEFAULT_NOT_SELECTED_TOOLTIP = n_("bean.sort.off"); + + /** + * Encapsule un {@link Decorator} dans un {@link ObjectToStringConverter}. + * + * @param decorator le decorateur a encapsuler. + * @return le converter encapsule dans un {@link ObjectToStringConverter} + */ + public static ObjectToStringConverter newDecoratedObjectToStringConverter(final Decorator<?> decorator) { + + return new ObjectToStringConverter() { + + @Override + public String getPreferredStringForItem(Object item) { + return item instanceof String ? (String) item : item == null ? "" : decorator.toString(item); + } + }; + } + + /** + * Ajout l'auto-complétion sur une liste déroulante, en utilisant le + * converteur donné pour afficher les données. + * + * @param combo la combo à décorer + * @param convertor le converter utilisé pour afficher les données. + */ + public static void decorate(JComboBox combo, ObjectToStringConverter convertor) { + + AutoCompleteDecorator.decorate(combo, convertor); + } + + /** + * Désactive l'aut-complétion sur une liste déroulante, en y repositionnant + * le modèle du document d'édition d'avant auto-complétion. + * + * @param combo la liste déroulante à décorer + * @param originalDocument le document original de l'édtieur de la + * liste déroulante. + */ + public static void undecorate(JComboBox combo, Document originalDocument) { + + // has not to be editable + combo.setEditable(false); + + // configure the text component=editor component + Component c = combo.getEditor().getEditorComponent(); + JTextComponent editorComponent = (JTextComponent) c; + editorComponent.setDocument(originalDocument); + editorComponent.setText(null); + //remove old property change listener + for (PropertyChangeListener l : c.getPropertyChangeListeners("editor")) { + if (l instanceof AutoCompletePropertyChangeListener) { + c.removePropertyChangeListener("editor", l); + } + } + } + + public static <O> MultiJXPathDecorator<O> createDecorator(JXPathDecorator<O> decorator) { + if (decorator == null) { + throw new NullPointerException( + "can not have a null decorator as parameter"); + } + String separator; + String separatorReplacement; + + if (decorator instanceof MultiJXPathDecorator<?>) { + + separator = ((MultiJXPathDecorator<?>) decorator).getSeparator(); + separatorReplacement = ((MultiJXPathDecorator<?>) decorator).getSeparatorReplacement(); + + } else { + + separator = "??" + new Date().getTime(); + separatorReplacement = " - "; + } + + return DecoratorUtils.newMultiJXPathDecorator( + decorator.getInternalClass(), + decorator.getInitialExpression(), + separator, + separatorReplacement + ); + } + + public static abstract class PopupHandler implements Runnable { + + public static final Log log = LogFactory.getLog(PopupHandler.class); + + public abstract JPopupMenu getPopup(); + + public abstract JComponent getInvoker(); + + @Override + public void run() { + + updatePopup(); + + Dimension dim = getPopup().getPreferredSize(); + + JComponent invoker = getInvoker(); + getPopup().show( + invoker, + (int) (invoker.getPreferredSize().getWidth() - dim.getWidth()), + invoker.getHeight() + ); + } + + /** Toggle the popup visible state. */ + public void togglePopup() { + boolean newValue = !getPopup().isVisible(); + + if (log.isTraceEnabled()) { + log.trace(newValue); + } + + if (!newValue) { + if (getPopup() != null) { + getPopup().setVisible(false); + } + return; + } + SwingUtilities.invokeLater(this); + } + + protected void updatePopup() { + getPopup().pack(); + } + + /** + * Creation de l'ui pour modifier le décorateur. + * + * @param decorator le decorateur a utiliser + */ + protected void preparePopup(String selectedTip, + String notSelectedTip, + String i18nPrefix, + String title, + ButtonGroup indexes, + JLabel popupLabel, + AbstractButton sortUp, + AbstractButton sortDown, + MultiJXPathDecorator<?> decorator) { + if (selectedTip == null) { + // use default selected tip text + selectedTip = DEFAULT_SELECTED_TOOLTIP; + } + if (notSelectedTip == null) { + // use default selected tip text + notSelectedTip = DEFAULT_NOT_SELECTED_TOOLTIP; + } + JPopupMenu popup = getPopup(); + + //Container container = ui.getIndexesContainer(); + for (int i = 0, max = decorator.getNbContext(); i < max; i++) { + String property = i18nPrefix + decorator.getProperty(i); + String propertyI18n = _(property); + JRadioButtonMenuItem button = new JRadioButtonMenuItem(propertyI18n); + button.putClientProperty(JAXXButtonGroup.BUTTON8GROUP_CLIENT_PROPERTY, indexes); + button.putClientProperty(JAXXButtonGroup.VALUE_CLIENT_PROPERTY, i); + popup.add(button); + if (selectedTip != null) { + button.putClientProperty(JAXXButtonGroup.SELECTED_TIP_CLIENT_PROPERTY, _(selectedTip, propertyI18n)); + } + if (notSelectedTip != null) { + button.putClientProperty(JAXXButtonGroup.NOT_SELECTED_TIP_CLIENT_PROPERTY, _(notSelectedTip, propertyI18n)); + } + button.setSelected(false); + indexes.add(button); + } + if (title == null) { + // use default popup title + title = DEFAULT_POPUP_LABEL; + + Class<?> internalClass = decorator.getInternalClass(); + String beanI18nKey; + if (internalClass == null) { + beanI18nKey = n_("bean.unknown.type"); + } else { + beanI18nKey = i18nPrefix + Introspector.decapitalize(internalClass.getSimpleName()); + } + String beanI18n = _(beanI18nKey); + title = _(title, beanI18n); + } else { + title = _(title); + } + + sortDown.putClientProperty(JAXXButtonGroup.SELECTED_TIP_CLIENT_PROPERTY, _("bean.sort.down.tip")); + sortDown.putClientProperty(JAXXButtonGroup.NOT_SELECTED_TIP_CLIENT_PROPERTY, _("bean.sort.down.toSelect.tip")); + + sortUp.putClientProperty(JAXXButtonGroup.SELECTED_TIP_CLIENT_PROPERTY, _("bean.sort.up.tip")); + sortUp.putClientProperty(JAXXButtonGroup.NOT_SELECTED_TIP_CLIENT_PROPERTY, _("bean.sort.up.toSelect.tip")); + + popupLabel.setText(title); + getPopup().setLabel(title); + getPopup().invalidate(); + } + } +} Added: trunk/jaxx-widgets/src/main/resources/icons/action-combobox-reset.png =================================================================== (Binary files differ) Property changes on: trunk/jaxx-widgets/src/main/resources/icons/action-combobox-reset.png ___________________________________________________________________ Added: svn:mime-type + application/octet-stream Added: trunk/jaxx-widgets/src/main/resources/icons/action-combobox-sort.png =================================================================== (Binary files differ) Property changes on: trunk/jaxx-widgets/src/main/resources/icons/action-combobox-sort.png ___________________________________________________________________ Added: svn:mime-type + application/octet-stream