Feature Requests: Comments on Triggers, Rearranging Triggers

In general, I agree. But I have a situation that is prompting me in the automated direction.

I have a list of triggers, ⌥⌘A through ⌥⌘Z that I originally started with, as potential hotkeys to switch to different desktops. I deliberately left out ⌥⌘Q and ⌥⌘W because of their nearly universal and annoying side effects if they aren't caught by KBM. I also wanted the keys to be mnemonic, so I still haven't used "J" but "K" was one of my first ones, for my KBM desktop. Over time I have been adding shifted versions of them, e.g.,⌥⇧⌘K became my KBM "meta" desk, for Forum research and AppleScript testing.

Because of the nature of how additions are made to the KBM Triggers list, all the shifted triggers are now at the bottom of the list and they are not in alphabetical order. Recently I added ⌥⇧⌘A for a second Admin desktop so now ⌥⌘A is at the top and ⌥⇧⌘A is at the bottom of a list of 36 triggers. My aesthetics rebell. Clean code should be readable, organized, and even beautiful, if possible. To clean it up would mean over ten minutes of tedium, retyping the list in the new order, and future changes would have to be done all over again, so I'm spending up to 10 hours seeing if I can program it, because for me the programming — and the learning that comes along with getting the programming to work — is fun (most of the time).

For instance, to replace the list of triggers, I am pretty sure that I have to first delete the existing list of triggers, one by one. In AppleScript, I got the list of triggers from KBM and stepped through the list, deleting them one at a time. But there was a knotty problem. If I had a test list of four triggers, the loop would delete two of them. If I made a test list of eight triggers, the loop would delete four of them. WTH?

I finally figured out that AppleScript was creating the list of triggers and then stepping through its internal list to execute the deletions. So with four triggers, it deletes trigger 1 and then the list shifts so that when it deletes trigger 2, it's actually deleting trigger 3. Then it attempts to delete trigger 3, but there is no trigger 3, so it quits. In a list of 36 triggers, it would presumably delete every odd trigger until it had deleted 18 of them — not my intent.

I am sure there is a simple AppleScript option/flag to process the list in reverse order, so as soon as I find that, I presume this step will be done and I can move onto the next step.

I think both of those will be simple, although actually coding the dictionary/look-up table for the key codes may take some creative use of VIM and SED or some such.

I am definitely not going to bother with this stuff in the first (or even second) pass.

Well that was quick. Here's the AppleScript that empties the Trigger List, before repopulating it:

-- identify the currently selected macro
tell application "Keyboard Maestro"
	set theSelectedMacro to macro id (item 1 of (get selectedMacros))
	
	-- get list of triggers
	set theTriggerList to triggers of theSelectedMacro
	
	-- for each trigger in the reversed list, delete it.
	repeat with theTrigger in (reverse of theTriggerList)
		delete theTrigger
	end repeat
	
end tell

Totally understand the desire -- the urge, even -- to reorganise.

If all you want to do is reverse the current list, try

tell application "Keyboard Maestro"
	set selectedMacro to macro id (item 1 of (get selectedMacros))
	
	set theTriggers to reverse of (get triggers of selectedMacro)
	repeat with eachItem in theTriggers
		copy eachItem to end of selectedMacro's triggers
	end repeat
	repeat count of theTriggers times
		delete first trigger of selectedMacro
	end repeat
end tell

If you want to sort the triggers, however, you are going to have to determine your desired sort order and then, perhaps, roll your own code for that. As an example of the possible problems -- if you want "alphabetically by the non-modifier key" then even if you only have hot key triggers an ASCII sort won't do it since:

The Hot Key ⌃⌥⌘A is pressed
The Hot Key ⌃⌘B is pressed

...sorts to:

The Hot Key ⌃⌘B is pressed
The Hot Key ⌃⌥⌘A is pressed

No, you just blat the lot!

tell application "Keyboard Maestro"
	set selectedMacro to macro id (item 1 of (get selectedMacros))
	delete every trigger of selectedMacro
end tell

Remember that you are dealing with references to triggers, not the triggers themselves. So when you delete item 1 of the triggers then go to delete item 2, it's item 2 of the current list -- i.e. item 3 of the original. That's why you were nuking every other trigger -- and why, in my script above, I added all the triggers to the end again then nuked the originals.

As usual with me, none of this is gospel. TBH, AS lists and references and so on do my head in, I don't fully understand them, but do know enough to keep poking around until things work :wink: So consider the above as pragmatic solutions/truths that could be improved on by someone who actually knows what they are doing!

1 Like

Thanks for all the detailed help, @Nige_S. I've been approaching the sorting problem in a different thread (How to sort a list by first non-modifier character?) and @ComplexPoint came up with the Shell commands to do that — IF I first insert delimiters around the modifier symbols so that the input line can be broken into distinct fields. It's some pretty basic Regex editing to do that and then remove them again. The tricky part is how to determine where they go if the trigger has no modifiers. But I don't think that's the case in my first usage so I can leave that as a special case to deal with later, if ever.

Well isn't that a whole lot easier!

Unfortunately, that approach doesn't work when...

I do want to sort the triggers, and in the other thread mentioned above I've been getting help working out the sorting order. But to actually sort the triggers themselves, in the macro, I think I need to:

  • Save the XML of each trigger and name it based on the description of the trigger.
  • Generate a list of all the names.
  • Sort the list of names using first whatever follows the modifier symbols (see other thread).
  • Insert the saved trigger XML into the macro in the order determined by the sorted list of names.

