From 1aadbae30bd6ea818722256370657f5d02b2906f Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Tue, 2 Feb 2010 00:39:41 +0000 Subject: [PATCH] Rework layout config to flatten and build up trees --- terminatorlib/config.py | 50 +++++++++++++++++++++---------------- terminatorlib/container.py | 15 +++++++---- terminatorlib/paned.py | 12 ++++++--- terminatorlib/terminal.py | 8 ++++-- terminatorlib/terminator.py | 44 +++++++++++++++++++++++++++++--- terminatorlib/window.py | 4 +-- 6 files changed, 95 insertions(+), 38 deletions(-) diff --git a/terminatorlib/config.py b/terminatorlib/config.py index f7a78d71..3036b937 100755 --- a/terminatorlib/config.py +++ b/terminatorlib/config.py @@ -206,7 +206,16 @@ DEFAULTS = { }, }, 'layouts': { - 'default': "{'type': 'Window', 'children': [{'type': 'Terminal'}]}", + 'default': { + 'window0': { + 'type': 'Window', + 'parent': '' + }, + 'child1': { + 'type': 'Terminal', + 'parent': 'window0' + } + } }, 'plugins': { }, @@ -381,7 +390,7 @@ class ConfigBase(Borg): if self.layouts is None: self.layouts = {} for layout in DEFAULTS['layouts']: - self.layouts[layout] = eval(DEFAULTS['layouts'][layout]) + self.layouts[layout] = copy(DEFAULTS['layouts'][layout]) def defaults_to_configspec(self): """Convert our tree of default values into a ConfigObj validation @@ -433,6 +442,15 @@ class ConfigBase(Borg): configspecdata['profiles'] = {} configspecdata['profiles']['__many__'] = section + section = {} + section['type'] = 'string' + section['parent'] = 'string' + section['profile'] = 'string(default=default)' + section['command'] = 'string(default="")' + configspecdata['layouts'] = {} + configspecdata['layouts']['__many__'] = {} + configspecdata['layouts']['__many__']['__many__'] = section + configspec = ConfigObj(configspecdata) if DEBUG == True: configspec.write(open('/tmp/terminator_configspec_debug.txt', 'w')) @@ -460,9 +478,11 @@ class ConfigBase(Borg): err('ConfigBase::load: config format is not valid') for (section_list, key, _other) in flatten_errors(parser, result): if key is not None: - print('[%s]: %s is invalid' % (','.join(section_list), key)) + err('[%s]: %s is invalid' % (','.join(section_list), key)) else: - print ('[%s] missing' % ','.join(section_list)) + err('[%s] missing' % ','.join(section_list)) + else: + dbg('config validated successfully') for section_name in self.sections: dbg('ConfigBase::load: Processing section: %s' % section_name) @@ -471,6 +491,7 @@ class ConfigBase(Borg): for profile in parser[section_name]: dbg('ConfigBase::load: Processing profile: %s' % profile) if not section.has_key(section_name): + # FIXME: Should this be outside the loop? section[profile] = copy(DEFAULTS['profiles']['default']) section[profile].update(parser[section_name][profile]) elif section_name == 'plugins': @@ -479,17 +500,10 @@ class ConfigBase(Borg): part)) section[part] = parser[section_name][part] elif section_name == 'layouts': - for part in parser[section_name]: + for layout in parser[section_name]: dbg('ConfigBase::load: Processing %s: %s' % (section_name, - part)) - try: - windows = [] - for window in parser[section_name][part]: - windows.append(eval(window)) - section[part] = windows - except Exception, ex: - err('Unable to parse layout: %s (%s: %s)' % (part, ex, - window)) + layout)) + section[layout] = parser[section_name][layout] else: try: section.update(parser[section_name]) @@ -519,13 +533,7 @@ class ConfigBase(Borg): parser['layouts'] = {} for layout in self.layouts: dbg('ConfigBase::save: Processing layout: %s' % layout) - if layout == 'default' and \ - str(self.layouts[layout]) == DEFAULTS['layouts']['default']: - continue; - parser['layouts'][layout] = [] - # FIXME: This look seems pointless and broken - for window in self.layouts[layout]: - parser['layouts'][layout].append(str(self.layouts[layout])) + parser['layouts'][layout] = self.layouts[layout] parser['plugins'] = {} for plugin in self.plugins: diff --git a/terminatorlib/container.py b/terminatorlib/container.py index 106d87f7..e6b8085f 100755 --- a/terminatorlib/container.py +++ b/terminatorlib/container.py @@ -190,7 +190,7 @@ the %s will also close all terminals within it.') % (reqtype, reqtype)) return(terminals) - def describe_layout(self): + def describe_layout(self, count, parent, global_layout): """Describe our current layout""" layout = {} maker = Factory() @@ -200,11 +200,16 @@ the %s will also close all terminals within it.') % (reqtype, reqtype)) return({}) layout['type'] = mytype - layout['children'] = [] - for child in self.get_children(): - layout['children'].append(child.describe_layout()) + layout['parent'] = parent + name = 'child%d' % count + count = count + 1 - return(layout) + global_layout[name] = layout + + for child in self.get_children(): + count = child.describe_layout(count, name, global_layout) + + return(count) def create_layout(self, layout): """Apply settings for our layout""" diff --git a/terminatorlib/paned.py b/terminatorlib/paned.py index 35ebbe23..3aa57f03 100755 --- a/terminatorlib/paned.py +++ b/terminatorlib/paned.py @@ -190,8 +190,9 @@ class Paned(Container): err('incorrect number of children for Paned: %s' % layout) return - for num in xrange(0, 2): - child = children[num] + num = 0 + for child_key in children: + child = children[child_key] if child['type'] == 'Terminal': continue elif child['type'] == 'VPaned': @@ -208,9 +209,12 @@ class Paned(Container): self.split_axis(terminal, False) else: err('unknown child type: %s' % child['type']) + num = num + 1 - self.get_child1().create_layout(children[0]) - self.get_child2().create_layout(children[1]) + keys = children.keys() + keys.sort() + self.get_child1().create_layout(children[keys[0]]) + self.get_child2().create_layout(children[keys[1]]) class HPaned(Paned, gtk.HPaned): """Merge gtk.HPaned into our base Paned Container""" diff --git a/terminatorlib/terminal.py b/terminatorlib/terminal.py index 4b42cf04..7b43725b 100755 --- a/terminatorlib/terminal.py +++ b/terminatorlib/terminal.py @@ -1165,11 +1165,15 @@ for %s (%s)' % (name, urlplugin.__class__.__name__)) window = util.get_top_window(self) window.set_urgency_hint(True) - def describe_layout(self): + def describe_layout(self, count, parent, global_layout): """Describe our layout""" layout = {} layout['type'] = 'Terminal' - return(layout) + layout['parent'] = parent + name = 'terminal%d' % count + count = count + 1 + global_layout[name] = layout + return(count) def create_layout(self, layout): """Apply our layout""" diff --git a/terminatorlib/terminator.py b/terminatorlib/terminator.py index 71b606e5..91363d4f 100755 --- a/terminatorlib/terminator.py +++ b/terminatorlib/terminator.py @@ -106,6 +106,7 @@ class Terminator(Borg): def create_layout(self, layoutname): """Create all the parts necessary to satisfy the specified layout""" layout = None + objects = {} layout = self.config.layout_get_config(layoutname) if not layout: @@ -113,12 +114,45 @@ class Terminator(Borg): err('layout %s not defined' % layout) raise(KeyError) + # Wind the flat objects into a hierarchy + hierarchy = {} + count = 0 + # Loop over the layout until we have consumed it, or hit 1000 loops. + # This is a stupid artificial limit, but it's safe. + while len(layout) > 0 and count < 1000: + count = count + 1 + if count == 1000: + err('hit maximum loop boundary. THIS IS VERY LIKELY A BUG') + for obj in layout.keys(): + if layout[obj]['type'].lower() == 'window': + hierarchy[obj] = {} + hierarchy[obj]['type'] = 'Window' + hierarchy[obj]['children'] = {} + objects[obj] = hierarchy[obj] + del(layout[obj]) + else: + # Now examine children to see if their parents exist yet + if not layout[obj].has_key('parent'): + err('Invalid object: %s' % obj) + del(layout[obj]) + continue + if objects.has_key(layout[obj]['parent']): + # Our parent has been created + childobj = {} + childobj['type'] = layout[obj]['type'] + childobj['children'] = {} + objects[layout[obj]['parent']]['children'][obj] = childobj + objects[obj] = childobj + del(layout[obj]) + + layout = hierarchy + for windef in layout: - if windef['type'] != 'Window': + if layout[windef]['type'] != 'Window': err('invalid layout format. %s' % layout) raise(ValueError) window, terminal = self.new_window() - window.create_layout(windef) + window.create_layout(layout[windef]) def reconfigure(self): """Update configuration for the whole application""" @@ -230,9 +264,11 @@ class Terminator(Borg): def describe_layout(self): """Describe our current layout""" - layout = [] + layout = {} + count = 0 for window in self.windows: - layout.append(window.describe_layout()) + parent = '' + count = window.describe_layout(count, parent, layout) return(layout) diff --git a/terminatorlib/window.py b/terminatorlib/window.py index 4f152e52..e921da00 100755 --- a/terminatorlib/window.py +++ b/terminatorlib/window.py @@ -581,12 +581,12 @@ class Window(Container, gtk.Window): err('layout describes no children: %s' % layout) return children = layout['children'] - if len(children) != 1: + if len(children) != 1: # We're a Window, we can only have one child err('incorrect number of children for Window: %s' % layout) return - child = children[0] + child = children[children.keys()[0]] terminal = self.get_children()[0] if child['type'] == 'VPaned': self.split_axis(terminal, True)