Merge from the pain train
[official-gcc.git] / libjava / javax / swing / undo / UndoManager.java
blobfcdcf34ec0f528145540ed0fd8c97f24a5d77704
1 /* AbstractTableModel.java --
2 Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 USA.
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
39 package javax.swing.undo;
41 import javax.swing.UIManager;
42 import javax.swing.event.UndoableEditEvent;
43 import javax.swing.event.UndoableEditListener;
46 /**
47 * A manager for providing an application’s undo/redo
48 * functionality.
50 * <p>Tyipcally, an application will create only one single instance
51 * of UndoManager. When the user performs an undoable action, for
52 * instance changing the color of an object from green to blue, the
53 * application registers an {@link UndoableEdit} object with the
54 * <code>UndoManager</code>. To implement the &#x201c;undo&#x201d; and
55 * &#x201c;redo&#x201d; menu commands, the application invokes the
56 * UndoManager&#x2019;s {@link #undo} and {@link #redo} methods. The
57 * human-readable text of these menu commands is provided by {@link
58 * #getUndoPresentationName} and {@link #getRedoPresentationName},
59 * respectively. To determine whether the menu item should be
60 * selectable or greyed out, use {@link #canUndo} and {@link
61 * #canRedo}.
63 * <p>The UndoManager will only keep a specified number of editing
64 * actions, the <em>limit</em>. The value of this parameter can be
65 * retrieved by calling {@link #getLimit} and set with {@link
66 * #setLimit}. If more UndoableEdits are added to the UndoManager,
67 * the oldest actions will be discarded.
69 * <p>Some applications do not provide separate menu commands for
70 * &#x201c;undo&#x201d; and &#x201c;redo.&#x201d; Instead, they
71 * have just a single command whose text switches between the two.
72 * Such applications would use an UndoManager with a <code>limit</code>
73 * of 1. The text of this combined menu item is available via
74 * {@link #getUndoOrRedoPresentationName}, and it is implemented
75 * by calling {@link #undoOrRedo}.
77 * <p><b>Thread Safety:</b> In constrast to the other classes of the
78 * <code>javax.swing.undo</code> package, the public methods of an
79 * <code>UndoManager</code> are safe to call from concurrent threads.
80 * The caller does not need to perform external synchronization, and
81 * {@link javax.swing.event.UndoableEvent} sources do not need to
82 * broadcast their events from inside the Swing worker thread.
84 * @author Sascha Brawer (brawer@dandelis.ch)
86 public class UndoManager
87 extends CompoundEdit
88 implements UndoableEditListener
90 /**
91 * The unique ID for serializing instances of this class. Determined
92 * using the <code>serialver</code> tool of Sun JDK 1.4.1_01 on
93 * GNU/Linux.
95 static final long serialVersionUID = -2077529998244066750L;
98 /**
99 * An index into the inherited {@link #edits} Vector that indicates
100 * at which position newly added editing actions would get inserted.
102 * <p>Normally, the value of <code>indexOfNextAdd</code> equals
103 * the number of UndoableEdits stored by this UndoManager, i.e.
104 * <code>edits.size()</code>. For each call to {@link #undo},
105 * <code>indexOfNextAdd</code> is decremented by one. For each
106 * call to {@link #redo}, it is incremented again.
108 int indexOfNextAdd;
112 * The maximum number of UndoableEdits stored by this UndoManager.
114 int limit;
118 * Constructs an UndoManager.
120 * <p>The <code>limit</code> of the freshly constructed UndoManager
121 * is 100.
123 public UndoManager()
125 limit = 100;
130 * Returns a string representation for this UndoManager. This may be
131 * useful for debugging purposes. For the text of menu items, please
132 * refer to {@link #getUndoPresentationName}, {@link
133 * #getRedoPresentationName}, and {@link
134 * #getUndoOrRedoPresentationName}.
136 public String toString()
138 return super.toString()
139 + " limit: " + limit
140 + " indexOfNextAdd: " + indexOfNextAdd;
145 * Puts this UndoManager into a state where it acts as a normal
146 * {@link CompoundEdit}. It is unlikely that an application would
147 * want to do this.
149 public synchronized void end()
151 super.end();
152 trimEdits(indexOfNextAdd, edits.size() - 1);
157 * Returns how many edits this UndoManager can maximally hold.
159 * @see #setLimit
161 public synchronized int getLimit()
163 return limit;
168 * Changes the maximal number of edits that this UndoManager can
169 * process. If there are currently more edits than the new limit
170 * allows, they will receive a {@link UndoableEdit#die() die}
171 * message in reverse order of addition.
173 * @param limit the new limit.
175 * @throws IllegalStateException if {@link #end()} has already been
176 * called on this UndoManager.
178 public synchronized void setLimit(int limit)
180 if (!isInProgress())
181 throw new IllegalStateException();
183 this.limit = limit;
184 trimForLimit();
189 * Discards all editing actions that are currently registered with
190 * this UndoManager. Each {@link UndoableEdit} will receive a {@link
191 * UndoableEdit#die() die message}.
193 public synchronized void discardAllEdits()
195 int size;
197 size = edits.size();
198 for (int i = size - 1; i >= 0; i--)
199 ((UndoableEdit) edits.get(i)).die();
200 indexOfNextAdd = 0;
201 edits.clear();
206 * Called by various internal methods in order to enforce
207 * the <code>limit</code> value.
209 protected void trimForLimit()
211 int high, s;
213 s = edits.size();
215 /* The Sun J2SE1.4.1_01 implementation can be observed to do
216 * nothing (instead of throwing an exception) with a negative or
217 * zero limit. It may be debatable whether this is the best
218 * behavior, but we replicate it for sake of compatibility.
220 if (limit <= 0 || s <= limit)
221 return;
223 high = Math.min(indexOfNextAdd + limit/2 - 1, s - 1);
224 trimEdits(high + 1, s - 1);
225 trimEdits(0, high - limit);
230 * Discards a range of edits. All edits in the range <code>[from
231 * .. to]</code> will receive a {@linkplain UndoableEdit#die() die
232 * message} before being removed from the edits array. If
233 * <code>from</code> is greater than <code>to</code>, nothing
234 * happens.
236 * @param from the lower bound of the range of edits to be
237 * discarded.
239 * @param to the upper bound of the range of edits to be discarded.
241 protected void trimEdits(int from, int to)
243 if (from > to)
244 return;
246 for (int i = to; i >= from; i--)
247 ((UndoableEdit) edits.get(i)).die();
249 // Remove the range [from .. to] from edits. If from == to, which
250 // is likely to be a very common case, we can do better than
251 // creating a sub-list and clearing it.
252 if (to == from)
253 edits.remove(from);
254 else
255 edits.subList(from, to + 1).clear();
257 if (indexOfNextAdd > to)
258 indexOfNextAdd = indexOfNextAdd - to + from - 1;
259 else if (indexOfNextAdd >= from)
260 indexOfNextAdd = from;
265 * Determines which significant edit would be undone if {@link
266 * #undo()} was called.
268 * @returns the significant edit that would be undone, or
269 * <code>null</code> if no significant edit would be affected by
270 * calling {@link #undo()}.
272 protected UndoableEdit editToBeUndone()
274 UndoableEdit result;
276 for (int i = indexOfNextAdd - 1; i >= 0; i--)
278 result = (UndoableEdit) edits.get(i);
279 if (result.isSignificant())
280 return result;
283 return null;
288 * Determines which significant edit would be redone if {@link
289 * #redo()} was called.
291 * @returns the significant edit that would be redone, or
292 * <code>null</code> if no significant edit would be affected by
293 * calling {@link #redo()}.
295 protected UndoableEdit editToBeRedone()
297 UndoableEdit result;
299 for (int i = indexOfNextAdd; i < edits.size(); i++)
301 result = (UndoableEdit) edits.get(i);
302 if (result.isSignificant())
303 return result;
306 return null;
311 * Undoes all editing actions in reverse order of addition,
312 * up to the specified action,
314 * @param edit the last editing action to be undone.
316 protected void undoTo(UndoableEdit edit)
317 throws CannotUndoException
319 UndoableEdit cur;
321 if (!edits.contains(edit))
322 throw new CannotUndoException();
324 while (true)
326 indexOfNextAdd -= 1;
327 cur = (UndoableEdit) edits.get(indexOfNextAdd);
328 cur.undo();
329 if (cur == edit)
330 return;
336 * Redoes all editing actions in the same order as they were
337 * added to this UndoManager, up to the specified action.
339 * @param edit the last editing action to be redone.
341 protected void redoTo(UndoableEdit edit)
342 throws CannotRedoException
344 UndoableEdit cur;
346 if (!edits.contains(edit))
347 throw new CannotRedoException();
349 while (true)
351 cur = (UndoableEdit) edits.get(indexOfNextAdd);
352 indexOfNextAdd += 1;
353 cur.redo();
354 if (cur == edit)
355 return;
361 * Undoes or redoes the last action. If the last action has already
362 * been undone, it will be re-done, and vice versa.
364 * <p>This is useful for applications that do not present a separate
365 * undo and redo facility, but just have a single menu item for
366 * undoing and redoing the very last action. Such applications will
367 * use an <code>UndoManager</code> whose <code>limit</code> is 1.
369 public synchronized void undoOrRedo()
370 throws CannotRedoException, CannotUndoException
372 if (indexOfNextAdd == edits.size())
373 undo();
374 else
375 redo();
380 * Determines whether it would be possible to either undo or redo
381 * this editing action.
383 * <p>This is useful for applications that do not present a separate
384 * undo and redo facility, but just have a single menu item for
385 * undoing and redoing the very last action. Such applications will
386 * use an <code>UndoManager</code> whose <code>limit</code> is 1.
388 * @return <code>true</code> to indicate that this action can be
389 * undone or redone; <code>false</code> if neither is possible at
390 * the current time.
392 public synchronized boolean canUndoOrRedo()
394 return indexOfNextAdd == edits.size() ? canUndo() : canRedo();
399 * Undoes one significant edit action. If insignificant actions have
400 * been posted after the last signficant action, the insignificant
401 * ones will be undone first.
403 * <p>However, if {@link #end()} has been called on this
404 * UndoManager, it will behave like a normal {@link
405 * CompoundEdit}. In this case, all actions will be undone in
406 * reverse order of addition. Typical applications will never call
407 * {@link #end()} on their <code>UndoManager</code>.
409 * @throws CannotUndoException if no action can be undone.
411 * @see #canUndo()
412 * @see #redo()
413 * @see #undoOrRedo()
415 public synchronized void undo()
416 throws CannotUndoException
418 if (!isInProgress())
420 super.undo();
421 return;
424 UndoableEdit edit = editToBeUndone();
425 if (edit == null)
426 throw new CannotUndoException();
428 undoTo(edit);
433 * Determines whether it would be possible to undo this editing
434 * action.
436 * @return <code>true</code> to indicate that this action can be
437 * undone; <code>false</code> otherwise.
439 * @see #undo()
440 * @see #canRedo()
441 * @see #canUndoOrRedo()
443 public synchronized boolean canUndo()
445 UndoableEdit edit;
447 if (!isInProgress())
448 return super.canUndo();
450 edit = editToBeUndone();
451 return edit != null && edit.canUndo();
457 * Redoes one significant edit action. If insignificant actions have
458 * been posted in between, the insignificant ones will be redone
459 * first.
461 * <p>However, if {@link #end()} has been called on this
462 * UndoManager, it will behave like a normal {@link
463 * CompoundEdit}. In this case, <em>all</em> actions will be redone
464 * in order of addition. Typical applications will never call {@link
465 * #end()} on their <code>UndoManager</code>.
467 * @throws CannotRedoException if no action can be redone.
469 * @see #canRedo()
470 * @see #redo()
471 * @see #undoOrRedo()
473 public synchronized void redo()
474 throws CannotRedoException
476 if (!isInProgress())
478 super.redo();
479 return;
482 UndoableEdit edit = editToBeRedone();
483 if (edit == null)
484 throw new CannotRedoException();
486 redoTo(edit);
491 * Determines whether it would be possible to redo this editing
492 * action.
494 * @return <code>true</code> to indicate that this action can be
495 * redone; <code>false</code> otherwise.
497 * @see #redo()
498 * @see #canUndo()
499 * @see #canUndoOrRedo()
501 public synchronized boolean canRedo()
503 UndoableEdit edit;
505 if (!isInProgress())
506 return super.canRedo();
508 edit = editToBeRedone();
509 return edit != null && edit.canRedo();
514 * Registers an undoable editing action with this UndoManager. If
515 * the capacity <code>limit</code> is reached, the oldest action
516 * will be discarded (and receives a {@linkplain UndoableEdit#die()
517 * die message}. Equally, any actions that were undone (but not re-done)
518 * will be discarded, too.
520 * @param edit the editing action that is added to this UndoManager.
522 * @return <code>true</code> if <code>edit</code> could be
523 * incorporated; <code>false</code> if <code>edit</code> has not
524 * been incorporated because {@link #end()} has already been called
525 * on this <code>UndoManager</code>.
527 public synchronized boolean addEdit(UndoableEdit edit)
529 boolean result;
531 // Discard any edits starting at indexOfNextAdd.
532 trimEdits(indexOfNextAdd, edits.size() - 1);
534 result = super.addEdit(edit);
535 indexOfNextAdd = edits.size();
536 trimForLimit();
537 return result;
542 * Calculates a localized text for presenting the undo or redo
543 * action to the user, for example in the form of a menu command.
545 * <p>This is useful for applications that do not present a separate
546 * undo and redo facility, but just have a single menu item for
547 * undoing and redoing the very last action. Such applications will
548 * use an <code>UndoManager</code> whose <code>limit</code> is 1.
550 * @return the redo presentation name if the last action has already
551 * been undone, or the undo presentation name otherwise.
553 * @see #getUndoPresentationName()
554 * @see #getRedoPresentationName()
556 public synchronized String getUndoOrRedoPresentationName()
558 if (indexOfNextAdd == edits.size())
559 return getUndoPresentationName();
560 else
561 return getRedoPresentationName();
566 * Calculates a localized text for presenting the undo action
567 * to the user, for example in the form of a menu command.
569 public synchronized String getUndoPresentationName()
571 UndoableEdit edit;
573 if (!isInProgress())
574 return super.getUndoPresentationName();
576 edit = editToBeUndone();
577 if (edit == null)
578 return UIManager.getString("AbstractUndoableEdit.undoText");
579 else
580 return edit.getUndoPresentationName();
585 * Calculates a localized text for presenting the redo action
586 * to the user, for example in the form of a menu command.
588 public synchronized String getRedoPresentationName()
590 UndoableEdit edit;
592 if (!isInProgress())
593 return super.getRedoPresentationName();
595 edit = editToBeRedone();
596 if (edit == null)
597 return UIManager.getString("AbstractUndoableEdit.redoText");
598 else
599 return edit.getRedoPresentationName();
604 * Registers the edit action of an {@link UndoableEditEvent}
605 * with this UndoManager.
607 * <p><b>Thread Safety:</b> This method may safely be invoked from
608 * concurrent threads. The caller does not need to perform external
609 * synchronization. This means that {@link
610 * javax.swing.event.UndoableEvent} sources do not need to broadcast
611 * their events from inside the Swing worker thread.
613 * @param event the event whose <code>edit</code> will be
614 * passed to {@link #addEdit}.
616 * @see UndoableEditEvent#getEdit()
617 * @see #addEdit
619 public void undoableEditHappened(UndoableEditEvent event)
621 // Note that this method does not need to be synchronized,
622 // because addEdit will obtain and release the mutex.
623 addEdit(event.getEdit());