Index: src/org/eclipse/jface/text/DefaultDocumentAdapter.java =================================================================== RCS file: /home/eclipse/org.eclipse.jface.text/src/org/eclipse/jface/text/DefaultDocumentAdapter.java,v retrieving revision 1.8 diff -u -r1.8 DefaultDocumentAdapter.java --- src/org/eclipse/jface/text/DefaultDocumentAdapter.java 17 Jun 2005 15:49:07 -0000 1.8 +++ src/org/eclipse/jface/text/DefaultDocumentAdapter.java 1 Sep 2005 17:45:37 -0000 @@ -87,8 +87,13 @@ if (!fIsForwarding) { fDocumentClone= null; - fOriginalContent= fDocument.get(); - fOriginalLineDelimiters= fDocument.getLegalLineDelimiters(); + if (fDocument != null) { + fOriginalContent= fDocument.get(); + fOriginalLineDelimiters= fDocument.getLegalLineDelimiters(); + } else { + fOriginalContent= null; + fOriginalLineDelimiters= null; + } } if (fDocument != null) Index: src/org/eclipse/jface/text/source/SourceViewerConfiguration.java =================================================================== RCS file: /home/eclipse/org.eclipse.jface.text/src/org/eclipse/jface/text/source/SourceViewerConfiguration.java,v retrieving revision 1.19 diff -u -r1.19 SourceViewerConfiguration.java --- src/org/eclipse/jface/text/source/SourceViewerConfiguration.java 21 Jun 2005 20:17:35 -0000 1.19 +++ src/org/eclipse/jface/text/source/SourceViewerConfiguration.java 1 Sep 2005 17:45:39 -0000 @@ -28,6 +28,7 @@ import org.eclipse.jface.text.ITextDoubleClickStrategy; import org.eclipse.jface.text.ITextHover; import org.eclipse.jface.text.IUndoManager; +import org.eclipse.jface.text.SharedDocumentUndoManager; import org.eclipse.jface.text.contentassist.IContentAssistant; import org.eclipse.jface.text.formatter.IContentFormatter; import org.eclipse.jface.text.hyperlink.DefaultHyperlinkPresenter; @@ -87,6 +88,10 @@ * @return an undo manager or null if no undo/redo should not be supported */ public IUndoManager getUndoManager(ISourceViewer sourceViewer) { + // Temporary variable to leave both styles in the code for now + boolean useSharedUndoManager = true; + if (useSharedUndoManager) + return new SharedDocumentUndoManager(25); return new DefaultUndoManager(25); } Index: src/org/eclipse/jface/text/SharedDocumentUndoManager.java =================================================================== RCS file: src/org/eclipse/jface/text/SharedDocumentUndoManager.java diff -N src/org/eclipse/jface/text/SharedDocumentUndoManager.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/jface/text/SharedDocumentUndoManager.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,441 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ + +package org.eclipse.jface.text; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.StyledText; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.events.KeyListener; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.MouseListener; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; + +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.core.commands.operations.IUndoContext; +import org.eclipse.core.commands.operations.OperationHistoryFactory; +import org.eclipse.core.runtime.IAdaptable; + +import org.eclipse.jface.dialogs.MessageDialog; + + +/** + * Shared document implementation of {@link org.eclipse.jface.text.IUndoManager}. + *

+ * 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. + *

+ * This class is not intended to be subclassed. + *

+ * + * @see org.eclipse.jface.text.ITextViewer + * @see org.eclipse.jface.text.ITextInputListener + * @see MouseListener + * @see KeyListener + * @see DocumentUndoManager + * + * @since 3.2 + */ +public class SharedDocumentUndoManager implements IUndoManager, IUndoManagerExtension { + + /** + * Internal listener to mouse and key events. + */ + class KeyAndMouseListener implements 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 == 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 SWT.ARROW_UP: + case SWT.ARROW_DOWN: + case SWT.ARROW_LEFT: + case SWT.ARROW_RIGHT: + if (isConnected()) { + fDocumentUndoManager.commit(); + } + break; + } + } + } + + /** + * Internal text input listener. + */ + class TextInputListener implements ITextInputListener { + + /* + * @see org.eclipse.jface.text.ITextInputListener#inputDocumentAboutToBeChanged(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.IDocument) + */ + public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) { + if (oldInput != null && fDocumentUndoManager != null) { + fDocumentUndoManager.disconnect(SharedDocumentUndoManager.this); + fDocumentUndoManager.removeDocumentUndoListener(fDocumentUndoListener); + fDocumentUndoListener= null; + fDocumentUndoManager= null; + } + } + + /* + * @see org.eclipse.jface.text.ITextInputListener#inputDocumentChanged(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.IDocument) + */ + public void inputDocumentChanged(IDocument oldInput, IDocument newInput) { + if (newInput instanceof IDocumentExtension5) { + fDocumentUndoManager= ((IDocumentExtension5)newInput).getUndoManager(); + fDocumentUndoManager.connect(SharedDocumentUndoManager.this); + setMaximalUndoLevel(fUndoLevel); + fDocumentUndoListener= new DocumentUndoListener(); + fDocumentUndoManager.addDocumentUndoListener(fDocumentUndoListener); + } + } + } + + /** + * Internal document undo listener. + */ + class DocumentUndoListener implements IDocumentUndoListener { + + /* + * @see org.eclipse.jface.text.IDocumentUndoListener#documentUndoNotification(DocumentUndoEvent) + */ + public void documentUndoNotification(DocumentUndoEvent event ){ + if (!isConnected()) return; + + int eventType= event.getEventType(); + if (((eventType & DocumentUndoEvent.ABOUT_TO_UNDO) != 0) || ((eventType & DocumentUndoEvent.ABOUT_TO_REDO) != 0)) { + if (event.isCompound()) { + ITextViewerExtension extension= null; + if (fTextViewer instanceof ITextViewerExtension) + extension= (ITextViewerExtension) fTextViewer; + + if (extension != 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) != 0) || ((eventType & DocumentUndoEvent.REDONE) != 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 != null) + extension.setRedraw(true); + } + // it it can be determined that the undo originated in this + // viewer, select and reveal the text. + IAdaptable uiInfo = event.getInfoAdapter(); + if (uiInfo != null) { + ITextViewer viewer = (ITextViewer)(uiInfo.getAdapter(ITextViewer.class)); + if (viewer == fTextViewer) + selectAndReveal(event.getOffset(), event.getText() == 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 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 SharedDocumentUndoManager(int undoLevel) { + fUndoLevel= undoLevel; + } + + /** + * Returns whether this undo manager is connected to a text viewer. + * + * @return true if connected, false otherwise + * @since 3.1 + */ + private boolean isConnected() { + return fTextViewer != null && fDocumentUndoManager != 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 != 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 != null) { + if (fKeyAndMouseListener != null) { + text.removeMouseListener(fKeyAndMouseListener); + text.removeKeyListener(fKeyAndMouseListener); + fKeyAndMouseListener= null; + } + if (fTextInputListener != null) { + fTextViewer.removeTextInputListener(fTextInputListener); + fTextInputListener= null; + } + } + } + + /** + * Shows the given exception in an error dialog. + * + * @param title the dialog title + * @param ex the exception + * @since 3.1 + */ + private void openErrorDialog(final String title, final Exception ex) { + Shell shell= null; + if (isConnected()) { + StyledText st= fTextViewer.getTextWidget(); + if (st != null && !st.isDisposed()) + shell= st.getShell(); + } + if (Display.getCurrent() != null) + MessageDialog.openError(shell, title, ex.getLocalizedMessage()); + else { + Display display; + final Shell finalShell= shell; + if (finalShell != null) + display= finalShell.getDisplay(); + else + display= Display.getDefault(); + display.syncExec(new Runnable() { + public void run() { + MessageDialog.openError(finalShell, title, ex.getLocalizedMessage()); + } + }); + } + } + + /* + * @see org.eclipse.jface.text.IUndoManager#setMaximalUndoLevel(int) + */ + public void setMaximalUndoLevel(int undoLevel) { + fUndoLevel= Math.max(0, undoLevel); + if (isConnected()) { + fDocumentUndoManager.setUndoLimit(fUndoLevel); + } + } + + /* + * @see org.eclipse.jface.text.IUndoManager#connect(org.eclipse.jface.text.ITextViewer) + */ + public void connect(ITextViewer textViewer) { + if (fTextViewer == null && textViewer != null) { + fTextViewer= textViewer; + addListeners(); + } + IDocument doc = fTextViewer.getDocument(); + if (doc instanceof IDocumentExtension5) { + fDocumentUndoManager= ((IDocumentExtension5)doc).getUndoManager(); + fDocumentUndoManager.connect(this); + setMaximalUndoLevel(fUndoLevel); + fDocumentUndoListener= new DocumentUndoListener(); + fDocumentUndoManager.addDocumentUndoListener(fDocumentUndoListener); + } + } + + /* + * @see org.eclipse.jface.text.IUndoManager#disconnect() + */ + public void disconnect() { + if (fTextViewer != null) { + removeListeners(); + fTextViewer= null; + if (fDocumentUndoManager != null) { + fDocumentUndoManager.disconnect(this); + fDocumentUndoManager.removeDocumentUndoListener(fDocumentUndoListener); + fDocumentUndoManager= null; + fDocumentUndoListener= null; + } + } + } + + /* + * @see org.eclipse.jface.text.IUndoManager#reset() + */ + public void reset() { + /* + * Disconnected and reconnecting will only initialize the history + * if there are no other clients tracking the undo history. + */ + if (isConnected()) { + fDocumentUndoManager.disconnect(this); + fDocumentUndoManager.connect(this); + } + + } + + /* + * @see org.eclipse.jface.text.IUndoManager#redoable() + */ + public boolean redoable() { + if (isConnected()) + return OperationHistoryFactory.getOperationHistory().canRedo(fDocumentUndoManager.getUndoContext()); + return false; + } + + /* + * @see org.eclipse.jface.text.IUndoManager#undoable() + */ + public boolean undoable() { + if (isConnected()) + return OperationHistoryFactory.getOperationHistory().canUndo(getUndoContext()); + return false; + } + + /* + * @see org.eclipse.jface.text.IUndoManager#redo() + */ + public void redo() { + if (isConnected() && redoable()) { + try { + OperationHistoryFactory.getOperationHistory().redo(getUndoContext(), null, null); + } catch (ExecutionException ex) { + openErrorDialog(JFaceTextMessages.getString("DefaultUndoManager.error.redoFailed.title"), ex); //$NON-NLS-1$ + } + } + } + + /* + * @see org.eclipse.jface.text.IUndoManager#undo() + */ + public void undo() { + if (isConnected() && undoable()) { + try { + OperationHistoryFactory.getOperationHistory().undo(getUndoContext(), 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 (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 org.eclipse.jface.text.IUndoManagerExtension#getUndoContext() + * @since 3.1 + */ + public IUndoContext getUndoContext() { + if (isConnected()) { + return fDocumentUndoManager.getUndoContext(); + } + return null; + } + +}