KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > openide > text > CloneableEditor


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.openide.text;
21
22
23 import java.awt.*;
24 import java.io.*;
25 import java.util.logging.Level JavaDoc;
26 import java.util.logging.Logger JavaDoc;
27 import javax.swing.*;
28 import javax.swing.border.Border JavaDoc;
29 import javax.swing.text.*;
30 import org.openide.awt.UndoRedo;
31 import org.openide.cookies.EditorCookie;
32 import org.openide.util.*;
33 import org.openide.util.actions.SystemAction;
34 import org.openide.windows.*;
35
36 /** Cloneable top component to hold the editor kit.
37  */

38 public class CloneableEditor extends CloneableTopComponent implements CloneableEditorSupport.Pane {
39     private static final String JavaDoc HELP_ID = "editing.editorwindow"; // !!! NOI18N
40
static final long serialVersionUID = -185739563792410059L;
41
42     /** editor pane */
43     protected JEditorPane pane;
44
45     /** Asociated editor support */
46     private CloneableEditorSupport support;
47
48     /** Flag indicating it was initialized this <code>CloneableEditor</code> */
49     private boolean initialized;
50
51     /** Position of cursor. Used to keep the value between deserialization
52      * and initialization time. */

53     private int cursorPosition = -1;
54
55     // #20647. More important custom component.
56

57     /** Custom editor component, which is used if specified by document
58      * which implements <code>NbDocument.CustomEditor</code> interface.
59      * @see NbDocument.CustomEditor#createEditor */

60     private Component customComponent;
61     private JToolBar customToolbar;
62
63     /** For externalization of subclasses only */
64     public CloneableEditor() {
65         this(null);
66     }
67
68     /** Creates new editor component associated with
69     * support object.
70     * @param support support that holds the document and operations above it
71     */

72     public CloneableEditor(CloneableEditorSupport support) {
73         super();
74         this.support = support;
75
76         updateName();
77         _setCloseOperation();
78     }
79     @SuppressWarnings JavaDoc("deprecation")
80     private void _setCloseOperation() {
81         setCloseOperation(CLOSE_EACH);
82     }
83
84     /** Gives access to {@link CloneableEditorSupport} object under
85      * this <code>CloneableEditor</code> component.
86      * @return the {@link CloneableEditorSupport} object
87      * that holds the document or <code>null</code>, what means
88      * this component is not in valid state yet and can be discarded */

89     protected CloneableEditorSupport cloneableEditorSupport() {
90         return support;
91     }
92
93     /** Overriden to explicitely set persistence type of CloneableEditor
94      * to PERSISTENCE_ONLY_OPENED */

95     public int getPersistenceType() {
96         return TopComponent.PERSISTENCE_ONLY_OPENED;
97     }
98
99     /** Get context help for this editor pane.
100      * If the registered editor kit provides a help ID in bean info
101      * according to the protocol described for {@link HelpCtx#findHelp},
102      * then that it used, else general help on the editor is provided.
103      * @return context help
104      */

105     public HelpCtx getHelpCtx() {
106         Object JavaDoc kit = support.cesKit();
107         HelpCtx fromKit = kit == null ? null : HelpCtx.findHelp(kit);
108
109         if (fromKit != null) {
110             return fromKit;
111         } else {
112             return new HelpCtx(HELP_ID);
113         }
114     }
115
116     /**
117      * Indicates whether this component can be closed.
118      * Adds scheduling of "emptying" editor pane and removing all sub components.
119      * {@inheritDoc}
120      */

121     public boolean canClose() {
122         boolean result = super.canClose();
123
124         if (result) {
125             SwingUtilities.invokeLater(
126                 new Runnable JavaDoc() {
127                     public void run() {
128                         // #23486: pane could not be initialized yet.
129
if (pane != null) {
130                             Document doc = support.createStyledDocument(pane.getEditorKit());
131                             pane.setDocument(doc);
132                             pane.setEditorKit(null);
133                         }
134
135                         removeAll();
136                         initialized = false;
137                     }
138                 }
139             );
140         }
141
142         return result;
143     }
144
145     /** Overrides superclass method. In case it is called first time,
146      * initializes this <code>CloneableEditor</code>. */

147     protected void componentShowing() {
148         super.componentShowing();
149         initialize();
150     }
151
152     /** Performs needed initialization */
153     private void initialize() {
154         if (initialized || discard()) {
155             return;
156         }
157
158         initialized = true;
159
160         Task prepareTask = support.prepareDocument();
161
162         // load the doc synchronously
163
prepareTask.waitFinished();
164
165         Document doc = support.getDocument();
166
167         setLayout(new BorderLayout());
168
169         final QuietEditorPane pane = new QuietEditorPane();
170
171         pane.getAccessibleContext().setAccessibleName(
172             NbBundle.getMessage(CloneableEditor.class, "ACS_CloneableEditor_QuietEditorPane", this.getName())
173         );
174         pane.getAccessibleContext().setAccessibleDescription(
175             NbBundle.getMessage(
176                 CloneableEditor.class, "ACSD_CloneableEditor_QuietEditorPane",
177                 this.getAccessibleContext().getAccessibleDescription()
178             )
179         );
180
181         this.pane = pane;
182
183         // Init action map: cut,copy,delete,paste actions.
184
javax.swing.ActionMap JavaDoc am = getActionMap();
185
186         //#43157 - editor actions need to be accessible from outside using the TopComponent.getLookup(ActionMap.class) call.
187
// used in main menu enabling/disabling logic.
188
javax.swing.ActionMap JavaDoc paneMap = pane.getActionMap();
189         am.setParent(paneMap);
190
191         //#41223 set the defaults befor the custom editor + kit get initialized, giving them opportunity to
192
// override defaults..
193
paneMap.put(DefaultEditorKit.cutAction, getAction(DefaultEditorKit.cutAction));
194         paneMap.put(DefaultEditorKit.copyAction, getAction(DefaultEditorKit.copyAction));
195         paneMap.put("delete", getAction(DefaultEditorKit.deleteNextCharAction)); // NOI18N
196
paneMap.put(DefaultEditorKit.pasteAction, getAction(DefaultEditorKit.pasteAction));
197
198         pane.setEditorKit(support.cesKit());
199
200         pane.setDocument(doc);
201
202         if (doc instanceof NbDocument.CustomEditor) {
203             NbDocument.CustomEditor ce = (NbDocument.CustomEditor) doc;
204             customComponent = ce.createEditor(pane);
205
206             if (customComponent == null) {
207                 throw new IllegalStateException JavaDoc(
208                     "Document:" + doc // NOI18N
209
+" implementing NbDocument.CustomEditor may not" // NOI18N
210
+" return null component"
211                 ); // NOI18N
212
}
213
214             add(support.wrapEditorComponent(customComponent), BorderLayout.CENTER);
215         } else { // not custom editor
216

217             // remove default JScrollPane border, borders are provided by window system
218
JScrollPane noBorderPane = new JScrollPane(pane);
219             pane.setBorder(null);
220             add(support.wrapEditorComponent(noBorderPane), BorderLayout.CENTER);
221         }
222
223         if (doc instanceof NbDocument.CustomToolbar) {
224             NbDocument.CustomToolbar ce = (NbDocument.CustomToolbar) doc;
225             customToolbar = ce.createToolbar(pane);
226
227             if (customToolbar == null) {
228                 throw new IllegalStateException JavaDoc(
229                     "Document:" + doc // NOI18N
230
+" implementing NbDocument.CustomToolbar may not" // NOI18N
231
+" return null toolbar"
232                 ); // NOI18N
233
}
234
235             Border JavaDoc b = (Border JavaDoc) UIManager.get("Nb.Editor.Toolbar.border"); //NOI18N
236
customToolbar.setBorder(b);
237             add(customToolbar, BorderLayout.NORTH);
238         }
239
240         pane.setWorking(QuietEditorPane.ALL);
241
242         // set the caret to right possition if this component was deserialized
243
if (cursorPosition != -1) {
244             Caret caret = pane.getCaret();
245
246             if (caret != null) {
247                 caret.setDot(cursorPosition);
248             }
249         }
250
251         support.ensureAnnotationsLoaded();
252     }
253
254     protected CloneableTopComponent createClonedObject() {
255         return support.createCloneableTopComponent();
256     }
257
258     /** Descendants overriding this method must either call
259      * this implementation or fire the
260      * {@link org.openide.cookies.EditorCookie.Observable#PROP_OPENED_PANES}
261      * property change on their own.
262      */

263     protected void componentOpened() {
264         super.componentOpened();
265
266         CloneableEditorSupport ces = cloneableEditorSupport();
267
268         if (ces != null) {
269             ces.firePropertyChange(EditorCookie.Observable.PROP_OPENED_PANES, null, null);
270         }
271     }
272
273     /** Descendants overriding this method must either call
274      * this implementation or fire the
275      * {@link org.openide.cookies.EditorCookie.Observable#PROP_OPENED_PANES}
276      * property change on their own.
277      */

278     protected void componentClosed() {
279         super.componentClosed();
280
281         CloneableEditorSupport ces = cloneableEditorSupport();
282
283         if (ces != null) {
284             ces.firePropertyChange(EditorCookie.Observable.PROP_OPENED_PANES, null, null);
285         }
286     }
287
288     /** Overrides superclass version. Opens top component only if
289      * it is in valid state.
290      * (Editor top component may become invalid after deserialization).<br>
291      * Also tries to open all other top components which are docked
292      * in editor mode on given workspace, but not visible.<br>
293      */

294     @SuppressWarnings JavaDoc("deprecation")
295     public void open(Workspace workspace) {
296         if (discard()) {
297             Logger.getAnonymousLogger().warning(
298                 "Can not open " + this + " component," // NOI18N
299
+" its support environment is not valid" // NOI18N
300
+" [support=" + support + ", env=" // NOI18N
301
+((support == null) ? null : support.cesEnv()) + "]"
302             ); // NOI18N
303
} else {
304             dockIfNeeded();
305             super.open(workspace);
306         }
307     }
308
309     /** When closing last view, also close the document.
310      * @return <code>true</code> if close succeeded
311      */

312     protected boolean closeLast() {
313         if (!support.canClose()) {
314             // if we cannot close the last window
315
return false;
316         }
317
318         // close everything and do not ask
319
support.notifyClosed();
320
321         if (support.getLastSelected() == this) {
322             support.setLastSelected(null);
323         }
324
325         return true;
326     }
327
328     /** The undo/redo manager of the support.
329      * @return the undo/redo manager shared by all editors for this support
330      */

331     public UndoRedo getUndoRedo() {
332         return support.getUndoRedo();
333     }
334
335     @Override JavaDoc
336     public Action[] getActions() {
337         Action[] a = super.getActions();
338
339         try {
340             ClassLoader JavaDoc l = Lookup.getDefault().lookup(ClassLoader JavaDoc.class);
341
342             if (l == null) {
343                 l = getClass().getClassLoader();
344             }
345
346             Class JavaDoc<? extends SystemAction> c = Class.forName("org.openide.actions.FileSystemAction", true, l).asSubclass(SystemAction.class); // NOI18N
347
SystemAction ra = SystemAction.findObject(c, true);
348
349             Action[] a2 = new Action[a.length + 1];
350             System.arraycopy(a, 0, a2, 0, a.length);
351             a2[a.length] = ra;
352             return a2;
353         } catch (Exception JavaDoc ex) {
354             // ok, we no action like this I guess
355
}
356
357         return a;
358     }
359
360     /** Transfer the focus to the editor pane.
361      */

362     @SuppressWarnings JavaDoc("deprecation")
363     public void requestFocus() {
364         super.requestFocus();
365
366         if ((customComponent != null) && !SwingUtilities.isDescendingFrom(pane, customComponent)) {
367             customComponent.requestFocus();
368         } else if (pane != null) {
369             pane.requestFocus();
370         }
371     }
372
373     /** Transfer the focus to the editor pane.
374      */

375     @SuppressWarnings JavaDoc("deprecation")
376     public boolean requestFocusInWindow() {
377         super.requestFocusInWindow();
378
379         if ((customComponent != null) && !SwingUtilities.isDescendingFrom(pane, customComponent)) {
380             return customComponent.requestFocusInWindow();
381         } else if (pane != null) {
382             return pane.requestFocusInWindow();
383         }
384
385         return false;
386     }
387
388     @SuppressWarnings JavaDoc("deprecation")
389     public boolean requestDefaultFocus() {
390         if ((customComponent != null) && !SwingUtilities.isDescendingFrom(pane, customComponent)) {
391             return customComponent.requestFocusInWindow();
392         } else if (pane != null) {
393             return pane.requestFocusInWindow();
394         }
395
396         return false;
397     }
398
399     // XXX is this method really needed?
400
/** @return Preferred size of editor top component */
401     public Dimension getPreferredSize() {
402         @SuppressWarnings JavaDoc("deprecation")
403         Rectangle bounds = WindowManager.getDefault().getCurrentWorkspace().getBounds();
404
405         return new Dimension(bounds.width / 2, bounds.height / 2);
406     }
407
408     private Action getAction(String JavaDoc key) {
409         if (key == null) {
410             return null;
411         }
412
413         // Try to find the action from kit.
414
EditorKit kit = support.cesKit();
415
416         if (kit == null) { // kit is cleared in closeDocument()
417

418             return null;
419         }
420
421         Action[] actions = kit.getActions();
422
423         for (int i = 0; i < actions.length; i++) {
424             if (key.equals(actions[i].getValue(Action.NAME))) {
425                 return actions[i];
426             }
427         }
428
429         return null;
430     }
431
432     /** Overrides superclass method. Remembers last selected component of
433      * support belonging to this component.
434      * @see #componentDeactivated */

435     protected void componentActivated() {
436         support.setLastSelected(this);
437     }
438
439     /** Updates the name and tooltip of this <code>CloneableEditor</code>
440      * {@link org.openide.windows.TopComponent TopCompoenent}
441      * according to the support retrieved from {@link #cloneableEditorSupport}
442      * method. The name and tooltip are in case of support presence
443      * updated thru its {@link CloneableEditorSupport#messageName} and
444      * {@link CloneableEditorSupport#messageToolTip} methods.
445      * @see #cloneableEditorSupport() */

446     public void updateName() {
447         final CloneableEditorSupport ces = cloneableEditorSupport();
448
449         if (ces != null) {
450             Mutex.EVENT.writeAccess(
451                 new Runnable JavaDoc() {
452                     public void run() {
453                         String JavaDoc name = ces.messageHtmlName();
454                         setHtmlDisplayName(name);
455                         name = ces.messageName();
456                         setDisplayName(name);
457                         setName(name); // XXX compatibility
458

459                         setToolTipText(ces.messageToolTip());
460                     }
461                 }
462             );
463         }
464     }
465
466     // override for simple and consistent IDs
467
protected String JavaDoc preferredID() {
468         final CloneableEditorSupport ces = cloneableEditorSupport();
469
470         if (ces != null) {
471             return ces.documentID();
472         }
473
474         return "";
475     }
476
477     public void writeExternal(ObjectOutput out) throws IOException {
478         super.writeExternal(out);
479
480         // Save environent if support is non-null.
481
// XXX #13685: When support is null, the tc will be discarded
482
// after deserialization.
483
out.writeObject((support != null) ? support.cesEnv() : null);
484
485         // #16461 Caret could be null?!,
486
// hot fix - making it robust for that case.
487
int pos = 0;
488
489         // 19559 Even pane could be null! Better solution would be put
490
// writeReplace method in place also, but it is a API change. For
491
// the time be just robust here.
492
JEditorPane p = pane;
493
494         if (p != null) {
495             Caret caret = p.getCaret();
496
497             if (caret != null) {
498                 pos = caret.getDot();
499             } else {
500                 if (p instanceof QuietEditorPane) {
501                     int lastPos = ((QuietEditorPane) p).getLastPosition();
502
503                     if (lastPos == -1) {
504                         Logger.getLogger(CloneableEditor.class.getName()).log(Level.WARNING, null,
505                                           new java.lang.IllegalStateException JavaDoc("Pane=" +
506                                                                               p +
507                                                                               "was not initialized yet!"));
508                     } else {
509                         pos = lastPos;
510                     }
511                 } else {
512                     Document doc = ((support != null) ? support.getDocument() : null);
513
514                     // Relevant only if document is non-null?!
515
if (doc != null) {
516                         Logger.getLogger(CloneableEditor.class.getName()).log(Level.WARNING, null,
517                                           new java.lang.IllegalStateException JavaDoc("Caret is null in editor pane=" +
518                                                                               p +
519                                                                               "\nsupport=" +
520                                                                               support +
521                                                                               "\ndoc=" +
522                                                                               doc));
523                     }
524                 }
525             }
526         }
527
528         out.writeObject(new Integer JavaDoc(pos));
529     }
530
531     public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException JavaDoc {
532         super.readExternal(in);
533
534         int offset;
535
536         Object JavaDoc firstObject = in.readObject();
537
538         // New deserialization that uses Env environment,
539
// and which could be null(!) see writeExternal.
540
if (firstObject instanceof CloneableOpenSupport.Env) {
541             CloneableOpenSupport.Env env = (CloneableOpenSupport.Env) firstObject;
542             CloneableOpenSupport os = env.findCloneableOpenSupport();
543             support = (CloneableEditorSupport) os;
544         }
545
546         // load cursor position
547
offset = ((Integer JavaDoc) in.readObject()).intValue();
548
549         if (!discard()) {
550             cursorPosition = offset;
551         }
552
553         updateName();
554     }
555
556     /**
557      * Replaces serializing object. Overrides superclass method. Adds checking
558      * for object validity. In case this object is invalid
559      * throws {@link java.io.NotSerializableException NotSerializableException}.
560      * @throws ObjectStreamException When problem during serialization occures.
561      * @throws NotSerializableException When this <code>CloneableEditor</code>
562      * is invalid and doesn't want to be serialized. */

563     protected Object JavaDoc writeReplace() throws ObjectStreamException {
564         if (discard()) {
565             throw new NotSerializableException("Serializing component is invalid: " + this); // NOI18N
566
}
567
568         return super.writeReplace();
569     }
570
571     /**
572      * Resolves deserialized object. Overrides superclass method. Adds checking
573      * for object validity. In case this object is invalid
574      * throws {@link java.io.InvalidObjectException InvalidObjectException}.
575      * @throws ObjecStreamException When problem during serialization occures.
576      * @throws InvalidObjectException When deserialized <code>CloneableEditor</code>
577      * is invalid and shouldn't be used. */

578     protected Object JavaDoc readResolve() throws ObjectStreamException {
579         if (discard()) {
580             throw new java.io.InvalidObjectException JavaDoc("Deserialized component is invalid: " + this); // NOI18N
581
} else {
582             support.initializeCloneableEditor(this);
583
584             return this;
585         }
586     }
587
588     /** This component should be discarded if the associated environment
589     * is not valid.
590     */

591     private boolean discard() {
592         return (support == null) || !support.cesEnv().isValid();
593     }
594
595     /** Dock this top component to editor mode if it is not docked
596      * in some mode at this time */

597     private void dockIfNeeded() {
598         // dock into editor mode if possible
599
Mode ourMode = WindowManager.getDefault().findMode(this);
600         if( null == ourMode ) {
601             //dock into 'editor' mode to avoid being tagged as a pre-version-4.0
602
//TopComponent that is allowed to be drag and dropped outside the editor area
603

604             //first check the active mode as it might be a floating editor window
605
TopComponent activeTc = TopComponent.getRegistry().getActivated();
606             if( null != activeTc ) {
607                 ourMode = WindowManager.getDefault().findMode( activeTc );
608                 if( !WindowManager.getDefault().isEditorMode( ourMode ) )
609                     ourMode = null;
610             }
611             if( null == ourMode )
612                 ourMode = WindowManager.getDefault().findMode( "editor" );
613             if( null != ourMode ) {
614                  ourMode.dockInto( this );
615             } else {
616                 //should not happen - editor mode is always defined
617
Logger.getAnonymousLogger().warning("The window system cannot find the default editor mode." );
618             }
619         }
620     }
621     
622     //
623
// Implements the CloneableEditorSupport.Pane interface
624
//
625
public CloneableTopComponent getComponent() {
626         return this;
627     }
628
629     public JEditorPane getEditorPane() {
630         initialize();
631
632         return pane;
633     }
634
635     /**
636      * callback for the Pane implementation to adjust itself to the openAt() request.
637      */

638     public void ensureVisible() {
639         open();
640         requestVisible();
641     }
642 }
643
Popular Tags