PageRenderTime 49ms CodeModel.GetById 23ms RepoModel.GetById 0ms 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
Possible License(s): BSD-3-Clause, AGPL-1.0, Apache-2.0, LGPL-2.0, LGPL-3.0, GPL-2.0, CC-BY-SA-3.0, LGPL-2.1, GPL-3.0, MPL-2.0-no-copyleft-exception, IPL-1.0
  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. package org.gjt.sp.jedit.buffer;
  23. //{{{ Imports
  24. import org.gjt.sp.jedit.Buffer;
  25. import org.gjt.sp.util.Log;
  26. //}}}
  27. /**
  28. * A class internal to jEdit's document model. You should not use it
  29. * directly. To improve performance, none of the methods in this class
  30. * check for out of bounds access, nor are they thread-safe. The
  31. * <code>Buffer</code> class, through which these methods must be
  32. * called through, implements such protection.
  33. *
  34. * @author Slava Pestov
  35. * @version $Id: UndoManager.java 5029 2004-04-30 19:55:28Z spestov $
  36. * @since jEdit 4.0pre1
  37. */
  38. public class UndoManager
  39. {
  40. //{{{ UndoManager constructor
  41. public UndoManager(Buffer buffer)
  42. {
  43. this.buffer = buffer;
  44. } //}}}
  45. //{{{ setLimit() method
  46. public void setLimit(int limit)
  47. {
  48. this.limit = limit;
  49. } //}}}
  50. //{{{ clear() method
  51. public void clear()
  52. {
  53. undosFirst = undosLast = redosFirst = redosLast = null;
  54. undoCount = 0;
  55. } //}}}
  56. //{{{ undo() method
  57. public int undo()
  58. {
  59. if(insideCompoundEdit())
  60. throw new InternalError("Unbalanced begin/endCompoundEdit()");
  61. if(undosLast == null)
  62. return -1;
  63. else
  64. {
  65. undoCount--;
  66. int caret = undosLast.undo();
  67. redosFirst = undosLast;
  68. undosLast = undosLast.prev;
  69. if(undosLast == null)
  70. undosFirst = null;
  71. return caret;
  72. }
  73. } //}}}
  74. //{{{ redo() method
  75. public int redo()
  76. {
  77. if(insideCompoundEdit())
  78. throw new InternalError("Unbalanced begin/endCompoundEdit()");
  79. if(redosFirst == null)
  80. return -1;
  81. else
  82. {
  83. undoCount++;
  84. int caret = redosFirst.redo();
  85. undosLast = redosFirst;
  86. if(undosFirst == null)
  87. undosFirst = undosLast;
  88. redosFirst = redosFirst.next;
  89. return caret;
  90. }
  91. } //}}}
  92. //{{{ beginCompoundEdit() method
  93. public void beginCompoundEdit()
  94. {
  95. if(compoundEditCount == 0)
  96. compoundEdit = new CompoundEdit();
  97. compoundEditCount++;
  98. } //}}}
  99. //{{{ endCompoundEdit() method
  100. public void endCompoundEdit()
  101. {
  102. if(compoundEditCount == 0)
  103. {
  104. Log.log(Log.WARNING,this,new Exception("Unbalanced begin/endCompoundEdit()"));
  105. return;
  106. }
  107. else if(compoundEditCount == 1)
  108. {
  109. if(compoundEdit.first == null)
  110. /* nothing done between begin/end calls */;
  111. else if(compoundEdit.first == compoundEdit.last)
  112. addEdit(compoundEdit.first);
  113. else
  114. addEdit(compoundEdit);
  115. compoundEdit = null;
  116. }
  117. compoundEditCount--;
  118. } //}}}
  119. //{{{ insideCompoundEdit() method
  120. public boolean insideCompoundEdit()
  121. {
  122. return compoundEditCount != 0;
  123. } //}}}
  124. //{{{ contentInserted() method
  125. public void contentInserted(int offset, int length, String text, boolean clearDirty)
  126. {
  127. Edit toMerge = getLastEdit();
  128. if(!clearDirty && toMerge instanceof Insert
  129. && redosFirst == null)
  130. {
  131. Insert ins = (Insert)toMerge;
  132. if(ins.offset == offset)
  133. {
  134. ins.str = text.concat(ins.str);
  135. ins.length += length;
  136. return;
  137. }
  138. else if(ins.offset + ins.length == offset)
  139. {
  140. ins.str = ins.str.concat(text);
  141. ins.length += length;
  142. return;
  143. }
  144. }
  145. Insert ins = new Insert(this,offset,length,text);
  146. if(clearDirty)
  147. {
  148. redoClearDirty = toMerge;
  149. undoClearDirty = ins;
  150. }
  151. if(compoundEdit != null)
  152. compoundEdit.add(ins);
  153. else
  154. addEdit(ins);
  155. } //}}}
  156. //{{{ contentRemoved() method
  157. public void contentRemoved(int offset, int length, String text, boolean clearDirty)
  158. {
  159. Edit toMerge = getLastEdit();
  160. if(!clearDirty && toMerge instanceof Remove
  161. && redosFirst == null)
  162. {
  163. Remove rem = (Remove)toMerge;
  164. if(rem.offset == offset)
  165. {
  166. rem.str = rem.str.concat(text);
  167. rem.hashcode = rem.str.hashCode();
  168. rem.length += length;
  169. KillRing.changed(rem);
  170. return;
  171. }
  172. else if(offset + length == rem.offset)
  173. {
  174. rem.str = text.concat(rem.str);
  175. rem.hashcode = rem.str.hashCode();
  176. rem.length += length;
  177. rem.offset = offset;
  178. KillRing.changed(rem);
  179. return;
  180. }
  181. }
  182. Remove rem = new Remove(this,offset,length,text);
  183. if(clearDirty)
  184. {
  185. redoClearDirty = toMerge;
  186. undoClearDirty = rem;
  187. }
  188. if(compoundEdit != null)
  189. compoundEdit.add(rem);
  190. else
  191. addEdit(rem);
  192. KillRing.add(rem);
  193. } //}}}
  194. //{{{ bufferSaved() method
  195. public void bufferSaved()
  196. {
  197. redoClearDirty = getLastEdit();
  198. if(redosFirst instanceof CompoundEdit)
  199. undoClearDirty = ((CompoundEdit)redosFirst).first;
  200. else
  201. undoClearDirty = redosFirst;
  202. } //}}}
  203. //{{{ Private members
  204. //{{{ Instance variables
  205. private Buffer buffer;
  206. // queue of undos. last is most recent, first is oldest
  207. private Edit undosFirst;
  208. private Edit undosLast;
  209. // queue of redos. first is most recent, last is oldest
  210. private Edit redosFirst;
  211. private Edit redosLast;
  212. private int limit;
  213. private int undoCount;
  214. private int compoundEditCount;
  215. private CompoundEdit compoundEdit;
  216. private Edit undoClearDirty, redoClearDirty;
  217. //}}}
  218. //{{{ addEdit() method
  219. private void addEdit(Edit edit)
  220. {
  221. if(undosFirst == null)
  222. undosFirst = undosLast = edit;
  223. else
  224. {
  225. undosLast.next = edit;
  226. edit.prev = undosLast;
  227. undosLast = edit;
  228. }
  229. redosFirst = redosLast = null;
  230. undoCount++;
  231. while(undoCount > limit)
  232. {
  233. undoCount--;
  234. if(undosFirst == undosLast)
  235. undosFirst = undosLast = null;
  236. else
  237. {
  238. undosFirst.next.prev = null;
  239. undosFirst = undosFirst.next;
  240. }
  241. }
  242. } //}}}
  243. //{{{ getLastEdit() method
  244. private Edit getLastEdit()
  245. {
  246. if(compoundEdit != null)
  247. return compoundEdit.last;
  248. else if(undosLast instanceof CompoundEdit)
  249. return ((CompoundEdit)undosLast).last;
  250. else
  251. return undosLast;
  252. } //}}}
  253. //}}}
  254. //{{{ Inner classes
  255. //{{{ Edit class
  256. abstract static class Edit
  257. {
  258. Edit prev, next;
  259. //{{{ undo() method
  260. abstract int undo();
  261. //}}}
  262. //{{{ redo() method
  263. abstract int redo();
  264. //}}}
  265. } //}}}
  266. //{{{ Insert class
  267. static class Insert extends Edit
  268. {
  269. //{{{ Insert constructor
  270. Insert(UndoManager mgr, int offset, int length, String str)
  271. {
  272. this.mgr = mgr;
  273. this.offset = offset;
  274. this.length = length;
  275. this.str = str;
  276. } //}}}
  277. //{{{ undo() method
  278. int undo()
  279. {
  280. mgr.buffer.remove(offset,length);
  281. if(mgr.undoClearDirty == this)
  282. mgr.buffer.setDirty(false);
  283. return offset;
  284. } //}}}
  285. //{{{ redo() method
  286. int redo()
  287. {
  288. mgr.buffer.insert(offset,str);
  289. if(mgr.redoClearDirty == this)
  290. mgr.buffer.setDirty(false);
  291. return offset + length;
  292. } //}}}
  293. UndoManager mgr;
  294. int offset;
  295. int length;
  296. String str;
  297. } //}}}
  298. //{{{ Remove class
  299. static class Remove extends Edit
  300. {
  301. //{{{ Remove constructor
  302. Remove(UndoManager mgr, int offset, int length, String str)
  303. {
  304. this.mgr = mgr;
  305. this.offset = offset;
  306. this.length = length;
  307. this.str = str;
  308. hashcode = str.hashCode();
  309. } //}}}
  310. //{{{ undo() method
  311. int undo()
  312. {
  313. mgr.buffer.insert(offset,str);
  314. if(mgr.undoClearDirty == this)
  315. mgr.buffer.setDirty(false);
  316. return offset + length;
  317. } //}}}
  318. //{{{ redo() method
  319. int redo()
  320. {
  321. mgr.buffer.remove(offset,length);
  322. if(mgr.redoClearDirty == this)
  323. mgr.buffer.setDirty(false);
  324. return offset;
  325. } //}}}
  326. //{{{ toString() method
  327. public String toString()
  328. {
  329. return str;
  330. } //}}}
  331. UndoManager mgr;
  332. int offset;
  333. int length;
  334. String str;
  335. int hashcode;
  336. boolean inKillRing;
  337. } //}}}
  338. //{{{ CompoundEdit class
  339. static class CompoundEdit extends Edit
  340. {
  341. //{{{ undo() method
  342. public int undo()
  343. {
  344. int retVal = -1;
  345. Edit edit = last;
  346. while(edit != null)
  347. {
  348. retVal = edit.undo();
  349. edit = edit.prev;
  350. }
  351. return retVal;
  352. } //}}}
  353. //{{{ redo() method
  354. public int redo()
  355. {
  356. int retVal = -1;
  357. Edit edit = first;
  358. while(edit != null)
  359. {
  360. retVal = edit.redo();
  361. edit = edit.next;
  362. }
  363. return retVal;
  364. } //}}}
  365. //{{{ add() method
  366. public void add(Edit edit)
  367. {
  368. if(first == null)
  369. first = last = edit;
  370. else
  371. {
  372. edit.prev = last;
  373. last.next = edit;
  374. last = edit;
  375. }
  376. } //}}}
  377. Edit first, last;
  378. } //}}}
  379. //}}}
  380. }