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?
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?
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.