Which is why we add the "new" triggers to the end of the trigger list, then delete the "old" ones from the beginning of the list.

Rather than mess around with XML etc, I'd take a similar approach for what you want to do.

  1. Get a list of trigger descriptions
  2. Create a "sorted list", based on description, of trigger indices
  3. Work through that sorted list, appending triggers by index
  4. Delete original triggers

Let me get this straight and clear. You're suggesting that the sorted list doesn't need to point to saved files of the XML for each trigger, or to KBM named clipboards or anything else that saves the XML code for each trigger, it only needs to have the index number of where that entry was/is in the original list. Just the original index number, nothing else.

Then you would go through the sorted list and add new triggers, simply by copying the original trigger by its index number as a new trigger, and do this for the whole sorted list. At this point the trigger list is twice as long as it should be, containing both the unsorted and the sorted triggers, in that order.

Then you delete the first half of the list by deleting items 1 through count, where count is a number you saved somewhere along the way, the size of the original list.

That's pretty cool, and I don't think that I would have thought of that, I was really focussed on how to save the XML to use it to create new triggers after I had deleted the originals. Your method, by keeping the originals around to copy them, is much cleaner and there's no temporary files or named clipboards or whatever to have to go back and clean up.

Thanks.

All that can be done in AppleScript with variations on what I've already done — except the Regex editing and the Shell-based sort. Using the Shell's sort is really convenient because it handles sorting by different fields so easily, but it requires inserting and removing delimiters into the items. I suppose I could pop back to KBM to do that and then go into AppleScript again to finish up.

You wouldn't happen to have slick tricks for how to run a Shell script from inside AppleScript, would you?

This, perhaps, demonstrates what's going on. Run it from your favourite script editor and it will bring KME to the front, then pause for 2 seconds within each loop so you can watch the trigger list changing in the UI:

tell application "Keyboard Maestro"
	activate
	set selectedMacro to macro id (item 1 of (get selectedMacros))
	delay 2
	set theTriggers to reverse of (get triggers of selectedMacro)
	repeat with eachItem in theTriggers
		copy eachItem to end of selectedMacro's triggers
		delay 2
	end repeat
	repeat count of theTriggers times
		delete first trigger of selectedMacro
		delay 2
	end repeat
end tell

One clarification:

count is a property of a list -- the number of items in it. You can also use length, which might be more in line with other languages, but AS or more English-like (you don't "length" the number of things in a box, you "count" them!).

set myList to {"one", "two", "three", "four"}
count of myList
--> 4

So we don't need to save the value, we just ask theTriggers how many items it contains.

Check the do shell script verb in the Standard Additions dictionary. But I think that, in this case, you're better off bouncing to KM, text processing there, then coming back to AS to make the trigger changes.

Don't forget that, as users of both AS and KM, we can call the KME's regex engine with AppleScript!

Also, a length is a measurement, a "real" number with potentially infinite decimals and roundoff error, a count is a whole number, an integer, no rounding, no error (unless you miscount).

This discussion from a year ago covered two two topics, making comments in the Triggers List and rearranging the Triggers List. The comments part terminated quickly when Peter said it was not likely to be added as a feature. The conversation on rearranging and sorting the Triggers List bore a lot of fruit.

I've found a way to add brief annotations into the middle of the Triggers List.

In one of my most commonly used macros, a single macro changes the Desktop focus to a new Space depending on the Trigger that initiated the macro. That macro has over 30 Triggers. To annotate the list, I just use the Typed String trigger and start the string with with an icon or special character that I cannot accidentally type.

In this case, I added a note at the end of the list of triggers to remind myself of the trigger that I have defined that will sort the list of triggers in the macro. So I can add new triggers at the end of the list (the only place they can be added, currently) and then run the sort macro by typing the key sequence. The annotation will sort at the end of the list, so it will be there to remind me next time.

Unfortunately, annotations in the middle of the list aren't going to sort into the desired place because Typed String triggers will sort together, separate from Hotkey Triggers, so I'll have to rely on the KMET macros to edit the trigger order in JSON by hand.

My solution to the many triggers for one macro problem is, to me, both easier to manage and easier to use than @August's. I split my triggers across multiple macros, each of which then invokes one core macro for the common operations. The core macro avoids the code duplication problem; the separately triggered macros solve much of the documentation problem by allowing more descriptive names for subsets of triggers. I do have some macros that would have more than 20 triggers without doing this.

I also want to avoid looking at a macro's source for functional info. I rely heavily on KeyCue from Ergonis Software to show me all currently available keyboard shortcuts and the operation or name of the macro it invokes.

The one downside of KeyCue is that it uses a columnar layout that ellipsizes (probably not a word) long names rather than line-wrapping, so August's penchant for long macro names could create problems. I do regularly find myself having to carefully choose names so that what I see in KeyCue is helpful. (There is another benefit to this, though. It helps keep the Macros column in KM narrower and more readable.)

I believe that in KeyCue you can do this by defining a separate hotkey for each set.

@August, my apologies. I should have responded long ago, but you apparently posted this between my original the edited version after I realized I should check for showing the groups separately.

Unfortunately, that is of no help. KeyCue doesn't take advantage of the extra width to widen columns, it just enlarges all the text, keeping the same relative column widths.