PageRenderTime 29ms CodeModel.GetById 1ms app.highlight 24ms RepoModel.GetById 1ms app.codeStats 0ms

/jEdit/tags/jedit-4-3-pre5/org/gjt/sp/jedit/buffer/UndoManager.java

#
Java | 444 lines | 311 code | 59 blank | 74 comment | 66 complexity | 97422ef336a8d0663f5845f9c9b80344 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, 2005 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.util.Log;
 27//}}}
 28
 29/**
 30 * A class internal to jEdit's document model. You should not use it
 31 * directly. To improve performance, none of the methods in this class
 32 * check for out of bounds access, nor are they thread-safe. The
 33 * <code>Buffer</code> class, through which these methods must be
 34 * called through, implements such protection.
 35 *
 36 * @author Slava Pestov
 37 * @version $Id: UndoManager.java 5339 2006-01-25 23:12:07Z spestov $
 38 * @since jEdit 4.0pre1
 39 */
 40public class UndoManager
 41{
 42	//{{{ UndoManager constructor
 43	public UndoManager(JEditBuffer buffer)
 44	{
 45		this.buffer = buffer;
 46	} //}}}
 47
 48	//{{{ setLimit() method
 49	public void setLimit(int limit)
 50	{
 51		this.limit = limit;
 52	} //}}}
 53
 54	//{{{ clear() method
 55	public void clear()
 56	{
 57		undosFirst = undosLast = redosFirst = redosLast = null;
 58		undoCount = 0;
 59	} //}}}
 60
 61	//{{{ undo() method
 62	public int undo()
 63	{
 64		if(insideCompoundEdit())
 65			throw new InternalError("Unbalanced begin/endCompoundEdit()");
 66
 67		if(undosLast == null)
 68			return -1;
 69		else
 70		{
 71			undoCount--;
 72
 73			int caret = undosLast.undo();
 74			redosFirst = undosLast;
 75			undosLast = undosLast.prev;
 76			if(undosLast == null)
 77				undosFirst = null;
 78			return caret;
 79		}
 80	} //}}}
 81
 82	//{{{ redo() method
 83	public int redo()
 84	{
 85		if(insideCompoundEdit())
 86			throw new InternalError("Unbalanced begin/endCompoundEdit()");
 87
 88		if(redosFirst == null)
 89			return -1;
 90		else
 91		{
 92			undoCount++;
 93
 94			int caret = redosFirst.redo();
 95			undosLast = redosFirst;
 96			if(undosFirst == null)
 97				undosFirst = undosLast;
 98			redosFirst = redosFirst.next;
 99			return caret;
100		}
101	} //}}}
102
103	//{{{ beginCompoundEdit() method
104	public void beginCompoundEdit()
105	{
106		if(compoundEditCount == 0)
107			compoundEdit = new CompoundEdit();
108
109		compoundEditCount++;
110	} //}}}
111
112	//{{{ endCompoundEdit() method
113	public void endCompoundEdit()
114	{
115		if(compoundEditCount == 0)
116		{
117			Log.log(Log.WARNING,this,new Exception("Unbalanced begin/endCompoundEdit()"));
118			return;
119		}
120		else if(compoundEditCount == 1)
121		{
122			if(compoundEdit.first == null)
123				/* nothing done between begin/end calls */;
124			else if(compoundEdit.first == compoundEdit.last)
125				addEdit(compoundEdit.first);
126			else
127				addEdit(compoundEdit);
128
129			compoundEdit = null;
130		}
131
132		compoundEditCount--;
133	} //}}}
134
135	//{{{ insideCompoundEdit() method
136	public boolean insideCompoundEdit()
137	{
138		return compoundEditCount != 0;
139	} //}}}
140
141	//{{{ contentInserted() method
142	public void contentInserted(int offset, int length, String text, boolean clearDirty)
143	{
144		Edit last = getLastEdit();
145		Edit toMerge = getMergeEdit();
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 = last;
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 last = getLastEdit();
183		Edit toMerge = getMergeEdit();
184
185		if(!clearDirty && toMerge instanceof Remove
186			&& redosFirst == null)
187		{
188			Remove rem = (Remove)toMerge;
189			if(rem.offset == offset)
190			{
191				rem.str = rem.str.concat(text);
192				rem.hashcode = rem.str.hashCode();
193				rem.length += length;
194				KillRing.getInstance().changed(rem);
195				return;
196			}
197			else if(offset + length == rem.offset)
198			{
199				rem.str = text.concat(rem.str);
200				rem.hashcode = rem.str.hashCode();
201				rem.length += length;
202				rem.offset = offset;
203				KillRing.getInstance().changed(rem);
204				return;
205			}
206		}
207
208		Remove rem = new Remove(this,offset,length,text);
209		if(clearDirty)
210		{
211			redoClearDirty = last;
212			undoClearDirty = rem;
213		}
214
215		if(compoundEdit != null)
216			compoundEdit.add(rem);
217		else
218			addEdit(rem);
219
220		KillRing.getInstance().add(rem);
221	} //}}}
222
223	//{{{ bufferSaved() method
224	public void bufferSaved()
225	{
226		redoClearDirty = getLastEdit();
227		if(redosFirst instanceof CompoundEdit)
228			undoClearDirty = ((CompoundEdit)redosFirst).first;
229		else
230			undoClearDirty = redosFirst;
231	} //}}}
232
233	//{{{ Private members
234
235	//{{{ Instance variables
236	private JEditBuffer buffer;
237
238	// queue of undos. last is most recent, first is oldest
239	private Edit undosFirst;
240	private Edit undosLast;
241
242	// queue of redos. first is most recent, last is oldest
243	private Edit redosFirst;
244	private Edit redosLast;
245
246	private int limit;
247	private int undoCount;
248	private int compoundEditCount;
249	private CompoundEdit compoundEdit;
250	private Edit undoClearDirty, redoClearDirty;
251	//}}}
252
253	//{{{ addEdit() method
254	private void addEdit(Edit edit)
255	{
256		if(undosFirst == null)
257			undosFirst = undosLast = edit;
258		else
259		{
260			undosLast.next = edit;
261			edit.prev = undosLast;
262			undosLast = edit;
263		}
264
265		redosFirst = redosLast = null;
266
267		undoCount++;
268
269		while(undoCount > limit)
270		{
271			undoCount--;
272
273			if(undosFirst == undosLast)
274				undosFirst = undosLast = null;
275			else
276			{
277				undosFirst.next.prev = null;
278				undosFirst = undosFirst.next;
279			}
280		}
281	} //}}}
282
283	//{{{ getMergeEdit() method
284	private Edit getMergeEdit()
285	{
286		Edit last = getLastEdit();
287		return (compoundEdit != null ? compoundEdit.last : last);
288	} //}}}
289
290	//{{{ getLastEdit() method
291	private Edit getLastEdit()
292	{
293		if(undosLast instanceof CompoundEdit)
294			return ((CompoundEdit)undosLast).last;
295		else
296			return undosLast;
297	} //}}}
298
299	//}}}
300
301	//{{{ Inner classes
302
303	//{{{ Edit class
304	abstract static class Edit
305	{
306		Edit prev, next;
307
308		//{{{ undo() method
309		abstract int undo();
310		//}}}
311
312		//{{{ redo() method
313		abstract int redo();
314		//}}}
315	} //}}}
316
317	//{{{ Insert class
318	static class Insert extends Edit
319	{
320		//{{{ Insert constructor
321		Insert(UndoManager mgr, int offset, int length, String str)
322		{
323			this.mgr = mgr;
324			this.offset = offset;
325			this.length = length;
326			this.str = str;
327		} //}}}
328
329		//{{{ undo() method
330		int undo()
331		{
332			mgr.buffer.remove(offset,length);
333			if(mgr.undoClearDirty == this)
334				mgr.buffer.setDirty(false);
335			return offset;
336		} //}}}
337
338		//{{{ redo() method
339		int redo()
340		{
341			mgr.buffer.insert(offset,str);
342			if(mgr.redoClearDirty == this)
343				mgr.buffer.setDirty(false);
344			return offset + length;
345		} //}}}
346
347		UndoManager mgr;
348		int offset;
349		int length;
350		String str;
351	} //}}}
352
353	//{{{ Remove class
354	static class Remove extends Edit
355	{
356		//{{{ Remove constructor
357		Remove(UndoManager mgr, int offset, int length, String str)
358		{
359			this.mgr = mgr;
360			this.offset = offset;
361			this.length = length;
362			this.str = str;
363			hashcode = str.hashCode();
364		} //}}}
365
366		//{{{ undo() method
367		int undo()
368		{
369			mgr.buffer.insert(offset,str);
370			if(mgr.undoClearDirty == this)
371				mgr.buffer.setDirty(false);
372			return offset + length;
373		} //}}}
374
375		//{{{ redo() method
376		int redo()
377		{
378			mgr.buffer.remove(offset,length);
379			if(mgr.redoClearDirty == this)
380				mgr.buffer.setDirty(false);
381			return offset;
382		} //}}}
383
384		//{{{ toString() method
385		public String toString()
386		{
387			return str;
388		} //}}}
389
390		UndoManager mgr;
391		int offset;
392		int length;
393		String str;
394		int hashcode;
395		boolean inKillRing;
396	} //}}}
397
398	//{{{ CompoundEdit class
399	static class CompoundEdit extends Edit
400	{
401		//{{{ undo() method
402		public int undo()
403		{
404			int retVal = -1;
405			Edit edit = last;
406			while(edit != null)
407			{
408				retVal = edit.undo();
409				edit = edit.prev;
410			}
411			return retVal;
412		} //}}}
413
414		//{{{ redo() method
415		public int redo()
416		{
417			int retVal = -1;
418			Edit edit = first;
419			while(edit != null)
420			{
421				retVal = edit.redo();
422				edit = edit.next;
423			}
424			return retVal;
425		} //}}}
426
427		//{{{ add() method
428		public void add(Edit edit)
429		{
430			if(first == null)
431				first = last = edit;
432			else
433			{
434				edit.prev = last;
435				last.next = edit;
436				last = edit;
437			}
438		} //}}}
439
440		Edit first, last;
441	} //}}}
442
443	//}}}
444}