/* * (c) Copyright 2010-2011 AgileBirds * * This file is part of OpenFlexo. * * OpenFlexo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenFlexo 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 Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenFlexo. If not, see <http://www.gnu.org/licenses/>. * */ package org.openflexo.inspector.widget; import java.awt.Color; import java.awt.Cursor; import java.awt.Dimension; import java.awt.Font; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.io.File; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import org.openflexo.inspector.AbstractController; import org.openflexo.inspector.InspectableModification; import org.openflexo.inspector.InspectableObject; import org.openflexo.inspector.InspectingWidget; import org.openflexo.inspector.InspectorModelView; import org.openflexo.inspector.InspectorObserver; import org.openflexo.inspector.InspectorTabbedPanel; import org.openflexo.inspector.TabModelView; import org.openflexo.inspector.model.GroupModel; import org.openflexo.inspector.model.InnerTabWidget; import org.openflexo.inspector.model.PropertyListModel; import org.openflexo.inspector.model.PropertyModel; import org.openflexo.inspector.widget.propertylist.PropertyListWidget; import org.openflexo.kvc.ChoiceList; import org.openflexo.kvc.KeyValueCoding; import org.openflexo.localization.FlexoLocalization; import org.openflexo.xmlcode.AccessorInvocationException; import org.openflexo.xmlcode.StringConvertable; import org.openflexo.xmlcode.StringEncoder; import org.openflexo.xmlcode.StringEncoder.Converter; import org.openflexo.xmlcode.StringRepresentable; /** * Abstract class representing a widget in the inspector * * @author bmangez, sguerin */ public abstract class DenaliWidget<T> extends JPanel implements InspectorObserver, InnerTabWidgetView { private static final Logger logger = Logger.getLogger(DenaliWidget.class.getPackage().getName()); private static final String READONLY = "readOnly"; // ========================================================================== // ============================= Constants // ================================== // ========================================================================== public static final String DROPDOWN = "DROPDOWN"; public static final String TEXT_FIELD = "TEXT_FIELD"; public static final String TEXT_FIELD_AND_LABEL = "TEXT_FIELD_AND_LABEL"; public static final String LOCALIZED_TEXT_FIELD = "LOCALIZED_TEXT_FIELD"; public static final String TEXT_AREA = "TEXT_AREA"; public static final String CHECKBOX = "CHECKBOX"; public static final String CHECKBOX_LIST = "CHECKBOX_LIST"; public static final String RADIOBUTTON_LIST = "RADIOBUTTON_LIST"; public static final String DROPDOWN_BUTTON = "DROPDOWN_BUTTON"; public static final String RADIO_DROPDOWN = "RADIO_DROPDOWN"; public static final String INTEGER = "INTEGER"; public static final String FLOAT = "FLOAT"; public static final String DOUBLE = "DOUBLE"; public static final String FILE = "FILE"; public static final String DIRECTORY = "DIRECTORY"; public static final String COLOR = "COLOR"; public static final String FONT = "FONT"; public static final String EXTERNAL = "EXTERNAL"; public static final String READ_ONLY_TEXT_FIELD = "READ_ONLY_TEXT_FIELD"; public static final String READ_ONLY_CHECKBOX = "READ_ONLY_CHECKBOX"; public static final String READ_ONLY_TEXT_AREA = "READ_ONLY_TEXT_AREA"; public static final String READ_ONLY_INTEGER = "READ_ONLY_INTEGER"; public static final String JAVA_CODE = "JAVA_CODE"; public static final String WYSIWYG_LIGHT = "WYSIWYG_LIGHT"; public static final String WYSIWYG_ULTRA_LIGHT = "WYSIWYG_ULTRA_LIGHT"; public static final String INFOLABEL = "INFOLABEL"; public static final String LABEL = "LABEL"; public static final String CUSTOM = "CUSTOM"; public static final String REGEXP = "REGEXP"; public static final String BUTTON = "BUTTON"; public static final Dimension MINIMUM_SIZE = new Dimension(30, 25); // ========================================================================== // ============================= Variables // ================================== // ========================================================================== // protected String _observedPropertyName; protected String _observedTabName; private InspectableObject _model; protected JLabel _label; private WidgetConditional conditional; protected PropertyModel _propertyModel; private int indexInTab; private TabModelView _tab; public static Font DEFAULT_LABEL_FONT = new Font("SansSerif", Font.PLAIN, 11); public static Font DEFAULT_MEDIUM_FONT = new Font("SansSerif", Font.PLAIN, 10); protected boolean modelUpdating = false; protected boolean widgetUpdating = false; boolean widgetRefreshPlanned = false; protected DenaliWidget(PropertyModel model, AbstractController controller) { super(); _propertyModel = model; _observedTabName = model._tabModelName; conditional = new WidgetConditional(model); _label = null; _controller = controller; } public void refreshMe() { if (getDynamicComponent().getParent() != null && getDynamicComponent().getParent() instanceof JComponent) { ((JComponent) getDynamicComponent().getParent()).revalidate(); ((JComponent) getDynamicComponent().getParent()).repaint(); } } /** * Overrides getMinimumSize * * @see javax.swing.JComponent#getMinimumSize() */ @Override public Dimension getMinimumSize() { return MINIMUM_SIZE; } AbstractController _controller; public AbstractController getController() { /*if (InspectorController.hasInstance()) return InspectorController.instance(); else return null;*/ return _controller; } public static InnerTabWidgetView instance(InnerTabWidget propModel, AbstractController controller) { InnerTabWidgetView answer = null; if (answer == null) { answer = createWidget(propModel, controller); } answer.setController(controller); return answer; } private static InnerTabWidgetView createWidget(InnerTabWidget widget, AbstractController controller) { if (widget instanceof PropertyModel) { PropertyModel model = (PropertyModel) widget; String propType = model.getWidget(); if (model instanceof PropertyListModel) { return new PropertyListWidget((PropertyListModel) model, controller); } if (propType.equals(DROPDOWN)) { return new DropDownWidget(model, controller); } else if (propType.equals(TEXT_FIELD)) { return new TextFieldWidget(model, controller); } else if (propType.equals(TEXT_FIELD_AND_LABEL)) { return new TextFieldAndLabelWidget(model, controller); } else if (propType.equals(LOCALIZED_TEXT_FIELD)) { return new LocalizedTextFieldWidget(model, controller); } else if (propType.equals(TEXT_AREA)) { return new TextAreaWidget(model, controller); } else if (propType.equals(CHECKBOX)) { return new CheckBoxWidget(model, controller); } else if (propType.equals(CHECKBOX_LIST)) { return new CheckBoxListWidget(model, controller); } else if (propType.equals(RADIOBUTTON_LIST)) { return new RadioButtonListWidget(model, controller); } else if (propType.equals(DROPDOWN_BUTTON)) { return new DropDownButtonWidget(model, controller); } else if (propType.equals(RADIO_DROPDOWN)) { return new RadioDropDownWidget(model, controller); } else if (propType.equals(INTEGER)) { return new IntegerWidget(model, controller); } else if (propType.equals(FLOAT)) { return new FloatWidget(model, controller); } else if (propType.equals(DOUBLE)) { return new DoubleWidget(model, controller); } else if (propType.equals(FILE)) { return new FileEditWidget(model, controller); } else if (propType.equals(DIRECTORY)) { return new DirectoryEditWidget(model, controller); } else if (propType.equals(COLOR)) { return new ColorWidget(model, controller); } else if (propType.equals(FONT)) { return new FontWidget(model, controller); } else if (propType.equals(EXTERNAL)) { return new ExternalWidget(model, controller); } else if (propType.equals(READ_ONLY_TEXT_FIELD)) { return new ReadOnlyWidget(model, controller); } else if (propType.equals(READ_ONLY_CHECKBOX)) { return new ReadOnlyCheckBoxWidget(model, controller); } else if (propType.equals(READ_ONLY_TEXT_AREA)) { return new ReadOnlyTextAreaWidget(model, controller); } else if (propType.equals(READ_ONLY_INTEGER)) { return new ReadOnlyIntegerWidget(model, controller); } else if (propType.equals(JAVA_CODE)) { return new JavaCodeWidget(model, controller); } else if (propType.equals(WYSIWYG_LIGHT)) { return new WysiwygLightWidget(model, controller, false); } else if (propType.equals(WYSIWYG_ULTRA_LIGHT)) { return new WysiwygLightWidget(model, controller, true); } else if (propType.equals(INFOLABEL)) { return new InfoLabelWidget(model, controller); } else if (propType.equals(LABEL)) { return new LabelWidget(model, controller); } else if (propType.equals(REGEXP)) { return new RegexpTextfieldWidget(model, controller); } else if (propType.equals(BUTTON)) { return new ButtonWidget(model, controller); } else if (propType.equals(CUSTOM)) { String className = model.getValueForParameter("className"); try { Class widgetClass = Class.forName(className); Class[] constructorClassParams = new Class[2]; constructorClassParams[0] = PropertyModel.class; constructorClassParams[1] = AbstractController.class; Constructor c = widgetClass.getConstructor(constructorClassParams); Object[] constructorParams = new Object[2]; constructorParams[0] = model; constructorParams[1] = controller; CustomWidget returned = (CustomWidget) c.newInstance(constructorParams); if (!returned.isMinimumSizeSet()) { returned.setMinimumSize(MINIMUM_SIZE); } return returned; } catch (ClassNotFoundException e) { if (logger.isLoggable(Level.WARNING)) { logger.warning("Class not found: " + className + ". See console for details."); } e.printStackTrace(); } catch (SecurityException e) { // Warns about the exception if (logger.isLoggable(Level.WARNING)) { logger.warning("Exception raised: " + e.getClass().getName() + ". See console for details."); } e.printStackTrace(); } catch (NoSuchMethodException e) { // Warns about the exception if (logger.isLoggable(Level.WARNING)) { logger.warning("Exception raised: " + e.getClass().getName() + ". See console for details."); } e.printStackTrace(); } catch (IllegalArgumentException e) { // Warns about the exception if (logger.isLoggable(Level.WARNING)) { logger.warning("Exception raised: " + e.getClass().getName() + ". See console for details."); } e.printStackTrace(); } catch (InstantiationException e) { // Warns about the exception if (logger.isLoggable(Level.WARNING)) { logger.warning("Exception raised: " + e.getClass().getName() + ". See console for details."); } e.printStackTrace(); } catch (IllegalAccessException e) { // Warns about the exception if (logger.isLoggable(Level.WARNING)) { logger.warning("Exception raised: " + e.getClass().getName() + ". See console for details."); } e.printStackTrace(); } catch (InvocationTargetException e) { // Warns about the exception if (logger.isLoggable(Level.WARNING)) { logger.warning("Exception raised: " + e.getClass().getName() + ". See console for details."); } e.printStackTrace(); } return null; } else if (logger.isLoggable(Level.WARNING)) { logger.warning("Unknow widget in inspector file: " + propType); } return new UnknownWidget(model, controller); } else if (widget instanceof GroupModel) { return new LineWidget((GroupModel) widget, controller); } else { if (logger.isLoggable(Level.WARNING)) { logger.warning("Unknown model of type " + widget.getClass().getSimpleName()); } return null; } } @Override public void setController(AbstractController controller) { _controller = controller; } // ========================================================================== // ========================== Updating from/to model // ======================== // ========================================================================== /** * Update the widget retrieving data from the model. This method is called when the observed property change. */ @Override public abstract void updateWidgetFromModel(); /** * Update the model given the actual state of the widget */ public abstract void updateModelFromWidget(); // ========================================================================== // ========================== Widget Store Management // ======================= // ========================================================================== @Override public synchronized void switchObserved(InspectableObject inspectable) { // SGU: I think we can try here to this: /*if (inspectable != getModel()) { if (widgetHasFocus()) { looseFocus(); } if (getModel() != null) { getModel().deleteInspectorObserver(this); } setModel(inspectable); getModel().addInspectorObserver(this); }*/ if (widgetHasFocus()) { looseFocus(); } if (getModel() != null) { getModel().deleteInspectorObserver(this); } setModel(inspectable); getModel().addInspectorObserver(this); try { if (isWidgetVisible()) { if (!modelUpdating && !widgetUpdating) { updateWidgetFromModel(); } else { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { updateWidgetFromModel(); } }); } } } catch (IllegalStateException e) { if (logger.isLoggable(Level.INFO)) { logger.info("IllegalStateException raised: loop in AWT loop"); // Cannot modify, since was initialized from the widget itself // Just catching this exception allow to update fields while // avoiding loops } } } public void performUpdate() { if (!widgetUpdating) { updateWidgetFromModel(); } } /* * private void refreshAllConditional() { if (getParent() != null) { for * (int i = 0; i < getParent().getComponents().length; i++) { if * (getParent().getComponent(i) instanceof DenaliWidget) { ((DenaliWidget) * getParent()).refreshConditional(); } } } } * * private void refreshConditional() { boolean conditionalValue = * checkConditionalValue(); if (conditionalValue && * getDynamicComponent().getParent().getParent() == null) { int index = * Math.min(indexInTab, _tab.getComponentCount()); * _tab.add(TabModelView.widgetPanel(this), index); * } else if (!conditionalValue && * getDynamicComponent().getParent().getParent() != null) { * _tab.remove(getDynamicComponent().getParent()); } _tab.updateUI(); } */ /* * public boolean checkConditionalValue() { return * conditional.checkConditionalValue(getModel()); } */ /* * public boolean checkConditionalValue() { if (_conditional != null && * !_conditional.equals("")) { try { StringTokenizer or = new * StringTokenizer(_conditional, "|"); while (or.hasMoreTokens()) { // OR * StringTokenizer or_and = new StringTokenizer(or.nextToken().trim(), "&"); * boolean and = true; while (or_and.hasMoreTokens() && and) { // AND String * cond = or_and.nextToken().trim(); boolean negate = false; if * (cond.indexOf("!=") != -1) { negate = true; } StringTokenizer stk = new * StringTokenizer(cond, "!="); String attribute = stk.nextToken().trim(); * String value = stk.nextToken().trim(); Object attributeValue = * getModel().objectForKey(attribute); if (attributeValue instanceof * KeyValueCoding) { if (_propertyModel.hasFormatter()) { attributeValue = * getStringRepresentation(attributeValue); } } if (attributeValue == null && * value.equals("null")) and = true; else if (attributeValue != null && * attributeValue.equals(value)) and = true; else and = false; * * if (negate) and = !and; } if (and == true) { return true; } } // if none * OR exists => return true. return false; } catch (Exception e) { if * (logger.isLoggable(Level.WARNING)) logger.warning("Exception in * DenaliWidget.checkConditionalValue (" + _conditional + ") for:" + * _propertyModel.label + ""); e.printStackTrace(); } } return true; } */ @Override public abstract JComponent getDynamicComponent(); // ========================================================================== // ============================= ObserverInterface // ========================== // ========================================================================== @Override public void update(final InspectableObject inspectable, final InspectableModification modification) { if (modification == null) { return; } if (widgetRefreshPlanned) { if (logger.isLoggable(Level.FINE)) { logger.fine("Loop detected...trying to avoid... please pray..."); } return; } /* * The purpose of the following lines is to avoid an update * of the widget while it is already updating the model (this is forbidden * by AWT). To do so, we will post a thread that will perform * the update operation after the execution of the current * AWT-loop */ if (modelUpdating && !widgetRefreshPlanned && modification.isReentrant() /* Do it only for reentrant modifications !!! */) { if (willUpdateModel(inspectable, modification)) { widgetRefreshPlanned = true; SwingUtilities.invokeLater(new Runnable() { /** * Overrides run * * @see java.lang.Runnable#run() */ @Override public void run() { if (logger.isLoggable(Level.FINE)) { logger.fine("pfew...that was close. Good Swing consultant: updating now"); } widgetRefreshPlanned = false; updateImmediately(inspectable, modification); } }); } return; } updateImmediately(inspectable, modification); } /** * @param inspectable * @param modification */ protected void updateImmediately(final InspectableObject inspectable, final InspectableModification modification) { if (!widgetUpdating && !modelUpdating && willUpdateModel(inspectable, modification)) { if (logger.isLoggable(Level.FINE)) { logger.fine("update in " + this.getClass().getName() + " with " + modification); } try { if (isWidgetVisible()) { updateWidgetFromModel(); } } catch (IllegalStateException e) { if (logger.isLoggable(Level.WARNING)) { logger.warning("!!!!IllegalStateException raised: loop in AWT loop. You should investigate this."); } } } } private boolean willUpdateModel(InspectableObject inspectable, InspectableModification modification) { String propertyName = modification.propertyName(); return inspectable == _model && propertyName != null && (propertyName.equals(getObservedPropertyName()) || propertyName.regionMatches(0, getObservedPropertyName(), getObservedPropertyName().lastIndexOf('.') + 1, propertyName.length())); } // ========================================================================== // ============================== Formatting // ================================ // ========================================================================== public synchronized void setStringValue(String newValueAsString) { if (typeIsString() || typeIsObject()) { setObjectValue((T) newValueAsString); } else if (typeIsStringConvertable()) { setObjectValue((T) getTypeConverter().convertFromString(newValueAsString)); } else { if (logger.isLoggable(Level.WARNING)) { logger.warning("There is an error in some configuration file :\n the property named '" + _propertyModel.name + "' has no way to handle string representation building !"); } System.out.println("TypeIsString=" + typeIsString()); System.out.println("getType()=" + getType()); System.out.println("hasObjectValue()=" + hasObjectValue()); System.out.println("getObjectValue()=" + getObjectValue()); } } public String getStringValue() { Object object = getObjectValue(); if (object != null) { return getStringRepresentation(object); } else { return null; } } public String getDisplayStringRepresentation(Object object) { if (object instanceof Enum && _propertyModel.hasFormatter()) { Method formatter; try { formatter = ((Enum) object).getDeclaringClass().getMethod(_propertyModel.getValueForParameter("format")); return (String) formatter.invoke(object); } catch (SecurityException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (NoSuchMethodException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InvocationTargetException e) { // TODO Auto-generated catch block e.printStackTrace(); } } return getStringRepresentation(object); } public String getStringRepresentation(Object object) { if (object instanceof String) { return (String) object; } else if (object instanceof KeyValueCoding && _propertyModel.hasFormatter()) { return _propertyModel.getFormattedObject((KeyValueCoding) object); } else if (object instanceof Enum) { return FlexoLocalization.localizedForKey(((Enum) object).name().toLowerCase()); } else if (object instanceof StringConvertable) { String str = ((StringConvertable) object).getConverter().convertToString(object); return str != null ? FlexoLocalization.localizedForKey(str.toLowerCase()) : ""; } else if (object instanceof Number) { return object.toString(); } else if (object instanceof Boolean) { return object.toString(); } else if (object instanceof File) { return object.toString(); } else if (object instanceof StringRepresentable) { return object.toString(); } else { if (logger.isLoggable(Level.WARNING)) { logger.warning("There is an error in some configuration file :\n the property named '" + _propertyModel.name + "' has no string representation formatter ! Object is a " + (object != null ? object.getClass().getName() : "null")); } return object.toString(); } } // ========================================================================== // ============================= Type resolution // ============================ // ========================================================================== protected Class _type; protected StringEncoder.Converter _typeConverter; protected Class getType() { if (_type == null) { retrieveType(); } return _type; } protected Class retrieveType() { if (hasObjectValue()) { Object value = getObjectValue(); if (value != null) { _type = value.getClass(); if (StringConvertable.class.isAssignableFrom(_type)) { _typeConverter = ((StringConvertable) value).getConverter(); } } else if (getPropertyModel().hasValueForParameter("type")) { try { _type = Class.forName(getPropertyModel().getValueForParameter("type")); } catch (ClassNotFoundException e) { e.printStackTrace(); } } else { try { _type = getModel().getTypeForKey(observedPropertyName()); } catch (Exception e) { e.printStackTrace(); } if (_type == null) { _type = getDefaultType(); } if (StringConvertable.class.isAssignableFrom(_type)) { _typeConverter = lookupStaticStringConverter(_type); } } if (_type != null) { while (_type.isAnonymousClass()) { _type = _type.getSuperclass(); } } return _type; } else { if (getPropertyModel().hasValueForParameter("type")) { try { _type = Class.forName(getPropertyModel().getValueForParameter("type")); } catch (ClassNotFoundException e) { e.printStackTrace(); } } return _type; } } private Converter<T> lookupStaticStringConverter(Class<T> type) { for (int i = 0; i < type.getFields().length; i++) { Field f = type.getFields()[i]; if (Modifier.isStatic(f.getModifiers()) && Converter.class.isAssignableFrom(f.getType())) { try { return (Converter<T>) f.get(null); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } } // if ("textColor".equals(_observedPropertyName) || "backColor".equals(_observedPropertyName)) // System.err.println("coucou"); return null; } /** * Returns a string representation */ @Override public String toString() { return getClass().getSimpleName() + "[" + observedPropertyName() + "]"; } protected boolean typeIsString() { if (getType() != null) { return String.class.isAssignableFrom(getType()); } else { return false; } } protected boolean typeIsObject() { return Object.class == getType(); } protected boolean typeIsStringConvertable() { return getTypeConverter() != null; } protected StringEncoder.Converter getTypeConverter() { return _typeConverter; } public boolean isChoiceList() { if (getPropertyModel().getBooleanValueForParameter("ignorechoicelist")) { return false; } if (getType() != null) { return ChoiceList.class.isAssignableFrom(getType()) || getType().getEnumConstants() != null; } else { return false; } } // ================================================================= // ==================== Dynamic access to values =================== // ================================================================= protected boolean valueInError = false; protected boolean focusListenerEnabled = true; @Override public void setBackground(Color bg) { super.setBackground(bg); if (getDynamicComponent() != null) { getDynamicComponent().setBackground(bg); } } @Override public void setForeground(Color fg) { super.setForeground(fg); if (getDynamicComponent() != null) { getDynamicComponent().setForeground(fg); } } /*public KeyValueCoding getTargetObject() { return _propertyModel.getTargetObject(getModel()); } public String getLastAccessor() { return _propertyModel.getLastAccessor(); }*/ /** * This method can be called to store the newValue in the model. * * @param newValue */ protected synchronized void setObjectValue(T newValue) { if (getController() != null && getController().getDelegate() != null && _propertyModel.allowDelegateHandling()) { // Special case for delegate handling KeyValueCoding target = _propertyModel.getTargetObject(getModel()); String lastAccessor = _propertyModel.getLastAccessor(); Object oldValue = getObjectValue(); if (oldValue == null && newValue == null) { return; } if (newValue != null && oldValue != null && oldValue.equals(newValue)) { return; } // logger.info("_propertyModel="+_propertyModel); // logger.info("target="+target); // logger.info("lastAccessor="+lastAccessor); // logger.info("delegate="+getController().getDelegate()); if (target != null && getController().getDelegate().handlesObjectOfClass(target.getClass())) { getController().getDelegate().setLocalizedPropertyName(getLocalizedLabel()); getController().getDelegate().setTarget(target); getController().getDelegate().setKey(lastAccessor); focusListenerEnabled = false; boolean ok = false; try { ok = getController().getDelegate().setObjectValue(newValue); } finally { if (!ok) { valueInError = true; SwingUtilities.invokeLater(new UpdateFromModel()); if (_tab != null) { _tab.valueChange(newValue, this); } modelUpdating = false; } focusListenerEnabled = true; if (ok) { notifyInspectedPropertyChanged(newValue); } } } } else { try { // Normal case here, just invoke this _propertyModel.setObjectValue(getModel(), newValue); notifyInspectedPropertyChanged(newValue); } catch (AccessorInvocationException e) { valueInError = true; if (_controller != null) { if (!_controller.handleException(getModel(), observedPropertyName(), newValue, e.getTargetException())) { // Revert to value defined in the model ! if (logger.isLoggable(Level.INFO)) { logger.info("Exception was NOT handled"); } SwingUtilities.invokeLater(new UpdateFromModel()); } else { if (logger.isLoggable(Level.FINE)) { logger.fine("Exception was handled"); } SwingUtilities.invokeLater(new UpdateFromModel()); if (_tab != null) { _tab.valueChange(newValue, this); } } } else { e.getTargetException().printStackTrace(); } valueInError = false; modelUpdating = false; } } /*Object oldValue = getObjectValue(); // logger.info("Old value="+oldValue+" New value="+newValue); if (oldValue == null) { if (newValue == null) { if (logger.isLoggable(Level.FINE)) logger.fine("Same null value. Ignored."); return; } } else if ((newValue != null) && (oldValue.equals(newValue))) { if (logger.isLoggable(Level.FINE)) logger.fine("Same value. Ignored."); return; } try { boolean ok = true; KeyValueCoding target = getTargetObject(); if (getController() == null || getController().getDelegate() == null || !getController().getDelegate().handlesObjectOfClass(target.getClass())) { if (newValue != null) if (logger.isLoggable(Level.FINE)) logger.fine("setObjectValue() for property " + observedPropertyName() + " with " + newValue + " (type is " + newValue.getClass() + ")"); else if (logger.isLoggable(Level.FINE)) logger.fine("setObjectValue() for property " + observedPropertyName() + " with " + newValue + ")"); if (logger.isLoggable(Level.FINE)) logger.fine("setObjectValue() for property " + observedPropertyName() + " with " + newValue); if (target != null) { target.setObjectForKey(newValue, getLastAccessor()); } else if (logger.isLoggable(Level.WARNING)) logger.warning("Target object is null for key " + observedPropertyName() + ". We should definitely investigate this."); } else { getController().getDelegate().setLocalizedPropertyName(getLocalizedLabel()); getController().getDelegate().setTarget(target); getController().getDelegate().setKey(getLastAccessor()); focusListenerEnabled = false; ok =getController().getDelegate().setObjectValue(newValue); if (!ok) { valueInError = true; SwingUtilities.invokeLater(new UpdateFromModel()); if (_tab != null) { _tab.valueChange(newValue, this); } modelUpdating = false; } focusListenerEnabled = true; } if (ok) { notifyInspectedPropertyChanged(newValue); //BMA : comment those line because already executed in notify // if (_tab != null) { // _tab.valueChange(newValue, this); // } } } catch (AccessorInvocationException e) { valueInError = true; if (InspectorController.instance() != null) { if (!InspectorController.instance().handleException(getModel(), observedPropertyName(), newValue, e.getTargetException())) { // Revert to value defined in the model ! if (logger.isLoggable(Level.INFO)) logger.info("Exception was NOT handled"); SwingUtilities.invokeLater(new UpdateFromModel()); } else { if (logger.isLoggable(Level.FINE)) logger.fine("Exception was handled"); SwingUtilities.invokeLater(new UpdateFromModel()); if (_tab != null) { _tab.valueChange(newValue, this); } } } else { e.getTargetException().printStackTrace(); } valueInError = false; modelUpdating = false; } catch (Exception e) { e.printStackTrace(); if (logger.isLoggable(Level.WARNING)) logger.warning("setObjectValue() with " + newValue + " failed for property " + observedPropertyName() + " for object " + getModel().getClass().getName() + " : exception " + e.getMessage()); }*/ } protected boolean hasObjectValue() { try { return _propertyModel.hasObjectValue(getModel()); } catch (AccessorInvocationException e) { if (logger.isLoggable(Level.WARNING)) { logger.warning("getObjectValue() failed for property " + getObservedPropertyName() + " for object " + getModel() + " : exception " + e.getMessage()); } e.getTargetException().printStackTrace(); return false; } /* try { if (getModel() == null) { return false; } KeyValueCoding target = getTargetObject(); if (target != null) { target.objectForKey(getLastAccessor()); return true; } else { return false; } } catch (InvalidObjectSpecificationException e) { return false; } catch (AccessorInvocationException e) { if (logger.isLoggable(Level.WARNING)) logger.warning("getObjectValue() failed for property " + observedPropertyName() + " for object " + getModel().getClass().getName() + " : exception " + e.getMessage()); e.getTargetException().printStackTrace(); return false; } catch (Exception e) { e.printStackTrace(); if (logger.isLoggable(Level.WARNING)) logger.warning("getObjectValue() failed for property " + observedPropertyName() + " for object " + getModel().getClass().getName() + " : exception " + e.getMessage()); return false; }*/ } protected T getObjectValue() { if (getModel() == null) { return null; } return (T) _propertyModel.getObjectValue(getModel()); /* try { if (getModel() == null) { return null; } KeyValueCoding target = getTargetObject(); if (target != null) { return (T)target.objectForKey(getLastAccessor()); } else { return null; } } catch (AccessorInvocationException e) { if (logger.isLoggable(Level.WARNING)) logger.warning("getObjectValue() failed for property " + observedPropertyName() + " for object " + getModel().getClass().getName() + " : exception " + e.getMessage()); e.getTargetException().printStackTrace(); return null; } catch (Exception e) { if (logger.isLoggable(Level.WARNING)) logger.warning("getObjectValue() failed for property " + observedPropertyName() + " for object " + getModel().getClass().getName() + " : exception " + e.getMessage()); return null; }*/ } /*protected synchronized void setBooleanValue(boolean value) { boolean oldValue = getBooleanValue(); if (oldValue == value) { logger.info("Same value. Ignored."); return; } try { KeyValueCoding target = getTargetObject(); if (target != null) { target.setBooleanValueForKey(value, getLastAccessor()); } notifyInspectedPropertyChanged(new Boolean(value)); } catch (Exception e) { if (logger.isLoggable(Level.WARNING)) logger.warning("setBooleanValue() with " + value + " failed for property " + observedPropertyName() + " for object " + getModel().getClass().getName() + " : exception " + e.getMessage()); } }*/ protected void notifyInspectedPropertyChanged(Object newValue) { if (_inspectorModelView != null) { _inspectorModelView.valueChange(newValue, this); } else if (_tab != null) { _tab.valueChange(newValue, this); } } public void notifyInspectedPropertyChanged() { notifyInspectedPropertyChanged(getObjectValue()); } /* protected synchronized boolean getBooleanValue() { try { KeyValueCoding target = getTargetObject(); if (target != null) { return target.booleanValueForKey(getLastAccessor()); } else { return false; } } catch (Exception e) { if (logger.isLoggable(Level.WARNING)) logger.warning("getBooleanValue() failed for property " + observedPropertyName() + " for object " + getModel().getClass().getName() + " : exception " + e.getMessage()); return false; } }*/ /* protected synchronized void setIntegerValue(int value) { int oldValue = getIntegerValue(); if (oldValue == value) { logger.info("Same value. Ignored."); return; } try { KeyValueCoding target = getTargetObject(); if (target != null) { target.setIntegerValueForKey(value, getLastAccessor()); } notifyInspectedPropertyChanged(new Integer(value)); } catch (Exception e) { if (logger.isLoggable(Level.WARNING)) logger.warning("setIntegerValue() with " + value + " failed for property " + observedPropertyName() + " for object " + getModel().getClass().getName() + " : exception " + e.getMessage()); } }*/ /*protected synchronized void setDoubleValue(double value) { double oldValue = getDoubleValue(); if (oldValue == value) { logger.info("Same value. Ignored."); return; } try { KeyValueCoding target = getTargetObject(); if (target != null) { target.setDoubleValueForKey(value, getLastAccessor()); } notifyInspectedPropertyChanged(new Double(value)); } catch (Exception e) { if (logger.isLoggable(Level.WARNING)) logger.warning("setIntegerValue() with " + value + " failed for property " + observedPropertyName() + " for object " + getModel().getClass().getName() + " : exception " + e.getMessage()); } }*/ /*protected synchronized int getIntegerValue() { try { KeyValueCoding target = getTargetObject(); if (target != null) { return target.integerValueForKey(getLastAccessor()); } else { return 0; } } catch (Exception e) { if (logger.isLoggable(Level.WARNING)) logger.warning("getIntegerValue() failed for property " + observedPropertyName() + " for object " + getModel().getClass().getName() + " : exception " + e.getMessage()); return 0; } }*/ /*protected synchronized double getDoubleValue() { try { KeyValueCoding target = getTargetObject(); if (target != null) { return target.doubleValueForKey(getLastAccessor()); } else { return 0; } } catch (Exception e) { if (logger.isLoggable(Level.WARNING)) logger.warning("getIntegerValue() failed for property " + observedPropertyName() + " for object " + getModel().getClass().getName() + " : exception " + e.getMessage()); return 0; } } protected float getFloatValue() { try { KeyValueCoding target = getTargetObject(); if (target != null) { return target.floatValueForKey(getLastAccessor()); } else { return 0; } } catch (Exception e) { if (logger.isLoggable(Level.WARNING)) logger.warning("getIntegerValue() failed for property " + observedPropertyName() + " for object " + getModel().getClass().getName() + " : exception " + e.getMessage()); return 0; } }*/ // ==================================================== // ===================== Accessors ==================== // ==================================================== protected String observedPropertyName() { return getObservedPropertyName(); } /** * Overrides getXMLModel * * @see org.openflexo.inspector.widget.InnerTabWidgetView#getXMLModel() */ @Override public InnerTabWidget getXMLModel() { return _propertyModel; } public InspectableObject getModel() { return _model; } protected synchronized void setModel(InspectableObject value) { _model = value; if (isWidgetVisible()) { retrieveType(); // refreshConditional(); } } public String getLocalizedLabel() { if (_propertyModel.getLocalizedLabel() != null) { return _propertyModel.getLocalizedLabel(); } else { return FlexoLocalization.localizedForKey(_propertyModel.label); } } @Override public JLabel getLabel() { if (_label == null && _propertyModel.label != null) { _label = new JLabel(_propertyModel.label + " : ", SwingConstants.RIGHT); if (_propertyModel.getLocalizedLabel() != null) { _label.setText(_propertyModel.getLocalizedLabel() + " : "); } else { _label.setText(FlexoLocalization.localizedForKey(_propertyModel.label, " : ", _label)); } _label.setForeground(Color.BLACK); // _label.setBackground(InspectorCst.BACK_COLOR); _label.setFont(DEFAULT_LABEL_FONT); if (_propertyModel.help != null && !_propertyModel.help.equals("")) { _label.setToolTipText(_propertyModel.help); logger.fine("setToolTipText() with " + _propertyModel.help); } if (_controller.getHelpDelegate() != null && _controller.getHelpDelegate().isHelpAvailableFor(_propertyModel)) { _label.addMouseListener(new MouseAdapter() { @Override public void mouseEntered(MouseEvent e) { if (_propertyModel.getLocalizedLabel() != null) { _label.setText("<html><u>" + _propertyModel.getLocalizedLabel() + "</u>" + " : " + "</html>"); } else { _label.setText("<html><u>" + FlexoLocalization.localizedForKey(_propertyModel.label) + "</u>" + " : " + "</html>"); } _label.setForeground(Color.BLUE); _label.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); } @Override public void mouseExited(MouseEvent e) { if (_propertyModel.getLocalizedLabel() != null) { _label.setText(_propertyModel.getLocalizedLabel() + " : "); } else { _label.setText(FlexoLocalization.localizedForKey(_propertyModel.label) + " : "); } _label.setForeground(Color.BLACK); _label.setCursor(Cursor.getDefaultCursor()); } @Override public void mouseClicked(MouseEvent e) { _controller.getHelpDelegate().displayHelpFor(getModel()); } }); } } return _label; } public abstract Class getDefaultType(); /** * @param model * @return */ protected boolean isReadOnly() { if (getPropertyModel().hasValueForParameter(READONLY)) { String ro = getPropertyModel().getValueForParameter(READONLY); if (ro.equalsIgnoreCase("true") || ro.equalsIgnoreCase("yes")) { return true; } else if (ro.equalsIgnoreCase("false") || ro.equalsIgnoreCase("no")) { return false; } else { // dynamic test boolean negate = ro.startsWith("!"); if (negate) { ro = ro.substring(1); } boolean isReadOnly = getModel() != null && getModel().booleanValueForKey(ro); if (negate) { isReadOnly = !isReadOnly; } return isReadOnly; } } return false; } public boolean isWidgetVisible() { if (getController() != null && !getController().isTabPanelVisible(getPropertyModel().getTabModel(), getModel())) { return false; } if (conditional != null) { return conditional.isVisible(getModel()); } return true; } /** * @param newValue * @param propertyName * @return */ @Override public boolean isStillVisible(Object newValue, String propertyName) { return isWidgetVisible(); // return conditional.isVisible(getModel()); } public boolean isStillInvisible(Object newValue, String propertyName) { // return conditional.isStillInvisible(newValue,propertyName); return !conditional.isVisible(getModel()); } public boolean dependsOfProperty(String propertyName) { return conditional.dependsOfProperty(propertyName); } @Override public boolean dependsOfProperty(DenaliWidget widget) { return dependsOfProperty(widget.observedPropertyName()); } public boolean shouldBeDisplayed() { return conditional.isVisible(getModel()); } public boolean isVisible(InspectableObject inspectable) { return conditional.isVisible(inspectable); } protected boolean _hasFocus; public void gainFocus() { if (logger.isLoggable(Level.FINE)) { logger.fine("DenaliWidget " + getClass().getSimpleName() + ":" + _propertyModel.name + " GAIN focus"); } _hasFocus = true; if (getInspectingWidget() != null && getInspectingWidget() instanceof InspectorTabbedPanel) { ((InspectorTabbedPanel) getInspectingWidget()).widgetGetFocus(this); } } public void looseFocus() { if (logger.isLoggable(Level.FINE)) { logger.fine("DenaliWidget " + getClass().getSimpleName() + ":" + _propertyModel.name + " LOOSE focus"); } if (!valueInError && focusListenerEnabled && !modelUpdating) { updateModelFromWidget(); } _hasFocus = false; } public boolean widgetHasFocus() { return _hasFocus; } public String getObservedPropertyName() { return _propertyModel.name; } public String getObservedTabName() { return _observedTabName; } public PropertyModel getPropertyModel() { return _propertyModel; } private InspectorModelView _inspectorModelView; @Override public void setTabModelView(TabModelView tab, int index) { _tab = tab; indexInTab = index; if (tab != null) { _inspectorModelView = tab.getInspectorModelView(); if (getInspectingWidget() instanceof InspectorTabbedPanel) { if (_propertyModel.name.equals(((InspectorTabbedPanel) getInspectingWidget()).getLastInspectedPropertyName())) { ((InspectorTabbedPanel) getInspectingWidget()).setNextFocusedWidget(this); } } } } public InspectingWidget getInspectingWidget() { if (_tab != null && _tab.getInspectorModelView() != null) { return _tab.getInspectorModelView().getInspectingWidget(); } return null; } public void setTabModelView(TabModelView tab) { _tab = tab; if (tab != null) { _inspectorModelView = tab.getInspectorModelView(); } } public int getIndexInTab() { return indexInTab; } public class UpdateFromModel implements Runnable { /** * Overrides run * * @see java.lang.Runnable#run() */ @Override public void run() { while (modelUpdating) { try { Thread.sleep(100); } catch (InterruptedException e) { } } if (!getModel().isDeleted()) { updateWidgetFromModel(); } valueInError = false; } } // ==================================================== // =============== Layout informations ================ // ==================================================== public static final String EXPAND_VERTICALLY = "expandVertically"; public static final String EXPAND_HORIZONTALLY = "expandHorizontally"; public static final String WIDGET_LAYOUT = "widgetLayout"; public static final String DISPLAY_LABEL = "displayLabel"; private WidgetLayout _widgetLayout = null; private Boolean _expandHorizontally = null; private Boolean _expandVertically = null; private Boolean _displayLabel = null; @Override public final boolean shouldExpandHorizontally() { if (_expandHorizontally == null) { if (getPropertyModel().hasValueForParameter(EXPAND_HORIZONTALLY)) { _expandHorizontally = getPropertyModel().getBooleanValueForParameter(EXPAND_HORIZONTALLY); } else { _expandHorizontally = defaultShouldExpandHorizontally(); } } return _expandHorizontally; } @Override public final boolean shouldExpandVertically() { if (_expandVertically == null) { if (getPropertyModel().hasValueForParameter(EXPAND_VERTICALLY)) { _expandVertically = getPropertyModel().getBooleanValueForParameter(EXPAND_VERTICALLY); } else { _expandVertically = defaultShouldExpandVertically(); } } return _expandVertically; } @Override public final WidgetLayout getWidgetLayout() { if (_widgetLayout == null) { if (getPropertyModel().hasValueForParameter(WIDGET_LAYOUT)) { if (getPropertyModel().getValueForParameter(WIDGET_LAYOUT).equals("1COL")) { _widgetLayout = WidgetLayout.LABEL_ABOVE_WIDGET_LAYOUT; } else if (getPropertyModel().getValueForParameter(WIDGET_LAYOUT).equals("2COL")) { _widgetLayout = WidgetLayout.LABEL_NEXTTO_WIDGET_LAYOUT; } else { _widgetLayout = getDefaultWidgetLayout(); } } else { _widgetLayout = getDefaultWidgetLayout(); } } return _widgetLayout; } public WidgetLayout getDefaultWidgetLayout() { return WidgetLayout.LABEL_NEXTTO_WIDGET_LAYOUT; } public boolean defaultShouldExpandHorizontally() { return true; } public boolean defaultShouldExpandVertically() { return false; } // Override this if you want public boolean defaultDisplayLabel() { return getPropertyModel().label != null; } @Override public final boolean displayLabel() { if (_displayLabel == null) { if (getPropertyModel().hasValueForParameter(DISPLAY_LABEL)) { _displayLabel = getPropertyModel().getBooleanValueForParameter(DISPLAY_LABEL); } else { _displayLabel = defaultDisplayLabel(); } } return _displayLabel; } public enum WidgetLayout { LABEL_NEXTTO_WIDGET_LAYOUT, LABEL_ABOVE_WIDGET_LAYOUT } }