Here is a preliminary script that updates a new leoSettings.leo file (path2) from the settings in a previous leoSettings.leo file (path1). Presumably, the path2 file is the leoSettings.leo file from a new distribution, and the path1 file a leoSettings.leo file containing settings that you have modified. You must specify the full path of both files in the top-level @button node.

Warning: This script modifies the path2 file. Make sure that you don't mind modifying this file.

Warning: This script is unsophisticated in its update algorithm. If it finds corresponding nodes (node with the same settings name) in both outlines it simply replaces the node in the path2 file with the node in the path1 file. In particular, it does not keep track of individual shortcuts: it repaces the body of one @shortcuts node with the body of the corresponding @shortcuts node.

Note: If the path1 file contains a settings node not in the path2 file it attempts to create a new node in the corresponding place in the path2 file. If this is not possible, say because the structures of the files are different, the script places the new node as the last child of the @settings node in the path2 file.

To use this script, cut the following code, and then use Leo's Paste Node command to paste the outline. You should get a node called '@button updateSettings'.

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet ekr_stylesheet?>
<leo_file>
<leo_header file_format="1" tnodes="17" max_tnode_index="17"/>
<vnodes>
<v t="ekr.20060403091254.89" a="EV"><vh>@button updateSettings</vh>
<v t="ekr.20060408100009"><vh>&lt;&lt;docstring&gt;&gt;</vh></v>
<v t="ekr.20060408065715"><vh>class updateParser (parserBaseClass)</vh>
<v t="ekr.20060408065715.1"><vh>ctor</vh></v>
<v t="ekr.20060408065715.2"><vh>visitNode</vh></v>
<v t="ekr.20060408075705"><vh>kind handlers (overrides only)</vh>
<v t="ekr.20060403091254.12"><vh>doFont</vh></v>
<v t="ekr.20060403091254.22"><vh>doShortcut</vh></v>
<v t="ekr.20060403091254.23"><vh>doShortcuts</vh></v>
</v>
<v t="ekr.20060408065715.3"><vh>set</vh></v>
</v>
<v t="ekr.20060330091638" a="E"><vh>class updateClass</vh>
<v t="ekr.20060330091638.1"><vh> ctor</vh></v>
<v t="ekr.20060408084420"><vh>add</vh></v>
<v t="ekr.20060408084420.1"><vh>change</vh></v>
<v t="ekr.20060408091522"><vh>error, message &amp; warning</vh></v>
<v t="ekr.20060408090927.1"><vh>open</vh></v>
<v t="ekr.20060330091638.3"><vh>update</vh></v>
</v>
</v>
</vnodes>
<tnodes>
<t tx="ekr.20060330091638">class updateClass:

    '''Update path2 (distribution) leoSettings.leo from path1 (user's) leoSettings.leo file.'''

    @others</t>
<t tx="ekr.20060330091638.1">def __init__ (self,c,path1,path2):

    self.c = c
    self.path1 = path1 # The user's previous (and preferred) leoSettings.leo file.
    self.path2 = path2 # The new distribution leoSettings.leo file.</t>
<t tx="ekr.20060330091638.3">def update (self):

    c = self.c
    c1 = self.open(self.path1)
    c2 = self.open(self.path2)
    g.app.log = c.frame.log
    if not c1 or not c2: return
    # Update c2 from settings in c1.
    d1 = updateParser(c1,'1').traverse()
    d2 = updateParser(c2,'2').traverse()
    changed = False
    for key in d1.keys():
        b1 = d1.get(key) # A bunch created by set or None.
        b2 = d2.get(key) # A bunch created by set or None.
        if key not in d2.keys():
            self.message('%7s %s = %s' % ('added',b1.name,b1.val))
            self.add(b1,c1,c2) ; changed = True
        elif b1.val == b2.val:
            pass # ; self.message('%7s %40s %s' % ('equal',b1.name,b1.val))
        else:
            self.message('%7s %s from %s to %s' % ('changed',b1.name,b2.val,b1.val))
            self.change(b1,b2) ; changed = True
    if changed:
        c2.save()
    self.message('update settings done')</t>
<t tx="ekr.20060403091254.12">def doFont (self,p,kind,name,val):

    self.set(p,kind,name,val)</t>
<t tx="ekr.20060403091254.22">def doShortcut(self,p,kind,name,val):

    self.set(p,kind,name,val)</t>
<t tx="ekr.20060403091254.23">def doShortcuts(self,p,kind,name,val):

    self.set(p,kind,name,val)</t>
<t tx="ekr.20060403091254.89">&lt;&lt; docstring &gt;&gt;

import leoConfig

# The user's previous (and preferred) leoSettings.leo file.
path1 = r'c:\prog\leoCVS\leo\config\leoSettings.leo'

# The new distribution leoSettings.leo file.
path2 = r'c:\prog\leoCVS\leo\config\leoSettings2.leo'

@others

# print '*' * 40

