| /******************************************************************************* |
| * Copyright (c) 2000, 2003 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Common Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/cpl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.ui.internal.misc; |
| |
| import org.eclipse.jface.action.*; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.widgets.*; |
| import java.util.*; |
| |
| /** |
| * A strategy for hooking accelerators on a control. |
| * <p> |
| * In SWT an accelerator which is defined in a shell menu is global. If |
| * enabled, it will always be executed regardless of the current focus control or its |
| * abilities. This creates a problem where the focus control has an equivalent |
| * key sequence. For example, if <code>Ctrl+C</code> is defined as the accelerator |
| * for <code>Copy</code> at the shell level it will override the same key sequence |
| * in a text widget within the shell, thus breaking the text widget. |
| * </p><p> |
| * To avoid this problem an <code>AcceleratorHook</code> may be used to define |
| * the accelerators locally on each control rather than globally on the shell. |
| * The accelerators defined in an <code>AcceleratorHook</code> are only operational |
| * when the control has focus. |
| * </p><p> |
| * To use this class, instantiate an instance for a particular control and register |
| * each action which has an accelerator. If the accelerator is pressed the action will |
| * be invoked. |
| * </p><p> |
| * This class may be instantiated; it is not intended to be subclassed. |
| * </p> |
| */ |
| public class AcceleratorHook implements Listener { |
| private ArrayList actionList; |
| |
| private class ActionItem { |
| public ActionItem(int accel, IAction act) { |
| accelerator = accel; |
| action = act; |
| } |
| public int accelerator; |
| public IAction action; |
| } |
| |
| private class FakeAction extends Action { |
| public FakeAction(String name) { |
| super(name); |
| } |
| public void run() { |
| } |
| } |
| /** |
| * AcceleratorHook constructor comment. |
| */ |
| public AcceleratorHook(Control ctrl) { |
| actionList = new ArrayList(5); |
| ctrl.addListener(SWT.KeyDown, this); |
| ctrl.addListener(SWT.KeyUp, this); |
| } |
| /** |
| * Adds an action to the control. If the action accelerator is pressed |
| * the action will be run. |
| * <p> |
| * The accelerator for the action is derived from the action. |
| * </p> |
| * |
| * @param action an action with unique accelerator |
| */ |
| public void add(IAction action) { |
| if (action.getAccelerator() == 0) |
| return; |
| actionList.add(new ActionItem(action.getAccelerator(), action)); |
| } |
| /** |
| * Adds an action to the control with a particular accelerator. |
| * If the accelerator is pressed the action will be run. |
| * <p> |
| * The accelerator for the action is derived from <code>strAccel</code> |
| * string. The content of this string must conform to the standard JFace |
| * conventions for accelerator declaration. For more information see |
| * the <code>org.eclipse.jface.action.Action</code> class. |
| * </p> |
| * |
| * @param action an action |
| * @param strAccel the action accelerator |
| * @see org.eclipse.jface.action.Action |
| */ |
| public void add(IAction action, String strAccel) { |
| Action fakeAction = new FakeAction("Fake\t"+strAccel);//$NON-NLS-1$ |
| if (fakeAction.getAccelerator() == 0) |
| return; |
| actionList.add(new ActionItem(fakeAction.getAccelerator(), action)); |
| } |
| /** |
| * Returns the first item which represents the action. |
| * |
| * @returns the first item to match, or <code>null</code>. |
| */ |
| private ActionItem findItem(IAction action) { |
| Iterator iter = actionList.iterator(); |
| while (iter.hasNext()) { |
| ActionItem item = (ActionItem)iter.next(); |
| if (item.action == action) |
| return item; |
| } |
| return null; |
| } |
| /** |
| * Returns the first item with an accelerator which maches |
| * the key event. |
| * |
| * @returns the first item to match, or <code>null</code>. |
| */ |
| private ActionItem findItem(Event e) { |
| // Map event to accel. |
| int accel = getAccel(e); |
| if (accel == 0) |
| return null; |
| |
| // Map accelerator to item. |
| Iterator iter = actionList.iterator(); |
| while (iter.hasNext()) { |
| ActionItem item = (ActionItem)iter.next(); |
| // System.out.println("Accel = " + Integer.toString(item.accelerator, 16)); |
| if (item.accelerator == accel) |
| return item; |
| } |
| return null; |
| } |
| /** |
| * Convert a key event to an accelerator. |
| * |
| * @param e the key event |
| * @return the int accelerator value |
| */ |
| private int getAccel(Event e) { |
| // Debug. |
| /* |
| System.out.println("KeyEvent"); |
| System.out.println("\tChar = " + Integer.toString((int)e.character, 16)); |
| System.out.println("\tKeyCode = " + Integer.toString(e.keyCode, 16)); |
| System.out.println("\tState Mask = " + Integer.toString(e.stateMask, 16)); |
| */ |
| |
| // Real work. |
| int key = (int)Character.toUpperCase(e.character); |
| int mods = 0; |
| if ((e.stateMask & SWT.ALT) > 0) |
| mods |= SWT.ALT; |
| if ((e.stateMask & SWT.SHIFT) > 0) |
| mods |= SWT.SHIFT; |
| if ((e.stateMask & SWT.CTRL) > 0) { |
| mods |= SWT.CTRL; |
| key = key + 'A' - 1; // Convert unicode to char. |
| } |
| int accel = key | mods | e.keyCode; |
| |
| // Debug |
| /* |
| System.out.println("Accel = " + Integer.toString(accel, 16)); |
| */ |
| return accel; |
| } |
| |
| /** |
| * Notifies that a key has been pressed on the system keyboard. |
| * <p> |
| * This method is a callback from the target control for this hook. |
| * Other clients are not expected to call this method. |
| * </p> |
| * |
| * @param e an event containing information about the key press |
| */ |
| public void handleEvent(Event event) { |
| ActionItem item = findItem(event); |
| if (item != null) |
| item.action.runWithEvent(event); |
| } |
| |
| /** |
| * Removes an action from the hook. Does nothing if the action is |
| * not found. |
| * |
| * @param action an action |
| */ |
| public void remove(IAction action) { |
| ActionItem item = findItem(action); |
| if (item != null) |
| actionList.remove(item); |
| } |
| } |