diff dwtx/jface/text/TextViewerUndoManager.d @ 129:eb30df5ca28b

Added JFace Text sources
author Frank Benoit <benoit@tionex.de>
date Sat, 23 Aug 2008 19:10:48 +0200
parents
children c4fb132a086c
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/text/TextViewerUndoManager.d	Sat Aug 23 19:10:48 2008 +0200
@@ -0,0 +1,448 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2008 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module dwtx.jface.text.TextViewerUndoManager;
+
+import dwt.dwthelper.utils;
+
+
+
+
+
+import dwt.DWT;
+import dwt.custom.StyledText;
+import dwt.events.KeyEvent;
+import dwt.events.KeyListener;
+import dwt.events.MouseEvent;
+import dwt.events.MouseListener;
+import dwt.widgets.Display;
+import dwt.widgets.Shell;
+import dwtx.core.commands.ExecutionException;
+import dwtx.core.commands.operations.IUndoContext;
+import dwtx.jface.dialogs.MessageDialog;
+import dwtx.text.undo.DocumentUndoEvent;
+import dwtx.text.undo.DocumentUndoManager;
+import dwtx.text.undo.DocumentUndoManagerRegistry;
+import dwtx.text.undo.IDocumentUndoListener;
+import dwtx.text.undo.IDocumentUndoManager;
+
+
+/**
+ * Implementation of {@link dwtx.jface.text.IUndoManager} using the shared
+ * document undo manager.
+ * <p>
+ * It registers with the connected text viewer as text input listener, and obtains
+ * its undo manager from the current document.  It also monitors mouse and keyboard
+ * activities in order to partition the stream of text changes into undo-able
+ * edit commands.
+ * <p>
+ * This class is not intended to be subclassed.
+ * </p>
+ * 
+ * @see ITextViewer
+ * @see ITextInputListener
+ * @see IDocumentUndoManager
+ * @see MouseListener
+ * @see KeyListener
+ * @see DocumentUndoManager
+ * 
+ * @since 3.2
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class TextViewerUndoManager : IUndoManager, IUndoManagerExtension {
+
+    
+    /**
+     * Internal listener to mouse and key events.
+     */
+    private class KeyAndMouseListener : MouseListener, KeyListener {
+
+        /*
+         * @see MouseListener#mouseDoubleClick
+         */
+        public void mouseDoubleClick(MouseEvent e) {
+        }
+
+        /*
+         * If the right mouse button is pressed, the current editing command is closed
+         * @see MouseListener#mouseDown
+         */
+        public void mouseDown(MouseEvent e) {
+            if (e.button is 1)
+                if (isConnected())
+                    fDocumentUndoManager.commit();
+        }
+
+        /*
+         * @see MouseListener#mouseUp
+         */
+        public void mouseUp(MouseEvent e) {
+        }
+
+        /*
+         * @see KeyListener#keyPressed
+         */
+        public void keyReleased(KeyEvent e) {
+        }
+
+        /*
+         * On cursor keys, the current editing command is closed
+         * @see KeyListener#keyPressed
+         */
+        public void keyPressed(KeyEvent e) {
+            switch (e.keyCode) {
+                case DWT.ARROW_UP:
+                case DWT.ARROW_DOWN:
+                case DWT.ARROW_LEFT:
+                case DWT.ARROW_RIGHT:
+                    if (isConnected()) {
+                        fDocumentUndoManager.commit();
+                    }
+                    break;
+            }
+        }
+    }
+
+
+    /**
+     * Internal text input listener.
+     */
+    private class TextInputListener : ITextInputListener {
+
+        /*
+         * @see dwtx.jface.text.ITextInputListener#inputDocumentAboutToBeChanged(dwtx.jface.text.IDocument, dwtx.jface.text.IDocument)
+         */
+        public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) {
+            disconnectDocumentUndoManager();
+        }
+
+        /*
+         * @see dwtx.jface.text.ITextInputListener#inputDocumentChanged(dwtx.jface.text.IDocument, dwtx.jface.text.IDocument)
+         */
+        public void inputDocumentChanged(IDocument oldInput, IDocument newInput) {
+            connectDocumentUndoManager(newInput);
+        }
+    }
+
+
+    /**
+     * Internal document undo listener.
+     */
+    private class DocumentUndoListener : IDocumentUndoListener {
+
+        /*
+         * @see dwtx.jface.text.IDocumentUndoListener#documentUndoNotification(DocumentUndoEvent)
+         */
+        public void documentUndoNotification(DocumentUndoEvent event ){
+            if (!isConnected()) return;
+            
+            int eventType= event.getEventType();
+            if (((eventType & DocumentUndoEvent.ABOUT_TO_UNDO) !is 0) || ((eventType & DocumentUndoEvent.ABOUT_TO_REDO) !is 0))  {
+                if (event.isCompound()) {
+                    ITextViewerExtension extension= null;
+                    if (fTextViewer instanceof ITextViewerExtension)
+                        extension= (ITextViewerExtension) fTextViewer;
+
+                    if (extension !is null)
+                        extension.setRedraw(false);
+                }
+                fTextViewer.getTextWidget().getDisplay().syncExec(new Runnable() {
+                    public void run() {
+                        if (fTextViewer instanceof TextViewer)
+                            ((TextViewer)fTextViewer).ignoreAutoEditStrategies(true);
+                    }
+                });
+                
+            } else if (((eventType & DocumentUndoEvent.UNDONE) !is 0) || ((eventType & DocumentUndoEvent.REDONE) !is 0))  {
+                fTextViewer.getTextWidget().getDisplay().syncExec(new Runnable() {
+                    public void run() {
+                        if (fTextViewer instanceof TextViewer)
+                            ((TextViewer)fTextViewer).ignoreAutoEditStrategies(false);
+                    }
+                });
+                if (event.isCompound()) {
+                    ITextViewerExtension extension= null;
+                    if (fTextViewer instanceof ITextViewerExtension)
+                        extension= (ITextViewerExtension) fTextViewer;
+
+                    if (extension !is null)
+                        extension.setRedraw(true);
+                }
+                
+                // Reveal the change if this manager's viewer has the focus.
+                if (fTextViewer !is null) {
+                    StyledText widget= fTextViewer.getTextWidget();
+                    if (widget !is null && !widget.isDisposed() && (widget.isFocusControl()))// || fTextViewer.getTextWidget() is control))
+                        selectAndReveal(event.getOffset(), event.getText() is null ? 0 : event.getText().length());
+                }
+            }
+        }
+
+    }
+
+    /** The internal key and mouse event listener */
+    private KeyAndMouseListener fKeyAndMouseListener;
+    /** The internal text input listener */
+    private TextInputListener fTextInputListener;
+
+
+    /** The text viewer the undo manager is connected to */
+    private ITextViewer fTextViewer;
+    
+    /** The undo level */
+    private int fUndoLevel;
+    
+    /** The document undo manager that is active. */
+    private IDocumentUndoManager fDocumentUndoManager;
+    
+    /** The document that is active. */
+    private IDocument fDocument;
+    
+    /** The document undo listener */
+    private IDocumentUndoListener fDocumentUndoListener;
+
+    /**
+     * Creates a new undo manager who remembers the specified number of edit commands.
+     *
+     * @param undoLevel the length of this manager's history
+     */
+    public TextViewerUndoManager(int undoLevel) {
+        fUndoLevel= undoLevel;
+    }
+
+    /**
+     * Returns whether this undo manager is connected to a text viewer.
+     *
+     * @return <code>true</code> if connected, <code>false</code> otherwise
+     */
+    private bool isConnected() {
+        return fTextViewer !is null && fDocumentUndoManager !is null;
+    }
+
+    /*
+     * @see IUndoManager#beginCompoundChange
+     */
+    public void beginCompoundChange() {
+        if (isConnected()) {
+            fDocumentUndoManager.beginCompoundChange();
+        }
+    }
+
+
+    /*
+     * @see IUndoManager#endCompoundChange
+     */
+    public void endCompoundChange() {
+        if (isConnected()) {
+            fDocumentUndoManager.endCompoundChange();
+        }
+    }
+
+    /**
+     * Registers all necessary listeners with the text viewer.
+     */
+    private void addListeners() {
+        StyledText text= fTextViewer.getTextWidget();
+        if (text !is null) {
+            fKeyAndMouseListener= new KeyAndMouseListener();
+            text.addMouseListener(fKeyAndMouseListener);
+            text.addKeyListener(fKeyAndMouseListener);
+            fTextInputListener= new TextInputListener();
+            fTextViewer.addTextInputListener(fTextInputListener);
+        }
+    }
+
+    /**
+     * Unregister all previously installed listeners from the text viewer.
+     */
+    private void removeListeners() {
+        StyledText text= fTextViewer.getTextWidget();
+        if (text !is null) {
+            if (fKeyAndMouseListener !is null) {
+                text.removeMouseListener(fKeyAndMouseListener);
+                text.removeKeyListener(fKeyAndMouseListener);
+                fKeyAndMouseListener= null;
+            }
+            if (fTextInputListener !is null) {
+                fTextViewer.removeTextInputListener(fTextInputListener);
+                fTextInputListener= null;
+            }
+        }
+    }
+
+    /**
+     * Shows the given exception in an error dialog.
+     *
+     * @param title the dialog title
+     * @param ex the exception
+     */
+    private void openErrorDialog(final String title, final Exception ex) {
+        Shell shell= null;
+        if (isConnected()) {
+            StyledText st= fTextViewer.getTextWidget();
+            if (st !is null && !st.isDisposed())
+                shell= st.getShell();
+        }
+        if (Display.getCurrent() !is null)
+            MessageDialog.openError(shell, title, ex.getLocalizedMessage());
+        else {
+            Display display;
+            final Shell finalShell= shell;
+            if (finalShell !is null)
+                display= finalShell.getDisplay();
+            else
+                display= Display.getDefault();
+            display.syncExec(new Runnable() {
+                public void run() {
+                    MessageDialog.openError(finalShell, title, ex.getLocalizedMessage());
+                }
+            });
+        }
+    }
+
+    /*
+     * @see dwtx.jface.text.IUndoManager#setMaximalUndoLevel(int)
+     */
+    public void setMaximalUndoLevel(int undoLevel) {
+        fUndoLevel= Math.max(0, undoLevel);
+        if (isConnected()) {
+            fDocumentUndoManager.setMaximalUndoLevel(fUndoLevel);
+        }
+    }
+
+    /*
+     * @see dwtx.jface.text.IUndoManager#connect(dwtx.jface.text.ITextViewer)
+     */
+    public void connect(ITextViewer textViewer) {
+        if (fTextViewer is null && textViewer !is null) {
+            fTextViewer= textViewer;
+            addListeners();
+        }
+        IDocument doc= fTextViewer.getDocument();
+        connectDocumentUndoManager(doc);
+    }
+
+    /*
+     * @see dwtx.jface.text.IUndoManager#disconnect()
+     */
+    public void disconnect() {
+        if (fTextViewer !is null) {
+            removeListeners();
+            fTextViewer= null;
+        }
+        disconnectDocumentUndoManager();
+    }
+
+    /*
+     * @see dwtx.jface.text.IUndoManager#reset()
+     */
+    public void reset() {
+        if (isConnected())
+            fDocumentUndoManager.reset();
+        
+    }
+
+    /*
+     * @see dwtx.jface.text.IUndoManager#redoable()
+     */
+    public bool redoable() {
+        if (isConnected())
+            return fDocumentUndoManager.redoable();
+        return false;
+    }
+
+    /*
+     * @see dwtx.jface.text.IUndoManager#undoable()
+     */
+    public bool undoable() {
+        if (isConnected())
+            return fDocumentUndoManager.undoable();
+        return false;
+    }
+
+    /*
+     * @see dwtx.jface.text.IUndoManager#redo()
+     */
+    public void redo() {
+        if (isConnected()) {
+            try {
+                fDocumentUndoManager.redo();
+            } catch (ExecutionException ex) {
+                openErrorDialog(JFaceTextMessages.getString("DefaultUndoManager.error.redoFailed.title"), ex); //$NON-NLS-1$
+            }
+        }
+    }
+
+    /*
+     * @see dwtx.jface.text.IUndoManager#undo()
+     */
+    public void undo() {
+        if (isConnected()) {
+            try {
+                fDocumentUndoManager.undo();
+            } catch (ExecutionException ex) {
+                openErrorDialog(JFaceTextMessages.getString("DefaultUndoManager.error.undoFailed.title"), ex); //$NON-NLS-1$
+            }
+        }
+    }
+
+    /**
+     * Selects and reveals the specified range.
+     *
+     * @param offset the offset of the range
+     * @param length the length of the range
+     */
+    private void selectAndReveal(int offset, int length) {
+        if (fTextViewer instanceof ITextViewerExtension5) {
+            ITextViewerExtension5 extension= (ITextViewerExtension5) fTextViewer;
+            extension.exposeModelRange(new Region(offset, length));
+        } else if (!fTextViewer.overlapsWithVisibleRegion(offset, length))
+            fTextViewer.resetVisibleRegion();
+
+        fTextViewer.setSelectedRange(offset, length);
+        fTextViewer.revealRange(offset, length);
+    }
+
+    /*
+     * @see dwtx.jface.text.IUndoManagerExtension#getUndoContext()
+     */
+    public IUndoContext getUndoContext() {
+        if (isConnected()) {
+            return fDocumentUndoManager.getUndoContext();
+        }
+        return null;
+    }
+    
+    private void connectDocumentUndoManager(IDocument document) {
+        disconnectDocumentUndoManager();
+        if (document !is null) {
+            fDocument= document;
+            DocumentUndoManagerRegistry.connect(fDocument);
+            fDocumentUndoManager= DocumentUndoManagerRegistry.getDocumentUndoManager(fDocument);
+            fDocumentUndoManager.connect(this);
+            setMaximalUndoLevel(fUndoLevel);
+            fDocumentUndoListener= new DocumentUndoListener();
+            fDocumentUndoManager.addDocumentUndoListener(fDocumentUndoListener);
+        }
+    }
+
+    private void disconnectDocumentUndoManager() {
+        if (fDocumentUndoManager !is null) {
+            fDocumentUndoManager.disconnect(this);
+            DocumentUndoManagerRegistry.disconnect(fDocument);
+            fDocumentUndoManager.removeDocumentUndoListener(fDocumentUndoListener);
+            fDocumentUndoListener= null;
+            fDocumentUndoManager= null;
+        }
+    }
+}