Mercurial > projects > dwt-addons
annotate dwtx/text/undo/DocumentUndoManager.d @ 192:c3583c6ec027
Added missing default cases for switch statements
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Mon, 03 Nov 2008 22:52:26 +0100 |
parents | f8d52b926852 |
children |
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; | |
192
c3583c6ec027
Added missing default cases for switch statements
Frank Benoit <benoit@tionex.de>
parents:
161
diff
changeset
|
697 default: |
129 | 698 } |
699 } | |
700 | |
701 } | |
702 | |
703 | |
704 /** | |
705 * The undo context for this document undo manager. | |
706 */ | |
707 private ObjectUndoContext fUndoContext; | |
708 | |
709 /** | |
710 * The document whose changes are being tracked. | |
711 */ | |
712 private IDocument fDocument; | |
713 | |
714 /** | |
715 * The currently constructed edit. | |
716 */ | |
717 private UndoableTextChange fCurrent; | |
718 | |
719 /** | |
720 * The internal document listener. | |
721 */ | |
722 private DocumentListener fDocumentListener; | |
723 | |
724 /** | |
725 * Indicates whether the current change belongs to a compound change. | |
726 */ | |
727 private bool fFoldingIntoCompoundChange= false; | |
728 | |
729 /** | |
730 * The operation history being used to store the undo history. | |
731 */ | |
732 private IOperationHistory fHistory; | |
733 | |
734 /** | |
735 * The operation history listener used for managing undo and redo before and | |
736 * after the individual edits are performed. | |
737 */ | |
738 private IOperationHistoryListener fHistoryListener; | |
739 | |
740 /** | |
741 * The text edit last added to the operation history. This must be tracked | |
742 * internally instead of asking the history, since outside parties may be | |
743 * placing items on our undo/redo history. | |
744 */ | |
745 private UndoableTextChange fLastAddedTextEdit= null; | |
746 | |
747 /** | |
748 * The document modification stamp for redo. | |
749 */ | |
750 private long fPreservedRedoModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP; | |
751 | |
752 /** | |
753 * Text buffer to collect viewer content which has been replaced | |
754 */ | |
755 private StringBuffer fPreservedTextBuffer; | |
756 | |
757 /** | |
758 * The document modification stamp for undo. | |
759 */ | |
760 private long fPreservedUndoModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP; | |
761 | |
762 /** | |
763 * The last delete text edit. | |
764 */ | |
765 private UndoableTextChange fPreviousDelete; | |
766 | |
767 /** | |
768 * Text buffer to collect text which is inserted into the viewer | |
769 */ | |
770 private StringBuffer fTextBuffer; | |
771 | |
772 /** Indicates inserting state. */ | |
773 private bool fInserting= false; | |
774 | |
775 /** Indicates overwriting state. */ | |
776 private bool fOverwriting= false; | |
777 | |
778 /** The registered document listeners. */ | |
779 private ListenerList fDocumentUndoListeners; | |
780 | |
781 /** The list of clients connected. */ | |
782 private List fConnected; | |
783 | |
784 /** | |
130 | 785 * |
129 | 786 * Create a DocumentUndoManager for the given document. |
130 | 787 * |
129 | 788 * @param document the document whose undo history is being managed. |
789 */ | |
130 | 790 public this(IDocument document) { |
161 | 791 // super(); |
792 Assert.isNotNull(cast(Object)document); | |
129 | 793 fDocument= document; |
794 fHistory= OperationHistoryFactory.getOperationHistory(); | |
161 | 795 fUndoContext= new ObjectUndoContext(cast(Object)fDocument); |
129 | 796 fConnected= new ArrayList(); |
797 fDocumentUndoListeners= new ListenerList(ListenerList.IDENTITY); | |
798 } | |
799 | |
800 /* | |
801 * @see dwtx.jface.text.IDocumentUndoManager#addDocumentUndoListener(dwtx.jface.text.IDocumentUndoListener) | |
802 */ | |
803 public void addDocumentUndoListener(IDocumentUndoListener listener) { | |
161 | 804 fDocumentUndoListeners.add(cast(Object)listener); |
129 | 805 } |
806 | |
807 /* | |
808 * @see dwtx.jface.text.IDocumentUndoManager#removeDocumentUndoListener(dwtx.jface.text.IDocumentUndoListener) | |
809 */ | |
810 public void removeDocumentUndoListener(IDocumentUndoListener listener) { | |
161 | 811 fDocumentUndoListeners.remove(cast(Object)listener); |
129 | 812 } |
813 | |
814 /* | |
815 * @see dwtx.jface.text.IDocumentUndoManager#getUndoContext() | |
816 */ | |
817 public IUndoContext getUndoContext() { | |
818 return fUndoContext; | |
819 } | |
820 | |
821 /* | |
822 * @see dwtx.jface.text.IDocumentUndoManager#commit() | |
823 */ | |
824 public void commit() { | |
825 // if fCurrent has never been placed on the history, do so now. | |
826 // this can happen when there are multiple programmatically commits in a | |
827 // single document change. | |
828 if (fLastAddedTextEdit !is fCurrent) { | |
829 fCurrent.pretendCommit(); | |
830 if (fCurrent.isValid()) | |
831 addToOperationHistory(fCurrent); | |
832 } | |
833 fCurrent.commit(); | |
834 } | |
130 | 835 |
129 | 836 /* |
837 * @see dwtx.text.undo.IDocumentUndoManager#reset() | |
838 */ | |
839 public void reset() { | |
840 if (isConnected()) { | |
841 shutdown(); | |
842 initialize(); | |
843 } | |
844 } | |
845 | |
846 /* | |
847 * @see dwtx.text.undo.IDocumentUndoManager#redoable() | |
848 */ | |
849 public bool redoable() { | |
850 return OperationHistoryFactory.getOperationHistory().canRedo(fUndoContext); | |
851 } | |
852 | |
853 /* | |
854 * @see dwtx.text.undo.IDocumentUndoManager#undoable() | |
855 */ | |
856 public bool undoable() { | |
857 return OperationHistoryFactory.getOperationHistory().canUndo(fUndoContext); | |
858 } | |
859 | |
860 /* | |
861 * @see dwtx.text.undo.IDocumentUndoManager#undo() | |
862 */ | |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
134
diff
changeset
|
863 public void redo() { |
129 | 864 if (isConnected() && redoable()) |
865 OperationHistoryFactory.getOperationHistory().redo(getUndoContext(), null, null); | |
866 } | |
867 | |
868 /* | |
869 * @see dwtx.text.undo.IDocumentUndoManager#undo() | |
870 */ | |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
134
diff
changeset
|
871 public void undo() { |
129 | 872 if (undoable()) |
873 OperationHistoryFactory.getOperationHistory().undo(fUndoContext, null, null); | |
874 } | |
875 | |
876 /* | |
877 * @see dwtx.jface.text.IDocumentUndoManager#connect(java.lang.Object) | |
878 */ | |
879 public void connect(Object client) { | |
880 if (!isConnected()) { | |
881 initialize(); | |
882 } | |
883 if (!fConnected.contains(client)) | |
884 fConnected.add(client); | |
885 } | |
886 | |
887 /* | |
888 * @see dwtx.jface.text.IDocumentUndoManager#disconnect(java.lang.Object) | |
889 */ | |
890 public void disconnect(Object client) { | |
891 fConnected.remove(client); | |
892 if (!isConnected()) { | |
893 shutdown(); | |
894 } | |
895 } | |
896 | |
897 /* | |
898 * @see dwtx.jface.text.IDocumentUndoManager#beginCompoundChange() | |
899 */ | |
900 public void beginCompoundChange() { | |
901 if (isConnected()) { | |
902 fFoldingIntoCompoundChange= true; | |
903 commit(); | |
904 } | |
905 } | |
906 | |
907 /* | |
908 * @see dwtx.jface.text.IDocumentUndoManager#endCompoundChange() | |
909 */ | |
910 public void endCompoundChange() { | |
911 if (isConnected()) { | |
912 fFoldingIntoCompoundChange= false; | |
913 commit(); | |
914 } | |
915 } | |
916 | |
917 /* | |
918 * @see dwtx.jface.text.IDocumentUndoManager#setUndoLimit(int) | |
919 */ | |
920 public void setMaximalUndoLevel(int undoLimit) { | |
921 fHistory.setLimit(fUndoContext, undoLimit); | |
922 } | |
923 | |
924 /** | |
925 * Fires a document undo event to all registered document undo listeners. | |
926 * Uses a robust iterator. | |
130 | 927 * |
129 | 928 * @param offset the document offset |
929 * @param text the text that was inserted | |
930 * @param preservedText the text being replaced | |
931 * @param source the source which triggered the event | |
932 * @param eventType the type of event causing the change | |
933 * @param isCompound a flag indicating whether the change is a compound change | |
934 * @see IDocumentUndoListener | |
935 */ | |
936 void fireDocumentUndo(int offset, String text, String preservedText, Object source, int eventType, bool isCompound) { | |
937 eventType= isCompound ? eventType | DocumentUndoEvent.COMPOUND : eventType; | |
938 DocumentUndoEvent event= new DocumentUndoEvent(fDocument, offset, text, preservedText, eventType, source); | |
939 Object[] listeners= fDocumentUndoListeners.getListeners(); | |
940 for (int i= 0; i < listeners.length; i++) { | |
134 | 941 (cast(IDocumentUndoListener)listeners[i]).documentUndoNotification(event); |
129 | 942 } |
943 } | |
944 | |
945 /** | |
946 * Adds any listeners needed to track the document and the operations | |
947 * history. | |
948 */ | |
949 private void addListeners() { | |
950 fHistoryListener= new HistoryListener(); | |
951 fHistory.addOperationHistoryListener(fHistoryListener); | |
952 listenToTextChanges(true); | |
953 } | |
954 | |
955 /** | |
956 * Removes any listeners that were installed by the document. | |
957 */ | |
958 private void removeListeners() { | |
959 listenToTextChanges(false); | |
960 fHistory.removeOperationHistoryListener(fHistoryListener); | |
961 fHistoryListener= null; | |
962 } | |
963 | |
964 /** | |
965 * Adds the given text edit to the operation history if it is not part of a | |
966 * compound change. | |
130 | 967 * |
129 | 968 * @param edit |
969 * the edit to be added | |
970 */ | |
971 private void addToOperationHistory(UndoableTextChange edit) { | |
972 if (!fFoldingIntoCompoundChange | |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
134
diff
changeset
|
973 || cast(UndoableCompoundTextChange)edit ) { |
129 | 974 fHistory.add(edit); |
975 fLastAddedTextEdit= edit; | |
976 } | |
977 } | |
978 | |
979 /** | |
980 * Disposes the undo history. | |
981 */ | |
982 private void disposeUndoHistory() { | |
983 fHistory.dispose(fUndoContext, true, true, true); | |
984 } | |
985 | |
986 /** | |
987 * Initializes the undo history. | |
988 */ | |
989 private void initializeUndoHistory() { | |
990 if (fHistory !is null && fUndoContext !is null) | |
991 fHistory.dispose(fUndoContext, true, true, false); | |
992 | |
993 } | |
994 | |
995 /** | |
996 * Checks whether the given text starts with a line delimiter and | |
997 * subsequently contains a white space only. | |
130 | 998 * |
129 | 999 * @param text the text to check |
1000 * @return <code>true</code> if the text is a line delimiter followed by | |
1001 * whitespace, <code>false</code> otherwise | |
1002 */ | |
1003 private bool isWhitespaceText(String text) { | |
1004 | |
1005 if (text is null || text.length() is 0) | |
1006 return false; | |
1007 | |
1008 String[] delimiters= fDocument.getLegalLineDelimiters(); | |
1009 int index= TextUtilities.startsWith(delimiters, text); | |
1010 if (index > -1) { | |
1011 char c; | |
1012 int length= text.length(); | |
161 | 1013 for (int i= delimiters[index].length; i < length; i++) { |
129 | 1014 c= text.charAt(i); |
1015 if (c !is ' ' && c !is '\t') | |
1016 return false; | |
1017 } | |
1018 return true; | |
1019 } | |
1020 | |
1021 return false; | |
1022 } | |
1023 | |
1024 /** | |
1025 * Switches the state of whether there is a text listener or not. | |
130 | 1026 * |
129 | 1027 * @param listen the state which should be established |
1028 */ | |
1029 private void listenToTextChanges(bool listen) { | |
1030 if (listen) { | |
1031 if (fDocumentListener is null && fDocument !is null) { | |
1032 fDocumentListener= new DocumentListener(); | |
1033 fDocument.addDocumentListener(fDocumentListener); | |
1034 } | |
1035 } else if (!listen) { | |
1036 if (fDocumentListener !is null && fDocument !is null) { | |
1037 fDocument.removeDocumentListener(fDocumentListener); | |
1038 fDocumentListener= null; | |
1039 } | |
1040 } | |
1041 } | |
1042 | |
1043 private void processChange(int modelStart, int modelEnd, | |
1044 String insertedText, String replacedText, | |
1045 long beforeChangeModificationStamp, | |
1046 long afterChangeModificationStamp) { | |
1047 | |
1048 if (insertedText is null) | |
1049 insertedText= ""; //$NON-NLS-1$ | |
1050 | |
1051 if (replacedText is null) | |
1052 replacedText= ""; //$NON-NLS-1$ | |
1053 | |
1054 int length= insertedText.length(); | |
1055 int diff= modelEnd - modelStart; | |
1056 | |
1057 if (fCurrent.fUndoModificationStamp is IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP) | |
1058 fCurrent.fUndoModificationStamp= beforeChangeModificationStamp; | |
1059 | |
1060 // normalize | |
1061 if (diff < 0) { | |
1062 int tmp= modelEnd; | |
1063 modelEnd= modelStart; | |
1064 modelStart= tmp; | |
1065 } | |
1066 | |
1067 if (modelStart is modelEnd) { | |
1068 // text will be inserted | |
1069 if ((length is 1) || isWhitespaceText(insertedText)) { | |
1070 // by typing or whitespace | |
1071 if (!fInserting | |
1072 || (modelStart !is fCurrent.fStart | |
1073 + fTextBuffer.length())) { | |
1074 fCurrent.fRedoModificationStamp= beforeChangeModificationStamp; | |
1075 if (fCurrent.attemptCommit()) | |
1076 fCurrent.fUndoModificationStamp= beforeChangeModificationStamp; | |
1077 | |
1078 fInserting= true; | |
1079 } | |
1080 if (fCurrent.fStart < 0) | |
1081 fCurrent.fStart= fCurrent.fEnd= modelStart; | |
1082 if (length > 0) | |
1083 fTextBuffer.append(insertedText); | |
1084 } else if (length > 0) { | |
1085 // by pasting or model manipulation | |
1086 fCurrent.fRedoModificationStamp= beforeChangeModificationStamp; | |
1087 if (fCurrent.attemptCommit()) | |
1088 fCurrent.fUndoModificationStamp= beforeChangeModificationStamp; | |
1089 | |
1090 fCurrent.fStart= fCurrent.fEnd= modelStart; | |
1091 fTextBuffer.append(insertedText); | |
1092 fCurrent.fRedoModificationStamp= afterChangeModificationStamp; | |
1093 if (fCurrent.attemptCommit()) | |
1094 fCurrent.fUndoModificationStamp= afterChangeModificationStamp; | |
1095 | |
1096 } | |
1097 } else { | |
1098 if (length is 0) { | |
1099 // text will be deleted by backspace or DEL key or empty | |
1100 // clipboard | |
161 | 1101 length= replacedText.length; |
129 | 1102 String[] delimiters= fDocument.getLegalLineDelimiters(); |
1103 | |
1104 if ((length is 1) | |
1105 || TextUtilities.equals(delimiters, replacedText) > -1) { | |
1106 | |
1107 // whereby selection is empty | |
1108 | |
1109 if (fPreviousDelete.fStart is modelStart | |
1110 && fPreviousDelete.fEnd is modelEnd) { | |
1111 // repeated DEL | |
1112 | |
1113 // correct wrong settings of fCurrent | |
1114 if (fCurrent.fStart is modelEnd | |
1115 && fCurrent.fEnd is modelStart) { | |
1116 fCurrent.fStart= modelStart; | |
1117 fCurrent.fEnd= modelEnd; | |
1118 } | |
1119 // append to buffer && extend edit range | |
1120 fPreservedTextBuffer.append(replacedText); | |
1121 ++fCurrent.fEnd; | |
1122 | |
1123 } else if (fPreviousDelete.fStart is modelEnd) { | |
1124 // repeated backspace | |
1125 | |
1126 // insert in buffer and extend edit range | |
161 | 1127 fPreservedTextBuffer.select(0,0); |
1128 fPreservedTextBuffer.replace(replacedText); | |
129 | 1129 fCurrent.fStart= modelStart; |
1130 | |
1131 } else { | |
1132 // either DEL or backspace for the first time | |
1133 | |
1134 fCurrent.fRedoModificationStamp= beforeChangeModificationStamp; | |
1135 if (fCurrent.attemptCommit()) | |
1136 fCurrent.fUndoModificationStamp= beforeChangeModificationStamp; | |
1137 | |
1138 // as we can not decide whether it was DEL or backspace | |
1139 // we initialize for backspace | |
1140 fPreservedTextBuffer.append(replacedText); | |
1141 fCurrent.fStart= modelStart; | |
1142 fCurrent.fEnd= modelEnd; | |
1143 } | |
1144 | |
1145 fPreviousDelete.set(modelStart, modelEnd); | |
1146 | |
1147 } else if (length > 0) { | |
1148 // whereby selection is not empty | |
1149 fCurrent.fRedoModificationStamp= beforeChangeModificationStamp; | |
1150 if (fCurrent.attemptCommit()) | |
1151 fCurrent.fUndoModificationStamp= beforeChangeModificationStamp; | |
1152 | |
1153 fCurrent.fStart= modelStart; | |
1154 fCurrent.fEnd= modelEnd; | |
1155 fPreservedTextBuffer.append(replacedText); | |
1156 } | |
1157 } else { | |
1158 // text will be replaced | |
1159 | |
1160 if (length is 1) { | |
161 | 1161 length= replacedText.length; |
129 | 1162 String[] delimiters= fDocument.getLegalLineDelimiters(); |
1163 | |
1164 if ((length is 1) | |
1165 || TextUtilities.equals(delimiters, replacedText) > -1) { | |
1166 // because of overwrite mode or model manipulation | |
1167 if (!fOverwriting | |
1168 || (modelStart !is fCurrent.fStart | |
1169 + fTextBuffer.length())) { | |
1170 fCurrent.fRedoModificationStamp= beforeChangeModificationStamp; | |
1171 if (fCurrent.attemptCommit()) | |
1172 fCurrent.fUndoModificationStamp= beforeChangeModificationStamp; | |
1173 | |
1174 fOverwriting= true; | |
1175 } | |
1176 | |
1177 if (fCurrent.fStart < 0) | |
1178 fCurrent.fStart= modelStart; | |
1179 | |
1180 fCurrent.fEnd= modelEnd; | |
1181 fTextBuffer.append(insertedText); | |
1182 fPreservedTextBuffer.append(replacedText); | |
1183 fCurrent.fRedoModificationStamp= afterChangeModificationStamp; | |
1184 return; | |
1185 } | |
1186 } | |
1187 // because of typing or pasting whereby selection is not empty | |
1188 fCurrent.fRedoModificationStamp= beforeChangeModificationStamp; | |
1189 if (fCurrent.attemptCommit()) | |
1190 fCurrent.fUndoModificationStamp= beforeChangeModificationStamp; | |
1191 | |
1192 fCurrent.fStart= modelStart; | |
1193 fCurrent.fEnd= modelEnd; | |
1194 fTextBuffer.append(insertedText); | |
1195 fPreservedTextBuffer.append(replacedText); | |
1196 } | |
1197 } | |
1198 // in all cases, the redo modification stamp is updated on the open | |
1199 // text edit | |
1200 fCurrent.fRedoModificationStamp= afterChangeModificationStamp; | |
1201 } | |
1202 | |
1203 /** | |
1204 * Initialize the receiver. | |
1205 */ | |
1206 private void initialize() { | |
1207 initializeUndoHistory(); | |
1208 | |
1209 // open up the current text edit | |
1210 fCurrent= new UndoableTextChange(this); | |
1211 fPreviousDelete= new UndoableTextChange(this); | |
1212 fTextBuffer= new StringBuffer(); | |
1213 fPreservedTextBuffer= new StringBuffer(); | |
1214 | |
1215 addListeners(); | |
1216 } | |
130 | 1217 |
129 | 1218 /** |
1219 * Reset processChange state. | |
130 | 1220 * |
129 | 1221 * @since 3.2 |
1222 */ | |
1223 private void resetProcessChangeState() { | |
1224 fInserting= false; | |
1225 fOverwriting= false; | |
1226 fPreviousDelete.reinitialize(); | |
1227 } | |
1228 | |
1229 /** | |
1230 * Shutdown the receiver. | |
1231 */ | |
1232 private void shutdown() { | |
1233 removeListeners(); | |
1234 | |
1235 fCurrent= null; | |
1236 fPreviousDelete= null; | |
161 | 1237 fTextBuffer.clear(); |
1238 fPreservedTextBuffer.clear(); | |
129 | 1239 |
1240 disposeUndoHistory(); | |
1241 } | |
1242 | |
1243 /** | |
1244 * Return whether or not any clients are connected to the receiver. | |
130 | 1245 * |
129 | 1246 * @return <code>true</code> if the receiver is connected to |
1247 * clients, <code>false</code> if it is not | |
1248 */ | |
1249 bool isConnected() { | |
1250 if (fConnected is null) | |
1251 return false; | |
1252 return !fConnected.isEmpty(); | |
1253 } | |
1254 | |
1255 /* | |
1256 * @see dwtx.jface.text.IDocumentUndoManager#transferUndoHistory(IDocumentUndoManager) | |
1257 */ | |
1258 public void transferUndoHistory(IDocumentUndoManager manager) { | |
1259 IUndoContext oldUndoContext= manager.getUndoContext(); | |
1260 // Get the history for the old undo context. | |
1261 IUndoableOperation [] operations= OperationHistoryFactory.getOperationHistory().getUndoHistory(oldUndoContext); | |
1262 for (int i= 0; i < operations.length; i++) { | |
1263 // First replace the undo context | |
1264 IUndoableOperation op= operations[i]; | |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
134
diff
changeset
|
1265 if (cast(IContextReplacingOperation)op ) { |
134 | 1266 (cast(IContextReplacingOperation)op).replaceContext(oldUndoContext, getUndoContext()); |
129 | 1267 } else { |
1268 op.addContext(getUndoContext()); | |
1269 op.removeContext(oldUndoContext); | |
1270 } | |
1271 // 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
|
1272 if (cast(UndoableTextChange)op ) { |
134 | 1273 (cast(UndoableTextChange)op).fDocumentUndoManager= this; |
129 | 1274 } |
1275 } | |
130 | 1276 |
129 | 1277 IUndoableOperation op= OperationHistoryFactory.getOperationHistory().getUndoOperation(getUndoContext()); |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
134
diff
changeset
|
1278 if (op !is null && !(cast(UndoableTextChange)op )) |
129 | 1279 return; |
130 | 1280 |
129 | 1281 // Record the transfer itself as an undoable change. |
1282 // If the transfer results from some open operation, recording this change will | |
1283 // cause our undo context to be added to the outer operation. If there is no | |
1284 // outer operation, there will be a local change to signify the transfer. | |
1285 // This also serves to synchronize the modification stamps with the documents. | |
1286 UndoableTextChange cmd= new UndoableTextChange(this); | |
1287 cmd.fStart= cmd.fEnd= 0; | |
1288 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
|
1289 if (cast(IDocumentExtension4)fDocument ) { |
134 | 1290 cmd.fRedoModificationStamp= (cast(IDocumentExtension4)fDocument).getModificationStamp(); |
129 | 1291 if (op !is null) |
134 | 1292 cmd.fUndoModificationStamp= (cast(UndoableTextChange)op).fRedoModificationStamp; |
129 | 1293 } |
1294 addToOperationHistory(cmd); | |
1295 } | |
1296 | |
130 | 1297 |
129 | 1298 } |