Macro group of current app

I searched for "group current app" and found

It does what I want, except that the group must already exist. How can I make it create a new group named for the current app and open that if I the group doesn't already exist?

Here's a modified version of that macro that should do what you want (requires Keyboard Maestro 8):

Go-To:Create Front-App's Group in Keyboard Maestro v1.1.kmmacros (5.2 KB)

If you'd rather just change the AppleScript that powers it in the version you have, you can do that by copying it here:

--------------------------------------------------------------------------------
# Auth: Christopher Stone
# Mod: Gabe Glick
# dCre: 2016/11/14 16:15
# dMod: 2017/12/2 09:39
# Appl: Keyboard Maestro Editor
# Task: Select the first group whose name matches the name of the front application, and create it if it doesn't exist.
# Libs: None
# Osax: None
# Tags: @Applescript, @Script, @System_Events, @Select, @Group, @Front, @Application
--------------------------------------------------------------------------------

try
	
	tell application "System Events"
		set procName to name of first process whose frontmost is true
	end tell
	
	tell application "Keyboard Maestro"
		activate
		if (exists of macro group procName) = false then
			set newGroup to make new macro group with properties {name:procName}
			select newGroup
		else
			select macro group procName
		end if
	end tell
	
on error e number n
	set e to e & return & return & "Num: " & n
	if n ≠ -128 then
		try
			tell application (path to frontmost application as text) to set ddButton to button returned of ¬
				(display dialog e with title "ERROR!" buttons {"Copy Error Message", "Cancel", "OK"} ¬
					default button "OK" giving up after 30)
			if ddButton = "Copy Error Message" then set the clipboard to e
		end try
	end if
end try

--------------------------------------------------------------------------------

I can't take much credit for this, as this is all scripting that @ccstone did originally; I just copied AppleScript code he wrote for a different macro and tweaked it where appropriate for this one. It worked well enough in my own testing, but let us know if you run into issues with it, or need a version that works with KM7.

1 Like

Thank you, Gabe, it’s just what I needed.

1 Like

Hey Guys,

This method will be a trifle faster than using System Events:

----------------------------------------------------------------
# Faster than using System Events
----------------------------------------------------------------
set AppleScript's text item delimiters to {".app:", ":"}
set frontApp to (path to frontmost application as text)
set frontApp to text item -2 of frontApp
----------------------------------------------------------------

You also run into the problem of macro groups not being named exactly the same as the frontmost application, so I’ve used name contains <process name> to provide more leeway.

If more than one group is found a pick-list is presented to the user.

NOTE — I’m not creating the group as Gabe did above — just pointing out one of the pitfalls of this type of script.

----------------------------------------------------------------
# Auth: Christopher Stone
# dCre: 2017/12/02 03:42
# dMod: 2017/12/02 03:47
# Appl: Keyboard Maestro
# Task: Go to Macro Group Associated with the frontmost app.
#     : Present pick-list if more than one group is found.
# Libs: None
# Osax: None
# Tags: @Applescript, @Script, @Keyboard_Maestro, @Group, @Associated, @Frontmost, @App, @Pick-List
----------------------------------------------------------------
# Faster than using System Events
----------------------------------------------------------------
set AppleScript's text item delimiters to {".app:", ":"}
set frontApp to (path to frontmost application as text)
set frontApp to text item -2 of frontApp
----------------------------------------------------------------

tell application "Keyboard Maestro"
   activate
   
   set macroGroup to name of macro groups whose name contains frontApp
   
   if length of macroGroup is 1 then
      
      select macro group (item 1 of macroGroup)
      
   else
      
      # Presents a pick-list if more than one group is found.
      set theChoice to choose from list macroGroup with title "Macro Group List" with prompt "Pick a Group to Select" default items {item 1 of macroGroup} without empty selection allowed
      
      if theChoice is not false then
         set theChoice to item 1 of theChoice
         select macro group theChoice
      end if
      
   end if
   
end tell

----------------------------------------------------------------

Here’s a prettified list of the macro group result I get for Script Debugger:

{
   "Script Debugger { JM }", 
   "Script Debugger 5", 
   "Script Debugger 6"
}

Most of my application groups are named exactly the same as the app, but there are a few that will yield multiple hits.

In my own go-to-macro-group script I’ve mapped these to a preferred macro-group rather than fooling with a pick-list.

Here’s one good reason to use System Events instead of the method above:

tell application "System Events"
   tell (first process whose frontmost is true)
      set frontAppBundleID to bundle identifier
      set frontAppName to name
   end tell
