Applescripting Dictionary.app

Hey guys,

I know that you can not really do much applescripting with the Dictionary.app, however if there is anyway around what I am trying to accomplish, please assist me.

What I am trying to do is simply create an applescript that will record in a text file on my desktop every word that I look up. It does not need to file the definitions but just the entry words, this way I can have a running record of every word that I look up.

Is there any way this could be done, or is this impossible.

Thanks.

Perhaps the easiest way to approach this would be to use a script which both enters words into Dictionary for you and appends them to the log. Then you just have to get used to using it instead of firing up Dictionary directly. The basic idea is this:

tell application (path to frontmost application as text)
	set searchWord to text returned of (display dialog "Enter a search term." default answer "" with title "Dictionary Search and Log")
end tell

open location "dict:///" & searchWord

set fRef to (open for access file "path:to:log file" with write permission) -- Use the correct path or path-finding code here.
try
	write (searchWord & linefeed) as «class utf8» to fRef starting at eof
end try
close access fRef

In a full script, you might want to add a check for duplicates in the log.

Scripting is a bit like fighting Hydra; for each new script, two new ideas are born.

Scripting Dictionary.app, is something I have wondered about a long time, how to get those words, for repetition, me being a foreigner and all.

I’ll just share my idea for now, and maybe come back to it. My idea is to implement a service, that works towards selections of documents. I think one would have to use System Events and the process name to get there, but maybe Automator helps.

Then make a copy of the selection from within the service, and then pass it on to Dictionary app. after first checking if indeed such a word exists in Websters. Which is not tested at this point in time.

There are things to this, how dictionary app is set up and such, that has to be considered, if it shows up the dictionary window, or just the box.

Another idea, as a service maybe just a bit awkward, (but you can override the keystroke cntrl-cmd D for it), could be to write a faceless background app, that intercepts calls to the dict protocol, and “steal the words” before calling dictionary.app.

This may thouh lead to problems with Spotlight, I am not sure of it.

I hope my idea helps, and are well worth investigating. I would love to see a result of this.

Awkward when you can’t get stuff out of your head!

Well, I fired up Automator, and created a text service. That took selected text in any program.

Then I coupled in a “Run Applescript procedure” below.

The code inside the Applescript “Automator-procedure” this: Don’t mock it, it works for me! :slight_smile:


on run {input, parameters}
	set glossaryName to "DictLookup.txt"
	set AutomatorIcon to (a reference to file ((path to applications folder as text) & "Automator.app:Contents:Resources:Automator.icns"))
	
	# checks to see if the current selection contains anything valid
	
	#	set theProbe to input as text
	considering diacriticals
		if first character of (input as text) is not in "abcdefghijklmnopqrstuvwxyz" then
			tell application "System Events" to set appname to name of first process whose frontmost is true
			using terms from application "Finder"
				tell application appname
					display dialog "The selection you tried to look up a dictionary definiton for contains non-valid characters.

Please copy the selection you used into an empty text document to figure out what is wrong.

Quitting for now." with title "Dictionary Logging Service" buttons {"Ok"} default button 1 with icon AutomatorIcon
					
				end tell
			end using terms from
			return input
		end if
	end considering
	try
		open location "dict://" & input
	end try
	tell application "TextEdit"
		try
			set glossaryDoc to its document glossaryName
		on error
			set glossaryDoc to null
		end try
	end tell
	
	try
		set theF to quoted form of (POSIX path of (path to desktop folder as text) & glossaryName)
	end try
	
	set foundword to true
	try
		do shell script "test -f " & theF & " || touch " & theF
		
		set foundword to (do shell script "grep '^" & input & "$' " & theF & ">/dev/null && echo \"true\" || echo \"false\"") as boolean
	end try
	
	if not foundword then
		
		if glossaryDoc is not null then
			tell application "TextEdit"
				tell its document glossaryName
					
					make new paragraph at beginning of its text with data ((input as text) & linefeed)
				end tell
				save glossaryDoc
			end tell
		else
			try
				do shell script "export TMPFILE=`mktemp /tmp/${tempfoo}.XXXXXX` && cat " & theF & "  >$TMPFILE ; echo " & input & ">|" & theF & " ; cat $TMPFILE >>" & theF
			end try
		end if
		
	end if
	do shell script "open -b \"com.apple.Dictionary\""
	
	return input
	
end run

It is of course awkward that you have to select the word, not just hoovering the mouse over it while you press cntrl-cmd D, but this is the best I can do at this time, as the probability for success with a couple of other schemes are less. So this, combined with Nigels solution. (Remember to use the same filename) should cover it for you, except for when firing up Dictionary from Spotlight.

by the way “correct path-finding code”

