Please use the Apache issue tracking system for new NetBeans issues (https://issues.apache.org/jira/projects/NETBEANS0/issues) !!
View | Details | Raw Unified | Return to bug 29447
Collapse All | Expand All

(-)openide-spec-vers.properties (-1 / +1 lines)
 Lines 4-7    Link Here 
4
# Must always be numeric (numbers separated by '.', e.g. 4.11).
4
# Must always be numeric (numbers separated by '.', e.g. 4.11).
5
# See http://openide.netbeans.org/versioning-policy.html for more.
5
# See http://openide.netbeans.org/versioning-policy.html for more.
6
6
7
org.openide.specification.version=4.8
7
org.openide.specification.version=4.9
(-)api/doc/changes/apichanges.xml (+30 lines)
 Lines 114-119    Link Here 
114
114
115
  <changes>
115
  <changes>
116
    <change>
116
    <change>
117
     <api name="nodes"/>
118
     <summary>InplaceEditor interface added to APIs, some deprecations in property sheet rewrite</summary>
119
     <version major="4" minor="9"/>
120
     <date day="16" month="7" year="2003"/>
121
     <author login="tboudreau"/>
122
     <compatibility addition="yes"/>
123
     <description>
124
       New interface that allows a property editor to supply an inline editor
125
       for the new property sheet added, as part of merging the new property
126
       sheet.  A method, registerInplaceEditorFactory() has been added to PropertyEnv to allow
127
       modules to supply an inplace editor globally for all properties of a 
128
       given type; also, Node.Property objects may supply a custom inplace editor
129
       instance via the hint &quot;inplaceEditor&quot; in getValue(String).
130
       <p>PropertySheetSettings is an old SystemOption subclass that offers
131
       settings that affect the display of the property sheet.  These settings
132
       are irrelevant to the new property sheet.</p><p>
133
       In order to provide some performance optimizations, it was
134
       necessary to un-final the class PropertyEnv.  However, it 
135
       should be treated as final outside the package - there should
136
       never be a need to subclass it.  A note has been added to
137
       its javadoc to this effect.</p>
138
     </description>
139
     <class package="org.openide.explorer.propertysheet" name="InplaceEditor"/>
140
     <class package="org.openide.explorer.propertysheet" name="PropertyEnv"/>
141
     <class package="org.openide.explorer.propertysheet" name="PropertySheet"/>
142
     <class package="org.openide.explorer.propertysheet" name="PropertySheetSettings"/>
143
     <issue number="29447"/>
144
    </change>
145
146
    <change>
117
     <api name="services"/>
147
     <api name="services"/>
118
     <summary>New <code>lookupItem()</code> method in Lookups</summary>
148
     <summary>New <code>lookupItem()</code> method in Lookups</summary>
119
     <version major="4" minor="8"/>
149
     <version major="4" minor="8"/>
(-)api/doc/org/openide/explorer/doc-files/api.html (+88 lines)
 Lines 71-76    Link Here 
71
<li><a href="#other-prop-eds">Other available property editors</a>
71
<li><a href="#other-prop-eds">Other available property editors</a>
72
</ul>
72
</ul>
73
73
74
<li><a href="#customps">Customizing the property sheet</a>
75
<ul>
76
<li><a href="#uimanager">UIManager settings that affect property sheet display characteristics</a></li>
77
<li><a href="#preferences">Use of the Preferences API to store property sheet settings</a></li>
78
<li><a href="#jvmflags">JVM flags that affect the behavior of the property sheet</a></li>
79
</ul>
80
</li>
81
74
<li><a href="#diagrams">UML diagrams</a>
82
<li><a href="#diagrams">UML diagrams</a>
75
<ul>
83
<ul>
76
<li><a href="#diagram_basic">General structure class diagram</a>
84
<li><a href="#diagram_basic">General structure class diagram</a>
 Lines 814-819    Link Here 
814
            <TH> Description
822
            <TH> Description
815
            </TH>
823
            </TH>
816
        </TR>
824
        </TR>
825
826
        <TR>
827
            <TD rowspan='2'> property sheet </TD>
828
            <TD> inplaceEditor </TD>
829
            <TD> org.openide.explorer.propertysheet.InplaceEditor </TD>
830
            <TD> Allows a property editor to supply an instance of the
831
                 InplaceEditor class to be used inline in the property
832
                 sheet instead of the default inline editor.  Note this
833
                 effect can be acheived globally for all properties of
834
                 a given type by writing an ExPropertyEditor implementation
835
                 that calls <code>PropertyEnv.registerInplaceEditor</code>
836
                 in its <code>attachEnv()</code> method, and registering that
837
                 property editor class using one of the mechanisms of 
838
                 <code>java.beans.PropertyEditorManager</code>.
839
            </TD>
840
        </TR>
841
        
817
        <TR>
842
        <TR>
818
            <TD rowspan='2'> property sheet </TD>
843
            <TD rowspan='2'> property sheet </TD>
819
            <TD> canEditAsText </TD>
844
            <TD> canEditAsText </TD>
 Lines 960-965    Link Here 
960
985
961
property editors.</p>
986
property editors.</p>
962
987
988
<h3><a name="customps">Customizing the property sheet</a></h3>
989
<h4><a name="jvmflags">JVM flags that affect the behavior of the property sheet</a></h4>
990
<p>Note that generally these flags may or may not be supported in
991
future versions.  The following flags may be passed to the JVM
992
in the form <code>runide -J-Dsome.property=true</code> which 
993
affect the user interface of the property sheet.  Generally they
994
represent either cases where different applications have different
995
requirements, or where there is some contention about which style
996
is the most effective.
997
</p>
998
<UL>
999
<LI><b>netbeans.ps.noCustomButtons</b> - Do not display custom editor buttons
1000
    for properties unless the user has clicked the property to put it into
1001
    edit mode.  This mimics the behavior of the original property sheet, and
1002
    looks somewhat cleaner, but has the side effect that properties are not
1003
    automatically updated by clicking on a checkbox, and combo box
1004
    editors do not automatically open on the first click.</LI>
1005
<LI><b>netbeans.ps.forceRadioBoolean</b> - always use a radio button editor for
1006
    boolean/Boolean properties, instead of a checkbox</LI>
1007
<LI><b>netbeans.ps.noCheckboxCaption</b> - do not display a caption on checkbox 
1008
    boolean editors displayed in the property sheet</LI>
1009
<LI><b>netbeans.ps.hideSingleExpansion</b> - In sort-by-category mode, hide the
1010
    category expander for nodes that posess only one set of properties</LI>
1011
<LI><b>netbeans.ps.neverMargin</b> - Suppress the left margin of the property
1012
    sheet - less aesthetically pleasing, but useful on small displays</LI>
1013
<LI><b>netbeans.reusable.strictthreads</b> - diagnostic flag to force an
1014
    exception to be thrown if code attempts to force the property sheet to
1015
    paint from some thread other than the AWT event thread</LI>
1016
</UL>
1017
<h4><a name="uimanager">UIManager settings that affect property sheet display characteristics</a></h4>
1018
<p>The property sheet will look for a number of custom values in UIManager,
1019
which may be supplied to affect its appearance, either by a themes.xml file
1020
in the user directory, or by a custom look and feel.  It is not required that
1021
UIManager return non-null for any of these values - they are optional for
1022
enhancing the presentation of the property sheet:
1023
</p>
1024
<UL>
1025
<LI><b>Tree.altbackground</b> - an alternate Color to use for every other
1026
    line in the property sheet</LI>
1027
<LI><b>PropSheet.setBackground</b> - Background color for expandable sets.  If
1028
    not set, a color is derived from the default table background color.</LI>
1029
<LI><b>PropSheet.setBackground</b> - Background color for expandable sets when selected.  If
1030
    not set, a color is derived from the default table selection background color.</LI>
1031
<LI><b>netbeans.ps.rowheight</b> - An Integer specifying a fixed height for rows
1032
    in the property sheet, regardless of font size.  If not set, the height is
1033
    derived from the font size.</LI>
1034
<LI><b>netbeans.ps.iconmargin</b> - Integer pixel count to add to the left column 
1035
    margin in the property sheet</LI>
1036
</UL>
1037
<h4><a name="preferences">Use of the Preferences API to store property sheet settings</a></h4>
1038
<p>The property sheet uses the java Preferences API to persist some trivial
1039
settings across sessions.  They are:
1040
<UL>
1041
<LI><B>showDescriptionArea</B> - A boolean key for whether the last state of the
1042
   description area was shown or hidden</LI>
1043
<LI><B>closedSetNames</B> - A string containing a comma delimited list of property sets the user has
1044
   de-expanded and not later reopened, used to set the default expanded state
1045
   of similarly named property sets when displayed in the property sheet.</LI>
1046
<LI><B>sortOrder</B> - An Integer key for the sort order (as defined in 
1047
   <code>org.openide.explorer.propertysheet.PropertySheet</code>) that should
1048
   be used by default.  </LI>
1049
</UL>
1050
</p>
963
<div class="nonnormative">
1051
<div class="nonnormative">
964
1052
965
<h2><a name="diagrams">UML Diagrams</a></h2>
1053
<h2><a name="diagrams">UML Diagrams</a></h2>
(-)src/org/openide/explorer/propertysheet/Boolean3WayEditor.java (+245 lines)
Added Link Here 
1
/*
2
 *                 Sun Public License Notice
3
 * 
4
 * The contents of this file are subject to the Sun Public License
5
 * Version 1.0 (the "License"). You may not use this file except in
6
 * compliance with the License. A copy of the License is available at
7
 * http://www.sun.com/
8
 * 
9
 * The Original Code is NetBeans. The Initial Developer of the Original
10
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun
11
 * Microsystems, Inc. All Rights Reserved.
12
 */
13
/*
14
 * Boolean3WayEditor.java
15
 *
16
 * Created on April 16, 2003, 7:05 PM
17
 */
18
19
package org.openide.explorer.propertysheet;
20
import java.awt.Component;
21
import java.awt.Graphics;
22
import java.awt.Rectangle;
23
import java.awt.event.*;
24
import java.beans.*;
25
import javax.swing.*;
26
import org.openide.util.*;
27
/** A property editor for Boolean values which can also be null to
28
 *  indicate the editor represents multiple conflicting values.
29
 *
30
 * @author  Tim Boudreau
31
 */
32
final class Boolean3WayEditor implements ExPropertyEditor, 
33
                                            InplaceEditor.Factory {
34
    Boolean v = null;
35
36
    public Boolean3WayEditor() {
37
    }
38
    
39
    /** Utility field holding list of PropertyChangeListeners. */
40
    private transient java.util.ArrayList propertyChangeListenerList;
41
    
42
    public String getAsText() {
43
        if (v == null) {
44
            return NbBundle.getMessage (Boolean3WayEditor.class, 
45
                "CTL_Different_Values");
46
        } else if (Boolean.TRUE.equals(v)) {
47
            return Boolean.TRUE.toString(); //XXX use hinting
48
        } else {
49
            return Boolean.FALSE.toString(); //XXX use hinting
50
        }
51
    }
52
    
53
    public java.awt.Component getCustomEditor() {
54
        return null;
55
    }
56
    
57
    public String getJavaInitializationString() {
58
        if (v == null) {
59
            return "null"; //NOI18N
60
        } else if (Boolean.TRUE.equals (v)){
61
            return "Boolean.TRUE"; //NOI18N
62
        } else {
63
            return "Boolean.FALSE"; //NOI18N
64
        }
65
    }
66
    
67
    public String[] getTags() {
68
        return null;
69
    }
70
    
71
    public Object getValue() {
72
        return v;
73
    }
74
    
75
    public boolean isPaintable() {
76
        return true;
77
    }
78
    
79
    private Boolean3Inplace renderer = null;
80
    public void paintValue(Graphics gfx, Rectangle box) {
81
        if (renderer == null) {
82
            renderer = new Boolean3Inplace();
83
        }
84
        renderer.setSize (box.width, box.height);
85
        renderer.getLayout().layoutContainer(renderer);
86
        Graphics g = gfx.create(box.x, box.y, box.width, box.height);
87
        renderer.setOpaque(false);
88
        renderer.paint (g);
89
        g.dispose();
90
    }
91
    
92
    public void setAsText(String text) {
93
        if (Boolean.TRUE.toString().compareToIgnoreCase(text) == 0) {
94
            setValue (Boolean.TRUE);
95
        } else {
96
            setValue (Boolean.FALSE);
97
        }
98
    }
99
    
100
    public void setValue(Object value) {
101
        if (v != value) {
102
            v = (Boolean) value;
103
        }
104
        firePropertyChange();
105
    }
106
    
107
    public boolean supportsCustomEditor() {
108
        return false;
109
    }
110
    
111
    public void attachEnv(PropertyEnv env) {
112
        env.registerInplaceEditorFactory (this);
113
    }
114
115
    /** Registers PropertyChangeListener to receive events.
116
     * @param listener The listener to register.
117
     *
118
     */
119
    public synchronized void addPropertyChangeListener(
120
        PropertyChangeListener listener) {
121
        if (propertyChangeListenerList == null ) {
122
            propertyChangeListenerList = new java.util.ArrayList();
123
        }
124
        propertyChangeListenerList.add(listener);
125
    }
126
    
127
    /** Removes PropertyChangeListener from the list of listeners.
128
     * @param listener The listener to remove.
129
     *
130
     */
131
    public synchronized void removePropertyChangeListener(
132
        PropertyChangeListener listener) {
133
        if (propertyChangeListenerList != null ) {
134
            propertyChangeListenerList.remove(listener);
135
        }
136
    }
137
    
138
    /** Notifies all registered listeners about the event.
139
     *
140
     * @param event The event to be fired
141
     *
142
     */
143
    private void firePropertyChange() {
144
        java.util.ArrayList list;
145
        synchronized (this) {
146
            if (propertyChangeListenerList == null) return;
147
            list = (java.util.ArrayList)propertyChangeListenerList.clone();
148
        }
149
        PropertyChangeEvent event = new PropertyChangeEvent (this, null, 
150
            null, null);
151
        for (int i = 0; i < list.size(); i++) {
152
            ((java.beans.PropertyChangeListener)list.get(i)).propertyChange(event);
153
        }
154
    }
155
    
156
    /** Implementation of InplaceEditor.Factory to create an inplace editor on demand.
157
     *   With the current implementation, this will actually never be called, because
158
     *   edit requests for boolean properties automatically toggle the value.  This may,
159
     *   however, be desirable for the reimplementation of PropertyPanel. */
160
    public InplaceEditor getInplaceEditor() {
161
        return new Boolean3Inplace();
162
    }
163
    
164
    private class Boolean3Inplace extends JCheckBox 
165
                                    implements InplaceEditor {
166
        Boolean3Inplace () {
167
            setModel (new ButtonModel3Way());
168
        }
169
                                        
170
        public String getText () {
171
            return NbBundle.getMessage (Boolean3WayEditor.class, 
172
                "CTL_Different_Values");
173
        }
174
        
175
        public void clear() {
176
            propertyModel = null;
177
        }
178
179
        public void connect(PropertyEditor pe, PropertyEnv env) {
180
            //do nothing
181
        }
182
183
        public javax.swing.JComponent getComponent() {
184
            return this;
185
        }
186
187
        public javax.swing.KeyStroke[] getKeyStrokes() {
188
            return null;
189
        }
190
191
        public PropertyEditor getPropertyEditor() {
192
            return Boolean3WayEditor.this;
193
        }
194
195
        public Object getValue() {
196
            return Boolean3WayEditor.this.getValue();
197
        }
198
199
        public void handleInitialInputEvent(InputEvent e) {
200
            if (e instanceof MouseEvent) {
201
                setValue (Boolean.TRUE);
202
            }
203
        }
204
205
        public void reset() {
206
            //do nothing
207
        }
208
209
        public void setValue(Object o) {
210
            //do nothing
211
        }
212
        
213
        public boolean supportsTextEntry() {
214
            return false;
215
        }
216
        
217
        private PropertyModel propertyModel = null;
218
        public void setPropertyModel (PropertyModel pm) {
219
            propertyModel = pm;
220
        }
221
      
222
        public PropertyModel getPropertyModel () {
223
            return propertyModel;
224
        }
225
        
226
        public boolean isKnownComponent(Component c) {
227
            return false;
228
        }
229
        
230
    }
231
232
    private class ButtonModel3Way extends DefaultButtonModel {
233
        public boolean isPressed () {
234
            return Boolean3WayEditor.this.v == null;
235
        }
236
        public boolean isArmed () {
237
            return true;
238
        }
239
        public boolean isSelected () {
240
            if (v == null) return true;
241
            return super.isSelected ();
242
        }
243
    }
244
    
245
}
(-)src/org/openide/explorer/propertysheet/Bundle.properties (-6 / +66 lines)
 Lines 17-27    Link Here 
17
# {1} - type (class) of the property (currently unused)
17
# {1} - type (class) of the property (currently unused)
18
PS_EditorTitle={0}
18
PS_EditorTitle={0}
19
PS_ArrayOf=Array of
19
PS_ArrayOf=Array of
20
FMT_ErrorSettingProperty=Invalid value. The property {1} could not be set. Reason:\n{0}
20
FMT_ErrorSettingProperty=The property could not be set. {0} is not a valid value for {1}.
21
21
22
#PropertySheet
22
#PropertySheet
23
CTL_NoPropertyEditor=(No Property Editor)
23
CTL_NoPropertyEditor=(No Property Editor)
24
EXC_Unknown_sorting_mode=Unknown Sorting Mode
25
CTL_NoProperties=<No Properties>
24
CTL_NoProperties=<No Properties>
26
CTL_Property_Read_Yes=(r/
25
CTL_Property_Read_Yes=(r/
27
CTL_Property_Read_No=(-/
26
CTL_Property_Read_No=(-/
 Lines 30-37    Link Here 
30
CTL_Property_Write_No=-) 
29
CTL_Property_Write_No=-) 
31
30
32
# tooltips and accessible names on buttons in the PropertySheet
31
# tooltips and accessible names on buttons in the PropertySheet
33
CTL_NoSort=Unsorted
32
CTL_NoSort=Sort by Category
34
ACS_CTL_NoSort=Unsorted
33
ACS_CTL_NoSort=Sort by Category
35
CTL_AlphaSort=Sort by Name
34
CTL_AlphaSort=Sort by Name
36
ACS_CTL_AlphaSort=Sort by Name
35
ACS_CTL_AlphaSort=Sort by Name
37
CTL_TypeSort=Sort by Type
36
CTL_TypeSort=Sort by Type
 Lines 42-47    Link Here 
42
ACS_CTL_Customize=Customizer
41
ACS_CTL_Customize=Customizer
43
CTL_Help=Help
42
CTL_Help=Help
44
ACS_CTL_Help=Help
43
ACS_CTL_Help=Help
44
ACSD_CTL_Help=Provides help about the object whose properties are displayed in the property sheet
45
45
46
# PropertyPanel
46
# PropertyPanel
47
# This message is printed to ide.log if user is customizing an object as bean 
47
# This message is printed to ide.log if user is customizing an object as bean 
 Lines 107-114    Link Here 
107
ACSD_PropertyPanelWriteComponent={0}
107
ACSD_PropertyPanelWriteComponent={0}
108
# WARNING: trailing space intentional here:
108
# WARNING: trailing space intentional here:
109
ACSD_BeanListDelimiter=, 
109
ACSD_BeanListDelimiter=, 
110
ACS_PropertySheetTabs=Property sheet tabs
111
ACSD_PropertySheetTabs=N/A
112
110
113
#IndexedEditorPanel
111
#IndexedEditorPanel
114
CTL_Properties=Properties
112
CTL_Properties=Properties
 Lines 135-137    Link Here 
135
ACSD_HideDetails=N/A
133
ACSD_HideDetails=N/A
136
ACSD_ShowDetails=N/A
134
ACSD_ShowDetails=N/A
137
ACSD_IndexedEditorPanel=N/A
135
ACSD_IndexedEditorPanel=N/A
136
137
138
#Properties defined in rewrite begin here
139
#Text for the property sheet popup menu
140
CTL_Help=Help
141
142
#Column names - not used by default L&F but others could
143
COLUMN_NAMES=Names
144
COLUMN_VALUES=Values
145
146
#Unreadable value text (for write-only properties, fwiw)
147
UNREADABLE=<unreadable>
148
#Text for null values
149
NULL=null
150
151
#Title for error dialog when a property is set to a bad value
152
ERRDLG_TITLE=Error setting value
153
#Localized msg for property veto exception on unknown sorting mode, if
154
#property sheet used at design-time
155
EXC_Unknown_sorting_mode=Unknown Sorting Mode
156
157
#Button labels for property dialogs
158
ok=Ok
159
default=Restore default value
160
cancel=Cancel
161
close=Close
162
163
#Text to prepend and append to boolean values to dissociate the 
164
#text value of the checkbox.
165
BOOLEAN_PREPEND=(
166
BOOLEAN_APPEND=)
167
FMT_BOOLEAN={0}{1}{2}
168
169
#Property sheet controls
170
ACS_Description=Description
171
ACSD_Description=Description of the currently selected property
172
ACS_DescriptionTitle=Property name
173
ACSD_DescriptionTitle=Name of the property currently being edited
174
CTL_NO_DESCRIPTION=No description available.
175
CTL_NO_SELECTION=Nothing selected
176
177
#text to use in invalid property when the actual property name is unknown
178
CTL_Unnamed_Property=The property
179
180
#Accessible text format for the property sheet table - the selected property
181
#name and value are given
182
FMT_ACST_SheetProperty={0} equals {1}
183
FMT_ACST_SheetSet=Property group {0}
184
ACST_No_Selection=Nothing selected
185
CTL_Multiple_Selection=Multiple objects selected
186
#Custom editor button tooltip
187
CTL_EDBUTTON_TIP=Click, or select and press CTRL-SPACE to open custom editor; right click for menu
188
#Delimiter for lists of node names when multiple nodes are selected
189
CTL_List_Delimiter=, 
190
CTL_ShowDescription=Show description area
191
CTL_HideDescription=Hide description area
192
#Label for the tab which lists properties that do not supply their own tab name
193
#in the case that the property sheet is displaying tabs
194
LBL_BasicTab=Basic properties
195
#Stand-in text for the property name in error dialogs when the user is
196
#editing a property of a JavaBean and the name is unknown
197
MSG_unknown_property_name=this property
(-)src/org/openide/explorer/propertysheet/ButtonPanel.java (+370 lines)
Added Link Here 
1
/*
2
 *                 Sun Public License Notice
3
 * 
4
 * The contents of this file are subject to the Sun Public License
5
 * Version 1.0 (the "License"). You may not use this file except in
6
 * compliance with the License. A copy of the License is available at
7
 * http://www.sun.com/
8
 * 
9
 * The Original Code is NetBeans. The Initial Developer of the Original
10
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun
11
 * Microsystems, Inc. All Rights Reserved.
12
 */
13
/*
14
 * ButtonPanel.java
15
 *
16
 * Created on December 15, 2002, 5:45 PM
17
 */
18
19
package org.openide.explorer.propertysheet;
20
import java.awt.*;
21
import java.awt.event.FocusListener;
22
import javax.swing.*;
23
/** This class acts as a container for property table cell
24
 * editors that support custom editors, and as a cell
25
 * renderer proxy that will display the custom editor button.
26
 * This ensures that renderers appear identical 
27
 * to editors, and that any changes to the appearance of the
28
 * button that launches the custom editor are made, they will
29
 * appear automatically in both renderers and editors.  The
30
 * <code>paint()</code> method, if called when unparented,
31
 * will automatically paint the custom editor button (assuming
32
 * that if it is not needed, the component that shows the 
33
 * value will be used as a renderer). The 
34
 * renderer or editor component is set using <code>setComponent()</code>.
35
 * @author  Tim Boudreau
36
 */
37
class ButtonPanel extends javax.swing.JComponent {
38
    static final ButtonPanel RENDERER_INSTANCE = new ButtonPanel();
39
    private static final Icon cuIcon = new BpIcon();
40
    /** Global button used for rendering custom editor.  */
41
    static final JButton customEditorButton = new JButton(cuIcon); 
42
    //for debugging focus issues
43
    static {
44
        //set name for debugging & tests
45
        customEditorButton.setName ("Custom editor button - default instance"); //NOI18N
46
        customEditorButton.setHorizontalTextPosition(SwingConstants.CENTER);
47
        customEditorButton.setHorizontalAlignment(SwingConstants.CENTER);
48
        customEditorButton.setMargin (null);
49
        customEditorButton.setText(null);
50
        customEditorButton.setIcon (cuIcon);
51
    }
52
    
53
    static int buttonWidth=-1;
54
    public static final Object editorActionKey = "openCustomEditor";
55
    /** Creates a new instance of ButtonPanel */
56
    public ButtonPanel() {
57
        setFocusTraversalPolicy (new PanelTraversalPolicy());
58
    }
59
    
60
    /**Overrides addNotify to install the button if <code>setButtonVisible(true)</code>
61
     * has been called, and installs the component assigned in <code>setComponent()</code>
62
     * (the renderer or editor).  */
63
    public void addNotify () {
64
        super.addNotify();
65
        //if the parent is not a CellRendererPane, we're being used
66
        //as a bona-fide component, not a renderer, so make sure the
67
        //component we need to contain is really our child
68
        if (!(getParent() instanceof CellRendererPane)) 
69
            installComponent(); 
70
71
        //Install the action to launch the custom editor
72
        InputMap imp = new InputMap();
73
        imp.put (KeyStroke.getKeyStroke (java.awt.event.KeyEvent.VK_ENTER, 
74
            java.awt.Event.CTRL_MASK), editorActionKey);
75
        setInputMap (JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, imp);
76
    }
77
    
78
    static JButton button = null;
79
    /** Add the custom editor button and position it appropriately. */
80
    private void installButton (Action a) {
81
        //pseudo-clone the default button instance and configure it.
82
        //If we're being used as a real editor, we can't assume we're
83
        //the only component that would use the static custom editor
84
        //button instance
85
        if (button == null) {
86
            button = new JButton ();
87
        }
88
        button.setSize (customEditorButton.getSize());
89
        button.setBounds (getWidth() - buttonWidth, 0, buttonWidth, getHeight());
90
        button.setIcon (cuIcon);
91
        button.setMargin (null);
92
        button.setName ("Custom editor button - editor instance");//NOI18N
93
        button.setAction(a);
94
        button.setText(null);
95
        add (button);
96
    }
97
    
98
    /** Calculate the correct size for the custom editor button. */
99
    private static final void calcButtonDims(Graphics g) {
100
        FontMetrics fm = g.getFontMetrics(); 
101
        int stWidth = 
102
            fm.charsWidth("...".toCharArray(), 0, 3);
103
        buttonWidth = stWidth + 5;
104
        //need an odd number to center the ... caption
105
        if (buttonWidth % 2 == 0) {
106
            buttonWidth++;
107
        }
108
    }
109
    
110
    public void updateUI () {
111
        super.updateUI();
112
    }
113
    
114
    /** The component to be rendered in the left side of the component or the
115
     *full component in the case the custom editor button should not be displayed. */
116
    JComponent comp = null;
117
    /**  Set the component that will render (or be 
118
     * editor for) the property value.  The component
119
     * <strong>must</strong> be set <strong>before</strong>
120
     * the instance is added to a container (the add will
121
     * happen on <code>addNotify()</code>)*/
122
    public void setComponent (JComponent c) {
123
        if (c == comp) return;
124
        if ((comp != null) && (comp.getParent() == this)) {
125
            remove (comp);
126
        }
127
        comp = c;
128
        if ((c != null) && (this != RENDERER_INSTANCE)){
129
            c.setBackground(getBackground());
130
            c.setForeground(getForeground());
131
            c.setEnabled (enabled);
132
        }
133
    }
134
    
135
    public void setBackground(Color c) {
136
        super.setBackground (c);
137
        if (button != null) {
138
            button.setBackground(c);
139
        }
140
    }
141
    
142
    public void setForeground(Color c) {
143
        super.setForeground (c);
144
    }
145
    /** Get the component currently assigned as the real editor
146
     *  embedded in this component.  While not strictly necessary,
147
     *  this is useful if there are issues with focus bugs stemming
148
     *  from specific component types which need to be handled by
149
     *  the parent table.   */
150
    public JComponent getComponent () {
151
        return comp;
152
    }
153
    
154
    private boolean buttonVisible=false;
155
    public void setButtonVisible(boolean value, Action a) {
156
        if (value == buttonVisible) {
157
            if (a != null) {
158
            }
159
            return;
160
        }
161
        if (value) {
162
            installButton(a);
163
        } else {
164
            remove (customEditorButton);
165
        }
166
        buttonVisible = value;
167
        //assign the custom editor action to ctrl-space
168
        getActionMap().put (editorActionKey, a);
169
    }
170
    
171
    /**Install the component specified by setComponent(), as a 
172
     * child of this component. */
173
    private void installComponent () {
174
        comp.setBounds (0, 0, getWidth() - buttonWidth,  getHeight());
175
        add (comp);
176
    }
177
    
178
    /** The paint method is overridden as follows:  if <code>
179
     *  getParent() instanceof CellRendererPane</code>,
180
     *  this component is being used as a renderer, so it will simply
181
     *  paint the component (the real property renderer) and the 
182
     *  button into its graphics context;  the component is not
183
     *  actually added to this component to do this. <P>
184
     *  If not, the component
185
     *  is being used as an editor, and it will call the super 
186
     *  method to paint the children of the component.  In the case
187
     *  that it is a renderer, the component will be painted
188
     *  <strong>with</strong> the custom property editor button 
189
     *  regardless of the status
190
     *  of whether <code>setButtonVisible</code> has been called -
191
     *  sheet code will not use this component as a renderer unless
192
     *  the button is needed.*/
193
    public void paint (Graphics g) {
194
        if (buttonWidth == -1) calcButtonDims(g);
195
        int buttonLeft = buttonVisible ? getWidth() - buttonWidth : getWidth(); 
196
        //Use optimized painting code if we're the default renderer component
197
        if ((this == RENDERER_INSTANCE) && (comp != null)) {
198
            Graphics buttonGraphics = g.create(buttonLeft, 0, buttonWidth, getHeight());
199
            customEditorButton.setBounds (buttonLeft, 0, buttonWidth, getHeight());
200
            customEditorButton.setBackground(getBackground());
201
            customEditorButton.paint (buttonGraphics);
202
            //We can guarantee the following classes will render 
203
            //correctly without a parent.  No way to know this for 
204
            //ad-hoc components supplied by properties or what-have-you,
205
            //which is unfortunate since addNotify() on said could do
206
            //unknown amounts of unnecesary work.  So we add it whether
207
            //we really need to or not.
208
            boolean added;
209
            
210
            if (comp instanceof SheetCellRenderer.StringRenderer ||
211
                comp instanceof SheetCellRenderer.CheckboxRenderer ||
212
                comp instanceof SheetCellRenderer.RadioButtonRenderer) {
213
                    added = false;
214
            } else {
215
                this.add (comp);
216
                added = true;
217
            }
218
                    
219
            //allocating memory inside a paint loop is pure evil, but unavoidable here
220
            //Note we're intentionally avoiding SwingUtilities.paintComponent()
221
            //here - it can cause cyclical component adds and throw an 
222
            //exception that the component's parent is being added to the component.
223
            //For components that can paint without a parent, this is more
224
            //efficient anyway
225
            if (added) {
226
                comp.setBounds (0,0,buttonLeft, getHeight());
227
                super.paint(g);
228
                this.remove (comp);
229
            } else {
230
                Graphics editorGraphics = g.create(0, 0, buttonLeft - 1, 
231
                    getHeight());
232
                LayoutManager lm = comp.getLayout();
233
                comp.setSize (buttonLeft-1, getHeight());
234
                if (lm != null) {
235
                    lm.layoutContainer(comp);
236
                }
237
                comp.paint (editorGraphics);
238
                editorGraphics.dispose();
239
            }
240
        } else {
241
            //if not, we're a container for a real cell editor and should use
242
            //standard component behaviour.  Ensure the custom editor button's
243
            //position and do the usual
244
            button.setBounds (getWidth() - buttonWidth, 0, buttonWidth, 
245
                getHeight());
246
            button.setBackground (getBackground());
247
            button.setForeground (getForeground());
248
            button.setIcon(cuIcon);
249
            super.paint (g);
250
        }
251
    }
252
    
253
    /** Overridden to force focus requests to the contained editor 
254
     *  component - setting focus to this component directly will
255
     *  never be desirable. */
256
    public void requestFocus () {
257
        if (comp != null) {
258
            comp.requestFocus();
259
        }
260
    }
261
    
262
    /** Utility getter for the width of the custom editor
263
     *  button.  This is used when the property sheet needs to 
264
     *  produce a tooltip or action for a rendered button, but
265
     *  does not want to instantiate an inplace editor to 
266
     *  simulate these things.  */
267
    public static final int getButtonWidth () {
268
        return customEditorButton.getWidth();
269
    }
270
    
271
    boolean enabled = true;
272
    /** Overridden to forward the setEnabled call to the contained
273
     *   component - the custom editor button should always be 
274
     *   enabled if present */
275
    public void setEnabled (boolean val) {
276
        if (comp != null) {
277
            comp.setEnabled (val);
278
            button.setEnabled (true);
279
        }
280
        enabled = val;
281
    }
282
    
283
    public void addFocusListener (FocusListener l) {
284
        if (comp != null) {
285
            customEditorButton.addFocusListener (l);
286
            comp.addFocusListener (l);
287
        }
288
    }
289
    
290
    public void removeFocusListener (FocusListener l) {
291
        if (comp != null) {
292
            customEditorButton.removeFocusListener (l);
293
            comp.removeFocusListener (l);
294
        }
295
    }
296
    
297
    private class PanelTraversalPolicy extends FocusTraversalPolicy {
298
        
299
        public Component getComponentAfter(Container focusCycleRoot, Component aComponent) {
300
            return getComponentBefore (focusCycleRoot, aComponent);
301
        }
302
        
303
        public Component getComponentBefore(Container focusCycleRoot, Component aComponent) {
304
            if (aComponent == comp) {
305
                return button;
306
            } else {
307
                return comp;
308
            }
309
        }
310
        
311
        public Component getDefaultComponent(Container focusCycleRoot) {
312
            if (comp != null) return comp;
313
            return button;
314
        }
315
        
316
        public Component getFirstComponent(Container focusCycleRoot) {
317
            if (comp != null) return comp;
318
            return button;
319
        }
320
        
321
        public Component getLastComponent(Container focusCycleRoot) {
322
            return button;
323
        }
324
        
325
        public Component getInitialComponent (Container focusCycleRoot) {
326
            return getDefaultComponent (null);
327
        }
328
    }
329
    
330
    private static class BpIcon implements Icon {
331
        boolean larger;
332
        public BpIcon () {
333
            Font f = UIManager.getFont("Table.font"); //NOI18N
334
            larger = f != null ? f.getSize() > 13 : false;
335
        }
336
        
337
        public int getIconHeight() {
338
            return buttonWidth;
339
        }
340
        
341
        public int getIconWidth() {
342
            return buttonWidth;
343
        }
344
        
345
        public void paintIcon(Component c, Graphics g, int x, int y) {
346
            int w = c.getWidth();
347
            int h = c.getHeight();
348
            int ybase = h-5;
349
            
350
            int pos2 = (w/2);// + (w % 2 == 0 ? 0 : 1); 
351
            int pos1 = pos2 - 4;
352
            int pos3 = pos2 + 4;
353
            g.setColor (c.getForeground());
354
            drawDot (g, pos1, ybase, larger);
355
            drawDot (g, pos2, ybase, larger);
356
            drawDot (g, pos3, ybase, larger);
357
        }
358
        
359
        private void drawDot (Graphics g, int x, int y, boolean larger) {
360
            if (!larger) {
361
                g.drawLine(x, y, x, y);
362
            } else{
363
                g.drawLine(x-1, y, x+1, y);
364
                g.drawLine(x, y-1, x, y+1);
365
            }
366
        }
367
        
368
    }
369
    
370
}
(-)src/org/openide/explorer/propertysheet/CheckboxInplaceEditor.java (+114 lines)
Added Link Here 
1
/*
2
 *                 Sun Public License Notice
3
 * 
4
 * The contents of this file are subject to the Sun Public License
5
 * Version 1.0 (the "License"). You may not use this file except in
6
 * compliance with the License. A copy of the License is available at
7
 * http://www.sun.com/
8
 * 
9
 * The Original Code is NetBeans. The Initial Developer of the Original
10
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun
11
 * Microsystems, Inc. All Rights Reserved.
12
 */
13
/*
14
 * BooleanInplaceEditor.java
15
 *
16
 * Created on January 4, 2003, 4:28 PM
17
 */
18
19
package org.openide.explorer.propertysheet;
20
import java.util.*;
21
import java.beans.*;
22
import java.awt.event.*;
23
import javax.swing.event.*;
24
import javax.swing.*;
25
import org.openide.explorer.propertysheet.*;
26
import org.openide.nodes.Node.*;
27
/** A basic property-editor-aware JCheckbox that updates
28
 *  the property appropriately.  Note that the property sheet
29
 *  implementation never instantiates an inplace editor for
30
 *  booleans, but toggles their state on the editing trigger.
31
 *  Nonetheless, for persistent components, this class is
32
 *  useful.
33
 *  @author Tim Boudreau
34
 */
35
class CheckboxInplaceEditor extends JCheckBox implements InplaceEditor {
36
    
37
    private PropertyEditor editor = null;
38
    
39
    public CheckboxInplaceEditor() {
40
        setActionCommand(COMMAND_SUCCESS);
41
    }
42
    
43
    public void connect(PropertyEditor p, PropertyEnv env) {
44
        if (editor == p) return;
45
        editor = p;
46
        reset();
47
    }
48
    
49
    public void removeNotify() {
50
        super.removeNotify();
51
        clear();
52
    }
53
    
54
    public void clear() {
55
        editor = null;
56
        pm=null;
57
    }
58
    
59
    public JComponent getComponent() {
60
        return this;
61
    }
62
    
63
    public Object getValue() {
64
        return isSelected() ?  Boolean.TRUE : Boolean.FALSE;
65
    }
66
    
67
    public void reset() {
68
        if (editor instanceof PropUtils.NoPropertyEditorEditor) {
69
            //only happens in platform use case
70
            return;
71
        }
72
        if (editor != null) {
73
            Boolean value = (Boolean) editor.getValue();
74
            setSelected(value.booleanValue());
75
            setText(value.toString());
76
        }
77
    }
78
    
79
    public KeyStroke[] getKeyStrokes() {
80
        return null;
81
    }
82
    
83
    public PropertyEditor getPropertyEditor () {
84
        return editor;
85
    }
86
    
87
    public void handleInitialInputEvent(InputEvent e) {
88
        if (e instanceof MouseEvent) {
89
            processMouseEvent ((MouseEvent) e);
90
        }
91
    }    
92
    
93
    public void setValue(Object o) {
94
        //do nothing
95
    }
96
    
97
    public boolean supportsTextEntry() {
98
        return false;
99
    }
100
    
101
    private PropertyModel pm = null;
102
    public PropertyModel getPropertyModel() {
103
        return pm;
104
    }
105
    
106
    public void setPropertyModel(PropertyModel pm) {
107
        this.pm = pm;
108
    }
109
110
    public boolean isKnownComponent(java.awt.Component c) {
111
        return false;
112
    }    
113
    
114
}
(-)src/org/openide/explorer/propertysheet/ColumnManager.java (-157 lines)
Removed Link Here 
1
/*
2
 *                 Sun Public License Notice
3
 * 
4
 * The contents of this file are subject to the Sun Public License
5
 * Version 1.0 (the "License"). You may not use this file except in
6
 * compliance with the License. A copy of the License is available at
7
 * http://www.sun.com/
8
 * 
9
 * The Original Code is NetBeans. The Initial Developer of the Original
10
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2000 Sun
11
 * Microsystems, Inc. All Rights Reserved.
12
 */
13
14
15
package org.openide.explorer.propertysheet;
16
17
18
import java.awt.Component;
19
import java.awt.Container;
20
import java.awt.Dimension;
21
import java.awt.Insets;
22
import java.awt.LayoutManager;
23
import java.io.Serializable;
24
25
26
/**
27
 * Column layout is used to layout components in a <code>NamesPanel</code>.
28
 *
29
 * @author     Jan Jancura
30
 * @version    1.14
31
 */
32
class ColumnManager implements LayoutManager, Serializable {
33
    /** generated Serialized Version UID */
34
    static final long serialVersionUID = -5706896066699438744L;
35
36
    /** If size of this CM depends on the other CM size, there is link on it. */
37
    private ColumnManager columnManager;
38
39
    /** One components height. All the components has the same. */
40
    private int height;
41
    
42
    private static float CRUCIAL_RATIO = (float)30/100;
43
44
45
    /**
46
     * Constructs a new ColumnManager.
47
     */
48
    public ColumnManager () {
49
        this (null);
50
    }
51
52
    /**
53
     * Constructs a new ColumnManager.
54
     */
55
    public ColumnManager(LayoutManager manager) {
56
        if(manager instanceof ColumnManager) {
57
            this.columnManager = (ColumnManager)manager;
58
        }
59
    }
60
61
    /**
62
     * Adds the specified component to the layout.
63
     *
64
     * @param <CODE>String position</CODE> the name of the position of the component
65
     * @param <CODE>Component component</CODE> the the component to be added
66
     */
67
    public void addLayoutComponent (String position, Component component) {
68
    }
69
70
    /**
71
     * Removes the specified component from the layout.
72
     *
73
     * @param <CODE>Component component</CODE> the component to remove.
74
     */
75
    public void removeLayoutComponent (Component component) {
76
    }
77
78
    /**
79
     * Returns the preferred dimensions for this layout given the components
80
     * in the specified target container.
81
     *
82
     * @param <CODE>Container target</CODE> The container which needs to be laid out.
83
     * @see java.awt.Container
84
     * @see #minimumLayoutSize
85
     */
86
    public Dimension preferredLayoutSize (Container target) {
87
        int k = target.getComponentCount ();
88
        if (k < 1) {
89
            return new Dimension (1, 1);
90
        }
91
        
92
        int width = 1;
93
        if (columnManager != null) {
94
            height = columnManager.getComponentHeight ();
95
        } else {
96
            height = target.getComponent (0).getPreferredSize ().height;
97
            //calculate the width by finding the largest button
98
            for (int i=0; i < k; i++) {
99
                width = Math.max (width, target.getComponent (i).getPreferredSize ().width);
100
            }
101
            int wholeWidth = target.getParent ().getWidth ();
102
            int crucialWidth = (int)(wholeWidth*CRUCIAL_RATIO);
103
            // forces Names part of property sheet be min.30% and max.70%
104
            width = Math.max (Math.min (width, wholeWidth-crucialWidth), crucialWidth);
105
        }
106
        
107
        return new Dimension (width, height * k);
108
    }
109
110
    /**
111
     * Returns component height.
112
     *
113
     * @return Component height.
114
     */
115
    public int getComponentHeight () {
116
        return height;
117
    }
118
119
    /**
120
     * Returns the minimum dimensions needed to layout the components
121
     * contained in the specified target container.
122
     *
123
     * @param </CODE>Container target</CODE> The container which needs to be laid out.
124
     * @see #preferredLayoutSize
125
     */
126
    public Dimension minimumLayoutSize (Container target) {
127
        return preferredLayoutSize (target);
128
    }
129
130
    /**
131
     * Lays out the container. This method will actually reshape the
132
     * components in the target in order to satisfy the constraints of
133
     * the BorderLayout object.
134
     *
135
     * @param <CODE>Component target</CODE> The specified container being laid out.
136
     * @see java.awt.Container
137
     */
138
    public void layoutContainer (Container target) {
139
        if (target.getComponentCount () < 1) return;
140
        Insets insets = target.getInsets ();
141
        int compHeight,
142
        k = target.getComponentCount (),
143
            y = 0,
144
                width = target.getSize ().width - (insets.left + insets.right);
145
146
        if (columnManager != null) {
147
            compHeight = columnManager.getComponentHeight ();
148
        } else {
149
            compHeight = target.getComponent (0).getPreferredSize ().height;
150
        }
151
152
        for (int i = 0; i < k; i++) {
153
            target.getComponent (i).setBounds (0, y, width, compHeight);
154
            y += compHeight;
155
        }
156
    }
157
}
(-)src/org/openide/explorer/propertysheet/ComboInplaceEditor.java (+285 lines)
Added Link Here 
1
/*
2
 *                 Sun Public License Notice
3
 * 
4
 * The contents of this file are subject to the Sun Public License
5
 * Version 1.0 (the "License"). You may not use this file except in
6
 * compliance with the License. A copy of the License is available at
7
 * http://www.sun.com/
8
 * 
9
 * The Original Code is NetBeans. The Initial Developer of the Original
10
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun
11
 * Microsystems, Inc. All Rights Reserved.
12
 */
13
/* 
14
 * ComboInplaceEditor.java
15
 *
16
 * Created on January 4, 2003, 4:29 PM
17
 */
18
19
package org.openide.explorer.propertysheet;
20
import java.awt.Color;
21
import java.awt.Component;
22
import java.awt.event.*;
23
import java.beans.*;
24
import java.util.*;
25
import javax.swing.event.*;
26
import javax.swing.*;
27
import javax.swing.border.*;
28
import javax.swing.text.JTextComponent;
29
import org.openide.explorer.propertysheet.*;
30
import org.openide.explorer.propertysheet.editors.EnhancedPropertyEditor;
31
import org.openide.nodes.Node.*;
32
33
/** JComboBox implementation of the InplaceEditor interface.
34
 *  @author Tim Boudreau
35
 */
36
class ComboInplaceEditor extends JComboBox implements InplaceEditor { 
37
    /** Some keystrokes need to be managed so the editor won't accidentally
38
     *  cause its own self-destruction.  */
39
    private static final KeyStroke[] cbKeyStrokes =
40
        new KeyStroke[] {KeyStroke.getKeyStroke(KeyEvent.VK_DOWN,0,false), 
41
                         KeyStroke.getKeyStroke(KeyEvent.VK_UP,0,false),
42
                         KeyStroke.getKeyStroke(KeyEvent.VK_DOWN,0,true), 
43
                         KeyStroke.getKeyStroke(KeyEvent.VK_UP,0,true),                         
44
                         KeyStroke.getKeyStroke (KeyEvent.VK_PAGE_DOWN,0,false), 
45
                         KeyStroke.getKeyStroke (KeyEvent.VK_PAGE_UP,0,false),
46
                         KeyStroke.getKeyStroke (KeyEvent.VK_PAGE_DOWN,0,true), 
47
                         KeyStroke.getKeyStroke (KeyEvent.VK_PAGE_UP,0,true)
48
                        };
49
    
50
    private PropertyEditor editor = null;
51
    
52
    boolean connecting = false;
53
    
54
    
55
    public ComboInplaceEditor() {
56
        super(new ComboModel());
57
        putClientProperty("JComboBox.isTableCellEditor", Boolean.TRUE);
58
        setActionCommand(COMMAND_SUCCESS);
59
        setUI (PropUtils.createComboUI(this));
60
        setBorder (BorderFactory.createEmptyBorder (0, 3, 0,0));
61
    }
62
    
63
    public void connect(PropertyEditor pe, PropertyEnv env) {
64
        connecting = true;
65
        if (editor == pe) return;
66
        editor = pe;
67
        ((ComboModel) getModel()).setPropertyEditor(pe);
68
        
69
        //Don't know if I really want to support this, but useful for testing.
70
        if (pe instanceof EnhancedPropertyEditor) {
71
            this.setEditable(
72
                ((EnhancedPropertyEditor) pe).supportsEditingTaggedValues());
73
        } else {
74
            if (env != null) {
75
                boolean editable = Boolean.TRUE.equals(
76
                    env.getFeatureDescriptor().getValue("canEditAsText"));
77
                this.setEditable (false);
78
            }
79
        }
80
        Object o = editor.getAsText();
81
        if (o == null) {
82
            o = editor.getValue();
83
        }
84
        setSelectedItem (o);
85
        connecting = false;
86
        reset();
87
    }
88
    
89
    public Object getSelectedItem() {
90
        if (connecting) return null;
91
        Object o = getModel().getSelectedItem();
92
        if (editor != null) {
93
            return editor.getAsText();
94
        }
95
        return o;
96
    }
97
    
98
    public void contentsChanged(ListDataEvent lde) {
99
        if (connecting) {
100
            return;
101
        }
102
        super.contentsChanged(lde);
103
    }
104
    
105
    protected void fireActionEvent() {
106
        if (connecting) return;
107
        //Preemptively hide the popup - looks more responsive - if the code
108
        //to write the property is slow, there could be an unresponsive
109
        //looking delay.
110
        if (this.isPopupVisible()) hidePopup();
111
        if ("comboBoxEdited".equals (getActionCommand())) {
112
            setActionCommand (COMMAND_SUCCESS);
113
        }
114
        super.fireActionEvent();
115
    }
116
    
117
    
118
    boolean needLayout=false;
119
    public void addNotify () {
120
        super.addNotify();
121
        needLayout = true;
122
    }
123
    
124
    public void removeNotify() {
125
        hidePopup();
126
        super.removeNotify();
127
    }
128
129
    public void processKeyEvent(KeyEvent e) {
130
        //XXX hacking handling a few keystrokes for now;  eventually
131
        //should be handled via inputMap/actionMap
132
        int code = e.getKeyCode();
133
        if (code == KeyEvent.VK_ESCAPE) {
134
            setActionCommand(COMMAND_FAILURE);
135
            fireActionEvent();
136
            return;
137
        } else if ((code == KeyEvent.VK_SPACE) || (code == KeyEvent.VK_ENTER)){
138
            if ((PropUtils.noCustomButtons) && !isPopupVisible()) {
139
                //If we are not showing custom editor buttons, spacebar should
140
                //open the popupp, it will not be opened by default.
141
                showPopup();
142
                return;
143
            } else {
144
                //somehow, pressing space with no selection sets value to null
145
                setActionCommand(this.isPopupVisible() 
146
                    && getSelectedItem() != null ? 
147
                    COMMAND_SUCCESS : COMMAND_FAILURE);
148
            }
149
        }
150
        setActionCommand(COMMAND_SUCCESS);
151
        super.processKeyEvent(e);
152
    }
153
    
154
    public void clear() {
155
        editor = null;
156
        ((ComboModel) getModel()).clear();
157
        pm=null;
158
        editorComp=null;
159
    }
160
    
161
    /** Overridden because occasionally a selection can
162
     *  be set after the model has been cleared, but the
163
     *  model must not fire a model change when it is
164
     *  cleared or the edited value will be set to null. */
165
    public void setSelectedIndex (int idx) {
166
        ComboModel mdl = (ComboModel) getModel();
167
        if (mdl.editor == null) return;
168
        super.setSelectedIndex (idx);
169
    }
170
    
171
    public JComponent getComponent() {
172
        return this;
173
    }
174
    
175
    public Object getValue() {
176
        return getModel().getSelectedItem();
177
    }
178
    
179
    public void reset() {
180
        ((ComboModel) getModel()).reset();
181
    }
182
    
183
    public KeyStroke[] getKeyStrokes() {
184
        return cbKeyStrokes;
185
    }
186
    
187
    public PropertyEditor getPropertyEditor() {
188
        return editor;
189
    }
190
    
191
    public void handleInitialInputEvent(InputEvent e) {
192
        //do nothing
193
    }
194
195
    protected void installAncestorListener() {
196
        //Do nothing, so moving this component to another cell
197
        //doesn't hide the popup
198
    }
199
    
200
    public void paint (java.awt.Graphics g) {
201
        if (needLayout) {
202
            //force re-layout, otherwise combobox will retain size from
203
            //when it was resized to accomodate a custom editor button
204
            this.getLayout().layoutContainer(this);
205
        }
206
        super.paint (g);
207
    }
208
    
209
    public void setValue(Object o) {
210
        if (isEditable()) {
211
            this.setSelectedItem(o);
212
        }
213
    }
214
    
215
    public boolean supportsTextEntry() {
216
        return isEditable();
217
    }
218
    
219
    private PropertyModel pm = null;
220
    public PropertyModel getPropertyModel() {
221
        return pm;
222
    }
223
    
224
    public void setPropertyModel(PropertyModel pm) {
225
        this.pm = pm;
226
    }
227
    
228
    JTextField editorComp=null;
229
    public void configureEditor(ComboBoxEditor anEditor, Object anItem) {
230
        //used to set up the inplace text editor in the case that isEditable() is true
231
        anEditor.setItem(anItem);
232
        if (anEditor.getEditorComponent() instanceof JTextField) {
233
            editorComp = (JTextField) anEditor.getEditorComponent();
234
            Object o = getValue();
235
            if (o instanceof String) {
236
                editorComp.setText ((String) o);
237
                editorComp.setSelectionStart(0);
238
                editorComp.setSelectionEnd (((String) o).length());
239
            }
240
            editorComp.addActionListener (
241
                new ActionListener() {
242
                public void actionPerformed (ActionEvent ae) {
243
                    if ("comboBoxEdited".equals (ae.getActionCommand())) {
244
                        ComboInplaceEditor.this.setActionCommand (COMMAND_SUCCESS);
245
                        JTextField comp = (JTextField) ae.getSource();
246
                        setValue (comp.getText());
247
                        comp.removeActionListener (this);
248
                        ComboInplaceEditor.this.fireActionEvent();
249
                    }
250
                }
251
            });
252
            editorComp.addFocusListener (
253
                new FocusListener() {
254
                public void focusGained (FocusEvent fe) {
255
                    //do nothing
256
                }
257
                
258
                public void focusLost (FocusEvent fe) {
259
                    Component c = fe.getOppositeComponent();
260
                    if ((c != ComboInplaceEditor.this) && 
261
                        (!ComboInplaceEditor.this.isAncestorOf (c))) {
262
                        ComboInplaceEditor.this.setActionCommand (COMMAND_FAILURE);
263
                        ((JTextField) fe.getSource()).removeFocusListener(this);
264
                        ComboInplaceEditor.this.fireActionEvent();
265
                    }
266
                }
267
                
268
            });
269
        }
270
        ((JComponent) anEditor.getEditorComponent()).setBorder (
271
            BorderFactory.createCompoundBorder (
272
                BorderFactory.createLineBorder (getForeground()),
273
                BorderFactory.createEmptyBorder (0,2,0,0)
274
        ));
275
        setBorder (null);
276
    }
277
278
    public boolean isKnownComponent(Component c) {
279
        if (isEditable()) {
280
            return c == getEditor().getEditorComponent();
281
        } else {
282
            return false;
283
        }
284
    }
285
}
(-)src/org/openide/explorer/propertysheet/ComboModel.java (+139 lines)
Added Link Here 
1
/*
2
 *                 Sun Public License Notice
3
 * 
4
 * The contents of this file are subject to the Sun Public License
5
 * Version 1.0 (the "License"). You may not use this file except in
6
 * compliance with the License. A copy of the License is available at
7
 * http://www.sun.com/
8
 * 
9
 * The Original Code is NetBeans. The Initial Developer of the Original
10
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun
11
 * Microsystems, Inc. All Rights Reserved.
12
 */
13
/*
14
 * ComboBox.java
15
 *
16
 * Created on January 4, 2003, 4:33 PM
17
 */
18
package org.openide.explorer.propertysheet;
19
import java.util.*;
20
import java.beans.*;
21
import java.awt.event.*;
22
import javax.swing.event.*;
23
import javax.swing.*;
24
import org.openide.explorer.propertysheet.*;
25
import org.openide.nodes.Node.*;
26
/** A reusable model for representing a list of property editor tags as a ComboBoxModel
27
 *  @author Tim Boudreau
28
*/
29
class ComboModel implements ComboBoxModel {
30
    
31
    Object item = null;
32
    
33
    PropertyEditor editor = null;
34
    
35
    /** A reusable event.  No need for specific
36
     *  indexes, we're not expecting the list of available tags
37
     *  on a property to change while the property editor is
38
     *  open, that would be way out of scope.     */
39
    private final ListDataEvent evt = new ListDataEvent(this, 0, 0, 0) {
40
        public int getIndex0() {
41
            return 0;
42
        }
43
        public int getIndex1() { 
44
            return 0;
45
        }
46
        public int getType() {
47
            return CONTENTS_CHANGED;
48
        }
49
    };
50
    
51
    /** Utility field used by event firing mechanism.  */
52
    private javax.swing.event.EventListenerList listenerList = null;
53
    
54
    /** Utility field holding list of ListDataListeners.  */
55
    private transient java.util.ArrayList listDataListenerList;
56
    
57
    public void setPropertyEditor(PropertyEditor ed) {
58
        if (editor != ed) {
59
            editor = ed;
60
            item = ed.getAsText();
61
            fireContentsChanged();
62
            reset();
63
        }
64
    }
65
    
66
    public void reset() {
67
        item = editor==null?null:editor.getValue();
68
        tags = null;
69
    }
70
    
71
    public void clear() {
72
        editor = null;
73
        item=null;
74
        tags = null;
75
    }
76
77
    Object[] tags = null;
78
    /** Tag caching - the tags are looked up ahead of
79
     *  time and stored, for performance (multiple calls to
80
     *  getTags() on org.netbeans.beaninfo.ObjectEditor can take
81
     *  several minutes[!]).  */
82
    private void fetchTags() {
83
        if (editor == null) tags = new Object[]{};
84
        tags = editor.getTags();
85
    }
86
    
87
    //XXX when tag caching is removed, also remove call to
88
    //clear() in SheetCellEditor.removeCellEditorListener()
89
    
90
    private Object[] getTags() {
91
        if (tags == null) fetchTags();
92
        return tags;
93
    }
94
    
95
    public Object getElementAt(int index) {
96
        if (editor != null) {
97
            Object result = getTags()[index];
98
            return result;
99
        }
100
        return null;
101
    }
102
    
103
    public Object getSelectedItem() {
104
        return item;
105
    }
106
    
107
    public int getSize() {
108
        if (editor != null)
109
            return getTags().length;
110
        return 0;
111
    }
112
    
113
    public void setSelectedItem(Object anItem) {
114
        item = anItem;
115
        //fireContentsChanged();
116
    }
117
    
118
    public synchronized void fireContentsChanged() {
119
        if (listDataListenerList == null) return;
120
        int max = listDataListenerList.size();
121
        for (int i=0; i < max; i++) {
122
            ListDataListener listener = (ListDataListener) listDataListenerList.get(i);
123
            listener.contentsChanged(evt);
124
        }
125
    }
126
    
127
    public synchronized void addListDataListener(javax.swing.event.ListDataListener listener) {
128
        if (listDataListenerList == null ) {
129
            listDataListenerList = new java.util.ArrayList();
130
        }
131
        listDataListenerList.add(listener);
132
    }
133
    
134
    public synchronized void removeListDataListener(javax.swing.event.ListDataListener listener) {
135
        if (listDataListenerList != null ) {
136
            listDataListenerList.remove(listener);
137
        }
138
    }
139
}
(-)src/org/openide/explorer/propertysheet/DefaultPropertyModel.java (-1 / +2 lines)
 Lines 31-37    Link Here 
31
    private Object bean;
31
    private Object bean;
32
32
33
    /** Name of the property of the bean. */
33
    /** Name of the property of the bean. */
34
    private String propertyName;
34
    String propertyName; //package private so error handling code can pick up 
35
      //the property name if the user enters an invalid value
35
36
36
    /** support for the properties changes. */
37
    /** support for the properties changes. */
37
    private PropertyChangeSupport support;
38
    private PropertyChangeSupport support;
(-)src/org/openide/explorer/propertysheet/DescriptionPanel.java (+238 lines)
Added Link Here 
1
/*
2
 *                 Sun Public License Notice
3
 * 
4
 * The contents of this file are subject to the Sun Public License
5
 * Version 1.0 (the "License"). You may not use this file except in
6
 * compliance with the License. A copy of the License is available at
7
 * http://www.sun.com/
8
 * 
9
 * The Original Code is NetBeans. The Initial Developer of the Original
10
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun
11
 * Microsystems, Inc. All Rights Reserved.
12
 */
13
/*
14
 * DescriptionPanel.java
15
 *
16
 * Created on June 25, 2003, 12:19 PM
17
 */
18
19
package org.openide.explorer.propertysheet;
20
21
import java.awt.Container;
22
import java.awt.Dimension;
23
import java.awt.Font;
24
import java.awt.FontMetrics;
25
import java.awt.Graphics;
26
import java.awt.Image;
27
import java.awt.LayoutManager;
28
import java.awt.event.MouseAdapter;
29
import java.awt.event.MouseEvent;
30
import java.util.StringTokenizer;
31
import javax.swing.Action;
32
import javax.swing.BorderFactory;
33
import javax.swing.Icon;
34
import javax.swing.ImageIcon;
35
import javax.swing.JButton;
36
import javax.swing.JLabel;
37
import javax.swing.JPanel;
38
import javax.swing.JTextArea;
39
import javax.swing.UIManager;
40
import javax.swing.border.Border;
41
import javax.swing.plaf.metal.MetalLookAndFeel;
42
import org.openide.util.NbBundle;
43
import org.openide.util.Utilities;
44
45
/** Description panel displayed in the property sheet.
46
 *
47
 * @author  Tim Boudreau
48
 */
49
class DescriptionPanel extends JPanel {
50
    private JTextArea descLabel;
51
    private JLabel titleLabel;
52
    private JButton helpButton;
53
    /** Creates a new instance of DescriptionPanel */
54
    public DescriptionPanel() {
55
        init();
56
        setBorder (BorderFactory.createEmptyBorder (1,7,1,5));
57
    }
58
    
59
    private void init() {
60
        //Set some text so the initial preferred size is accurate
61
        descLabel = new JTextArea(" "); //NOI18N
62
        titleLabel = new JLabel(" "); //NOI18N
63
        
64
        descLabel.setFocusable(false);
65
        descLabel.setBackground(getBackground());
66
        descLabel.setEditable(false);
67
        descLabel.setFont (titleLabel.getFont());
68
        descLabel.setForeground(titleLabel.getForeground());
69
        descLabel.setWrapStyleWord(true);
70
        descLabel.setLineWrap(true);
71
        descLabel.setRows (2);
72
73
        titleLabel.setFont (getFont().deriveFont (Font.BOLD));
74
        
75
        helpButton = new JButton();
76
        helpButton.setName("PropertySheetHelpButton"); //NOI18N
77
        helpButton.setText("");
78
        if (UIManager.getLookAndFeel() instanceof MetalLookAndFeel) {
79
            helpButton.setBorderPainted(false);
80
            //issue 34159 Metal rollover buttons do not use rollover border
81
            helpButton.addMouseListener(new MouseAdapter() {
82
                public void mouseEntered (MouseEvent me) {
83
                    helpButton.setBorderPainted(true);
84
                }
85
                public void mouseExited (MouseEvent me) {
86
                    helpButton.setBorderPainted(false);
87
                }
88
            });
89
        }
90
        helpButton.setContentAreaFilled(false);
91
        helpButton.getAccessibleContext().setAccessibleName(
92
            NbBundle.getMessage(DescriptionPanel.class, "ACS_CTL_Help")); //NOI18N
93
        helpButton.setToolTipText(
94
            NbBundle.getMessage(DescriptionPanel.class, "CTL_Help")); //NOI18N
95
        helpButton.getAccessibleContext().setAccessibleDescription(
96
            NbBundle.getMessage(DescriptionPanel.class, "ACSD_CTL_Help")); //NOI18N        
97
        
98
        //set names to help unit tests
99
        helpButton.setName("PropertySheetHelpButton"); //NOI18N
100
        setName("Property sheet description panel"); //NOI18N
101
        descLabel.setName("Property sheet description label"); //NOI18N
102
        titleLabel.setName("Property sheet description title label"); //NOI18N
103
104
        descLabel.getAccessibleContext().setAccessibleName(
105
            NbBundle.getMessage(DescriptionPanel.class,
106
            "ACS_Description")); //NOI18N
107
        
108
        descLabel.getAccessibleContext().setAccessibleDescription(
109
            NbBundle.getMessage(DescriptionPanel.class,
110
            "ACSD_Description")); //NOI18N
111
        
112
        titleLabel.getAccessibleContext().setAccessibleName(
113
            NbBundle.getMessage(DescriptionPanel.class,
114
            "ACS_DescriptionTitle")); //NOI18N
115
        
116
        titleLabel.getAccessibleContext().setAccessibleDescription(
117
            NbBundle.getMessage(DescriptionPanel.class,
118
            "ACSD_DescriptionTitle")); //NOI18N
119
120
        Image help = Utilities.loadImage(
121
            "org/openide/resources/propertysheet/propertySheetHelp.gif"); //NOI18N
122
        
123
        ImageIcon helpIcon = new ImageIcon(help); //NOI18N
124
        helpButton.setIcon(helpIcon);
125
        
126
        Border b = BorderFactory.createEmptyBorder (0,2,0,0);
127
        titleLabel.setBorder(b);
128
        descLabel.setBorder(b);
129
        
130
        add (titleLabel);
131
        add (descLabel);
132
        add (helpButton);
133
        setLayout (new Layout());
134
    }
135
    
136
    private class Layout implements LayoutManager {
137
        
138
        public void layoutContainer(java.awt.Container parent) {
139
            Icon ic = helpButton.getIcon();
140
            Dimension hSize = new Dimension (ic.getIconWidth()+2, 
141
                ic.getIconHeight()+2);
142
            Dimension tSize = titleLabel.getPreferredSize();
143
            int topRowHeight = Math.max (hSize.height+2, tSize.height);
144
            int w=getWidth();
145
            int h=getHeight();
146
            helpButton.setBounds (w-hSize.width, 0, hSize.width, topRowHeight);
147
            titleLabel.setBounds (0, 0, w-hSize.width, topRowHeight);
148
            descLabel.setBounds (0, topRowHeight, w, h-topRowHeight);
149
        }
150
        
151
        public java.awt.Dimension minimumLayoutSize(Container parent) {
152
            return preferredLayoutSize(parent);
153
        }
154
155
        
156
        public Dimension preferredLayoutSize(Container parent) {
157
            Dimension hSize = helpButton.getMinimumSize();
158
            Dimension tSize = titleLabel.getPreferredSize();
159
            Dimension dSize = descLabel.getPreferredSize();
160
            return new Dimension (Math.max (hSize.width + tSize.width, 
161
                dSize.width), Math.max (hSize.height+2, tSize.height) 
162
                + Math.min(dSize.height, hSize.height-2));
163
        }
164
        
165
        public void removeLayoutComponent(java.awt.Component comp) {
166
            //do nothing
167
        }
168
169
        public void addLayoutComponent(String name, java.awt.Component comp) {
170
            //do nothing
171
        }
172
    }
173
    
174
    public void setDescription (String title, String description) {
175
        titleLabel.setText (title);
176
        descLabel.setText (description);
177
        titleLabel.getAccessibleContext().setAccessibleName(title);
178
        if (titleLabel.getPreferredSize().width > titleLabel.getWidth()) {
179
            titleLabel.setToolTipText(title);
180
        } else {
181
            titleLabel.setToolTipText(null);
182
        }
183
        descLabel.getAccessibleContext().setAccessibleName(description);
184
        if (description.length() > 50) {
185
            descLabel.setToolTipText(createHtmlTooltip(title, description));
186
        } else {
187
            descLabel.setToolTipText(null);
188
        }
189
        revalidate();
190
        repaint();
191
    }
192
    
193
    private String createHtmlTooltip(String title, String s) {
194
        StringTokenizer tk = new StringTokenizer (s, " "); //NOI18N
195
        StringBuffer sb = new StringBuffer (s.length() + 20);
196
        sb.append ("<html>"); //NOI18N
197
        sb.append ("<b><u>"); //NOI18N
198
        sb.append (title);
199
        sb.append ("</u></b><br>"); //NOI18N
200
        int charCount=0;
201
        while (tk.hasMoreTokens()) {
202
            String a = tk.nextToken();
203
            a = Utilities.replaceString(a, "<", "&lt;"); //NOI18N
204
            a = Utilities.replaceString(a, ">", "&gt;"); //NOI18N
205
            charCount += a.length();
206
            sb.append (a);
207
            if (tk.hasMoreTokens()) {
208
                charCount++;
209
            }
210
            if (charCount > 80) {
211
                sb.append ("<br>"); //NOI18N
212
                charCount = 0;
213
            } else {
214
                sb.append (' '); //NOI18N
215
            }
216
        }
217
        sb.append ("</html>"); //NOI18N
218
        return sb.toString();
219
    }
220
    
221
    
222
    
223
    public void setHelpAction (Action a) {
224
        helpButton.setAction (a);
225
        helpButton.setContentAreaFilled(false);
226
        helpButton.getAccessibleContext().setAccessibleName(
227
            NbBundle.getMessage(DescriptionPanel.class, "ACS_CTL_Help")); //NOI18N
228
        helpButton.setToolTipText(
229
            NbBundle.getMessage(DescriptionPanel.class, "CTL_Help")); //NOI18N
230
        helpButton.getAccessibleContext().setAccessibleDescription(
231
            NbBundle.getMessage(DescriptionPanel.class, "ACSD_CTL_Help")); //NOI18N  
232
        Image help = Utilities.loadImage(
233
            "org/openide/resources/propertysheet/propertySheetHelp.gif"); //NOI18N
234
        helpButton.setText(null);
235
        ImageIcon helpIcon = new ImageIcon(help); //NOI18N
236
        helpButton.setIcon(helpIcon);
237
    }
238
}
(-)src/org/openide/explorer/propertysheet/EmptyPanel.java (-59 lines)
Removed Link Here 
1
/*
2
 *                 Sun Public License Notice
3
 * 
4
 * The contents of this file are subject to the Sun Public License
5
 * Version 1.0 (the "License"). You may not use this file except in
6
 * compliance with the License. A copy of the License is available at
7
 * http://www.sun.com/
8
 * 
9
 * The Original Code is NetBeans. The Initial Developer of the Original
10
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2000 Sun
11
 * Microsystems, Inc. All Rights Reserved.
12
 */
13
14
package org.openide.explorer.propertysheet;
15
16
import java.awt.*;
17
18
/**
19
* Empty panel with given text in the center of them.
20
*
21
* @author   Jan Jancura
22
*/
23
final class EmptyPanel extends javax.swing.JPanel {
24
    /** generated Serialized Version UID */
25
    static final long serialVersionUID = -5681425006155127558L;
26
27
    private String text = org.openide.util.NbBundle.getBundle (EmptyPanel.class).getString ("CTL_No_properties");
28
29
    /*
30
    * Creates new panel vith given message.
31
    */
32
    EmptyPanel (String text) {
33
        this.text = text;
34
    }
35
36
    /*
37
    * Standart painting method.
38
    */
39
    public void paintBorder (Graphics g) {
40
        super.paintBorder (g);
41
        Dimension   size = getSize ();
42
        Color       c = g.getColor ();
43
        Color       bc = getBackground ();
44
        FontMetrics fontMetrics = g.getFontMetrics();
45
        g.setColor (bc.brighter ().brighter ());
46
        g.drawString (
47
            text,
48
            (size.width - fontMetrics.stringWidth (text)) / 2,
49
            10 + fontMetrics.getMaxAscent ()
50
        );
51
        g.setColor (bc.darker ());
52
        g.drawString (
53
            text,
54
            (size.width - fontMetrics.stringWidth (text)) / 2 - 1,
55
            10 + fontMetrics.getMaxAscent () - 1
56
        );
57
        g.setColor (c);
58
    }
59
}
(-)src/org/openide/explorer/propertysheet/InplaceEditor.java (+307 lines)
Added Link Here 
1
/*
2
 *                 Sun Public License Notice
3
 * 
4
 * The contents of this file are subject to the Sun Public License
5
 * Version 1.0 (the "License"). You may not use this file except in
6
 * compliance with the License. A copy of the License is available at
7
 * http://www.sun.com/
8
 * 
9
 * The Original Code is NetBeans. The Initial Developer of the Original
10
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun
11
 * Microsystems, Inc. All Rights Reserved.
12
 */
13
/*
14
 * InplaceEditor.java
15
 *
16
 * Created on December 22, 2002, 2:50 PM
17
 */
18
package org.openide.explorer.propertysheet;
19
import java.awt.Component;
20
import java.awt.event.*;
21
import java.beans.PropertyEditor;
22
import java.beans.FeatureDescriptor;
23
import javax.swing.JComponent;
24
import javax.swing.KeyStroke;
25
import org.openide.nodes.Node.Property;
26
/** Interface defining the contract of reusable inline cell editors for
27
 *  properties.  Generally, this interface will be implemented
28
 *  on a component subclass.  Note
29
 *  that such components do not have to be concerned about providing
30
 *  a custom editor button for properties with custom property
31
 *  editors.  If needed, the rendering infrastructure will provide
32
 *  one.
33
 * <P>Inplace editors are designed to be reusable - that is, a single
34
 *  instance may be reconfigured and reused to edit multiple properties
35
 *  over its lifespan.  The <code>connect()</code> and <code>clear()</code>
36
 *  methods provide a means of configuring an instance to represent a
37
 *  property, and then de-configure it when it is no longer needed.  The
38
 *  typical lifecycle of an inplace editor is as follows: <OL><LI>The
39
 *  user clicks a property in the property sheet.</LI><LI>The property
40
 *  sheet identifies the property clicked, and locates the correct
41
 *  inplace editor (either a default one or a custom implementation supplied
42
 *  by the property or property editor).</LI><LI><code>connect()</code> is
43
 *  called to configure the editor</LI><LI>The component returned from
44
 *  <code>getComponent()</code> is displayed on screen and given focus</LI>
45
 *  <LI>The user enters text or otherwise manipulates the component to change
46
 *  the value</LI><LI>When the component determines that the user has
47
 *  either concluded editing (usually pressing Enter) or cancelled editing
48
 *  (pressing Escape), the inplace editor fires <code>ACTION_SUCCESS</code>
49
 *  or <code>ACTION_FAILURE</code></LI><LI>The property sheet detects this
50
 *  action event and removes the editor component</LI><LI>The property sheet
51
 *  updates the property</LI><LI>The property sheet calls <code>clear()</code>
52
 *  to dispose of any state or references held by the inplace editor</LI></OL>
53
 *  <P> This interface contains
54
 *  a means for editor components to process the mouse or keyboard
55
 *  events that caused them to be instantiated.  This is
56
 *  particularly useful for components with popup windows which
57
 *  should immediately display such a popup on instantiation,
58
 *  in order to be presented to the user in the most natural
59
 *  fashion.
60
 *  <P>If you implement this interface to provide a custom inplace
61
 *  editor for a particular property, it is wise to also write a
62
 *  custom PropertyEditor whose <code>paint()</code> method will
63
 *  paint an image identical to what your editor looks like when
64
 *  it is instantiated.  The simplest way to do this is to create
65
 *  a renderer instance of your inplace editor, and use it in the
66
 *  <code>paint()</code> method of your property editor.
67
 *  <P>The methods of this interface should <strong>never</strong>
68
 *  be called from any thread except the AWT event thread.  The backing
69
 *  implementation is not thread-safe.  This includes <code>ActionEvent</code>s
70
 *  fired by instances of <code>InplaceEditor</code>.
71
 *  <P>In no cases should an instance of <code>InplaceEditor</code>
72
 *  attempt to directly update the value of the represented property
73
 *  or property editor.  If the property should be updated, ensure
74
 *  that <code>getValue()</code> will return the correct value, and
75
 *  fire the action command COMMAND_SUCCESS.  Implementations
76
 *  should also not assume that because one of these events has been
77
 *  fired, that therefore the property editor has been updated with
78
 *  the new value.  Components that display inplace editors
79
 *  are responsible for the timing of and policy for updates to the represented
80
 *  properties.  Inplace editors merely display the contents of a property
81
 *  editor, provide a way for the user to edit that value, and notify
82
 *  the infrastructure when the user has made a change. 
83
 * <P>Standard implementations of this interface for text entry, combo
84
 *  boxes and checkboxes are provided by the property sheet infrastructure.
85
 *  There are several ways to provide a custom inplace editor for use in
86
 *  the property sheet:<UL><LI><B>Globally</B> - a module supplying a
87
 *  property editor for a given class may call
88
 *  <code>PropertyEnv.registerInplaceEditorFactory(InplaceEditor.Factory)</code>.
89
 *  When the user invokes an editor operation, the returned inplace editor
90
 *  will be used.</LI><LI><B>On a per-property basis</B> - A
91
 *  <code>Node.Property</CODE> may provide a custom inplace editor via hinting.
92
 *  To do this, the <code>Node.Property</CODE> instance should return an
93
 *  instance of <code>InplaceEditor</code> from <code>getValue
94
 *  (&quot;inplaceEditor&quot;)</code></LI></UL>
95
 *  If both methods are used on the same property, the inplace editor provided
96
 *  by the per-property hint takes precedence.
97
 * @version 1.0
98
 * @author Tim Boudreau
99
 */
100
public interface InplaceEditor {
101
    /** Action command that tells the property sheet to update
102
     *  the property's value with the value from this inplace editor and close
103
     *  the inplace editor.  */
104
    public static final String COMMAND_SUCCESS="success";  //NOI18N
105
    /** Action command that tells the property sheet that editing
106
     *  is completed, but the value should not be updated, the
107
     *  editor should simply be removed.  */
108
    public static final String COMMAND_FAILURE="failure";  //NOI18N
109
    /** Connect this editor with a property editor.  The
110
     *  <code>PropertyEditor</code> instance will already be
111
     *  initialized with the initial value, and if it is an
112
     *  instance of ExPropertyEditor, <code>ExPropertyEditor.attachEnv(env)</code>
113
     *  will already have been called.  The <code>PropertyEnv</code>
114
     *  instance is passed to allow rendering hints to be passed to
115
     *  the <code>InplaceEditor</code> instance. <P> Implementations
116
     *  which may be connected to <code>PropertyEditor</code> instances
117
     *  that do not implement <code>ExPropertyEditor</code> must handle
118
     *  the case that the <code>env</code> property may be null.
119
     * @param pe The property editor
120
     * @param env An instance of PropertyEnv, if the editor is an instance of ExPropertyEditor,
121
     * or null if it is not  */
122
    public void connect (PropertyEditor pe, PropertyEnv env);
123
    /** Returns the physical inplace editor component that should be displayed
124
     *  on-screen.  Typical implementations of this
125
     *  interface are <code>JComponent</code> subclasses which implement this interface
126
     *  and simply return <code>this</code> from this method.  If you
127
     *  implement this interface separately from the inplace editor
128
     *  component, it is expected that the same component instance
129
     *  will be returned from this instance from the first time
130
     *  <code>connect()</code> is called, until such a time as <code>clear()</code>
131
     *  is called.
132
     * @return The component that should be displayed to the user to edit the property  */
133
    public JComponent getComponent();
134
    /** Dispose of any state and references to the property or value being
135
     *  edited, to avoid memory leaks due to held references.  The property display
136
     *  code will call this once an inplace editor component has been closed.  
137
     *  A call to this method should return the inplace editor to the state it
138
     *  is in after its constructor is called. */
139
    public void clear();
140
    /** Returns the value currently displayed or selected in the editor.  This
141
     *  may or may not correspond to the current value of the Property being
142
     *  represented, and may not represent a valid final value for the property,
143
     *  but rather represents the edit in progress. <P>  This method may return
144
     *  a <code>String</code>, in which case the property editor will be updated
145
     *  using its <code>setAsText()</code> method, and the value taken from the
146
     *  property editor.  Implementations are free to also return either null when
147
     *  appropriate, a String appropriate for use with the property editor's 
148
     *  <code>setAsText()</code> method, or an object instance compatible with the property in question's
149
     *  <code>setValue()</code> method.
150
     * @return The value currently shown in the editor component provided by
151
     * <code>getComponent()</code>
152
     */
153
    public Object getValue();
154
    /** Set the value to be <i>displayed</i> in the inplace editor.  Implementations
155
     *  should take care to avoid triggering a property change event in the
156
     *  property editor connected to this inplace editor.  This method is used
157
     *  to restore the partial value of an editor in the case that some
158
     *  external event causes it to be temporarily removed. <P> This method
159
     *  is optional, and primarily useful for editors that support text entry.
160
     *  Editors which do not support text entry may supply an empty implementation
161
     *  of this method. <P> It is required that <code>setValue()</code> for
162
     *  a given <code>InplaceEditor</code> be able to handle any possible
163
     *  type that it can return from <code>getValue()</code>, since it is 
164
     *  used to temporarily cache and then restore the value mid-edit.
165
     * @param o The value that should be displayed in the editor component.  This
166
     *  should be an object the component is capable of displaying.  It may be
167
     *  a String or any other object type, provided the component is capable
168
     *  of displaying it.  This method will only ever be called with a value
169
     *  object supplied from <code>getValue()</code>, so this method should
170
     *  be compatible with anything that <code>getValue()</code> on a given
171
     *  <code>InplaceEditor</code> implementation may return */
172
    public void setValue (Object o);
173
    /** Indicates whether an inplace editor supports the direct entry of text or not.
174
     *  In particular, this method is used to support managing the background
175
     *  color of the editor component.  The default selection color is
176
     *  used by the property sheet to indicate selection in the property sheet.  Editors
177
     *  supporting text entry should not have their background color set to
178
     *  the default selection color, so that the user may distinguish selected 
179
     *  text (which would otherwise have the same background color whether it
180
     *  were selected or not).
181
     * @return True if the editor component supplied by <code>getComponent()</code> supports
182
     * direct text entry by the user. */
183
    public boolean supportsTextEntry ();
184
    /** Restore the inplace editor to the value returned by the property editor's
185
     *  <code>getValue()</code> method,  discarding any edits.
186
     * @throws NullPointerException If called before a call to <code>connect()</code> or after a call to
187
     * <code>clear()</code>, since in that case the property editor is null.
188
     */
189
    public void reset ();
190
    /** Add an action listener to the InplaceEditor.  Note that the
191
     *  source property for ActionEvents fired by an InplaceEditor
192
     *  <strong>must</strong> be an instance of InplaceEditor.  The
193
     *  property sheet infrastructure will recognize two action
194
     *  commands:  <code>COMMAND_SUCCESS</code> and <code>COMMAND_FAILURE</code>.  
195
     *  Other action events
196
     *  (such as may be generated by a component subclass implementing
197
     *  this interface) may be fired, but will be ignored by the
198
     *  property sheet infrastructure.
199
     * @param al The action listener to add   */
200
    public void addActionListener (ActionListener al);
201
    /** Remove an action listener from an InplaceEditor.
202
     * @param al The action listener to remove  */
203
    public void removeActionListener (ActionListener al);
204
    /** Keystrokes that should be ignored by the containing component when
205
     *  this inplace editor is open, even if they are in the <code>InputMap</code>
206
     *  of the container.<P>
207
     *  JTable (and potentially other components) will respond to
208
     *  keystrokes sent to an embedded component.  In particular, this
209
     *  is a problem in JDK 1.4 with embedded JComboBoxes - the down
210
     *  arrow key, used for combo box navigation, also changes the selection
211
     *  and closes the editor.  Since it is not always possible to determine reliably the
212
     *  keystrokes an inplace editor will consume at instantiation
213
     *  time, this allows them to be specified explicitly, so the table
214
     *  knows what to ignore.
215
     * @return The keystrokes a container of the editor component should ignore even if they
216
     * are mapped to actions in it.  */
217
    public KeyStroke[] getKeyStrokes();
218
    /** Some inplace editors will want to use the initial keyboard or mouse event that
219
     *  causes them to be instantiated (e.g., pressing space to instantiate
220
     *  a JComboBox should probably also open said combobox), so the user
221
     *  doesn't have to press space twice on what looks like a combo box
222
     *  (the renderer, then the editor once instantiated) to make it behave
223
     *  like a combo box.  Components without this requirement should simply
224
     *  provide an empty implementation of this method (e.g. pressing space
225
     *  over a string editor should just instantiate it, not cause a space
226
     *  to be inserted in the text of the component). <P>
227
     *  Some components will also want to use the mouse event that
228
     *  causes them to be instantiated to set their state as if they
229
     *  were present for the initial event (e.g. when moving from one open
230
     *  JComboBox editor directly to another via a mouse event).  Generally
231
     *  this is useful for inplace editors with popup windows of some
232
     *  sort, to make sure the component is instantiated in the most
233
     *  useful state possible for the user.
234
     * @param e The input event (a KeyEvent or MouseEvent) which the component should
235
     * process.   */
236
    public void handleInitialInputEvent (InputEvent e);
237
     /** Get the <code>java.beans.PropertyEditor</code>
238
      *  instance associated with this
239
      *  inplace editor.  For efficiency, client code uses
240
      *  this method to cache the property editor being
241
      *  used, rather than perform gratuitous lookups of the
242
      *  property editor on the property it represents.
243
      *  Inplace editor implementations are expected to cache
244
      *  the property editor they are initialized with until <code>clear()</code>
245
      *  is called.
246
      * @return The property editor this InplaceEditor represents   */
247
    public PropertyEditor getPropertyEditor();
248
    /** Inplace editors cache the property model used to update a
249
     *  property value at the conclusion of editing.  After a call to
250
     *  <code>setPropertyModel()</code> this method should return the
251
     *  property model that should be updated with the value from this
252
     *  inplace editor.  After a subsequent call to <code>clear()</code>
253
     *  this method should return null.  <P>  Under no circumstances
254
     *  should an InplaceEditor implementation attempt to modify the
255
     *  property model - this is the job of the infrastructure that
256
     *  instantiated the InplaceEditor.
257
     * @return The property model representing the property being edited   */
258
    public PropertyModel getPropertyModel();
259
    /** Set the property model that should be updated in the event of a
260
     *  change.
261
     * @param pm The property model this inplace editor will represent */
262
    public void setPropertyModel(PropertyModel pm);
263
    /** Returns true if a component is one the inplace editor instantiated.
264
     *  The property sheet tracks focus and will close an inplace editor
265
     *  if focus is lost to an unknown component.  Since inplace editors may
266
     *  instantiate popup components that can receive focus, if focus is
267
     *  lost while an inplace editor is open, the property sheet will query
268
     *  the current inplace editor to ensure that the recipient of focus is
269
     *  truly not a child of the inplace editor.  For most InplaceEditor
270
     *  implementations, it is safe simply to return false from this method.
271
     * @param c A component which has received focus
272
     * @return True if the passed component was instantiated by the inplace editor as part of
273
     * its normal operation (for example, a popup which is not a child of 
274
     * whatever is returned from <code>getComponent()</code>in the component
275
     * hierarchy, but which is effectively part of the editor).  */
276
    public boolean isKnownComponent (Component c);
277
    /** A factory for inplace editor instances.  A module may provide a property
278
     *  editor which provides a custom inplace editor for any properties
279
     *  of the type it edits.  This is accomplished as follows:  <UL><LI>The 
280
     *  property editor must implement <code>ExPropertyEditor</code>.</LI><LI>
281
     *  In the <code>attachEnv()</code> method of that interface, it must call
282
     *  <code>env.registerInplaceEditorFactory()</code>, passing an instance
283
     *  of <code>InplaceEditor.Factory</code>.  If a user attempts to edit
284
     *  the property, the inplace editor returned from <code>Factory.getInplaceEditor()</code>
285
     *  will be used.</LI></UL>
286
     *  <p><strong>A note about using InplaceEditor instances to render properties:</strong>
287
     *  If a custom property editor is, as is encouraged, using an instance of its 
288
     *  InplaceEditor to paint the value rendered in table cells, this method <strong>
289
     *  must not</strong> return the instance being used for rendering - that instance
290
     *  may be reconfigured at any time with a different value in order to paint
291
     *  another cell on the property sheet.   */
292
    public interface Factory {
293
        /** Fetch or create an inplace editor instance.  The system guarantees that
294
         *  there will never be more than one open inplace editor at a time, so it is
295
         *  safe to return the same static instance repeatedly from this method - when the
296
         *  editor is opened, it will be configured for the property it is editing.
297
         *  The optimal approach to implementing this method is to create the editor
298
         *  on the first call, and maintain a reference to it using a static field,
299
         *  so a single instance may be shared, but hold the reference to it using
300
         *  <code>java.lang.ref.WeakReference</code> or
301
         *  <code>java.lang.ref.SoftReference</code>, so that the instance may be
302
         *  garbage collected if it is no longer needed.
303
         * @return An inplace editor instance
304
         */
305
        public InplaceEditor getInplaceEditor ();
306
    }
307
}
(-)src/org/openide/explorer/propertysheet/InplaceEditorFactory.java (+134 lines)
Added Link Here 
1
/*
2
 *                 Sun Public License Notice
3
 * 
4
 * The contents of this file are subject to the Sun Public License
5
 * Version 1.0 (the "License"). You may not use this file except in
6
 * compliance with the License. A copy of the License is available at
7
 * http://www.sun.com/
8
 * 
9
 * The Original Code is NetBeans. The Initial Developer of the Original
10
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun
11
 * Microsystems, Inc. All Rights Reserved.
12
 */
13
/*
14
 * InplaceEditorFactory.java
15
 *
16
 * Created on January 4, 2003, 4:52 PM
17
 */
18
19
package org.openide.explorer.propertysheet;
20
import org.openide.explorer.propertysheet.InplaceEditor;
21
import org.openide.explorer.propertysheet.PropUtils;
22
import org.openide.explorer.propertysheet.editors.EnhancedPropertyEditor;
23
import org.openide.nodes.Node.Property;
24
import java.beans.PropertyEditor;
25
/** Factory providing inplace editor implementations.  Provides appropriate
26
 *  InplaceEditor implementations, depending on the type of the property, the
27
 *  results of PropertyEditor.getTags(), or any hinting provided by the property
28
 *  editor or PropertyEnv to use a custom inplace editor implementation.
29
  * @author  Tim Boudreau
30
  */
31
final class InplaceEditorFactory {
32
    /** Creates a new instance of InplaceEditorFactory */
33
    private InplaceEditorFactory() {
34
    }
35
    
36
    private static InplaceEditor checkbox=null;
37
    private static InplaceEditor text=null;
38
    private static InplaceEditor rbEditor = null;
39
    private static InplaceEditor combo=null;
40
    
41
    static InplaceEditor getComboBoxEditor(boolean newInstance) {
42
        if (newInstance) return new ComboInplaceEditor();
43
        if (combo == null) {
44
            combo = new ComboInplaceEditor();
45
        }
46
        return combo;
47
    }
48
49
    static InplaceEditor getStringEditor(boolean newInstance) {
50
        if (newInstance) return new StringInplaceEditor();
51
        if (text == null) text = new StringInplaceEditor();
52
        return text;
53
    }
54
    
55
    static InplaceEditor getCheckboxEditor(boolean newInstance) {
56
        if (newInstance) return new CheckboxInplaceEditor();
57
        if (checkbox == null) checkbox = new CheckboxInplaceEditor();
58
        return checkbox;
59
    }
60
    
61
    static InplaceEditor getRadioButtonEditor (boolean newInstance) {
62
        if (newInstance) return new RadioButtonEditor();
63
        if (rbEditor == null) rbEditor = new RadioButtonEditor();
64
        return rbEditor;
65
    }
66
    
67
    /** Factory method that returns an appropriate inplace 
68
     *  editor for an object.  Special handling is provided for
69
     *  instances of Node.Property which can provide hints or
70
     *  even their own legacy inplace editor implementation. 
71
     *  <P>The returned instance will be connected to the 
72
     *  object (the component provided by getComponent() will
73
     *  render the property object correctly with no additional
74
     *  intervention needed.  If <code>newInstance</code> is
75
     *  true, will create a new instance of the inplace editor
76
     *  component (for use with PropertyPanel and other cases
77
     *  where multiple inplace editors can be displayed at the
78
     *  same time); otherwise a shared instance will be configured
79
     *  and returned.<P> Note that for the case of unknown object
80
     *  types (non Node.Property objects), the returned InplaceEditor
81
     *  will have no way of knowing how to update the object with
82
     *  a new value, and client code must listen for actions on
83
     *  the InplaceEditor and do this manually - the update method
84
     *  of the InplaceEditor will do nothing.  */
85
    public static InplaceEditor getInplaceEditor (Property p, boolean newInstance) {
86
        PropertyEditor ped = PropUtils.getPropertyEditor (p);
87
        InplaceEditor result=(InplaceEditor) p.getValue (
88
                "inplaceEditor"); //NOI18N
89
        
90
        PropertyEnv env = null;
91
        if (ped instanceof ExPropertyEditor) {
92
            ExPropertyEditor epe = (ExPropertyEditor) ped;
93
            //configure the editor/propertyenv
94
            env = new PropertyEnv();
95
            env.setFeatureDescriptor (p);
96
            env.setEditable (p.canWrite());
97
            epe.attachEnv (env);
98
            if (result == null) {
99
                result = env.getInplaceEditor();
100
            }
101
        } else if (ped instanceof EnhancedPropertyEditor) {
102
            //handle legacy inplace custom editors
103
            EnhancedPropertyEditor enh = (EnhancedPropertyEditor) ped;
104
            if (enh.hasInPlaceCustomEditor()) {
105
                //Use our wrapper component to handle this
106
                result = new WrapperInplaceEditor (enh);
107
            }
108
        }
109
        
110
        //Okay, the result is null, provide one of the standard inplace editors
111
        if (result == null) {
112
            Class c = p.getValueType();
113
            if ((c == Boolean.class) || (c == Boolean.TYPE)) {
114
                if (ped instanceof PropUtils.NoPropertyEditorEditor) {
115
                    //platform case
116
                    result = getStringEditor (newInstance);
117
                } else {
118
                    boolean useRadioButtons = PropUtils.forceRadioButtons ||
119
                        (p.getValue ("stringValues") != null); //NOI18N
120
                    result = useRadioButtons ? 
121
                        getRadioButtonEditor (newInstance) :
122
                        getCheckboxEditor (newInstance);
123
                }
124
            } else if (ped.getTags() != null) {
125
                result = getComboBoxEditor (newInstance);
126
            } else {
127
                result = getStringEditor (newInstance);
128
            }
129
        }
130
        result.setPropertyModel (new NodePropertyModel (p, null));
131
        result.connect (ped, env);
132
        return result;
133
    }
134
}
(-)src/org/openide/explorer/propertysheet/NamesPanel.java (-101 lines)
Removed Link Here 
1
/*
2
 *                 Sun Public License Notice
3
 * 
4
 * The contents of this file are subject to the Sun Public License
5
 * Version 1.0 (the "License"). You may not use this file except in
6
 * compliance with the License. A copy of the License is available at
7
 * http://www.sun.com/
8
 * 
9
 * The Original Code is NetBeans. The Initial Developer of the Original
10
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2000 Sun
11
 * Microsystems, Inc. All Rights Reserved.
12
 */
13
14
15
package org.openide.explorer.propertysheet;
16
17
18
import java.awt.Dimension;
19
import javax.swing.JPanel;
20
21
22
/**
23
 * This is continer which manages Components in one column. All components have the same size
24
 * which is setted by the first component's preferred size or by setter method
25
 * setItemHeight (int aHeight).
26
 *
27
 * @author   Jan Jancura, Jaroslav Tulach
28
 */
29
class NamesPanel extends JPanel {
30
    /** generated Serialized Version UID */
31
    static final long serialVersionUID = 1620670226589808833L;
32
33
    /** Indicates whether the panel has its own focus cycle. */
34
    private boolean focusCycleRoot;
35
    
36
37
    /**
38
     * Construct NamesPanel.
39
     */
40
    public NamesPanel () {
41
        setLayout(new ColumnManager());
42
    }
43
44
    /**
45
     * Construct NamesPanel which size depends on the other NamesPanel size..
46
     */
47
    public NamesPanel(NamesPanel namesPanel) {
48
        setLayout(new ColumnManager(namesPanel.getLayout()));
49
    }
50
51
    // XXX when jdk1.3 will become unsupported revise use of 
52
    // set/isFocusCycleRoot method. In jdk1.4 this method was 
53
    // added to java.awt.Container.
54
    /** Sets focus cycle root. 
55
     * @see #focusCycleRoot */
56
    public void setFocusCycleRoot(boolean focusCycleRoot) {
57
        this.focusCycleRoot = focusCycleRoot;
58
    }
59
    
60
    /** Indicates whether this panel has focus cycle.
61
     * Overrides superclass method.
62
     * @see #focusCycleRoot */
63
    public boolean isFocusCycleRoot() {
64
        return focusCycleRoot;
65
    }
66
    
67
    /** The preferred size of this panel is the size that is required by the text in the largest button
68
     */
69
    public Dimension getPreferredSize () {
70
        return getLayout ().preferredLayoutSize (this);
71
    }
72
73
    // bugfix of #13152 - makes sure no
74
    // PropertyPanels are in "write" state
75
    void reset() {
76
        // ensure that there is no PropertyPanel
77
        // in "write state"
78
        int count = getComponentCount();
79
        for (int i = 0; i < count; i++) {
80
            if(getComponent(i) instanceof PropertyPanel) {
81
                PropertyPanel p = (PropertyPanel)getComponent(i);
82
                if (p.isWriteState()) {
83
                    p.refresh(); // back to the read state
84
                }
85
            }
86
        }
87
    }
88
    
89
    /** Overrides superclass method to ensure that all <code>SheetButton</code>s
90
    /* added to this panel are depressed when validation occures. */
91
    public void validate() {
92
        int count = getComponentCount();
93
        for (int i = 0; i < count; i++) {
94
            if (getComponent(i) instanceof SheetButton) {
95
                SheetButton b = (SheetButton)getComponent(i);
96
                b.setPressed(false);
97
            }
98
        }
99
        super.validate();
100
    }
101
}
(-)src/org/openide/explorer/propertysheet/NodePropertyModel.java (+133 lines)
Added Link Here 
1
/*
2
 * NodePropertyModel.java
3
 *
4
 * Created on April 22, 2003, 5:09 PM
5
 */
6
7
package org.openide.explorer.propertysheet;
8
import org.openide.nodes.Node;
9
import java.beans.PropertyChangeSupport;
10
import java.beans.PropertyChangeListener;
11
import java.beans.PropertyEditor;
12
import java.beans.FeatureDescriptor;
13
import java.lang.reflect.InvocationTargetException;
14
15
/** Implementation of the <code>PropertyModel</code> interface keeping
16
 * a <code>Node.Property</code>.  Refactored from PropertyPanel.SimpleModel
17
 * as part of the property sheet rewrite.  */
18
class NodePropertyModel implements ExPropertyModel {
19
    
20
    //This class was originally PropertyPanel.SimpleModel up to
21
    //PropertyPanel 1.123
22
    
23
    /** Property to work with.  */
24
    private Node.Property prop;
25
26
    /** Array of beans(nodes) to which belong the property.  */
27
    private Object[] beans;
28
29
    /** Property change support.  */
30
    private PropertyChangeSupport sup = new PropertyChangeSupport(this);
31
32
    /** Construct simple model instance.
33
     * @param property proeprty to work with
34
     * @param beans array of beans(nodes) to which belong the property
35
     */
36
    public NodePropertyModel(Node.Property property, Object[] beans) {
37
        this.prop = property;
38
        this.beans = beans;
39
    }
40
41
    /** Implements <code>PropertyModel</code> interface.  */
42
    public Object getValue() throws InvocationTargetException {
43
        try {
44
            return prop.getValue();
45
        } catch(IllegalAccessException iae) {
46
            throw annotateException(iae);
47
        } catch(InvocationTargetException ite) {
48
            throw annotateException(ite);
49
        }
50
    }
51
52
    /** Implements <code>PropertyModel</code> interface.  */
53
    public void setValue(Object v) throws InvocationTargetException {
54
        try {
55
            prop.setValue(v);
56
            sup.firePropertyChange(PropertyModel.PROP_VALUE, null, null);
57
        } catch(IllegalAccessException iae) {
58
            throw annotateException(iae);
59
        } catch(IllegalArgumentException iaae) {
60
            throw annotateException(iaae);
61
        } catch(InvocationTargetException ite) {
62
            throw annotateException(ite);
63
        }
64
    }
65
66
    /** Annotates specified exception. Helper method.
67
     * @param exception original exception to annotate
68
     * @return <code>IvocationTargetException</code> which annotates the
69
     *       original exception
70
     */
71
    private InvocationTargetException annotateException(Exception exception) {
72
        if(exception instanceof InvocationTargetException) {
73
            return (InvocationTargetException)exception;
74
        } else {
75
            return new InvocationTargetException(exception);
76
        }
77
    }
78
79
    /** Implements <code>PropertyModel</code> interface.  */
80
    public Class getPropertyType() {
81
        return prop.getValueType();
82
    }
83
84
    /** Implements <code>PropertyModel</code> interface.  */
85
    public Class getPropertyEditorClass() {
86
        Object ed = prop.getPropertyEditor();
87
        if (ed != null) {
88
            return ed.getClass();
89
        }
90
        return null;
91
    }
92
93
    /** Mainly a hack to avoid gratuitous calls to fetch property editors.
94
     *  @since 1.123.2.1 - branch propsheet_issue_29447
95
     */
96
    public PropertyEditor getPropertyEditor() {
97
        return PropUtils.getPropertyEditor(prop);
98
    }
99
100
    /** Implements <code>PropertyModel</code> interface.  */
101
    public void addPropertyChangeListener(PropertyChangeListener l) {
102
        sup.addPropertyChangeListener(l);
103
    }
104
105
    /** Implements <code>PropertyModel</code> interface.  */
106
    public void removePropertyChangeListener(PropertyChangeListener l) {
107
        sup.removePropertyChangeListener(l);
108
    }
109
110
    /** Implements <code>ExPropertyModel</code> interface.  */
111
    public Object[] getBeans() {
112
        return beans;
113
    }
114
115
    /** Implements <code>ExPropertyModel</code> interface.  */
116
    public FeatureDescriptor getFeatureDescriptor() {
117
        return prop;
118
    }
119
120
    void fireValueChanged() {
121
        sup.firePropertyChange(PropertyModel.PROP_VALUE, null, null);
122
    }
123
    
124
    /** Package private method to return the property, so error handling
125
     *  can use the display name in the dialog for the user if the user
126
     *  enters an invalid value  */
127
    Node.Property getProperty() {
128
        return prop;
129
    }
130
131
}
132
133
(-)src/org/openide/explorer/propertysheet/PropUtils.java (+1004 lines)
Added Link Here 
1
/*
2
 *                 Sun Public License Notice
3
 * 
4
 * The contents of this file are subject to the Sun Public License
5
 * Version 1.0 (the "License"). You may not use this file except in
6
 * compliance with the License. A copy of the License is available at
7
 * http://www.sun.com/
8
 * 
9
 * The Original Code is NetBeans. The Initial Developer of the Original
10
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun
11
 * Microsystems, Inc. All Rights Reserved.
12
 */
13
/*
14
 * PropUtils.java
15
 *
16
 * Created on January 4, 2003, 7:31 PM
17
 */
18
19
package org.openide.explorer.propertysheet;
20
import java.awt.event.*;
21
import java.awt.*;
22
import java.beans.*;
23
import java.lang.reflect.*;
24
import java.util.*;
25
import javax.accessibility.*;
26
import javax.swing.*;
27
import javax.swing.plaf.basic.BasicComboBoxUI;
28
import javax.swing.plaf.metal.*;
29
import java.text.MessageFormat;
30
import java.util.prefs.Preferences;
31
import javax.swing.border.Border;
32
import javax.swing.plaf.SplitPaneUI;
33
import javax.swing.plaf.basic.BasicComboPopup;
34
import javax.swing.plaf.basic.BasicSplitPaneUI;
35
import javax.swing.plaf.basic.ComboPopup;
36
import org.openide.nodes.Node.*;
37
import org.openide.*;
38
import org.openide.util.*;
39
import org.openide.nodes.*;
40
41
/** A few utility methods useful to implementors of Inplace Editors.
42
 * @author  Tim Boudreau
43
 */
44
final class PropUtils {
45
    /**If true, hides custom editor buttons unless in editing mode.
46
     * Auto popup of combo boxes is suppressed in this mode */
47
    static boolean noCustomButtons = 
48
        Boolean.getBoolean("netbeans.ps.noCustomButtons"); //NOI18N
49
    /**If true, radio button boolean editor will always be used */
50
    static final boolean forceRadioButtons = 
51
        Boolean.getBoolean ("netbeans.ps.forceRadioBoolean"); //NOI18N
52
    /**If true, caption on the checkbox boolean editor will be suppressed */
53
    static final boolean noCheckboxCaption = 
54
        Boolean.getBoolean ("netbeans.ps.noCheckboxCaption"); //NOI18N
55
    /** Flag which, when true, property set expansion handles will not be
56
     * shown when the node only has one property set.  Leaving as an option
57
     * since there is still disagreement about the right way this should
58
     * work, and I don't want to repeatedly reimplement it */
59
    static final boolean hideSingleExpansion = Boolean.getBoolean (
60
        "netbeans.ps.hideSingleExpansion"); //NOI18N
61
    /**If true, the left margin for expandable sets will be suppressed.
62
     * Mainly desirable for small screens.  */
63
    static final boolean neverMargin = Boolean.getBoolean(
64
        "netbeans.ps.neverMargin"); //NOI18N
65
    /** UIManager key for alternate color for table - if present, color will
66
     *  alternate between the standard background and this color    */
67
    private static final String KEY_ALTBG = "Tree.altbackground"; //NOI18N
68
    /** UIManager key for the background color for expandable sets.  If not
69
     *  set, the color will be derived from the default tree background
70
     *  color */
71
    private static final String KEY_SETBG = "PropSheet.setBackground"; //NOI18N
72
    /** UIManager key for background color of expandable sets when selected. If not
73
     *  set, the color will be derived from the default tree selection background
74
     *  color */
75
    private static final String KEY_SELSETBG = "PropSheet.selectedSetBackground"; //NOI18N
76
    /** UIManager key for integer icon margin, amount of space to add beside 
77
     *  the expandable set icon to make up the margin */
78
    private static final String KEY_ICONMARGIN = "netbeans.ps.iconmargin"; //NOI18N
79
    /** UIManager key for fixed row height for rows in property sheet.  If not
80
     *  explicitly set, row height will be derived from the target font */
81
    static final String KEY_ROWHEIGHT = "netbeans.ps.rowheight"; //NOI18N
82
    /** Preferences key for the show description area property */
83
    private static final String PREF_KEY_SHOWDESCRIPTION="showDescriptionArea"; //NOI18N
84
    /** Preferences key for the storage of closed set names */
85
    private static final String PREF_KEY_CLOSEDSETNAMES="closedSetNames"; //NOI18N
86
    /** Preferences key for the storage of sort order */
87
    private static final String PREF_KEY_SORTORDER="sortOrder"; //NOI18N
88
    
89
    /** Private constructor to hide from API */
90
    private PropUtils() {
91
        //do nothing
92
    }
93
    
94
    static void updateProp (PropertyModel mdl, PropertyEditor ed, 
95
        String title) {
96
        Object newValue = ed.getValue();
97
        try {
98
            Object oldValue = mdl.getValue();
99
            
100
            // test if newValue is not equal to oldValue
101
            if ((newValue != null && !newValue.equals(oldValue)) || 
102
                (newValue == null && oldValue != null)) {
103
                mdl.setValue(newValue);
104
            }
105
        } catch (Exception e) {
106
            processThrowable (e, title, newValue);
107
        }
108
    }
109
    
110
    /** Update a property using the model and value cached by an inplace editor */
111
    static void updateProp (InplaceEditor ine) {
112
        Component c = ine.getComponent();
113
        Cursor oldCursor = c.getCursor();
114
        try {
115
            c.setCursor (Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
116
            Object o = ine.getValue();
117
            String newValString = o == null ? NbBundle.getMessage (PropUtils.class, "NULL") : o.toString();
118
            try {
119
                if (o instanceof String) {
120
                    ine.getPropertyEditor().setAsText ((String) o);
121
                } else {
122
                    ine.getPropertyEditor().setValue (ine.getValue());
123
                }
124
            } catch (Exception e) {
125
                PropertyModel pm = ine.getPropertyModel();
126
                String propName;
127
                if (pm instanceof NodePropertyModel) {
128
                    Node.Property p = ((NodePropertyModel) pm).getProperty();
129
                    propName = p.getDisplayName();
130
                } else if (pm instanceof DefaultPropertyModel) {
131
                    propName = ((DefaultPropertyModel) pm).propertyName;
132
                } else {
133
                    //who knows what it is...
134
                    propName = NbBundle.getMessage (PropUtils.class, 
135
                        "MSG_unknown_property_name"); //NOI18N
136
                }
137
                processThrowable (e, newValString, propName);
138
            }
139
            PropUtils.updateProp (ine.getPropertyModel(), ine.getPropertyEditor(), newValString); //XXX
140
        } finally {
141
            c.setCursor (oldCursor);
142
        }
143
    }
144
    
145
    /** Processes <code>Throwable</code> thrown from <code>setAsText</code>
146
     * or <code>setValue</code> call on <code>editor</code>. Helper method. */
147
    private static void processThrowable(Throwable throwable, String title, Object newValue) {
148
        //Copied from old PropertyPanel impl
149
        if(throwable instanceof ThreadDeath) {
150
            throw (ThreadDeath)throwable;
151
        }
152
        ErrorManager em = ErrorManager.getDefault();
153
          ErrorManager.Annotation[] anns = em.findAnnotations(throwable);
154
        if (((anns == null) || (anns.length==0)) && (throwable.getLocalizedMessage() != throwable.getMessage())) { //XXX See issue 34569
155
            String msg = MessageFormat.format(NbBundle.getMessage(PropUtils.class,
156
                    "FMT_ErrorSettingProperty"), new Object[] {newValue, title});
157
            em.annotate(throwable, ErrorManager.USER, msg, throwable.getLocalizedMessage(), throwable, new java.util.Date());
158
        }
159
        em.notify(throwable);
160
    }
161
162
        
163
164
    /** Utility method to fetch a comparator for properties
165
     *  based on sorting mode defined in PropertySheet.  */
166
    static Comparator getComparator (int sortingMode) {
167
        switch (sortingMode) {
168
        case PropertySheet.UNSORTED:
169
            return null;
170
        case PropertySheet.SORTED_BY_NAMES:
171
            return SORTER_NAME;
172
        case PropertySheet.SORTED_BY_TYPES:
173
            return SORTER_TYPE;
174
        default:
175
            throw new IllegalArgumentException (
176
            "Unknown sorting mode: " + Integer.toString(sortingMode)); //NOI18N
177
        }
178
    }
179
180
    //Comparators copied from original propertysheet implementation
181
    /** Comparator which compares types */
182
    private final static Comparator SORTER_TYPE = new Comparator () {
183
                public int compare (Object l, Object r) {
184
                    if (! (l instanceof Node.Property)) {
185
                        throw new IllegalArgumentException(
186
                        "Can compare only Node.Property instances."); // NOI18N
187
                    }
188
                    if (! (r instanceof Node.Property)) {
189
                        throw new IllegalArgumentException(
190
                        "Can compare only Node.Property instances."); // NOI18N
191
                    }
192
                    
193
                    Class t1 = ((Node.Property)l).getValueType();
194
                    Class t2 = ((Node.Property)r).getValueType();
195
                    String s1 = t1 != null ? t1.getName() : "";
196
                    String s2 = t2 != null ? t2.getName() : "";
197
                    
198
                    int s = s1.compareToIgnoreCase (s2);
199
                    if (s != 0) return s;
200
201
                    s1 = ((Node.Property)l).getDisplayName();
202
                    s2 = ((Node.Property)r).getDisplayName();
203
                    return s1.compareToIgnoreCase(s2);
204
                }
205
                public String toString() {
206
                    return "Type comparator";//NOI18N
207
                }
208
            };
209
210
    /** Comparator which compares PropertyDetils names */
211
    private final static Comparator SORTER_NAME = new Comparator () {
212
                public int compare (Object l, Object r) {
213
                    if (! (l instanceof Node.Property)) {
214
                        throw new IllegalArgumentException(
215
                        "Can compare only Node.Property instances."); // NOI18N
216
                    }
217
                    if (! (r instanceof Node.Property)) {
218
                        throw new IllegalArgumentException(
219
                        "Can compare only Node.Property instances."); // NOI18N
220
                    }
221
                    String s1 = ((Node.Property)l).getDisplayName();
222
                    String s2 = ((Node.Property)r).getDisplayName();
223
                    return String.CASE_INSENSITIVE_ORDER.compare(s1, s2);
224
                }
225
                public String toString() {
226
                    return "Name comparator";//NOI18N
227
                }
228
            };
229
            
230
    private static java.util.List missing = null;
231
            
232
    /** A Combobox UI class for a borderless combobox - looks better in the
233
     *  propertysheet. */   
234
    private static class CleanComboUI extends BasicComboBoxUI {
235
        private JButton button=null;
236
        protected void installDefaults() {
237
            LookAndFeel.installColorsAndFont( comboBox,
238
                                              "ComboBox.background",
239
                                              "ComboBox.foreground",
240
                                              "ComboBox.font" );
241
            comboBox.setBorder (BorderFactory.createEmptyBorder (0,3,0,0));
242
        }
243
        
244
        protected ComboPopup createPopup() {
245
            return new CleanComboPopup( comboBox );
246
        }        
247
248
        protected JButton createArrowButton() {
249
            Icon i = new MetalComboBoxIcon();
250
            button = new JButton (i);
251
            button.setContentAreaFilled (false);
252
            button.setBorder (null);
253
            return button;
254
        }
255
        protected java.awt.Insets getInsets () {
256
            java.awt.Insets i = super.getInsets();
257
            i.right += 2;
258
            return i;
259
        }
260
        public void paint( java.awt.Graphics g, JComponent c ) {
261
            //kill the lowered on the button when it is depressed
262
            super.paint (g, c);
263
        }
264
265
        /** This focus listener is a workaround for JDK bug 4168483 - 
266
         *  a bogus FocusLost event is sent to the combo box when the
267
         *  popup is shown.  This results in a variety of messy behaviors.
268
         *  The main workaround here is to always show the popup on a 
269
         *  focus gained event, and ignore focus lost events (they will be
270
         *  trapped by the property sheet if focus moves to another component,
271
         *  and removeEditor() will be called anyway;  other focus lost events
272
         *  will be events in which removeEditor() will be called because the
273
         *  editor's action has been performed.  */
274
        protected FocusListener createFocusListener() {
275
            return new FocusListener () {
276
                public void focusGained( FocusEvent e ) {
277
                    if (comboBox.getParent() == null) {
278
                        // believe it or not, this can happen if a dialog
279
                        //(such as open file server can't start) pops up
280
                        //while an editor is being instantiated.  Some kind
281
                        //of order-of-operations problem
282
                        return;
283
                    }
284
                    hasFocus = true;
285
                    try {
286
                        //Ensure the combo box has a selection.  Avoid messing
287
                        //with legacy editors like the form editor's - can cause 
288
                        //exceptions
289
                        if (comboBox instanceof ComboInplaceEditor) {
290
                            Object o = comboBox.getSelectedItem();
291
                            if (o != null) {
292
                                comboBox.getModel().setSelectedItem(o);
293
                            } else {
294
                                if (comboBox.getModel().getSize() >= 0) {
295
                                    comboBox.setSelectedIndex(0);
296
                                }
297
                            }
298
                            if (!comboBox.isEditable() && 
299
                                //don't uatomatically show popup if custom editor button is
300
                                //only visible when editing, give the user a chance to choose
301
                                !noCustomButtons) {
302
                                comboBox.showPopup();
303
                            }
304
                        }
305
                    } catch (IllegalComponentStateException icse) {
306
                        //Workaround for peculiar JDK bug - it tries to set focus to a
307
                        //combobox that is not on screen
308
                    }
309
                    
310
                    // Notify assistive technologies that the combo box
311
                    // gained focus.
312
                    if (comboBox instanceof Accessible) {
313
                        AccessibleContext ac = 
314
                            ((Accessible)comboBox).getAccessibleContext();
315
                        if (ac != null) {
316
                            ac.firePropertyChange(
317
                                AccessibleContext.ACCESSIBLE_STATE_PROPERTY, 
318
                                null, AccessibleState.FOCUSED);
319
                        }
320
                    }
321
                }
322
323
                public void focusLost( FocusEvent e ) {
324
                    hasFocus = false;
325
                    comboBox.hidePopup();
326
                    if (comboBox instanceof Accessible) {
327
                        AccessibleContext ac = 
328
                            ((Accessible)comboBox).getAccessibleContext();
329
                        if (ac != null) {
330
                            ac.firePropertyChange(
331
                                AccessibleContext.ACCESSIBLE_STATE_PROPERTY, 
332
                                AccessibleState.FOCUSED, null);
333
                        }
334
                    }
335
                }
336
            };
337
        }
338
        
339
        public void paintCurrentValue(Graphics g,Rectangle bounds,boolean hasFocus) {
340
            ListCellRenderer renderer = comboBox.getRenderer();
341
            Component c;
342
            c = renderer.getListCellRendererComponent( listBox,
343
                                                           comboBox.getSelectedItem(),
344
                                                           -1,
345
                                                           false,
346
                                                           false );
347
            c.setFont(comboBox.getFont());
348
            c.setForeground(comboBox.getForeground());
349
            c.setBackground(comboBox.getBackground());
350
351
            // Fix for 4238829: should lay out the JPanel.
352
            boolean shouldValidate = false;
353
            if (c instanceof JPanel)  {
354
                shouldValidate = true;
355
            }
356
357
            currentValuePane.paintComponent(g,c,comboBox,bounds.x,bounds.y,
358
                                            bounds.width,bounds.height, shouldValidate);
359
        }
360
        
361
        protected Rectangle rectangleForCurrentValue () {
362
            Rectangle r = super.rectangleForCurrentValue();
363
            if (editor != null) {
364
                r.x += 1;
365
                r.y += 1;
366
                r.width -=1;
367
                r.height -=1;
368
            }
369
            return r;
370
        }
371
        
372
        private class CleanComboLayout extends 
373
                                        BasicComboBoxUI.ComboBoxLayoutManager {
374
            public void layoutContainer(Container parent) {
375
                super.layoutContainer (parent);
376
                if (editor != null) {
377
                    java.awt.Rectangle r = rectangleForCurrentValue();
378
                    r.x = 0;
379
                    r.y = 0;
380
                    r.height = comboBox.getHeight();
381
                    editor.setBounds (r);
382
                }
383
            }
384
        }
385
        
386
        protected ComboBoxEditor createEditor() {
387
            return new CleanComboBoxEditor();
388
        }
389
        
390
        private class CleanComboPopup extends BasicComboPopup {
391
            public CleanComboPopup (JComboBox box) {
392
                super (box);
393
            }
394
            
395
            protected Rectangle computePopupBounds(int px,int py,int pw,int ph) {
396
                Dimension d = list.getPreferredSize();
397
                Rectangle r = Utilities.getUsableScreenBounds();
398
                if (pw < d.width) {
399
                    pw = Math.min(d.width, r.width -px);
400
                }
401
                if (ph < d.height) {
402
                    ph = Math.min (r.height - py, d.height);
403
                }
404
                if (px + pw > r.width - px) {
405
                    px -= r.width - pw;
406
                }
407
                Rectangle result = new Rectangle (px, py, pw, ph);
408
                return result;
409
            }
410
            
411
        }
412
    }
413
414
    static class CleanComboBoxEditor extends javax.swing.plaf.basic.BasicComboBoxEditor {
415
        public CleanComboBoxEditor () {
416
            editor = new JTextField();
417
            Color c = UIManager.getColor ("Table.SelectionBackground");//NOI18N
418
            if (c == null) {
419
                c = Color.BLACK;
420
            }
421
            editor.setBorder (BorderFactory.createLineBorder (c));
422
//            editor.setBorder (BorderFactory.createEmptyBorder());
423
        }
424
    }
425
    
426
    /** Property editor for properties which belong to more than one property, but have
427
     *  different values.
428
     */
429
    static final class DifferentValuesEditor implements PropertyEditor {
430
        
431
        private PropertyEditor ed;
432
        
433
        private boolean notSet = true;
434
        
435
        public DifferentValuesEditor(PropertyEditor ed) {
436
            this.ed = ed;
437
        }
438
        
439
        public void addPropertyChangeListener(PropertyChangeListener listener) {
440
            ed.addPropertyChangeListener(listener);
441
        }
442
        
443
        public String getAsText() {
444
            String result;
445
            if (notSet) {
446
                result = NbBundle.getMessage(PropUtils.class,
447
                "CTL_Different_Values"); //NOI18N
448
            } else {
449
                result = ed.getAsText();
450
            }
451
            return result;
452
        }
453
        
454
        public java.awt.Component getCustomEditor() {
455
            return ed.getCustomEditor();
456
        }
457
        
458
        public String getJavaInitializationString() {
459
            return ed.getJavaInitializationString();
460
        }
461
        
462
        public String[] getTags() {
463
            return ed.getTags();
464
        }
465
        
466
        public Object getValue() {
467
            Object result;
468
            if (notSet) {
469
                result = null;
470
            } else {
471
                result = ed.getValue();
472
            }
473
            return result;
474
        }
475
        
476
        public boolean isPaintable() {
477
            return notSet ? false : ed.isPaintable();
478
        }
479
        
480
        public void paintValue(java.awt.Graphics gfx, java.awt.Rectangle box) {
481
            //issue 33341 - don't allow editors to paint if value not set
482
            if (isPaintable()) {
483
                ed.paintValue(gfx, box);
484
            }
485
        }
486
        
487
        public void removePropertyChangeListener(PropertyChangeListener listener) {
488
            ed.removePropertyChangeListener(listener);
489
        }
490
        
491
        public void setAsText(String text) throws java.lang.IllegalArgumentException {
492
            ed.setAsText(text);
493
            notSet = false;
494
        }
495
        
496
        public void setValue(Object value) {
497
            ed.setValue(value);
498
            notSet=false;
499
        }
500
        
501
        public boolean supportsCustomEditor() {
502
            return false;
503
        }
504
    }
505
    
506
    /** Dummy property editor for properties which have no real editor.  */
507
    static final class NoPropertyEditorEditor implements PropertyEditor {
508
        
509
        public void addPropertyChangeListener(PropertyChangeListener listener) {
510
            //no-op
511
        }
512
        
513
        public String getAsText() {
514
            return NbBundle.getMessage(PropertySheet.class,
515
            "CTL_NoPropertyEditor");
516
        }
517
        
518
        public java.awt.Component getCustomEditor() {
519
            return null;
520
        }
521
        
522
        public String getJavaInitializationString() {
523
            return "";
524
        }
525
        
526
        public String[] getTags() {
527
            return null;
528
        }
529
        
530
        public Object getValue() {
531
            return getAsText();
532
        }
533
        
534
        public boolean isPaintable() {
535
            return false;
536
        }
537
        
538
        public void paintValue(java.awt.Graphics gfx, java.awt.Rectangle box) {
539
            //no-op
540
        }
541
        
542
        public void removePropertyChangeListener(PropertyChangeListener listener) {
543
            //no-op
544
        }
545
        
546
        public void setAsText(String text) throws java.lang.IllegalArgumentException {
547
            //no-op
548
        }
549
        
550
        public void setValue(Object value) {
551
            //no-op
552
        }
553
        
554
        public boolean supportsCustomEditor() {
555
            return false;
556
        }
557
        
558
    }
559
    
560
    /** Create a ComboBoxUI that does not display borders on the combo box.
561
     *  Since the property sheet is rendered as a table, this looks better.  
562
     *  This UI also delegates closing of its popup to the property sheet, which
563
     *  has better knowledge of when this is appropriate, in the case of focus 
564
     *  changes.  Inplace editors which employ a combo box should use the UI
565
     *  returned by this method, rather than the default for the look and feel.
566
     *  Thus the appearance will be consistent with the rest of the property
567
     *  sheet. */
568
    public static javax.swing.plaf.ComboBoxUI createComboUI (JComboBox box) {
569
            return new CleanComboUI();
570
    }
571
    
572
    /** A convenience map for missing property editor classes, so users
573
     *  are only notified once, not every time a table cell is drawn.
574
     */
575
    private static java.util.List getMissing() {
576
        if (missing == null) {
577
            missing = new ArrayList();
578
        }
579
        return missing;
580
    }
581
    
582
    /** Gets a property editor appropriate to the class.  First
583
     *  checks property editors registered via XML layers, then
584
     *  falls back to <code>java.beans.PropertyEditorManager</code>.
585
     */
586
    static PropertyEditor getPropertyEditor(Class c) {
587
        PropertyEditor result = PropertyEditorManager.findEditor(c);
588
        if (result == null) {
589
            result = new NoPropertyEditorEditor();
590
        }
591
        return result;
592
    }
593
    
594
    /** Gets a property editor appropriate to the property.
595
     *  This method centralizes all code for fetching property editors
596
     *  used by the property sheet, such that future alternative
597
     *  registration systems for property editors may be easily
598
     *  implemented.
599
     *  <P><strong>Note:</strong>  This method will return a property
600
     *  editor with the value of the property already assigned.  Client
601
     *  code need not set the value in the property editor unless it
602
     *  should be changed, as this can result in unnecessary event
603
     *  firing.
604
     */
605
    static PropertyEditor getPropertyEditor(Property p) {
606
        PropertyEditor result = p.getPropertyEditor();
607
        if (result == null) {
608
            result = getPropertyEditor(p.getValueType()); //XXX is this agood idea?
609
        }
610
        //handle a type with no registered property editor here
611
        if (result == null) {
612
            java.util.List missing = getMissing();
613
            String type = p.getValueType().getName();
614
            if (!(missing.contains(type))) {
615
                ErrorManager.getDefault().log(ErrorManager.USER,
616
                "No property editor registered for type " + type); //NOI18N
617
                missing.add(type);
618
            }
619
            result = new NoPropertyEditorEditor();
620
        } else if (p.canRead()) {
621
            try {
622
                try {
623
                    try {
624
                        updateEdFromProp(p, result, p.getDisplayName());
625
                        
626
                    } catch (ProxyNode.DifferentValuesException dve) {
627
                        if (p.getValueType() == Boolean.class || p.getValueType() == Boolean.TYPE) {
628
                            result = new Boolean3WayEditor();
629
                        } else {
630
                            result = new DifferentValuesEditor(result);
631
                        }
632
                    }
633
                } catch (IllegalAccessException iae) {
634
                    IllegalStateException ise = new IllegalStateException("Error getting property value");
635
                    ErrorManager.getDefault().annotate(ise, iae);
636
                    throw ise;
637
                }
638
            } catch (InvocationTargetException ite) {
639
                IllegalStateException ise = new IllegalStateException("Error getting property value");
640
                ErrorManager.getDefault().annotate(ise, ite);
641
                throw ise;
642
            }
643
        }
644
        return result;
645
    }
646
    
647
    static void updateEdFromProp(Property p, PropertyEditor ed, 
648
                                    String title) throws 
649
                                    ProxyNode.DifferentValuesException, 
650
                                    IllegalAccessException, 
651
                                    InvocationTargetException {
652
        Object newValue = p.getValue();
653
        Object oldValue = ed.getValue();
654
        // test if newValue is not equal to oldValue
655
        if ((newValue != null && !newValue.equals(oldValue)) ||
656
        (newValue == null && oldValue != null)) {
657
            ed.setValue(newValue);
658
        }
659
    }
660
661
    /** Field to hold the width of the margin.  This is used for painting, so the
662
     *  grid is not displayed in the margin, and for figuring out if a mouse event
663
     *  occured in the margin (toggle expanded on a single click) or not.  */
664
    static int marginWidth=-1;    
665
    /** Field to hold additional space between spinner icons and set text */
666
    private static int iconMargin = -1;
667
    /** Color for selected property set expanders - should be
668
     *  darker than the selection color for regular properties,
669
     *  to differentiate and be consistent with their unselected color */
670
    static Color selectedSetRendererColor = null;
671
    /** Color for property set expanders */
672
    static Color setRendererColor=null;
673
    
674
    static Icon collapsedIcon = null;
675
    static Icon expandedIcon = null;
676
    static int spinnerHeight = -1;
677
    static Color controlColor = null;
678
    static Color shadowColor = null;
679
    
680
    static Color getControlColor () {
681
        if (controlColor == null) {
682
            deriveColorsAndMargin();
683
        }
684
        return controlColor;
685
    }
686
    
687
    static Color getShadowColor () {
688
        if (shadowColor == null) {
689
            deriveColorsAndMargin();
690
        }
691
        return shadowColor;
692
    }
693
    
694
    private static Color altBg = null;
695
    static Color getAltBg() {
696
        if (altBg == null) {
697
            deriveColorsAndMargin();
698
        }
699
        return altBg;
700
    }
701
    
702
    static boolean noAltBg() {
703
        if (noAltBg == null) {
704
            noAltBg = 
705
                UIManager.getColor(KEY_ALTBG) == null ? //NOI18N
706
                Boolean.TRUE : Boolean.FALSE;
707
        }
708
        return noAltBg.booleanValue();
709
    }
710
    
711
    static Boolean noAltBg=null;
712
    
713
    /** Derive a background color for expandable set entries which is
714
     *  slightly different from the table's background color */
715
    private static void deriveColorsAndMargin() {
716
        controlColor = UIManager.getColor ("control"); //NOI18N
717
        if (controlColor == null) controlColor = Color.LIGHT_GRAY;
718
        int red, green, blue;
719
        
720
        setRendererColor = UIManager.getColor(KEY_SETBG); //NOI18N
721
        if (setRendererColor == null) {
722
            red = adjustColorComponent (controlColor.getRed(), -25, -25);
723
            green = adjustColorComponent (controlColor.getGreen(), -25, -25);
724
            blue = adjustColorComponent (controlColor.getBlue(), -25, -25);
725
            setRendererColor = new Color (red, green, blue);
726
        }
727
728
        shadowColor = UIManager.getColor ("controlShadow"); //NOI18N
729
        if (shadowColor == null) shadowColor = Color.GRAY;
730
        
731
        selectedSetRendererColor = UIManager.getColor (
732
            KEY_SELSETBG); //NOI18N
733
        if (selectedSetRendererColor == null) {
734
            Color col = UIManager.getColor ("activeCaptionBorder"); //NOI18N
735
            if (col == null) col = Color.BLUE;
736
            red = adjustColorComponent (col.getRed(), -25, -25);
737
            green = adjustColorComponent (col.getGreen(), -25, -25);
738
            blue = adjustColorComponent (col.getBlue(), -25, -25);
739
            selectedSetRendererColor = new Color (red, green, blue);
740
        }
741
        
742
        altBg = UIManager.getColor (KEY_ALTBG); //NOI18N
743
        if (altBg == null) {
744
            altBg = UIManager.getColor ("Tree.background"); //NOI18N
745
            if (altBg == null) {
746
                altBg = Color.WHITE;
747
            }
748
            noAltBg=Boolean.TRUE;
749
        } else {
750
            noAltBg=Boolean.FALSE;
751
        }
752
        
753
        collapsedIcon = UIManager.getIcon ("Tree.collapsedIcon"); //NOI18N
754
        if (collapsedIcon == null) {
755
            collapsedIcon = new Icon () {
756
                public int getIconHeight () {
757
                    return 7;
758
                }
759
                
760
                public int getIconWidth() {
761
                    return 7;
762
                }
763
                public void paintIcon (Component c, Graphics g, int x, int y) {
764
                    //do nothing
765
                }
766
            };
767
            expandedIcon = collapsedIcon;
768
        }
769
        
770
        expandedIcon = UIManager.getIcon ("Tree.expandedIcon"); //NOI18N
771
        if (expandedIcon == null) {
772
            //Hack for jdk 1.4.2 beta GTK L&F
773
            expandedIcon = collapsedIcon;
774
        }
775
        
776
        if (collapsedIcon != null) {
777
            marginWidth = Math.max (14, collapsedIcon.getIconWidth() - 2);
778
        } else {
779
            //on the off chance a L&F doesn't provide this
780
            marginWidth = 13;
781
        }
782
        
783
        Integer i = (Integer) UIManager.get (KEY_ICONMARGIN); //NOI18N
784
        if (i != null) {
785
            iconMargin = i.intValue();
786
        } else {
787
            iconMargin = 0;
788
        }
789
        i = (Integer) UIManager.get (KEY_ROWHEIGHT); //NOI18N
790
        if (i != null) {
791
            spinnerHeight = i.intValue();
792
        } else {
793
            spinnerHeight = expandedIcon.getIconHeight();
794
        }
795
    }
796
    
797
    static Icon getExpandedIcon () {
798
        if (expandedIcon == null) {
799
            deriveColorsAndMargin();
800
        }
801
        return expandedIcon;
802
    }
803
804
    static Icon getCollapsedIcon () {
805
        if (collapsedIcon == null) {
806
            deriveColorsAndMargin();
807
        }
808
        return collapsedIcon;
809
    }
810
    
811
    static Color getSetRendererColor () {
812
        if (setRendererColor == null) {
813
            deriveColorsAndMargin();
814
        }
815
        return setRendererColor;
816
    }
817
    
818
    static Color getSelectedSetRendererColor() {
819
        if (selectedSetRendererColor == null) {
820
            deriveColorsAndMargin();
821
        }
822
        return selectedSetRendererColor;
823
    }
824
    
825
    static int getMarginWidth() {
826
        if (marginWidth == -1) {
827
            deriveColorsAndMargin();
828
        }
829
        return marginWidth;
830
    }
831
    
832
    static int getSpinnerHeight() {
833
        if (spinnerHeight == -1) {
834
            deriveColorsAndMargin();
835
        }
836
        return spinnerHeight;
837
    }
838
839
    static int getIconMargin () {
840
        if (iconMargin == -1) {
841
            deriveColorsAndMargin();
842
        }
843
        return iconMargin;
844
    }
845
    
846
    /** Adjust an rgb color component.
847
     * @param base the color, an RGB value 0-255
848
     * @param adjBright the amount to subtract if base > 128
849
     * @param adjDark the amount to add if base <=128  */
850
    private static int adjustColorComponent (int base, int adjBright, int adjDark) {
851
        if (base > 128) {
852
            base -= adjBright;
853
        } else {
854
            base += adjDark;
855
        }
856
        if (base < 0) base = 0;
857
        if (base > 255) base = 255;
858
        return base;
859
    }
860
    
861
    private static String bptn=null;
862
    static String basicPropsTabName () {
863
        if (bptn == null) {
864
            bptn = NbBundle.getMessage (PropUtils.class, "LBL_BasicTab");
865
        }
866
        //return bptn;
867
        return "Properties"; //XXX debug
868
    }
869
    
870
    private static Comparator comp = null;
871
    static Comparator getTabListComparator() {
872
        if (comp == null) {
873
            comp = new TabListComparator();
874
        }
875
        return comp;
876
    }
877
    
878
    /** Comparator for sorting the list of tabs such that basic properties
879
     *  is always the first tab. */
880
    private static class TabListComparator implements Comparator {
881
        public int compare(Object o1, Object o2) {
882
            String s1 = (String) o1;
883
            String s2 = (String) o2;
884
            if (s1 == s2) return 0;
885
            String bn = basicPropsTabName();
886
            if (bn.equals(s1)) return -1;
887
            if (bn.equals(s2)) return 1;
888
            return s1.compareTo(s2);
889
        }
890
    }
891
    
892
    static SplitPaneUI createSplitPaneUI () {
893
        return new CleanSplitPaneUI();
894
    }
895
    private static class CleanSplitPaneUI extends BasicSplitPaneUI {
896
        protected void installDefaults(){ 
897
            super.installDefaults();
898
            divider.setBorder (new SplitBorder());
899
        }
900
    }
901
    
902
    private static class SplitBorder implements Border {
903
        
904
        public Insets getBorderInsets(Component c) {
905
            if (UIManager.getLookAndFeel() instanceof MetalLookAndFeel) {
906
                return new Insets (2,0,1,0);
907
            } else {
908
                return new Insets (1,0,1,0);
909
            }
910
        }
911
        
912
        public boolean isBorderOpaque() {
913
            return true;
914
        }
915
        
916
        public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
917
            Shape s = g.getClip();
918
            if (UIManager.getLookAndFeel() instanceof MetalLookAndFeel) {
919
                g.setColor (UIManager.getColor ("controlShadow"));
920
                g.drawLine (x, y, x+width, y);
921
                g.setColor (UIManager.getColor ("controlHighlight"));
922
                g.drawLine (x, y+1, x+width, y+1);
923
                g.drawLine (x, y + height-1, x+width, y + height-1);
924
                g.setColor (UIManager.getColor ("controlShadow"));
925
                g.drawLine (x, y + height -2, x+width, y + height-2);
926
            } else {
927
                //don't do flush 3d for non-metal L&F
928
                g.setColor (UIManager.getColor ("controlHighlight"));
929
                g.drawLine (x, y, x+width, y);
930
                g.setColor (UIManager.getColor ("controlShadow"));
931
                g.drawLine (x, y + height-1, x+width, y + height-1);
932
            }
933
        }
934
        
935
    }
936
    
937
    private static Preferences getPreferences() {
938
        return Preferences.systemNodeForPackage(
939
            PropertySheet.class);
940
    }
941
    
942
    static boolean shouldShowDescription() {
943
        return getPreferences().getBoolean(PREF_KEY_SHOWDESCRIPTION, true);
944
    }
945
    
946
    static void saveShowDescription(boolean b) {
947
        getPreferences().putBoolean(PREF_KEY_SHOWDESCRIPTION, b);
948
    }
949
    
950
    static String[] getSavedClosedSetNames () {
951
        String s = getPreferences().get(PREF_KEY_CLOSEDSETNAMES, null);
952
        if (s != null) {
953
            StringTokenizer tok = new StringTokenizer (s, ","); //NOI18N
954
            String[] result = new String[tok.countTokens()];
955
            int i=0;
956
            while (tok.hasMoreElements()) {
957
                result[i] = tok.nextToken();
958
                i++;
959
            }
960
            return result;
961
        } else {
962
            return new String[0];
963
        }
964
    }
965
    
966
    static void putSavedClosedSetNames (Set s) {
967
        if (s.size() > 0) {
968
            StringBuffer sb = new StringBuffer(s.size() * 20);
969
            Iterator i = s.iterator();
970
            while (i.hasNext()) {
971
                sb.append (i.next());
972
                if (i.hasNext()) {
973
                    sb.append (','); //NOI18N
974
                }
975
            }
976
            getPreferences().put (PREF_KEY_CLOSEDSETNAMES, sb.toString());
977
        } else {
978
            getPreferences().put (PREF_KEY_CLOSEDSETNAMES, "");
979
        }
980
    }
981
    
982
    static void putSortOrder (int i) {
983
        getPreferences().putInt(PREF_KEY_SORTORDER, i);
984
    }
985
    
986
    static int getSavedSortOrder() {
987
        return getPreferences().getInt(PREF_KEY_SORTORDER, PropertySheet.UNSORTED);
988
    }
989
    
990
    static boolean shouldDrawMargin (PropertySetModel psm) {
991
        if (neverMargin) return false;
992
        //hide margin and expansion handle if only one property
993
        //set, if flag is set (there is disagreement about the
994
        //right thing to do here, so it's an option for now)
995
        int setCount = psm.getSetCount();
996
        if (psm.getComparator() != null) return false;
997
        
998
        boolean includeMargin = 
999
            setCount > 1 ||
1000
            (!(setCount == 1 && hideSingleExpansion));
1001
        return includeMargin;
1002
    }
1003
    
1004
}
(-)src/org/openide/explorer/propertysheet/PropertyDialogManager.java (+4 lines)
 Lines 355-360    Link Here 
355
                }
355
                }
356
            }
