Mercurial > projects > dwt-addons
annotate dwtx/text/undo/DocumentUndoManager.d @ 161:f8d52b926852
...
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Wed, 27 Aug 2008 14:49:30 +0200 |
parents | 3678e4f1a766 |
children | c3583c6ec027 |
rev | line source |
---|---|
129 | 1 /******************************************************************************* |
2 * Copyright (c) 2006, 2008 IBM Corporation and others. | |
3 * All rights reserved. This program and the accompanying materials | |
4 * are made available under the terms of the Eclipse Public License v1.0 | |
5 * which accompanies this distribution, and is available at | |
6 * http://www.eclipse.org/legal/epl-v10.html | |
7 * | |
8 * Contributors: | |
9 * IBM Corporation - initial API and implementation | |
10 * Port to the D programming language: | |
11 * Frank Benoit <benoit@tionex.de> | |
12 *******************************************************************************/ | |
13 module dwtx.text.undo.DocumentUndoManager; | |
14 | |
131 | 15 import dwtx.text.undo.DocumentUndoManagerRegistry; // packageimport |
16 import dwtx.text.undo.DocumentUndoEvent; // packageimport | |
17 import dwtx.text.undo.IDocumentUndoListener; // packageimport | |
18 import dwtx.text.undo.UndoMessages; // packageimport | |
19 import dwtx.text.undo.IDocumentUndoManager; // packageimport | |
20 | |
21 | |
129 | 22 import dwt.dwthelper.utils; |
23 | |
153
f70d9508c95c
Fix java Collection imports
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
24 import dwtx.dwtxhelper.Collection; |
f70d9508c95c
Fix java Collection imports
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
25 |
129 | 26 |
27 import dwtx.core.commands.ExecutionException; | |
28 import dwtx.core.commands.operations.AbstractOperation; | |
29 import dwtx.core.commands.operations.IContextReplacingOperation; | |
30 import dwtx.core.commands.operations.IOperationHistory; | |
31 import dwtx.core.commands.operations.IOperationHistoryListener; | |
32 import dwtx.core.commands.operations.IUndoContext; | |
33 import dwtx.core.commands.operations.IUndoableOperation; | |
34 import dwtx.core.commands.operations.ObjectUndoContext; | |
35 import dwtx.core.commands.operations.OperationHistoryEvent; | |
36 import dwtx.core.commands.operations.OperationHistoryFactory; | |
37 import dwtx.core.runtime.Assert; | |
38 import dwtx.core.runtime.IAdaptable; | |
39 import dwtx.core.runtime.IProgressMonitor; | |
40 import dwtx.core.runtime.IStatus; | |
41 import dwtx.core.runtime.ListenerList; | |
42 import dwtx.core.runtime.Status; | |
43 import dwtx.jface.text.BadLocationException; | |
44 import dwtx.jface.text.DocumentEvent; | |
45 import dwtx.jface.text.IDocument; | |
46 import dwtx.jface.text.IDocumentExtension4; | |
47 import dwtx.jface.text.IDocumentListener; | |
48 import dwtx.jface.text.TextUtilities; | |
49 | |
50 /** | |
51 * A standard implementation of a document-based undo manager that | |
52 * creates an undo history based on changes to its document. | |
53 * <p> | |
54 * Based on the 3.1 implementation of DefaultUndoManager, it was implemented | |
55 * using the document-related manipulations defined in the original | |
56 * DefaultUndoManager, by separating the document manipulations from the | |
57 * viewer-specific processing.</p> | |
58 * <p> | |
59 * The classes representing individual text edits (formerly text commands) | |
60 * were promoted from inner types to their own classes in order to support | |
61 * reassignment to a different undo manager.<p> | |
62 * <p> | |
63 * This class is not intended to be subclassed. | |
64 * </p> | |
130 | 65 * |
129 | 66 * @see IDocumentUndoManager |
67 * @see DocumentUndoManagerRegistry | |
68 * @see IDocumentUndoListener | |
69 * @see dwtx.jface.text.IDocument | |
70 * @since 3.2 | |
71 * @noextend This class is not intended to be subclassed by clients. | |
72 */ | |
73 public class DocumentUndoManager : IDocumentUndoManager { | |
130 | 74 |
75 | |
129 | 76 /** |
77 * Represents an undo-able text change, described as the | |
78 * replacement of some preserved text with new text. | |
79 * <p> | |
80 * Based on the DefaultUndoManager.TextCommand from R3.1. | |
81 * </p> | |
82 */ | |
83 private static class UndoableTextChange : AbstractOperation { | |
84 | |
85 /** The start index of the replaced text. */ | |
86 protected int fStart= -1; | |
87 | |
88 /** The end index of the replaced text. */ | |
89 protected int fEnd= -1; | |
90 | |
91 /** The newly inserted text. */ | |
92 protected String fText; | |
93 | |
94 /** The replaced text. */ | |
95 protected String fPreservedText; | |
96 | |
97 /** The undo modification stamp. */ | |
98 protected long fUndoModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP; | |
99 | |
100 /** The redo modification stamp. */ | |
101 protected long fRedoModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP; | |
102 | |
103 /** The undo manager that generated the change. */ | |
104 protected DocumentUndoManager fDocumentUndoManager; | |
105 | |
106 /** | |
107 * Creates a new text change. | |
130 | 108 * |
129 | 109 * @param manager the undo manager for this change |
110 */ | |
130 | 111 this(DocumentUndoManager manager) { |
129 | 112 super(UndoMessages.getString("DocumentUndoManager.operationLabel")); //$NON-NLS-1$ |
113 this.fDocumentUndoManager= manager; | |
114 addContext(manager.getUndoContext()); | |
115 } | |
116 | |
117 /** | |
118 * Re-initializes this text change. | |
119 */ | |
120 protected void reinitialize() { | |
121 fStart= fEnd= -1; | |
122 fText= fPreservedText= null; | |
123 fUndoModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP; | |
124 fRedoModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP; | |
125 } | |
126 | |
127 /** | |
128 * Sets the start and the end index of this change. | |
130 | 129 * |
129 | 130 * @param start the start index |
131 * @param end the end index | |
132 */ | |
133 protected void set(int start, int end) { | |
134 fStart= start; | |
135 fEnd= end; | |
136 fText= null; | |
137 fPreservedText= null; | |
138 } | |
139 | |
140 /* | |
141 * @see dwtx.core.commands.operations.IUndoableOperation#dispose() | |
142 */ | |
143 public void dispose() { | |
144 reinitialize(); | |
145 } | |
146 | |
147 /** | |
148 * Undo the change described by this change. | |
149 */ | |
150 protected void undoTextChange() { | |
151 try { | |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
134
diff
changeset
|
152 if (auto de4 = cast(IDocumentExtension4)fDocumentUndoManager.fDocument ) |
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
134
diff
changeset
|
153 de4.replace(fStart, fText |
129 | 154 .length(), fPreservedText, fUndoModificationStamp); |
155 else | |
156 fDocumentUndoManager.fDocument.replace(fStart, fText.length(), | |
157 fPreservedText); | |
158 } catch (BadLocationException x) { | |
159 } | |
160 } | |
161 | |
162 /* | |
163 * @see dwtx.core.commands.operations.IUndoableOperation#canUndo() | |
164 */ | |
165 public bool canUndo() { | |
166 if (isValid()) { | |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
134
diff
changeset
|
167 if (cast(IDocumentExtension4)fDocumentUndoManager.fDocument) { |
134 | 168 long docStamp= (cast(IDocumentExtension4) fDocumentUndoManager.fDocument) |
129 | 169 .getModificationStamp(); |
170 | |
171 // Normal case: an undo is valid if its redo will restore | |
172 // document to its current modification stamp | |
173 bool canUndo= docStamp is IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP | |
174 || docStamp is getRedoModificationStamp(); | |
175 | |
176 /* | |
177 * Special case to check if the answer is false. If the last | |
178 * document change was empty, then the document's modification | |
179 * stamp was incremented but nothing was committed. The | |
180 * operation being queried has an older stamp. In this case | |
181 * only, the comparison is different. A sequence of document | |
182 * changes that include an empty change is handled correctly | |
183 * when a valid commit follows the empty change, but when | |
184 * #canUndo() is queried just after an empty change, we must | |
185 * special case the check. The check is very specific to prevent | |
186 * false positives. see | |
187 * https://bugs.eclipse.org/bugs/show_bug.cgi?id=98245 | |
188 */ | |
189 if (!canUndo | |
190 && this is fDocumentUndoManager.fHistory | |
191 .getUndoOperation(fDocumentUndoManager.fUndoContext) | |
192 // this is the latest operation | |
193 && this !is fDocumentUndoManager.fCurrent | |
194 // there is a more current operation not on the stack | |
195 && !fDocumentUndoManager.fCurrent.isValid() | |
196 // the current operation is not a valid document | |
197 // modification | |
198 && fDocumentUndoManager.fCurrent.fUndoModificationStamp !is | |
199 // the invalid current operation has a document stamp | |
200 IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP) { | |
201 canUndo= fDocumentUndoManager.fCurrent.fRedoModificationStamp is docStamp; | |
202 } | |
203 /* | |
204 * When the composite is the current operation, it may hold the | |
205 * timestamp of a no-op change. We check this here rather than | |
206 * in an override of canUndo() in UndoableCompoundTextChange simply to | |
207 * keep all the special case checks in one place. | |
208 */ | |
209 if (!canUndo | |
210 && this is fDocumentUndoManager.fHistory | |
211 .getUndoOperation(fDocumentUndoManager.fUndoContext) | |
212 && // this is the latest operation | |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
134
diff
changeset
|
213 null !is cast(UndoableCompoundTextChange)this |
129 | 214 && this is fDocumentUndoManager.fCurrent |
215 && // this is the current operation | |
216 this.fStart is -1 | |
217 && // the current operation text is not valid | |
218 fDocumentUndoManager.fCurrent.fRedoModificationStamp !is IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP) { | |
219 // but it has a redo stamp | |
220 canUndo= fDocumentUndoManager.fCurrent.fRedoModificationStamp is docStamp; | |
221 } | |
222 return canUndo; | |
223 | |
224 } | |
225 // if there is no timestamp to check, simply return true per the | |
226 // 3.0.1 behavior | |
227 return true; | |
228 } | |
229 return false; | |
230 } | |
231 | |
232 /* | |
233 * @see dwtx.core.commands.operations.IUndoableOperation#canRedo() | |
234 */ | |
235 public bool canRedo() { | |
236 if (isValid()) { | |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
134
diff
changeset
|
237 if (cast(IDocumentExtension4)fDocumentUndoManager.fDocument ) { |
134 | 238 long docStamp= (cast(IDocumentExtension4) fDocumentUndoManager.fDocument) |
129 | 239 .getModificationStamp(); |
240 return docStamp is IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP | |
241 || docStamp is getUndoModificationStamp(); | |
242 } | |
243 // if there is no timestamp to check, simply return true per the | |
244 // 3.0.1 behavior | |
245 return true; | |
246 } | |
247 return false; | |
248 } | |
249 | |
250 /* | |
251 * @see dwtx.core.commands.operations.IUndoableOperation#canExecute() | |
252 */ | |
253 public bool canExecute() { | |
254 return fDocumentUndoManager.isConnected(); | |
255 } | |
256 | |
257 /* | |
258 * @see dwtx.core.commands.operations.IUndoableOperation.IUndoableOperation#execute(IProgressMonitor, IAdaptable) | |
259 */ | |
260 public IStatus execute(IProgressMonitor monitor, IAdaptable uiInfo) { | |
261 // Text changes execute as they are typed, so executing one has no | |
262 // effect. | |
263 return Status.OK_STATUS; | |
264 } | |
265 | |
266 /** | |
267 * {@inheritDoc} | |
268 * Notifies clients about the undo. | |
269 */ | |
270 public IStatus undo(IProgressMonitor monitor, IAdaptable uiInfo) { | |
271 if (isValid()) { | |
161 | 272 fDocumentUndoManager.fireDocumentUndo(fStart, fPreservedText, fText, cast(Object)uiInfo, DocumentUndoEvent.ABOUT_TO_UNDO, false); |
129 | 273 undoTextChange(); |
274 fDocumentUndoManager.resetProcessChangeState(); | |
161 | 275 fDocumentUndoManager.fireDocumentUndo(fStart, fPreservedText, fText, cast(Object)uiInfo, DocumentUndoEvent.UNDONE, false); |
129 | 276 return Status.OK_STATUS; |
277 } | |
278 return IOperationHistory.OPERATION_INVALID_STATUS; | |
279 } | |
280 | |
281 /** | |
282 * Re-applies the change described by this change. | |
283 */ | |
284 protected void redoTextChange() { | |
285 try { | |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
134
diff
changeset
|
286 if (cast(IDocumentExtension4)fDocumentUndoManager.fDocument) |
134 | 287 (cast(IDocumentExtension4) fDocumentUndoManager.fDocument).replace(fStart, fEnd - fStart, fText, fRedoModificationStamp); |
129 | 288 else |
289 fDocumentUndoManager.fDocument.replace(fStart, fEnd - fStart, fText); | |
290 } catch (BadLocationException x) { | |
291 } | |
292 } | |
293 | |
294 /** | |
295 * Re-applies the change described by this change that was previously | |
296 * undone. Also notifies clients about the redo. | |
130 | 297 * |
129 | 298 * @param monitor the progress monitor to use if necessary |
299 * @param uiInfo an adaptable that can provide UI info if needed | |
300 * @return the status | |
301 */ | |
302 public IStatus redo(IProgressMonitor monitor, IAdaptable uiInfo) { | |
303 if (isValid()) { | |
161 | 304 fDocumentUndoManager.fireDocumentUndo(fStart, fText, fPreservedText, cast(Object)uiInfo, DocumentUndoEvent.ABOUT_TO_REDO, false); |
129 | 305 redoTextChange(); |
306 fDocumentUndoManager.resetProcessChangeState(); | |
161 | 307 fDocumentUndoManager.fireDocumentUndo(fStart, fText, fPreservedText, cast(Object)uiInfo, DocumentUndoEvent.REDONE, false); |
129 | 308 return Status.OK_STATUS; |
309 } | |
310 return IOperationHistory.OPERATION_INVALID_STATUS; | |
311 } | |
312 | |
313 /** | |
314 * Update the change in response to a commit. | |
315 */ | |
316 | |
317 protected void updateTextChange() { | |
318 fText= fDocumentUndoManager.fTextBuffer.toString(); | |
161 | 319 fDocumentUndoManager.fTextBuffer.clear(); |
129 | 320 fPreservedText= fDocumentUndoManager.fPreservedTextBuffer.toString(); |
161 | 321 fDocumentUndoManager.fPreservedTextBuffer.clear(); |
129 | 322 } |
323 | |
324 /** | |
325 * Creates a new uncommitted text change depending on whether a compound | |
326 * change is currently being executed. | |
130 | 327 * |
129 | 328 * @return a new, uncommitted text change or a compound text change |
329 */ | |
330 protected UndoableTextChange createCurrent() { | |
331 if (fDocumentUndoManager.fFoldingIntoCompoundChange) | |
332 return new UndoableCompoundTextChange(fDocumentUndoManager); | |
333 return new UndoableTextChange(fDocumentUndoManager); | |
334 } | |
335 | |
336 /** | |
337 * Commits the current change into this one. | |
338 */ | |
339 protected void commit() { | |
340 if (fStart < 0) { | |
341 if (fDocumentUndoManager.fFoldingIntoCompoundChange) { | |
342 fDocumentUndoManager.fCurrent= createCurrent(); | |
343 } else { | |
344 reinitialize(); | |
345 } | |
346 } else { | |
347 updateTextChange(); | |
348 fDocumentUndoManager.fCurrent= createCurrent(); | |
349 } | |
350 fDocumentUndoManager.resetProcessChangeState(); | |
351 } | |
352 | |
353 /** | |
354 * Updates the text from the buffers without resetting the buffers or adding | |
355 * anything to the stack. | |
356 */ | |
357 protected void pretendCommit() { | |
358 if (fStart > -1) { | |
359 fText= fDocumentUndoManager.fTextBuffer.toString(); | |
360 fPreservedText= fDocumentUndoManager.fPreservedTextBuffer.toString(); | |
361 } | |
362 } | |
363 | |
364 /** | |
365 * Attempt a commit of this change and answer true if a new fCurrent was | |
366 * created as a result of the commit. | |
130 | 367 * |
129 | 368 * @return <code>true</code> if the change was committed and created |
369 * a new <code>fCurrent</code>, <code>false</code> if not | |
370 */ | |
371 protected bool attemptCommit() { | |
372 pretendCommit(); | |
373 if (isValid()) { | |
374 fDocumentUndoManager.commit(); | |
375 return true; | |
376 } | |
377 return false; | |
378 } | |
379 | |
380 /** | |
381 * Checks whether this text change is valid for undo or redo. | |
130 | 382 * |
129 | 383 * @return <code>true</code> if the change is valid for undo or redo |
384 */ | |
385 protected bool isValid() { | |
386 return fStart > -1 && fEnd > -1 && fText !is null; | |
387 } | |
388 | |
389 /* | |
390 * @see java.lang.Object#toString() | |
391 */ | |
160 | 392 public override String toString() { |
129 | 393 String delimiter= ", "; //$NON-NLS-1$ |
394 StringBuffer text= new StringBuffer(super.toString()); | |
395 text.append("\n"); //$NON-NLS-1$ | |
161 | 396 text.append(this.classinfo.name); |
129 | 397 text.append(" undo modification stamp: "); //$NON-NLS-1$ |
398 text.append(fUndoModificationStamp); | |
399 text.append(" redo modification stamp: "); //$NON-NLS-1$ | |
400 text.append(fRedoModificationStamp); | |
401 text.append(" start: "); //$NON-NLS-1$ | |
402 text.append(fStart); | |
403 text.append(delimiter); | |
404 text.append("end: "); //$NON-NLS-1$ | |
405 text.append(fEnd); | |
406 text.append(delimiter); | |
407 text.append("text: '"); //$NON-NLS-1$ | |
408 text.append(fText); | |
409 text.append('\''); | |
410 text.append(delimiter); | |
411 text.append("preservedText: '"); //$NON-NLS-1$ | |
412 text.append(fPreservedText); | |
413 text.append('\''); | |
414 return text.toString(); | |
415 } | |
416 | |
417 /** | |
418 * Return the undo modification stamp | |
130 | 419 * |
129 | 420 * @return the undo modification stamp for this change |
421 */ | |
422 protected long getUndoModificationStamp() { | |
423 return fUndoModificationStamp; | |
424 } | |
425 | |
426 /** | |
427 * Return the redo modification stamp | |
130 | 428 * |
129 | 429 * @return the redo modification stamp for this change |
430 */ | |
431 protected long getRedoModificationStamp() { | |
432 return fRedoModificationStamp; | |
433 } | |
434 } | |
130 | 435 |
436 | |
129 | 437 /** |
438 * Represents an undo-able text change consisting of several individual | |
439 * changes. | |
440 */ | |
441 private static class UndoableCompoundTextChange : UndoableTextChange { | |
442 | |
443 /** The list of individual changes */ | |
161 | 444 private List fChanges; |
129 | 445 |
446 /** | |
447 * Creates a new compound text change. | |
130 | 448 * |
129 | 449 * @param manager |
450 * the undo manager for this change | |
451 */ | |
130 | 452 this(DocumentUndoManager manager) { |
161 | 453 fChanges= new ArrayList(); |
129 | 454 super(manager); |
455 } | |
456 | |
457 /** | |
458 * Adds a new individual change to this compound change. | |
130 | 459 * |
129 | 460 * @param change the change to be added |
461 */ | |
462 protected void add(UndoableTextChange change) { | |
463 fChanges.add(change); | |
464 } | |
465 | |
466 /* | |
467 * @see dwtx.text.undo.UndoableTextChange#undo(dwtx.core.runtime.IProgressMonitor, dwtx.core.runtime.IAdaptable) | |
468 */ | |
469 public IStatus undo(IProgressMonitor monitor, IAdaptable uiInfo) { | |
470 | |
471 int size= fChanges.size(); | |
472 if (size > 0) { | |
473 UndoableTextChange c; | |
474 | |
134 | 475 c= cast(UndoableTextChange) fChanges.get(0); |
161 | 476 fDocumentUndoManager.fireDocumentUndo(c.fStart, c.fPreservedText, c.fText, cast(Object)uiInfo, DocumentUndoEvent.ABOUT_TO_UNDO, true); |
129 | 477 |
478 for (int i= size - 1; i >= 0; --i) { | |
134 | 479 c= cast(UndoableTextChange) fChanges.get(i); |
129 | 480 c.undoTextChange(); |
481 } | |
482 fDocumentUndoManager.resetProcessChangeState(); | |
161 | 483 fDocumentUndoManager.fireDocumentUndo(c.fStart, c.fPreservedText, c.fText, cast(Object)uiInfo, |
129 | 484 DocumentUndoEvent.UNDONE, true); |
485 } | |
486 return Status.OK_STATUS; | |
487 } | |
488 | |
489 /* | |
490 * @see dwtx.text.undo.UndoableTextChange#redo(dwtx.core.runtime.IProgressMonitor, dwtx.core.runtime.IAdaptable) | |
491 */ | |
492 public IStatus redo(IProgressMonitor monitor, IAdaptable uiInfo) { | |
493 | |
494 int size= fChanges.size(); | |
495 if (size > 0) { | |
496 | |
497 UndoableTextChange c; | |
134 | 498 c= cast(UndoableTextChange) fChanges.get(size - 1); |
161 | 499 fDocumentUndoManager.fireDocumentUndo(c.fStart, c.fText, c.fPreservedText, cast(Object)uiInfo, DocumentUndoEvent.ABOUT_TO_REDO, true); |
129 | 500 |
501 for (int i= 0; i <= size - 1; ++i) { | |
134 | 502 c= cast(UndoableTextChange) fChanges.get(i); |
129 | 503 c.redoTextChange(); |
504 } | |
505 fDocumentUndoManager.resetProcessChangeState(); | |
161 | 506 fDocumentUndoManager.fireDocumentUndo(c.fStart, c.fText, c.fPreservedText, cast(Object)uiInfo, DocumentUndoEvent.REDONE, true); |
129 | 507 } |
508 | |
509 return Status.OK_STATUS; | |
510 } | |
511 | |
512 /* | |
513 * @see dwtx.text.undo.UndoableTextChange#updateTextChange() | |
514 */ | |
515 protected void updateTextChange() { | |
516 // first gather the data from the buffers | |
517 super.updateTextChange(); | |
518 | |
519 // the result of the update is stored as a child change | |
520 UndoableTextChange c= new UndoableTextChange(fDocumentUndoManager); | |
521 c.fStart= fStart; | |
522 c.fEnd= fEnd; | |
523 c.fText= fText; | |
524 c.fPreservedText= fPreservedText; | |
525 c.fUndoModificationStamp= fUndoModificationStamp; | |
526 c.fRedoModificationStamp= fRedoModificationStamp; | |
527 add(c); | |
528 | |
529 // clear out all indexes now that the child is added | |
530 reinitialize(); | |
531 } | |
532 | |
533 /* | |
534 * @see dwtx.text.undo.UndoableTextChange#createCurrent() | |
535 */ | |
536 protected UndoableTextChange createCurrent() { | |
537 | |
538 if (!fDocumentUndoManager.fFoldingIntoCompoundChange) | |
539 return new UndoableTextChange(fDocumentUndoManager); | |
540 | |
541 reinitialize(); | |
542 return this; | |
543 } | |
544 | |
545 /* | |
546 * @see dwtx.text.undo.UndoableTextChange#commit() | |
547 */ | |
548 protected void commit() { | |
549 // if there is pending data, update the text change | |
550 if (fStart > -1) | |
551 updateTextChange(); | |
552 fDocumentUndoManager.fCurrent= createCurrent(); | |
553 fDocumentUndoManager.resetProcessChangeState(); | |
554 } | |
130 | 555 |
129 | 556 /* |
557 * @see dwtx.text.undo.UndoableTextChange#isValid() | |
558 */ | |
559 protected bool isValid() { | |
560 return fStart > -1 || fChanges.size() > 0; | |
561 } | |
562 | |
563 /* | |
564 * @see dwtx.text.undo.UndoableTextChange#getUndoModificationStamp() | |
565 */ | |
566 protected long getUndoModificationStamp() { | |
567 if (fStart > -1) | |
568 return super.getUndoModificationStamp(); | |
569 else if (fChanges.size() > 0) | |
134 | 570 return (cast(UndoableTextChange) fChanges.get(0)) |
129 | 571 .getUndoModificationStamp(); |
572 | |
573 return fUndoModificationStamp; | |
574 } | |
575 | |
576 /* | |
577 * @see dwtx.text.undo.UndoableTextChange#getRedoModificationStamp() | |
578 */ | |
579 protected long getRedoModificationStamp() { | |
580 if (fStart > -1) | |
581 return super.getRedoModificationStamp(); | |
582 else if (fChanges.size() > 0) | |
134 | 583 return (cast(UndoableTextChange) fChanges.get(fChanges.size() - 1)) |
129 | 584 .getRedoModificationStamp(); |
585 | |
586 return fRedoModificationStamp; | |
587 } | |
588 } | |
130 | 589 |
129 | 590 |
591 /** | |
592 * Internal listener to document changes. | |
593 */ | |
594 private class DocumentListener : IDocumentListener { | |
595 | |
596 private String fReplacedText; | |
597 | |
598 /* | |
599 * @see dwtx.jface.text.IDocumentListener#documentAboutToBeChanged(dwtx.jface.text.DocumentEvent) | |
600 */ | |
601 public void documentAboutToBeChanged(DocumentEvent event) { | |
602 try { | |
603 fReplacedText= event.getDocument().get(event.getOffset(), | |
604 event.getLength()); | |
605 fPreservedUndoModificationStamp= event.getModificationStamp(); | |
606 } catch (BadLocationException x) { | |
607 fReplacedText= null; | |
608 } | |
609 } | |
610 | |
611 /* | |
612 * @see dwtx.jface.text.IDocumentListener#documentChanged(dwtx.jface.text.DocumentEvent) | |
613 */ | |
614 public void documentChanged(DocumentEvent event) { | |
615 fPreservedRedoModificationStamp= event.getModificationStamp(); | |
616 | |
617 // record the current valid state for the top operation in case it | |
618 // remains the | |
619 // top operation but changes state. | |
620 IUndoableOperation op= fHistory.getUndoOperation(fUndoContext); | |
621 bool wasValid= false; | |
622 if (op !is null) | |
623 wasValid= op.canUndo(); | |
624 // Process the change, providing the before and after timestamps | |
625 processChange(event.getOffset(), event.getOffset() | |
626 + event.getLength(), event.getText(), fReplacedText, | |
627 fPreservedUndoModificationStamp, | |
628 fPreservedRedoModificationStamp); | |
629 | |
630 // now update fCurrent with the latest buffers from the document | |
631 // change. | |
632 fCurrent.pretendCommit(); | |
633 | |
634 if (op is fCurrent) { | |
635 // if the document change did not cause a new fCurrent to be | |
636 // created, then we should | |
637 // notify the history that the current operation changed if its | |
638 // validity has changed. | |
639 if (wasValid !is fCurrent.isValid()) | |
640 fHistory.operationChanged(op); | |
641 } else { | |
642 // if the change created a new fCurrent that we did not yet add | |
643 // to the | |
644 // stack, do so if it's valid and we are not in the middle of a | |
645 // compound change. | |
646 if (fCurrent !is fLastAddedTextEdit && fCurrent.isValid()) { | |
647 addToOperationHistory(fCurrent); | |
648 } | |
649 } | |
650 } | |
651 } | |
652 | |
653 /* | |
654 * @see IOperationHistoryListener | |
655 */ | |
656 private class HistoryListener : IOperationHistoryListener { | |
130 | 657 |
129 | 658 private IUndoableOperation fOperation; |
659 | |
159 | 660 public void historyNotification(OperationHistoryEvent event) { |
129 | 661 final int type= event.getEventType(); |
662 switch (type) { | |
663 case OperationHistoryEvent.ABOUT_TO_UNDO: | |
664 case OperationHistoryEvent.ABOUT_TO_REDO: | |
665 // if this is one of our operations | |
666 if (event.getOperation().hasContext(fUndoContext)) { | |
667 // if we are undoing/redoing an operation we generated, then | |
668 // ignore | |
669 // the document changes associated with this undo or redo. | |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
134
diff
changeset
|
670 if (cast(UndoableTextChange)event.getOperation() ) { |
129 | 671 listenToTextChanges(false); |
672 | |
673 // in the undo case only, make sure compounds are closed | |
674 if (type is OperationHistoryEvent.ABOUT_TO_UNDO) { | |
675 if (fFoldingIntoCompoundChange) { | |
676 endCompoundChange(); | |
677 } | |
678 } | |
679 } else { | |
680 // the undo or redo has our context, but it is not one | |
681 // of our edits. We will listen to the changes, but will | |
682 // reset the state that tracks the undo/redo history. | |
683 commit(); | |
684 fLastAddedTextEdit= null; | |
685 } | |
686 fOperation= event.getOperation(); | |
687 } | |
688 break; | |
689 case OperationHistoryEvent.UNDONE: | |
690 case OperationHistoryEvent.REDONE: | |
691 case OperationHistoryEvent.OPERATION_NOT_OK: | |
692 if (event.getOperation() is fOperation) { | |
693 listenToTextChanges(true); | |
694 fOperation= null; | |
695 } | |
696 break; | |
697 } | |
698 } | |
699 | |
700 } | |
701 | |
702 | |
703 /** | |
704 * The undo context for this document undo manager. | |
705 */ | |
706 private ObjectUndoContext fUndoContext; | |
707 | |
708 /** | |
709 * The document whose changes are being tracked. | |
710 */ | |
711 private IDocument fDocument; | |
712 | |
713 /** | |
714 * The currently constructed edit. | |
715 */ | |
716 private UndoableTextChange fCurrent; | |
717 | |
718 /** | |
719 * The internal document listener. | |
720 */ | |
721 private DocumentListener fDocumentListener; | |
722 | |
723 /** | |
724 * Indicates whether the current change belongs to a compound change. | |
725 */ | |
726 private bool fFoldingIntoCompoundChange= false; | |
727 | |
728 /** | |
729 * The operation history being used to store the undo history. | |
730 */ | |
731 private IOperationHistory fHistory; | |
732 | |
733 /** | |
734 * The operation history listener used for managing undo and redo before and | |
735 * after the individual edits are performed. | |
736 */ | |
737 private IOperationHistoryListener fHistoryListener; | |
738 | |
739 /** | |
740 * The text edit last added to the operation history. This must be tracked | |
741 * internally instead of asking the history, since outside parties may be | |
742 * placing items on our undo/redo history. | |
743 */ | |
744 private UndoableTextChange fLastAddedTextEdit= null; | |
745 | |
746 /** | |
747 * The document modification stamp for redo. | |
748 */ | |
749 private long fPreservedRedoModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP; | |
750 | |
751 /** | |
752 * Text buffer to collect viewer content which has been replaced | |
753 */ | |
754 private StringBuffer fPreservedTextBuffer; | |
755 | |
756 /** | |
757 * The document modification stamp for undo. | |
758 */ | |
759 private long fPreservedUndoModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP; | |
760 | |
761 /** | |
762 * The last delete text edit. | |
763 */ | |
764 private UndoableTextChange fPreviousDelete; | |
765 | |
766 /** | |
767 * Text buffer to collect text which is inserted into the viewer | |
768 */ | |
769 private StringBuffer fTextBuffer; | |
770 | |
771 /** Indicates inserting state. */ | |
772 private bool fInserting= false; | |
773 | |
774 /** Indicates overwriting state. */ | |
775 private bool fOverwriting= false; | |
776 | |
777 /** The registered document listeners. */ | |
778 private ListenerList fDocumentUndoListeners; | |
779 | |
780 /** The list of clients connected. */ | |
781 private List fConnected; | |
782 | |
783 /** | |
130 | 784 * |
129 | 785 * Create a DocumentUndoManager for the given document. |
130 | 786 * |
129 | 787 * @param document the document whose undo history is being managed. |
788 */ | |
130 | 789 public this(IDocument document) { |
161 | 790 // super(); |
791 Assert.isNotNull(cast(Object)document); | |
129 | 792 fDocument= document; |
793 fHistory= OperationHistoryFactory.getOperationHistory(); | |
161 | 794 fUndoContext= new ObjectUndoContext(cast(Object)fDocument); |
129 | 795 fConnected= new ArrayList(); |
796 fDocumentUndoListeners= new ListenerList(ListenerList.IDENTITY); | |
797 } | |
798 | |
799 /* | |
800 * @see dwtx.jface.text.IDocumentUndoManager#addDocumentUndoListener(dwtx.jface.text.IDocumentUndoListener) | |
801 */ | |
802 public void addDocumentUndoListener(IDocumentUndoListener listener) { | |
161 | 803 fDocumentUndoListeners.add(cast(Object)listener); |
129 | 804 } |
805 | |
806 /* | |
807 * @see dwtx.jface.text.IDocumentUndoManager#removeDocumentUndoListener(dwtx.jface.text.IDocumentUndoListener) | |
808 */ | |
809 public void removeDocumentUndoListener(IDocumentUndoListener listener) { | |
161 | 810 fDocumentUndoListeners.remove(cast(Object)listener); |
129 | 811 } |
812 | |
813 /* | |
814 * @see dwtx.jface.text.IDocumentUndoManager#getUndoContext() | |
815 */ | |
816 public IUndoContext getUndoContext() { | |
817 return fUndoContext; | |
818 } | |
819 | |
820 /* | |
821 * @see dwtx.jface.text.IDocumentUndoManager#commit() | |
822 */ | |
823 public void commit() { | |
824 // if fCurrent has never been placed on the history, do so now. | |
825 // this can happen when there are multiple programmatically commits in a | |
826 // single document change. | |
827 if (fLastAddedTextEdit !is fCurrent) { | |
828 fCurrent.pretendCommit(); | |
829 if (fCurrent.isValid()) | |
830 addToOperationHistory(fCurrent); | |
831 } | |
832 fCurrent.commit(); | |
833 } | |
130 | 834 |
129 | 835 /* |
836 * @see dwtx.text.undo.IDocumentUndoManager#reset() | |
837 */ | |
838 public void reset() { | |
839 if (isConnected()) { | |
840 shutdown(); | |
841 initialize(); | |
842 } | |
843 } | |
844 | |
845 /* | |
846 * @see dwtx.text.undo.IDocumentUndoManager#redoable() | |
847 */ | |
848 public bool redoable() { | |
849 return OperationHistoryFactory.getOperationHistory().canRedo(fUndoContext); | |
850 } | |
851 | |
852 /* | |
853 * @see dwtx.text.undo.IDocumentUndoManager#undoable() | |
854 */ | |
855 public bool undoable() { | |
856 return OperationHistoryFactory.getOperationHistory().canUndo(fUndoContext); | |
857 } | |
858 | |
859 /* | |
860 * @see dwtx.text.undo.IDocumentUndoManager#undo() | |
861 */ | |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
134
diff
changeset
|
862 public void redo() { |
129 | 863 if (isConnected() && redoable()) |
864 OperationHistoryFactory.getOperationHistory().redo(getUndoContext(), null, null); | |
865 } | |
866 | |
867 /* | |
868 * @see dwtx.text.undo.IDocumentUndoManager#undo() | |
869 */ | |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
134
diff
changeset
|
870 public void undo() { |
129 | 871 if (undoable()) |
872 OperationHistoryFactory.getOperationHistory().undo(fUndoContext, null, null); | |
873 } | |
874 | |
875 /* | |
876 * @see dwtx.jface.text.IDocumentUndoManager#connect(java.lang.Object) | |
877 */ | |
878 public void connect(Object client) { | |
879 if (!isConnected()) { | |
880 initialize(); | |
881 } | |
882 if (!fConnected.contains(client)) | |
883 fConnected.add(client); | |
884 } | |
885 | |
886 /* | |
887 * @see dwtx.jface.text.IDocumentUndoManager#disconnect(java.lang.Object) | |
888 */ | |
889 public void disconnect(Object client) { | |
890 fConnected.remove(client); | |
891 if (!isConnected()) { | |
892 shutdown(); | |
893 } | |
894 } | |
895 | |
896 /* | |
897 * @see dwtx.jface.text.IDocumentUndoManager#beginCompoundChange() | |
898 */ | |
899 public void beginCompoundChange() { | |
900 if (isConnected()) { | |
901 fFoldingIntoCompoundChange= true; | |
902 commit(); | |
903 } | |
904 } | |
905 | |
906 /* | |
907 * @see dwtx.jface.text.IDocumentUndoManager#endCompoundChange() | |
908 */ | |
909 public void endCompoundChange() { | |
910 if (isConnected()) { | |
911 fFoldingIntoCompoundChange= false; | |
912 commit(); | |
913 } | |
914 } | |
915 | |
916 /* | |
917 * @see dwtx.jface.text.IDocumentUndoManager#setUndoLimit(int) | |
918 */ | |
919 public void setMaximalUndoLevel(int undoLimit) { | |
920 fHistory.setLimit(fUndoContext, undoLimit); | |
921 } | |
922 | |
923 /** | |
924 * Fires a document undo event to all registered document undo listeners. | |
925 * Uses a robust iterator. | |
130 | 926 * |
129 | 927 * @param offset the document offset |
928 * @param text the text that was inserted | |
929 * @param preservedText the text being replaced | |
930 * @param source the source which triggered the event | |
931 * @param eventType the type of event causing the change | |
932 * @param isCompound a flag indicating whether the change is a compound change | |
933 * @see IDocumentUndoListener | |
934 */ | |
935 void fireDocumentUndo(int offset, String text, String preservedText, Object source, int eventType, bool isCompound) { | |
936 eventType= isCompound ? eventType | DocumentUndoEvent.COMPOUND : eventType; | |
937 DocumentUndoEvent event= new DocumentUndoEvent(fDocument, offset, text, preservedText, eventType, source); | |
938 Object[] listeners= fDocumentUndoListeners.getListeners(); | |
939 for (int i= 0; i < listeners.length; i++) { | |
134 | 940 (cast(IDocumentUndoListener)listeners[i]).documentUndoNotification(event); |
129 | 941 } |
942 } | |
943 | |
944 /** | |
945 * Adds any listeners needed to track the document and the operations | |
946 * history. | |
947 */ | |
948 private void addListeners() { | |
949 fHistoryListener= new HistoryListener(); | |
950 fHistory.addOperationHistoryListener(fHistoryListener); | |
951 listenToTextChanges(true); | |
952 } | |
953 | |
954 /** | |
955 * Removes any listeners that were installed by the document. | |
956 */ | |
957 private void removeListeners() { | |
958 listenToTextChanges(false); | |
959 fHistory.removeOperationHistoryListener(fHistoryListener); | |
960 fHistoryListener= null; | |
961 } | |
962 | |
963 /** | |
964 * Adds the given text edit to the operation history if it is not part of a | |
965 * compound change. | |
130 | 966 * |
129 | 967 * @param edit |
968 * the edit to be added | |
969 */ | |
970 private void addToOperationHistory(UndoableTextChange edit) { | |
971 if (!fFoldingIntoCompoundChange | |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
134
diff
changeset
|
972 || cast(UndoableCompoundTextChange)edit ) { |
129 | 973 fHistory.add(edit); |
974 fLastAddedTextEdit= edit; | |
975 } | |
976 } | |
977 | |
978 /** | |
979 * Disposes the undo history. | |
980 */ | |
981 private void disposeUndoHistory() { | |
982 fHistory.dispose(fUndoContext, true, true, true); | |
983 } | |
984 | |
985 /** | |
986 * Initializes the undo history. | |
987 */ | |
988 private void initializeUndoHistory() { | |
989 if (fHistory !is null && fUndoContext !is null) | |
990 fHistory.dispose(fUndoContext, true, true, false); | |
991 | |
992 } | |
993 | |
994 /** | |
995 * Checks whether the given text starts with a line delimiter and | |
996 * subsequently contains a white space only. | |
130 | 997 * |
129 | 998 * @param text the text to check |
999 * @return <code>true</code> if the text is a line delimiter followed by | |
1000 * whitespace, <code>false</code> otherwise | |
1001 */ | |
1002 private bool isWhitespaceText(String text) { | |
1003 | |
1004 if (text is null || text.length() is 0) | |
1005 return false; | |
1006 | |
1007 String[] delimiters= fDocument.getLegalLineDelimiters(); | |
1008 int index= TextUtilities.startsWith(delimiters, text); | |
1009 if (index > -1) { | |
1010 char c; | |
1011 int length= text.length(); | |
161 | 1012 for (int i= delimiters[index].length; i < length; i++) { |
129 | 1013 c= text.charAt(i); |
1014 if (c !is ' ' && c !is '\t') | |
1015 return false; | |
1016 } | |
1017 return true; | |
1018 } | |
1019 | |
1020 return false; | |
1021 } | |
1022 | |
1023 /** | |
1024 * Switches the state of whether there is a text listener or not. | |
130 | 1025 * |
129 | 1026 * @param listen the state which should be established |
1027 */ | |
1028 private void listenToTextChanges(bool listen) { | |
1029 if (listen) { | |
1030 if (fDocumentListener is null && fDocument !is null) { | |
1031 fDocumentListener= new DocumentListener(); | |
1032 fDocument.addDocumentListener(fDocumentListener); | |
1033 } | |
1034 } else if (!listen) { | |
1035 if (fDocumentListener !is null && fDocument !is null) { | |
1036 fDocument.removeDocumentListener(fDocumentListener); | |
1037 fDocumentListener= null; | |
1038 } | |
1039 } | |
1040 } | |
1041 | |
1042 private void processChange(int modelStart, int modelEnd, | |
1043 String insertedText, String replacedText, | |
1044 long beforeChangeModificationStamp, | |
1045 long afterChangeModificationStamp) { | |
1046 | |
1047 if (insertedText is null) | |
1048 insertedText= ""; //$NON-NLS-1$ | |
1049 | |
1050 if (replacedText is null) | |
1051 replacedText= ""; //$NON-NLS-1$ | |
1052 | |
1053 int length= insertedText.length(); | |
1054 int diff= modelEnd - modelStart; | |
1055 | |
1056 if (fCurrent.fUndoModificationStamp is IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP) | |
1057 fCurrent.fUndoModificationStamp= beforeChangeModificationStamp; | |
1058 | |
1059 // normalize | |
1060 if (diff < 0) { | |
1061 int tmp= modelEnd; | |
1062 modelEnd= modelStart; | |
1063 modelStart= tmp; | |
1064 } | |
1065 | |
1066 if (modelStart is modelEnd) { | |
1067 // text will be inserted | |
1068 if ((length is 1) || isWhitespaceText(insertedText)) { | |
1069 // by typing or whitespace | |
1070 if (!fInserting | |
1071 || (modelStart !is fCurrent.fStart | |
1072 + fTextBuffer.length())) { | |
1073 fCurrent.fRedoModificationStamp= beforeChangeModificationStamp; | |
1074 if (fCurrent.attemptCommit()) | |
1075 fCurrent.fUndoModificationStamp= beforeChangeModificationStamp; | |
1076 | |
1077 fInserting= true; | |
1078 } | |
1079 if (fCurrent.fStart < 0) | |
1080 fCurrent.fStart= fCurrent.fEnd= modelStart; | |
1081 if (length > 0) | |
1082 fTextBuffer.append(insertedText); | |
1083 } else if (length > 0) { | |
1084 // by pasting or model manipulation | |
1085 fCurrent.fRedoModificationStamp= beforeChangeModificationStamp; | |
1086 if (fCurrent.attemptCommit()) | |
1087 fCurrent.fUndoModificationStamp= beforeChangeModificationStamp; | |
1088 | |
1089 fCurrent.fStart= fCurrent.fEnd= modelStart; | |
1090 fTextBuffer.append(insertedText); | |
1091 fCurrent.fRedoModificationStamp= afterChangeModificationStamp; | |
1092 if (fCurrent.attemptCommit()) | |
1093 fCurrent.fUndoModificationStamp= afterChangeModificationStamp; | |
1094 | |
1095 } | |
1096 } else { | |
1097 if (length is 0) { | |
1098 // text will be deleted by backspace or DEL key or empty | |
1099 // clipboard | |
161 | 1100 length= replacedText.length; |
129 | 1101 String[] delimiters= fDocument.getLegalLineDelimiters(); |
1102 | |
1103 if ((length is 1) | |
1104 || TextUtilities.equals(delimiters, replacedText) > -1) { | |
1105 | |
1106 // whereby selection is empty | |
1107 | |
1108 if (fPreviousDelete.fStart is modelStart | |
1109 && fPreviousDelete.fEnd is modelEnd) { | |
1110 // repeated DEL | |
1111 | |
1112 // correct wrong settings of fCurrent | |
1113 if (fCurrent.fStart is modelEnd | |
1114 && fCurrent.fEnd is modelStart) { | |
1115 fCurrent.fStart= modelStart; | |
1116 fCurrent.fEnd= modelEnd; | |
1117 } | |
1118 // append to buffer && extend edit range | |
1119 fPreservedTextBuffer.append(replacedText); | |
1120 ++fCurrent.fEnd; | |
1121 | |
1122 } else if (fPreviousDelete.fStart is modelEnd) { | |
1123 // repeated backspace | |
1124 | |
1125 // insert in buffer and extend edit range | |
161 | 1126 fPreservedTextBuffer.select(0,0); |
1127 fPreservedTextBuffer.replace(replacedText); | |
129 | 1128 fCurrent.fStart= modelStart; |
1129 | |
1130 } else { | |
1131 // either DEL or backspace for the first time | |
1132 | |
1133 fCurrent.fRedoModificationStamp= beforeChangeModificationStamp; | |
1134 if (fCurrent.attemptCommit()) | |
1135 fCurrent.fUndoModificationStamp= beforeChangeModificationStamp; | |
1136 | |
1137 // as we can not decide whether it was DEL or backspace | |
1138 // we initialize for backspace | |
1139 fPreservedTextBuffer.append(replacedText); | |
1140 fCurrent.fStart= modelStart; | |
1141 fCurrent.fEnd= modelEnd; | |
1142 } | |
1143 | |
1144 fPreviousDelete.set(modelStart, modelEnd); | |
1145 | |
1146 } else if (length > 0) { | |
1147 // whereby selection is not empty | |
1148 fCurrent.fRedoModificationStamp= beforeChangeModificationStamp; | |
1149 if (fCurrent.attemptCommit()) | |
1150 fCurrent.fUndoModificationStamp= beforeChangeModificationStamp; | |
1151 | |
1152 fCurrent.fStart= modelStart; | |
1153 fCurrent.fEnd= modelEnd; | |
1154 fPreservedTextBuffer.append(replacedText); | |
1155 } | |
1156 } else { | |
1157 // text will be replaced | |
1158 | |
1159 if (length is 1) { | |
161 | 1160 length= replacedText.length; |
129 | 1161 String[] delimiters= fDocument.getLegalLineDelimiters(); |
1162 | |
1163 if ((length is 1) | |
1164 || TextUtilities.equals(delimiters, replacedText) > -1) { | |
1165 // because of overwrite mode or model manipulation | |
1166 if (!fOverwriting | |
1167 || (modelStart !is fCurrent.fStart | |
1168 + fTextBuffer.length())) { | |
1169 fCurrent.fRedoModificationStamp= beforeChangeModificationStamp; | |
1170 if (fCurrent.attemptCommit()) | |
1171 fCurrent.fUndoModificationStamp= beforeChangeModificationStamp; | |
1172 | |
1173 fOverwriting= true; | |
1174 } | |
1175 | |
1176 if (fCurrent.fStart < 0) | |
1177 fCurrent.fStart= modelStart; | |
1178 | |
1179 fCurrent.fEnd= modelEnd; | |
1180 fTextBuffer.append(insertedText); | |
1181 fPreservedTextBuffer.append(replacedText); | |
1182 fCurrent.fRedoModificationStamp= afterChangeModificationStamp; | |
1183 return; | |
1184 } | |
1185 } | |
1186 // because of typing or pasting whereby selection is not empty | |
1187 fCurrent.fRedoModificationStamp= beforeChangeModificationStamp; | |
1188 if (fCurrent.attemptCommit()) | |
1189 fCurrent.fUndoModificationStamp= beforeChangeModificationStamp; | |
1190 | |
1191 fCurrent.fStart= modelStart; | |
1192 fCurrent.fEnd= modelEnd; | |
1193 fTextBuffer.append(insertedText); | |
1194 fPreservedTextBuffer.append(replacedText); | |
1195 } | |
1196 } | |
1197 // in all cases, the redo modification stamp is updated on the open | |
1198 // text edit | |
1199 fCurrent.fRedoModificationStamp= afterChangeModificationStamp; | |
1200 } | |
1201 | |
1202 /** | |
1203 * Initialize the receiver. | |
1204 */ | |
1205 private void initialize() { | |
1206 initializeUndoHistory(); | |
1207 | |
1208 // open up the current text edit | |
1209 fCurrent= new UndoableTextChange(this); | |
1210 fPreviousDelete= new UndoableTextChange(this); | |
1211 fTextBuffer= new StringBuffer(); | |
1212 fPreservedTextBuffer= new StringBuffer(); | |
1213 | |
1214 addListeners(); | |
1215 } | |
130 | 1216 |
129 | 1217 /** |
1218 * Reset processChange state. | |
130 | 1219 * |
129 | 1220 * @since 3.2 |
1221 */ | |
1222 private void resetProcessChangeState() { | |
1223 fInserting= false; | |
1224 fOverwriting= false; | |
1225 fPreviousDelete.reinitialize(); | |
1226 } | |
1227 | |
1228 /** | |
1229 * Shutdown the receiver. | |
1230 */ | |
1231 private void shutdown() { | |
1232 removeListeners(); | |
1233 | |
1234 fCurrent= null; | |
1235 fPreviousDelete= null; | |
161 | 1236 fTextBuffer.clear(); |
1237 fPreservedTextBuffer.clear(); | |
129 | 1238 |
1239 disposeUndoHistory(); | |
1240 } | |
1241 | |
1242 /** | |
1243 * Return whether or not any clients are connected to the receiver. | |
130 | 1244 * |
129 | 1245 * @return <code>true</code> if the receiver is connected to |
1246 * clients, <code>false</code> if it is not | |
1247 */ | |
1248 bool isConnected() { | |
1249 if (fConnected is null) | |
1250 return false; | |
1251 return !fConnected.isEmpty(); | |
1252 } | |
1253 | |
1254 /* | |
1255 * @see dwtx.jface.text.IDocumentUndoManager#transferUndoHistory(IDocumentUndoManager) | |
1256 */ | |
1257 public void transferUndoHistory(IDocumentUndoManager manager) { | |
1258 IUndoContext oldUndoContext= manager.getUndoContext(); | |
1259 // Get the history for the old undo context. | |
1260 IUndoableOperation [] operations= OperationHistoryFactory.getOperationHistory().getUndoHistory(oldUndoContext); | |
1261 for (int i= 0; i < operations.length; i++) { | |
1262 // First replace the undo context | |
1263 IUndoableOperation op= operations[i]; | |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
134
diff
changeset
|
1264 if (cast(IContextReplacingOperation)op ) { |
134 | 1265 (cast(IContextReplacingOperation)op).replaceContext(oldUndoContext, getUndoContext()); |
129 | 1266 } else { |
1267 op.addContext(getUndoContext()); | |
1268 op.removeContext(oldUndoContext); | |
1269 } | |
1270 // Now update the manager that owns the text edit. | |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
134
diff
changeset
|
1271 if (cast(UndoableTextChange)op ) { |
134 | 1272 (cast(UndoableTextChange)op).fDocumentUndoManager= this; |
129 | 1273 } |
1274 } | |
130 | 1275 |
129 | 1276 IUndoableOperation op= OperationHistoryFactory.getOperationHistory().getUndoOperation(getUndoContext()); |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
134
diff
changeset
|
1277 if (op !is null && !(cast(UndoableTextChange)op )) |
129 | 1278 return; |
130 | 1279 |
129 | 1280 // Record the transfer itself as an undoable change. |
1281 // If the transfer results from some open operation, recording this change will | |
1282 // cause our undo context to be added to the outer operation. If there is no | |
1283 // outer operation, there will be a local change to signify the transfer. | |
1284 // This also serves to synchronize the modification stamps with the documents. | |
1285 UndoableTextChange cmd= new UndoableTextChange(this); | |
1286 cmd.fStart= cmd.fEnd= 0; | |
1287 cmd.fText= cmd.fPreservedText= ""; //$NON-NLS-1$ | |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
134
diff
changeset
|
1288 if (cast(IDocumentExtension4)fDocument ) { |
134 | 1289 cmd.fRedoModificationStamp= (cast(IDocumentExtension4)fDocument).getModificationStamp(); |
129 | 1290 if (op !is null) |
134 | 1291 cmd.fUndoModificationStamp= (cast(UndoableTextChange)op).fRedoModificationStamp; |
129 | 1292 } |
1293 addToOperationHistory(cmd); | |
1294 } | |
1295 | |
130 | 1296 |
129 | 1297 } |