Merge changes from trunk - diverged
This commit is contained in:
commit
183914c7dc
17
ChangeLog
17
ChangeLog
@ -1,9 +1,24 @@
|
||||
terminator 0.97:
|
||||
terminator 1.0:
|
||||
* Allow font dimming in inactive terminals
|
||||
* Allow URL handler plugins to override label text for URL context
|
||||
menus
|
||||
* When copying a URL, run it through the URL handler first so the
|
||||
resulting URL is copied, rather than the original text
|
||||
* Allow users to configure a custom URL handler, since the
|
||||
default GTK library option is failing a lot of users in non-GNOME
|
||||
environments.
|
||||
* Allow rotation of a group of terminals (Andre Hilsendeger)
|
||||
* Add a keyboard shortcut to insert a terminal's number (Stephen J
|
||||
Boddy)
|
||||
* Add a keyboard shortcut to edit the window title (Stephen J Boddy)
|
||||
* Add an easy way to balance terminals by double clicking on their
|
||||
separator (Stephen J Boddy)
|
||||
* Add a plugin by Sinan Nalkaya to log the contents of terminals.
|
||||
* Support configuration of TERM and COLORTERM, via a patch from
|
||||
John Feuerstein
|
||||
* Support reading configuration from alternate files, via a patch
|
||||
from Pavel Khlebovich
|
||||
* Bug fixes
|
||||
|
||||
terminator 0.96:
|
||||
* Unity support for opening new windows (Lucian Adrian Grijincu)
|
||||
|
6
debian/changelog
vendored
6
debian/changelog
vendored
@ -1,3 +1,9 @@
|
||||
terminator (1.0) precise; urgency=low
|
||||
|
||||
* New upstream release of 1.0
|
||||
|
||||
-- Chris Jones <cmsj@tenshu.net> Fri, 19 Oct 2012 12:57:42 -0700
|
||||
|
||||
terminator (0.96ppa6) oneiric; urgency=low
|
||||
|
||||
* No-change rebuild for oneiric PPA
|
||||
|
@ -115,6 +115,11 @@ Controls how much to reduce the colour values of fonts in terminals that do not
|
||||
factor. A font colour that was RGB(200,200,200) with an inactive_color_offset of 0.5 would set inactive terminals to
|
||||
RGB(100,100,100).
|
||||
.TP
|
||||
.B always_split_with_profile
|
||||
Controls whether splits/tabs will continue to use the profile of their peer terminal. If set to False, they will always use
|
||||
the default profile.
|
||||
Default value: \fBFalse\fR
|
||||
.TP
|
||||
.B enabled_plugins
|
||||
A list of plugins which should be loaded by default. All other plugin classes will be ignored. The default value includes two
|
||||
plugins related to Launchpad, which are enabled by default to provide continuity with earlier releases where these were the
|
||||
@ -377,6 +382,14 @@ Default value: \fBblock\fR
|
||||
Sets what type of terminal should be emulated.
|
||||
Default value: \fBxterm\fR
|
||||
.TP
|
||||
.B xterm
|
||||
This translates into the value that will be set for TERM in the environment of your terminals.
|
||||
Default value: \fBxterm\fR
|
||||
.TP
|
||||
.B colorterm
|
||||
This translates into the value that will be set for COLORTERM in the environment of your terminals.
|
||||
Default value: \fBgnome-terminal\fR
|
||||
.TP
|
||||
.B use_system_font
|
||||
Whether or not to use the GNOME default monospace font for terminals.
|
||||
Default value: \fBTrue\fR
|
||||
@ -423,7 +436,7 @@ Default value: \fBFalse\fR
|
||||
.TP
|
||||
.B focus_on_close
|
||||
Sets which terminal should get the focus when another terminal is closed. Values can be "prev", "next" or "auto".
|
||||
Using "auto", if the closed terminal is within a splitted window, the focus will be on the sibling terminal rather than another tab.
|
||||
Using "auto", if the closed terminal is within a split window, the focus will be on the sibling terminal rather than another tab.
|
||||
Default value: \fBauto\fR
|
||||
.TP
|
||||
.B exit_action
|
||||
|
10
po/es.po
10
po/es.po
@ -8,14 +8,14 @@ msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2011-08-21 01:31+0100\n"
|
||||
"PO-Revision-Date: 2012-05-04 22:28+0000\n"
|
||||
"Last-Translator: Juan Pablo <Unknown>\n"
|
||||
"PO-Revision-Date: 2012-10-05 03:56+0000\n"
|
||||
"Last-Translator: Paco Molinero <paco@byasl.com>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-05-24 10:30+0000\n"
|
||||
"X-Generator: Launchpad (build 15288)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-06 04:33+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
|
||||
#: ../data/terminator.desktop.in.h:1
|
||||
msgid "Multiple terminals in one window"
|
||||
@ -333,7 +333,7 @@ msgstr "Captura de terminal"
|
||||
|
||||
#: ../terminatorlib/prefseditor.py:942 ../terminatorlib/prefseditor.py:947
|
||||
msgid "New Profile"
|
||||
msgstr "Nuevo Perfil"
|
||||
msgstr "Perfil nuevo"
|
||||
|
||||
#: ../terminatorlib/prefseditor.py:987 ../terminatorlib/prefseditor.py:992
|
||||
msgid "New Layout"
|
||||
|
@ -24,7 +24,7 @@ import sys
|
||||
from terminatorlib.util import dbg, err
|
||||
try:
|
||||
from terminatorlib import ipc
|
||||
except ImportErrror:
|
||||
except ImportError:
|
||||
err('Unable to initialise Terminator remote library. This probably means dbus is not available')
|
||||
sys.exit(1)
|
||||
|
||||
@ -35,6 +35,8 @@ COMMANDS={
|
||||
'hsplit': ['terminal_hsplit', 'Split the current terminal horizontally'],
|
||||
'vsplit': ['terminal_vsplit', 'Split the current terminal vertically'],
|
||||
'terminals': ['get_terminals', 'Get a list of all terminals'],
|
||||
'terminal_tab': ['get_terminal_tab', 'Get the UUID of a parent tab'],
|
||||
'terminal_tab_title': ['get_terminal_tab_title', 'Get the title of a parent tab'],
|
||||
}
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
14
terminator
14
terminator
@ -62,11 +62,23 @@ if __name__ == '__main__':
|
||||
dbg('dbus disabled by command line')
|
||||
raise ImportError
|
||||
from terminatorlib import ipc
|
||||
import dbus
|
||||
try:
|
||||
dbus_service = ipc.DBusService()
|
||||
except ipc.DBusException:
|
||||
dbg('Unable to become master process, requesting a new window')
|
||||
ipc.new_window(OPTIONS.layout)
|
||||
# get rid of the None and True types so dbus can handle them (empty
|
||||
# and 'True' strings are used instead), also arrays are joined
|
||||
# (the -x argument for example)
|
||||
optionslist = {}
|
||||
for opt, val in OPTIONS.__dict__.items():
|
||||
if type(val) == type([]):
|
||||
val = ' '.join(val)
|
||||
if val == True:
|
||||
val = 'True'
|
||||
optionslist[opt] = val and '%s'%val or ''
|
||||
optionslist = dbus.Dictionary(optionslist, signature='ss')
|
||||
ipc.new_window(optionslist)
|
||||
sys.exit()
|
||||
except ImportError:
|
||||
dbg('dbus not imported')
|
||||
|
@ -1,7 +1,7 @@
|
||||
%{!?python_sitelib: %define python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")}
|
||||
|
||||
Name: terminator
|
||||
Version: 0.96
|
||||
Version: 1.0
|
||||
Release: 1%{?dist}
|
||||
Summary: Store and run multiple GNOME terminals in one window
|
||||
|
||||
|
@ -105,6 +105,7 @@ DEFAULTS = {
|
||||
'LaunchpadCodeURLHandler',
|
||||
'APTURLHandler'],
|
||||
'suppress_multiple_term_dialog': False,
|
||||
'always_split_with_profile': False,
|
||||
},
|
||||
'keybindings': {
|
||||
'zoom_in' : '<Control>plus',
|
||||
@ -185,6 +186,8 @@ DEFAULTS = {
|
||||
'cursor_shape' : 'block',
|
||||
'cursor_color' : '#aaaaaa',
|
||||
'emulation' : 'xterm',
|
||||
'term' : 'xterm',
|
||||
'colorterm' : 'gnome-terminal',
|
||||
'font' : 'Mono 10',
|
||||
'foreground_color' : '#aaaaaa',
|
||||
'show_titlebar' : True,
|
||||
@ -351,9 +354,10 @@ class Config(object):
|
||||
self.gconf = gconf.client_get_default()
|
||||
|
||||
value = self.gconf.get('/apps/metacity/general/focus_mode')
|
||||
self.system_focus = value.get_string()
|
||||
self.gconf.notify_add('/apps/metacity/general/focus_mode',
|
||||
self.on_gconf_notify)
|
||||
if value:
|
||||
self.system_focus = value.get_string()
|
||||
self.gconf.notify_add('/apps/metacity/general/focus_mode',
|
||||
self.on_gconf_notify)
|
||||
return(self.system_focus)
|
||||
|
||||
def on_gconf_notify(self, _client, _cnxn_id, _entry, _what):
|
||||
@ -427,6 +431,8 @@ class ConfigBase(Borg):
|
||||
Borg.__init__(self, self.__class__.__name__)
|
||||
|
||||
self.prepare_attributes()
|
||||
import optionparse
|
||||
self.command_line_options = optionparse.options
|
||||
self.load()
|
||||
|
||||
def prepare_attributes(self):
|
||||
@ -474,6 +480,9 @@ class ConfigBase(Borg):
|
||||
|
||||
keytype = '%s(default=%s)' % (keytype, value)
|
||||
|
||||
if key == 'custom_url_handler':
|
||||
keytype = 'string(default="")'
|
||||
|
||||
section[key] = keytype
|
||||
configspecdata['global_config'] = section
|
||||
|
||||
@ -528,7 +537,10 @@ class ConfigBase(Borg):
|
||||
dbg('ConfigBase::load: config already loaded')
|
||||
return
|
||||
|
||||
filename = os.path.join(get_config_dir(), 'config')
|
||||
if not self.command_line_options.config:
|
||||
self.command_line_options.config = os.path.join(get_config_dir(), 'config')
|
||||
filename = self.command_line_options.config
|
||||
|
||||
dbg('looking for config file: %s' % filename)
|
||||
try:
|
||||
configfile = open(filename, 'r')
|
||||
@ -624,7 +636,7 @@ class ConfigBase(Borg):
|
||||
if not os.path.isdir(config_dir):
|
||||
os.makedirs(config_dir)
|
||||
try:
|
||||
parser.write(open(os.path.join(config_dir, 'config'), 'w'))
|
||||
parser.write(open(self.command_line_options.config, 'w'))
|
||||
except Exception, ex:
|
||||
err('ConfigBase::save: Unable to save config: %s' % ex)
|
||||
|
||||
|
@ -20,7 +20,7 @@ True
|
||||
"""
|
||||
|
||||
from borg import Borg
|
||||
from util import dbg, err
|
||||
from util import dbg, err, inject_uuid
|
||||
|
||||
# pylint: disable-msg=R0201
|
||||
# pylint: disable-msg=W0613
|
||||
@ -91,7 +91,9 @@ class Factory(Borg):
|
||||
return(None)
|
||||
|
||||
dbg('Factory::make: created a %s' % product)
|
||||
return(func(**kwargs))
|
||||
output = func(**kwargs)
|
||||
inject_uuid(output)
|
||||
return(output)
|
||||
|
||||
def make_window(self, **kwargs):
|
||||
"""Make a Window"""
|
||||
|
@ -10,6 +10,7 @@ import dbus.glib
|
||||
from borg import Borg
|
||||
from terminator import Terminator
|
||||
from config import Config
|
||||
from factory import Factory
|
||||
from util import dbg
|
||||
|
||||
CONFIG = Config()
|
||||
@ -58,13 +59,17 @@ class DBusService(Borg, dbus.service.Object):
|
||||
if not self.terminator:
|
||||
self.terminator = Terminator()
|
||||
|
||||
@dbus.service.method(BUS_NAME)
|
||||
def new_window(self, layout='default'):
|
||||
@dbus.service.method(BUS_NAME, in_signature='a{ss}')
|
||||
def new_window(self, options=dbus.Dictionary()):
|
||||
"""Create a new Window"""
|
||||
dbg('dbus method called: new_window')
|
||||
self.terminator.create_layout(layout)
|
||||
dbg('dbus method called: new_window with parameters %s'%(options))
|
||||
oldopts = self.terminator.config.options_get()
|
||||
oldopts.__dict__ = options
|
||||
self.terminator.config.options_set(oldopts)
|
||||
self.terminator.create_layout(oldopts.layout)
|
||||
self.terminator.layout_done()
|
||||
|
||||
|
||||
@dbus.service.method(BUS_NAME)
|
||||
def terminal_hsplit(self, uuid=None):
|
||||
"""Split a terminal horizontally, by UUID"""
|
||||
@ -93,6 +98,26 @@ class DBusService(Borg, dbus.service.Object):
|
||||
"""Return a list of all the terminals"""
|
||||
return [x.uuid.urn for x in self.terminator.terminals]
|
||||
|
||||
@dbus.service.method(BUS_NAME)
|
||||
def get_terminal_tab(self, uuid):
|
||||
"""Return the UUID of the parent tab of a given terminal"""
|
||||
maker = Factory()
|
||||
terminal = self.terminator.find_terminal_by_uuid(uuid)
|
||||
window = terminal.get_toplevel()
|
||||
root_widget = window.get_children()[0]
|
||||
if maker.isinstance(root_widget, 'Notebook'):
|
||||
return root_widget.uuid.urn
|
||||
|
||||
@dbus.service.method(BUS_NAME)
|
||||
def get_terminal_tab_title(self, uuid):
|
||||
"""Return the title of a parent tab of a given terminal"""
|
||||
maker = Factory()
|
||||
terminal = self.terminator.find_terminal_by_uuid(uuid)
|
||||
window = terminal.get_toplevel()
|
||||
root_widget = window.get_children()[0]
|
||||
if maker.isinstance(root_widget, "Notebook"):
|
||||
return root_widget.get_tab_label(terminal).get_label()
|
||||
|
||||
def with_proxy(func):
|
||||
"""Decorator function to connect to the session dbus bus"""
|
||||
dbg('dbus client call: %s' % func.func_name)
|
||||
@ -103,9 +128,9 @@ def with_proxy(func):
|
||||
return _exec
|
||||
|
||||
@with_proxy
|
||||
def new_window(session, layout='default'):
|
||||
def new_window(session, options):
|
||||
"""Call the dbus method to open a new window"""
|
||||
session.new_window(layout)
|
||||
session.new_window(options)
|
||||
|
||||
@with_proxy
|
||||
def terminal_hsplit(session, uuid):
|
||||
@ -122,3 +147,13 @@ def get_terminals(session, uuid):
|
||||
"""Call the dbus method to return a list of all terminals"""
|
||||
print '\n'.join(session.get_terminals(uuid))
|
||||
|
||||
@with_proxy
|
||||
def get_terminal_tab(session, uuid):
|
||||
"""Call the dbus method to return the toplevel tab for a terminal"""
|
||||
print session.get_terminal_tab(uuid)
|
||||
|
||||
@with_proxy
|
||||
def get_terminal_tab_title(session, uuid):
|
||||
"""Call the dbus method to return the title of a tab"""
|
||||
print session.get_terminal_tab_title(uuid)
|
||||
|
||||
|
@ -136,6 +136,10 @@ class Notebook(Container, gtk.Notebook):
|
||||
sibling = maker.make('terminal')
|
||||
sibling.set_cwd(cwd)
|
||||
sibling.spawn_child()
|
||||
if widget.group and self.config['split_to_group']:
|
||||
sibling.set_group(None, widget.group)
|
||||
if self.config['always_split_with_profile']:
|
||||
sibling.force_set_profile(None, widget.get_profile())
|
||||
|
||||
self.insert_page(container, None, page_num)
|
||||
self.set_tab_reorderable(container, True)
|
||||
@ -195,7 +199,7 @@ class Notebook(Container, gtk.Notebook):
|
||||
children.append(self.get_nth_page(page))
|
||||
return(children)
|
||||
|
||||
def newtab(self, debugtab=False, widget=None, cwd=None, metadata=None):
|
||||
def newtab(self, debugtab=False, widget=None, cwd=None, metadata=None, profile=None):
|
||||
"""Add a new tab, optionally supplying a child widget"""
|
||||
dbg('making a new tab')
|
||||
maker = Factory()
|
||||
@ -206,6 +210,8 @@ class Notebook(Container, gtk.Notebook):
|
||||
if cwd:
|
||||
widget.set_cwd(cwd)
|
||||
widget.spawn_child(debugserver=debugtab)
|
||||
if profile and self.config['always_split_with_profile']:
|
||||
widget.force_set_profile(None, profile)
|
||||
|
||||
signals = {'close-term': self.wrapcloseterm,
|
||||
'split-horiz': self.split_horiz,
|
||||
@ -472,6 +478,8 @@ class TabLabel(gtk.HBox):
|
||||
self.set_orientation(gtk.ORIENTATION_VERTICAL)
|
||||
self.label.set_angle(90)
|
||||
elif position == gtk.POS_RIGHT:
|
||||
if hasattr(self, 'set_orientation'):
|
||||
self.set_orientation(gtk.ORIENTATION_VERTICAL)
|
||||
self.label.set_angle(270)
|
||||
else:
|
||||
if hasattr(self, 'set_orientation'):
|
||||
|
@ -26,6 +26,8 @@ import config
|
||||
import version
|
||||
from translation import _
|
||||
|
||||
options = None
|
||||
|
||||
def execute_cb(option, opt, value, lparser):
|
||||
"""Callback for use in parsing execute options"""
|
||||
assert value is None
|
||||
@ -40,7 +42,6 @@ def parse_options():
|
||||
"""Parse the command line options"""
|
||||
usage = "usage: %prog [options]"
|
||||
|
||||
configobj = config.Config()
|
||||
parser = OptionParser(usage)
|
||||
|
||||
parser.add_option('-v', '--version', action='store_true', dest='version',
|
||||
@ -53,26 +54,31 @@ def parse_options():
|
||||
dest='borderless', help=_('Disable window borders'))
|
||||
parser.add_option('-H', '--hidden', action='store_true', dest='hidden',
|
||||
help=_('Hide the window at startup'))
|
||||
parser.add_option('-T', '--title', dest='forcedtitle', help=_('Specify a \
|
||||
title for the window'))
|
||||
parser.add_option('--geometry', dest='geometry', type='string', help=_('Set \
|
||||
the preferred size and position of the window (see X man page)'))
|
||||
parser.add_option('-e', '--command', dest='command', help=_('Specify a \
|
||||
command to execute inside the terminal'))
|
||||
parser.add_option('-T', '--title', dest='forcedtitle',
|
||||
help=_('Specify a title for the window'))
|
||||
parser.add_option('--geometry', dest='geometry', type='string',
|
||||
help=_('Set the preferred size and position of the window'
|
||||
'(see X man page)'))
|
||||
parser.add_option('-e', '--command', dest='command',
|
||||
help=_('Specify a command to execute inside the terminal'))
|
||||
parser.add_option('-g', '--config', dest='config',
|
||||
help=_('Specify a config file'))
|
||||
parser.add_option('-x', '--execute', dest='execute', action='callback',
|
||||
callback=execute_cb, help=_('Use the rest of the command line as a \
|
||||
command to execute inside the terminal, and its arguments'))
|
||||
callback=execute_cb,
|
||||
help=_('Use the rest of the command line as a command to execute'
|
||||
'nside the terminal, and its arguments'))
|
||||
parser.add_option('--working-directory', metavar='DIR',
|
||||
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('-c', '--classname', dest='classname', help=_('Set a \
|
||||
custom name (WM_CLASS) property on the window'))
|
||||
parser.add_option('-l', '--layout', dest='layout', help=_('Select a layout'))
|
||||
parser.add_option('-p', '--profile', dest='profile', help=_('Use a \
|
||||
different profile as the default'))
|
||||
parser.add_option('-i', '--icon', dest='forcedicon', help=_('Set a custom \
|
||||
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'))
|
||||
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',
|
||||
help=_('Disable DBus'))
|
||||
parser.add_option('-d', '--debug', action='count', dest='debug',
|
||||
@ -82,10 +88,11 @@ icon for the window (by file or name)'))
|
||||
parser.add_option('--debug-methods', action='store', dest='debug_methods',
|
||||
help=_('Comma separated list of methods to limit debugging to'))
|
||||
for item in ['--sm-client-id', '--sm-config-prefix', '--screen', '-n',
|
||||
'--no-gconf' ]:
|
||||
'--no-gconf' ]:
|
||||
parser.add_option(item, dest='dummy', action='store',
|
||||
help=SUPPRESS_HELP)
|
||||
|
||||
global options
|
||||
(options, args) = parser.parse_args()
|
||||
if len(args) != 0:
|
||||
parser.error('Additional unexpected arguments found: %s' % args)
|
||||
@ -122,6 +129,7 @@ icon for the window (by file or name)'))
|
||||
if options.layout is None:
|
||||
options.layout = 'default'
|
||||
|
||||
configobj = config.Config()
|
||||
if options.profile and options.profile not in configobj.list_profiles():
|
||||
options.profile = None
|
||||
|
||||
|
@ -50,6 +50,10 @@ class Paned(Container):
|
||||
sibling = self.maker.make('terminal')
|
||||
sibling.set_cwd(cwd)
|
||||
sibling.spawn_child()
|
||||
if widget.group and self.config['split_to_group']:
|
||||
sibling.set_group(None, widget.group)
|
||||
if self.config['always_split_with_profile']:
|
||||
sibling.force_set_profile(None, widget.get_profile())
|
||||
|
||||
self.add(container)
|
||||
self.show_all()
|
||||
@ -111,7 +115,10 @@ class Paned(Container):
|
||||
handler = handler[0]
|
||||
self.connect_child(widget, signal, handler, *args)
|
||||
|
||||
widget.grab_focus()
|
||||
if metadata and \
|
||||
metadata.has_key('had_focus') and \
|
||||
metadata['had_focus'] == True:
|
||||
widget.grab_focus()
|
||||
|
||||
elif isinstance(widget, gtk.Paned):
|
||||
try:
|
||||
@ -220,9 +227,15 @@ class Paned(Container):
|
||||
children.append(self.get_child2())
|
||||
return(children)
|
||||
|
||||
def get_child_metadata(self, widget):
|
||||
"""Return metadata about a child"""
|
||||
metadata = {}
|
||||
metadata['had_focus'] = widget.has_focus()
|
||||
|
||||
def wrapcloseterm(self, widget):
|
||||
"""A child terminal has closed, so this container must die"""
|
||||
dbg('Paned::wrapcloseterm: Called on %s' % widget)
|
||||
|
||||
if self.closeterm(widget):
|
||||
# At this point we only have one child, which is the surviving term
|
||||
sibling = self.children[0]
|
||||
@ -235,7 +248,6 @@ class Paned(Container):
|
||||
parent.remove(self)
|
||||
self.cnxids.remove_all()
|
||||
parent.add(sibling, metadata)
|
||||
sibling.grab_focus()
|
||||
del(self)
|
||||
else:
|
||||
dbg("Paned::wrapcloseterm: self.closeterm failed")
|
||||
|
108
terminatorlib/plugins/logger.py
Normal file
108
terminatorlib/plugins/logger.py
Normal file
@ -0,0 +1,108 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
# Plugin by Sinan Nalkaya <sardok@gmail.com>
|
||||
# See LICENSE of Terminator package.
|
||||
|
||||
""" logger.py - Terminator Plugin to log 'content' of individual
|
||||
terminals """
|
||||
|
||||
import os
|
||||
import sys
|
||||
import gtk
|
||||
import terminatorlib.plugin as plugin
|
||||
from terminatorlib.translation import _
|
||||
|
||||
AVAILABLE = ['Logger']
|
||||
|
||||
class Logger(plugin.MenuItem):
|
||||
""" Add custom command to the terminal menu"""
|
||||
capabilities = ['terminal_menu']
|
||||
loggers = None
|
||||
dialog_action = gtk.FILE_CHOOSER_ACTION_SAVE
|
||||
dialog_buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
|
||||
gtk.STOCK_SAVE, gtk.RESPONSE_OK)
|
||||
|
||||
def __init__(self):
|
||||
plugin.MenuItem.__init__(self)
|
||||
if not self.loggers:
|
||||
self.loggers = {}
|
||||
|
||||
def callback(self, menuitems, menu, terminal):
|
||||
""" Add save menu item to the menu"""
|
||||
vte_terminal = terminal.get_vte()
|
||||
if not self.loggers.has_key(vte_terminal):
|
||||
item = gtk.MenuItem(_('Start Logger'))
|
||||
item.connect("activate", self.start_logger, terminal)
|
||||
else:
|
||||
item = gtk.MenuItem(_('Stop Logger'))
|
||||
item.connect("activate", self.stop_logger, terminal)
|
||||
item.set_has_tooltip(True)
|
||||
item.set_tooltip_text("Saving at '" + self.loggers[vte_terminal]["filepath"] + "'")
|
||||
menuitems.append(item)
|
||||
|
||||
def write_content(self, terminal, row_start, col_start, row_end, col_end):
|
||||
""" Final function to write a file """
|
||||
content = terminal.get_text_range(row_start, col_start, row_end, col_end,
|
||||
lambda *a: True)
|
||||
fd = self.loggers[terminal]["fd"]
|
||||
# Don't write the last char which is always '\n'
|
||||
fd.write(content[:-1])
|
||||
self.loggers[terminal]["col"] = col_end
|
||||
self.loggers[terminal]["row"] = row_end
|
||||
|
||||
def save(self, terminal):
|
||||
""" 'contents-changed' callback """
|
||||
last_saved_col = self.loggers[terminal]["col"]
|
||||
last_saved_row = self.loggers[terminal]["row"]
|
||||
(col, row) = terminal.get_cursor_position()
|
||||
# Save only when buffer is nearly full,
|
||||
# for the sake of efficiency
|
||||
if row - last_saved_row < terminal.get_row_count():
|
||||
return
|
||||
self.write_content(terminal, last_saved_row, last_saved_col, row, col)
|
||||
|
||||
def start_logger(self, _widget, Terminal):
|
||||
""" Handle menu item callback by saving text to a file"""
|
||||
savedialog = gtk.FileChooserDialog(title="Save Log File As",
|
||||
action=self.dialog_action,
|
||||
buttons=self.dialog_buttons)
|
||||
savedialog.set_do_overwrite_confirmation(True)
|
||||
savedialog.set_local_only(True)
|
||||
savedialog.show_all()
|
||||
response = savedialog.run()
|
||||
if response == gtk.RESPONSE_OK:
|
||||
try:
|
||||
logfile = os.path.join(savedialog.get_current_folder(),
|
||||
savedialog.get_filename())
|
||||
fd = open(logfile, 'w+')
|
||||
# Save log file path,
|
||||
# associated file descriptor, signal handler id
|
||||
# and last saved col,row positions respectively.
|
||||
vte_terminal = Terminal.get_vte()
|
||||
(col, row) = vte_terminal.get_cursor_position()
|
||||
|
||||
self.loggers[vte_terminal] = {"filepath":logfile,
|
||||
"handler_id":0, "fd":fd,
|
||||
"col":col, "row":row}
|
||||
# Add contents-changed callback
|
||||
self.loggers[vte_terminal]["handler_id"] = vte_terminal.connect('contents-changed', self.save)
|
||||
except:
|
||||
e = sys.exc_info()[1]
|
||||
error = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR,
|
||||
gtk.BUTTONS_OK, e.strerror)
|
||||
error.run()
|
||||
error.destroy()
|
||||
savedialog.destroy()
|
||||
|
||||
def stop_logger(self, _widget, terminal):
|
||||
vte_terminal = terminal.get_vte()
|
||||
last_saved_col = self.loggers[vte_terminal]["col"]
|
||||
last_saved_row = self.loggers[vte_terminal]["row"]
|
||||
(col, row) = vte_terminal.get_cursor_position()
|
||||
if last_saved_col != col or last_saved_row != row:
|
||||
# Save unwritten bufer to the file
|
||||
self.write_content(vte_terminal, last_saved_row, last_saved_col, row, col)
|
||||
fd = self.loggers[vte_terminal]["fd"]
|
||||
fd.close()
|
||||
vte_terminal.disconnect(self.loggers[vte_terminal]["handler_id"])
|
||||
del(self.loggers[vte_terminal])
|
@ -382,7 +382,7 @@
|
||||
<object class="GtkTable" id="global_config_table">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="n_rows">15</property>
|
||||
<property name="n_rows">16</property>
|
||||
<property name="n_columns">2</property>
|
||||
<property name="column_spacing">6</property>
|
||||
<child>
|
||||
@ -857,6 +857,36 @@
|
||||
<property name="bottom_attach">15</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label34">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Re-use profiles for new terminals</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="top_attach">15</property>
|
||||
<property name="bottom_attach">16</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="always_split_with_profile">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="use_action_appearance">False</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<signal name="toggled" handler="on_always_split_with_profile_toggled" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">2</property>
|
||||
<property name="top_attach">15</property>
|
||||
<property name="bottom_attach">16</property>
|
||||
<property name="x_options">GTK_EXPAND</property>
|
||||
<property name="y_options">GTK_EXPAND</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
|
@ -238,6 +238,9 @@ class PrefsEditor:
|
||||
#Hide size text from the title bar
|
||||
widget = guiget('title_hide_sizetextcheck')
|
||||
widget.set_active(self.config['title_hide_sizetext'])
|
||||
#Always split with profile
|
||||
widget = guiget('always_split_with_profile')
|
||||
widget.set_active(self.config['always_split_with_profile'])
|
||||
|
||||
## Profile tab
|
||||
# Populate the profile list
|
||||
@ -610,6 +613,11 @@ class PrefsEditor:
|
||||
self.config['title_hide_sizetext'] = widget.get_active()
|
||||
self.config.save()
|
||||
|
||||
def on_always_split_with_profile_toggled(self, widget):
|
||||
"""Always split with profile setting changed"""
|
||||
self.config['always_split_with_profile'] = widget.get_active()
|
||||
self.config.save()
|
||||
|
||||
def on_allow_bold_checkbutton_toggled(self, widget):
|
||||
"""Allow bold setting changed"""
|
||||
self.config['allow_bold'] = widget.get_active()
|
||||
|
@ -14,7 +14,6 @@ import gobject
|
||||
import pango
|
||||
import subprocess
|
||||
import urllib
|
||||
import uuid
|
||||
|
||||
from util import dbg, err, gerr
|
||||
import util
|
||||
@ -89,7 +88,6 @@ class Terminal(gtk.VBox):
|
||||
command = None
|
||||
clipboard = None
|
||||
pid = None
|
||||
uuid = None
|
||||
|
||||
matches = None
|
||||
config = None
|
||||
@ -107,6 +105,7 @@ class Terminal(gtk.VBox):
|
||||
composite_support = None
|
||||
|
||||
cnxids = None
|
||||
targets_for_new_group = None
|
||||
|
||||
def __init__(self):
|
||||
"""Class initialiser"""
|
||||
@ -119,6 +118,7 @@ class Terminal(gtk.VBox):
|
||||
# FIXME: Surely these should happen in Terminator::register_terminal()?
|
||||
self.connect('enumerate', self.terminator.do_enumerate)
|
||||
self.connect('focus-in', self.terminator.focus_changed)
|
||||
self.connect('focus-out', self.terminator.focus_left)
|
||||
|
||||
self.matches = {}
|
||||
self.cnxids = Signalman()
|
||||
@ -131,9 +131,6 @@ class Terminal(gtk.VBox):
|
||||
|
||||
self.pending_on_vte_size_allocate = False
|
||||
|
||||
self.uuid = uuid.uuid4()
|
||||
dbg('assigning Terminal a TERMINATOR_UUID of: %s' % self.uuid.urn)
|
||||
|
||||
self.vte = vte.Terminal()
|
||||
self.vte._expose_data = None
|
||||
if not hasattr(self.vte, "set_opacity") or \
|
||||
@ -167,8 +164,8 @@ class Terminal(gtk.VBox):
|
||||
|
||||
self.connect_signals()
|
||||
|
||||
os.putenv('TERM', 'xterm')
|
||||
os.putenv('COLORTERM', 'gnome-terminal')
|
||||
os.putenv('TERM', self.config['term'])
|
||||
os.putenv('COLORTERM', self.config['colorterm'])
|
||||
|
||||
env_proxy = os.getenv('http_proxy')
|
||||
if not env_proxy:
|
||||
@ -770,7 +767,40 @@ class Terminal(gtk.VBox):
|
||||
def on_group_button_press(self, widget, event):
|
||||
"""Handler for the group button"""
|
||||
if event.button == 1:
|
||||
self.create_popup_group_menu(widget, event)
|
||||
if event.type == gtk.gdk._2BUTTON_PRESS or \
|
||||
event.type == gtk.gdk._3BUTTON_PRESS:
|
||||
# Ignore these, or they make the interaction bad
|
||||
return False
|
||||
# Super key applies interaction to all terms in group
|
||||
include_siblings=event.state & gtk.gdk.MOD4_MASK == gtk.gdk.MOD4_MASK
|
||||
if include_siblings:
|
||||
targets=self.terminator.get_sibling_terms(self)
|
||||
else:
|
||||
targets=[self]
|
||||
if event.state & gtk.gdk.CONTROL_MASK == gtk.gdk.CONTROL_MASK:
|
||||
dbg('on_group_button_press: toggle terminal to focused terminals group')
|
||||
focused=self.get_toplevel().get_focussed_terminal()
|
||||
if focused in targets: targets.remove(focused)
|
||||
if self != focused:
|
||||
if self.group==focused.group:
|
||||
new_group=None
|
||||
else:
|
||||
new_group=focused.group
|
||||
[term.set_group(None, new_group) for term in targets]
|
||||
[term.titlebar.update(focused) for term in targets]
|
||||
return True
|
||||
elif event.state & gtk.gdk.SHIFT_MASK == gtk.gdk.SHIFT_MASK:
|
||||
dbg('on_group_button_press: rename of terminals group')
|
||||
self.targets_for_new_group = targets
|
||||
self.titlebar.create_group()
|
||||
return True
|
||||
elif event.type == gtk.gdk.BUTTON_PRESS:
|
||||
# Single Click gives popup
|
||||
dbg('on_group_button_press: group menu popup')
|
||||
self.create_popup_group_menu(widget, event)
|
||||
return True
|
||||
else:
|
||||
dbg('on_group_button_press: unknown group button interaction')
|
||||
return(False)
|
||||
|
||||
def on_keypress(self, widget, event):
|
||||
@ -947,9 +977,10 @@ class Terminal(gtk.VBox):
|
||||
if gtk.targets_include_text(drag_context.targets) or \
|
||||
gtk.targets_include_uri(drag_context.targets):
|
||||
# copy text to destination
|
||||
txt = selection_data.data.strip()
|
||||
txt = selection_data.data.strip(' ')
|
||||
if txt[0:7] == 'file://':
|
||||
txt = "'%s'" % urllib.unquote(txt[7:])
|
||||
txt = selection_data.data.strip('\n')
|
||||
for term in self.terminator.get_target_terms(self):
|
||||
term.feed(txt)
|
||||
return
|
||||
@ -1247,6 +1278,8 @@ class Terminal(gtk.VBox):
|
||||
pass
|
||||
|
||||
envv = []
|
||||
envv.append('TERM=%s' % self.config['term'])
|
||||
envv.append('COLORTERM=%s' % self.config['colorterm'])
|
||||
envv.append('TERMINATOR_UUID=%s' % self.uuid.urn)
|
||||
if self.terminator.dbus_name:
|
||||
envv.append('TERMINATOR_DBUS_NAME=%s' % self.terminator.dbus_name)
|
||||
|
@ -349,19 +349,21 @@ class Terminator(Borg):
|
||||
idx = terminals.index(term)
|
||||
term.feed(numstr % (idx + 1))
|
||||
|
||||
def get_sibling_terms(self, widget):
|
||||
termset = []
|
||||
for term in self.terminals:
|
||||
if term.group == widget.group:
|
||||
termset.append(term)
|
||||
return(termset)
|
||||
|
||||
def get_target_terms(self, widget):
|
||||
"""Get the terminals we should currently be broadcasting to"""
|
||||
if self.groupsend == self.groupsend_type['all']:
|
||||
return(self.terminals)
|
||||
elif self.groupsend == self.groupsend_type['group']:
|
||||
termset = []
|
||||
for term in self.terminals:
|
||||
if term == widget or (term.group != None and term.group ==
|
||||
widget.group):
|
||||
termset.append(term)
|
||||
return(termset)
|
||||
else:
|
||||
return([widget])
|
||||
if widget.group != None:
|
||||
return(self.get_sibling_terms(widget))
|
||||
return([widget])
|
||||
|
||||
def get_focussed_terminal(self):
|
||||
"""iterate over all the terminals to find which, if any, has focus"""
|
||||
@ -376,6 +378,9 @@ class Terminator(Borg):
|
||||
terminal.titlebar.update(widget)
|
||||
return
|
||||
|
||||
def focus_left(self, widget):
|
||||
self.last_focused_term=widget
|
||||
|
||||
def describe_layout(self):
|
||||
"""Describe our current layout"""
|
||||
layout = {}
|
||||
|
@ -94,7 +94,7 @@ class Titlebar(gtk.EventBox):
|
||||
|
||||
def connect_icon(self, func):
|
||||
"""Connect the supplied function to clicking on the group icon"""
|
||||
self.ebox.connect('button-release-event', func)
|
||||
self.ebox.connect('button-press-event', func)
|
||||
|
||||
def update(self, other=None):
|
||||
"""Update our contents"""
|
||||
@ -247,6 +247,7 @@ class Titlebar(gtk.EventBox):
|
||||
if self.groupentry.get_text()=='' and freegroups:
|
||||
self.groupentry.set_text(freegroups.pop())
|
||||
self.groupentry.show()
|
||||
self.grouplabel.hide()
|
||||
self.groupentry.grab_focus()
|
||||
self.update_visibility()
|
||||
|
||||
@ -254,6 +255,7 @@ class Titlebar(gtk.EventBox):
|
||||
"""Hide the group name entry"""
|
||||
self.groupentry.set_text('')
|
||||
self.groupentry.hide()
|
||||
self.grouplabel.show()
|
||||
self.get_parent().grab_focus()
|
||||
|
||||
def groupentry_activate(self, widget):
|
||||
@ -261,7 +263,14 @@ class Titlebar(gtk.EventBox):
|
||||
groupname = self.groupentry.get_text()
|
||||
dbg('Titlebar::groupentry_activate: creating group: %s' % groupname)
|
||||
self.groupentry_cancel(None, None)
|
||||
self.emit('create-group', groupname)
|
||||
last_focused_term=self.terminator.last_focused_term
|
||||
if self.terminal.targets_for_new_group:
|
||||
[term.titlebar.emit('create-group', groupname) for term in self.terminal.targets_for_new_group]
|
||||
self.terminal.targets_for_new_group = None
|
||||
else:
|
||||
self.emit('create-group', groupname)
|
||||
last_focused_term.grab_focus()
|
||||
self.terminator.focus_changed(last_focused_term)
|
||||
|
||||
def groupentry_keypress(self, widget, event):
|
||||
"""Handle keypresses on the entry widget"""
|
||||
|
@ -28,6 +28,7 @@ import gtk
|
||||
import os
|
||||
import pwd
|
||||
import inspect
|
||||
import uuid
|
||||
|
||||
# set this to true to enable debugging output
|
||||
DEBUG = False
|
||||
@ -276,3 +277,16 @@ def enumerate_descendants(parent):
|
||||
len(terminals), parent))
|
||||
return(containers, terminals)
|
||||
|
||||
def make_uuid():
|
||||
"""Generate a UUID for an object"""
|
||||
return uuid.uuid4()
|
||||
|
||||
def inject_uuid(target):
|
||||
"""Inject a UUID into an existing object"""
|
||||
uuid = make_uuid()
|
||||
if not hasattr(target, "uuid") or target.uuid == None:
|
||||
dbg("Injecting UUID %s into: %s" % (uuid, target))
|
||||
target.uuid = uuid
|
||||
else:
|
||||
dbg("Object already has a UUID: %s" % target)
|
||||
|
||||
|
@ -21,4 +21,4 @@ TerminatorVersion supplies our version number.
|
||||
"""
|
||||
|
||||
APP_NAME = 'terminator'
|
||||
APP_VERSION = '0.96'
|
||||
APP_VERSION = '1.0'
|
||||
|
@ -71,10 +71,10 @@ class Window(Container, gtk.Window):
|
||||
|
||||
options = self.config.options_get()
|
||||
if options:
|
||||
if options.forcedtitle is not None:
|
||||
if options.forcedtitle:
|
||||
self.title.force_title(options.forcedtitle)
|
||||
|
||||
if options.role is not None:
|
||||
if options.role:
|
||||
self.set_role(options.role)
|
||||
|
||||
if options.classname is not None:
|
||||
@ -83,7 +83,7 @@ class Window(Container, gtk.Window):
|
||||
if options.forcedicon is not None:
|
||||
icon_to_apply = options.forcedicon
|
||||
|
||||
if options.geometry is not None:
|
||||
if options.geometry:
|
||||
if not self.parse_geometry(options.geometry):
|
||||
err('Window::__init__: Unable to parse geometry: %s' %
|
||||
options.geometry)
|
||||
@ -248,6 +248,7 @@ class Window(Container, gtk.Window):
|
||||
def tab_new(self, widget=None, debugtab=False, _param1=None, _param2=None):
|
||||
"""Make a new tab"""
|
||||
cwd = None
|
||||
profile = None
|
||||
|
||||
if self.get_property('term_zoomed') == True:
|
||||
err("You can't create a tab while a terminal is maximised/zoomed")
|
||||
@ -255,11 +256,13 @@ class Window(Container, gtk.Window):
|
||||
|
||||
if widget:
|
||||
cwd = widget.get_cwd()
|
||||
profile = widget.get_profile()
|
||||
|
||||
maker = Factory()
|
||||
if not self.is_child_notebook():
|
||||
dbg('Making a new Notebook')
|
||||
notebook = maker.make('Notebook', window=self)
|
||||
self.get_child().newtab(debugtab, cwd=cwd)
|
||||
self.get_child().newtab(debugtab, cwd=cwd, profile=profile)
|
||||
|
||||
def on_delete_event(self, window, event, data=None):
|
||||
"""Handle a window close request"""
|
||||
@ -373,8 +376,10 @@ class Window(Container, gtk.Window):
|
||||
|
||||
def show(self, startup=False):
|
||||
"""Undo the startup show request if started in hidden mode"""
|
||||
gtk.Window.show(self)
|
||||
#Present is necessary to grab focus when window is hidden from taskbar
|
||||
#Present is necessary to grab focus when window is hidden from taskbar.
|
||||
#It is important to call present() before show(), otherwise the window
|
||||
#won't be brought to front if an another application has the focus.
|
||||
#Last note: present() will implicitly call gtk.Window.show()
|
||||
self.present()
|
||||
|
||||
#Window must be shown, then hidden for the hotkeys to be registered
|
||||
@ -454,6 +459,11 @@ class Window(Container, gtk.Window):
|
||||
sibling = maker.make('Terminal')
|
||||
sibling.set_cwd(cwd)
|
||||
sibling.spawn_child()
|
||||
if widget.group and self.config['split_to_group']:
|
||||
sibling.set_group(None, widget.group)
|
||||
if self.config['always_split_with_profile']:
|
||||
sibling.force_set_profile(None, widget.get_profile())
|
||||
|
||||
self.add(container)
|
||||
container.show_all()
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user