001/*
002 *  $Source: v:/cvsroot/open/projects/WebARTS/ca/bc/webarts/widgets/dnd/FileDrop.java,v $
003 *  $Name:  $
004 *  $Revision: 1.1 $
005 *  $Date: 2005-04-10 11:53:16 -0700 (Sun, 10 Apr 2005) $
006 *  $Locker:  $
007 */
008/*
009 *  Copyright (C) 2001 WebARTS Design, North Vancouver Canada
010 *  http://www..webarts.bc.ca
011 *
012 *  This program is free software; you can redistribute it and/or modify
013 *  it under the terms of the GNU General Public License as published by
014 *  the Free Software Foundation; either version 2 of the License, or
015 *  (at your option) any later version.
016 *
017 *  This program is distributed in the hope that it will be useful,
018 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
019 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
020 *  GNU General Public License for more details.
021 *
022 *  You should have received a copy of the GNU General Public License
023 *  along with this program; if not, write to the Free Software
024 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
025 */
026package ca.bc.webarts.widgets.dnd;
027
028/**
029 * This class makes it easy to drag and drop files from the operating
030 * system to a Java program. Any <tt>java.awt.Component</tt> can be
031 * dropped onto, but only <tt>javax.swing.JComponent</tt>s will indicate
032 * the drop event with a changed border.
033 * <p/>
034 * To use this class, construct a new <tt>FileDrop</tt> by passing
035 * it the target component and a <tt>Listener</tt> to receive notification
036 * when file(s) have been dropped. Here is an example:
037 * <p/>
038 * <code><pre>
039 *      JPanel myPanel = new JPanel();
040 *      new FileDrop( myPanel, new FileDrop.Listener()
041 *      {   public void filesDropped( java.io.File[] files )
042 *          {
043 *              // handle file drop
044 *              ...
045 *          }   // end filesDropped
046 *      }); // end FileDrop.Listener
047 * </pre></code>
048 * <p/>
049 * You can specify the border that will appear when files are being dragged by
050 * calling the constructor with a <tt>javax.swing.border.Border</tt>. Only
051 * <tt>JComponent</tt>s will show any indication with a border.
052 * <p/>
053 * You can turn on some debugging features by passing a <tt>PrintStream</tt>
054 * object (such as <tt>System.out</tt>) into the full constructor. A <tt>null</tt>
055 * value will result in no extra debugging information being output.
056 * <p/>
057 *
058 * <p>I'm releasing this code into the Public Domain. Enjoy.
059 * </p>
060 * <p><em>Original author: Robert Harder, rharder@usa.net</em></p>
061 *
062 * @author  Robert Harder
063 * @author  rharder@usa.net
064 * @version 1.0
065 */
066public class FileDrop
067{
068    private transient javax.swing.border.Border normalBorder;
069    private transient java.awt.dnd.DropTargetListener dropListener;
070
071
072    /** Discover if the running JVM is modern enough to have drag and drop. */
073    private static Boolean supportsDnD;
074
075    // Default border color
076    private static java.awt.Color defaultBorderColor = new java.awt.Color( 0f, 0f, 1f, 0.25f );
077
078    /**
079     * Constructs a {@link FileDrop} with a default light-blue border
080     * and, if <var>c</var> is a {@link java.awt.Container}, recursively
081     * sets all elements contained within as drop targets, though only
082     * the top level container will change borders.
083     *
084     * @param c Component on which files will be dropped.
085     * @param listener Listens for <tt>filesDropped</tt>.
086     * @since 1.0
087     */
088    public FileDrop(
089    final java.awt.Component c,
090    final Listener listener )
091    {   this( null,  // Logging stream
092              c,     // Drop target
093              javax.swing.BorderFactory.createMatteBorder( 2, 2, 2, 2, defaultBorderColor ), // Drag border
094              true, // Recursive
095              listener );
096    }   // end constructor
097
098
099
100
101    /**
102     * Constructor with a default border and the option to recursively set drop targets.
103     * If your component is a <tt>java.awt.Container</tt>, then each of its children
104     * components will also listen for drops, though only the parent will change borders.
105     *
106     * @param c Component on which files will be dropped.
107     * @param recursive Recursively set children as drop targets.
108     * @param listener Listens for <tt>filesDropped</tt>.
109     * @since 1.0
110     */
111    public FileDrop(
112    final java.awt.Component c,
113    final boolean recursive,
114    final Listener listener )
115    {   this( null,  // Logging stream
116              c,     // Drop target
117              javax.swing.BorderFactory.createMatteBorder( 2, 2, 2, 2, defaultBorderColor ), // Drag border
118              recursive, // Recursive
119              listener );
120    }   // end constructor
121
122
123    /**
124     * Constructor with a default border and debugging optionally turned on.
125     * With Debugging turned on, more status messages will be displayed to
126     * <tt>out</tt>. A common way to use this constructor is with
127     * <tt>System.out</tt> or <tt>System.err</tt>. A <tt>null</tt> value for
128     * the parameter <tt>out</tt> will result in no debugging output.
129     *
130     * @param out PrintStream to record debugging info or null for no debugging.
131     * @param out
132     * @param c Component on which files will be dropped.
133     * @param listener Listens for <tt>filesDropped</tt>.
134     * @since 1.0
135     */
136    public FileDrop(
137    final java.io.PrintStream out,
138    final java.awt.Component c,
139    final Listener listener )
140    {   this( out,  // Logging stream
141              c,    // Drop target
142              javax.swing.BorderFactory.createMatteBorder( 2, 2, 2, 2, defaultBorderColor ),
143              false, // Recursive
144              listener );
145    }   // end constructor
146
147
148
149    /**
150     * Constructor with a default border, debugging optionally turned on
151     * and the option to recursively set drop targets.
152     * If your component is a <tt>java.awt.Container</tt>, then each of its children
153     * components will also listen for drops, though only the parent will change borders.
154     * With Debugging turned on, more status messages will be displayed to
155     * <tt>out</tt>. A common way to use this constructor is with
156     * <tt>System.out</tt> or <tt>System.err</tt>. A <tt>null</tt> value for
157     * the parameter <tt>out</tt> will result in no debugging output.
158     *
159     * @param out PrintStream to record debugging info or null for no debugging.
160     * @param out
161     * @param c Component on which files will be dropped.
162     * @param recursive Recursively set children as drop targets.
163     * @param listener Listens for <tt>filesDropped</tt>.
164     * @since 1.0
165     */
166    public FileDrop(
167    final java.io.PrintStream out,
168    final java.awt.Component c,
169    final boolean recursive,
170    final Listener listener)
171    {   this( out,  // Logging stream
172              c,    // Drop target
173              javax.swing.BorderFactory.createMatteBorder( 2, 2, 2, 2, defaultBorderColor ), // Drag border
174              recursive, // Recursive
175              listener );
176    }   // end constructor
177
178
179
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
203
204    /**
205     * Constructor with a specified border and the option to recursively set drop targets.
206     * If your component is a <tt>java.awt.Container</tt>, then each of its children
207     * components will also listen for drops, though only the parent will change borders.
208     *
209     * @param c Component on which files will be dropped.
210     * @param dragBorder Border to use on <tt>JComponent</tt> when dragging occurs.
211     * @param recursive Recursively set children as drop targets.
212     * @param listener Listens for <tt>filesDropped</tt>.
213     * @since 1.0
214     */
215    public FileDrop(
216    final java.awt.Component c,
217    final javax.swing.border.Border dragBorder,
218    final boolean recursive,
219    final Listener listener)
220    {   this(
221            null,
222            c,
223            dragBorder,
224            recursive,
225            listener );
226    }   // end constructor
227
228
229
230    /**
231     * Constructor with a specified border and debugging optionally turned on.
232     * With Debugging turned on, more status messages will be displayed to
233     * <tt>out</tt>. A common way to use this constructor is with
234     * <tt>System.out</tt> or <tt>System.err</tt>. A <tt>null</tt> value for
235     * the parameter <tt>out</tt> will result in no debugging output.
236     *
237     * @param out PrintStream to record debugging info or null for no debugging.
238     * @param c Component on which files will be dropped.
239     * @param dragBorder Border to use on <tt>JComponent</tt> when dragging occurs.
240     * @param listener Listens for <tt>filesDropped</tt>.
241     * @since 1.0
242     */
243    public FileDrop(
244    final java.io.PrintStream out,
245    final java.awt.Component c,
246    final javax.swing.border.Border dragBorder,
247    final Listener listener)
248    {   this(
249            out,    // Logging stream
250            c,      // Drop target
251            dragBorder, // Drag border
252            false,  // Recursive
253            listener );
254    }   // end constructor
255
256
257
258
259
260    /**
261     * Full constructor with a specified border and debugging optionally turned on.
262     * With Debugging turned on, more status messages will be displayed to
263     * <tt>out</tt>. A common way to use this constructor is with
264     * <tt>System.out</tt> or <tt>System.err</tt>. A <tt>null</tt> value for
265     * the parameter <tt>out</tt> will result in no debugging output.
266     *
267     * @param out PrintStream to record debugging info or null for no debugging.
268     * @param c Component on which files will be dropped.
269     * @param dragBorder Border to use on <tt>JComponent</tt> when dragging occurs.
270     * @param recursive Recursively set children as drop targets.
271     * @param listener Listens for <tt>filesDropped</tt>.
272     * @since 1.0
273     */
274    public FileDrop(
275    final java.io.PrintStream out,
276    final java.awt.Component c,
277    final javax.swing.border.Border dragBorder,
278    final boolean recursive,
279    final Listener listener)
280    {
281
282        if( supportsDnD() )
283        {   // Make a drop listener
284            dropListener = new java.awt.dnd.DropTargetListener()
285            {   
286              public void dragEnter( java.awt.dnd.DropTargetDragEvent evt )
287                {       
288                    log( out, "FileDrop: dragEnter event." );
289
290                    // Is this an acceptable drag event?
291                    if( isDraggedFileList( out, evt ) )
292                    {
293                        // If it's a Swing component, set its border
294                        if( c instanceof javax.swing.JComponent )
295                        {   javax.swing.JComponent jc = (javax.swing.JComponent) c;
296                            normalBorder = jc.getBorder();
297                            log( out, "FileDrop: normal border saved." );
298                            jc.setBorder( dragBorder );
299                            log( out, "FileDrop: drag border set." );
300                        }   // end if: JComponent
301
302                        // Acknowledge that it's okay to enter
303                        //evt.acceptDrag( java.awt.dnd.DnDConstants.ACTION_COPY_OR_MOVE );
304                        evt.acceptDrag( java.awt.dnd.DnDConstants.ACTION_COPY );
305                        log( out, "FileDrop: event accepted." );
306                    }   // end if: drag ok
307                    else
308                    {   // Reject the drag event
309                        evt.rejectDrag();
310                        log( out, "FileDrop: event rejected." );
311                    }   // end else: drag not ok
312                }   // end dragEnter
313
314                public void dragOver( java.awt.dnd.DropTargetDragEvent evt )
315                {   // This is called continually as long as the mouse is
316                    // over the drag target.
317                }   // end dragOver
318
319                public void drop( java.awt.dnd.DropTargetDropEvent evt )
320                {   log( out, "FileDrop: drop event." );
321                    try
322                    {   // Get whatever was dropped
323                        java.awt.datatransfer.Transferable tr = evt.getTransferable();
324
325                        // Is it a file list?
326                        //if (tr.isDataFlavorSupported (java.awt.datatransfer.DataFlavor.javaFileListFlavor))
327                        if( isDroppedFileList( out, evt ) )
328                        {
329                            // Say we'll take it.
330                            //evt.acceptDrop ( java.awt.dnd.DnDConstants.ACTION_COPY_OR_MOVE );
331                            evt.acceptDrop ( java.awt.dnd.DnDConstants.ACTION_COPY );
332                            log( out, "FileDrop: file list accepted." );
333
334                            // Get a useful list
335                            java.util.List fileList = (java.util.List)
336                                tr.getTransferData(java.awt.datatransfer.DataFlavor.javaFileListFlavor);
337                                //tr.getTransferData(java.awt.datatransfer.DataFlavor.stringFlavor );
338                            java.util.Iterator iterator = fileList.iterator();
339
340                            /*
341                            String [] filenamesArray = new String [ fileList.size() ];
342                            fileList.toArray( filenamesArray );
343                            log( out, "FileDrop: dropped filenames:" );
344                            for (int iii = 0; iii < filenamesArray.length; iii++)
345                            {
346                              log( out, "         " + filenamesArray[iii]);
347                            }
348                            */
349                            // Convert list to array
350                            java.io.File[] filesTemp = new java.io.File[ fileList.size() ];
351                            fileList.toArray( filesTemp );
352                            final java.io.File[] files = filesTemp;
353
354                            // Alert listener to drop.
355                            if( listener != null )
356                                listener.filesDropped( files );
357
358                            // Mark that drop is completed.
359                            evt.getDropTargetContext().dropComplete(true);
360                            log( out, "FileDrop: drop complete." );
361                        }   // end if: file list
362                        else
363                        {   log( out, "FileDrop: not a file list - abort." );
364                            evt.rejectDrop();
365                        }   // end else: not a file list
366                    }   // end try
367                    catch ( java.io.IOException io)
368                    {   log( out, "FileDrop: IOException - abort:" );
369                        io.printStackTrace( out );
370                        evt.rejectDrop();
371                    }   // end catch IOException
372                    catch (java.awt.datatransfer.UnsupportedFlavorException ufe)
373                    {   log( out, "FileDrop: UnsupportedFlavorException - abort:" );
374                        ufe.printStackTrace( out );
375                        evt.rejectDrop();
376                    }   // end catch: UnsupportedFlavorException
377                    finally
378                    {
379                        // If it's a Swing component, reset its border
380                        if( c instanceof javax.swing.JComponent )
381                        {   javax.swing.JComponent jc = (javax.swing.JComponent) c;
382                            jc.setBorder( normalBorder );
383                            log( out, "FileDrop: normal border restored." );
384                        }   // end if: JComponent
385                    }   // end finally
386                }   // end drop
387
388                public void dragExit( java.awt.dnd.DropTargetEvent evt )
389                {   log( out, "FileDrop: dragExit event." );
390                    // If it's a Swing component, reset its border
391                    if( c instanceof javax.swing.JComponent )
392                    {   javax.swing.JComponent jc = (javax.swing.JComponent) c;
393                        jc.setBorder( normalBorder );
394                        log( out, "FileDrop: normal border restored." );
395                    }   // end if: JComponent
396                }   // end dragExit
397
398                public void dropActionChanged( java.awt.dnd.DropTargetDragEvent evt )
399                {   log( out, "FileDrop: dropActionChanged event." );
400                    // Is this an acceptable drag event?
401                    if( isDraggedFileList( out, evt ) )
402                    {   //evt.acceptDrag( java.awt.dnd.DnDConstants.ACTION_COPY_OR_MOVE );
403                        evt.acceptDrag( java.awt.dnd.DnDConstants.ACTION_COPY );
404                        log( out, "FileDrop: event accepted." );
405                    }   // end if: drag ok
406                    else
407                    {   evt.rejectDrag();
408                        log( out, "FileDrop: event rejected." );
409                    }   // end else: drag not ok
410                }   // end dropActionChanged
411            }; // end DropTargetListener
412
413            // Make the component (and possibly children) drop targets
414            makeDropTarget( out, c, recursive );
415        }   // end if: supports dnd
416        else
417        {   log( out, "FileDrop: Drag and drop is not supported with this JVM" );
418        }   // end else: does not support DnD
419    }   // end constructor
420
421
422    private static boolean supportsDnD()
423    {   // Static Boolean
424        if( supportsDnD == null )
425        {
426            boolean support = false;
427            try
428            {   Class arbitraryDndClass = Class.forName( "java.awt.dnd.DnDConstants" );
429                support = true;
430            }   // end try
431            catch( Exception e )
432            {   support = false;
433            }   // end catch
434            supportsDnD = new Boolean( support );
435        }   // end if: first time through
436        return supportsDnD.booleanValue();
437    }   // end supportsDnD
438
439
440
441
442
443    private void makeDropTarget( final java.io.PrintStream out, final java.awt.Component c, boolean recursive )
444    {
445        // Make drop target
446        final java.awt.dnd.DropTarget dt = new java.awt.dnd.DropTarget();
447        try
448        {   dt.addDropTargetListener( dropListener );
449        }   // end try
450        catch( java.util.TooManyListenersException e )
451        {   e.printStackTrace();
452            log(out, "FileDrop: Drop will not work due to previous error. Do you have another listener attached?" );
453        }   // end catch
454
455        // Listen for hierarchy changes and remove the drop target when the parent gets cleared out.
456        c.addHierarchyListener( new java.awt.event.HierarchyListener()
457        {   public void hierarchyChanged( java.awt.event.HierarchyEvent evt )
458            {   log( out, "FileDrop: Hierarchy changed." );
459                java.awt.Component parent = c.getParent();
460                if( parent == null )
461                {   c.setDropTarget( null );
462                    log( out, "FileDrop: Drop target cleared from component." );
463                }   // end if: null parent
464                else
465                {   new java.awt.dnd.DropTarget(c, dropListener);
466                    log( out, "FileDrop: Drop target added to component." );
467                }   // end else: parent not null
468            }   // end hierarchyChanged
469        }); // end hierarchy listener
470        if( c.getParent() != null )
471            new java.awt.dnd.DropTarget(c, dropListener);
472
473        if( recursive && (c instanceof java.awt.Container ) )
474        {
475            // Get the container
476            java.awt.Container cont = (java.awt.Container) c;
477
478            // Get it's components
479            java.awt.Component[] comps = cont.getComponents();
480
481            // Set it's components as listeners also
482            for( int i = 0; i < comps.length; i++ )
483                makeDropTarget( out, comps[i], recursive );
484        }   // end if: recursively set components as listener
485    }   // end dropListener
486
487
488
489    /** Determine if the dropped data is a file list. */
490    private boolean isDroppedFileList( final java.io.PrintStream out, final java.awt.dnd.DropTargetDropEvent evt )
491    {   
492      boolean ok = false;
493
494        // Get data flavors being dragged
495        java.awt.datatransfer.DataFlavor[] flavors = evt.getCurrentDataFlavors();
496        //log( out, "FileDrop: Number of flavors="+flavors.length );
497        // See if any of the flavors are a file list
498        int i = 0;
499        while( !ok && i < flavors.length )
500        {   // Is the flavor a file list?
501            //if( flavors[i].equals( java.awt.datatransfer.DataFlavor.javaFileListFlavor ) )
502            if( flavors[i].getMimeType().startsWith("text/uri-list"))
503            {
504                ok = true;
505                //log( out, "FileDrop: Acceptable data flavors. "+ flavors[i].getMimeType() );
506            }
507            //log( out, "FileDrop: Found data flavors. "+ flavors[i].getMimeType() );
508            i++;
509        }   // end while: through flavors
510
511        // If logging is enabled, show data flavors
512        if( out != null )
513        {   if( flavors.length == 0 )
514                log( out, "FileDrop: no data flavors." );
515            for( i = 0; i < flavors.length; i++ )
516                log( out, flavors[i].toString() );
517        }   // end if: logging enabled
518
519        return ok;
520    }   // end isDragOk
521
522
523    /** Determine if the dragged data is a file list. */
524    private boolean isDraggedFileList( final java.io.PrintStream out, final java.awt.dnd.DropTargetDragEvent evt )
525    {   
526      boolean ok = false;
527
528        // Get data flavors being dragged
529        java.awt.datatransfer.DataFlavor[] flavors = evt.getCurrentDataFlavors();
530        //log( out, "FileDrop: Number of flavors="+flavors.length );
531        // See if any of the flavors are a file list
532        int i = 0;
533        while( !ok && i < flavors.length )
534        {   // Is the flavor a file list?
535            //if( flavors[i].equals( java.awt.datatransfer.DataFlavor.javaFileListFlavor ) )
536            if( flavors[i].getMimeType().startsWith("text/uri-list"))
537            {
538                ok = true;
539                //log( out, "FileDrop: Acceptable data flavors. "+ flavors[i].getMimeType() );
540            }
541            //log( out, "FileDrop: Found data flavors. "+ flavors[i].getMimeType() );
542            i++;
543        }   // end while: through flavors
544
545        // If logging is enabled, show data flavors
546        if( out != null )
547        {   if( flavors.length == 0 )
548                log( out, "FileDrop: no data flavors." );
549            for( i = 0; i < flavors.length; i++ )
550                log( out, flavors[i].toString() );
551        }   // end if: logging enabled
552
553        return ok;
554    }   // end isDragOk
555
556
557    /** Outputs <tt>message</tt> to <tt>out</tt> if it's not null. */
558    private static void log( java.io.PrintStream out, String message )
559    {   // Log message if requested
560        if( out != null )
561            out.println( message );
562    }   // end log
563
564
565
566
567    /**
568     * Removes the drag-and-drop hooks from the component and optionally
569     * from the all children. You should call this if you add and remove
570     * components after you've set up the drag-and-drop.
571     * This will recursively unregister all components contained within
572     * <var>c</var> if <var>c</var> is a {@link java.awt.Container}.
573     *
574     * @param c The component to unregister as a drop target
575     * @since 1.0
576     */
577    public static boolean remove( java.awt.Component c)
578    {   return remove( null, c, true );
579    }   // end remove
580
581
582
583    /**
584     * Removes the drag-and-drop hooks from the component and optionally
585     * from the all children. You should call this if you add and remove
586     * components after you've set up the drag-and-drop.
587     *
588     * @param out Optional {@link java.io.PrintStream} for logging drag and drop messages
589     * @param c The component to unregister
590     * @param recursive Recursively unregister components within a container
591     * @since 1.0
592     */
593    public static boolean remove( java.io.PrintStream out, java.awt.Component c, boolean recursive )
594    {   // Make sure we support dnd.
595        if( supportsDnD() )
596        {   log( out, "FileDrop: Removing drag-and-drop hooks." );
597            c.setDropTarget( null );
598            if( recursive && ( c instanceof java.awt.Container ) )
599            {   java.awt.Component[] comps = ((java.awt.Container)c).getComponents();
600                for( int i = 0; i < comps.length; i++ )
601                    remove( out, comps[i], recursive );
602                return true;
603            }   // end if: recursive
604            else return false;
605        }   // end if: supports DnD
606        else return false;
607    }   // end remove
608
609
610
611
612
613
614    /** Runs a sample program that shows dropped files */
615    public static void main( String[] args )
616    {
617        javax.swing.JFrame frame = new javax.swing.JFrame( "FileDrop" );
618        //javax.swing.border.TitledBorder dragBorder = new javax.swing.border.TitledBorder( "Drop 'em" );
619        final javax.swing.JTextArea text = new javax.swing.JTextArea();
620        frame.getContentPane().add(
621            new javax.swing.JScrollPane( text ),
622            java.awt.BorderLayout.CENTER );
623
624        new FileDrop( System.out, text, /*dragBorder,*/ new FileDrop.Listener()
625        {   public void filesDropped( java.io.File[] files )
626            {   for( int i = 0; i < files.length; i++ )
627                {   try
628                    {   text.append( files[i].getCanonicalPath() + "\n" );
629                    }   // end try
630                    catch( java.io.IOException e ) {}
631                }   // end for: through each dropped file
632            }   // end filesDropped
633        }); // end FileDrop.Listener
634
635        frame.setBounds( 100, 100, 300, 400 );
636        frame.setDefaultCloseOperation( frame.EXIT_ON_CLOSE );
637        frame.show();
638    }   // end main
639
640
641
642
643
644/* ********  I N N E R   I N T E R F A C E   L I S T E N E R  ******** */
645
646
647    /**
648     * Implement this inner interface to listen for when files are dropped. For example
649     * your class declaration may begin like this:
650     * <code><pre>
651     *      public class MyClass implements FileDrop.Listener
652     *      ...
653     *      public void filesDropped( java.io.File[] files )
654     *      {
655     *          ...
656     *      }   // end filesDropped
657     *      ...
658     * </pre></code>
659     *
660     * @since 1.0
661     */
662    public interface Listener
663    {
664        /**
665         * This method is called when files have been successfully dropped.
666         *
667         * @param files An array of <tt>File</tt>s that were dropped.
668         * @since 1.0
669         */
670        public abstract void filesDropped( java.io.File[] files );
671    }   // end inner-interface Listener
672
673}   // end class FileDrop
674