updateClass(c,path1,path2).update()</t>
<t tx="ekr.20060408065715">class updateParser (leoConfig.parserBaseClass):

    '''A settings parser that returns bunches used for updates.'''

    @others</t>
<t tx="ekr.20060408065715.1">def __init__ (self,c,tag):

    # Init the base class.
    leoConfig.parserBaseClass.__init__(self,c)
    self.tag = tag</t>
<t tx="ekr.20060408065715.2">def visitNode (self,p):

        """Init any settings found in node p."""

        munge = g.app.config.munge

        kind,name,val = self.parseHeadline(p.headString())
        kind = munge(kind)

        if kind == "settings":
            pass
        elif kind not in self.control_types and val in (u'None',u'none','None','none','',None):
            # None is valid for all data types.
            self.set(p.copy(),kind,name,None)
        elif kind in self.control_types or kind in self.basic_types:
            kindHandler = self.dispatchDict.get(kind)
            try:
                return kindHandler(p.copy(),kind,name,val)
            except TypeError:
                g.es_exception()
                print "*** no handler",kind
        elif name:
            # self.error("unknown type %s for setting %s" % (kind,name))
            # Just assume the type is a string.
            self.set(p.copy(),kind,name,val)

        return None</t>
<t tx="ekr.20060408065715.3"># Unlike in Leo's core, p and p.c are stable.

def set (self,p,kind,name,val):

    """Init the setting for name to val."""

    c = self.c ; d = self.settingsDict
    if not kind: return

    key = self.munge(name)
    if d.get(key):
        # This is a user error, and should be corrected before merging.
        g.es('*** ignoring duplicate setting for %s in %s' % (
            name,c.fileName()))
    else:
        d[key] = g.Bunch(p=p,kind=kind,name=name,val=val)
        if 0:
            if kind.startswith('strings'): kind = 'strings[]'
            g.trace('%s %10s %45s %s' %(self.tag,kind,name,val))
</t>
<t tx="ekr.20060408075705"></t>
<t tx="ekr.20060408084420">def add (self,bunch1,c1,c2):

    '''Add a node to c2 in the same position as bunch.p in c1.

    If this is not possible, put the node at the end of @settings tree.'''

    p1 = bunch1.p
    parent1 = p1.parent() # Must exist because p is in an @settings tree.
    n = p1.childIndex()
    h = parent1.headString()
    settings2 = g.findNodeAnywhere(c2,'@settings')
    assert (settings2) # This has already been checked.

    # Create the node.
    if h.startswith('@settings'):
        p2 = parent2 = settings2.insertAsLastChild()
    else:
        parent2 = g.findNodeInTree(c2,settings2,h)
        if parent2:
            p2 = parent2.insertAsNthChild(n)
        else:
            self.warning('no corresponding parent node for %s' % h)
            p2 = settings2.insertAsLastChild()

    # Copy the head and body strings.
    p2.setBodyString(p1.bodyString())
    p2.setHeadString(p1.headString())</t>
<t tx="ekr.20060408084420.1">def change (self,bunch1,bunch2):

    '''Change the bunch2.p to match bunch1.p.'''

    p1 = bunch1.p ; p2 = bunch2.p
    p2.setBodyString(p1.bodyString())
    p2.setHeadString(p1.headString())</t>
<t tx="ekr.20060408090927.1">def open (self, path):

    if not g.os_path_exists(path):
        self.error('does not exist: %s' % s)
        return None

    c = g.app.config.openSettingsFile(path)

    if not c:
        return None
    elif g.findNodeAnywhere(c,'@settings'):
        return c
    else:
        self.error('no @settings node in %s' % path)
        return None</t>
<t tx="ekr.20060408091522">def error (self,s):

    g.es_print(s, color='red')

def message (self,s):

    g.es_print(s, color='blue')

def warning (self,s):

    g.es_print(s, color='red')</t>
<t tx="ekr.20060408100009">'''Here is a **preliminary** script that updates a new leoSettings.leo file
(path2) from the settings in a previous leoSettings.leo file (path1).
Presumably, the path2 file is the leoSettings.leo file from a new distribution,
and the path1 file a leoSettings.leo file containing settings that you have
modified.  You must specify the full path of both files in the top-level @button node.

**Warning**: This script modifies the path2 file. Make sure that you don't mind
modifying this file.

**Warning**: This script is unsophisticated in its update algorithm. If if finds
corresponding nodes (node with the same settings name) in both outlines it simply
replaces the node in the path2 file with the node in the path1 file. In
particular, it does not keep track of individual shortcuts: it repaces the body
of one @shortcuts node with the body of the corresponding @shortcuts node.

**Note**: If the path1 file contains a settings node not in the path2 file it
attempts to create a new node in the corresponding place in the path2 file. If
this is not possible, say because the structures of the files are different, the
script places the new node as the last child of the @settings node in the path2
file. '''</t>
</tnodes>
</leo_file>