# Chapter 8: Customizing Leo

This chapter discusses how to customize Leo using the plugins and other means. See Specifying settings for a description of how to change Leo's settings.

Contents

- Chapter 8: Customizing Leo
  - Specifying settings
    - Organizer nodes
    - @ignore nodes
    - Simple settings nodes
    - Complex settings nodes
  - Input modes
  - Adding extensible attributes to nodes and .leo files
  - Specifying Tk options using .leo_xresources
  - Translating Leo's menus and messages

## Specifying settings

Leo stores options in `@settings` trees, that is, outlines whose headline is `@settings`. When opening a .leo file, Leo looks for `@settings` trees not only in the outline being opened but also in various leoSettings.leo files. The key design goal of `@settings` trees was that Leo's user options must be infinitely flexible. That goal has been accomplished. Indeed, users can create arbitrarily complex user options with `@settings` trees. Leo settings outlines are, in fact, much more flexible and powerful than any scheme based on flat text.

The `myLeoSettings.leo` file is a way of ensuring that your customized settings are not altered when updating Leo from cvs or while installing a new version of Leo. The myLeoSettings.leo acts much like Python's site-customize.py file. The `myLeoSettings.leo` file will never be part of any Leo distribution, and it will never exist in Leo's cvs repository. This solution is much better than trying to update leoSettings.leo with scripts.

The `Settings` command opens the file leoSettings.leo.

Leo stores options in `@settings` trees, that is, parts of Leo outlines whose root node has the headline `@settings`. When opening a .leo file, Leo looks for `@settings` trees in the following places:

- The file `leoSettings.leo` in the leo/config directory.
- The file `leoSettings.leo` in the user's home directory.
- The file `myLeoSettings.leo` in the leo/config directory.
- The file `myLeoSettings.leo` in the user's home directory.
- The file being loaded.

Settings that appear later in the above list override settings found earlier. For example, any setting specified in an `@settings` tree in the file being loaded overrides any setting seen in any `leoSettings.leo` file.

The following sections describe the kinds of nodes in `@settings` trees.

### Organizer nodes

Organizer nodes have headlines that do no start with @. Organizer nodes may be inserted freely without changing the meaning of an @setting tree.

### @ignore nodes

Leo ignores any subtree of an `@settings` tree whose headline starts with `@ignore`.

You can use several other kinds of nodes to cause Leo to ignore parts of an `@settings` tree:

- `@if` expression

  A node whose headline starts with `@if` expression acts like an organizer node if the expression evaluates to True, otherwise acts like an `@ignore` node. If the expression is empty the body text should contain a script that will be evaluated (in an empty context).

- `@ifplatform` platform-name

  Same as `@if sys.platform == "platform-name":` except that it isn't necessary to import sys.

### Simple settings nodes

Simple settings nodes have headlines of the form:

    @<type> name = val

set the value of `name` to `val`, with the indicated type.

<type> may be one of the following:

<type> | Valid values
@bool | True, False, 0, 1
@color | A Tk color name or value, such as 'red' or 'xf2fddff' (without the quotes)
@directory | A path to a directory
@float | A floating point number of the form nn.ff.
@int | An integer
@ints[list] | An integer (must be one of the ints in the list). Example: @ints meaningOfLife[0,42,666]=42
@keys[name] | Gives a name to a set of bindings for the Check Bindings script in leoSettings.leo.
@path | A path to a directory or file
@ratio | A floating point number between 0.0 and 1.0, inclusive.
@string | A string
@strings[list] | A string (must be one of the strings in the list). Example: @strings tk_relief['flat','groove','raised']='groove'

Note: For a list of Tk color specifiers see:

- http://www.tcl.tk/man/tcl8.4/TkCmd/colors.htm
- http://www.tcl.tk/man/tcl8.4/TkLib/GetColor.htm

Important: you can use the show-colors minibuffer command to guide you in making these settings.

### Complex settings nodes

Complex settings nodes have headlines of the form:

    @<type> description

The type may be one of the following:

<type> | Valid values
@buttons | Child @button nodes create global buttons
@commands | Child @command nodes create global buttons
@enabled-plugins | Body text contains a list of enabled plugins
@font | Body text contains a font description
@menus | Child @menu and @item nodes create menus and menu items.
@mode [name] | Body text contains a list of shortcut specifiers.
@recentfiles | Body text contains a list of file paths.
@shortcuts | Body text contains a list of shortcut specifies.

The actual settings are specified in the body text. At present, there are seven kinds of complex settings nodes:

