Mercurial > projects > dwt-addons
view dwtx/jface/text/DefaultUndoManager.d @ 192:c3583c6ec027
Added missing default cases for switch statements
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Mon, 03 Nov 2008 22:52:26 +0100 |
parents | 1a5b8f8129df |
children |
line wrap: on
line source
/******************************************************************************* * Copyright (c) 2000, 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.DefaultUndoManager; import dwtx.jface.text.IDocumentPartitioningListener; // packageimport import dwtx.jface.text.DefaultTextHover; // packageimport import dwtx.jface.text.AbstractInformationControl; // packageimport import dwtx.jface.text.TextUtilities; // packageimport import dwtx.jface.text.IInformationControlCreatorExtension; // packageimport import dwtx.jface.text.AbstractInformationControlManager; // packageimport import dwtx.jface.text.ITextViewerExtension2; // packageimport import dwtx.jface.text.IDocumentPartitioner; // packageimport import dwtx.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport import dwtx.jface.text.ITextSelection; // packageimport import dwtx.jface.text.Document; // packageimport import dwtx.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport import dwtx.jface.text.ITextListener; // packageimport import dwtx.jface.text.BadPartitioningException; // packageimport import dwtx.jface.text.ITextViewerExtension5; // packageimport import dwtx.jface.text.IDocumentPartitionerExtension3; // packageimport import dwtx.jface.text.IUndoManager; // packageimport import dwtx.jface.text.ITextHoverExtension2; // packageimport import dwtx.jface.text.IRepairableDocument; // packageimport import dwtx.jface.text.IRewriteTarget; // packageimport import dwtx.jface.text.DefaultPositionUpdater; // packageimport import dwtx.jface.text.RewriteSessionEditProcessor; // packageimport import dwtx.jface.text.TextViewerHoverManager; // packageimport import dwtx.jface.text.DocumentRewriteSession; // packageimport import dwtx.jface.text.TextViewer; // packageimport import dwtx.jface.text.ITextViewerExtension8; // packageimport import dwtx.jface.text.RegExMessages; // packageimport import dwtx.jface.text.IDelayedInputChangeProvider; // packageimport import dwtx.jface.text.ITextOperationTargetExtension; // packageimport import dwtx.jface.text.IWidgetTokenOwner; // packageimport import dwtx.jface.text.IViewportListener; // packageimport import dwtx.jface.text.GapTextStore; // packageimport import dwtx.jface.text.MarkSelection; // packageimport import dwtx.jface.text.IDocumentPartitioningListenerExtension; // packageimport import dwtx.jface.text.IDocumentAdapterExtension; // packageimport import dwtx.jface.text.IInformationControlExtension; // packageimport import dwtx.jface.text.IDocumentPartitioningListenerExtension2; // packageimport import dwtx.jface.text.DefaultDocumentAdapter; // packageimport import dwtx.jface.text.ITextViewerExtension3; // packageimport import dwtx.jface.text.IInformationControlCreator; // packageimport import dwtx.jface.text.TypedRegion; // packageimport import dwtx.jface.text.ISynchronizable; // packageimport import dwtx.jface.text.IMarkRegionTarget; // packageimport import dwtx.jface.text.TextViewerUndoManager; // packageimport import dwtx.jface.text.IRegion; // packageimport import dwtx.jface.text.IInformationControlExtension2; // packageimport import dwtx.jface.text.IDocumentExtension4; // packageimport import dwtx.jface.text.IDocumentExtension2; // packageimport import dwtx.jface.text.IDocumentPartitionerExtension2; // packageimport import dwtx.jface.text.Assert; // packageimport import dwtx.jface.text.DefaultInformationControl; // packageimport import dwtx.jface.text.IWidgetTokenOwnerExtension; // packageimport import dwtx.jface.text.DocumentClone; // packageimport import dwtx.jface.text.IFindReplaceTarget; // packageimport import dwtx.jface.text.IAutoEditStrategy; // packageimport import dwtx.jface.text.ILineTrackerExtension; // packageimport import dwtx.jface.text.IUndoManagerExtension; // packageimport import dwtx.jface.text.TextSelection; // packageimport import dwtx.jface.text.DefaultAutoIndentStrategy; // packageimport import dwtx.jface.text.IAutoIndentStrategy; // packageimport import dwtx.jface.text.IPainter; // packageimport import dwtx.jface.text.IInformationControl; // packageimport import dwtx.jface.text.IInformationControlExtension3; // packageimport import dwtx.jface.text.ITextViewerExtension6; // packageimport import dwtx.jface.text.IInformationControlExtension4; // packageimport import dwtx.jface.text.DefaultLineTracker; // packageimport import dwtx.jface.text.IDocumentInformationMappingExtension; // packageimport import dwtx.jface.text.IRepairableDocumentExtension; // packageimport import dwtx.jface.text.ITextHover; // packageimport import dwtx.jface.text.FindReplaceDocumentAdapter; // packageimport import dwtx.jface.text.ILineTracker; // packageimport import dwtx.jface.text.Line; // packageimport import dwtx.jface.text.ITextViewerExtension; // packageimport import dwtx.jface.text.IDocumentAdapter; // packageimport import dwtx.jface.text.TextEvent; // packageimport import dwtx.jface.text.BadLocationException; // packageimport import dwtx.jface.text.AbstractDocument; // packageimport import dwtx.jface.text.AbstractLineTracker; // packageimport import dwtx.jface.text.TreeLineTracker; // packageimport import dwtx.jface.text.ITextPresentationListener; // packageimport import dwtx.jface.text.Region; // packageimport import dwtx.jface.text.ITextViewer; // packageimport import dwtx.jface.text.IDocumentInformationMapping; // packageimport import dwtx.jface.text.MarginPainter; // packageimport import dwtx.jface.text.IPaintPositionManager; // packageimport import dwtx.jface.text.TextPresentation; // packageimport import dwtx.jface.text.IFindReplaceTargetExtension; // packageimport import dwtx.jface.text.ISlaveDocumentManagerExtension; // packageimport import dwtx.jface.text.ISelectionValidator; // packageimport import dwtx.jface.text.IDocumentExtension; // packageimport import dwtx.jface.text.PropagatingFontFieldEditor; // packageimport import dwtx.jface.text.ConfigurableLineTracker; // packageimport import dwtx.jface.text.SlaveDocumentEvent; // packageimport import dwtx.jface.text.IDocumentListener; // packageimport import dwtx.jface.text.PaintManager; // packageimport import dwtx.jface.text.IFindReplaceTargetExtension3; // packageimport import dwtx.jface.text.ITextDoubleClickStrategy; // packageimport import dwtx.jface.text.IDocumentExtension3; // packageimport import dwtx.jface.text.Position; // packageimport import dwtx.jface.text.TextMessages; // packageimport import dwtx.jface.text.CopyOnWriteTextStore; // packageimport import dwtx.jface.text.WhitespaceCharacterPainter; // packageimport import dwtx.jface.text.IPositionUpdater; // packageimport import dwtx.jface.text.DefaultTextDoubleClickStrategy; // packageimport import dwtx.jface.text.ListLineTracker; // packageimport import dwtx.jface.text.ITextInputListener; // packageimport import dwtx.jface.text.BadPositionCategoryException; // packageimport import dwtx.jface.text.IWidgetTokenKeeperExtension; // packageimport import dwtx.jface.text.IInputChangedListener; // packageimport import dwtx.jface.text.ITextOperationTarget; // packageimport import dwtx.jface.text.IDocumentInformationMappingExtension2; // packageimport import dwtx.jface.text.ITextViewerExtension7; // packageimport import dwtx.jface.text.IInformationControlExtension5; // packageimport import dwtx.jface.text.IDocumentRewriteSessionListener; // packageimport import dwtx.jface.text.JFaceTextUtil; // packageimport import dwtx.jface.text.AbstractReusableInformationControlCreator; // packageimport import dwtx.jface.text.TabsToSpacesConverter; // packageimport import dwtx.jface.text.CursorLinePainter; // packageimport import dwtx.jface.text.ITextHoverExtension; // packageimport import dwtx.jface.text.IEventConsumer; // packageimport import dwtx.jface.text.IDocument; // packageimport import dwtx.jface.text.IWidgetTokenKeeper; // packageimport import dwtx.jface.text.DocumentCommand; // packageimport import dwtx.jface.text.TypedPosition; // packageimport import dwtx.jface.text.IEditingSupportRegistry; // packageimport import dwtx.jface.text.IDocumentPartitionerExtension; // packageimport import dwtx.jface.text.AbstractHoverInformationControlManager; // packageimport import dwtx.jface.text.IEditingSupport; // packageimport import dwtx.jface.text.IMarkSelection; // packageimport import dwtx.jface.text.ISlaveDocumentManager; // packageimport import dwtx.jface.text.DocumentEvent; // packageimport import dwtx.jface.text.DocumentPartitioningChangedEvent; // packageimport import dwtx.jface.text.ITextStore; // packageimport import dwtx.jface.text.JFaceTextMessages; // packageimport import dwtx.jface.text.DocumentRewriteSessionEvent; // packageimport import dwtx.jface.text.SequentialRewriteTextStore; // packageimport import dwtx.jface.text.DocumentRewriteSessionType; // packageimport import dwtx.jface.text.TextAttribute; // packageimport import dwtx.jface.text.ITextViewerExtension4; // packageimport import dwtx.jface.text.ITypedRegion; // packageimport import dwt.dwthelper.utils; import dwtx.dwtxhelper.Collection; 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.Event; import dwt.widgets.Display; import dwt.widgets.Shell; import dwtx.core.commands.ExecutionException; import dwtx.core.commands.operations.AbstractOperation; import dwtx.core.commands.operations.IOperationHistory; import dwtx.core.commands.operations.IOperationHistoryListener; import dwtx.core.commands.operations.IUndoContext; import dwtx.core.commands.operations.IUndoableOperation; import dwtx.core.commands.operations.ObjectUndoContext; import dwtx.core.commands.operations.OperationHistoryEvent; import dwtx.core.commands.operations.OperationHistoryFactory; import dwtx.core.runtime.IAdaptable; import dwtx.core.runtime.IProgressMonitor; import dwtx.core.runtime.IStatus; import dwtx.core.runtime.Status; import dwtx.jface.dialogs.MessageDialog; /** * Standard implementation of {@link dwtx.jface.text.IUndoManager}. * <p> * It registers with the connected text viewer as text input listener and * document listener and logs all changes. It also monitors mouse and keyboard * activities in order to partition the stream of text changes into undo-able * edit commands. * </p> * <p> * Since 3.1 this undo manager is a facade to the global operation history. * </p> * <p> * The usage of {@link dwtx.core.runtime.IAdaptable} in the JFace * layer has been approved by Platform UI, see: https://bugs.eclipse.org/bugs/show_bug.cgi?id=87669#c9 * </p> * <p> * This class is not intended to be subclassed. * </p> * * @see dwtx.jface.text.ITextViewer * @see dwtx.jface.text.ITextInputListener * @see dwtx.jface.text.IDocumentListener * @see dwtx.core.commands.operations.IUndoableOperation * @see dwtx.core.commands.operations.IOperationHistory * @see MouseListener * @see KeyListener * @deprecated As of 3.2, replaced by {@link TextViewerUndoManager} * @noextend This class is not intended to be subclassed by clients. */ public class DefaultUndoManager : IUndoManager, IUndoManagerExtension { /** * Represents an undo-able edit command. * <p> * Since 3.1 this implements the interface for IUndoableOperation. * </p> */ class TextCommand : AbstractOperation { /** The start index of the replaced text. */ protected int fStart= -1; /** The end index of the replaced text. */ protected int fEnd= -1; /** The newly inserted text. */ protected String fText; /** The replaced text. */ protected String fPreservedText; /** The undo modification stamp. */ protected long fUndoModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP; /** The redo modification stamp. */ protected long fRedoModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP; /** * Creates a new text command. * * @param context the undo context for this command * @since 3.1 */ this(IUndoContext context) { super(JFaceTextMessages.getString("DefaultUndoManager.operationLabel")); //$NON-NLS-1$ addContext(context); } /** * Re-initializes this text command. */ protected void reinitialize() { fStart= fEnd= -1; fText= fPreservedText= null; fUndoModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP; fRedoModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP; } /** * Sets the start and the end index of this command. * * @param start the start index * @param end the end index */ protected void set(int start, int end) { fStart= start; fEnd= end; fText= null; fPreservedText= null; } /* * @see dwtx.core.commands.operations.IUndoableOperation#dispose() * @since 3.1 */ public void dispose() { reinitialize(); } /** * Undo the change described by this command. * * @since 2.0 */ protected void undoTextChange() { try { IDocument document= fTextViewer.getDocument(); if ( cast(IDocumentExtension4)document ) (cast(IDocumentExtension4)document).replace(fStart, fText.length(), fPreservedText, fUndoModificationStamp); else document.replace(fStart, fText.length(), fPreservedText); } catch (BadLocationException x) { } } /* * @see dwtx.core.commands.operations.IUndoableOperation#canUndo() * @since 3.1 */ public bool canUndo() { if (isConnected() && isValid()) { IDocument doc= fTextViewer.getDocument(); if ( cast(IDocumentExtension4)doc ) { long docStamp= (cast(IDocumentExtension4)doc).getModificationStamp(); // Normal case: an undo is valid if its redo will restore document // to its current modification stamp bool canUndo= docStamp is IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP || docStamp is getRedoModificationStamp(); /* Special case to check if the answer is false. * If the last document change was empty, then the document's * modification stamp was incremented but nothing was committed. * The operation being queried has an older stamp. In this case only, * the comparison is different. A sequence of document changes that * include an empty change is handled correctly when a valid commit * follows the empty change, but when #canUndo() is queried just after * an empty change, we must special case the check. The check is very * specific to prevent false positives. * see https://bugs.eclipse.org/bugs/show_bug.cgi?id=98245 */ if (!canUndo && this is fHistory.getUndoOperation(fUndoContext) && // this is the latest operation this !is fCurrent && // there is a more current operation not on the stack !fCurrent.isValid() && // the current operation is not a valid document modification fCurrent.fUndoModificationStamp !is // the invalid current operation has a document stamp IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP) { canUndo= fCurrent.fRedoModificationStamp is docStamp; } /* * When the composite is the current command, it may hold the timestamp * of a no-op change. We check this here rather than in an override of * canUndo() in CompoundTextCommand simply to keep all the special case checks * in one place. */ if (!canUndo && this is fHistory.getUndoOperation(fUndoContext) && // this is the latest operation null !is cast(CompoundTextCommand)this && this is fCurrent && // this is the current operation this.fStart is -1 && // the current operation text is not valid fCurrent.fRedoModificationStamp !is IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP) { // but it has a redo stamp canUndo= fCurrent.fRedoModificationStamp is docStamp; } } // if there is no timestamp to check, simply return true per the 3.0.1 behavior return true; } return false; } /* * @see dwtx.core.commands.operations.IUndoableOperation#canRedo() * @since 3.1 */ public bool canRedo() { if (isConnected() && isValid()) { IDocument doc= fTextViewer.getDocument(); if ( cast(IDocumentExtension4)doc ) { long docStamp= (cast(IDocumentExtension4)doc).getModificationStamp(); return docStamp is IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP || docStamp is getUndoModificationStamp(); } // if there is no timestamp to check, simply return true per the 3.0.1 behavior return true; } return false; } /* * @see dwtx.core.commands.operations.IUndoableOperation#canExecute() * @since 3.1 */ public bool canExecute() { return isConnected(); } /* * @see dwtx.core.commands.operations.IUndoableOperation#execute(dwtx.core.runtime.IProgressMonitor, dwtx.core.runtime.IAdaptable) * @since 3.1 */ public IStatus execute(IProgressMonitor monitor, IAdaptable uiInfo) { // Text commands execute as they are typed, so executing one has no effect. return Status.OK_STATUS; } /* * Undo the change described by this command. Also selects and * reveals the change. */ /** * Undo the change described by this command. Also selects and * reveals the change. * * @param monitor the progress monitor to use if necessary * @param uiInfo an adaptable that can provide UI info if needed * @return the status */ public IStatus undo(IProgressMonitor monitor, IAdaptable uiInfo) { if (isValid()) { undoTextChange(); selectAndReveal(fStart, fPreservedText is null ? 0 : fPreservedText.length()); resetProcessChangeSate(); return Status.OK_STATUS; } return IOperationHistory.OPERATION_INVALID_STATUS; } /** * Re-applies the change described by this command. * * @since 2.0 */ protected void redoTextChange() { try { IDocument document= fTextViewer.getDocument(); if ( cast(IDocumentExtension4)document ) (cast(IDocumentExtension4)document).replace(fStart, fEnd - fStart, fText, fRedoModificationStamp); else fTextViewer.getDocument().replace(fStart, fEnd - fStart, fText); } catch (BadLocationException x) { } } /** * Re-applies the change described by this command that previously been * rolled back. Also selects and reveals the change. * * @param monitor the progress monitor to use if necessary * @param uiInfo an adaptable that can provide UI info if needed * @return the status */ public IStatus redo(IProgressMonitor monitor, IAdaptable uiInfo) { if (isValid()) { redoTextChange(); resetProcessChangeSate(); selectAndReveal(fStart, fText is null ? 0 : fText.length()); return Status.OK_STATUS; } return IOperationHistory.OPERATION_INVALID_STATUS; } /** * Update the command in response to a commit. * * @since 3.1 */ protected void updateCommand() { fText= fTextBuffer.toString(); fTextBuffer.truncate(0); fPreservedText= fPreservedTextBuffer.toString(); fPreservedTextBuffer.truncate(0); } /** * Creates a new uncommitted text command depending on whether * a compound change is currently being executed. * * @return a new, uncommitted text command or a compound text command */ protected TextCommand createCurrent() { return fFoldingIntoCompoundChange ? new CompoundTextCommand(fUndoContext) : new TextCommand(fUndoContext); } /** * Commits the current change into this command. */ protected void commit() { if (fStart < 0) { if (fFoldingIntoCompoundChange) { fCurrent= createCurrent(); } else { reinitialize(); } } else { updateCommand(); fCurrent= createCurrent(); } resetProcessChangeSate(); } /** * Updates the text from the buffers without resetting * the buffers or adding anything to the stack. * * @since 3.1 */ protected void pretendCommit() { if (fStart > -1) { fText= fTextBuffer.toString(); fPreservedText= fPreservedTextBuffer.toString(); } } /** * Attempt a commit of this command and answer true if a new * fCurrent was created as a result of the commit. * * @return true if the command was committed and created a * new fCurrent, false if not. * @since 3.1 */ protected bool attemptCommit() { pretendCommit(); if (isValid()) { this.outer.commit(); return true; } return false; } /** * Checks whether this text command is valid for undo or redo. * * @return <code>true</code> if the command is valid for undo or redo * @since 3.1 */ protected bool isValid() { return fStart > -1 && fEnd > -1 && fText !is null; } /* * @see java.lang.Object#toString() * @since 3.1 */ public override String toString() { String delimiter= ", "; //$NON-NLS-1$ StringBuffer text= new StringBuffer(super.toString()); text.append("\n"); //$NON-NLS-1$ text.append(this.classinfo.name); text.append(" undo modification stamp: "); //$NON-NLS-1$ text.append(fUndoModificationStamp); text.append(" redo modification stamp: "); //$NON-NLS-1$ text.append(fRedoModificationStamp); text.append(" start: "); //$NON-NLS-1$ text.append(fStart); text.append(delimiter); text.append("end: "); //$NON-NLS-1$ text.append(fEnd); text.append(delimiter); text.append("text: '"); //$NON-NLS-1$ text.append(fText); text.append('\''); text.append(delimiter); text.append("preservedText: '"); //$NON-NLS-1$ text.append(fPreservedText); text.append('\''); return text.toString(); } /** * Return the undo modification stamp * * @return the undo modification stamp for this command * @since 3.1 */ protected long getUndoModificationStamp() { return fUndoModificationStamp; } /** * Return the redo modification stamp * * @return the redo modification stamp for this command * @since 3.1 */ protected long getRedoModificationStamp() { return fRedoModificationStamp; } } /** * Represents an undo-able edit command consisting of several * individual edit commands. */ class CompoundTextCommand : TextCommand { /** The list of individual commands */ private List fCommands; /** * Creates a new compound text command. * * @param context the undo context for this command * @since 3.1 */ this(IUndoContext context) { super(context); fCommands= new ArrayList(); } /** * Adds a new individual command to this compound command. * * @param command the command to be added */ protected void add(TextCommand command) { fCommands.add(command); } /* * @see dwtx.jface.text.DefaultUndoManager.TextCommand#undo() */ public IStatus undo(IProgressMonitor monitor, IAdaptable uiInfo) { resetProcessChangeSate(); int size= fCommands.size(); if (size > 0) { TextCommand c; for (int i= size -1; i > 0; --i) { c= cast(TextCommand) fCommands.get(i); c.undoTextChange(); } c= cast(TextCommand) fCommands.get(0); c.undo(monitor, uiInfo); } return Status.OK_STATUS; } /* * @see dwtx.jface.text.DefaultUndoManager.TextCommand#redo() */ public IStatus redo(IProgressMonitor monitor, IAdaptable uiInfo) { resetProcessChangeSate(); int size= fCommands.size(); if (size > 0) { TextCommand c; for (int i= 0; i < size -1; ++i) { c= cast(TextCommand) fCommands.get(i); c.redoTextChange(); } c= cast(TextCommand) fCommands.get(size -1); c.redo(monitor, uiInfo); } return Status.OK_STATUS; } /* * @see TextCommand#updateCommand */ protected void updateCommand() { // first gather the data from the buffers super.updateCommand(); // the result of the command update is stored as a child command TextCommand c= new TextCommand(fUndoContext); c.fStart= fStart; c.fEnd= fEnd; c.fText= fText; c.fPreservedText= fPreservedText; c.fUndoModificationStamp= fUndoModificationStamp; c.fRedoModificationStamp= fRedoModificationStamp; add(c); // clear out all indexes now that the child is added reinitialize(); } /* * @see TextCommand#createCurrent */ protected TextCommand createCurrent() { if (!fFoldingIntoCompoundChange) return new TextCommand(fUndoContext); reinitialize(); return this; } /* * @see dwtx.jface.text.DefaultUndoManager.TextCommand#commit() */ protected void commit() { // if there is pending data, update the command if (fStart > -1) updateCommand(); fCurrent= createCurrent(); resetProcessChangeSate(); } /** * Checks whether the command is valid for undo or redo. * * @return true if the command is valid. * @since 3.1 */ protected bool isValid() { if (isConnected()) return (fStart > -1 || fCommands.size() > 0); return false; } /** * Returns the undo modification stamp. * * @return the undo modification stamp * @since 3.1 */ protected long getUndoModificationStamp() { if (fStart > -1) return super.getUndoModificationStamp(); else if (fCommands.size() > 0) return (cast(TextCommand)fCommands.get(0)).getUndoModificationStamp(); return fUndoModificationStamp; } /** * Returns the redo modification stamp. * * @return the redo modification stamp * @since 3.1 */ protected long getRedoModificationStamp() { if (fStart > -1) return super.getRedoModificationStamp(); else if (fCommands.size() > 0) return (cast(TextCommand)fCommands.get(fCommands.size()-1)).getRedoModificationStamp(); return fRedoModificationStamp; } } /** * Internal listener to mouse and key events. */ 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) 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: commit(); break; default: } } } /** * Internal listener to document changes. */ class DocumentListener : IDocumentListener { private String fReplacedText; /* * @see dwtx.jface.text.IDocumentListener#documentAboutToBeChanged(dwtx.jface.text.DocumentEvent) */ public void documentAboutToBeChanged(DocumentEvent event) { try { fReplacedText= event.getDocument().get(event.getOffset(), event.getLength()); fPreservedUndoModificationStamp= event.getModificationStamp(); } catch (BadLocationException x) { fReplacedText= null; } } /* * @see dwtx.jface.text.IDocumentListener#documentChanged(dwtx.jface.text.DocumentEvent) */ public void documentChanged(DocumentEvent event) { fPreservedRedoModificationStamp= event.getModificationStamp(); // record the current valid state for the top operation in case it remains the // top operation but changes state. IUndoableOperation op= fHistory.getUndoOperation(fUndoContext); bool wasValid= false; if (op !is null) wasValid= op.canUndo(); // Process the change, providing the before and after timestamps processChange(event.getOffset(), event.getOffset() + event.getLength(), event.getText(), fReplacedText, fPreservedUndoModificationStamp, fPreservedRedoModificationStamp); // now update fCurrent with the latest buffers from the document change. fCurrent.pretendCommit(); if (op is fCurrent) { // if the document change did not cause a new fCurrent to be created, then we should // notify the history that the current operation changed if its validity has changed. if (wasValid !is fCurrent.isValid()) fHistory.operationChanged(op); } else { // if the change created a new fCurrent that we did not yet add to the // stack, do so if it's valid and we are not in the middle of a compound change. if (fCurrent !is fLastAddedCommand && fCurrent.isValid()) { addToCommandStack(fCurrent); } } } } /** * Internal text input listener. */ class TextInputListener : ITextInputListener { /* * @see dwtx.jface.text.ITextInputListener#inputDocumentAboutToBeChanged(dwtx.jface.text.IDocument, dwtx.jface.text.IDocument) */ public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) { if (oldInput !is null && fDocumentListener !is null) { oldInput.removeDocumentListener(fDocumentListener); commit(); } } /* * @see dwtx.jface.text.ITextInputListener#inputDocumentChanged(dwtx.jface.text.IDocument, dwtx.jface.text.IDocument) */ public void inputDocumentChanged(IDocument oldInput, IDocument newInput) { if (newInput !is null) { if (fDocumentListener is null) fDocumentListener= new DocumentListener(); newInput.addDocumentListener(fDocumentListener); } } } /* * @see IOperationHistoryListener * @since 3.1 */ class HistoryListener : IOperationHistoryListener { private IUndoableOperation fOperation; public void historyNotification(OperationHistoryEvent event) { int type= event.getEventType(); switch (type) { case OperationHistoryEvent.ABOUT_TO_UNDO: case OperationHistoryEvent.ABOUT_TO_REDO: // if this is one of our operations if (event.getOperation().hasContext(fUndoContext)) { fTextViewer.getTextWidget().getDisplay().syncExec(dgRunnable((OperationHistoryEvent event_, int type_ ) { // if we are undoing/redoing a command we generated, then ignore // the document changes associated with this undo or redo. if (event_.getOperation() ) { if ( cast(TextViewer)fTextViewer ) (cast(TextViewer)fTextViewer).ignoreAutoEditStrategies_package(true); listenToTextChanges(false); // in the undo case only, make sure compounds are closed if (type_ is OperationHistoryEvent.ABOUT_TO_UNDO) { if (fFoldingIntoCompoundChange) { endCompoundChange(); } } } else { // the undo or redo has our context, but it is not one of // our commands. We will listen to the changes, but will // reset the state that tracks the undo/redo history. commit(); fLastAddedCommand= null; } }, event, type )); fOperation= event.getOperation(); } break; case OperationHistoryEvent.UNDONE: case OperationHistoryEvent.REDONE: case OperationHistoryEvent.OPERATION_NOT_OK: if (event.getOperation() is fOperation) { fTextViewer.getTextWidget().getDisplay().syncExec(new class() Runnable { public void run() { listenToTextChanges(true); fOperation= null; if ( cast(TextViewer)fTextViewer ) (cast(TextViewer)fTextViewer).ignoreAutoEditStrategies_package(false); } }); } break; default: } } } /** Text buffer to collect text which is inserted into the viewer */ private StringBuffer fTextBuffer; /** Text buffer to collect viewer content which has been replaced */ private StringBuffer fPreservedTextBuffer; /** The document modification stamp for undo. */ protected long fPreservedUndoModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP; /** The document modification stamp for redo. */ protected long fPreservedRedoModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP; /** The internal key and mouse event listener */ private KeyAndMouseListener fKeyAndMouseListener; /** The internal document listener */ private DocumentListener fDocumentListener; /** The internal text input listener */ private TextInputListener fTextInputListener; /** Indicates inserting state */ private bool fInserting= false; /** Indicates overwriting state */ private bool fOverwriting= false; /** Indicates whether the current change belongs to a compound change */ private bool fFoldingIntoCompoundChange= false; /** The text viewer the undo manager is connected to */ private ITextViewer fTextViewer; /** Supported undo level */ private int fUndoLevel; /** The currently constructed edit command */ private TextCommand fCurrent; /** The last delete edit command */ private TextCommand fPreviousDelete; /** * The undo context. * @since 3.1 */ private IOperationHistory fHistory; /** * The operation history. * @since 3.1 */ private IUndoContext fUndoContext; /** * The operation history listener used for managing undo and redo before * and after the individual commands are performed. * @since 3.1 */ private IOperationHistoryListener fHistoryListener; /** * The command last added to the operation history. This must be tracked * internally instead of asking the history, since outside parties may be placing * items on our undo/redo history. */ private TextCommand fLastAddedCommand= null; /** * Creates a new undo manager who remembers the specified number of edit commands. * * @param undoLevel the length of this manager's history */ public this(int undoLevel) { fTextBuffer= new StringBuffer(); fPreservedTextBuffer= new StringBuffer(); fHistoryListener= new HistoryListener(); fHistory= OperationHistoryFactory.getOperationHistory(); setMaximalUndoLevel(undoLevel); } /** * Returns whether this undo manager is connected to a text viewer. * * @return <code>true</code> if connected, <code>false</code> otherwise * @since 3.1 */ private bool isConnected() { return fTextViewer !is null; } /* * @see IUndoManager#beginCompoundChange */ public void beginCompoundChange() { if (isConnected()) { fFoldingIntoCompoundChange= true; commit(); } } /* * @see IUndoManager#endCompoundChange */ public void endCompoundChange() { if (isConnected()) { fFoldingIntoCompoundChange= false; commit(); } } /** * 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); fHistory.addOperationHistoryListener(fHistoryListener); listenToTextChanges(true); } } /** * 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; } listenToTextChanges(false); fHistory.removeOperationHistoryListener(fHistoryListener); } } /** * Adds the given command to the operation history if it is not part of * a compound change. * * @param command the command to be added * @since 3.1 */ private void addToCommandStack(TextCommand command){ if (!fFoldingIntoCompoundChange || cast(CompoundTextCommand)command ) { fHistory.add(command); fLastAddedCommand= command; } } /** * Disposes the command stack. * * @since 3.1 */ private void disposeCommandStack() { fHistory.dispose(fUndoContext, true, true, true); } /** * Initializes the command stack. * * @since 3.1 */ private void initializeCommandStack() { if (fHistory !is null && fUndoContext !is null) fHistory.dispose(fUndoContext, true, true, false); } /** * Switches the state of whether there is a text listener or not. * * @param listen the state which should be established */ private void listenToTextChanges(bool listen) { if (listen) { if (fDocumentListener is null && fTextViewer.getDocument() !is null) { fDocumentListener= new DocumentListener(); fTextViewer.getDocument().addDocumentListener(fDocumentListener); } } else if (!listen) { if (fDocumentListener !is null && fTextViewer.getDocument() !is null) { fTextViewer.getDocument().removeDocumentListener(fDocumentListener); fDocumentListener= null; } } } /** * Closes the current editing command and opens a new one. */ private void commit() { // if fCurrent has never been placed on the command stack, do so now. // this can happen when there are multiple programmatically commits in a single // document change. if (fLastAddedCommand !is fCurrent) { fCurrent.pretendCommit(); if (fCurrent.isValid()) addToCommandStack(fCurrent); } fCurrent.commit(); } /** * Reset processChange state. * * @since 3.2 */ private void resetProcessChangeSate() { fInserting= false; fOverwriting= false; fPreviousDelete.reinitialize(); } /** * Checks whether the given text starts with a line delimiter and * subsequently contains a white space only. * * @param text the text to check * @return <code>true</code> if the text is a line delimiter followed by whitespace, <code>false</code> otherwise */ private bool isWhitespaceText(String text) { if (text is null || text.length() is 0) return false; String[] delimiters= fTextViewer.getDocument().getLegalLineDelimiters(); int index= TextUtilities.startsWith(delimiters, text); if (index > -1) { char c; int length= text.length(); for (int i= delimiters[index].length; i < length; i++) { c= text.charAt(i); if (c !is ' ' && c !is '\t') return false; } return true; } return false; } private void processChange(int modelStart, int modelEnd, String insertedText, String replacedText, long beforeChangeModificationStamp, long afterChangeModificationStamp) { if (insertedText is null) insertedText= ""; //$NON-NLS-1$ if (replacedText is null) replacedText= ""; //$NON-NLS-1$ int length= insertedText.length(); int diff= modelEnd - modelStart; if (fCurrent.fUndoModificationStamp is IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP) fCurrent.fUndoModificationStamp= beforeChangeModificationStamp; // normalize if (diff < 0) { int tmp= modelEnd; modelEnd= modelStart; modelStart= tmp; } if (modelStart is modelEnd) { // text will be inserted if ((length is 1) || isWhitespaceText(insertedText)) { // by typing or whitespace if (!fInserting || (modelStart !is fCurrent.fStart + fTextBuffer.length())) { fCurrent.fRedoModificationStamp= beforeChangeModificationStamp; if (fCurrent.attemptCommit()) fCurrent.fUndoModificationStamp= beforeChangeModificationStamp; fInserting= true; } if (fCurrent.fStart < 0) fCurrent.fStart= fCurrent.fEnd= modelStart; if (length > 0) fTextBuffer.append(insertedText); } else if (length >= 0) { // by pasting or model manipulation fCurrent.fRedoModificationStamp= beforeChangeModificationStamp; if (fCurrent.attemptCommit()) fCurrent.fUndoModificationStamp= beforeChangeModificationStamp; fCurrent.fStart= fCurrent.fEnd= modelStart; fTextBuffer.append(insertedText); fCurrent.fRedoModificationStamp= afterChangeModificationStamp; if (fCurrent.attemptCommit()) fCurrent.fUndoModificationStamp= afterChangeModificationStamp; } } else { if (length is 0) { // text will be deleted by backspace or DEL key or empty clipboard length= replacedText.length; String[] delimiters= fTextViewer.getDocument().getLegalLineDelimiters(); if ((length is 1) || TextUtilities.equals(delimiters, replacedText) > -1) { // whereby selection is empty if (fPreviousDelete.fStart is modelStart && fPreviousDelete.fEnd is modelEnd) { // repeated DEL // correct wrong settings of fCurrent if (fCurrent.fStart is modelEnd && fCurrent.fEnd is modelStart) { fCurrent.fStart= modelStart; fCurrent.fEnd= modelEnd; } // append to buffer && extend command range fPreservedTextBuffer.append(replacedText); ++fCurrent.fEnd; } else if (fPreviousDelete.fStart is modelEnd) { // repeated backspace // insert in buffer and extend command range fPreservedTextBuffer.select(0, 0); fPreservedTextBuffer.replace(replacedText); fCurrent.fStart= modelStart; } else { // either DEL or backspace for the first time fCurrent.fRedoModificationStamp= beforeChangeModificationStamp; if (fCurrent.attemptCommit()) fCurrent.fUndoModificationStamp= beforeChangeModificationStamp; // as we can not decide whether it was DEL or backspace we initialize for backspace fPreservedTextBuffer.append(replacedText); fCurrent.fStart= modelStart; fCurrent.fEnd= modelEnd; } fPreviousDelete.set(modelStart, modelEnd); } else if (length > 0) { // whereby selection is not empty fCurrent.fRedoModificationStamp= beforeChangeModificationStamp; if (fCurrent.attemptCommit()) fCurrent.fUndoModificationStamp= beforeChangeModificationStamp; fCurrent.fStart= modelStart; fCurrent.fEnd= modelEnd; fPreservedTextBuffer.append(replacedText); } } else { // text will be replaced if (length is 1) { length= replacedText.length; String[] delimiters= fTextViewer.getDocument().getLegalLineDelimiters(); if ((length is 1) || TextUtilities.equals(delimiters, replacedText) > -1) { // because of overwrite mode or model manipulation if (!fOverwriting || (modelStart !is fCurrent.fStart + fTextBuffer.length())) { fCurrent.fRedoModificationStamp= beforeChangeModificationStamp; if (fCurrent.attemptCommit()) fCurrent.fUndoModificationStamp= beforeChangeModificationStamp; fOverwriting= true; } if (fCurrent.fStart < 0) fCurrent.fStart= modelStart; fCurrent.fEnd= modelEnd; fTextBuffer.append(insertedText); fPreservedTextBuffer.append(replacedText); fCurrent.fRedoModificationStamp= afterChangeModificationStamp; return; } } // because of typing or pasting whereby selection is not empty fCurrent.fRedoModificationStamp= beforeChangeModificationStamp; if (fCurrent.attemptCommit()) fCurrent.fUndoModificationStamp= beforeChangeModificationStamp; fCurrent.fStart= modelStart; fCurrent.fEnd= modelEnd; fTextBuffer.append(insertedText); fPreservedTextBuffer.append(replacedText); } } // in all cases, the redo modification stamp is updated on the open command fCurrent.fRedoModificationStamp= afterChangeModificationStamp; } /** * Shows the given exception in an error dialog. * * @param title the dialog title * @param ex the exception * @since 3.1 */ private void openErrorDialog(String title, 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.msg/+getLocalizedMessage()+/); else { Display display; Shell finalShell= shell; if (finalShell !is null) display= finalShell.getDisplay(); else display= Display.getDefault(); display.syncExec(dgRunnable( { MessageDialog.openError(finalShell, title, ex.msg/+getLocalizedMessage()+/); })); } } /* * @see dwtx.jface.text.IUndoManager#setMaximalUndoLevel(int) */ public void setMaximalUndoLevel(int undoLevel) { fUndoLevel= Math.max(0, undoLevel); if (isConnected()) { fHistory.setLimit(fUndoContext, fUndoLevel); } } /* * @see dwtx.jface.text.IUndoManager#connect(dwtx.jface.text.ITextViewer) */ public void connect(ITextViewer textViewer) { if (!isConnected() && textViewer !is null) { fTextViewer= textViewer; if (fUndoContext is null) fUndoContext= new ObjectUndoContext(this); fHistory.setLimit(fUndoContext, fUndoLevel); initializeCommandStack(); // open up the current command fCurrent= new TextCommand(fUndoContext); fPreviousDelete= new TextCommand(fUndoContext); addListeners(); } } /* * @see dwtx.jface.text.IUndoManager#disconnect() */ public void disconnect() { if (isConnected()) { removeListeners(); fCurrent= null; fTextViewer= null; disposeCommandStack(); fTextBuffer.clear(); fPreservedTextBuffer.clear(); fUndoContext= null; } } /* * @see dwtx.jface.text.IUndoManager#reset() */ public void reset() { if (isConnected()) { initializeCommandStack(); fCurrent= new TextCommand(fUndoContext); fFoldingIntoCompoundChange= false; fInserting= false; fOverwriting= false; fTextBuffer.truncate(0); fPreservedTextBuffer.truncate(0); fPreservedUndoModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP; fPreservedRedoModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP; } } /* * @see dwtx.jface.text.IUndoManager#redoable() */ public bool redoable() { return fHistory.canRedo(fUndoContext); } /* * @see dwtx.jface.text.IUndoManager#undoable() */ public bool undoable() { return fHistory.canUndo(fUndoContext); } /* * @see dwtx.jface.text.IUndoManager#redo() */ public void redo() { if (isConnected() && redoable()) { try { fHistory.redo(fUndoContext, null, null); } 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() && undoable()) { try { fHistory.undo(fUndoContext, null, null); } 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 * @since 3.0 */ protected void selectAndReveal(int offset, int length) { if ( cast(ITextViewerExtension5)fTextViewer ) { ITextViewerExtension5 extension= cast(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() * @since 3.1 */ public IUndoContext getUndoContext() { return fUndoContext; } }