356
            }
357
            );
357
            );
358
            if (component == null) {
359
                throw new NullPointerException (editor.getClass().getName()
360
                    + " returned null from getCustomEditor()");
361
            }
358
            component.addPropertyChangeListener(listener =
362
            component.addPropertyChangeListener(listener =
359
                new PropertyChangeListener() {
363
                new PropertyChangeListener() {
360
                    /** forward possible help context change in custom editor */
364
                    /** forward possible help context change in custom editor */
(-)src/org/openide/explorer/propertysheet/PropertyEnv.java (-4 / +52 lines)
 Lines 22-33    Link Here 
22
22
23
23
24
/**
24
/**
25
 * Instance of this class contains information that
25
 * PropertyEnv is a class which allows an object (such as a 
26
 * is being passed to the property editor (instance of
26
 * Node.Property instance) to communicate hints to property
27
 * ExtendedPropertyEditor) from the IDE.
27
 * editor instances that affect the behavior or visual
28
 * rendering of the property in a <code>PropertySheet</code>
29
 * or <code>PropertyPanel</code> component.  An instance of
30
 * PropertyEnv is passed to the <code>attachEnv()</code>
31
 * method of property editors implementing ExPropertyEditor.
32
 * Such property editors may then call <code>
33
 * env.getFeatureDescriptor().get("someHintString")</code>
34
 * to retrieve hints that affect their behavior from the
35
 * Node.Property object whose properties they are displaying.
36
 * <P><strong>Note:</strong>
37
 * client code should treat this class as final;  it should
38
 * never be necessary to create instances of this class 
39
 * outside the propertysheet package or to subclass it.
28
 * @author  dstrupl
40
 * @author  dstrupl
29
 */
41
 */
30
public final class PropertyEnv {
42
public class PropertyEnv {
31
  
43
  
32
    /** Name of the state property. */
44
    /** Name of the state property. */
33
    public static final String PROP_STATE = "state"; //NOI18N
45
    public static final String PROP_STATE = "state"; //NOI18N
 Lines 236-241    Link Here 
236
            change = new PropertyChangeSupport (this);
248
            change = new PropertyChangeSupport (this);
237
        }
249
        }
238
        return change;
250
        return change;
251
    }
252
    
253
    InplaceEditor.Factory factory=null;
254
    
255
    /**Register a factory for InplaceEditor instances that the property
256
     * sheet should use as an inline editor for the property.
257
     * This allows modules to supply custom inline editors globally
258
     * for a type.  It can be overridden on a property-by-property
259
     * basis for properties that supply a hint for an inplace editor
260
     * using <code>getValue(String)</code>.
261
     * @since 1.13
262
     * @see org.openide.nodes.Node.Property     
263
     * @see org.openide.explorer.propertysheet.InplaceEditor
264
     */
265
    public void registerInplaceEditorFactory (InplaceEditor.Factory 
266
                                                   factory) {
267
        this.factory = factory;
268
    }
269
    
270
    InplaceEditor getInplaceEditor() {
271
        InplaceEditor result;
272
        if (factory != null) {
273
            result = factory.getInplaceEditor();
274
        } else {
275
            result = null;
276
        }
277
        return result;
278
    }
279
    
280
    boolean editable=true;
281
    void setEditable (boolean editable) {
282
        this.editable = editable;
283
    }
284
    
285
    boolean isEditable () {
286
        return editable;
239
    }
287
    }
240
    
288
    
241
}
289
}
(-)src/org/openide/explorer/propertysheet/PropertyModel.java (-2 / +6 lines)
 Lines 16-24    Link Here 
