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

Last change on this file since 1911 was 1911, checked in by Gubaer, 15 years ago

fixed #3193: No layer is left highlighted when the bottom most one is deleted
fixed some references to Way.nodes
clean up of warnings
added documentation in LayerListDialog

File size: 37.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;
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<Object> fileList = (java.util.List<Object>)
342 tr.getTransferData(java.awt.datatransfer.DataFlavor.javaFileListFlavor);
343
344 // Convert list to array
345 java.io.File[] filesTemp = new java.io.File[ fileList.size() ];
346 fileList.toArray( filesTemp );
347 final java.io.File[] files = filesTemp;
348
349 // Alert listener to drop.
350 if( listener != null ) {
351 listener.filesDropped( files );
352 }
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
379 // Mark that drop is completed.
380 evt.getDropTargetContext().dropComplete(true);
381 log(out, "FileDrop: drop complete.");
382 handled = true;
383 break;
384 }
385 }
386 if(!handled){
387 log( out, "FileDrop: not a file list or reader - abort." );
388 evt.rejectDrop();
389 }
390 // END 2007-09-12 Nathan Blomquist -- Linux (KDE/Gnome) support added.
391 } // end else: not a file list
392 } // end try
393 catch ( java.io.IOException io)
394 { log( out, "FileDrop: IOException - abort:" );
395 io.printStackTrace( out );
396 evt.rejectDrop();
397 } // end catch IOException
398 catch (java.awt.datatransfer.UnsupportedFlavorException ufe)
399 { log( out, "FileDrop: UnsupportedFlavorException - abort:" );
400 ufe.printStackTrace( out );
401 evt.rejectDrop();
402 } // end catch: UnsupportedFlavorException
403 finally
404 {
405 // If it's a Swing component, reset its border
406 if( c instanceof javax.swing.JComponent )
407 { javax.swing.JComponent jc = (javax.swing.JComponent) c;
408 jc.setBorder( normalBorder );
409 log( out, "FileDrop: normal border restored." );
410 } // end if: JComponent
411 } // end finally
412 } // end drop
413
414 public void dragExit( java.awt.dnd.DropTargetEvent evt )
415 { log( out, "FileDrop: dragExit event." );
416 // If it's a Swing component, reset its border
417 if( c instanceof javax.swing.JComponent )
418 { javax.swing.JComponent jc = (javax.swing.JComponent) c;
419 jc.setBorder( normalBorder );
420 log( out, "FileDrop: normal border restored." );
421 } // end if: JComponent
422 } // end dragExit
423
424 public void dropActionChanged( java.awt.dnd.DropTargetDragEvent evt )
425 { log( out, "FileDrop: dropActionChanged event." );
426 // Is this an acceptable drag event?
427 if( isDragOk( out, evt ) )
428 { //evt.acceptDrag( java.awt.dnd.DnDConstants.ACTION_COPY_OR_MOVE );
429 evt.acceptDrag( java.awt.dnd.DnDConstants.ACTION_COPY );
430 log( out, "FileDrop: event accepted." );
431 } // end if: drag ok
432 else
433 { evt.rejectDrag();
434 log( out, "FileDrop: event rejected." );
435 } // end else: drag not ok
436 } // end dropActionChanged
437 }; // end DropTargetListener
438
439 // Make the component (and possibly children) drop targets
440 makeDropTarget( out, c, recursive );
441 } // end if: supports dnd
442 else
443 { log( out, "FileDrop: Drag and drop is not supported with this JVM" );
444 } // end else: does not support DnD
445 } // end constructor
446
447
448 private static boolean supportsDnD()
449 { // Static Boolean
450 if( supportsDnD == null )
451 {
452 boolean support = false;
453 try
454 { Class arbitraryDndClass = Class.forName( "java.awt.dnd.DnDConstants" );
455 support = true;
456 } // end try
457 catch( Exception e )
458 { support = false;
459 } // end catch
460 supportsDnD = new Boolean( support );
461 } // end if: first time through
462 return supportsDnD.booleanValue();
463 } // end supportsDnD
464
465
466 // BEGIN 2007-09-12 Nathan Blomquist -- Linux (KDE/Gnome) support added.
467 private static String ZERO_CHAR_STRING = "" + (char)0;
468 private static File[] createFileArray(BufferedReader bReader, PrintStream out)
469 {
470 try {
471 java.util.List<File> list = new java.util.ArrayList<File>();
472 java.lang.String line = null;
473 while ((line = bReader.readLine()) != null) {
474 try {
475 // kde seems to append a 0 char to the end of the reader
476 if(ZERO_CHAR_STRING.equals(line)) {
477 continue;
478 }
479
480 java.io.File file = new java.io.File(new java.net.URI(line));
481 list.add(file);
482 } catch (Exception ex) {
483 log(out, "Error with " + line + ": " + ex.getMessage());
484 }
485 }
486
487 return list.toArray(new File[list.size()]);
488 } catch (IOException ex) {
489 log(out, "FileDrop: IOException");
490 }
491 return new File[0];
492 }
493 // END 2007-09-12 Nathan Blomquist -- Linux (KDE/Gnome) support added.
494
495
496 private void makeDropTarget( final java.io.PrintStream out, final java.awt.Component c, boolean recursive )
497 {
498 // Make drop target
499 final java.awt.dnd.DropTarget dt = new java.awt.dnd.DropTarget();
500 try
501 { dt.addDropTargetListener( dropListener );
502 } // end try
503 catch( java.util.TooManyListenersException e )
504 { e.printStackTrace();
505 log(out, "FileDrop: Drop will not work due to previous error. Do you have another listener attached?" );
506 } // end catch
507
508 // Listen for hierarchy changes and remove the drop target when the parent gets cleared out.
509 c.addHierarchyListener( new java.awt.event.HierarchyListener()
510 { public void hierarchyChanged( java.awt.event.HierarchyEvent evt )
511 { log( out, "FileDrop: Hierarchy changed." );
512 java.awt.Component parent = c.getParent();
513 if( parent == null )
514 { c.setDropTarget( null );
515 log( out, "FileDrop: Drop target cleared from component." );
516 } // end if: null parent
517 else
518 { new java.awt.dnd.DropTarget(c, dropListener);
519 log( out, "FileDrop: Drop target added to component." );
520 } // end else: parent not null
521 } // end hierarchyChanged
522 }); // end hierarchy listener
523 if( c.getParent() != null ) {
524 new java.awt.dnd.DropTarget(c, dropListener);
525 }
526
527 if( recursive && (c instanceof java.awt.Container ) )
528 {
529 // Get the container
530 java.awt.Container cont = (java.awt.Container) c;
531
532 // Get it's components
533 java.awt.Component[] comps = cont.getComponents();
534
535 // Set it's components as listeners also
536 for( int i = 0; i < comps.length; i++ ) {
537 makeDropTarget( out, comps[i], recursive );
538 }
539 } // end if: recursively set components as listener
540 } // end dropListener
541
542
543
544 /** Determine if the dragged data is a file list. */
545 private boolean isDragOk( final java.io.PrintStream out, final java.awt.dnd.DropTargetDragEvent evt )
546 { boolean ok = false;
547
548 // Get data flavors being dragged
549 java.awt.datatransfer.DataFlavor[] flavors = evt.getCurrentDataFlavors();
550
551 // See if any of the flavors are a file list
552 int i = 0;
553 while( !ok && i < flavors.length )
554 {
555 // BEGIN 2007-09-12 Nathan Blomquist -- Linux (KDE/Gnome) support added.
556 // Is the flavor a file list?
557 final DataFlavor curFlavor = flavors[i];
558 if( curFlavor.equals( java.awt.datatransfer.DataFlavor.javaFileListFlavor ) ||
559 curFlavor.isRepresentationClassReader()){
560 ok = true;
561 }
562 // END 2007-09-12 Nathan Blomquist -- Linux (KDE/Gnome) support added.
563 i++;
564 } // end while: through flavors
565
566 // If logging is enabled, show data flavors
567 if( out != null )
568 { if( flavors.length == 0 ) {
569 log( out, "FileDrop: no data flavors." );
570 }
571 for( i = 0; i < flavors.length; i++ ) {
572 log( out, flavors[i].toString() );
573 }
574 } // end if: logging enabled
575
576 return ok;
577 } // end isDragOk
578
579
580 /** Outputs <tt>message</tt> to <tt>out</tt> if it's not null. */
581 private static void log( java.io.PrintStream out, String message )
582 { // Log message if requested
583 if( out != null ) {
584 out.println( message );
585 }
586 } // end log
587
588
589
590
591 /**
592 * Removes the drag-and-drop hooks from the component and optionally
593 * from the all children. You should call this if you add and remove
594 * components after you've set up the drag-and-drop.
595 * This will recursively unregister all components contained within
596 * <var>c</var> if <var>c</var> is a {@link java.awt.Container}.
597 *
598 * @param c The component to unregister as a drop target
599 * @since 1.0
600 */
601 public static boolean remove( java.awt.Component c)
602 { return remove( null, c, true );
603 } // end remove
604
605
606
607 /**
608 * Removes the drag-and-drop hooks from the component and optionally
609 * from the all children. You should call this if you add and remove
610 * components after you've set up the drag-and-drop.
611 *
612 * @param out Optional {@link java.io.PrintStream} for logging drag and drop messages
613 * @param c The component to unregister
614 * @param recursive Recursively unregister components within a container
615 * @since 1.0
616 */
617 public static boolean remove( java.io.PrintStream out, java.awt.Component c, boolean recursive )
618 { // Make sure we support dnd.
619 if( supportsDnD() )
620 { log( out, "FileDrop: Removing drag-and-drop hooks." );
621 c.setDropTarget( null );
622 if( recursive && ( c instanceof java.awt.Container ) )
623 { java.awt.Component[] comps = ((java.awt.Container)c).getComponents();
624 for( int i = 0; i < comps.length; i++ ) {
625 remove( out, comps[i], recursive );
626 }
627 return true;
628 } // end if: recursive
629 else return false;
630 } // end if: supports DnD
631 else return false;
632 } // end remove
633
634
635
636
637 /* ******** I N N E R I N T E R F A C E L I S T E N E R ******** */
638
639
640 /**
641 * Implement this inner interface to listen for when files are dropped. For example
642 * your class declaration may begin like this:
643 * <code><pre>
644 * public class MyClass implements FileDrop.Listener
645 * ...
646 * public void filesDropped( java.io.File[] files )
647 * {
648 * ...
649 * } // end filesDropped
650 * ...
651 * </pre></code>
652 *
653 * @since 1.1
654 */
655 public static interface Listener {
656
657 /**
658 * This method is called when files have been successfully dropped.
659 *
660 * @param files An array of <tt>File</tt>s that were dropped.
661 * @since 1.0
662 */
663 public abstract void filesDropped( java.io.File[] files );
664
665
666 } // end inner-interface Listener
667
668
669 /* ******** I N N E R C L A S S ******** */
670
671
672 /**
673 * This is the event that is passed to the
674 * {@link FileDropListener#filesDropped filesDropped(...)} method in
675 * your {@link FileDropListener} when files are dropped onto
676 * a registered drop target.
677 *
678 * <p>I'm releasing this code into the Public Domain. Enjoy.</p>
679 *
680 * @author Robert Harder
681 * @author rob@iharder.net
682 * @version 1.2
683 */
684 public static class Event extends java.util.EventObject {
685
686 private java.io.File[] files;
687
688 /**
689 * Constructs an {@link Event} with the array
690 * of files that were dropped and the
691 * {@link FileDrop} that initiated the event.
692 *
693 * @param files The array of files that were dropped
694 * @source The event source
695 * @since 1.1
696 */
697 public Event( java.io.File[] files, Object source ) {
698 super( source );
699 this.files = files;
700 } // end constructor
701
702 /**
703 * Returns an array of files that were dropped on a
704 * registered drop target.
705 *
706 * @return array of files that were dropped
707 * @since 1.1
708 */
709 public java.io.File[] getFiles() {
710 return files;
711 } // end getFiles
712
713 } // end inner class Event
714
715
716
717 /* ******** I N N E R C L A S S ******** */
718
719
720 /**
721 * At last an easy way to encapsulate your custom objects for dragging and dropping
722 * in your Java programs!
723 * When you need to create a {@link java.awt.datatransfer.Transferable} object,
724 * use this class to wrap your object.
725 * For example:
726 * <pre><code>
727 * ...
728 * MyCoolClass myObj = new MyCoolClass();
729 * Transferable xfer = new TransferableObject( myObj );
730 * ...
731 * </code></pre>
732 * Or if you need to know when the data was actually dropped, like when you're
733 * moving data out of a list, say, you can use the {@link TransferableObject.Fetcher}
734 * inner class to return your object Just in Time.
735 * For example:
736 * <pre><code>
737 * ...
738 * final MyCoolClass myObj = new MyCoolClass();
739 *
740 * TransferableObject.Fetcher fetcher = new TransferableObject.Fetcher()
741 * { public Object getObject(){ return myObj; }
742 * }; // end fetcher
743 *
744 * Transferable xfer = new TransferableObject( fetcher );
745 * ...
746 * </code></pre>
747 *
748 * The {@link java.awt.datatransfer.DataFlavor} associated with
749 * {@link TransferableObject} has the representation class
750 * <tt>net.iharder.dnd.TransferableObject.class</tt> and MIME type
751 * <tt>application/x-net.iharder.dnd.TransferableObject</tt>.
752 * This data flavor is accessible via the static
753 * {@link #DATA_FLAVOR} property.
754 *
755 *
756 * <p>I'm releasing this code into the Public Domain. Enjoy.</p>
757 *
758 * @author Robert Harder
759 * @author rob@iharder.net
760 * @version 1.2
761 */
762 public static class TransferableObject implements java.awt.datatransfer.Transferable
763 {
764 /**
765 * The MIME type for {@link #DATA_FLAVOR} is
766 * <tt>application/x-net.iharder.dnd.TransferableObject</tt>.
767 *
768 * @since 1.1
769 */
770 public final static String MIME_TYPE = "application/x-net.iharder.dnd.TransferableObject";
771
772
773 /**
774 * The default {@link java.awt.datatransfer.DataFlavor} for
775 * {@link TransferableObject} has the representation class
776 * <tt>net.iharder.dnd.TransferableObject.class</tt>
777 * and the MIME type
778 * <tt>application/x-net.iharder.dnd.TransferableObject</tt>.
779 *
780 * @since 1.1
781 */
782 public final static java.awt.datatransfer.DataFlavor DATA_FLAVOR =
783 new java.awt.datatransfer.DataFlavor( FileDrop.TransferableObject.class, MIME_TYPE );
784
785
786 private Fetcher fetcher;
787 private Object data;
788
789 private java.awt.datatransfer.DataFlavor customFlavor;
790
791
792
793 /**
794 * Creates a new {@link TransferableObject} that wraps <var>data</var>.
795 * Along with the {@link #DATA_FLAVOR} associated with this class,
796 * this creates a custom data flavor with a representation class
797 * determined from <code>data.getClass()</code> and the MIME type
798 * <tt>application/x-net.iharder.dnd.TransferableObject</tt>.
799 *
800 * @param data The data to transfer
801 * @since 1.1
802 */
803 public TransferableObject( Object data )
804 { this.data = data;
805 this.customFlavor = new java.awt.datatransfer.DataFlavor( data.getClass(), MIME_TYPE );
806 } // end constructor
807
808
809
810 /**
811 * Creates a new {@link TransferableObject} that will return the
812 * object that is returned by <var>fetcher</var>.
813 * No custom data flavor is set other than the default
814 * {@link #DATA_FLAVOR}.
815 *
816 * @see Fetcher
817 * @param fetcher The {@link Fetcher} that will return the data object
818 * @since 1.1
819 */
820 public TransferableObject( Fetcher fetcher )
821 { this.fetcher = fetcher;
822 } // end constructor
823
824
825
826 /**
827 * Creates a new {@link TransferableObject} that will return the
828 * object that is returned by <var>fetcher</var>.
829 * Along with the {@link #DATA_FLAVOR} associated with this class,
830 * this creates a custom data flavor with a representation class <var>dataClass</var>
831 * and the MIME type
832 * <tt>application/x-net.iharder.dnd.TransferableObject</tt>.
833 *
834 * @see Fetcher
835 * @param dataClass The {@link java.lang.Class} to use in the custom data flavor
836 * @param fetcher The {@link Fetcher} that will return the data object
837 * @since 1.1
838 */
839 public TransferableObject( Class dataClass, Fetcher fetcher )
840 { this.fetcher = fetcher;
841 this.customFlavor = new java.awt.datatransfer.DataFlavor( dataClass, MIME_TYPE );
842 } // end constructor
843
844 /**
845 * Returns the custom {@link java.awt.datatransfer.DataFlavor} associated
846 * with the encapsulated object or <tt>null</tt> if the {@link Fetcher}
847 * constructor was used without passing a {@link java.lang.Class}.
848 *
849 * @return The custom data flavor for the encapsulated object
850 * @since 1.1
851 */
852 public java.awt.datatransfer.DataFlavor getCustomDataFlavor()
853 { return customFlavor;
854 } // end getCustomDataFlavor
855
856
857 /* ******** T R A N S F E R A B L E M E T H O D S ******** */
858
859
860 /**
861 * Returns a two- or three-element array containing first
862 * the custom data flavor, if one was created in the constructors,
863 * second the default {@link #DATA_FLAVOR} associated with
864 * {@link TransferableObject}, and third the
865 * {@link java.awt.datatransfer.DataFlavor.stringFlavor}.
866 *
867 * @return An array of supported data flavors
868 * @since 1.1
869 */
870 public java.awt.datatransfer.DataFlavor[] getTransferDataFlavors()
871 {
872 if( customFlavor != null )
873 return new java.awt.datatransfer.DataFlavor[]
874 { customFlavor,
875 DATA_FLAVOR,
876 java.awt.datatransfer.DataFlavor.stringFlavor
877 }; // end flavors array
878 else
879 return new java.awt.datatransfer.DataFlavor[]
880 { DATA_FLAVOR,
881 java.awt.datatransfer.DataFlavor.stringFlavor
882 }; // end flavors array
883 } // end getTransferDataFlavors
884
885
886
887 /**
888 * Returns the data encapsulated in this {@link TransferableObject}.
889 * If the {@link Fetcher} constructor was used, then this is when
890 * the {@link Fetcher#getObject getObject()} method will be called.
891 * If the requested data flavor is not supported, then the
892 * {@link Fetcher#getObject getObject()} method will not be called.
893 *
894 * @param flavor The data flavor for the data to return
895 * @return The dropped data
896 * @since 1.1
897 */
898 public Object getTransferData( java.awt.datatransfer.DataFlavor flavor )
899 throws java.awt.datatransfer.UnsupportedFlavorException, java.io.IOException
900 {
901 // Native object
902 if( flavor.equals( DATA_FLAVOR ) )
903 return fetcher == null ? data : fetcher.getObject();
904
905 // String
906 if( flavor.equals( java.awt.datatransfer.DataFlavor.stringFlavor ) )
907 return fetcher == null ? data.toString() : fetcher.getObject().toString();
908
909 // We can't do anything else
910 throw new java.awt.datatransfer.UnsupportedFlavorException(flavor);
911 } // end getTransferData
912
913
914
915
916 /**
917 * Returns <tt>true</tt> if <var>flavor</var> is one of the supported
918 * flavors. Flavors are supported using the <code>equals(...)</code> method.
919 *
920 * @param flavor The data flavor to check
921 * @return Whether or not the flavor is supported
922 * @since 1.1
923 */
924 public boolean isDataFlavorSupported( java.awt.datatransfer.DataFlavor flavor )
925 {
926 // Native object
927 if( flavor.equals( DATA_FLAVOR ) )
928 return true;
929
930 // String
931 if( flavor.equals( java.awt.datatransfer.DataFlavor.stringFlavor ) )
932 return true;
933
934 // We can't do anything else
935 return false;
936 } // end isDataFlavorSupported
937
938
939 /* ******** I N N E R I N T E R F A C E F E T C H E R ******** */
940
941 /**
942 * Instead of passing your data directly to the {@link TransferableObject}
943 * constructor, you may want to know exactly when your data was received
944 * in case you need to remove it from its source (or do anyting else to it).
945 * When the {@link #getTransferData getTransferData(...)} method is called
946 * on the {@link TransferableObject}, the {@link Fetcher}'s
947 * {@link #getObject getObject()} method will be called.
948 *
949 * @author Robert Harder
950 * @copyright 2001
951 * @version 1.1
952 * @since 1.1
953 */
954 public static interface Fetcher
955 {
956 /**
957 * Return the object being encapsulated in the
958 * {@link TransferableObject}.
959 *
960 * @return The dropped object
961 * @since 1.1
962 */
963 public abstract Object getObject();
964 } // end inner interface Fetcher
965
966
967
968 } // end class TransferableObject
969
970
971
972
973
974} // end class FileDrop
Note: See TracBrowser for help on using the repository browser.