| /******************************************************************************* |
| * Copyright (c) 2005, 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 |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.ui.javaeditor; |
| |
| |
| 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.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.ListenerList; |
| |
| import org.eclipse.jface.text.ITextViewer; |
| |
| import org.eclipse.ui.PlatformUI; |
| import org.eclipse.ui.commands.ICommandService; |
| |
| import org.eclipse.jdt.internal.ui.JavaPlugin; |
| |
| /** |
| * 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(ListenerList.IDENTITY); |
| |
| 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(); |
| } |
| |
| /** |
| * 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= null; |
| } |
| } |
| |
| private void addListeners() { |
| fWidgetEventSource= fViewer.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() { |
| 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(); |
| Object[] listeners= fListenerList.getListeners(); |
| for (int i= 0; i < listeners.length; i++) { |
| ICompoundEditListener listener= (ICompoundEditListener) listeners[i]; |
| try { |
| listener.endCompoundEdit(); |
| } catch (Exception e) { |
| JavaPlugin.log(e); |
| } |
| } |
| } |
| |
| /** |
| * 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.add(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.remove(listener); |
| } |
| |
| } |