Scriptability isn’t necessarily a limiting factor, as System Events can get the attributes of ui elements for the application process.
Most selectable text will be in a ui element of class text area or text field, or something of that ilk. These objects typically have an attribute called AXSelectedText, the value of which will yield what @Lantro is after.
I too would be very interested to see an example script using the method @CJK describes, but in the meantime, it's possible to achieve this goal using without too much trouble using AppleScript and an Automator service. Create a service like this in Automator:
using an AppleScript like this:
on run {input}
tell application "Keyboard Maestro Engine"
setvariable "Selection" to input
end tell
end run
And run it from KM like this:
(The |Chrome part is because Chrome doesn't seem to respond properly to the APPLICATION token, so we have to specify it manually)
This will save the currently selected text to a variable called "Selection" in KM, all without going through the clipboard. (That said, in my experience it's easier and faster to just go through the clipboard and delete the most recent clipboard entry once you have what you need from it.)
use sys : application "System Events"
use scripting additions
property initialRun : true
set _P to a reference to process "Spotlight"
set _M to a reference to menu bar item "Spotlight" of menu bar 1 of _P
set _W to a reference to window "Spotlight" of _P
set _T to a reference to text field 1 of _W
get [¬
"After dismissing this alert, Spotlight will appear. ", ¬
"Please select some text within its search bar. ", ¬
"You have about 5 seconds to do this from the time ", ¬
"Spotlight appears.", linefeed, linefeed, ¬
"Press OK when ready."]
if initialRun then display alert (the result as text)
set initialRun to false
-- Raise Spotlight
-- click _M --> does not work
-- Change the following line as needed to match the key combo used to trigger Spotlight:
tell sys to keystroke space using {option down, command down}
repeat 15 times -- 3 seconds maximum
if _T exists then ¬
exit repeat
delay 0.2
end repeat
if _W exists then if _T exists then tell _T
set its value to "Select some of this text in Spotlight."
delay 5
set t to "You selected the following text:"
set selectedText to the value of its attribute "AXSelectedText"
if the selectedText is in {"", missing value} then ¬
set t to "No text was selected."
return display notification the selectedText with title t
end tell
"Spotlight did not appear. Sorry."
Summary
An example AppleScript to demonstrate the retrieval of the AXSelectedText attribute in order to store a text selection in a variable.
Thanks for sharing that. Very interesting.
The challenge, of course, if figuring out the UI elements that contain the selection. Any guidelines for this?
I created a sort of clipboard side save routine for my clipboard stacks, so I just meant that I might submit a request for a combined routine so that it’s available as an action, sort of like making a subroutine in AppleScript. I realize that there are caveats to storing the clipboard contents into a named clipboard (mainly that you won’t always get all the clip flavors), so you probably had that fact in mind when you replied.
This is true in theory. And again, in specific cases, you can probably get it to work.
However I looked into this (a lot) in the v8 development cycle and I could not make it work in any general sense. While given a specific element, you may well be able to get the text out of it, there are few applications that properly support that together with an ability to actually find the focussed element.
So if you know the specific application, and you know the specific window, and you know the specific element, then you have a half decent change of getting the selection from it using the Accessibility API, but in the general case of “give me the text of the current selection”, unfortunately I do not believe there is a good general solution to that that actually works in practice.
In the KM Editor Edit > Actions, it shows up as "Delete Current Clipboard", although the Action is really "Delete Past Clipboard", where the Past Clipboard # is 0.
I use this all the time, and frankly, it is much easier to use this than trying to fuss with a script to read a selection.
Here's the code I've been working on so far to capture the selected text in a generalised situation. Notes follow below.
use sys : application "System Events"
-- Use the following delay to choose an application window
-- and highlight some text. Then ensure that the window remains
-- in focus until the script terminates.
delay 5
set P to the first process whose frontmost is true
set _W to a reference to the first window of P
set _U to a reference to ¬
(UI elements of P whose ¬
name of attributes contains "AXSelectedText" and ¬
value of attribute "AXSelectedText" is not "" and ¬
class of value of attribute "AXSelectedText" is not class)
tell sys to if (count _U) ≠ 0 then ¬
return the value of ¬
attribute "AXSelectedText" of ¬
_U's contents's first item
set _U to a reference to UI elements of _W
tell sys to repeat while (_U exists)
tell (a reference to ¬
(_U whose ¬
name of attributes contains "AXSelectedText" and ¬
value of attribute "AXSelectedText" is not "" and ¬
class of value of attribute "AXSelectedText" is not class)) ¬
to if (count) ≠ 0 then return the value of ¬
attribute "AXSelectedText" of its contents's first item
set _U to a reference to (UI elements of _U)
end repeat
"No selection found."
System info:AppleScript version: "2.7", system version: "10.13.3"
Notes
The script remains a work in progress, so don't expect a robust piece of deployment-standard code. I apologise that it's not the most readable script in its present format.
There's a 5-second delay at the start of the script to allow a user to select an application window, highlight some text, and then allow the script to run to completion.
It hunts for selected text in the front window of the frontmost application, as I thought this was a fairly reasonable condition under which real-life situations would fall.
It returns the selected text (if found), or a message stating that no selected text was found. It is a simple tweak should you want the actual UI element that contains the selected text to be returned instead (which might be useful in situations where text would be inserted to replace that which was highlighted).
Limitations
The first limitation is obvious: as my script relies on referencing the front window of a frontmost application for the root of its UI-tree traversal, it currently cannot identify applications whose processes don't register themselves as having focus, such as some menu bar applications that remain background only (iTerm, SnippetsLab, Alfred, Spotlight, ...). However, if the variable P is set to one of these specific processes, it successfully obtains any highlighted text. Therefore, a workaround to this problem is probably very achievable.
It also won't traverse menu bar elements. My initial script did do this, and was able to return text highlighted in the search box of the Help menu. However, the time cost of searching the menus weighed against the probability that selected text would be found there made its sacrifice seem reasonable.
It can't traverse the desktop, should a user decide to select a file and edit the filename, highlighting the text in the editing field that appears. The desktop falls under Finder's process, and lacks a window reference under System Events (although it has one under Finder's application inheritance tree).[FIXED]
Selecting text on a Safari webpage (and, it seems, Chrome as well, although I work mostly with Safari) goes unnoticed. Upon inspection, it seems that the UI elements that make up the AXWebArea of a Safari document all hold their own, unique set of attributes, tailored to the HTML DOM. This means all the static text and text area objects have no AXSelectedText attribute. A solution to this is to do JavaScript in the front document: document.getSelection().toString(); However, the rest of Safari'sUI is compliant, so highlighting text in the search bar, for instance, will be detected.
Feel free to feedback any other issues that crop up, together with how to reproduce them. Or, better yet, feel free to improve the code should anyone feel so inclined. I'm not pledging myself to get this script working seamlessly; this was more just to highlight the possibilities, and I think the more realistic solutions to the OP's original dilemma have been provided. But it's an interesting educational exercise.
Reading over these issues, #1-3 are actually a subset of the same problem, which stems from seeking out an active application window to traverse. So, a three-bird-one-stone fix might very well pop into my head next week.
Hi, I am a newbie with AppleScript, just wondering how to use it in plain AppleScript to set the selected text to a useable variable to paste in different app (e.g. Chrome to Word)
Please it would be kind of you to explain how the script works or some pointers in the direction.
Hi Chris
This works a treat with word. I really wanted to use CJK's script to get the selected text in chrome/ any app, then use it in other apps s/a word or a different tab of chrome( tab selection based on url)
I believe, now I need to read about handlers
I think magic of emplace happens with handler 2 " on setWordSelectionToText(theText, activateBool)"
If I had a different app, I need to "tell" that particular app to use variable "thetext"
I cant paste it (not involving clipboard), but it wud depend on that particular app's dictionary
is that correct?
I was hoping for a generic solution if I cud
Thank you again, U are kind
As you can see, you have to go to a lot of trouble to avoid using the Clipboard, and even then it may not work for all apps.
Why do you want to avoid using the Clipboard?
If you don't want the data to remain on the clipboard after pasting, it is very simple to remove/delete it using the KM Delete Past Clipboard action, where you use "0" for the past clipboard number. This refers to the current System Clipboard.
Hi, i wud copy a selection in Browser, and paste it DropBox paper, sometimes in word, then I wud copy the url & paste it.
My Idea was to set selection to variable which cud be inserted, and I wud copy the url to clipboard-> paste it
It wud save me change tabs x2
I was hoping to do with applescript as cud be used on any Mac
Thanks for helping Cheers
Hi Chris,
Thanks for updated Macro, I have managed to add Chrome title to it as well
Just out of interest, the macro Feb12
had "window.getSelection()+'';"
where as the the most recent one has window.getSelection().toString()
The output for both is similar to naked eye, are they different ?
Also do we have a similar javascript to paste a variable in chrome
(as get selection from window.getSelection().toString() in Tab1, then emplace it in Tab2
I have used this as textexpander snippet, and works brilliantly as a work around
Thank you for ur help