subidoo - Enhanced subfolder creation for Mac OS X

If you also suffer from subfolder creation sickness, then I might have a cure for you :slight_smile:

Overview
subidoo is an AppleScript that simplifies the creation of subfolders in Mac OS X. Especially in list view, subfolders are tedious to create. subidoo can create multiple subfolders in multiple locations at once, saves the names of the last 300 subfolders created and supports convenient folder sets, which can contain collections of frequently used subfolders.

You can download subidoo for free right here:

subidoo - Enhanced subfolder creation for Mac OS X (v1.5, ca. 137 KB)

System Requirements
Mac OS X 10.5.1 or later

  • subidoo may run on other systems as well, but I did/could not test it
  • subidoo was tested on Intel- and PowerPC-based Macs

Installation
Just drag the script into your Applications/Scripts folder (or wherever you like to put it).

Afterwards you »can«:
a) drag subidoo into the toolbar of a Finder window to use it as a Toolbar Script.
b) drag subidoo into the Dock to use it as a Dock Item.

I find both solutions very convenient for creating subfolders, but personally think, that subidoo works best when used from the toolbar.

Usage
¢ Options panel:
To invoke the options panel in order to manage and create folder sets, just start subidoo by double-clicking its icon without any items selected in the Finder (except subidoo itself).

¢ Toolbar Script/Dock Item:
Select folder items in the frontmost Finder window. Then start subidoo by clicking its icon in the dock or toolbar. After providing names for subfolders or choosing folder sets, they will be created within the selected folder items.
If you don’t select any items in the Finder, the options panel for managing your folder sets will be displayed.

¢ Droplet:
subidoo also works extremely well as a droplet. Therefor you can simply drag & drop folder items onto it. After providing names for subfolders or choosing folder sets, they will be created within the folder items, which you dragged & dropped onto the script.

Folder Sets
subidoo now supports convenient folder sets, which can contain collections of frequently used folder names. This is very useful, when you need to create the same bunch of subfolders over and over again. System administrators will love this! For example, if you are often creating the same set of subfolders representing the 12 months of a year, e.g. ‘01 January’, ‘02 February’, ‘03 March’, then just save them in a folder set named ‘Months’! Now you can choose to create this folder set in chosen folders whenever you want by just a click!
To manage, create and edit your folder sets, just start subidoo without selecting any items in The Finder.

Tips
When you are asked to provide subfolder names, you can enter multiple entries separated by a colon. Nested subfolder structures can be entered by separating subfolder names by a slash.

If you want to manually edit the database file containing the folder sets, you can find it here:

/Users/yourname/Library/Application Support/subidoo/foldersets.db

After manipulating the database file, please always save it with UTF-8 text encoding.

Known Problems
If you select folder items in the Finder and then start subidoo out of a Finder window, it will not recognize the selected folders. This is because AppleScript seems to be unable to recognize selected items in ‘background’ Finder windows (when you start subidoo out of a Finder window, it is itself the frontmost selection). This does not happen if you are using subidoo as a Toolbar Script or Dock Item. So this is absolutely recommended.

Troubleshoot/Help
-/-

Version History
¢ Version 1.5b

  • subidoo now finally supports folder sets! wohoo!
  • (sub)folders are not anymore created with the Finder,
    but using «mkdir -p» on the command line
  • subidoo now supports relative subfolder structures, so
    that you can create subfolders in subfolders in subfolders in…
    ¢ Version 1.0
  • upon request of my wife, I removed the gave-up mode in the dialog, where the user can enter the subfoldernames. now the user has ‘endless’ time to enter the subfoldernames without the dialog suddenly disappearing after 50 seconds.
    ¢ Version 0.9b
  • First public release! Yo!

Important: Opening and saving the below script code in Script Editor won’t result in a usable AppleScript! That is because subidoo internally relies on a Python script, which is located inside its Application bundle. Therefor please download the complete script here.


-- created: 07.12.2005
-- modified: 18.02.2008
-- version: 1.5
-- tested with:
-- ¢ Mac OS X 10.5.1
-- ¢ Intel and PowerPC based Macs
-- history:
-- v1.5 (18.02.2008):
-- ¢ finally subidoo supports folder sets! wohoo!
-- ¢ subidoo now supports relative folder structures, so
-- you can also create subfolders in subfolders in subfolders...
-- ¢ (sub)folders are now created using the «mkdir -p»-command
-- -->> type 'man mkdir' into a Terminal window
-- v1.0 (11.02.2006):
-- ¢ upon request of my wife, I removed the gave-up mode in the dialog,
-- where the user enters the subfolder names. now the user has 'endless'
-- time to enter the subfoler names without the dialog window suddenly
-- disappearing after 50 seconds.
-- future:
-- ¢ icon, icon, icon!

