Is This Possible? Search for All Macros That Contain Disabled Actions

Howdy folks, I am wondering if there is a way to search all macros for any that currently have disabled actions, either a macro that somebody has built or via a smart group.

Whenever I am modifying macros that already work well, I duplicate any actions I will be working on and disable them, that way I have a backup in case I mess something up. If the changes I make work well initially, I leave the macro alone for a few days or a week just to confirm over a longer trial period. Eventually after I have verified that there are no issues i'll go back and delete the original actions that I duplicated and disabled.

So it would be handy to quickly locate any macros that have disabled actions because sometimes I forget which macros I've been working on recently. I do have a smart group that shows me any macros that have been modified in the last week, but that only gets me partway there because it also includes macros whose name i've changed. Since I use Dan Thomas' palette organizer macro quite frequently, this smart group often has macros that have only had a name change and no changes to the actions themselves.

Any advice on this is greatly appreciated. Thanks in advance!

-Chris

Were you a Boy Scout, always prepared, and set a "Note" of "Disabled" every time you disabled an action? (That would be a useful macro in itself -- toggling an action's enabled/disabled and removing/setting the "Note" field!) You could then use a Smart Group...

Assuming you haven't, you could AppleScript a run through every macro and, for every disabled action, set the "Note" appropriately. I haven't looked yet, but I'm guessing you may need to recurse into eg groups and conditional blocks... A one-off hit and you can then continue as above.

If nobody comes up with a better idea I'll be happy to give it a go!

…and If, Switch … etc.

If you’re interested have a look here which mentions the recursion bit. Having done this for several reasons I’d recommend using JXA for performance reasons but maybe a different approach that parses each macro’s plist would yield even better performance if you intend trawling through every macro!

Oooh -- could be as simple as (pseudocode):

for eachItem in (get every macro)
    if xml of eachItem contains "<key>IsActive</key>" then some tabs then "<false/>" then
        append " - *Disabled Action(s)*" to name of macro
end

...and a Smart Group to suit.

1 Like

Sounds promising…

I actually started doing something like that over the weekend. I have a favorite action that consists solely of a colored comment with the words "Follow up with these changes", which I then created a smart group for. It works ok for now, but I would like to come up with something more fool proof for the times I forget to do that.

I'll tinker around with the AppleScript suggestions below, but my very first attempts at parsing the macros xml has given me error messages, so no doubt I'm not writing the syntax properly.

Update: the following script works when the amount of tab indentations (\t) is known... but that number is dependent on if the actions in question are grouped. So I would need some way to specify an indeterminate number of \t characters between the <IsActive> and <false/> portions of the xml. But I am not sure how to use RegEx in AppleScript or if that is even possible.

Test AppleScript (click to expand/collapse)
tell application "Keyboard Maestro"
	tell macro group "Test AppleScript Group"
		set macrosList to name of every macro
		repeat with a from 1 to length of macrosList
			set macroName to item a of macrosList
			--display dialog macroName
			tell macro macroName
				set macroXML to its xml
				--display dialog macroXML
			end tell
			if macroXML contains "<key>IsActive</key>
			<false/>" then
				set b to "yes"
			else
				set b to "no"
			end if
			display dialog macroName & b
		end repeat
	end tell	
end tell
1 Like

Going a little more "brute force", I think that all you need to check for is "more than one tab" -- one tab is the "macro disabled" level, so more than one must be an action (I think it goes 1, 3, 5... as you nest actions).

So, using explicit tab chars rather than including them in the strings to make things more obvious:

set theSplitter to ("<key>IsActive</key>
")
set {oldTIDs, AppleScript's text item delimiters} to {AppleScript's text item delimiters, theSplitter}
tell application "Keyboard Maestro"
	set theSelection to selection
	repeat with eachItem in the theSelection
		try
			set testText to text item 2 of (get xml of eachItem)
		on error
			exit repeat
		end try
		--set testText to every text item of (xml of eachItem)
		if paragraph 1 of testText contains (tab & tab & "<false/>") then
			set name of eachItem to name of eachItem & " -- **Disabled Action**"
		end if
	end repeat
end tell

set AppleScript's text item delimiters to oldTIDs

Even that's a bit "safety first"! It looks like you only get an IsActive key when a macro or action is disabled, so you could reduce it to

tell application "Keyboard Maestro"
	set theSelection to selection
	repeat with eachItem in the theSelection
		if (xml of eachItem) contains (tab & tab & "<key>IsActive</key>") then set name of eachItem to name of eachItem & " -- **Disabled Action**"
	end repeat
end tell

...again riffing on the fact that the key/value pairs for any action is indented by more than one tab.

Once you're sorted on the "old" actions you can then switch to using a macro to toggle disabled/enabled, adding and removing a line in the "Notes" of the action that you can then use in a Smart Group of note:**Disabled Action**. Something like this, which I'm sure could be improved...

Toggle Action Disable:Enable.kmmacros (11.2 KB)

Image

Your comment here made me think of a macro I already had built from over a year ago to toggle disabling/enabling actions.

I realized that after toggling them (done via RegEx) I could then insert a prebuilt comment with whatever text I want it to say, in this case "Disabled Action (timestamp)".

It works quite well for now... though it would still require me to individually remove each comment action once I'm done. But until I can think of some better way to parse the plist file via AppleScript it'll do the trick.

Macro below if anybody is interested in seeing/testing it out.

41)[AS-KM] Select Menu Item- Toggle Action(s).kmmacros (10 KB)

Macro screenshot (click to expand/collapse)

Did you try the "Toggle Action..." macro above? Once you've a baseline sorted it might do, or could easily be extended to do, what you want. I've no proof, but my gut feeling is that it's safer to edit an active-in-the-Editor macro directly via KM or indirectly via KM and AS than by hacking the plist in the background.

I did try a "scanning" AS but got stuck with Groups -- they seem to be an anomaly in that they don't have a list of actions you can recurse through but are an XML of actions, which gets away from the "target actions as objects" approach I was trying for. For anyone interested in having a go themselves:

tell application "Keyboard Maestro"
	repeat with eachMacro in (get the selection)
		set actionList to actions of eachMacro
		processActions(actionList) of me
	end repeat
end tell

display dialog "Done"

on processActions(theActions)
	tell application "Keyboard Maestro"
		repeat with eachAction in theActions
			tell eachAction
				if thenactions of eachAction is not missing value then
					processActions(actions of thenactions of eachAction) of me
				end if
				if elseactions of eachAction is not missing value then
					processActions(actions of elseactions of eachAction) of me
				end if
				if tryactions of eachAction is not missing value then
					processActions(actions of tryactions of eachAction) of me
				end if
				if catchactions of eachAction is not missing value then
					processActions(actions of catchactions of eachAction) of me
				end if
				if enabled is false and notes does not contain "**Disabled Action**" then
					if notes is missing value then
						set notes to "**Disabled Action**"
					else
						set notes to "**Disabled Action**" & return & notes
					end if
				end if
			end tell
		end repeat
	end tell
end processActions

I hadn't had time to try it initially, no. I did get to use it yesterday afternoon though.

One thing I noticed with your macro is if an action (or multiple actions) already have notes, these are lost as when a note is applied to more than one note, it contains "multiple notes" or something to that effect. When it's updated, then each individual action loses the note it originally had. :thinking:

I too am having difficulties with the AppleScript approach but i'll keep tinkering with it.

That's... interesting. The macro was meant to be run on one action at a time. If you're using it to disable an action and there's already a note it should "select all" then left-arrow to take you to the beginning of the note -- perhaps your machine's faster than mine and needs a slight pause there?