129
|
1 /*******************************************************************************
|
|
2 * Copyright (c) 2006, 2008 IBM Corporation and others.
|
|
3 * All rights reserved. This program and the accompanying materials
|
|
4 * are made available under the terms of the Eclipse Public License v1.0
|
|
5 * which accompanies this distribution, and is available at
|
|
6 * http://www.eclipse.org/legal/epl-v10.html
|
|
7 *
|
|
8 * Contributors:
|
|
9 * IBM Corporation - initial API and implementation
|
|
10 * Port to the D programming language:
|
|
11 * Frank Benoit <benoit@tionex.de>
|
|
12 *******************************************************************************/
|
|
13
|
|
14 module dwtx.jface.text.TextViewerUndoManager;
|
|
15
|
|
16 import dwt.dwthelper.utils;
|
|
17
|
|
18
|
|
19
|
|
20
|
|
21
|
|
22 import dwt.DWT;
|
|
23 import dwt.custom.StyledText;
|
|
24 import dwt.events.KeyEvent;
|
|
25 import dwt.events.KeyListener;
|
|
26 import dwt.events.MouseEvent;
|
|
27 import dwt.events.MouseListener;
|
|
28 import dwt.widgets.Display;
|
|
29 import dwt.widgets.Shell;
|
|
30 import dwtx.core.commands.ExecutionException;
|
|
31 import dwtx.core.commands.operations.IUndoContext;
|
|
32 import dwtx.jface.dialogs.MessageDialog;
|
|
33 import dwtx.text.undo.DocumentUndoEvent;
|
|
34 import dwtx.text.undo.DocumentUndoManager;
|
|
35 import dwtx.text.undo.DocumentUndoManagerRegistry;
|
|
36 import dwtx.text.undo.IDocumentUndoListener;
|
|
37 import dwtx.text.undo.IDocumentUndoManager;
|
|
38
|
|
39
|
|
40 /**
|
|
41 * Implementation of {@link dwtx.jface.text.IUndoManager} using the shared
|
|
42 * document undo manager.
|
|
43 * <p>
|
|
44 * It registers with the connected text viewer as text input listener, and obtains
|
|
45 * its undo manager from the current document. It also monitors mouse and keyboard
|
|
46 * activities in order to partition the stream of text changes into undo-able
|
|
47 * edit commands.
|
|
48 * <p>
|
|
49 * This class is not intended to be subclassed.
|
|
50 * </p>
|
|
51 *
|
|
52 * @see ITextViewer
|
|
53 * @see ITextInputListener
|
|
54 * @see IDocumentUndoManager
|
|
55 * @see MouseListener
|
|
56 * @see KeyListener
|
|
57 * @see DocumentUndoManager
|
|
58 *
|
|
59 * @since 3.2
|
|
60 * @noextend This class is not intended to be subclassed by clients.
|
|
61 */
|
|
62 public class TextViewerUndoManager : IUndoManager, IUndoManagerExtension {
|
|
63
|
|
64
|
|
65 /**
|
|
66 * Internal listener to mouse and key events.
|
|
67 */
|
|
68 private class KeyAndMouseListener : MouseListener, KeyListener {
|
|
69
|
|
70 /*
|
|
71 * @see MouseListener#mouseDoubleClick
|
|
72 */
|
|
73 public void mouseDoubleClick(MouseEvent e) {
|
|
74 }
|
|
75
|
|
76 /*
|
|
77 * If the right mouse button is pressed, the current editing command is closed
|
|
78 * @see MouseListener#mouseDown
|
|
79 */
|
|
80 public void mouseDown(MouseEvent e) {
|
|
81 if (e.button is 1)
|
|
82 if (isConnected())
|
|
83 fDocumentUndoManager.commit();
|
|
84 }
|
|
85
|
|
86 /*
|
|
87 * @see MouseListener#mouseUp
|
|
88 */
|
|
89 public void mouseUp(MouseEvent e) {
|
|
90 }
|
|
91
|
|
92 /*
|
|
93 * @see KeyListener#keyPressed
|
|
94 */
|
|
95 public void keyReleased(KeyEvent e) {
|
|
96 }
|
|
97
|
|
98 /*
|
|
99 * On cursor keys, the current editing command is closed
|
|
100 * @see KeyListener#keyPressed
|
|
101 */
|
|
102 public void keyPressed(KeyEvent e) {
|
|
103 switch (e.keyCode) {
|
|
104 case DWT.ARROW_UP:
|
|
105 case DWT.ARROW_DOWN:
|
|
106 case DWT.ARROW_LEFT:
|
|
107 case DWT.ARROW_RIGHT:
|
|
108 if (isConnected()) {
|
|
109 fDocumentUndoManager.commit();
|
|
110 }
|
|
111 break;
|
|
112 }
|
|
113 }
|
|
114 }
|
|
115
|
|
116
|
|
117 /**
|
|
118 * Internal text input listener.
|
|
119 */
|
|
120 private class TextInputListener : ITextInputListener {
|
|
121
|
|
122 /*
|
|
123 * @see dwtx.jface.text.ITextInputListener#inputDocumentAboutToBeChanged(dwtx.jface.text.IDocument, dwtx.jface.text.IDocument)
|
|
124 */
|
|
125 public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) {
|
|
126 disconnectDocumentUndoManager();
|
|
127 }
|
|
128
|
|
129 /*
|
|
130 * @see dwtx.jface.text.ITextInputListener#inputDocumentChanged(dwtx.jface.text.IDocument, dwtx.jface.text.IDocument)
|
|
131 */
|
|
132 public void inputDocumentChanged(IDocument oldInput, IDocument newInput) {
|
|
133 connectDocumentUndoManager(newInput);
|
|
134 }
|
|
135 }
|
|
136
|
|
137
|
|
138 /**
|
|
139 * Internal document undo listener.
|
|
140 */
|
|
141 private class DocumentUndoListener : IDocumentUndoListener {
|
|
142
|
|
143 /*
|
|
144 * @see dwtx.jface.text.IDocumentUndoListener#documentUndoNotification(DocumentUndoEvent)
|
|
145 */
|
|
146 public void documentUndoNotification(DocumentUndoEvent event ){
|
|
147 if (!isConnected()) return;
|
|
148
|
|
149 int eventType= event.getEventType();
|
|
150 if (((eventType & DocumentUndoEvent.ABOUT_TO_UNDO) !is 0) || ((eventType & DocumentUndoEvent.ABOUT_TO_REDO) !is 0)) {
|
|
151 if (event.isCompound()) {
|
|
152 ITextViewerExtension extension= null;
|
|
153 if (fTextViewer instanceof ITextViewerExtension)
|
|
154 extension= (ITextViewerExtension) fTextViewer;
|
|
155
|
|
156 if (extension !is null)
|
|
157 extension.setRedraw(false);
|
|
158 }
|
|
159 fTextViewer.getTextWidget().getDisplay().syncExec(new Runnable() {
|
|
160 public void run() {
|
|
161 if (fTextViewer instanceof TextViewer)
|
|
162 ((TextViewer)fTextViewer).ignoreAutoEditStrategies(true);
|
|
163 }
|
|
164 });
|
|
165
|
|
166 } else if (((eventType & DocumentUndoEvent.UNDONE) !is 0) || ((eventType & DocumentUndoEvent.REDONE) !is 0)) {
|
|
167 fTextViewer.getTextWidget().getDisplay().syncExec(new Runnable() {
|
|
168 public void run() {
|
|
169 if (fTextViewer instanceof TextViewer)
|
|
170 ((TextViewer)fTextViewer).ignoreAutoEditStrategies(false);
|
|
171 }
|
|
172 });
|
|
173 if (event.isCompound()) {
|
|
174 ITextViewerExtension extension= null;
|
|
175 if (fTextViewer instanceof ITextViewerExtension)
|
|
176 extension= (ITextViewerExtension) fTextViewer;
|
|
177
|
|
178 if (extension !is null)
|
|
179 extension.setRedraw(true);
|
|
180 }
|
|
181
|
|
182 // Reveal the change if this manager's viewer has the focus.
|
|
183 if (fTextViewer !is null) {
|
|
184 StyledText widget= fTextViewer.getTextWidget();
|
|
185 if (widget !is null && !widget.isDisposed() && (widget.isFocusControl()))// || fTextViewer.getTextWidget() is control))
|
|
186 selectAndReveal(event.getOffset(), event.getText() is null ? 0 : event.getText().length());
|
|
187 }
|
|
188 }
|
|
189 }
|
|
190
|
|
191 }
|
|
192
|
|
193 /** The internal key and mouse event listener */
|
|
194 private KeyAndMouseListener fKeyAndMouseListener;
|
|
195 /** The internal text input listener */
|
|
196 private TextInputListener fTextInputListener;
|
|
197
|
|
198
|
|
199 /** The text viewer the undo manager is connected to */
|
|
200 private ITextViewer fTextViewer;
|
|
201
|
|
202 /** The undo level */
|
|
203 private int fUndoLevel;
|
|
204
|
|
205 /** The document undo manager that is active. */
|
|
206 private IDocumentUndoManager fDocumentUndoManager;
|
|
207
|
|
208 /** The document that is active. */
|
|
209 private IDocument fDocument;
|
|
210
|
|
211 /** The document undo listener */
|
|
212 private IDocumentUndoListener fDocumentUndoListener;
|
|
213
|
|
214 /**
|
|
215 * Creates a new undo manager who remembers the specified number of edit commands.
|
|
216 *
|
|
217 * @param undoLevel the length of this manager's history
|
|
218 */
|
|
219 public TextViewerUndoManager(int undoLevel) {
|
|
220 fUndoLevel= undoLevel;
|
|
221 }
|
|
222
|
|
223 /**
|
|
224 * Returns whether this undo manager is connected to a text viewer.
|
|
225 *
|
|
226 * @return <code>true</code> if connected, <code>false</code> otherwise
|
|
227 */
|
|
228 private bool isConnected() {
|
|
229 return fTextViewer !is null && fDocumentUndoManager !is null;
|
|
230 }
|
|
231
|
|
232 /*
|
|
233 * @see IUndoManager#beginCompoundChange
|
|
234 */
|
|
235 public void beginCompoundChange() {
|
|
236 if (isConnected()) {
|
|
237 fDocumentUndoManager.beginCompoundChange();
|
|
238 }
|
|
239 }
|
|
240
|
|
241
|
|
242 /*
|
|
243 * @see IUndoManager#endCompoundChange
|
|
244 */
|
|
245 public void endCompoundChange() {
|
|
246 if (isConnected()) {
|
|
247 fDocumentUndoManager.endCompoundChange();
|
|
248 }
|
|
249 }
|
|
250
|
|
251 /**
|
|
252 * Registers all necessary listeners with the text viewer.
|
|
253 */
|
|
254 private void addListeners() {
|
|
255 StyledText text= fTextViewer.getTextWidget();
|
|
256 if (text !is null) {
|
|
257 fKeyAndMouseListener= new KeyAndMouseListener();
|
|
258 text.addMouseListener(fKeyAndMouseListener);
|
|
259 text.addKeyListener(fKeyAndMouseListener);
|
|
260 fTextInputListener= new TextInputListener();
|
|
261 fTextViewer.addTextInputListener(fTextInputListener);
|
|
262 }
|
|
263 }
|
|
264
|
|
265 /**
|
|
266 * Unregister all previously installed listeners from the text viewer.
|
|
267 */
|
|
268 private void removeListeners() {
|
|
269 StyledText text= fTextViewer.getTextWidget();
|
|
270 if (text !is null) {
|
|
271 if (fKeyAndMouseListener !is null) {
|
|
272 text.removeMouseListener(fKeyAndMouseListener);
|
|
273 text.removeKeyListener(fKeyAndMouseListener);
|
|
274 fKeyAndMouseListener= null;
|
|
275 }
|
|
276 if (fTextInputListener !is null) {
|
|
277 fTextViewer.removeTextInputListener(fTextInputListener);
|
|
278 fTextInputListener= null;
|
|
279 }
|
|
280 }
|
|
281 }
|
|
282
|
|
283 /**
|
|
284 * Shows the given exception in an error dialog.
|
|
285 *
|
|
286 * @param title the dialog title
|
|
287 * @param ex the exception
|
|
288 */
|
|
289 private void openErrorDialog(final String title, final Exception ex) {
|
|
290 Shell shell= null;
|
|
291 if (isConnected()) {
|
|
292 StyledText st= fTextViewer.getTextWidget();
|
|
293 if (st !is null && !st.isDisposed())
|
|
294 shell= st.getShell();
|
|
295 }
|
|
296 if (Display.getCurrent() !is null)
|
|
297 MessageDialog.openError(shell, title, ex.getLocalizedMessage());
|
|
298 else {
|
|
299 Display display;
|
|
300 final Shell finalShell= shell;
|
|
301 if (finalShell !is null)
|
|
302 display= finalShell.getDisplay();
|
|
303 else
|
|
304 display= Display.getDefault();
|
|
305 display.syncExec(new Runnable() {
|
|
306 public void run() {
|
|
307 MessageDialog.openError(finalShell, title, ex.getLocalizedMessage());
|
|
308 }
|
|
309 });
|
|
310 }
|
|
311 }
|
|
312
|
|
313 /*
|
|
314 * @see dwtx.jface.text.IUndoManager#setMaximalUndoLevel(int)
|
|
315 */
|
|
316 public void setMaximalUndoLevel(int undoLevel) {
|
|
317 fUndoLevel= Math.max(0, undoLevel);
|
|
318 if (isConnected()) {
|
|
319 fDocumentUndoManager.setMaximalUndoLevel(fUndoLevel);
|
|
320 }
|
|
321 }
|
|
322
|
|
323 /*
|
|
324 * @see dwtx.jface.text.IUndoManager#connect(dwtx.jface.text.ITextViewer)
|
|
325 */
|
|
326 public void connect(ITextViewer textViewer) {
|
|
327 if (fTextViewer is null && textViewer !is null) {
|
|
328 fTextViewer= textViewer;
|
|
329 addListeners();
|
|
330 }
|
|
331 IDocument doc= fTextViewer.getDocument();
|
|
332 connectDocumentUndoManager(doc);
|
|
333 }
|
|
334
|
|
335 /*
|
|
336 * @see dwtx.jface.text.IUndoManager#disconnect()
|
|
337 */
|
|
338 public void disconnect() {
|
|
339 if (fTextViewer !is null) {
|
|
340 removeListeners();
|
|
341 fTextViewer= null;
|
|
342 }
|
|
343 disconnectDocumentUndoManager();
|
|
344 }
|
|
345
|
|
346 /*
|
|
347 * @see dwtx.jface.text.IUndoManager#reset()
|
|
348 */
|
|
349 public void reset() {
|
|
350 if (isConnected())
|
|
351 fDocumentUndoManager.reset();
|
|
352
|
|
353 }
|
|
354
|
|
355 /*
|
|
356 * @see dwtx.jface.text.IUndoManager#redoable()
|
|
357 */
|
|
358 public bool redoable() {
|
|
359 if (isConnected())
|
|
360 return fDocumentUndoManager.redoable();
|
|
361 return false;
|
|
362 }
|
|
363
|
|
364 /*
|
|
365 * @see dwtx.jface.text.IUndoManager#undoable()
|
|
366 */
|
|
367 public bool undoable() {
|
|
368 if (isConnected())
|
|
369 return fDocumentUndoManager.undoable();
|
|
370 return false;
|
|
371 }
|
|
372
|
|
373 /*
|
|
374 * @see dwtx.jface.text.IUndoManager#redo()
|
|
375 */
|
|
376 public void redo() {
|
|
377 if (isConnected()) {
|
|
378 try {
|
|
379 fDocumentUndoManager.redo();
|
|
380 } catch (ExecutionException ex) {
|
|
381 openErrorDialog(JFaceTextMessages.getString("DefaultUndoManager.error.redoFailed.title"), ex); //$NON-NLS-1$
|
|
382 }
|
|
383 }
|
|
384 }
|
|
385
|
|
386 /*
|
|
387 * @see dwtx.jface.text.IUndoManager#undo()
|
|
388 */
|
|
389 public void undo() {
|
|
390 if (isConnected()) {
|
|
391 try {
|
|
392 fDocumentUndoManager.undo();
|
|
393 } catch (ExecutionException ex) {
|
|
394 openErrorDialog(JFaceTextMessages.getString("DefaultUndoManager.error.undoFailed.title"), ex); //$NON-NLS-1$
|
|
395 }
|
|
396 }
|
|
397 }
|
|
398
|
|
399 /**
|
|
400 * Selects and reveals the specified range.
|
|
401 *
|
|
402 * @param offset the offset of the range
|
|
403 * @param length the length of the range
|
|
404 */
|
|
405 private void selectAndReveal(int offset, int length) {
|
|
406 if (fTextViewer instanceof ITextViewerExtension5) {
|
|
407 ITextViewerExtension5 extension= (ITextViewerExtension5) fTextViewer;
|
|
408 extension.exposeModelRange(new Region(offset, length));
|
|
409 } else if (!fTextViewer.overlapsWithVisibleRegion(offset, length))
|
|
410 fTextViewer.resetVisibleRegion();
|
|
411
|
|
412 fTextViewer.setSelectedRange(offset, length);
|
|
413 fTextViewer.revealRange(offset, length);
|
|
414 }
|
|
415
|
|
416 /*
|
|
417 * @see dwtx.jface.text.IUndoManagerExtension#getUndoContext()
|
|
418 */
|
|
419 public IUndoContext getUndoContext() {
|
|
420 if (isConnected()) {
|
|
421 return fDocumentUndoManager.getUndoContext();
|
|
422 }
|
|
423 return null;
|
|
424 }
|
|
425
|
|
426 private void connectDocumentUndoManager(IDocument document) {
|
|
427 disconnectDocumentUndoManager();
|
|
428 if (document !is null) {
|
|
429 fDocument= document;
|
|
430 DocumentUndoManagerRegistry.connect(fDocument);
|
|
431 fDocumentUndoManager= DocumentUndoManagerRegistry.getDocumentUndoManager(fDocument);
|
|
432 fDocumentUndoManager.connect(this);
|
|
433 setMaximalUndoLevel(fUndoLevel);
|
|
434 fDocumentUndoListener= new DocumentUndoListener();
|
|
435 fDocumentUndoManager.addDocumentUndoListener(fDocumentUndoListener);
|
|
436 }
|
|
437 }
|
|
438
|
|
439 private void disconnectDocumentUndoManager() {
|
|
440 if (fDocumentUndoManager !is null) {
|
|
441 fDocumentUndoManager.disconnect(this);
|
|
442 DocumentUndoManagerRegistry.disconnect(fDocument);
|
|
443 fDocumentUndoManager.removeDocumentUndoListener(fDocumentUndoListener);
|
|
444 fDocumentUndoListener= null;
|
|
445 fDocumentUndoManager= null;
|
|
446 }
|
|
447 }
|
|
448 }
|