-- some basic properties
property mytitle : "subidoo"
property mydomain : "com.jos." & mytitle
property myversion : "1.5b"

-- handler called when Finder items are dropped onto the script icon
on open dropitems
	set mode to "on open"
	my main(mode, dropitems)
end open

-- handler called when the script is opened with a double-click onto its icon
on run
	set mode to "on run"
	set args to missing value
	my main(mode, args)
end run

-- I am the main function of the script and control the application flow
on main(mode, args)
	try
		-- creating application support folder & copying default database file if necessary
		my initialize()
		-- user dropped Finder items onto the script
		if mode is "on open" then
			-- process dropped Finder items
			my procselitems(mode, args)
			-- user opened the script with a double-click
		else if mode is "on run" then
			-- the user might have selected items in the Finder
			tell application "Finder"
				set selitems to selection as list
			end tell
			if selitems is {} then
				-- dock item?
				my guiloop()
			else
				-- is the script itself the selected item or are there no selected items at all?
				-- > in this case we display the options panel
				set myname to name of (info for (path to me))
				set selitemname to name of (info for ((item 1 of selitems) as alias))
				if length of selitems = 1 and myname = selitemname then
					my guiloop()
				else
					-- process selected Finder items
					my procselitems(mode, selitems)
				end if
			end if
		end if
		-- catching unexpected errors, hopefully not too often
	on error errmsg number errnum
		my dsperrmsg(errmsg, errnum)
	end try
end main

-- I am initializing the script, taking precautions for a successful operation 
on initialize()
	-- path to my own application support folder
	set myasfpath to (((path to application support folder from user domain) as Unicode text) & mytitle & ":")
	-- if the application support folder does not yet exist, we try to create it
	-- occuring errors (missing permissions?) will be catched by
	-- the try/on error-block of the main function
	try
		set myasfalias to myasfpath as alias
	on error
		do shell script "mkdir -p" & space & quoted form of (POSIX path of myasfpath)
		-- if the script's application support folder did not exist so far, then
		-- chances are good, that the user uses the script for the very first time,
		-- so we display an inviting welcome message
		my dspwelcomemsg()
	end try
	-- path to the user's database file inside the application support folder, which contains the folder sets
	set dbfilepath to myasfpath & "foldersets.db"
	-- path to the default database file inside the script's package folder
	set defdbfilepath to (((path to me) as Unicode text) & "Contents:Resources:foldersets.db")
	-- if the user's database file does not yet exist, we copy the default database file to the
	-- application support folder
	try
		set dbfilealias to dbfilepath as alias
	on error
		do shell script "cp" & space & quoted form of (POSIX path of defdbfilepath) & space & quoted form of (POSIX path of dbfilepath)
	end try
end initialize

-- I am processing the selected/dropped Finder items and create the chosen subfolders
-- therein if possible
on procselitems(mode, selitems)
	-- we are only interested in dropped/selected folder items
	set selfolders to {}
	repeat with selitem in selitems
		try
			set selinfo to (info for (selitem as alias))
			if folder of selinfo and name of selinfo does not end with ".app" then
				set selfolders to selfolders & (selitem as Unicode text)
			end if
		end try
	end repeat
	-- unfortunately the user only selected/dropped files
	if selfolders is {} then
		if mode is "on run" then
			set errmsg to "You did only select non-folder items in the Finder." & return & return & "Please try again by selecting folders only."
		else if mode is "on open" then
			set errmsg to "You did only drop non-folder items onto " & mytitle & "." & return & return & "Please try again by dropping folders only."
		end if
		my dsperrmsg(errmsg, "--")
		-- early finish
		return
		-- we found selected/dropped folders!
	else
		-- asking the user to provide (sub)folder names to be created inside
		--  the dropped or selected folders
		set countselfolders to length of selfolders
		set relfoldernames to my askforrelfoldernames(mode, countselfolders)
		if relfoldernames is not missing value then
			-- so now that we got (sub)folder names, we have to create a string
			-- of quoted folder paths to be used with the mkdir-command below
			repeat with selfolder in selfolders
				set stringfolders to ""
				set countrelfoldernames to length of relfoldernames
				repeat with i from 1 to countrelfoldernames
					-- .e.g. 'Invoices/2008/01 January/'
					set relfoldername to item i of relfoldernames
					-- e.g. '/Users/martin/Documents/Customers/BigCompany/Invoices/2008/01 January/'
					set newpath to quoted form of ((POSIX path of selfolder) & relfoldername)
					if i is not equal to countrelfoldernames then
						set stringfolders to stringfolders & newpath & space
					else
						set stringfolders to stringfolders & newpath
					end if
				end repeat
				-- finally creating the subfolders using the mkdir-command with the p-option
				set command to "mkdir -p" & space & stringfolders
				set command to command as «class utf8»
				set shellresult to do shell script command
			end repeat
		end if
	end if
