pcb-rnd knowledge pool

 

Menu patching tutorial

menu_patch by Tibor 'Igor2' Palinkas on 2020-07-18

Tags: howto, menu, patching, patch, custom, change, hotkey, key

node source

 

 

Abstract: The run-time menu patching system in pcb-rnd is a powerful infrastructure that lets users to make temporary or persistent, global or project-local modifications to the menu system. This article shows how to use the system and how to write menu patches.

 

Documentation

The following documentation may help understanding details of the system:

Hello world example

The simplest thing to do is creating a new menu item, appending it at the end of the parent submenu. This is useful when creating a custom menu shorthand for a frequently executed action. This example is on the top of the page because it can be used to demonstrate how to load and manage menu files.

The example menu file, ex_hello.lht is:


ha:rnd-menu-v1 {
 li:main_menu {
  ha:File {
   li:submenu {
    ha:hello  = { a={<key>h;<key>h}; action=message(hello) }
    ha:world  = { a={<key>h;<key>w}; action=message(world) }
   }
  }
 }
}

Once loaded, this will create two new menus at the bottom of the File menu. The new menus will print "hello" and "world" in the message log (open the message log window, e.g. using the {w m} hotkey to see these messages once the menu items are clicked). The new items can be invoked by hotkey {h h} and {h w} as well; this is specified using the usual menu file syntax.

How to test menu files/patches

The easiest way to test a menu file (or menu patch) is using the GUI to load it temporarily. Open the preferences window (File menu, Preferences, or hotkey {i c p}) and navigate to the "Menu" tab.

This tab shows the list of menu files/patches used for merging the final menu system. The list is long with a lot of plugins creating a few menus.

Click on the "Load..." button and select ex_hello.lht . This will create a new entry in the list, around the bottom, with priority 300. The menu merge will happen automatically and the new menus are already accessible from the File menu.

Loading a menu this way has temporary effect: once pcb-rnd is stopped, this list item is forgotten. It is also possible to add a menu file/patch to the list in a persistent way, see below.

In the menu tab of the preferences dialog, there is also an unload button that can be used to (temporarily) remove unwanted menu files/patches from the list. Or if you are developing a new menu file/patch and keep on editing the file in the background using a text editor, you can use the reload button any time to see your latest edits.

If you are in doubt about what a specific menu file/patch from this list really does, especially the ones generated by plugins, you can use the export button to save it in a file.

How make a persistent change

Once a menu file or menu patch is finished, you may want to use it for all pcb-rnd sessions, adding it to the above list permanently. This is done by saving the file at some easy to access path, e.g. under ~/.pcb-rnd/ and adding it to the conf node rc/menu_patches.

That node, rc/menu_patches, is a list of full paths to menu files and menu patches. You can easily append to that list placing the following subtree under li:pcb-rnd-conf-v1 subtree in any of your config files:


 ha:append {
  ha:rc {
   li:menu_patches {
    {~/.pcb-rnd/ex_hello.lht}
   }
  }
 }

The most trivial place for this is ~/.pcb-rnd/pcb-conf.lht , which is the user level configuration - any pcb-rnd instance ran by the user with this home directory will be affected.

But it is also possible to include such a config subtree in the project file or even in the board file's config section. That means it is possible to creare per project or even per board extra menus.

Menu file examples

The "hello world" example above demonstrated how to append new menu items at the end of existing submenus. But it is also possible to create new submenus :


ha:rnd-menu-v1 {
 li:main_menu {
  ha:File {
   li:submenu {
    ha:demo {
     li:submenu {
      ha:hello  = { a={<key>h;<key>h}; action=message(hello) }
      ha:world  = { a={<key>h;<key>w}; action=message(world) }
     }
    }
   }
  }
 }
}

This will create a new submenu called 'demo' at the bottom of the File menu and will create hello and world menu items within that.

Note: the rule for such menu files is "overwrite or append". During the merge each node in the tree is checked against the current in-memory menu image; if the node matches, it is an append, if it doesn't match it is an overwrite. For example the File menu matches the one in the stock menu file, thus pcb-rnd will not create a new File menu. However, the demo submenu within the File menu doesn't exist in the original menu file, so it has to be created, which is an append operation.

An overwrite operation is when an existing tree node is specified with differing content. There are mainly two examples on this.

First example is replacing an existing menu item. Suppose using the File/revert menu is just too dangerous and you want to have a error message instead of reverting, using the following menu file :


ha:rnd-menu-v1 {
 li:main_menu {
  ha:File {
   li:submenu {
    ha:Revert = { action=message(ERROR, "revert is too dangerous!") }
   }
  }
 }
}

Since File/Revert existed, it is kept, including its original hotkey; but since the action File/Revert/action differs, it is overwritten and now the menu runs the message() action instead of the original Load(Revert,none) action.

The same overwrite happens on a different level if we replace a menu item with a whole submenu (or vice versa). For example the following menu file replaces File/Revert with a whole submenu hosting the hello world menu items:


ha:rnd-menu-v1 {
 li:main_menu {
  ha:File {
   li:submenu {
    ha:Revert {
     li:submenu {
      ha:hello  = { a={<key>h;<key>h}; action=message(hello) }
      ha:world  = { a={<key>h;<key>w}; action=message(world) }
     }
    }
   }
  }
 }
}

Such overwrite works only as long as all menu names on the path fully match, in case sensitive manner!

Note: once you unload the file making an overwrite, the overwrite is undone and whatever state the menu image had before the overwrite is regained.

Menu patch examples

The above method of injecting a partial menu file to overwrite (or append to) existing subtrees works for most of the cases. However, in some cases different operations are needed, such as removing an existing submenu or menu item. For such advanced operations it is possible to use menu patches instead of menu files.

A menu patch is an ordered list of instructions on how the current menu image should be edited.

Menu patches can be loaded, unloaded, configured the same way as menu files; for pcb-rnd it doesn't make a difference whether you describe your intentions as a menu file or a menu patch.

A typical example of a menu patch is to change the key binding of an existing menu. For example this menu patch changes the hotkey of File/Revert to {f shift-r} so it is harder to type accidentally:


ha:rnd-menu-patch-v1 {
 prio=400
 li:patch {
  ha:overwrite_menu_props {
   path = /main_menu/File/Revert
   ha:props { a={<key>f;shift<key>r};}
  }
 }
}

Or as a more drastic measure this menu patch removes the Revert menu:


ha:rnd-menu-patch-v1 {
 prio=400
  li:patch {
   ha:remove_menu {
    path = /main_menu/File/Revert
   }
 }
}