PageRenderTime 16ms CodeModel.GetById 2ms app.highlight 10ms RepoModel.GetById 1ms app.codeStats 0ms

/jEdit/tags/jedit-4-2-pre14/org/gjt/sp/jedit/buffer/UndoManager.java

#
Java | 438 lines | 307 code | 58 blank | 73 comment | 68 complexity | 511fdfb8096070fbd587c90a33ec86af MD5 | raw file
  1/*
  2 * UndoManager.java - Buffer undo manager
  3 * :tabSize=8:indentSize=8:noTabs=false:
  4 * :folding=explicit:collapseFolds=1:
  5 *
  6 * Copyright (C) 2001, 2003 Slava Pestov
  7 *
  8 * This program is free software; you can redistribute it and/or
  9 * modify it under the terms of the GNU General Public License
 10 * as published by the Free Software Foundation; either version 2
 11 * of the License, or any later version.
 12 *
 13 * This program is distributed in the hope that it will be useful,
 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 16 * GNU General Public License for more details.
 17 *
 18 * You should have received a copy of the GNU General Public License
 19 * along with this program; if not, write to the Free Software
 20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 21 */
 22
 23package org.gjt.sp.jedit.buffer;
 24
 25//{{{ Imports
 26import org.gjt.sp.jedit.Buffer;
 27import org.gjt.sp.util.Log;
 28//}}}
 29
 30/**
 31 * A class internal to jEdit's document model. You should not use it
 32 * directly. To improve performance, none of the methods in this class
 33 * check for out of bounds access, nor are they thread-safe. The
 34 * <code>Buffer</code> class, through which these methods must be
 35 * called through, implements such protection.
 36 *
 37 * @author Slava Pestov
 38 * @version $Id: UndoManager.java 5029 2004-04-30 19:55:28Z spestov $
 39 * @since jEdit 4.0pre1
 40 */
 41public class UndoManager
 42{
 43	//{{{ UndoManager constructor
 44	public UndoManager(Buffer buffer)
 45	{
 46		this.buffer = buffer;
 47	} //}}}
 48
 49	//{{{ setLimit() method
 50	public void setLimit(int limit)
 51	{
 52		this.limit = limit;
 53	} //}}}
 54
 55	//{{{ clear() method
 56	public void clear()
 57	{
 58		undosFirst = undosLast = redosFirst = redosLast = null;
 59		undoCount = 0;
 60	} //}}}
 61
 62	//{{{ undo() method
 63	public int undo()
 64	{
 65		if(insideCompoundEdit())
 66			throw new InternalError("Unbalanced begin/endCompoundEdit()");
 67
 68		if(undosLast == null)
 69			return -1;
 70		else
 71		{
 72			undoCount--;
 73
 74			int caret = undosLast.undo();
 75			redosFirst = undosLast;
 76			undosLast = undosLast.prev;
 77			if(undosLast == null)
 78				undosFirst = null;
 79			return caret;
 80		}
 81	} //}}}
 82
 83	//{{{ redo() method
 84	public int redo()
 85	{
 86		if(insideCompoundEdit())
 87			throw new InternalError("Unbalanced begin/endCompoundEdit()");
 88
 89		if(redosFirst == null)
 90			return -1;
 91		else
 92		{
 93			undoCount++;
 94
 95			int caret = redosFirst.redo();
 96			undosLast = redosFirst;
 97			if(undosFirst == null)
 98				undosFirst = undosLast;
 99			redosFirst = redosFirst.next;
100			return caret;
101		}
102	} //}}}
103
104	//{{{ beginCompoundEdit() method
105	public void beginCompoundEdit()
106	{
107		if(compoundEditCount == 0)
108			compoundEdit = new CompoundEdit();
109
110		compoundEditCount++;
111	} //}}}
112
113	//{{{ endCompoundEdit() method
114	public void endCompoundEdit()
115	{
116		if(compoundEditCount == 0)
117		{
118			Log.log(Log.WARNING,this,new Exception("Unbalanced begin/endCompoundEdit()"));
119			return;
120		}
121		else if(compoundEditCount == 1)
122		{
123			if(compoundEdit.first == null)
124				/* nothing done between begin/end calls */;
125			else if(compoundEdit.first == compoundEdit.last)
126				addEdit(compoundEdit.first);
127			else
128				addEdit(compoundEdit);
129
130			compoundEdit = null;
131		}
132
133		compoundEditCount--;
134	} //}}}
135
136	//{{{ insideCompoundEdit() method
137	public boolean insideCompoundEdit()
138	{
139		return compoundEditCount != 0;
140	} //}}}
141
142	//{{{ contentInserted() method
143	public void contentInserted(int offset, int length, String text, boolean clearDirty)
144	{
145		Edit toMerge = getLastEdit();
146
147		if(!clearDirty && toMerge instanceof Insert
148			&& redosFirst == null)
149		{
150			Insert ins = (Insert)toMerge;
151			if(ins.offset == offset)
152			{
153				ins.str = text.concat(ins.str);
154				ins.length += length;
155				return;
156			}
157			else if(ins.offset + ins.length == offset)
158			{
159				ins.str = ins.str.concat(text);
160				ins.length += length;
161				return;
162			}
163		}
164
165		Insert ins = new Insert(this,offset,length,text);
166
167		if(clearDirty)
168		{
169			redoClearDirty = toMerge;
170			undoClearDirty = ins;
171		}
172
173		if(compoundEdit != null)
174			compoundEdit.add(ins);
175		else
176			addEdit(ins);
177	} //}}}
178
179	//{{{ contentRemoved() method
180	public void contentRemoved(int offset, int length, String text, boolean clearDirty)
181	{
182		Edit toMerge = getLastEdit();
183
184		if(!clearDirty && toMerge instanceof Remove
185			&& redosFirst == null)
186		{
187			Remove rem = (Remove)toMerge;
188			if(rem.offset == offset)
189			{
190				rem.str = rem.str.concat(text);
191				rem.hashcode = rem.str.hashCode();
192				rem.length += length;
193				KillRing.changed(rem);
194				return;
195			}
196			else if(offset + length == rem.offset)
197			{
198				rem.str = text.concat(rem.str);
199				rem.hashcode = rem.str.hashCode();
200				rem.length += length;
201				rem.offset = offset;
202				KillRing.changed(rem);
203				return;
204			}
205		}
206
207		Remove rem = new Remove(this,offset,length,text);
208		if(clearDirty)
209		{
210			redoClearDirty = toMerge;
211			undoClearDirty = rem;
212		}
213
214		if(compoundEdit != null)
215			compoundEdit.add(rem);
216		else
217			addEdit(rem);
218
219		KillRing.add(rem);
220	} //}}}
221
222	//{{{ bufferSaved() method
223	public void bufferSaved()
224	{
225		redoClearDirty = getLastEdit();
226		if(redosFirst instanceof CompoundEdit)
227			undoClearDirty = ((CompoundEdit)redosFirst).first;
228		else
229			undoClearDirty = redosFirst;
230	} //}}}
231
232	//{{{ Private members
233
234	//{{{ Instance variables
235	private Buffer buffer;
236
237	// queue of undos. last is most recent, first is oldest
238	private Edit undosFirst;
239	private Edit undosLast;
240
241	// queue of redos. first is most recent, last is oldest
242	private Edit redosFirst;
243	private Edit redosLast;
244
245	private int limit;
246	private int undoCount;
247	private int compoundEditCount;
248	private CompoundEdit compoundEdit;
249	private Edit undoClearDirty, redoClearDirty;
250	//}}}
251
252	//{{{ addEdit() method
253	private void addEdit(Edit edit)
254	{
255		if(undosFirst == null)
256			undosFirst = undosLast = edit;
257		else
258		{
259			undosLast.next = edit;
260			edit.prev = undosLast;
261			undosLast = edit;
262		}
263
264		redosFirst = redosLast = null;
265
266		undoCount++;
267
268		while(undoCount > limit)
269		{
270			undoCount--;
271
272			if(undosFirst == undosLast)
273				undosFirst = undosLast = null;
274			else
275			{
276				undosFirst.next.prev = null;
277				undosFirst = undosFirst.next;
278			}
279		}
280	} //}}}
281
282	//{{{ getLastEdit() method
283	private Edit getLastEdit()
284	{
285		if(compoundEdit != null)
286			return compoundEdit.last;
287		else if(undosLast instanceof CompoundEdit)
288			return ((CompoundEdit)undosLast).last;
289		else
290			return undosLast;
291	} //}}}
292
293	//}}}
294
295	//{{{ Inner classes
296
297	//{{{ Edit class
298	abstract static class Edit
299	{
300		Edit prev, next;
301
302		//{{{ undo() method
303		abstract int undo();
304		//}}}
305
306		//{{{ redo() method
307		abstract int redo();
308		//}}}
309	} //}}}
310
311	//{{{ Insert class
312	static class Insert extends Edit
313	{
314		//{{{ Insert constructor
315		Insert(UndoManager mgr, int offset, int length, String str)
316		{
317			this.mgr = mgr;
318			this.offset = offset;
319			this.length = length;
320			this.str = str;
321		} //}}}
322
323		//{{{ undo() method
324		int undo()
325		{
326			mgr.buffer.remove(offset,length);
327			if(mgr.undoClearDirty == this)
328				mgr.buffer.setDirty(false);
329			return offset;
330		} //}}}
331
332		//{{{ redo() method
333		int redo()
334		{
335			mgr.buffer.insert(offset,str);
336			if(mgr.redoClearDirty == this)
337				mgr.buffer.setDirty(false);
338			return offset + length;
339		} //}}}
340
341		UndoManager mgr;
342		int offset;
343		int length;
344		String str;
345	} //}}}
346
347	//{{{ Remove class
348	static class Remove extends Edit
349	{
350		//{{{ Remove constructor
351		Remove(UndoManager mgr, int offset, int length, String str)
352		{
353			this.mgr = mgr;
354			this.offset = offset;
355			this.length = length;
356			this.str = str;
357			hashcode = str.hashCode();
358		} //}}}
359
360		//{{{ undo() method
361		int undo()
362		{
363			mgr.buffer.insert(offset,str);
364			if(mgr.undoClearDirty == this)
365				mgr.buffer.setDirty(false);
366			return offset + length;
367		} //}}}
368
369		//{{{ redo() method
370		int redo()
371		{
372			mgr.buffer.remove(offset,length);
373			if(mgr.redoClearDirty == this)
374				mgr.buffer.setDirty(false);
375			return offset;
376		} //}}}
377
378		//{{{ toString() method
379		public String toString()
380		{
381			return str;
382		} //}}}
383
384		UndoManager mgr;
385		int offset;
386		int length;
387		String str;
388		int hashcode;
389		boolean inKillRing;
390	} //}}}
391
392	//{{{ CompoundEdit class
393	static class CompoundEdit extends Edit
394	{
395		//{{{ undo() method
396		public int undo()
397		{
398			int retVal = -1;
399			Edit edit = last;
400			while(edit != null)
401			{
402				retVal = edit.undo();
403				edit = edit.prev;
404			}
405			return retVal;
406		} //}}}
407
408		//{{{ redo() method
409		public int redo()
410		{
411			int retVal = -1;
412			Edit edit = first;
413			while(edit != null)
414			{
415				retVal = edit.redo();
416				edit = edit.next;
417			}
418			return retVal;
419		} //}}}
420
421		//{{{ add() method
422		public void add(Edit edit)
423		{
424			if(first == null)
425				first = last = edit;
426			else
427			{
428				edit.prev = last;
429				last.next = edit;
430				last = edit;
431			}
432		} //}}}
433
434		Edit first, last;
435	} //}}}
436
437	//}}}
438}