end procselitems

-- I am displaying the options panel and trigger actions on the basis of
-- the chosen menu items
on guiloop()
	set chosenmenuitem to my dspoptionsmenu()
	if chosenmenuitem is missing value then
		return
	else
		if chosenmenuitem is "Add folder set" then
			my miaddfset()
		else if chosenmenuitem is "Delete folder set(s)" then
			my midelfsets()
		else if chosenmenuitem is "Rename folder set" then
			my mirenfset()
		else if chosenmenuitem is "Show folder sets" then
			my mishowfsets()
		else if chosenmenuitem is "Add folder name(s) to folder set" then
			my miaddfolderstofset()
		else if chosenmenuitem is "Delete folder name(s) from folder set" then
			my midelfoldersfromfset()
		else if chosenmenuitem is "Create folder set from recent folder names" then
			my micrtfsetfromsysfolders("Recent folder names")
		else if chosenmenuitem is "Add recent folder names to a folder set" then
			my miaddsysfolderstofset("Recent folder names")
		else if chosenmenuitem is "Create folder set from last folder names" then
			my micrtfsetfromsysfolders("Last folder names")
		else if chosenmenuitem is "Add last folder names to a folder set" then
			my miaddsysfolderstofset("Last folder names")
		else if chosenmenuitem is "Open database file" then
			my miopendbfile()
		end if
	end if
	my guiloop()
end guiloop

-- I am displaying the options menu and return the chosen menu items
on dspoptionsmenu()
	set spacer to "....................."
	set menuitems to {"Add folder set", "Delete folder set(s)", "Rename folder set", "Show folder sets", spacer, "Add folder name(s) to folder set", "Delete folder name(s) from folder set", spacer, "Create folder set from recent folder names", "Add recent folder names to a folder set", spacer, "Create folder set from last folder names", "Add last folder names to a folder set", spacer, "Open database file"}
	choose from list menuitems with title mytitle with prompt "Please choose an option:" without multiple selections allowed and empty selection allowed
	set choice to result
	if choice is not false then
		set chosenmenuitem to item 1 of choice
		return chosenmenuitem
	else
		return missing value
	end if
end dspoptionsmenu

-- I am asking the user to choose a single folder set name from a list
-- of existing folder set names
on choosefoldersetname(showhidden)
	set foldersetnames to my getfoldersetnames(showhidden)
	if foldersetnames is missing value then
		return missing value
	else
		choose from list foldersetnames with title mytitle with prompt "Please choose a folder set:" without multiple selections allowed and empty selection allowed
		set choice to result
		if choice is not false then
			set chosenfoldersetname to item 1 of choice
			return chosenfoldersetname
		else
			return missing value
		end if
	end if
end choosefoldersetname

-- I am asking the user to choose one or more folder set names from a list
-- of existing folder set names
on choosefoldersetnames(showhidden)
	set foldersetnames to my getfoldersetnames(showhidden)
	if foldersetnames is missing value then
		return missing value
	else
		choose from list foldersetnames with title mytitle with prompt "Please choose one or more folder sets:" with multiple selections allowed without empty selection allowed
		set choice to result
		if choice is not false then
			set chosenfoldersetnames to (choice as list)
			return chosenfoldersetnames
		else
			return missing value
		end if
	end if
end choosefoldersetnames

-- I am adding a new folder set to the existing database of
-- folder sets
on miaddfset()
	set srcfolder to my choosesrcfolder()
	if srcfolder is not missing value then
		set relfolders to my searchrelfolders(srcfolder)
		if relfolders is not missing value then
			-- for enhanced convenience, we suggest the user to use
			-- the name of the above chosen source folder for the
			-- new folder set
			set srcfolderinfo to info for (srcfolder as alias)
			set srcfoldername to displayed name of srcfolderinfo
			set newfoldersetname to my getnewfoldersetname(srcfoldername)
			if newfoldersetname is not missing value then
				my addfolderset(newfoldersetname, relfolders)
			end if
		end if
	end if
