I love being able to drag-and-drop files or directories onto my AIR apps and have it recursively process all of the files in the directory structure. This is handy for batch file processing utilities, which seems to be a common theme in many of my AIR apps.
The first thing you need to do is set up your AIR app to handle the drag and drop operation. This is pretty straightforward, and is done by setting up listeners for the NATIVE_DRAG_ENTER and NATIVE_DRAGE_DROP events.
private function onCreationComplete() : void {
this.addEventListener(NativeDragEvent.NATIVE_DRAG_ENTER, onDragIn);
this.addEventListener(NativeDragEvent.NATIVE_DRAG_DROP, onDrop);
}
The code that handles the NATIVE_DRAG_ENTER event needs to determine whether or not to accept the dragged items. In this method you see it essentially accepting anything that is dragged into the app. This does not have to be the case, if you prefer to be more selective about the files that are dragged in.
There are several method the more discriminating users may want to call on the event. The event has a clipboard object, with a number of methods. getFormat(), and getData() are the two most useful in my opinion. You can use these to check for a particular filename or extension, etc.
private function onDragIn(event : NativeDragEvent) : void {
NativeDragManager.acceptDragDrop(this);
}
You see I use the clipboard.getData() method to retrieve the list of dropped files. If you’re only interested in drag-and-drop, you can stop here. The method processDroppedFiles, which accepts an array, is the method that starts the actual processing.
private function onDrop(event : NativeDragEvent) : void {
try {
var dropfiles:Array = event.clipboard.getData(ClipboardFormats.FILE_LIST_FORMAT) as Array;
processDroppedFiles(dropfiles);
}
catch (error : IOError) {
trace("Error during drag-and-drop procedure.");
}
}
A file in my files array can either be a file, in which case I want to do my processing. That’s what the method processFile() does, which I will not detail in this post, because it does whatever work your application is doing with the files.
The file could also be a directory, in which case, I want to call processDirectory(). isDirectory is a property on the file that is true if the file is a directory, and false if… well, duh.
private function processDroppedFiles(files : Array) : void {
for each (var file:File in files){
if (file.isDirectory)
processDirectory(file);
else
processFile(file);
}
}
I could have skipped out on this method if I wanted to… I’m adding it to make it a little more clear what’s happening. The method processDroppedFiles() needs an Array of Files, whereas processDirectory() accepts a File. At this point, we know that the file in question is a directory, so we call the getDirectoryListing() method which returns an Array, and we recurse to processDroppedFiles().
private function processDirectory(dir : File) : void {
processDroppedFiles(dir.getDirectoryListing());
}
As I pointed out, you could just as easily have included the recursive call in the processDroppedFiles() method itself, like this:
private function processDroppedFiles(files : Array) : void {
for each (var file:File in files){
if (file.isDirectory)
processDroppedFiles(file.getDirectoryListing());
else
processFile(file);
}
}
And this will traverse the directory tree, eventually calling processFile() on each file in the tree.