¢ as path to is part of Standard Additions, the Finder is not needed
¢ class alias has a property POSIX path, therefore the parameter as text is not needed
¢ the try block is not needed too, because the desktop exists always

set theF to quoted form of (POSIX path of (path to desktop folder) & "DictLookup.txt")

Hello!

I am perfectly aware of what you wrote, but thanks for your concern anywany. :slight_smile:

That was a slip, but on the other hand, it wasn’t that easy to make it work under Automator in the first place. write with «class utf8» was not a success for instance, or if it was starting at eof! And I could just forget about seeing any log statements that gave sense from within there.

The try statements stays!

I have however updated the script above now, which is also updated to function better! :smiley:

Edit:

I wasn’t aware of that posix path of is a property of an alias, but under normal circumstances I figured it would coerce to text beautifully, (the alias), but Automator, was a rather abnormal experience this time! Ergo…

Edit++

It now creates the file correctly the first time it is run.

Of course it would be nice to be able to log clicked words within Dictionary as well. :frowning:

:slight_smile:

That would have been nice, but this only works when services for text work. Now, maybe it is possible to dive into the cocoa text subsystem and do something with a “word under cursor” routine. That is, if such a routine exists. And I’d think something would have to be written in cocoa to pull it off.

So, I don’t think this can be pulled much further. However, it does work when you use Dictionary.app from spotlight, if you right-click the selected text and choose services.

Edit

In the mean time, it could be possible to display a dialog with a grep pattern, that gets the matches from websters, that can then be selected, put into the log file, and shown in Dictionary.app

Since no services or anything is working in Dictionary.app’s main page with words, I guess you would really have to resort to UIscripting, which is rather laborious and luxurious, that should be implemented by an ambidextrous person as yourself. :wink:

In the mean time, I’m hacking up something with your solution and grep!

Hope you can use this Nigel, it is great fun! :slight_smile:


script DictionaryIndexAndLog
	# © MacUser06/McUsr 2012 in collaboration with Nigel Garvey! You may not post this elsewhere nor infringe it.
	property scriptTitle : "Dictionary Index and log"
	property dictionaryIcon : (a reference to file ((path to applications folder as text) & "Dictionary.app:Contents:Resources:Dictionary.icns"))
	property strictlyForDictionary : false # change this to true if you want only strictlyForDictionary matches for dictionary, AND to avoid typing "^"AND "$" ;) ( you can also change the parameters for grep to allow for Extended or Perl patterns (man grep)
	# This is great for learning the basics of regexp!!!
	property cachespath : ((path to library folder from user domain as text) & "caches:" & "net.mcusr.Dictionary.Websters.lookup")
	property glossaryName : "DictLogger.txt"
	local script_cache
	try
		set script_cache to load script alias cachespath
	on error
		script newScriptCache
			property searchWord : ""
		end script
		set script_cache to newScriptCache
	end try
	
	local formerSearch, searchWord
	set searchWord to script_cache's searchWord
	local failed
	set failed to false
	
	tell application (path to frontmost application as text)
		try
			set searchWord to text returned of (display dialog "Enter a search term, Miniminum one letter. Wildcards where you like them!" with title my scriptTitle default answer searchWord with icon dictionaryIcon)
		on error
			set failed to true
		end try
	end tell
	if searchWord = "" or failed then
		return 0
	else
		if strictlyForDictionary then
			set searchWord to quoted form of ("^" & searchWord & "$")
		else
			set searchWord to quoted form of searchWord
		end if
		local searchresult
		try
			set searchresult to (do shell script "egrep " & searchWord & " /usr/share/dict/web2 ; exit 0 ")
		end try
		if searchresult = "" then
			tell application (path to frontmost application as text)
				try
					display dialog "I didn't find any match" with title my scriptTitle with icon dictionaryIcon buttons {"Ok"} default button 1
				end try
				# set searchWord to formerSearch
			end tell
		else
			# We save the successful search
			if strictlyForDictionary then
				set searchWord to text 3 thru -3 of searchWord
			else
				set searchWord to text 2 thru -2 of searchWord
			end if
			
			set script_cache's searchWord to searchWord
			store script script_cache in cachespath replacing yes
			
			local wordlist
			set wordlist to every paragraph of searchresult
			tell me to activate
			try
				set wordToLookup to choose from list wordlist default items item 1 of wordlist with prompt "Choose word to lookup" with title my scriptTitle
				
			on error
				tell application (path to frontmost application as text)
					try
						display dialog "Too many words to show in list! Please try again!" with title my scriptTitle with icon dictionaryIcon buttons {"Ok"} default button 1
						set wordToLookup to false
					end try
				end tell
				
			end try
			
			if wordToLookup is not false then
				
				local glossaryfile
				set glossaryfile to ((path to desktop folder as text) & glossaryName)
				try
					set theF to quoted form of POSIX path of glossaryfile
				end try
				log theF
				local foundword
				set {foundword, wordToLookup} to {true, wordToLookup as text}
				tell application "Dictionary" to open location "dict:///" & wordToLookup
				try
					do shell script "test -f " & theF & " || touch " & theF
					
					set foundword to (do shell script "grep '^" & wordToLookup & "$' " & theF & ">/dev/null && echo \"true\" || echo \"false\"") as boolean
				end try
				
				if not foundword then
					local glossarydoc
					
					tell application id "com.apple.textedit"
						try
							set glossarydoc to its document glossaryName
						on error
							set glossarydoc to null
						end try
					end tell
					
					if glossarydoc is not null then
						tell application id "com.apple.textedit"
							tell its document glossaryName
								
								make new paragraph at beginning of its text with data ((wordToLookup as text) & linefeed)
							end tell
							save glossarydoc
						end tell
					else
						try
							do shell script "export TMPFILE=`mktemp /tmp/${Dicttempfoo}.XXXXXX` && cat " & theF & "  >$TMPFILE ; echo " & wordToLookup & ">|" & theF & " ; cat $TMPFILE >>" & theF
						end try
					end if
				end if
				do shell script "open -b \"com.apple.Dictionary\""
				
			end if
		end if
	end if
	return