end miaddfset

-- I am deleting one or more chosen folder sets from the database file
on midelfsets()
	-- we don't want the user to delete the internally used folder set names
	-- ('Last folder names', 'Recent folder names'), so we set
	-- «showhidden» to false
	set showhidden to false
	set chosenfoldersetnames to my choosefoldersetnames(showhidden)
	if chosenfoldersetnames is not missing value then
		repeat with chosenfoldersetname in chosenfoldersetnames
			my delfolderset(chosenfoldersetname)
		end repeat
	end if
end midelfsets

-- I am renaming an existing folder set
on mirenfset()
	-- we don't want the user to rename the internally used folder set names
	-- ('Last folder names', 'Recent folder names'), so we set
	-- «showhidden» to false
	set showhidden to false
	set oldfoldersetname to my choosefoldersetname(showhidden)
	if oldfoldersetname is not missing value then
		set newfoldersetname to my getnewfoldersetname(oldfoldersetname)
		if newfoldersetname is not missing value then
			my renamefolderset(oldfoldersetname, newfoldersetname)
		end if
	end if
end mirenfset

-- I am displaying a list of available folder sets
on mishowfsets()
	-- let's hide the internally used folder sets from the user's eyes
	set showhidden to false
	set foldersetname to my choosefoldersetname(showhidden)
	if foldersetname is not missing value then
		set relfolders to my getfolderset(foldersetname)
		if relfolders is {} then
			set errmsg to "Sorry, the folder set '" & foldersetname & "' does not contain any folder names."
			my dsperrmsg(errmsg, "--")
		else
			choose from list relfolders with title mytitle with prompt "Folder names of folder set '" & foldersetname & "':" with empty selection allowed
		end if
	end if
end mishowfsets

-- I am adding new folder names to an already existing folder set
on miaddfolderstofset()
	-- let's hide the internally used folder sets from the user's eyes
	set showhidden to false
	set foldersetname to my choosefoldersetname(showhidden)
	if foldersetname is not missing value then
		tell me
			activate
			display dialog "Do you want to manually enter the additional folder names or do you want to choose a folder?" buttons {"Choose folder", "Enter manually"} default button 2 with title mytitle
			set dlgresult to result
		end tell
		if button returned of dlgresult is "Choose folder" then
			set srcfolder to my choosesrcfolder()
			if srcfolder is not missing value then
				set relfolders to my searchrelfolders(srcfolder)
			else
				set relfolders to missing value
			end if
		else if button returned of dlgresult is "Enter manually" then
			set relfolders to my askforfoldernames()
		end if
		if relfolders is not missing value then
			my addrelfolderstofolderset(foldersetname, relfolders)
		end if
	end if
end miaddfolderstofset

-- I am removing selected folder names from a folder set
on midelfoldersfromfset()
	-- let's hide the internally used folder sets from the user's eyes
	set showhidden to false
	set foldersetname to my choosefoldersetname(showhidden)
	if foldersetname is not missing value then
		set relfolders to my getfolderset(foldersetname)
		if relfolders is {} then
			set errmsg to "Sorry, the folder set '" & foldersetname & "' does not contain any folder names."
			my dsperrmsg(errmsg, "--")
		else
			choose from list relfolders with title mytitle with prompt "Please choose folder names to delete from the folder set '" & foldersetname & "':" cancel button name "Cancel" OK button name "Delete" with multiple selections allowed without empty selection allowed
			set choice to result
			if choice is not false then
				set relfolders to (choice as list)
				my delrelfoldersfromfolderset(foldersetname, relfolders)
			end if
		end if
	end if
end midelfoldersfromfset

-- I am creating a new folder set from selected recent or last folder names
on micrtfsetfromsysfolders(foldersetname)
	if not my foldersetnameexists(foldersetname) then
		set errmsg to "Sorry, the folder set '" & foldersetname & "' does not exist in the database."
		my dsperrmsg(errmsg, "--")
	else
		set relfolders to my getfolderset(foldersetname)
		if relfolders is {} then
			set errmsg to "Sorry, the folder set '" & foldersetname & "' does not contain any folder names."
			my dsperrmsg(errmsg, "--")
		else
			choose from list relfolders with title mytitle with prompt "Please choose folder names from the folder set '" & foldersetname & "' to create a new folder set:" with multiple selections allowed without empty selection allowed
			set choice to result
			if choice is not false then
				set relfolders to (choice as list)
				set newfoldersetname to my getnewfoldersetname("")
				if newfoldersetname is not missing value then
					my addfolderset(newfoldersetname, relfolders)
				end if
			end if
		end if
	end if
