jaxx.runtime.swing.editor.bean.BeanUIUtil Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jaxx-widgets Show documentation
Show all versions of jaxx-widgets Show documentation
Collection of swing widgets wrote with JAXX
/*
* #%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.swing.JAXXButtonGroup;
import jaxx.runtime.swing.JAXXRuntimeException;
import org.apache.commons.beanutils.MethodUtils;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jdesktop.swingx.autocomplete.AutoCompleteComboBoxEditor;
import org.jdesktop.swingx.autocomplete.AutoCompleteDecorator;
import org.jdesktop.swingx.autocomplete.AutoCompleteDocument;
import org.jdesktop.swingx.autocomplete.ComboBoxAdaptor;
import org.jdesktop.swingx.autocomplete.ObjectToStringConverter;
import org.nuiton.decorator.Decorator;
import org.nuiton.decorator.DecoratorUtil;
import org.nuiton.decorator.JXPathDecorator;
import org.nuiton.decorator.MultiJXPathDecorator;
import javax.swing.AbstractButton;
import javax.swing.ActionMap;
import javax.swing.ButtonGroup;
import javax.swing.ComboBoxEditor;
import javax.swing.InputMap;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPopupMenu;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.JSeparator;
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.ActionListener;
import java.awt.event.FocusListener;
import java.awt.event.KeyListener;
import java.beans.Introspector;
import java.beans.PropertyChangeListener;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.List;
import static java.util.Arrays.asList;
import static java.util.Collections.unmodifiableList;
import static org.nuiton.i18n.I18n.t;
import static org.nuiton.i18n.I18n.n;
/**
* Class with usefull methods used in bean uis.
*
* @author Tony Chemit - [email protected]
* @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");
public static final Object[] EMPTY_CLASS_ARRAY = new Object[0];
public static void invokeMethod(Method mut, Object source, Object... params) {
if (mut != null) {
try {
mut.invoke(source, params);
} catch (IllegalAccessException e) {
throw new JAXXRuntimeException(e);
} catch (InvocationTargetException e) {
throw new JAXXRuntimeException(e.getCause());
}
}
}
/**
* 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) {
// tchemit 2010-10-05 since swingx 1.6.2, undecorate stuff is no more
// public and we want to use it, so hack it...
combo.putClientProperty("oldEditor", combo.getEditor());
combo.putClientProperty("oldDocument", combo.getEditor().getEditorComponent());
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 comboBox la liste déroulante à décorer
* @param originalDocument le document original de l'édtieur de la
* liste déroulante.
*/
public static void undecorate(JComboBox comboBox, Document originalDocument) {
// has not to be editable
comboBox.setEditable(false);
// configure the text component=editor component
Component c = comboBox.getEditor().getEditorComponent();
JTextComponent editorComponent = (JTextComponent) c;
editorComponent.setDocument(originalDocument);
editorComponent.setText(null);
//undecorate(comboBox);
//remove old property change listener
//for (PropertyChangeListener l : c.getPropertyChangeListeners("editor")) {
// if (l instanceof AutoCompletePropertyChangeListener) {
// c.removePropertyChangeListener("editor", l);
// }
//}
ComboBoxEditor oldEditor = (ComboBoxEditor) comboBox.getClientProperty("oldEditor");
if (editorComponent.getDocument() instanceof AutoCompleteDocument) {
AutoCompleteDocument doc = (AutoCompleteDocument) editorComponent.getDocument();
if (doc.isStrictMatching()) {
ActionMap map = comboBox.getActionMap();
for (String key : COMBO_BOX_ACTIONS) {
map.put(key, null);
}
}
//remove old property change listener
for (PropertyChangeListener l : comboBox.getPropertyChangeListeners("editor")) {
//if (l instanceof AutoComplete.PropertyChangeListener) {
if (l.getClass().getName().contains("AutoComplete")) {
comboBox.removePropertyChangeListener("editor", l);
}
}
for (PropertyChangeListener l : comboBox.getPropertyChangeListeners("enabled")) {
//if (l instanceof AutoComplete.PropertyChangeListener) {
if (l.getClass().getName().contains("AutoComplete")) {
comboBox.removePropertyChangeListener("enabled", l);
}
}
AutoCompleteComboBoxEditor editor = (AutoCompleteComboBoxEditor) comboBox.getEditor();
comboBox.setEditor(oldEditor);
//remove old key listener
for (KeyListener l : editorComponent.getKeyListeners()) {
//if (l instanceof AutoComplete.KeyAdapter) {
if (l.getClass().getName().contains("AutoComplete")) {
editorComponent.removeKeyListener(l);
break;
}
}
undecorate(editorComponent, originalDocument);
for (ActionListener l : comboBox.getActionListeners()) {
if (l instanceof ComboBoxAdaptor) {
comboBox.removeActionListener(l);
break;
}
}
//TODO remove aqua fix
//TODO reset editibility
}
}
static void undecorate(JTextComponent textComponent, Document originalDocument) {
Document doc = textComponent.getDocument();
if (doc instanceof AutoCompleteDocument) {
//remove autocomplete key/action mappings
InputMap map = textComponent.getInputMap();
while (map.getParent() != null) {
InputMap parent = map.getParent();
//if (parent instanceof AutoComplete.InputMap) {
if (parent.getClass().getName().contains("AutoComplete")) {
map.setParent(parent.getParent());
}
map = parent;
}
textComponent.getActionMap().put("nonstrict-backspace", null);
//remove old focus listener
for (FocusListener l : textComponent.getFocusListeners()) {
//if (l instanceof AutoComplete.FocusAdapter) {
if (l.getClass().getName().contains("AutoComplete")) {
textComponent.removeFocusListener(l);
break;
}
}
//reset to original document
//textComponent.setDocument(((AutoCompleteDocument) doc).delegate);
textComponent.setDocument(originalDocument);
}
}
public static MultiJXPathDecorator createDecorator(JXPathDecorator decorator) {
if (decorator == null) {
throw new NullPointerException(
"can not have a null decorator as parameter");
}
String separator;
String separatorReplacement;
if (decorator instanceof Cloneable) {
Cloneable cloneable = (Cloneable) decorator;
try {
Object clone = MethodUtils.invokeExactMethod(cloneable,
"clone",
EMPTY_CLASS_ARRAY
);
return (MultiJXPathDecorator) clone;
} catch (Exception e) {
throw new IllegalStateException("Could not clone decorator " + decorator, e);
}
}
if (decorator instanceof MultiJXPathDecorator>) {
separator = ((MultiJXPathDecorator>) decorator).getSeparator();
separatorReplacement = ((MultiJXPathDecorator>) decorator).getSeparatorReplacement();
} else {
separator = "??" + new Date().getTime();
separatorReplacement = " - ";
}
return DecoratorUtil.newMultiJXPathDecorator(
decorator.getType(),
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 selectedTip
* @param notSelectedTip
* @param i18nPrefix
* @param title
* @param indexes
* @param popupLabel
* @param sortUp
* @param sortDown
* @param decorator le decorateur a utiliser
*/
public void preparePopup(String selectedTip,
String notSelectedTip,
String i18nPrefix,
String title,
ButtonGroup indexes,
JSeparator popupSeparator,
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();
int nbContext = decorator.getNbContext();
if (nbContext > 1) {
for (int i = 0; i < nbContext; i++) {
String property = i18nPrefix + decorator.getProperty(i);
String propertyI18n = t(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, t(selectedTip, propertyI18n));
}
if (notSelectedTip != null) {
button.putClientProperty(JAXXButtonGroup.NOT_SELECTED_TIP_CLIENT_PROPERTY, t(notSelectedTip, propertyI18n));
}
button.setSelected(false);
indexes.add(button);
}
}
if (title == null) {
// use default popup title
title = DEFAULT_POPUP_LABEL;
Class> type = decorator.getType();
String beanI18nKey;
if (type == null) {
beanI18nKey = n("bean.unknown.type");
} else {
beanI18nKey = i18nPrefix + Introspector.decapitalize(type.getSimpleName());
}
String beanI18n = t(beanI18nKey);
title = t(title, beanI18n);
} else {
title = t(title);
}
sortDown.putClientProperty(JAXXButtonGroup.SELECTED_TIP_CLIENT_PROPERTY, t("bean.sort.down.tip"));
sortDown.putClientProperty(JAXXButtonGroup.NOT_SELECTED_TIP_CLIENT_PROPERTY, t("bean.sort.down.toSelect.tip"));
sortUp.putClientProperty(JAXXButtonGroup.SELECTED_TIP_CLIENT_PROPERTY, t("bean.sort.up.tip"));
sortUp.putClientProperty(JAXXButtonGroup.NOT_SELECTED_TIP_CLIENT_PROPERTY, t("bean.sort.up.toSelect.tip"));
if (nbContext < 2) {
getPopup().remove(popupSeparator);
getPopup().remove(popupLabel);
}
popupLabel.setText(title);
getPopup().setLabel(title);
getPopup().invalidate();
}
}
//these keys were pulled from BasicComboBoxUI from Sun JDK 1.6.0_20
private static final List COMBO_BOX_ACTIONS = unmodifiableList(asList("selectNext",
"selectNext2", "selectPrevious", "selectPrevious2", "pageDownPassThrough",
"pageUpPassThrough", "homePassThrough", "endPassThrough"));
}