AppleScript for Beginners IX - Getting the Drop on Droplets

A droplet is exactly what it sounds like; an icon (representing a script YOU write) you can drag and drop a file, a folder, or groups of either, and make something happen. They are extremely versatile, and as usual, you are only limited by your own imagination. For instance, my kids are amateur musicians that enjoy creating projects in GarageBand. When they have composed something interesting, I can drop the files onto a droplet that imports them into my iTunes library (for subsequent iPod syncing) and then emails them to grandparents. Very simple, clean, and I can modify the droplet any time I want. We are going to start by examining the anatomy of a droplet, so you know all the parts and pieces that you have to work with before we jump into writing one and then using it.

Remember our discussion last time about errors, when I introduced the concept of a native handler? It is the same for droplets; there is a native handler that must be used for it to function correctly. (There is also an optional handler, which we will cover as well, since it is pretty useful.) The required handler is the on open handler, and if it is not in your script somewhere, your droplet won’t work at all.

Before we continue, we should also discuss the unique way this type of script must be saved to be functional. (Although you can save droplets anywhere you wish, I recommend the desktop to begin with, so it will be simple to find and use for testing.) Open up the script below in Script Editor:


on open the_Droppings
	display dialog "Hey! you dropped " & (the_Droppings's length) & " item(s) on me!"
end open

Go ahead and compile it to make sure it compiles, then select Save As… from the File menu. In the middle of the Save As dialog window, in the lower half, you will see File Format: and a pull-down menu of different choices. You want to select application once you have decided on a name for this droplet, and save it to your desktop.

That is it. You now have a droplet. Go ahead and play around with it by dragging and dropping files and folders onto it’s icon and see what happens, I’ll wait right here.

Did you notice that regardless of whether you dropped a single file, or a folder full of files onto the icon, the count was always 1? Of course, if you selected multiple files or folders, you got an accurate count of how many you dropped. Good, let’s explore this further. The first line:

on open the_Droppings is the necessary format for the on open handler, complete with a variable name (in this example; the_Droppings) afterwards. You MUST have this pattern in every droplet you devise. The on open handler is expecting a list of files or folder references, and so you need to provide a variable name to hold the alias locations of all the files and folders you drop there. (An alias is simply a reference path to the real location of the file; dropping a file or folder on your droplet doesn’t move it.) You need to remember that a list is expected, even if you only plan on dropping a single file or folder onto your droplet, so that you construct the script inside the handler correctly. In my case, I am only asking for the length of the list of items dropped (the_Droppings’s length) to be reproduced in the display dialog, which is simple.

Let’s say you want a text document that lists the names of the files inside of folders you drop onto the droplet:


on open the_Droppings
	tell application "Finder" to set file_Names to the name of every file in item 1 of the_Droppings
	set the_Text to ""
	repeat with a_Name in file_Names
		set the_Text to the_Text & a_Name & return
	end repeat
	tell application "TextEdit" to make new document with properties {text:the_Text}
end open

In this case, we are only allowing the processing of a single folder, as you can see in the line that tells the Finder to only consider item 1 of the_Droppings. Since we know that the on open handler is expecting a list, we simply tell it to deal with the first item of the list, which would be the only item if you drop a single folder onto the droplet icon. If you try dropping a file onto this droplet, you discover one of the inherent weaknesses of droplets; you get no error information in its native form, even with a try block. (Go ahead and put one in there; you’ll see what I mean.) Fortunately, a droplet only deals with files or folders; nothing else. If your droplet does not function correctly, that often means it is expecting one thing, and receiving the other.

When we tell the Finder to set file_Names to the name of every file in item 1 of the_Droppings, the variable file_Names is also a list, thus requiring us to use the repeat to create the text for the TextEdit document. That repeat is pretty simple; we initially set the variable the_Text to an empty string (“”), and repeat through the list of file_Names, adding each file name and a return to the_Text. Once we get to the TextEdit tell statement, the variable the_Text holds all the filenames, one per line, and we get the nifty document listing all the filenames.

Keep in mind as well that when you drop a folder onto a droplet, and it processes all the files in that folder, they will typically be processed in their Finder sorted order. This is not always going to be the case if you collect a random assortment of files from different locations in your system, or if you drop a few folders of files onto a single droplet. Each folder’s contents may very well be processed in order, but you will not get a final sorting of ALL the files unless you write that code yourself.

It gets tricky when you want to have the freedom of dropping folders, files, or folders containing sub-folders onto a droplet and expecting the droplet to just go on and process all the files in all the droppings in the same fashion. Keep in mind that the on open handler is expecting a list. It cannot know all by itself if that list is a list of folders, files, or a combination, so you need to write that into your code yourself:


on open the_Droppings
	set dropped_Files to {}
	set dropped_Folders to {}
	repeat with a_Drop in the_Droppings
		if folder of (info for a_Drop) then
			set end of dropped_Folders to a_Drop
		else
			set end of dropped_Files to a_Drop
		end if
	end repeat
	display dialog "You dropped " & (dropped_Files's length) & " files, and " & (dropped_Folders's length) & " folders just now."
end open

Since a droplet cannot automatiacally discern between folders and files, we loop through the list of dropped items, and simply test to see if it is a folder: if folder of (info for a_Drop) then. If this evaluates to false, it has to be a file, so the script will produce a list of folders (dropped_Folders) and a list of files (dropped_Files) that you can now process however you want.

The info for command is a very convenient term to obtain information about files and folders without invoking the Finder. The list of properties you can access is extensive; look in the StandardAdditions AppleScript library under File Commands.

There is an optional handler that you can use in your droplets that can function as a sort of help file; the on run handler. If you have a few droplets scattered around your system, and you can’t remember what they are there for, double-clicking them will activate the on run handler, which you can use to offer an explanation:


on open the_Droppings
	display dialog "Hey! you dropped " & (the_Droppings's length) & " item(s) on me!"
end open

on run
	display dialog "This counts the number of dropped items" giving up after 3
end run

It is a good idea to use this handler so that you need not open the script up every time you wonder what it was that you wrote in the first place. In fact, you can take the on run handler one step further and set your droplet up to perform double duty. I only learned of this technique recently (thank you, Bruce Phillips), and have not had much experience with it, but it displays a fundamental strength of AppleScript that I keep needing to remind myself about: Don’t be afraid to try anything.

Here is a sample script of what I am talking about:


on run
	choose folder with prompt "Which folder would like average file size calculated?"
	open {result}
end run

on open user_Choice
	tell application "Finder" to set xx to every file in item 1 of user_Choice
	set tot_size to 0
	repeat with a_File in xx
		set tot_size to tot_size + ((info for (a_File as alias))'s size)
	end repeat
	display dialog "Total files: " & (xx's length) & return & "Average Size: " & (((tot_size / 1.048576E+6) / (xx's length)) as integer) & " MB"
end open

In this situation, we use the on run section of our droplet script to prompt the user to choose a folder, which is then sent to the on open handler to be processed. We can use the with prompt modifier to act as a brief explanation of what the droplet/application does. This way, not only does the script function as a droplet, it also achieves the ability to work as an application, and double-clicking on the icon will launch it (and explain its purpose) all in one motion. It is currently designed to only act on the first item in the list sent to the handler, but that could be easily modified to deal with a list of folders, so work on that if you are interested. You might also design your handler to prompt the user to choose file instead and then write the script to deal with folders of files as well.

I think that droplets are cool, and I recommend that you consider using them, even for simple tasks. You can drag files and folders out of any Finder window to drop onto your droplet, so don’t feel like you have to keep your droplets in different places to do different things; put them all on your desktop, or in a folder on your desktop, and drop stuff on them from anywhere.

You can also add them to the dock and drag items onto them there…

Hi,
I have a question about drop a folder into a droplet and then traverse 3 files in that folder. I have tried many times to figure how to really read the content from those text files. none of them working. Can you give me a hint? The following are something I have tried earlier.
//--------------------------------------------------------------------------------
on open user_Choice
tell application “Finder” to set xx to every file in item 1 of user_Choice
set myList to “user_Choice”
repeat with aa in myList
display dialog aa
end repeat
end open
//--------------------------------------------------------------------------------
on open user_Choice
tell application “Finder” to set xx to every file in item 1 of user_Choice

repeat with aa in xx
	set aid to (read xx)
	display dialog aid
end repeat

end open
//--------------------------------------------------------------------------------

Hi,

the Finder returns items of class Finder file specifier, which must be coerced to alias or text,
because only the Finder can handle them.


on open user_Choice
	tell application "Finder" to set fileList to every file in item 1 of user_Choice
	
	repeat with aFile in fileList
		set aid to (read (aFile as alias))
		display dialog aid
	end repeat
end open

in your second script you’re going to open the list of files (xx) instead of the index variable (aa).
These errors can be avoided by using better readable variable names :wink:

Thanks a lot. It works very well!

… and so to 2020 and Catalina :slight_smile: … the “counting” scripts no longer work as such, that is to say, if you drop a mixture of folders and files, they are counted separately, so you gat a dialogue with the folder count first, followed by a dialogue with the file count. This can be a big nuisance if you just want to display a dialogue with “You dropped n items” and then go ahead and treat the items according to whether they’re folders or files without throwing up extra dialogues. I’ve not found a workaround for this, so if anyone has any suggestions, I’m all ears :smiley:

It’s not a folders v. files issue, but one of quarantined items v. non-quarantined items. About the only workaround I’ve seen is a bit complicated:

use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions
property filesToOpen : {}

on open fileList
	set my filesToOpen to my filesToOpen & fileList
	-- cancel any pending performSelector: requests
	current application's NSObject's cancelPreviousPerformRequestsWithTarget:me
	-- handle files after a short delay in case further events are received
	tell me to performSelector:"doOpen" withObject:(missing value) afterDelay:0.5
end open

on doOpen()
	copy my filesToOpen to fileList
	set my filesToOpen to {} -- reset for next time
	
	set aRes to length of fileList
	display dialog aRes as string
	
	repeat with i in fileList
		set j to POSIX path of i
		display dialog j
	end repeat
	
	tell me to quit
end doOpen

on quit
	continue quit
end quit

Complicated indeed !! Oddly, the display dialogs don’t display for me, unless I add a display dialog in the “on open” section. It can be anything eg display dialog “foobar” but without it, the others don’t show ! Appart from that, your solution works fine !

Thank you very much for taking the time to answer.