end micrtfsetfromsysfolders

-- I am adding selected recent or last folder names to an existing folder set
on miaddsysfolderstofset(foldersetname)
	if not my foldersetnameexists(foldersetname) then
		set errmsg to "Sorry, the folder set '" & foldersetname & "' does not exist in the database."
		my dsperrmsg(errmsg, "--")
	else
		set relfolders to my getfolderset(foldersetname)
		if relfolders is {} then
			set errmsg to "Sorry, the folder set '" & foldersetname & "' does not contain any folder names."
			my dsperrmsg(errmsg, "--")
		else
			choose from list relfolders with title mytitle with prompt "Please choose folder names from the folder set '" & foldersetname & "' to add them to an existing folder set:" with multiple selections allowed without empty selection allowed
			set choice to result
			if choice is not false then
				set relfolders to (choice as list)
				-- let's hide the internally used folder sets from the user's eyes
				set showhidden to false
				set foldersetname to my choosefoldersetname(showhidden)
				if foldersetname is not missing value then
					my addrelfolderstofolderset(foldersetname, relfolders)
				end if
			end if
		end if
	end if
end miaddsysfolderstofset

-- I am opening the database file in the default application
on miopendbfile()
	-- path to my application support folder
	set myasfpath to (((path to application support folder from user domain) as Unicode text) & mytitle & ":")
	-- path to the database file located in my application support folder
	set dbfilepath to quoted form of (POSIX path of (myasfpath & "foldersets.db"))
	set command to "open" & space & dbfilepath
	set command to command as «class utf8»
	do shell script command
end miopendbfile

-- I am asking the user to provide a new folder set name
on getnewfoldersetname(suggestion)
	try
		tell me
			display dialog "Please choose a name for your new folder set:" default answer suggestion buttons {"Cancel", "Select"} default button 2 with title mytitle
			set dlgresult to result
		end tell
	on error errmsg number errnum
		if errnum is equal to -128 then
			return missing value
		else
			error errmsg number errnum
		end if
	end try
	set newfoldersetname to text returned of dlgresult
	if newfoldersetname is "" then
		my getnewfoldersetname(suggestion)
	else
		if my foldersetnameexists(newfoldersetname) then
			set errmsg to "The chosen folder set name '" & newfoldersetname & "' already exists in the database. " & return & return & newfoldersetname & return & return & "Please choose a different folder set name."
			my dsperrmsg(errmsg, "--")
			my getnewfoldersetname(suggestion)
		else
			return newfoldersetname
		end if
	end if
end getnewfoldersetname

-- I am asking the user to choose a source folder to search for contained (sub)folder names
on choosesrcfolder()
	try
		set srcfolder to choose folder with prompt "Please choose a source folder:" without multiple selections allowed
		return srcfolder
	on error errmsg number errnum
		if errnum is equal to -128 then
			return missing value
		end if
	end try
end choosesrcfolder

-- I am asking the user to choose a folder and return the relative folders contained therein
on searchrelfolders(srcfolder)
	set srcfolder to POSIX path of (srcfolder as Unicode text)
	tell me
		activate
		display dialog "Do you want to scan for subfolders beyond the first level?" buttons {"Yes", "No"} default button 2 with title mytitle
		set dlgresult to result
	end tell
	-- if the user choosed to scan subfolders, we set a warn level
	-- if we find more folders than set by the warn level (e.g. > 250),
	-- we display a dialog and ask the user if we should continue the scan process
	-- this is to avoid problems, that might occur, when the user accidently choosed
	-- the wrong source folder, e.g. the hard disk itself...
	if button returned of dlgresult is "Yes" then
		set subfolders to true
		set warnlevel to 250
	else if button returned of dlgresult is "No" then
		set subfolders to false
		set warnlevel to false
	end if
	set relfolders to my aspybridge("getrelfolders", {srcfolder, subfolders, warnlevel}, "lines")
	if relfolders is {} then
		set errmsg to "Sorry, your source folder did not contain any folders."
		my dsperrmsg(errmsg, "--")
		return missing value
	else if (item 1 of relfolders) begins with "<<Error>>" then
		set warnmsg to "Warning: " & return & return & ((characters 11 through -1 of (item 1 of relfolders)) as Unicode text) & return & return & "Do you want to continue anyway?"
		try
			tell me
				activate
				display dialog warnmsg buttons {"Cancel", "Continue"} default button 1 with title mytitle with icon caution
			end tell
		on error errmsg number errnum
			if errnum is equal to -128 then
				return missing value
			else
				error errmsg number errnum
			end if
		end try
		set warnlevel to false
		set relfolders to my aspybridge("getrelfolders", {srcfolder, subfolders, warnlevel}, "lines")
		return relfolders
	else
		return relfolders
	end if
