Insert Switch action with all options from a list

I don't know if this is already possible or not, but if not, here's a feature suggestion @peternlewis

If I have a list of items (in my particular case today is a list of 27 Logic plugins) and I want to use the Switch action to do something different depending on the plugin picked using the Prompt With List action, it would be great if we could have an option to paste the list of items and when we click "OK" it would automatically create a Switch action with each line on that list using a separate condition instead of clicking the + button for each one and pasting each item individually.

So in my case it would look something like this, but for 27 plugins:
image

You could create a macro which populates a KM action, much the way you are asking for. Doing that is a little beyond my skill level.

1 Like

I believe this is probably what I need:

I need to read it when I have some time and then test it.

But I still think this could be a native solution for the Switch action to make the process easier and faster without relying on another macro like the one Dan shared, even though that one seems super useful for other things (that hopefully KM will be able to achieve one day, such as reordering things inside actions, for example)

I think the solution depends on how often you need to do this. If it's just an occasional one-off then you could cobble together a macro to press tab a few times to get to the next box and paste items from your list one at a time.

But it is certainly possible to do this by altering an Action's XML using Variables. Here is a proof of concept:

Create Switch Case Action from a List v1.00.kmmacros (4.1 KB)

To make this Macro, I first made a temporary Action to use as a template, that looked like this:

Then I right-clicked on this Action and selected "Copy as XML"

I pasted this XML code into a text editor and replaced the "PLACEHOLDER" text with the KM Variables I wanted to use.

So, this (bold used just to make clear):

Is edited to this:

Now I used the new text of the XML in my "Insert Text by Pasting" Action.

One thing to note - if this text is pasted from the text editor straight back into the KM "Insert Text by Pasting" Action, it will create the Action rather than inserting as text so, you first need to temporarily add some random character at the front of the text to "break it" and then remove that random character once it is pasted into the KM Action... In my case I temporarily added a "1" at the start:

image

As I said, this is more of a proof of concept but it shows how the XML text can be manipulated before pasting back into KM as a new Action. In this case Variables were used instead of actual text to add the name of the Variable in the Switch Case and the text in the three Switch Cases.

But all of the text in the template XML can be created and manipulated so, in theory it would be possible to make a more sophisticated Macro that allowed for any length of list as each item in the Switch Case is simply a block of text that could be repeated:

So, it is possible to make a Macro that counted the number of lines in your list and built up a block of text based on that. And here is a Macro that does that (i.e. there can be any number of items in the list)

Create Switch Case Action from a List v2.00 (any number of items in the list).kmmacros (7.2 KB)

1 Like

and wrapping Zabobon's approach in a subroutine, we could do something like this (subroutine and sample macro, illustrating its use) to copy a pasteable switch action to the clipboard:

Test Macros.kmmacros (8.1 KB)


Expand disclosure triangle to view JS source
return (() => {
    "use strict";

    ObjC.import("AppKit");

    const main = () => {
        const
            conditionArrayXML = conditionListXML(
                words(kmvar.local_Condition_Type)
                .map(toSentence)
                .join("")
            )(
                kmvar.local_Values_as_Lines_of_Text
                .split(/\n/u)
            );

        return copyText(
            `<array>
                <dict>
                    <key>ActionUID</key>
                    <integer>15795850</integer>
                    <key>CaseEntries</key>
                    <array>
                    ${conditionArrayXML}
                    </array>
                    <key>MacroActionType</key>
                    <string>Switch</string>
                    <key>Source</key>
                    <string>Variable</string>
                    <key>Variable</key>
                    <string>${kmvar.local_Variable_Name}</string>
                </dict>
            </array>`
        );
    };

    // conditionListXML :: String -> String -> XML String
    const conditionListXML = conditionTypeName =>
        testValues => testValues.map(
            conditionXML(conditionTypeName)
        )
        .join("\n");


    // conditionXML :: String -> String -> XML String
    const conditionXML = conditionTypeName =>
        s => `<dict>
            <key>Actions</key>
            <array/>
            <key>ConditionType</key>
            <string>${conditionTypeName}</string>
            <key>TestValue</key>
            <string>${s}</string>
        </dict>`;

    // ----------------------- JXA -----------------------

    // copyText :: String -> IO String
    const copyText = s => {
        const pb = $.NSPasteboard.generalPasteboard;

        return (
            pb.clearContents,
            pb.setStringForType(
                $(s),
                $.NSPasteboardTypeString
            ),
            s
        );
    };

    // --------------------- GENERIC ---------------------

    // toSentence :: String -> String
    const toSentence = s =>
    // Sentence case - first character
    // capitalized, and rest lowercase.
        0 < s.length
            ? s[0].toLocaleUpperCase() + s.slice(1)
            .toLocaleLowerCase()
            : s;

    // words :: String -> [String]
    const words = s =>
    // List of space-delimited sub-strings.
    // Leading and trailling space ignored.
        s.split(/\s+/u).filter(Boolean);

    return main();
})();
1 Like

Since it's me, you can probably guess what's coming... Yup -- AppleScript!

Not entirely what you're after, but if you

  1. Copy a return-delimited list to the Clipboard
  2. Add a new Switch/Case action to your macro
  3. Make sure the action is still selected
  4. Run this AppleScript
set itemList to every paragraph of (get the clipboard)

set XMLHead to "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">
<plist version=\"1.0\">
<dict>
	<key>Actions</key>
	<array/>
	<key>ConditionType</key>
	<string>Is</string>
	<key>TestValue</key>
	<string>"

set XMLTail to "</string>
</dict>
</plist>
"

tell application "Keyboard Maestro"
	set theAction to item 1 of (get selection)
	if case entries of theAction is missing value then
		display dialog "Make sure you have a \"Switch/Case\" action selected"
		return 0
	end if
	repeat with eachItem in itemList
		set theXML to XMLHead & eachItem & XMLTail
		make new case entry with properties {xml:theXML} at end of case entries of theAction
	end repeat
	delete first case entry of theAction
end tell

...the end result will be what you're after. You can, of course, pop the above into a KM macro's Execute an AppleScript action and run it by hotkey trigger.

I think it would be possible to automate the whole "add the action" process, but I'm failing hard at that. Also, there's the question of where in the macro the action should be added -- this way you have total control of that.

1 Like

As people note, you can make a macro to do this now, or create the macro XML manually.

This simply isn't something that happens often enough for me to implement directly in the editor.