Copy any Text Selection

I understand that globally, this is pretty much impossible. The actual “selection” definitions and their methods range greatly from application to application, if they are even scriptable. However, I’ve been thinking of an alternate way to get a text selection, but it needs some refinement. I figure, if something is text-based and selected, the program will pretty much definitely have the option to Command-C copy it. So, if I tell the frontmost application to Command-C, I can then manipulate the clipboard however I want (I backup the old clipboard contents first. This seems to be working fine, except when there is no selection. If nothing is selected, it just errors because the menu command is greyed out. Here’s my question, how can I check to see if a certain menu option is available or not? I don’t think the usual “Menu item 5 of the 2nd menu” will work, because sometimes they add in extra things, such as Redo, Undo history, etc. Any ideas?

Heres my general code and checking routine:

on run
	set OldClipboard to the clipboard
	tell application "System Events"
		set FrontApp to (name of (processes whose frontmost is true)) as text
		tell process FrontApp
			keystroke "c" using {command down}
		end tell
	end tell
	delay 0.05
	set myRef to the clipboard
	display dialog myRef
	try
		set theOutput to myRef as string
		display dialog theOutput
		return theOutput
	on error
		tell application "Finder"
			display dialog "error"
		end tell
	end try
end run

The try code at the bottom is my attempt to doublecheck that the selection is actually text. Not sure it works 100% yet though.

Edit: The other odd thing is when running from Dashboard (the reason for this script), it doesn’t work. Console is reporting failures in Apple’s code, though! “bindings/objc/WebScriptObject.mm:214:[383] JavaScript exception: Base is not an object” Hmmm…

Update on this. I think the problem might be that the “keystroke” commands only work when the application is the frontmost one, regardless of if it is in a tell block or not. Can anyone confirm this? Then, along the same lines, can anyone confirm if the UI scripting commands “Like select item copy from blah blah blah” require the application to be frontmost also?

If those are both true, I think I’m running out of ideas. I can have my widget hide the dashboard again to get the selection, but theres no way to check if there is a selection to determine whether or not to do that. Any ideas anyone?

This works fine for me:

tell application "System Events"
	try
		tell application "Dashboard" to activate
	end try
	delay 3
	set FrontApp to (name of (processes whose frontmost is true)) as text
	tell process FrontApp to click menu item "Copy" of menu 1 of menu bar item "Edit" of menu bar 1
end tell

Under normal circumstances I’m not aware of a way to send keystrokes to or manipulate the menu bars of inactive applications, but it seems that Dashboard is a special case. This might be an illusion, however, if it requires that the script already be set in motion in the background. If you’re still having trouble, you might try something like: get enabled of menu item “Copy” of menu 1 of menu bar item “Edit” of menu bar 1 of process FrontApp

Great, that seems to work! However, I’m having another problem. I want the script to return nothing if there is no selection. Below is my attached script that works fine, but seems to still run even if I have no selection:

on run
	tell application "System Events"
		set FrontApp to (name of (processes whose frontmost is true)) as text
		try
			tell process FrontApp to click menu item "Copy" of menu 1 of menu bar item "Edit" of menu bar 1
			delay 0.3
		on error
			return
		end try
		try
			set Output to the clipboard as text
			do shell script "logger " & Output
			return Output
		on error
			return
		end try
	end tell
end run

I think the problem is that “return” doesn’t seem to exit the script. I’ve tried “quit” too, and it doesn’t work. Any ideas?

A couple ideas for you:

on run
	set OldClipboard to the clipboard
	tell application "System Events"
		set FrontApp to (name of (processes whose frontmost is true)) as text
		tell process FrontApp to tell menu item "Copy" of menu 1 of ¬
			menu bar item "Edit" of menu bar 1 to if enabled is true then click
	end tell
	delay 0.05
	set myRef to the clipboard
	if myRef is OldClipboard then error number -128
	try
		set Output to the clipboard as text
		do shell script "logger " & Output
		return Output
	end try
end run

As I mentioned before you can test the state of menu items before attempting to click them. This plays off the fact that most programs disable the “Copy” menu item if there is nothing to copy. This might not always be true, but it’s probably your best bet because telling an inactive menu item to click isn’t going to throw an error anyway. If you want to stop your script in its tracks no matter what, you’ll probably want to just test to see if the clipboard has changed and then deliberately throw an error to stop the script in its tracks. The code above should fail quietly in this case.

Great! That seems to be doing everything I want. I actually decided to forgo the “error number” route, as it was too hard to catch on the Dashboard side of things. I decided to rather use that check of yours to either return what I should return, or just return a space. Looking for a space is really easy, and I know that that won’t ever be necessary. Anyway, thanks so much for your help. In case you (or anyone else) is interested, this is my code for it.

on run
	set OldClipboard to the clipboard
	tell application "System Events"
		set FrontApp to (name of (processes whose frontmost is true)) as text
		tell process FrontApp to click menu item "Copy" of menu 1 of menu bar item "Edit" of menu bar 1
		delay 0.1
		set myRef to the clipboard
		try
			set Output to the clipboard as text
			do shell script "logger " & Output
			
			if myRef is not OldClipboard then
				set the clipboard to OldClipboard
				return Output
			else
				set the clipboard to OldClipboard
				return " "
			end if
		on error
			set the clipboard to OldClipboard
			return " "
		end try
	end tell
end run

I’m having a few weird caveats when dealing with non-text items. I’m not sure if this can be entirely solved, though. If in Photoshop, selected a section, it doesn’t go to text, so I still return a space and am cool. However, having a picture selected in Mail (not really gonna happen in this instance, but thought I’d check) it gets converted to a “?”. Having an item selected in the finder causes the script to error out, as it doesn’t have the “Copy” menu item anymore, it switches to Copy “{filename}”. Again, I’m not sure if I want to try to deal witht this or not, but thought I’d let you know.

You’re welcome, and thank you for the detailed questions, I’m learning quite a bit of useful things here myself. My first impulse would be to test FrontApp to see if it is the Finder, and if it is then send something like tell app finder to copy. Well, that would be nice, except that Finder’s dictionary clearly states that this command is “(NOT AVAILABLE YET)”

So now we have to get real clever and code a work-around for our work-around:

tell application "Finder" to activate
tell application "System Events" to tell process "Finder" to tell menu 1 of menu bar item "Edit" of menu bar 1
	set theMenu to title of every menu item
	repeat with x in theMenu
		if x starts with "Copy" then click menu item x
	end repeat
end tell

I activate the Finder here just for illustrative purposes, it doesn’t have to be frontmost to get the menu item titles. Also, you might have to fiddle with this if there are any apps that have multiple Copy options in their Edit menu.

Interesting development here. I noticed over on MacOSXHints.com that someone figured out how to send keystrokes to apps under the dashboard. I tried out various system event methods of reproducing their result, but in the end found that the only “way in” is by following their hint, which is to simply activate the spotlight search bar and then dismiss it, alike so:

try
	tell application "Dashboard" to activate
end try
delay 3
tell application "System Events"
	keystroke space using command down -- the default spotlight shortcut
	key code 53 -- escape out of spotlight
	delay 0.2 -- wait for spotlight to clear out
	keystroke "c" using command down -- tell active app to copy
end tell

This could be a temporary bug, even if it isn’t, it’s not safe to make assumptions about a user’s hotkeys. I tested it with Quicksilver set to command-space, however, and it worked the same. A bit puzzling, but worth mentioning I think.

Original article here:
http://www.macosxhints.com/article.php?story=20050627203154837&lsrc=osxh