Mercurial > projects > dwt-addons
annotate dwtx/jface/text/DefaultUndoManager.d @ 200:eb3414669eb0 default tip
fix for dmd 1.041 and tango 0.99.8
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Sat, 28 Mar 2009 03:09:57 +0100 |
parents | c3583c6ec027 |
children |
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; | |
192
c3583c6ec027
Added missing default cases for switch statements
Frank Benoit <benoit@tionex.de>
parents:
162
diff
changeset
|
788 default: |
129 | 789 } |
790 } | |
791 } | |
792 | |
793 /** | |
794 * Internal listener to document changes. | |
795 */ | |
796 class DocumentListener : IDocumentListener { | |
797 | |
798 private String fReplacedText; | |
799 | |
800 /* | |
801 * @see dwtx.jface.text.IDocumentListener#documentAboutToBeChanged(dwtx.jface.text.DocumentEvent) | |
802 */ | |
803 public void documentAboutToBeChanged(DocumentEvent event) { | |
804 try { | |
805 fReplacedText= event.getDocument().get(event.getOffset(), event.getLength()); | |
806 fPreservedUndoModificationStamp= event.getModificationStamp(); | |
807 } catch (BadLocationException x) { | |
808 fReplacedText= null; | |
809 } | |
810 } | |
811 | |
812 /* | |
813 * @see dwtx.jface.text.IDocumentListener#documentChanged(dwtx.jface.text.DocumentEvent) | |
814 */ | |
815 public void documentChanged(DocumentEvent event) { | |
816 fPreservedRedoModificationStamp= event.getModificationStamp(); | |
817 | |
818 // record the current valid state for the top operation in case it remains the | |
819 // top operation but changes state. | |
820 IUndoableOperation op= fHistory.getUndoOperation(fUndoContext); | |
821 bool wasValid= false; | |
822 if (op !is null) | |
823 wasValid= op.canUndo(); | |
824 // Process the change, providing the before and after timestamps | |
825 processChange(event.getOffset(), event.getOffset() + event.getLength(), event.getText(), fReplacedText, fPreservedUndoModificationStamp, fPreservedRedoModificationStamp); | |
826 | |
827 // now update fCurrent with the latest buffers from the document change. | |
828 fCurrent.pretendCommit(); | |
829 | |
830 if (op is fCurrent) { | |
831 // if the document change did not cause a new fCurrent to be created, then we should | |
832 // notify the history that the current operation changed if its validity has changed. | |
833 if (wasValid !is fCurrent.isValid()) | |
834 fHistory.operationChanged(op); | |
835 } | |
836 else { | |
837 // if the change created a new fCurrent that we did not yet add to the | |
838 // stack, do so if it's valid and we are not in the middle of a compound change. | |
839 if (fCurrent !is fLastAddedCommand && fCurrent.isValid()) { | |
840 addToCommandStack(fCurrent); | |
841 } | |
842 } | |
843 } | |
844 } | |
845 | |
846 /** | |
847 * Internal text input listener. | |
848 */ | |
849 class TextInputListener : ITextInputListener { | |
850 | |
851 /* | |
852 * @see dwtx.jface.text.ITextInputListener#inputDocumentAboutToBeChanged(dwtx.jface.text.IDocument, dwtx.jface.text.IDocument) | |
853 */ | |
854 public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) { | |
855 if (oldInput !is null && fDocumentListener !is null) { | |
856 oldInput.removeDocumentListener(fDocumentListener); | |
857 commit(); | |
858 } | |
859 } | |
860 | |
861 /* | |
862 * @see dwtx.jface.text.ITextInputListener#inputDocumentChanged(dwtx.jface.text.IDocument, dwtx.jface.text.IDocument) | |
863 */ | |
864 public void inputDocumentChanged(IDocument oldInput, IDocument newInput) { | |
865 if (newInput !is null) { | |
866 if (fDocumentListener is null) | |
867 fDocumentListener= new DocumentListener(); | |
868 newInput.addDocumentListener(fDocumentListener); | |
869 } | |
870 } | |
871 | |
872 } | |
873 | |
874 /* | |
875 * @see IOperationHistoryListener | |
876 * @since 3.1 | |
877 */ | |
878 class HistoryListener : IOperationHistoryListener { | |
879 private IUndoableOperation fOperation; | |
880 | |
156 | 881 public void historyNotification(OperationHistoryEvent event) { |
882 int type= event.getEventType(); | |
129 | 883 switch (type) { |
884 case OperationHistoryEvent.ABOUT_TO_UNDO: | |
885 case OperationHistoryEvent.ABOUT_TO_REDO: | |
886 // if this is one of our operations | |
887 if (event.getOperation().hasContext(fUndoContext)) { | |
162 | 888 fTextViewer.getTextWidget().getDisplay().syncExec(dgRunnable((OperationHistoryEvent event_, int type_ ) { |
156 | 889 // if we are undoing/redoing a command we generated, then ignore |
890 // the document changes associated with this undo or redo. | |
162 | 891 if (event_.getOperation() ) { |
156 | 892 if ( cast(TextViewer)fTextViewer ) |
162 | 893 (cast(TextViewer)fTextViewer).ignoreAutoEditStrategies_package(true); |
156 | 894 listenToTextChanges(false); |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
895 |
156 | 896 // in the undo case only, make sure compounds are closed |
897 if (type_ is OperationHistoryEvent.ABOUT_TO_UNDO) { | |
898 if (fFoldingIntoCompoundChange) { | |
899 endCompoundChange(); | |
129 | 900 } |
901 } | |
156 | 902 } else { |
903 // the undo or redo has our context, but it is not one of | |
904 // our commands. We will listen to the changes, but will | |
905 // reset the state that tracks the undo/redo history. | |
906 commit(); | |
907 fLastAddedCommand= null; | |
129 | 908 } |
156 | 909 }, event, type )); |
129 | 910 fOperation= event.getOperation(); |
911 } | |
912 break; | |
913 case OperationHistoryEvent.UNDONE: | |
914 case OperationHistoryEvent.REDONE: | |
915 case OperationHistoryEvent.OPERATION_NOT_OK: | |
916 if (event.getOperation() is fOperation) { | |
135 | 917 fTextViewer.getTextWidget().getDisplay().syncExec(new class() Runnable { |
129 | 918 public void run() { |
919 listenToTextChanges(true); | |
920 fOperation= null; | |
138 | 921 if ( cast(TextViewer)fTextViewer ) |
162 | 922 (cast(TextViewer)fTextViewer).ignoreAutoEditStrategies_package(false); |
129 | 923 } |
924 }); | |
925 } | |
926 break; | |
192
c3583c6ec027
Added missing default cases for switch statements
Frank Benoit <benoit@tionex.de>
parents:
162
diff
changeset
|
927 default: |
129 | 928 } |
929 } | |
930 | |
931 } | |
932 | |
933 /** Text buffer to collect text which is inserted into the viewer */ | |
162 | 934 private StringBuffer fTextBuffer; |
129 | 935 /** Text buffer to collect viewer content which has been replaced */ |
162 | 936 private StringBuffer fPreservedTextBuffer; |
129 | 937 /** The document modification stamp for undo. */ |
938 protected long fPreservedUndoModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP; | |
939 /** The document modification stamp for redo. */ | |
940 protected long fPreservedRedoModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP; | |
941 /** The internal key and mouse event listener */ | |
942 private KeyAndMouseListener fKeyAndMouseListener; | |
943 /** The internal document listener */ | |
944 private DocumentListener fDocumentListener; | |
945 /** The internal text input listener */ | |
946 private TextInputListener fTextInputListener; | |
947 | |
948 | |
949 /** Indicates inserting state */ | |
950 private bool fInserting= false; | |
951 /** Indicates overwriting state */ | |
952 private bool fOverwriting= false; | |
953 /** Indicates whether the current change belongs to a compound change */ | |
954 private bool fFoldingIntoCompoundChange= false; | |
955 | |
956 /** The text viewer the undo manager is connected to */ | |
957 private ITextViewer fTextViewer; | |
958 | |
959 /** Supported undo level */ | |
960 private int fUndoLevel; | |
961 /** The currently constructed edit command */ | |
962 private TextCommand fCurrent; | |
963 /** The last delete edit command */ | |
964 private TextCommand fPreviousDelete; | |
965 | |
966 /** | |
967 * The undo context. | |
968 * @since 3.1 | |
969 */ | |
970 private IOperationHistory fHistory; | |
971 /** | |
972 * The operation history. | |
973 * @since 3.1 | |
974 */ | |
975 private IUndoContext fUndoContext; | |
976 /** | |
977 * The operation history listener used for managing undo and redo before | |
978 * and after the individual commands are performed. | |
979 * @since 3.1 | |
980 */ | |
159 | 981 private IOperationHistoryListener fHistoryListener; |
129 | 982 |
983 /** | |
984 * The command last added to the operation history. This must be tracked | |
985 * internally instead of asking the history, since outside parties may be placing | |
986 * items on our undo/redo history. | |
987 */ | |
988 private TextCommand fLastAddedCommand= null; | |
989 | |
990 /** | |
991 * Creates a new undo manager who remembers the specified number of edit commands. | |
992 * | |
993 * @param undoLevel the length of this manager's history | |
994 */ | |
133
7d818bd32d63
Fix ctors to this with gvim regexp
Frank Benoit <benoit@tionex.de>
parents:
131
diff
changeset
|
995 public this(int undoLevel) { |
162 | 996 fTextBuffer= new StringBuffer(); |
997 fPreservedTextBuffer= new StringBuffer(); | |
998 | |
159 | 999 fHistoryListener= new HistoryListener(); |
129 | 1000 fHistory= OperationHistoryFactory.getOperationHistory(); |
1001 setMaximalUndoLevel(undoLevel); | |
1002 } | |
1003 | |
1004 /** | |
1005 * Returns whether this undo manager is connected to a text viewer. | |
1006 * | |
1007 * @return <code>true</code> if connected, <code>false</code> otherwise | |
1008 * @since 3.1 | |
1009 */ | |
1010 private bool isConnected() { | |
1011 return fTextViewer !is null; | |
1012 } | |
1013 | |
1014 /* | |
1015 * @see IUndoManager#beginCompoundChange | |
1016 */ | |
1017 public void beginCompoundChange() { | |
1018 if (isConnected()) { | |
1019 fFoldingIntoCompoundChange= true; | |
1020 commit(); | |
1021 } | |
1022 } | |
1023 | |
1024 | |
1025 /* | |
1026 * @see IUndoManager#endCompoundChange | |
1027 */ | |
1028 public void endCompoundChange() { | |
1029 if (isConnected()) { | |
1030 fFoldingIntoCompoundChange= false; | |
1031 commit(); | |
1032 } | |
1033 } | |
1034 | |
1035 /** | |
1036 * Registers all necessary listeners with the text viewer. | |
1037 */ | |
1038 private void addListeners() { | |
1039 StyledText text= fTextViewer.getTextWidget(); | |
1040 if (text !is null) { | |
1041 fKeyAndMouseListener= new KeyAndMouseListener(); | |
1042 text.addMouseListener(fKeyAndMouseListener); | |
1043 text.addKeyListener(fKeyAndMouseListener); | |
1044 fTextInputListener= new TextInputListener(); | |
1045 fTextViewer.addTextInputListener(fTextInputListener); | |
1046 fHistory.addOperationHistoryListener(fHistoryListener); | |
1047 listenToTextChanges(true); | |
1048 } | |
1049 } | |
1050 | |
1051 /** | |
1052 * Unregister all previously installed listeners from the text viewer. | |
1053 */ | |
1054 private void removeListeners() { | |
1055 StyledText text= fTextViewer.getTextWidget(); | |
1056 if (text !is null) { | |
1057 if (fKeyAndMouseListener !is null) { | |
1058 text.removeMouseListener(fKeyAndMouseListener); | |
1059 text.removeKeyListener(fKeyAndMouseListener); | |
1060 fKeyAndMouseListener= null; | |
1061 } | |
1062 if (fTextInputListener !is null) { | |
1063 fTextViewer.removeTextInputListener(fTextInputListener); | |
1064 fTextInputListener= null; | |
1065 } | |
1066 listenToTextChanges(false); | |
1067 fHistory.removeOperationHistoryListener(fHistoryListener); | |
1068 } | |
1069 } | |
1070 | |
1071 /** | |
1072 * Adds the given command to the operation history if it is not part of | |
1073 * a compound change. | |
1074 * | |
1075 * @param command the command to be added | |
1076 * @since 3.1 | |
1077 */ | |
1078 private void addToCommandStack(TextCommand command){ | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
1079 if (!fFoldingIntoCompoundChange || cast(CompoundTextCommand)command ) { |
129 | 1080 fHistory.add(command); |
1081 fLastAddedCommand= command; | |
1082 } | |
1083 } | |
1084 | |
1085 /** | |
1086 * Disposes the command stack. | |
1087 * | |
1088 * @since 3.1 | |
1089 */ | |
1090 private void disposeCommandStack() { | |
1091 fHistory.dispose(fUndoContext, true, true, true); | |
1092 } | |
1093 | |
1094 /** | |
1095 * Initializes the command stack. | |
1096 * | |
1097 * @since 3.1 | |
1098 */ | |
1099 private void initializeCommandStack() { | |
1100 if (fHistory !is null && fUndoContext !is null) | |
1101 fHistory.dispose(fUndoContext, true, true, false); | |
1102 | |
1103 } | |
1104 | |
1105 /** | |
1106 * Switches the state of whether there is a text listener or not. | |
1107 * | |
1108 * @param listen the state which should be established | |
1109 */ | |
1110 private void listenToTextChanges(bool listen) { | |
1111 if (listen) { | |
1112 if (fDocumentListener is null && fTextViewer.getDocument() !is null) { | |
1113 fDocumentListener= new DocumentListener(); | |
1114 fTextViewer.getDocument().addDocumentListener(fDocumentListener); | |
1115 } | |
1116 } else if (!listen) { | |
1117 if (fDocumentListener !is null && fTextViewer.getDocument() !is null) { | |
1118 fTextViewer.getDocument().removeDocumentListener(fDocumentListener); | |
1119 fDocumentListener= null; | |
1120 } | |
1121 } | |
1122 } | |
1123 | |
1124 /** | |
1125 * Closes the current editing command and opens a new one. | |
1126 */ | |
1127 private void commit() { | |
1128 // if fCurrent has never been placed on the command stack, do so now. | |
1129 // this can happen when there are multiple programmatically commits in a single | |
1130 // document change. | |
1131 if (fLastAddedCommand !is fCurrent) { | |
1132 fCurrent.pretendCommit(); | |
1133 if (fCurrent.isValid()) | |
1134 addToCommandStack(fCurrent); | |
1135 } | |
1136 fCurrent.commit(); | |
1137 } | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
1138 |
129 | 1139 /** |
1140 * Reset processChange state. | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
1141 * |
129 | 1142 * @since 3.2 |
1143 */ | |
1144 private void resetProcessChangeSate() { | |
1145 fInserting= false; | |
1146 fOverwriting= false; | |
1147 fPreviousDelete.reinitialize(); | |
1148 } | |
1149 | |
1150 /** | |
1151 * Checks whether the given text starts with a line delimiter and | |
1152 * subsequently contains a white space only. | |
1153 * | |
1154 * @param text the text to check | |
1155 * @return <code>true</code> if the text is a line delimiter followed by whitespace, <code>false</code> otherwise | |
1156 */ | |
1157 private bool isWhitespaceText(String text) { | |
1158 | |
1159 if (text is null || text.length() is 0) | |
1160 return false; | |
1161 | |
1162 String[] delimiters= fTextViewer.getDocument().getLegalLineDelimiters(); | |
1163 int index= TextUtilities.startsWith(delimiters, text); | |
1164 if (index > -1) { | |
1165 char c; | |
1166 int length= text.length(); | |
162 | 1167 for (int i= delimiters[index].length; i < length; i++) { |
129 | 1168 c= text.charAt(i); |
1169 if (c !is ' ' && c !is '\t') | |
1170 return false; | |
1171 } | |
1172 return true; | |
1173 } | |
1174 | |
1175 return false; | |
1176 } | |
1177 | |
1178 private void processChange(int modelStart, int modelEnd, String insertedText, String replacedText, long beforeChangeModificationStamp, long afterChangeModificationStamp) { | |
1179 | |
1180 if (insertedText is null) | |
1181 insertedText= ""; //$NON-NLS-1$ | |
1182 | |
1183 if (replacedText is null) | |
1184 replacedText= ""; //$NON-NLS-1$ | |
1185 | |
1186 int length= insertedText.length(); | |
1187 int diff= modelEnd - modelStart; | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
1188 |
129 | 1189 if (fCurrent.fUndoModificationStamp is IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP) |
1190 fCurrent.fUndoModificationStamp= beforeChangeModificationStamp; | |
1191 | |
1192 // normalize | |
1193 if (diff < 0) { | |
1194 int tmp= modelEnd; | |
1195 modelEnd= modelStart; | |
1196 modelStart= tmp; | |
1197 } | |
1198 | |
1199 if (modelStart is modelEnd) { | |
1200 // text will be inserted | |
1201 if ((length is 1) || isWhitespaceText(insertedText)) { | |
1202 // by typing or whitespace | |
1203 if (!fInserting || (modelStart !is fCurrent.fStart + fTextBuffer.length())) { | |
1204 fCurrent.fRedoModificationStamp= beforeChangeModificationStamp; | |
1205 if (fCurrent.attemptCommit()) | |
1206 fCurrent.fUndoModificationStamp= beforeChangeModificationStamp; | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
1207 |
129 | 1208 fInserting= true; |
1209 } | |
1210 if (fCurrent.fStart < 0) | |
1211 fCurrent.fStart= fCurrent.fEnd= modelStart; | |
1212 if (length > 0) | |
1213 fTextBuffer.append(insertedText); | |
1214 } else if (length >= 0) { | |
1215 // by pasting or model manipulation | |
1216 fCurrent.fRedoModificationStamp= beforeChangeModificationStamp; | |
1217 if (fCurrent.attemptCommit()) | |
1218 fCurrent.fUndoModificationStamp= beforeChangeModificationStamp; | |
1219 | |
1220 fCurrent.fStart= fCurrent.fEnd= modelStart; | |
1221 fTextBuffer.append(insertedText); | |
1222 fCurrent.fRedoModificationStamp= afterChangeModificationStamp; | |
1223 if (fCurrent.attemptCommit()) | |
1224 fCurrent.fUndoModificationStamp= afterChangeModificationStamp; | |
1225 | |
1226 } | |
1227 } else { | |
1228 if (length is 0) { | |
1229 // text will be deleted by backspace or DEL key or empty clipboard | |
162 | 1230 length= replacedText.length; |
129 | 1231 String[] delimiters= fTextViewer.getDocument().getLegalLineDelimiters(); |
1232 | |
1233 if ((length is 1) || TextUtilities.equals(delimiters, replacedText) > -1) { | |
1234 | |
1235 // whereby selection is empty | |
1236 | |
1237 if (fPreviousDelete.fStart is modelStart && fPreviousDelete.fEnd is modelEnd) { | |
1238 // repeated DEL | |
1239 | |
1240 // correct wrong settings of fCurrent | |
1241 if (fCurrent.fStart is modelEnd && fCurrent.fEnd is modelStart) { | |
1242 fCurrent.fStart= modelStart; | |
1243 fCurrent.fEnd= modelEnd; | |
1244 } | |
1245 // append to buffer && extend command range | |
1246 fPreservedTextBuffer.append(replacedText); | |
1247 ++fCurrent.fEnd; | |
1248 | |
1249 } else if (fPreviousDelete.fStart is modelEnd) { | |
1250 // repeated backspace | |
1251 | |
1252 // insert in buffer and extend command range | |
162 | 1253 fPreservedTextBuffer.select(0, 0); |
1254 fPreservedTextBuffer.replace(replacedText); | |
129 | 1255 fCurrent.fStart= modelStart; |
1256 | |
1257 } else { | |
1258 // either DEL or backspace for the first time | |
1259 | |
1260 fCurrent.fRedoModificationStamp= beforeChangeModificationStamp; | |
1261 if (fCurrent.attemptCommit()) | |
1262 fCurrent.fUndoModificationStamp= beforeChangeModificationStamp; | |
1263 | |
1264 // as we can not decide whether it was DEL or backspace we initialize for backspace | |
1265 fPreservedTextBuffer.append(replacedText); | |
1266 fCurrent.fStart= modelStart; | |
1267 fCurrent.fEnd= modelEnd; | |
1268 } | |
1269 | |
1270 fPreviousDelete.set(modelStart, modelEnd); | |
1271 | |
1272 } else if (length > 0) { | |
1273 // whereby selection is not empty | |
1274 fCurrent.fRedoModificationStamp= beforeChangeModificationStamp; | |
1275 if (fCurrent.attemptCommit()) | |
1276 fCurrent.fUndoModificationStamp= beforeChangeModificationStamp; | |
1277 | |
1278 fCurrent.fStart= modelStart; | |
1279 fCurrent.fEnd= modelEnd; | |
1280 fPreservedTextBuffer.append(replacedText); | |
1281 } | |
1282 } else { | |
1283 // text will be replaced | |
1284 | |
1285 if (length is 1) { | |
162 | 1286 length= replacedText.length; |
129 | 1287 String[] delimiters= fTextViewer.getDocument().getLegalLineDelimiters(); |
1288 | |
1289 if ((length is 1) || TextUtilities.equals(delimiters, replacedText) > -1) { | |
1290 // because of overwrite mode or model manipulation | |
1291 if (!fOverwriting || (modelStart !is fCurrent.fStart + fTextBuffer.length())) { | |
1292 fCurrent.fRedoModificationStamp= beforeChangeModificationStamp; | |
1293 if (fCurrent.attemptCommit()) | |
1294 fCurrent.fUndoModificationStamp= beforeChangeModificationStamp; | |
1295 | |
1296 fOverwriting= true; | |
1297 } | |
1298 | |
1299 if (fCurrent.fStart < 0) | |
1300 fCurrent.fStart= modelStart; | |
1301 | |
1302 fCurrent.fEnd= modelEnd; | |
1303 fTextBuffer.append(insertedText); | |
1304 fPreservedTextBuffer.append(replacedText); | |
1305 fCurrent.fRedoModificationStamp= afterChangeModificationStamp; | |
1306 return; | |
1307 } | |
1308 } | |
1309 // because of typing or pasting whereby selection is not empty | |
1310 fCurrent.fRedoModificationStamp= beforeChangeModificationStamp; | |
1311 if (fCurrent.attemptCommit()) | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
1312 fCurrent.fUndoModificationStamp= beforeChangeModificationStamp; |
129 | 1313 |
1314 fCurrent.fStart= modelStart; | |
1315 fCurrent.fEnd= modelEnd; | |
1316 fTextBuffer.append(insertedText); | |
1317 fPreservedTextBuffer.append(replacedText); | |
1318 } | |
1319 } | |
1320 // in all cases, the redo modification stamp is updated on the open command | |
1321 fCurrent.fRedoModificationStamp= afterChangeModificationStamp; | |
1322 } | |
1323 | |
1324 /** | |
1325 * Shows the given exception in an error dialog. | |
1326 * | |
1327 * @param title the dialog title | |
1328 * @param ex the exception | |
1329 * @since 3.1 | |
1330 */ | |
156 | 1331 private void openErrorDialog(String title, Exception ex) { |
129 | 1332 Shell shell= null; |
1333 if (isConnected()) { | |
1334 StyledText st= fTextViewer.getTextWidget(); | |
1335 if (st !is null && !st.isDisposed()) | |
1336 shell= st.getShell(); | |
1337 } | |
1338 if (Display.getCurrent() !is null) | |
162 | 1339 MessageDialog.openError(shell, title, ex.msg/+getLocalizedMessage()+/); |
129 | 1340 else { |
1341 Display display; | |
156 | 1342 Shell finalShell= shell; |
129 | 1343 if (finalShell !is null) |
1344 display= finalShell.getDisplay(); | |
1345 else | |
1346 display= Display.getDefault(); | |
156 | 1347 display.syncExec(dgRunnable( { |
162 | 1348 MessageDialog.openError(finalShell, title, ex.msg/+getLocalizedMessage()+/); |
156 | 1349 })); |
129 | 1350 } |
1351 } | |
1352 | |
1353 /* | |
1354 * @see dwtx.jface.text.IUndoManager#setMaximalUndoLevel(int) | |
1355 */ | |
1356 public void setMaximalUndoLevel(int undoLevel) { | |
1357 fUndoLevel= Math.max(0, undoLevel); | |
1358 if (isConnected()) { | |
1359 fHistory.setLimit(fUndoContext, fUndoLevel); | |
1360 } | |
1361 } | |
1362 | |
1363 /* | |
1364 * @see dwtx.jface.text.IUndoManager#connect(dwtx.jface.text.ITextViewer) | |
1365 */ | |
1366 public void connect(ITextViewer textViewer) { | |
1367 if (!isConnected() && textViewer !is null) { | |
1368 fTextViewer= textViewer; | |
1369 if (fUndoContext is null) | |
1370 fUndoContext= new ObjectUndoContext(this); | |
1371 | |
1372 fHistory.setLimit(fUndoContext, fUndoLevel); | |
1373 | |
1374 initializeCommandStack(); | |
1375 | |
1376 // open up the current command | |
1377 fCurrent= new TextCommand(fUndoContext); | |
1378 | |
1379 fPreviousDelete= new TextCommand(fUndoContext); | |
1380 addListeners(); | |
1381 } | |
1382 } | |
1383 | |
1384 /* | |
1385 * @see dwtx.jface.text.IUndoManager#disconnect() | |
1386 */ | |
1387 public void disconnect() { | |
1388 if (isConnected()) { | |
1389 | |
1390 removeListeners(); | |
1391 | |
1392 fCurrent= null; | |
1393 fTextViewer= null; | |
1394 disposeCommandStack(); | |
162 | 1395 fTextBuffer.clear(); |
1396 fPreservedTextBuffer.clear(); | |
129 | 1397 fUndoContext= null; |
1398 } | |
1399 } | |
1400 | |
1401 /* | |
1402 * @see dwtx.jface.text.IUndoManager#reset() | |
1403 */ | |
1404 public void reset() { | |
1405 if (isConnected()) { | |
1406 initializeCommandStack(); | |
1407 fCurrent= new TextCommand(fUndoContext); | |
1408 fFoldingIntoCompoundChange= false; | |
1409 fInserting= false; | |
1410 fOverwriting= false; | |
162 | 1411 fTextBuffer.truncate(0); |
1412 fPreservedTextBuffer.truncate(0); | |
129 | 1413 fPreservedUndoModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP; |
1414 fPreservedRedoModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP; | |
1415 } | |
1416 } | |
1417 | |
1418 /* | |
1419 * @see dwtx.jface.text.IUndoManager#redoable() | |
1420 */ | |
1421 public bool redoable() { | |
1422 return fHistory.canRedo(fUndoContext); | |
1423 } | |
1424 | |
1425 /* | |
1426 * @see dwtx.jface.text.IUndoManager#undoable() | |
1427 */ | |
1428 public bool undoable() { | |
1429 return fHistory.canUndo(fUndoContext); | |
1430 } | |
1431 | |
1432 /* | |
1433 * @see dwtx.jface.text.IUndoManager#redo() | |
1434 */ | |
1435 public void redo() { | |
1436 if (isConnected() && redoable()) { | |
1437 try { | |
1438 fHistory.redo(fUndoContext, null, null); | |
1439 } catch (ExecutionException ex) { | |
1440 openErrorDialog(JFaceTextMessages.getString("DefaultUndoManager.error.redoFailed.title"), ex); //$NON-NLS-1$ | |
1441 } | |
1442 } | |
1443 } | |
1444 | |
1445 /* | |
1446 * @see dwtx.jface.text.IUndoManager#undo() | |
1447 */ | |
1448 public void undo() { | |
1449 if (isConnected() && undoable()) { | |
1450 try { | |
1451 fHistory.undo(fUndoContext, null, null); | |
1452 } catch (ExecutionException ex) { | |
1453 openErrorDialog(JFaceTextMessages.getString("DefaultUndoManager.error.undoFailed.title"), ex); //$NON-NLS-1$ | |
1454 } | |
1455 } | |
1456 } | |
1457 | |
1458 /** | |
1459 * Selects and reveals the specified range. | |
1460 * | |
1461 * @param offset the offset of the range | |
1462 * @param length the length of the range | |
1463 * @since 3.0 | |
1464 */ | |
1465 protected void selectAndReveal(int offset, int length) { | |
138 | 1466 if ( cast(ITextViewerExtension5)fTextViewer ) { |
134 | 1467 ITextViewerExtension5 extension= cast(ITextViewerExtension5) fTextViewer; |
129 | 1468 extension.exposeModelRange(new Region(offset, length)); |
1469 } else if (!fTextViewer.overlapsWithVisibleRegion(offset, length)) | |
1470 fTextViewer.resetVisibleRegion(); | |
1471 | |
1472 fTextViewer.setSelectedRange(offset, length); | |
1473 fTextViewer.revealRange(offset, length); | |
1474 } | |
1475 | |
1476 /* | |
1477 * @see dwtx.jface.text.IUndoManagerExtension#getUndoContext() | |
1478 * @since 3.1 | |
1479 */ | |
1480 public IUndoContext getUndoContext() { | |
1481 return fUndoContext; | |
1482 } | |
1483 | |
1484 } |