KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > openide > windows > CloneableOpenSupport


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 package org.openide.windows;
20
21 import org.openide.awt.StatusDisplayer;
22 import org.openide.util.*;
23
24 import java.beans.*;
25
26
27 /** Simple support for an openable objects.
28 * Can be used either as an {@link org.openide.cookies.OpenCookie},
29 * {@link org.openide.cookies.ViewCookie}, or {@link org.openide.cookies.CloseCookie},
30 * depending on which cookies the subclass implements.
31 *
32 * @author Jaroslav Tulach
33 */

34 public abstract class CloneableOpenSupport extends Object JavaDoc {
35     private static java.awt.Container JavaDoc container;
36
37     /** the environment that provides connection to outside world */
38     protected Env env;
39
40     /** All opened editors on this file.
41      * <em>Warning:</em> Treat this field like <code>final</code>.
42      * Internally the instance is used as <code>WeakListener</code>
43      * on <code>Env</code> validity changes.
44      * Changing the instance in subclasses would lead to breaking
45      * of that listening, thus to errorneous behaviour. */

46     protected CloneableTopComponent.Ref allEditors;
47
48     /** New support for a given environment.
49     * @param env environment to take all date from/to
50     */

51     public CloneableOpenSupport(Env env) {
52         this.env = env;
53
54         Listener l = new Listener(env);
55         this.allEditors = l;
56
57         // attach property change listener to be informed about loosing validity
58
env.addPropertyChangeListener(org.openide.util.WeakListeners.propertyChange(l, env));
59
60         // attach vetoable change listener to be cancel loosing validity when modified
61
env.addVetoableChangeListener(org.openide.util.WeakListeners.vetoableChange(l, env));
62     }
63
64     /** Opens and focuses or just focuses already opened
65      * <code>CloneableTopComponent</code>.
66      * <p><b>Note: The actual processing of this method is scheduled into AWT thread
67      * in case it is called from other than the AWT thread.</b></p>
68      * @see org.openide.cookies.OpenCookie#open
69      * @see #openCloneableTopComponent
70      */

71     public void open() {
72         //Bugfix #10688 open() is now run in AWT thread
73
Mutex.EVENT.writeAccess(
74             new Runnable JavaDoc() {
75                 public void run() {
76                     CloneableTopComponent editor = openCloneableTopComponent();
77                     editor.requestActive();
78                 }
79             }
80         );
81     }
82
83     /** Focuses existing component to view, or if none exists creates new.
84     * The default implementation simply calls {@link #open}.
85     * @see org.openide.cookies.ViewCookie#view
86     */

87     public void view() {
88         open();
89     }
90
91     /** Focuses existing component to view, or if none exists creates new.
92     * The default implementation simply calls {@link #open}.
93     * @see org.openide.cookies.EditCookie#edit
94     */

95     public void edit() {
96         open();
97     }
98
99     /** Closes all components.
100     * @return <code>true</code> if every component is successfully closed or <code>false</code> if the user cancelled the request
101     * @see org.openide.cookies.CloseCookie#close
102     */

103     public boolean close() {
104         return close(true);
105     }
106
107     /** Closes all opened windows.
108     * @param ask true if we should ask user
109     * @return true if sucesfully closed
110     */

111     protected boolean close(final boolean ask) {
112         if (allEditors.isEmpty()) {
113             return true;
114         }
115
116         //Bugfix #10688 close() is now run in AWT thread
117
//also bugfix of 10714 - whole close (boolean) is run in AWT thread
118
Boolean JavaDoc ret = Mutex.EVENT.writeAccess(
119                 new Mutex.Action<Boolean JavaDoc>() {
120                     public Boolean JavaDoc run() {
121                         //synchronized (allEditors) {
122
synchronized (getLock()) {
123                             // user canceled the action
124
if (ask && !canClose()) {
125                                 return Boolean.FALSE;
126                             }
127
128                             java.util.Enumeration JavaDoc en = allEditors.getComponents();
129
130                             while (en.hasMoreElements()) {
131                                 TopComponent c = (TopComponent) en.nextElement();
132
133                                 if (!c.close()) {
134                                     return Boolean.FALSE;
135                                 }
136                             }
137                         }
138
139                         return Boolean.TRUE;
140                     }
141                 }
142             );
143
144         return ret.booleanValue();
145     }
146
147     /** Should test whether all data is saved, and if not, prompt the user
148     * to save.
149     * The default implementation returns <code>true</code>.
150     *
151     * @return <code>true</code> if everything can be closed
152     */

153     protected boolean canClose() {
154         return true;
155     }
156
157     /** Simply open for an editor. */
158     protected final CloneableTopComponent openCloneableTopComponent() {
159         //synchronized (allEditors) {
160
synchronized (getLock()) {
161             CloneableTopComponent ret = allEditors.getArbitraryComponent();
162
163             if (ret != null) {
164                 ret.open();
165
166                 return ret;
167             } else {
168                 // no opened editor
169
String JavaDoc msg = messageOpening();
170
171                 if (msg != null) {
172                     StatusDisplayer.getDefault().setStatusText(msg);
173                 }
174
175                 CloneableTopComponent editor = createCloneableTopComponent();
176                 editor.setReference(allEditors);
177                 editor.open();
178
179                 msg = messageOpened();
180
181                 if (msg == null) {
182                     msg = ""; // NOI18N
183
}
184
185                 StatusDisplayer.getDefault().setStatusText(msg);
186
187                 return editor;
188             }
189         }
190     }
191
192     /** Creates lock object used in close and openCloneableTopComponent. */
193     private Object JavaDoc getLock() {
194         if (container == null) {
195             container = new java.awt.Container JavaDoc();
196         }
197
198         return container.getTreeLock();
199     }
200
201     /** A method to create a new component. Must be overridden in subclasses.
202     * @return the cloneable top component for this support
203     */

204     protected abstract CloneableTopComponent createCloneableTopComponent();
205
206     /** Message to display when an object is being opened.
207     * @return the message or null if nothing should be displayed
208     */

209     protected abstract String JavaDoc messageOpening();
210
211     /** Message to display when an object has been opened.
212     * @return the message or null if nothing should be displayed
213     */

214     protected abstract String JavaDoc messageOpened();
215
216     /** Abstract interface that is used by CloneableOpenSupport to
217     * talk to outside world.
218     */

219     public static interface Env extends java.io.Serializable JavaDoc {
220         /** that is fired when the objects wants to mark itself as
221         * invalid, so all components should be closed.
222         */

223         public static final String JavaDoc PROP_VALID = "valid"; // NOI18N
224

225         /** that is fired when the objects wants to mark itself modified
226         * or not modified.
227         */

228         public static final String JavaDoc PROP_MODIFIED = "modified"; // NOI18N
229

230         /** Adds property listener.
231         */

232         public void addPropertyChangeListener(PropertyChangeListener l);
233
234         /** Removes property listener.
235         */

236         public void removePropertyChangeListener(PropertyChangeListener l);
237
238         /** Adds veto listener.
239         */

240         public void addVetoableChangeListener(VetoableChangeListener l);
241
242         /** Removes veto listener.
243         */

244         public void removeVetoableChangeListener(VetoableChangeListener l);
245
246         /** Test whether the support is in valid state or not.
247         * It could be invalid after deserialization when the object it
248         * referenced to does not exist anymore.
249         *
250         * @return true or false depending on its state
251         */

252         public boolean isValid();
253
254         /** Test whether the object is modified or not.
255         * @return true if the object is modified
256         */

257         public boolean isModified();
258
259         /** Support for marking the environement modified.
260         * @exception IOException if the environment cannot be marked modified
261         * (for example when the file is readonly), when such exception
262         * is the support should discard all previous changes
263         */

264         public void markModified() throws java.io.IOException JavaDoc;
265
266         /** Reverse method that can be called to make the environment
267         * unmodified.
268         */

269         public void unmarkModified();
270
271         /** Method that allows environment to find its
272         * cloneable open support.
273         */

274         public CloneableOpenSupport findCloneableOpenSupport();
275     }
276
277     /** Property change & veto listener. To react to dispose/delete of
278     * the data object.
279     */

280     private static final class Listener extends CloneableTopComponent.Ref implements PropertyChangeListener,
281         VetoableChangeListener, Runnable JavaDoc {
282         /** generated Serialized Version UID */
283         static final long serialVersionUID = -1934890789745432531L;
284
285         /** environment to use as connection to outside world */
286         private Env env;
287
288         /** Constructor.
289         */

290         public Listener(Env env) {
291             this.env = env;
292         }
293
294         /** Getter for the associated CloneableOpenSupport
295         * @return the support or null if none was found
296         */

297         private CloneableOpenSupport support() {
298             return env.findCloneableOpenSupport();
299         }
300
301         public void propertyChange(PropertyChangeEvent ev) {
302             if (Env.PROP_VALID.equals(ev.getPropertyName())) {
303                 // do not check it if old value is not true
304
if (Boolean.FALSE.equals(ev.getOldValue())) {
305                     return;
306                 }
307
308                 Mutex.EVENT.readAccess(this);
309             }
310         }
311
312         /** Closes the support in AWT thread.
313          */

314         public void run() {
315             // loosing validity
316
CloneableOpenSupport os = support();
317
318             if (os != null) {
319                 // mark the object as not being modified, so nobody
320
// will ask for save
321
env.unmarkModified();
322
323                 os.close(false);
324             }
325         }
326
327         /** Forbids setValid (false) on data object when there is an
328         * opened editor.
329         *
330         * @param ev PropertyChangeEvent
331         */

332         public void vetoableChange(PropertyChangeEvent ev)
333         throws PropertyVetoException {
334             if (Env.PROP_VALID.equals(ev.getPropertyName())) {
335                 // do not check it if old value is not true
336
if (Boolean.FALSE.equals(ev.getOldValue())) {
337                     return;
338                 }
339
340                 if (env.isModified()) {
341                     // if the object is modified
342
CloneableOpenSupport os = support();
343
344                     if ((os != null) && !os.canClose()) {
345                         // is modified and has not been sucessfully closed
346
throw new PropertyVetoException(
347                         // [PENDING] this is not a very good detail message!
348
"", ev // NOI18N
349
);
350                     }
351                 }
352             }
353         }
354
355         /** Resolvable to connect to the right data object. This
356         * method is used for connectiong CloneableTopComponents via
357         * their CloneableTopComponent.Ref
358         */

359         public Object JavaDoc readResolve() {
360             CloneableOpenSupport os = support();
361
362             if (os == null) {
363                 // problem! no replace!?
364
return this;
365             }
366
367             // use the editor support's CloneableTopComponent.Ref
368
return os.allEditors;
369         }
370     }
371 }
372
Popular Tags