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

Last change on this file since 4936 was 3710, checked in by bastiK, 13 years ago

new list of recently opened files; little preference rework

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