source: josm/trunk/src/org/openstreetmap/josm/gui/FileDrop.java@ 2587

Last change on this file since 2587 was 2512, checked in by stoecker, 14 years ago

i18n updated, fixed files to reduce problems when applying patches, fix #4017

File size: 36.1 KB
Line 
1/* code from: http://iharder.sourceforge.net/current/java/filedrop/
2 (public domain) with only very small additions */
3package org.openstreetmap.josm.gui;
4
5import java.awt.datatransfer.DataFlavor;
6import java.io.BufferedReader;
7import java.io.File;
8import java.io.IOException;
9import java.io.PrintStream;
10import java.io.Reader;
11import java.util.Arrays;
12import java.util.List;
13
14import javax.swing.BorderFactory;
15
16import org.openstreetmap.josm.Main;
17import org.openstreetmap.josm.actions.OpenFileAction;
18
19import org.openstreetmap.josm.gui.FileDrop.TransferableObject;
20
21/**
22 * This class makes it easy to drag and drop files from the operating
23 * system to a Java program. Any <tt>java.awt.Component</tt> can be
24 * dropped onto, but only <tt>javax.swing.JComponent</tt>s will indicate
25 * the drop event with a changed border.
26 * <p/>
27 * To use this class, construct a new <tt>FileDrop</tt> by passing
28 * it the target component and a <tt>Listener</tt> to receive notification
29 * when file(s) have been dropped. Here is an example:
30 * <p/>
31 * <code><pre>
32 * JPanel myPanel = new JPanel();
33 * new FileDrop( myPanel, new FileDrop.Listener()
34 * { public void filesDropped( java.io.File[] files )
35 * {
36 * // handle file drop
37 * ...
38 * } // end filesDropped
39 * }); // end FileDrop.Listener
40 * </pre></code>
41 * <p/>
42 * You can specify the border that will appear when files are being dragged by
43 * calling the constructor with a <tt>javax.swing.border.Border</tt>. Only
44 * <tt>JComponent</tt>s will show any indication with a border.
45 * <p/>
46 * You can turn on some debugging features by passing a <tt>PrintStream</tt>
47 * object (such as <tt>System.out</tt>) into the full constructor. A <tt>null</tt>
48 * value will result in no extra debugging information being output.
49 * <p/>
50 *
51 * <p>I'm releasing this code into the Public Domain. Enjoy.
52 * </p>
53 * <p><em>Original author: Robert Harder, rharder@usa.net</em></p>
54 * <p>2007-09-12 Nathan Blomquist -- Linux (KDE/Gnome) support added.</p>
55 *
56 * @author Robert Harder
57 * @author rharder@users.sf.net
58 * @version 1.0.1
59 */
60public class FileDrop
61{
62 private transient javax.swing.border.Border normalBorder;
63 private transient java.awt.dnd.DropTargetListener dropListener;
64
65 /** Discover if the running JVM is modern enough to have drag and drop. */
66 private static Boolean supportsDnD;
67
68 // Default border color
69 private static java.awt.Color defaultBorderColor = new java.awt.Color( 0f, 0f, 1f, 0.25f );
70
71 /* Constructor for JOSM file drop */
72 public FileDrop(final java.awt.Component c){
73 this(
74 null, // Logging stream
75 c, // Drop target
76 BorderFactory.createMatteBorder( 2, 2, 2, 2, defaultBorderColor ), // Drag border
77 true, // Recursive
78 new FileDrop.Listener(){
79 public void filesDropped( java.io.File[] files ){
80 // start asynchronous loading of files
81 OpenFileAction.OpenFileTask task = new OpenFileAction.OpenFileTask(Arrays.asList(files));
82 Main.worker.submit(task);
83 }
84 }
85 );
86 }
87
88 /**
89 * Constructs a {@link FileDrop} with a default light-blue border
90 * and, if <var>c</var> is a {@link java.awt.Container}, recursively
91 * sets all elements contained within as drop targets, though only
92 * the top level container will change borders.
93 *
94 * @param c Component on which files will be dropped.
95 * @param listener Listens for <tt>filesDropped</tt>.
96 * @since 1.0
97 */
98 public FileDrop(
99 final java.awt.Component c,
100 final Listener listener )
101 { this( null, // Logging stream
102 c, // Drop target
103 javax.swing.BorderFactory.createMatteBorder( 2, 2, 2, 2, defaultBorderColor ), // Drag border
104 true, // Recursive
105 listener );
106 } // end constructor
107
108 /**
109 * Constructor with a default border and the option to recursively set drop targets.
110 * If your component is a <tt>java.awt.Container</tt>, then each of its children
111 * components will also listen for drops, though only the parent will change borders.
112 *
113 * @param c Component on which files will be dropped.
114 * @param recursive Recursively set children as drop targets.
115 * @param listener Listens for <tt>filesDropped</tt>.
116 * @since 1.0
117 */
118 public FileDrop(
119 final java.awt.Component c,
120 final boolean recursive,
121 final Listener listener )
122 { this( null, // Logging stream
123 c, // Drop target
124 javax.swing.BorderFactory.createMatteBorder( 2, 2, 2, 2, defaultBorderColor ), // Drag border
125 recursive, // Recursive
126 listener );
127 } // end constructor
128
129 /**
130 * Constructor with a default border and debugging optionally turned on.
131 * With Debugging turned on, more status messages will be displayed to
132 * <tt>out</tt>. A common way to use this constructor is with
133 * <tt>System.out</tt> or <tt>System.err</tt>. A <tt>null</tt> value for
134 * the parameter <tt>out</tt> will result in no debugging output.
135 *
136 * @param out PrintStream to record debugging info or null for no debugging.
137 * @param out
138 * @param c Component on which files will be dropped.
139 * @param listener Listens for <tt>filesDropped</tt>.
140 * @since 1.0
141 */
142 public FileDrop(
143 final java.io.PrintStream out,
144 final java.awt.Component c,
145 final Listener listener )
146 { this( out, // Logging stream
147 c, // Drop target
148 javax.swing.BorderFactory.createMatteBorder( 2, 2, 2, 2, defaultBorderColor ),
149 false, // Recursive
150 listener );
151 } // end constructor
152
153 /**
154 * Constructor with a default border, debugging optionally turned on
155 * and the option to recursively set drop targets.
156 * If your component is a <tt>java.awt.Container</tt>, then each of its children
157 * components will also listen for drops, though only the parent will change borders.
158 * With Debugging turned on, more status messages will be displayed to
159 * <tt>out</tt>. A common way to use this constructor is with
160 * <tt>System.out</tt> or <tt>System.err</tt>. A <tt>null</tt> value for
161 * the parameter <tt>out</tt> will result in no debugging output.
162 *
163 * @param out PrintStream to record debugging info or null for no debugging.
164 * @param out
165 * @param c Component on which files will be dropped.
166 * @param recursive Recursively set children as drop targets.
167 * @param listener Listens for <tt>filesDropped</tt>.
168 * @since 1.0
169 */
170 public FileDrop(
171 final java.io.PrintStream out,
172 final java.awt.Component c,
173 final boolean recursive,
174 final Listener listener)
175 { this( out, // Logging stream
176 c, // Drop target
177 javax.swing.BorderFactory.createMatteBorder( 2, 2, 2, 2, defaultBorderColor ), // Drag border
178 recursive, // Recursive
179 listener );
180 } // end constructor
181
182 /**
183 * Constructor with a specified border
184 *
185 * @param c Component on which files will be dropped.
186 * @param dragBorder Border to use on <tt>JComponent</tt> when dragging occurs.
187 * @param listener Listens for <tt>filesDropped</tt>.
188 * @since 1.0
189 */
190 public FileDrop(
191 final java.awt.Component c,
192 final javax.swing.border.Border dragBorder,
193 final Listener listener)
194 { this(
195 null, // Logging stream
196 c, // Drop target
197 dragBorder, // Drag border
198 false, // Recursive
199 listener );
200 } // end constructor
201
202 /**
203 * Constructor with a specified border and the option to recursively set drop targets.
204 * If your component is a <tt>java.awt.Container</tt>, then each of its children
205 * components will also listen for drops, though only the parent will change borders.
206 *
207 * @param c Component on which files will be dropped.
208 * @param dragBorder Border to use on <tt>JComponent</tt> when dragging occurs.
209 * @param recursive Recursively set children as drop targets.
210 * @param listener Listens for <tt>filesDropped</tt>.
211 * @since 1.0
212 */
213 public FileDrop(
214 final java.awt.Component c,
215 final javax.swing.border.Border dragBorder,
216 final boolean recursive,
217 final Listener listener)
218 { this(
219 null,
220 c,
221 dragBorder,
222 recursive,
223 listener );
224 } // end constructor
225
226 /**
227 * Constructor with a specified border and debugging optionally turned on.
228 * With Debugging turned on, more status messages will be displayed to
229 * <tt>out</tt>. A common way to use this constructor is with
230 * <tt>System.out</tt> or <tt>System.err</tt>. A <tt>null</tt> value for
231 * the parameter <tt>out</tt> will result in no debugging output.
232 *
233 * @param out PrintStream to record debugging info or null for no debugging.
234 * @param c Component on which files will be dropped.
235 * @param dragBorder Border to use on <tt>JComponent</tt> when dragging occurs.
236 * @param listener Listens for <tt>filesDropped</tt>.
237 * @since 1.0
238 */
239 public FileDrop(
240 final java.io.PrintStream out,
241 final java.awt.Component c,
242 final javax.swing.border.Border dragBorder,
243 final Listener listener)
244 { this(
245 out, // Logging stream
246 c, // Drop target
247 dragBorder, // Drag border
248 false, // Recursive
249 listener );
250 } // end constructor
251
252 /**
253 * Full constructor with a specified border and debugging optionally turned on.
254 * With Debugging turned on, more status messages will be displayed to
255 * <tt>out</tt>. A common way to use this constructor is with
256 * <tt>System.out</tt> or <tt>System.err</tt>. A <tt>null</tt> value for
257 * the parameter <tt>out</tt> will result in no debugging output.
258 *
259 * @param out PrintStream to record debugging info or null for no debugging.
260 * @param c Component on which files will be dropped.
261 * @param dragBorder Border to use on <tt>JComponent</tt> when dragging occurs.
262 * @param recursive Recursively set children as drop targets.
263 * @param listener Listens for <tt>filesDropped</tt>.
264 * @since 1.0
265 */
266 public FileDrop(
267 final java.io.PrintStream out,
268 final java.awt.Component c,
269 final javax.swing.border.Border dragBorder,
270 final boolean recursive,
271 final Listener listener)
272 {
273
274 if( supportsDnD() )
275 { // Make a drop listener
276 dropListener = new java.awt.dnd.DropTargetListener()
277 { public void dragEnter( java.awt.dnd.DropTargetDragEvent evt )
278 { log( out, "FileDrop: dragEnter event." );
279
280 // Is this an acceptable drag event?
281 if( isDragOk( out, evt ) )
282 {
283 // If it's a Swing component, set its border
284 if( c instanceof javax.swing.JComponent )
285 { javax.swing.JComponent jc = (javax.swing.JComponent) c;
286 normalBorder = jc.getBorder();
287 log( out, "FileDrop: normal border saved." );
288 jc.setBorder( dragBorder );
289 log( out, "FileDrop: drag border set." );
290 } // end if: JComponent
291
292 // Acknowledge that it's okay to enter
293 //evt.acceptDrag( java.awt.dnd.DnDConstants.ACTION_COPY_OR_MOVE );
294 evt.acceptDrag( java.awt.dnd.DnDConstants.ACTION_COPY );
295 log( out, "FileDrop: event accepted." );
296 } // end if: drag ok
297 else
298 { // Reject the drag event
299 evt.rejectDrag();
300 log( out, "FileDrop: event rejected." );
301 } // end else: drag not ok
302 } // end dragEnter
303
304 public void dragOver( java.awt.dnd.DropTargetDragEvent evt )
305 { // This is called continually as long as the mouse is
306 // over the drag target.
307 } // end dragOver
308
309 public void drop( java.awt.dnd.DropTargetDropEvent evt )
310 { log( out, "FileDrop: drop event." );
311 try
312 { // Get whatever was dropped
313 java.awt.datatransfer.Transferable tr = evt.getTransferable();
314
315 // Is it a file list?
316 if (tr.isDataFlavorSupported (java.awt.datatransfer.DataFlavor.javaFileListFlavor))
317 {
318 // Say we'll take it.
319 //evt.acceptDrop ( java.awt.dnd.DnDConstants.ACTION_COPY_OR_MOVE );
320 evt.acceptDrop ( java.awt.dnd.DnDConstants.ACTION_COPY );
321 log( out, "FileDrop: file list accepted." );
322
323 // Get a useful list
324 List fileList = (java.util.List)
325 tr.getTransferData(java.awt.datatransfer.DataFlavor.javaFileListFlavor);
326
327 // Convert list to array
328 final File[] files = (File[]) fileList.toArray();
329
330 // Alert listener to drop.
331 if( listener != null ) {
332 listener.filesDropped( files );
333 }
334
335 // Mark that drop is completed.
336 evt.getDropTargetContext().dropComplete(true);
337 log( out, "FileDrop: drop complete." );
338 } // end if: file list
339 else // this section will check for a reader flavor.
340 {
341 // Thanks, Nathan!
342 // BEGIN 2007-09-12 Nathan Blomquist -- Linux (KDE/Gnome) support added.
343 DataFlavor[] flavors = tr.getTransferDataFlavors();
344 boolean handled = false;
345 for (int zz = 0; zz < flavors.length; zz++) {
346 if (flavors[zz].isRepresentationClassReader()) {
347 // Say we'll take it.
348 //evt.acceptDrop ( java.awt.dnd.DnDConstants.ACTION_COPY_OR_MOVE );
349 evt.acceptDrop(java.awt.dnd.DnDConstants.ACTION_COPY);
350 log(out, "FileDrop: reader accepted.");
351
352 Reader reader = flavors[zz].getReaderForText(tr);
353
354 BufferedReader br = new BufferedReader(reader);
355
356 if(listener != null) {
357 listener.filesDropped(createFileArray(br, out));
358 }
359
360 // Mark that drop is completed.
361 evt.getDropTargetContext().dropComplete(true);
362 log(out, "FileDrop: drop complete.");
363 handled = true;
364 break;
365 }
366 }
367 if(!handled){
368 log( out, "FileDrop: not a file list or reader - abort." );
369 evt.rejectDrop();
370 }
371 // END 2007-09-12 Nathan Blomquist -- Linux (KDE/Gnome) support added.
372 } // end else: not a file list
373 } // end try
374 catch ( java.io.IOException io)
375 { log( out, "FileDrop: IOException - abort:" );
376 io.printStackTrace( out );
377 evt.rejectDrop();
378 } // end catch IOException
379 catch (java.awt.datatransfer.UnsupportedFlavorException ufe)
380 { log( out, "FileDrop: UnsupportedFlavorException - abort:" );
381 ufe.printStackTrace( out );
382 evt.rejectDrop();
383 } // end catch: UnsupportedFlavorException
384 finally
385 {
386 // If it's a Swing component, reset its border
387 if( c instanceof javax.swing.JComponent )
388 { javax.swing.JComponent jc = (javax.swing.JComponent) c;
389 jc.setBorder( normalBorder );
390 log( out, "FileDrop: normal border restored." );
391 } // end if: JComponent
392 } // end finally
393 } // end drop
394
395 public void dragExit( java.awt.dnd.DropTargetEvent evt )
396 { log( out, "FileDrop: dragExit event." );
397 // If it's a Swing component, reset its border
398 if( c instanceof javax.swing.JComponent )
399 { javax.swing.JComponent jc = (javax.swing.JComponent) c;
400 jc.setBorder( normalBorder );
401 log( out, "FileDrop: normal border restored." );
402 } // end if: JComponent
403 } // end dragExit
404
405 public void dropActionChanged( java.awt.dnd.DropTargetDragEvent evt )
406 { log( out, "FileDrop: dropActionChanged event." );
407 // Is this an acceptable drag event?
408 if( isDragOk( out, evt ) )
409 { //evt.acceptDrag( java.awt.dnd.DnDConstants.ACTION_COPY_OR_MOVE );
410 evt.acceptDrag( java.awt.dnd.DnDConstants.ACTION_COPY );
411 log( out, "FileDrop: event accepted." );
412 } // end if: drag ok
413 else
414 { evt.rejectDrag();
415 log( out, "FileDrop: event rejected." );
416 } // end else: drag not ok
417 } // end dropActionChanged
418 }; // end DropTargetListener
419
420 // Make the component (and possibly children) drop targets
421 makeDropTarget( out, c, recursive );
422 } // end if: supports dnd
423 else
424 { log( out, "FileDrop: Drag and drop is not supported with this JVM" );
425 } // end else: does not support DnD
426 } // end constructor
427
428 private static boolean supportsDnD()
429 { // Static Boolean
430 if( supportsDnD == null )
431 {
432 boolean support = false;
433 try
434 { Class arbitraryDndClass = Class.forName( "java.awt.dnd.DnDConstants" );
435 support = true;
436 } // end try
437 catch( Exception e )
438 { support = false;
439 } // end catch
440 supportsDnD = new Boolean( support );
441 } // end if: first time through
442 return supportsDnD.booleanValue();
443 } // end supportsDnD
444
445 // BEGIN 2007-09-12 Nathan Blomquist -- Linux (KDE/Gnome) support added.
446 private static String ZERO_CHAR_STRING = "" + (char)0;
447 private static File[] createFileArray(BufferedReader bReader, PrintStream out)
448 {
449 try {
450 java.util.List<File> list = new java.util.ArrayList<File>();
451 java.lang.String line = null;
452 while ((line = bReader.readLine()) != null) {
453 try {
454 // kde seems to append a 0 char to the end of the reader
455 if(ZERO_CHAR_STRING.equals(line)) {
456 continue;
457 }
458
459 java.io.File file = new java.io.File(new java.net.URI(line));
460 list.add(file);
461 } catch (Exception ex) {
462 log(out, "Error with " + line + ": " + ex.getMessage());
463 }
464 }
465
466 return list.toArray(new File[list.size()]);
467 } catch (IOException ex) {
468 log(out, "FileDrop: IOException");
469 }
470 return new File[0];
471 }
472 // END 2007-09-12 Nathan Blomquist -- Linux (KDE/Gnome) support added.
473
474 private void makeDropTarget( final java.io.PrintStream out, final java.awt.Component c, boolean recursive )
475 {
476 // Make drop target
477 final java.awt.dnd.DropTarget dt = new java.awt.dnd.DropTarget();
478 try
479 { dt.addDropTargetListener( dropListener );
480 } // end try
481 catch( java.util.TooManyListenersException e )
482 { e.printStackTrace();
483 log(out, "FileDrop: Drop will not work due to previous error. Do you have another listener attached?" );
484 } // end catch
485
486 // Listen for hierarchy changes and remove the drop target when the parent gets cleared out.
487 c.addHierarchyListener( new java.awt.event.HierarchyListener()
488 { public void hierarchyChanged( java.awt.event.HierarchyEvent evt )
489 { log( out, "FileDrop: Hierarchy changed." );
490 java.awt.Component parent = c.getParent();
491 if( parent == null )
492 { c.setDropTarget( null );
493 log( out, "FileDrop: Drop target cleared from component." );
494 } // end if: null parent
495 else
496 { new java.awt.dnd.DropTarget(c, dropListener);
497 log( out, "FileDrop: Drop target added to component." );
498 } // end else: parent not null
499 } // end hierarchyChanged
500 }); // end hierarchy listener
501 if( c.getParent() != null ) {
502 new java.awt.dnd.DropTarget(c, dropListener);
503 }
504
505 if( recursive && (c instanceof java.awt.Container ) )
506 {
507 // Get the container
508 java.awt.Container cont = (java.awt.Container) c;
509
510 // Get it's components
511 java.awt.Component[] comps = cont.getComponents();
512
513 // Set it's components as listeners also
514 for( int i = 0; i < comps.length; i++ ) {
515 makeDropTarget( out, comps[i], recursive );
516 }
517 } // end if: recursively set components as listener
518 } // end dropListener
519
520 /** Determine if the dragged data is a file list. */
521 private boolean isDragOk( final java.io.PrintStream out, final java.awt.dnd.DropTargetDragEvent evt )
522 { boolean ok = false;
523
524 // Get data flavors being dragged
525 java.awt.datatransfer.DataFlavor[] flavors = evt.getCurrentDataFlavors();
526
527 // See if any of the flavors are a file list
528 int i = 0;
529 while( !ok && i < flavors.length )
530 {
531 // BEGIN 2007-09-12 Nathan Blomquist -- Linux (KDE/Gnome) support added.
532 // Is the flavor a file list?
533 final DataFlavor curFlavor = flavors[i];
534 if( curFlavor.equals( java.awt.datatransfer.DataFlavor.javaFileListFlavor ) ||
535 curFlavor.isRepresentationClassReader()){
536 ok = true;
537 }
538 // END 2007-09-12 Nathan Blomquist -- Linux (KDE/Gnome) support added.
539 i++;
540 } // end while: through flavors
541
542 // If logging is enabled, show data flavors
543 if( out != null )
544 { if( flavors.length == 0 ) {
545 log( out, "FileDrop: no data flavors." );
546 }
547 for( i = 0; i < flavors.length; i++ ) {
548 log( out, flavors[i].toString() );
549 }
550 } // end if: logging enabled
551
552 return ok;
553 } // end isDragOk
554
555 /** Outputs <tt>message</tt> to <tt>out</tt> if it's not null. */
556 private static void log( java.io.PrintStream out, String message )
557 { // Log message if requested
558 if( out != null ) {
559 out.println( message );
560 }
561 } // end log
562
563 /**
564 * Removes the drag-and-drop hooks from the component and optionally
565 * from the all children. You should call this if you add and remove
566 * components after you've set up the drag-and-drop.
567 * This will recursively unregister all components contained within
568 * <var>c</var> if <var>c</var> is a {@link java.awt.Container}.
569 *
570 * @param c The component to unregister as a drop target
571 * @since 1.0
572 */
573 public static boolean remove( java.awt.Component c)
574 { return remove( null, c, true );
575 } // end remove
576
577 /**
578 * Removes the drag-and-drop hooks from the component and optionally
579 * from the all children. You should call this if you add and remove
580 * components after you've set up the drag-and-drop.
581 *
582 * @param out Optional {@link java.io.PrintStream} for logging drag and drop messages
583 * @param c The component to unregister
584 * @param recursive Recursively unregister components within a container
585 * @since 1.0
586 */
587 public static boolean remove( java.io.PrintStream out, java.awt.Component c, boolean recursive )
588 { // Make sure we support dnd.
589 if( supportsDnD() )
590 { log( out, "FileDrop: Removing drag-and-drop hooks." );
591 c.setDropTarget( null );
592 if( recursive && ( c instanceof java.awt.Container ) )
593 { java.awt.Component[] comps = ((java.awt.Container)c).getComponents();
594 for( int i = 0; i < comps.length; i++ ) {
595 remove( out, comps[i], recursive );
596 }
597 return true;
598 } // end if: recursive
599 else return false;
600 } // end if: supports DnD
601 else return false;
602 } // end remove
603
604 /* ******** I N N E R I N T E R F A C E L I S T E N E R ******** */
605
606 /**
607 * Implement this inner interface to listen for when files are dropped. For example
608 * your class declaration may begin like this:
609 * <code><pre>
610 * public class MyClass implements FileDrop.Listener
611 * ...
612 * public void filesDropped( java.io.File[] files )
613 * {
614 * ...
615 * } // end filesDropped
616 * ...
617 * </pre></code>
618 *
619 * @since 1.1
620 */
621 public static interface Listener {
622
623 /**
624 * This method is called when files have been successfully dropped.
625 *
626 * @param files An array of <tt>File</tt>s that were dropped.
627 * @since 1.0
628 */
629 public abstract void filesDropped( java.io.File[] files );
630
631 } // end inner-interface Listener
632
633 /* ******** I N N E R C L A S S ******** */
634
635 /**
636 * This is the event that is passed to the
637 * {@link FileDropListener#filesDropped filesDropped(...)} method in
638 * your {@link FileDropListener} when files are dropped onto
639 * a registered drop target.
640 *
641 * <p>I'm releasing this code into the Public Domain. Enjoy.</p>
642 *
643 * @author Robert Harder
644 * @author rob@iharder.net
645 * @version 1.2
646 */
647 public static class Event extends java.util.EventObject {
648
649 private java.io.File[] files;
650
651 /**
652 * Constructs an {@link Event} with the array
653 * of files that were dropped and the
654 * {@link FileDrop} that initiated the event.
655 *
656 * @param files The array of files that were dropped
657 * @source The event source
658 * @since 1.1
659 */
660 public Event( java.io.File[] files, Object source ) {
661 super( source );
662 this.files = files;
663 } // end constructor
664
665 /**
666 * Returns an array of files that were dropped on a
667 * registered drop target.
668 *
669 * @return array of files that were dropped
670 * @since 1.1
671 */
672 public java.io.File[] getFiles() {
673 return files;
674 } // end getFiles
675
676 } // end inner class Event
677
678 /* ******** I N N E R C L A S S ******** */
679
680 /**
681 * At last an easy way to encapsulate your custom objects for dragging and dropping
682 * in your Java programs!
683 * When you need to create a {@link java.awt.datatransfer.Transferable} object,
684 * use this class to wrap your object.
685 * For example:
686 * <pre><code>
687 * ...
688 * MyCoolClass myObj = new MyCoolClass();
689 * Transferable xfer = new TransferableObject( myObj );
690 * ...
691 * </code></pre>
692 * Or if you need to know when the data was actually dropped, like when you're
693 * moving data out of a list, say, you can use the {@link TransferableObject.Fetcher}
694 * inner class to return your object Just in Time.
695 * For example:
696 * <pre><code>
697 * ...
698 * final MyCoolClass myObj = new MyCoolClass();
699 *
700 * TransferableObject.Fetcher fetcher = new TransferableObject.Fetcher()
701 * { public Object getObject(){ return myObj; }
702 * }; // end fetcher
703 *
704 * Transferable xfer = new TransferableObject( fetcher );
705 * ...
706 * </code></pre>
707 *
708 * The {@link java.awt.datatransfer.DataFlavor} associated with
709 * {@link TransferableObject} has the representation class
710 * <tt>net.iharder.dnd.TransferableObject.class</tt> and MIME type
711 * <tt>application/x-net.iharder.dnd.TransferableObject</tt>.
712 * This data flavor is accessible via the static
713 * {@link #DATA_FLAVOR} property.
714 *
715 *
716 * <p>I'm releasing this code into the Public Domain. Enjoy.</p>
717 *
718 * @author Robert Harder
719 * @author rob@iharder.net
720 * @version 1.2
721 */
722 public static class TransferableObject implements java.awt.datatransfer.Transferable
723 {
724 /**
725 * The MIME type for {@link #DATA_FLAVOR} is
726 * <tt>application/x-net.iharder.dnd.TransferableObject</tt>.
727 *
728 * @since 1.1
729 */
730 public final static String MIME_TYPE = "application/x-net.iharder.dnd.TransferableObject";
731
732 /**
733 * The default {@link java.awt.datatransfer.DataFlavor} for
734 * {@link TransferableObject} has the representation class
735 * <tt>net.iharder.dnd.TransferableObject.class</tt>
736 * and the MIME type
737 * <tt>application/x-net.iharder.dnd.TransferableObject</tt>.
738 *
739 * @since 1.1
740 */
741 public final static java.awt.datatransfer.DataFlavor DATA_FLAVOR =
742 new java.awt.datatransfer.DataFlavor( FileDrop.TransferableObject.class, MIME_TYPE );
743
744 private Fetcher fetcher;
745 private Object data;
746
747 private java.awt.datatransfer.DataFlavor customFlavor;
748
749 /**
750 * Creates a new {@link TransferableObject} that wraps <var>data</var>.
751 * Along with the {@link #DATA_FLAVOR} associated with this class,
752 * this creates a custom data flavor with a representation class
753 * determined from <code>data.getClass()</code> and the MIME type
754 * <tt>application/x-net.iharder.dnd.TransferableObject</tt>.
755 *
756 * @param data The data to transfer
757 * @since 1.1
758 */
759 public TransferableObject( Object data )
760 { this.data = data;
761 this.customFlavor = new java.awt.datatransfer.DataFlavor( data.getClass(), MIME_TYPE );
762 } // end constructor
763
764 /**
765 * Creates a new {@link TransferableObject} that will return the
766 * object that is returned by <var>fetcher</var>.
767 * No custom data flavor is set other than the default
768 * {@link #DATA_FLAVOR}.
769 *
770 * @see Fetcher
771 * @param fetcher The {@link Fetcher} that will return the data object
772 * @since 1.1
773 */
774 public TransferableObject( Fetcher fetcher )
775 { this.fetcher = fetcher;
776 } // end constructor
777
778 /**
779 * Creates a new {@link TransferableObject} that will return the
780 * object that is returned by <var>fetcher</var>.
781 * Along with the {@link #DATA_FLAVOR} associated with this class,
782 * this creates a custom data flavor with a representation class <var>dataClass</var>
783 * and the MIME type
784 * <tt>application/x-net.iharder.dnd.TransferableObject</tt>.
785 *
786 * @see Fetcher
787 * @param dataClass The {@link java.lang.Class} to use in the custom data flavor
788 * @param fetcher The {@link Fetcher} that will return the data object
789 * @since 1.1
790 */
791 public TransferableObject( Class dataClass, Fetcher fetcher )
792 { this.fetcher = fetcher;
793 this.customFlavor = new java.awt.datatransfer.DataFlavor( dataClass, MIME_TYPE );
794 } // end constructor
795
796 /**
797 * Returns the custom {@link java.awt.datatransfer.DataFlavor} associated
798 * with the encapsulated object or <tt>null</tt> if the {@link Fetcher}
799 * constructor was used without passing a {@link java.lang.Class}.
800 *
801 * @return The custom data flavor for the encapsulated object
802 * @since 1.1
803 */
804 public java.awt.datatransfer.DataFlavor getCustomDataFlavor()
805 { return customFlavor;
806 } // end getCustomDataFlavor
807
808 /* ******** T R A N S F E R A B L E M E T H O D S ******** */
809
810 /**
811 * Returns a two- or three-element array containing first
812 * the custom data flavor, if one was created in the constructors,
813 * second the default {@link #DATA_FLAVOR} associated with
814 * {@link TransferableObject}, and third the
815 * {@link java.awt.datatransfer.DataFlavor.stringFlavor}.
816 *
817 * @return An array of supported data flavors
818 * @since 1.1
819 */
820 public java.awt.datatransfer.DataFlavor[] getTransferDataFlavors()
821 {
822 if( customFlavor != null )
823 return new java.awt.datatransfer.DataFlavor[]
824 { customFlavor,
825 DATA_FLAVOR,
826 java.awt.datatransfer.DataFlavor.stringFlavor
827 }; // end flavors array
828 else
829 return new java.awt.datatransfer.DataFlavor[]
830 { DATA_FLAVOR,
831 java.awt.datatransfer.DataFlavor.stringFlavor
832 }; // end flavors array
833 } // end getTransferDataFlavors
834
835 /**
836 * Returns the data encapsulated in this {@link TransferableObject}.
837 * If the {@link Fetcher} constructor was used, then this is when
838 * the {@link Fetcher#getObject getObject()} method will be called.
839 * If the requested data flavor is not supported, then the
840 * {@link Fetcher#getObject getObject()} method will not be called.
841 *
842 * @param flavor The data flavor for the data to return
843 * @return The dropped data
844 * @since 1.1
845 */
846 public Object getTransferData( java.awt.datatransfer.DataFlavor flavor )
847 throws java.awt.datatransfer.UnsupportedFlavorException, java.io.IOException
848 {
849 // Native object
850 if( flavor.equals( DATA_FLAVOR ) )
851 return fetcher == null ? data : fetcher.getObject();
852
853 // String
854 if( flavor.equals( java.awt.datatransfer.DataFlavor.stringFlavor ) )
855 return fetcher == null ? data.toString() : fetcher.getObject().toString();
856
857 // We can't do anything else
858 throw new java.awt.datatransfer.UnsupportedFlavorException(flavor);
859 } // end getTransferData
860
861 /**
862 * Returns <tt>true</tt> if <var>flavor</var> is one of the supported
863 * flavors. Flavors are supported using the <code>equals(...)</code> method.
864 *
865 * @param flavor The data flavor to check
866 * @return Whether or not the flavor is supported
867 * @since 1.1
868 */
869 public boolean isDataFlavorSupported( java.awt.datatransfer.DataFlavor flavor )
870 {
871 // Native object
872 if( flavor.equals( DATA_FLAVOR ) )
873 return true;
874
875 // String
876 if( flavor.equals( java.awt.datatransfer.DataFlavor.stringFlavor ) )
877 return true;
878
879 // We can't do anything else
880 return false;
881 } // end isDataFlavorSupported
882
883 /* ******** I N N E R I N T E R F A C E F E T C H E R ******** */
884
885 /**
886 * Instead of passing your data directly to the {@link TransferableObject}
887 * constructor, you may want to know exactly when your data was received
888 * in case you need to remove it from its source (or do anyting else to it).
889 * When the {@link #getTransferData getTransferData(...)} method is called
890 * on the {@link TransferableObject}, the {@link Fetcher}'s
891 * {@link #getObject getObject()} method will be called.
892 *
893 * @author Robert Harder
894 * @copyright 2001
895 * @version 1.1
896 * @since 1.1
897 */
898 public static interface Fetcher
899 {
900 /**
901 * Return the object being encapsulated in the
902 * {@link TransferableObject}.
903 *
904 * @return The dropped object
905 * @since 1.1
906 */
907 public abstract Object getObject();
908 } // end inner interface Fetcher
909
910 } // end class TransferableObject
911
912} // end class FileDrop
Note: See TracBrowser for help on using the repository browser.