end tell

{frontAppName, frontAppBundleID}

Many apps are sensible enough to change the Bundle Identifier when a major version change occurs.

For instance:

Script Debugger 5

{
   "Script Debugger", 
   "com.latenightsw.ScriptDebugger5"
}

Script Debugger 6

{
   "Script Debugger", 
   "com.latenightsw.ScriptDebugger6"
}

By getting the Bundle Identifier you can easily discriminate between versions.

Unfortunately not all developers are as well behaved as Mark Alldritt, so this method isn’t infallible.

You can usually get adequate information out of the Finder and/or System Events:

----------------------------------------------------------------

tell application "System Events"
   tell (first process whose frontmost is true)
      set frontAppBundleID to bundle identifier
      set frontAppName to name
   end tell
end tell

tell application "Finder"
   set appAlias to application file id frontAppBundleID as alias
end tell

tell application "System Events"
   set appVersion to short version of appAlias
end tell

set frontAppName to frontAppName & space & appVersion

--> "Script Debugger 6.0.6"

----------------------------------------------------------------

But sometimes you might need a secret string like the CFBundleVersion hidden in the Info.plist of an app.

----------------------------------------------------------------
# Auth: Christopher Stone
# dCre: 2017/12/02 04:30
# dMod: 2017/12/02 04:39
# Appl: Frontmost Application
# Task: Get App Name, Short Version, and Bundle Version
# Libs: None
# Osax: None
# Tags: @Applescript, @Script, @ASObjC, @App, @Name, @Short, @Version, @Bundle
----------------------------------------------------------------
use AppleScript version "2.4" -- Yosemite or later
use framework "Foundation"
use scripting additions
----------------------------------------------------------------

set AppleScript's text item delimiters to {".app/", "/"}
set frontAppPath to POSIX path of (path to frontmost application as text)
set frontAppName to text item -2 of frontAppPath

----------------------------------------------------------------

