blob: 23db31c74f601a2587d1652947f148f010579dc1 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1998-2003 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25package javax.swing.text;
26
27import java.util.Vector;
28import java.io.IOException;
29import java.io.ObjectInputStream;
30import java.io.Serializable;
31import javax.swing.undo.AbstractUndoableEdit;
32import javax.swing.undo.CannotRedoException;
33import javax.swing.undo.CannotUndoException;
34import javax.swing.undo.UndoableEdit;
35import javax.swing.SwingUtilities;
36import java.lang.ref.WeakReference;
37import java.lang.ref.ReferenceQueue;
38
39/**
40 * An implementation of the AbstractDocument.Content interface
41 * implemented using a gapped buffer similar to that used by emacs.
42 * The underlying storage is a array of unicode characters with
43 * a gap somewhere. The gap is moved to the location of changes
44 * to take advantage of common behavior where most changes are
45 * in the same location. Changes that occur at a gap boundary are
46 * generally cheap and moving the gap is generally cheaper than
47 * moving the array contents directly to accomodate the change.
48 * <p>
49 * The positions tracking change are also generally cheap to
50 * maintain. The Position implementations (marks) store the array
51 * index and can easily calculate the sequential position from
52 * the current gap location. Changes only require update to the
53 * the marks between the old and new gap boundaries when the gap
54 * is moved, so generally updating the marks is pretty cheap.
55 * The marks are stored sorted so they can be located quickly
56 * with a binary search. This increases the cost of adding a
57 * mark, and decreases the cost of keeping the mark updated.
58 *
59 * @author Timothy Prinzing
60 */
61public class GapContent extends GapVector implements AbstractDocument.Content, Serializable {
62
63 /**
64 * Creates a new GapContent object. Initial size defaults to 10.
65 */
66 public GapContent() {
67 this(10);
68 }
69
70 /**
71 * Creates a new GapContent object, with the initial
72 * size specified. The initial size will not be allowed
73 * to go below 2, to give room for the implied break and
74 * the gap.
75 *
76 * @param initialLength the initial size
77 */
78 public GapContent(int initialLength) {
79 super(Math.max(initialLength,2));
80 char[] implied = new char[1];
81 implied[0] = '\n';
82 replace(0, 0, implied, implied.length);
83
84 marks = new MarkVector();
85 search = new MarkData(0);
86 queue = new ReferenceQueue();
87 }
88
89 /**
90 * Allocate an array to store items of the type
91 * appropriate (which is determined by the subclass).
92 */
93 protected Object allocateArray(int len) {
94 return new char[len];
95 }
96
97 /**
98 * Get the length of the allocated array.
99 */
100 protected int getArrayLength() {
101 char[] carray = (char[]) getArray();
102 return carray.length;
103 }
104
105 // --- AbstractDocument.Content methods -------------------------
106
107 /**
108 * Returns the length of the content.
109 *
110 * @return the length >= 1
111 * @see AbstractDocument.Content#length
112 */
113 public int length() {
114 int len = getArrayLength() - (getGapEnd() - getGapStart());
115 return len;
116 }
117
118 /**
119 * Inserts a string into the content.
120 *
121 * @param where the starting position >= 0, < length()
122 * @param str the non-null string to insert
123 * @return an UndoableEdit object for undoing
124 * @exception BadLocationException if the specified position is invalid
125 * @see AbstractDocument.Content#insertString
126 */
127 public UndoableEdit insertString(int where, String str) throws BadLocationException {
128 if (where > length() || where < 0) {
129 throw new BadLocationException("Invalid insert", length());
130 }
131 char[] chars = str.toCharArray();
132 replace(where, 0, chars, chars.length);
133 return new InsertUndo(where, str.length());
134 }
135
136 /**
137 * Removes part of the content.
138 *
139 * @param where the starting position >= 0, where + nitems < length()
140 * @param nitems the number of characters to remove >= 0
141 * @return an UndoableEdit object for undoing
142 * @exception BadLocationException if the specified position is invalid
143 * @see AbstractDocument.Content#remove
144 */
145 public UndoableEdit remove(int where, int nitems) throws BadLocationException {
146 if (where + nitems >= length()) {
147 throw new BadLocationException("Invalid remove", length() + 1);
148 }
149 String removedString = getString(where, nitems);
150 UndoableEdit edit = new RemoveUndo(where, removedString);
151 replace(where, nitems, empty, 0);
152 return edit;
153
154 }
155
156 /**
157 * Retrieves a portion of the content.
158 *
159 * @param where the starting position >= 0
160 * @param len the length to retrieve >= 0
161 * @return a string representing the content
162 * @exception BadLocationException if the specified position is invalid
163 * @see AbstractDocument.Content#getString
164 */
165 public String getString(int where, int len) throws BadLocationException {
166 Segment s = new Segment();
167 getChars(where, len, s);
168 return new String(s.array, s.offset, s.count);
169 }
170
171 /**
172 * Retrieves a portion of the content. If the desired content spans
173 * the gap, we copy the content. If the desired content does not
174 * span the gap, the actual store is returned to avoid the copy since
175 * it is contiguous.
176 *
177 * @param where the starting position >= 0, where + len <= length()
178 * @param len the number of characters to retrieve >= 0
179 * @param chars the Segment object to return the characters in
180 * @exception BadLocationException if the specified position is invalid
181 * @see AbstractDocument.Content#getChars
182 */
183 public void getChars(int where, int len, Segment chars) throws BadLocationException {
184 int end = where + len;
185 if (where < 0 || end < 0) {
186 throw new BadLocationException("Invalid location", -1);
187 }
188 if (end > length() || where > length()) {
189 throw new BadLocationException("Invalid location", length() + 1);
190 }
191 int g0 = getGapStart();
192 int g1 = getGapEnd();
193 char[] array = (char[]) getArray();
194 if ((where + len) <= g0) {
195 // below gap
196 chars.array = array;
197 chars.offset = where;
198 } else if (where >= g0) {
199 // above gap
200 chars.array = array;
201 chars.offset = g1 + where - g0;
202 } else {
203 // spans the gap
204 int before = g0 - where;
205 if (chars.isPartialReturn()) {
206 // partial return allowed, return amount before the gap
207 chars.array = array;
208 chars.offset = where;
209 chars.count = before;
210 return;
211 }
212 // partial return not allowed, must copy
213 chars.array = new char[len];
214 chars.offset = 0;
215 System.arraycopy(array, where, chars.array, 0, before);
216 System.arraycopy(array, g1, chars.array, before, len - before);
217 }
218 chars.count = len;
219 }
220
221 /**
222 * Creates a position within the content that will
223 * track change as the content is mutated.
224 *
225 * @param offset the offset to track >= 0
226 * @return the position
227 * @exception BadLocationException if the specified position is invalid
228 */
229 public Position createPosition(int offset) throws BadLocationException {
230 while ( queue.poll() != null ) {
231 unusedMarks++;
232 }
233 if (unusedMarks > Math.max(5, (marks.size() / 10))) {
234 removeUnusedMarks();
235 }
236 int g0 = getGapStart();
237 int g1 = getGapEnd();
238 int index = (offset < g0) ? offset : offset + (g1 - g0);
239 search.index = index;
240 int sortIndex = findSortIndex(search);
241 MarkData m;
242 StickyPosition position;
243 if (sortIndex < marks.size()
244 && (m = marks.elementAt(sortIndex)).index == index
245 && (position = m.getPosition()) != null) {
246 //position references the correct StickyPostition
247 } else {
248 position = new StickyPosition();
249 m = new MarkData(index,position,queue);
250 position.setMark(m);
251 marks.insertElementAt(m, sortIndex);
252 }
253
254 return position;
255 }
256
257 /**
258 * Holds the data for a mark... separately from
259 * the real mark so that the real mark (Position
260 * that the caller of createPosition holds) can be
261 * collected if there are no more references to
262 * it. The update table holds only a reference
263 * to this data.
264 */
265 final class MarkData extends WeakReference {
266
267 MarkData(int index) {
268 super(null);
269 this.index = index;
270 }
271 MarkData(int index, StickyPosition position, ReferenceQueue queue) {
272 super(position, queue);
273 this.index = index;
274 }
275
276 /**
277 * Fetch the location in the contiguous sequence
278 * being modeled. The index in the gap array
279 * is held by the mark, so it is adjusted according
280 * to it's relationship to the gap.
281 */
282 public final int getOffset() {
283 int g0 = getGapStart();
284 int g1 = getGapEnd();
285 int offs = (index < g0) ? index : index - (g1 - g0);
286 return Math.max(offs, 0);
287 }
288
289 StickyPosition getPosition() {
290 return (StickyPosition)get();
291 }
292 int index;
293 }
294
295 final class StickyPosition implements Position {
296
297 StickyPosition() {
298 }
299
300 void setMark(MarkData mark) {
301 this.mark = mark;
302 }
303
304 public final int getOffset() {
305 return mark.getOffset();
306 }
307
308 public String toString() {
309 return Integer.toString(getOffset());
310 }
311
312 MarkData mark;
313 }
314
315 // --- variables --------------------------------------
316
317 private static final char[] empty = new char[0];
318 private transient MarkVector marks;
319
320 /**
321 * Record used for searching for the place to
322 * start updating mark indexs when the gap
323 * boundaries are moved.
324 */
325 private transient MarkData search;
326
327 /**
328 * The number of unused mark entries
329 */
330 private transient int unusedMarks = 0;
331
332 private transient ReferenceQueue queue;
333
334 final static int GROWTH_SIZE = 1024 * 512;
335
336 // --- gap management -------------------------------
337
338 /**
339 * Make the gap bigger, moving any necessary data and updating
340 * the appropriate marks
341 */
342 protected void shiftEnd(int newSize) {
343 int oldGapEnd = getGapEnd();
344
345 super.shiftEnd(newSize);
346
347 // Adjust marks.
348 int dg = getGapEnd() - oldGapEnd;
349 int adjustIndex = findMarkAdjustIndex(oldGapEnd);
350 int n = marks.size();
351 for (int i = adjustIndex; i < n; i++) {
352 MarkData mark = marks.elementAt(i);
353 mark.index += dg;
354 }
355 }
356
357 /**
358 * Overridden to make growth policy less agressive for large
359 * text amount.
360 */
361 int getNewArraySize(int reqSize) {
362 if (reqSize < GROWTH_SIZE) {
363 return super.getNewArraySize(reqSize);
364 } else {
365 return reqSize + GROWTH_SIZE;
366 }
367 }
368
369 /**
370 * Move the start of the gap to a new location,
371 * without changing the size of the gap. This
372 * moves the data in the array and updates the
373 * marks accordingly.
374 */
375 protected void shiftGap(int newGapStart) {
376 int oldGapStart = getGapStart();
377 int dg = newGapStart - oldGapStart;
378 int oldGapEnd = getGapEnd();
379 int newGapEnd = oldGapEnd + dg;
380 int gapSize = oldGapEnd - oldGapStart;
381
382 // shift gap in the character array
383 super.shiftGap(newGapStart);
384
385 // update the marks
386 if (dg > 0) {
387 // Move gap up, move data and marks down.
388 int adjustIndex = findMarkAdjustIndex(oldGapStart);
389 int n = marks.size();
390 for (int i = adjustIndex; i < n; i++) {
391 MarkData mark = marks.elementAt(i);
392 if (mark.index >= newGapEnd) {
393 break;
394 }
395 mark.index -= gapSize;
396 }
397 } else if (dg < 0) {
398 // Move gap down, move data and marks up.
399 int adjustIndex = findMarkAdjustIndex(newGapStart);
400 int n = marks.size();
401 for (int i = adjustIndex; i < n; i++) {
402 MarkData mark = marks.elementAt(i);
403 if (mark.index >= oldGapEnd) {
404 break;
405 }
406 mark.index += gapSize;
407 }
408 }
409 resetMarksAtZero();
410 }
411
412 /**
413 * Resets all the marks that have an offset of 0 to have an index of
414 * zero as well.
415 */
416 protected void resetMarksAtZero() {
417 if (marks != null && getGapStart() == 0) {
418 int g1 = getGapEnd();
419 for (int counter = 0, maxCounter = marks.size();
420 counter < maxCounter; counter++) {
421 MarkData mark = marks.elementAt(counter);
422 if (mark.index <= g1) {
423 mark.index = 0;
424 }
425 else {
426 break;
427 }
428 }
429 }
430 }
431
432 /**
433 * Adjust the gap end downward. This doesn't move
434 * any data, but it does update any marks affected
435 * by the boundary change. All marks from the old
436 * gap start down to the new gap start are squeezed
437 * to the end of the gap (their location has been
438 * removed).
439 */
440 protected void shiftGapStartDown(int newGapStart) {
441 // Push aside all marks from oldGapStart down to newGapStart.
442 int adjustIndex = findMarkAdjustIndex(newGapStart);
443 int n = marks.size();
444 int g0 = getGapStart();
445 int g1 = getGapEnd();
446 for (int i = adjustIndex; i < n; i++) {
447 MarkData mark = marks.elementAt(i);
448 if (mark.index > g0) {
449 // no more marks to adjust
450 break;
451 }
452 mark.index = g1;
453 }
454
455 // shift the gap in the character array
456 super.shiftGapStartDown(newGapStart);
457
458 resetMarksAtZero();
459 }
460
461 /**
462 * Adjust the gap end upward. This doesn't move
463 * any data, but it does update any marks affected
464 * by the boundary change. All marks from the old
465 * gap end up to the new gap end are squeezed
466 * to the end of the gap (their location has been
467 * removed).
468 */
469 protected void shiftGapEndUp(int newGapEnd) {
470 int adjustIndex = findMarkAdjustIndex(getGapEnd());
471 int n = marks.size();
472 for (int i = adjustIndex; i < n; i++) {
473 MarkData mark = marks.elementAt(i);
474 if (mark.index >= newGapEnd) {
475 break;
476 }
477 mark.index = newGapEnd;
478 }
479
480 // shift the gap in the character array
481 super.shiftGapEndUp(newGapEnd);
482
483 resetMarksAtZero();
484 }
485
486 /**
487 * Compares two marks.
488 *
489 * @param o1 the first object
490 * @param o2 the second object
491 * @return < 0 if o1 < o2, 0 if the same, > 0 if o1 > o2
492 */
493 final int compare(MarkData o1, MarkData o2) {
494 if (o1.index < o2.index) {
495 return -1;
496 } else if (o1.index > o2.index) {
497 return 1;
498 } else {
499 return 0;
500 }
501 }
502
503 /**
504 * Finds the index to start mark adjustments given
505 * some search index.
506 */
507 final int findMarkAdjustIndex(int searchIndex) {
508 search.index = Math.max(searchIndex, 1);
509 int index = findSortIndex(search);
510
511 // return the first in the series
512 // (ie. there may be duplicates).
513 for (int i = index - 1; i >= 0; i--) {
514 MarkData d = marks.elementAt(i);
515 if (d.index != search.index) {
516 break;
517 }
518 index -= 1;
519 }
520 return index;
521 }
522
523 /**
524 * Finds the index of where to insert a new mark.
525 *
526 * @param o the mark to insert
527 * @return the index
528 */
529 final int findSortIndex(MarkData o) {
530 int lower = 0;
531 int upper = marks.size() - 1;
532 int mid = 0;
533
534 if (upper == -1) {
535 return 0;
536 }
537
538 int cmp = 0;
539 MarkData last = marks.elementAt(upper);
540 cmp = compare(o, last);
541 if (cmp > 0)
542 return upper + 1;
543
544 while (lower <= upper) {
545 mid = lower + ((upper - lower) / 2);
546 MarkData entry = marks.elementAt(mid);
547 cmp = compare(o, entry);
548
549 if (cmp == 0) {
550 // found a match
551 return mid;
552 } else if (cmp < 0) {
553 upper = mid - 1;
554 } else {
555 lower = mid + 1;
556 }
557 }
558
559 // didn't find it, but we indicate the index of where it would belong.
560 return (cmp < 0) ? mid : mid + 1;
561 }
562
563 /**
564 * Remove all unused marks out of the sorted collection
565 * of marks.
566 */
567 final void removeUnusedMarks() {
568 int n = marks.size();
569 MarkVector cleaned = new MarkVector(n);
570 for (int i = 0; i < n; i++) {
571 MarkData mark = marks.elementAt(i);
572 if (mark.get() != null) {
573 cleaned.addElement(mark);
574 }
575 }
576 marks = cleaned;
577 unusedMarks = 0;
578 }
579
580
581 static class MarkVector extends GapVector {
582
583 MarkVector() {
584 super();
585 }
586
587 MarkVector(int size) {
588 super(size);
589 }
590
591 /**
592 * Allocate an array to store items of the type
593 * appropriate (which is determined by the subclass).
594 */
595 protected Object allocateArray(int len) {
596 return new MarkData[len];
597 }
598
599 /**
600 * Get the length of the allocated array
601 */
602 protected int getArrayLength() {
603 MarkData[] marks = (MarkData[]) getArray();
604 return marks.length;
605 }
606
607 /**
608 * Returns the number of marks currently held
609 */
610 public int size() {
611 int len = getArrayLength() - (getGapEnd() - getGapStart());
612 return len;
613 }
614
615 /**
616 * Inserts a mark into the vector
617 */
618 public void insertElementAt(MarkData m, int index) {
619 oneMark[0] = m;
620 replace(index, 0, oneMark, 1);
621 }
622
623 /**
624 * Add a mark to the end
625 */
626 public void addElement(MarkData m) {
627 insertElementAt(m, size());
628 }
629
630 /**
631 * Fetches the mark at the given index
632 */
633 public MarkData elementAt(int index) {
634 int g0 = getGapStart();
635 int g1 = getGapEnd();
636 MarkData[] array = (MarkData[]) getArray();
637 if (index < g0) {
638 // below gap
639 return array[index];
640 } else {
641 // above gap
642 index += g1 - g0;
643 return array[index];
644 }
645 }
646
647 /**
648 * Replaces the elements in the specified range with the passed
649 * in objects. This will NOT adjust the gap. The passed in indices
650 * do not account for the gap, they are the same as would be used
651 * int <code>elementAt</code>.
652 */
653 protected void replaceRange(int start, int end, Object[] marks) {
654 int g0 = getGapStart();
655 int g1 = getGapEnd();
656 int index = start;
657 int newIndex = 0;
658 Object[] array = (Object[]) getArray();
659 if (start >= g0) {
660 // Completely passed gap
661 index += (g1 - g0);
662 end += (g1 - g0);
663 }
664 else if (end >= g0) {
665 // straddles gap
666 end += (g1 - g0);
667 while (index < g0) {
668 array[index++] = marks[newIndex++];
669 }
670 index = g1;
671 }
672 else {
673 // below gap
674 while (index < end) {
675 array[index++] = marks[newIndex++];
676 }
677 }
678 while (index < end) {
679 array[index++] = marks[newIndex++];
680 }
681 }
682
683 MarkData[] oneMark = new MarkData[1];
684
685 }
686
687 // --- serialization -------------------------------------
688
689 private void readObject(ObjectInputStream s)
690 throws ClassNotFoundException, IOException {
691 s.defaultReadObject();
692 marks = new MarkVector();
693 search = new MarkData(0);
694 queue = new ReferenceQueue();
695 }
696
697
698 // --- undo support --------------------------------------
699
700 /**
701 * Returns a Vector containing instances of UndoPosRef for the
702 * Positions in the range
703 * <code>offset</code> to <code>offset</code> + <code>length</code>.
704 * If <code>v</code> is not null the matching Positions are placed in
705 * there. The vector with the resulting Positions are returned.
706 *
707 * @param v the Vector to use, with a new one created on null
708 * @param offset the starting offset >= 0
709 * @param length the length >= 0
710 * @return the set of instances
711 */
712 protected Vector getPositionsInRange(Vector v, int offset, int length) {
713 int endOffset = offset + length;
714 int startIndex;
715 int endIndex;
716 int g0 = getGapStart();
717 int g1 = getGapEnd();
718
719 // Find the index of the marks.
720 if (offset < g0) {
721 if (offset == 0) {
722 // findMarkAdjustIndex start at 1!
723 startIndex = 0;
724 }
725 else {
726 startIndex = findMarkAdjustIndex(offset);
727 }
728 if (endOffset >= g0) {
729 endIndex = findMarkAdjustIndex(endOffset + (g1 - g0) + 1);
730 }
731 else {
732 endIndex = findMarkAdjustIndex(endOffset + 1);
733 }
734 }
735 else {
736 startIndex = findMarkAdjustIndex(offset + (g1 - g0));
737 endIndex = findMarkAdjustIndex(endOffset + (g1 - g0) + 1);
738 }
739
740 Vector placeIn = (v == null) ? new Vector(Math.max(1, endIndex -
741 startIndex)) : v;
742
743 for (int counter = startIndex; counter < endIndex; counter++) {
744 placeIn.addElement(new UndoPosRef(marks.elementAt(counter)));
745 }
746 return placeIn;
747 }
748
749 /**
750 * Resets the location for all the UndoPosRef instances
751 * in <code>positions</code>.
752 * <p>
753 * This is meant for internal usage, and is generally not of interest
754 * to subclasses.
755 *
756 * @param positions the UndoPosRef instances to reset
757 */
758 protected void updateUndoPositions(Vector positions, int offset,
759 int length) {
760 // Find the indexs of the end points.
761 int endOffset = offset + length;
762 int g1 = getGapEnd();
763 int startIndex;
764 int endIndex = findMarkAdjustIndex(g1 + 1);
765
766 if (offset != 0) {
767 startIndex = findMarkAdjustIndex(g1);
768 }
769 else {
770 startIndex = 0;
771 }
772
773 // Reset the location of the refenences.
774 for(int counter = positions.size() - 1; counter >= 0; counter--) {
775 UndoPosRef ref = (UndoPosRef)positions.elementAt(counter);
776 ref.resetLocation(endOffset, g1);
777 }
778 // We have to resort the marks in the range startIndex to endIndex.
779 // We can take advantage of the fact that it will be in
780 // increasing order, accept there will be a bunch of MarkData's with
781 // the index g1 (or 0 if offset == 0) interspersed throughout.
782 if (startIndex < endIndex) {
783 Object[] sorted = new Object[endIndex - startIndex];
784 int addIndex = 0;
785 int counter;
786 if (offset == 0) {
787 // If the offset is 0, the positions won't have incremented,
788 // have to do the reverse thing.
789 // Find the elements in startIndex whose index is 0
790 for (counter = startIndex; counter < endIndex; counter++) {
791 MarkData mark = marks.elementAt(counter);
792 if (mark.index == 0) {
793 sorted[addIndex++] = mark;
794 }
795 }
796 for (counter = startIndex; counter < endIndex; counter++) {
797 MarkData mark = marks.elementAt(counter);
798 if (mark.index != 0) {
799 sorted[addIndex++] = mark;
800 }
801 }
802 }
803 else {
804 for (counter = startIndex; counter < endIndex; counter++) {
805 MarkData mark = marks.elementAt(counter);
806 if (mark.index != g1) {
807 sorted[addIndex++] = mark;
808 }
809 }
810 for (counter = startIndex; counter < endIndex; counter++) {
811 MarkData mark = marks.elementAt(counter);
812 if (mark.index == g1) {
813 sorted[addIndex++] = mark;
814 }
815 }
816 }
817 // And replace
818 marks.replaceRange(startIndex, endIndex, sorted);
819 }
820 }
821
822 /**
823 * Used to hold a reference to a Mark that is being reset as the
824 * result of removing from the content.
825 */
826 final class UndoPosRef {
827 UndoPosRef(MarkData rec) {
828 this.rec = rec;
829 this.undoLocation = rec.getOffset();
830 }
831
832 /**
833 * Resets the location of the Position to the offset when the
834 * receiver was instantiated.
835 *
836 * @param endOffset end location of inserted string.
837 * @param g1 resulting end of gap.
838 */
839 protected void resetLocation(int endOffset, int g1) {
840 if (undoLocation != endOffset) {
841 this.rec.index = undoLocation;
842 }
843 else {
844 this.rec.index = g1;
845 }
846 }
847
848 /** Previous Offset of rec. */
849 protected int undoLocation;
850 /** Mark to reset offset. */
851 protected MarkData rec;
852 } // End of GapContent.UndoPosRef
853
854
855 /**
856 * UnoableEdit created for inserts.
857 */
858 class InsertUndo extends AbstractUndoableEdit {
859 protected InsertUndo(int offset, int length) {
860 super();
861 this.offset = offset;
862 this.length = length;
863 }
864
865 public void undo() throws CannotUndoException {
866 super.undo();
867 try {
868 // Get the Positions in the range being removed.
869 posRefs = getPositionsInRange(null, offset, length);
870 string = getString(offset, length);
871 remove(offset, length);
872 } catch (BadLocationException bl) {
873 throw new CannotUndoException();
874 }
875 }
876
877 public void redo() throws CannotRedoException {
878 super.redo();
879 try {
880 insertString(offset, string);
881 string = null;
882 // Update the Positions that were in the range removed.
883 if(posRefs != null) {
884 updateUndoPositions(posRefs, offset, length);
885 posRefs = null;
886 }
887 } catch (BadLocationException bl) {
888 throw new CannotRedoException();
889 }
890 }
891
892 /** Where string was inserted. */
893 protected int offset;
894 /** Length of string inserted. */
895 protected int length;
896 /** The string that was inserted. This will only be valid after an
897 * undo. */
898 protected String string;
899 /** An array of instances of UndoPosRef for the Positions in the
900 * range that was removed, valid after undo. */
901 protected Vector posRefs;
902 } // GapContent.InsertUndo
903
904
905 /**
906 * UndoableEdit created for removes.
907 */
908 class RemoveUndo extends AbstractUndoableEdit {
909 protected RemoveUndo(int offset, String string) {
910 super();
911 this.offset = offset;
912 this.string = string;
913 this.length = string.length();
914 posRefs = getPositionsInRange(null, offset, length);
915 }
916
917 public void undo() throws CannotUndoException {
918 super.undo();
919 try {
920 insertString(offset, string);
921 // Update the Positions that were in the range removed.
922 if(posRefs != null) {
923 updateUndoPositions(posRefs, offset, length);
924 posRefs = null;
925 }
926 string = null;
927 } catch (BadLocationException bl) {
928 throw new CannotUndoException();
929 }
930 }
931
932 public void redo() throws CannotRedoException {
933 super.redo();
934 try {
935 string = getString(offset, length);
936 // Get the Positions in the range being removed.
937 posRefs = getPositionsInRange(null, offset, length);
938 remove(offset, length);
939 } catch (BadLocationException bl) {
940 throw new CannotRedoException();
941 }
942 }
943
944 /** Where the string was removed from. */
945 protected int offset;
946 /** Length of string removed. */
947 protected int length;
948 /** The string that was removed. This is valid when redo is valid. */
949 protected String string;
950 /** An array of instances of UndoPosRef for the Positions in the
951 * range that was removed, valid before undo. */
952 protected Vector posRefs;
953 } // GapContent.RemoveUndo
954}