16
import java.beans.PropertyChangeListener;
16
import java.beans.PropertyChangeListener;
17
import java.lang.reflect.InvocationTargetException;
17
import java.lang.reflect.InvocationTargetException;
18
18
19
/** Model defining the right behaviour of property.
19
/** 
20
* This model should be used for communication with PropertyBean bean.
20
* A model defining the behavior of a property.  This model is used to allow
21
* components such as PropertyPanel to be used with arbitrary JavaBeans, without
22
* requiring them to be instances of Node.Property.
21
*
23
*
24
* @see DefaultPropertyModel
25
* @see NodePropertyModel
22
* @author Jaroslav Tulach, Petr Hamernik
26
* @author Jaroslav Tulach, Petr Hamernik
23
*/
27
*/
24
public interface PropertyModel {
28
public interface PropertyModel {
(-)src/org/openide/explorer/propertysheet/PropertyPanel.java (-133 / +59 lines)
 Lines 60-86    Link Here 
60
import javax.swing.text.Document;
60
import javax.swing.text.Document;
61
61
62
62
63
/** Visual Java Bean for editing of properties. It takes the model
63
/** A visual component for editing individual properties. It takes an instance of
64
 * and represents the property editor for it.
64
 * PropertyModel and displays an editor component for it.
65
 *
66
 * @author Jaroslav Tulach, Petr Hamernik, Jan Jancura, David Strupl
65
 * @author Jaroslav Tulach, Petr Hamernik, Jan Jancura, David Strupl
67
 */
66
 */
68
public class PropertyPanel extends JComponent implements javax.accessibility.Accessible {
67
public class PropertyPanel extends JComponent implements javax.accessibility.Accessible {
69
68
70
    /**
69
    /** Constant defining a preference for rendering the value.
71
     * Constant defining preferences in displaying of value.
72
     * Value should be displayed in read-only mode.
70
     * Value should be displayed in read-only mode.
73
     */
71
     */
74
    public static final int PREF_READ_ONLY = 0x0001;
72
    public static final int PREF_READ_ONLY = 0x0001;
75
73
76
    /**
74
    /** Constant defining a preference for rendering the value.
77
     * Constant defining preferences in displaying of value.
78
     * Value should be displayed in custom editor.
75
     * Value should be displayed in custom editor.
79
     */
76
     */
80
    public static final int PREF_CUSTOM_EDITOR = 0x0002;
77
    public static final int PREF_CUSTOM_EDITOR = 0x0002;
81
78
82
    /**
79
    /** Constant defining a preference for rendering the value.
83
     * Constant defining preferences in displaying of value.
84
     * Value should be displayed in editor only.
80
     * Value should be displayed in editor only.
85
     */
81
     */
86
    public static final int PREF_INPUT_STATE = 0x0004;
82
    public static final int PREF_INPUT_STATE = 0x0004;
 Lines 279-293    Link Here 
279
            disabledColor = UIManager.getColor("textInactiveText");
275
            disabledColor = UIManager.getColor("textInactiveText");
280
            foregroundColor = new Color (0, 0, 128);
276
            foregroundColor = new Color (0, 0, 128);
281
        }
277
        }
282
        
283
        model.addPropertyChangeListener (getModelListener());
278
        model.addPropertyChangeListener (getModelListener());
284
        updateEditor ();
279
        updateEditor ();
285
        reset();
280
        reset();
286
    }
281
    }