- `@buttons`

  An @buttons tree in a settings file defines global buttons that are created in the icon area of all .leo files. All @button nodes in the @commands tree create global buttons. All @button nodes outside the commands tree create buttons local to the settings file.

- `@commands`

  New in Leo 4.4.8: An @commands tree in a settings file defines global commands. All @command nodes in the @commands tree create global commands. All @command nodes outside the commands tree create commands local to the settings file.

- `@data`

  The body text contains a list of strings, one per line. Lines starting with '#' are ignored.

- `@enabled-plugins`

  The body text of the @enabled plugins node contains a list of enabled plugins. This body text is treated exactly as the contents of the pluginsManager.txt file. Notes:

  > 1. Leo attempts to load all plugins every time an @enabled-plugins node is seen. If the plugin has already been loaded, Leo silently ignores the request to re-enable the plugin. Leo never attempts to disable a plugin while processing enabled plugin strings. Thus, plugins enabled in an @enabled-plugins node in leoSettings.leo will be enabled regardless of the contents of any other @enabled-plugins node.
  > 2. Leo will read the pluginsManager.txt file only if no @enabled-plugins node is found. The pluginsManager.txt file will no longer be part of the Leo distribution. Instead, the default plugins will be enabled in an @enabled-plugins node in leoSettings.leo.
  > 3. g.app.gui.getEnabledPlugins contains the last value last processed @enabled-plugins node, or the contents of the first pluginsManager.txt file encountered.

- `@font`

  The body text contains a list of settings for a font. For example:

      body_text_font_family = Courier New
      body_text_font_size = None
      body_text_font_slant = None
      body_text_font_weight = None

  Important: you can use the show-fonts minibuffer command to guide you in making these settings.

- `@menus`

  Leo creates its menus from the @menu and @item nodes in the @menus tree. Within @menus trees, @menu nodes create menus and @item nodes create menu items.

  The menu name always follows @menu. If the menu name is 'Plugins', Leo will create the Plugins menu and populate the menu by calling the 'create-optional-menus' hook. This creates the Plugins menu as usual. Nested @menu nodes define submenus.

  The command name follows @item. If the body text of an @item node exists, this body text is the menu name. Otherwise, the menu name is the command name. However, if the command name starts with a '*', hyphens are removed from the menu name. Menu names and command names may contain a single ampersand (&). If present, the following character is underlined in the name. If the command name in an @item node is just a hyphen (-), the item represents a menu separator.

- `@mode` <mode name>

  The body text contains a list of shortcut specifiers. `@mode` nodes work just like `@shortcuts` nodes, but in addition they have the side effect of creating the `enter-<mode name>-mode` command.

- `@recentfiles`

  The body text contains a list of paths of recently opened files, one path per line. Leo writes the list of recent files to `.leoRecentFiles.txt` in Leo's `config` directory, again one file per line.

- `@shortcuts`

  The body text contains a list of shortcut specifiers.

## Input modes

Leo now allows you to specify input modes. You enter mode `x` with the `enter-x-mode` command. The purpose of a mode is to create different bindings for keys within a mode. Often plain keys are useful in input modes.

You can specify modes with `@mode` nodes in leoSettings.leo. `@mode` nodes work just like `@shortcuts` nodes, but in addition they have the side effect of creating the `enter-<mode name>-mode` command.

Notes:

- You can exit any mode using the `keyboard-quit` (Control-g) command. This is the only binding that is automatically created in each mode. All other bindings must be specified in the `@mode` node. In particular, the bindings specified in @shortcuts nodes are not in effect in mode (again, except for the keyboard-quit binding).
- Leo supports something akin to tab completion within modes: if you type a key that isn't bound in a mode a 'Mode' tab will appear in the log pane. This tab shows all the keys that you can type and the commands to which they are bound. The mode-help command does the same thing.
- `@shortcuts` nodes specify the bindings for what might be called the 'top-level' mode. These are the bindings in effect when no internal state is present, for example, just after executing the keyboard-quit command.
- The `top_level_unbound_key_action` setting determines what happens to unbound keys in the top-level mode. Leo ignores unbound keys in all other modes. The possibilities are 'insert', 'replace' and 'ignore'.
- The `set-insert-mode`, `set-overwrite-mode` and `set-ignore-mode` commands alter what happens to unbound keys in the top-level mode.

With all these options it should be possible to emulate the keyboard behavior of any other editor.

## Adding extensible attributes to nodes and .leo files

Leo's .leo file format is extensible. The basis for extending .leo files are the `t.unknownAttributes` and `v.unknownAttributes` ivars of tnodes and vnodes, or uA's for short. Leo translates between uA's and xml attributes in the corresponding `<v>` and `<t>` elements in .leo files. Plugins may also use `v.tempAttributes` or `t.tempAttributes` ivars to hold temporary information that will not be written to the .leo file.

