New feature - Layout launcher opened with option or shortcut

This commit is contained in:
Stephen Boddy 2013-09-03 19:59:28 +02:00
commit 2f9d337687
13 changed files with 279 additions and 31 deletions

View File

@ -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). Specifies the preferred size and position of Terminator's window; see X(7).
.TP .TP
.B \-e, \-\-command=COMMAND .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 .TP
.B \-x, \-\-execute COMMAND [ARGS] .B \-x, \-\-execute COMMAND [ARGS]
Runs \fBthe rest of the command line\fR instead of your default shell or profile specified command. 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 Start Terminator with a specific layout. The argument here is the name
of a saved layout. of a saved layout.
.TP .TP
.B \-s, \-\-select-layout=LAYOUT
Open the layout launcher window instead of the normal terminal.
.TP
.B \-p, \-\-profile=PROFILE .B \-p, \-\-profile=PROFILE
Use a different profile as the default Use a different profile as the default
.TP .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: The following keybindings can be used to control Terminator:
.TP .TP
.B Super+R .B Super+R
\fBR\fotate terminals clockwise. \fBR\fRotate terminals clockwise.
.TP .TP
.B Super+Shift+R .B Super+Shift+R
\fBR\fotate terminals counter-clockwise. \fBR\fRotate terminals counter-clockwise.
.TP .TP
.B Ctrl+Shift+O .B Ctrl+Shift+O
Split terminals H\fBo\fRrizontally. Split terminals H\fBo\fRrizontally.

View File

@ -180,7 +180,7 @@ setup(name=APP_NAME.capitalize(),
], ],
packages=['terminatorlib', 'terminatorlib.configobj', packages=['terminatorlib', 'terminatorlib.configobj',
'terminatorlib.plugins'], 'terminatorlib.plugins'],
package_data={'terminatorlib': ['preferences.glade']}, package_data={'terminatorlib': ['preferences.glade', 'layoutlauncher.glade']},
cmdclass={'build': BuildData, 'install_data': InstallData, 'uninstall': Uninstall}, cmdclass={'build': BuildData, 'install_data': InstallData, 'uninstall': Uninstall},
distclass=TerminatorDist distclass=TerminatorDist
) )

View File

@ -47,6 +47,7 @@ from terminatorlib.terminator import Terminator
from terminatorlib.factory import Factory from terminatorlib.factory import Factory
from terminatorlib.version import APP_NAME, APP_VERSION from terminatorlib.version import APP_NAME, APP_VERSION
from terminatorlib.util import dbg, err from terminatorlib.util import dbg, err
from terminatorlib.layoutlauncher import LayoutLauncher
if __name__ == '__main__': if __name__ == '__main__':
dbus_service = None dbus_service = None
@ -97,13 +98,17 @@ if __name__ == '__main__':
TERMINATOR.set_origcwd(ORIGCWD) TERMINATOR.set_origcwd(ORIGCWD)
TERMINATOR.set_dbus_data(dbus_service) TERMINATOR.set_dbus_data(dbus_service)
TERMINATOR.reconfigure() TERMINATOR.reconfigure()
try: if OPTIONS.select:
dbg('Creating a terminal with layout: %s' % OPTIONS.layout) # launch gui, return selection
TERMINATOR.create_layout(OPTIONS.layout) LAYOUTLAUNCHER=LayoutLauncher()
except (KeyError,ValueError), ex: else:
err('layout creation failed, creating a window ("%s")' % ex) try:
TERMINATOR.new_window() dbg('Creating a terminal with layout: %s' % OPTIONS.layout)
TERMINATOR.layout_done() 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: if OPTIONS.debug > 2:
import terminatorlib.debugserver as debugserver import terminatorlib.debugserver as debugserver

View File

