View Single Post
[edited to replace my "v0.1" semi-broken hackery of Greg's script]

Woohoo! Thanks Greg!

This just about does the trick.

I modified the script to a 'droplet', which remembers lists of files and folders to watch and exports the new versions alongside the graffle files (e.g. foo.graffle -> foo.graffle.png). I just drag the graffle files I want auto-exported to this script app, and start the script after I start OG. If OG isn't running when the script is launched, it will 'batch update' any watched files/folders and then exit.

[AS rant: I've got years of experience in C, Perl, and other assorted languages, yet AppleScript is just so damned opaque to me.... I wanted to test if OG had any open, modified docs, but I could only get the expression "count (document's modified)" to work - my old English teacher would crucify me :-) I also found weirdness with AppleScript's handling of the quit statement (see comments in the code below), and it took me ages of fiddling to figure out the difference between the alias and 'object' (Finder?) file reference types... I can say for sure Greg that without your script to work from I would've given up in dismay]


BTW, I played with calling the script via a Folder Action ('items added'), and via launchd (WatchPaths), and found that in both these cases the script will only be called for *new* files, not modifications to existing files. Presumably OG opens and overwrites the same file when saving, rt. creating a new one and unlinking the old, and thus neither launchd's WatchPaths nor the Folder Action pick up the change (as the file modification doesn't change the containing directory). Pity, as this would be a neat way of on-demand launching the script.


Thanks very much for the script! (Omni got a sale out of it - I had OG 3.2, which didn't support the export applescript function, and this was the last straw for me to upgrade :-)

Thanks,
Ben

Code:
-- Watch and auto-export OmniGraffle files to web-friendly versions
-- files/folders to watch are to be dropped onto this script (saved as a Stay Open application)
--  if a folder is watched all OG files in the folder will be monitored
-- exported files are created alongside the originals, named with the new_extension
--  based on / hacked from the original from Greg Titus (http://forums.omnigroup.com/showthread.php?p=537)

-- a file extension which Graffle supports for export
property new_extension : "png"

-- amount of time to wait in between checking for file changes
property seconds_between_checks : 15

-- evidently there are two kinds of file references in AS: a Finder file reference, and an alias
--  apparently aliases are "more useful"
-- list of 'aliases' (folders, files) to watch (added to via 'on open') 
property watch_files : {}
property watch_folders : {}

on run
	set t to "OmniGraffle Auto-Exporter" & return & ¬
		"Drop files/folders to be monitored on this script and they'll be automatically exported to " & new_extension & ¬
		" versions." & return & return & ¬
		"This script will stay open whilst OmniGraffle is running." & return
	set t to t & "Currently monitoring " & (count watch_files) & " file(s) and " & (count watch_folders) & " folder(s). "
	if (count watch_files) > 0 or (count watch_folders) > 0 then
		set r to button returned of (display dialog t & "Keep these?" buttons {"Yes", "No", "Show"} default button "Yes" with icon note)
		if r is "Show" then
			tell application "Finder"
				activate
				reveal {watch_files, watch_folders} -- one hit, so they're all selected
			end tell
		end if
		if r is "No" then
			set watch_files to {}
			set watch_folders to {}
		end if
	else
		display dialog t buttons {"Ok"} with icon note
	end if
end run

on open item_list
	tell application "Finder"
		
		repeat with this_item in item_list
			-- need to be careful of the list operations on watch_files, watch_folders - make sure only to add aliases
			--   e.g. 'copy this_item to end of watch_files' corrupts the list on the second iteration, whereas
			--  'copy (this_item as alias) to end of watch_files' works fine
			-- think you can find documentation on this? hah! (the Wikipedia AppleScript page was the
			--  only reference I found that actually stated that 'on open' returns a list of aliases!)
			
			if kind of this_item is "OmniGraffle document" then
				if this_item is not in watch_files then copy (this_item as alias) to end of watch_files
			else if kind of this_item is "folder" then
				if this_item is not in watch_folders then copy (this_item as alias) to end of watch_folders
			end if
			
		end repeat
		
		(*
		set s to ""
		set s to s & "files: (" & (count of watch_files) & "): " & return
		repeat with i in watch_files
			set s to s & "	[" & (i as string) & "]" & return
		end repeat
		set s to s & "folders: (" & (count of watch_folders) & "): " & return
		repeat with i in watch_folders
			set s to s & "	[" & (i as string) & "]" & return
		end repeat
		display dialog s
		*)
		
	end tell
end open

on idle
	
	-- if omnigraffle's not already running, close it after exporting the new files
	tell application "System Events"
		set isRunning to (name of processes) contains "OmniGraffle Professional"
	end tell
	
	tell application "Finder"
		-- gather list of files to update
		set file_list to watch_files
		repeat with this_folder in watch_folders
			-- display dialog "processing " & (this_folder as string) & "|" & class of this_folder
			-- 'files of' ... returns 'class docf' (file?) objects, not aliases
			set folder_files to (files of this_folder whose kind is "OmniGraffle document")
			repeat with this_file in folder_files
				set this_file to (this_file as alias)
				--display dialog "f: " & this_file & "|" & class of this_file
				if this_file is not in file_list then copy this_file to end of file_list
				--beep 1
			end repeat
		end repeat
		(*
		set s to ""
		set s to s & "file_list: (" & (count of file_list) & "): " & return
		repeat with i in file_list
			set s to s & "	[" & (i as string) & "]" & return
		end repeat
		display dialog s
		*)
		
		-- ok, now have list of candidate OG files
		
		set processed_items to false
		repeat with this_file in file_list
			
			-- figure out what the new exported name would be
			set new_name to (this_file as string) & "." & new_extension
			
			-- if there isn't an export file or if the graffle file is newer, do the export
			if (not (exists document file new_name)) or modification date of this_file > modification date of document file new_name then
				my process_item(this_file, new_name)
				set processed_items to true
			end if
			
		end repeat -- file_list
		
	end tell
	
	if isRunning then
		-- wait before we look again
		return seconds_between_checks
	else
		if processed_items then -- will have started up OG, so close it
			tell application "OmniGraffle Professional"
				-- close the app (if there are no open files, which there shouldn't be, as OG wasn't running before we started it)
				--display dialog "ndocs: " & (count (document's modified))
				if (count (document's modified)) is 0 then
					quit
				end if
			end tell
		end if
		quit -- me too
		-- it seems the 'quit' statement merely sends a Quit event/signal to the script/app, 
		--  and further this signal isn't processed until after the handler returns and the idle time expires
		-- reduce the idle time to some small, non-0 value to minimise the delay before exiting (0 = no change)
		return 0.1
	end if
	
end idle


-- this sub-routine does the export 
on process_item(source_file, exported_file)
	set the source_item to the POSIX path of the source_file
	
	with timeout of 900 seconds
		tell application "OmniGraffle Professional"
			-- if the file's being actively edited, a random object may be selected when we come to 
			-- do the export, and with an object selected the export will default to 'selected graphics'  - oops!
			-- sooo, override the 'default' (and return it later)
			set oldAreaType to area type of current export settings
			set area type of current export settings to all graphics
			
			-- open the file if it isn't already open
			set needToOpen to (count (documents whose path is source_item)) is 0
			if needToOpen then
				open source_file
			end if
			
			-- do the export
			set docsWithPath to documents whose path is source_item
			set theDoc to first item of docsWithPath
			save theDoc in file exported_file
			
			-- if the file wasn't already open, close it again
			if needToOpen then
				close theDoc
			end if
			
			set area type of current export settings to oldAreaType
		end tell
	end timeout
end process_item

Last edited by af3556; 2006-04-16 at 08:15 AM..