/* * TimelineVisualEdit.java * Eisenkraut * * Copyright (c) 2004-2016 Hanns Holger Rutz. All rights reserved. * * This software is published under the GNU General Public License v3+ * * * For further information, please contact Hanns Holger Rutz at * contact@sciss.de */ package de.sciss.eisenkraut.edit; import javax.swing.undo.UndoableEdit; import de.sciss.app.BasicUndoableEdit; import de.sciss.app.PerformableEdit; import de.sciss.eisenkraut.session.Session; import de.sciss.io.Span; /** * An <code>UndoableEdit</code> that describes the modification of the * timeline visual properties (position, selection, visible span). * This edit is always "insignificant", placing it on the * pending stack of the undo manager, and not appearing as separately * undoable edits. By fusing all visual properties into one edit * class, successive visual edits can be collapsed into one edit object * without flooding the undo manager's list. * * @see UndoManager */ @SuppressWarnings("serial") public class TimelineVisualEdit extends BasicUndoableEdit { private final Session doc; private Object source; private long oldPos, newPos; private Span oldVisible, newVisible, oldSel, newSel; private int actionMask; private static final int ACTION_POSITION = 0x01; private static final int ACTION_SCROLL = 0x02; private static final int ACTION_SELECT = 0x04; /* * Creates and performs the edit. This method * invokes the <code>Timeline.setSelectionSpan</code> method, * thus dispatching a <code>TimelineEvent</code>. * * @param source who originated the edit. the source is * passed to the <code>Timeline.setSelectionSpan</code> method. * @param doc session into whose <code>Timeline</code> is * to be selected / deselected. * @param span the new timeline selection span. */ private TimelineVisualEdit(Object source, Session doc) { super(); this.source = source; this.doc = doc; actionMask = 0; } public static TimelineVisualEdit position(Object source, Session doc, long pos) { final TimelineVisualEdit tve = new TimelineVisualEdit(source, doc); tve.actionMask = ACTION_POSITION; tve.oldPos = doc.timeline.getPosition(); tve.newPos = pos; return tve; } public static TimelineVisualEdit scroll(Object source, Session doc, Span newVisible) { final TimelineVisualEdit tve = new TimelineVisualEdit(source, doc); tve.actionMask = ACTION_SCROLL; tve.oldVisible = doc.timeline.getVisibleSpan(); tve.newVisible = newVisible; return tve; } public static TimelineVisualEdit select(Object source, Session doc, Span newSel) { final TimelineVisualEdit tve = new TimelineVisualEdit(source, doc); tve.actionMask = ACTION_SELECT; tve.oldSel = doc.timeline.getSelectionSpan(); tve.newSel = newSel; return tve; } public PerformableEdit perform() { if ((actionMask & ACTION_POSITION) != 0) { doc.timeline.setPosition(source, newPos); } if ((actionMask & ACTION_SCROLL) != 0) { doc.timeline.setVisibleSpan(source, newVisible); } if ((actionMask & ACTION_SELECT) != 0) { doc.timeline.setSelectionSpan(source, newSel); } source = this; return this; } /** * @return false to tell the UndoManager it should not feature * the edit as a single undoable step in the history. * which is especially important since <code>TimelineAxis</code> * will generate lots of edits when the user drags * the timeline selection. */ public boolean isSignificant() { return false; } /** * Undoes the edit * by calling the <code>Timeline.setSelectionSpan</code>, * method, thus dispatching a <code>TimelineEvent</code>. */ public void undo() { super.undo(); if ((actionMask & ACTION_POSITION) != 0) { doc.timeline.setPosition(source, oldPos); } if ((actionMask & ACTION_SCROLL) != 0) { doc.timeline.setVisibleSpan(source, oldVisible); } if ((actionMask & ACTION_SELECT) != 0) { doc.timeline.setSelectionSpan(source, oldSel); } } /** * Redoes the edit. The original source is discarded * which means, that, since a new <code>TimelineEvent</code> * is dispatched, even the original object * causing the edit will not know the details * of the action, hence thoroughly look * and adapt itself to the new edit. */ public void redo() { super.redo(); perform(); } /** * Collapses multiple successive EditSetReceiverBounds edit * into one single edit. The new edit is sucked off by * the old one. */ public boolean addEdit(UndoableEdit anEdit) { if (anEdit instanceof TimelineVisualEdit) { final TimelineVisualEdit tve = (TimelineVisualEdit) anEdit; if ((tve.actionMask & ACTION_POSITION) != 0) { newPos = tve.newPos; if ((actionMask & ACTION_POSITION) == 0) { oldPos = tve.oldPos; } } if ((tve.actionMask & ACTION_SCROLL) != 0) { newVisible = tve.newVisible; if ((actionMask & ACTION_SCROLL) == 0) { oldVisible = tve.oldVisible; } } if ((tve.actionMask & ACTION_SELECT) != 0) { newSel = tve.newSel; if ((actionMask & ACTION_SELECT) == 0) { oldSel = tve.oldSel; } } actionMask |= tve.actionMask; anEdit.die(); return true; } else { return false; } } /** * Collapses multiple successive edits * into one single edit. The old edit is sucked off by * the new one. */ public boolean replaceEdit(UndoableEdit anEdit) { if (anEdit instanceof TimelineVisualEdit) { final TimelineVisualEdit tve = (TimelineVisualEdit) anEdit; if ((tve.actionMask & ACTION_POSITION) != 0) { oldPos = tve.oldPos; if ((actionMask & ACTION_POSITION) == 0) { newPos = tve.newPos; } } if ((tve.actionMask & ACTION_SCROLL) != 0) { oldVisible = tve.oldVisible; if ((actionMask & ACTION_SCROLL) == 0) { newVisible = tve.newVisible; } } if ((tve.actionMask & ACTION_SELECT) != 0) { oldSel = tve.oldSel; if ((actionMask & ACTION_SELECT) == 0) { newSel = tve.newSel; } } actionMask |= tve.actionMask; anEdit.die(); return true; } else { return false; } } public String getPresentationName() { return getResourceString("editSetTimelineView"); } }