When I use the Prompt For User Input action, typing the delete key deletes the entire input string instead of deleting one letter. Is there any way to change this? Would I have to use a custom HTML input interface to be able to delete just a single character at a time?
Only if the whole of the input string is selected. Otherwise the delete key deletes one letter at a time. Or am I missing something?
My guess is that they're using a default initial search string because the prompt populates with it fully selected. I looked but didn't see any obvious way to change this behaviour and tried adding a keystroke action to send a right arrow press even though I was sure it wouldn't work (it didn't). This might be (at least partly) why I never use default initial search strings.
You could trigger the right arrow key as an asynchronous submacro. In this example, it works without the included pause, but it might be necessary on older (slower) macs.
User Input → Example.kmmacros (20 KB)
Press → Key (async).kmmacros (20 KB)
I've often thought it might be useful if Peter were able to add an Asynchronous Group action for cases such as these. The idea is that you could engroup a number of actions that would run asynchronously (i.e. as though they were an independent macro), in line within the caller macro. It's a bit bloaty to add a dedicated macro for single keypresses like this, but it's the only recourse as things stand (as far as I'm aware).
I've had the same thought myself, as I have a number of one or two line async macros that do nothing other than control a box in another macro.
-rob.
That could get very confusing -- the async Group would be a separate instance, without access to local and instance variables for example.
Challenge accepted
Make your Group (remembering the limitations above). Right-click the Group and "Copy as XML". Paste into your favourite text editor and remove the first four lines, including <array>
. Remove the last two lines, </array>
and </plist>
. You should be left with a <dict>
containing your Group of actions.
In your macro add an "Execute an AppleScript" action with:
set theXML to "xml_goes_here"
tell application "Keyboard Maestro Engine"
do script theXML
end tell
...and paste in your edited XML, replacing xml_goes here
.
Set the action to "Asynchronously" and you're done!
Brilliant!
And obviously we're going to want to automate that...
I've got close but I must have made a basic mistake. Can you see what it is?
Make Async Group.kmmacros (54 KB)
Hello Neil (@noisneil)
the Code inside of the Script Actions is always escaped. So you have to escape everything inside the Script Action that’s related to the created Actions - and leave only the parts unescaped which are necessary to create the other Actions.
Once you’ve understood this principle you can create everything inside a Script Action - even other Script Actions which would be executed. This is to use for synchronous as well as asynchronous execution.
Best wishes and merry Christmas from Germany
Tobias
Right yeah. But haven't I done that with the HTML encoding filter?
Ah... I think this is the issue. There's no such option for the Execute AppleScript action.
It’s in the output options — ignore, set variable, etc. IIRC it’s the bottom option.
A thousand apols. I was looking under the gear icon. Still not sure where I've gone wrong... (Actually maybe I do...)
In case you're interested, this is why I was confounded:
New computer; hadn't yet turned off smart quotes!
So here it is... Merry Christmas (etc...)!
Convert Selected Group of Actions to Async AppleScript.kmmacros (46 KB)
Here (finally) is an AppleScript to do the same. By pulling the original XML we get the double-quotes escaped for us which I think means we only have to handle <
and >
in the XML.
Select an action or "Group" action and this will create a new async AS action below it that executes that functionality directly, and will also disable and "undisclose" the original (keeping the original means you can easily edit it and then regenerate the AS action).
You can, of course, put the script into a macro for easy triggering. As usual I've not included error handling -- you should probably at least check that only one action (which would include a single selected Group containing many actions) is selected.
AppleScript
set preXML to "<dict>
<key>DisplayKind</key>
<string>Asynchronously</string>
<key>HonourFailureSettings</key>
<true/>
<key>IncludeStdErr</key>
<false/>
<key>IncludedVariables</key>
<array>
<string>9999</string>
</array>
<key>MacroActionType</key>
<string>ExecuteAppleScript</string>
<key>Path</key>
<string></string>
<key>Text</key>
<string>set theXML to \""
set postXML to "
\"
tell application \"Keyboard Maestro Engine\"
do script theXML
end tell
</string>
<key>TimeOutAbortsMacro</key>
<true/>
<key>TrimResults</key>
<true/>
<key>TrimResultsNew</key>
<true/>
<key>UseText</key>
<true/>
</dict>
"
tell application "Keyboard Maestro"
set theAction to item 1 of (get selection)
set theXML to paragraphs 4 thru -3 of (get xml of theAction)
set AppleScript's text item delimiters to return
set theXML to theXML as text
set AppleScript's text item delimiters to "<"
set theXML to every text item of theXML
set AppleScript's text item delimiters to "<"
set theXML to theXML as text
set AppleScript's text item delimiters to ">"
set theXML to every text item of theXML
set AppleScript's text item delimiters to ">"
set theXML to theXML as text
set theXML to preXML & theXML & postXML
make new action at after theAction with properties {xml:theXML}
set disclosed of theAction to false
set enabled of theAction to false
end tell
Wow … Nige (@Nige_S) & Neil (@noisneil) both ways look great.
Nige, I like the way you’re using TID‘s on the XML … never seen that…
If I where either one of you I would use some code posted by Chris (@ccstone) using ASObjC and combine this code with yours.
I apparently don’t have either the code or the link to it at hand currently so I can not help with that …
The Code I am talking about lets you make sure that at least one Action is selected and you could use the other bits of that code to initiate an abort of the Macro when the user has no Actions selected or build something that waits for the user to let him/ her selecting Action(s) first to continue.
Maybe if I could build Macros only with my voice like I can dictate my replies - I would possibly have done this by myself.
I hope you had a beautiful Christmas - wish you - and of course everyone else who‘s reading this a happy new year.
Greetings from Germany
Tobias
Unthinkingly old-school. Since we have KM available, let's use the Engine's search and replace:
tell application "Keyboard Maestro Engine"
set theXML to search theXML for "<" replace "<"
set theXML to search theXML for ">" replace ">"
end tell
No need -- you can do it with vanilla AS. Just confirm the number of selected items is 1
and that the class of that item is action
:
if (count of theAction) is not 1 or class of item 1 of theAction is not action then
Another fun fact is that if the action we're turning into AS is disabled, so is the resulting AS action (should have thought of that...). So let's add in a "force enable" so we don't have to remember to enable it when editing/regenerating.
Putting that all together:
Revised Script
set preXML to "<dict>
<key>DisplayKind</key>
<string>Asynchronously</string>
<key>HonourFailureSettings</key>
<true/>
<key>IncludeStdErr</key>
<false/>
<key>IncludedVariables</key>
<array>
<string>9999</string>
</array>
<key>MacroActionType</key>
<string>ExecuteAppleScript</string>
<key>Path</key>
<string></string>
<key>Text</key>
<string>set theXML to \""
set postXML to "
\"
tell application \"Keyboard Maestro Engine\"
do script theXML
end tell
</string>
<key>TimeOutAbortsMacro</key>
<true/>
<key>TrimResults</key>
<true/>
<key>TrimResultsNew</key>
<true/>
<key>UseText</key>
<true/>
</dict>
"
tell application "Keyboard Maestro"
set theSelection to (get selection)
if (count of theSelection) is not 1 or class of item 1 of theSelection is not action then
display alert "Wrong Number of Actions Selected!" message "Try again but with exactly 1 action selected
Cancelling script..." as critical
return 1
end if
set theAction to item 1 of theSelection
set enabled of theAction to true
set theXML to paragraphs 4 thru -3 of (get xml of theAction)
set AppleScript's text item delimiters to return
set theXML to theXML as text
tell application "Keyboard Maestro Engine"
set theXML to search theXML for "<" replace "<"
set theXML to search theXML for ">" replace ">"
end tell
make new action at after theAction with properties {xml:(preXML & theXML & postXML)}
set disclosed of theAction to false
set enabled of theAction to false
end tell
I'm away until the 11th but can't wait to try this when I'm back. Well done Nige!
Hey, I've just realised that this technique can be used to run a Subroutine Asynchronously. This is something that cannot be done at present using the inbuilt Action settings.
You can do that in a roundabout way by having a macro (Macro 1) execute a dummy macro (Macro 2) asynchronously which in turn executes the subroutine (Macro 3).
Doing so means the subroutine Macro 3 executes independently of the Macro 1.
All I know about this is it works!
Here's the test:
[WIP] Asynch Test Macros.kmmacros|attachment (7.1 KB)