KM8: XML of Display Text in Window Does Not Update by Script

@peternlewis,

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
3 Likes