end searchrelfolders

-- I am asking the user to provide (sub)folder names, either by entering
-- them manually or by choosing an existing folder set
-- Yes, I am ugly...
on askforrelfoldernames(mode, countselfolders)
	if countselfolders is equal to 1 then
		if mode is "on run" then
			set selfoldmsg to "» You have selected 1 folder in the Finder «"
		else if mode is "on open" then
			set selfoldmsg to "» You dropped 1 folder onto " & mytitle & " «"
		end if
	else
		if mode is "on run" then
			set selfoldmsg to "» You have selected " & countselfolders & " folders in the Finder «"
		else if mode is "on open" then
			set selfoldmsg to "» You dropped " & countselfolders & " folders onto " & mytitle & " «"
		end if
	end if
	-- if the folder set 'Last folder names' exists and contains
	-- folder names, we conveniently use them for the default answer
	set defanswer to ""
	set foldersetname to "Last folder names"
	if my foldersetnameexists(foldersetname) then
		set relfolders to my getfolderset(foldersetname)
		if relfolders is not {} then
			set countrelfolders to length of relfolders
			repeat with i from 1 to countrelfolders
				set relfolder to item i of relfolders
				if i is equal to countrelfolders then
					set defanswer to defanswer & relfolder
				else
					set defanswer to defanswer & relfolder & ":"
				end if
			end repeat
		end if
	end if
	-- if no folder sets exist so far, we do not show the «Folder sets»-button
	set showhidden to true
	set foldersetnames to my aspybridge("getfsetnames", {showhidden}, "lines")
	if foldersetnames is {} then
		set dlgbuttons to {"Cancel", "Enter"}
	else
		set dlgbuttons to {"Folder sets", "Cancel", "Enter"}
	end if
	-- the dialog
	try
		set msg to "Please enter your desired folder names manually or choose folder sets:" & return & return & "(Enter multiple folder names separated by a colon," & return & "enter subfolder names separated by a slash)" & return & return & selfoldmsg
		tell me
			activate
			display dialog msg default answer defanswer buttons {"Folder sets", "Cancel", "Enter"} default button 3 with title mytitle
			set dlgresult to result
		end tell
	on error errmsg number errnum
		if errnum is equal to -128 then
			return missing value
		else
			error errmsg number errnum
		end if
	end try
	if button returned of dlgresult is "Enter" then
		set usrinput to text returned of dlgresult
		if usrinput is "" then
			my askforrelfoldernames(mode, countselfolders)
		else
			set relfoldernames to my checkenteredfoldernames(usrinput)
			-- oops, no valid folder names
			if relfoldernames is {} then
				my askforrelfoldernames(mode, countselfolders)
			else
				-- saving entered folder names to the 'Last folder names'-folder set
				if my foldersetnameexists("Last folder names") then
					my delfolderset("Last folder names")
				end if
				my addfolderset("Last folder names", relfoldernames)
				-- saving entered folder names to the 'Recent folder names'-folder set
				if my foldersetnameexists("Recent folder names") then
					my addrelfolderstofolderset("Recent folder names", relfoldernames)
				else
					my addfolderset("Recent folder names", relfoldernames)
				end if
				return relfoldernames
			end if
		end if
	else if button returned of dlgresult is "Folder sets" then
		-- we want the user to see the internally used folder sets, so that
		-- he can choose folder names from them
		set showhidden to true
		set foldersetnames to my choosefoldersetnames(showhidden)
		if foldersetnames is missing value then
			my askforrelfoldernames(mode, countselfolders)
		else
			set relfoldernames to {}
			repeat with foldersetname in foldersetnames
				set relfolders to my getfolderset(foldersetname)
				if relfolders is {} then
					set errmsg to "Sorry, the folder set '" & foldersetname & "' does not contain any folder names."
					my dsperrmsg(errmsg, "--")
				else
					if foldersetname is in {"Last folder names", "Recent folder names"} then
						choose from list relfolders with prompt "Please choose folder names from the folder set '" & foldersetname & "':" with title mytitle with multiple selections allowed without empty selection allowed
						set choice to result
						if choice is not false then
							set relfolders to (choice as list)
						else
							set relfolders to {}
						end if
					end if
					repeat with relfolder in relfolders
						if relfolder is not in relfoldernames then
							set relfoldernames to relfoldernames & relfolder
						end if
					end repeat
				end if
			end repeat
			if relfoldernames is {} then
				my askforrelfoldernames(mode, countselfolders)
			else
				return relfoldernames
			end if
		end if
	end if
