Merged gcj-eclipse branch to trunk.
[official-gcc.git] / libjava / classpath / javax / swing / TransferHandler.java
blobd594a8244bb2bd8213e369f7b12c2ab0c2f031b6
1 /* TransferHandler.java --
2 Copyright (C) 2004, 2005, 2006, Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
39 package javax.swing;
41 import gnu.classpath.NotImplementedException;
43 import java.awt.Toolkit;
44 import java.awt.datatransfer.Clipboard;
45 import java.awt.datatransfer.DataFlavor;
46 import java.awt.datatransfer.Transferable;
47 import java.awt.datatransfer.UnsupportedFlavorException;
48 import java.awt.event.ActionEvent;
49 import java.awt.event.InputEvent;
50 import java.beans.BeanInfo;
51 import java.beans.IntrospectionException;
52 import java.beans.Introspector;
53 import java.beans.PropertyDescriptor;
54 import java.io.IOException;
55 import java.io.Serializable;
56 import java.lang.reflect.Method;
58 public class TransferHandler implements Serializable
61 /**
62 * An implementation of {@link Transferable} that can be used to export
63 * data from a component's property.
65 private static class PropertyTransferable
66 implements Transferable
68 /**
69 * The component from which we export.
71 private JComponent component;
73 /**
74 * The property descriptor of the property that we handle.
76 private PropertyDescriptor property;
78 /**
79 * Creates a new PropertyTransferable.
81 * @param c the component from which we export
82 * @param prop the property from which we export
84 PropertyTransferable(JComponent c, PropertyDescriptor prop)
86 component = c;
87 property = prop;
90 /**
91 * Returns the data flavors supported by the Transferable.
93 * @return the data flavors supported by the Transferable
95 public DataFlavor[] getTransferDataFlavors()
97 DataFlavor[] flavors;
98 Class propClass = property.getPropertyType();
99 String mime = DataFlavor.javaJVMLocalObjectMimeType + "; class="
100 + propClass.getName();
103 DataFlavor flavor = new DataFlavor(mime);
104 flavors = new DataFlavor[]{ flavor };
106 catch (ClassNotFoundException ex)
108 flavors = new DataFlavor[0];
110 return flavors;
114 * Returns <code>true</code> when the specified data flavor is supported,
115 * <code>false</code> otherwise.
117 * @return <code>true</code> when the specified data flavor is supported,
118 * <code>false</code> otherwise
120 public boolean isDataFlavorSupported(DataFlavor flavor)
122 Class propClass = property.getPropertyType();
123 return flavor.getPrimaryType().equals("application")
124 && flavor.getSubType().equals("x-java-jvm-local-objectref")
125 && propClass.isAssignableFrom(flavor.getRepresentationClass());
129 * Returns the actual transfer data.
131 * @param flavor the data flavor
133 * @return the actual transfer data
135 public Object getTransferData(DataFlavor flavor)
136 throws UnsupportedFlavorException, IOException
138 if (isDataFlavorSupported(flavor))
140 Method getter = property.getReadMethod();
141 Object o;
144 o = getter.invoke(component, null);
145 return o;
147 catch (Exception ex)
149 throw new IOException("Property read failed: "
150 + property.getName());
153 else
154 throw new UnsupportedFlavorException(flavor);
158 static class TransferAction extends AbstractAction
160 private String command;
162 public TransferAction(String command)
164 super(command);
165 this.command = command;
168 public void actionPerformed(ActionEvent event)
170 JComponent component = (JComponent) event.getSource();
171 TransferHandler transferHandler = component.getTransferHandler();
172 Clipboard clipboard = getClipboard(component);
174 if (clipboard == null)
176 // Access denied!
177 Toolkit.getDefaultToolkit().beep();
178 return;
181 if (command.equals(COMMAND_COPY))
182 transferHandler.exportToClipboard(component, clipboard, COPY);
183 else if (command.equals(COMMAND_CUT))
184 transferHandler.exportToClipboard(component, clipboard, MOVE);
185 else if (command.equals(COMMAND_PASTE))
187 Transferable transferable = clipboard.getContents(null);
189 if (transferable != null)
190 transferHandler.importData(component, transferable);
195 * Get the system cliboard or null if the caller isn't allowed to
196 * access the system clipboard.
198 * @param component a component, used to get the toolkit.
199 * @return the clipboard
201 private static Clipboard getClipboard(JComponent component)
205 return component.getToolkit().getSystemClipboard();
207 catch (SecurityException se)
209 return null;
214 private static final long serialVersionUID = -967749805571669910L;
216 private static final String COMMAND_COPY = "copy";
217 private static final String COMMAND_CUT = "cut";
218 private static final String COMMAND_PASTE = "paste";
220 public static final int NONE = 0;
221 public static final int COPY = 1;
222 public static final int MOVE = 2;
223 public static final int COPY_OR_MOVE = 3;
225 private static Action copyAction = new TransferAction(COMMAND_COPY);
226 private static Action cutAction = new TransferAction(COMMAND_CUT);
227 private static Action pasteAction = new TransferAction(COMMAND_PASTE);
229 private int sourceActions;
230 private Icon visualRepresentation;
233 * The name of the property into/from which this TransferHandler
234 * imports/exports.
236 private String propertyName;
238 public static Action getCopyAction()
240 return copyAction;
243 public static Action getCutAction()
245 return cutAction;
248 public static Action getPasteAction()
250 return pasteAction;
253 protected TransferHandler()
255 this.sourceActions = NONE;
258 public TransferHandler(String property)
260 propertyName = property;
261 this.sourceActions = property != null ? COPY : NONE;
265 * Returns <code>true</code> if the data in this TransferHandler can be
266 * imported into the specified component. This will be the case when:
267 * <ul>
268 * <li>The component has a readable and writable property with the property
269 * name specified in the TransferHandler constructor.</li>
270 * <li>There is a dataflavor with a mime type of
271 * <code>application/x-java-jvm-local-object-ref</code>.</li>
272 * <li>The dataflavor's representation class matches the class of the
273 * property in the component.</li>
274 * </li>
276 * @param c the component to check
277 * @param flavors the possible data flavors
279 * @return <code>true</code> if the data in this TransferHandler can be
280 * imported into the specified component, <code>false</code>
281 * otherwise
283 public boolean canImport(JComponent c, DataFlavor[] flavors)
285 PropertyDescriptor propDesc = getPropertyDescriptor(c);
286 boolean canImport = false;
287 if (propDesc != null)
289 // Check if the property is writable. The readable check is already
290 // done in getPropertyDescriptor().
291 Method writer = propDesc.getWriteMethod();
292 if (writer != null)
294 Class[] params = writer.getParameterTypes();
295 if (params.length == 1)
297 // Number of parameters ok, now check mime type and
298 // representation class.
299 DataFlavor flavor = getPropertyDataFlavor(params[0], flavors);
300 if (flavor != null)
301 canImport = true;
305 return canImport;
309 * Creates a {@link Transferable} that can be used to export data
310 * from the specified component.
312 * This method returns <code>null</code> when the specified component
313 * doesn't have a readable property that matches the property name
314 * specified in the <code>TransferHandler</code> constructor.
316 * @param c the component to create a transferable for
318 * @return a {@link Transferable} that can be used to export data
319 * from the specified component, or null if the component doesn't
320 * have a readable property like the transfer handler
322 protected Transferable createTransferable(JComponent c)
324 Transferable transferable = null;
325 if (propertyName != null)
327 PropertyDescriptor prop = getPropertyDescriptor(c);
328 if (prop != null)
329 transferable = new PropertyTransferable(c, prop);
331 return transferable;
334 public void exportAsDrag(JComponent c, InputEvent e, int action)
335 throws NotImplementedException
337 // TODO: Implement this properly
341 * This method is invoked after data has been exported.
342 * Subclasses should implement this method to remove the data that has been
343 * transferred when the action was <code>MOVE</code>.
345 * The default implementation does nothing because MOVE is not supported.
347 * @param c the source component
348 * @param data the data that has been transferred or <code>null</code>
349 * when the action is NONE
350 * @param action the action that has been performed
352 protected void exportDone(JComponent c, Transferable data, int action)
354 // Nothing to do in the default implementation.
358 * Exports the property of the component <code>c</code> that was
359 * specified for this TransferHandler to the clipboard, performing
360 * the specified action.
362 * This will check if the action is allowed by calling
363 * {@link #getSourceActions(JComponent)}. If the action is not allowed,
364 * then no export is performed.
366 * In either case the method {@link #exportDone} will be called with
367 * the action that has been performed, or {@link #NONE} if the action
368 * was not allowed or could otherwise not be completed.
369 * Any IllegalStateException that is thrown by the Clipboard due to
370 * beeing unavailable will be propagated through this method.
372 * @param c the component from which to export
373 * @param clip the clipboard to which the data will be exported
374 * @param action the action to perform
376 * @throws IllegalStateException when the clipboard is not available
378 public void exportToClipboard(JComponent c, Clipboard clip, int action)
379 throws IllegalStateException
381 action &= getSourceActions(c);
382 Transferable transferable = createTransferable(c);
383 if (transferable != null && action != NONE)
387 clip.setContents(transferable, null);
388 exportDone(c, transferable, action);
390 catch (IllegalStateException ex)
392 exportDone(c, transferable, NONE);
393 throw ex;
396 else
397 exportDone(c, null, NONE);
400 public int getSourceActions(JComponent c)
402 return sourceActions;
405 public Icon getVisualRepresentation(Transferable t)
407 return visualRepresentation;
411 * Imports the transfer data represented by <code>t</code> into the specified
412 * component <code>c</code> by setting the property of this TransferHandler
413 * on that component. If this succeeds, this method returns
414 * <code>true</code>, otherwise <code>false</code>.
417 * @param c the component to import into
418 * @param t the transfer data to import
420 * @return <code>true</code> if the transfer succeeds, <code>false</code>
421 * otherwise
423 public boolean importData(JComponent c, Transferable t)
425 boolean ok = false;
426 PropertyDescriptor prop = getPropertyDescriptor(c);
427 if (prop != null)
429 Method writer = prop.getWriteMethod();
430 if (writer != null)
432 Class[] params = writer.getParameterTypes();
433 if (params.length == 1)
435 DataFlavor flavor = getPropertyDataFlavor(params[0],
436 t.getTransferDataFlavors());
437 if (flavor != null)
441 Object value = t.getTransferData(flavor);
442 writer.invoke(c, new Object[]{ value });
443 ok = true;
445 catch (Exception ex)
447 // If anything goes wrong here, do nothing and return
448 // false;
454 return ok;
458 * Returns the property descriptor for the property of this TransferHandler
459 * in the specified component, or <code>null</code> if no such property
460 * exists in the component. This method only returns properties that are
461 * at least readable (that is, it has a public no-arg getter method).
463 * @param c the component to check
465 * @return the property descriptor for the property of this TransferHandler
466 * in the specified component, or <code>null</code> if no such
467 * property exists in the component
469 private PropertyDescriptor getPropertyDescriptor(JComponent c)
471 PropertyDescriptor prop = null;
472 if (propertyName != null)
474 Class clazz = c.getClass();
475 BeanInfo beanInfo;
478 beanInfo = Introspector.getBeanInfo(clazz);
480 catch (IntrospectionException ex)
482 beanInfo = null;
484 if (beanInfo != null)
486 PropertyDescriptor[] props = beanInfo.getPropertyDescriptors();
487 for (int i = 0; i < props.length && prop == null; i++)
489 PropertyDescriptor desc = props[i];
490 if (desc.getName().equals(propertyName))
492 Method reader = desc.getReadMethod();
493 if (reader != null)
495 Class[] params = reader.getParameterTypes();
496 if (params == null || params.length == 0)
497 prop = desc;
503 return prop;
507 * Searches <code>flavors</code> to find a suitable data flavor that
508 * has the mime type application/x-java-jvm-local-objectref and a
509 * representation class that is the same as the specified <code>clazz</code>.
510 * When no such data flavor is found, this returns <code>null</code>.
512 * @param clazz the representation class required for the data flavor
513 * @param flavors the possible data flavors
515 * @return the suitable data flavor or null if none is found
517 private DataFlavor getPropertyDataFlavor(Class clazz, DataFlavor[] flavors)
519 DataFlavor found = null;
520 for (int i = 0; i < flavors.length && found == null; i++)
522 DataFlavor flavor = flavors[i];
523 if (flavor.getPrimaryType().equals("application")
524 && flavor.getSubType().equals("x-java-jvm-local-objectref")
525 && clazz.isAssignableFrom(flavor.getRepresentationClass()))
526 found = flavor;
528 return found;