001: /*
002: * Sun Public License Notice
003: *
004: * The contents of this file are subject to the Sun Public License
005: * Version 1.0 (the "License"). You may not use this file except in
006: * compliance with the License. A copy of the License is available at
007: * http://www.sun.com/
008: *
009: * The Original Code is NetBeans. The Initial Developer of the Original
010: * Code is Sun Microsystems, Inc. Portions Copyright 1997-2000 Sun
011: * Microsystems, Inc. All Rights Reserved.
012: */
013:
014: package org.netbeans.editor;
015:
016: import javax.swing.text.BadLocationException;
017: import javax.swing.text.Document;
018: import javax.swing.text.JTextComponent;
019:
020: /**
021: * The last several jumps in either the current file or across several files is
022: * stored here in the list. It's possible to track this list.
023: *
024: * @author Miloslav Metelka
025: * @version 1.00
026: */
027: public class JumpList {
028:
029: /**
030: * Maximum size to which the list will be shrinked if it exceeds the
031: * THRESHOLD_SIZE.
032: */
033: private static final int MAX_SIZE = 50;
034:
035: /**
036: * Reaching this count means that the size should be checked and possibly
037: * shrinked to the MAX_SIZE.
038: */
039: private static final int CHECK_COUNT = 10;
040:
041: /** Current jump list entry */
042: private static Entry currentEntry;
043:
044: private static int checkCnt;
045:
046: public static void checkAddEntry() {
047: JTextComponent c = Utilities.getLastActiveComponent();
048: if (c != null) {
049: checkAddEntry(c, c.getCaret().getDot());
050: }
051: }
052:
053: public static void checkAddEntry(JTextComponent c) {
054: checkAddEntry(c, c.getCaret().getDot());
055: }
056:
057: public static void checkAddEntry(JTextComponent c, int pos) {
058: if (currentEntry == null || currentEntry.getComponent() != c
059: || currentEntry.getPosition() != pos) {
060: addEntry(c, pos);
061: }
062: }
063:
064: public static void addEntry(JTextComponent c, int pos) {
065: try {
066: Entry e = new Entry(c, pos, currentEntry);
067: currentEntry = e;
068: if (++checkCnt >= CHECK_COUNT) { // perform size check
069: sizeCheck();
070: }
071: } catch (BadLocationException e) {
072: // entry not added
073: }
074: }
075:
076: /**
077: * @param c
078: * current component. It's used to compare the current component
079: * and position with those stored in the entries.
080: */
081: public static void jumpPrev(JTextComponent c) {
082: int dotPos = c.getCaret().getDot();
083: if (currentEntry != null) {
084: while (true) {
085: int entryPos = currentEntry.getPosition();
086: JTextComponent entryComp = currentEntry.getComponent();
087: if (entryComp != null
088: && (entryComp != c || (entryPos >= 0 && entryPos != dotPos))) {
089: if (currentEntry.setDot()) {
090: break;
091: }
092: }
093: if (currentEntry.prev != null) { // must check not to end up
094: // with null
095: currentEntry = currentEntry.prev;
096: } else {
097: break; // break when on the last entry
098: }
099: }
100: }
101: }
102:
103: /**
104: * @param c
105: * current component. It's used to compare the current component
106: * and position with those stored in the entries.
107: */
108: public static void jumpNext(JTextComponent c) {
109: int dotPos = c.getCaret().getDot();
110: if (currentEntry != null) {
111: while (true) {
112: int entryPos = currentEntry.getPosition();
113: JTextComponent entryComp = currentEntry.getComponent();
114: if (entryComp != null
115: && (entryComp != c || (entryPos >= 0 && entryPos != dotPos))) {
116: if (currentEntry.setDot()) {
117: break;
118: }
119: }
120: if (currentEntry.next != null) { // must check not to end up
121: // with null
122: currentEntry = currentEntry.next;
123: } else {
124: break; // break when on the last entry
125: }
126: }
127: }
128: }
129:
130: /**
131: * @param c
132: * current component. It's used to compare the current component
133: * to those stored in the jump list entries.
134: */
135: public static void jumpPrevComponent(JTextComponent c) {
136: if (currentEntry != null) {
137: while (true) {
138: JTextComponent entryComp = currentEntry.getComponent();
139: if (entryComp != null && entryComp != c) {
140: if (currentEntry.setDot()) {
141: break;
142: }
143: }
144: if (currentEntry.prev != null) { // must check not to end up
145: // with null
146: currentEntry = currentEntry.prev;
147: } else {
148: break; // break when on the last entry
149: }
150: }
151: }
152: }
153:
154: /**
155: * @param c
156: * current component. It's used to compare the current component
157: * to those stored in the jump list entries.
158: */
159: public static void jumpNextComponent(JTextComponent c) {
160: if (currentEntry != null) {
161: while (true) {
162: JTextComponent entryComp = currentEntry.getComponent();
163: if (entryComp != null && entryComp != c) {
164: if (currentEntry.setDot()) {
165: break;
166: }
167: }
168: if (currentEntry.next != null) { // must check not to end up
169: // with null
170: currentEntry = currentEntry.next;
171: } else {
172: break; // break when on the last entry
173: }
174: }
175: }
176: }
177:
178: public static String dump() {
179: StringBuffer sb = new StringBuffer();
180: int i = 0;
181: Entry e = currentEntry;
182: if (e != null) {
183: while (true) {
184: if (e.prev != null) {
185: e = e.prev;
186: i--;
187: } else {
188: break;
189: }
190: }
191:
192: while (e != null) {
193: JTextComponent comp = e.getComponent();
194: String docStr = (comp != null) ? (String) comp
195: .getDocument().getProperty(
196: Document.TitleProperty)
197: : "<Invalid document>"; // NOI18N
198: if (docStr == null) { // no title property
199: docStr = "Untitled"; // NOI18N
200: }
201: sb.append("[" + i++ + "]=" + docStr + ", "
202: + e.getPosition() + "\n"); // NOI18N
203: e = e.next;
204: }
205: } else { // null current entry
206: sb.append("Empty list"); // NOI18N
207: }
208: return sb.toString();
209: }
210:
211: private static void sizeCheck() {
212: int cnt = MAX_SIZE;
213: Entry e = currentEntry;
214: while (e != null && cnt > 0) {
215: e = e.prev;
216: cnt--; // #19429
217: }
218: if (e != null) { // reached the one that should be the first
219: e.makeFirst();
220: }
221: }
222:
223: public static class Entry {
224:
225: /** ID of the stored position component */
226: private int componentID;
227:
228: /** ID of the position stored in the document */
229: private int posID;
230:
231: /** Previous entry in the linked list */
232: Entry prev;
233:
234: /** Next entry in the linked list */
235: Entry next;
236:
237: Entry(JTextComponent component, int offset, Entry last)
238: throws BadLocationException {
239: componentID = Registry.getID(component);
240: posID = ((BaseDocument) component.getDocument())
241: .storePosition(offset);
242: if (last != null) { // apend after the last entry
243: last.next = this ;
244: this .prev = last;
245: }
246: }
247:
248: public int getPosition() {
249: JTextComponent c = Registry.getComponent(componentID);
250: int pos = -1;
251: if (c != null) {
252: pos = ((BaseDocument) c.getDocument())
253: .getStoredPosition(posID);
254: }
255: return pos;
256: }
257:
258: public JTextComponent getComponent() {
259: return Registry.getComponent(componentID);
260: }
261:
262: /**
263: * Set the dot to the component and position stored in the mark.
264: *
265: * @return true if the caret was successfully moved
266: */
267: public boolean setDot() {
268: JTextComponent c = getComponent();
269: if (c != null) {
270: if (Utilities.getLastActiveComponent() != c) {
271: Utilities.requestFocus(c); // possibly request for the
272: // component
273: }
274:
275: int pos = getPosition();
276: if (pos >= 0 && pos <= c.getDocument().getLength()) {
277: c.getCaret().setDot(pos); // set the dot
278: return true;
279: }
280: }
281: return false;
282: }
283:
284: void makeLast() {
285: if (next != null) {
286: next.prev = null;
287: next = null;
288: }
289: }
290:
291: void makeFirst() {
292: if (prev != null) {
293: prev.next = null;
294: prev = null;
295: }
296: }
297:
298: protected void finalize() throws Throwable {
299: JTextComponent c = Registry.getComponent(componentID);
300: if (c != null) {
301: ((BaseDocument) c.getDocument())
302: .removeStoredPosition(posID);
303: }
304: super.finalize();
305: }
306:
307: }
308:
309: }
|