alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*
 *                 Sun Public License Notice
 * 
 * The contents of this file are subject to the Sun Public License
 * Version 1.0 (the "License"). You may not use this file except in
 * compliance with the License. A copy of the License is available at
 * http://www.sun.com/
 * 
 * The Original Code is NetBeans. The Initial Developer of the Original
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2004 Sun
 * Microsystems, Inc. All Rights Reserved.
 */

package org.openide.explorer.propertysheet;

import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.KeyboardFocusManager;
import java.awt.LayoutManager;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.FocusEvent;
import java.awt.event.KeyEvent;
import java.beans.FeatureDescriptor;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyEditor;
import java.beans.PropertyVetoException;
import java.beans.VetoableChangeListener;

import org.openide.nodes.Node;
import org.openide.util.NbBundle;

import javax.swing.*;
import javax.swing.event.ChangeListener;
import org.netbeans.modules.openide.explorer.TTVEnvBridge;


/** 

PropertyPanel is a generic GUI component for displaying and editing a JavaBeans™ * property or any compatible getter/setter pair for which there is a property editor * available, in accordance with the JavaBeans specification. It makes it possible to * instantiate an appropriate GUI component for a property and provides the plumbing * between user interation with the gui component and calls to the getter/setter pair * to update the value.

* *

The simplest way to use PropertyPanel is by driving it from an instance of * PropertySupport.Reflection. To do that, simply pass the name of the * property and an object with a getter/setter pair matching that property to the * PropertySupport.Reflection's constructor, and pass the resulting instance of * PropertySupport.Reflection to the PropertyPanel constructor.

* *

A more efficient approach is to implement Node.Property or pass an existing Node.Property * object to the PropertyPanel's constructor or PropertyPanel.setProperty - thus * bypassing the use of reflection to locate the getter and setter.

* *

A note on uses of Node.Property and PropertyModel: PropertyPanel was * originally designed to work with instances of PropertyModel, and has since been * rewritten to be driven by instances of Node.Property. The main reason for this * is simplification - there is considerable overlap between PropertyModel and * Node.Property; particularly, DefaultPropertyModel and PropertySupport.Reflection * effectively are two ways of doing exactly the same thing.

* *

Use of PropertyModel is still supported, but discouraged except under special * circumstances. The one significant difference between Node.Property * and PropertyModel is that PropertyModel permits listening for changes.

*

It is generally accepted that GUI components whose contents unexpectedly change * due to events beyond their control does not tend to lead to quality, usable user * interfaces. However, there are cases where a UI will, for example, contain several * components and modification to one should immediately be reflected in the other. * For such a case, use of PropertyModel is still supported. For other cases, * it makes more sense to use BeanNode and for the designer of the UI * to make a design choice as to how to handle (if at all) unexpected changes happening to * properties it is displaying. If all you need to do is display or edit a * property, use one of the constructors that takes a Node.Property object or * use setProperty. PropertyModel will be deprecated at some point * in the future, when a suitable replacement more consistent with * Node.Property is created.

* * PropertyModel and displays an editor component for it. * @author Jaroslav Tulach, Petr Hamernik, Jan Jancura, David Strupl, Tim Boudreau */ public class PropertyPanel extends JComponent implements javax.accessibility.Accessible { /** Constant defining a preference for rendering the value. * Value should be displayed in read-only mode. */ public static final int PREF_READ_ONLY = 0x0001; /** Constant defining a preference for rendering the value. * Value should be displayed in custom editor. */ public static final int PREF_CUSTOM_EDITOR = 0x0002; /** Constant defining a preference for rendering the value. * Value should be displayed in editor only. */ public static final int PREF_INPUT_STATE = 0x0004; /** Constant defining a preference for a borderless UI suitable for * use in a table */ public static final int PREF_TABLEUI = 0x0008; /** Name of the 'preferences' property. */ public static final String PROP_PREFERENCES = "preferences"; // NOI18N /** Name of the 'model' property. */ public static final String PROP_MODEL = "model"; // NOI18N /** Name of the read-only property 'propertyEditor'. * @deprecated - the property editor is re-fetched from the underlying * property object as needed. It is up to the property object to * cache or not cache the property editor. This property will no longer * be fired. */ public static final String PROP_PROPERTY_EDITOR = "propertyEditor"; // NOI18N /** Name of property 'state' that describes the state of the embeded PropertyEditor. * @see PropertyEnv#getState * @since 2.20 */ public static final String PROP_STATE = PropertyEnv.PROP_STATE; /** Holds value of property preferences. */ private int preferences; /** Holds value of property model. */ private PropertyModel model; /** * If this is true the changes made in the property editor * are immediately propagated to the value of the property * (to the property model). */ private boolean changeImmediate = true; /** The inner component, either a custom property editor, an * InplaceEditor's component or null, depending on the mode and state */ Component inner=null; /** Listener that will listen for changes in model, editor, env */ private Listener listener=null; /** The property which will drive the PropertyPanel */ private Node.Property prop; /** Flag to avoid an endless loop when setProperty is called by setModel */ private boolean settingModel=false; /** Creates new PropertyPanel backed by a dummy property */ public PropertyPanel () { this (ModelProperty.toProperty(null), 0, null); } /** * Creates new PropertyPanel with DefaultPropertyModel * @param preferences the preferences that affect how this propertypanel * will operate * @param bean The instance of bean * @param propertyName The name of the property to be displayed */ public PropertyPanel ( Object bean, String propertyName, int preferences ) { //XXX inefficient, get DefaultPropertyModel out of the loop this ( ModelProperty.toProperty(new DefaultPropertyModel (bean, propertyName)), preferences, //XXX can probably subst null for below new DefaultPropertyModel(bean, propertyName)); } /** Creates a new PropertyPanel. While not quite deprecated, do not * use this constructor if your intention is to display a Node.Property * object; use the constructor that takes a Node.Property object directly * instead. * @param model The model to display * @see org.openide.explorer.propertysheet.PropertyModel */ public PropertyPanel (PropertyModel model, int preferences) { this(null, preferences, model); } /** * Create a new property panel for the specified property with the * specified preferences. * @param p * @param preferences */ public PropertyPanel(Node.Property p, int preferences) { this(p, preferences, null); } /** * Create a new property panel for displaying/editing the specified * property * @param p A Property object for this node to represent * @see org.openide.nodes.Node.Property */ public PropertyPanel(Node.Property p) { this(p, 0, null); } /** Create a property panel that displays a property belonging to several * nodes. This is useful e.g. for TreeTableView. The property panel will * display the standard "different values" condition for cases * where the value of various properties does not match. *

* Note that this method assumes that none of the nodes will have two * different property sets each containing a property with the requested * name. The behavior of this constructor is undefined in this case, * and will likely result in a ClassCastException. *

* This constructor is fail-fast, and will preemptively check that the * properties to be proxied indeed exist and are of the same type. * * @param nodes The nodes that will supply properties * @param propertyName The name of the property to look for * @throws ClassCastException if the named property for one of the nodes * has a different value type than for others * @throws NullPointerException if even one of the nodes does not have * a property of the given name. */ PropertyPanel (Node[] nodes, String propertyName) throws ClassCastException, NullPointerException { //Protected for now - TreeTableView can use it this (nodes.length == 1 ? ModelProperty.findProperty(nodes[0], propertyName) : ModelProperty.toProperty(nodes, propertyName)); } /** The constructor all the other constructors call */ private PropertyPanel(Node.Property p, int preferences, PropertyModel mdl) { if (p == null) { prop = ModelProperty.toProperty(mdl); } else { prop = p; } this.preferences = preferences; initializing = true; setModel(mdl); initializing = false; setOpaque(true); //for debugging, allow CTRL-. to dump the state to stderr getInputMap(WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_PERIOD, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()), "dump"); getActionMap().put("dump", new AbstractAction() { //NOI18N public void actionPerformed(ActionEvent ae) { System.err.println(""); //NOI18N System.err.println(PropertyPanel.this); System.err.println(""); //NOI18N } }); //#44226 - Unpretty, but this allows the TreeTableView to invoke a custom editor dialog when //necessary - with the TTV rewrite, all cell editor infrastructure will be moved to //org.netbeans.modules.openide.explorer, and they will simply share editor classes. Since that //involves an API change (some package private methods of PropertyEnv need to be accessible to //the editor classes), this will have to wait for after 4.0 - Tim getActionMap().put("invokeCustomEditor", new CustomEditorProxyAction()); //NOI18N } /** * Held in action map to allow TreeTableView to invoke the custom editor over read-only cells */ private class CustomEditorProxyAction extends AbstractAction { public void actionPerformed(ActionEvent e) { Action wrapped = getWrapped(); if (wrapped != null) { wrapped.actionPerformed (e); } else { Toolkit.getDefaultToolkit().beep(); } } private Action getWrapped() { Node.Property p = getProperty(); EditablePropertyDisplayer pd = getPropertyDisplayer() instanceof EditablePropertyDisplayer ? (EditablePropertyDisplayer) getPropertyDisplayer() : new EditablePropertyDisplayer(p); return pd.getCustomEditorAction(); } public boolean isEnabled() { Action wrapped = getWrapped(); if (wrapped != null) { return wrapped.isEnabled(); } else { return getProperty() != null; } } } private boolean initializing = false; public void setBackground(Color c) { if (inner != null) { inner.setBackground(c); } super.setBackground(c); } public void setForeground(Color c) { if (inner != null) { inner.setForeground(c); } super.setForeground(c); } /** Returns an appropriate property displayer instance depending on the * preferences. For non-editable modes, will use a lightweight, near-stateless * RendererPropertyDisplayer component */ private PropertyDisplayer findPropertyDisplayer() { PropertyDisplayer result; Node.Property prop = getProperty(); if ((preferences & PREF_CUSTOM_EDITOR) == 0 && ((preferences & PREF_READ_ONLY) != 0 || !isEnabled())) { //Always use a renderer if we're inline and non-editable return getRendererComponent(prop); } switch (preferences) { case 9 : case 1 : //PREF_READ_ONLY result = getRendererComponent (prop); break; case 10 : case 2 : //PREF_CUSTOM_EDITOR result = new CustomEditorDisplayer(prop, model); break; case 11: case 3 : //PREF_CUSTOM_EDITOR & PREF_READ_ONLY result = new CustomEditorDisplayer(prop, model); //XXX remember to set enabled on the components break; case 12: case 4 : //PREF_INPUT_STATE result = new EditablePropertyDisplayer(prop, model); break; case 13: case 5 : //PREF_INPUT_STATE & PREF_READ_ONLY result = getRendererComponent (prop); break; case 14: case 6 : //PREF_INPUT_STATE & PREF_CUSTOM_EDIITOR result = new CustomEditorDisplayer(prop, model); //Only difference with this combination is it should display //an error dialog on commit if the entered value is bad break; case 15: case 7 : //PREF_INPUT_STATE & PREF_CUSTOM_EDITOR & PREF_READ_ONLY result = new CustomEditorDisplayer(prop, model); break; case 0 : case 8 : default : result = new EditablePropertyDisplayer(prop, model); break; } if (result instanceof PropertyDisplayer_Inline) { PropertyDisplayer_Inline inline = (PropertyDisplayer_Inline) result; boolean tableUI = (preferences & PREF_TABLEUI) != 0 || Boolean.TRUE.equals(getClientProperty("flat")); //NOI18N inline.setTableUI(tableUI); //NOI18N if (inline.isTableUI()) { inline.setUseLabels(!tableUI); } } boolean isTableUI = (preferences & PREF_TABLEUI) != 0; if (result instanceof CustomEditorDisplayer) { ((PropertyDisplayer_Editable) result).setUpdatePolicy(changeImmediate ? PropertyDisplayer.UPDATE_ON_FOCUS_LOST : PropertyDisplayer.UPDATE_ON_EXPLICIT_REQUEST); } else if (result instanceof PropertyDisplayer_Editable) { ((PropertyDisplayer_Editable) result).setUpdatePolicy(isTableUI ? PropertyDisplayer.UPDATE_ON_CONFIRMATION : PropertyDisplayer.UPDATE_ON_FOCUS_LOST); } if ((preferences & PREF_READ_ONLY) != 0 && result instanceof CustomEditorDisplayer) { ((CustomEditorDisplayer) result).setEnabled(false); } else if (result instanceof PropertyDisplayer_Editable) { if (!isEnabled()) { ((PropertyDisplayer_Editable)result).setEnabled(isEnabled()); } } return result; } /** Convenience method to allow reuse of mutable renderer components - * will either create a renderer or reuse the current displayer if there * is one and it's a renderer */ private RendererPropertyDisplayer getRendererComponent(Node.Property prop) { RendererPropertyDisplayer result; if (inner instanceof RendererPropertyDisplayer) { //re-use the one we already have if possible ((RendererPropertyDisplayer) inner).setProperty(prop); result = (RendererPropertyDisplayer) inner; } else { result = new RendererPropertyDisplayer(prop); } return result; } private PropertyDisplayer displayer=null; /** Fetch the PropertyDisplayer which will do the actual work of displaying * the property. It will be created if necessary */ private PropertyDisplayer getPropertyDisplayer() { if (displayer == null) { setDisplayer (findPropertyDisplayer()); } return displayer; } /** Installs the component we will embed to display the property */ private void installDisplayerComponent() { //Fetch or instantiate the component we will embed to display the //property. Depending on the prefs, it may be a RendererPropertyDisplayer, //an EditablePropertyDisplayer or a CustomPropertyDisplayer. PropertyDisplayer displayer = getPropertyDisplayer(); //Find who has focus now, so if we have focus, focus won't end up set //to null Component focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getPermanentFocusOwner(); boolean hadFocus = focusOwner == this || isAncestorOf(focusOwner); if (hadFocus) { //If we had focus, clear the global focus owner for now, so that //when the existing component is removed, it does not cause //focus to get briefly set to a random component KeyboardFocusManager.getCurrentKeyboardFocusManager().clearGlobalFocusOwner(); } //Fetch the new inner component (the custom editor or Inplace editor) Component newInner = displayer.getComponent(); //Set the enabled state appropriately. For implementations of //PropertyDisplayer_Editable, this will already be handled; for render- //only cases, it should be handled explicitly if (!(displayer instanceof PropertyDisplayer_Editable)) { //only for renderers newInner.setEnabled(isEnabled() && getProperty().canWrite()); } newInner.setForeground(getForeground()); newInner.setBackground(getBackground()); //Make sure the inner component has really changed if (newInner != inner) { synchronized (getTreeLock()) { //remove the odl component if (inner != null) { remove (inner); } //and add the new one (if any) if (newInner != null) { add (newInner); //invalidate its layout so it will be re-laid out newInner.invalidate(); inner = newInner; } } } //Force a re-layout immediately if visible if (isShowing() && !(getParent() instanceof javax.swing.CellRendererPane)) { validate(); } //Restore focus if necessary if (hadFocus && isEnabled() && (preferences & PREF_READ_ONLY) == 0) { requestFocus(); } //Simply adding a component to a container can sometimes cause it to be //given focus even though it's not focusable. If this has happened, //find the next component in the focus cycle root and force focus to that. //Mainly a problem with JFileChooser, but we also have a few property //editors that force focus on addNotify which should be fixed if (!isEnabled() || (preferences & PREF_READ_ONLY) != 0) { Component focus = KeyboardFocusManager.getCurrentKeyboardFocusManager(). getFocusOwner(); if (focus == inner || ( (inner instanceof Container) && ((Container) inner).isAncestorOf(focus))) { this.transferFocusUpCycle(); } } } public void doLayout() { layout(); } public void layout() { if (inner != null) { inner.setBounds(0,0, getWidth(), getHeight()); } } public Dimension getMinimumSize() { return getPreferredSize(); } public Dimension getPreferredSize() { Dimension result; if (!isDisplayable() && (preferences & PREF_CUSTOM_EDITOR) == 0) { //XXX use rendererfactory to make this more efficient and just //configure a shared renderer instacne with the property & fetch size result = getRendererComponent( getProperty()).getComponent().getPreferredSize(); } else if (inner != null) { result = inner.getPreferredSize(); } else { result = PropUtils.getMinimumPanelSize(); } return result; } /** Sets the property displayer we are using to display the property, * detaching listeners, etc */ private void setDisplayer (PropertyDisplayer nue) { if (displayer != null) { detachFromDisplayer(displayer); } displayer = nue; if (nue != null) { attachToDisplayer(displayer); } } /** Attach any necessary listeners to the property displayer to be used */ private void attachToDisplayer (PropertyDisplayer displayer) { if (displayer instanceof PropertyDisplayer_Inline) { updateDisplayerFromClientProps(); } if (displayer instanceof CustomEditorDisplayer) { ((CustomEditorDisplayer) displayer).setRemoteEnvListener(getListener()); ((CustomEditorDisplayer) displayer).setRemoteEnvVetoListener(getListener()); } if (displayer instanceof EditablePropertyDisplayer) { ((EditablePropertyDisplayer) displayer).setRemoteEnvListener(getListener()); ((EditablePropertyDisplayer) displayer).setRemoteEnvVetoListener(getListener()); PropertyEnv env = ((EditablePropertyDisplayer) displayer).getPropertyEnv(); if (env != null) { env.setFeatureDescriptor(getProperty()); } } } /** Remove any listeners and dispose any state relating to a displayer * we are no longer interested in */ private void detachFromDisplayer (PropertyDisplayer displayer) { if (displayer instanceof CustomEditorDisplayer) { ((CustomEditorDisplayer) displayer).setRemoteEnvVetoListener(null); } if (displayer instanceof EditablePropertyDisplayer) { ((EditablePropertyDisplayer) displayer).setRemoteEnvVetoListener(null); } if (displayer instanceof PropertyDisplayer_Editable) { ((PropertyDisplayer_Editable) displayer).dispose(); } } /** Overridden to catch changes in those client properties that are * relevant to PropertyPanel */ protected void firePropertyChange(String nm, Object old, Object nue) { if (("flat".equals(nm) || "radioButtonMax".equals(nm) || "suppressCustomEditor".equals(nm) || "useLabels".equals(nm)) && displayer != null && displayer instanceof PropertyDisplayer_Inline) { //NOI18N updateDisplayerFromClientProp(nm, nue); } super.firePropertyChange(nm, old, nue); } /** Update the current property displayer based on previously set client * properties */ private void updateDisplayerFromClientProp (String nm, Object val) { PropertyDisplayer displayer = getPropertyDisplayer(); if (displayer instanceof PropertyDisplayer_Inline) { PropertyDisplayer_Inline inline = (PropertyDisplayer_Inline) displayer; if ("flat".equals(nm)) { //NOI18N inline.setTableUI(Boolean.TRUE.equals(val)); if (Boolean.TRUE.equals(val)) { inline.setUseLabels(false); } else if (Boolean.FALSE.equals(val) && getClientProperty("useLabels") == null) { //NOI18N inline.setUseLabels(true); } } else if ("radioButtonMax".equals(nm)) { //NOI18N int max = val instanceof Integer ? ((Integer) val).intValue() : 0; inline.setRadioButtonMax(max); } else if ("suppressCustomEditor".equals(nm)) { //NOI18N inline.setShowCustomEditorButton(!Boolean.TRUE.equals(val)); } else if ("useLabels".equals(nm)) { //NOI18N inline.setUseLabels(Boolean.TRUE.equals(val)); } } } /** Overridden to return false in cases that the preferences specify a * read-only state */ public boolean isFocusable() { return super.isFocusable() && isEnabled() && (preferences & PREF_READ_ONLY) == 0; } /** Overridden to do * nothing in a read only state, since some custom property editors (File * chooser) are capable of receiving focus even if they are disabled, * effectively making focus disappear */ public void requestFocus() { //Do this because even if everything is disabled, JFileChooser's UI //*does* supply some focusable components if (!isEnabled() || (preferences & PREF_READ_ONLY) != 0) { return; } else if (inner != null && inner.isEnabled()) { super.requestFocus(); inner.requestFocus(); } } /** In the case that some client properties may have been set before a * PropertyRenderer was added, set up its values accordingly. */ private void updateDisplayerFromClientProps() { String[] props = new String[] {"flat", "radioButtonMax", "suppressCustomEditor", "useLabels"}; //NOI18N for (int i=0; i < props.length; i++) { Object o = getClientProperty(props[i]); if (o != null) { updateDisplayerFromClientProp(props[i], o); } } } protected void processFocusEvent (FocusEvent fe) { super.processFocusEvent(fe); if (fe.getID() == fe.FOCUS_GAINED) { if (inner != null && inner.isEnabled() && inner.isFocusTraversable()) { inner.requestFocus(); } } } /** Lazily create the listener for listening to the property editor, env * and model */ private Listener getListener() { if (listener == null) { listener = new Listener(); } return listener; } /** Overridden to install the inner component that will display the property*/ public void addNotify() { attachToModel(); if (displayer != null) { attachToDisplayer(displayer); } if (inner == null) { installDisplayerComponent(); } super.addNotify(); } /** Overridden to dispose the component that actually displays the property * and any state information associated with it */ public void removeNotify() { super.removeNotify(); detachFromModel(); if (displayer != null && (!(displayer instanceof RendererPropertyDisplayer))) { detachFromDisplayer(displayer); displayer = null; } if (!(inner instanceof RendererPropertyDisplayer)) { //Renderers hold no references the property panel doesn't, so avoid //creating a new one for performance reasons in TTV - PropertyPanel //will be repeatedly added to and removed from a CellRendererPane remove(inner); inner = null; } } /* public Dimension getPreferredSize() { Dimension result; if (!isDisplayable() && (preferences & PREF_CUSTOM_EDITOR) == 0) { //XXX use rendererfactory to make this more efficient and just //configure a shared renderer instacne with the property & fetch size result = getRendererComponent( getProperty()).getComponent().getPreferredSize(); } else if (inner != null) { result = inner.getPreferredSize(); } else { result = super.getPreferredSize(); } return result; } public Dimension getMinimumSize() { return getPreferredSize(); } */ /** Returns the preferences set for this property panel. The preferences * determine such things as read-only mode and whether an inline or custom * editor is displayed * @return The preferences */ public int getPreferences () { return preferences; } /** Setter for visual preferences in displaying * of the value of the property. * @param preferences PREF_XXXX constants */ public void setPreferences (int preferences) { if (preferences != this.preferences) { int oldPreferences = this.preferences; this.preferences = preferences; hardReset(); firePropertyChange(PROP_PREFERENCES, oldPreferences,preferences); } } /** Get the property model associated with this property panel. Note that * while the PropertyModel usages of PropertyPanel are not yet deprecated, * the preferred and more efficient use of PropertyPanel is directly with * a Node.Property instance rather than a PropertyModel.

Note: * This method is primarily here for backward compatibility, and the single * use case where it is truly desirable to have a GUI component which reflects * changes made by some source other than itself. If you have used one * of the constructors which takes a Node.Property instance * or the setProperty method, this method will return a * PropertyModel instance generated for the underlying Node.Property object. * In such a case, the PropertyModel instance thus obtained will not * fire changes due to changes made by calling setValue * on the Node.Property. For details on why this is the case, * see issue * 37779. * * @return Either the PropertyModel set in the constructor or via setModel, * or a generated instance of PropertyModel which wraps the Node.Property * which was set in the constructor or via setProperty. */ public PropertyModel getModel() { if (model == null) { return new NodePropertyModel(getProperty(), null); } return model; } /** Setter for property model. * Note that while the PropertyModel usages of PropertyPanel are not yet deprecated, * the preferred and more efficient use of PropertyPanel is directly with * a Node.Property instance rather than a PropertyModel. The PropertyPanel * will either construct a wrapper Node.Property instance or find the * underlying Node.Property instance, and use that to drive its infrastructure. * The only remaining use case for PropertyModel here is if the component * needs to listen for changes in the underlying value which do not * originate in this PropertyPanel. * *@param model New model. */ public void setModel(PropertyModel model) { if (model != this.model) { settingModel = true; if (this.model != null && listener != null) { detachFromModel(); } try { if (!initializing) { setProperty(ModelProperty.toProperty(model)); this.model = model; if (model != null) { if (isDisplayable()) { attachToModel(); } } } else { this.model = model; attachToModel(); } } finally { settingModel = false; } } } /** Attach listeners to an instance of PropertyModel */ private final void attachToModel() { if (model != null) { model.addPropertyChangeListener(getListener()); } } /** Detach listeners from an instance of PropertyModel */ private final void detachFromModel() { if (model != null) { model.removePropertyChangeListener(getListener()); } } Object[] beans = null; Object[] getBeans() { return beans; } /** Set or change the property this PropertyPanel will display * @param p the Property */ public final void setProperty (Node.Property p) { Object bridgeID = getClientProperty("beanBridgeIdentifier"); if (bridgeID != null) { TTVEnvBridge bridge = TTVEnvBridge.findInstance(bridgeID); if (bridge != null) { beans = bridge.getCurrentBeans(); bridge.clear(); } } if (p != prop) { prop = p; if (!settingModel) { //model will be recreated dynamically model = null; } if (displayer != null) { if (displayer instanceof PropertyDisplayer_Mutable) { ((PropertyDisplayer_Mutable) displayer).setProperty(p); } else { hardReset(); } } } } /** Reset any edits in progress and restore the property's value to the * component displaying it. In custom editor mode, this may cause the * entire inner component to be replaced with a new instance of the same * thing */ final void reset() { if ((preferences & PREF_CUSTOM_EDITOR) != 0) { getPropertyDisplayer().refresh(); } else { hardReset(); } } /** Do a full reset, replacing the inner component */ final void hardReset() { setDisplayer(findPropertyDisplayer()); if (isDisplayable()) { //XXX maybe check for isShowing, and if not just clear the inner component? installDisplayerComponent(); } } /** * Fetch the property that this PropertyPanel displays. For cases where * the PropertyPanel was initialized with an instance of PropertyModel, * the return value of this method is not officially defined. * @return the property */ public final Node.Property getProperty() { if (prop == null && model != null) { prop = ModelProperty.toProperty(model); } return prop; } /** Getter for the state of the property editor. The editor can be in * not valid states just if it implements the ExPropertyEditor * and changes state by the setState method of the PropertyEnv * environment. *

* @return PropertyEnv.STATE_VALID if the editor is not the ExPropertyEditor * one or other constant from PropertyEnv.STATE_* that was assigned to PropertyEnv * @since 2.20 */ public final Object getState () { if (displayer != null && displayer instanceof PropertyDisplayer_Editable) { return ((PropertyDisplayer_Editable) displayer).getPropertyEnv().getState(); } else { PropertyEditor ed = propertyEditor(); if (ed instanceof ExPropertyEditor) { //XXX until we kill ReusablePropertyModel, anyway ReusablePropertyEnv env = reusableEnv; reusableModel.setProperty(prop); ((ExPropertyEditor) ed).attachEnv(env); return env.getState(); } } return PropertyEnv.STATE_VALID; } private ReusablePropertyEnv reusableEnv = new ReusablePropertyEnv(); private ReusablePropertyModel reusableModel = new ReusablePropertyModel(reusableEnv); /** If the editor is ExPropertyEditor it tries to change the * getState property to PropertyEnv.STATE_VALID * state. This may be vetoed, in such case a warning is presented to the user * and the getState will still return the original value * (different from STATE_VALID). *

* Also updates the value if * org.openide.explorer.propertysheet.editors.EnhancedCustomPropertyEditor * is used. */ public void updateValue() { if (displayer != null && displayer instanceof PropertyDisplayer_Editable) { PropertyEnv env = ((PropertyDisplayer_Editable) displayer).getPropertyEnv(); if (PropertyEnv.STATE_NEEDS_VALIDATION.equals(env.getState())) { env.setState(PropertyEnv.STATE_VALID); } if (!changeImmediate) { try { ((PropertyDisplayer_Editable) displayer).commit(); } catch (IllegalArgumentException iae) { PropertyDialogManager.notify(iae); } } } } /** * Getter for current property editor depending on the model. * It may be null if it is not possible * to obtain a property editor.

* Note: When not in custom editor mode, PropertyPanel does * not cache the editor supplied by the property. If the PropertyPanel * was initialized from an instance of (deprecated) DefaultPropertyModel, * a different instance of the property editor may be constructed and * returned for each call. Client code must take this into account. * . For cases of initialization via a Node.Property object, * it is up to the supplied Property to cache or not cache the property * editor returned from getPropertyEditor() as suits its * needs. * @deprecated The property panel does not cache the property editor, and * depending on its state, it may not consistently return the same property * editor instance on repeated calls. The current implementation will * do so for editable states, but there is no guarantee this will remain * so in the future. * @return the property editor or null */ public PropertyEditor getPropertyEditor() { return propertyEditor(); } /** Internal implementation of getPropertyEditor(). */ private PropertyEditor propertyEditor() { PropertyEditor result = null; if (displayer != null) { //Use the package private methods to fetch the same editor //being used by the displayer, so the state will be appropriate if (displayer instanceof CustomEditorDisplayer) { result = ((CustomEditorDisplayer) displayer).getPropertyEditor(); } else if (displayer instanceof EditablePropertyDisplayer) { result = ((EditablePropertyDisplayer) displayer).getPropertyEditor(); } } if (result == null) { //Fetch the property editor using the utility method (which will //handle no-property editor and multiple selection states //appropriately) result = PropUtils.getPropertyEditor(getProperty()); } return result; } /** Sets whether or not this component is enabled. * * all panel components gets disabled when enabled parameter is set false * @param enabled flag defining the action. */ public void setEnabled(boolean enabled) { // bugfix# 10171, explicitly disable components inside the custom editor super.setEnabled(enabled); if (inner != null) { PropertyDisplayer displayer = getPropertyDisplayer(); if (displayer instanceof PropertyDisplayer_Editable) { //Make sure we iterate all components in the custom editor //by calling setEnabled on the PropertyDisplayer interface, //not the component ((PropertyDisplayer_Editable) displayer).setEnabled(enabled); } else { //We're probably using a renderer, we now need an editable //component hardReset(); } } } /** Getter for property changeImmediate. * If this is true the changes made in the property editor * are immediately propagated to the value of the property * (to the property model). * * @return Value of property changeImmediate. */ public boolean isChangeImmediate() { return changeImmediate; } /** Setter for property changeImmediate. * IF this is true the changes made in the property editor * are immediately propagated to the value of the property * (to the property model). * @param changeImmediate New value of property changeImmediate. */ public void setChangeImmediate(boolean changeImmediate) { if (this.changeImmediate == changeImmediate) { return; } this.changeImmediate = changeImmediate; if (isShowing()) { PropertyDisplayer displayer = getPropertyDisplayer(); if (displayer instanceof PropertyDisplayer_Editable) { ((PropertyDisplayer_Editable) displayer).setUpdatePolicy(changeImmediate ? PropertyDisplayer.UPDATE_ON_FOCUS_LOST : PropertyDisplayer.UPDATE_ON_EXPLICIT_REQUEST); } } firePropertyChange(PropertyEnv.PROP_CHANGE_IMMEDIATE, changeImmediate?Boolean.FALSE:Boolean.TRUE, changeImmediate?Boolean.TRUE:Boolean.FALSE); } /** Overridden to provide information from the embedded property renderer * if not in custom editor mode */ public String toString() { if ((preferences & PREF_CUSTOM_EDITOR) != 0) { //custom editor mode, not much useful to say here return super.toString() + " - " + prefsToString(getPreferences()); //NOI18N } else { return getClass().getName() + System.identityHashCode(this) + prefsToString(getPreferences()) + " propertyRenderer: " + (inner == null ? " null " : inner.toString()); //NOI18N } } /** Utility method to convert PropertyPanel preferences to human-readable * form */ private static String prefsToString(int prefs) { StringBuffer sb = new StringBuffer(" prefs:"); //NOI18N int[] vals = new int[] { PREF_CUSTOM_EDITOR, PREF_INPUT_STATE, PREF_READ_ONLY }; String[] s = new String[] { "PREF_CUSTOM_EDITOR", "PREF_INPUT_STATE", "PREF_READ_ONLY" }; //NOI18N boolean found=false; for (int i=0; i < vals.length; i++) { if ((vals[i] & prefs) != 0) { sb.append(s[i]); } if (found && i != vals.length-1) { sb.append(","); //NOI18N } found=true; } return sb.toString(); } private final boolean isGtk = "GTK".equals(UIManager.getLookAndFeel().getID()) || //NOI18N UIManager.getLookAndFeel().getClass().getSuperclass().getName().indexOf ("Synth") != -1; //NOI18N /* * Overridden to fill in the background color, since Synth/GTKLookAndFeel ignores * setOpaque(true). * @see http://www.netbeans.org/issues/show_bug.cgi?id=43024 */ public void paint (Graphics g) { if (isGtk) { //Presumably we can get this fixed for JDK 1.5.1 Color c = getBackground(); if (c == null) c = UIManager.getColor("control"); //NOI18N if (c == null) c = Color.LIGHT_GRAY; g.setColor (c); g.fillRect (0, 0, getWidth(), getHeight()); } super.paint(g); } ////////////////// Accessibility support /////////////////////////////////// public javax.accessibility.AccessibleContext getAccessibleContext() { if (accessibleContext == null) { accessibleContext = new AccessiblePropertyPanel(); } return accessibleContext; } private class AccessiblePropertyPanel extends AccessibleJComponent { AccessiblePropertyPanel() {} public javax.accessibility.AccessibleRole getAccessibleRole() { return javax.accessibility.AccessibleRole.PANEL; } public String getAccessibleName() { String name = super.getAccessibleName(); if (name == null && model instanceof ExPropertyModel) { FeatureDescriptor fd = ((ExPropertyModel)model).getFeatureDescriptor(); name = NbBundle.getMessage(PropertyPanel.class, "ACS_PropertyPanel", fd.getDisplayName()); //NOI18N } return name; } public String getAccessibleDescription() { String description = super.getAccessibleDescription(); if (description == null && model instanceof ExPropertyModel) { FeatureDescriptor fd = ((ExPropertyModel)model).getFeatureDescriptor(); description = NbBundle.getMessage(PropertyPanel.class, "ACSD_PropertyPanel", fd.getShortDescription()); //NOI18N } return description; } } private class Listener implements PropertyChangeListener, VetoableChangeListener, ChangeListener { public void propertyChange(PropertyChangeEvent evt) { if (evt.getSource() instanceof PropertyEnv) { firePropertyChange(PropertyPanel.PROP_STATE, evt.getOldValue(), evt.getNewValue()); } if (evt.getSource() instanceof PropertyModel) { if (evt.getOldValue() == null && evt.getNewValue() == null) { //the old hack of firing a null-null change to get the //infrastructure to dump everything and start from scratch hardReset(); } else { //Just notify the displayer that the value may have changed, //no need for anything radical reset(); } } } public void vetoableChange(PropertyChangeEvent evt) throws PropertyVetoException { //do nothing - the displayer will take care of it for us } public void stateChanged(javax.swing.event.ChangeEvent e) { //do nothing } } }

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.