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

Last change on this file since 3351 was 3083, checked in by bastiK, 14 years ago

added svn:eol-style=native to source files

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