KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > openide > explorer > ExplorerActions


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.explorer;
20
21 import org.openide.DialogDisplayer;
22 import org.openide.NotifyDescriptor;
23 import org.openide.actions.CopyAction;
24 import org.openide.actions.CutAction;
25 import org.openide.actions.DeleteAction;
26 import org.openide.actions.PasteAction;
27 import org.openide.filesystems.FileSystem;
28 import org.openide.filesystems.Repository;
29 import org.openide.nodes.Node;
30 import org.openide.util.Mutex;
31 import org.openide.util.NbBundle;
32 import org.openide.util.actions.ActionPerformer;
33 import org.openide.util.datatransfer.*;
34 import java.awt.datatransfer.*;
35 import java.awt.event.*;
36
37 import java.beans.PropertyChangeEvent JavaDoc;
38 import java.beans.PropertyChangeListener JavaDoc;
39
40 import java.io.IOException JavaDoc;
41
42 import java.util.HashMap JavaDoc;
43 import java.util.logging.Level JavaDoc;
44 import java.util.logging.Logger JavaDoc;
45
46 import javax.swing.*;
47 import org.openide.util.Exceptions;
48
49
50 /**
51  * This class contains the default implementation of reactions to the standard
52  * explorer actions. It can be attached to any {@link ExplorerManager}. Then
53  * this class will listen to changes of selected nodes or the explored context
54  * of that manager, and update the state of cut/copy/paste/delete actions. <P>
55  * An instance of this class can only be attached to one manager at a time. Use
56  * {@link #attach} and {@link #detach} to make the connection.
57  *
58  * @deprecated Use {@link ExplorerUtils#actionCopy}, etc, see {@link ExplorerUtils} javadoc
59  * for details
60  * @author Jan Jancura, Petr Hamernik, Ian Formanek, Jaroslav Tulach
61  */

