KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > core > NbSheet


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.core;
21
22 import java.awt.BorderLayout JavaDoc;
23 import java.beans.PropertyChangeEvent JavaDoc;
24 import java.beans.PropertyChangeListener JavaDoc;
25 import java.io.IOException JavaDoc;
26 import java.io.ObjectInput JavaDoc;
27 import java.io.ObjectOutput JavaDoc;
28 import java.io.ObjectStreamException JavaDoc;
29 import java.io.Serializable JavaDoc;
30 import java.text.MessageFormat JavaDoc;
31 import java.util.ArrayList JavaDoc;
32 import java.util.Arrays JavaDoc;
33 import java.util.HashMap JavaDoc;
34 import java.util.Iterator JavaDoc;
35 import java.util.List JavaDoc;
36 import java.util.Map JavaDoc;
37 import java.util.logging.Level JavaDoc;
38 import java.util.logging.Logger JavaDoc;
39 import javax.swing.SwingUtilities JavaDoc;
40 import org.openide.explorer.ExplorerManager;
41 import org.openide.explorer.propertysheet.PropertySheet;
42 import org.openide.nodes.Node;
43 import org.openide.nodes.NodeAdapter;
44 import org.openide.nodes.NodeEvent;
45 import org.openide.nodes.NodeListener;
46 import org.openide.nodes.NodeOp;
47 import org.openide.util.Exceptions;
48 import org.openide.util.HelpCtx;
49 import org.openide.util.NbBundle;
50 import org.openide.util.Mutex;
51 import org.openide.util.Utilities;
52 import org.openide.util.io.NbMarshalledObject;
53 import org.openide.util.io.SafeException;
54 import org.openide.windows.Mode;
55 import org.openide.windows.TopComponent;
56 import org.openide.windows.Workspace;
57 import org.openide.windows.WindowManager;
58
59 /**
60  * Default view for properties.
61  */

