Merge changes from trunk - diverged

This commit is contained in:
Stephen Boddy 2012-10-22 14:34:00 +02:00
commit 183914c7dc
22 changed files with 410 additions and 68 deletions

View File

@ -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
View File

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

View File

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

View File

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

View File

@ -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__':

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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])

View File

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

View File

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

View File

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

View File

@ -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 = {}

View File

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

View File

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

View File

@ -21,4 +21,4 @@ TerminatorVersion supplies our version number.
"""
APP_NAME = 'terminator'
APP_VERSION = '0.96'
APP_VERSION = '1.0'

View File

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