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

Last change on this file since 1231 was 1231, checked in by ulfl, 15 years ago

drop files on the main window to open it

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