Many thanks for your help and patience in solving this problem.
The key was the ObjC code you provided here:
Thanks to you and @ShaneStanley, we now have a complete solution that does all of the following:
- Converts the KM Action XML into an ASObjC Dictionary object
- Extracts the "
<StyledText>
" section, which contains the RTFD of the action - Converts that into "normal", updatable, rich text
- Updates the rich text, changing the variable names in it.
- Updates the Dictionary object, and saves it to a new XML string.
- Using that XML string, the KM Action object is updated.
Shanes code also provides a plain text version of the rich text. So that will solve the other issue I had with getting plain text from a Comment Action.
Here's my test script (NOT suitable for production use)
Based on Shane's code here, modified by me to refactor into handler
Therefore, any errors are mine.
It was tested using this Action:
and produced this result:
(*
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
How to Update Rich Text in KM Action
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
DATE: 2017-10-03
AUTHOR: ShaneStanley
REF:
• How Do I base64 Decode and Encode Multiple Lines?
• Late Night Software Ltd.,
• http://forum.latenightsw.com/t/how-do-i-base64-decode-and-encode-multiple-lines/759/11?u=jmichaeltx
The part this doesn’t really cover is how to edit the styled text (attributed string), which can be complicated depending on what you want to do. Assuming you don’t want to change the attributes themselves, the methods you’d use are replaceCharactersInRange:withString: and deleteCharactersInRange:. You get the ranges you use based on the unstyled text, as above.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*)
use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use framework "AppKit"
use scripting additions
tell application "Keyboard Maestro"
set oMacro to item 1 of (get selected macros)
set actionList to actions in oMacro
set oAction to item 1 in actionList
tell oAction
set actXML to xml
end tell -- oAction
set prefixCur to "TEST__"
set prefixNew to "Local__"
set actXMLRev to my kmUpdateXMLRichText(actXML, prefixCur, prefixNew)
set xml of oAction to actXMLRev
set name of oAction to "AFTER XML Changes"
end tell
--~~~~~~~~~~~~~~~~~~~ END OF MAIN SCRIPT ~~~~~~~~~~~~~~~~~~~~~~
on kmUpdateXMLRichText(pXMLStr, pChgFromStr, pChgToStr)
local theString, stringData, mutableDict, theError, theData, mutableAttString, plistData, xmlRevStr
set theString to pXMLStr
set theString to current application's NSString's stringWithString:theString
-- convert string to data
set stringData to theString's dataUsingEncoding:(current application's NSUTF8StringEncoding)
-- convert plist to mutable dictionary
set {mutableDict, theError} to current application's NSPropertyListSerialization's propertyListWithData:stringData options:(current application's NSPropertyListMutableContainersAndLeaves) |format|:(missing value) |error|:(reference)
if mutableDict is missing value then error (theError's localizedDescription() as text)
-- extract RTFD data and convert to a mutable atributed string
set theData to mutableDict's objectForKey:"StyledText"
--- Decode Rich Text ---
set mutableAttString to current application's NSMutableAttributedString's alloc()'s initWithRTFD:theData documentAttributes:(missing value)
--- GET PLAIN TEXT from Rich Text ---
set plainString to mutableAttString's |string|()
----------------------------------------------
-- CHANGE All Occurrences of pChgFromStr --
----------------------------------------------
-- modify the mutable atributed string
-- how you do that depends on what you want to do, obviously
set theRange to plainString's rangeOfString:pChgFromStr
mutableAttString's replaceCharactersInRange:theRange withString:pChgToStr
----------------------------------
-- convert back to RTFD data
----------------------------------
set theData to mutableAttString's RTFDFromRange:{0, mutableAttString's |length|()} documentAttributes:(missing value)
-- update the dictionary
mutableDict's setObject:theData forKey:"StyledText"
mutableDict's setObject:(mutableAttString's |string|()) forKey:"Text"
-- make new plist from dictionary ---
set {plistData, theError} to current application's NSPropertyListSerialization's dataWithPropertyList:mutableDict |format|:(current application's NSPropertyListXMLFormat_v1_0) options:0 |error|:(reference)
-----------------------------------
-- GET REVISED XML --
-----------------------------------
-- get text version
set xmlRevStr to (current application's NSString's alloc()'s initWithData:plistData encoding:(current application's NSUTF8StringEncoding)) as text
return xmlRevStr
end kmUpdateXMLRichText