001: /*******************************************************************************
002: * Copyright (c) 2000, 2006 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.ui.internal.handlers;
011:
012: import java.lang.reflect.InvocationTargetException;
013: import java.lang.reflect.Method;
014:
015: import org.eclipse.core.commands.AbstractHandler;
016: import org.eclipse.core.commands.ExecutionEvent;
017: import org.eclipse.core.commands.ExecutionException;
018: import org.eclipse.core.runtime.IConfigurationElement;
019: import org.eclipse.core.runtime.IExecutableExtension;
020: import org.eclipse.swt.SWT;
021: import org.eclipse.swt.widgets.Composite;
022: import org.eclipse.swt.widgets.Control;
023: import org.eclipse.swt.widgets.Display;
024: import org.eclipse.ui.internal.ExceptionHandler;
025:
026: /**
027: * Handles the cut command in both dialogs and windows. This handler is enabled
028: * if the focus control supports the "cut" method.
029: *
030: * @since 3.0
031: */
032: public class WidgetMethodHandler extends AbstractHandler implements
033: IExecutableExtension {
034:
035: /**
036: * The parameters to pass to the method this handler invokes. This handler
037: * always passes no parameters.
038: */
039: protected static final Class[] NO_PARAMETERS = new Class[0];
040:
041: /**
042: * The name of the method to be invoked by this handler. This value should
043: * never be <code>null</code>.
044: */
045: protected String methodName;
046:
047: public Object execute(final ExecutionEvent event)
048: throws ExecutionException {
049: final Method methodToExecute = getMethodToExecute();
050: if (methodToExecute != null) {
051: try {
052: final Control focusControl = Display.getCurrent()
053: .getFocusControl();
054: if ((focusControl instanceof Composite)
055: && ((((Composite) focusControl).getStyle() & SWT.EMBEDDED) != 0)) {
056: /*
057: * Okay. Have a seat. Relax a while. This is going to be a
058: * bumpy ride. If it is an embedded widget, then it *might*
059: * be a Swing widget. At the point where this handler is
060: * executing, the key event is already bound to be
061: * swallowed. If I don't do something, then the key will be
062: * gone for good. So, I will try to forward the event to the
063: * Swing widget. Unfortunately, we can't even count on the
064: * Swing libraries existing, so I need to use reflection
065: * everywhere. And, to top it off, I need to dispatch the
066: * event on the Swing event queue, which means that it will
067: * be carried out asynchronously to the SWT event queue.
068: */
069: try {
070: final Object focusComponent = getFocusComponent();
071: if (focusComponent != null) {
072: Runnable methodRunnable = new Runnable() {
073: public void run() {
074: try {
075: methodToExecute.invoke(
076: focusComponent, null);
077: } catch (final IllegalAccessException e) {
078: // The method is protected, so do
079: // nothing.
080: } catch (final InvocationTargetException e) {
081: /*
082: * I would like to log this exception --
083: * and possibly show a dialog to the
084: * user -- but I have to go back to the
085: * SWT event loop to do this. So, back
086: * we go....
087: */
088: focusControl.getDisplay()
089: .asyncExec(
090: new Runnable() {
091: public void run() {
092: ExceptionHandler
093: .getInstance()
094: .handleException(
095: new ExecutionException(
096: "An exception occurred while executing " //$NON-NLS-1$
097: + methodToExecute
098: .getName(),
099: e
100: .getTargetException()));
101: }
102: });
103: }
104: }
105: };
106:
107: swingInvokeLater(methodRunnable);
108: }
109: } catch (final ClassNotFoundException e) {
110: // There is no Swing support, so do nothing.
111:
112: } catch (final NoSuchMethodException e) {
113: // The API has changed, which seems amazingly unlikely.
114: throw new Error(
115: "Something is seriously wrong here"); //$NON-NLS-1$
116: }
117:
118: } else {
119:
120: methodToExecute.invoke(focusControl, null);
121: }
122:
123: } catch (IllegalAccessException e) {
124: // The method is protected, so do nothing.
125:
126: } catch (InvocationTargetException e) {
127: throw new ExecutionException(
128: "An exception occurred while executing " //$NON-NLS-1$
129: + methodToExecute.getName(), e
130: .getTargetException());
131:
132: }
133: }
134:
135: return null;
136: }
137:
138: /**
139: * Invoke a runnable on the swing EDT.
140: *
141: * @param methodRunnable
142: * @throws ClassNotFoundException
143: * @throws NoSuchMethodException
144: * @throws IllegalAccessException
145: * @throws InvocationTargetException
146: */
147: protected void swingInvokeLater(Runnable methodRunnable)
148: throws ClassNotFoundException, NoSuchMethodException,
149: IllegalAccessException, InvocationTargetException {
150: final Class swingUtilitiesClass = Class
151: .forName("javax.swing.SwingUtilities"); //$NON-NLS-1$
152: final Method swingUtilitiesInvokeLaterMethod = swingUtilitiesClass
153: .getMethod("invokeLater", //$NON-NLS-1$
154: new Class[] { Runnable.class });
155: swingUtilitiesInvokeLaterMethod.invoke(swingUtilitiesClass,
156: new Object[] { methodRunnable });
157: }
158:
159: /**
160: * Find the swing focus component, if it is available.
161: *
162: * @return Hopefully, the swing focus component, but it can return <code>null</code>.
163: * @throws ClassNotFoundException
164: * @throws NoSuchMethodException
165: * @throws IllegalAccessException
166: * @throws InvocationTargetException
167: */
168: protected Object getFocusComponent() throws ClassNotFoundException,
169: NoSuchMethodException, IllegalAccessException,
170: InvocationTargetException {
171: final Class focusManagerClass = Class
172: .forName("javax.swing.FocusManager"); //$NON-NLS-1$
173: final Method focusManagerGetCurrentManagerMethod = focusManagerClass
174: .getMethod("getCurrentManager", null); //$NON-NLS-1$
175: final Object focusManager = focusManagerGetCurrentManagerMethod
176: .invoke(focusManagerClass, null);
177: final Method focusManagerGetFocusOwner = focusManagerClass
178: .getMethod("getFocusOwner", null); //$NON-NLS-1$
179: final Object focusComponent = focusManagerGetFocusOwner.invoke(
180: focusManager, null);
181: return focusComponent;
182: }
183:
184: public final boolean isEnabled() {
185: return getMethodToExecute() != null;
186: }
187:
188: /**
189: * Looks up the method on the focus control.
190: *
191: * @return The method on the focus control; <code>null</code> if none.
192: */
193: protected Method getMethodToExecute() {
194: final Control focusControl = Display.getCurrent()
195: .getFocusControl();
196: Method method = null;
197:
198: if (focusControl != null) {
199: final Class clazz = focusControl.getClass();
200: try {
201: method = clazz.getMethod(methodName, NO_PARAMETERS);
202: } catch (NoSuchMethodException e) {
203: // Fall through...
204: }
205: }
206:
207: if ((method == null)
208: && (focusControl instanceof Composite)
209: && ((((Composite) focusControl).getStyle() & SWT.EMBEDDED) != 0)) {
210: /*
211: * We couldn't find the appropriate method on the current focus
212: * control. It is possible that the current focus control is an
213: * embedded SWT composite, which could be containing some Swing
214: * components. If this is the case, then we should try to pass
215: * through to the underlying Swing component hierarchy. Insha'allah,
216: * this will work.
217: */
218: try {
219: final Object focusComponent = getFocusComponent();
220: if (focusComponent != null) {
221: final Class clazz = focusComponent.getClass();
222:
223: try {
224: method = clazz.getMethod(methodName,
225: NO_PARAMETERS);
226: } catch (NoSuchMethodException e) {
227: // Do nothing.
228: }
229: }
230: } catch (final ClassNotFoundException e) {
231: // There is no Swing support, so do nothing.
232:
233: } catch (final NoSuchMethodException e) {
234: // The API has changed, which seems amazingly unlikely.
235: throw new Error("Something is seriously wrong here"); //$NON-NLS-1$
236: } catch (IllegalAccessException e) {
237: // The API has changed, which seems amazingly unlikely.
238: throw new Error("Something is seriously wrong here"); //$NON-NLS-1$
239: } catch (InvocationTargetException e) {
240: // The API has changed, which seems amazingly unlikely.
241: throw new Error("Something is seriously wrong here"); //$NON-NLS-1$
242: }
243: }
244:
245: return method;
246: }
247:
248: /*
249: * (non-Javadoc)
250: *
251: * @see org.eclipse.core.runtime.IExecutableExtension#setInitializationData(org.eclipse.core.runtime.IConfigurationElement,
252: * java.lang.String, java.lang.Object)
253: */
254: public void setInitializationData(IConfigurationElement config,
255: String propertyName, Object data) {
256: // The data is really just a string (i.e., the method name).
257: methodName = data.toString();
258: }
259: }
|