back leo next

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.

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.

@if 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.

  • @ifhostname hostA,!hostB

    Evaluates to True iff: h=g.computeMachineName(); h==hostA and h!=hostB. The "!" version allows matching to every machineName except the given one to allow differing settings on only a few machines.

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:

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.
@menuat Child @menu and @item nodes modify menu tree create by @menus.
@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 nine 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, one per line. The first mention of a plugin controls whether the plugin is enabled or disabled. If the line starts with '#' the plugin is disabled. Otherwise the plugin is enabled.

    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. g.app.gui.getEnabledPlugins contains the last value last processed @enabled-plugins node.
    3. Leo no longer uses the pluginsManager.txt file.
  • @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, @item and @popup 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.

    @popup <widget-name> creates a popup menu for use by the rClick plugin.

    The children of this node should be @menu and @item nodes, used as with @menus.

  • @menuat <path> <action> [<source>]

    The @menuat setting has 2-3 parameters in its head text, its children are @menu and @item nodes as for the @menu setting. @menuat modifies the menu tree created by @menus. It is intended to be used in myLeoSettings.leo to modify the menu tree created in leoSettings.leo. This allows you to change the menus without having to re-create the entire menu tree from leoSettings.leo, and ensures you don't miss out when new things are added in the @menus in leoSettings.leo, as you would if you replaced the @menus in leoSettings.leo with one in myLeoSettings.leo. @menuat should occur in a @settings tree, but not as a descendant of a @menus tree. There is an example of the use of the @menuat setting in the file .../leo/core/test/menuAtTest.leo.

    • path

      This specifies a target location in the menu tree as defined by @menus and modified by earlier @menuat settings. The path takes the form /entry1/entry2/entry3 where each entry is the name of a menu or item with all text except a-z and 0-9 removed. Upper case letters are converted to lower case. So for example to use the Outline->Move->Move Down menu item as a target, you would specify a path as /outline/move/movedown.

    • action

      There are 5 available actions:

      • before

        The supplied items and sub menus will be inserted immediately before the target menu or item.

      • after

        The supplied items and sub menus will be inserted immediately after the target menu or item.

      • append

        The supplied items and sub menus will be appended at the end of the menu containing the target menu or item.

      • cut

        The target menu or item will be removed from the menu tree and saved to an internal clipboard. This can be used for deleting menus or items. Descendants of the @menuat setting are ignored.

      • copy

        The target menu or item will be copied and saved to an internal clipboard. Descendants of the @menuat setting are ignored.

    • source

      By default the before, after, and append actions insert the menus and items supplied as descendants of the @menuat setting. If you specify "clipboard" (without the quotes) as the source, the contents of the clipboard from a previous cut or copy action will be used instead. This parameter is optional and can be left blank.

  • @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.)

back leo next