KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > objectweb > fractal > gui > undo > model > BasicUndoManager


1 /***
2  * FractalGUI: a graphical tool to edit Fractal component configurations.
3  * Copyright (C) 2003 France Telecom R&D
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18  *
19  * Contact: fractal@objectweb.org
20  *
21  * Authors: Eric Bruneton, Patrice Fauvel
22  */

23
24 package org.objectweb.fractal.gui.undo.model;
25
26 import org.objectweb.fractal.api.control.BindingController;
27
28 import org.objectweb.fractal.gui.graph.model.GraphModel;
29 import org.objectweb.fractal.gui.graph.model.GraphModelListener;
30 import org.objectweb.fractal.gui.graph.model.Rect;
31 import org.objectweb.fractal.gui.model.ClientInterface;
32 import org.objectweb.fractal.gui.model.Component;
33 import org.objectweb.fractal.gui.model.ConfigurationListener;
34 import org.objectweb.fractal.gui.model.Interface;
35 import org.objectweb.fractal.gui.model.ServerInterface;
36 import org.objectweb.fractal.gui.model.Configuration;
37
38 import java.util.ArrayList JavaDoc;
39 import java.util.List JavaDoc;
40 import java.util.Map JavaDoc;
41 import java.util.HashMap JavaDoc;
42 import java.util.Iterator JavaDoc;
43 import java.awt.Color JavaDoc;
44
45 /**
46  * Basic implementation of the {@link UndoManager} interface.
47  */