end askforrelfoldernames

-- I am also asking the user to provide (sub)folder names, but am used
-- from within the options panel
on askforfoldernames()
	set msg to "Please enter your desired folder names:" & return & return & "(Enter multiple folder names separated by a colon," & return & "enter subfolders separated by a slash)"
	try
		tell me
			activate
			display dialog msg default answer "" buttons {"Cancel", "Enter"} default button 2 with title mytitle
			set dlgresult to result
		end tell
	on error errmsg number errnum
		if errnum is equal to -128 then
			return missing value
		else
			error errmsg number errnum
		end if
	end try
	set usrinput to text returned of dlgresult
	if usrinput is "" then
		my askforfoldernames()
	else
		set foldernames to my checkenteredfoldernames(usrinput)
		-- oops, no valid folder names
		if foldernames is {} then
			my askforfoldernames()
		else
			return foldernames
		end if
	end if
end askforfoldernames

-- I am validating the entered folder names
on checkenteredfoldernames(usrinput)
	considering case, diacriticals and punctuation
		set olddelims to AppleScript's text item delimiters
		set AppleScript's text item delimiters to ":"
		set inputfoldernames to text items of usrinput
		set AppleScript's text item delimiters to olddelims
	end considering
	-- sorting out 'empty' entries, deleting double entries, ignoring invalid entries
	set foldernames to {}
	repeat with inputfoldername in inputfoldernames
		set inputfoldername to inputfoldername as Unicode text
		if inputfoldername is not "" and inputfoldername is not in foldernames and inputfoldername does not start with "/" then
			set foldernames to foldernames & inputfoldername
		end if
	end repeat
	return foldernames
end checkenteredfoldernames

-- I am returning a list of available folder set names in the database file
-- If there are no folder set names available, I display an error message and
-- return «missing value»
on getfoldersetnames(showhidden)
	set foldersetnames to my aspybridge("getfsetnames", {showhidden}, "lines")
	if foldersetnames is {} then
		set errmsg to "Your database file does not contain any folder set names."
		my dsperrmsg(errmsg, "--")
		return missing value
	else
		return foldersetnames
	end if
end getfoldersetnames

-- I am adding a new folder set to the database file
on addfolderset(foldersetname, relfolders)
	my aspybridge("addfset", {foldersetname, relfolders}, missing value)
end addfolderset

-- I am deleting a folder set
on delfolderset(foldersetname)
	my aspybridge("delfset", {foldersetname}, missing value)
end delfolderset

-- I am renaming a folder set
on renamefolderset(oldfoldersetname, newfoldersetname)
	my aspybridge("renfset", {oldfoldersetname, newfoldersetname}, missing value)
end renamefolderset

-- I am returning the folder names saved in a given folder set (name)
on getfolderset(foldersetname)
	set relfolders to my aspybridge("getfset", {foldersetname}, "lines")
	return relfolders
end getfolderset

-- I am adding a list of folder names to a given folder set (name)
on addrelfolderstofolderset(foldersetname, relfolders)
	my aspybridge("addrelfolderstofset", {foldersetname, relfolders}, missing value)
end addrelfolderstofolderset

-- I am deleting a list of folder names from a given folder set (name)
on delrelfoldersfromfolderset(foldersetname, relfolders)
	my aspybridge("delrelfoldersfromfset", {foldersetname, relfolders}, missing value)
end delrelfoldersfromfolderset

on foldersetnameexists(foldersetname)
	return (my aspybridge("hasfsetname", {foldersetname}, "boolean"))
end foldersetnameexists

on getquotedstringfolders(relfolders)
	set countrelfolders to length of relfolders
	set quotedstringfolders to ""
	repeat with i from 1 to countrelfolders
		set relfolder to item i of relfolders
		if i is equal to countrelfolders then
			set quotedstringfolders to quotedstringfolders & quoted form of relfolder
		else
			set quotedstringfolders to quotedstringfolders & quoted form of relfolder & space
		end if
	end repeat
	return quotedstringfolders
