From tchemit@users.nuiton.org Sat Sep 4 23:14:18 2010 From: tchemit@users.nuiton.org To: jaxx-commits@list.nuiton.org Subject: [Jaxx-commits] r2061 - in trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing: . editor/bean Date: Sat, 04 Sep 2010 23:14:18 +0200 Message-ID: <20100904211418.A3C0315A04@nuiton.codelutin.com> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="===============2188264101954059632==" --===============2188264101954059632== Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Author: tchemit Date: 2010-09-04 23:14:18 +0200 (Sat, 04 Sep 2010) New Revision: 2061 Url: http://nuiton.org/repositories/revision/jaxx/2061 Log: Evolution #848: Introduce jaxx.runtime.swing.editor.bean package Evolution #849: Deprecates EntityComboBox, use now BeanComboBox Added: trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing/editor/bean/BeanComboB= ox.jaxx trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing/editor/bean/BeanComboB= oxHandler.java Modified: trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing/EntityComboBox.jaxx trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing/EntityComboBoxHandler.= java Modified: trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing/EntityComboBox.= jaxx =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing/EntityComboBox.jaxx 2= 010-09-04 21:00:33 UTC (rev 2060) +++ trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing/EntityComboBox.jaxx 2= 010-09-04 21:14:18 UTC (rev 2061) @@ -21,6 +21,8 @@ License along with this program. If not, see . #L% + + @deprecated since 2.2, use now {@link BeanComboBox} --> =20 * @see EntityComboBox + * @deprecated since 2.2, use now {@link BeanComboBoxHandler}. */ +@Deprecated public class EntityComboBoxHandler implements PropertyChangeListener { =20 public static final Log log =3D LogFactory.getLog(EntityComboBoxHandler.= class); Added: trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing/editor/bean/BeanCo= mboBox.jaxx =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing/editor/bean/BeanCombo= Box.jaxx (rev 0) +++ trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing/editor/bean/BeanCombo= Box.jaxx 2010-09-04 21:14:18 UTC (rev 2061) @@ -0,0 +1,137 @@ + + +
+ =20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + =20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Added: trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing/editor/bean/BeanCo= mboBoxHandler.java =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing/editor/bean/BeanCombo= BoxHandler.java (rev 0) +++ trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing/editor/bean/BeanCombo= BoxHandler.java 2010-09-04 21:14:18 UTC (rev 2061) @@ -0,0 +1,491 @@ +/* + * #%L + * JAXX :: Widgets + *=20 + * $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=20 + * published by the Free Software Foundation, either version 3 of the=20 + * License, or (at your option) any later version. + *=20 + * 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. + *=20 + * You should have received a copy of the GNU General Lesser Public=20 + * License along with this program. If not, see + * . + * #L% + */ + +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; +import jaxx.runtime.swing.JAXXButtonGroup; +import jaxx.runtime.swing.renderer.DecoratorListCellRenderer; +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.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.List; + +import static org.nuiton.i18n.I18n._; +import static org.nuiton.i18n.I18n.n_; + +/** + * Le handler d'un {@link BeanComboBox}. + *

+ * Note: ce handler n'est pas staeless et n'est donc pas partageable entre p= lusieurs ui. + * + * @author tchemit + * @param le type des objet contenus dans le mod=C3=A8le du composant. + * @see BeanComboBox + */ +public class BeanComboBoxHandler implements PropertyChangeListener { + + public static final Log log =3D LogFactory.getLog(BeanComboBoxHandler.cl= ass); + + public static final String SELECTED_ITEM_PROPERTY =3D "selectedItem"; + + public static final String INDEX_PROPERTY =3D "index"; + + public static final String AUTO_COMPLETE_PROPERTY =3D "autoComplete"; + + public static final String DATA_PROPERTY =3D "data"; + + /** 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 poss= ible 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 =3D ui; + } + + protected final FocusListener EDITOR_TEXT_COMP0NENT_FOCUSLISTENER =3D ne= w 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) { + } + }; + + /** + * Initialise le handler de l'ui + * + * @param decorator le decorateur a utiliser + * @param data la liste des donn=C3=A9es a g=C3=A9rer + */ + public void init(JXPathDecorator decorator, List data) { + + if (init) { + throw new IllegalStateException("can not init the handler twice"= ); + } + init =3D true; + if (decorator =3D=3D null) { + throw new NullPointerException("can not have a null decorator as= parameter"); + } + + JAXXButtonGroup indexes =3D ui.getIndexes(); + + MultiJXPathDecorator d; + if (decorator instanceof MultiJXPathDecorator) { + // should clone decorator ? + d =3D (MultiJXPathDecorator) decorator; + } else { + d =3D DecoratorUtils.newMultiJXPathDecorator(decorator.getIntern= alClass(), decorator.getInitialExpression(), " - "); + } + this.decorator =3D d; + + // init combobox renderer base on given decorator + ui.getCombobox().setRenderer(new DecoratorListCellRenderer(d)); + + convertor =3D newDecoratedObjectToStringConverter(d); + + // keep a trace of original document (to make possible reverse autom= -complete)=20 + JTextComponent editorComponent =3D (JTextComponent) ui.getCombobox()= .getEditor().getEditorComponent(); + originalDocument =3D editorComponent.getDocument(); + + // build popup + preparePopup(d); + + ui.autoComplete =3D true; + + 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 =3D !ui.getPopup().isVisible(); + + if (log.isTraceEnabled()) { + log.trace(newValue); + } + + if (!newValue) { + if (ui.getPopup() !=3D null) { + ui.getPopup().setVisible(false); + } + return; + } + SwingUtilities.invokeLater(new Runnable() { + + @Override + public void run() { + ui.getPopup().pack(); + Dimension dim =3D ui.getPopup().getPreferredSize(); + JToggleButton invoker =3D ui.getChangeDecorator(); + ui.getPopup().show(invoker, (int) (invoker.getPreferredSize(= ).getWidth() - dim.getWidth()), invoker.getHeight()); + } + }); + } + + /** + * Modifie l'=C3=A9tat autoComplete de l'ui. + * + * @param oldValue l'ancienne valeur + * @param newValue la nouvelle valeur + */ + protected void setAutoComplete(Boolean oldValue, Boolean newValue) { + oldValue =3D oldValue !=3D null && oldValue; + newValue =3D newValue !=3D null && newValue; + if (oldValue =3D=3D newValue) { + return; + } + if (log.isDebugEnabled()) { + log.debug("autocomplete state : <" + oldValue + " to " + newValu= e + ">"); + } + if (!newValue) { + JTextComponent editorComponent =3D (JTextComponent) ui.getCombob= ox().getEditor().getEditorComponent(); + editorComponent.removeFocusListener(EDITOR_TEXT_COMP0NENT_FOCUSL= ISTENER); + undecorate(ui.getCombobox(), originalDocument); + } else { + decorate(ui.getCombobox(), convertor); + JTextComponent editorComponent =3D (JTextComponent) ui.getCombob= ox().getEditor().getEditorComponent(); + editorComponent.addFocusListener(EDITOR_TEXT_COMP0NENT_FOCUSLIST= ENER); + } + } + + /** + * Modifie l'index du d=C3=A9corateur + * + * @param oldValue l'ancienne valeur + * @param newValue la nouvelle valeur + */ + @SuppressWarnings({"unchecked"}) + protected void setIndex(Integer oldValue, Integer newValue) { + if (newValue.equals(oldValue)) { + return; + } + if (log.isDebugEnabled()) { + log.debug("check state : <" + oldValue + " to " + newValue + ">"= ); + } + + // change decorator context + decorator.setContextIndex(newValue); + + // keep selected item + Object previousSelectedItem =3D ui.getSelectedItem(); + Boolean wasAutoComplete =3D ui.isAutoComplete(); + + if (wasAutoComplete) { + ui.setAutoComplete(false); + } + + // remove autocomplete + if (previousSelectedItem !=3D null) { + ui.getCombobox().setSelectedItem(null); + ui.selectedItem =3D null; + } + + + try { + // Sort data with the decorator jxpath tokens. + DecoratorUtils.sort(decorator, ui.getData(), newValue); + } catch (Exception e) { + log.warn(e.getMessage(), e); + //System.out.println("newValue :: "+decorator+" : "+newValue); + //System.out.println("datas :: "+ui.getData()); + } + + // reload the model + SwingUtil.fillComboBox(ui.getCombobox(), ui.getData(), null); + + if (wasAutoComplete) { + ui.setAutoComplete(true); + } + + if (previousSelectedItem !=3D null) { + ui.setSelectedItem(previousSelectedItem); + } + + ui.getCombobox().requestFocus(); + } + + /** + * Modifie la valeur s=C3=A9lectionn=C3=A9e dans la liste d=C3=A9roulant= e. + * + * @param oldValue l'ancienne valeur + * @param newValue la nouvelle valeur + */ + protected void setSelectedItem(Object oldValue, Object newValue) { + if (ui.getBean() =3D=3D null) { + return; + } + + if (newValue =3D=3D null) { + if (ui.getCombobox().getSelectedItem() =3D=3D null) { + return; + } + ui.getCombobox().setSelectedItem(null); + + if (ui.isAutoComplete()) { + ui.setAutoComplete(false); + ui.setAutoComplete(true); + } + + if (oldValue =3D=3D null) { + return; + } + } + if (log.isDebugEnabled()) { + log.debug(ui.getProperty() + " on " + ui.getBean().getClass() + = " :: " + oldValue + " to " + newValue); + } + + try { + Method mut =3D getMutator(); + if (mut !=3D null) { + mut.invoke(ui.getBean(), newValue); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** @return le document de l'=C3=A9diteur avant compl=C3=A9tion. */ + public Document getOriginalDocument() { + return originalDocument; + } + + public MultiJXPathDecorator getDecorator() { + return decorator; + } + + /** + * Creation de l'ui pour modifier le d=C3=A9corateur. + * + * @param decorator le decorateur a utiliser + */ + protected void preparePopup(MultiJXPathDecorator decorator) { + String selectedTip =3D ui.getSelectedToolTipText(); + if (selectedTip =3D=3D null) { + // use default selected tip text + selectedTip =3D BeanComboBox.DEFAULT_SELECTED_TOOLTIP; + } + String notSelectedTip =3D ui.getNotSelectedToolTipText(); + if (notSelectedTip =3D=3D null) { + // use default selected tip text + notSelectedTip =3D BeanComboBox.DEFAULT_NOT_SELECTED_TOOLTIP; + } + JPopupMenu popup =3D ui.getPopup(); + + //Container container =3D ui.getIndexesContainer(); + for (int i =3D 0, max =3D decorator.getNbContext(); i < max; i++) { + String property =3D ui.getI18nPrefix() + decorator.getProperty(i= ); + String propertyI18n =3D _(property); + JRadioButtonMenuItem button =3D new JRadioButtonMenuItem(propert= yI18n); + button.putClientProperty(JAXXButtonGroup.BUTTON8GROUP_CLIENT_PRO= PERTY, ui.getIndexes()); + button.putClientProperty(JAXXButtonGroup.VALUE_CLIENT_PROPERTY, = i); + popup.add(button); + if (selectedTip !=3D null) { + button.putClientProperty(JAXXButtonGroup.SELECTED_TIP_CLIENT= _PROPERTY, _(selectedTip, propertyI18n)); + } + if (notSelectedTip !=3D null) { + button.putClientProperty(JAXXButtonGroup.NOT_SELECTED_TIP_CL= IENT_PROPERTY, _(notSelectedTip, propertyI18n)); + } + button.setSelected(false); + ui.getIndexes().add(button); + } + String title =3D ui.getPopupTitleText(); + if (title =3D=3D null) { + // use default popup title + title =3D BeanComboBox.DEFAULT_POPUP_LABEL; + + Class internalClass =3D decorator.getInternalClass(); + String beanI18nKey; + if (internalClass =3D=3D null) { + beanI18nKey =3D n_("entitycombobox.unknown.type"); + } else { + beanI18nKey =3D ui.getI18nPrefix() + Introspector.decapitali= ze(internalClass.getSimpleName()); + } + String beanI18n =3D _(beanI18nKey); + title =3D _(title, beanI18n); + } else { + title =3D _(title); + } + ui.getPopupLabel().setText(title); + ui.getPopup().setLabel(title); + ui.getPopup().invalidate(); + } + + public Class getTargetClass() { + Method m =3D getMutator(); + return m =3D=3D null ? null : m.getParameterTypes()[0]; + } + + /** @return le mutateur a utiliser pour modifier le bean associ=C3=A9. */ + protected Method getMutator() { + if (mutator =3D=3D null) { + Object bean =3D ui.getBean(); + if (bean =3D=3D null) { + throw new NullPointerException("could not find bean in " + u= i); + } + String property =3D ui.getProperty(); + if (property =3D=3D null) { + throw new NullPointerException("could not find property in "= + ui); + } + + try { + PropertyDescriptor descriptor =3D PropertyUtils.getPropertyD= escriptor(bean, property); + if (descriptor !=3D null) { + mutator =3D descriptor.getWriteMethod(); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + 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 newDecoratedObjectToStringConverte= r(final Decorator decorator) { + + return new ObjectToStringConverter() { + + @Override + public String getPreferredStringForItem(Object item) { + return item instanceof String ? (String) item : item =3D=3D = null ? "" : decorator.toString(item); + } + }; + } + + /** + * Ajout l'auto-compl=C3=A9tion sur une liste d=C3=A9roulante, en utilis= ant le + * converteur donn=C3=A9 pour afficher les donn=C3=A9es. + * + * @param combo la combo =C3=A0 d=C3=A9corer + * @param convertor le converter utilis=C3=A9 pour afficher les donn=C3= =A9es. + */ + public static void decorate(JComboBox combo, ObjectToStringConverter con= vertor) { + + AutoCompleteDecorator.decorate(combo, convertor); + } + + /** + * D=C3=A9sactive l'aut-compl=C3=A9tion sur une liste d=C3=A9roulante, e= n y repositionnant + * le mod=C3=A8le du document d'=C3=A9dition d'avant auto-compl=C3=A9tio= n. + * + * @param combo la liste d=C3=A9roulante =C3=A0 d=C3=A9corer + * @param originalDocument le document original de l'=C3=A9dtieur de la + * liste d=C3=A9roulante. + */ + public static void undecorate(JComboBox combo, Document originalDocument= ) { + + // has not to be editable + combo.setEditable(false); + + // configure the text component=3Deditor component + Component c =3D combo.getEditor().getEditorComponent(); + JTextComponent editorComponent =3D (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 =3D evt.getPropertyName(); + + if (SELECTED_ITEM_PROPERTY.equals(propertyName)) { + setSelectedItem(evt.getOldValue(), evt.getNewValue()); + return; + } + + if (INDEX_PROPERTY.equals(propertyName)) { + setIndex((Integer) evt.getOldValue(), (Integer) evt.getNewValue(= )); + return; + } + if (AUTO_COMPLETE_PROPERTY.equals(propertyName)) { + setAutoComplete((Boolean) evt.getOldValue(), (Boolean) evt.getNe= wValue()); + return; + } + if (DATA_PROPERTY.equals(propertyName)) { + // list has changed, force reload of index + setIndex(-1, ui.getIndex()); + } + + } +} --===============2188264101954059632==--