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

Revision 3710, 36.0 KB checked in by bastiK, 18 months ago (diff)

new list of recently opened files; little preference rework

  • Property svn:eol-style set to native
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.