287
282
288
    /** Uses an instance of SimpleModel as model.*/
283
    /** Uses an instance of NodePropertyModel as model.*/
289
    PropertyPanel(Node.Property p, Object []beans) {
284
    PropertyPanel(Node.Property p, Object []beans) {
290
        this(new SimpleModel(p, beans), 0);
285
        this(new NodePropertyModel(p, beans), 0);
291
    }
286
    }
292
    
287
    
293
    
288
    
 Lines 486-493    Link Here 
486
        c.setToolTipText(getPanelToolTipText());
481
        c.setToolTipText(getPanelToolTipText());
487
        add (c, BorderLayout.CENTER);
482
        add (c, BorderLayout.CENTER);
488
483
489
        updateNeighbourPanels();
490
        
491
        revalidate();
484
        revalidate();
492
        repaint ();
485
        repaint ();
493
486
 Lines 585-604    Link Here 
585
        return isWriteState;
578
        return isWriteState;
586
    }
579
    }
587
    
580
    
588
    // private methods -------------------------------------------------------
589
    
590
    /** Updates all neighbour panels. */
591
    private void updateNeighbourPanels() {
592
        Component c = getParent();
593
        while ((c != null) && (!(c instanceof NamesPanel))) {
594
            c = c.getParent();
595
        }
596
        if (c instanceof NamesPanel) {
597
            NamesPanel np = (NamesPanel)c;
598
            np.reset();
599
        }
600
    }
