diff --git a/doc/terminator.1 b/doc/terminator.1 index c63edc16..71d86a20 100644 --- a/doc/terminator.1 +++ b/doc/terminator.1 @@ -45,7 +45,9 @@ based on the wishes of the child shell. Specifies the preferred size and position of Terminator's window; see X(7). .TP .B \-e, \-\-command=COMMAND -Runs the specified command instead of your default shell or profile specified command +Runs the specified command instead of your default shell or profile specified command. Note: if +Terminator is launched as x-terminal-emulator \-e behaves like \-x, and the longform becomes +\-\-execute2=COMMAND .TP .B \-x, \-\-execute COMMAND [ARGS] Runs \fBthe rest of the command line\fR instead of your default shell or profile specified command. @@ -63,6 +65,9 @@ Set a custom name (WM_CLASS) property on the window Start Terminator with a specific layout. The argument here is the name of a saved layout. .TP +.B \-s, \-\-select-layout=LAYOUT +Open the layout launcher window instead of the normal terminal. +.TP .B \-p, \-\-profile=PROFILE Use a different profile as the default .TP @@ -94,10 +99,10 @@ used to spawn a new tab in the first Terminator window. The following keybindings can be used to control Terminator: .TP .B Super+R -\fBR\fotate terminals clockwise. +\fBR\fRotate terminals clockwise. .TP .B Super+Shift+R -\fBR\fotate terminals counter-clockwise. +\fBR\fRotate terminals counter-clockwise. .TP .B Ctrl+Shift+O Split terminals H\fBo\fRrizontally. diff --git a/setup.py b/setup.py index 4e5ee4c7..f8ea03fd 100755 --- a/setup.py +++ b/setup.py @@ -180,7 +180,7 @@ setup(name=APP_NAME.capitalize(), ], packages=['terminatorlib', 'terminatorlib.configobj', 'terminatorlib.plugins'], - package_data={'terminatorlib': ['preferences.glade']}, + package_data={'terminatorlib': ['preferences.glade', 'layoutlauncher.glade']}, cmdclass={'build': BuildData, 'install_data': InstallData, 'uninstall': Uninstall}, distclass=TerminatorDist ) diff --git a/terminator b/terminator index 4302e11d..7175d264 100755 --- a/terminator +++ b/terminator @@ -47,6 +47,7 @@ from terminatorlib.terminator import Terminator from terminatorlib.factory import Factory from terminatorlib.version import APP_NAME, APP_VERSION from terminatorlib.util import dbg, err +from terminatorlib.layoutlauncher import LayoutLauncher if __name__ == '__main__': dbus_service = None @@ -97,13 +98,17 @@ if __name__ == '__main__': TERMINATOR.set_origcwd(ORIGCWD) TERMINATOR.set_dbus_data(dbus_service) TERMINATOR.reconfigure() - try: - dbg('Creating a terminal with layout: %s' % OPTIONS.layout) - TERMINATOR.create_layout(OPTIONS.layout) - except (KeyError,ValueError), ex: - err('layout creation failed, creating a window ("%s")' % ex) - TERMINATOR.new_window() - TERMINATOR.layout_done() + if OPTIONS.select: + # launch gui, return selection + LAYOUTLAUNCHER=LayoutLauncher() + else: + try: + dbg('Creating a terminal with layout: %s' % OPTIONS.layout) + TERMINATOR.create_layout(OPTIONS.layout) + except (KeyError,ValueError), ex: + err('layout creation failed, creating a window ("%s")' % ex) + TERMINATOR.new_window() + TERMINATOR.layout_done() if OPTIONS.debug > 2: import terminatorlib.debugserver as debugserver diff --git a/terminatorlib/config.py b/terminatorlib/config.py index ec581bc4..f122287a 100755 --- a/terminatorlib/config.py +++ b/terminatorlib/config.py @@ -173,7 +173,8 @@ DEFAULTS = { 'broadcast_all' : '', 'insert_number' : '', 'insert_padded' : '', - 'edit_window_title': '' + 'edit_window_title': '', + 'layout_launcher' : '' }, 'profiles': { 'default': { @@ -619,6 +620,11 @@ class ConfigBase(Borg): section_name) self.loaded = True + + def reload(self): + """Force a reload of the base config""" + self.loaded = False + self.load() def save(self): """Save the config to a file""" diff --git a/terminatorlib/container.py b/terminatorlib/container.py index 59acdb3b..fba84fa4 100755 --- a/terminatorlib/container.py +++ b/terminatorlib/container.py @@ -256,6 +256,9 @@ the %s will also close all terminals within it.') % (reqtype, reqtype)) if hasattr(position, '__iter__'): position = ':'.join([str(x) for x in position]) layout['position'] = position + + if hasattr(self, 'ratio'): + layout['ratio'] = self.ratio if hasattr(self, 'get_size'): layout['size'] = self.get_size() diff --git a/terminatorlib/layoutlauncher.glade b/terminatorlib/layoutlauncher.glade new file mode 100644 index 00000000..477077fc --- /dev/null +++ b/terminatorlib/layoutlauncher.glade @@ -0,0 +1,95 @@ + + + + + + False + Terminator Layout Launcher + + + + True + False + 7 + 7 + + + True + False + 0 + 0 + in + + + 150 + 200 + True + True + layoutstore + False + 0 + + + + Layout + + + + 0 + + + + + + + + + + + + True + True + 0 + + + + + True + False + + + + + + Launch + 100 + True + True + True + False + + + + False + True + end + 1 + + + + + False + True + 1 + + + + + + + + + + + + diff --git a/terminatorlib/layoutlauncher.py b/terminatorlib/layoutlauncher.py new file mode 100755 index 00000000..41a0c416 --- /dev/null +++ b/terminatorlib/layoutlauncher.py @@ -0,0 +1,97 @@ +#!/usr/bin/python +# Terminator by Chris Jones +# GPL v2 only +"""layoutlauncher.py - class for the Layout Launcher window""" + +import os +import gtk + +from util import dbg, err, spawn_new_terminator +import config +from translation import _ +from terminator import Terminator +from plugin import PluginRegistry + +class LayoutLauncher: + """Class implementing the various parts of the preferences editor""" + terminator = None + config = None + registry = None + plugins = None + keybindings = None + window = None + builder = None + layouttreeview = None + layouttreestore = None + + def __init__ (self): + self.terminator = Terminator() + self.terminator.register_launcher_window(self) + + self.config = config.Config() + self.builder = gtk.Builder() + try: + # Figure out where our library is on-disk so we can open our UI + (head, _tail) = os.path.split(config.__file__) + librarypath = os.path.join(head, 'layoutlauncher.glade') + gladefile = open(librarypath, 'r') + gladedata = gladefile.read() + except Exception, ex: + print "Failed to find layoutlauncher.glade" + print ex + return + + self.builder.add_from_string(gladedata) + self.window = self.builder.get_object('layoutlauncherwin') + self.builder.connect_signals(self) + self.window.connect('destroy', self.on_destroy_event) + self.window.show_all() + self.layouttreeview = self.builder.get_object('layoutlist') + self.layouttreestore = self.builder.get_object('layoutstore') + self.update_layouts() + + def on_destroy_event(self, widget, data=None): + """Handle window destruction""" + dbg('destroying self') + self.terminator.deregister_launcher_window(self) + self.window.destroy() + del(self.window) + + def update_layouts(self): + """Update the contents of the layout""" + self.layouttreestore.clear() + layouts = self.config.list_layouts() + for layout in sorted(layouts, cmp=lambda x,y: cmp(x.lower(), y.lower())): + if layout != "default": + self.layouttreestore.append([layout]) + else: + self.layouttreestore.prepend([layout]) + + def on_launchbutton_clicked(self, widget): + """Handle button click""" + self.launch_layout() + + def on_row_activated(self, widget, path, view_column): + """Handle item double-click and return""" + self.launch_layout() + + def launch_layout(self): + """Launch the selected layout as new instance""" + dbg('We have takeoff!') + selection=self.layouttreeview.get_selection() + (listmodel, rowiter) = selection.get_selected() + if not rowiter: + # Something is wrong, just jump to the first item in the list + selection.select_iter(self.layouttreestore.get_iter_first()) + (listmodel, rowiter) = selection.get_selected() + layout = listmodel.get_value(rowiter, 0) + dbg('Clicked for %s' % layout) + spawn_new_terminator(self.terminator.origcwd, ['-u', '-l', layout]) + +if __name__ == '__main__': + import util + util.DEBUG = True + import terminal + LAYOUTLAUNCHER = LayoutLauncher() + + gtk.main() diff --git a/terminatorlib/optionparse.py b/terminatorlib/optionparse.py index e395f590..1c36ad21 100755 --- a/terminatorlib/optionparse.py +++ b/terminatorlib/optionparse.py @@ -86,7 +86,9 @@ icon for the window (by file or name)')) 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')) + help=_('Launch with the given layout')) + parser.add_option('-s', '--select-layout', action='store_true', + dest='select', help=_('Select a layout from a list')) parser.add_option('-p', '--profile', dest='profile', help=_('Use a different profile as the default')) parser.add_option('-u', '--no-dbus', action='store_true', dest='nodbus', diff --git a/terminatorlib/paned.py b/terminatorlib/paned.py index 3a3eafab..119b437d 100755 --- a/terminatorlib/paned.py +++ b/terminatorlib/paned.py @@ -350,9 +350,10 @@ class Paned(Container): self.get_child1().create_layout(children[keys[0]]) self.get_child2().create_layout(children[keys[1]]) - # Store the position for later - if layout['position']: - self.position = int(layout['position']) + # Set the position with ratio. For some reason more reliable than by pos. + if layout.has_key('ratio'): + self.ratio = float(layout['ratio']) + self.set_position_by_ratio() def grab_focus(self): """We don't want focus, we want a Terminal to have it""" diff --git a/terminatorlib/prefseditor.py b/terminatorlib/prefseditor.py index 46c4aca5..bbc063b7 100755 --- a/terminatorlib/prefseditor.py +++ b/terminatorlib/prefseditor.py @@ -143,11 +143,13 @@ class PrefsEditor: 'broadcast_all' : 'Broadcast key events to all', 'insert_number' : 'Insert terminal number', 'insert_padded' : 'Insert zero padded terminal number', - 'edit_window_title': 'Edit window title' + 'edit_window_title': 'Edit window title', + 'layout_launcher' : 'Open layout launcher window' } def __init__ (self, term): self.config = config.Config() + self.config.base.reload() self.term = term self.builder = gtk.Builder() self.keybindings = Keybindings() diff --git a/terminatorlib/terminal.py b/terminatorlib/terminal.py index e540f85c..1fe71957 100755 --- a/terminatorlib/terminal.py +++ b/terminatorlib/terminal.py @@ -15,7 +15,7 @@ import pango import subprocess import urllib -from util import dbg, err, gerr +from util import dbg, err, gerr, spawn_new_terminator import util from config import Config from cwd import get_default_cwd @@ -27,6 +27,7 @@ from searchbar import Searchbar from translation import _ from signalman import Signalman import plugin +from terminatorlib.layoutlauncher import LayoutLauncher try: import vte @@ -1680,18 +1681,7 @@ class Terminal(gtk.VBox): self.terminator.new_window(self.terminator.pid_cwd(self.pid)) def key_new_terminator(self): - cmd = sys.argv[0] - - if not os.path.isabs(cmd): - # Command is not an absolute path. Figure out where we are - cmd = os.path.join (self.origcwd, sys.argv[0]) - if not os.path.isfile(cmd): - # we weren't started as ./terminator in a path. Give up - err('Terminal::key_new_window: Unable to locate Terminator') - return False - - dbg("Terminal::key_new_window: Spawning: %s" % cmd) - subprocess.Popen([cmd, '-u']) + spawn_new_terminator(self.origcwd, ['-u']) def key_broadcast_off(self): self.set_groupsend(None, self.terminator.groupsend_type['off']) @@ -1742,6 +1732,9 @@ class Terminal(gtk.VBox): dialog.destroy() return + def key_layout_launcher(self): + LAYOUTLAUNCHER=LayoutLauncher() + def key_page_up(self): self.scroll_by_page(-1) diff --git a/terminatorlib/terminator.py b/terminatorlib/terminator.py index 464d4c3c..831bd094 100755 --- a/terminatorlib/terminator.py +++ b/terminatorlib/terminator.py @@ -19,6 +19,7 @@ class Terminator(Borg): """master object for the application""" windows = None + launcher_windows = None windowtitle = None terminals = None groups = None @@ -48,6 +49,8 @@ class Terminator(Borg): if not self.windows: self.windows = [] + if not self.launcher_windows: + self.launcher_windows = [] if not self.terminals: self.terminals = [] if not self.groups: @@ -129,6 +132,27 @@ class Terminator(Borg): dbg('no windows remain, quitting') gtk.main_quit() + def register_launcher_window(self, window): + """Register a new launcher window widget""" + if window not in self.launcher_windows: + dbg('Terminator::register_launcher_window: registering %s:%s' % (id(window), + type(window))) + self.launcher_windows.append(window) + + def deregister_launcher_window(self, window): + """de-register a launcher window widget""" + dbg('Terminator::deregister_launcher_window: de-registering %s:%s' % + (id(window), type(window))) + if window in self.launcher_windows: + self.launcher_windows.remove(window) + else: + err('%s is not in registered window list' % window) + + if len(self.launcher_windows) == 0 and len(self.windows) == 0: + # We have no windows left, we should exit + dbg('no windows remain, quitting') + gtk.main_quit() + def register_terminal(self, terminal): """Register a new terminal widget""" if terminal not in self.terminals: @@ -238,7 +262,6 @@ class Terminator(Borg): raise(ValueError) dbg('Creating a window') window, terminal = self.new_window() - window.create_layout(layout[windef]) if layout[windef].has_key('position'): parts = layout[windef]['position'].split(':') if len(parts) == 2: @@ -251,6 +274,7 @@ class Terminator(Borg): window.resize(winx, winy) if layout[windef].has_key('title'): window.title.force_title(layout[windef]['title']) + window.create_layout(layout[windef]) def layout_done(self): """Layout operations have finished, record that fact""" diff --git a/terminatorlib/util.py b/terminatorlib/util.py index b8ad5aba..c9143df2 100755 --- a/terminatorlib/util.py +++ b/terminatorlib/util.py @@ -29,6 +29,7 @@ import os import pwd import inspect import uuid +import subprocess # set this to true to enable debugging output DEBUG = False @@ -289,3 +290,17 @@ def inject_uuid(target): else: dbg("Object already has a UUID: %s" % target) +def spawn_new_terminator(cwd, args): + """Start a new terminator instance with the given arguments""" + cmd = sys.argv[0] + + if not os.path.isabs(cmd): + # Command is not an absolute path. Figure out where we are + cmd = os.path.join (cwd, sys.argv[0]) + if not os.path.isfile(cmd): + # we weren't started as ./terminator in a path. Give up + err('Unable to locate Terminator') + return False + + dbg("Spawning: %s" % cmd) + subprocess.Popen([cmd]+args)