62 public class ExplorerActions {
63     /** actions to work with */
64     private static CopyAction copy = null;
65     private static CutAction cut = null;
66     private static DeleteAction delete = null;
67     private static PasteAction paste = null;
68
69     /** copy action performer */
70     private final CopyCutActionPerformer copyActionPerformer = new CopyCutActionPerformer(true);
71
72     /** cut action performer */
73     private final CopyCutActionPerformer cutActionPerformer = new CopyCutActionPerformer(false);
74
75     /** delete action performer */
76     private final DeleteActionPerformer deleteActionPerformer = new DeleteActionPerformer();
77
78     /** own paste action */
79     private final OwnPaste pasteActionPerformer = new OwnPaste();
80     private ActionStateUpdater actionStateUpdater;
81
82     /** the manager we are listening on */
83     private ExplorerManager manager;
84
85     /** must the delete be confirmed */
86     private Boolean JavaDoc confirmDelete;
87
88     /** attach as performers or just update the actions */
89     private boolean attachPerformers;
90
91     /** Creates new instance.
92      */

93     public ExplorerActions() {
94         this(true);
95     }
96
97     /** Creates new instance with a decision whether the action should update
98      * performers (the old behaviour) or only set the state of cut,copy,delete,
99      * and paste actions.
100      */

101     ExplorerActions(boolean attachPerformers) {
102         this.attachPerformers = attachPerformers;
103     }
104
105     /** Getter for the copy action.
106      */

107     final Action copyAction() {
108         return copyActionPerformer;
109     }
110
111     /** The cut action */
112     final Action cutAction() {
113         return cutActionPerformer;
114     }
115
116     /** The delete action
117      */

118     final Action deleteAction() {
119         return deleteActionPerformer;
120     }
121
122     /** Own paste action
123      */

124     final Action pasteAction() {
125         return pasteActionPerformer;
126     }
127
128     /** Attach to new manager.
129      * @param m the manager to listen on
130      */

131     public synchronized void attach(ExplorerManager m) {
132         if (manager != null) {
133             // first of all detach
134
detach();
135         }
136
137         manager = m;
138
139         // update ExplorerPanel possibly associated with the manager
140
ExplorerPanel.associateActions(this, m);
141
142         // Sets action state updater and registers listening on manager and
143
// exclipboard.
144
actionStateUpdater = new ActionStateUpdater();
145         manager.addPropertyChangeListener(org.openide.util.WeakListeners.propertyChange(actionStateUpdater, manager));
146
147         Clipboard c = getClipboard();
148
149         if (c instanceof ExClipboard) {
150             ExClipboard clip = (ExClipboard) c;
151             clip.addClipboardListener(
152                 (ClipboardListener) org.openide.util.WeakListeners.create(
153                     ClipboardListener.class, actionStateUpdater, clip
154                 )
155             );
156         }
157
158         updateActions();
159     }
160
161     /** Detach from manager currently being listened on. */
162     public synchronized void detach() {
163         if (manager == null) {
164             return;
165         }
166
167         // Unregisters (weak) listening on manager and exclipboard (see attach).
168
actionStateUpdater = null;
169
170         stopActions();
171
172         manager = null;
173     }
174
175     /** Access method for use from ExplorerPanel, and also
176      * via reflection (!) from RegistryImpl in core.
177      * @deprecated Kill me later; see #18137 for explanation.
178      */

179     ExplorerManager getAttachedManager() {
180         return manager;
181     }
182
183     /** Set whether to confirm deletions.
184     * @param yes <code>true</code> to confirm deletions
185     */

186     public final void setConfirmDelete(boolean yes) {
187         confirmDelete = Boolean.valueOf(yes);
188     }
189
190     /** Should deletions be confirmed?
191     * @return <code>true</code> if deletions must be confirmed
192     */

193     public final boolean isConfirmDelete() {
194         return (confirmDelete == null) ? ExplorerPanel.isConfirmDelete() : confirmDelete.booleanValue();
195     }
196
197     /** Stops listening on all actions */
198     private void stopActions() {
199         if (copyActionPerformer != null) {
200             if (attachPerformers) {
201                 if (copy.getActionPerformer() instanceof CopyCutActionPerformer) {
202                     copy.setActionPerformer(null);
203                 }
204
205                 if (cut.getActionPerformer() instanceof CopyCutActionPerformer) {
206                     cut.setActionPerformer(null);
207                 }
208
209                 paste.setPasteTypes(null);
210
211                 if (delete.getActionPerformer() instanceof DeleteActionPerformer) {
212                     delete.setActionPerformer(null);
213                 }
214             } else {
215                 copyActionPerformer.setEnabled(false);
216                 cutActionPerformer.setEnabled(false);
217                 deleteActionPerformer.setEnabled(false);
218                 pasteActionPerformer.setEnabled(false);
219             }
220         }
221     }
222
223     /** Updates the state of all actions.
224      * @param path list of selected nodes
225      */

226     private void updateActions() {
227         if (manager == null) {
228             return;
229         }
230
231         Node[] path = manager.getSelectedNodes();
232
233         if (copy == null) {
234             copy = (CopyAction) CopyAction.findObject(CopyAction.class, true);
235             cut = (CutAction) CutAction.findObject(CutAction.class, true);
236             paste = (PasteAction) PasteAction.findObject(PasteAction.class, true);
237             delete = (DeleteAction) DeleteAction.findObject(DeleteAction.class, true);
238         }
239
240         int i;
241         int k = (path != null) ? path.length : 0;
242
243         if (k > 0) {
244             boolean incest = false;
245
246             if (k > 1) {
247                 // Do a special check for parenthood. Affects delete (for a long time),
248
// copy (#13418), cut (#13426). If one node is a parent of another,
249
// assume that the situation is sketchy and prevent it.
250
// For k==1 it is impossible so do not waste time on it.
251
HashMap JavaDoc allNodes = new HashMap JavaDoc(101);
252
253                 for (i = 0; i < k; i++) {
254                     if (!checkParents(path[i], allNodes)) {
255                         incest = true;
256
257                         break;
258                     }
259                 }
260             }
261
262             for (i = 0; i < k; i++) {
263                 if (incest || !path[i].canCopy()) {
264                     if (attachPerformers) {
265                         copy.setActionPerformer(null);
266                     } else {
267                         copyActionPerformer.setEnabled(false);
268                     }
269
270                     break;
271                 }
272             }
273
274             if (i == k) {
275                 if (attachPerformers) {
276                     copy.setActionPerformer(copyActionPerformer);
277                 } else {
278                     copyActionPerformer.setEnabled(true);
279                 }
280             }
281
282             for (i = 0; i < k; i++) {
283                 if (incest || !path[i].canCut()) {
284                     if (attachPerformers) {
285                         cut.setActionPerformer(null);
286                     } else {
287                         cutActionPerformer.setEnabled(false);
288                     }
289
290                     break;
291                 }
292             }
293
294             if (i == k) {
295                 if (attachPerformers) {
296                     cut.setActionPerformer(cutActionPerformer);
297                 } else {
298                     cutActionPerformer.setEnabled(true);
299                 }
300             }
301
302             for (i = 0; i < k; i++) {
303                 if (incest || !path[i].canDestroy()) {
304                     if (attachPerformers) {
305                         delete.setActionPerformer(null);
306                     } else {
307                         deleteActionPerformer.setEnabled(false);
308                     }
309
310                     break;
311                 }
312             }
313
314             if (i == k) {
315                 if (attachPerformers) {
316                     delete.setActionPerformer(deleteActionPerformer);
317                 } else {
318                     deleteActionPerformer.setEnabled(true);
319                 }
320             }
321         } else { // k==0, i.e. no nodes selected
322

323             if (attachPerformers) {
324                 copy.setActionPerformer(null);
325                 cut.setActionPerformer(null);
326                 delete.setActionPerformer(null);
327             } else {
328                 copyActionPerformer.setEnabled(false);
329                 cutActionPerformer.setEnabled(false);
330                 deleteActionPerformer.setEnabled(false);
331             }
332         }
333
334         updatePasteAction(path);
335     }
336
337     /** Adds all parent nodes into the set.
338      * @param set set of all nodes
339      * @param node the node to check
340      * @return false if one of the nodes is parent of another
341      */

342     private boolean checkParents(Node node, HashMap JavaDoc set) {
343         if (set.get(node) != null) {
344             return false;
345         }
346
347         // this signals that this node is the original one
348
set.put(node, this);
349
350         for (;;) {
351             node = node.getParentNode();
352
353             if (node == null) {
354                 return true;
355             }
356
357             if (set.put(node, node) == this) {
358                 // our parent is a node that is also in the set
359
return false;
360             }
361         }
362     }
363
364     /** Updates paste action.
365     * @param path selected nodes
366     */

367     private void updatePasteAction(Node[] path) {
368         ExplorerManager man = manager;
369
370         if (man == null) {
371             if (attachPerformers) {
372                 paste.setPasteTypes(null);
373             } else {
374                 pasteActionPerformer.setPasteTypes(null);
375             }
376
377             return;
378         }
379
380         if ((path != null) && (path.length > 1)) {
381             if (attachPerformers) {
382                 paste.setPasteTypes(null);
383             } else {
384                 pasteActionPerformer.setPasteTypes(null);
385             }
386
387             return;
388         } else {
389             Node node = man.getExploredContext();
390             Node[] selectedNodes = man.getSelectedNodes();
391
392             if ((selectedNodes != null) && (selectedNodes.length == 1)) {
393                 node = selectedNodes[0];
394             }
395
396             if (node != null) {
397                 Transferable trans = getClipboard().getContents(this);
398                 updatePasteTypes(trans, node);
399             }
400         }
401     }
402
403     /** Actually updates paste types. */
404     private void updatePasteTypes(Transferable trans, Node pan) {
405         if (trans != null) {
406             // First, just ask the node if it likes this transferable, whatever it may be.
407
// If it does, then fine.
408
PasteType[] pasteTypes = (pan == null) ? new PasteType[] { } : pan.getPasteTypes(trans);
409
410             if (pasteTypes.length != 0) {
411                 if (attachPerformers) {
412                     paste.setPasteTypes(pasteTypes);
413                 } else {
414                     pasteActionPerformer.setPasteTypes(pasteTypes);
415                 }
416
417                 return;
418             }
419
420             boolean flavorSupported = false;
421
422             try {
423                 flavorSupported = trans.isDataFlavorSupported(ExTransferable.multiFlavor);
424             } catch (java.lang.Exception JavaDoc e) {
425                 // patch to get the Netbeans start under Solaris
426
// [PENDINGworkaround]
427
}
428
429             if (flavorSupported) {
430                 // The node did not accept this multitransfer as is--try to break it into
431
// individual transfers and paste them in sequence instead.
432
try {
433                     MultiTransferObject obj = (MultiTransferObject) trans.getTransferData(ExTransferable.multiFlavor);
434                     int count = obj.getCount();
435                     boolean ok = true;
436                     Transferable[] t = new Transferable[count];
437                     PasteType[] p = new PasteType[count];
438
439                     for (int i = 0; i < count; i++) {
440                         t[i] = obj.getTransferableAt(i);
441                         pasteTypes = (pan == null) ? new PasteType[] { } : pan.getPasteTypes(t[i]);
442
443                         if (pasteTypes.length == 0) {
444                             ok = false;
445
446                             break;
447                         }
448
449                         // [PENDING] this is ugly! ideally should be some way of comparing PasteType's for similarity?
450
p[i] = pasteTypes[0];
451                     }
452
453                     if (ok) {
454                         PasteType[] arrOfPaste = new PasteType[] { new MultiPasteType(t, p) };
455
456                         if (attachPerformers) {
457                             paste.setPasteTypes(arrOfPaste);
458                         } else {
459                             pasteActionPerformer.setPasteTypes(arrOfPaste);
460                         }
461
462                         return;
463                     }
464                 } catch (UnsupportedFlavorException e) {
465                     // [PENDING] notify?!
466
} catch (IOException JavaDoc e) {
467                     // [PENDING] notify?!
468
}
469             }
470         }
471
472         if (attachPerformers) {
473             if (paste != null) {
474                 paste.setPasteTypes(null);
475             }
476         } else {
477             pasteActionPerformer.setPasteTypes(null);
478         }
479     }
480
481     /** If our clipboard is not found return the default system clipboard. */
482     private static Clipboard getClipboard() {
483         Clipboard c = (java.awt.datatransfer.Clipboard JavaDoc) org.openide.util.Lookup.getDefault().lookup(
484                 java.awt.datatransfer.Clipboard JavaDoc.class
485             );
486
487         if (c == null) {
488             c = java.awt.Toolkit.getDefaultToolkit().getSystemClipboard();
489         }
490
491         return c;
492     }
493
494     /** Updates actions state via updater (if the updater is present). */
495     private void updateActionsState() {
496         ActionStateUpdater asu;
497
498         synchronized (this) {
499             asu = actionStateUpdater;
500         }
501
502         if (asu != null) {
503             asu.update();
504         }
505     }
506
507     /** Paste type used when in clipbopard is MultiTransferable */
508     private static class MultiPasteType extends PasteType {
509         /** Array of transferables */
510         Transferable[] t;
511
512         /** Array of paste types */
513         PasteType[] p;
514
515         /** Constructs new MultiPasteType for the given content of the clipboard */
516         MultiPasteType(Transferable[] t, PasteType[] p) {
517             this.t = t;
518             this.p = p;
519         }
520
521         /** Performs the paste action.
522         * @return Transferable which should be inserted into the clipboard after
523         * paste action. It can be null, which means that clipboard content
524         * should be cleared.
525         */

526         public Transferable paste() throws IOException JavaDoc {
527             int size = p.length;
528             Transferable[] arr = new Transferable[size];
529
530             for (int i = 0; i < size; i++) {
531                 Transferable newTransferable = p[i].paste();
532
533                 if (newTransferable != null) {
534                     arr[i] = newTransferable;
535                 } else {
536                     // keep the orginal
537
arr[i] = t[i];
538                 }
539             }
540
541             return new ExTransferable.Multi(arr);
542         }
543     }
544
545     /** Own implementation of paste action
546      */

547     private class OwnPaste extends AbstractAction {
548         private PasteType[] pasteTypes = new PasteType[] { };
549
550         OwnPaste() {
551         }
552
553         public boolean isEnabled() {
554             updateActionsState();
555
556             return super.isEnabled();
557         }
558
559         public void setPasteTypes(PasteType[] arr) {
560             synchronized (this) {
561                 this.pasteTypes = arr;
562             }
563
564             setEnabled(arr != null);
565         }
566
567         public void actionPerformed(ActionEvent e) {
568             // bugfix #36262, added check to avoid NPE if pasteTypes are null
569
if (pasteTypes == null) {
570                 throw new IllegalStateException JavaDoc("Should not be invoked at all. Paste types: null"); // NOI18N
571
} else {
572                 throw new IllegalStateException JavaDoc(
573                     "Should not be invoked at all. Paste types: " + java.util.Arrays.asList(pasteTypes)
574                 ); // NOI18N
575
}
576         }
577
578         public Object JavaDoc getValue(String JavaDoc s) {
579             updateActionsState();
580
581             if ("delegates".equals(s)) { // NOI18N
582

583                 return pasteTypes;
584             }
585
586             return super.getValue(s);
587         }
588     }
589
590     /** Class which performs copy and cut actions */
591     private class CopyCutActionPerformer extends AbstractAction implements org.openide.util.actions.ActionPerformer {
592         /** determine if adapter is used for copy or cut action. */
593         private boolean copyCut;
594
595         /** Create new adapter */
596         public CopyCutActionPerformer(boolean b) {
597             copyCut = b;
598         }
599
600         public boolean isEnabled() {
601             updateActionsState();
602
603             return super.isEnabled();
604         }
605
606         /** Perform copy or cut action. */
607         public void performAction(org.openide.util.actions.SystemAction action) {
608             Transferable trans = null;
609             Node[] sel = manager.getSelectedNodes();
610
611             if (sel.length != 1) {
612                 Transferable[] arrayTrans = new Transferable[sel.length];
613
614                 for (int i = 0; i < sel.length; i++)
615                     if ((arrayTrans[i] = getTransferableOwner(sel[i])) == null) {
616                         return;
617                     }
618
619                 trans = new ExTransferable.Multi(arrayTrans);
620             } else {
621                 trans = getTransferableOwner(sel[0]);
622             }
623
624             if (trans != null) {
625                 Clipboard clipboard = getClipboard();
626
627                 clipboard.setContents(trans, new StringSelection("")); // NOI18N
628
}
629         }
630
631         private Transferable getTransferableOwner(Node node) {
632             try {
633                 return copyCut ? node.clipboardCopy() : node.clipboardCut();
634             } catch (java.io.IOException JavaDoc e) {
635                 Logger.getLogger(ExplorerActions.class.getName()).log(Level.WARNING, null, e);
636
637                 return null;
638             }
639         }
640
641         /** Invoked when an action occurs.
642          *
643          */

644         public void actionPerformed(ActionEvent e) {
645             performAction(null);
646         }
647     }
648
649     /** Class which performs delete action */
650     private class DeleteActionPerformer extends AbstractAction implements ActionPerformer {
651         DeleteActionPerformer() {
652         }
653
654         public boolean isEnabled() {
655             updateActionsState();
656
657             return super.isEnabled();
658         }
659
660         /** Perform delete action. */
661         public void performAction(org.openide.util.actions.SystemAction action) {
662             final Node[] sel = manager.getSelectedNodes();
663
664             if ((sel == null) || (sel.length == 0)) {
665                 return;
666             }
667
668             // perform action if confirmed
669
if (!isConfirmDelete() || doConfirm(sel)) {
670                 // clear selected nodes
671
try {
672                     if (manager != null) {
673                         manager.setSelectedNodes(new Node[] { });
674                     }
675                 } catch (java.beans.PropertyVetoException JavaDoc e) {
676                     // never thrown, setting empty selected nodes cannot be vetoed
677
}
678
679                 doDestroy(sel);
680                 class Run implements Runnable JavaDoc {
681                     public void run() {
682                         if (attachPerformers) {
683                             delete.setActionPerformer(null); // fixes bug #673
684
} else {
685                             setEnabled(false);
686                         }
687                     }
688                 }
689                 org.openide.util.Mutex.EVENT.readAccess(new Run());
690             }
691         }
692
693         private boolean doConfirm(Node[] sel) {
694             String JavaDoc message;
695             String JavaDoc title;
696             boolean customDelete = true;
697
698             for (int i = 0; i < sel.length; i++) {
699                 if (!Boolean.TRUE.equals(sel[i].getValue("customDelete"))) { // NOI18N
700
customDelete = false;
701
702                     break;
703                 }
704             }
705
706             if (customDelete) {
707                 return true;
708             }
709
710             if (sel.length == 1) {
711                 message = NbBundle.getMessage(
712                         ExplorerActions.class, "MSG_ConfirmDeleteObject", sel[0].getDisplayName()
713                     );
714                 title = NbBundle.getMessage(ExplorerActions.class, "MSG_ConfirmDeleteObjectTitle");
715             } else {
716                 message = NbBundle.getMessage(
717                         ExplorerActions.class, "MSG_ConfirmDeleteObjects", new Integer JavaDoc(sel.length)
718                     );
719                 title = NbBundle.getMessage(ExplorerActions.class, "MSG_ConfirmDeleteObjectsTitle");
720             }
721
722             NotifyDescriptor desc = new NotifyDescriptor.Confirmation(message, title, NotifyDescriptor.YES_NO_OPTION);
723
724             return NotifyDescriptor.YES_OPTION.equals(DialogDisplayer.getDefault().notify(desc));
725         }
726
727         /*
728         private String fullName(org.openide.loaders.DataObject obj) {
729             FileObject f = obj.getPrimaryFile();
730             if (f.isRoot()) {
731                 try {
732                     return f.getFileSystem().getDisplayName();
733                 } catch (FileStateInvalidException e) {
734                     return ""; //NOI18N
735                 }
736             } else {
737                 return f.toString();
738             }
739         }
740         */

741         private void doDestroy(final Node[] sel) {
742             try {
743                 Repository.getDefault().getDefaultFileSystem().runAtomicAction(new FileSystem.AtomicAction() {
744
745                                                                                    public void run() throws IOException JavaDoc {
746                                                                                        for (int i = 0; i <
747                                                                                                        sel.length; i++) {
748                                                                                            try {
749                                                                                                sel[i].destroy();
750                                                                                            }
751                                                                                            catch (IOException JavaDoc e) {
752                                                                                                Exceptions.printStackTrace(e);
753                                                                                            }
754                                                                                        }
755                                                                                    }
756                                                                                });
757             } catch (IOException JavaDoc ioe) {
758                 throw (IllegalStateException JavaDoc) new IllegalStateException JavaDoc(ioe.toString()).initCause(ioe);
759             }
760         }
761
762         /** Invoked when an action occurs.
763          *
764          */

765         public void actionPerformed(ActionEvent e) {
766             performAction(null);
767         }
768     }
769
770     /** Class which register changes in manager, and clipboard, coalesces
771      * them if they are frequent and performs the update of actions state. */

772     private class ActionStateUpdater implements PropertyChangeListener JavaDoc, ClipboardListener, ActionListener {
773         private final Timer timer;
774         private boolean planned;
775
776         ActionStateUpdater() {
777             timer = new FixIssue29405Timer(150, this);
778             timer.setCoalesce(true);
779             timer.setRepeats(false);
780         }
781
782         public synchronized void propertyChange(PropertyChangeEvent JavaDoc e) {
783             timer.restart();
784             planned = true;
785         }
786
787         public void clipboardChanged(ClipboardEvent ev) {
788             final ExplorerManager em = manager;
789
790             if (!ev.isConsumed() && (em != null)) {
791                 Mutex.EVENT.writeAccess(
792                     new Runnable JavaDoc() {
793                         public void run() {
794                             updatePasteAction(em.getSelectedNodes());
795                         }
796                     }
797                 );
798             }
799         }
800
801         public void actionPerformed(ActionEvent evt) {
802             updateActions();
803
804             synchronized (this) {
805                 timer.stop();
806                 planned = false;
807             }
808         }
809
810         /** Updates actions states now if there is pending event. */
811         public void update() {
812             boolean update;
813
814             synchronized (this) {
815                 update = planned;
816                 planned = false;
817             }
818
819             if (update) {
820                 timer.stop();
821                 updateActions();
822             }
823         }
824     }
825
826     /** Timer which fixes problem with running status (issue #29405). */
827     private static class FixIssue29405Timer extends javax.swing.Timer JavaDoc {
828         private boolean running;
829
830         public FixIssue29405Timer(int delay, ActionListener l) {
831             super(delay, l);
832         }
833
834         public void restart() {
835             super.restart();
836             running = true;
837         }
838
839         public void stop() {
840             running = false;
841             super.stop();
842         }
843
844         public boolean isRunning() {
845             return running;
846         }
847     }
848      // End of FixIssue29405Timer class.
849
}
850
Popular Tags