|
What this is
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-2003 Sun
* Microsystems, Inc. All Rights Reserved.
*/
package org.openide.explorer.propertysheet;
import java.awt.*;
import java.awt.event.*;
import java.beans.*;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.lang.reflect.Method;
import java.util.*;
import java.util.List;
import javax.swing.*;
import javax.swing.event.*;
import org.openide.ErrorManager;
import org.openide.util.HelpCtx;
import org.openide.util.NbBundle;
import org.openide.nodes.Node;
import org.openide.nodes.Node.PropertySet;
import org.openide.nodes.NodeAdapter;
import org.openide.util.Lookup;
import org.openide.util.Mutex;
import org.openide.util.RequestProcessor;
/**
* Implements a property sheet for a set of nodes.
*
* Note that this class should be final, but for backward compatibility,
* cannot be. Subclassing this class is strongly discouraged
*
* @author Tim Boudreau, Jan Jancura, Jaroslav Tulach
*/
public class PropertySheet extends JPanel {
/** generated Serialized Version UID */
static final long serialVersionUID = -7698351033045864945L;
// public constants ........................................................
/** Deprecated - no code outside the property sheet should be interested
* in how items are sorted.
*@deprecated Relic of the original property sheet implementation, will never be fired. */
public static final String PROPERTY_SORTING_MODE = "sortingMode"; // NOI18N
/** Property giving current value color.
*@deprecated Relic of the original property sheet implementation, will never be fired. */
public static final String PROPERTY_VALUE_COLOR = "valueColor"; // NOI18N
/** Property giving current disabled property color.
*@deprecated Relic of the original property sheet implementation, , will never be fired. */
public static final String PROPERTY_DISABLED_PROPERTY_COLOR = "disabledPropertyColor"; // NOI18N
/** Property with the current page index.
*@deprecated Relic of the original property sheet implementation, , will never be fired.*/
public static final String PROPERTY_CURRENT_PAGE = "currentPage"; // NOI18N
/** Property for plastic mode.
*@deprecated Relic of the original property sheet implementation, , will never be fired. */
public static final String PROPERTY_PLASTIC = "plastic"; // NOI18N
/** Property for the painting style.
*@deprecated Relic of the original property sheet implementation, will never be fired. */
public static final String PROPERTY_PROPERTY_PAINTING_STYLE = "propertyPaintingStyle"; // NOI18N
/** Property for whether only writable properties should be displayed.
*@deprecated Relic of the original property sheet implementation, will never be fired.*/
public static final String PROPERTY_DISPLAY_WRITABLE_ONLY = "displayWritableOnly"; // NOI18N
/** Constant for showing properties as a string always.
*@deprecated Relic of the original property sheet implementation, useless. */
public static final int ALWAYS_AS_STRING = 1;
/** Constant for preferably showing properties as string.
*@deprecated Relic of the original property sheet implementation, does useless. */
public static final int STRING_PREFERRED = 2;
/** Constant for preferably painting property values.
*@deprecated Relic of the original property sheet implementation, does useless. */
public static final int PAINTING_PREFERRED = 3;
/** Constant for unsorted sorting mode. */
public static final int UNSORTED = 0;
/** Constant for by-name sorting mode. */
public static final int SORTED_BY_NAMES = 1;
/** Constant for by-type sorting mode.
* @deprecated Not supported since NetBeans 3.6
**/
public static final int SORTED_BY_TYPES = 2;
/** Icon for the toolbar.
* @deprecated Presumably noone uses this variable. If you want to customize
* the property sheet look you can change the image files directly (or use your
* own).
*/
static protected Icon iNoSort;
/** Icon for the toolbar.
* @deprecated Presumably noone uses this variable. If you want to customize
* the property sheet look you can change the image files directly (or use your
* own).
*/
static protected Icon iAlphaSort;
/** Icon for the toolbar.
* @deprecated Presumably noone uses this variable. If you want to customize
* the property sheet look you can change the image files directly (or use your
* own).
*/
static protected Icon iTypeSort;
/** Icon for the toolbar.
* @deprecated Presumably noone uses this variable. If you want to customize
* the property sheet look you can change the image files directly (or use your
* own).
*/
static protected Icon iDisplayWritableOnly;
/** Icon for the toolbar.
* @deprecated Presumably noone uses this variable. If you want to customize
* the property sheet look you can change the image files directly (or use your
* own).
*/
static protected Icon iCustomize;
/** Action command/input map key for popup menu invocation action */
private static final String ACTION_INVOKE_POPUP = "invokePopup"; //NOI18N
/** Action command/input map key for help invocation action */
private static final String ACTION_INVOKE_HELP = "invokeHelp"; //NOI18N
/** Init delay for second change of the selected nodes. */
private static final int INIT_DELAY = 70;
/** Maximum delay for repeated change of the selected nodes. */
private static final int MAX_DELAY = 150;
/**Debugging option to suppress all use of tabs */
private static final boolean neverTabs = Boolean.getBoolean("netbeans.ps.nevertabs"); //NOI18N
/** Holds the sort mode for the property sheet */
private int sortingMode = UNSORTED;
/**Tracks whether the description area should be shown */
boolean showDesc;
/** Temporary storage for the last selected node in the case the property
* sheet was removed temporarily from a container (winsys DnD) */
private Reference storedNode = null;
//Package private for unit tests
SheetTable table = new SheetTable();
PSheet psheet = new PSheet();
HelpAction helpAction=new HelpAction();
/** Create a new property sheet */
public PropertySheet() {
init();
initActions();
}
/** Install actions the property sheet will need */
private void initActions() {
Action invokePopupAction = new MutableAction (MutableAction.INVOKE_POPUP, this);
table.getInputMap().put(
KeyStroke.getKeyStroke(KeyEvent.VK_F10, KeyEvent.SHIFT_MASK),
ACTION_INVOKE_POPUP);
table.getActionMap().put(ACTION_INVOKE_POPUP, invokePopupAction);
getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(
KeyStroke.getKeyStroke(KeyEvent.VK_F10, KeyEvent.SHIFT_MASK),
ACTION_INVOKE_POPUP);
getActionMap().put(ACTION_INVOKE_POPUP, invokePopupAction);
getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(
KeyStroke.getKeyStroke(KeyEvent.VK_F1, 0), ACTION_INVOKE_HELP);
getActionMap().put(ACTION_INVOKE_HELP, helpAction);
}
public void addNotify() {
super.addNotify();
Node oldSelection = null;
if (storedNode != null) {
oldSelection = (Node) storedNode.get();
}
if (oldSelection != null) {
setCurrentNode (oldSelection);
}
}
public void updateUI() {
UIManager.get ("nb.propertysheet"); //Causes default colors for the property sheet to be bootstrapped into
//UIDefaults - see core/swing/plaf
super.updateUI();
}
public void removeNotify() {
Node lastSel = null;
if (pclistener != null) {
//Save the last selection - if we're being transiently removed,
//i.e. because of drag and drop, we'll want to reset it on the
//next addNotify if it hasn't disappeared
lastSel = pclistener.detach();
}
doSetNodes(null);
if (lastSel != null) {
//Save the selected node in case we're re-added to a container
storedNode = new WeakReference(lastSel);
}
super.removeNotify();
table.getReusablePropertyEnv().setBeans(null);
table.getReusablePropertyEnv().setNode(null);
table.getReusablePropertyModel().setProperty(null);
}
/** Prepare the initial state of the property sheet */
private void init() {
Font f = UIManager.getFont("controlFont"); //NOI18N
if (f == null) {
//Aqua
f = UIManager.getFont("Tree.font"); //NOI18N
}
if (f != null) {
table.setFont(f);
}
showDesc = PropUtils.shouldShowDescription ();
setLayout(new BorderLayout());
psheet.setBackground(table.getBackground());
setBackground(table.getBackground());
psheet.setMarginColor(PropUtils.getSetRendererColor());
psheet.add (table);
add (psheet, BorderLayout.CENTER);
table.setBorder(BorderFactory.createEmptyBorder());
setDescriptionVisible (showDesc);
setMinimumSize(new Dimension(100, 50));
psheet.setEmptyString(NbBundle.getMessage(
PropertySheet.class, "CTL_NoProperties")); //NOI18N
TabSelectionListener listener = new TabSelectionListener();
psheet.addSelectionChangeListener(listener);
table.addChangeListener(listener);
try {
setSortingMode (PropUtils.getSavedSortOrder());
} catch (PropertyVetoException e) {
//Should never happen unless someone manually modifies
//backing storage
ErrorManager.getDefault().notify(e);
}
}
private class TabSelectionListener implements ChangeListener, FocusListener {
public void stateChanged(ChangeEvent e) {
helpAction.checkContext();
if (e.getSource() instanceof SheetTable) {
SheetTable tbl = (SheetTable) e.getSource();
FeatureDescriptor fd = tbl.getSelection();
Component focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getPermanentFocusOwner();
if (focusOwner != tbl && !tbl.isKnownComponent(focusOwner) && !isAncestorOf(focusOwner)) {
fd = null;
}
if (fd != null) {
String ttl = fd.getDisplayName();
String desc = fd.getShortDescription();
psheet.setDescription(ttl, desc);
} else {
Node n = pclistener.getNode();
if (n != null) {
String ttl = n.getDisplayName();
String desc = (String) n.getValue("nodeDescription"); //NOI18N
if (desc == null) {
desc = n.getShortDescription();
}
psheet.setDescription (ttl, desc);
} else {
psheet.setDescription (null, null);
}
}
} else {
if (!psheet.isAdjusting()) {
psheet.storeScrollAndTabInfo();
}
PropertySet[] sets = (PropertySet[]) psheet.getTabbedContainerSelection();
if (sets != null) {
table.getPropertySetModel().setPropertySets(sets);
if (sets.length > 0 && !psheet.isAdjusting()) {
String tab = (String) sets[0].getValue("tabName"); //NOI18N
tab = tab == null ?
PropUtils.basicPropsTabName() : tab;
psheet.manager().storeLastSelectedGroup(tab);
psheet.adjustForName(tab);
}
}
}
}
public void focusGained(FocusEvent e) {
ChangeEvent ce = new ChangeEvent (table);
stateChanged (ce);
}
public void focusLost(FocusEvent e) {
focusGained(e);
}
}
/** Enable/disable display of the description area */
void setDescriptionVisible(boolean val) {
if (isDescriptionVisible() != val) {
int state = psheet.getState();
if (!val) {
int newState = (state & PSheet.STATE_HAS_TABS) != 0 ?
PSheet.STATE_HAS_TABS : 0;
psheet.setState (newState);
} else {
int newState = (state & PSheet.STATE_HAS_TABS) != 0 ?
PSheet.STATE_HAS_TABS | PSheet.STATE_HAS_DESCRIPTION :
PSheet.STATE_HAS_DESCRIPTION;
psheet.setState (newState);
}
PropUtils.saveShowDescription(val);
}
}
boolean isDescriptionVisible() {
return (psheet.getState() & PSheet.STATE_HAS_DESCRIPTION) != 0;
}
/** Overridden to route focus requests to the table */
public void requestFocus() {
if (table.getParent() != null) {
table.requestFocus();
} else {
super.requestFocus();
}
}
/** Overridden to route focus requests to the table */
public boolean requestFocusInWindow() {
if (table.getParent() != null) {
return table.requestFocusInWindow();
} else {
return super.requestFocusInWindow();
}
}
/**
* Set the nodes explored by this property sheet.
*
* @param nodes nodes to be explored
*/
private void doSetNodes (Node[] nodes) {
if (nodes == null || nodes.length == 0) {
table.getPropertySetModel().setPropertySets(null);
return;
}
final Node n = (nodes.length == 1) ? nodes[0] : new ProxyNode(nodes);
setCurrentNode(n);
}
// delayed setting nodes (partly impl issue 27781)
private transient Node[] helperNodes;
/**Set the nodes explored by this property sheet.
* @param nodes nodes to be explored or null to clear the sheet
*/
public synchronized void setNodes (Node[] nodes) {
final boolean loggable = PropUtils.isLoggable(PropertySheet.class);
if (loggable) {
PropUtils.log (PropertySheet.class, "SetNodes " + Arrays.asList(nodes));
}
//Performance - check equality and avoid some extra repaints - repainting
//the property sheet can be expensive
if (nodes != null && nodes.length > 0 && pclistener != null) {
if (nodes.length == 1 && nodes[0] == pclistener.getNode()) {
if (loggable) {
PropUtils.log (PropertySheet.class, " Same node selected as before; no redisplay needed");
}
return;
} else if (pclistener.getNode() instanceof ProxyNode) {
if (loggable) {
PropUtils.log (PropertySheet.class,
" Selected node is a proxy node - comparing contents.");
}
Node[] currNodes = ((ProxyNode) pclistener.getNode()).getOriginalNodes();
if (Arrays.asList(nodes).equals(Arrays.asList(currNodes))) {
if (loggable) {
PropUtils.log (PropertySheet.class, " Proxy node represents the same " +
"nodes already showing. Showing: " +
Arrays.asList(currNodes) + " requested " +
Arrays.asList(nodes));
HashSet currs = new HashSet(Arrays.asList(currNodes));
HashSet reqs = new HashSet(Arrays.asList(nodes));
if (currs.size() != currNodes.length) {
PropUtils.log (PropertySheet.class,
" A hashSet of the current nodes does NOT have the same number " +
" of elements as the array of current nodes! Check " +
"your hashCode()/equals() contract. One or more nodes in " +
"the array are claiming to be the same node.");
}
if (reqs.size() != nodes.length) {
PropUtils.log (PropertySheet.class,
" A hashSet of the requested selected nodes does NOT have the same number " +
" of elements as the array of current nodes! Check your hashCode()/equals() contract" +
" One or more nodes in the array are claiming to be the same node.");
}
}
return;
}
}
} else if (nodes == null || nodes.length == 0) {
if (pclistener != null) {
pclistener.detach();
}
if (SwingUtilities.isEventDispatchThread()) {
if (loggable) {
PropUtils.log (PropertySheet.class, " Nodes cleared on event queue. Emptying model.");
}
table.getPropertySetModel().setPropertySets(null);
} else {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
if (loggable) {
PropUtils.log (PropertySheet.class, " Nodes " +
"cleared off event queue. Empty model later on EQ.");
}
table.getPropertySetModel().setPropertySets(null);
}
});
}
return;
}
RequestProcessor.Task task = getScheduleTask ();
helperNodes = nodes;
//Clear any saved node if setNodes is called while we're offscreen
storedNode = null;
if (task.equals (initTask)) {
//if task is only init task then set nodes immediatelly
scheduleTask.schedule (0);
task.schedule (INIT_DELAY);
} else {
// in a task run then increase delay and reschedule task
int delay = task.getDelay () * 2;
if (delay > MAX_DELAY) delay = MAX_DELAY;
if (delay < INIT_DELAY) delay = INIT_DELAY;
if (loggable) {
PropUtils.log (PropertySheet.class, " Scheduling delayed update of selected nodes.");
}
task.schedule (delay);
}
}
private transient RequestProcessor.Task scheduleTask;
private transient RequestProcessor.Task initTask;
private synchronized RequestProcessor.Task getScheduleTask () {
if (scheduleTask == null) {
scheduleTask = RequestProcessor.getDefault ().post (new Runnable () {
public void run () {
final Node[] nodes = helperNodes;
SwingUtilities.invokeLater (new Runnable () {
public void run () {
final boolean loggable = PropUtils.isLoggable(PropertySheet.class);
if (loggable) {
PropUtils.log (PropertySheet.class, "Delayed " +
"updater setting nodes to " +
Arrays.asList(nodes));
}
doSetNodes (nodes);
}
});
}
});
initTask = RequestProcessor.getDefault ().post (new Runnable () {
public void run () {
}
});
}
// if none task runs then return initTask to wait for next changes
if (initTask.isFinished () && scheduleTask.isFinished ()) {
return initTask;
}
// if some task runs then return schedule task which will set nodes
return scheduleTask;
}
// end of delayed
/** This has to be called from the AWT thread. */
void setCurrentNode(Node node) {
Node old = pclistener.getNode();
if (old != node) {
psheet.storeScrollAndTabInfo();
}
final boolean loggable = PropUtils.isLoggable(PropertySheet.class);
if (loggable) {
PropUtils.log (PropertySheet.class, "SetCurrentNode:" + node);
}
// table.setNode (node);
PropertySetModel psm = table.getPropertySetModel();
Node.PropertySet[] ps = node.getPropertySets();
//bloc below copied from original impl - is this common/needed?
if (ps == null) {
// illegal node behavior => log warning about it
ErrorManager.getDefault().log(ErrorManager.WARNING,
"Node "+ node +": getPropertySets() returns null!"); // NOI18N
ps = new Node.PropertySet[] {};
//Prepare the reusable model/env's node
}
table.getReusablePropertyEnv().setNode(node);
assert noNullPropertyLists(ps) : "Node " + node + " returns null from getProperties() for one or " +
"more of its property sets"; //NOI18N
if (table.isEditing()) {
table.removeEditor();
}
boolean usingTabs = needTabs(node);
if (usingTabs) {
psheet.setState (psheet.getState() | PSheet.STATE_HAS_TABS);
TabInfo info = getTabItems(node);
psheet.setTabbedContainerItems(info.sets,info.titles);
psheet.manager().setCurrentNodeName(node.getName());
psm.setPropertySets(info.getSets(0));
} else {
psm.setPropertySets(ps);
psheet.setState ((psheet.getState() & PSheet.STATE_HAS_DESCRIPTION) != 0 ?
PSheet.STATE_HAS_DESCRIPTION : 0);
psheet.setTabbedContainerItems(new Object[0], new String[0]);
}
psheet.adjustForName(node.getName());
table.setBeanName(node.getDisplayName());
String description = (String) node.getValue("nodeDescription"); //NOI18N
psheet.setDescription(node.getDisplayName(), description == null ? node.getShortDescription() : description);
pclistener.attach(node);
if (isDescriptionVisible()) {
helpAction.checkContext();
}
}
private boolean noNullPropertyLists (PropertySet[] ps) {
boolean result = true;
for (int i=0; i < ps.length; i++) {
result &= ps[i].getProperties() != null;
if (!result) break;
}
return result;
}
/**Deprecated, does nothing.
* @param style Irrelevant
* @deprecated Relic of the original property sheet implementation. Does nothing.*/
public void setPropertyPaintingStyle(int style) {
}
/**Deprecated, returns no meaningful value.
* @return the mode
* @see #setPropertyPaintingStyle
* @deprecated Relic of the original property sheet implementation. Does nothing. */
public int getPropertyPaintingStyle() {
return 0;
}
/**
* Set the sorting mode.
* @param sortingMode one of {@link #UNSORTED} or {@link #SORTED_BY_NAMES}. {@link #SORTED_BY_TYPES} is
* no longer supported.
* @throws PropertyVetoException if a value other than one of the defined sorting modes is set
*/
public void setSortingMode(int sortingMode) throws PropertyVetoException {
try {
table.getPropertySetModel().setComparator(PropUtils.getComparator(sortingMode));
this.sortingMode = sortingMode;
psheet.setMarginPainted(getSortingMode() == UNSORTED);
PropUtils.putSortOrder (sortingMode);
} catch (IllegalArgumentException iae) {
throw new PropertyVetoException(
NbBundle.getMessage(
PropertySheet.class, "EXC_Unknown_sorting_mode"),
new PropertyChangeEvent(this, PROPERTY_SORTING_MODE, new Integer(0), new Integer(sortingMode))
); //NOI18N
}
}
/**Get the sorting mode.
* @return the mode
* @see #setSortingMode */
public int getSortingMode() {
return sortingMode;
}
/** Deprecated. Does nothing.
* @param index index of the page to select
* @deprecated Relic of the original property sheet implementation. Does nothing.
*/
public void setCurrentPage(int index) {
}
/**
* Deprecated. Does nothing.
* @deprecated Relic of the original property sheet implementation. Does nothing.
* @param str name of the tab to select
* @return always returns false
*/
public boolean setCurrentPage(String str) {
return false;
}
/**Deprecated. Does nothing.
* @return index of currently selected page
* @deprecated Relic of the original property sheet implementation. Does nothing. */
public int getCurrentPage() {
// return pages.getSelectedIndex ();
return 0;
}
/**Deprecated. Does nothing.
* @param plastic true if so
* @deprecated Relic of the original property sheet implementation. Display of properties
* is handled by the look and feel.
*/
public void setPlastic(boolean plastic) {
}
/**Test whether buttons in sheet are plastic.
* @return
|
| ... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2024 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.