From 678057a0d364dfeae3855ccb772b7f25836a5134 Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Mon, 1 Feb 2010 12:11:44 +0000 Subject: [PATCH] First run at layout loading, creating, enumerating and saving. It sucks and works poorly at the moment, but here it is --- terminator | 8 +++-- terminatorlib/config.py | 60 +++++++++++++++++++++++++++++------- terminatorlib/container.py | 20 ++++++++++++ terminatorlib/factory.py | 31 +++++++++++++------ terminatorlib/optionparse.py | 1 + terminatorlib/paned.py | 34 ++++++++++++++++++++ terminatorlib/prefseditor.py | 14 +++++---- terminatorlib/terminal.py | 10 ++++++ terminatorlib/terminator.py | 26 ++++++++++++++++ terminatorlib/window.py | 27 ++++++++++++++++ 10 files changed, 202 insertions(+), 29 deletions(-) diff --git a/terminator b/terminator index dc3ef747..7cd86a2f 100755 --- a/terminator +++ b/terminator @@ -43,7 +43,7 @@ import terminatorlib.optionparse from terminatorlib.terminator import Terminator from terminatorlib.factory import Factory from terminatorlib.version import APP_NAME, APP_VERSION -from terminatorlib.util import dbg +from terminatorlib.util import dbg, err if __name__ == '__main__': dbg ("%s starting up, version %s" % (APP_NAME, APP_VERSION)) @@ -54,7 +54,11 @@ if __name__ == '__main__': TERMINATOR = Terminator() TERMINATOR.origcwd = ORIGCWD TERMINATOR.reconfigure() - TERMINATOR.new_window() + try: + TERMINATOR.create_layout(OPTIONS.layout) + except (KeyError,ValueError), ex: + err('layout creation failed, creating a window ("%s")' % ex) + TERMINATOR.new_window() if OPTIONS.debug > 2: import terminatorlib.debugserver as debugserver diff --git a/terminatorlib/config.py b/terminatorlib/config.py index 05686d1e..f7a78d71 100755 --- a/terminatorlib/config.py +++ b/terminatorlib/config.py @@ -206,8 +206,8 @@ DEFAULTS = { }, }, 'layouts': { - 'default': {'type': 'Terminal'} - }, + 'default': "{'type': 'Window', 'children': [{'type': 'Terminal'}]}", + }, 'plugins': { }, } @@ -266,9 +266,9 @@ class Config(object): """List all configured profiles""" return(self.base.profiles.keys()) - def add_layout(self, layout): + def add_layout(self, name, layout): """Add a new layout""" - return(self.base.add_layout(layout)) + return(self.base.add_layout(name, layout)) def del_layout(self, layout): """Delete a layout""" @@ -335,6 +335,14 @@ class Config(object): """Set a whole config tree for a given plugin""" return(self.base.set_plugin(plugin, tree)) + def layout_get_config(self, layout): + """Return a layout""" + return(self.base.get_layout(layout)) + + def layout_set_config(self, layout, tree): + """Set a layout""" + return(self.base.set_layout(layout, tree)) + class ConfigBase(Borg): """Class to provide access to our user configuration""" loaded = None @@ -371,7 +379,9 @@ class ConfigBase(Borg): if self.plugins is None: self.plugins = {} if self.layouts is None: - self.layouts = copy(DEFAULTS['layouts']) + self.layouts = {} + for layout in DEFAULTS['layouts']: + self.layouts[layout] = eval(DEFAULTS['layouts'][layout]) def defaults_to_configspec(self): """Convert our tree of default values into a ConfigObj validation @@ -463,11 +473,23 @@ class ConfigBase(Borg): if not section.has_key(section_name): section[profile] = copy(DEFAULTS['profiles']['default']) section[profile].update(parser[section_name][profile]) - elif section_name == ['layouts', 'plugins']: + elif section_name == 'plugins': for part in parser[section_name]: dbg('ConfigBase::load: Processing %s: %s' % (section_name, part)) section[part] = parser[section_name][part] + elif section_name == 'layouts': + for part 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)) else: try: section.update(parser[section_name]) @@ -497,8 +519,13 @@ class ConfigBase(Borg): parser['layouts'] = {} for layout in self.layouts: dbg('ConfigBase::save: Processing layout: %s' % layout) - parser['layouts'][layout] = dict_diff( - DEFAULTS['layouts']['default'], self.layouts[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['plugins'] = {} for plugin in self.plugins: @@ -568,10 +595,21 @@ class ConfigBase(Borg): self.profiles[profile] = copy(DEFAULTS['profiles']['default']) return(True) - def add_layout(self, layout): + def add_layout(self, name, layout): """Add a new layout""" - if layout in self.layouts: + if name in self.layouts: return(False) - self.layouts[layout] = {'type': 'Terminal'} + self.layouts[name] = layout return(True) + def get_layout(self, layout): + """Return a layout""" + if self.layouts.has_key(layout): + return(self.layouts[layout]) + else: + err('layout does not exist: %s' % layout) + + def set_layout(self, layout, tree): + """Set a layout""" + self.layouts[layout] = tree + diff --git a/terminatorlib/container.py b/terminatorlib/container.py index ce9cf1ce..106d87f7 100755 --- a/terminatorlib/container.py +++ b/terminatorlib/container.py @@ -190,4 +190,24 @@ the %s will also close all terminals within it.') % (reqtype, reqtype)) return(terminals) + def describe_layout(self): + """Describe our current layout""" + layout = {} + maker = Factory() + mytype = maker.type(self) + if not mytype: + err('unable to detemine own type. %s' % self) + return({}) + + layout['type'] = mytype + layout['children'] = [] + for child in self.get_children(): + layout['children'].append(child.describe_layout()) + + return(layout) + + def create_layout(self, layout): + """Apply settings for our layout""" + raise NotImplementedError('create_layout') + # vim: set expandtab ts=4 sw=4: diff --git a/terminatorlib/factory.py b/terminatorlib/factory.py index b4f15dcf..d9a59d3d 100755 --- a/terminatorlib/factory.py +++ b/terminatorlib/factory.py @@ -26,6 +26,14 @@ from util import dbg, err # pylint: disable-msg=W0613 class Factory(Borg): """Definition of a class that makes other classes""" + types = {'Terminal': 'terminal', + 'VPaned': 'paned', + 'HPaned': 'paned', + 'Paned': 'paned', + 'Notebook': 'notebook', + 'Container': 'container', + 'Window': 'window'} + def __init__(self): """Class initialiser""" Borg.__init__(self, self.__class__.__name__) @@ -37,28 +45,31 @@ class Factory(Borg): def isinstance(self, product, classtype): """Check if a given product is a particular type of object""" - types = {'Terminal': 'terminal', - 'VPaned': 'paned', - 'HPaned': 'paned', - 'Paned': 'paned', - 'Notebook': 'notebook', - 'Container': 'container', - 'Window': 'window'} - if classtype in types.keys(): + if classtype in self.types.keys(): # This is quite ugly, but we're importing from the current # directory if that makes sense, otherwise falling back to # terminatorlib. Someone with real Python skills should fix # this to be less insane. try: - module = __import__(types[classtype], None, None, ['']) + module = __import__(self.types[classtype], None, None, ['']) except ImportError: - module = __import__('terminatorlib.%s' % types[classtype], + module = __import__('terminatorlib.%s' % self.types[classtype], None, None, ['']) return(isinstance(product, getattr(module, classtype))) else: err('Factory::isinstance: unknown class type: %s' % classtype) return(False) + def type(self, product): + """Determine the type of an object we've previously created""" + for atype in self.types: + # Skip over generic types + if atype in ['Container', 'Paned']: + continue + if self.isinstance(product, atype): + return(atype) + return(None) + def make(self, product, **kwargs): """Make the requested product""" try: diff --git a/terminatorlib/optionparse.py b/terminatorlib/optionparse.py index d3761fff..50aa87dc 100755 --- a/terminatorlib/optionparse.py +++ b/terminatorlib/optionparse.py @@ -67,6 +67,7 @@ command to execute inside the terminal, and its arguments') dest='working_directory', help='Set the working directory') parser.add_option('-r', '--role', dest='role', help='Set a custom \ WM_WINDOW_ROLE property on the window') + parser.add_option('-l', '--layout', dest='layout', help='Select a layout') for item in ['--sm-client-id', '--sm-config-prefix', '--screen', '-n', '--no-gconf', '-p', '--profile' ]: parser.add_option(item, dest='dummy', action='store', diff --git a/terminatorlib/paned.py b/terminatorlib/paned.py index e603cc61..35ebbe23 100755 --- a/terminatorlib/paned.py +++ b/terminatorlib/paned.py @@ -178,6 +178,40 @@ class Paned(Container): # This is not a key we can handle self.emit('resize-term', keyname) + def create_layout(self, layout): + """Apply layout configuration""" + if not layout.has_key('children'): + err('layout specifies no children: %s' % layout) + return + + children = layout['children'] + if len(children) != 2: + # Paned widgets can only have two children + err('incorrect number of children for Paned: %s' % layout) + return + + for num in xrange(0, 2): + child = children[num] + if child['type'] == 'Terminal': + continue + elif child['type'] == 'VPaned': + if num == 0: + terminal = self.get_child1() + else: + terminal = self.get_child2() + self.split_axis(terminal, True) + elif child['type'] == 'HPaned': + if num == 0: + terminal = self.get_child1() + else: + terminal = self.get_child2() + self.split_axis(terminal, False) + else: + err('unknown child type: %s' % child['type']) + + self.get_child1().create_layout(children[0]) + self.get_child2().create_layout(children[1]) + class HPaned(Paned, gtk.HPaned): """Merge gtk.HPaned into our base Paned Container""" def __init__(self): diff --git a/terminatorlib/prefseditor.py b/terminatorlib/prefseditor.py index a4958bee..39c89677 100755 --- a/terminatorlib/prefseditor.py +++ b/terminatorlib/prefseditor.py @@ -662,21 +662,23 @@ class PrefsEditor: def on_layoutaddbutton_clicked(self, _button): """Add a new layout to the list""" + terminator = Terminator() + current_layout = terminator.describe_layout() guiget = self.builder.get_object treeview = guiget('layoutlist') model = treeview.get_model() values = [ r[0] for r in model ] - newlayout = _('New Layout') - if newlayout in values: + name = _('New Layout') + if name in values: i = 1 - while newlayout in values: + while name in values: i = i + 1 - newlayout = '%s %d' % (_('New Layout'), i) + name = '%s %d' % (_('New Layout'), i) - if self.config.add_layout(newlayout): - model.append([newlayout, True]) + if self.config.add_layout(name, current_layout): + model.append([name, True]) def on_layoutremovebutton_clicked(self, _button): """Remove a layout from the list""" diff --git a/terminatorlib/terminal.py b/terminatorlib/terminal.py index e1719baa..3d4e90cb 100755 --- a/terminatorlib/terminal.py +++ b/terminatorlib/terminal.py @@ -1159,6 +1159,16 @@ for %s (%s)' % (name, urlplugin.__class__.__name__)) window = util.get_top_window(self) window.set_urgency_hint(True) + def describe_layout(self): + """Describe our layout""" + layout = {} + layout['type'] = 'Terminal' + return(layout) + + def create_layout(self, layout): + """Apply our layout""" + print "Terminal::create_layout: Unimplemented. %s" % layout + # There now begins a great list of keyboard event handlers def key_zoom_in(self): self.zoom_in() diff --git a/terminatorlib/terminator.py b/terminatorlib/terminator.py index 99c64ae4..71b606e5 100755 --- a/terminatorlib/terminator.py +++ b/terminatorlib/terminator.py @@ -103,6 +103,23 @@ class Terminator(Borg): return(window, terminal) + def create_layout(self, layoutname): + """Create all the parts necessary to satisfy the specified layout""" + layout = None + + layout = self.config.layout_get_config(layoutname) + if not layout: + # User specified a non-existent layout. default to one Terminal + err('layout %s not defined' % layout) + raise(KeyError) + + for windef in layout: + if windef['type'] != 'Window': + err('invalid layout format. %s' % layout) + raise(ValueError) + window, terminal = self.new_window() + window.create_layout(windef) + def reconfigure(self): """Update configuration for the whole application""" @@ -210,4 +227,13 @@ class Terminator(Borg): for terminal in self.terminals: terminal.titlebar.update(widget) return + + def describe_layout(self): + """Describe our current layout""" + layout = [] + for window in self.windows: + layout.append(window.describe_layout()) + + return(layout) + # vim: set expandtab ts=4 sw=4: diff --git a/terminatorlib/window.py b/terminatorlib/window.py index 38cbd3de..4f152e52 100755 --- a/terminatorlib/window.py +++ b/terminatorlib/window.py @@ -575,6 +575,33 @@ class Window(Container, gtk.Window): if next is not None: terminals[next].grab_focus() + def create_layout(self, layout): + """Apply any config items from our layout""" + if not layout.has_key('children'): + err('layout describes no children: %s' % layout) + return + children = layout['children'] + 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] + terminal = self.get_children()[0] + if child['type'] == 'VPaned': + self.split_axis(terminal, True) + elif child['type'] == 'HPaned': + self.split_axis(terminal, False) + elif child['type'] == 'Notebook': + self.tab_new() + elif child['type'] == 'Terminal': + pass + else: + err('unknown child type: %s' % child['type']) + return + + self.get_children()[0].create_layout(child) + class WindowTitle(object): """Class to handle the setting of the window title"""