end script
tell DictionaryIndexAndLog to run

Polished.

Don’t enter regexp’s like this: “a*” that will return the whole dictionary, and crash the script.

It gives more fun with grep -E.

It can be great for Scrabble, I’ll have to make a permutation routine, to build the patterns with! :smiley:

Very nice. :slight_smile:

For more control, may I suggest changing these lines .

set searchWord to quoted form of searchWord

.

set my searchWord to text 2 thru -2 of my searchWord

. to these?

set searchWord to quoted form of ("^" & searchWord & "$")

.

set my searchWord to text 3 thru -3 of my searchWord

In wildcard mode ” or in any case without the above modifications ” the list offered by the script can include words which exist, but for which Dictionary can’t actually produce definitions and which it doesn’t offer in its own list display.

I must apologise for the ambiguity here. I meant “log words which the user may click in Dictionary’s definition display when following on from an original query.”

I’d really like to be able to enter any wildcard I want. I didn’t write it (which I should have had), but I supposed people to enter ^ and $ at their own discretion.

I am going to overlook it in a few. I will play some more and ponder. I also think that if I want to play with it for crossword puzzles, it should also generally be a fun way of practicing regexps. The regexp can be fun for crosswords, with patterns like: .[t]+[r]+. :slight_smile: And Scrabble too! :slight_smile:

I think I’d rather have the saving of the searchpattern higher up, before the dictionary lookup really. so that I can reuse it, without having to do a dictionary lookup in order to save the pattern.

This is about the Automator script posted higher above.

Now, I think that would have been doable, it depends if the calls to the links are sent to launchservices, or done internally. The consequences of overriding the default application for dict:// are unforeseen, how will this inflict with Spotlight for instance? That are great for looking up words and definitions quickly, escpecially for us that doesn’t have English as our natural language.

The user may actually just select the word and then choose the service, which is awkward more awkward than just following the link, but it is possible.

I have not tried to install the service on cntrl-cmd D yet, but I think this shortcut key, which is the normal for dictionary lookup, won’t conflict with the regular one, so that the regular one works when no word is selected.

Edit

I’ll make an app at my leisure, but as soon as I can, that intercepts the dict:// protocol, and see how it turns out, with Spotlight and all. If that works, then the service won’t be necessary for the most part, where you have access to the dictonary by the contextual menu, Or can use the short cut key directly when hoovering over the word. That is at least what I hope.

Hello!

I know understand what Nigel meant when he stated that it didn’t work when clicking on the words, that doesn’t work for me either, and is a problem with dictionary app. → In the dictionary, it works great with wikipedia from Dictionary.app when you click on something to follow a link.

And, I can’t intercept the calls to dictionary app from other apps, I did try, I installed an dict -url handler to intercept and reflect the calls to Dictonary.app, but the apps, are using a more direct route. Now maybe it is possible to go there via the cocoa text subsystem, but that is a far more elaborate approach, eve if the apps calls dictionary via the cocoa text subsystem, I have no idea if it is possible to override it, intercept the call, and reflect it. There would also perhaps be repercussions to this, as Spotlight might have stopped showing word definitinions, which it is great for!

That leaves us with actually having to select a word, and use the service, which do work inside dictionary app, when using it for the dictionary. Which is a way to select words in the dictonary, that works! :wink:

As for the script, I am changing it to grep -E, adding a line that you can comment out, but higher up, as Nigel suggested. With that line uncommented, it will work perfectly with Dictionary.app but will be less fun to play with, each to his own.