set plistPath to frontAppPath & "Contents/Info.plist"
set theDict to current application's NSDictionary's dictionaryWithContentsOfFile:plistPath
set shortVersionString to (theDict's valueForKeyPath:"CFBundleShortVersionString") as text
set bundleVersion to (theDict's valueForKeyPath:"CFBundleVersion") as text

return frontAppName & space & shortVersionString & space & "(" & bundleVersion & ")"

----------------------------------------------------------------

--> "Script Debugger 6.0.6 (6A212)"

----------------------------------------------------------------

Are your eyes crossed yet?  :alien:

The bottom line?

One way or another this job can be done in a way that should satisfy most users.

** Ha! It’s later than I thought — so if I’ve rambled any it’s 'cause I’m a little tired.

-Chris

2 Likes

Thanks for weighing in, Chris! This was highly educational as always, and I can confirm that the speed difference in not using System Events to acquire the front app’s name is consistent and noticeable.

1 Like

Hey Guys,

@JMichaelTX came up with a clever idea.

This script will get the macro groups that are associated with the name of the frontmost app, regardless of whether the group name contains the app name or not.

----------------------------------------------------------------
# Auth: @JMichaelTX - altered a bit by @ccstone
# dCre: 2017/12/02 15:34
# dMod: 2017/12/03 01:37
# Appl: Keyboard Maestro
# Task: Get Macro Group(s) of the frontmost application.
# Libs: None
# Osax: None
# Tags: @Applescript, @Script, @Keyboard_Maestro, @KM, @Get, @Macro, @Group, @Frontmost, @Application
----------------------------------------------------------------

# Use the Keyboard Maestro Engine to get the name of the frontmost app.
#   - This technique is much faster than using System Events.
tell application "Keyboard Maestro Engine" to set appName to process tokens "%Application%1%"

tell application "Keyboard Maestro"
   set macroGroupList to macro groups whose available application xml contains appName
end tell

----------------------------------------------------------------

Thumbs-up to JM; that was a really smart idea.

Okay, let’s just run with that ball…

The XML of the Macro Group contains the Bundle ID of the app it’s associated with, so we can do something like this:

----------------------------------------------------------------

tell application "System Events" to set frontAppBundleID to bundle identifier of first process whose frontmost is true

tell application "Keyboard Maestro"
    activate

    set foundMacroGroups to macro groups whose available application xml contains frontAppBundleID
    set frontAppGroup to item 1 of foundMacroGroups
    
    select frontAppGroup
    
end tell

----------------------------------------------------------------

In my case I’m probably going to want to select a single group most of the time, however we can get creative and do something like this to select one or more found groups:

----------------------------------------------------------------

tell application "System Events" to set frontAppBundleID to bundle identifier of first process whose frontmost is true

tell application "Keyboard Maestro"
   activate
   set foundMacroGroups to select (macro groups whose available application xml contains frontAppBundleID)
end tell

----------------------------------------------------------------

This macro will select all Macro Groups that contain the Bundle ID of the frontmost app.

-Chris

2 Likes

Wow, way to go @JMichaelTX and Chris! I would never have guessed that using KM tokens to acquire the app name would be so fast, and using the XML to check for groups that are only available for the app name regardless of what the group is named really is a super smart idea. Thank you both for the illuminating examples!

1 Like

I replaced my shortcut for launching Keyboard Maestro (⌥⌘↩) with a macro based on this:

try
   set AppleScript's text item delimiters to {".app/", "/"}
   set frontAppPath to POSIX path of (path to frontmost application as text)
   set procName to text item -2 of frontAppPath
   
   tell application "Keyboard Maestro"
      activate
      if (exists of macro group procName) = false then
         set newGroup to make new macro group with properties {name:procName}
         select newGroup
      else
         select macro group procName
      end if
   end tell
   
on error e number n
   set e to e & return & return & "Num: " & n
   if n ≠ -128 then
      try
         tell application (path to frontmost application as text) to set ddButton to button returned of ¬
            (display dialog e with title "ERROR!" buttons {"Copy Error Message", "Cancel", "OK"} ¬
               default button "OK" giving up after 30)
         if ddButton = "Copy Error Message" then set the clipboard to e
      end try
   end if
end try

However, I’m really not trying to turn Keyboard Maestro into QuicKeys, I promise.

1 Like

I modified your amazing macro to also include an action to instantly bring focus to macros. It's bette this way I think.

Here is the macro:

goto km group of the app.kmmacros (30.1 KB)

Thank for the kind words, @nikivi! This is a great idea, and fortunately, the version you posted can be optimized to be even faster. Here's a modified version that includes the AppleScript-only way (i.e. without using System Events) of getting the front app name that Chris suggested in post 4, and uses the new-to-KM8 (at least, I think it is) View > Select Macros Column menu command in a Select or Show Menu Item action instead of Chris's GUI script, which is no longer needed now that we have this native menu command, to bring focus to the macro. In my tests, the older version took on average 0.72 seconds to complete and this version takes 0.38 seconds, making it almost twice as fast:

goto km group of the app.kmmacros (30.4 KB)

I've left the timing actions I used to test the elapsed time in the macro (disabled by default) in case you'd like to test the difference for yourself. Thanks again for the smart idea on how to improve it!

1 Like

I used Nikita's suggestion to focus the selection on macros and JM's and Chris Stone's idea of selecting all relative groups, and wound up with this.

set AppleScript's text item delimiters to {".app/", "/"}
set frontAppPath to POSIX path of (path to frontmost application as text)
set procName to text item -2 of frontAppPath

tell application "Keyboard Maestro"
   launch
   set tester to (exists of macro group procName)
end tell

if tester then
   tell application "System Events" to set frontAppBundleID to bundle identifier of first process whose frontmost is true
   tell application "Keyboard Maestro"
      activate
      set foundMacroGroups to select (macro groups whose available application xml contains frontAppBundleID)
   end tell
else
   tell application "Keyboard Maestro"
      activate
      set newGroup to make new macro group with properties {name:procName}
      select newGroup
   end tell
end if

2 Likes

Nice idea, @Lantro! This inspired me to tweak it a bit more and see if I could also accommodate for scenarios where groups that include the front app's bundle ID exist, but are not named after that app (for example, I have all the Microsoft Office apps lumped together in a single Office group, so I don't want the script making a new group for Excel), and I think I managed to come up with something that works and takes into account all of the ideas presented here. By default, this version presents a pick-list for cases where multiple macro groups contain either the name or bundle ID of the front app, but I've also included a script action to select all macro groups with the front app's bundle ID so that users can use whichever one they like:

Open KM in Group for Front App v1.3.kmmacros (8.5 KB)

Select All Macro Groups AppleScript

[details=Show Code]

----------------------------------------------------------------
# Auth: Christopher Stone, @JMichaelTX, Laine Lee, Gabe Glick
# dCre: 2017/12/06 18:30
# Appl: Keyboard Maestro
# Task: Go to all macro groups associated with the frontmost app.
#     : Create new macro group if none exists.
# Libs: None
# Osax: None
# Tags: @Applescript, @Script, @Keyboard_Maestro, @Group, @Associated, @Frontmost, @App
----------------------------------------------------------------

try
	
	tell application "System Events"
		tell (first process whose frontmost is true)
			set frontAppBundleID to bundle identifier
			set frontAppName to name
		end tell
	end tell
	
	tell application "Keyboard Maestro"
		launch
		set XMLtester to (exists of (macro groups whose available application xml contains frontAppBundleID))
		set nameTester to (exists of macro group frontAppName)
	end tell
	
	if XMLtester = true then
		tell application "Keyboard Maestro"
			activate
			set foundMacroGroups to select (macro groups whose available application xml contains frontAppBundleID)
		end tell
	else if nameTester = true then
		tell application "Keyboard Maestro"
			activate
			set foundMacroGroups to select (macro groups whose name contains frontAppName)
		end tell
	else
		tell application "Keyboard Maestro"
			activate
			set newGroup to make new macro group with properties {name:frontAppName}
			select newGroup
		end tell
	end if
	
on error e number n
	set e to e & return & return & "Num: " & n
	if n ≠ -128 then
		try
			tell application (path to frontmost application as text) to set ddButton to button returned of ¬
				(display dialog e with title "ERROR!" buttons {"Copy Error Message", "Cancel", "OK"} ¬
					default button "OK" giving up after 30)
			if ddButton = "Copy Error Message" then set the clipboard to e
		end try
	end if
	
end try

----------------------------------------------------------------
```[/details]

###Pick-List Version AppleScript
[details=Show Code]
```applescript
----------------------------------------------------------------
# Auth: Christopher Stone, @JMichaelTX, Laine Lee, Gabe Glick
# dCre: 2017/12/06 18:48
# Appl: Keyboard Maestro
# Task: Go to all macro groups associated with the frontmost app.
#     : Create new macro group if none exists.
# Libs: None
# Osax: None
# Tags: @Applescript, @Script, @Keyboard_Maestro, @Group, @Associated, @Frontmost, @App
----------------------------------------------------------------

try
	
	tell application "System Events"
		tell (first process whose frontmost is true)
			set frontAppBundleID to bundle identifier
			set frontAppName to name
		end tell
	end tell
	
	tell application "Keyboard Maestro"
		launch
		set XMLtester to (exists of (macro groups whose available application xml contains frontAppBundleID))
		set nameTester to (exists of macro group frontAppName)
	end tell
	
	if XMLtester = true then
		tell application "Keyboard Maestro"
			activate
			set macroGroup to name of macro groups whose available application xml contains frontAppBundleID
			
			if length of macroGroup is 1 then
				select macro group (item 1 of macroGroup)
			else
				
				# Presents a pick-list if more than one group is found.
				set theChoice to choose from list macroGroup with title "Macro Group List" with prompt "Pick a Group to Select" default items {item 1 of macroGroup} without empty selection allowed
				
				if theChoice is not false then
					set theChoice to item 1 of theChoice
					select macro group theChoice
				end if
				
			end if
		end tell
	else if nameTester = true then
		tell application "Keyboard Maestro"
			activate
			set macroGroup to name of macro groups whose name contains frontAppName
			
			if length of macroGroup is 1 then
				select macro group (item 1 of macroGroup)
			else
				
				# Presents a pick-list if more than one group is found.
				set theChoice to choose from list macroGroup with title "Macro Group List" with prompt "Pick a Group to Select" default items {item 1 of macroGroup} without empty selection allowed
				
				if theChoice is not false then
					set theChoice to item 1 of theChoice
					select macro group theChoice
				end if
				
			end if
		end tell
	else
		tell application "Keyboard Maestro"
			activate
			set newGroup to make new macro group with properties {name:frontAppName}
			select newGroup
		end tell
	end if
	
on error e number n
	set e to e & return & return & "Num: " & n
	if n ≠ -128 then
		try
			tell application (path to frontmost application as text) to set ddButton to button returned of ¬
				(display dialog e with title "ERROR!" buttons {"Copy Error Message", "Cancel", "OK"} ¬
					default button "OK" giving up after 30)
			if ddButton = "Copy Error Message" then set the clipboard to e
		end try
	end if
	
end try

----------------------------------------------------------------
```[/details]

Thank you to everyone who posted here for all the great ideas!
2 Likes