Collectively, these four kinds of ivars are called attribute ivars. Attribute ivars must be Python dictionaries, whose keys are names of plugins and whose values are other dictionaries, called inner dictionaries, for exclusive use of each plugin. For example, a plugin named 'xyzzy' would set `t.unknownAttributes` as follows:

    # Create the uA if necessary.
    if not hasattr(p.v.t,'unknownAttributes'):
        p.v.t.unknownAttributes = {}

    # Get the inner dictionary for the 'xyzzy' plugin, creating it if necessary.
    d = p.v.t.unknownAttributes.get('xyzzy',{})

    # Set some values. These values must be picklable.
    d ['duration'] = 5
    d ['notes'] = "This is a note."

    # Update the uA.
    p.v.t.unknownAttributes ['xyzzy'] = d

    if hasattr(p.v.t,"unknownAttributes"):
        d = p.v.t.unknownAttributes.get("xyzzy",{})
        g.es(d['duration'])
        g.es(d['notes'])

Plugins would use similar code to create `v.unknownAttributes`, `t.tempAttributes`, and `v.tempAttributes` ivars.

Important: All members of inner dictionaries should be picklable: Leo uses Python's Pickle module to encode all values in these dictionaries. Leo will discard any attributes that can not be pickled. This should not be a major problem to plugins. For example, instead of putting a tnode into these dictionaries, a plugin could put the tnode's gnx (a string) in the dictionary.

Note: Leo does not pickle members of inner dictionaries whose name (key) starts with `str_`. The values of such members should be a Python string. This convention allows strings to appear in .leo files in a more readable format.

Important: Plugins must not use `v.unknownAttributes` inside `@thin` trees. Indeed Leo uses hidden machinery to write `t.unknownAttributes`. Leo does not write `t.unknownAttributes` to thin derived files. Instead Leo writes a representation of all `t.unknownAttributes` contained in the `@thin` tree to a special xml attribute called `descendentTnodeUnknownAttributes` in the `<v>` element corresponding to the `@thin` node. Yes, this is complicated, but it works. Leo can not write `v.unknownAttributes` in `@thin` trees because only tnodes have gnx's in thin derived files. In effect, vnodes are anonymous.

Plugins that must associate attributes with vnodes should support only `@file` trees. A completely different alternative would be for the plugin to extend how Leo reads and writes `<v>` elements in .leo files, but that would be much more complicated than using `t.unknownAttributes`

Here are the details about how Leo associates uA's with `<v>` and `<t>` elements in .leo files:

- Native xml attributes are the attributes of `<v>` and `<t>` elements that are known (treated specially) by Leo's read/write code. The only native attribute of `<t>` elements is `tx`. The native attributes of `<v>` elements are `a`, `t`, `vtag`, `tnodeList`, `marks`, `expanded` and `descendentTnodeUnknownAttributes`. All other attributes of `<v>` and `<t>` elements are foreign xml attributes.
- When reading a .leo file, Leo will create `t.unknownAttributes` or `v.unknownAttributes` ivars for any `tnode` or `vnode` whose corresponding `<v>` or `<t>` element contains a foreign xml attribute.
- When writing a file, Leo will write foreign xml attributes in `<v>` or `<t>` elements if the corresponding vnode or tnode contains an `unknownAttributes` ivar.
- Leo performs the usual xml escapes on these strings when reading or writing the `unknownAttributes` ivars.

## Specifying Tk options using .leo_xresources

Leo looks for a file called `.leo_xresources` in the users home directory. If found, Leo will pass that file to Tk's `option_readfile` method for the top widget. This allows users to set Tk options.

## Translating Leo's menus and messages

It is easy to translate Leo's menu strings: simply create an @menus tree in leoSettings.leo or myLeoSettings.leo that contains the translated menu names.

New in Leo 4.4.8: Leo now contains support for translating messages sent to Leo's log:

- Rather than using an '_' function to denote strings to be translated, Leo's g.es and g.es_print functions translate "odd" (first, third, fifth) arguments, leaving "even" arguments untranslated. Keyword arguments, color, newline, etc. are never translated.
- All calls to g.es and g.es_print in Leo's core follow this convention.
- g.translateString does the actual translation using Python's gettext module.
- You can use the script in the node "@button print g.es stats" in scripts.leo to create catalogs of all scripts that need to be translated. Such catalogs are used by Python's gettext module. (This script was also used to check that the proper arguments to g.es and g.es_print were translated.)