KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > properties > PropertiesOpen


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-2007 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.modules.properties;
21
22 import java.awt.*;
23 import java.awt.event.*;
24 import java.beans.*;
25 import java.io.IOException JavaDoc;
26 import java.io.ObjectInput JavaDoc;
27 import java.io.ObjectOutput JavaDoc;
28 import java.io.Serializable JavaDoc;
29 import java.util.*;
30 import javax.swing.Action JavaDoc;
31 import javax.swing.event.ChangeListener JavaDoc;
32 import javax.swing.*;
33 import javax.swing.undo.CannotRedoException JavaDoc;
34 import javax.swing.undo.CannotUndoException JavaDoc;
35
36 import org.openide.actions.FindAction;
37 import org.openide.awt.UndoRedo;
38 import org.openide.cookies.CloseCookie;
39 import org.openide.cookies.OpenCookie;
40 import org.openide.cookies.SaveCookie;
41 import org.openide.filesystems.FileObject;
42 import org.openide.filesystems.FileStateInvalidException;
43 import org.openide.filesystems.FileStatusEvent;
44 import org.openide.filesystems.FileStatusListener;
45 import org.openide.filesystems.FileSystem;
46 import org.openide.loaders.DataObject;
47 import org.openide.loaders.OpenSupport;
48 import org.openide.nodes.Node;
49 import org.openide.NotifyDescriptor;
50 import org.openide.DialogDisplayer;
51 import org.openide.ErrorManager;
52 import org.openide.util.actions.CallbackSystemAction;
53 import org.openide.util.actions.SystemAction;
54 import org.openide.util.*;
55 import org.openide.windows.*;
56 import org.openide.util.Utilities;
57 import org.openide.DialogDescriptor;
58 import org.openide.filesystems.FileUtil;
59
60
61 /**
62  * Support for opening bundle of .properties files (OpenCookie) in table view editor.
63  *
64  * @author Petr Jiricka, Peter Zavadsky
65  */

