New feature - Layout launcher opened with option or shortcut
This commit is contained in:
commit
2f9d337687
@ -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.
|
||||
|
2
setup.py
2
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
|
||||
)
|
||||
|
19
terminator
19
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
|
||||
|
@ -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"""
|
||||
|
@ -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()
|
||||
|
95
terminatorlib/layoutlauncher.glade
Normal file
95
terminatorlib/layoutlauncher.glade
Normal 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
97
terminatorlib/layoutlauncher.py
Executable 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()
|
@ -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',
|
||||
|
@ -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"""
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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"""
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user