2006-11-10 05:18:31 +00:00
#!/usr/bin/python
2007-07-28 01:33:48 +00:00
# Terminator - multiple gnome terminals in one window
2008-02-08 10:20:48 +00:00
# Copyright (C) 2006-2008 cmsj@tenshu.net
2007-07-28 01:33:48 +00:00
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, version 2 only.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
2008-02-12 22:06:19 +00:00
"""Terminator by Chris Jones <cmsj@tenshu.net>"""
2007-12-27 17:44:38 +00:00
2008-01-03 08:10:39 +00:00
# import standard python libs
2008-02-25 00:00:10 +00:00
import os, platform, sys, string, time, math
2008-02-12 13:49:36 +00:00
from optparse import OptionParser
2008-01-03 08:10:39 +00:00
import gettext
gettext.install ('terminator')
# import unix-lib
2007-08-26 23:24:32 +00:00
import pwd
2008-01-03 08:10:39 +00:00
2008-03-06 20:00:08 +00:00
TARGET_TYPE_VTE = 8
2008-02-26 00:25:01 +00:00
# import gconf if possible, if not construct a fake replacement
class fakegconfclient:
def get_string (self, key):
return ("")
def get_list (self, key, type):
return ([])
def add_dir (self, profile, path):
return (True)
def notify_add (self, profile, callback):
return (True)
def get_bool (self, key):
return (False)
def get (self, key):
return (0)
class fakegconf:
CLIENT_PRELOAD_RECURSIVE = False
VALUE_STRING = ""
VALUE_INT = 0
VALUE_FLOAT = 0.0
VALUE_BOOL = False
def client_get_default (self):
foo = fakegconfclient ()
return (foo)
try:
import gconf
except:
pass
2008-01-29 23:58:08 +00:00
# import gtk libs
2008-01-03 08:10:39 +00:00
# check just in case anyone runs it on a non-gnome system.
try:
2008-02-26 00:25:01 +00:00
import gobject, gtk, pango
2008-01-03 08:10:39 +00:00
except:
print >> sys.stderr, _("You need to install the python bindings for " \
2008-02-26 00:25:01 +00:00
"gobject, gtk and pango to run Terminator.")
2008-01-03 08:10:39 +00:00
sys.exit(1)
2008-01-29 23:58:08 +00:00
# import a library for viewing URLs
try:
# gnome.url_show() is really useful
import gnome
url_show = gnome.url_show
except:
# webbrowser.open() is not really useful, but will do as a fallback
import webbrowser
url_show = webbrowser.open
2008-01-03 08:10:39 +00:00
# import vte-bindings
2007-07-29 18:09:33 +00:00
try:
import vte
except:
2008-01-03 08:10:39 +00:00
error = gtk.MessageDialog (None, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK,
_('You need to install python bindings for libvte ("python-vte" in debian/ubuntu)'))
error.run()
2007-07-29 18:09:33 +00:00
sys.exit (1)
2006-11-10 05:18:31 +00:00
2008-02-17 01:28:18 +00:00
def openurl (url):
try:
url_show (url)
except:
pass
2006-11-10 05:18:31 +00:00
class TerminatorTerm:
2007-05-08 23:15:26 +00:00
2007-02-24 14:53:10 +00:00
# Our settings
2006-11-10 05:18:31 +00:00
defaults = {
2007-12-16 17:11:22 +00:00
'gt_dir' : '/apps/gnome-terminal',
2008-01-29 14:00:02 +00:00
'_profile_dir' : '%s/profiles',
2008-03-01 01:14:32 +00:00
'titlebars' : True,
'titletips' : False,
2006-11-10 05:18:31 +00:00
'allow_bold' : True,
2008-02-12 23:11:04 +00:00
'silent_bell' : True,
2006-11-10 05:18:31 +00:00
'background_color' : '#000000',
2008-01-06 03:23:26 +00:00
'background_darkness' : 0.5,
'background_type' : 'solid',
2006-11-10 05:18:31 +00:00
'backspace_binding' : 'ascii-del',
2007-07-28 00:44:32 +00:00
'delete_binding' : 'delete-sequence',
2008-01-29 15:18:33 +00:00
'cursor_blink' : False,
2006-11-10 05:18:31 +00:00
'emulation' : 'xterm',
2008-01-31 22:15:23 +00:00
'font' : 'Serif 10',
2006-11-10 05:18:31 +00:00
'foreground_color' : '#AAAAAA',
2008-02-02 01:49:32 +00:00
'scrollbar_position' : "right",
2008-01-06 03:25:54 +00:00
'scroll_background' : True,
2006-11-10 05:18:31 +00:00
'scroll_on_keystroke' : False,
'scroll_on_output' : False,
'scrollback_lines' : 100,
2007-05-08 23:15:26 +00:00
'focus' : 'sloppy',
2008-02-24 23:46:56 +00:00
'exit_action' : 'close',
2008-03-06 14:57:16 +00:00
'overlay_type' : "rectangle",
2008-02-17 23:08:27 +00:00
'palette' : '#000000000000:#CDCD00000000:#0000CDCD0000:#CDCDCDCD0000:#30BF30BFA38E:#A53C212FA53C:#0000CDCDCDCD:#FAFAEBEBD7D7:#404040404040:#FFFF00000000:#0000FFFF0000:#FFFFFFFF0000:#00000000FFFF:#FFFF0000FFFF:#0000FFFFFFFF:#FFFFFFFFFFFF',
2007-12-16 17:00:53 +00:00
'word_chars' : '-A-Za-z0-9,./?%&#:_',
'mouse_autohide' : True,
2006-11-10 05:18:31 +00:00
}
2007-11-09 03:43:11 +00:00
matches = {}
2008-02-24 22:33:26 +00:00
def __init__ (self, terminator, profile = None, command = None, cwd = None):
2007-12-16 17:11:22 +00:00
self.defaults['profile_dir'] = self.defaults['_profile_dir']%(self.defaults['gt_dir'])
2007-02-24 14:53:10 +00:00
2008-01-29 13:54:16 +00:00
self.terminator = terminator
2006-11-10 05:18:31 +00:00
self.gconf_client = gconf.client_get_default ()
2008-01-28 04:27:18 +00:00
self.command = command
2007-08-31 19:33:39 +00:00
2008-02-24 22:33:26 +00:00
self.cwd = cwd or os.getcwd();
2008-01-08 08:59:22 +00:00
if profile == None:
profile = self.gconf_client.get_string (self.defaults['gt_dir'] + '/global/default_profile')
2008-01-06 09:31:37 +00:00
self.profile = ""
2007-12-16 17:11:22 +00:00
profiles = self.gconf_client.get_list (self.defaults['gt_dir'] + '/global/profile_list', 'string')
2008-01-06 09:31:37 +00:00
if profile in profiles:
self.profile = '%s/%s'%(self.defaults['profile_dir'], profile)
2008-01-06 22:23:16 +00:00
else:
if profile != "Default" and "Default" in profiles:
self.profile = '%s/Default'%(self.defaults['profile_dir'])
2007-11-16 00:19:05 +00:00
2008-01-06 09:31:37 +00:00
if self.profile:
self.gconf_client.add_dir (self.profile, gconf.CLIENT_PRELOAD_RECURSIVE)
self.gconf_client.notify_add (self.profile, self.on_gconf_notification)
2008-03-01 00:24:14 +00:00
if os.path.exists (pwd.getpwuid(os.getuid ())[5] + "/.terminatorrc"):
f = open (pwd.getpwuid (os.getuid ())[5] + "/.terminatorrc")
config = f.readlines ()
f.close ()
for line in config:
try:
line = line.strip ()
if line[0] == '#':
2008-02-26 00:25:01 +00:00
pass
2008-03-01 00:24:14 +00:00
elif line:
(key,value) = line.split ("=")
print >> sys.stderr, _('''Overriding setting '%s' from value '%s' to: '%s' ''')%(key.strip (), self.defaults[key.strip ()], value.strip ())
2008-03-02 12:11:34 +00:00
if value.strip() == "True":
self.defaults[key.strip ()] = True
elif value.strip() == "False":
self.defaults[key.strip ()] = False
else:
self.defaults[key.strip ()] = value.strip ()
2008-03-01 00:24:14 +00:00
except:
pass
2008-01-06 09:31:37 +00:00
2007-05-08 23:15:26 +00:00
self.gconf_client.add_dir ('/apps/metacity/general', gconf.CLIENT_PRELOAD_RECURSIVE)
2008-01-06 09:31:37 +00:00
self.gconf_client.notify_add ('/apps/metacity/general/focus_mode', self.on_gconf_notification)
2007-07-28 18:36:51 +00:00
self.clipboard = gtk.clipboard_get (gtk.gdk.SELECTION_CLIPBOARD)
2007-02-25 02:37:56 +00:00
2008-02-02 01:49:32 +00:00
self.scrollbar_position = self.reconf ('scrollbar_position')
2006-11-10 05:18:31 +00:00
self._vte = vte.Terminal ()
2007-11-09 04:43:05 +00:00
self._vte.set_size (80, 24)
2006-11-10 05:18:31 +00:00
self.reconfigure_vte ()
2007-02-24 14:53:10 +00:00
self._vte.show ()
2006-11-10 05:18:31 +00:00
2008-03-01 01:14:32 +00:00
self._termbox = gtk.HBox ()
self._termbox.show()
2008-03-02 11:46:21 +00:00
self._title = gtk.Label()
2008-03-02 12:44:08 +00:00
self._title.show()
self._titlebox = gtk.EventBox()
self._titlebox.add(self._title)
2008-03-02 11:46:21 +00:00
self._box = gtk.VBox ()
self._box.show()
2008-03-02 12:44:08 +00:00
self._box.pack_start(self._titlebox, False)
2008-03-02 11:46:21 +00:00
self._box.pack_start(self._termbox)
2008-04-12 12:10:34 +00:00
self._titlebox.show()
if not self.reconf('titlebars'):
self._title.set_property ('visible', False)
2008-03-01 01:14:32 +00:00
2006-11-10 05:18:31 +00:00
self._scrollbar = gtk.VScrollbar (self._vte.get_adjustment ())
2008-02-19 23:50:08 +00:00
if self.scrollbar_position != "hidden" and self.scrollbar_position != "disabled":
2007-02-24 14:53:10 +00:00
self._scrollbar.show ()
2006-11-10 05:18:31 +00:00
2008-02-02 01:49:32 +00:00
if self.scrollbar_position == 'right':
2008-03-01 01:14:32 +00:00
packfunc = self._termbox.pack_start
2008-02-02 01:49:32 +00:00
else:
2008-03-01 01:14:32 +00:00
packfunc = self._termbox.pack_end
2008-02-02 01:49:32 +00:00
packfunc (self._vte)
packfunc (self._scrollbar, False)
2008-03-02 13:17:56 +00:00
2007-10-13 23:11:29 +00:00
self._vte.connect ("key-press-event", self.on_vte_key_press)
2006-11-10 05:18:31 +00:00
self._vte.connect ("button-press-event", self.on_vte_button_press)
2006-11-17 07:23:04 +00:00
self._vte.connect ("popup-menu", self.on_vte_popup_menu)
2008-02-24 20:42:08 +00:00
"""drag and drop"""
2008-03-07 12:09:59 +00:00
srcvtetargets = [ ( "vte", gtk.TARGET_SAME_APP, TARGET_TYPE_VTE ) ]
dsttargets = [ ( "vte", gtk.TARGET_SAME_APP, TARGET_TYPE_VTE ), ('text/plain', 0, 0) , ("STRING", 0, 0), ("COMPOUND_TEXT", 0, 0)]
2008-03-06 19:06:23 +00:00
self._vte.drag_source_set( gtk.gdk.CONTROL_MASK | gtk.gdk.BUTTON3_MASK, srcvtetargets, gtk.gdk.ACTION_MOVE)
self._titlebox.drag_source_set( gtk.gdk.BUTTON1_MASK, srcvtetargets, gtk.gdk.ACTION_MOVE)
2008-03-06 14:56:30 +00:00
#self._vte.drag_dest_set(gtk.DEST_DEFAULT_MOTION | gtk.DEST_DEFAULT_HIGHLIGHT |gtk.DEST_DEFAULT_DROP ,dsttargets, gtk.gdk.ACTION_MOVE)
self._vte.drag_dest_set(gtk.DEST_DEFAULT_MOTION | gtk.DEST_DEFAULT_HIGHLIGHT |gtk.DEST_DEFAULT_DROP ,dsttargets, gtk.gdk.ACTION_MOVE)
2008-03-06 20:00:08 +00:00
self._vte.connect("drag-begin", self.on_drag_begin, self)
self._titlebox.connect("drag-begin", self.on_drag_begin, self)
2008-02-24 20:42:08 +00:00
self._vte.connect("drag-data-get", self.on_drag_data_get, self)
2008-03-06 19:06:23 +00:00
self._titlebox.connect("drag-data-get", self.on_drag_data_get, self)
2008-02-25 00:39:09 +00:00
#for testing purpose: drag-motion
2008-03-04 19:08:31 +00:00
self._vte.connect("drag-motion", self.on_drag_motion, self)
2008-02-24 20:42:08 +00:00
self._vte.connect("drag-data-received", self.on_drag_data_received, self)
2008-02-26 18:30:21 +00:00
2008-02-24 16:44:51 +00:00
self._vte.connect ("composited-changed", self.on_composited_changed)
2008-02-25 01:03:39 +00:00
2008-03-01 01:14:32 +00:00
self._vte.connect ("window-title-changed", self.on_vte_title_change)
2008-03-02 11:46:21 +00:00
self._vte.connect ("grab-focus", self.on_vte_focus)
2008-03-02 12:44:08 +00:00
self._vte.connect ("focus-out-event", self.on_vte_focus_out)
self._vte.connect ("focus-in-event", self.on_vte_focus_in)
2007-07-28 01:17:37 +00:00
2008-02-24 20:42:08 +00:00
2007-11-09 05:58:47 +00:00
exit_action = self.gconf_client.get_string (self.profile + "/exit_action")
2008-02-24 23:46:56 +00:00
exit_action = self.reconf ("exit_action")
2007-11-09 05:58:47 +00:00
if exit_action == "restart":
2007-07-28 01:17:37 +00:00
self._vte.connect ("child-exited", self.spawn_child)
2008-02-24 23:46:56 +00:00
# We need to support "left" because some buggy versions of gnome-terminal
# set it in some situations
2008-02-24 20:42:08 +00:00
if exit_action in ("close", "left"):
2008-01-29 13:54:16 +00:00
self._vte.connect ("child-exited", lambda close_term: self.terminator.closeterm (self))
2006-11-10 05:18:31 +00:00
2007-05-08 23:15:26 +00:00
self._vte.add_events (gtk.gdk.ENTER_NOTIFY_MASK)
self._vte.connect ("enter_notify_event", self.on_vte_notify_enter)
2006-11-14 08:04:06 +00:00
2008-02-17 01:28:18 +00:00
self.matches['full_uri'] = self._vte.match_add ('''\<(news:|telnet:|nntp:|file:/|https?:|ftps?:|webcal:)//([-A-Za-z0-9]+(:[-A-Za-z0-9,?;.:/!%$^*&~"#']+)?@)?[-A-Za-z0-9.]+(:[0-9]+)?(/[-A-Za-z0-9_$.+!*(),;:@&=?/~#%]*[^]'.}>) \t\r\n,\"])?\>/?''')
self.matches['addr_only'] = self._vte.match_add ('''\<(www|ftp)[-A-Za-z0-9]*\.[-A-Za-z0-9.]+(:[0-9]+)?(/[-A-Za-z0-9_$.+!*(),;:@&=?/~#%]*[^]'.}>) \t\r\n,\"])?\>/?''')
self.matches['email'] = self._vte.match_add ('''\<(mailto:)?[a-z0-9][a-z0-9.-]*@[a-z0-9][a-z0-9-]*(\.[a-z0-9][a-z0-9-]*)+\>''')
self.matches['nntp'] = self._vte.match_add ('''\<news:[-A-Z\^_a-z{|}~!"#$%&'()*+,./0-9;:=?`]+@[-A-Za-z0-9.]+(:[0-9]+)?\>''')
2007-02-24 14:53:10 +00:00
2008-01-14 17:04:13 +00:00
self.spawn_child ()
2008-02-24 20:42:08 +00:00
2008-03-06 20:00:08 +00:00
def on_drag_begin(self, widget, drag_context, data):
if os.path.exists("/usr/share/icons/hicolor/48x48/apps/terminator.png"):
widget.drag_source_set_icon_pixbuf( gtk.gdk.pixbuf_new_from_file("/usr/share/icons/hicolor/48x48/apps/terminator.png"))
2008-02-24 20:42:08 +00:00
def on_drag_data_get(self,widget, drag_context, selection_data, info, time, data):
print "Drag data get"
2008-02-25 20:41:36 +00:00
selection_data.set("vte",info, str(data.terminator.term_list.index (self)))
2008-03-06 20:00:08 +00:00
2008-02-25 20:41:36 +00:00
def on_drag_motion(self, widget, drag_context, x, y, time, data):
2008-03-04 19:08:31 +00:00
#print "Drag Motion on ",
2008-03-06 14:54:31 +00:00
"""
x-special/gnome-icon-list
text/uri-list
UTF8_STRING
COMPOUND_TEXT
TEXT
STRING
text/plain;charset=utf-8
text/plain;charset=UTF-8
text/plain
"""
2008-03-06 14:56:30 +00:00
#print "motion"
2008-03-06 14:54:31 +00:00
if 'text/plain' in drag_context.targets:
#copy text from another widget
return
2008-03-04 20:25:51 +00:00
srcwidget = drag_context.get_source_widget()
2008-03-06 19:06:23 +00:00
if (isinstance(srcwidget, gtk.EventBox) and srcwidget == self._titlebox) or widget == srcwidget:
2008-03-04 20:25:51 +00:00
#on self
return
2008-03-05 16:28:20 +00:00
2008-03-04 20:25:51 +00:00
alloc = widget.allocation
rect = gtk.gdk.Rectangle(0, 0, alloc.width, alloc.height)
2008-03-04 19:08:31 +00:00
widget.window.invalidate_rect(rect, True)
widget.window.process_updates(True)
2008-03-04 20:25:51 +00:00
2008-03-04 19:08:31 +00:00
context = widget.window.cairo_create()
2008-03-04 20:25:51 +00:00
if self.gconf_client.get_bool (self.profile + "/use_theme_colors"):
color = self._vte.get_style ().text[gtk.STATE_NORMAL]
else:
color = gtk.gdk.color_parse (self.reconf ('foreground_color'))
2008-02-24 20:42:08 +00:00
2008-02-25 20:41:36 +00:00
2008-03-07 12:09:59 +00:00
context.set_source_rgba(color.red, color.green, color.blue, 0.5)
pos = self.get_location(widget, x, y)
2008-03-04 20:25:51 +00:00
topleft = (0,0)
topright = (alloc.width,0)
2008-03-05 16:28:20 +00:00
topmiddle = (alloc.width/2,0)
2008-03-04 20:25:51 +00:00
bottomleft = (0, alloc.height)
bottomright = (alloc.width,alloc.height)
2008-03-05 16:28:20 +00:00
bottommiddle = (alloc.width/2, alloc.height)
2008-03-04 20:25:51 +00:00
middle = (alloc.width/2, alloc.height/2)
2008-03-05 16:28:20 +00:00
middleleft = (0, alloc.height/2)
middleright = (alloc.width, alloc.height/2)
overlay_type = self.reconf ('overlay_type')
2008-03-04 19:08:31 +00:00
#print "%f %f %d %d" %(coef1, coef2, b1,b2)
2008-03-04 20:25:51 +00:00
coord = ()
2008-03-07 12:09:59 +00:00
if pos == "right":
2008-03-04 19:08:31 +00:00
#print "right"
2008-03-05 16:28:20 +00:00
if overlay_type == "triangle":
coord = (topright, middle, bottomright)
else:
coord = (topright, topmiddle, bottommiddle, bottomright)
2008-03-07 12:09:59 +00:00
if pos == "top":
2008-03-04 19:08:31 +00:00
#print "top"
2008-03-05 16:28:20 +00:00
if overlay_type == "triangle":
coord = (topleft, middle, topright)
else:
coord = (topleft, topright, middleright , middleleft)
2008-03-07 12:09:59 +00:00
if pos == "left":
2008-03-04 19:08:31 +00:00
#print "left"
2008-03-05 16:28:20 +00:00
if overlay_type == "triangle":
coord = (topleft, middle, bottomleft)
else:
coord = (topleft, topmiddle, bottommiddle, bottomleft)
2008-03-07 12:09:59 +00:00
if pos == "bottom":
2008-03-04 19:08:31 +00:00
#print "bottom"
2008-03-05 16:28:20 +00:00
if overlay_type == "triangle":
coord = (bottomleft, middle, bottomright)
else:
coord = (bottomleft, bottomright, middleright , middleleft)
2008-03-04 20:25:51 +00:00
if len(coord) > 0 :
2008-03-05 16:28:20 +00:00
context.move_to(coord[len(coord)-1][0],coord[len(coord)-1][1])
2008-03-04 20:25:51 +00:00
for i in coord:
context.line_to(i[0],i[1])
2008-03-04 19:08:31 +00:00
2008-03-04 20:25:51 +00:00
context.fill()
2008-02-25 20:41:36 +00:00
2008-02-24 20:42:08 +00:00
def on_drag_drop(self, widget, drag_context, x, y, time):
print "Drag Drop on",
parent = widget.get_parent()
print parent,
def on_drag_data_received(self, widget, drag_context, x, y, selection_data, info, time, data):
print "Drag Data Received on "
2008-03-06 14:54:31 +00:00
if selection_data.type == 'text/plain':
#copy text to destination
2008-03-06 20:00:08 +00:00
#print "%s %s" % (selection_data.type, selection_data.target)
2008-03-06 14:54:31 +00:00
txt = selection_data.data.strip()
if txt[0:7] == "file://":
2008-03-06 20:00:08 +00:00
txt = "'%s'" % txt[7:]
self._vte.feed_child(txt)
2008-03-06 14:54:31 +00:00
return
2008-03-06 20:00:08 +00:00
2008-02-24 20:42:08 +00:00
widgetsrc = data.terminator.term_list[int(selection_data.data)]
2008-03-04 20:25:51 +00:00
srcvte = drag_context.get_source_widget()
2008-02-25 00:39:09 +00:00
#check if computation requireds
2008-03-06 19:13:10 +00:00
if (isinstance(srcvte, gtk.EventBox) and srcvte == self._titlebox) or srcvte == widget:
2008-02-25 00:39:09 +00:00
print "on itself"
return
2008-03-04 20:25:51 +00:00
srchbox = widgetsrc.get_box()
dsthbox = widget.get_parent().get_parent()
2008-02-25 00:39:09 +00:00
dstpaned = dsthbox.get_parent()
srcpaned = srchbox.get_parent()
if isinstance(dstpaned, gtk.Window) and isinstance(srcpaned, gtk.Window):
print "Only one terminal"
return
2008-03-07 12:09:59 +00:00
pos = self.get_location(widget, x, y)
data.terminator.remove(widgetsrc)
data.terminator.add(self, widgetsrc,pos)
return
def get_location(self, vte, x, y):
pos = ""
2008-02-25 00:39:09 +00:00
#get the diagonales function for the receiving widget
2008-03-07 12:09:59 +00:00
coef1 = float(vte.allocation.height)/float(vte.allocation.width)
coef2 = -float(vte.allocation.height)/float(vte.allocation.width)
2008-02-24 20:42:08 +00:00
b1 = 0
2008-03-07 12:09:59 +00:00
b2 = vte.allocation.height
#determine position in rectangle
2008-02-25 00:39:09 +00:00
"""
--------
|\ /|
| \ / |
| \/ |
| /\ |
| / \ |
|/ \|
--------
"""
2008-02-24 20:42:08 +00:00
if (x*coef1 + b1 > y ) and (x*coef2 + b2 < y ):
pos = "right"
if (x*coef1 + b1 > y ) and (x*coef2 + b2 > y ):
pos = "top"
if (x*coef1 + b1 < y ) and (x*coef2 + b2 > y ):
pos = "left"
if (x*coef1 + b1 < y ) and (x*coef2 + b2 < y ):
pos = "bottom"
2008-03-07 12:09:59 +00:00
return pos
2008-02-24 20:42:08 +00:00
2008-01-14 17:04:13 +00:00
def spawn_child (self, event=None):
2007-08-26 23:47:42 +00:00
update_records = self.gconf_client.get_bool (self.profile + "/update_records") or True
login = self.gconf_client.get_bool (self.profile + "/login_shell") or False
2008-01-28 04:27:18 +00:00
if self.command:
2008-02-12 22:06:19 +00:00
args = self.command
shell = self.command[0]
2008-01-28 04:27:18 +00:00
elif self.gconf_client.get_bool (self.profile + "/use_custom_command") == True:
2007-08-26 23:47:42 +00:00
args = self.gconf_client.get_string (self.profile + "/custom_command").split ()
2007-08-31 19:27:27 +00:00
shell = args[0]
2007-07-28 01:17:37 +00:00
else:
2007-08-26 23:24:32 +00:00
shell = pwd.getpwuid (os.getuid ())[6]
2007-08-26 23:47:42 +00:00
args = [os.path.basename (shell)]
2008-02-24 22:33:26 +00:00
self._pid = self._vte.fork_command (command = shell, argv = args, envv = [], directory=self.cwd, loglastlog = login, logwtmp = update_records, logutmp = update_records)
def get_cwd (self):
""" Return the current working directory of the subprocess.
2008-02-25 00:00:10 +00:00
This function requires OS specific behaviours
2008-02-24 22:33:26 +00:00
"""
2008-02-25 00:00:10 +00:00
system = platform.system ()
if system == 'Linux':
cwd = os.path.realpath ('/proc/%s/cwd' % self._pid)
else:
# We don't have a child cwd getter for this platform, so let
# TerminatorTerm use its default
cwd = None
return (cwd)
2006-11-10 05:18:31 +00:00
2008-01-29 15:18:33 +00:00
def reconf (self, property):
value = self.gconf_client.get ('%s/%s'%(self.profile, property))
ret = None
if not value:
try:
ret = self.defaults[property]
except:
pass
else:
if value.type == gconf.VALUE_STRING:
ret = value.get_string ()
elif value.type == gconf.VALUE_INT:
ret = value.get_int ()
elif value.type == gconf.VALUE_FLOAT:
ret = value.get_float ()
elif value.type == gconf.VALUE_BOOL:
ret = value.get_bool ()
2008-02-26 19:57:22 +00:00
2008-01-29 15:18:33 +00:00
if ret == None:
2008-02-12 23:11:04 +00:00
print >> sys.stderr, _('Unknown value requested. Unable to find in gconf profile or default settings: ') + property
2008-02-25 09:50:06 +00:00
return (ret)
2008-01-29 15:18:33 +00:00
2006-11-10 05:18:31 +00:00
def reconfigure_vte (self):
2006-11-10 20:15:33 +00:00
# Set our emulation
2006-11-10 05:18:31 +00:00
self._vte.set_emulation (self.defaults['emulation'])
2007-07-16 22:59:17 +00:00
# Set our wordchars
2008-01-29 15:21:58 +00:00
self._vte.set_word_chars (self.reconf ('word_chars'))
2007-07-16 22:59:17 +00:00
# Set our mouselation
2007-12-16 17:00:53 +00:00
self._vte.set_mouse_autohide (self.defaults['mouse_autohide'])
2007-07-16 22:59:17 +00:00
# Set our compatibility
2008-01-29 15:21:58 +00:00
backspace = self.reconf ('backspace_binding')
delete = self.reconf ('delete_binding')
2007-07-28 00:44:32 +00:00
# Note, each of the 4 following comments should replace the line beneath it, but the python-vte bindings don't appear to support this constant, so the magic values are being assumed from the C enum :/
if backspace == "ascii-del":
# backbind = vte.ERASE_ASCII_BACKSPACE
backbind = 2
else:
# backbind = vte.ERASE_AUTO_BACKSPACE
backbind = 1
if delete == "escape-sequence":
# delbind = vte.ERASE_DELETE_SEQUENCE
delbind = 3
else:
# delbind = vte.ERASE_AUTO
delbind = 0
self._vte.set_backspace_binding (backbind)
self._vte.set_delete_binding (delbind)
2007-07-16 22:59:17 +00:00
2006-11-10 20:15:33 +00:00
# Set our font, preferably from gconf settings
2007-02-24 14:53:10 +00:00
if self.gconf_client.get_bool (self.profile + "/use_system_font"):
2008-01-31 22:15:23 +00:00
font_name = (self.gconf_client.get_string ("/desktop/gnome/interface/monospace_font_name") or self.defaults['font'])
2006-11-10 05:18:31 +00:00
else:
2008-01-31 22:15:23 +00:00
font_name = self.reconf ('font')
2006-11-10 05:18:31 +00:00
try:
self._vte.set_font (pango.FontDescription (font_name))
except:
pass
2006-11-10 20:15:33 +00:00
# Set our boldness
2008-01-29 15:21:58 +00:00
self._vte.set_allow_bold (self.reconf ('allow_bold'))
2006-11-10 20:15:33 +00:00
# Set our color scheme, preferably from gconf settings
2008-02-17 23:08:27 +00:00
palette = self.reconf ('palette')
2008-02-17 22:59:43 +00:00
if self.gconf_client.get_bool (self.profile + "/use_theme_colors"):
2007-08-10 21:44:02 +00:00
fg_color = self._vte.get_style ().text[gtk.STATE_NORMAL]
bg_color = self._vte.get_style ().base[gtk.STATE_NORMAL]
2007-07-27 23:33:43 +00:00
else:
2008-01-29 15:21:58 +00:00
fg_color = gtk.gdk.color_parse (self.reconf ('foreground_color'))
bg_color = gtk.gdk.color_parse (self.reconf ('background_color'))
2008-01-08 09:08:23 +00:00
2007-07-27 22:31:58 +00:00
colors = palette.split (':')
palette = []
for color in colors:
2008-01-06 09:31:37 +00:00
if color:
palette.append (gtk.gdk.color_parse (color))
2007-07-27 23:33:43 +00:00
self._vte.set_colors (fg_color, bg_color, palette)
2006-11-10 20:15:33 +00:00
2008-02-25 11:02:02 +00:00
# Set our background image, transparency and type
# Many thanks to the authors of gnome-terminal, on which this code is based.
background_type = self.reconf ('background_type')
# set background image settings
if background_type == "image":
self._vte.set_background_image_file (self.reconf ('background_image'))
self._vte.set_scroll_background (self.reconf('scroll_background'))
else:
self._vte.set_background_image_file('')
self._vte.set_scroll_background(False)
# set transparency for the background (image)
if background_type in ("image", "transparent"):
self._vte.set_background_tint_color (bg_color)
self._vte.set_background_saturation(1 - (self.reconf ('background_darkness')))
self._vte.set_opacity(int(self.reconf('background_darkness') * 65535))
else:
self._vte.set_background_saturation(1)
self._vte.set_opacity(65535)
if not self._vte.is_composited():
self._vte.set_background_transparent (background_type == "transparent")
else:
self._vte.set_background_transparent (False)
2006-11-10 20:15:33 +00:00
# Set our cursor blinkiness
2008-01-29 15:21:58 +00:00
self._vte.set_cursor_blinks = (self.reconf ('cursor_blink'))
2006-11-10 20:15:33 +00:00
# Set our audible belliness
2008-02-12 23:11:04 +00:00
silent_bell = self.reconf ('silent_bell')
self._vte.set_audible_bell = not silent_bell
self._vte.set_visible_bell = silent_bell
2006-11-10 20:15:33 +00:00
# Set our scrolliness
2008-01-29 15:21:58 +00:00
self._vte.set_scrollback_lines (self.reconf ('scrollback_lines'))
self._vte.set_scroll_on_keystroke (self.reconf ('scroll_on_keystroke'))
self._vte.set_scroll_on_output (self.reconf ('scroll_on_output'))
2006-11-10 20:15:33 +00:00
2008-02-02 01:49:32 +00:00
scrollbar_position = self.reconf ('scrollbar_position')
if scrollbar_position != self.scrollbar_position:
2008-02-19 23:50:08 +00:00
if scrollbar_position == 'hidden' or scrollbar_position == 'disabled':
2008-02-02 01:49:32 +00:00
self._scrollbar.hide ()
2008-02-02 10:47:29 +00:00
else:
self._scrollbar.show ()
2008-02-02 01:49:32 +00:00
if scrollbar_position == 'right':
self._box.remove (self._scrollbar)
self._box.remove (self._vte)
self._box.pack_start (self._vte)
self._box.pack_start (self._scrollbar)
elif scrollbar_position == 'left':
self._box.remove (self._vte)
self._box.remove (self._scrollbar)
self._box.pack_start(self._scrollbar)
self._box.pack_start(self._vte)
self.scrollbar_position = scrollbar_position
2007-05-08 23:15:26 +00:00
# Set our sloppiness
self.focus = self.gconf_client.get_string ("/apps/metacity/general/focus_mode") or self.defaults['focus']
2006-11-10 05:18:31 +00:00
def on_gconf_notification (self, client, cnxn_id, entry, what):
self.reconfigure_vte ()
2008-02-24 16:44:51 +00:00
def on_composited_changed (self, widget):
self.reconfigure_vte ()
2006-11-10 05:18:31 +00:00
def on_vte_button_press (self, term, event):
2006-11-10 20:15:33 +00:00
# Left mouse button should transfer focus to this vte widget
2006-11-10 05:18:31 +00:00
if event.button == 1:
self._vte.grab_focus ()
2008-02-25 20:41:36 +00:00
return False
2008-02-24 20:42:08 +00:00
# Right mouse button should display a context menu if ctrl not pressed
if event.button == 3 and event.state & gtk.gdk.CONTROL_MASK == 0:
2006-11-17 07:23:04 +00:00
self.do_popup (event)
2006-11-10 05:18:31 +00:00
return True
2006-11-14 08:04:06 +00:00
def on_vte_notify_enter (self, term, event):
2007-05-08 23:15:26 +00:00
if (self.focus == "sloppy" or self.focus == "mouse"):
term.grab_focus ()
return False
2006-11-14 08:04:06 +00:00
2007-02-24 14:53:10 +00:00
def do_scrollbar_toggle (self):
if self._scrollbar.get_property ('visible'):
self._scrollbar.hide ()
else:
self._scrollbar.show ()
2008-03-01 01:14:32 +00:00
def do_title_toggle (self):
if self._title.get_property ('visible'):
self._title.hide ()
else:
self._title.show ()
2008-01-14 22:45:43 +00:00
#keybindings for the individual splited terminals (affects only the
#the selected terminal)
2007-10-13 23:11:29 +00:00
def on_vte_key_press (self, term, event):
keyname = gtk.gdk.keyval_name (event.keyval)
2008-01-14 22:45:43 +00:00
mask = gtk.gdk.CONTROL_MASK
if (event.state & mask) == mask:
if keyname == 'plus':
self.zoom (True)
return (True)
elif keyname == 'minus':
self.zoom (False)
return (True)
# bindings that should be moved to Terminator as they all just call
# a function of Terminator. It would be cleaner is TerminatorTerm
# has absolutely no reference to Terminator.
# N (next) - P (previous) - O (horizontal) - E (vertical) - W (close)
mask = gtk.gdk.CONTROL_MASK | gtk.gdk.SHIFT_MASK
2007-11-07 19:18:19 +00:00
if (event.state & mask) == mask:
2007-10-13 23:11:29 +00:00
if keyname == 'N':
2008-01-29 13:54:16 +00:00
self.terminator.go_next (self)
2007-10-13 23:11:29 +00:00
return (True)
elif keyname == "P":
2008-01-29 13:54:16 +00:00
self.terminator.go_prev (self)
2007-10-13 23:11:29 +00:00
return (True)
2008-01-05 00:47:57 +00:00
elif keyname == 'O':
2008-01-29 13:54:16 +00:00
self.terminator.splitaxis (self, False)
2007-10-13 23:11:29 +00:00
return (True)
2008-01-05 00:47:57 +00:00
elif keyname == 'E':
2008-01-29 13:54:16 +00:00
self.terminator.splitaxis (self, True)
2007-10-13 23:11:29 +00:00
return (True)
2007-12-28 10:48:26 +00:00
elif keyname == 'W':
2008-01-29 13:54:16 +00:00
self.terminator.closeterm (self)
2007-10-13 23:11:29 +00:00
return (True)
2008-01-05 00:50:56 +00:00
elif keyname == 'C':
self._vte.copy_clipboard ()
return (True)
elif keyname == 'V':
self._vte.paste_clipboard ()
return (True)
2008-02-22 23:39:59 +00:00
elif keyname == 'S':
self.do_scrollbar_toggle ()
return (True)
2008-03-08 20:13:22 +00:00
elif keyname == 'T':
self.terminator.newtab(self)
return (True)
2008-02-22 23:39:59 +00:00
elif keyname in ('Up', 'Down', 'Left', 'Right'):
self.terminator.resizeterm (self, keyname)
return (True)
2008-03-13 17:48:24 +00:00
mask = gtk.gdk.CONTROL_MASK
if (event.state & mask) == mask:
if keyname == 'Page_Down':
self.terminator.next_tab(self)
return (True)
elif keyname == 'Page_Up':
self.terminator.previous_tab(self)
return (True)
2007-11-29 17:16:04 +00:00
if keyname and (keyname == 'Tab' or keyname.endswith('_Tab')):
2007-11-16 00:19:05 +00:00
if event.state == gtk.gdk.CONTROL_MASK:
2008-01-29 13:54:16 +00:00
self.terminator.go_next (self)
2007-11-16 00:19:05 +00:00
return (True)
if (event.state & mask) == mask:
2008-01-29 13:54:16 +00:00
self.terminator.go_prev (self)
2007-11-16 00:19:05 +00:00
return (True)
2007-10-13 23:11:29 +00:00
return (False)
2008-01-06 00:00:22 +00:00
def zoom (self, zoom_in):
pangodesc = self._vte.get_font ()
2008-01-06 14:33:35 +00:00
fontsize = pangodesc.get_size ()
2008-01-06 00:00:22 +00:00
2008-01-06 14:33:35 +00:00
if fontsize > pango.SCALE and not zoom_in:
fontsize -= pango.SCALE
2008-01-06 00:00:22 +00:00
elif zoom_in:
2008-01-06 14:33:35 +00:00
fontsize += pango.SCALE
2008-01-06 00:00:22 +00:00
2008-01-06 14:33:35 +00:00
pangodesc.set_size (fontsize)
2008-01-06 00:00:22 +00:00
self._vte.set_font (pangodesc)
2006-11-17 07:23:04 +00:00
def on_vte_popup_menu (self, term):
self.do_popup ()
def do_popup (self, event = None):
2007-02-24 14:53:10 +00:00
menu = self.create_popup_menu (event)
2006-11-17 07:23:04 +00:00
menu.popup (None, None, None, event.button, event.time)
2007-02-24 14:53:10 +00:00
def create_popup_menu (self, event):
2006-11-17 07:23:04 +00:00
menu = gtk.Menu ()
2008-02-12 23:22:25 +00:00
url = None
if event:
url = self._vte.match_check (int (event.x / self._vte.get_char_width ()), int (event.y / self._vte.get_char_height ()))
2006-11-17 07:23:04 +00:00
2007-02-25 02:37:56 +00:00
if url:
2007-11-09 03:43:11 +00:00
if url[1] != self.matches['email']:
address = url[0]
2007-12-29 03:01:28 +00:00
nameopen = _("_Open Link")
namecopy = _("_Copy Link Address")
2007-11-09 03:43:11 +00:00
else:
2007-11-09 04:36:01 +00:00
if url[0][0:7] != "mailto:":
2007-11-09 03:43:11 +00:00
address = "mailto:" + url[0]
else:
address = url[0]
2007-12-29 03:01:28 +00:00
nameopen = _("_Send Mail To...")
namecopy = _("_Copy Email Address")
2007-11-09 03:43:11 +00:00
item = gtk.MenuItem (nameopen)
2008-02-17 01:28:18 +00:00
item.connect ("activate", lambda menu_item: openurl (address))
2007-02-25 02:37:56 +00:00
menu.append (item)
2007-11-09 03:43:11 +00:00
item = gtk.MenuItem (namecopy)
2007-02-25 03:30:29 +00:00
item.connect ("activate", lambda menu_item: self.clipboard.set_text (url[0]))
menu.append (item)
2007-02-25 02:37:56 +00:00
2007-02-25 03:30:29 +00:00
item = gtk.MenuItem ()
2007-02-25 02:37:56 +00:00
menu.append (item)
2006-11-17 07:23:04 +00:00
2007-02-25 03:30:29 +00:00
item = gtk.ImageMenuItem (gtk.STOCK_COPY)
item.connect ("activate", lambda menu_item: self._vte.copy_clipboard ())
item.set_sensitive (self._vte.get_has_selection ())
menu.append (item)
2006-11-17 07:23:04 +00:00
item = gtk.ImageMenuItem (gtk.STOCK_PASTE)
item.connect ("activate", lambda menu_item: self._vte.paste_clipboard ())
menu.append (item)
2007-02-25 03:30:29 +00:00
item = gtk.MenuItem ()
menu.append (item)
2008-02-22 23:39:59 +00:00
item = gtk.CheckMenuItem (_("Show _scrollbar"))
2007-02-24 14:53:10 +00:00
item.set_active (self._scrollbar.get_property ('visible'))
item.connect ("toggled", lambda menu_item: self.do_scrollbar_toggle ())
menu.append (item)
2008-03-01 01:14:32 +00:00
item = gtk.CheckMenuItem (_("Show Title"))
item.set_active (self._title.get_property ('visible'))
item.connect ("toggled", lambda menu_item: self.do_title_toggle ())
menu.append (item)
2007-02-24 14:53:10 +00:00
2007-07-29 02:06:52 +00:00
item = gtk.MenuItem ()
menu.append (item)
2008-01-05 00:47:57 +00:00
item = gtk.MenuItem (_("Split H_orizontally"))
2008-01-29 13:54:16 +00:00
item.connect ("activate", lambda menu_item: self.terminator.splitaxis (self, False))
2007-07-29 02:06:52 +00:00
menu.append (item)
2008-01-05 00:47:57 +00:00
item = gtk.MenuItem (_("Split V_ertically"))
2008-01-29 13:54:16 +00:00
item.connect ("activate", lambda menu_item: self.terminator.splitaxis (self, True))
2007-07-29 02:06:52 +00:00
menu.append (item)
2008-03-08 20:13:22 +00:00
item = gtk.MenuItem (_("Open _Tab"))
item.connect ("activate", lambda menu_item: self.terminator.newtab (self))
menu.append (item)
2007-07-29 02:06:52 +00:00
2007-07-29 15:41:59 +00:00
item = gtk.MenuItem ()
menu.append (item)
item = gtk.ImageMenuItem (gtk.STOCK_CLOSE)
2008-01-29 13:54:16 +00:00
item.connect ("activate", lambda menu_item: self.terminator.closeterm (self))
2007-07-29 15:41:59 +00:00
menu.append (item)
2006-11-17 07:23:04 +00:00
menu.show_all ()
return menu
2008-01-06 02:28:21 +00:00
def on_vte_title_change(self, vte):
2008-03-02 12:11:34 +00:00
if self.reconf ('titletips'):
2008-03-01 01:14:32 +00:00
vte.set_property ("has-tooltip", True)
vte.set_property ("tooltip-text", vte.get_window_title ())
2008-03-02 11:46:21 +00:00
#set the title anyhow, titlebars setting only show/hide the label
self._title.set_text(vte.get_window_title ())
2008-03-08 20:13:22 +00:00
self.terminator.set_window_title("Terminator: %s" % vte.get_window_title ())
notebookpage = self.terminator.get_first_notebook_page(vte)
while notebookpage != None:
notebookpage[0].set_tab_label_text(notebookpage[1], vte.get_window_title ())
notebookpage = self.terminator.get_first_notebook_page(notebookpage[0])
2008-01-06 02:28:21 +00:00
2008-03-02 12:44:08 +00:00
def on_vte_focus_in(self, vte, event):
self._titlebox.modify_bg(gtk.STATE_NORMAL,self.terminator.window.get_style().bg[gtk.STATE_SELECTED])
self._title.modify_fg(gtk.STATE_NORMAL, self.terminator.window.get_style().fg[gtk.STATE_SELECTED])
return
def on_vte_focus_out(self, vte, event):
self._titlebox.modify_bg(gtk.STATE_NORMAL, self.terminator.window.get_style().bg[gtk.STATE_NORMAL])
self._title.modify_fg(gtk.STATE_NORMAL, self.terminator.window.get_style().fg[gtk.STATE_NORMAL])
return
2008-01-06 02:28:21 +00:00
2008-03-02 11:46:21 +00:00
def on_vte_focus(self, vte):
if vte.get_window_title ():
2008-03-08 20:13:22 +00:00
self.terminator.set_window_title("Terminator: %s" % vte.get_window_title ())
notebookpage = self.terminator.get_first_notebook_page(vte)
while notebookpage != None:
notebookpage[0].set_tab_label_text(notebookpage[1], vte.get_window_title ())
notebookpage = self.terminator.get_first_notebook_page(notebookpage[0])
2008-03-02 12:11:34 +00:00
2006-11-10 05:18:31 +00:00
def get_box (self):
return self._box
2008-02-27 20:11:47 +00:00
def destroy(self):
self.get_box().destroy()
self._vte.destroy()
2006-11-10 05:18:31 +00:00
class Terminator:
2008-03-01 00:09:49 +00:00
def __init__ (self, profile, command = None, fullscreen = False, maximise = False, borderless = False):
2007-08-31 19:27:27 +00:00
self.profile = profile
2006-11-14 08:04:06 +00:00
self.gconf_client = gconf.client_get_default ()
2008-01-28 04:27:18 +00:00
self.command = command
2006-11-14 08:04:06 +00:00
2007-12-27 17:44:38 +00:00
self._fullscreen = False
2006-11-10 05:18:31 +00:00
self.window = gtk.Window ()
2008-01-05 01:05:41 +00:00
self.window.set_title ("Terminator")
2008-02-08 11:05:19 +00:00
# FIXME: This really shouldn't be a hardcoded path
try:
self.window.set_icon_from_file ("/usr/share/icons/hicolor/48x48/apps/terminator.png")
except:
self.icon = self.window.render_icon (gtk.STOCK_DIALOG_INFO, gtk.ICON_SIZE_BUTTON)
self.window.set_icon (self.icon)
2007-12-27 17:44:38 +00:00
self.window.connect ("key-press-event", self.on_key_press)
2006-11-10 05:18:31 +00:00
self.window.connect ("delete_event", self.on_delete_event)
self.window.connect ("destroy", self.on_destroy_event)
2007-11-09 03:19:25 +00:00
2007-11-07 19:57:56 +00:00
self.window.set_property ('allow-shrink', True)
2006-11-10 05:18:31 +00:00
2008-03-01 00:09:49 +00:00
if fullscreen:
self.fullscreen_toggle ()
if maximise:
self.maximize ()
if borderless:
self.window.set_decorated (False)
2008-02-23 21:54:39 +00:00
# Set RGBA colormap if possible so VTE can use real alpha
# channels for transparency.
screen = self.window.get_screen()
colormap = screen.get_rgba_colormap()
2008-02-24 16:44:51 +00:00
if colormap:
2008-02-23 21:54:39 +00:00
self.window.set_colormap(colormap)
2007-07-29 02:06:52 +00:00
# Start out with just one terminal
# FIXME: This should be really be decided from some kind of profile
2008-01-28 04:27:18 +00:00
term = (TerminatorTerm (self, self.profile, self.command))
2007-10-13 23:11:29 +00:00
self.term_list = [term]
2007-07-29 02:06:52 +00:00
self.window.add (term.get_box ())
2008-02-02 01:49:32 +00:00
self.window.show ()
2007-07-29 02:06:52 +00:00
2007-12-27 17:44:38 +00:00
def maximize (self):
""" Maximize the Terminator."""
self.window.maximize ()
def toggle_fullscreen (self):
""" Toggle the fullscreen state of the window. If it is in
fullscreen state, it will be unfullscreened. If it is not, it
will be set to fullscreen state.
"""
if self._fullscreen:
self.window.unfullscreen ()
else:
self.window.fullscreen ()
self._fullscreen = not self._fullscreen
2007-12-28 10:48:26 +00:00
def on_delete_event (self, window, event, data=None):
2007-11-07 19:38:26 +00:00
if len (self.term_list) == 1:
return False
2007-12-28 10:48:26 +00:00
# show dialog
2007-12-29 03:01:28 +00:00
dialog = gtk.Dialog (_("Close?"), window, gtk.DIALOG_MODAL,
2007-12-28 10:48:26 +00:00
(gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, gtk.STOCK_CLOSE, gtk.RESPONSE_ACCEPT))
dialog.set_has_separator (False)
dialog.set_resizable (False)
2007-12-29 03:01:28 +00:00
primairy = gtk.Label (_('<big><b>Close all terminals?</b></big>'))
2007-12-28 10:48:26 +00:00
primairy.set_use_markup (True)
primairy.set_alignment (0, 0.5)
2007-12-29 03:01:28 +00:00
secundairy = gtk.Label (_("This window has %s terminals open. Closing the window will also close all terminals.") % len(self.term_list))
2007-12-28 10:48:26 +00:00
secundairy.set_line_wrap(True)
primairy.set_alignment (0, 0.5)
labels = gtk.VBox ()
labels.pack_start (primairy, False, False, 6)
labels.pack_start (secundairy, False, False, 6)
image = gtk.image_new_from_stock(gtk.STOCK_DIALOG_WARNING, gtk.ICON_SIZE_DIALOG)
image.set_alignment (0.5, 0)
box = gtk.HBox()
box.pack_start (image, False, False, 6)
box.pack_start (labels, False, False, 6)
dialog.vbox.pack_start (box, False, False, 12)
dialog.show_all ()
result = dialog.run ()
2007-02-25 03:30:29 +00:00
dialog.destroy ()
2007-12-28 10:48:26 +00:00
return not (result == gtk.RESPONSE_ACCEPT)
2006-11-10 05:18:31 +00:00
def on_destroy_event (self, widget, data=None):
gtk.main_quit ()
2008-01-14 22:45:43 +00:00
# keybindings for the whole terminal window (affects the main
# windows containing the splited terminals)
2007-12-27 17:44:38 +00:00
def on_key_press (self, window, event):
""" Callback for the window to determine what to do with special
keys. Currently handled key-combo's:
2008-01-14 22:45:43 +00:00
* F11: toggle fullscreen state of the window.
* CTRL - SHIFT - Q: close all terminals
2007-12-27 17:44:38 +00:00
"""
keyname = gtk.gdk.keyval_name (event.keyval)
mask = gtk.gdk.CONTROL_MASK | gtk.gdk.SHIFT_MASK
2008-01-14 15:41:52 +00:00
if (keyname == 'F11'):
self.toggle_fullscreen ()
return (True)
2007-12-27 17:44:38 +00:00
if (event.state & mask) == mask:
2007-12-29 02:05:11 +00:00
if keyname == 'Q':
if not self.on_delete_event (window, gtk.gdk.Event (gtk.gdk.DELETE)):
self.on_destroy_event (window, gtk.gdk.Event (gtk.gdk.DESTROY))
2007-12-27 17:44:38 +00:00
2008-03-02 11:46:21 +00:00
def set_window_title(self, title):
2008-03-02 13:17:56 +00:00
"""
Modifies Terminator window title
"""
2008-03-02 11:46:21 +00:00
self.window.set_title(title)
2008-02-27 20:27:29 +00:00
def add(self, widget, terminal, pos = "bottom"):
"""
Add a term to another at position pos
"""
2008-02-27 20:11:47 +00:00
vertical = pos in ("top", "bottom")
2008-01-14 22:45:43 +00:00
pane = (vertical) and gtk.VPaned () or gtk.HPaned ()
# get the parent of the provided terminal
parent = widget.get_box ().get_parent ()
2007-08-10 21:35:06 +00:00
if isinstance (parent, gtk.Window):
# We have just one term
widget.get_box ().reparent (pane)
2008-02-27 20:11:47 +00:00
if pos in ("top", "left"):
pane.remove(widget.get_box ())
pane.pack1 (terminal.get_box (), True, True)
pane.pack2 (widget.get_box (), True, True)
else:
pane.pack1 (widget.get_box (), True, True)
pane.pack2 (terminal.get_box (), True, True)
2007-08-10 23:33:00 +00:00
parent.add (pane)
2008-01-14 22:45:43 +00:00
2008-02-25 09:50:06 +00:00
position = (vertical) and parent.allocation.height \
or parent.allocation.width
2007-08-10 21:35:06 +00:00
2008-03-08 20:13:22 +00:00
if isinstance (parent, gtk.Notebook):
page = -1
for i in range(0, parent.get_n_pages()):
if parent.get_nth_page(i) == widget.get_box():
page = i
break
widget.get_box ().reparent (pane)
if pos in ("top", "left"):
pane.remove(widget.get_box ())
pane.pack1 (terminal.get_box (), True, True)
pane.pack2 (widget.get_box (), True, True)
else:
pane.pack1 (widget.get_box (), True, True)
pane.pack2 (terminal.get_box (), True, True)
#parent.remove_page(page)
pane.show()
parent.insert_page(pane, None, page)
parent.set_tab_label_text(pane, widget._vte.get_window_title())
parent.set_tab_label_packing(pane, True, True, gtk.PACK_START)
parent.set_current_page(page)
position = (vertical) and parent.allocation.height \
or parent.allocation.width
2007-08-10 21:35:06 +00:00
if isinstance (parent, gtk.Paned):
# We are inside a split term
2008-02-25 09:50:06 +00:00
position = (vertical) and widget.get_box().allocation.height \
or widget.get_box().allocation.width
2007-08-10 21:35:06 +00:00
if (widget.get_box () == parent.get_child1 ()):
widget.get_box ().reparent (pane)
2007-11-07 19:05:15 +00:00
parent.pack1 (pane, True, True)
2007-08-10 21:35:06 +00:00
else:
widget.get_box ().reparent (pane)
2007-11-07 19:05:15 +00:00
parent.pack2 (pane, True, True)
2008-02-27 20:11:47 +00:00
if pos in ("top", "left"):
pane.remove(widget.get_box ())
pane.pack1 (terminal.get_box (), True, True)
pane.pack2 (widget.get_box (), True, True)
else:
pane.pack1 (widget.get_box (), True, True)
pane.pack2 (terminal.get_box (), True, True)
2007-11-07 19:05:15 +00:00
pane.pack1 (widget.get_box (), True, True)
2008-01-14 22:45:43 +00:00
pane.pack2 (terminal.get_box (), True, True)
2007-08-10 21:35:06 +00:00
2008-01-14 22:45:43 +00:00
# show all, set position of the divider
2007-12-16 16:49:49 +00:00
pane.show ()
2008-01-14 22:45:43 +00:00
pane.set_position (position / 2)
terminal.get_box ().show ()
2007-10-13 23:11:29 +00:00
# insert the term reference into the list
index = self.term_list.index (widget)
2008-01-14 22:45:43 +00:00
self.term_list.insert (index + 1, terminal)
# make the new terminal grab the focus
terminal._vte.grab_focus ()
2007-10-13 23:11:29 +00:00
2008-01-14 22:45:43 +00:00
return (terminal)
2008-03-13 17:48:24 +00:00
def on_page_reordered(self, notebook, child, page_num):
print page_num
2008-03-08 20:13:22 +00:00
def newtab(self,widget):
terminal = TerminatorTerm (self, self.profile, None, widget.get_cwd())
widgetbox = widget.get_box ()
parent = widgetbox.get_parent ()
2008-03-13 17:48:24 +00:00
if isinstance(parent, gtk.Paned) or isinstance(parent, gtk.Window):
2008-03-08 20:13:22 +00:00
#no notebook yet.
notebook = gtk.Notebook()
2008-03-13 17:48:24 +00:00
notebook.connect('page-reordered',self.on_page_reordered)
2008-03-08 20:13:22 +00:00
notebook.set_property('homogeneous', True)
2008-03-13 17:48:24 +00:00
notebook.set_tab_reorderable(widgetbox, True)
2008-04-08 19:35:48 +00:00
2008-03-13 17:48:24 +00:00
if isinstance(parent, gtk.Paned):
if parent.get_child1() == widgetbox:
widgetbox.reparent(notebook)
parent.pack1(notebook)
else:
widgetbox.reparent(notebook)
parent.pack2(notebook)
elif isinstance(parent, gtk.Window):
widgetbox.reparent(notebook)
parent.add(notebook)
notebook.set_tab_reorderable(widgetbox,True)
2008-04-08 19:35:48 +00:00
notebooklabel = ""
if widget._vte.get_window_title() is not None:
notebooklabel = widget._vte.get_window_title()
notebook.set_tab_label_text(widgetbox, notebooklabel)
2008-03-08 20:13:22 +00:00
notebook. set_tab_label_packing(widgetbox, True, True, gtk.PACK_START)
2008-03-13 17:48:24 +00:00
notebook.show()
2008-03-08 20:13:22 +00:00
elif isinstance(parent, gtk.Notebook):
notebook = parent
else:
return (False)
notebook.append_page(terminal.get_box(),terminal._vte.get_window_title())
notebook. set_tab_label_packing(terminal.get_box(), True, True, gtk.PACK_START)
2008-03-13 17:48:24 +00:00
notebook.set_tab_reorderable(terminal.get_box(),True)
2008-03-08 20:13:22 +00:00
notebook.set_current_page(-1)
index = self.term_list.index(widget)
self.term_list.insert (index + 1, terminal)
terminal.get_box ().show ()
terminal._vte.grab_focus ()
return (True)
2008-02-27 20:11:47 +00:00
2008-03-08 20:13:22 +00:00
return terminal
2008-02-27 20:11:47 +00:00
def splitaxis (self, widget, vertical=True):
""" Split the provided widget on the horizontal or vertical axis. """
# create a new terminal and parent pane.
terminal = TerminatorTerm (self, self.profile, None, widget.get_cwd())
pos = vertical and "bottom" or "right"
self.add(widget, terminal, pos)
terminal.get_box ().show ()
return terminal
def remove(self, widget):
2008-02-27 20:27:29 +00:00
"""Remove a TerminatorTerm from the Terminator view and terms list
Returns True on success, False on failure"""
2007-07-29 15:41:59 +00:00
parent = widget.get_box ().get_parent ()
sibling = None
if isinstance (parent, gtk.Window):
# We are the only term
if not self.on_delete_event (parent, gtk.gdk.Event (gtk.gdk.DELETE)):
self.on_destroy_event (parent, gtk.gdk.Event (gtk.gdk.DESTROY))
return
2007-12-16 16:49:49 +00:00
2007-07-29 15:41:59 +00:00
if isinstance (parent, gtk.Paned):
2007-12-29 10:18:53 +00:00
index = self.term_list.index (widget)
2007-07-29 15:41:59 +00:00
grandparent = parent.get_parent ()
# Discover sibling while all objects exist
if widget.get_box () == parent.get_child1 ():
sibling = parent.get_child2 ()
if widget.get_box () == parent.get_child2 ():
sibling = parent.get_child1 ()
if not sibling:
# something is wrong, give up
2008-02-12 23:11:04 +00:00
print >> sys.stderr, "Error: %s is not a child of %s"%(widget, parent)
2008-02-27 20:11:47 +00:00
return False
2007-07-29 15:41:59 +00:00
2008-02-27 20:11:47 +00:00
parent.remove(widget.get_box())
2008-03-08 20:13:22 +00:00
if isinstance(grandparent, gtk.Notebook):
page = -1
for i in range(0, grandparent.get_n_pages()):
if grandparent.get_nth_page(i) == parent:
page = i
break
parent.remove(sibling)
grandparent.remove_page(page)
grandparent.insert_page(sibling, None,page)
grandparent.set_tab_label_packing(sibling, True, True, gtk.PACK_START)
else:
grandparent.remove (parent)
sibling.reparent (grandparent)
grandparent.resize_children()
2007-12-28 10:48:26 +00:00
parent.destroy ()
2008-02-27 20:11:47 +00:00
self.term_list.remove (widget)
2007-12-29 10:18:53 +00:00
if not isinstance (sibling, gtk.Paned):
for term in self.term_list:
if term.get_box () == sibling:
term._vte.grab_focus ()
break
else:
if index == 0: index = 1
self.term_list[index - 1]._vte.grab_focus ()
2008-03-08 20:13:22 +00:00
elif isinstance (parent, gtk.Notebook):
parent.remove(widget.get_box())
nbpages = parent.get_n_pages()
index = self.term_list.index (widget)
self.term_list.remove (widget)
if nbpages == 1:
sibling = parent.get_nth_page(0)
parent.remove(sibling)
gdparent = parent.get_parent()
if isinstance(gdparent, gtk.Window):
gdparent.remove(parent)
gdparent.add(sibling)
elif isinstance(gdparent, gtk.Paned):
if gdparent.get_child1() == parent:
gdparent.remove(parent)
gdparent.pack1(sibling)
else:
gdparent.remove(parent)
gdparent.pack2(sibling)
parent.destroy()
if index == 0: index = 1
self.term_list[index - 1]._vte.grab_focus ()
2007-10-13 23:11:29 +00:00
2008-02-27 20:11:47 +00:00
return True
def closeterm (self, widget):
if self.remove(widget):
widget.destroy ()
return True
return False
2007-07-29 15:41:59 +00:00
2007-10-13 23:11:29 +00:00
def go_next (self, term):
current = self.term_list.index (term)
next = current
if current == len (self.term_list) - 1:
next = 0
else:
next += 1
2008-03-08 20:13:22 +00:00
nextterm = self.term_list[next]
if isinstance(nextterm.get_box().get_parent(), gtk.Notebook):
box = nextterm.get_box()
parent = box.get_parent()
for i in range(0, parent.get_n_pages()):
if box == parent.get_nth_page(i):
parent.set_current_page(i)
break
notebookpage = self.get_first_notebook_page(nextterm.get_box())
if notebookpage:
child = None
for i in range(0, notebookpage[0].get_n_pages()):
if notebookpage[0].get_nth_page(i) == notebookpage[1]:
notebookpage[0].set_current_page(i)
break
nextterm._vte.grab_focus ()
2007-10-13 23:11:29 +00:00
def go_prev (self, term):
current = self.term_list.index (term)
previous = current
if current == 0:
previous = len (self.term_list) - 1
else:
previous -= 1
2008-01-08 08:59:22 +00:00
#self.window.set_title(self.term_list[previous]._vte.get_window_title())
2008-03-08 20:13:22 +00:00
previousterm = self.term_list[previous]
if isinstance(previousterm.get_box().get_parent(), gtk.Notebook):
box = previousterm.get_box()
parent = box.get_parent()
for i in range(0, parent.get_n_pages()):
if box == parent.get_nth_page(i):
parent.set_current_page(i)
break
notebookpage = self.get_first_notebook_page(previousterm.get_box())
if notebookpage:
child = None
for i in range(0, notebookpage[0].get_n_pages()):
if notebookpage[0].get_nth_page(i) == notebookpage[1]:
notebookpage[0].set_current_page(i)
break
previousterm._vte.grab_focus ()
2008-02-22 23:39:59 +00:00
def resizeterm (self, widget, keyname):
vertical = False
if keyname in ('Up', 'Down'):
vertical = True
elif keyname in ('Left', 'Right'):
vertical = False
else:
return
2008-02-23 00:02:26 +00:00
parent = self.get_first_parent_paned(widget.get_box (),vertical)
2008-02-22 23:39:59 +00:00
if parent == None:
return
#We have a corresponding parent pane
#
#allocation = parent.get_allocation()
if keyname in ('Up', 'Down'):
maxi = parent.get_child1().get_allocation().height + parent.get_child2().get_allocation().height - 1
else:
maxi = parent.get_child1().get_allocation().width + parent.get_child2().get_allocation().width - 1
move = 10
if keyname in ('Up', 'Left'):
move = -10
move = max(2, parent.get_position() + move)
move = min(maxi, move)
parent.set_position(move)
2008-03-13 17:48:24 +00:00
def previous_tab(self, term):
notebook = self.get_first_parent_notebook(term.get_box())
notebook.prev_page()
return
def next_tab(self, term):
notebook = self.get_first_parent_notebook(term.get_box())
notebook.next_page()
return
def get_first_parent_notebook(self, widget):
if isinstance (widget, gtk.Window):
return None
parent = widget.get_parent()
if isinstance (parent, gtk.Notebook):
return parent
return self.get_first_parent_notebook(parent)
2008-02-22 23:39:59 +00:00
2008-02-23 00:02:26 +00:00
def get_first_parent_paned (self, widget, vertical = None):
"""This method returns the first parent pane of a widget.
if vertical is True returns the first VPaned
if vertical is False return the first Hpaned
if is None return the First Paned"""
2008-02-22 23:39:59 +00:00
if isinstance (widget, gtk.Window):
return None
parent = widget.get_parent()
2008-02-23 00:02:26 +00:00
if isinstance (parent, gtk.Paned) and vertical is None:
return parent
2008-02-22 23:39:59 +00:00
if isinstance (parent, gtk.VPaned) and vertical:
return parent
elif isinstance (parent, gtk.HPaned) and not vertical:
return parent
2008-02-23 00:02:26 +00:00
return self.get_first_parent_paned(parent, vertical)
2008-03-08 20:13:22 +00:00
def get_first_notebook_page(self, widget):
if isinstance (widget, gtk.Window):
return None
parent = widget.get_parent()
if isinstance (parent, gtk.Notebook):
page = -1
for i in range(0, parent.get_n_pages()):
if parent.get_nth_page(i) == widget:
return (parent, widget)
return self.get_first_notebook_page(parent)
2008-02-22 23:39:59 +00:00
2007-07-29 02:06:52 +00:00
if __name__ == '__main__':
2008-02-26 00:25:01 +00:00
try:
if (gconf):
pass
except:
# Install a fake gconf setup
gconf = fakegconf ()
2007-08-31 19:27:27 +00:00
2008-02-25 09:50:06 +00:00
def execute_cb (option, opt, value, parser):
2008-02-26 18:30:21 +00:00
assert value is None
value = []
while parser.rargs:
arg = parser.rargs[0]
value.append (arg)
del (parser.rargs[0])
setattr(parser.values, option.dest, value)
2007-08-31 19:27:27 +00:00
2008-02-25 09:50:06 +00:00
def execute_cb (option, opt, value, parser):
assert value is None
value = []
while parser.rargs:
arg = parser.rargs[0]
value.append (arg)
del (parser.rargs[0])
setattr(parser.values, option.dest, value)
2008-02-12 13:49:36 +00:00
usage = "usage: %prog [options]"
parser = OptionParser (usage)
parser.add_option ("-d", "--debug", action="store_true", dest="debug", help="Enable debugging information")
parser.add_option ("-m", "--maximise", action="store_true", dest="maximise", help="Open the Terminator window maximised")
parser.add_option ("-f", "--fullscreen", action="store_true", dest="fullscreen", help="Set the window into fullscreen mode")
parser.add_option ("-b", "--borderless", action="store_true", dest="borderless", help="Turn off the window's borders")
parser.add_option ("-p", "--profile", dest="profile", help="Specify a GNOME Terminal profile to emulate")
parser.add_option ("-e", "--command", dest="command", help="Execute the argument to this option inside the terminal")
parser.add_option ("-x", "--execute", dest="execute", action="callback", callback=execute_cb, help="Execute the remainder of the command line inside the terminal")
2008-02-26 00:25:01 +00:00
parser.add_option ("-g", "--no-gconf", dest="nogconf", action="store_true", help="Disable gconf usage, falling back on ~/.terminatorrc and defaults")
2008-02-12 13:49:36 +00:00
(options, args) = parser.parse_args ()
if len (args) != 0:
parser.error("Expecting zero additional arguments, found: %d"%len (args))
2008-02-12 22:06:19 +00:00
command = []
if (options.command):
command.append (options.command)
if (options.execute):
command = options.execute
2008-02-26 00:25:01 +00:00
if (options.nogconf):
del (gconf)
gconf = fakegconf ()
2008-02-12 22:06:19 +00:00
2008-03-01 00:09:49 +00:00
term = Terminator (options.profile, command, options.fullscreen, options.maximise, options.borderless)
2008-01-05 01:05:41 +00:00
2006-11-10 05:18:31 +00:00
gtk.main ()