Mercurial > projects > dwt-addons
annotate dwtx/jface/text/DefaultUndoManager.d @ 162:1a5b8f8129df
...
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Mon, 08 Sep 2008 00:51:37 +0200 |
parents | 3678e4f1a766 |
children | c3583c6ec027 |
rev | line source |
---|---|
129 | 1 /******************************************************************************* |
2 * Copyright (c) 2000, 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 | |
131 | 14 |
151 | 15 module dwtx.jface.text.DefaultUndoManager; |
16 | |
131 | 17 import dwtx.jface.text.IDocumentPartitioningListener; // packageimport |
18 import dwtx.jface.text.DefaultTextHover; // packageimport | |
19 import dwtx.jface.text.AbstractInformationControl; // packageimport | |
20 import dwtx.jface.text.TextUtilities; // packageimport | |
21 import dwtx.jface.text.IInformationControlCreatorExtension; // packageimport | |
22 import dwtx.jface.text.AbstractInformationControlManager; // packageimport | |
23 import dwtx.jface.text.ITextViewerExtension2; // packageimport | |
24 import dwtx.jface.text.IDocumentPartitioner; // packageimport | |
25 import dwtx.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport | |
26 import dwtx.jface.text.ITextSelection; // packageimport | |
27 import dwtx.jface.text.Document; // packageimport | |
28 import dwtx.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport | |
29 import dwtx.jface.text.ITextListener; // packageimport | |
30 import dwtx.jface.text.BadPartitioningException; // packageimport | |
31 import dwtx.jface.text.ITextViewerExtension5; // packageimport | |
32 import dwtx.jface.text.IDocumentPartitionerExtension3; // packageimport | |
33 import dwtx.jface.text.IUndoManager; // packageimport | |
34 import dwtx.jface.text.ITextHoverExtension2; // packageimport | |
35 import dwtx.jface.text.IRepairableDocument; // packageimport | |
36 import dwtx.jface.text.IRewriteTarget; // packageimport | |
37 import dwtx.jface.text.DefaultPositionUpdater; // packageimport | |
38 import dwtx.jface.text.RewriteSessionEditProcessor; // packageimport | |
39 import dwtx.jface.text.TextViewerHoverManager; // packageimport | |
40 import dwtx.jface.text.DocumentRewriteSession; // packageimport | |
41 import dwtx.jface.text.TextViewer; // packageimport | |
42 import dwtx.jface.text.ITextViewerExtension8; // packageimport | |
43 import dwtx.jface.text.RegExMessages; // packageimport | |
44 import dwtx.jface.text.IDelayedInputChangeProvider; // packageimport | |
45 import dwtx.jface.text.ITextOperationTargetExtension; // packageimport | |
46 import dwtx.jface.text.IWidgetTokenOwner; // packageimport | |
47 import dwtx.jface.text.IViewportListener; // packageimport | |
48 import dwtx.jface.text.GapTextStore; // packageimport | |
49 import dwtx.jface.text.MarkSelection; // packageimport | |
50 import dwtx.jface.text.IDocumentPartitioningListenerExtension; // packageimport | |
51 import dwtx.jface.text.IDocumentAdapterExtension; // packageimport | |
52 import dwtx.jface.text.IInformationControlExtension; // packageimport | |
53 import dwtx.jface.text.IDocumentPartitioningListenerExtension2; // packageimport | |
54 import dwtx.jface.text.DefaultDocumentAdapter; // packageimport | |
55 import dwtx.jface.text.ITextViewerExtension3; // packageimport | |
56 import dwtx.jface.text.IInformationControlCreator; // packageimport | |
57 import dwtx.jface.text.TypedRegion; // packageimport | |
58 import dwtx.jface.text.ISynchronizable; // packageimport | |
59 import dwtx.jface.text.IMarkRegionTarget; // packageimport | |
60 import dwtx.jface.text.TextViewerUndoManager; // packageimport | |
61 import dwtx.jface.text.IRegion; // packageimport | |
62 import dwtx.jface.text.IInformationControlExtension2; // packageimport | |
63 import dwtx.jface.text.IDocumentExtension4; // packageimport | |
64 import dwtx.jface.text.IDocumentExtension2; // packageimport | |
65 import dwtx.jface.text.IDocumentPartitionerExtension2; // packageimport | |
66 import dwtx.jface.text.Assert; // packageimport | |
67 import dwtx.jface.text.DefaultInformationControl; // packageimport | |
68 import dwtx.jface.text.IWidgetTokenOwnerExtension; // packageimport | |
69 import dwtx.jface.text.DocumentClone; // packageimport | |
70 import dwtx.jface.text.IFindReplaceTarget; // packageimport | |
71 import dwtx.jface.text.IAutoEditStrategy; // packageimport | |
72 import dwtx.jface.text.ILineTrackerExtension; // packageimport | |
73 import dwtx.jface.text.IUndoManagerExtension; // packageimport | |
74 import dwtx.jface.text.TextSelection; // packageimport | |
75 import dwtx.jface.text.DefaultAutoIndentStrategy; // packageimport | |
76 import dwtx.jface.text.IAutoIndentStrategy; // packageimport | |
77 import dwtx.jface.text.IPainter; // packageimport | |
78 import dwtx.jface.text.IInformationControl; // packageimport | |
79 import dwtx.jface.text.IInformationControlExtension3; // packageimport | |
80 import dwtx.jface.text.ITextViewerExtension6; // packageimport | |
81 import dwtx.jface.text.IInformationControlExtension4; // packageimport | |
82 import dwtx.jface.text.DefaultLineTracker; // packageimport | |
83 import dwtx.jface.text.IDocumentInformationMappingExtension; // packageimport | |
84 import dwtx.jface.text.IRepairableDocumentExtension; // packageimport | |
85 import dwtx.jface.text.ITextHover; // packageimport | |
86 import dwtx.jface.text.FindReplaceDocumentAdapter; // packageimport | |
87 import dwtx.jface.text.ILineTracker; // packageimport | |
88 import dwtx.jface.text.Line; // packageimport | |
89 import dwtx.jface.text.ITextViewerExtension; // packageimport | |
90 import dwtx.jface.text.IDocumentAdapter; // packageimport | |
91 import dwtx.jface.text.TextEvent; // packageimport | |
92 import dwtx.jface.text.BadLocationException; // packageimport | |
93 import dwtx.jface.text.AbstractDocument; // packageimport | |
94 import dwtx.jface.text.AbstractLineTracker; // packageimport | |
95 import dwtx.jface.text.TreeLineTracker; // packageimport | |
96 import dwtx.jface.text.ITextPresentationListener; // packageimport | |
97 import dwtx.jface.text.Region; // packageimport | |
98 import dwtx.jface.text.ITextViewer; // packageimport | |
99 import dwtx.jface.text.IDocumentInformationMapping; // packageimport | |
100 import dwtx.jface.text.MarginPainter; // packageimport | |
101 import dwtx.jface.text.IPaintPositionManager; // packageimport | |
102 import dwtx.jface.text.TextPresentation; // packageimport | |
103 import dwtx.jface.text.IFindReplaceTargetExtension; // packageimport | |
104 import dwtx.jface.text.ISlaveDocumentManagerExtension; // packageimport | |
105 import dwtx.jface.text.ISelectionValidator; // packageimport | |
106 import dwtx.jface.text.IDocumentExtension; // packageimport | |
107 import dwtx.jface.text.PropagatingFontFieldEditor; // packageimport | |
108 import dwtx.jface.text.ConfigurableLineTracker; // packageimport | |
109 import dwtx.jface.text.SlaveDocumentEvent; // packageimport | |
110 import dwtx.jface.text.IDocumentListener; // packageimport | |
111 import dwtx.jface.text.PaintManager; // packageimport | |
112 import dwtx.jface.text.IFindReplaceTargetExtension3; // packageimport | |
113 import dwtx.jface.text.ITextDoubleClickStrategy; // packageimport | |
114 import dwtx.jface.text.IDocumentExtension3; // packageimport | |
115 import dwtx.jface.text.Position; // packageimport | |
116 import dwtx.jface.text.TextMessages; // packageimport | |
117 import dwtx.jface.text.CopyOnWriteTextStore; // packageimport | |
118 import dwtx.jface.text.WhitespaceCharacterPainter; // packageimport | |
119 import dwtx.jface.text.IPositionUpdater; // packageimport | |
120 import dwtx.jface.text.DefaultTextDoubleClickStrategy; // packageimport | |
121 import dwtx.jface.text.ListLineTracker; // packageimport | |
122 import dwtx.jface.text.ITextInputListener; // packageimport | |
123 import dwtx.jface.text.BadPositionCategoryException; // packageimport | |
124 import dwtx.jface.text.IWidgetTokenKeeperExtension; // packageimport | |
125 import dwtx.jface.text.IInputChangedListener; // packageimport | |
126 import dwtx.jface.text.ITextOperationTarget; // packageimport | |
127 import dwtx.jface.text.IDocumentInformationMappingExtension2; // packageimport | |
128 import dwtx.jface.text.ITextViewerExtension7; // packageimport | |
129 import dwtx.jface.text.IInformationControlExtension5; // packageimport | |
130 import dwtx.jface.text.IDocumentRewriteSessionListener; // packageimport | |
131 import dwtx.jface.text.JFaceTextUtil; // packageimport | |
132 import dwtx.jface.text.AbstractReusableInformationControlCreator; // packageimport | |
133 import dwtx.jface.text.TabsToSpacesConverter; // packageimport | |
134 import dwtx.jface.text.CursorLinePainter; // packageimport | |
135 import dwtx.jface.text.ITextHoverExtension; // packageimport | |
136 import dwtx.jface.text.IEventConsumer; // packageimport | |
137 import dwtx.jface.text.IDocument; // packageimport | |
138 import dwtx.jface.text.IWidgetTokenKeeper; // packageimport | |
139 import dwtx.jface.text.DocumentCommand; // packageimport | |
140 import dwtx.jface.text.TypedPosition; // packageimport | |
141 import dwtx.jface.text.IEditingSupportRegistry; // packageimport | |
142 import dwtx.jface.text.IDocumentPartitionerExtension; // packageimport | |
143 import dwtx.jface.text.AbstractHoverInformationControlManager; // packageimport | |
144 import dwtx.jface.text.IEditingSupport; // packageimport | |
145 import dwtx.jface.text.IMarkSelection; // packageimport | |
146 import dwtx.jface.text.ISlaveDocumentManager; // packageimport | |
147 import dwtx.jface.text.DocumentEvent; // packageimport | |
148 import dwtx.jface.text.DocumentPartitioningChangedEvent; // packageimport | |
149 import dwtx.jface.text.ITextStore; // packageimport | |
150 import dwtx.jface.text.JFaceTextMessages; // packageimport | |
151 import dwtx.jface.text.DocumentRewriteSessionEvent; // packageimport | |
152 import dwtx.jface.text.SequentialRewriteTextStore; // packageimport | |
153 import dwtx.jface.text.DocumentRewriteSessionType; // packageimport | |
154 import dwtx.jface.text.TextAttribute; // packageimport | |
155 import dwtx.jface.text.ITextViewerExtension4; // packageimport | |
156 import dwtx.jface.text.ITypedRegion; // packageimport | |
157 | |
129 | 158 import dwt.dwthelper.utils; |
159 | |
160 | |
153
f70d9508c95c
Fix java Collection imports
Frank Benoit <benoit@tionex.de>
parents:
151
diff
changeset
|
161 import dwtx.dwtxhelper.Collection; |
f70d9508c95c
Fix java Collection imports
Frank Benoit <benoit@tionex.de>
parents:
151
diff
changeset
|
162 |
129 | 163 |
164 import dwt.DWT; | |
165 import dwt.custom.StyledText; | |
166 import dwt.events.KeyEvent; | |
167 import dwt.events.KeyListener; | |
168 import dwt.events.MouseEvent; | |
169 import dwt.events.MouseListener; | |
162 | 170 import dwt.widgets.Event; |
129 | 171 import dwt.widgets.Display; |
172 import dwt.widgets.Shell; | |
173 import dwtx.core.commands.ExecutionException; | |
174 import dwtx.core.commands.operations.AbstractOperation; | |
175 import dwtx.core.commands.operations.IOperationHistory; | |
176 import dwtx.core.commands.operations.IOperationHistoryListener; | |
177 import dwtx.core.commands.operations.IUndoContext; | |
178 import dwtx.core.commands.operations.IUndoableOperation; | |
179 import dwtx.core.commands.operations.ObjectUndoContext; | |
180 import dwtx.core.commands.operations.OperationHistoryEvent; | |
181 import dwtx.core.commands.operations.OperationHistoryFactory; | |
182 import dwtx.core.runtime.IAdaptable; | |
183 import dwtx.core.runtime.IProgressMonitor; | |
184 import dwtx.core.runtime.IStatus; | |
185 import dwtx.core.runtime.Status; | |
186 import dwtx.jface.dialogs.MessageDialog; | |
187 | |
188 | |
189 /** | |
190 * Standard implementation of {@link dwtx.jface.text.IUndoManager}. | |
191 * <p> | |
192 * It registers with the connected text viewer as text input listener and | |
193 * document listener and logs all changes. It also monitors mouse and keyboard | |
194 * activities in order to partition the stream of text changes into undo-able | |
195 * edit commands. | |
196 * </p> | |
197 * <p> | |
198 * Since 3.1 this undo manager is a facade to the global operation history. | |
199 * </p> | |
200 * <p> | |
201 * The usage of {@link dwtx.core.runtime.IAdaptable} in the JFace | |
202 * layer has been approved by Platform UI, see: https://bugs.eclipse.org/bugs/show_bug.cgi?id=87669#c9 | |
203 * </p> | |
204 * <p> | |
205 * This class is not intended to be subclassed. | |
206 * </p> | |
207 * | |
208 * @see dwtx.jface.text.ITextViewer | |
209 * @see dwtx.jface.text.ITextInputListener | |
210 * @see dwtx.jface.text.IDocumentListener | |
211 * @see dwtx.core.commands.operations.IUndoableOperation | |
212 * @see dwtx.core.commands.operations.IOperationHistory | |
213 * @see MouseListener | |
214 * @see KeyListener | |
215 * @deprecated As of 3.2, replaced by {@link TextViewerUndoManager} | |
216 * @noextend This class is not intended to be subclassed by clients. | |
217 */ | |
218 public class DefaultUndoManager : IUndoManager, IUndoManagerExtension { | |
219 | |
220 /** | |
221 * Represents an undo-able edit command. | |
222 * <p> | |
223 * Since 3.1 this implements the interface for IUndoableOperation. | |
224 * </p> | |
225 */ | |
226 class TextCommand : AbstractOperation { | |
227 | |
228 /** The start index of the replaced text. */ | |
229 protected int fStart= -1; | |
230 /** The end index of the replaced text. */ | |
231 protected int fEnd= -1; | |
232 /** The newly inserted text. */ | |
233 protected String fText; | |
234 /** The replaced text. */ | |
235 protected String fPreservedText; | |
236 | |
237 /** The undo modification stamp. */ | |
238 protected long fUndoModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP; | |
239 /** The redo modification stamp. */ | |
240 protected long fRedoModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP; | |
241 | |
242 /** | |
243 * Creates a new text command. | |
244 * | |
245 * @param context the undo context for this command | |
246 * @since 3.1 | |
247 */ | |
133
7d818bd32d63
Fix ctors to this with gvim regexp
Frank Benoit <benoit@tionex.de>
parents:
131
diff
changeset
|
248 this(IUndoContext context) { |
129 | 249 super(JFaceTextMessages.getString("DefaultUndoManager.operationLabel")); //$NON-NLS-1$ |
250 addContext(context); | |
251 } | |
252 | |
253 /** | |
254 * Re-initializes this text command. | |
255 */ | |
256 protected void reinitialize() { | |
257 fStart= fEnd= -1; | |
258 fText= fPreservedText= null; | |
259 fUndoModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP; | |
260 fRedoModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP; | |
261 } | |
262 | |
263 /** | |
264 * Sets the start and the end index of this command. | |
265 * | |
266 * @param start the start index | |
267 * @param end the end index | |
268 */ | |
269 protected void set(int start, int end) { | |
270 fStart= start; | |
271 fEnd= end; | |
272 fText= null; | |
273 fPreservedText= null; | |
274 } | |
275 | |
276 /* | |
277 * @see dwtx.core.commands.operations.IUndoableOperation#dispose() | |
278 * @since 3.1 | |
279 */ | |
280 public void dispose() { | |
281 reinitialize(); | |
282 } | |
283 | |
284 /** | |
285 * Undo the change described by this command. | |
286 * | |
287 * @since 2.0 | |
288 */ | |
289 protected void undoTextChange() { | |
290 try { | |
291 IDocument document= fTextViewer.getDocument(); | |
138 | 292 if ( cast(IDocumentExtension4)document ) |
134 | 293 (cast(IDocumentExtension4)document).replace(fStart, fText.length(), fPreservedText, fUndoModificationStamp); |
129 | 294 else |
295 document.replace(fStart, fText.length(), fPreservedText); | |
296 } catch (BadLocationException x) { | |
297 } | |
298 } | |
299 | |
300 /* | |
301 * @see dwtx.core.commands.operations.IUndoableOperation#canUndo() | |
302 * @since 3.1 | |
303 */ | |
304 public bool canUndo() { | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
305 |
129 | 306 if (isConnected() && isValid()) { |
307 IDocument doc= fTextViewer.getDocument(); | |
138 | 308 if ( cast(IDocumentExtension4)doc ) { |
134 | 309 long docStamp= (cast(IDocumentExtension4)doc).getModificationStamp(); |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
310 |
129 | 311 // Normal case: an undo is valid if its redo will restore document |
312 // to its current modification stamp | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
313 bool canUndo= docStamp is IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP || |
129 | 314 docStamp is getRedoModificationStamp(); |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
315 |
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
316 /* Special case to check if the answer is false. |
129 | 317 * If the last document change was empty, then the document's |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
318 * modification stamp was incremented but nothing was committed. |
129 | 319 * The operation being queried has an older stamp. In this case only, |
320 * the comparison is different. A sequence of document changes that | |
321 * include an empty change is handled correctly when a valid commit | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
322 * follows the empty change, but when #canUndo() is queried just after |
129 | 323 * an empty change, we must special case the check. The check is very |
324 * specific to prevent false positives. | |
325 * see https://bugs.eclipse.org/bugs/show_bug.cgi?id=98245 | |
326 */ | |
327 if (!canUndo && | |
328 this is fHistory.getUndoOperation(fUndoContext) && // this is the latest operation | |
329 this !is fCurrent && // there is a more current operation not on the stack | |
330 !fCurrent.isValid() && // the current operation is not a valid document modification | |
331 fCurrent.fUndoModificationStamp !is // the invalid current operation has a document stamp | |
332 IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP) { | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
333 canUndo= fCurrent.fRedoModificationStamp is docStamp; |
129 | 334 } |
335 /* | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
336 * When the composite is the current command, it may hold the timestamp |
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
337 * of a no-op change. We check this here rather than in an override of |
129 | 338 * canUndo() in CompoundTextCommand simply to keep all the special case checks |
339 * in one place. | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
340 */ |
129 | 341 if (!canUndo && |
342 this is fHistory.getUndoOperation(fUndoContext) && // this is the latest operation | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
343 null !is cast(CompoundTextCommand)this && |
129 | 344 this is fCurrent && // this is the current operation |
345 this.fStart is -1 && // the current operation text is not valid | |
346 fCurrent.fRedoModificationStamp !is IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP) { // but it has a redo stamp | |
347 canUndo= fCurrent.fRedoModificationStamp is docStamp; | |
348 } | |
349 | |
350 } | |
351 // if there is no timestamp to check, simply return true per the 3.0.1 behavior | |
352 return true; | |
353 } | |
354 return false; | |
355 } | |
356 | |
357 /* | |
358 * @see dwtx.core.commands.operations.IUndoableOperation#canRedo() | |
359 * @since 3.1 | |
360 */ | |
361 public bool canRedo() { | |
362 if (isConnected() && isValid()) { | |
363 IDocument doc= fTextViewer.getDocument(); | |
138 | 364 if ( cast(IDocumentExtension4)doc ) { |
134 | 365 long docStamp= (cast(IDocumentExtension4)doc).getModificationStamp(); |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
366 return docStamp is IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP || |
129 | 367 docStamp is getUndoModificationStamp(); |
368 } | |
369 // if there is no timestamp to check, simply return true per the 3.0.1 behavior | |
370 return true; | |
371 } | |
372 return false; | |
373 } | |
374 | |
375 /* | |
376 * @see dwtx.core.commands.operations.IUndoableOperation#canExecute() | |
377 * @since 3.1 | |
378 */ | |
379 public bool canExecute() { | |
380 return isConnected(); | |
381 } | |
382 | |
383 /* | |
384 * @see dwtx.core.commands.operations.IUndoableOperation#execute(dwtx.core.runtime.IProgressMonitor, dwtx.core.runtime.IAdaptable) | |
385 * @since 3.1 | |
386 */ | |
387 public IStatus execute(IProgressMonitor monitor, IAdaptable uiInfo) { | |
388 // Text commands execute as they are typed, so executing one has no effect. | |
389 return Status.OK_STATUS; | |
390 } | |
391 | |
392 /* | |
393 * Undo the change described by this command. Also selects and | |
394 * reveals the change. | |
395 */ | |
396 | |
397 /** | |
398 * Undo the change described by this command. Also selects and | |
399 * reveals the change. | |
400 * | |
401 * @param monitor the progress monitor to use if necessary | |
402 * @param uiInfo an adaptable that can provide UI info if needed | |
403 * @return the status | |
404 */ | |
405 public IStatus undo(IProgressMonitor monitor, IAdaptable uiInfo) { | |
406 if (isValid()) { | |
407 undoTextChange(); | |
408 selectAndReveal(fStart, fPreservedText is null ? 0 : fPreservedText.length()); | |
409 resetProcessChangeSate(); | |
410 return Status.OK_STATUS; | |
411 } | |
412 return IOperationHistory.OPERATION_INVALID_STATUS; | |
413 } | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
414 |
129 | 415 /** |
416 * Re-applies the change described by this command. | |
417 * | |
418 * @since 2.0 | |
419 */ | |
420 protected void redoTextChange() { | |
421 try { | |
422 IDocument document= fTextViewer.getDocument(); | |
138 | 423 if ( cast(IDocumentExtension4)document ) |
134 | 424 (cast(IDocumentExtension4)document).replace(fStart, fEnd - fStart, fText, fRedoModificationStamp); |
129 | 425 else |
426 fTextViewer.getDocument().replace(fStart, fEnd - fStart, fText); | |
427 } catch (BadLocationException x) { | |
428 } | |
429 } | |
430 | |
431 /** | |
432 * Re-applies the change described by this command that previously been | |
433 * rolled back. Also selects and reveals the change. | |
434 * | |
435 * @param monitor the progress monitor to use if necessary | |
436 * @param uiInfo an adaptable that can provide UI info if needed | |
437 * @return the status | |
438 */ | |
439 public IStatus redo(IProgressMonitor monitor, IAdaptable uiInfo) { | |
440 if (isValid()) { | |
441 redoTextChange(); | |
442 resetProcessChangeSate(); | |
443 selectAndReveal(fStart, fText is null ? 0 : fText.length()); | |
444 return Status.OK_STATUS; | |
445 } | |
446 return IOperationHistory.OPERATION_INVALID_STATUS; | |
447 } | |
448 | |
449 /** | |
450 * Update the command in response to a commit. | |
451 * | |
452 * @since 3.1 | |
453 */ | |
454 | |
455 protected void updateCommand() { | |
456 fText= fTextBuffer.toString(); | |
162 | 457 fTextBuffer.truncate(0); |
129 | 458 fPreservedText= fPreservedTextBuffer.toString(); |
162 | 459 fPreservedTextBuffer.truncate(0); |
129 | 460 } |
461 | |
462 /** | |
463 * Creates a new uncommitted text command depending on whether | |
464 * a compound change is currently being executed. | |
465 * | |
466 * @return a new, uncommitted text command or a compound text command | |
467 */ | |
468 protected TextCommand createCurrent() { | |
469 return fFoldingIntoCompoundChange ? new CompoundTextCommand(fUndoContext) : new TextCommand(fUndoContext); | |
470 } | |
471 | |
472 /** | |
473 * Commits the current change into this command. | |
474 */ | |
475 protected void commit() { | |
476 if (fStart < 0) { | |
477 if (fFoldingIntoCompoundChange) { | |
478 fCurrent= createCurrent(); | |
479 } else { | |
480 reinitialize(); | |
481 } | |
482 } else { | |
483 updateCommand(); | |
484 fCurrent= createCurrent(); | |
485 } | |
486 resetProcessChangeSate(); | |
487 } | |
488 | |
489 /** | |
490 * Updates the text from the buffers without resetting | |
491 * the buffers or adding anything to the stack. | |
492 * | |
493 * @since 3.1 | |
494 */ | |
495 protected void pretendCommit() { | |
496 if (fStart > -1) { | |
497 fText= fTextBuffer.toString(); | |
498 fPreservedText= fPreservedTextBuffer.toString(); | |
499 } | |
500 } | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
501 |
129 | 502 /** |
503 * Attempt a commit of this command and answer true if a new | |
504 * fCurrent was created as a result of the commit. | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
505 * |
129 | 506 * @return true if the command was committed and created a |
507 * new fCurrent, false if not. | |
508 * @since 3.1 | |
509 */ | |
510 protected bool attemptCommit() { | |
511 pretendCommit(); | |
512 if (isValid()) { | |
137 | 513 this.outer.commit(); |
129 | 514 return true; |
515 } | |
516 return false; | |
517 } | |
518 | |
519 /** | |
520 * Checks whether this text command is valid for undo or redo. | |
521 * | |
522 * @return <code>true</code> if the command is valid for undo or redo | |
523 * @since 3.1 | |
524 */ | |
525 protected bool isValid() { | |
526 return fStart > -1 && | |
527 fEnd > -1 && | |
528 fText !is null; | |
529 } | |
530 | |
531 /* | |
532 * @see java.lang.Object#toString() | |
533 * @since 3.1 | |
534 */ | |
160 | 535 public override String toString() { |
129 | 536 String delimiter= ", "; //$NON-NLS-1$ |
537 StringBuffer text= new StringBuffer(super.toString()); | |
538 text.append("\n"); //$NON-NLS-1$ | |
162 | 539 text.append(this.classinfo.name); |
129 | 540 text.append(" undo modification stamp: "); //$NON-NLS-1$ |
541 text.append(fUndoModificationStamp); | |
542 text.append(" redo modification stamp: "); //$NON-NLS-1$ | |
543 text.append(fRedoModificationStamp); | |
544 text.append(" start: "); //$NON-NLS-1$ | |
545 text.append(fStart); | |
546 text.append(delimiter); | |
547 text.append("end: "); //$NON-NLS-1$ | |
548 text.append(fEnd); | |
549 text.append(delimiter); | |
550 text.append("text: '"); //$NON-NLS-1$ | |
551 text.append(fText); | |
552 text.append('\''); | |
553 text.append(delimiter); | |
554 text.append("preservedText: '"); //$NON-NLS-1$ | |
555 text.append(fPreservedText); | |
556 text.append('\''); | |
557 return text.toString(); | |
558 } | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
559 |
129 | 560 /** |
561 * Return the undo modification stamp | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
562 * |
129 | 563 * @return the undo modification stamp for this command |
564 * @since 3.1 | |
565 */ | |
566 protected long getUndoModificationStamp() { | |
567 return fUndoModificationStamp; | |
568 } | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
569 |
129 | 570 /** |
571 * Return the redo modification stamp | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
572 * |
129 | 573 * @return the redo modification stamp for this command |
574 * @since 3.1 | |
575 */ | |
576 protected long getRedoModificationStamp() { | |
577 return fRedoModificationStamp; | |
578 } | |
579 } | |
580 | |
581 /** | |
582 * Represents an undo-able edit command consisting of several | |
583 * individual edit commands. | |
584 */ | |
585 class CompoundTextCommand : TextCommand { | |
586 | |
587 /** The list of individual commands */ | |
162 | 588 private List fCommands; |
129 | 589 |
590 /** | |
591 * Creates a new compound text command. | |
592 * | |
593 * @param context the undo context for this command | |
594 * @since 3.1 | |
595 */ | |
133
7d818bd32d63
Fix ctors to this with gvim regexp
Frank Benoit <benoit@tionex.de>
parents:
131
diff
changeset
|
596 this(IUndoContext context) { |
129 | 597 super(context); |
162 | 598 fCommands= new ArrayList(); |
129 | 599 } |
600 | |
601 /** | |
602 * Adds a new individual command to this compound command. | |
603 * | |
604 * @param command the command to be added | |
605 */ | |
606 protected void add(TextCommand command) { | |
607 fCommands.add(command); | |
608 } | |
609 | |
610 /* | |
611 * @see dwtx.jface.text.DefaultUndoManager.TextCommand#undo() | |
612 */ | |
613 public IStatus undo(IProgressMonitor monitor, IAdaptable uiInfo) { | |
614 resetProcessChangeSate(); | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
615 |
129 | 616 int size= fCommands.size(); |
617 if (size > 0) { | |
618 | |
619 TextCommand c; | |
620 | |
621 for (int i= size -1; i > 0; --i) { | |
134 | 622 c= cast(TextCommand) fCommands.get(i); |
129 | 623 c.undoTextChange(); |
624 } | |
625 | |
134 | 626 c= cast(TextCommand) fCommands.get(0); |
129 | 627 c.undo(monitor, uiInfo); |
628 } | |
629 | |
630 return Status.OK_STATUS; | |
631 } | |
632 | |
633 /* | |
634 * @see dwtx.jface.text.DefaultUndoManager.TextCommand#redo() | |
635 */ | |
636 public IStatus redo(IProgressMonitor monitor, IAdaptable uiInfo) { | |
637 resetProcessChangeSate(); | |
638 | |
639 int size= fCommands.size(); | |
640 if (size > 0) { | |
641 | |
642 TextCommand c; | |
643 | |
644 for (int i= 0; i < size -1; ++i) { | |
134 | 645 c= cast(TextCommand) fCommands.get(i); |
129 | 646 c.redoTextChange(); |
647 } | |
648 | |
134 | 649 c= cast(TextCommand) fCommands.get(size -1); |
129 | 650 c.redo(monitor, uiInfo); |
651 } | |
652 return Status.OK_STATUS; | |
653 } | |
654 | |
655 /* | |
656 * @see TextCommand#updateCommand | |
657 | |
658 */ | |
659 | |
660 protected void updateCommand() { | |
661 // first gather the data from the buffers | |
662 super.updateCommand(); | |
663 | |
664 // the result of the command update is stored as a child command | |
665 TextCommand c= new TextCommand(fUndoContext); | |
666 c.fStart= fStart; | |
667 c.fEnd= fEnd; | |
668 c.fText= fText; | |
669 c.fPreservedText= fPreservedText; | |
670 c.fUndoModificationStamp= fUndoModificationStamp; | |
671 c.fRedoModificationStamp= fRedoModificationStamp; | |
672 add(c); | |
673 | |
674 // clear out all indexes now that the child is added | |
675 reinitialize(); | |
676 } | |
677 | |
678 /* | |
679 * @see TextCommand#createCurrent | |
680 */ | |
681 protected TextCommand createCurrent() { | |
682 | |
683 if (!fFoldingIntoCompoundChange) | |
684 return new TextCommand(fUndoContext); | |
685 | |
686 reinitialize(); | |
687 return this; | |
688 } | |
689 | |
690 /* | |
691 * @see dwtx.jface.text.DefaultUndoManager.TextCommand#commit() | |
692 */ | |
693 protected void commit() { | |
694 // if there is pending data, update the command | |
695 if (fStart > -1) | |
696 updateCommand(); | |
697 fCurrent= createCurrent(); | |
698 resetProcessChangeSate(); | |
699 } | |
700 | |
701 /** | |
702 * Checks whether the command is valid for undo or redo. | |
703 * | |
704 * @return true if the command is valid. | |
705 * @since 3.1 | |
706 */ | |
707 protected bool isValid() { | |
708 if (isConnected()) | |
709 return (fStart > -1 || fCommands.size() > 0); | |
710 return false; | |
711 } | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
712 |
129 | 713 /** |
714 * Returns the undo modification stamp. | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
715 * |
129 | 716 * @return the undo modification stamp |
717 * @since 3.1 | |
718 */ | |
719 protected long getUndoModificationStamp() { | |
720 if (fStart > -1) | |
721 return super.getUndoModificationStamp(); | |
722 else if (fCommands.size() > 0) | |
134 | 723 return (cast(TextCommand)fCommands.get(0)).getUndoModificationStamp(); |
129 | 724 |
725 return fUndoModificationStamp; | |
726 } | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
727 |
129 | 728 /** |
729 * Returns the redo modification stamp. | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
730 * |
129 | 731 * @return the redo modification stamp |
732 * @since 3.1 | |
733 */ | |
734 protected long getRedoModificationStamp() { | |
735 if (fStart > -1) | |
736 return super.getRedoModificationStamp(); | |
737 else if (fCommands.size() > 0) | |
134 | 738 return (cast(TextCommand)fCommands.get(fCommands.size()-1)).getRedoModificationStamp(); |
129 | 739 |
740 return fRedoModificationStamp; | |
741 } | |
742 } | |
743 | |
744 /** | |
745 * Internal listener to mouse and key events. | |
746 */ | |
747 class KeyAndMouseListener : MouseListener, KeyListener { | |
748 | |
749 /* | |
750 * @see MouseListener#mouseDoubleClick | |
751 */ | |
752 public void mouseDoubleClick(MouseEvent e) { | |
753 } | |
754 | |
755 /* | |
756 * If the right mouse button is pressed, the current editing command is closed | |
757 * @see MouseListener#mouseDown | |
758 */ | |
759 public void mouseDown(MouseEvent e) { | |
760 if (e.button is 1) | |
761 commit(); | |
762 } | |
763 | |
764 /* | |
765 * @see MouseListener#mouseUp | |
766 */ | |
767 public void mouseUp(MouseEvent e) { | |
768 } | |
769 | |
770 /* | |
771 * @see KeyListener#keyPressed | |
772 */ | |
773 public void keyReleased(KeyEvent e) { | |
774 } | |
775 | |
776 /* | |
777 * On cursor keys, the current editing command is closed | |
778 * @see KeyListener#keyPressed | |
779 */ | |
780 public void keyPressed(KeyEvent e) { | |
781 switch (e.keyCode) { | |
782 case DWT.ARROW_UP: | |
783 case DWT.ARROW_DOWN: | |
784 case DWT.ARROW_LEFT: | |
785 case DWT.ARROW_RIGHT: | |
786 commit(); | |
787 break; | |
788 } | |
789 } | |
790 } | |
791 | |
792 /** | |
793 * Internal listener to document changes. | |
794 */ | |
795 class DocumentListener : IDocumentListener { | |
796 | |
797 private String fReplacedText; | |
798 | |
799 /* | |
800 * @see dwtx.jface.text.IDocumentListener#documentAboutToBeChanged(dwtx.jface.text.DocumentEvent) | |
801 */ | |
802 public void documentAboutToBeChanged(DocumentEvent event) { | |
803 try { | |
804 fReplacedText= event.getDocument().get(event.getOffset(), event.getLength()); | |
805 fPreservedUndoModificationStamp= event.getModificationStamp(); | |
806 } catch (BadLocationException x) { | |
807 fReplacedText= null; | |
808 } | |
809 } | |
810 | |
811 /* | |
812 * @see dwtx.jface.text.IDocumentListener#documentChanged(dwtx.jface.text.DocumentEvent) | |
813 */ | |
814 public void documentChanged(DocumentEvent event) { | |
815 fPreservedRedoModificationStamp= event.getModificationStamp(); | |
816 | |
817 // record the current valid state for the top operation in case it remains the | |
818 // top operation but changes state. | |
819 IUndoableOperation op= fHistory.getUndoOperation(fUndoContext); | |
820 bool wasValid= false; | |
821 if (op !is null) | |
822 wasValid= op.canUndo(); | |
823 // Process the change, providing the before and after timestamps | |
824 processChange(event.getOffset(), event.getOffset() + event.getLength(), event.getText(), fReplacedText, fPreservedUndoModificationStamp, fPreservedRedoModificationStamp); | |
825 | |
826 // now update fCurrent with the latest buffers from the document change. | |
827 fCurrent.pretendCommit(); | |
828 | |
829 if (op is fCurrent) { | |
830 // if the document change did not cause a new fCurrent to be created, then we should | |
831 // notify the history that the current operation changed if its validity has changed. | |
832 if (wasValid !is fCurrent.isValid()) | |
833 fHistory.operationChanged(op); | |
834 } | |
835 else { | |
836 // if the change created a new fCurrent that we did not yet add to the | |
837 // stack, do so if it's valid and we are not in the middle of a compound change. | |
838 if (fCurrent !is fLastAddedCommand && fCurrent.isValid()) { | |
839 addToCommandStack(fCurrent); | |
840 } | |
841 } | |
842 } | |
843 } | |
844 | |
845 /** | |
846 * Internal text input listener. | |
847 */ | |
848 class TextInputListener : ITextInputListener { | |
849 | |
850 /* | |
851 * @see dwtx.jface.text.ITextInputListener#inputDocumentAboutToBeChanged(dwtx.jface.text.IDocument, dwtx.jface.text.IDocument) | |
852 */ | |
853 public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) { | |
854 if (oldInput !is null && fDocumentListener !is null) { | |
855 oldInput.removeDocumentListener(fDocumentListener); | |
856 commit(); | |
857 } | |
858 } | |
859 | |
860 /* | |
861 * @see dwtx.jface.text.ITextInputListener#inputDocumentChanged(dwtx.jface.text.IDocument, dwtx.jface.text.IDocument) | |
862 */ | |
863 public void inputDocumentChanged(IDocument oldInput, IDocument newInput) { | |
864 if (newInput !is null) { | |
865 if (fDocumentListener is null) | |
866 fDocumentListener= new DocumentListener(); | |
867 newInput.addDocumentListener(fDocumentListener); | |
868 } | |
869 } | |
870 | |
871 } | |
872 | |
873 /* | |
874 * @see IOperationHistoryListener | |
875 * @since 3.1 | |
876 */ | |
877 class HistoryListener : IOperationHistoryListener { | |
878 private IUndoableOperation fOperation; | |
879 | |
156 | 880 public void historyNotification(OperationHistoryEvent event) { |
881 int type= event.getEventType(); | |
129 | 882 switch (type) { |
883 case OperationHistoryEvent.ABOUT_TO_UNDO: | |
884 case OperationHistoryEvent.ABOUT_TO_REDO: | |
885 // if this is one of our operations | |
886 if (event.getOperation().hasContext(fUndoContext)) { | |
162 | 887 fTextViewer.getTextWidget().getDisplay().syncExec(dgRunnable((OperationHistoryEvent event_, int type_ ) { |
156 | 888 // if we are undoing/redoing a command we generated, then ignore |
889 // the document changes associated with this undo or redo. | |
162 | 890 if (event_.getOperation() ) { |
156 | 891 if ( cast(TextViewer)fTextViewer ) |
162 | 892 (cast(TextViewer)fTextViewer).ignoreAutoEditStrategies_package(true); |
156 | 893 listenToTextChanges(false); |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
894 |
156 | 895 // in the undo case only, make sure compounds are closed |
896 if (type_ is OperationHistoryEvent.ABOUT_TO_UNDO) { | |
897 if (fFoldingIntoCompoundChange) { | |
898 endCompoundChange(); | |
129 | 899 } |
900 } | |
156 | 901 } else { |
902 // the undo or redo has our context, but it is not one of | |
903 // our commands. We will listen to the changes, but will | |
904 // reset the state that tracks the undo/redo history. | |
905 commit(); | |
906 fLastAddedCommand= null; | |
129 | 907 } |
156 | 908 }, event, type )); |
129 | 909 fOperation= event.getOperation(); |
910 } | |
911 break; | |
912 case OperationHistoryEvent.UNDONE: | |
913 case OperationHistoryEvent.REDONE: | |
914 case OperationHistoryEvent.OPERATION_NOT_OK: | |
915 if (event.getOperation() is fOperation) { | |
135 | 916 fTextViewer.getTextWidget().getDisplay().syncExec(new class() Runnable { |
129 | 917 public void run() { |
918 listenToTextChanges(true); | |
919 fOperation= null; | |
138 | 920 if ( cast(TextViewer)fTextViewer ) |
162 | 921 (cast(TextViewer)fTextViewer).ignoreAutoEditStrategies_package(false); |
129 | 922 } |
923 }); | |
924 } | |
925 break; | |
926 } | |
927 } | |
928 | |
929 } | |
930 | |
931 /** Text buffer to collect text which is inserted into the viewer */ | |
162 | 932 private StringBuffer fTextBuffer; |
129 | 933 /** Text buffer to collect viewer content which has been replaced */ |
162 | 934 private StringBuffer fPreservedTextBuffer; |
129 | 935 /** The document modification stamp for undo. */ |
936 protected long fPreservedUndoModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP; | |
937 /** The document modification stamp for redo. */ | |
938 protected long fPreservedRedoModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP; | |
939 /** The internal key and mouse event listener */ | |
940 private KeyAndMouseListener fKeyAndMouseListener; | |
941 /** The internal document listener */ | |
942 private DocumentListener fDocumentListener; | |
943 /** The internal text input listener */ | |
944 private TextInputListener fTextInputListener; | |
945 | |
946 | |
947 /** Indicates inserting state */ | |
948 private bool fInserting= false; | |
949 /** Indicates overwriting state */ | |
950 private bool fOverwriting= false; | |
951 /** Indicates whether the current change belongs to a compound change */ | |
952 private bool fFoldingIntoCompoundChange= false; | |
953 | |
954 /** The text viewer the undo manager is connected to */ | |
955 private ITextViewer fTextViewer; | |
956 | |
957 /** Supported undo level */ | |
958 private int fUndoLevel; | |
959 /** The currently constructed edit command */ | |
960 private TextCommand fCurrent; | |
961 /** The last delete edit command */ | |
962 private TextCommand fPreviousDelete; | |
963 | |
964 /** | |
965 * The undo context. | |
966 * @since 3.1 | |
967 */ | |
968 private IOperationHistory fHistory; | |
969 /** | |
970 * The operation history. | |
971 * @since 3.1 | |
972 */ | |
973 private IUndoContext fUndoContext; | |
974 /** | |
975 * The operation history listener used for managing undo and redo before | |
976 * and after the individual commands are performed. | |
977 * @since 3.1 | |
978 */ | |
159 | 979 private IOperationHistoryListener fHistoryListener; |
129 | 980 |
981 /** | |
982 * The command last added to the operation history. This must be tracked | |
983 * internally instead of asking the history, since outside parties may be placing | |
984 * items on our undo/redo history. | |
985 */ | |
986 private TextCommand fLastAddedCommand= null; | |
987 | |
988 /** | |
989 * Creates a new undo manager who remembers the specified number of edit commands. | |
990 * | |
991 * @param undoLevel the length of this manager's history | |
992 */ | |
133
7d818bd32d63
Fix ctors to this with gvim regexp
Frank Benoit <benoit@tionex.de>
parents:
131
diff
changeset
|
993 public this(int undoLevel) { |
162 | 994 fTextBuffer= new StringBuffer(); |
995 fPreservedTextBuffer= new StringBuffer(); | |
996 | |
159 | 997 fHistoryListener= new HistoryListener(); |
129 | 998 fHistory= OperationHistoryFactory.getOperationHistory(); |
999 setMaximalUndoLevel(undoLevel); | |
1000 } | |
1001 | |
1002 /** | |
1003 * Returns whether this undo manager is connected to a text viewer. | |
1004 * | |
1005 * @return <code>true</code> if connected, <code>false</code> otherwise | |
1006 * @since 3.1 | |
1007 */ | |
1008 private bool isConnected() { | |
1009 return fTextViewer !is null; | |
1010 } | |
1011 | |
1012 /* | |
1013 * @see IUndoManager#beginCompoundChange | |
1014 */ | |
1015 public void beginCompoundChange() { | |
1016 if (isConnected()) { | |
1017 fFoldingIntoCompoundChange= true; | |
1018 commit(); | |
1019 } | |
1020 } | |
1021 | |
1022 | |
1023 /* | |
1024 * @see IUndoManager#endCompoundChange | |
1025 */ | |
1026 public void endCompoundChange() { | |
1027 if (isConnected()) { | |
1028 fFoldingIntoCompoundChange= false; | |
1029 commit(); | |
1030 } | |
1031 } | |
1032 | |
1033 /** | |
1034 * Registers all necessary listeners with the text viewer. | |
1035 */ | |
1036 private void addListeners() { | |
1037 StyledText text= fTextViewer.getTextWidget(); | |
1038 if (text !is null) { | |
1039 fKeyAndMouseListener= new KeyAndMouseListener(); | |
1040 text.addMouseListener(fKeyAndMouseListener); | |
1041 text.addKeyListener(fKeyAndMouseListener); | |
1042 fTextInputListener= new TextInputListener(); | |
1043 fTextViewer.addTextInputListener(fTextInputListener); | |
1044 fHistory.addOperationHistoryListener(fHistoryListener); | |
1045 listenToTextChanges(true); | |
1046 } | |
1047 } | |
1048 | |
1049 /** | |
1050 * Unregister all previously installed listeners from the text viewer. | |
1051 */ | |
1052 private void removeListeners() { | |
1053 StyledText text= fTextViewer.getTextWidget(); | |
1054 if (text !is null) { | |
1055 if (fKeyAndMouseListener !is null) { | |
1056 text.removeMouseListener(fKeyAndMouseListener); | |
1057 text.removeKeyListener(fKeyAndMouseListener); | |
1058 fKeyAndMouseListener= null; | |
1059 } | |
1060 if (fTextInputListener !is null) { | |
1061 fTextViewer.removeTextInputListener(fTextInputListener); | |
1062 fTextInputListener= null; | |
1063 } | |
1064 listenToTextChanges(false); | |
1065 fHistory.removeOperationHistoryListener(fHistoryListener); | |
1066 } | |
1067 } | |
1068 | |
1069 /** | |
1070 * Adds the given command to the operation history if it is not part of | |
1071 * a compound change. | |
1072 * | |
1073 * @param command the command to be added | |
1074 * @since 3.1 | |
1075 */ | |
1076 private void addToCommandStack(TextCommand command){ | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
1077 if (!fFoldingIntoCompoundChange || cast(CompoundTextCommand)command ) { |
129 | 1078 fHistory.add(command); |
1079 fLastAddedCommand= command; | |
1080 } | |
1081 } | |
1082 | |
1083 /** | |
1084 * Disposes the command stack. | |
1085 * | |
1086 * @since 3.1 | |
1087 */ | |
1088 private void disposeCommandStack() { | |
1089 fHistory.dispose(fUndoContext, true, true, true); | |
1090 } | |
1091 | |
1092 /** | |
1093 * Initializes the command stack. | |
1094 * | |
1095 * @since 3.1 | |
1096 */ | |
1097 private void initializeCommandStack() { | |
1098 if (fHistory !is null && fUndoContext !is null) | |
1099 fHistory.dispose(fUndoContext, true, true, false); | |
1100 | |
1101 } | |
1102 | |
1103 /** | |
1104 * Switches the state of whether there is a text listener or not. | |
1105 * | |
1106 * @param listen the state which should be established | |
1107 */ | |
1108 private void listenToTextChanges(bool listen) { | |
1109 if (listen) { | |
1110 if (fDocumentListener is null && fTextViewer.getDocument() !is null) { | |
1111 fDocumentListener= new DocumentListener(); | |
1112 fTextViewer.getDocument().addDocumentListener(fDocumentListener); | |
1113 } | |
1114 } else if (!listen) { | |
1115 if (fDocumentListener !is null && fTextViewer.getDocument() !is null) { | |
1116 fTextViewer.getDocument().removeDocumentListener(fDocumentListener); | |
1117 fDocumentListener= null; | |
1118 } | |
1119 } | |
1120 } | |
1121 | |
1122 /** | |
1123 * Closes the current editing command and opens a new one. | |
1124 */ | |
1125 private void commit() { | |
1126 // if fCurrent has never been placed on the command stack, do so now. | |
1127 // this can happen when there are multiple programmatically commits in a single | |
1128 // document change. | |
1129 if (fLastAddedCommand !is fCurrent) { | |
1130 fCurrent.pretendCommit(); | |
1131 if (fCurrent.isValid()) | |
1132 addToCommandStack(fCurrent); | |
1133 } | |
1134 fCurrent.commit(); | |
1135 } | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
1136 |
129 | 1137 /** |
1138 * Reset processChange state. | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
1139 * |
129 | 1140 * @since 3.2 |
1141 */ | |
1142 private void resetProcessChangeSate() { | |
1143 fInserting= false; | |
1144 fOverwriting= false; | |
1145 fPreviousDelete.reinitialize(); | |
1146 } | |
1147 | |
1148 /** | |
1149 * Checks whether the given text starts with a line delimiter and | |
1150 * subsequently contains a white space only. | |
1151 * | |
1152 * @param text the text to check | |
1153 * @return <code>true</code> if the text is a line delimiter followed by whitespace, <code>false</code> otherwise | |
1154 */ | |
1155 private bool isWhitespaceText(String text) { | |
1156 | |
1157 if (text is null || text.length() is 0) | |
1158 return false; | |
1159 | |
1160 String[] delimiters= fTextViewer.getDocument().getLegalLineDelimiters(); | |
1161 int index= TextUtilities.startsWith(delimiters, text); | |
1162 if (index > -1) { | |
1163 char c; | |
1164 int length= text.length(); | |
162 | 1165 for (int i= delimiters[index].length; i < length; i++) { |
129 | 1166 c= text.charAt(i); |
1167 if (c !is ' ' && c !is '\t') | |
1168 return false; | |
1169 } | |
1170 return true; | |
1171 } | |
1172 | |
1173 return false; | |
1174 } | |
1175 | |
1176 private void processChange(int modelStart, int modelEnd, String insertedText, String replacedText, long beforeChangeModificationStamp, long afterChangeModificationStamp) { | |
1177 | |
1178 if (insertedText is null) | |
1179 insertedText= ""; //$NON-NLS-1$ | |
1180 | |
1181 if (replacedText is null) | |
1182 replacedText= ""; //$NON-NLS-1$ | |
1183 | |
1184 int length= insertedText.length(); | |
1185 int diff= modelEnd - modelStart; | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
1186 |
129 | 1187 if (fCurrent.fUndoModificationStamp is IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP) |
1188 fCurrent.fUndoModificationStamp= beforeChangeModificationStamp; | |
1189 | |
1190 // normalize | |
1191 if (diff < 0) { | |
1192 int tmp= modelEnd; | |
1193 modelEnd= modelStart; | |
1194 modelStart= tmp; | |
1195 } | |
1196 | |
1197 if (modelStart is modelEnd) { | |
1198 // text will be inserted | |
1199 if ((length is 1) || isWhitespaceText(insertedText)) { | |
1200 // by typing or whitespace | |
1201 if (!fInserting || (modelStart !is fCurrent.fStart + fTextBuffer.length())) { | |
1202 fCurrent.fRedoModificationStamp= beforeChangeModificationStamp; | |
1203 if (fCurrent.attemptCommit()) | |
1204 fCurrent.fUndoModificationStamp= beforeChangeModificationStamp; | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
1205 |
129 | 1206 fInserting= true; |
1207 } | |
1208 if (fCurrent.fStart < 0) | |
1209 fCurrent.fStart= fCurrent.fEnd= modelStart; | |
1210 if (length > 0) | |
1211 fTextBuffer.append(insertedText); | |
1212 } else if (length >= 0) { | |
1213 // by pasting or model manipulation | |
1214 fCurrent.fRedoModificationStamp= beforeChangeModificationStamp; | |
1215 if (fCurrent.attemptCommit()) | |
1216 fCurrent.fUndoModificationStamp= beforeChangeModificationStamp; | |
1217 | |
1218 fCurrent.fStart= fCurrent.fEnd= modelStart; | |
1219 fTextBuffer.append(insertedText); | |
1220 fCurrent.fRedoModificationStamp= afterChangeModificationStamp; | |
1221 if (fCurrent.attemptCommit()) | |
1222 fCurrent.fUndoModificationStamp= afterChangeModificationStamp; | |
1223 | |
1224 } | |
1225 } else { | |
1226 if (length is 0) { | |
1227 // text will be deleted by backspace or DEL key or empty clipboard | |
162 | 1228 length= replacedText.length; |
129 | 1229 String[] delimiters= fTextViewer.getDocument().getLegalLineDelimiters(); |
1230 | |
1231 if ((length is 1) || TextUtilities.equals(delimiters, replacedText) > -1) { | |
1232 | |
1233 // whereby selection is empty | |
1234 | |
1235 if (fPreviousDelete.fStart is modelStart && fPreviousDelete.fEnd is modelEnd) { | |
1236 // repeated DEL | |
1237 | |
1238 // correct wrong settings of fCurrent | |
1239 if (fCurrent.fStart is modelEnd && fCurrent.fEnd is modelStart) { | |
1240 fCurrent.fStart= modelStart; | |
1241 fCurrent.fEnd= modelEnd; | |
1242 } | |
1243 // append to buffer && extend command range | |
1244 fPreservedTextBuffer.append(replacedText); | |
1245 ++fCurrent.fEnd; | |
1246 | |
1247 } else if (fPreviousDelete.fStart is modelEnd) { | |
1248 // repeated backspace | |
1249 | |
1250 // insert in buffer and extend command range | |
162 | 1251 fPreservedTextBuffer.select(0, 0); |
1252 fPreservedTextBuffer.replace(replacedText); | |
129 | 1253 fCurrent.fStart= modelStart; |
1254 | |
1255 } else { | |
1256 // either DEL or backspace for the first time | |
1257 | |
1258 fCurrent.fRedoModificationStamp= beforeChangeModificationStamp; | |
1259 if (fCurrent.attemptCommit()) | |
1260 fCurrent.fUndoModificationStamp= beforeChangeModificationStamp; | |
1261 | |
1262 // as we can not decide whether it was DEL or backspace we initialize for backspace | |
1263 fPreservedTextBuffer.append(replacedText); | |
1264 fCurrent.fStart= modelStart; | |
1265 fCurrent.fEnd= modelEnd; | |
1266 } | |
1267 | |
1268 fPreviousDelete.set(modelStart, modelEnd); | |
1269 | |
1270 } else if (length > 0) { | |
1271 // whereby selection is not empty | |
1272 fCurrent.fRedoModificationStamp= beforeChangeModificationStamp; | |
1273 if (fCurrent.attemptCommit()) | |
1274 fCurrent.fUndoModificationStamp= beforeChangeModificationStamp; | |
1275 | |
1276 fCurrent.fStart= modelStart; | |
1277 fCurrent.fEnd= modelEnd; | |
1278 fPreservedTextBuffer.append(replacedText); | |
1279 } | |
1280 } else { | |
1281 // text will be replaced | |
1282 | |
1283 if (length is 1) { | |
162 | 1284 length= replacedText.length; |
129 | 1285 String[] delimiters= fTextViewer.getDocument().getLegalLineDelimiters(); |
1286 | |
1287 if ((length is 1) || TextUtilities.equals(delimiters, replacedText) > -1) { | |
1288 // because of overwrite mode or model manipulation | |
1289 if (!fOverwriting || (modelStart !is fCurrent.fStart + fTextBuffer.length())) { | |
1290 fCurrent.fRedoModificationStamp= beforeChangeModificationStamp; | |
1291 if (fCurrent.attemptCommit()) | |
1292 fCurrent.fUndoModificationStamp= beforeChangeModificationStamp; | |
1293 | |
1294 fOverwriting= true; | |
1295 } | |
1296 | |
1297 if (fCurrent.fStart < 0) | |
1298 fCurrent.fStart= modelStart; | |
1299 | |
1300 fCurrent.fEnd= modelEnd; | |
1301 fTextBuffer.append(insertedText); | |
1302 fPreservedTextBuffer.append(replacedText); | |
1303 fCurrent.fRedoModificationStamp= afterChangeModificationStamp; | |
1304 return; | |
1305 } | |
1306 } | |
1307 // because of typing or pasting whereby selection is not empty | |
1308 fCurrent.fRedoModificationStamp= beforeChangeModificationStamp; | |
1309 if (fCurrent.attemptCommit()) | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
1310 fCurrent.fUndoModificationStamp= beforeChangeModificationStamp; |
129 | 1311 |
1312 fCurrent.fStart= modelStart; | |
1313 fCurrent.fEnd= modelEnd; | |
1314 fTextBuffer.append(insertedText); | |
1315 fPreservedTextBuffer.append(replacedText); | |
1316 } | |
1317 } | |
1318 // in all cases, the redo modification stamp is updated on the open command | |
1319 fCurrent.fRedoModificationStamp= afterChangeModificationStamp; | |
1320 } | |
1321 | |
1322 /** | |
1323 * Shows the given exception in an error dialog. | |
1324 * | |
1325 * @param title the dialog title | |
1326 * @param ex the exception | |
1327 * @since 3.1 | |
1328 */ | |
156 | 1329 private void openErrorDialog(String title, Exception ex) { |
129 | 1330 Shell shell= null; |
1331 if (isConnected()) { | |
1332 StyledText st= fTextViewer.getTextWidget(); | |
1333 if (st !is null && !st.isDisposed()) | |
1334 shell= st.getShell(); | |
1335 } | |
1336 if (Display.getCurrent() !is null) | |
162 | 1337 MessageDialog.openError(shell, title, ex.msg/+getLocalizedMessage()+/); |
129 | 1338 else { |
1339 Display display; | |
156 | 1340 Shell finalShell= shell; |
129 | 1341 if (finalShell !is null) |
1342 display= finalShell.getDisplay(); | |
1343 else | |
1344 display= Display.getDefault(); | |
156 | 1345 display.syncExec(dgRunnable( { |
162 | 1346 MessageDialog.openError(finalShell, title, ex.msg/+getLocalizedMessage()+/); |
156 | 1347 })); |
129 | 1348 } |
1349 } | |
1350 | |
1351 /* | |
1352 * @see dwtx.jface.text.IUndoManager#setMaximalUndoLevel(int) | |
1353 */ | |
1354 public void setMaximalUndoLevel(int undoLevel) { | |
1355 fUndoLevel= Math.max(0, undoLevel); | |
1356 if (isConnected()) { | |
1357 fHistory.setLimit(fUndoContext, fUndoLevel); | |
1358 } | |
1359 } | |
1360 | |
1361 /* | |
1362 * @see dwtx.jface.text.IUndoManager#connect(dwtx.jface.text.ITextViewer) | |
1363 */ | |
1364 public void connect(ITextViewer textViewer) { | |
1365 if (!isConnected() && textViewer !is null) { | |
1366 fTextViewer= textViewer; | |
1367 if (fUndoContext is null) | |
1368 fUndoContext= new ObjectUndoContext(this); | |
1369 | |
1370 fHistory.setLimit(fUndoContext, fUndoLevel); | |
1371 | |
1372 initializeCommandStack(); | |
1373 | |
1374 // open up the current command | |
1375 fCurrent= new TextCommand(fUndoContext); | |
1376 | |
1377 fPreviousDelete= new TextCommand(fUndoContext); | |
1378 addListeners(); | |
1379 } | |
1380 } | |
1381 | |
1382 /* | |
1383 * @see dwtx.jface.text.IUndoManager#disconnect() | |
1384 */ | |
1385 public void disconnect() { | |
1386 if (isConnected()) { | |
1387 | |
1388 removeListeners(); | |
1389 | |
1390 fCurrent= null; | |
1391 fTextViewer= null; | |
1392 disposeCommandStack(); | |
162 | 1393 fTextBuffer.clear(); |
1394 fPreservedTextBuffer.clear(); | |
129 | 1395 fUndoContext= null; |
1396 } | |
1397 } | |
1398 | |
1399 /* | |
1400 * @see dwtx.jface.text.IUndoManager#reset() | |
1401 */ | |
1402 public void reset() { | |
1403 if (isConnected()) { | |
1404 initializeCommandStack(); | |
1405 fCurrent= new TextCommand(fUndoContext); | |
1406 fFoldingIntoCompoundChange= false; | |
1407 fInserting= false; | |
1408 fOverwriting= false; | |
162 | 1409 fTextBuffer.truncate(0); |
1410 fPreservedTextBuffer.truncate(0); | |
129 | 1411 fPreservedUndoModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP; |
1412 fPreservedRedoModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP; | |
1413 } | |
1414 } | |
1415 | |
1416 /* | |
1417 * @see dwtx.jface.text.IUndoManager#redoable() | |
1418 */ | |
1419 public bool redoable() { | |
1420 return fHistory.canRedo(fUndoContext); | |
1421 } | |
1422 | |
1423 /* | |
1424 * @see dwtx.jface.text.IUndoManager#undoable() | |
1425 */ | |
1426 public bool undoable() { | |
1427 return fHistory.canUndo(fUndoContext); | |
1428 } | |
1429 | |
1430 /* | |
1431 * @see dwtx.jface.text.IUndoManager#redo() | |
1432 */ | |
1433 public void redo() { | |
1434 if (isConnected() && redoable()) { | |
1435 try { | |
1436 fHistory.redo(fUndoContext, null, null); | |
1437 } catch (ExecutionException ex) { | |
1438 openErrorDialog(JFaceTextMessages.getString("DefaultUndoManager.error.redoFailed.title"), ex); //$NON-NLS-1$ | |
1439 } | |
1440 } | |
1441 } | |
1442 | |
1443 /* | |
1444 * @see dwtx.jface.text.IUndoManager#undo() | |
1445 */ | |
1446 public void undo() { | |
1447 if (isConnected() && undoable()) { | |
1448 try { | |
1449 fHistory.undo(fUndoContext, null, null); | |
1450 } catch (ExecutionException ex) { | |
1451 openErrorDialog(JFaceTextMessages.getString("DefaultUndoManager.error.undoFailed.title"), ex); //$NON-NLS-1$ | |
1452 } | |
1453 } | |
1454 } | |
1455 | |
1456 /** | |
1457 * Selects and reveals the specified range. | |
1458 * | |
1459 * @param offset the offset of the range | |
1460 * @param length the length of the range | |
1461 * @since 3.0 | |
1462 */ | |
1463 protected void selectAndReveal(int offset, int length) { | |
138 | 1464 if ( cast(ITextViewerExtension5)fTextViewer ) { |
134 | 1465 ITextViewerExtension5 extension= cast(ITextViewerExtension5) fTextViewer; |
129 | 1466 extension.exposeModelRange(new Region(offset, length)); |
1467 } else if (!fTextViewer.overlapsWithVisibleRegion(offset, length)) | |
1468 fTextViewer.resetVisibleRegion(); | |
1469 | |
1470 fTextViewer.setSelectedRange(offset, length); | |
1471 fTextViewer.revealRange(offset, length); | |
1472 } | |
1473 | |
1474 /* | |
1475 * @see dwtx.jface.text.IUndoManagerExtension#getUndoContext() | |
1476 * @since 3.1 | |
1477 */ | |
1478 public IUndoContext getUndoContext() { | |
1479 return fUndoContext; | |
1480 } | |
1481 | |
1482 } |