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).
.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.

View File

@ -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
)

View File

@ -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,6 +98,10 @@ if __name__ == '__main__':
TERMINATOR.set_origcwd(ORIGCWD)
TERMINATOR.set_dbus_data(dbus_service)
TERMINATOR.reconfigure()
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)

View File

@ -173,7 +173,8 @@ DEFAULTS = {
'broadcast_all' : '',
'insert_number' : '',
'insert_padded' : '',
'edit_window_title': ''
'edit_window_title': '',
'layout_launcher' : ''
},
'profiles': {
'default': {
@ -620,6 +621,11 @@ class ConfigBase(Borg):
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"""
dbg('ConfigBase::save: saving config')

View File

@ -257,6 +257,9 @@ the %s will also close all terminals within it.') % (reqtype, reqtype))
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()

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',
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',

View File

@ -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"""

View File

@ -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()

View File

@ -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)

View File

@ -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"""

View File

@ -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)