diff options
author | teicher | 2005-05-17 12:53:31 +0000 |
---|---|---|
committer | teicher | 2005-05-17 12:53:31 +0000 |
commit | cde44bc86d4bf6af3ec3f3693ed9f6f57c5a16ab (patch) | |
tree | ee4f9cd3388832ca9ed3a32750f0faf52c8b7c51 /org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/texteditor/CompoundEditExitStrategy.java | |
parent | 2eda3a34139bb073898849da15fe35cde63b7026 (diff) | |
download | eclipse.platform.text-cde44bc86d4bf6af3ec3f3693ed9f6f57c5a16ab.tar.gz eclipse.platform.text-cde44bc86d4bf6af3ec3f3693ed9f6f57c5a16ab.tar.xz eclipse.platform.text-cde44bc86d4bf6af3ec3f3693ed9f6f57c5a16ab.zip |
Bug 84353 [typing] Hippie completion fills undo stack
- added CompoundEditExitStrategy that detects end-of-compound-edit events
- used by (Java)MoveLinesAction, HippieCompleteAction
Diffstat (limited to 'org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/texteditor/CompoundEditExitStrategy.java')
-rw-r--r-- | org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/texteditor/CompoundEditExitStrategy.java | 236 |
1 files changed, 236 insertions, 0 deletions
diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/texteditor/CompoundEditExitStrategy.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/texteditor/CompoundEditExitStrategy.java new file mode 100644 index 00000000000..600235d0ec4 --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/texteditor/CompoundEditExitStrategy.java @@ -0,0 +1,236 @@ +package org.eclipse.ui.internal.texteditor; + + +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.StyledText; +import org.eclipse.swt.custom.VerifyKeyListener; +import org.eclipse.swt.events.FocusEvent; +import org.eclipse.swt.events.FocusListener; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.MouseListener; +import org.eclipse.swt.events.VerifyEvent; + +import org.eclipse.jface.text.ITextViewer; + +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.core.commands.IExecutionListener; +import org.eclipse.core.commands.NotHandledException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.commands.ICommandService; + +/** + * Exit strategy for commands that want to fold repeated execution into one compound edit. See + * {@link org.eclipse.jface.text.IRewriteTarget#endCompoundChange() IRewriteTarget.endCompoundChange}. + * As long as a strategy is installed on an {@link ITextViewer}, it will detect the end of a + * compound operation when any of the following conditions becomes true: + * <ul> + * <li>the viewer's text widget loses the keyboard focus</li> + * <li>the mouse is clicked or double clicked inside the viewer's widget</li> + * <li>a command other than the ones specified is executed</li> + * <li>the viewer receives any key events that are not modifier combinations</li> + * </ul> + * <p> + * If the end of a compound edit is detected, any registered {@link ICompoundEditListener}s are + * notified and the strategy is disarmed (spring-loaded). + * </p> + * + * @since 3.1 + */ +public final class CompoundEditExitStrategy { + /** + * Listens for events that may trigger the end of a compound edit. + */ + private final class EventListener implements MouseListener, FocusListener, VerifyKeyListener, IExecutionListener { + + /* + * @see org.eclipse.swt.events.MouseListener#mouseDoubleClick(org.eclipse.swt.events.MouseEvent) + */ + public void mouseDoubleClick(MouseEvent e) { + // mouse actions end the compound change + fireEndCompoundEdit(); + } + + /* + * @see org.eclipse.swt.events.MouseListener#mouseDown(org.eclipse.swt.events.MouseEvent) + */ + public void mouseDown(MouseEvent e) { + // mouse actions end the compound change + fireEndCompoundEdit(); + } + + public void mouseUp(MouseEvent e) {} + + public void focusGained(FocusEvent e) {} + + /* + * @see org.eclipse.swt.events.FocusListener#focusLost(org.eclipse.swt.events.FocusEvent) + */ + public void focusLost(FocusEvent e) { + // losing focus ends the change + fireEndCompoundEdit(); + } + + public void notHandled(String commandId, NotHandledException exception) {} + + public void postExecuteFailure(String commandId, ExecutionException exception) {} + + public void postExecuteSuccess(String commandId, Object returnValue) {} + + /* + * @see org.eclipse.core.commands.IExecutionListener#preExecute(java.lang.String, org.eclipse.core.commands.ExecutionEvent) + */ + public void preExecute(String commandId, ExecutionEvent event) { + // any command other than the known ones end the compound change + for (int i= 0; i < fCommandIds.length; i++) { + if (commandId.equals(fCommandIds[i])) + return; + } + fireEndCompoundEdit(); + } + + /* + * @see org.eclipse.swt.custom.VerifyKeyListener#verifyKey(org.eclipse.swt.events.VerifyEvent) + */ + public void verifyKey(VerifyEvent event) { + // any key press that is not a modifier combo ends the compound change + final int maskWithoutShift= SWT.MODIFIER_MASK & ~SWT.SHIFT; + if ((event.keyCode & SWT.MODIFIER_MASK) == 0 && (event.stateMask & maskWithoutShift) == 0) + fireEndCompoundEdit(); + } + + } + + private final String[] fCommandIds; + private final EventListener fEventListener= new EventListener(); + private final ListenerList fListenerList= new ListenerList(); + private final ListenerList.INotifier fNotifier= new ListenerList.INotifier() { + public void notifyListener(Object listener) { + try { + ((ICompoundEditListener) listener).endCompoundEdit(); + } catch (Exception e) { + IStatus status= new Status(IStatus.ERROR, TextEditorPlugin.PLUGIN_ID, IStatus.OK, "listener notification failed", e); //$NON-NLS-1$ + TextEditorPlugin.getDefault().getLog().log(status); + } + } + }; + + private ITextViewer fViewer; + private StyledText fWidgetEventSource; + + /** + * Creates a new strategy, equivalent to calling + * {@linkplain #CompoundEditExitStrategy(String[]) CompoundEditExitStrategy(new String[] { commandId })}. + * + * @param commandId the command id of the repeatable command + */ + public CompoundEditExitStrategy(String commandId) { + if (commandId == null) + throw new NullPointerException("commandId"); //$NON-NLS-1$ + fCommandIds= new String[] {commandId}; + } + + /** + * Creates a new strategy, ending upon execution of any command other than the ones + * specified. + * + * @param commandIds the ids of the repeatable commands + */ + public CompoundEditExitStrategy(String[] commandIds) { + for (int i= 0; i < commandIds.length; i++) { + if (commandIds[i] == null) + throw new NullPointerException("commandIds[" + i + "]"); //$NON-NLS-1$ //$NON-NLS-2$ + } + fCommandIds= new String[commandIds.length]; + System.arraycopy(commandIds, 0, fCommandIds, 0, commandIds.length); + } + + /** + * Installs the receiver on <code>viewer</code> and arms it. After this call returns, any + * registered listeners will be notified if a compound edit ends. + * + * @param viewer the viewer to install on + */ + public void arm(ITextViewer viewer) { + disarm(); + if (viewer == null) + throw new NullPointerException("editor"); //$NON-NLS-1$ + fViewer= viewer; + addListeners(fViewer); + } + + /** + * Disarms the receiver. After this call returns, any registered listeners will be not be + * notified any more until <code>install</code> is called again. Note that the listeners are + * not removed. + * <p> + * Note that the receiver is automatically disarmed when the end of a compound edit has + * been detected and before the listeners are notified. + * </p> + */ + public void disarm() { + if (isInstalled()) { + removeListeners(fViewer); + fViewer= null; + } + } + + private void addListeners(ITextViewer viewer) { + fWidgetEventSource= viewer.getTextWidget(); + if (fWidgetEventSource != null) { + fWidgetEventSource.addVerifyKeyListener(fEventListener); + fWidgetEventSource.addMouseListener(fEventListener); + fWidgetEventSource.addFocusListener(fEventListener); + } + + ICommandService commandService= (ICommandService)PlatformUI.getWorkbench().getAdapter(ICommandService.class); + if (commandService != null) + commandService.addExecutionListener(fEventListener); + } + + private void removeListeners(ITextViewer editor) { + ICommandService commandService = (ICommandService)PlatformUI.getWorkbench().getAdapter(ICommandService.class); + if (commandService != null) + commandService.removeExecutionListener(fEventListener); + + if (fWidgetEventSource != null) { + fWidgetEventSource.removeFocusListener(fEventListener); + fWidgetEventSource.removeMouseListener(fEventListener); + fWidgetEventSource.removeVerifyKeyListener(fEventListener); + fWidgetEventSource= null; + } + } + + private boolean isInstalled() { + return fViewer != null; + } + + private void fireEndCompoundEdit() { + disarm(); + fListenerList.notifyListeners(fNotifier); + } + + /** + * Adds a compound edit listener. Multiple registration is possible. Note that the receiver is + * automatically disarmed before the listeners are notified. + * + * @param listener the new listener + */ + public void addCompoundListener(ICompoundEditListener listener) { + fListenerList.addListener(listener); + } + + /** + * Removes a compound edit listener. If <code>listener</code> is registered multiple times, an + * arbitrary instance is removed. If <code>listener</code> is not currently registered, + * nothing happens. + * + * @param listener the listener to be removed. + */ + public void removeCompoundListener(ICompoundEditListener listener) { + fListenerList.removeListener(listener); + } + +} |