13aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn/* 23aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * Copyright (C) 2013 The Android Open Source Project 33aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * 43aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * Licensed under the Apache License, Version 2.0 (the "License"); 53aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * you may not use this file except in compliance with the License. 63aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * You may obtain a copy of the License at 73aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * 83aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * http://www.apache.org/licenses/LICENSE-2.0 93aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * 103aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * Unless required by applicable law or agreed to in writing, software 113aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * distributed under the License is distributed on an "AS IS" BASIS, 123aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 133aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * See the License for the specific language governing permissions and 143aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * limitations under the License. 153aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn */ 163aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 173aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackbornpackage android.content; 183aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 193aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackbornimport android.os.Parcel; 203aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackbornimport android.os.Parcelable; 213aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackbornimport android.os.ParcelableParcel; 223aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackbornimport android.text.TextUtils; 233aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 243aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackbornimport java.util.ArrayList; 253aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackbornimport java.util.HashMap; 263aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 273aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn/** 283aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * Top-level class for managing and interacting with the global undo state for 293aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * a document or application. This class supports both undo and redo and has 303aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * helpers for merging undoable operations together as they are performed. 313aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * 323aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * <p>A single undoable operation is represented by {@link UndoOperation} which 333aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * apps implement to define their undo/redo behavior. The UndoManager keeps 343aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * a stack of undo states; each state can have one or more undo operations 353aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * inside of it.</p> 363aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * 373aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * <p>Updates to the stack must be done inside of a {@link #beginUpdate}/{@link #endUpdate()} 383aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * pair. During this time you can add new operations to the stack with 393aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * {@link #addOperation}, retrieve and modify existing operations with 403aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * {@link #getLastOperation}, control the label shown to the user for this operation 413aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * with {@link #setUndoLabel} and {@link #suggestUndoLabel}, etc.</p> 423aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * 433aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * <p>Every {link UndoOperation} is associated with an {@link UndoOwner}, which identifies 443aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * the data it belongs to. The owner is used to indicate how operations are dependent 453aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * on each other -- operations with the same owner are dependent on others with the 463aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * same owner. For example, you may have a document with multiple embedded objects. If the 473aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * document itself and each embedded object use different owners, then you 483aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * can provide undo semantics appropriate to the user's context: while within 493aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * an embedded object, only edits to that object are seen and the user can 503aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * undo/redo them without needing to impact edits in other objects; while 513aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * within the larger document, all edits can be seen and the user must 523aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * undo/redo them as a single stream.</p> 53b811e64cb325c8b9c46a2e8e97ef1aa86ac8664bDianne Hackborn * 54b811e64cb325c8b9c46a2e8e97ef1aa86ac8664bDianne Hackborn * @hide 553aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn */ 563aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackbornpublic class UndoManager { 573aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn private final HashMap<String, UndoOwner> mOwners = new HashMap<String, UndoOwner>(); 583aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn private final ArrayList<UndoState> mUndos = new ArrayList<UndoState>(); 593aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn private final ArrayList<UndoState> mRedos = new ArrayList<UndoState>(); 603aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn private int mUpdateCount; 613aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn private int mHistorySize = 20; 623aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn private UndoState mWorking; 633aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn private int mCommitId = 1; 643aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn private boolean mInUndo; 653aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn private boolean mMerged; 663aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 673aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn private int mStateSeq; 683aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn private int mNextSavedIdx; 693aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn private UndoOwner[] mStateOwners; 703aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 713aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn /** 723aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * Never merge with the last undo state. 733aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn */ 743aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn public static final int MERGE_MODE_NONE = 0; 753aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 763aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn /** 773aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * Allow merge with the last undo state only if it contains 783aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * operations with the caller's owner. 793aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn */ 803aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn public static final int MERGE_MODE_UNIQUE = 1; 813aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 823aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn /** 833aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * Always allow merge with the last undo state, if possible. 843aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn */ 853aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn public static final int MERGE_MODE_ANY = 2; 863aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 873aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn public UndoOwner getOwner(String tag, Object data) { 883aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (tag == null) { 893aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn throw new NullPointerException("tag can't be null"); 903aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 913aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (data == null) { 923aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn throw new NullPointerException("data can't be null"); 933aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 943aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn UndoOwner owner = mOwners.get(tag); 953aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (owner != null) { 963aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (owner.mData != data) { 973aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (owner.mData != null) { 983aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn throw new IllegalStateException("Owner " + owner + " already exists with data " 993aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn + owner.mData + " but giving different data " + data); 1003aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 1013aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn owner.mData = data; 1023aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 1033aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return owner; 1043aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 1053aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 1063aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn owner = new UndoOwner(tag); 1073aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn owner.mManager = this; 1083aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn owner.mData = data; 1093aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mOwners.put(tag, owner); 1103aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return owner; 1113aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 1123aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 1133aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn void removeOwner(UndoOwner owner) { 1143aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn // XXX need to figure out how to prune. 1153aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (false) { 1163aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mOwners.remove(owner.mTag); 1173aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn owner.mManager = null; 1183aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 1193aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 1203aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 1213aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn /** 1223aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * Flatten the current undo state into a Parcelable object, which can later be restored 1233aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * with {@link #restoreInstanceState(android.os.Parcelable)}. 1243aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn */ 1253aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn public Parcelable saveInstanceState() { 1263aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (mUpdateCount > 0) { 1273aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn throw new IllegalStateException("Can't save state while updating"); 1283aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 1293aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn ParcelableParcel pp = new ParcelableParcel(getClass().getClassLoader()); 1303aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn Parcel p = pp.getParcel(); 1313aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mStateSeq++; 1323aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (mStateSeq <= 0) { 1333aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mStateSeq = 0; 1343aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 1353aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mNextSavedIdx = 0; 1363aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn p.writeInt(mHistorySize); 1373aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn p.writeInt(mOwners.size()); 1383aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn // XXX eventually we need to be smart here about limiting the 1393aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn // number of undo states we write to not exceed X bytes. 1403aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn int i = mUndos.size(); 1413aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn while (i > 0) { 1423aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn p.writeInt(1); 1433aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn i--; 1443aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mUndos.get(i).writeToParcel(p); 1453aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 1463aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn i = mRedos.size(); 1473aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn p.writeInt(i); 1483aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn while (i > 0) { 1493aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn p.writeInt(2); 1503aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn i--; 1513aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mRedos.get(i).writeToParcel(p); 1523aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 1533aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn p.writeInt(0); 1543aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return pp; 1553aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 1563aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 1573aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn void saveOwner(UndoOwner owner, Parcel out) { 1583aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (owner.mStateSeq == mStateSeq) { 1593aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn out.writeInt(owner.mSavedIdx); 1603aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } else { 1613aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn owner.mStateSeq = mStateSeq; 1623aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn owner.mSavedIdx = mNextSavedIdx; 1633aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn out.writeInt(owner.mSavedIdx); 1643aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn out.writeString(owner.mTag); 1653aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mNextSavedIdx++; 1663aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 1673aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 1683aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 1693aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn /** 1703aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * Restore an undo state previously created with {@link #saveInstanceState()}. This will 1713aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * restore the UndoManager's state to almost exactly what it was at the point it had 1723aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * been previously saved; the only information not restored is the data object 1733aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * associated with each {@link UndoOwner}, which requires separate calls to 1743aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * {@link #getOwner(String, Object)} to re-associate the owner with its data. 1753aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn */ 1763aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn public void restoreInstanceState(Parcelable state) { 1773aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (mUpdateCount > 0) { 1783aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn throw new IllegalStateException("Can't save state while updating"); 1793aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 1803aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn forgetUndos(null, -1); 1813aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn forgetRedos(null, -1); 1823aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn ParcelableParcel pp = (ParcelableParcel)state; 1833aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn Parcel p = pp.getParcel(); 1843aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mHistorySize = p.readInt(); 1853aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mStateOwners = new UndoOwner[p.readInt()]; 1863aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 1873aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn int stype; 1883aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn while ((stype=p.readInt()) != 0) { 1893aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn UndoState ustate = new UndoState(this, p, pp.getClassLoader()); 1903aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (stype == 1) { 1913aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mUndos.add(0, ustate); 1923aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } else { 1933aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mRedos.add(0, ustate); 1943aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 1953aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 1963aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 1973aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 1983aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn UndoOwner restoreOwner(Parcel in) { 1993aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn int idx = in.readInt(); 2003aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn UndoOwner owner = mStateOwners[idx]; 2013aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (owner == null) { 2023aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn String tag = in.readString(); 2033aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn owner = new UndoOwner(tag); 2043aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mStateOwners[idx] = owner; 2053aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mOwners.put(tag, owner); 2063aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 2073aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return owner; 2083aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 2093aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 2103aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn /** 2113aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * Set the maximum number of undo states that will be retained. 2123aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn */ 2133aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn public void setHistorySize(int size) { 2143aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mHistorySize = size; 2153aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (mHistorySize >= 0 && countUndos(null) > mHistorySize) { 2163aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn forgetUndos(null, countUndos(null) - mHistorySize); 2173aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 2183aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 2193aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 2203aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn /** 2213aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * Return the current maximum number of undo states. 2223aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn */ 2233aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn public int getHistorySize() { 2243aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return mHistorySize; 2253aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 2263aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 2273aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn /** 2283aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * Perform undo of last/top <var>count</var> undo states. The states impacted 2293aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * by this can be limited through <var>owners</var>. 2303aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * @param owners Optional set of owners that should be impacted. If null, all 2313aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * undo states will be visible and available for undo. If non-null, only those 2323aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * states that contain one of the owners specified here will be visible. 2333aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * @param count Number of undo states to pop. 2343aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * @return Returns the number of undo states that were actually popped. 2353aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn */ 2363aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn public int undo(UndoOwner[] owners, int count) { 2373aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (mWorking != null) { 2383aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn throw new IllegalStateException("Can't be called during an update"); 2393aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 2403aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 2413aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn int num = 0; 2423aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn int i = -1; 2433aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 2443aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mInUndo = true; 2453aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 2463aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn UndoState us = getTopUndo(null); 2473aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (us != null) { 2483aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn us.makeExecuted(); 2493aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 2503aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 2513aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn while (count > 0 && (i=findPrevState(mUndos, owners, i)) >= 0) { 2523aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn UndoState state = mUndos.remove(i); 2533aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn state.undo(); 2543aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mRedos.add(state); 2553aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn count--; 2563aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn num++; 2573aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 2583aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 2593aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mInUndo = false; 2603aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 2613aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return num; 2623aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 2633aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 2643aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn /** 2653aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * Perform redo of last/top <var>count</var> undo states in the transient redo stack. 2663aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * The states impacted by this can be limited through <var>owners</var>. 2673aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * @param owners Optional set of owners that should be impacted. If null, all 2683aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * undo states will be visible and available for undo. If non-null, only those 2693aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * states that contain one of the owners specified here will be visible. 2703aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * @param count Number of undo states to pop. 2713aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * @return Returns the number of undo states that were actually redone. 2723aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn */ 2733aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn public int redo(UndoOwner[] owners, int count) { 2743aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (mWorking != null) { 2753aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn throw new IllegalStateException("Can't be called during an update"); 2763aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 2773aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 2783aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn int num = 0; 2793aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn int i = -1; 2803aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 2813aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mInUndo = true; 2823aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 2833aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn while (count > 0 && (i=findPrevState(mRedos, owners, i)) >= 0) { 2843aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn UndoState state = mRedos.remove(i); 2853aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn state.redo(); 2863aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mUndos.add(state); 2873aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn count--; 2883aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn num++; 2893aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 2903aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 2913aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mInUndo = false; 2923aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 2933aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return num; 2943aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 2953aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 2963aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn /** 2973aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * Returns true if we are currently inside of an undo/redo operation. This is 2983aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * useful for editors to know whether they should be generating new undo state 2993aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * when they see edit operations happening. 3003aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn */ 3013aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn public boolean isInUndo() { 3023aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return mInUndo; 3033aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 3043aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 3053aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn public int forgetUndos(UndoOwner[] owners, int count) { 3063aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (count < 0) { 3073aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn count = mUndos.size(); 3083aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 3093aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 3103aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn int removed = 0; 3113aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn for (int i=0; i<mUndos.size() && removed < count; i++) { 3123aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn UndoState state = mUndos.get(i); 3133aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (count > 0 && matchOwners(state, owners)) { 3143aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn state.destroy(); 3153aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mUndos.remove(i); 3163aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn removed++; 3173aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 3183aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 3193aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 3203aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return removed; 3213aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 3223aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 3233aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn public int forgetRedos(UndoOwner[] owners, int count) { 3243aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (count < 0) { 3253aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn count = mRedos.size(); 3263aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 3273aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 3283aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn int removed = 0; 3293aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn for (int i=0; i<mRedos.size() && removed < count; i++) { 3303aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn UndoState state = mRedos.get(i); 3313aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (count > 0 && matchOwners(state, owners)) { 3323aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn state.destroy(); 3333aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mRedos.remove(i); 3343aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn removed++; 3353aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 3363aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 3373aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 3383aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return removed; 3393aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 3403aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 3413aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn /** 3423aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * Return the number of undo states on the undo stack. 3433aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * @param owners If non-null, only those states containing an operation with one of 3443aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * the owners supplied here will be counted. 3453aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn */ 3463aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn public int countUndos(UndoOwner[] owners) { 3473aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (owners == null) { 3483aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return mUndos.size(); 3493aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 3503aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 3513aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn int count=0; 3523aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn int i=0; 3533aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn while ((i=findNextState(mUndos, owners, i)) >= 0) { 3543aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn count++; 3553aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn i++; 3563aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 3573aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return count; 3583aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 3593aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 3603aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn /** 3613aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * Return the number of redo states on the undo stack. 3623aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * @param owners If non-null, only those states containing an operation with one of 3633aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * the owners supplied here will be counted. 3643aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn */ 3653aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn public int countRedos(UndoOwner[] owners) { 3663aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (owners == null) { 3673aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return mRedos.size(); 3683aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 3693aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 3703aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn int count=0; 3713aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn int i=0; 3723aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn while ((i=findNextState(mRedos, owners, i)) >= 0) { 3733aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn count++; 3743aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn i++; 3753aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 3763aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return count; 3773aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 3783aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 3793aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn /** 3803aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * Return the user-visible label for the top undo state on the stack. 3813aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * @param owners If non-null, will select the top-most undo state containing an 3823aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * operation with one of the owners supplied here. 3833aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn */ 3843aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn public CharSequence getUndoLabel(UndoOwner[] owners) { 3853aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn UndoState state = getTopUndo(owners); 3863aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return state != null ? state.getLabel() : null; 3873aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 3883aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 3893aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn /** 3903aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * Return the user-visible label for the top redo state on the stack. 3913aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * @param owners If non-null, will select the top-most undo state containing an 3923aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * operation with one of the owners supplied here. 3933aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn */ 3943aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn public CharSequence getRedoLabel(UndoOwner[] owners) { 3953aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn UndoState state = getTopRedo(owners); 3963aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return state != null ? state.getLabel() : null; 3973aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 3983aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 3993aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn /** 4003aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * Start creating a new undo state. Multiple calls to this function will nest until 4013aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * they are all matched by a later call to {@link #endUpdate}. 4023aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * @param label Optional user-visible label for this new undo state. 4033aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn */ 4043aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn public void beginUpdate(CharSequence label) { 4053aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (mInUndo) { 4063aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn throw new IllegalStateException("Can't being update while performing undo/redo"); 4073aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 4083aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (mUpdateCount <= 0) { 4093aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn createWorkingState(); 4103aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mMerged = false; 4113aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mUpdateCount = 0; 4123aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 4133aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 4143aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mWorking.updateLabel(label); 4153aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mUpdateCount++; 4163aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 4173aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 4183aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn private void createWorkingState() { 4193aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mWorking = new UndoState(this, mCommitId++); 4203aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (mCommitId < 0) { 4213aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mCommitId = 1; 4223aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 4233aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 4243aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 4253aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn /** 4263aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * Returns true if currently inside of a {@link #beginUpdate}. 4273aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn */ 4283aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn public boolean isInUpdate() { 4293aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return mUpdateCount > 0; 4303aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 4313aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 4323aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn /** 4333aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * Forcibly set a new for the new undo state being built within a {@link #beginUpdate}. 4343aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * Any existing label will be replaced with this one. 4353aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn */ 4363aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn public void setUndoLabel(CharSequence label) { 4373aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (mWorking == null) { 4383aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn throw new IllegalStateException("Must be called during an update"); 4393aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 4403aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mWorking.setLabel(label); 4413aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 4423aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 4433aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn /** 4443aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * Set a new for the new undo state being built within a {@link #beginUpdate}, but 4453aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * only if there is not a label currently set for it. 4463aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn */ 4473aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn public void suggestUndoLabel(CharSequence label) { 4483aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (mWorking == null) { 4493aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn throw new IllegalStateException("Must be called during an update"); 4503aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 4513aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mWorking.updateLabel(label); 4523aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 4533aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 4543aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn /** 4553aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * Return the number of times {@link #beginUpdate} has been called without a matching 4563aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * {@link #endUpdate} call. 4573aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn */ 4583aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn public int getUpdateNestingLevel() { 4593aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return mUpdateCount; 4603aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 4613aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 4623aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn /** 4633aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * Check whether there is an {@link UndoOperation} in the current {@link #beginUpdate} 4643aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * undo state. 4653aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * @param owner Optional owner of the operation to look for. If null, will succeed 4663aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * if there is any operation; if non-null, will only succeed if there is an operation 4673aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * with the given owner. 4683aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * @return Returns true if there is a matching operation in the current undo state. 4693aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn */ 4703aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn public boolean hasOperation(UndoOwner owner) { 4713aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (mWorking == null) { 4723aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn throw new IllegalStateException("Must be called during an update"); 4733aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 4743aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return mWorking.hasOperation(owner); 4753aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 4763aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 4773aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn /** 4783aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * Return the most recent {@link UndoOperation} that was added to the update. 4793aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * @param mergeMode May be either {@link #MERGE_MODE_NONE} or {@link #MERGE_MODE_ANY}. 4803aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn */ 4813aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn public UndoOperation<?> getLastOperation(int mergeMode) { 4823aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return getLastOperation(null, null, mergeMode); 4833aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 4843aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 4853aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn /** 4863aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * Return the most recent {@link UndoOperation} that was added to the update and 4873aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * has the given owner. 4883aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * @param owner Optional owner of last operation to retrieve. If null, the last 4893aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * operation regardless of owner will be retrieved; if non-null, the last operation 4903aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * matching the given owner will be retrieved. 4913aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * @param mergeMode May be either {@link #MERGE_MODE_NONE}, {@link #MERGE_MODE_UNIQUE}, 4923aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * or {@link #MERGE_MODE_ANY}. 4933aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn */ 4943aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn public UndoOperation<?> getLastOperation(UndoOwner owner, int mergeMode) { 4953aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return getLastOperation(null, owner, mergeMode); 4963aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 4973aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 4983aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn /** 4993aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * Return the most recent {@link UndoOperation} that was added to the update and 5003aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * has the given owner. 5013aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * @param clazz Optional class of the last operation to retrieve. If null, the 5023aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * last operation regardless of class will be retrieved; if non-null, the last 5033aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * operation whose class is the same as the given class will be retrieved. 5043aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * @param owner Optional owner of last operation to retrieve. If null, the last 5053aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * operation regardless of owner will be retrieved; if non-null, the last operation 5063aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * matching the given owner will be retrieved. 5073aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * @param mergeMode May be either {@link #MERGE_MODE_NONE}, {@link #MERGE_MODE_UNIQUE}, 5083aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * or {@link #MERGE_MODE_ANY}. 5093aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn */ 5103aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn public <T extends UndoOperation> T getLastOperation(Class<T> clazz, UndoOwner owner, 5113aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn int mergeMode) { 5123aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (mWorking == null) { 5133aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn throw new IllegalStateException("Must be called during an update"); 5143aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 5153aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (mergeMode != MERGE_MODE_NONE && !mMerged && !mWorking.hasData()) { 5163aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn UndoState state = getTopUndo(null); 5173aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn UndoOperation<?> last; 5183aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (state != null && (mergeMode == MERGE_MODE_ANY || !state.hasMultipleOwners()) 5193aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn && state.canMerge() && (last=state.getLastOperation(clazz, owner)) != null) { 5203aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (last.allowMerge()) { 5213aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mWorking.destroy(); 5223aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mWorking = state; 5233aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mUndos.remove(state); 5243aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mMerged = true; 5253aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return (T)last; 5263aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 5273aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 5283aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 5293aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 5303aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return mWorking.getLastOperation(clazz, owner); 5313aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 5323aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 5333aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn /** 5343aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * Add a new UndoOperation to the current update. 5353aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * @param op The new operation to add. 5363aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * @param mergeMode May be either {@link #MERGE_MODE_NONE}, {@link #MERGE_MODE_UNIQUE}, 5373aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * or {@link #MERGE_MODE_ANY}. 5383aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn */ 5393aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn public void addOperation(UndoOperation<?> op, int mergeMode) { 5403aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (mWorking == null) { 5413aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn throw new IllegalStateException("Must be called during an update"); 5423aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 5433aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn UndoOwner owner = op.getOwner(); 5443aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (owner.mManager != this) { 5453aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn throw new IllegalArgumentException( 5463aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn "Given operation's owner is not in this undo manager."); 5473aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 5483aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (mergeMode != MERGE_MODE_NONE && !mMerged && !mWorking.hasData()) { 5493aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn UndoState state = getTopUndo(null); 5503aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (state != null && (mergeMode == MERGE_MODE_ANY || !state.hasMultipleOwners()) 5513aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn && state.canMerge() && state.hasOperation(op.getOwner())) { 5523aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mWorking.destroy(); 5533aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mWorking = state; 5543aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mUndos.remove(state); 5553aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mMerged = true; 5563aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 5573aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 5583aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mWorking.addOperation(op); 5593aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 5603aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 5613aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn /** 5623aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * Finish the creation of an undo state, matching a previous call to 5633aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * {@link #beginUpdate}. 5643aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn */ 5653aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn public void endUpdate() { 5663aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (mWorking == null) { 5673aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn throw new IllegalStateException("Must be called during an update"); 5683aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 5693aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mUpdateCount--; 5703aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 5713aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (mUpdateCount == 0) { 5723aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn pushWorkingState(); 5733aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 5743aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 5753aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 5763aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn private void pushWorkingState() { 5773aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn int N = mUndos.size() + 1; 5783aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 5793aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (mWorking.hasData()) { 5803aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mUndos.add(mWorking); 5813aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn forgetRedos(null, -1); 5823aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mWorking.commit(); 5833aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (N >= 2) { 5843aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn // The state before this one can no longer be merged, ever. 5853aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn // The only way to get back to it is for the user to perform 5863aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn // an undo. 5873aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mUndos.get(N-2).makeExecuted(); 5883aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 5893aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } else { 5903aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mWorking.destroy(); 5913aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 5923aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mWorking = null; 5933aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 5943aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (mHistorySize >= 0 && N > mHistorySize) { 5953aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn forgetUndos(null, N - mHistorySize); 5963aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 5973aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 5983aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 5993aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn /** 6003aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * Commit the last finished undo state. This undo state can no longer be 6013aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * modified with further {@link #MERGE_MODE_UNIQUE} or 6023aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * {@link #MERGE_MODE_ANY} merge modes. If called while inside of an update, 6033aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * this will push any changes in the current update on to the undo stack 6043aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * and result with a fresh undo state, behaving as if {@link #endUpdate()} 6053aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * had been called enough to unwind the current update, then the last state 6063aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * committed, and {@link #beginUpdate} called to restore the update nesting. 6073aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * @param owner The optional owner to determine whether to perform the commit. 6083aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * If this is non-null, the commit will only execute if the current top undo 6093aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * state contains an operation with the given owner. 6103aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * @return Returns an integer identifier for the committed undo state, which 6113aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * can later be used to try to uncommit the state to perform further edits on it. 6123aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn */ 6133aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn public int commitState(UndoOwner owner) { 6143aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (mWorking != null && mWorking.hasData()) { 6153aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (owner == null || mWorking.hasOperation(owner)) { 6163aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mWorking.setCanMerge(false); 6173aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn int commitId = mWorking.getCommitId(); 6183aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn pushWorkingState(); 6193aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn createWorkingState(); 6203aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mMerged = true; 6213aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return commitId; 6223aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 6233aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } else { 6243aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn UndoState state = getTopUndo(null); 6253aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (state != null && (owner == null || state.hasOperation(owner))) { 6263aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn state.setCanMerge(false); 6273aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return state.getCommitId(); 6283aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 6293aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 6303aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return -1; 6313aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 6323aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 6333aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn /** 6343aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * Attempt to undo a previous call to {@link #commitState}. This will work 6353aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * if the undo state at the top of the stack has the given id, and has not been 6363aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * involved in an undo operation. Otherwise false is returned. 6373aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * @param commitId The identifier for the state to be uncommitted, as returned 6383aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * by {@link #commitState}. 6393aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * @param owner Optional owner that must appear in the committed state. 6403aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn * @return Returns true if the uncommit is successful, else false. 6413aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn */ 6423aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn public boolean uncommitState(int commitId, UndoOwner owner) { 6433aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (mWorking != null && mWorking.getCommitId() == commitId) { 6443aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (owner == null || mWorking.hasOperation(owner)) { 6453aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return mWorking.setCanMerge(true); 6463aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 6473aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } else { 6483aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn UndoState state = getTopUndo(null); 6493aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (state != null && (owner == null || state.hasOperation(owner))) { 6503aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (state.getCommitId() == commitId) { 6513aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return state.setCanMerge(true); 6523aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 6533aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 6543aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 6553aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return false; 6563aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 6573aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 6583aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn UndoState getTopUndo(UndoOwner[] owners) { 6593aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (mUndos.size() <= 0) { 6603aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return null; 6613aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 6623aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn int i = findPrevState(mUndos, owners, -1); 6633aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return i >= 0 ? mUndos.get(i) : null; 6643aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 6653aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 6663aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn UndoState getTopRedo(UndoOwner[] owners) { 6673aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (mRedos.size() <= 0) { 6683aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return null; 6693aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 6703aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn int i = findPrevState(mRedos, owners, -1); 6713aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return i >= 0 ? mRedos.get(i) : null; 6723aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 6733aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 6743aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn boolean matchOwners(UndoState state, UndoOwner[] owners) { 6753aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (owners == null) { 6763aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return true; 6773aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 6783aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn for (int i=0; i<owners.length; i++) { 6793aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (state.matchOwner(owners[i])) { 6803aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return true; 6813aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 6823aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 6833aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return false; 6843aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 6853aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 6863aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn int findPrevState(ArrayList<UndoState> states, UndoOwner[] owners, int from) { 6873aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn final int N = states.size(); 6883aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 6893aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (from == -1) { 6903aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn from = N-1; 6913aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 6923aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (from >= N) { 6933aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return -1; 6943aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 6953aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (owners == null) { 6963aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return from; 6973aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 6983aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 6993aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn while (from >= 0) { 7003aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn UndoState state = states.get(from); 7013aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (matchOwners(state, owners)) { 7023aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return from; 7033aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 7043aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn from--; 7053aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 7063aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 7073aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return -1; 7083aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 7093aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 7103aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn int findNextState(ArrayList<UndoState> states, UndoOwner[] owners, int from) { 7113aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn final int N = states.size(); 7123aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 7133aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (from < 0) { 7143aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn from = 0; 7153aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 7163aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (from >= N) { 7173aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return -1; 7183aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 7193aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (owners == null) { 7203aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return from; 7213aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 7223aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 7233aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn while (from < N) { 7243aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn UndoState state = states.get(from); 7253aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (matchOwners(state, owners)) { 7263aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return from; 7273aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 7283aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn from++; 7293aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 7303aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 7313aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return -1; 7323aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 7333aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 7343aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn final static class UndoState { 7353aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn private final UndoManager mManager; 7363aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn private final int mCommitId; 7373aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn private final ArrayList<UndoOperation<?>> mOperations = new ArrayList<UndoOperation<?>>(); 7383aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn private ArrayList<UndoOperation<?>> mRecent; 7393aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn private CharSequence mLabel; 7403aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn private boolean mCanMerge = true; 7413aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn private boolean mExecuted; 7423aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 7433aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn UndoState(UndoManager manager, int commitId) { 7443aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mManager = manager; 7453aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mCommitId = commitId; 7463aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 7473aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 7483aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn UndoState(UndoManager manager, Parcel p, ClassLoader loader) { 7493aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mManager = manager; 7503aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mCommitId = p.readInt(); 7513aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mCanMerge = p.readInt() != 0; 7523aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mExecuted = p.readInt() != 0; 7533aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(p); 7543aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn final int N = p.readInt(); 7553aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn for (int i=0; i<N; i++) { 7563aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn UndoOwner owner = mManager.restoreOwner(p); 7573aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn UndoOperation op = (UndoOperation)p.readParcelable(loader); 7583aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn op.mOwner = owner; 7593aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mOperations.add(op); 7603aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 7613aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 7623aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 7633aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn void writeToParcel(Parcel p) { 7643aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (mRecent != null) { 7653aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn throw new IllegalStateException("Can't save state before committing"); 7663aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 7673aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn p.writeInt(mCommitId); 7683aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn p.writeInt(mCanMerge ? 1 : 0); 7693aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn p.writeInt(mExecuted ? 1 : 0); 7703aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn TextUtils.writeToParcel(mLabel, p, 0); 7713aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn final int N = mOperations.size(); 7723aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn p.writeInt(N); 7733aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn for (int i=0; i<N; i++) { 7743aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn UndoOperation op = mOperations.get(i); 7753aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mManager.saveOwner(op.mOwner, p); 7763aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn p.writeParcelable(op, 0); 7773aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 7783aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 7793aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 7803aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn int getCommitId() { 7813aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return mCommitId; 7823aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 7833aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 7843aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn void setLabel(CharSequence label) { 7853aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mLabel = label; 7863aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 7873aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 7883aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn void updateLabel(CharSequence label) { 7893aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (mLabel != null) { 7903aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mLabel = label; 7913aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 7923aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 7933aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 7943aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn CharSequence getLabel() { 7953aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return mLabel; 7963aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 7973aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 7983aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn boolean setCanMerge(boolean state) { 7993aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn // Don't allow re-enabling of merging if state has been executed. 8003aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (state && mExecuted) { 8013aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return false; 8023aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 8033aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mCanMerge = state; 8043aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return true; 8053aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 8063aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 8073aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn void makeExecuted() { 8083aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mExecuted = true; 8093aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 8103aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 8113aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn boolean canMerge() { 8123aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return mCanMerge && !mExecuted; 8133aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 8143aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 8153aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn int countOperations() { 8163aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return mOperations.size(); 8173aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 8183aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 8193aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn boolean hasOperation(UndoOwner owner) { 8203aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn final int N = mOperations.size(); 8213aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (owner == null) { 8223aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return N != 0; 8233aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 8243aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn for (int i=0; i<N; i++) { 8253aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (mOperations.get(i).getOwner() == owner) { 8263aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return true; 8273aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 8283aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 8293aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return false; 8303aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 8313aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 8323aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn boolean hasMultipleOwners() { 8333aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn final int N = mOperations.size(); 8343aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (N <= 1) { 8353aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return false; 8363aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 8373aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn UndoOwner owner = mOperations.get(0).getOwner(); 8383aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn for (int i=1; i<N; i++) { 8393aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (mOperations.get(i).getOwner() != owner) { 8403aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return true; 8413aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 8423aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 8433aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return false; 8443aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 8453aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 8463aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn void addOperation(UndoOperation<?> op) { 8473aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (mOperations.contains(op)) { 8483aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn throw new IllegalStateException("Already holds " + op); 8493aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 8503aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mOperations.add(op); 8513aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (mRecent == null) { 8523aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mRecent = new ArrayList<UndoOperation<?>>(); 8533aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mRecent.add(op); 8543aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 8553aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn op.mOwner.mOpCount++; 8563aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 8573aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 8583aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn <T extends UndoOperation> T getLastOperation(Class<T> clazz, UndoOwner owner) { 8593aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn final int N = mOperations.size(); 8603aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (clazz == null && owner == null) { 8613aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return N > 0 ? (T)mOperations.get(N-1) : null; 8623aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 8633aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn // First look for the top-most operation with the same owner. 8643aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn for (int i=N-1; i>=0; i--) { 8653aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn UndoOperation<?> op = mOperations.get(i); 8663aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (owner != null && op.getOwner() != owner) { 8673aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn continue; 8683aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 8693aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn // Return this operation if it has the same class that the caller wants. 8703aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn // Note that we don't search deeper for the class, because we don't want 8713aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn // to end up with a different order of operations for the same owner. 8723aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (clazz != null && op.getClass() != clazz) { 8733aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return null; 8743aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 8753aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return (T)op; 8763aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 8773aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 8783aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return null; 8793aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 8803aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 8813aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn boolean matchOwner(UndoOwner owner) { 8823aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn for (int i=mOperations.size()-1; i>=0; i--) { 8833aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (mOperations.get(i).matchOwner(owner)) { 8843aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return true; 8853aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 8863aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 8873aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return false; 8883aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 8893aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 8903aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn boolean hasData() { 8913aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn for (int i=mOperations.size()-1; i>=0; i--) { 8923aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (mOperations.get(i).hasData()) { 8933aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return true; 8943aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 8953aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 8963aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn return false; 8973aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 8983aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 8993aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn void commit() { 9003aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn final int N = mRecent != null ? mRecent.size() : 0; 9013aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn for (int i=0; i<N; i++) { 9023aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mRecent.get(i).commit(); 9033aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 9043aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mRecent = null; 9053aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 9063aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 9073aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn void undo() { 9083aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn for (int i=mOperations.size()-1; i>=0; i--) { 9093aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mOperations.get(i).undo(); 9103aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 9113aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 9123aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 9133aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn void redo() { 9143aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn final int N = mOperations.size(); 9153aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn for (int i=0; i<N; i++) { 9163aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mOperations.get(i).redo(); 9173aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 9183aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 9193aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn 9203aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn void destroy() { 9213aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn for (int i=mOperations.size()-1; i>=0; i--) { 9223aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn UndoOwner owner = mOperations.get(i).mOwner; 9233aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn owner.mOpCount--; 9243aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (owner.mOpCount <= 0) { 9253aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn if (owner.mOpCount < 0) { 9263aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn throw new IllegalStateException("Underflow of op count on owner " + owner 9273aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn + " in op " + mOperations.get(i)); 9283aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 9293aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn mManager.removeOwner(owner); 9303aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 9313aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 9323aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 9333aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn } 9343aa49b6fece334ace7525d42c1f6d0b7cdc1fbfbDianne Hackborn} 935