48
49 public class BasicUndoManager implements
50   UndoManager,
51   ConfigurationListener,
52   GraphModelListener,
53   BindingController
54 {
55
56   /**
57    * A mandatory client interface bound to a {@link Configuration configuration}
58    * model. This is the configuration whose root component is changed when
59    * undoing or redoing modifications.
60    */

61
62   public final static String JavaDoc CONFIGURATION_BINDING = "configuration";
63
64   /**
65    * An optional client interface bound to a {@link GraphModel graph} model.
66    * This is the graph model that is changed when undoing or redoing
67    * modifications.
68    */

69
70   public final static String JavaDoc GRAPH_BINDING = "graph";
71
72   /**
73    * A collection client interface bound to the {@link UndoListener listeners}
74    * of this component.
75    */

76
77   public final static String JavaDoc UNDO_LISTENERS_BINDING = "undo-listeners";
78
79   /**
80    * Maximum number of modifications stored by this component.
81    */

82
83   private final static int MAX_UNDO = 50;
84
85   /**
86    * The configuration client interface.
87    */

88
89   private Configuration configuration;
90
91   /**
92    * The graph client interface.
93    */

94
95   private GraphModel graph;
96
97   /**
98    * The listeners client interface.
99    */

100
101   private Map JavaDoc undoListeners;
102
103   /**
104    * The list of changes that can be undone.
105    */

106
107   private List JavaDoc undoList;
108
109   /**
110    * The list of changes that can be redone.
111    */

112
113   private List JavaDoc redoList;
114
115   /**
116    * If the {@link #undo undo} method is currently executing.
117    */

118
119   private boolean isUndoing;
120
121   /**
122    * If the {@link #redo redo} method is currently executing.
123    */

124
125   private boolean isRedoing;
126
127   /**
128    * Constructs a new {@link BasicUndoManager} component.
129    */

130
131   public BasicUndoManager () {
132     undoList = new ArrayList JavaDoc();
133     redoList = new ArrayList JavaDoc();
134     undoListeners = new HashMap JavaDoc();
135   }
136
137   // -------------------------------------------------------------------------
138
// Implementation of the BindingController interface
139
// -------------------------------------------------------------------------
140

141   public String JavaDoc[] listFc () {
142     int size = undoListeners.size();
143     String JavaDoc[] names = new String JavaDoc[size + 2];
144     undoListeners.keySet().toArray(names);
145     names[size] = CONFIGURATION_BINDING;
146     names[size + 1] = GRAPH_BINDING;
147     return names;
148   }
149
150   public Object JavaDoc lookupFc (final String JavaDoc clientItfName) {
151     if (CONFIGURATION_BINDING.equals(clientItfName)) {
152       return configuration;
153     } else if (GRAPH_BINDING.equals(clientItfName)) {
154       return graph;
155     } else if (clientItfName.startsWith(UNDO_LISTENERS_BINDING)) {
156       return undoListeners.get(clientItfName);
157     }
158     return null;
159   }
160
161
162   public void bindFc (
163     final String JavaDoc clientItfName,
164     final Object JavaDoc serverItf)
165   {
166     if (CONFIGURATION_BINDING.equals(clientItfName)) {
167       configuration = (Configuration)serverItf;
168     } else if (GRAPH_BINDING.equals(clientItfName)) {
169       graph = (GraphModel)serverItf;
170     } else if (clientItfName.startsWith(UNDO_LISTENERS_BINDING)) {
171       undoListeners.put(clientItfName, serverItf);
172     }
173   }
174
175   public void unbindFc (final String JavaDoc clientItfName) {
176     if (CONFIGURATION_BINDING.equals(clientItfName)) {
177       configuration = null;
178     } else if (GRAPH_BINDING.equals(clientItfName)) {
179       graph = null;
180     } else if (clientItfName.startsWith(UNDO_LISTENERS_BINDING)) {
181       undoListeners.remove(clientItfName);
182     }
183   }
184
185   // -------------------------------------------------------------------------
186
// Implementation of the ConfigurationListener interface
187
// -------------------------------------------------------------------------
188

189   public void changeCountChanged (Component component, long changeCount) {
190     // does nothing
191
}
192
193   public void rootComponentChanged (final Component oldValue) {
194     clear();
195   }
196
197   public void nameChanged (final Component component, final String JavaDoc oldValue) {
198     if (configuration.getRootComponent().contains(component)) {
199       addAction(new Action(component, 1) {
200         void run () {
201           ((Component)target).setName(oldValue);
202         }
203       });
204     }
205   }
206
207   public void typeChanged (final Component component, final String JavaDoc oldValue) {
208     if (configuration.getRootComponent().contains(component)) {
209       addAction(new Action(component, 1) {
210         void run () {
211           ((Component)target).setType(oldValue);
212         }
213       });
214     }
215   }
216
217   public void implementationChanged (
218     final Component component,
219     final String JavaDoc oldValue)
220   {
221     if (configuration.getRootComponent().contains(component)) {
222       addAction(new Action(component, 1) {
223         void run () {
224           ((Component)target).setImplementation(oldValue);
225         }
226       });
227     }
228   }
229
230   public void interfaceNameChanged (final Interface i, final String JavaDoc oldValue) {
231     if (configuration.getRootComponent().contains(i.getOwner())) {
232       addAction(new Action(i, 1) {
233         void run () {
234           ((Interface)target).setName(oldValue);
235         }
236       });
237     }
238   }
239
240   public void interfaceSignatureChanged (
241     final Interface i,
242     final String JavaDoc oldValue)
243   {
244     if (configuration.getRootComponent().contains(i.getOwner())) {
245       addAction(new Action(i, 1) {
246         void run () {
247           ((Interface)target).setSignature(oldValue);
248         }
249       });
250     }
251   }
252
253   public void interfaceContingencyChanged (
254     final Interface i,
255     final boolean oldValue)
256   {
257     if (configuration.getRootComponent().contains(i.getOwner())) {
258       addAction(new Action(i, 1) {
259         void run () {
260           ((Interface)target).setIsOptional(oldValue);
261         }
262       });
263     }
264   }
265
266   public void interfaceCardinalityChanged (
267     final Interface i,
268     final boolean oldValue)
269   {
270     if (configuration.getRootComponent().contains(i.getOwner())) {
271       addAction(new Action(i, 1) {
272         void run () {
273           ((Interface)target).setIsCollection(oldValue);
274         }
275       });
276     }
277   }
278
279   public void clientInterfaceAdded (
280     final Component component,
281     final ClientInterface i,
282     final int index)
283   {
284     if (configuration.getRootComponent().contains(component)) {
285       addAction(new Action(null, 1) {
286         void run () {
287           component.removeClientInterface(i);
288         }
289       });
290     }
291   }
292
293   public void clientInterfaceRemoved (
294     final Component component,
295     final ClientInterface i,
296     final int index)
297   {
298     if (configuration.getRootComponent().contains(component)) {
299       addAction(new Action(null, 1) {
300         void run () {
301           component.addClientInterface(i);
302         }
303       });
304     }
305   }
306
307   public void serverInterfaceAdded (
308     final Component component,
309     final ServerInterface i,
310     final int index)
311   {
312     if (configuration.getRootComponent().contains(component)) {
313       addAction(new Action(null, 1) {
314         void run () {
315           component.removeServerInterface(i);
316         }
317       });
318     }
319   }
320
321   public void serverInterfaceRemoved (
322     final Component component,
323     final ServerInterface i,
324     final int index)
325   {
326     if (configuration.getRootComponent().contains(component)) {
327       addAction(new Action(null, 1) {
328         void run () {
329           component.addServerInterface(i);
330         }
331       });
332     }
333   }
334
335   public void interfaceBound (
336     final ClientInterface citf,
337     final ServerInterface sitf)
338   {
339     if (configuration.getRootComponent().contains(citf.getOwner())) {
340       addAction(new Action(null, 1) {
341         void run () {
342           citf.getOwner().unbind(citf);
343         }
344       });
345     }
346   }
347
348   public void interfaceRebound (
349     final ClientInterface citf,
350     final ServerInterface oldSitf)
351   {
352     if (configuration.getRootComponent().contains(citf.getOwner())) {
353       addAction(new Action(null, 1) {
354         void run () {
355           citf.getOwner().rebind(citf, oldSitf);
356         }
357       });
358     }
359   }
360
361   public void interfaceUnbound (
362     final ClientInterface citf,
363     final ServerInterface sitf)
364   {
365     if (configuration.getRootComponent().contains(citf.getOwner())) {
366       addAction(new Action(null, 1) {
367         void run () {
368           citf.getOwner().bind(citf, null, sitf);
369         }
370       });
371     }
372   }
373
374   public void attributeControllerChanged (
375     final Component component,
376     final String JavaDoc oldValue)
377   {
378     if (configuration.getRootComponent().contains(component)) {
379       addAction(new Action(component, 1) {
380         void run () {
381           ((Component)target).setAttributeController(oldValue);
382         }
383       });
384     }
385   }
386
387   public void attributeChanged (
388     final Component component,
389     final String JavaDoc attributeName,
390     final String JavaDoc oldValue)
391   {
392     if (configuration.getRootComponent().contains(component)) {
393       addAction(new Action(component, 1) {
394         void run () {
395           ((Component)target).setAttribute(attributeName, oldValue);
396         }
397       });
398     }
399   }
400
401   public void templateControllerDescriptorChanged (
402     final Component component,
403     final String JavaDoc oldValue)
404   {
405     if (configuration.getRootComponent().contains(component)) {
406       addAction(new Action(component, 1) {
407         void run () {
408           ((Component)target).setTemplateControllerDescriptor(oldValue);
409         }
410       });
411     }
412   }
413
414   public void componentControllerDescriptorChanged (
415     final Component component,
416     final String JavaDoc oldValue)
417   {
418     if (configuration.getRootComponent().contains(component)) {
419       addAction(new Action(component, 1) {
420         void run () {
421           ((Component)target).setComponentControllerDescriptor(oldValue);
422         }
423       });
424     }
425   }
426
427   public void subComponentAdded (
428     final Component parent,
429     final Component child,
430     final int index)
431   {
432     if (configuration.getRootComponent().contains(parent)) {
433       addAction(new Action(null, 1) {
434         void run () {
435           parent.removeSubComponent(child);
436         }
437       });
438     }
439   }
440
441   public void subComponentRemoved (
442     final Component parent,
443     final Component child,
444     final int index)
445   {
446     if (configuration.getRootComponent().contains(parent)) {
447       addAction(new Action(null, 1) {
448         void run () {
449           parent.addSubComponent(child);
450         }
451       });
452     }
453   }
454
455   // -------------------------------------------------------------------------
456
// Implementation of the GraphModelListener
457
// -------------------------------------------------------------------------
458

459   public void componentColorChanged (
460     final Component component,
461     final Color JavaDoc oldColor)
462   {
463     if (configuration.getRootComponent().contains(component)) {
464       addAction(new Action(component, 0) {
465         void run () {
466           graph.setComponentColor((Component)target, oldColor);
467         }
468       });
469     }
470   }
471
472   public void componentPositionChanged (
473     final Component component,
474     final Rect oldValue)
475   {
476     if (configuration.getRootComponent().contains(component)) {
477       addAction(new Action(component, 0) {
478         void run () {
479           graph.setComponentPosition((Component)target, oldValue);
480         }
481       });
482     }
483   }
484
485   // -------------------------------------------------------------------------
486
// Implementation of the UndoManager interface
487
// -------------------------------------------------------------------------
488

489   public boolean canUndo () {
490     return undoList.size() > 0;
491   }
492
493   public void undo () {
494     isUndoing = true;
495     try {
496       Action a = (Action)undoList.remove(undoList.size() - 1);
497       configuration.setChangeCount(
498         configuration.getChangeCount() - 2*a.deltaChangeCount);
499       a.run();
500     } finally {
501       isUndoing = false;
502     }
503     if (undoList.size() == 0) {
504       notifyListeners();
505     }
506   }
507
508   public boolean canRedo () {
509     return redoList.size() > 0;
510   }
511
512   public void redo () {
513     isRedoing = true;
514     try {
515       ((Action)redoList.remove(redoList.size() - 1)).run();
516     } finally {
517       isRedoing = false;
518     }
519     if (redoList.size() == 0) {
520       notifyListeners();
521     }
522   }
523
524   public void clear () {
525     undoList.clear();
526     redoList.clear();
527     notifyListeners();
528   }
529
530   // -------------------------------------------------------------------------
531
// Other methods
532
// -------------------------------------------------------------------------
533

534   /**
535    * An action that can executed to undo or redo something.
536    */

537
538   abstract class Action {
539
540     /**
541      * The target of this action. Actions with identical target may be
542      * coalesced (see {@link BasicUndoManager#addAction addAction}).
543      */

544
545     Object JavaDoc target;
546
547     /**
548      * The changeCount variation when this action or its opposite is run.
549      */

550
551     int deltaChangeCount;
552
553     /**
554      * Constructs a new {@link BasicUndoManager.Action}.
555      *
556      * @param target the target of this action.
557      * @param deltaChangeCount the changeCount variation when this action or its
558      * opposite is run.
559      */

560
561     Action (final Object JavaDoc target, int deltaChangeCount) {
562       this.target = target;
563       this.deltaChangeCount = deltaChangeCount;
564     }
565
566     /**
567      * Executes this action.
568      */

569
570     abstract void run ();
571   }
572
573   /**
574    * Adds the given action to the undo or redo list.
575    *
576    * @param a an action.
577    */

578
579   private void addAction (final Action a) {
580     if (!isUndoing && !isRedoing && redoList.size() > 0) {
581       redoList.clear();
582       notifyListeners();
583     }
584     List JavaDoc l = isUndoing ? redoList : undoList;
585     if (l.size() == 0) {
586       l.add(a);
587       notifyListeners();
588     } else {
589       Action last = (Action)l.get(l.size() - 1);
590       if (last.getClass() == a.getClass() &&
591           last.target == a.target &&
592           a.target != null)
593       {
594         // ignore a
595
return;
596       }
597       if (l.size() >= MAX_UNDO) {
598         l.remove(0);
599       }
600       l.add(a);
601     }
602   }
603
604   /**
605    * Notifies the listeners of this model that its state has changed.
606    */

607
608   private void notifyListeners () {
609     Iterator JavaDoc i = undoListeners.values().iterator();
610     while (i.hasNext()) {
611       ((UndoListener)i.next()).undoStateChanged();
612     }
613   }
614 }
615
Popular Tags