A “tag selector”, akin to “group selector”

Well, what you want is a set that is the union of all the tags of the selection (minus the folder tags). Applescript doesn’t have any “canned” resources to support set functions on lists, but LateNight Software has a scripting addition that will:

http://www.latenightsw.com/freeware/RecordTools/index.html

or you could move to a language like Python or Ruby where there’s a lot more offered than just sets…

HTH, Charles

Ah, thank you very much for this tip cturner. I will check this out :smiley:

This works for me:

tell application id "DNtp"	
	try
		set this_selection to the selection
		if this_selection is {} then error "Please select some items."
		
		set theTags to name of every parent of current database whose tag type is ordinary tag
		
		set myTags to (choose from list theTags with multiple selections allowed)
		
		repeat with this_item in this_selection
			set theTags to tags of this_item
			if exists tags of this_item then
				set current_tags to the tags of this_item
				set new_tags to current_tags & myTags
			else
				set new_tags to myTags
			end if
			set the tags of this_item to new_tags
		end repeat
	on error error_message number error_number
		if the error_number is not -128 then display alert "DEVONthink" message error_message as warning
	end try
end tell

I just added a script-subroutine to alphabetically sort the list:
(Thanks to https://macosxautomation.com/applescript/sbrt/sbrt-05.html), and changed a few lines

tell application id "DNtp"
	try
		set this_selection to the selection
		if this_selection is {} then error "Please select some items."
		set taglist to name of every parent of current database whose tag type is ordinary tag
		--#################################
		set theTags to simple_sort(taglist) of me
		--#################################
		set myTags to (choose from list theTags with multiple selections allowed)
		repeat with this_item in this_selection
			set theTags to tags of this_item
			if exists tags of this_item then
				set current_tags to the tags of this_item
				set new_tags to current_tags & myTags
			else
				set new_tags to myTags
			end if
			set the tags of this_item to new_tags
		end repeat
	on error error_message number error_number
		if the error_number is not -128 then display alert "DEVONthink" message error_message as warning
	end try
end tell

on simple_sort(my_list)
	set the index_list to {}
	set the sorted_list to {}
	repeat (the number of items in my_list) times
		set the low_item to ""
		repeat with i from 1 to (number of items in my_list)
			if i is not in the index_list then
				set this_item to item i of my_list as text
				if the low_item is "" then
					set the low_item to this_item
					set the low_item_index to i
				else if this_item comes before the low_item then
					set the low_item to this_item
					set the low_item_index to i
				end if
			end if
		end repeat
		set the end of sorted_list to the low_item
		set the end of the index_list to the low_item_index
	end repeat
	return the sorted_list
end simple_sort
1 Like

@duende54498 This script is awesome. Thanks for sharing!

Happy you find it usefull

@duende54498 I love using your script, and was wondering if it’s possible to modify it so that it lists all of the user’s tags from their databases (i.e., as opposed to only showing the tags that currently exist in the database where the file is located)?

I’ve tried adapting your script several times, but haven’t had any luck so far. Thanks for any help you can lend!

As I don’t work with multiple databases, I’m not really savvy on that topic.
You might try something like:
“set taglist to name of every parent of current database whose tag type is ordinary tag”
and then incorporate a subroutine to split the individual tag-names
or something similar
Good luck!

@duende54498 Thanks for getting back to me!

I tried several variations of the line you’ve listed above, and I think the following gets closest:

set taglist to name of every parent of every database whose tag type is ordinary tag

The output is pretty wonky, however - as it spits out a massive horizontal block of tags that all run together. As you can tell from this description, I’m a newbie! I suspect the issue is with the lack of a subroutine for splitting the tags out, as you note above. I’ll keep looking around and tinkering with the script, and let you know how it goes.

By chance, do you know of any good resources for scripting newbies that might deal with similar issues that I could learn from? As always, thanks for your help!

Help > Documentation > Automation > AppleScript

Why are you trying to get a list of all the tags in every database?

@duende54498 Thanks again for all of your help with everything. I was able to cobble your script together with a few others to get it working for all the databases at once.

In case you might find it useful, I’ve included it below. I’m sure there are more clean and efficient ways of combining the two subroutines, but this seems to work pretty well. As usual, all the usual caveats apply:

tell application id "DNtp"
	try
		set this_selection to the selection
		if this_selection is {} then error "Please select some items."
		
		set theDatabases to databases
		set theResults to {}
		repeat with thisDatabase in theDatabases
			set thisDatabasesResults to (name of every parent in thisDatabase whose tag type is ordinary tag)
			set theResults to theResults & thisDatabasesResults
		end repeat
		set taglistduplicates to theResults
		
		set taglist to RemoveDuplicates(taglistduplicates) of me
		set theTags to simple_sort(taglist) of me
		
		set myTags to (choose from list theTags with multiple selections allowed)
		repeat with this_item in this_selection
			set theTags to tags of this_item
			if exists tags of this_item then
				set current_tags to the tags of this_item
				set new_tags to current_tags & myTags
			else
				set new_tags to myTags
			end if
			set the tags of this_item to new_tags
		end repeat
	on error error_message number error_number
		if the error_number is not -128 then display alert "DEVONthink" message error_message as warning
	end try
end tell

on simple_sort(my_list)
	set the index_list to {}
	set the sorted_list to {}
	repeat (the number of items in my_list) times
		set the low_item to ""
		repeat with i from 1 to (number of items in my_list)
			if i is not in the index_list then
				set this_item to item i of my_list as text
				if the low_item is "" then
					set the low_item to this_item
					set the low_item_index to i
				else if this_item comes before the low_item then
					set the low_item to this_item
					set the low_item_index to i
				end if
			end if
		end repeat
		set the end of sorted_list to the low_item
		set the end of the index_list to the low_item_index
	end repeat
	return the sorted_list
end simple_sort

on RemoveDuplicates(inputList)
	set outputList to {}
	repeat with i from 1 to length of inputList
		set thisItem to item i of inputList
		set testItem to {thisItem}
		if (outputList does not contain testItem) then
			set end of outputList to thisItem
		end if
	end repeat
	return outputList
end RemoveDuplicates

#Sources
	#https://discourse.devontechnologies.com/t/a-tag-selector-akin-to-group-selector/9755/24
	#https://macosxautomation.com/applescript/sbrt/sbrt-05.html
	#https://discourse.devontechnologies.com/t/script-lookup-safari-url-in-open-databases/24989
	#https://macscripter.net/viewtopic.php?id=8372

Here’s a revised script that uses the foundation framework to speed up sorting & filtering of tags (in case of hundreds or even thousands of tags this makes a huge difference):

use AppleScript version "2.4"
use framework "Foundation"
use scripting additions

tell application id "DNtp"
	try
		set this_selection to the selection
		if this_selection is {} then error "Please select some items."
		
		set theDatabases to databases
		set rawTags to {}
		repeat with thisDatabase in theDatabases
			set thisDatabaseTags to (name of every parent in thisDatabase whose tag type is ordinary tag)
			set rawTags to rawTags & thisDatabaseTags
		end repeat
		
		tell current application's NSSet to set tagSet to setWithArray_(rawTags)
		tell current application's NSSortDescriptor to set theDescriptor to sortDescriptorWithKey_ascending_(missing value, true)
		set theTags to (tagSet's sortedArrayUsingDescriptors:{theDescriptor}) as list
		
		set selectedTags to (choose from list theTags with multiple selections allowed)
		repeat with this_item in this_selection
			if exists tags of this_item then
				set itemTags to the tags of this_item
				set itemTags to itemTags & selectedTags
				set the tags of this_item to itemTags
			else
				set the tags of this_item to selectedTags
			end if
		end repeat
	on error error_message number error_number
		if the error_number is not -128 then display alert "DEVONthink" message error_message as warning
	end try
end tell
2 Likes

Great job guys!

Thanks a ton @cgrunenberg!!