@ -173,7 +173,8 @@ DEFAULTS = {
'broadcast_all' : '', 'broadcast_all' : '',
'insert_number' : '', 'insert_number' : '',
'insert_padded' : '', 'insert_padded' : '',
'edit_window_title': '' 'edit_window_title': '',
'layout_launcher' : ''
}, },
'profiles': { 'profiles': {
'default': { 'default': {
@ -619,6 +620,11 @@ class ConfigBase(Borg):
section_name) section_name)
self.loaded = True self.loaded = True
def reload(self):
"""Force a reload of the base config"""
self.loaded = False
self.load()
def save(self): def save(self):
"""Save the config to a file""" """Save the config to a file"""

View File

@ -256,6 +256,9 @@ the %s will also close all terminals within it.') % (reqtype, reqtype))
if hasattr(position, '__iter__'): if hasattr(position, '__iter__'):
position = ':'.join([str(x) for x in position]) position = ':'.join([str(x) for x in position])
layout['position'] = position layout['position'] = position
if hasattr(self, 'ratio'):
layout['ratio'] = self.ratio
if hasattr(self, 'get_size'): if hasattr(self, 'get_size'):
layout['size'] = self.get_size() layout['size'] = self.get_size()

View File

@ -0,0 +1,95 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires lib="gtk+" version="2.16"/>
<!-- interface-naming-policy project-wide -->
<object class="GtkWindow" id="layoutlauncherwin">
<property name="can_focus">False</property>
<property name="title" translatable="yes">Terminator Layout Launcher</property>
<signal name="destroy-event" handler="on_destroy_event" swapped="no"/>
<child>
<object class="GtkVBox" id="vbox1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="border_width">7</property>
<property name="spacing">7</property>
<child>
<object class="GtkFrame" id="frame1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label_xalign">0</property>
<property name="label_yalign">0</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkTreeView" id="layoutlist">
<property name="width_request">150</property>
<property name="height_request">200</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="model">layoutstore</property>
<property name="headers_clickable">False</property>
<property name="search_column">0</property>
<signal name="row-activated" handler="on_row_activated" swapped="no"/>
<child>
<object class="GtkTreeViewColumn" id="treeviewcolumn1">
<property name="title" translatable="yes">Layout</property>
<child>
<object class="GtkCellRendererText" id="cellrenderertext1"/>
<attributes>
<attribute name="text">0</attribute>
</attributes>
</child>
</object>
</child>
</object>
</child>
<child type="label_item">
<placeholder/>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<placeholder/>
</child>
<child>
<object class="GtkButton" id="launchbutton">
<property name="label" translatable="yes">Launch</property>
<property name="width_request">100</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_action_appearance">False</property>
<signal name="clicked" handler="on_launchbutton_clicked" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack_type">end</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
</object>
<object class="GtkListStore" id="layoutstore">
<columns>
<!-- column-name layoutname -->
<column type="gchararray"/>
</columns>
</object>
</interface>

97
terminatorlib/layoutlauncher.py Executable file
View File

@ -0,0 +1,97 @@
#!/usr/bin/python
# Terminator by Chris Jones <cmsj@tenshu.net>
# 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()

View File

@ -86,7 +86,9 @@ icon for the window (by file or name)'))
parser.add_option('-r', '--role', dest='role', parser.add_option('-r', '--role', dest='role',
help=_('Set a custom WM_WINDOW_ROLE property on the window')) help=_('Set a custom WM_WINDOW_ROLE property on the window'))
parser.add_option('-l', '--layout', dest='layout', 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', parser.add_option('-p', '--profile', dest='profile',
help=_('Use a different profile as the default')) help=_('Use a different profile as the default'))
parser.add_option('-u', '--no-dbus', action='store_true', dest='nodbus', parser.add_option('-u', '--no-dbus', action='store_true', dest='nodbus',

View File

@ -350,9 +350,10 @@ class Paned(Container):
self.get_child1().create_layout(children[keys[0]]) self.get_child1().create_layout(children[keys[0]])
self.get_child2().create_layout(children[keys[1]]) self.get_child2().create_layout(children[keys[1]])
# Store the position for later # Set the position with ratio. For some reason more reliable than by pos.
if layout['position']: if layout.has_key('ratio'):
self.position = int(layout['position']) self.ratio = float(layout['ratio'])
self.set_position_by_ratio()
def grab_focus(self): def grab_focus(self):
"""We don't want focus, we want a Terminal to have it""" """We don't want focus, we want a Terminal to have it"""

View File

@ -143,11 +143,13 @@ class PrefsEditor:
'broadcast_all' : 'Broadcast key events to all', 'broadcast_all' : 'Broadcast key events to all',
'insert_number' : 'Insert terminal number', 'insert_number' : 'Insert terminal number',
'insert_padded' : 'Insert zero padded 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): def __init__ (self, term):
self.config = config.Config() self.config = config.Config()
self.config.base.reload()
self.term = term self.term = term
self.builder = gtk.Builder() self.builder = gtk.Builder()
self.keybindings = Keybindings() self.keybindings = Keybindings()

View File

@ -15,7 +15,7 @@ import pango
import subprocess import subprocess
import urllib import urllib
from util import dbg, err, gerr from util import dbg, err, gerr, spawn_new_terminator
import util import util
from config import Config from config import Config
from cwd import get_default_cwd from cwd import get_default_cwd
@ -27,6 +27,7 @@ from searchbar import Searchbar
from translation import _ from translation import _
from signalman import Signalman from signalman import Signalman
import plugin import plugin
from terminatorlib.layoutlauncher import LayoutLauncher
try: try:
import vte import vte
@ -1680,18 +1681,7 @@ class Terminal(gtk.VBox):
self.terminator.new_window(self.terminator.pid_cwd(self.pid)) self.terminator.new_window(self.terminator.pid_cwd(self.pid))
def key_new_terminator(self): def key_new_terminator(self):
cmd = sys.argv[0] spawn_new_terminator(self.origcwd, ['-u'])
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'])
def key_broadcast_off(self): def key_broadcast_off(self):
self.set_groupsend(None, self.terminator.groupsend_type['off']) self.set_groupsend(None, self.terminator.groupsend_type['off'])
@ -1742,6 +1732,9 @@ class Terminal(gtk.VBox):
dialog.destroy() dialog.destroy()
return return
def key_layout_launcher(self):
LAYOUTLAUNCHER=LayoutLauncher()
def key_page_up(self): def key_page_up(self):
self.scroll_by_page(-1) self.scroll_by_page(-1)

View File

@ -19,6 +19,7 @@ class Terminator(Borg):
"""master object for the application""" """master object for the application"""
windows = None windows = None
launcher_windows = None
windowtitle = None windowtitle = None
terminals = None terminals = None
groups = None groups = None
@ -48,6 +49,8 @@ class Terminator(Borg):
if not self.windows: if not self.windows:
self.windows = [] self.windows = []
if not self.launcher_windows:
self.launcher_windows = []
if not self.terminals: if not self.terminals:
self.terminals = [] self.terminals = []
if not self.groups: if not self.groups:
@ -129,6 +132,27 @@ class Terminator(Borg):
dbg('no windows remain, quitting') dbg('no windows remain, quitting')
gtk.main_quit() 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): def register_terminal(self, terminal):
"""Register a new terminal widget""" """Register a new terminal widget"""
if terminal not in self.terminals: if terminal not in self.terminals:
@ -238,7 +262,6 @@ class Terminator(Borg):
raise(ValueError) raise(ValueError)
dbg('Creating a window') dbg('Creating a window')
window, terminal = self.new_window() window, terminal = self.new_window()
window.create_layout(layout[windef])
if layout[windef].has_key('position'): if layout[windef].has_key('position'):
parts = layout[windef]['position'].split(':') parts = layout[windef]['position'].split(':')
if len(parts) == 2: if len(parts) == 2:
@ -251,6 +274,7 @@ class Terminator(Borg):
window.resize(winx, winy) window.resize(winx, winy)
if layout[windef].has_key('title'): if layout[windef].has_key('title'):
window.title.force_title(layout[windef]['title']) window.title.force_title(layout[windef]['title'])
window.create_layout(layout[windef])
def layout_done(self): def layout_done(self):
"""Layout operations have finished, record that fact""" """Layout operations have finished, record that fact"""

View File

@ -29,6 +29,7 @@ import os
import pwd import pwd
import inspect import inspect
import uuid import uuid
import subprocess
# set this to true to enable debugging output # set this to true to enable debugging output
DEBUG = False DEBUG = False
@ -289,3 +290,17 @@ def inject_uuid(target):
else: else:
dbg("Object already has a UUID: %s" % target) 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)