601
    
602
    /**
581
    /**
603
     * Update the current property editor depending on the model.
582
     * Update the current property editor depending on the model.
604
     */
583
     */
 Lines 1232-1247    Link Here 
1232
    /** Gets <code>customizedButton</code>, so called 'three-dot-button'. */
1211
    /** Gets <code>customizedButton</code>, so called 'three-dot-button'. */
1233
    private SheetButton getCustomizeButton() {
1212
    private SheetButton getCustomizeButton() {
1234
        if (customizeButton == null) {
1213
        if (customizeButton == null) {
1235
            customizeButton = new SheetButton("...", plastic, plastic); // NOI18N
1214
            customizeButton = new SheetButton(cLabel, plastic, plastic); // NOI18N
1236
            customizeButton.setFocusTraversable(true);
1215
            customizeButton.setFocusTraversable(true);
1237
            
1216
            
1238
            Font currentFont = customizeButton.getFont ();
1217
            Font currentFont = customizeButton.getFont ();
1239
            customizeButton.setFont (
1218
            customizeButton.setFont(
1240
                new Font (
1219
                currentFont.deriveFont(currentFont.getStyle () | Font.BOLD)
1241
                    currentFont.getName (),
1242
                    currentFont.getStyle () | Font.BOLD,
1243
                    currentFont.getSize ()
1244
                )
1245
            );
1220
            );
1246
            customizeButton.addFocusListener(getWriteComponentListener());
1221
            customizeButton.addFocusListener(getWriteComponentListener());
1247
            customizeButton.addSheetButtonListener(new CustomizeListener());
1222
            customizeButton.addSheetButtonListener(new CustomizeListener());
 Lines 1258-1263    Link Here 
1258
        
1233
        
1259
        return customizeButton;
1234
        return customizeButton;
1260
    }
1235
    }
1236
    /*
1237
    private char processMnemonic (final String s) {
1238
        int i = s.indexOf ("&"); //NOI18N
1239
        char result = '-';
1240
        if ((i != -1) && (i < s.length() -1)) {
1241
            StringBuffer sb = new StringBuffer (s);
1242
            sb.deleteCharAt(i);
1243
            result = sb.charAt(i);
1244
            s = sb.toString();
1245
        }
1246
        return result;
1247
    }
1248
     */
1249
    
1250
    /** Property that, when fired, indicates that the text for the
1251
     *  label on the custom editor button has been changed */
1252
    public static final String PROP_CUSTOMIZEBUTTONLABEL = 
1253
        "customizeButtonLabel"; //NOI18N
1254
    /** Default value for the text of the customize button */
1255
    private static final String DEFAULT_CBLABEL="..."; //NOI18N
1256
    /** Holds the value of the text for the customize button, including
1257
     * mnemonic 
1258
     * @since 1.125 */
1259
    private String cLabel = DEFAULT_CBLABEL;
1260
    
1261
    /** Get the value of the text for 
1262
     */
1263
    public String getCustomizeButtonLabel () {
1264
        return cLabel;
1265
    }
1266
    
1267
    /** Set the text for the button which will launch the custom
1268
     *  property editor.
1269
     *
1270
     * @param s The text to use, or null to restore the default.  
1271
     *  The first occurance of &amp; in the string indicates the
1272
     *  subsequent character should be the mnemonic for the button.
1273
     * @since 1.125
1274
     */
1275
    public void setCustomizeButtonLabel (String s) {
1276
        if (cLabel.equals (s)) return;
1277
        if (s == null) s = "..."; //NOI18N
1278
        String old = cLabel;
1279
        cLabel = s;
1280
        if (customizeButton != null) {
1281
            customizeButton.setLabel (s);
1282
        }
1283
        firePropertyChange(PROP_CUSTOMIZEBUTTONLABEL, old, s);
1284
    }
1261
1285
1262
    /** Processes <code>Exception</code> thrown from 
1286
    /** Processes <code>Exception</code> thrown from 
1263
     * <code>setAsText</code> or <code>setValue</code> call 
1287
     * <code>setAsText</code> or <code>setValue</code> call 
 Lines 1440-1543    Link Here 
1440
        public void removePropertyChangeListener(PropertyChangeListener l) {
1464
        public void removePropertyChangeListener(PropertyChangeListener l) {
1441
        }
1465
        }
1442
    } // End of class EmptyModel.
1466
    } // End of class EmptyModel.
1443
    
1444
    
1445
    /** Implementation of the <code>PropertyModel</code> interface keeping 
1446
     * a <code>Node.Property</code>. */
1447
    static class SimpleModel implements ExPropertyModel {
1448
        /** Property to work with. */
1449
        private Node.Property prop;
1450
        /** Array of beans(nodes) to which belong the property. */
1451
        private Object []beans;
1452
        /** Property change support. */
1453
        private PropertyChangeSupport sup = new PropertyChangeSupport(this);
1454
1455
        /** Construct simple model instance.
1456
         * @param property proeprty to work with
1457
         * @param beans array of beans(nodes) to which belong the property */
1458
        public SimpleModel(Node.Property property, Object[] beans) {
1459
            this.prop = property;
1460
            this.beans = beans;
1461
        }
1462
        
1463
        
1464
        /** Implements <code>PropertyModel</code> interface. */
1465
        public Object getValue() throws InvocationTargetException {
1466
            try {
1467
                return prop.getValue();
1468
            } catch(IllegalAccessException iae) {
1469
                throw annotateException(iae);
1470
            } catch(InvocationTargetException ite) {
1471
                throw annotateException(ite);
1472
            }
1473
        }
1474
        
1475
        /** Implements <code>PropertyModel</code> interface. */
1476
        public void setValue(Object v) throws InvocationTargetException {
1477
            try {
1478
                prop.setValue(v);
1479
                sup.firePropertyChange(PropertyModel.PROP_VALUE, null, null);
1480
            } catch(IllegalAccessException iae) {
1481
                throw annotateException(iae);
1482
            } catch(IllegalArgumentException iaae) {
1483
                throw annotateException(iaae);
1484
            } catch(InvocationTargetException ite) {
1485
                throw annotateException(ite);
1486
            }
1487
        }
1488
1489
        /** Annotates specified exception. Helper method.
1490
         * @param exception original exception to annotate
1491
         * @return <code>IvocationTargetException</code> which annotates the
1492
         *       original exception */
1493
        private InvocationTargetException annotateException(Exception exception) {
1494
            if(exception instanceof InvocationTargetException) {
1495
                return (InvocationTargetException)exception;
1496
            } else {
1497
                return new InvocationTargetException(exception);
1498
            }
1499
        }
1500
1501
        /** Implements <code>PropertyModel</code> interface. */
1502
        public Class getPropertyType() {
1503
            return prop.getValueType();
1504
        }
1505
1506
        /** Implements <code>PropertyModel</code> interface. */
1507
        public Class getPropertyEditorClass() {
1508
            Object ed = prop.getPropertyEditor();
1509
            if (ed != null) {
1510
                return ed.getClass();
1511
            }
1512
            return null;
1513
        }
1514
1515
        /** Implements <code>PropertyModel</code> interface. */
1516
        public void addPropertyChangeListener(PropertyChangeListener l) {
1517
            sup.addPropertyChangeListener(l);
1518
        }
1519
1520
        /** Implements <code>PropertyModel</code> interface. */
1521
        public void removePropertyChangeListener(PropertyChangeListener l) {
1522
            sup.removePropertyChangeListener(l);
1523
        }
1524
        
1525
        /** Implements <code>ExPropertyModel</code> interface. */
1526
        public Object[] getBeans() {
1527
            return beans;
1528
        }
1529
        
1530
        /** Implements <code>ExPropertyModel</code> interface. */
1531
        public FeatureDescriptor getFeatureDescriptor() {
1532
            return prop;
1533
        }
1534
        
1535
        void fireValueChanged() {
1536
            sup.firePropertyChange(PropertyModel.PROP_VALUE, null, null);
1537
        }
1538
1539
    } // End of class SimpleModel.
1540
1541
    
1467
    
1542
    /** Listener on textField(<code>JTextField</code>) or comboBox(<code>JComboBox</code>)
1468
    /** Listener on textField(<code>JTextField</code>) or comboBox(<code>JComboBox</code>)
1543
     * - input line components controlled by this panel.
1469
     * - input line components controlled by this panel.
(-)src/org/openide/explorer/propertysheet/PropertyRenderer.java (+309 lines)
Added Link Here 
1
/*
2
 *                 Sun Public License Notice
3
 * 
4
 * The contents of this file are subject to the Sun Public License
5
 * Version 1.0 (the "License"). You may not use this file except in
6
 * compliance with the License. A copy of the License is available at
7
 * http://www.sun.com/
8
 * 
9
 * The Original Code is NetBeans. The Initial Developer of the Original
10
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun
11
 * Microsystems, Inc. All Rights Reserved.
12
 */
13
/*
14
 * PropertyRenderer.java
15
 *
16
 * Created on June 5, 2003, 10:59 PM
17
 */
18
19
package org.openide.explorer.propertysheet;
20
import java.awt.*;
21
import java.awt.event.*;
22
import javax.swing.*;
23
import javax.swing.event.*;
24
import javax.swing.border.Border;
25
import org.openide.nodes.Node.Property;
26
import org.openide.util.NbBundle;
27
28
/** A JComponent that can render a property value using the same infrastructure as the
29
 *  property sheet.  Allows components such as PropertyPanel and TreeTableView to 
30
 *  reuse the lightweight rendering infrastructure of the property sheet.  Generally
31
 *  this class is not useful to module authors;  it is public because for historical
32
 *  reasons, explorer and the property sheet do not share a package.
33
 *
34
 * @author  Tim Boudreau
35
 */
36
final class PropertyRenderer extends JPanel implements FocusListener {
37
    
38
    /** Creates a new instance of PropertyRenderer */
39
    private PropertyRenderer() {
40
        setBorder (BorderFactory.createLineBorder (Color.ORANGE)); //XXX
41
        Action editAction = new EditAction();
42
        Action customEditAction = new CustomEditAction();
43
        Action cancelAction = new CancelAction();
44
        InputMap imp = getInputMap (WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
45
        ActionMap am = getActionMap();
46
        imp.put (KeyStroke.getKeyStroke (KeyEvent.VK_SPACE, 0), "edit");
47
        imp.put (KeyStroke.getKeyStroke (KeyEvent.VK_ESCAPE, 0), "cancel");
48
        imp.put (KeyStroke.getKeyStroke (KeyEvent.VK_SPACE, KeyEvent.CTRL_DOWN_MASK), "custom");
49
        am.put ("edit", editAction);
50
        am.put ("cancel", cancelAction);
51
        am.put ("custom", customEditAction);
52
        this.setFocusable(true);
53
    }
54
    
55
    public void processFocusEvent (FocusEvent fe) {
56
        super.processFocusEvent (fe);
57
        if (fe.getID() == FocusEvent.FOCUS_GAINED) {
58
            edit();
59
        }
60
    }
61
    
62
    
63
    public static JComponent createPropertyRenderer (Property p) {
64
        PropertyRenderer result = new PropertyRenderer();
65
        result.setProperty (p);
66
        return result;
67
    }
68
    
69
    /** Utility method for rendering a property, for use by tables and other components
70
     *  that simply need to paint a property value somewhere, and do not want to instantiate
71
     *  a component to do it   */
72
    public static void renderProperty (Property p, Graphics g, JComponent c, boolean selected, int x, int y, int w, int h) {
73
        JComponent renderer = SheetCellRenderer.getDefault().getRenderer(p, selected, false, false);
74
        SheetCellRenderer.getDefault().configureRenderer(p, renderer, selected, false, renderer.hasFocus(), c.getForeground(), c.getBackground(), c.getForeground(), Color.BLUE.brighter().brighter());
75
        Graphics rg = g.create(x, y, w, h);
76
        renderer.setBackground (Color.WHITE);
77
        if (renderer instanceof JComboBox) {
78
            ((JComboBox) renderer).setUI (PropUtils.createComboUI((JComboBox) renderer));
79
        }
80
        try {
81
            renderer.setBounds (0,0,  w, h);
82
            LayoutManager lm = renderer.getLayout();
83
            //Do this for combo boxes
84
            if (lm != null) {
85
                renderer.getLayout().layoutContainer(renderer);
86
            }
87
            renderer.paint (rg);
88
        } finally {
89
            rg.dispose();
90
        }
91
    }
92
    
93
    private Property prop=null;
94
    void setProperty (Property p) {
95
        if (prop != p) {
96
            prop = p;
97
            if (getParent() != null) {
98
                repaint();
99
            }
100
        }
101
    }
102
    
103
    public void paint (Graphics g) {
104
        if (prop != null) {
105
            this.paintBorder(g);
106
            renderProperty (prop, g, this, false, 0, 0, getWidth(), getHeight());
107
        } else {
108
            super.paint(g);
109
        }
110
    }
111
    
112
    public void addNotify () {
113
        this.addMouseListener (getMouseListener());
114
        setLayout (getTrivialLayout());
115
        super.addNotify();
116
    }
117
    
118
    public void removeNotify() {
119
        if (editorComp != null) {
120
            cancelEdit();
121
        }
122
        super.removeNotify();
123
        setLayout (null);
124
        this.removeMouseListener (mouseListener);
125
    }
126
    
127
    private static MouseListener mouseListener=null;
128
    private static final MouseListener getMouseListener() {
129
        if (mouseListener == null) {
130
            mouseListener = new PrMl();
131
        }
132
        return mouseListener;
133
    }
134
    
135
    private boolean onCustomEditorButton (MouseEvent e) {
136
        if (PropUtils.noCustomButtons) return false;
137
        //see if we're in the approximate bounds of the custom editor button
138
        if (e.getX() > getWidth() - ButtonPanel.customEditorButton.getWidth()) {
139
            Point pt = e.getPoint();
140
            boolean supp = PropUtils.getPropertyEditor(prop).supportsCustomEditor();
141
            return (supp);
142
        }
143
        return false;
144
    }
145
    
146
    private static PropertyRenderer editingRenderer = null;
147
    private static void cancelOtherEdits () {
148
        if (editingRenderer != null) {
149
            editingRenderer.cancelEdit();
150
        }
151
    }
152
    
153
    private void edit (InputEvent ie) {
154
        cancelOtherEdits();
155
        editingRenderer = this;
156
        if (prop.getValueType() == Boolean.class || 
157
            prop.getValueType() == Boolean.TYPE) {
158
                try {
159
                    System.out.println("Doing boolean toggle");
160
                    Boolean val = (Boolean) prop.getValue();
161
                    if (Boolean.TRUE.equals (val)) {
162
                        prop.setValue (Boolean.FALSE);
163
                    } else {
164
                        prop.setValue (Boolean.TRUE);
165
                    }
166
                    revalidate();
167
                    repaint();
168
                } catch (Exception e) {
169
                    //XXX
170
                }
171
        } else {
172
            edit();
173
            InplaceEditor ine = SheetCellEditor.getDefault().getInplaceEditor();
174
            ine.handleInitialInputEvent(ie);
175
        }
176
    }
177
    
178
    Component editorComp=null;
179
    private void edit() {
180
        if (editorComp != null) return;
181
        ActionListener al = new ActionListener () {
182
            public void actionPerformed (ActionEvent ae) {
183
                System.out.println("Action: " + ae.getActionCommand());
184
                if (ae.getActionCommand() == InplaceEditor.COMMAND_SUCCESS) {
185
                    stopEdit();
186
                } else if (ae.getActionCommand() == InplaceEditor.COMMAND_FAILURE) {
187
                    cancelEdit();
188
                }
189
            }
190
        };
191
        //XXX remove the listener!
192
        editorComp = SheetCellEditor.getDefault().getEditorComponent (
193
            prop, al, getForeground(), getBackground(), getForeground(), 
194
            getBackground());
195
        if (editorComp instanceof JComboBox) {
196
            ((JComboBox) editorComp).setUI (PropUtils.createComboUI((JComboBox) editorComp));
197
        }
198
        add (editorComp);
199
        editorComp.addFocusListener (this);
200
        revalidate();
201
        repaint();
202
    }
203
    
204
    private void stopEdit() {
205
        cancelEdit();
206
    }
207
    
208
    private void cancelEdit() {
209
        editorComp.removeFocusListener(this);
210
        remove (editorComp);
211
        editorComp = null;
212
        revalidate();
213
        repaint();
214
    }
215
    
216
    static class PrMl extends MouseAdapter {
217
        public void mousePressed (MouseEvent me) {
218
            PropertyRenderer ren = (PropertyRenderer) me.getSource();
219
            if (ren.onCustomEditorButton(me)) {
220
                //invoke custom editor
221
            } else {
222
                ren.edit();
223
            }
224
        }
225
    }
226
    
227
    
228
    static LayoutManager trivialLayout=null;
229
    static LayoutManager getTrivialLayout() {
230
        if (trivialLayout == null) {
231
            trivialLayout = new TrivialLayout();
232
        }
233
        return trivialLayout;
234
    }
235
    
236
    static Dimension defaultSize = new Dimension (80, 20);
237
    static class TrivialLayout implements LayoutManager {
238
        
239
        public void addLayoutComponent(String name, Component comp) {
240
            //do nothing
241
        }
242
        
243
        public void layoutContainer(Container parent) {
244
            PropertyRenderer ren = ((PropertyRenderer) parent);
245
            Border b = ren.getBorder();
246
            Insets in=null;
247
            if (b != null) {
248
                in = b.getBorderInsets(ren);
249
            }
250
            if (ren.editorComp != null) {
251
                if (in == null) {
252
                    ren.editorComp.setBounds (0, 0, ren.getWidth(), ren.getHeight());
253
                } else {
254
                    ren.editorComp.setBounds (in.left, in.top, ren.getWidth() - in.right, ren.getHeight() - in.bottom);
255
                }
256
            }
257
        }
258
        
259
        public Dimension minimumLayoutSize(Container parent) {
260
            return defaultSize; //XCXX
261
        }
262
        
263
        public Dimension preferredLayoutSize(Container parent) {
264
            return defaultSize; //XCXX
265
        }
266
        
267
        public void removeLayoutComponent(Component comp) {
268
            //do nothing
269
        }
270
        
271
    }
272
    
273
    public void focusGained(FocusEvent e) {
274
    }
275
    
276
    public void focusLost(FocusEvent e) {
277
        InplaceEditor ine = SheetCellEditor.getDefault().getInplaceEditor();
278
        if (ine != null) {
279
            if (!ine.isKnownComponent(e.getOppositeComponent())) {
280
                cancelEdit();
281
            }
282
        }
283
    }
284
    
285
    public class CancelAction extends AbstractAction {
286
        
287
        public void actionPerformed(ActionEvent e) {
288
            cancelEdit();
289
        }
290
        
291
    }
292
    
293
    public class EditAction extends AbstractAction {
294
        
295
        public void actionPerformed(ActionEvent e) {
296
            edit();
297
        }
298
        
299
    }
300
    
301
    public class CustomEditAction extends AbstractAction {
302
        
303
        public void actionPerformed(ActionEvent e) {
304
            //invoke custom editor
305
        }
306
        
307
    }
308
    
309
}
(-)src/org/openide/explorer/propertysheet/PropertySetModel.java (+79 lines)
Added Link Here 
1
/*
2
 *                 Sun Public License Notice
3
 * 
4
 * The contents of this file are subject to the Sun Public License
5
 * Version 1.0 (the "License"). You may not use this file except in
6
 * compliance with the License. A copy of the License is available at
7
 * http://www.sun.com/
8
 * 
9
 * The Original Code is NetBeans. The Initial Developer of the Original
10
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun
11
 * Microsystems, Inc. All Rights Reserved.
12
 */
13
/*
14
 * PropertySetModel.java
15
 *
16
 * Created on January 5, 2003, 5:22 PM
17
 */
18
19
package org.openide.explorer.propertysheet;
20
import java.beans.*;
21
import java.util.Comparator;
22
import org.openide.nodes.Node.*;
23
/** Interface for the property set model for property sheets.
24
 *  PropertySetModel is a model-within-a-model that manages
25
 *  the available properties and property sets and 
26
 *  the available property sets' expanded state.  Since
27
 *  both Node.Property and Node.PropertySet extend FeatureDescriptor,
28
 *  the getters for indexed elements return FeatureDescriptor
29
 *  objects - clients must test and cast to determine which
30
 *  they are dealing with.
31
 *  @author  Tim Boudreau
32
 */
33
interface PropertySetModel {
34
    
35
    /** Determines if a given property set is expanded */
36
    boolean isExpanded (FeatureDescriptor fd);
37
    
38
    /** Set the expanded state for a feature descriptor of the
39
     *  given index.  */
40
    void toggleExpanded (int index);
41
    
42
    /** Returns either a Node.Property or a Node.PropertySet 
43
     *  instance for a given index.  */
44
    FeatureDescriptor getFeatureDescriptor (int index);
45
    
46
    /** Registers a PropertySetModelListener to receive events.
47
     * @param listener The listener to register. */
48
    void addPropertySetModelListener(PropertySetModelListener listener);
49
    
50
    /** Removes a PropertySetModelListener from the list of listeners.
51
     * @param listener The listener to remove. */
52
    void removePropertySetModelListener(PropertySetModelListener listener);
53
    
54
    /** Assign the property sets this model will manage */
55
    void setPropertySets (PropertySet[] sets);
56
    
57
    /** Utility method to determine if a given index holds a property
58
     *  or property set.*/
59
    boolean isProperty (int index);
60
    
61
    /** Get the number of feature descriptors (properties and
62
     *  property sets) currently represented by the model, not
63
     *  including properties belonging to unexpanded property
64
     *  sets - in other words, the current number of objects
65
     *  a component rendering this model is being asked to display. */
66
    int getCount ();
67
    
68
    /** Get the index, in the model, of a given feature descriptor.
69
     *  If it is not currently available (either not part of the model
70
     *  at all, or part of an unexpanded property set), returns -1. */
71
    int indexOf (FeatureDescriptor fd);
72
73
    /** Set the comparator the model will use for sorting properties */
74
    void setComparator (Comparator c);
75
    
76
    int getSetCount();
77
    
78
    Comparator getComparator ();
79
}
(-)src/org/openide/explorer/propertysheet/PropertySetModelEvent.java (+80 lines)
Added Link Here 
1
/*
2
 *                 Sun Public License Notice
3
 * 
4
 * The contents of this file are subject to the Sun Public License
5
 * Version 1.0 (the "License"). You may not use this file except in
6
 * compliance with the License. A copy of the License is available at
7
 * http://www.sun.com/
8
 * 
9
 * The Original Code is NetBeans. The Initial Developer of the Original
10
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun
11
 * Microsystems, Inc. All Rights Reserved.
12
 */
13
/*
14
 * PropertySetModelEvent.java
15
 *
16
 * Created on December 30, 2002, 11:56 AM
17
 */
18
19
package org.openide.explorer.propertysheet;
20
import java.awt.event.*;
21
/** Event type that carries information about changes in the property
22
 *  set model, such as changes in the number of rows that should be 
23
 *  shown in the table due to expanding or closing categories.  In
24
 *  particular it is used to maintain the current selection across a
25
 *  change which can alter the selected index.
26
 * @author  Tim Boudreau
27
 */
28
class PropertySetModelEvent extends java.util.EventObject {
29
    public static final int TYPE_INSERT=0;
30
    public static final int TYPE_REMOVE=1;
31
    public static final int TYPE_WHOLESALE_CHANGE=2;
32
    int type=2;
33
    int start=-1;
34
    int end=-1;
35
    boolean reordering=false;
36
    
37
    /**  Create a new model event of <code>TYPE_WHOLESALE_CHANGE</code>. */
38
    public PropertySetModelEvent(Object source) {
39
        super (source);
40
    }
41
42
    /** Create a new model event with the specified parameters. */
43
    public PropertySetModelEvent(Object source, int type, int start, 
44
        int end, boolean reordering) {
45
        super (source);
46
        this.type=type;
47
        this.start = start;
48
        this.end = end;
49
        this.reordering = reordering;
50
    }
51
    
52
    /** Get the type of event.  This will be one of 
53
    * TYPE_INSERT, 
54
    * TYPE_REMOVE, or
55
    * TYPE_WHOLESALE_CHANGE,
56
    * depending on the type of change (expansion of a category,
57
    * de-expansion of a category, or a wholesale change like changing
58
    * the node displayed, which completely invalidates the displayed
59
    * data.  */
60
    public int getType () {
61
        return type;
62
    }
63
    
64
    /** Get the first row affected by this change.  */
65
    public int getStartRow () {
66
        return start;
67
    }
68
    
69
    /** Get the last row affected by this change.  This should be the
70
     *  affected row <strong>prior</strong> to the change;  that is, if
71
     *  a category is de-expanded, removing properties 20-30, this value
72
     *  should be 30. */
73
    public int getEndRow () {
74
        return end;
75
    }
76
    
77
    public boolean isReordering () {
78
        return reordering;
79
    }
80
}
(-)src/org/openide/explorer/propertysheet/PropertySetModelImpl.java (+309 lines)
Added Link Here 
1
/*
2
 *                 Sun Public License Notice
3
 * 
4
 * The contents of this file are subject to the Sun Public License
5
 * Version 1.0 (the "License"). You may not use this file except in
6
 * compliance with the License. A copy of the License is available at
7
 * http://www.sun.com/
8
 * 
9
 * The Original Code is NetBeans. The Initial Developer of the Original
10
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun
11
 * Microsystems, Inc. All Rights Reserved.
12
 */
13
/*
14
 * PropertySetModel.java
15
 *
16
 * Created on December 28, 2002, 6:57 PM
17
 */
18
19
package org.openide.explorer.propertysheet;
20
import org.openide.nodes.Node;
21
import org.openide.nodes.Node.Property;
22
import org.openide.nodes.Node.PropertySet;
23
import java.beans.*;
24
import java.util.*;
25
import javax.swing.event.*;
26
import org.openide.util.NbBundle;
27
28
/** A model defining the expansion state for property sets, and
29
 *  thus the index order of properties.  The model exposes both
30
 *  properties and property sets.  For cases where only a subset of
31
 *  the property sets available on the node should be displayed in
32
 *  a table, this class can be subclassed to do such filtering.
33
 *  <P><B>Implementation note:</B>
34
 *  The current implementation is fairly naive, in order to get the
35
 *  rest of the new PropertySheet code working - it builds a list of
36
 *  the properties and property sets it will expose, and rebuilds it
37
 *  on a change.  This should eventually be replaced by a better 
38
 *  performing implementation.  
39
 *
40
  * @author  Tim Boudreau
41
 */
42
class PropertySetModelImpl implements PropertySetModel {
43
    private boolean[] expanded=null;
44
    private ArrayList fds=new ArrayList();
45
    private Comparator comparator=null;
46
    private transient java.util.ArrayList listenerList;
47
    private PropertySet[] sets = null;
48
    private transient int setCount = 0;
49
    
50
    /** Retains the persistent list of sets the user has explicitly
51
     *  closed, so they remain closed for other similar nodes
52
     */
53
    static HashSet closedSets = new HashSet (5);
54
    static {
55
        closedSets.addAll(Arrays.asList (PropUtils.getSavedClosedSetNames()));
56
    }
57
    
58
    public PropertySetModelImpl() {
59
    }
60
    
61
    public PropertySetModelImpl(PropertySet[] ps) {
62
        if (ps == null) ps = new PropertySet[0];
63
        setPropertySets (ps);
64
    }
65
    
66
    public int getCount() {
67
        return fds.size();
68
    }
69
    
70
    public  FeatureDescriptor getFeatureDescriptor(int index) {
71
        if (index == -1) return null;
72
        return (FeatureDescriptor) fds.get(index);
73
    }
74
    
75
    public int indexOf(FeatureDescriptor fd) {
76
        return fds.indexOf (fd);
77
    }
78
    
79
    public boolean isProperty(int index) {
80
        return getFeatureDescriptor (index) instanceof Node.Property;
81
    }
82
    
83
    public void setComparator(Comparator c) {
84
        if (c != comparator) {
85
            firePendingChange (true);
86
            comparator = c;
87
            PropertySet[] oldsets = sets;
88
            fds.clear();
89
            init();
90
            fireChange(true);
91
        }
92
    }
93
    
94
    public void setPropertySets(PropertySet[] sets) {
95
        setCount = sets == null ? 0 : sets.length;
96
        if (sets == this.sets) return;
97
        firePendingChange (false);
98
        if (sets == null) sets = new PropertySet[0];
99
        this.sets = sets;
100
        resetArray (sets);
101
        init();
102
        fireChange(false);
103
    }
104
    
105
    private int firstSetIndex=-1;
106
    private void init () {
107
        fds.clear();
108
        if (comparator == null) {
109
            initExpandable();
110
        } else {
111
            initPlain();
112
        }
113
    }
114
    
115
    private void initPlain () {
116
        if (sets == null) return;
117
        int pcount=0;
118
        
119
        for (int i=0; i < sets.length; i++) {
120
            pcount += sets[i].getProperties().length;
121
        }
122
        Property[] props = new Property[pcount];
123
        int l = 0;
124
        for (int i=0; i < sets.length; i++) {
125
            Property[] p = sets[i].getProperties();
126
            System.arraycopy(p, 0, props, l, p.length);
127
            l += p.length;
128
        }
129
        Arrays.sort (props, comparator);
130
        fds.addAll (Arrays.asList (props));
131
    }
132
    
133
    private void initExpandable () {
134
        if ((sets == null) || (sets.length == 0)) return;
135
        firstSetIndex = 0;
136
        Property[] p;
137
        for (int i=0; i < sets.length; i++) {
138
            //Show the set expandable only if it's not the default set
139
            if (PropUtils.hideSingleExpansion) {
140
                if ((sets.length > 1) || 
141
                    ((sets.length == 1) && 
142
                     (!NbBundle.getMessage(PropertySetModelImpl.class, 
143
                        "CTL_Properties").equals(sets[0].getDisplayName())))) {
144
                    fds.add (sets[i]);
145
                }
146
            } else {
147
                fds.add (sets[i]);
148
            }
149
            if (expanded[i]) {
150
                p = sets[i].getProperties();
151
                if (p.length > 0) {
152
                    fds.addAll (Arrays.asList(p));
153
                } else {
154
                    fds.remove (sets[i]);
155
                }
156
            }
157
        }
158
    }
159
    
160
    private void resetArray (PropertySet[] sets) {
161
        int size=sets.length;
162
        if ((expanded == null) || (expanded.length < size)) expanded = new boolean[size];
163
        for (int i=0; i < sets.length; i++) {
164
            expanded[i] = !closedSets.contains (sets[i].getDisplayName());
165
        }
166
    }
167
    
168
    private int lookupSet (FeatureDescriptor fd) {
169
        if (sets != null) {
170
            List l = Arrays.asList (sets);
171
            return l.indexOf (fd);
172
        } else {
173
            return -1;
174
        }
175
    }
176
177
    public boolean isExpanded (FeatureDescriptor set) {
178
        int index = lookupSet (set);
179
        if (index == -1) return false;
180
        return expanded[index];
181
    }
182
    
183
    public void toggleExpanded(int index) {
184
        FeatureDescriptor fd = getFeatureDescriptor(index);
185
        if (fd instanceof Property) 
186
            throw new IllegalArgumentException ("Cannot expand a property."); //NOI18N
187
        int setIndex = lookupSet (fd);
188
        int eventType = expanded[setIndex] ? 
189
            PropertySetModelEvent.TYPE_INSERT 
190
            : PropertySetModelEvent.TYPE_REMOVE;
191
        int len = ((PropertySet) fd).getProperties().length;
192
        
193
        expanded[setIndex] = !expanded[setIndex]; 
194
        
195
        firePendingChange (eventType, index+1, index+len, false);
196
        
197
        if (!expanded[setIndex]) {
198
            closedSets.add (fd.getDisplayName());
199
        } else {
200
            closedSets.remove (fd.getDisplayName());
201
        }
202
        if (expanded[setIndex]) {
203
            fds.addAll (index+1, Arrays.asList(sets[setIndex].getProperties()));
204
        } else {
205
            for (int i = index + len; i > index; i--) {
206
                fds.remove (i);
207
            }
208
        }
209
        fireChange (eventType, index+1, index + len);
210
        PropUtils.putSavedClosedSetNames (closedSets);
211
    }
212
    
213
    public final synchronized void addPropertySetModelListener(PropertySetModelListener listener) {
214
        if (listenerList == null ) {
215
            listenerList = new java.util.ArrayList();
216
        }
217
        listenerList.add(listener);
218
    }
219
    
220
    public final synchronized void removePropertySetModelListener(PropertySetModelListener listener) {
221
        if (listenerList != null ) {
222
            listenerList.remove(listener);
223
        }
224
    }
225
    
226
    /** A single event that can be reused. */
227
    private transient PropertySetModelEvent event = null;
228
    /** Getter which lazily instantiates the single event that
229
     *  will be used for al model events. */
230
    private PropertySetModelEvent getEvent() {
231
        if (event == null)
232
            event = new PropertySetModelEvent (this);
233
        return event;
234
    }
235
    
236
    /** Fire a change of type 
237
     *  <code>PropertySetModelEvent.TYPE_WHOLESALE_CHANGE</code>. */
238
    private final void firePendingChange (int type, 
239
        int start, int end, boolean reordering) {
240
        if (listenerList == null) return;
241
        Iterator i = listenerList.iterator();
242
        PropertySetModelListener curr;
243
        getEvent().type=PropertySetModelEvent.TYPE_WHOLESALE_CHANGE;
244
        event.end = end;
245
        event.type = type;
246
        event.reordering = reordering;
247
        while (i.hasNext()) {
248
            curr = (PropertySetModelListener) i.next();
249
            curr.pendingChange(event);
250
        }
251
    }
252
253
    /** Fire a change of type 
254
     *  <code>PropertySetModelEvent.TYPE_WHOLESALE_CHANGE</code>. */
255
    private final void fireChange (boolean reordering) {
256
        if (listenerList == null) return;
257
        Iterator i = listenerList.iterator();
258
        PropertySetModelListener curr;
259
        getEvent().type=PropertySetModelEvent.TYPE_WHOLESALE_CHANGE;
260
        event.reordering = reordering;
261
        while (i.hasNext()) {
262
            curr = (PropertySetModelListener) i.next();
263
            curr.wholesaleChange(event);
264
        }
265
    }
266
    
267
    /** Fire a change of type 
268
     *  <code>PropertySetModelEvent.TYPE_WHOLESALE_CHANGE</code>. */
269
    private final void firePendingChange (boolean reordering) {
270
        if (listenerList == null) return;
271
        Iterator i = listenerList.iterator();
272
        PropertySetModelListener curr;
273
        getEvent().type=PropertySetModelEvent.TYPE_WHOLESALE_CHANGE;
274
        event.reordering = reordering;
275
        while (i.hasNext()) {
276
            curr = (PropertySetModelListener) i.next();
277
            curr.pendingChange(event);
278
        }
279
    }
280
    
281
    /** Fire a change with the given parameters.  
282
     *@param type The integer event type, as defined in <code>PropertySetModelEvent</code>.
283
     *@param start The first affected row (note that when expanding, the first affected
284
     *row is the first one <strong>following</strong> the row representing the property
285
     *set that was expanded)
286
     *@param end The last affected row.  */
287
    private final void fireChange (int type, int start, int end) {
288
        if (listenerList == null) return;
289
        getEvent().start = start;
290
        event.end = end;
291
        event.type = type;
292
        event.reordering = false;
293
        Iterator i = listenerList.iterator();
294
        PropertySetModelListener curr;
295
        while (i.hasNext()) {
296
            curr = (PropertySetModelListener) i.next();
297
            curr.boundedChange(event);
298
        }
299
    }    
300
    
301
    public Comparator getComparator() {
302
        return comparator;
303
    }
304
    
305
    public int getSetCount() {
306
        return setCount;
307
    }
308
    
309
}
(-)src/org/openide/explorer/propertysheet/PropertySetModelListener.java (+39 lines)
Added Link Here 
1
/*
2
 *                 Sun Public License Notice
3
 * 
4
 * The contents of this file are subject to the Sun Public License
5
 * Version 1.0 (the "License"). You may not use this file except in
6
 * compliance with the License. A copy of the License is available at
7
 * http://www.sun.com/
8
 * 
9
 * The Original Code is NetBeans. The Initial Developer of the Original
10
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun
11
 * Microsystems, Inc. All Rights Reserved.
12
 */
13
/*
14
 * PropertySetModelListener.java
15
 *
16
 * Created on December 30, 2002, 12:13 PM
17
 */
18
19
package org.openide.explorer.propertysheet;
20
21
/** Listener interface for PropertySetModel changes.
22
 *
23
 * @author  Tim Boudreau
24
 */
25
interface PropertySetModelListener extends java.util.EventListener {
26
    /* Indicates a change is about to occur, but the model data is still
27
     *  valid with its pre-change values.  */
28
    public void pendingChange (PropertySetModelEvent e);
29
    /** A change which has known constraints, such as the insertion or
30
     *  removal of rows due to expansion/de-expansion of a category in
31
     *  a property sheet.  The affected rows are available from the
32
     *  event object. */
33
    public void boundedChange (PropertySetModelEvent e);
34
    /** Called when a change occurs that is so far reaching that the
35
     *  entire model is invalidated.  In this case, the affected
36
     *  row properties of the event are irrelevant and should not
37
     *  be used.*/
38
    public void wholesaleChange (PropertySetModelEvent e);
39
}
(-)src/org/openide/explorer/propertysheet/PropertySheet.java (-537 / +847 lines)
 Lines 1-11    Link Here 
1
/*
1
/*
2
 *                 Sun Public License Notice
2
 *                 Sun Public License Notice
3
 * 
3
 *
4
 * The contents of this file are subject to the Sun Public License
4
 * The contents of this file are subject to the Sun Public License
5
 * Version 1.0 (the "License"). You may not use this file except in
5
 * Version 1.0 (the "License"). You may not use this file except in
6
 * compliance with the License. A copy of the License is available at
6
 * compliance with the License. A copy of the License is available at
7
 * http://www.sun.com/
7
 * http://www.sun.com/
8
 * 
8
 *
9
 * The Original Code is NetBeans. The Initial Developer of the Original
9
 * The Original Code is NetBeans. The Initial Developer of the Original
10
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun
10
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun
11
 * Microsystems, Inc. All Rights Reserved.
11
 * Microsystems, Inc. All Rights Reserved.
 Lines 16-96    Link Here 
16
import java.awt.*;
16
import java.awt.*;
17
import java.awt.datatransfer.*;
17
import java.awt.datatransfer.*;
18
import java.awt.event.*;
18
import java.awt.event.*;
19
import java.awt.geom.Area;
20
import java.awt.geom.Rectangle2D;
19
import java.beans.*;
21
import java.beans.*;
20
import java.lang.reflect.Method;
22
import java.lang.reflect.Method;
23
import java.util.*;
21
24
22
import javax.swing.*;
25
import javax.swing.*;
26
import javax.swing.border.*;
23
import javax.swing.event.*;
27
import javax.swing.event.*;
28
import javax.swing.plaf.metal.MetalLookAndFeel;
29
import javax.swing.text.JTextComponent;
24
30
25
import org.openide.ErrorManager;
31
import org.openide.ErrorManager;
26
import org.openide.actions.*;
32
import org.openide.actions.*;
27
import org.openide.awt.SplittedPanel;
28
import org.openide.util.HelpCtx;
33
import org.openide.util.HelpCtx;
29
import org.openide.util.NbBundle;
34
import org.openide.util.NbBundle;
30
import org.openide.util.Mutex;
31
import org.openide.util.WeakListener;
32
import org.openide.nodes.Node;
35
import org.openide.nodes.Node;
33
import org.openide.nodes.NodeAdapter;
36
import org.openide.nodes.Node.PropertySet;
34
import org.openide.nodes.NodeListener;
35
import org.openide.nodes.NodeOperation;
36
import org.openide.util.Lookup;
37
import org.openide.util.Lookup;
37
import org.openide.util.RequestProcessor;
38
import org.openide.util.RequestProcessor;
38
39
/**
39
/**
40
* Implements a "property sheet" for a set of selected beans.
40
 * Implements a property sheet for a set of nodes.
41
*
41
 *
42
* <P>
42
 * <strong>Note that this class should be final, but for backward compatibility,
43
* <TABLE BORDER COLS=3 WIDTH=100%>
43
 * cannot be.  Subclassing this class is strongly discouraged</strong>
44
* <TR><TH WIDTH=15%>Property<TH WIDTH=15%>Property Type<TH>Description
44
 *
45
* <TR><TD> <code>paintingStyle</code> <TD> <code>int</code>     <TD> style of painting properties ({@link #ALWAYS_AS_STRING}, {@link #STRING_PREFERRED}, {@link #PAINTING_PREFERRED})
45
 * @author   Tim Boudreau, Jan Jancura, Jaroslav Tulach
46
* <TR><TD> <code>currentPage</code>   <TD> <code>int</code>     <TD> currently showed page (e.g. properties, expert, events)
46
 * @version  2.0, Jan 6, 2003
47
* <TR><TD> <code>expert</code>        <TD> <code>boolean</code> <TD> expert mode as in the JavaBeans specifications
47
 */
48
* </TABLE>
49
*
50
* @author   Jan Jancura, Jaroslav Tulach
51
* @version  1.23, Sep 07, 1998
52
*/
53
public class PropertySheet extends JPanel {
48
public class PropertySheet extends JPanel {
54
    /** generated Serialized Version UID */
49
    /** generated Serialized Version UID */
55
    static final long serialVersionUID = -7698351033045864945L;
50
    static final long serialVersionUID = -7698351033045864945L;
56
    
51
    
57
58
    // public constants ........................................................
52
    // public constants ........................................................
59
53
    
60
    /** Property giving current sorting mode. */
54
    /** Deprecated - no code outside the property sheet should be interested
55
     *  in how items are sorted.
56
     *@deprecated Relic of the original property sheet implementation, will never be fired. */
61
    public static final String    PROPERTY_SORTING_MODE = "sortingMode"; // NOI18N
57
    public static final String    PROPERTY_SORTING_MODE = "sortingMode"; // NOI18N
62
    /** Property giving current value color. */
58
    /** Property giving current value color.
59
     *@deprecated Relic of the original property sheet implementation, will never be fired. */
63
    public static final String    PROPERTY_VALUE_COLOR = "valueColor"; // NOI18N
60
    public static final String    PROPERTY_VALUE_COLOR = "valueColor"; // NOI18N
64
    /** Property giving current disabled property color. */
61
    /** Property giving current disabled property color.
62
     *@deprecated Relic of the original property sheet implementation, , will never be fired. */
65
    public static final String    PROPERTY_DISABLED_PROPERTY_COLOR = "disabledPropertyColor"; // NOI18N
63
    public static final String    PROPERTY_DISABLED_PROPERTY_COLOR = "disabledPropertyColor"; // NOI18N
66
    /** Property with the current page index. */
64
    /** Property with the current page index.
65
     *@deprecated Relic of the original property sheet implementation, , will never be fired.*/
67
    public static final String    PROPERTY_CURRENT_PAGE = "currentPage"; // NOI18N
66
    public static final String    PROPERTY_CURRENT_PAGE = "currentPage"; // NOI18N
68
    /** Property for "plastic" mode. */ // NOI18N
67
    /** Property for plastic mode.
68
     *@deprecated Relic of the original property sheet implementation, , will never be fired.   */
69
    public static final String    PROPERTY_PLASTIC = "plastic"; // NOI18N
69
    public static final String    PROPERTY_PLASTIC = "plastic"; // NOI18N
70
    /** Property for the painting style. */
70
    /** Property for the painting style.
71
     *@deprecated Relic of the original property sheet implementation, will never be fired. */
71
    public static final String    PROPERTY_PROPERTY_PAINTING_STYLE = "propertyPaintingStyle"; // NOI18N
72
    public static final String    PROPERTY_PROPERTY_PAINTING_STYLE = "propertyPaintingStyle"; // NOI18N
72
    /** Property for whether only writable properties should be displayed. */
73
    /** Property for whether only writable properties should be displayed.
74
     *@deprecated Relic of the original property sheet implementation, will never be fired.*/
73
    public static final String    PROPERTY_DISPLAY_WRITABLE_ONLY = "displayWritableOnly"; // NOI18N
75
    public static final String    PROPERTY_DISPLAY_WRITABLE_ONLY = "displayWritableOnly"; // NOI18N
74
76
    
75
    /** Constant for showing properties as a string always. */
77
    /** Constant for showing properties as a string always.
78
     *@deprecated Relic of the original property sheet implementation, useless.  */
76
    public static final int       ALWAYS_AS_STRING = 1;
79
    public static final int       ALWAYS_AS_STRING = 1;
77
    /** Constant for preferably showing properties as string. */
80
    /** Constant for preferably showing properties as string.
81
     *@deprecated Relic of the original property sheet implementation, does useless.    */
78
    public static final int       STRING_PREFERRED = 2;
82
    public static final int       STRING_PREFERRED = 2;
79
    /** Constant for preferably painting property values. */
83
    /** Constant for preferably painting property values.
84
     *@deprecated Relic of the original property sheet implementation, does useless.   */
80
    public static final int       PAINTING_PREFERRED = 3;
85
    public static final int       PAINTING_PREFERRED = 3;
81
86
    
82
    /** Constant for unsorted sorting mode. */
87
    /** Constant for unsorted sorting mode. */
83
    public static final int       UNSORTED = 0;
88
    public static final int       UNSORTED = 0;
84
    /** Constant for by-name sorting mode. */
89
    /** Constant for by-name sorting mode. */
85
    public static final int       SORTED_BY_NAMES = 1;
90
    public static final int       SORTED_BY_NAMES = 1;
86
    /** Constant for by-type sorting mode. */
91
    /** Constant for by-type sorting mode. */
87
    public static final int       SORTED_BY_TYPES = 2;
92
    public static final int       SORTED_BY_TYPES = 2;
88
89
    /** Init delay for second change of the selected nodes. */
90
    private static final int INIT_DELAY = 70;
91
    
92
    /** Maximum delay for repeated change of the selected nodes. */
93
    private static final int MAX_DELAY = 350;
94
    
93
    
95
    /** Icon for the toolbar.
94
    /** Icon for the toolbar.
96
     * @deprecated Presumably noone uses this variable. If you want to customize
95
     * @deprecated Presumably noone uses this variable. If you want to customize
 Lines 123-262    Link Here 
123
     */
122
     */
124
    static protected Icon         iCustomize;
123
    static protected Icon         iCustomize;
125
    
124
    
126
    static final String PROP_HAS_CUSTOMIZER = "hasCustomizer"; // NOI18N
125
    /** Action command/input map key for popup menu invocation action */
127
    static final String PROP_PAGE_HELP_ID = "pageHelpID"; // NOI18N
126
    private static final String ACTION_INVOKE_POPUP = "invokePopup"; //NOI18N
127
    
128
    /** Action command/input map key for help invocation action */
129
    private static final String ACTION_INVOKE_HELP = "invokeHelp"; //NOI18N
130
    
131
    /** Init delay for second change of the selected nodes. */
132
    private static final int INIT_DELAY = 70;
133
    
134
    /** Maximum delay for repeated change of the selected nodes. */
135
    private static final int MAX_DELAY = 350;
136
    
128
    
137
    
129
    private static String getString(String key) {
138
    private static String getString(String key) {
130
        return NbBundle.getBundle(PropertySheet.class).getString(key);
139
        return NbBundle.getBundle(PropertySheet.class).getString(key);
131
    }
140
    }
132
    
141
    
133
    /** Remember the position of the splitter. This is used when the property window is re-used for another node  */
142
    private int sortingMode = UNSORTED;
134
    private int savedSplitterPosition = SplittedPanel.FIRST_PREFERRED;
135
136
    // private variables for visual controls ...........................................
137
138
    private transient JTabbedPane           pages;
139
    private transient EmptyPanel            emptyPanel;
140
    
143
    
141
    private transient int                   pageIndex = 0;
142
143
    private transient ChangeListener        tabListener =
144
        new ChangeListener () {
145
            public void stateChanged (ChangeEvent e) {
146
                int index = pages.getSelectedIndex ();
147
                
148
                setCurrentPage (index);
149
            }
150
        };
151
    private Node activeNode;
152
    private NodeListener activeNodeListener;
153
    private boolean displayWritableOnly;
154
    private int propertyPaintingStyle;
155
    private int sortingMode;
156
    private boolean plastic;
157
    private Color disabledPropertyColor;
158
    private Color valueColor;
159
160
    // init .............................................................................
161
162
    public PropertySheet() {
144
    public PropertySheet() {
163
        setLayout (new BorderLayout ());
145
        init();
146
        initActions();
147
    }
148
    
149
    private void initActions() {
150
        getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(
151
        KeyStroke.getKeyStroke(KeyEvent.VK_F10, KeyEvent.ALT_MASK),
152
        ACTION_INVOKE_POPUP);
153
        getActionMap().put(ACTION_INVOKE_POPUP, invokePopupAction);
164
        
154
        
165
        boolean problem = false;
155
        getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(
156
        KeyStroke.getKeyStroke(KeyEvent.VK_F1, 0), ACTION_INVOKE_HELP);
157
        getActionMap().put(ACTION_INVOKE_HELP, getHelpAction());
158
        getHelpAction().setEnabled(false);
159
    }
160
    
161
    public void addNotify() {
162
        super.addNotify();
163
        getTable().addMouseListener(listener);
164
        js.addMouseListener(listener);
165
        addMouseListener(listener);
166
        getTable().getInputMap().put(
167
        KeyStroke.getKeyStroke(KeyEvent.VK_F10, KeyEvent.ALT_MASK),
168
        ACTION_INVOKE_POPUP);
169
        getTable().getActionMap().put(ACTION_INVOKE_POPUP, invokePopupAction);
170
        setEmpty(true);
171
    }
172
    
173
    public void reshape (int x, int y, int w, int h) {
174
        Rectangle r = getBounds();
175
        super.reshape (x, y, w, h);
176
        if ((x != r.x) && (y != r.y) && (w != r.width) && (h != r.height)) {
177
            if (infoPanel != null) {
178
                infoPanel.setDividerLocation (
179
                    Math.max (getHeight() - 60, getHeight() - infoPanel.getBottomComponent().getPreferredSize().height));
180
            }
181
        }
182
    }
183
    
184
    public void removeNotify() {
185
        getTable().removeMouseListener(listener);
186
        js.removeMouseListener(listener);
187
        removeMouseListener(listener);
188
        //Clear the static reference to this property sheet
189
        SheetMenu.getDefault().clear();
190
        getTable().getInputMap().put(
191
        KeyStroke.getKeyStroke(KeyEvent.VK_F10, KeyEvent.ALT_DOWN_MASK),
192
        null);
193
        getTable().getActionMap().put(ACTION_INVOKE_POPUP, null);
194
        if (pclistener != null) {
195
            getPCListener().detach();
196
        }
197
        super.removeNotify();
198
    }
199
    
200
    private JScrollPane js;
201
    private JViewport viewport;
202
    private void init() {
203
        showDesc = PropUtils.shouldShowDescription ();
204
        setLayout(new BorderLayout());
205
        setBorder(null);
206
        setNodes (new Node[0]);
207
        js = new JScrollPane();
208
        viewport = new MarginViewport();
209
        js.setViewport(viewport);
210
        viewport.setView(getTable());
211
        js.setBackground(table.getBackground());
212
        viewport.setBackground(table.getBackground());
213
        setBackground(table.getBackground());
214
        js.setBorder(null);
215
        js.getViewport().setBorder(null);
216
        table.setBorder(null);
217
        js.setName("PropertySheetScrollPane"); //NOI18N
218
        js.getViewport().setName("PropertySheetScrollPaneViewport"); //NOI18N
219
        setDescriptionVisible (showDesc);
220
        setMinimumSize(new Dimension(100, 50));
166
        try {
221
        try {
167
            Class c = Class.forName("org.openide.explorer.propertysheet.PropertySheet$PropertySheetSettingsInvoker"); // NOI18N
222
            setSortingMode (PropUtils.getSavedSortOrder());
168
            Runnable r = (Runnable)c.newInstance();
223
        } catch (PropertyVetoException e) {
169
            current.set(this);
224
            //do nothing
170
            r.run();
171
        } catch (Exception e) {
172
            problem = true;
173
        } catch (LinkageError le) {
174
            problem = true;
175
        }
176
        if (problem) {
177
            // set defaults without P ropertySheetSettings
178
            displayWritableOnly = false;
179
            propertyPaintingStyle = PAINTING_PREFERRED;
180
            sortingMode = SORTED_BY_NAMES;
181
            plastic = false;
182
            disabledPropertyColor = UIManager.getColor("textInactiveText");
183
            valueColor = new Color (0, 0, 128);
184
        }
225
        }
226
    }
227
    
228
    boolean showDesc;
229
    
230
    boolean isDescriptionVisible() {
231
        return infoPanel != null;
232
    }
233
    
234
    void setDescriptionVisible(boolean val) {
235
        if (val) {
236
            infoPanel = createDescriptionComponent();
237
            synchronized (getTreeLock()) {
238
                remove(js);
239
                infoPanel.setTopComponent(js);
240
                add(infoPanel, BorderLayout.CENTER);
241
                infoPanel.setDividerLocation (
242
                    Math.max (getHeight() - 60, getHeight() - infoPanel.getBottomComponent().getPreferredSize().height));
243
            }
244
        } else {
245
            synchronized (getTreeLock()) {
246
                if (infoPanel != null) {
247
                    remove(infoPanel);
248
                    infoPanel.removeMouseListener (listener);
249
                    infoPanel = null;
250
                }
251
                add(js, BorderLayout.CENTER);
252
            }
253
        }
254
        revalidate();
255
        repaint();
256
        //Viewport doesn't revalidate if description doesn't change
257
        PropUtils.saveShowDescription (val);
258
    }
185
259
186
        pages = new HelpAwareJTabbedPane ();
260
    public void requestFocus() {
187
        pages.getAccessibleContext().setAccessibleName(getString("ACS_PropertySheetTabs"));
261
        if (getTable().getParent() != null) {
188
        pages.getAccessibleContext().setAccessibleDescription(getString("ACSD_PropertySheetTabs"));
262
            getTable().requestFocus();
189
        pages.addChangeListener(tabListener);
263
        } else {
190
        emptyPanel = new EmptyPanel (getString ("CTL_NoProperties"));
264
            super.requestFocus();
191
        pages.setTabPlacement (JTabbedPane.BOTTOM);
265
        }
192
//        add (emptyPanel, BorderLayout.CENTER);
193
194
        PropertySheetToolbar p = new PropertySheetToolbar(this);
195
        p.setBorder (UIManager.getBorder ("Toolbar.border"));
196
        addPropertyChangeListener(p);
197
        add (p, BorderLayout.NORTH);
198
199
        
200
//        setNodes(new Node[0]);
201
202
        setPreferredSize(new Dimension(280, 300));
203
    }
266
    }
204
    
267
    
205
        /** Overridden to provide a larger preferred size if the default font
268
    JSplitPane infoPanel = null;
206
         *  is larger, for locales that require this.   */
269
    private JSplitPane createDescriptionComponent() {
207
        public Dimension getPreferredSize() {
270
        final DescriptionPanel thePanel = new DescriptionPanel();
208
            //issue 34157, bad sizing/split location for Chinese locales that require
271
        final JSplitPane jsp = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
209
            //a larger default font size
272
        jsp.setUI (PropUtils.createSplitPaneUI());
210
            Dimension result = super.getPreferredSize();
273
        jsp.setResizeWeight(1);
211
            int fontsize = 
274
        if (UIManager.getLookAndFeel() instanceof MetalLookAndFeel) {
212
                javax.swing.UIManager.getFont ("Tree.font").getSize(); //NOI18N
275
            jsp.setDividerSize(7);
213
            if (fontsize > 11) {
276
        } else {
214
                int factor = fontsize - 11;
277
            jsp.setDividerSize(4);
215
                result.height += 15 * factor;
278
        }
216
                result.width += 50 * factor;
279
        
217
                Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
280
        //Set up a listener to notice when the selected property has changed
218
                if (result.height > screen.height) {
281
        getTable().addChangeListener(new ChangeListener() {
219
                    result.height = screen.height -30;
282
            public void stateChanged(ChangeEvent e) {
283
                KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
284
                Component c = kfm.getPermanentFocusOwner();
285
                if (c == null) {
286
                    //XXX strange nb-specific problem - all focus changes
287
                    //first cause a focus event on null.  Winsys rewrite should fix.
288
                    return;
220
                }
289
                }
221
                if (result.width > screen.width) {
290
                
222
                    result.width = screen.width -30;
291
                if (c != getTable()) {
292
                    if (thePanel.isAncestorOf(c)) {
293
                        //don't change the text if focus goes to the description panel
294
                        return;
295
                    }
296
                } 
297
                if ((thePanel.getParent() == null) || (jsp.getParent() == null)) {
298
                    //if the panel is gone, it's been hidden - rmeove the listener
299
                    getTable().removeChangeListener(this);
223
                }
300
                }
224
            } else {
301
                FeatureDescriptor fd = getTable().getSelection();
225
                result.width += 20;
302
                
226
                result.height +=20;
303
                //Use the current node description if there's no other description
304
                //to display
305
                String ttl;
306
                String description = ""; //NOI18N
307
                if (fd == null) {
308
                    thePanel.setDescription(fallbackTitle, fallbackDescription);
309
                    return;
310
                } else {
311
                    ttl = fd.getDisplayName();
312
                    description = fd.getShortDescription();
313
                    currHelpID = (String) fd.getValue("helpID");
314
                    getHelpAction().setEnabled(getHelpAction().hasContext()
315
                    || currHelpID != null);
316
                    
317
                    if (ttl == null) ttl = fd.getName();
318
                    if (description == null || description.equals(ttl)) {
319
                        description = NbBundle.getMessage(
320
                        PropertySheet.class,
321
                        "CTL_NO_DESCRIPTION"); //NOI18N
322
                    }
323
                }
324
                thePanel.setDescription (ttl, description);
325
            }
326
        });
327
        if (getTable().hasFocus()) {
328
            FeatureDescriptor fd = getTable().getSelection();
329
            if (fd != null) {
330
                thePanel.setDescription (fd.getDisplayName(), fd.getShortDescription());
227
            }
331
            }
228
            return result;
332
        } else {
333
            thePanel.setDescription (fallbackTitle, fallbackDescription);
229
        }
334
        }
230
    static ThreadLocal current = new ThreadLocal();
335
        thePanel.addMouseListener (listener);
231
336
        thePanel.setHelpAction(getHelpAction());
232
    /** Reference to PropertySheetSettings are separated here.*/
337
        jsp.setBottomComponent(thePanel);
233
    private static class PropertySheetSettingsInvoker implements Runnable {
338
        jsp.setBorder(null);
234
        // constructor avoid IllegalAccessException during creating new instance
339
        return jsp;
235
        public PropertySheetSettingsInvoker() {}
340
    }
236
341
    
237
        public void run() {
342
    String currHelpID = null;
238
            PropertySheet instance = (PropertySheet)current.get();
343
    private HelpAction helpAction=null;
239
            current.set(null);
344
    //Package private - SheetMenu will use it
240
            if (instance == null) {
345
    HelpAction getHelpAction() {
241
                throw new IllegalStateException();
346
        if (helpAction == null) {
242
            }
347
            helpAction = new HelpAction();
243
            PropertySheetSettings pss = PropertySheetSettings.getDefault();
244
            instance.displayWritableOnly = pss.getDisplayWritableOnly();
245
            instance.propertyPaintingStyle = pss.getPropertyPaintingStyle();
246
            instance.sortingMode = pss.getSortingMode();
247
            instance.plastic = pss.getPlastic();
248
            instance.disabledPropertyColor = pss.getDisabledPropertyColor();
249
            instance.valueColor = pss.getValueColor();
250
        }
348
        }
349
        return helpAction;
251
    }
350
    }
252
351
    
352
    /**Set the nodes explored by this property sheet.
353
     * @param nodes nodes to be explored or null to clear the sheet
354
     */
355
    public void setNodes(final Node[] nodes) {
356
        setHelperNodes (nodes);
357
    }
358
    
253
    /**
359
    /**
254
     * Set the nodes explored by this property sheet.
360
     * Set the nodes explored by this property sheet.
255
     *
361
     *
256
     * @param nodes nodes to be explored
362
     * @param nodes nodes to be explored
257
     */
363
     */
258
    public void setNodes (Node[] nodes) {
364
    private void doSetNodes (Node[] nodes) {
259
        setHelperNodes (nodes);
365
        if (nodes == null || nodes.length == 0) {
366
            getTable().getPropertySetModel().setPropertySets(null);
367
            setEmpty(true);
368
            return;
369
        }
370
        final Node n = (nodes.length == 1) ? nodes[0] : new ProxyNode(nodes);
371
        setCurrentNode(n);        
260
    }
372
    }
261
    
373
    
262
    // delayed setting nodes (partly impl issue 27781)
374
    // delayed setting nodes (partly impl issue 27781)
 Lines 289-300    Link Here 
289
        if (scheduleTask == null) {
401
        if (scheduleTask == null) {
290
            scheduleTask = RequestProcessor.getDefault ().post (new Runnable () {
402
            scheduleTask = RequestProcessor.getDefault ().post (new Runnable () {
291
                public void run () {
403
                public void run () {
292
                    Node[] nodes = getHelperNodes ();
404
                    final Node[] nodes = getHelperNodes ();
405
                    /*
293
                    final Node n = (nodes.length == 1) ? nodes[0] : 
406
                    final Node n = (nodes.length == 1) ? nodes[0] : 
294
                        (nodes.length==0 ? null : new ProxyNode (nodes));
407
                        (nodes.length==0 ? null : new ProxyNode (nodes));
408
                     */
295
                    SwingUtilities.invokeLater (new Runnable () {
409
                    SwingUtilities.invokeLater (new Runnable () {
296
                        public void run () {
410
                        public void run () {
297
                            setCurrentNode (n);
411
                            doSetNodes (nodes);
298
                        }
412
                        }
299
                    });
413
                    });
300
                }
414
                }
 Lines 313-412    Link Here 
313
        return scheduleTask;
427
        return scheduleTask;
314
    }
428
    }
315
    
429
    
316
    // end of delayed
430
    // end of delayed    
317
    
431
    
432
    /** Description to display when no property is selected */
433
    private String fallbackDescription = ""; //NOI18N
434
    /** Title to display when no property is selected */
435
    private String fallbackTitle = ""; //NOI18N
436
    /** Flag true if the property set was empty or the node was null */
437
    boolean empty = false;
438
    private void setEmpty(boolean val) {
439
        empty = val;
440
    }
318
    
441
    
319
    /**
442
    private boolean getEmpty() {
320
     * Set property paint mode.
443
        return empty;
321
     * @param style one of {@link #ALWAYS_AS_STRING}, {@link #STRING_PREFERRED}, or {@link #PAINTING_PREFERRED}
444
    }
322
     */
445
    boolean usingTabs=false;
323
    public void setPropertyPaintingStyle (int style) {
446
    /** This has to be called from the AWT thread. */
324
        if (style == propertyPaintingStyle) return;
447
    //hmm, does it still?  Probably this should be thread-safe.
448
    private void setCurrentNode(Node node) {
449
        //        getTable().setNode (node);
450
        PropertySetModel psm = getTable().getPropertySetModel();
451
        Node.PropertySet[] ps = node.getPropertySets();
452
        //bloc below copied from original impl - is this common/needed?
453
        if (ps == null) {
454
            // illegal node behavior => log warning about it
455
            ErrorManager.getDefault().log(ErrorManager.WARNING,
456
            "Node "+ node +": getPropertySets() returns null!"); // NOI18N
457
            ps = new Node.PropertySet[] {};
458
            //Prepare the reusable model/env's node
459
            ReusablePropertyEnv.NODE = node;
460
        }
461
        
462
        boolean empty = ps.length == 0;
463
        usingTabs = checkForTabs(ps);
464
        setEmpty(empty);
465
        if (usingTabs) {
466
            ps = getTabbedPane().getCurrentPropertySets();
467
        }
468
        
469
        fallbackTitle = node.getDisplayName();
470
        fallbackDescription = (String)node.getValue("nodeDescription"); // NOI18N
471
        if (fallbackDescription == null) {
472
            fallbackDescription = node.getShortDescription();
473
        }
474
        if (fallbackDescription == null || fallbackDescription.equals(fallbackTitle)) {
475
            fallbackDescription = NbBundle.getMessage(PropertySheet.class,
476
            "CTL_NO_DESCRIPTION"); //NOI18N
477
        }
325
        
478
        
326
        int oldVal = propertyPaintingStyle;
479
        if (node != null) {
327
        propertyPaintingStyle = style;
480
            getPCListener().attach(node);
328
        firePropertyChange(PROPERTY_PROPERTY_PAINTING_STYLE, new Integer(oldVal), new Integer(style));
481
        }
482
        //        if (empty) repaint ();
483
        getHelpAction().setProvider(node);
484
        //If using tabs, the tab pane model setSelectedIndex will do this for us
485
        psm.setPropertySets(ps);
486
        viewport.revalidate();
487
        viewport.repaint();
329
    }
488
    }
330
489
    
331
    /**
490
    
332
     * Get property paint mode.
491
    /**Deprecated, does nothing.
333
     *
492
     * @param style one of {@link #ALWAYS_AS_STRING}, {@link #STRING_PREFERRED}, or {@link #PAINTING_PREFERRED}
493
     * @deprected Relic of the original property sheet implementation.  Does nothing.*/
494
    public void setPropertyPaintingStyle(int style) {
495
    }
496
    
497
    /**Deprecated, returns no meaningful value.
334
     * @return the mode
498
     * @return the mode
335
     * @see #setPropertyPaintingStyle
499
     * @see #setPropertyPaintingStyle
336
     */
500
     * @deprected Relic of the original property sheet implementation.  Does nothing. */
337
    public int getPropertyPaintingStyle () {
501
    public int getPropertyPaintingStyle() {
338
        return propertyPaintingStyle;
502
        return 0;
339
    }
503
    }
340
504
    
341
    /**
505
    /**Set the sorting mode.
342
     * Set the sorting mode.
506
     * @param sortingMode one of {@link #UNSORTED}, {@link #SORTED_BY_NAMES}, {@link #SORTED_BY_TYPES} */
343
     *
507
    public void setSortingMode(int sortingMode) throws PropertyVetoException {
344
     * @param sortingMode one of {@link #UNSORTED}, {@link #SORTED_BY_NAMES}, {@link #SORTED_BY_TYPES}
508
        try {
345
     */
509
            getTable().getPropertySetModel().setComparator(PropUtils.getComparator(sortingMode));
346
    public void setSortingMode (int sortingMode) throws PropertyVetoException {
510
            this.sortingMode = sortingMode;
347
        if (this.sortingMode == sortingMode) return;
511
            if (infoPanel == null) {
348
        
512
                revalidate();
349
        int oldVal = this.sortingMode;
513
                repaint();
350
        this.sortingMode = sortingMode;
514
            }
351
        firePropertyChange(PROPERTY_SORTING_MODE, new Integer(oldVal), new Integer(sortingMode));
515
            PropUtils.putSortOrder (sortingMode);
516
        } catch (IllegalArgumentException iae) {
517
            throw new PropertyVetoException(
518
            NbBundle.getMessage(
519
            PropertySheet.class, "EXC_Unknown_sorting_mode"),
520
            new PropertyChangeEvent(this, PROPERTY_SORTING_MODE, new Integer(0), new Integer(sortingMode))
521
            ); //NOI18N
522
        }
352
    }
523
    }
353
524
    
354
    /**
525
    /**Get the sorting mode.
355
     * Get the sorting mode.
356
     *
357
     * @return the mode
526
     * @return the mode
358
     * @see #setSortingMode
527
     * @see #setSortingMode   */
359
     */
528
    public int getSortingMode() {
360
    public int getSortingMode () {
361
        return sortingMode;
529
        return sortingMode;
362
    }
530
    }
363
531
    
364
    /**
532
    private SheetTable table = new SheetTable();
365
     * Set the currently selected page.
533
    /** Get the table this property sheet displays.  Package private to
366
     *
534
     *  enable unit tests on the table.  */
535
    SheetTable getTable() {
536
        return table;
537
    }
538
    
539
    /** Deprecated.  Does nothing.
367
     * @param index index of the page to select
540
     * @param index index of the page to select
541
     * @deprected Relic of the original property sheet implementation.  Does nothing.
368
     */
542
     */
369
    public void setCurrentPage (int index) {
543
    public void setCurrentPage(int index) {
370
        if (pageIndex == index)
371
            return;
372
        pageIndex = index;
373
        if (index < 0)
374
            return;
375
        
376
        if (index != pages.getSelectedIndex ()) {
377
            pages.setSelectedIndex (index);
378
        }
379
        
380
        int selected = pages.getSelectedIndex();
381
        if (selected >= 0) {
382
            Component comp = pages.getComponentAt(selected);
383
            if (comp instanceof PropertySheetTab) {
384
                ((PropertySheetTab)comp).ensurePaneCreated();
385
            }
386
        }
387
        firePropertyChange(PROP_PAGE_HELP_ID, null, null);
388
    }
389
390
    /**
391
    * Set the currently selected page.
392
    *
393
    * @param str name of the tab to select
394
    */
395
    public boolean setCurrentPage (String str) {
396
        int index = pages.indexOfTab (str);
397
        if (index < 0) return false;
398
        setCurrentPage (index);
399
        return true;
400
    }
544
    }
401
545
    
402
    /**
546
    /**Deprecated.  Does nothing.
403
    * Get the currently selected page.
547
     * @param str name of the tab to select
404
    * @return index of currently selected page
548
     * @deprected Relic of the original property sheet implementation.  Does nothing.*/
405
    */
549
    public boolean setCurrentPage(String str) {
406
    public int getCurrentPage () {
550
        return false;
407
        return pages.getSelectedIndex ();
408
    }
551
    }
409
    
552
    
553
    /**Deprecated.  Does nothing.
554
     * @return index of currently selected page
555
     * @deprected Relic of the original property sheet implementation.  Does nothing. */
556
    public int getCurrentPage() {
557
        //        return pages.getSelectedIndex ();
558
        return 0;
559
    }
560
    /*
410
    String getPageHelpID() {
561
    String getPageHelpID() {
411
        if (isAncestorOf(pages)) {
562
        if (isAncestorOf(pages)) {
412
            Component comp = pages.getSelectedComponent();
563
            Component comp = pages.getSelectedComponent();
 Lines 419-783    Link Here 
419
        }
570
        }
420
        return null;
571
        return null;
421
    }
572
    }
422
573
     */
423
    /**
574
    
424
    * Set whether buttons in sheet should be plastic.
575
    /**Deprecated.  Does nothing.
425
    * @param plastic true if so
576
     * @param plastic true if so
426
    */
577
     * @deprected Relic of the original property sheet implementation.  Display of properties
427
    public void setPlastic (boolean plastic) {
578
     * is handled by the look and feel.
428
        if (this.plastic == plastic) return;
579
     */
429
        this.plastic = plastic;
580
    public void setPlastic(boolean plastic) {
430
        firePropertyChange(PROPERTY_PLASTIC,
431
            plastic ? Boolean.FALSE:Boolean.TRUE, 
432
            plastic ? Boolean.TRUE:Boolean.FALSE);
433
    }
581
    }
434
582
    
435
    /**
583
    /**Test whether buttons in sheet are plastic.
436
    * Test whether buttons in sheet are plastic.
584
     * @return <code>true</code> if so
437
    * @return <code>true</code> if so
585
     * @deprected Relic of the original property sheet implementation.  Does nothing.*/
438
    */
586
    public boolean getPlastic() {
439
    public boolean getPlastic () {
587
        return false;
440
        return plastic;
441
    }
588
    }
442
589
    
443
    /**
590
    /**Deprecated.  Does nothing.
444
    * Set the foreground color of values.
591
     * @param color the new color
445
    * @param color the new color
592
     * @deprected Relic of the original property sheet implementation.  Display of properties
446
    */
593
     * is handled by the look and feel.  */
447
    public void setValueColor (Color color) {
594
    public void setValueColor(Color color) {
448
        if (valueColor.equals(color)) return;
449
        
450
        Color oldVal = valueColor;
451
        valueColor = color;
452
        firePropertyChange(PROPERTY_VALUE_COLOR, oldVal, color);
453
    }
595
    }
454
596
    
455
    /**
597
    /**Deprecated.  Does nothing.
456
    * Get the foreground color of values.
598
     * @deprected Relic of the original property sheet implementation.  Display of properties
457
    * @return the color
599
     * is handled by the look and feel.
458
    */
600
     * @return the color */
459
    public Color getValueColor() {
601
    public Color getValueColor() {
460
        return valueColor;
602
        return Color.BLACK;
461
    }
603
    }
462
604
    
463
    /**
605
    /**Deprecated.  Does nothing.
464
    * Set the foreground color of disabled properties.
606
     * @deprected Relic of the original property sheet implementation.  Does nothing.
465
    * @param color the new color
607
     * @param color the new color  */
466
    */
608
    public void setDisabledPropertyColor(Color color) {
467
    public void setDisabledPropertyColor (Color color) {
468
        if (disabledPropertyColor.equals(color)) return;
469
        
470
        Color oldVal = disabledPropertyColor;
471
        disabledPropertyColor = color;
472
        
473
        firePropertyChange(PROPERTY_DISABLED_PROPERTY_COLOR, oldVal, color);
474
    }
475
476
    /**
477
    * Get the foreground color of disabled properties.
478
    * @return the color
479
    */
480
    public Color getDisabledPropertyColor () {
481
        return disabledPropertyColor;
482
    }
483
484
    /**
485
    * Set whether only writable properties are displayed.
486
    * @param b <code>true</code> if this is desired
487
    */
488
    public void setDisplayWritableOnly (boolean b) {
489
        if (displayWritableOnly == b) return;
490
        displayWritableOnly = b;
491
        firePropertyChange(PROPERTY_DISPLAY_WRITABLE_ONLY,
492
            b ? Boolean.FALSE:Boolean.TRUE, 
493
            b ? Boolean.TRUE:Boolean.FALSE);
494
    }
609
    }
495
610
    
496
    /**
611
    /**Deprecated.  Does not return a meaningful value.
497
    * Test whether only writable properties are currently displayed.
612
     * @deprected Relic of the original property sheet implementation.  Display of properties
498
    * @return <code>true</code> if so
613
     * is handled by the look and feel.
499
    */
614
     * @return the color */
500
    public boolean getDisplayWritableOnly () {
615
    public Color getDisabledPropertyColor() {
501
        return displayWritableOnly;
616
        return Color.GRAY;
502
    }
617
    }
503
    
618
    
504
    void setSavedPosition (int savedPostion) {
619
    /**Deprecated.  Does nothing.
505
        savedSplitterPosition = savedPostion;
620
     * @param b <code>true</code> if this is desired
621
     * @deprected Relic of the original property sheet implementation.  Does nothing.*/
622
    public void setDisplayWritableOnly(boolean b) {
506
    }
623
    }
507
    
624
    
508
    int getSavedPosition () {
625
    /**Deprecated.  Does not return a meaningful value.
509
        return savedSplitterPosition;
626
     * @deprected Relic of the original property sheet implementation.  Does nothing.
627
     * @return <code>true</code> if so */
628
    public boolean getDisplayWritableOnly() {
629
        return false;
510
    }
630
    }
511
512
    // private helper methods ....................................................................
513
    
631
    
514
    private final String detachFromNode () {
632
    
515
        String result = null;
633
    private final class HelpAction extends AbstractAction {
516
        if (activeNode != null) {
634
        private HelpCtx ctx;
517
            activeNode.removeNodeListener( activeNodeListener );
635
        public HelpAction() {
518
            attached = false;
636
            super(NbBundle.getMessage(PropertySheet.class,
519
            Node.PropertySet [] oldP = activeNode.getPropertySets();
637
            "CTL_Help"));
520
            if (oldP == null) {
638
        }
521
                // illegal node behavior => log warning about it
639
        
522
                ErrorManager.getDefault ().log (ErrorManager.WARNING,
640
        public void setProvider(HelpCtx.Provider provider) {
523
                    "Node "+activeNode+": getPropertySets() returns null!"); // NOI18N
641
            this.ctx = provider.getHelpCtx();
524
                oldP = new Node.PropertySet[] {};
642
            setEnabled(hasContext());
525
            }
643
        }
526
            if ((pageIndex >= 0) && (pageIndex < oldP.length)) {
644
        
527
                result = oldP[pageIndex].getDisplayName();
645
        public boolean hasContext() {
646
            return (ctx != null) && (ctx != HelpCtx.DEFAULT_HELP);
647
        }
648
        
649
        public void actionPerformed(ActionEvent e) {
650
            if (ctx == null) {
651
                Toolkit.getDefaultToolkit().beep();
652
                return;
528
            }
653
            }
529
654
            
530
            for (int i = 0, tabCount = pages.getTabCount(); i < tabCount; i++) {
655
            try {
531
                ((PropertySheetTab)pages.getComponentAt(i)).detachPropertyChangeListener();
656
                //Copied from original property sheet implementation
657
                Class c = ((ClassLoader)Lookup.getDefault().lookup(
658
                ClassLoader.class)).loadClass(
659
                "org.netbeans.api.javahelp.Help"); // NOI18N
660
                
661
                Object o = Lookup.getDefault().lookup(c);
662
                if (o != null) {
663
                    Method m = c.getMethod("showHelp", // NOI18N
664
                    new Class[] {HelpCtx.class});
665
                    HelpCtx toInvoke = currHelpID != null ?
666
                    new HelpCtx(currHelpID) : ctx;
667
                    m.invoke(o, new Object[] {toInvoke});
668
                    return;
669
                }
670
            } catch (ClassNotFoundException cnfe) {
671
                // ignore - maybe javahelp module is not installed, not so strange
672
            } catch (Exception ee) {
673
                ee.printStackTrace();
674
                // potentially more serious
675
                ErrorManager.getDefault().notify(
676
                ErrorManager.INFORMATIONAL, ee);
532
            }
677
            }
533
            pages.removeAll();
678
            // Did not work.
679
            Toolkit.getDefaultToolkit().beep();
534
        }
680
        }
535
        return result;
536
    }
681
    }
537
    
682
    
538
    public void addNotify () {
683
    
539
        super.addNotify();
684
    final Action sortNamesAction = new AbstractAction(
540
        if (activeNode != null) {
685
    NbBundle.getMessage(PropertySheet.class, "CTL_AlphaSort")) {
541
            if (!attached) {
686
        public void actionPerformed(ActionEvent ae) {
542
                attachToNode (activeNode);
687
            try {
543
                createPages();
688
                setSortingMode(SORTED_BY_NAMES);
544
                if (storedTab != null) {
689
            } catch (PropertyVetoException pve) {
545
                    navToCorrectPage (storedTab);
690
                //can't happen
546
                    storedTab = null;
547
                } else {
548
                    if (pages.getTabCount() > 0) {
549
                        String first = pages.getTitleAt(0);
550
                        navToCorrectPage (first);
551
                    } else {
552
                        add (emptyPanel, BorderLayout.CENTER);
553
                    }
554
                }
555
            }
691
            }
556
        } else {
557
            remove (pages);
558
            add (emptyPanel, BorderLayout.CENTER);
559
        }
692
        }
693
    };
694
    
695
    final Action unsortedAction = new AbstractAction(
696
    NbBundle.getMessage(PropertySheet.class, "CTL_NoSort")) {
697
        public void actionPerformed(ActionEvent ae) {
698
            try {
699
                setSortingMode(UNSORTED);
700
            } catch (PropertyVetoException pve) {
701
                //can't happen
702
            }
703
        }
704
    };
705
    
706
    final Action invokePopupAction = new AbstractAction(ACTION_INVOKE_POPUP) {
707
        public void actionPerformed(ActionEvent ae) {
708
            if (!isEnabled()) return;
709
            SheetMenu sm = SheetMenu.getDefault();
710
            sm.show(PropertySheet.this, 0, 0);
711
        }
712
        public boolean isEnabled() {
713
            return !Boolean.TRUE.equals(getClientProperty("disablePopup"));
714
        }
715
    };
716
    
717
    MouseListener listener = new MouseAdapter() {
718
        public void mousePressed(MouseEvent e) {
719
            maybeShowPopup(e);
720
        }
721
        
722
        public void mouseReleased(MouseEvent e) {
723
            maybeShowPopup(e);
724
        }
725
        
726
        private void maybeShowPopup(MouseEvent e) {
727
            if (e.isPopupTrigger()) {
728
                SheetMenu.getDefault().show(e.getComponent(),
729
                e.getX(), e.getY());
730
            }
731
        }
732
    };
733
    
734
    final Action showDescriptionAction = new AbstractAction(
735
    NbBundle.getMessage(PropertySheet.class, "CTL_ShowDescription" )) {
736
        public void actionPerformed(ActionEvent ae) {
737
            setDescriptionVisible(!isDescriptionVisible());
738
        }
739
    };
740
    
741
    
742
    ///Everything below here is stuff depended on by components that are going away,
743
    //so the whole thing will build.
744
    
745
    /** @deprecated Relic of the original property sheet implementation.  Returns
746
     *   no useful value. */
747
    public int getSavedPosition() {
748
        return 20;
560
    }
749
    }
561
    
750
    
562
    private String storedTab = null;
751
    /** @deprecated Relic of the original property sheet implementation.  Returns
563
    public void removeNotify() {
752
     *   no useful value. */
564
        if (attached) storedTab = detachFromNode();
753
    public void setSavedPosition(int value) {}
565
        super.removeNotify();
754
    
755
    /** @deprecated Relic of the original property sheet implementation.  Returns
756
     *   no useful value. */
757
    public void invokeCustomAction(){}
758
    
759
    /** @deprecated Relic of the original property sheet implementation.  Returns
760
     *   no useful value. */
761
    void invokeCustomization() {}
762
    
763
    /** @deprecated Relic of the original property sheet implementation.  */
764
    void invokeHelp() {
765
        getHelpAction().actionPerformed(
766
        new ActionEvent(this, 0, null));
566
    }
767
    }
567
768
    
568
    private boolean attached=false;
769
    private SheetPCListener pclistener = null;
569
    private final void attachToNode (Node node) {
770
    private SheetPCListener getPCListener() {
570
        //XXX There is probably no reason to be using WeakListener here -
771
        if (pclistener == null) {
571
        //attach and detach occasions are well-defined unless two threads
772
            pclistener = new SheetPCListener();
572
        //enter setCurrentNode concurrently.  Test for this added to
573
        //setCurrentNode to determine if there are really cases of this
574
//        activeNodeListener = WeakListener.node (new ActiveNodeListener(), 
575
//            activeNode);
576
        if (activeNodeListener == null) {
577
            activeNodeListener = new ActiveNodeListener();
578
        }
579
        activeNode.addNodeListener(activeNodeListener);
580
        attached = true;
581
    }
582
    
583
    private final void createPages () {
584
        Node.PropertySet [] propsets = activeNode.getPropertySets();
585
        if (propsets == null) {
586
            // illegal node behavior => log warning about it
587
            ErrorManager.getDefault ().log (ErrorManager.WARNING, 
588
                "Node "+activeNode+": getPropertySets() returns null!"); // NOI18N
589
            propsets = new Node.PropertySet[] {};
590
        }
591
592
        for (int i = 0, n = propsets.length; i < n; i++) {
593
            Node.PropertySet set = propsets[i];
594
595
            if (set.isHidden())
596
                continue;
597
598
            pages.addTab(set.getDisplayName(),
599
                         null, 
600
                         new PropertySheetTab(set, activeNode, this),
601
                         set.getShortDescription()
602
                         );
603
        }
773
        }
774
        return pclistener;
604
    }
775
    }
605
    
776
    
606
    private final void navToCorrectPage (String selectedTabName) {
777
    private final class SheetPCListener implements
607
        if (isAncestorOf(emptyPanel)) {
778
    PropertyChangeListener {
608
                remove(emptyPanel);
779
        
609
            }
780
        /** Cache the current node locally only in the listener */
610
            add(pages, BorderLayout.CENTER);
781
        private Node currNode=null;
611
            if (selectedTabName != null) {
782
        /** Attach to a node, detaching from the last one if non-null.  */
612
                setCurrentPage(selectedTabName);
783
        public void attach(Node n) {
613
            }
784
            if (currNode != n) {
614
785
                if ((currNode != null)) {
615
            int selected = pages.getSelectedIndex();
786
                    currNode.removePropertyChangeListener(this);
616
            if (selected >= 0) {
787