Author: tchemit Date: 2010-09-06 18:36:18 +0200 (Mon, 06 Sep 2010) New Revision: 2067 Url: http://nuiton.org/repositories/revision/jaxx/2067 Log: Evolution #848: Introduce jaxx.runtime.swing.editor.bean package Evolution #851: Introduce BeanListHeader Anomalie #853: when using MultiJxPathDecorator, can not use extra token formatter in expression Added: 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 trunk/jaxx-widgets/src/main/resources/icons/action-bean-sort-down.png trunk/jaxx-widgets/src/main/resources/icons/action-bean-sort-up.png trunk/jaxx-widgets/src/main/resources/icons/action-bean-sort.png Modified: trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing/EntityComboBox.jaxx trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing/EntityComboBoxHandler.java 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/resources/i18n/jaxx-widgets-en_GB.properties trunk/jaxx-widgets/src/main/resources/i18n/jaxx-widgets-fr_FR.properties Modified: trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing/EntityComboBox.jaxx =================================================================== --- trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing/EntityComboBox.jaxx 2010-09-06 13:01:57 UTC (rev 2066) +++ trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing/EntityComboBox.jaxx 2010-09-06 16:36:18 UTC (rev 2067) @@ -69,11 +69,11 @@ <String id='popupTitleText' javaBean='null'/> - <String id='i18nPrefix' javaBean='"entitycombobox.common."'/> + <String id='i18nPrefix' javaBean='"beancombobox.common."'/> <!-- popup to change sorted property--> <JPopupMenu id='popup' - border='{new TitledBorder(_("entitycombobox.popup.title"))}' + border='{new TitledBorder(_("beancombobox.popup.title"))}' onPopupMenuWillBecomeInvisible='getChangeDecorator().setSelected(false)' onPopupMenuCanceled='getChangeDecorator().setSelected(false)'> <JLabel id='popupLabel'/> @@ -84,11 +84,11 @@ import static org.nuiton.i18n.I18n.n_; import jaxx.runtime.decorator.JXPathDecorator; -public static final String DEFAULT_POPUP_LABEL = n_("entitycombobox.popup.label"); +public static final String DEFAULT_POPUP_LABEL = n_("bean.popup.label"); -public static final String DEFAULT_SELECTED_TOOLTIP = n_("entitycombobox.sort.on"); +public static final String DEFAULT_SELECTED_TOOLTIP = n_("bean.sort.on"); -public static final String DEFAULT_NOT_SELECTED_TOOLTIP = n_("entitycombobox.sort.off"); +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); @@ -106,7 +106,7 @@ <!-- le boutton pour reinitialiser la valeur sélectionnée --> <JToolBar floatable='false' borderPainted='false' visible='{isShowReset()}'> <JButton actionIcon='combobox-reset' - toolTipText='entitycombobox.action.reset.tip' + toolTipText='beancombobox.action.reset.tip' focusable='false' focusPainted='false' enabled='{isEditable() && isEnabled()}' @@ -129,7 +129,7 @@ <JToolBar floatable='false' borderPainted='false' visible='{isShowDecorator()}'> <JToggleButton id='changeDecorator' actionIcon='combobox-sort' - toolTipText='entitycombobox.action.sort.tip' + toolTipText='beancombobox.action.sort.tip' focusable='false' focusPainted='false' onActionPerformed='getHandler().togglePopup()'/> Modified: trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing/EntityComboBoxHandler.java =================================================================== --- trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing/EntityComboBoxHandler.java 2010-09-06 13:01:57 UTC (rev 2066) +++ trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing/EntityComboBoxHandler.java 2010-09-06 16:36:18 UTC (rev 2067) @@ -359,7 +359,7 @@ Class<?> internalClass = decorator.getInternalClass(); String beanI18nKey; if (internalClass == null) { - beanI18nKey = n_("entitycombobox.unknown.type"); + beanI18nKey = n_("bean.unknown.type"); } else { beanI18nKey = ui.getI18nPrefix() + Introspector.decapitalize(internalClass.getSimpleName()); } 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-06 13:01:57 UTC (rev 2066) +++ trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing/editor/bean/BeanComboBox.jaxx 2010-09-06 16:36:18 UTC (rev 2067) @@ -26,67 +26,85 @@ <Table fill='both' insets='0' genericType='O' onFocusGained='combobox.requestFocus()' onFocusLost='hidePopup()'> - - <!-- auto complete property --> - <Boolean id='autoComplete' javaBean='false'/> - <!-- show reset property --> - <Boolean id='showReset' javaBean='false'/> + <!-- auto complete property --> + <Boolean id='autoComplete' javaBean='false'/> - <!-- show decorator property --> - <Boolean id='showDecorator' javaBean='true'/> + <!-- flag to reverse the sort --> + <Boolean id='reverseSort' javaBean='false'/> + + <!-- show reset property --> + <Boolean id='showReset' javaBean='false'/> - <!-- editable combo property --> - <Boolean id='editable' javaBean='true'/> + <!-- show decorator property --> + <Boolean id='showDecorator' javaBean='true'/> - <!-- bean property linked state --> - <String id='property' javaBean='""'/> + <!-- editable combo property --> + <Boolean id='editable' javaBean='true'/> - <!-- bean property --> - <Object id='bean' javaBean='null'/> + <!-- bean property linked state --> + <String id='property' javaBean='""'/> - <!-- selectedItem property --> - <Object id='selectedItem' javaBean='null'/> + <!-- bean property --> + <Object id='bean' javaBean='null'/> - <!-- sort index property --> - <Integer id='index' javaBean='0'/> + <!-- selectedItem property --> + <Object id='selectedItem' javaBean='null'/> - <!-- datas of the combo-box --> - <java.util.List id='data' genericType='O' javaBean='null'/> + <!-- sort index property --> + <Integer id='index' javaBean='0'/> - <!-- model of sorted property --> - <ButtonGroup id='indexes' useToolTipText='true' - onStateChanged='setIndex((Integer)indexes.getSelectedValue())'/> + <!-- datas of the combo-box --> + <java.util.List id='data' genericType='O' javaBean='null'/> - <!-- ui handler --> - <BeanComboBoxHandler id='handler' genericType='O' constructorParams='this'/> + <!-- model of sorted property --> + <ButtonGroup id='indexes' useToolTipText='true' + onStateChanged='setIndex((Integer)indexes.getSelectedValue())'/> - <String id='selectedToolTipText' javaBean='null'/> + <ButtonGroup id='sortGroup' useToolTipText='true' + selectedValue='{isReverseSort()}' + onStateChanged='setReverseSort((Boolean)sortGroup.getSelectedValue())'/> + + <!-- ui handler --> + <BeanComboBoxHandler id='handler' genericType='O' constructorParams='this'/> - <String id='notSelectedToolTipText' javaBean='null'/> + <String id='selectedToolTipText' javaBean='null'/> - <String id='popupTitleText' javaBean='null'/> - - <String id='i18nPrefix' javaBean='"entitycombobox.common."'/> + <String id='notSelectedToolTipText' javaBean='null'/> - <!-- popup to change sorted property--> - <JPopupMenu id='popup' - border='{new TitledBorder(_("entitycombobox.popup.title"))}' - onPopupMenuWillBecomeInvisible='getChangeDecorator().setSelected(false)' - onPopupMenuCanceled='getChangeDecorator().setSelected(false)'> - <JLabel id='popupLabel'/> - <JSeparator/> - </JPopupMenu> + <String id='popupTitleText' javaBean='null'/> - <script><![CDATA[ + <String id='i18nPrefix' javaBean='"entitycombobox.common."'/> + + <!-- popup to change sorted property--> + <JPopupMenu id='popup' + border='{new TitledBorder(_("beancombobox.popup.title"))}' + onPopupMenuWillBecomeInvisible='getChangeDecorator().setSelected(false)' + onPopupMenuCanceled='getChangeDecorator().setSelected(false)'> + <JLabel id='popupSortLabel' actionIcon="bean-sort" text="bean.sort.label"/> + + <JRadioButtonMenuItem id='sortUp' buttonGroup="sortGroup" value='{false}' + actionIcon="bean-sort-up" text='bean.sort.up' + selected='{!isReverseSort()}'/> + + <JRadioButtonMenuItem id='sortDown' buttonGroup="sortGroup" value='{true}' + actionIcon="bean-sort-down" text='bean.sort.down' + selected='{isReverseSort()}'/> + + <JSeparator/> + <JLabel id='popupLabel'/> + <JSeparator/> + </JPopupMenu> + + <script><![CDATA[ import static org.nuiton.i18n.I18n.n_; import jaxx.runtime.decorator.JXPathDecorator; -public static final String DEFAULT_POPUP_LABEL = n_("entitycombobox.popup.label"); +public static final String DEFAULT_POPUP_LABEL = n_("bean.popup.label"); -public static final String DEFAULT_SELECTED_TOOLTIP = n_("entitycombobox.sort.on"); +public static final String DEFAULT_SELECTED_TOOLTIP = n_("bean.sort.on"); -public static final String DEFAULT_NOT_SELECTED_TOOLTIP = n_("entitycombobox.sort.off"); +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); @@ -98,40 +116,42 @@ } } ]]> - </script> - <row> - <cell anchor='west'> - <!-- le boutton pour reinitialiser la valeur sélectionnée --> - <JToolBar floatable='false' borderPainted='false' visible='{isShowReset()}'> - <JButton actionIcon='combobox-reset' - toolTipText='entitycombobox.action.reset.tip' - focusable='false' - focusPainted='false' - enabled='{isEditable() && isEnabled()}' - onActionPerformed='setSelectedItem(null)'/> - </JToolBar> + </script> + <row> + <cell anchor='west'> + <!-- le boutton pour reinitialiser la valeur sélectionnée --> + <JToolBar floatable='false' borderPainted='false' + visible='{isShowReset()}'> + <JButton actionIcon='combobox-reset' + toolTipText='beancombobox.action.reset.tip' + focusable='false' + focusPainted='false' + enabled='{isEditable() && isEnabled()}' + onActionPerformed='setSelectedItem(null)'/> + </JToolBar> - </cell> - <cell weightx='1'> - <!-- la liste déroulante --> - <JComboBox id='combobox' - selectedItem='{getSelectedItem()}' - enabled='{isEnabled()}' - editable='{isEditable()}' - focusable='{isEnabled() && isEditable()}' - onFocusGained='hidePopup()' - onItemStateChanged='setSelectedItem(combobox.getSelectedItem())'/> - </cell> - <cell anchor='east' fill='both' insets='0'> - <!-- le boutton pour changer le tri --> - <JToolBar floatable='false' borderPainted='false' visible='{isShowDecorator()}'> - <JToggleButton id='changeDecorator' - actionIcon='combobox-sort' - toolTipText='entitycombobox.action.sort.tip' - focusable='false' - focusPainted='false' - onActionPerformed='getHandler().togglePopup()'/> - </JToolBar> - </cell> - </row> + </cell> + <cell weightx='1'> + <!-- la liste déroulante --> + <JComboBox id='combobox' + selectedItem='{getSelectedItem()}' + enabled='{isEnabled()}' + editable='{isEditable()}' + focusable='{isEnabled() && isEditable()}' + onFocusGained='hidePopup()' + onItemStateChanged='setSelectedItem(combobox.getSelectedItem())'/> + </cell> + <cell anchor='east' fill='both' insets='0'> + <!-- le boutton pour changer le tri --> + <JToolBar floatable='false' borderPainted='false' + visible='{isShowDecorator()}'> + <JToggleButton id='changeDecorator' + actionIcon='combobox-sort' + toolTipText='beancombobox.action.sort.tip' + focusable='false' + focusPainted='false' + onActionPerformed='getHandler().togglePopup()'/> + </JToolBar> + </cell> + </row> </Table> 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-06 13:01:57 UTC (rev 2066) +++ trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing/editor/bean/BeanComboBoxHandler.java 2010-09-06 16:36:18 UTC (rev 2067) @@ -55,6 +55,7 @@ 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._; @@ -63,7 +64,7 @@ /** * Le handler d'un {@link BeanComboBox}. * <p/> - * Note: ce handler n'est pas staeless et n'est donc pas partageable entre plusieurs ui. + * Note: ce handler n'est pas stateless et n'est donc pas partageable entre plusieurs ui. * * @author tchemit <chemit@codelutin.com> * @param <O> le type des objet contenus dans le modèle du composant. @@ -140,7 +141,11 @@ // should clone decorator ? d = (MultiJXPathDecorator<O>) decorator; } else { - d = DecoratorUtils.newMultiJXPathDecorator(decorator.getInternalClass(), decorator.getInitialExpression(), " - "); + d = DecoratorUtils.newMultiJXPathDecorator( + decorator.getInternalClass(), + decorator.getInitialExpression(), + "??" + new Date().getTime(), + " - "); } this.decorator = d; @@ -185,14 +190,27 @@ @Override public void run() { - ui.getPopup().pack(); + + updatePopup(); + Dimension dim = ui.getPopup().getPreferredSize(); JToggleButton invoker = ui.getChangeDecorator(); - ui.getPopup().show(invoker, (int) (invoker.getPreferredSize().getWidth() - dim.getWidth()), invoker.getHeight()); + ui.getPopup().show( + invoker, + (int) (invoker.getPreferredSize().getWidth() - dim.getWidth()), + invoker.getHeight() + ); } }); } + + protected void updatePopup() { + JPopupMenu popup = ui.getPopup(); + popup.pack(); + + } + /** * Modifie l'état autoComplete de l'ui. * @@ -225,17 +243,40 @@ * @param oldValue l'ancienne valeur * @param newValue la nouvelle valeur */ - @SuppressWarnings({"unchecked"}) protected void setIndex(Integer oldValue, Integer newValue) { - if (newValue.equals(oldValue)) { + if (newValue == null || newValue.equals(oldValue)) { return; } if (log.isDebugEnabled()) { log.debug("check state : <" + oldValue + " to " + newValue + ">"); } + updateComboBox(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 + ">"); + } + + updateComboBox(ui.getIndex(), newValue); + } + + @SuppressWarnings({"unchecked"}) + protected void updateComboBox(int index, boolean reversesort) { + // change decorator context - decorator.setContextIndex(newValue); + decorator.setContextIndex(index); // keep selected item Object previousSelectedItem = ui.getSelectedItem(); @@ -251,10 +292,15 @@ ui.selectedItem = null; } - + List<O> data = ui.getData(); try { // Sort data with the decorator jxpath tokens. - DecoratorUtils.sort(decorator, ui.getData(), newValue); + DecoratorUtils.sort(decorator, + data, + index, + reversesort + ); + } catch (Exception e) { log.warn(e.getMessage(), e); //System.out.println("newValue :: "+decorator+" : "+newValue); @@ -262,7 +308,7 @@ } // reload the model - SwingUtil.fillComboBox(ui.getCombobox(), ui.getData(), null); + SwingUtil.fillComboBox(ui.getCombobox(), data, null); if (wasAutoComplete) { ui.setAutoComplete(true); @@ -367,7 +413,7 @@ Class<?> internalClass = decorator.getInternalClass(); String beanI18nKey; if (internalClass == null) { - beanI18nKey = n_("entitycombobox.unknown.type"); + beanI18nKey = n_("bean.unknown.type"); } else { beanI18nKey = ui.getI18nPrefix() + Introspector.decapitalize(internalClass.getSimpleName()); } @@ -376,9 +422,19 @@ } 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() { @@ -469,23 +525,37 @@ public void propertyChange(PropertyChangeEvent evt) { String propertyName = evt.getPropertyName(); - if (SELECTED_ITEM_PROPERTY.equals(propertyName)) { + if (BeanComboBox.PROPERTY_SELECTED_ITEM.equals(propertyName)) { setSelectedItem(evt.getOldValue(), evt.getNewValue()); return; } - if (INDEX_PROPERTY.equals(propertyName)) { - setIndex((Integer) evt.getOldValue(), (Integer) evt.getNewValue()); + if (BeanComboBox.PROPERTY_AUTO_COMPLETE.equals(propertyName)) { + + setAutoComplete((Boolean) evt.getOldValue(), (Boolean) evt.getNewValue()); return; } - if (AUTO_COMPLETE_PROPERTY.equals(propertyName)) { - setAutoComplete((Boolean) evt.getOldValue(), (Boolean) evt.getNewValue()); + + 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 (DATA_PROPERTY.equals(propertyName)) { + + 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()); } - } } Added: 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 (rev 0) +++ trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing/editor/bean/BeanListHeader.jaxx 2010-09-06 16:36:18 UTC (rev 2067) @@ -0,0 +1,153 @@ +<!-- + #%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% + --> + +<JPanel id='top' layout='{new BorderLayout()}' genericType='O'> + + <import> + static org.nuiton.i18n.I18n.n_ + jaxx.runtime.decorator.JXPathDecorator + </import> + + <!-- show reset property --> + <Boolean id='showReset' javaBean='false'/> + + <!-- show decorator property --> + <Boolean id='showDecorator' javaBean='true'/> + + <!-- flag to reverse the sort --> + <Boolean id='reverseSort' javaBean='false'/> + + <!-- editable combo property --> + <Boolean id='editable' javaBean='true'/> + + <!-- bean property --> + <Class genericType='O' id='beanType' javaBean='null'/> + + <!-- label --> + <String id='labelText' javaBean='null'/> + + <!-- sort index property --> + <Integer id='index' javaBean='0'/> + + <!-- datas of the combo-box --> + <java.util.List id='data' genericType='O' javaBean='null'/> + + <!-- model of sorted property --> + <ButtonGroup id='indexes' useToolTipText='true' + onStateChanged='setIndex((Integer)indexes.getSelectedValue())'/> + + <ButtonGroup id='sortGroup' useToolTipText='true' + selectedValue='{isReverseSort()}' + onStateChanged='setReverseSort((Boolean)sortGroup.getSelectedValue())'/> + + <!-- ui handler --> + <BeanListHeaderHandler id='handler' genericType='O' constructorParams='this'/> + + <String id='selectedToolTipText' javaBean='null'/> + + <String id='notSelectedToolTipText' javaBean='null'/> + + <String id='popupTitleText' javaBean='null'/> + + <String id='i18nPrefix' javaBean='"beanlist.common."'/> + + <!-- popup to change sorted property--> + <JPopupMenu id='popup' + border='{new TitledBorder(_("beanlist.popup.title"))}' + onPopupMenuWillBecomeInvisible='getChangeDecorator().setSelected(false)' + onPopupMenuCanceled='getChangeDecorator().setSelected(false)'> + + <JLabel id='popupSortLabel' actionIcon="bean-sort" text="bean.sort.label"/> + + <JRadioButtonMenuItem id='sortUp' buttonGroup="sortGroup" value='{false}' + actionIcon="bean-sort-up" text='bean.sort.up' + selected='{!isReverseSort()}'/> + + <JRadioButtonMenuItem id='sortDown' buttonGroup="sortGroup" value='{true}' + actionIcon="bean-sort-down" text='bean.sort.down' + selected='{isReverseSort()}'/> + + <JSeparator/> + + <JLabel id='popupLabel'/> + + </JPopupMenu> + + <JList id='list' javaBean="new JList()"/> + + <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); +} + +protected void hidePopup() { + if (popup.isVisible()) { + popup.setVisible(false); + } +} + +protected Object updateSort(boolean reverseSort) { + if (reverseSort) { + return sortDown; + } + return sortUp; +} +]]> + </script> + + <JLabel id="label" constraints='BorderLayout.WEST' opaque='false' + text='{SwingUtil.getStringValue(labelText)}'/> + + <JToolBar floatable='false' borderPainted='false' + constraints='BorderLayout.EAST'> + + <!-- le boutton pour reinitialiser la valeur sélectionnée --> + + <JButton actionIcon='combobox-reset' + toolTipText='beanlist.action.reset.tip' + focusable='false' + focusPainted='false' + visible='{isShowReset()}' + enabled='{isEnabled()}' + onActionPerformed='getList().getSelectionModel().clearSelection()'/> + + <!-- le boutton pour changer le tri --> + <JToggleButton id='changeDecorator' + actionIcon='combobox-sort' + toolTipText='beanlist.action.sort.tip' + focusable='false' + focusPainted='false' + visible='{isShowDecorator()}' + onActionPerformed='getHandler().togglePopup()'/> + </JToolBar> +</JPanel> + Added: 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 (rev 0) +++ trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing/editor/bean/BeanListHeaderHandler.java 2010-09-06 16:36:18 UTC (rev 2067) @@ -0,0 +1,349 @@ +/* + * #%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.DecoratorUtils; +import jaxx.runtime.decorator.JXPathDecorator; +import jaxx.runtime.decorator.MultiJXPathDecorator; +import jaxx.runtime.swing.JAXXButtonGroup; +import jaxx.runtime.swing.renderer.DecoratorListCellRenderer; +import org.apache.commons.collections.primitives.ArrayIntList; +import org.apache.commons.collections.primitives.IntList; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +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/> + * Note: ce handler n'est pas stateless et n'est donc pas partageable entre + * plusieurs ui. + * + * @author tchemit <chemit@codelutin.com> + * @param <O> le type des objet contenus dans le modèle du composant. + * @see BeanListHeader + * @since 2.2 + */ +public class BeanListHeaderHandler<O> implements PropertyChangeListener { + + public static final Log log = LogFactory.getLog(BeanListHeaderHandler.class); + + /** ui if the handler */ + protected BeanListHeader<O> ui; + + /** the decorator of data */ + protected MultiJXPathDecorator<O> decorator; + + protected boolean init; + + public BeanListHeaderHandler(BeanListHeader<O> ui) { + this.ui = ui; + } + + /** + * 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<O> decorator, List<O> data) { + + if (init) { + 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; + + // init combobox renderer base on given decorator + ui.getList().setCellRenderer(new DecoratorListCellRenderer(d)); + + // build popup + preparePopup(d); + + ui.addPropertyChangeListener(this); + + // set datas + ui.setData(data); + + // select sort button + indexes.setSelectedButton(ui.getIndex()); + } + + /** 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() + ); + } + }); + } + + protected void updatePopup() { + JPopupMenu popup = ui.getPopup(); + popup.pack(); + + } + + /** + * 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 + ">"); + } + updateComboBox(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 + ">"); + } + + updateComboBox(ui.getIndex(), newValue); + } + + @SuppressWarnings({"unchecked"}) + protected void updateComboBox(int index, boolean reverseSort) { + + // change decorator context + decorator.setContextIndex(index); + + String expression = decorator.getExpression(); + log.info("will use expression (index = " + index + ") : " + expression); + + // get the current selection in list + Object[] selection = ui.getList().getSelectedValues(); + + List<O> datas = ui.getData(); + try { + + // Sort data with the decorator jxpath tokens. + DecoratorUtils.sort(decorator, + datas, + index, + reverseSort); + } catch (Exception e) { + log.warn(e.getMessage(), e); + } + + // reload the model + ui.getList().setListData(datas.toArray(new Object[datas.size()])); + + // re-apply selection + if (selection.length > 0) { + + // re compute selection (the new data could not contains some + // previously selected items) + IntList newSelection = new ArrayIntList(); + for (Object o : selection) { + if (datas.contains(o)) { + + newSelection.add(datas.indexOf(o)); + } + } + + if (!newSelection.isEmpty()) { + + // there is still a selection to re-apply + int[] ints = newSelection.toArray(new int[newSelection.size()]); + newSelection.clear(); + ui.getList().setSelectedIndices(ints); + } + } + + ui.getList().requestFocus(); + } + + public MultiJXPathDecorator<O> getDecorator() { + 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(); + + 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()); + } + + } +} Modified: trunk/jaxx-widgets/src/main/resources/i18n/jaxx-widgets-en_GB.properties =================================================================== --- trunk/jaxx-widgets/src/main/resources/i18n/jaxx-widgets-en_GB.properties 2010-09-06 13:01:57 UTC (rev 2066) +++ trunk/jaxx-widgets/src/main/resources/i18n/jaxx-widgets-en_GB.properties 2010-09-06 16:36:18 UTC (rev 2067) @@ -2,6 +2,23 @@ aboutframe.license=License aboutframe.ok=OK aboutframe.thirdparty=Third party +bean.popup.label=Object '%1$s' +bean.sort.down=Descending sort +bean.sort.down.tip=Actually, sort order is descending +bean.sort.down.toSelect.tip=To sort in Descending order +bean.sort.label=Sort order +bean.sort.off=Click to activate the sort on this property +bean.sort.on=This is the property actually used +bean.sort.up=Ascending sort +bean.sort.up.tip=Actually, sort order is acending +bean.sort.up.toSelect.tip=Click here to sort in ascending order +bean.unknown.type=Object of unkown type +beancombobox.action.reset.tip=Reset the selected value +beancombobox.action.sort.tip=Change the sorted property +beancombobox.popup.title=Change the sorted property +beanlist.action.reset.tip=Reset the selected selection +beanlist.action.sort.tip=Change the sorted property +beanlist.popup.title=Change the sorted property columnselector.action.tip=Select the columns config.action.quit=Quit config.action.quit.tip=Quit the configuration editor @@ -36,13 +53,6 @@ config.unvalid=Option is not valid \! (previous value \: %1$s, required type \: %2$s) config.value=Value config.value.tip=Value of the option -entitycombobox.action.reset.tip=Reset the selected value -entitycombobox.action.sort.tip=Change the sorted property -entitycombobox.popup.label=Object '%1$s' -entitycombobox.popup.title=Change the sorted property -entitycombobox.sort.off=Click to activate the sort on this property -entitycombobox.sort.on=This is the property actually used -entitycombobox.unknown.type=Object of unkown type errorUI.action.close=Close errorUI.message=An error wad detected... errorUI.title=Error... Modified: trunk/jaxx-widgets/src/main/resources/i18n/jaxx-widgets-fr_FR.properties =================================================================== --- trunk/jaxx-widgets/src/main/resources/i18n/jaxx-widgets-fr_FR.properties 2010-09-06 13:01:57 UTC (rev 2066) +++ trunk/jaxx-widgets/src/main/resources/i18n/jaxx-widgets-fr_FR.properties 2010-09-06 16:36:18 UTC (rev 2067) @@ -2,6 +2,23 @@ aboutframe.license=Licence aboutframe.ok=OK aboutframe.thirdparty=Tierce partie +bean.popup.label=Objet '%1$s' +bean.sort.down=Tri d\u00E9croissant +bean.sort.down.tip=Tri d\u00E9croissant actuellement utilis\u00E9 +bean.sort.down.toSelect.tip=Cliquer pour trier selon l'ordre d\u00E9croissant +bean.sort.label=Ordre de tri +bean.sort.off=Cliquer pour activer le tri sur la propri\u00E9t\u00E9 '%1$s' +bean.sort.on=Le tri est effectu\u00E9 sur la propri\u00E9t\u00E9 '%1$s' +bean.sort.up=Tri croissant +bean.sort.up.tip=Tri croissant actuellement utilis\u00E9 +bean.sort.up.toSelect.tip=Cliquer pour trier selon l'ordre croissant +bean.unknown.type=Objet de type inconnu +beancombobox.action.reset.tip=R\u00E9initialiser la valeur de la liste d\u00E9roulante +beancombobox.action.sort.tip=Modifier le tri de la liste d\u00E9roulante +beancombobox.popup.title=Modifier le tri +beanlist.action.reset.tip=R\u00E9initialiser la valeur de la liste d\u00E9roulante +beanlist.action.sort.tip=Modifier le tri de la liste d\u00E9roulante +beanlist.popup.title=Modifier le tri columnselector.action.tip=S\u00E9lectionner les colonnes config.action.quit=Quitter config.action.quit.tip=Quitter l'\u00E9diteur de configuration @@ -36,13 +53,6 @@ config.unvalid=Option non valide (valeur originale \: %1$s, type requis \: %2$s) config.value=Valeur config.value.tip=Valeur de l'option -entitycombobox.action.reset.tip=R\u00E9initialiser la valeur de la liste d\u00E9roulante -entitycombobox.action.sort.tip=Modifier le tri de la liste d\u00E9roulante -entitycombobox.popup.label=Objet '%1$s' -entitycombobox.popup.title=Modifier le tri -entitycombobox.sort.off=Cliquer pour activer le tri sur la propri\u00E9t\u00E9 '%1$s' -entitycombobox.sort.on=Le tri est effectu\u00E9 sur la propri\u00E9t\u00E9 '%1$s' -entitycombobox.unknown.type=Objet de type inconnu errorUI.action.close=Fermer errorUI.message=Une erreur est survenue \! errorUI.title=Erreur... Added: trunk/jaxx-widgets/src/main/resources/icons/action-bean-sort-down.png =================================================================== (Binary files differ) Property changes on: trunk/jaxx-widgets/src/main/resources/icons/action-bean-sort-down.png ___________________________________________________________________ Added: svn:mime-type + application/octet-stream Added: trunk/jaxx-widgets/src/main/resources/icons/action-bean-sort-up.png =================================================================== (Binary files differ) Property changes on: trunk/jaxx-widgets/src/main/resources/icons/action-bean-sort-up.png ___________________________________________________________________ Added: svn:mime-type + application/octet-stream Added: trunk/jaxx-widgets/src/main/resources/icons/action-bean-sort.png =================================================================== (Binary files differ) Property changes on: trunk/jaxx-widgets/src/main/resources/icons/action-bean-sort.png ___________________________________________________________________ Added: svn:mime-type + application/octet-stream