I made some minor adjustments to the dictonary lookup/ regexp-play script.

Now it uses egrep, as grep -E isn’t quite the same, not on my machine anyhow.

I have added an error message, when there are too many matches that the choose from list can handle.

By the way; the Fn key works for scrolling fast through a choose from list.

Now It doesn’t save searchterms for which no match were found. Rip it out if you don’t like it. :slight_smile:

Hopefully the last post.

I found out the hard way, that Quicksilver doesn’t like this script at all. for the first, there is the neglect of saving the property by its script runner, but I was aware of that.

What it really doesn’t like, is the open location of the dict:// protocol.

Well, I use the script below to call it up from the script menu, the same script saved as an app, with the path adjusted to point to it as the main.scpt inside the resources/scripts folder.

Then I invoke the app from Spotlight.


#  © McUsr 2012 and put in public domain! You are not allowed to post this elsewhere, nor in a publicly accessible repository
-- Please refer to this post: http://macscripter.net/edit.php?id=156009
property tlvl : me
# property hfsTlvlName : "hfspath:to:the:stub"
# property hfsScriptName : "hfspath:to:the:real:script"
# property scriptToRun : load script (hfsScriptName as alias)

if isnewer for quoted form of POSIX path of hfsScriptName against quoted form of POSIX path of hfsTlvlName then
	
	set my scriptToRun to load script (hfsScriptName as alias)
	do shell script "touch " & quoted form of POSIX path of hfsTlvlName
end if

tell scriptToRun to run

on isnewer for posixFileA against posixFileB
	return (do shell script "[ " & posixFileA & " -nt " & posixFileB & " ] && echo true || echo false ") as boolean
end isnewer

Hey guys, thanks a lot. This is good enough for me and it is what I needed.

:cool:

Hello!

You can add the shortcut cntrl-cmd D to the service from the keyboard preferences pane. It doesn’t conflict withe regular behaviour of using cntrl-cmd D when hoovering over an unselected word.

If you use Spotlight for lookup, then you can just hit cntrl-cmd D after having chosen to see more than the definition, to add it to your “glossary”.

Edit

This may be quite useful for a lot of people that doens’t have english as their natitve language. Now, if only apple enhanced the language support like this for other languages, so we could all view them in Dictionary.app :slight_smile:

I take more requests on this, should anyone have any.

My first idea is to keep the list unique, by removing previous occurences, so the last looked up words are the one that are shown first in the list. (Also an enhancement.)

Another approach is to not add any words that is there from before, as you then can look up the word again from within the TextEdit document, without adding to it. (Another precaution here, is to set TextEdit as the default.app so that it will be guarranteed to open in it, as not all TextEditors you can set as the default app for txt documents, uses cocoa and can handle text services. BBEdit (8.7 at least) being the most prominent of them.)

I also ponder a second list, so that you can keep your own explanation of the word, so that no extranous words are added here, due to the former.

And maybe a choose from list, to either look up the word again, not adding it, or showing the word with your explanation.

Another idea is to integrate it with a repetition program.

In the mean time: This little snippet sorts a list in TextEdit, keeping just the unique lines.


tell application "TextEdit"
	local tids
	set {tids, AppleScript's text item delimiters} to {AppleScript's text item delimiters, linefeed}
	set text of its front document to (paragraphs 2 thru -1 of (do shell script "sort -ufd <<<" & quoted form of (every paragraph of its front document as text))) as text
	set AppleScript's text item delimiters to tids
end tell

I have updated the Automator service in post #4 to only add unique words, and consider if TextEdit has the file DictLookup.txt open. This, because TextEdit is somewhat stupid, with regards to files that are changed on disk, while they are open in TextEdit. (It doesn’t reload files that are changed on disk.)

Now you can use the service, to lookup the words you have looked up, without adding them once again! :slight_smile:

Edit

I had forgotten to see to that the file is created initially. And just adds that the last word you lookup gets to be on the first line, if you haven’t looked it up earlier.

Now the Automator service is totally as it should be, when you look up words without standing in the glossary file in TextEdit, then whatever document or application you were in will stay active, with the dictionary window with the word in front of it, if there was a word to be found.!

:slight_smile:

Now The Automar action in post #4 is totally as it should be.

This was hard to get to make properly, as Automator has its own set of quirks, that just has to be experienced.

For instance, the automatic coercion to text, doesn’t work in the “execute Applescript action”, as you would expect.

I had to coerce

(input as text) & linefeed

in order to have TextEdit make a new paragraph.

I figured having TextEdit do this, when the document is open in TextEdit, leads to a much quiter and pleasant screen. And it did! Now the screen stays like it is, save the one single dictionary window that pops up, if you selected something that can be taken for a word. (Has printable characters).