end getquotedstringfolders

-- I am the connection to the Python script controlling and managing the
-- database file. Yes, I am ugly, but I am hard-working and busy as a bee.
on aspybridge(action, args, returntype)
	-- pyth to myself
	set mypath to ((path to me) as Unicode text)
	-- path to the Python script
	set pyscriptpath to quoted form of (POSIX path of (mypath & "Contents:Resources:Scripts:aspybridge.py"))
	-- path to my application support folder
	set myasfpath to (((path to application support folder from user domain) as Unicode text) & mytitle & ":")
	-- path to the database file located in my application support folder
	set dbfilepath to quoted form of (POSIX path of (myasfpath & "foldersets.db"))
	
	if action is "getfsetnames" then
		set showhidden to item 1 of args
		set command to "python" & space & pyscriptpath & space & action & space & showhidden & space & dbfilepath
		
	else if action is "addfset" then
		set foldersetname to item 1 of args
		set quotedstringfolders to my getquotedstringfolders(item 2 of args)
		set command to "python" & space & pyscriptpath & space & action & space & quoted form of foldersetname & space & quotedstringfolders & space & dbfilepath
		
	else if action is "delfset" then
		set foldersetname to item 1 of args
		set command to "python" & space & pyscriptpath & space & action & space & quoted form of foldersetname & space & dbfilepath
		
	else if action is "getrelfolders" then
		set srcfolder to item 1 of args
		set subfolders to (item 2 of args) as Unicode text
		set warnlevel to (item 3 of args) as Unicode text
		set command to "python" & space & pyscriptpath & space & action & space & quoted form of srcfolder & space & subfolders & space & warnlevel & space & dbfilepath
		
	else if action is "hasfsetname" then
		set foldersetname to item 1 of args
		set command to "python" & space & pyscriptpath & space & action & space & quoted form of foldersetname & space & dbfilepath
		
	else if action is "renfset" then
		set oldfoldersetname to item 1 of args
		set newfoldersetname to item 2 of args
		set command to "python" & space & pyscriptpath & space & action & space & quoted form of oldfoldersetname & space & quoted form of newfoldersetname & space & dbfilepath
		
	else if action is "addrelfolderstofset" then
		set foldersetname to item 1 of args
		set quotedstringfolders to my getquotedstringfolders(item 2 of args)
		set command to "python" & space & pyscriptpath & space & action & space & quoted form of foldersetname & space & quotedstringfolders & space & dbfilepath
		
	else if action is "delrelfoldersfromfset" then
		set foldersetname to item 1 of args
		set quotedstringfolders to my getquotedstringfolders(item 2 of args)
		set command to "python" & space & pyscriptpath & space & action & space & quoted form of foldersetname & space & quotedstringfolders & space & dbfilepath
		
	else if action is "getfset" then
		set foldersetname to item 1 of args
		set command to "python" & space & pyscriptpath & space & action & space & quoted form of foldersetname & space & dbfilepath
		
	end if
	
	set command to command as «class utf8»
	set shellresult to do shell script command
	
	if returntype is "lines" then
		return (paragraphs of shellresult)
	else if returntype is "boolean" then
		if shellresult is in {"True", "1"} then
			return true
		else if shellresult is in {"False", "0"} then
			return false
		end if
	else
		return shellresult
	end if
end aspybridge

-- I am displaying a welcome message to uses who start subidoo for the first time
on dspwelcomemsg()
	tell application "System Events"
		set usrname to (full name of current user) as Unicode text
	end tell
	set welcomemsg to "Hello " & usrname & ", and welcome to " & mytitle & "." & return & return & "It seems, that you are using this application for the first time." & return & return & "If you want to manage your folder sets, just start the application by double-clicking its icon without any items selected in the Finder." & return & return & "If you want to create subfolders, just drop folders onto " & mytitle & "'s icon or select folders in the Finder before starting " & mytitle & "." & return & return & "Have fun!"
	tell me
		activate
		display dialog welcomemsg with title mytitle buttons {"OK"} default button 1
	end tell
end dspwelcomemsg

-- I am displaying error messages
on dsperrmsg(errmsg, errnum)
	tell me
		activate
		display dialog "Sorry, an error occured:" & return & return & errmsg & " (" & errnum & ")" buttons {"Never mind"} default button 1 with icon stop with title mytitle
	end tell
end dsperrmsg