66 public class PropertiesOpen extends CloneableOpenSupport
67                             implements OpenCookie, CloseCookie {
68
69     /** Main properties dataobject */
70     PropertiesDataObject propDataObject;
71     
72     /** Listener for modificationc on dataobject, adding and removing save cookie */
73     PropertyChangeListener modifL;
74
75     /** UndoRedo manager for this properties open support */
76     protected transient UndoRedo.Manager undoRedoManager;
77
78     /** This object is used for marking all undoable edits performed as one atomic undoable action. */
79     transient Object JavaDoc atomicUndoRedoFlag;
80     
81
82     /** Constructor */
83     public PropertiesOpen(PropertiesDataObject propDataObject) {
84         super(new Environment(propDataObject));
85         
86         this.propDataObject = propDataObject;
87         
88         this.propDataObject.addPropertyChangeListener(WeakListeners.propertyChange(modifL =
89             new ModifiedListener(), this.propDataObject));
90     }
91
92
93     /**
94      * Tests whether all data is saved, and if not, prompts the user to save.
95      *
96      * @return <code>true</code> if everything can be closed
97      */

98     protected boolean canClose() {
99         SaveCookie saveCookie = propDataObject.getCookie(SaveCookie.class);
100         if (saveCookie == null) {
101             return true;
102         }
103         stopEditing();
104         if (!shouldAskSave()) {
105             return true;
106         }
107         
108         /* Create and display a confirmation dialog - Save/Discard/Cancel: */
109         String JavaDoc title = NbBundle.getMessage(PropertiesOpen.class,
110                                            "CTL_Question"); //NOI18N
111
String JavaDoc question = NbBundle.getMessage(PropertiesOpen.class,
112                                               "MSG_SaveFile", //NOI18N
113
propDataObject.getName());
114         String JavaDoc optionSave = NbBundle.getMessage(PropertiesOpen.class,
115                                                 "CTL_Save"); //NOI18N
116
String JavaDoc optionDiscard = NbBundle.getMessage(PropertiesOpen.class,
117                                                    "CTL_Discard"); //NOI18N
118
NotifyDescriptor descr = new DialogDescriptor(
119                 question,
120                 title, //title
121
true, //modal
122
new Object JavaDoc[] {optionSave,
123                               optionDiscard,
124                               NotifyDescriptor.CANCEL_OPTION},
125                 optionSave, //default option
126
DialogDescriptor.DEFAULT_ALIGN, //alignment of the options
127
null, //help context
128
(ActionListener) null);
129         descr.setMessageType(NotifyDescriptor.QUESTION_MESSAGE);
130         Object JavaDoc answer = DialogDisplayer.getDefault().notify(descr);
131         
132         /* Save the file if the answer was "Save": */
133         if (answer == optionSave) {
134             try {
135                 saveCookie.save();
136                 propDataObject.updateModificationStatus();
137             }
138             catch (IOException JavaDoc e) {
139                 ErrorManager.getDefault().notify(e);
140                 return false;
141             }
142         }
143         propDataObject.updateModificationStatus();
144
145         return (answer == optionSave || answer == optionDiscard);
146     }
147     
148     private void stopEditing() {
149         Enumeration en = allEditors.getComponents();
150         while (en.hasMoreElements()) {
151             Object JavaDoc o = en.nextElement();
152             if (o instanceof PropertiesCloneableTopComponent) {
153                 BundleEditPanel bep = (BundleEditPanel)((PropertiesCloneableTopComponent)o).getComponent(0);
154                 bep.stopEditing();
155             }
156         }
157     }
158     
159     /**
160      * Overrides superclass abstract method.
161      * A method to create a new component.
162      * @return the cloneable top component for this support
163      */

164     protected CloneableTopComponent createCloneableTopComponent() {
165         return new PropertiesCloneableTopComponent(propDataObject);
166     }
167
168     /**
169      * Overrides superclass abstract method.
170      * Message to display when an object is being opened.
171      * @return the message or null if nothing should be displayed
172      */

173     protected String JavaDoc messageOpening() {
174         return NbBundle.getMessage(PropertiesOpen.class, "LBL_ObjectOpen", // NOI18N
175
propDataObject.getName(),
176             propDataObject.getPrimaryFile().toString()
177         );
178     }
179
180     /**
181      * Overrides superclass abstract method.
182      * Message to display when an object has been opened.
183      * @return the message or null if nothing should be displayed
184      */

185     protected String JavaDoc messageOpened() {
186         return NbBundle.getMessage(PropertiesOpen.class, "LBL_ObjectOpened"); // NOI18N
187
}
188
189     /** @return whether has open table view component. */
190     public synchronized boolean hasOpenedTableComponent() {
191         return !allEditors.isEmpty();
192     }
193
194     /** Gets UndoRedo manager for this OpenSupport. */
195     public UndoRedo getUndoRedo () {
196         if(undoRedoManager != null)
197             return undoRedoManager;
198         else
199             return new CompoundUndoRedoManager(propDataObject);
200     }
201
202     /** Helper method. Closes documents. */
203     private synchronized void closeDocuments() {
204         closeEntry((PropertiesFileEntry)propDataObject.getPrimaryEntry());
205         for (Iterator it = propDataObject.secondaryEntries().iterator(); it.hasNext(); ) {
206             closeEntry((PropertiesFileEntry)it.next());
207         }
208     }
209
210     /** Helper method. Closes entry. */
211     private void closeEntry(PropertiesFileEntry entry) {
212         PropertiesEditorSupport editorSupport = entry.getPropertiesEditor();
213         if (editorSupport.hasOpenedEditorComponent())
214             // Has opened editor view for this entry -> don't close document.
215
return;
216         else {
217             // Hasn't opened editor view for this entry -> close document.
218
editorSupport.forceNotifyClosed();
219             
220             // #17221. Don't reparse invalid or virtual file.
221
if(entry.getFile().isValid() && !entry.getFile().isVirtual()) {
222                 entry.getHandler().autoParse();
223             }
224         }
225     }
226
227     /**
228      * Helper method. Should be called only if the object has SaveCookie
229      * @return true if closing this editor whithout saving would result in loss of data
230      * because al least one of the modified files is not open in the code editor
231      */

232     private boolean shouldAskSave() {
233         // for each entry : if there is a SaveCookie and no open editor component, return true.
234
// if passed for all entries, return false
235
PropertiesFileEntry entry = (PropertiesFileEntry)propDataObject.getPrimaryEntry();
236         SaveCookie savec = (SaveCookie)entry.getCookie(SaveCookie.class);
237         
238         if ((savec != null) && !entry.getPropertiesEditor().hasOpenedEditorComponent())
239             return true;
240         for (Iterator it = propDataObject.secondaryEntries().iterator(); it.hasNext(); ) {
241             entry = (PropertiesFileEntry)it.next();
242             savec = (SaveCookie)entry.getCookie(SaveCookie.class);
243             if ((savec != null) && !entry.getPropertiesEditor().hasOpenedEditorComponent())
244                 return true;
245         }
246         return false;
247     }
248
249     
250     /** Nested class. Environment that connects the open support together with <code>DataObject</code>. */
251     private static class Environment implements CloneableOpenSupport.Env, Serializable JavaDoc,
252         PropertyChangeListener, VetoableChangeListener {
253             
254         /** Generated Serialized Version UID */
255         static final long serialVersionUID = -1934890789745432531L;
256         
257         /** Object to serialize and be connected to. */
258         private DataObject dataObject;
259         
260         /** Support for firing of property changes. */
261         private transient PropertyChangeSupport propSupp;
262         
263         /** Support for firing of vetoable changes. */
264         private transient VetoableChangeSupport vetoSupp;
265
266         
267         /**
268          * Constructor. Attaches itself as listener to
269          * the data object so, all property changes of the data object
270          * are also rethrown to own listeners.
271          * @param dataObject data object to be attached to
272          */

273         public Environment(PropertiesDataObject dataObject) {
274             this.dataObject = dataObject;
275             dataObject.addPropertyChangeListener(WeakListeners.propertyChange(this, dataObject));
276             dataObject.addVetoableChangeListener(WeakListeners.vetoableChange(this, dataObject));
277         }
278
279         
280         /** Implements <code>CloneableOpenSupport.Env</code> interface. Adds property listener. */
281         public void addPropertyChangeListener(PropertyChangeListener l) {
282             prop().addPropertyChangeListener(l);
283         }
284
285         /** Implements <code>CloneableOpenSupport.Env</code> interface. Removes property listener. */
286         public void removePropertyChangeListener(PropertyChangeListener l) {
287             prop().removePropertyChangeListener(l);
288         }
289
290         /** Implements <code>CloneableOpenSupport.Env</code> interface. Adds veto listener. */
291         public void addVetoableChangeListener(VetoableChangeListener l) {
292             veto().addVetoableChangeListener(l);
293         }
294
295         /** Implements <code>CloneableOpenSupport.Env</code> interface. Removes veto listener. */
296         public void removeVetoableChangeListener(VetoableChangeListener l) {
297             veto().removeVetoableChangeListener(l);
298         }
299
300         /**
301          * Implements <code>CloneableOpenSupport</code> interface.
302          * Method that allows environment to find its cloneable open support.
303          * @return the support or null if the environemnt is not in valid
304          * state and the CloneableOpenSupport cannot be found for associated
305          * data object
306          */

307         public CloneableOpenSupport findCloneableOpenSupport() {
308             return (CloneableOpenSupport)dataObject.getCookie(OpenCookie.class);
309         }
310         
311         /**
312          * Implements <code>CloneableOpenSupport.Env</code> interface.
313          * Test whether the support is in valid state or not.
314          * It could be invalid after deserialization when the object it
315          * referenced to does not exist anymore.
316          * @return true or false depending on its state
317          */

318         public boolean isValid() {
319             return dataObject.isValid();
320         }
321         
322         /**
323          * Implements <code>CloneableOpenSupport.Env</code> interface.
324          * Test whether the object is modified or not.
325          * @return true if the object is modified
326          */

327         public boolean isModified() {
328             return dataObject.isModified();
329         }
330
331         /**
332          * Implements <code>CloneableOpenSupport.Env</code> interface.
333          * Support for marking the environement modified.
334          * @exception IOException if the environment cannot be marked modified
335          * (for example when the file is readonly), when such exception
336          * is the support should discard all previous changes
337          */

338         public void markModified() throws java.io.IOException JavaDoc {
339             dataObject.setModified(true);
340         }
341         
342         /**
343          * Implements <code>CloneableOpenSupport.Env</code> interface.
344          * Reverse method that can be called to make the environment unmodified.
345          */

346         public void unmarkModified() {
347             dataObject.setModified(false);
348         }
349         
350         /**
351          * Implements <code>PropertyChangeListener</code> interface.
352          * Accepts property changes from <code>DataObject</code> and fires them to own listeners.
353          */

354         public void propertyChange(PropertyChangeEvent evt) {
355             if(DataObject.PROP_MODIFIED.equals(evt.getPropertyName())) {
356                 if(dataObject.isModified()) {
357                     dataObject.addVetoableChangeListener(this);
358                 } else {
359                     dataObject.removeVetoableChangeListener(this);
360                 }
361             } else if(DataObject.PROP_VALID.equals(evt.getPropertyName ())) {
362                 // We will handle the object invalidation here.
363
// Do not check it if old value is not true.
364
if(Boolean.FALSE.equals(evt.getOldValue())) return;
365
366                 // Loosing validity.
367
PropertiesOpen support = (PropertiesOpen)findCloneableOpenSupport();
368                 if(support != null) {
369                     
370                     // Mark the object as not being modified, so nobody
371
// will ask for save.
372
unmarkModified();
373                     
374                     support.close(false);
375                 }
376             } else {
377                 firePropertyChange (
378                     evt.getPropertyName(),
379                     evt.getOldValue(),
380                     evt.getNewValue()
381                 );
382             }
383         }
384         
385         /**
386          * Implements <code>VetoAbleChangeListener</code> interface.
387          * Accepts vetoable changes and fires them to own listeners.
388          */

389         public void vetoableChange(PropertyChangeEvent evt) throws PropertyVetoException {
390             fireVetoableChange (
391                 evt.getPropertyName(),
392                 evt.getOldValue(),
393                 evt.getNewValue()
394             );
395         }
396         
397         /** Fires property change.
398         * @param name the name of property that changed
399         * @param oldValue old value
400         * @param newValue new value
401         */

402         private void firePropertyChange (String JavaDoc name, Object JavaDoc oldValue, Object JavaDoc newValue) {
403             prop().firePropertyChange(name, oldValue, newValue);
404         }
405         
406         /** Fires vetoable change.
407         * @param name the name of property that changed
408         * @param oldValue old value
409         * @param newValue new value
410         */

411         private void fireVetoableChange (String JavaDoc name, Object JavaDoc oldValue, Object JavaDoc newValue) throws PropertyVetoException {
412             veto().fireVetoableChange(name, oldValue, newValue);
413         }
414         
415         /** Lazy gets property change support. */
416         private PropertyChangeSupport prop() {
417             if(propSupp == null) {
418                 synchronized(this) {
419                     if(propSupp == null) {
420                         propSupp = new PropertyChangeSupport(this);
421                     }
422                 }
423             }
424             return propSupp;
425         }
426         
427         /** Lazy gets vetoable change support. */
428         private VetoableChangeSupport veto() {
429             if(vetoSupp == null) {
430                 synchronized(this) {
431                     if(vetoSupp == null) {
432                         vetoSupp = new VetoableChangeSupport(this);
433                     }
434                 }
435             }
436             return vetoSupp;
437         }
438     } // End of inner class Environment.
439

440     
441     /** Inner class. Listens to modifications and updates save cookie. */
442     private final class ModifiedListener implements SaveCookie, PropertyChangeListener {
443
444         /** Gives notification that the DataObject was changed.
445         * @param ev PropertyChangeEvent
446         */

447         public void propertyChange(PropertyChangeEvent evt) {
448             // Data object changed, reset the UndoRedo manager.
449
if(evt.getSource().equals(propDataObject))
450                 ((CompoundUndoRedoManager)PropertiesOpen.this.getUndoRedo()).reset(propDataObject);
451
452             if ((evt.getSource() == propDataObject) && (DataObject.PROP_MODIFIED.equals(evt.getPropertyName()))) {
453                 if (((Boolean JavaDoc)evt.getNewValue()).booleanValue()) {
454                     addSaveCookie();
455                 } else {
456                     removeSaveCookie();
457                 }
458             }
459         }
460
461         /** Implements <code>SaveCookie</code> interface. */
462         public void save() throws IOException JavaDoc {
463             stopEditing();
464             // do saving job
465
saveDocument();
466         }
467
468         /** Save the document in this thread.
469         * Create "orig" document for the case that the save would fail.
470         * @exception IOException on I/O error
471         */

472         public void saveDocument() throws IOException JavaDoc {
473             PropertiesFileEntry pfe = (PropertiesFileEntry)propDataObject.getPrimaryEntry();
474             SaveCookie save = (SaveCookie)pfe.getCookie(SaveCookie.class);
475             if (save != null)
476                 save.save();
477             for (Iterator it = propDataObject.secondaryEntries().iterator(); it.hasNext();) {
478                 save = (SaveCookie)((PropertiesFileEntry)it.next()).getCookie(SaveCookie.class);
479                 if(save != null)
480                     save.save();
481             }
482         }
483
484         /** Adds save cookie to the dataobject. */
485         private void addSaveCookie() {
486             if(propDataObject.getCookie(SaveCookie.class) == null) {
487                 propDataObject.getCookieSet0().add(this);
488             }
489         }
490         
491         /** Removes save cookie from the dataobject. */
492         private void removeSaveCookie() {
493             if(propDataObject.getCookie(SaveCookie.class) == this) {
494                 propDataObject.getCookieSet0().remove(this);
495             }
496         }
497     } // End of inner class ModifiedListener.
498

499     
500     /** Inner class for opening at a given key. */
501     public class PropertiesOpenAt implements OpenCookie {
502
503         /** Entry the key belongs to. */
504         private PropertiesFileEntry entry;
505         
506         /** Key where to open at. */
507         private String JavaDoc key;
508
509         
510         /** Construcor. */
511         PropertiesOpenAt(PropertiesFileEntry entry, String JavaDoc key) {
512             this.entry = entry;
513             this.key = key;
514         }
515
516         
517         /** Setter for key property. */
518         public void setKey(String JavaDoc key) {
519             this.key = key;
520         }
521
522         /** Implememnts <code>OpenCookie</code>. Opens document. */
523         public void open() {
524             // Instead of PropertiesOpen.super.open() so we get reference to TopComponent.
525
// Note: It is strange for me that calling PropetiesOpen.this.openCloneableTopComponent throw s exception at run-time.
526
final PropertiesCloneableTopComponent editor = (PropertiesCloneableTopComponent)PropertiesOpen.super.openCloneableTopComponent();
527             editor.requestActive();
528             
529             BundleStructure bs = propDataObject.getBundleStructure();
530             // Find indexes.
531
int entryIndex = bs.getEntryIndexByFileName(entry.getFile().getName());
532             int rowIndex = bs.getKeyIndexByName(key);
533             
534             if ((entryIndex != -1) && (rowIndex != -1)) {
535                 final int row = rowIndex;
536                 final int column = entryIndex + 1;
537
538                 SwingUtilities.invokeLater(new Runnable JavaDoc() {
539                     public void run() {
540                         JTable table = ((BundleEditPanel)editor.getComponent(0)).getTable();
541                         // Autoscroll to cell if possible and necessary.
542
if (table.getAutoscrolls()) {
543                             Rectangle cellRect = table.getCellRect(row, column, false);
544                             if (cellRect != null) {
545                                 table.scrollRectToVisible(cellRect);
546                             }
547                         }
548
549                         // Update selection & edit.
550
table.getColumnModel().getSelectionModel().setSelectionInterval(row, column);
551                         table.getSelectionModel().setSelectionInterval(row, column);
552
553                         table.editCellAt(row, column);
554                     }
555                 });
556             }
557         }
558     } // End of inner class PropertiesOpenAt.
559

560     
561     /** Cloneable top component which represents table view of resource bundles. */
562     public static class PropertiesCloneableTopComponent extends CloneableTopComponent {
563
564         /** Reference to underlying <code>PropertiesDataObject</code>. */
565         private PropertiesDataObject propDataObject;
566         
567         /** Listener for changes on <code>propDataObject</code> name and cookie properties.
568          * Changes display name of components accordingly. */

569         private transient PropertyChangeListener dataObjectListener;
570         
571         /** Generated serial version UID. */
572         static final long serialVersionUID =2836248291419024296L;
573         
574         
575         /** Default constructor for deserialization. */
576         public PropertiesCloneableTopComponent() {
577         }
578         
579         /** Constructor.
580         * @param propDataObject data object we belong to */

581         public PropertiesCloneableTopComponent (PropertiesDataObject propDataObject) {
582             this.propDataObject = propDataObject;
583
584             initialize();
585         }
586
587         /**
588          */

589         public void open() {
590             if (discard()) {
591                 return;
592             }
593             super.open();
594         }
595
596         public void requestActive() {
597             super.requestActive();
598             getComponent(0).requestFocusInWindow();
599         }
600         
601         public boolean canClose () {
602             ((BundleEditPanel)getComponent(0)).stopEditing();
603             return super.canClose();
604         }
605         
606         /** Initializes this instance. Used by construction and deserialization. */
607         private void initialize() {
608             initComponents();
609             setupActions();
610             setActivatedNodes(new Node[] {propDataObject.getNodeDelegate()});
611
612             dataObjectListener = new NameUpdater();
613             propDataObject.addPropertyChangeListener(
614                     WeakListeners.propertyChange(dataObjectListener,
615                                                  propDataObject));
616             
617             updateName();
618         }
619         
620         /* Based on class DataNode.PropL. */
621         final class NameUpdater implements PropertyChangeListener,
622                                            FileStatusListener,
623                                            Runnable JavaDoc {
624             
625             /** */
626             private static final int NO_ACTION = 0;
627             /** */
628             private static final int ACTION_UPDATE_NAME = 1;
629             /** */
630             private static final int ACTION_UPDATE_DISPLAY_NAME = 2;
631             
632             /** weak version of this listener */
633             private FileStatusListener weakL;
634             /** previous filesystem we were attached to */
635             private FileSystem previous;
636             
637             /** */
638             private final int action;
639             
640             /**
641              */

642             NameUpdater() {
643                 this(NO_ACTION);
644                 updateStatusListener();
645             }
646             
647             /**
648              */

649             NameUpdater(int action) {
650                 this.action = action;
651             }
652             
653             /** Updates listening on a status of filesystem. */
654             private void updateStatusListener() {
655                 if (previous != null) {
656                     previous.removeFileStatusListener(weakL);
657                 }
658                 try {
659                     previous = propDataObject.getPrimaryFile().getFileSystem();
660                     if (weakL == null) {
661                         weakL = org.openide.filesystems.FileUtil
662                                 .weakFileStatusListener(this, previous);
663                     }
664                     previous.addFileStatusListener(weakL);
665                 } catch (FileStateInvalidException ex) {
666                     previous = null;
667                 }
668             }
669             
670             /**
671              * Notifies listener about change in annotataion of a few files.
672              */

673             public void annotationChanged(FileStatusEvent ev) {
674                 if (!ev.isNameChange()) {
675                     return;
676                 }
677                 
678                 boolean thisChanged = false;
679                 for (FileObject fo : propDataObject.files()) {
680                     if (ev.hasChanged(fo)) {
681                         thisChanged = true;
682                         break;
683                     }
684                 }
685                 if (thisChanged) {
686                     Mutex.EVENT.writeAccess(
687                             new NameUpdater(ACTION_UPDATE_DISPLAY_NAME));
688                 }
689             }
690             
691             /**
692              */

693             public void propertyChange(PropertyChangeEvent e) {
694                 if (!propDataObject.isValid()) {
695                     return;
696                 }
697                 
698                 final String JavaDoc property = e.getPropertyName();
699                 if (property == null) {
700                     return;
701                 }
702                 if (property.equals(DataObject.PROP_NAME)) {
703                     Mutex.EVENT.writeAccess(
704                             new NameUpdater(ACTION_UPDATE_NAME));
705                 } else if (property.equals(DataObject.PROP_PRIMARY_FILE)) {
706                     updateStatusListener();
707                     Mutex.EVENT.writeAccess(
708                             new NameUpdater(ACTION_UPDATE_NAME));
709                 } else if (property.equals(DataObject.PROP_COOKIE)
710                            || property.equals(DataObject.PROP_FILES)) {
711                     Mutex.EVENT.writeAccess(
712                             new NameUpdater(ACTION_UPDATE_DISPLAY_NAME));
713                 }
714             }
715             
716             /**
717              */

718             public void run() {
719                 assert EventQueue.isDispatchThread();
720                 
721                 if (action == ACTION_UPDATE_NAME) {
722                     updateName();
723                 } else if (action == ACTION_UPDATE_DISPLAY_NAME) {
724                     updateDisplayName();
725                 } else {
726                     assert false;
727                 }
728             }
729             
730         }
731         
732         /**
733          * Sets up action Find that it is activated/deactivated appropriately
734          * and so that it does what it should do.
735          */

736         private void setupActions() {
737             JTable bundleTable = ((BundleEditPanel) getComponent(0)).getTable();
738             FindAction findAction = SystemAction.get(FindAction.class);
739             Action JavaDoc action = FindPerformer.getFindPerformer(bundleTable);
740             getActionMap().put(findAction.getActionMapKey(), action);
741         }
742         
743         /**
744          */

745         private void updateName() {
746             assert EventQueue.isDispatchThread();
747             
748             final String JavaDoc name = propDataObject.getName();
749             final String JavaDoc displayName = displayName();
750             final String JavaDoc htmlDisplayName = htmlDisplayName();
751             final String JavaDoc toolTip = messageToolTip();
752             
753             Enumeration<CloneableTopComponent> en = getReference().getComponents();
754             while (en.hasMoreElements()) {
755                 CloneableTopComponent tc = en.nextElement();
756                 tc.setName(name);
757                 tc.setDisplayName(displayName);
758                 tc.setHtmlDisplayName(htmlDisplayName);
759                 tc.setToolTipText(toolTip);
760             }
761         }
762         
763         /**
764          */

765         private void updateDisplayName() {
766             assert EventQueue.isDispatchThread();
767             
768             final String JavaDoc displayName = displayName();
769             final String JavaDoc htmlDisplayName = htmlDisplayName();
770             
771             Enumeration<CloneableTopComponent> en = getReference().getComponents();
772             while (en.hasMoreElements()) {
773                 CloneableTopComponent tc = en.nextElement();
774                 tc.setDisplayName(displayName);
775                 tc.setHtmlDisplayName(htmlDisplayName);
776             }
777         }
778         
779         /**
780          */

781         private String JavaDoc addModifiedInfo(String JavaDoc name) {
782             boolean modified
783                     = propDataObject.getCookie(SaveCookie.class) != null;
784             int version = modified ? 1 : 3;
785             return NbBundle.getMessage(PropertiesCloneableTopComponent.class,
786                                        "LBL_EditorName", //NOI18N
787
new Integer JavaDoc(version),
788                                        name);
789         }
790
791         /**
792          * Builds a display name for this component.
793          *
794          * @return the created display name
795          * @see #htmlDisplayName
796          */

797         private String JavaDoc displayName() {
798             String JavaDoc nameBase = propDataObject.getNodeDelegate().getDisplayName();
799             return addModifiedInfo(nameBase);
800         }
801         
802         /**
803          * Builds a HTML display name for this component.
804          *
805          * @return the created display name
806          * @see #displayName()
807          */

808         private String JavaDoc htmlDisplayName() {
809             final Node node = propDataObject.getNodeDelegate();
810             String JavaDoc displayName = node.getHtmlDisplayName();
811             if (displayName != null) {
812                 if (!displayName.startsWith("<html>")) { //NOI18N
813
displayName = "<html>" + displayName; //NOI18N
814
}
815             } else {
816                 displayName = node.getDisplayName();
817             }
818             return addModifiedInfo(displayName);
819         }
820         
821         /** Gets string for tooltip. */
822         private String JavaDoc messageToolTip() {
823             FileObject fo = propDataObject.getPrimaryFile();
824             return FileUtil.getFileDisplayName(fo);
825         }
826         
827         /**
828          * Overrides superclass method. When closing last view, also close the document.
829          * @return <code>true</code> if close succeeded
830          */

831         protected boolean closeLast () {
832             if (!propDataObject.getOpenSupport().canClose ()) {
833                 // if we cannot close the last window
834
return false;
835             }
836             propDataObject.getOpenSupport().closeDocuments();
837
838             return true;
839         }
840
841         /**
842          * Overrides superclass method.
843          * Is called from the superclass <code>clone<code> method to create new component from this one.
844          * This implementation only clones the object by calling super.clone method.
845          * @return the copy of this object
846          */

847         protected CloneableTopComponent createClonedObject () {
848             return new PropertiesCloneableTopComponent(propDataObject);
849         }
850
851         /** Overrides superclass method. Gets <code>Icon</code>. */
852         public Image getIcon () {
853             return Utilities.loadImage("org/netbeans/modules/properties/propertiesEditorMode.gif"); // NOI18N
854
}
855
856         /** Overrides superclass method. Gets help context. */
857         public HelpCtx getHelpCtx () {
858             return new HelpCtx(Util.HELP_ID_MODIFYING);
859         }
860         
861         protected String JavaDoc preferredID() {
862             return getName();
863         }
864         
865         public int getPersistenceType() {
866             return PERSISTENCE_ONLY_OPENED;
867         }
868         
869         /**
870          * Overrides superclass method.
871          * Gets compound UndoRedo manager from all UndozRedo managers from all editor supports.
872          */

873         public UndoRedo getUndoRedo () {
874             return propDataObject.getOpenSupport().getUndoRedo();
875         }
876
877         /** Inits the subcomponents. Sets layout for this top component and adds <code>BundleEditPanel</code> to it.
878          * @see BundleEditPanel */

879         private void initComponents() {
880             GridBagLayout gridbag = new GridBagLayout();
881             setLayout(gridbag);
882
883             GridBagConstraints c = new GridBagConstraints();
884             c.fill = GridBagConstraints.BOTH;
885             c.weightx = 1.0;
886             c.weighty = 1.0;
887             c.gridwidth = GridBagConstraints.REMAINDER;
888             JPanel panel = new BundleEditPanel(propDataObject, new PropertiesTableModel(propDataObject.getBundleStructure()));
889             gridbag.setConstraints(panel, c);
890             add(panel);
891         }
892         
893         /** This component should be discarded if the associated environment
894          * is not valid.
895          */

896         private boolean discard () {
897             return propDataObject == null;
898         }
899         
900
901         /**
902          * Overrides superclass method. Serialize this top component.
903          * Subclasses wishing to store state must call the super method, then write to the stream.
904          * @param out the stream to serialize to
905          */

906         public void writeExternal (ObjectOutput JavaDoc out) throws IOException JavaDoc {
907             super.writeExternal(out);
908             out.writeObject(propDataObject);
909         }
910
911         /**
912          * Overrides superclass method. Deserialize this top component.
913          * Subclasses wishing to store state must call the super method, then read from the stream.
914          * @param in the stream to deserialize from
915          */

916         public void readExternal (ObjectInput JavaDoc in) throws IOException JavaDoc, ClassNotFoundException JavaDoc {
917             super.readExternal(in);
918
919             propDataObject = (PropertiesDataObject)in.readObject();
920             
921             initialize();
922         }
923     } // End of nested class PropertiesCloneableTopComponent.
924

925
926     /**
927      * <code>UndoRedo</code> manager for <code>PropertiesOpen</code> support. It contains weak references
928      * to all UndoRedo managers from all PropertiesEditor supports (for each entry of dataobject one manager).
929      * It uses it's "timeStamp" methods to find out which one of these managers comes to play.
930      */

931     private static class CompoundUndoRedoManager implements UndoRedo {
932         
933         /** Set of weak references to all "underlying" editor support undoredo managers. */
934         private WeakSet<Manager> managers = new WeakSet<Manager>(5);
935         
936         // Constructor
937

938         /** Collects all UndoRedo managers from all editor support of all entries. */
939         public CompoundUndoRedoManager(PropertiesDataObject obj) {
940             init(obj);
941         }
942
943         /** Initialize set of managers. */
944         private void init(PropertiesDataObject obj) {
945             managers.add( ((PropertiesFileEntry)obj.getPrimaryEntry()).getPropertiesEditor().getUndoRedoManager());
946             for (Iterator it = obj.secondaryEntries().iterator(); it.hasNext(); ) {
947                 managers.add( ((PropertiesFileEntry)it.next()).getPropertiesEditor().getUndoRedoManager() );
948             }
949         }
950
951         /** Resets the managers. Used when data object has changed. */
952         public synchronized void reset(PropertiesDataObject obj) {
953             managers.clear();
954             init(obj);
955         }
956
957         /** Gets manager which undo edit comes to play.*/
958         private UndoRedo getNextUndo() {
959             UndoRedo chosenManager = null;
960             long time = 0L; // time to compare with
961
long timeManager; // time of next undo of actual manager
962

963             for (Iterator<Manager> it = managers.iterator(); it.hasNext(); ) {
964                 PropertiesEditorSupport.UndoRedoStampFlagManager manager = (PropertiesEditorSupport.UndoRedoStampFlagManager)it.next();
965                 timeManager = manager.getTimeStampOfEditToBeUndone();
966                 if(timeManager > time) {
967                     time = timeManager;
968                     chosenManager = manager;
969                 }
970             }
971             return chosenManager;
972         }
973         
974         /** Gets manager which redo edit comes to play.*/
975         private UndoRedo getNextRedo() {
976             UndoRedo chosenManager = null;
977             long time = 0L; // time to compare with
978
long timeManager; // time of next redo of actual manager
979

980             for (Iterator<Manager> it = managers.iterator(); it.hasNext(); ) {
981                 PropertiesEditorSupport.UndoRedoStampFlagManager manager = (PropertiesEditorSupport.UndoRedoStampFlagManager)it.next();
982                 timeManager = manager.getTimeStampOfEditToBeRedone();
983                 if(timeManager > time) {
984                     time = timeManager;
985                     chosenManager = manager;
986                 }
987             }
988             return chosenManager;
989         }
990         
991         /** Implements <code>UndoRedo</code>. Test whether at least one of managers can Undo.
992          * @return <code>true</code> if undo is allowed
993          */

994         public synchronized boolean canUndo () {
995             for (Manager manager : managers) {
996                 if (manager.canUndo()) {
997                     return true;
998                 }
999             }
1000            return false;
1001        }
1002
1003        /** Implements <code>UndoRedo</code>. Test whether at least one of managers can Redo.
1004         * @return <code>true</code> if redo is allowed
1005         */

1006        public synchronized boolean canRedo () {
1007            for (Manager manager : managers) {
1008                if (manager.canRedo()) {
1009                    return true;
1010                }
1011            }
1012            return false;
1013        }
1014
1015        /** Implements <code>UndoRedo</code>. Undo an edit. It finds a manager which next undo edit has the highest
1016         * time stamp and makes undo on it.
1017         * @exception CannotUndoException if it fails
1018         */

1019        public synchronized void undo () throws CannotUndoException JavaDoc {
1020            PropertiesEditorSupport.UndoRedoStampFlagManager chosenManager = (PropertiesEditorSupport.UndoRedoStampFlagManager)getNextUndo();
1021
1022            if(chosenManager == null)
1023                throw new CannotUndoException JavaDoc();
1024            else {
1025                Object JavaDoc atomicFlag = chosenManager.getAtomicFlagOfEditToBeUndone();
1026                if(atomicFlag == null) // not linked with other edits as one atomic action
1027
chosenManager.undo();
1028                else { // atomic undo compound from more edits in underlying managers
1029
boolean undone;
1030                    do { // the atomic action can consists from more undo edits from same manager
1031
undone = false;
1032                        for (Iterator<Manager> it = managers.iterator(); it.hasNext(); ) {
1033                            PropertiesEditorSupport.UndoRedoStampFlagManager manager = (PropertiesEditorSupport.UndoRedoStampFlagManager)it.next();
1034                            if(atomicFlag.equals(manager.getAtomicFlagOfEditToBeUndone())) {
1035                                manager.undo();
1036                                undone = true;
1037                            }
1038                        }
1039                    } while(undone);
1040                }
1041            }
1042        }
1043
1044        /** Implements <code>UndoRedo</code>. Redo a previously undone edit. It finds a manager which next undo edit has the highest
1045         * time stamp and makes undo on it.
1046         * @exception CannotRedoException if it fails
1047         */

1048        public synchronized void redo () throws CannotRedoException JavaDoc {
1049            PropertiesEditorSupport.UndoRedoStampFlagManager chosenManager = (PropertiesEditorSupport.UndoRedoStampFlagManager)getNextRedo();
1050
1051            if(chosenManager == null)
1052                throw new CannotRedoException JavaDoc();
1053            else {
1054                Object JavaDoc atomicFlag = chosenManager.getAtomicFlagOfEditToBeRedone();
1055                if(atomicFlag == null) // not linked with other edits as one atomic action
1056
chosenManager.redo();
1057                else { // atomic redo compound from more edits in underlying managers
1058
boolean redone;
1059                    do { // the atomic action can consists from more redo edits from same manager
1060
redone = false;
1061                        for (Iterator<Manager> it = managers.iterator(); it.hasNext(); ) {
1062                            PropertiesEditorSupport.UndoRedoStampFlagManager manager = (PropertiesEditorSupport.UndoRedoStampFlagManager)it.next();
1063                            if(atomicFlag.equals(manager.getAtomicFlagOfEditToBeRedone())) {
1064                                manager.redo();
1065                                redone = true;
1066                            }
1067                        }
1068                    } while(redone);
1069                }
1070            }
1071        }
1072
1073        /** Implements <code>UndoRedo</code>. Empty implementation. Does nothing.
1074         * @param l the listener to add
1075         */

1076        public void addChangeListener (ChangeListener JavaDoc l) {
1077            // PENDING up to now listen on separate managers
1078
}
1079
1080        /** Implements <code>UndoRedo</code>. Empty implementation. Does nothing.
1081         * @param l the listener to remove
1082         * @see #addChangeListener
1083         */

1084        public void removeChangeListener (ChangeListener JavaDoc l) {
1085            // PENDING
1086
}
1087
1088        /** Implements <code>UndoRedo</code>. Get a human-presentable name describing the
1089         * undo operation.
1090         * @return the name
1091         */

1092        public synchronized String JavaDoc getUndoPresentationName () {
1093            UndoRedo chosenManager = getNextUndo();
1094
1095            if(chosenManager == null)
1096                return "Undo"; // NOI18N // AbstractUndoableEdit.UndoName is not accessible
1097
else
1098                return chosenManager.getUndoPresentationName();
1099        }
1100
1101        /** Implements <code>UndoRedo</code>. Get a human-presentable name describing the
1102         * redo operation.
1103         * @return the name
1104         */

1105        public synchronized String JavaDoc getRedoPresentationName () {
1106            UndoRedo chosenManager = getNextRedo();
1107            if(chosenManager == null)
1108                return "Redo"; // NOI18N // AbstractUndoableEdit.RedoName is not accessible
1109
else
1110                return chosenManager.getRedoPresentationName();
1111        }
1112        
1113    } // End of nested class CompoundUndoRedoManager.
1114

1115}
1116
Popular Tags