aboutsummaryrefslogtreecommitdiff
path: root/src/share/classes/javax/swing/text/GapContent.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/share/classes/javax/swing/text/GapContent.java')
-rw-r--r--src/share/classes/javax/swing/text/GapContent.java954
1 files changed, 954 insertions, 0 deletions
diff --git a/src/share/classes/javax/swing/text/GapContent.java b/src/share/classes/javax/swing/text/GapContent.java
new file mode 100644
index 000000000..23db31c74
--- /dev/null
+++ b/src/share/classes/javax/swing/text/GapContent.java
@@ -0,0 +1,954 @@
+/*
+ * Copyright 1998-2003 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package javax.swing.text;
+
+import java.util.Vector;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+import javax.swing.undo.AbstractUndoableEdit;
+import javax.swing.undo.CannotRedoException;
+import javax.swing.undo.CannotUndoException;
+import javax.swing.undo.UndoableEdit;
+import javax.swing.SwingUtilities;
+import java.lang.ref.WeakReference;
+import java.lang.ref.ReferenceQueue;
+
+/**
+ * An implementation of the AbstractDocument.Content interface
+ * implemented using a gapped buffer similar to that used by emacs.
+ * The underlying storage is a array of unicode characters with
+ * a gap somewhere. The gap is moved to the location of changes
+ * to take advantage of common behavior where most changes are
+ * in the same location. Changes that occur at a gap boundary are
+ * generally cheap and moving the gap is generally cheaper than
+ * moving the array contents directly to accomodate the change.
+ * <p>
+ * The positions tracking change are also generally cheap to
+ * maintain. The Position implementations (marks) store the array
+ * index and can easily calculate the sequential position from
+ * the current gap location. Changes only require update to the
+ * the marks between the old and new gap boundaries when the gap
+ * is moved, so generally updating the marks is pretty cheap.
+ * The marks are stored sorted so they can be located quickly
+ * with a binary search. This increases the cost of adding a
+ * mark, and decreases the cost of keeping the mark updated.
+ *
+ * @author Timothy Prinzing
+ */
+public class GapContent extends GapVector implements AbstractDocument.Content, Serializable {
+
+ /**
+ * Creates a new GapContent object. Initial size defaults to 10.
+ */
+ public GapContent() {
+ this(10);
+ }
+
+ /**
+ * Creates a new GapContent object, with the initial
+ * size specified. The initial size will not be allowed
+ * to go below 2, to give room for the implied break and
+ * the gap.
+ *
+ * @param initialLength the initial size
+ */
+ public GapContent(int initialLength) {
+ super(Math.max(initialLength,2));
+ char[] implied = new char[1];
+ implied[0] = '\n';
+ replace(0, 0, implied, implied.length);
+
+ marks = new MarkVector();
+ search = new MarkData(0);
+ queue = new ReferenceQueue();
+ }
+
+ /**
+ * Allocate an array to store items of the type
+ * appropriate (which is determined by the subclass).
+ */
+ protected Object allocateArray(int len) {
+ return new char[len];
+ }
+
+ /**
+ * Get the length of the allocated array.
+ */
+ protected int getArrayLength() {
+ char[] carray = (char[]) getArray();
+ return carray.length;
+ }
+
+ // --- AbstractDocument.Content methods -------------------------
+
+ /**
+ * Returns the length of the content.
+ *
+ * @return the length >= 1
+ * @see AbstractDocument.Content#length
+ */
+ public int length() {
+ int len = getArrayLength() - (getGapEnd() - getGapStart());
+ return len;
+ }
+
+ /**
+ * Inserts a string into the content.
+ *
+ * @param where the starting position >= 0, < length()
+ * @param str the non-null string to insert
+ * @return an UndoableEdit object for undoing
+ * @exception BadLocationException if the specified position is invalid
+ * @see AbstractDocument.Content#insertString
+ */
+ public UndoableEdit insertString(int where, String str) throws BadLocationException {
+ if (where > length() || where < 0) {
+ throw new BadLocationException("Invalid insert", length());
+ }
+ char[] chars = str.toCharArray();
+ replace(where, 0, chars, chars.length);
+ return new InsertUndo(where, str.length());
+ }
+
+ /**
+ * Removes part of the content.
+ *
+ * @param where the starting position >= 0, where + nitems < length()
+ * @param nitems the number of characters to remove >= 0
+ * @return an UndoableEdit object for undoing
+ * @exception BadLocationException if the specified position is invalid
+ * @see AbstractDocument.Content#remove
+ */
+ public UndoableEdit remove(int where, int nitems) throws BadLocationException {
+ if (where + nitems >= length()) {
+ throw new BadLocationException("Invalid remove", length() + 1);
+ }
+ String removedString = getString(where, nitems);
+ UndoableEdit edit = new RemoveUndo(where, removedString);
+ replace(where, nitems, empty, 0);
+ return edit;
+
+ }
+
+ /**
+ * Retrieves a portion of the content.
+ *
+ * @param where the starting position >= 0
+ * @param len the length to retrieve >= 0
+ * @return a string representing the content
+ * @exception BadLocationException if the specified position is invalid
+ * @see AbstractDocument.Content#getString
+ */
+ public String getString(int where, int len) throws BadLocationException {
+ Segment s = new Segment();
+ getChars(where, len, s);
+ return new String(s.array, s.offset, s.count);
+ }
+
+ /**
+ * Retrieves a portion of the content. If the desired content spans
+ * the gap, we copy the content. If the desired content does not
+ * span the gap, the actual store is returned to avoid the copy since
+ * it is contiguous.
+ *
+ * @param where the starting position >= 0, where + len <= length()
+ * @param len the number of characters to retrieve >= 0
+ * @param chars the Segment object to return the characters in
+ * @exception BadLocationException if the specified position is invalid
+ * @see AbstractDocument.Content#getChars
+ */
+ public void getChars(int where, int len, Segment chars) throws BadLocationException {
+ int end = where + len;
+ if (where < 0 || end < 0) {
+ throw new BadLocationException("Invalid location", -1);
+ }
+ if (end > length() || where > length()) {
+ throw new BadLocationException("Invalid location", length() + 1);
+ }
+ int g0 = getGapStart();
+ int g1 = getGapEnd();
+ char[] array = (char[]) getArray();
+ if ((where + len) <= g0) {
+ // below gap
+ chars.array = array;
+ chars.offset = where;
+ } else if (where >= g0) {
+ // above gap
+ chars.array = array;
+ chars.offset = g1 + where - g0;
+ } else {
+ // spans the gap
+ int before = g0 - where;
+ if (chars.isPartialReturn()) {
+ // partial return allowed, return amount before the gap
+ chars.array = array;
+ chars.offset = where;
+ chars.count = before;
+ return;
+ }
+ // partial return not allowed, must copy
+ chars.array = new char[len];
+ chars.offset = 0;
+ System.arraycopy(array, where, chars.array, 0, before);
+ System.arraycopy(array, g1, chars.array, before, len - before);
+ }
+ chars.count = len;
+ }
+
+ /**
+ * Creates a position within the content that will
+ * track change as the content is mutated.
+ *
+ * @param offset the offset to track >= 0
+ * @return the position
+ * @exception BadLocationException if the specified position is invalid
+ */
+ public Position createPosition(int offset) throws BadLocationException {
+ while ( queue.poll() != null ) {
+ unusedMarks++;
+ }
+ if (unusedMarks > Math.max(5, (marks.size() / 10))) {
+ removeUnusedMarks();
+ }
+ int g0 = getGapStart();
+ int g1 = getGapEnd();
+ int index = (offset < g0) ? offset : offset + (g1 - g0);
+ search.index = index;
+ int sortIndex = findSortIndex(search);
+ MarkData m;
+ StickyPosition position;
+ if (sortIndex < marks.size()
+ && (m = marks.elementAt(sortIndex)).index == index
+ && (position = m.getPosition()) != null) {
+ //position references the correct StickyPostition
+ } else {
+ position = new StickyPosition();
+ m = new MarkData(index,position,queue);
+ position.setMark(m);
+ marks.insertElementAt(m, sortIndex);
+ }
+
+ return position;
+ }
+
+ /**
+ * Holds the data for a mark... separately from
+ * the real mark so that the real mark (Position
+ * that the caller of createPosition holds) can be
+ * collected if there are no more references to
+ * it. The update table holds only a reference
+ * to this data.
+ */
+ final class MarkData extends WeakReference {
+
+ MarkData(int index) {
+ super(null);
+ this.index = index;
+ }
+ MarkData(int index, StickyPosition position, ReferenceQueue queue) {
+ super(position, queue);
+ this.index = index;
+ }
+
+ /**
+ * Fetch the location in the contiguous sequence
+ * being modeled. The index in the gap array
+ * is held by the mark, so it is adjusted according
+ * to it's relationship to the gap.
+ */
+ public final int getOffset() {
+ int g0 = getGapStart();
+ int g1 = getGapEnd();
+ int offs = (index < g0) ? index : index - (g1 - g0);
+ return Math.max(offs, 0);
+ }
+
+ StickyPosition getPosition() {
+ return (StickyPosition)get();
+ }
+ int index;
+ }
+
+ final class StickyPosition implements Position {
+
+ StickyPosition() {
+ }
+
+ void setMark(MarkData mark) {
+ this.mark = mark;
+ }
+
+ public final int getOffset() {
+ return mark.getOffset();
+ }
+
+ public String toString() {
+ return Integer.toString(getOffset());
+ }
+
+ MarkData mark;
+ }
+
+ // --- variables --------------------------------------
+
+ private static final char[] empty = new char[0];
+ private transient MarkVector marks;
+
+ /**
+ * Record used for searching for the place to
+ * start updating mark indexs when the gap
+ * boundaries are moved.
+ */
+ private transient MarkData search;
+
+ /**
+ * The number of unused mark entries
+ */
+ private transient int unusedMarks = 0;
+
+ private transient ReferenceQueue queue;
+
+ final static int GROWTH_SIZE = 1024 * 512;
+
+ // --- gap management -------------------------------
+
+ /**
+ * Make the gap bigger, moving any necessary data and updating
+ * the appropriate marks
+ */
+ protected void shiftEnd(int newSize) {
+ int oldGapEnd = getGapEnd();
+
+ super.shiftEnd(newSize);
+
+ // Adjust marks.
+ int dg = getGapEnd() - oldGapEnd;
+ int adjustIndex = findMarkAdjustIndex(oldGapEnd);
+ int n = marks.size();
+ for (int i = adjustIndex; i < n; i++) {
+ MarkData mark = marks.elementAt(i);
+ mark.index += dg;
+ }
+ }
+
+ /**
+ * Overridden to make growth policy less agressive for large
+ * text amount.
+ */
+ int getNewArraySize(int reqSize) {
+ if (reqSize < GROWTH_SIZE) {
+ return super.getNewArraySize(reqSize);
+ } else {
+ return reqSize + GROWTH_SIZE;
+ }
+ }
+
+ /**
+ * Move the start of the gap to a new location,
+ * without changing the size of the gap. This
+ * moves the data in the array and updates the
+ * marks accordingly.
+ */
+ protected void shiftGap(int newGapStart) {
+ int oldGapStart = getGapStart();
+ int dg = newGapStart - oldGapStart;
+ int oldGapEnd = getGapEnd();
+ int newGapEnd = oldGapEnd + dg;
+ int gapSize = oldGapEnd - oldGapStart;
+
+ // shift gap in the character array
+ super.shiftGap(newGapStart);
+
+ // update the marks
+ if (dg > 0) {
+ // Move gap up, move data and marks down.
+ int adjustIndex = findMarkAdjustIndex(oldGapStart);
+ int n = marks.size();
+ for (int i = adjustIndex; i < n; i++) {
+ MarkData mark = marks.elementAt(i);
+ if (mark.index >= newGapEnd) {
+ break;
+ }
+ mark.index -= gapSize;
+ }
+ } else if (dg < 0) {
+ // Move gap down, move data and marks up.
+ int adjustIndex = findMarkAdjustIndex(newGapStart);
+ int n = marks.size();
+ for (int i = adjustIndex; i < n; i++) {
+ MarkData mark = marks.elementAt(i);
+ if (mark.index >= oldGapEnd) {
+ break;
+ }
+ mark.index += gapSize;
+ }
+ }
+ resetMarksAtZero();
+ }
+
+ /**
+ * Resets all the marks that have an offset of 0 to have an index of
+ * zero as well.
+ */
+ protected void resetMarksAtZero() {
+ if (marks != null && getGapStart() == 0) {
+ int g1 = getGapEnd();
+ for (int counter = 0, maxCounter = marks.size();
+ counter < maxCounter; counter++) {
+ MarkData mark = marks.elementAt(counter);
+ if (mark.index <= g1) {
+ mark.index = 0;
+ }
+ else {
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Adjust the gap end downward. This doesn't move
+ * any data, but it does update any marks affected
+ * by the boundary change. All marks from the old
+ * gap start down to the new gap start are squeezed
+ * to the end of the gap (their location has been
+ * removed).
+ */
+ protected void shiftGapStartDown(int newGapStart) {
+ // Push aside all marks from oldGapStart down to newGapStart.
+ int adjustIndex = findMarkAdjustIndex(newGapStart);
+ int n = marks.size();
+ int g0 = getGapStart();
+ int g1 = getGapEnd();
+ for (int i = adjustIndex; i < n; i++) {
+ MarkData mark = marks.elementAt(i);
+ if (mark.index > g0) {
+ // no more marks to adjust
+ break;
+ }
+ mark.index = g1;
+ }
+
+ // shift the gap in the character array
+ super.shiftGapStartDown(newGapStart);
+
+ resetMarksAtZero();
+ }
+
+ /**
+ * Adjust the gap end upward. This doesn't move
+ * any data, but it does update any marks affected
+ * by the boundary change. All marks from the old
+ * gap end up to the new gap end are squeezed
+ * to the end of the gap (their location has been
+ * removed).
+ */
+ protected void shiftGapEndUp(int newGapEnd) {
+ int adjustIndex = findMarkAdjustIndex(getGapEnd());
+ int n = marks.size();
+ for (int i = adjustIndex; i < n; i++) {
+ MarkData mark = marks.elementAt(i);
+ if (mark.index >= newGapEnd) {
+ break;
+ }
+ mark.index = newGapEnd;
+ }
+
+ // shift the gap in the character array
+ super.shiftGapEndUp(newGapEnd);
+
+ resetMarksAtZero();
+ }
+
+ /**
+ * Compares two marks.
+ *
+ * @param o1 the first object
+ * @param o2 the second object
+ * @return < 0 if o1 < o2, 0 if the same, > 0 if o1 > o2
+ */
+ final int compare(MarkData o1, MarkData o2) {
+ if (o1.index < o2.index) {
+ return -1;
+ } else if (o1.index > o2.index) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * Finds the index to start mark adjustments given
+ * some search index.
+ */
+ final int findMarkAdjustIndex(int searchIndex) {
+ search.index = Math.max(searchIndex, 1);
+ int index = findSortIndex(search);
+
+ // return the first in the series
+ // (ie. there may be duplicates).
+ for (int i = index - 1; i >= 0; i--) {
+ MarkData d = marks.elementAt(i);
+ if (d.index != search.index) {
+ break;
+ }
+ index -= 1;
+ }
+ return index;
+ }
+
+ /**
+ * Finds the index of where to insert a new mark.
+ *
+ * @param o the mark to insert
+ * @return the index
+ */
+ final int findSortIndex(MarkData o) {
+ int lower = 0;
+ int upper = marks.size() - 1;
+ int mid = 0;
+
+ if (upper == -1) {
+ return 0;
+ }
+
+ int cmp = 0;
+ MarkData last = marks.elementAt(upper);
+ cmp = compare(o, last);
+ if (cmp > 0)
+ return upper + 1;
+
+ while (lower <= upper) {
+ mid = lower + ((upper - lower) / 2);
+ MarkData entry = marks.elementAt(mid);
+ cmp = compare(o, entry);
+
+ if (cmp == 0) {
+ // found a match
+ return mid;
+ } else if (cmp < 0) {
+ upper = mid - 1;
+ } else {
+ lower = mid + 1;
+ }
+ }
+
+ // didn't find it, but we indicate the index of where it would belong.
+ return (cmp < 0) ? mid : mid + 1;
+ }
+
+ /**
+ * Remove all unused marks out of the sorted collection
+ * of marks.
+ */
+ final void removeUnusedMarks() {
+ int n = marks.size();
+ MarkVector cleaned = new MarkVector(n);
+ for (int i = 0; i < n; i++) {
+ MarkData mark = marks.elementAt(i);
+ if (mark.get() != null) {
+ cleaned.addElement(mark);
+ }
+ }
+ marks = cleaned;
+ unusedMarks = 0;
+ }
+
+
+ static class MarkVector extends GapVector {
+
+ MarkVector() {
+ super();
+ }
+
+ MarkVector(int size) {
+ super(size);
+ }
+
+ /**
+ * Allocate an array to store items of the type
+ * appropriate (which is determined by the subclass).
+ */
+ protected Object allocateArray(int len) {
+ return new MarkData[len];
+ }
+
+ /**
+ * Get the length of the allocated array
+ */
+ protected int getArrayLength() {
+ MarkData[] marks = (MarkData[]) getArray();
+ return marks.length;
+ }
+
+ /**
+ * Returns the number of marks currently held
+ */
+ public int size() {
+ int len = getArrayLength() - (getGapEnd() - getGapStart());
+ return len;
+ }
+
+ /**
+ * Inserts a mark into the vector
+ */
+ public void insertElementAt(MarkData m, int index) {
+ oneMark[0] = m;
+ replace(index, 0, oneMark, 1);
+ }
+
+ /**
+ * Add a mark to the end
+ */
+ public void addElement(MarkData m) {
+ insertElementAt(m, size());
+ }
+
+ /**
+ * Fetches the mark at the given index
+ */
+ public MarkData elementAt(int index) {
+ int g0 = getGapStart();
+ int g1 = getGapEnd();
+ MarkData[] array = (MarkData[]) getArray();
+ if (index < g0) {
+ // below gap
+ return array[index];
+ } else {
+ // above gap
+ index += g1 - g0;
+ return array[index];
+ }
+ }
+
+ /**
+ * Replaces the elements in the specified range with the passed
+ * in objects. This will NOT adjust the gap. The passed in indices
+ * do not account for the gap, they are the same as would be used
+ * int <code>elementAt</code>.
+ */
+ protected void replaceRange(int start, int end, Object[] marks) {
+ int g0 = getGapStart();
+ int g1 = getGapEnd();
+ int index = start;
+ int newIndex = 0;
+ Object[] array = (Object[]) getArray();
+ if (start >= g0) {
+ // Completely passed gap
+ index += (g1 - g0);
+ end += (g1 - g0);
+ }
+ else if (end >= g0) {
+ // straddles gap
+ end += (g1 - g0);
+ while (index < g0) {
+ array[index++] = marks[newIndex++];
+ }
+ index = g1;
+ }
+ else {
+ // below gap
+ while (index < end) {
+ array[index++] = marks[newIndex++];
+ }
+ }
+ while (index < end) {
+ array[index++] = marks[newIndex++];
+ }
+ }
+
+ MarkData[] oneMark = new MarkData[1];
+
+ }
+
+ // --- serialization -------------------------------------
+
+ private void readObject(ObjectInputStream s)
+ throws ClassNotFoundException, IOException {
+ s.defaultReadObject();
+ marks = new MarkVector();
+ search = new MarkData(0);
+ queue = new ReferenceQueue();
+ }
+
+
+ // --- undo support --------------------------------------
+
+ /**
+ * Returns a Vector containing instances of UndoPosRef for the
+ * Positions in the range
+ * <code>offset</code> to <code>offset</code> + <code>length</code>.
+ * If <code>v</code> is not null the matching Positions are placed in
+ * there. The vector with the resulting Positions are returned.
+ *
+ * @param v the Vector to use, with a new one created on null
+ * @param offset the starting offset >= 0
+ * @param length the length >= 0
+ * @return the set of instances
+ */
+ protected Vector getPositionsInRange(Vector v, int offset, int length) {
+ int endOffset = offset + length;
+ int startIndex;
+ int endIndex;
+ int g0 = getGapStart();
+ int g1 = getGapEnd();
+
+ // Find the index of the marks.
+ if (offset < g0) {
+ if (offset == 0) {
+ // findMarkAdjustIndex start at 1!
+ startIndex = 0;
+ }
+ else {
+ startIndex = findMarkAdjustIndex(offset);
+ }
+ if (endOffset >= g0) {
+ endIndex = findMarkAdjustIndex(endOffset + (g1 - g0) + 1);
+ }
+ else {
+ endIndex = findMarkAdjustIndex(endOffset + 1);
+ }
+ }
+ else {
+ startIndex = findMarkAdjustIndex(offset + (g1 - g0));
+ endIndex = findMarkAdjustIndex(endOffset + (g1 - g0) + 1);
+ }
+
+ Vector placeIn = (v == null) ? new Vector(Math.max(1, endIndex -
+ startIndex)) : v;
+
+ for (int counter = startIndex; counter < endIndex; counter++) {
+ placeIn.addElement(new UndoPosRef(marks.elementAt(counter)));
+ }
+ return placeIn;
+ }
+
+ /**
+ * Resets the location for all the UndoPosRef instances
+ * in <code>positions</code>.
+ * <p>
+ * This is meant for internal usage, and is generally not of interest
+ * to subclasses.
+ *
+ * @param positions the UndoPosRef instances to reset
+ */
+ protected void updateUndoPositions(Vector positions, int offset,
+ int length) {
+ // Find the indexs of the end points.
+ int endOffset = offset + length;
+ int g1 = getGapEnd();
+ int startIndex;
+ int endIndex = findMarkAdjustIndex(g1 + 1);
+
+ if (offset != 0) {
+ startIndex = findMarkAdjustIndex(g1);
+ }
+ else {
+ startIndex = 0;
+ }
+
+ // Reset the location of the refenences.
+ for(int counter = positions.size() - 1; counter >= 0; counter--) {
+ UndoPosRef ref = (UndoPosRef)positions.elementAt(counter);
+ ref.resetLocation(endOffset, g1);
+ }
+ // We have to resort the marks in the range startIndex to endIndex.
+ // We can take advantage of the fact that it will be in
+ // increasing order, accept there will be a bunch of MarkData's with
+ // the index g1 (or 0 if offset == 0) interspersed throughout.
+ if (startIndex < endIndex) {
+ Object[] sorted = new Object[endIndex - startIndex];
+ int addIndex = 0;
+ int counter;
+ if (offset == 0) {
+ // If the offset is 0, the positions won't have incremented,
+ // have to do the reverse thing.
+ // Find the elements in startIndex whose index is 0
+ for (counter = startIndex; counter < endIndex; counter++) {
+ MarkData mark = marks.elementAt(counter);
+ if (mark.index == 0) {
+ sorted[addIndex++] = mark;
+ }
+ }
+ for (counter = startIndex; counter < endIndex; counter++) {
+ MarkData mark = marks.elementAt(counter);
+ if (mark.index != 0) {
+ sorted[addIndex++] = mark;
+ }
+ }
+ }
+ else {
+ for (counter = startIndex; counter < endIndex; counter++) {
+ MarkData mark = marks.elementAt(counter);
+ if (mark.index != g1) {
+ sorted[addIndex++] = mark;
+ }
+ }
+ for (counter = startIndex; counter < endIndex; counter++) {
+ MarkData mark = marks.elementAt(counter);
+ if (mark.index == g1) {
+ sorted[addIndex++] = mark;
+ }
+ }
+ }
+ // And replace
+ marks.replaceRange(startIndex, endIndex, sorted);
+ }
+ }
+
+ /**
+ * Used to hold a reference to a Mark that is being reset as the
+ * result of removing from the content.
+ */
+ final class UndoPosRef {
+ UndoPosRef(MarkData rec) {
+ this.rec = rec;
+ this.undoLocation = rec.getOffset();
+ }
+
+ /**
+ * Resets the location of the Position to the offset when the
+ * receiver was instantiated.
+ *
+ * @param endOffset end location of inserted string.
+ * @param g1 resulting end of gap.
+ */
+ protected void resetLocation(int endOffset, int g1) {
+ if (undoLocation != endOffset) {
+ this.rec.index = undoLocation;
+ }
+ else {
+ this.rec.index = g1;
+ }
+ }
+
+ /** Previous Offset of rec. */
+ protected int undoLocation;
+ /** Mark to reset offset. */
+ protected MarkData rec;
+ } // End of GapContent.UndoPosRef
+
+
+ /**
+ * UnoableEdit created for inserts.
+ */
+ class InsertUndo extends AbstractUndoableEdit {
+ protected InsertUndo(int offset, int length) {
+ super();
+ this.offset = offset;
+ this.length = length;
+ }
+
+ public void undo() throws CannotUndoException {
+ super.undo();
+ try {
+ // Get the Positions in the range being removed.
+ posRefs = getPositionsInRange(null, offset, length);
+ string = getString(offset, length);
+ remove(offset, length);
+ } catch (BadLocationException bl) {
+ throw new CannotUndoException();
+ }
+ }
+
+ public void redo() throws CannotRedoException {
+ super.redo();
+ try {
+ insertString(offset, string);
+ string = null;
+ // Update the Positions that were in the range removed.
+ if(posRefs != null) {
+ updateUndoPositions(posRefs, offset, length);
+ posRefs = null;
+ }
+ } catch (BadLocationException bl) {
+ throw new CannotRedoException();
+ }
+ }
+
+ /** Where string was inserted. */
+ protected int offset;
+ /** Length of string inserted. */
+ protected int length;
+ /** The string that was inserted. This will only be valid after an
+ * undo. */
+ protected String string;
+ /** An array of instances of UndoPosRef for the Positions in the
+ * range that was removed, valid after undo. */
+ protected Vector posRefs;
+ } // GapContent.InsertUndo
+
+
+ /**
+ * UndoableEdit created for removes.
+ */
+ class RemoveUndo extends AbstractUndoableEdit {
+ protected RemoveUndo(int offset, String string) {
+ super();
+ this.offset = offset;
+ this.string = string;
+ this.length = string.length();
+ posRefs = getPositionsInRange(null, offset, length);
+ }
+
+ public void undo() throws CannotUndoException {
+ super.undo();
+ try {
+ insertString(offset, string);
+ // Update the Positions that were in the range removed.
+ if(posRefs != null) {
+ updateUndoPositions(posRefs, offset, length);
+ posRefs = null;
+ }
+ string = null;
+ } catch (BadLocationException bl) {
+ throw new CannotUndoException();
+ }
+ }
+
+ public void redo() throws CannotRedoException {
+ super.redo();
+ try {
+ string = getString(offset, length);
+ // Get the Positions in the range being removed.
+ posRefs = getPositionsInRange(null, offset, length);
+ remove(offset, length);
+ } catch (BadLocationException bl) {
+ throw new CannotRedoException();
+ }
+ }
+
+ /** Where the string was removed from. */
+ protected int offset;
+ /** Length of string removed. */
+ protected int length;
+ /** The string that was removed. This is valid when redo is valid. */
+ protected String string;
+ /** An array of instances of UndoPosRef for the Positions in the
+ * range that was removed, valid before undo. */
+ protected Vector posRefs;
+ } // GapContent.RemoveUndo
+}