62 public final class NbSheet extends TopComponent {
63     
64     /**
65      * Name of a property that can be passed in a Node instance. The value
66      * of the property must be String and can be an alternative to displayName.
67      */

68     private static final String JavaDoc PROP_LONGER_DISPLAY_NAME = "longerDisplayName"; // NOI18N
69

70     /** generated Serialized Version UID */
71     static final long serialVersionUID = 7807519514644165460L;
72
73     /** shared sheet */
74     private static NbSheet sharedSheet;
75     /** listener to the property changes */
76     transient private final Listener JavaDoc listener;
77     /** listener to the node changes, especially their destruction */
78     transient private final SheetNodesListener snListener;
79     /** Should property sheet listen to the global changes ? */
80     boolean global;
81     /** the property sheet that is used to display nodes */
82     private PropertySheet propertySheet;
83     /** the nodes that are displayed in the property sheet */
84     private Node[] nodes = new Node[0];
85
86     /** Constructor for new sheet.
87     * The sheet does not listen to global changes */

88     public NbSheet () {
89         this (false);
90     }
91
92     /** @param global should the content change when global properties changes?
93     */

94     public NbSheet (boolean global) {
95         this.global = global;
96         this.propertySheet = new PropertySheet ();
97
98         // Instructs winsys to name this mode as single if only property sheet
99
// is docked in this mode
100
// it's workaround, should be solved throgh some Mode API in future
101
// # 16888. Properties sheet is in single mode in SDI only.
102
// putClientProperty(ModeImpl.NAMING_TYPE, ModeImpl.SDI_ONLY_COMP_NAME); // TEMP
103
//Bugfix #36087: Fix naming type
104
putClientProperty("NamingType", "BothOnlyCompName"); // NOI18N
105

106         setLayout (new BorderLayout JavaDoc ());
107         add(propertySheet, BorderLayout.CENTER);
108
109         setIcon (Utilities.loadImage("org/netbeans/core/resources/frames/properties.gif", true)); // NOI18N
110

111         // #36738 Component has to have a name from begining.
112
updateTitle();
113         // XXX - please rewrite to regular API when available - see issue #55955
114
putClientProperty("SlidingName", NbBundle.getMessage(NbSheet.class, "CTL_PropertiesWindow")); //NOI18N
115

116         // name listener and node listener
117
listener = new Listener JavaDoc ();
118
119         snListener = new SheetNodesListener();
120
121         // set accessiblle description
122
getAccessibleContext ().setAccessibleName (
123             NbBundle.getBundle(NbSheet.class).getString ("ACSN_PropertiesSheet"));
124         getAccessibleContext ().setAccessibleDescription (
125             NbBundle.getBundle(NbSheet.class).getString ("ACSD_PropertiesSheet"));
126         setActivatedNodes(null);
127     }
128     
129     /* Singleton accessor. As NbSheet is persistent singleton this
130      * accessor makes sure that NbSheet is deserialized by window system.
131      * Uses known unique TopComponent ID "properties" to get NbSheet instance
132      * from window system. "properties" is name of settings file defined in module layer.
133      */

134     public static NbSheet findDefault () {
135         if (sharedSheet == null) {
136             TopComponent tc = WindowManager.getDefault().findTopComponent("properties"); // NOI18N
137
if (tc != null) {
138                 if (tc instanceof NbSheet) {
139                     sharedSheet = (NbSheet) tc;
140                 } else {
141                     //Incorrect settings file?
142
IllegalStateException JavaDoc exc = new IllegalStateException JavaDoc
143                     ("Incorrect settings file. Unexpected class returned." // NOI18N
144
+ " Expected:" + NbSheet.class.getName() // NOI18N
145
+ " Returned:" + tc.getClass().getName()); // NOI18N
146
Logger.getLogger(NbSheet.class.getName()).log(Level.WARNING, null, exc);
147                     //Fallback to accessor reserved for window system.
148
NbSheet.getDefault();
149                 }
150             } else {
151                 //NbSheet cannot be deserialized
152
//Fallback to accessor reserved for window system.
153
NbSheet.getDefault();
154             }
155         }
156         return sharedSheet;
157     }
158     
159     protected String JavaDoc preferredID () {
160         return "properties"; //NOI18N
161
}
162     
163     /* Singleton accessor reserved for window system ONLY. Used by window system to create
164      * NbSheet instance from settings file when method is given. Use <code>findDefault</code>
165      * to get correctly deserialized instance of NbSheet. */

166     public static NbSheet getDefault () {
167         if (sharedSheet == null) {
168             sharedSheet = new NbSheet(true);
169         }
170         return sharedSheet;
171     }
172     
173     /** Overriden to explicitely set persistence type of NbSheet
174      * to PERSISTENCE_ALWAYS */

175     public int getPersistenceType() {
176         return TopComponent.PERSISTENCE_ALWAYS;
177     }
178     
179     public HelpCtx getHelpCtx () {
180         // #40372 fix - for non-global properties display (assumed to be in a dialog), don't show the help button
181
return (global ? org.openide.explorer.ExplorerUtils.getHelpCtx (nodes, new HelpCtx (NbSheet.class)) : null);
182     }
183
184     /** Transfer the focus to the property sheet.
185      */

186     @SuppressWarnings JavaDoc("deprecation")
187     public void requestFocus () {
188         super.requestFocus();
189         propertySheet.requestFocus();
190     }
191     
192     /** Transfer the focus to the property sheet.
193      */

194     @SuppressWarnings JavaDoc("deprecation")
195     public boolean requestFocusInWindow () {
196         super.requestFocusInWindow();
197         return propertySheet.requestFocusInWindow();
198     }
199
200     /** always open global property sheet in its special mode */
201     @SuppressWarnings JavaDoc("deprecation")
202     public void open (Workspace workspace) {
203         if (global) {
204             Workspace realWorkspace = (workspace == null)
205                                       ? WindowManager.getDefault().getCurrentWorkspace()
206                                       : workspace;
207             Mode tcMode = realWorkspace.findMode(this);
208             if (tcMode == null) {
209                 // dock into our mode if not docked yet
210
Mode mode = realWorkspace.findMode("properties"); // NOI18N
211
if (mode == null) {
212                     mode = realWorkspace.createMode(
213                         "properties", // NOI18N
214
NbBundle.getBundle(NbSheet.class).getString("CTL_PropertiesWindow"),
215                         null
216                     );
217                 }
218                 mode.dockInto(this);
219             }
220         }
221         // behave like superclass
222
super.open(workspace);
223
224         if(global) {
225             // Set the nodes when opening.
226
SwingUtilities.invokeLater(listener);
227         }
228     }
229
230     /** cache the title formatters, they are used frequently and are slow to construct */
231     private static MessageFormat JavaDoc globalPropertiesFormat = null;
232     private static MessageFormat JavaDoc localPropertiesFormat = null;
233
234     /** Changes name of the component to reflect currently displayed nodes.
235     * Called when set of displayed nodes has changed.
236     */

237     protected void updateTitle () {
238         // different naming for global and local sheets
239
Mode ourMode = WindowManager.getDefault().findMode(this);
240         String JavaDoc nodeTitle = null;
241
242         // Fix a bug #12890, copy the nodes to prevent race condition.
243
List JavaDoc<Node> copyNodes = new ArrayList JavaDoc<Node>(Arrays.asList(nodes));
244
245         Node node = null;
246
247         if (!copyNodes.isEmpty()) {
248             node = copyNodes.get(0);
249         }
250
251         if(node == null) {
252             nodeTitle = ""; // NOI18N
253
} else {
254             nodeTitle = node.getDisplayName();
255             Object JavaDoc alternativeDisplayName = node.getValue(PROP_LONGER_DISPLAY_NAME);
256             if (alternativeDisplayName instanceof String JavaDoc) {
257                 nodeTitle = (String JavaDoc)alternativeDisplayName;
258             }
259         }
260         Object JavaDoc[] titleParams = new Object JavaDoc[] {
261             Integer.valueOf(copyNodes.size()),
262             nodeTitle
263         };
264         // different naming if docked in properties mode
265
if ((ourMode != null) &&
266             ("properties".equals(ourMode.getName()))) { // NOI18N
267
if (globalPropertiesFormat == null) {
268                 globalPropertiesFormat = new MessageFormat JavaDoc(NbBundle.getMessage(NbSheet.class, "CTL_FMT_GlobalProperties"));
269             }
270             setName(globalPropertiesFormat.format(titleParams));
271         } else {
272             if (localPropertiesFormat == null) {
273                 localPropertiesFormat = new MessageFormat JavaDoc(NbBundle.getMessage(NbSheet.class, "CTL_FMT_LocalProperties"));
274             }
275             setName(localPropertiesFormat.format(titleParams));
276         }
277         setToolTipText(getName());
278     }
279
280     /** Nodes to display.
281     */

282     public void setNodes (Node[] nodes) {
283         setNodesWithoutReattaching(nodes);
284         // re-attach to listen to new nodes
285
snListener.detach();
286         snListener.attach(nodes);
287     }
288     
289     final Node[] getNodes() {
290         return nodes;
291     }
292
293     /** Helper method, called from SheetNodesListener inner class */
294     private void setNodesWithoutReattaching (Node[] nodes) {
295         this.nodes = nodes;
296         propertySheet.setNodes(nodes);
297         SwingUtilities.invokeLater(new Runnable JavaDoc() {
298             public void run() {
299                 updateTitle();
300             }
301         });
302     }
303 /*
304     public Dimension getPreferredSize () {
305         return propertySheet.getPreferredSize();
306     }
307  */

308
309     /** Serialize this property sheet */
310     public void writeExternal (ObjectOutput JavaDoc out)
311     throws IOException JavaDoc {
312         super.writeExternal(out);
313
314         if (global) {
315             // write dummy array
316
out.writeObject (null);
317         } else {
318             Node.Handle[] arr = NodeOp.toHandles (nodes);
319             out.writeObject(arr);
320         }
321
322         out.writeBoolean(global);
323     }
324
325     /** Deserialize this property sheet. */
326     public void readExternal (ObjectInput JavaDoc in)
327     throws IOException JavaDoc, ClassNotFoundException JavaDoc {
328         try {
329             super.readExternal(in);
330         } catch (SafeException se) {
331             // ignore--we really do not care about the explorer manager that much
332
//System.err.println("ignoring a SafeException: " + se.getLocalizedMessage ());
333
}
334         Object JavaDoc obj = in.readObject ();
335
336         if (obj instanceof NbMarshalledObject || obj instanceof ExplorerManager) {
337             // old version read the Boolean
338
global = ((Boolean JavaDoc)in.readObject()).booleanValue();
339         } else {
340             Node[] nodes;
341
342             if (obj == null) {
343                 // handles can also be null for global
344
// property sheet
345
nodes = TopComponent.getRegistry().getActivatedNodes();
346             } else {
347                 // new version, first read the nodes and then the global boolean
348
Node.Handle[] arr = (Node.Handle[])obj;
349
350                 try {
351                     nodes = NodeOp.fromHandles (arr);
352                 } catch (IOException JavaDoc ex) {
353                     Exceptions.attachLocalizedMessage(ex,
354                                                       NbBundle.getBundle(NbSheet.class).getString("EXC_CannotLoadNodes"));
355                     Logger.getLogger(NbSheet.class.getName()).log(Level.WARNING, null, ex);
356                     nodes = new Node[0];
357                 }
358             }
359
360             global = in.readBoolean ();
361
362             setNodes (nodes);
363         }
364
365         /*
366               if (obj instanceof Boolean) {
367                 global = (Boolean)in.readObject ()
368
369               global = ((Boolean)in.readObject()).booleanValue();
370         /*
371               // start global listening if needed, but wait until
372               // deserialization is done (ExplorerManager is uses
373               // post-deserialization validating too, so we are forced
374               // to use it)
375               ((ObjectInputStream)in).registerValidation(
376                 new ObjectInputValidation () {
377                   public void validateObject () {
378                     updateGlobalListening(false);
379                   }
380                 }, 0
381               );
382         */

383         // JST: I guess we are not and moreover the type casting is really ugly
384
// updateGlobalListening (global);
385
}
386
387     /** Resolve to singleton instance, if needed. */
388     public Object JavaDoc readResolve ()
389     throws ObjectStreamException JavaDoc {
390         if (global) {
391             return getDefault();
392         } else {
393             if ((nodes == null) || (nodes.length <= 0)) {
394                 return null;
395             }
396         }
397         return this;
398     }
399
400     protected Object JavaDoc writeReplace() throws ObjectStreamException JavaDoc {
401         if (global) {
402             return new Replacer();
403         } else {
404             return super.writeReplace();
405         }
406     }
407
408     private static final class Replacer implements Serializable JavaDoc {
409         static final long serialVersionUID=-7897067133215740572L;
410         Replacer() {}
411         private Object JavaDoc readResolve() throws ObjectStreamException JavaDoc {
412             return NbSheet.getDefault();
413         }
414     }
415
416     /** Helper, listener variable must be initialized before
417     * calling this */

418     private void updateGlobalListening(boolean listen) {
419         if (global) {
420             if (listen) {
421                 TopComponent.getRegistry().addPropertyChangeListener(
422                     listener);
423             } else {
424                 TopComponent.getRegistry().removePropertyChangeListener (listener);
425             }
426         }
427     }
428     
429     protected void componentOpened() {
430         updateGlobalListening (true);
431     }
432     
433     protected void componentClosed() {
434         updateGlobalListening (false);
435     }
436     
437     protected void componentDeactivated() {
438         super.componentDeactivated();
439         if (Utilities.isMac()) {
440             propertySheet.firePropertyChange("MACOSX", true, false);
441         }
442     }
443     
444     /** Change listener to changes in selected nodes. And also
445     * nodes listener to listen to global changes of the nodes.
446     */

447     private class Listener extends Object JavaDoc implements Runnable JavaDoc, PropertyChangeListener JavaDoc {
448         Listener() {}
449         public void propertyChange (PropertyChangeEvent JavaDoc ev) {
450             if (TopComponent.Registry.PROP_ACTIVATED_NODES.equals( ev.getPropertyName() )) {
451                 activate();
452             }
453             /*
454             if ((ev.getPropertyName().equals(TopComponent.Registry.PROP_ACTIVATED)) &&
455                 (ev.getNewValue() == Sheet.this)) {
456                 return; // we do not want to call setNodes if we are
457                         // the activated window
458             }
459             activate ();
460              */

461         }
462
463         public void run() {
464             activate();
465         }
466
467         public void activate () {
468             Node[] arr = TopComponent.getRegistry ().getActivatedNodes();
469             setNodes (arr);
470         }
471
472     }
473     /** Change listener to changes in selected nodes. And also
474     * nodes listener to listen to global changes of the nodes.
475     */

476     private class SheetNodesListener extends NodeAdapter implements Runnable JavaDoc {
477
478         /* maps nodes to their listeners (Node, WeakListener) */
479         private HashMap JavaDoc<Node,NodeListener> listenerMap;
480
481         /* maps nodes to their proeprty change listeners (Node, WeakListener)*/
482         private HashMap JavaDoc<Node,PropertyChangeListener JavaDoc> pListenerMap;
483
484         SheetNodesListener() {}
485
486         /** Fired when the node is deleted.
487          * @param ev event describing the node
488          */

489         public void nodeDestroyed(NodeEvent ev) {
490             Node destroyedNode = ev.getNode();
491             NodeListener listener = listenerMap.get(destroyedNode);
492             PropertyChangeListener JavaDoc pListener = pListenerMap.get(destroyedNode);
493             // stop to listen to destroyed node
494
destroyedNode.removeNodeListener(listener);
495             destroyedNode.removePropertyChangeListener(pListener);
496             listenerMap.remove(destroyedNode);
497             pListenerMap.remove(destroyedNode);
498             // close top component (our outer class) if last node was destroyed
499
if (listenerMap.isEmpty() && !global) {
500                 //fix #39251 start - posting the closing of TC to awtevent thread
501
Mutex.EVENT.readAccess(new Runnable JavaDoc() {
502                     public void run() {
503                         close();
504                     }
505                 });
506                 //fix #39251 end
507
} else {
508                 setNodesWithoutReattaching(
509                     (listenerMap.keySet().toArray(new Node[listenerMap.size()]))
510                 );
511             }
512         }
513
514         public void attach (Node[] nodes) {
515             listenerMap = new HashMap JavaDoc<Node,NodeListener>(nodes.length * 2);
516             pListenerMap = new HashMap JavaDoc<Node,PropertyChangeListener JavaDoc>(nodes.length * 2);
517             NodeListener curListener = null;
518             PropertyChangeListener JavaDoc pListener = null;
519             // start to listen to all given nodes and map nodes to
520
// their listeners
521
for (int i = 0; i < nodes.length; i++) {
522                 curListener = org.openide.nodes.NodeOp.weakNodeListener (this, nodes[i]);
523                 pListener = org.openide.util.WeakListeners.propertyChange(this, nodes[i]);
524                 listenerMap.put(nodes[i], curListener);
525                 pListenerMap.put(nodes[i], pListener);
526                 nodes[i].addNodeListener(curListener);
527                 nodes[i].addPropertyChangeListener(pListener);
528             };
529         }
530
531         public void detach () {
532             if (listenerMap == null) {
533                 return;
534             }
535             // stop to listen to all nodes
536
for (Iterator JavaDoc iter = listenerMap.entrySet().iterator(); iter.hasNext(); ) {
537                 Map.Entry JavaDoc curEntry = (Map.Entry JavaDoc)iter.next();
538                 ((Node)curEntry.getKey()).removeNodeListener((NodeListener)curEntry.getValue());
539             }
540             for (Iterator JavaDoc iter = pListenerMap.entrySet().iterator(); iter.hasNext(); ) {
541                 Map.Entry JavaDoc curEntry = (Map.Entry JavaDoc)iter.next();
542                 ((Node)curEntry.getKey()).removePropertyChangeListener((PropertyChangeListener JavaDoc)curEntry.getValue());
543             }
544             // destroy the map
545
listenerMap = null;
546             pListenerMap = null;
547         }
548
549         public void propertyChange(PropertyChangeEvent JavaDoc pce) {
550             if (Node.PROP_DISPLAY_NAME.equals(pce.getPropertyName())) {
551                 SwingUtilities.invokeLater(this);
552             }
553         }
554
555         public void run() {
556             updateTitle();
557         }
558
559     } // End of SheetNodesListener.
560

561 }
562
Popular Tags