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

Last change on this file since 6084 was 6084, checked in by bastiK, 11 years ago

see #8902 - add missing @Override annotations (patch by shinigami)

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