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-03-29 01:36:23 +00:00
# Global defines
APP_NAME = 'terminator'
APP_VERSION = '0.9'
2008-01-03 08:10:39 +00:00
# import standard python libs
2008-06-09 17:11:32 +00:00
import os, platform, sys, string, time, math, subprocess
2008-02-12 13:49:36 +00:00
from optparse import OptionParser
2008-01-03 08:10:39 +00:00
2008-04-09 07:11:51 +00:00
try:
import gettext
gettext.install (APP_NAME)
except:
def _ (text):
return text
2008-01-03 08:10:39 +00:00
# import unix-lib
2007-08-26 23:24:32 +00:00
import pwd
2008-01-03 08:10:39 +00:00
2008-05-22 14:37:59 +00:00
TARGET_TYPE_VTE = 8
2008-04-02 21:10:32 +00:00
# import our configuration loader
2008-05-22 14:37:59 +00:00
from terminatorlib import config
from terminatorlib.config import dbg
#import encoding list
from terminatorlib.encoding import TerminatorEncoding
# Sort out cwd detection code, if available
pid_get_cwd = lambda pid: None
if platform.system() == 'FreeBSD':
try:
from terminatorlib import freebsd
pid_get_cwd = lambda pid: freebsd.get_process_cwd(pid)
2008-05-23 21:30:53 +00:00
dbg ('Using FreeBSD pid_get_cwd')
2008-05-22 14:37:59 +00:00
except:
2008-05-23 21:30:53 +00:00
dbg ('FreeBSD version too old for pid_get_cwd')
2008-05-22 14:37:59 +00:00
pass
2008-05-23 21:30:53 +00:00
elif platform.system() == 'Linux':
dbg ('Using Linux pid_get_cwd')
2008-05-22 14:37:59 +00:00
pid_get_cwd = lambda pid: os.path.realpath ('/proc/%s/cwd' % pid)
2008-05-23 21:30:53 +00:00
else:
dbg ('Unable to set a pid_get_cwd, unknown system: %s'%platform.system)
2008-02-26 00:25:01 +00:00
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:
2008-06-16 23:10:54 +00:00
err (_("You need to install the python bindings for " \
"gobject, gtk and pango to run Terminator."))
2008-01-03 08:10:39 +00:00
sys.exit(1)
2008-06-09 13:15:30 +00:00
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):
2008-06-09 17:11:32 +00:00
try:
2008-06-09 17:21:02 +00:00
if subprocess.call(["xdg-open", url]) != 0:
raise
2008-06-09 17:11:32 +00:00
except:
2008-06-09 13:15:30 +00:00
try:
url_show (url)
except:
pass
2008-02-17 01:28:18 +00:00
2008-05-20 15:21:34 +00:00
class TerminatorTerm (gtk.VBox):
2007-05-08 23:15:26 +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):
2008-05-20 15:21:34 +00:00
gtk.VBox.__init__ (self)
2008-01-29 13:54:16 +00:00
self.terminator = terminator
2008-04-02 21:10:32 +00:00
self.conf = terminator.conf
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-03-28 18:30:10 +00:00
if not os.path.exists(self.cwd) or not os.path.isdir(self.cwd):
2008-03-29 01:29:09 +00:00
self.cwd = pwd.getpwuid(os.getuid ())[5]
2008-02-24 22:33:26 +00:00
2007-07-28 18:36:51 +00:00
self.clipboard = gtk.clipboard_get (gtk.gdk.SELECTION_CLIPBOARD)
2008-04-02 21:10:32 +00:00
self.scrollbar_position = self.conf.scrollbar_position
2008-02-02 01:49:32 +00:00
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-13 16:41:25 +00:00
self._title.show()
self._titlebox = gtk.EventBox()
self._titlebox.add(self._title)
2008-05-20 15:21:34 +00:00
self.show()
self.pack_start(self._titlebox, False)
self.pack_start(self._termbox)
2008-05-29 12:25:46 +00:00
if self.conf.titlebars:
2008-03-13 16:41:25 +00:00
self._titlebox.show()
2008-03-02 12:11:34 +00:00
else:
2008-03-13 16:41:25 +00:00
self._titlebox.hide()
2008-04-02 21:10: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-05-29 12:25:46 +00:00
if self.scrollbar_position == 'left':
2008-03-01 01:14:32 +00:00
packfunc = self._termbox.pack_end
2008-05-29 12:25:46 +00:00
else:
packfunc = self._termbox.pack_start
2008-02-02 01:49:32 +00:00
packfunc (self._vte)
packfunc (self._scrollbar, False)
2008-05-22 14:37:59 +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-05-22 14:37:59 +00:00
"""drag and drop"""
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)]
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)
#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)
self._vte.connect("drag-begin", self.on_drag_begin, self)
self._titlebox.connect("drag-begin", self.on_drag_begin, self)
self._vte.connect("drag-data-get", self.on_drag_data_get, self)
self._titlebox.connect("drag-data-get", self.on_drag_data_get, self)
#for testing purpose: drag-motion
self._vte.connect("drag-motion", self.on_drag_motion, self)
self._vte.connect("drag-data-received", self.on_drag_data_received, self)
2008-02-24 16:44:51 +00:00
self._vte.connect ("composited-changed", self.on_composited_changed)
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-13 16:41:25 +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-04-02 21:10:32 +00:00
exit_action = self.conf.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-04-09 23:22:20 +00:00
elif 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-04-09 07:06:33 +00:00
self.add_matches()
2007-02-24 14:53:10 +00:00
2008-06-13 23:42:04 +00:00
dbg ('SEGBUG: Setting http_proxy')
2008-04-09 23:22:20 +00:00
env_proxy = os.getenv ('http_proxy')
2008-05-29 12:25:46 +00:00
if not env_proxy and self.conf.http_proxy and self.conf.http_proxy != '':
2008-04-09 23:22:20 +00:00
os.putenv ('http_proxy', self.conf.http_proxy)
2008-04-09 23:29:40 +00:00
2008-06-13 23:42:04 +00:00
dbg ('SEGBUG: Setting COLORTERM')
2008-05-20 15:21:34 +00:00
os.putenv ('COLORTERM', 'gnome-terminal')
2008-06-13 23:42:04 +00:00
dbg ('SEGBUG: TerminatorTerm __init__ complete')
2007-07-28 01:17:37 +00:00
2008-05-22 14:37:59 +00:00
def on_drag_begin(self, widget, drag_context, data):
dbg ('Drag begins')
2008-06-03 23:10:09 +00:00
widget.drag_source_set_icon_pixbuf(self.terminator.icon_theme.load_icon (APP_NAME, 48, 0))
2008-05-22 14:37:59 +00:00
def on_drag_data_get(self,widget, drag_context, selection_data, info, time, data):
dbg ("Drag data get")
selection_data.set("vte",info, str(data.terminator.term_list.index (self)))
def on_drag_motion(self, widget, drag_context, x, y, time, data):
dbg ("Drag Motion on ")
"""
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
"""
if 'text/plain' in drag_context.targets:
#copy text from another widget
return
srcwidget = drag_context.get_source_widget()
if (isinstance(srcwidget, gtk.EventBox) and srcwidget == self._titlebox) or widget == srcwidget:
#on self
return
alloc = widget.allocation
rect = gtk.gdk.Rectangle(0, 0, alloc.width, alloc.height)
widget.window.invalidate_rect(rect, True)
widget.window.process_updates(True)
context = widget.window.cairo_create()
if self.conf.use_theme_colors:
color = self._vte.get_style ().text[gtk.STATE_NORMAL]
else:
color = gtk.gdk.color_parse (self.conf.foreground_color)
context.set_source_rgba(color.red, color.green, color.blue, 0.5)
pos = self.get_location(widget, x, y)
topleft = (0,0)
topright = (alloc.width,0)
topmiddle = (alloc.width/2,0)
bottomleft = (0, alloc.height)
bottomright = (alloc.width,alloc.height)
bottommiddle = (alloc.width/2, alloc.height)
middle = (alloc.width/2, alloc.height/2)
middleleft = (0, alloc.height/2)
middleright = (alloc.width, alloc.height/2)
#print "%f %f %d %d" %(coef1, coef2, b1,b2)
coord = ()
if pos == "right":
coord = (topright, topmiddle, bottommiddle, bottomright)
if pos == "top":
coord = (topleft, topright, middleright , middleleft)
if pos == "left":
coord = (topleft, topmiddle, bottommiddle, bottomleft)
if pos == "bottom":
coord = (bottomleft, bottomright, middleright , middleleft)
if len(coord) > 0 :
context.move_to(coord[len(coord)-1][0],coord[len(coord)-1][1])
for i in coord:
context.line_to(i[0],i[1])
context.fill()
def on_drag_drop(self, widget, drag_context, x, y, time):
parent = widget.get_parent()
dbg ('Drag drop on %s'%parent)
def on_drag_data_received(self, widget, drag_context, x, y, selection_data, info, time, data):
dbg ("Drag Data Received")
if selection_data.type == 'text/plain':
#copy text to destination
#print "%s %s" % (selection_data.type, selection_data.target)
txt = selection_data.data.strip()
if txt[0:7] == "file://":
txt = "'%s'" % txt[7:]
self._vte.feed_child(txt)
return
widgetsrc = data.terminator.term_list[int(selection_data.data)]
srcvte = drag_context.get_source_widget()
#check if computation requireds
if (isinstance(srcvte, gtk.EventBox) and srcvte == self._titlebox) or srcvte == widget:
dbg (" on itself")
return
srchbox = widgetsrc
dsthbox = widget.get_parent().get_parent()
dstpaned = dsthbox.get_parent()
srcpaned = srchbox.get_parent()
if isinstance(dstpaned, gtk.Window) and isinstance(srcpaned, gtk.Window):
dbg (" Only one terminal")
return
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 = ""
#get the diagonales function for the receiving widget
coef1 = float(vte.allocation.height)/float(vte.allocation.width)
coef2 = -float(vte.allocation.height)/float(vte.allocation.width)
b1 = 0
b2 = vte.allocation.height
#determine position in rectangle
"""
--------
|\ /|
| \ / |
| \/ |
| /\ |
| / \ |
|/ \|
--------
"""
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"
return pos
2008-05-23 21:30:53 +00:00
2008-04-09 07:06:33 +00:00
def add_matches (self, lboundry="[[:<:]]", rboundry="[[:>:]]"):
userchars = "-A-Za-z0-9"
passchars = "-A-Za-z0-9,?;.:/!%$^*&~\"#'"
hostchars = "-A-Za-z0-9"
2008-06-05 11:32:36 +00:00
pathchars = "-A-Za-z0-9_$.+!*(),;:@&=?/~#%'"
2008-04-09 07:06:33 +00:00
schemes = "(news:|telnet:|nntp:|file:/|https?:|ftps?:|webcal:)"
user = "[" + userchars + "]+(:[" + passchars + "]+)?"
urlpath = "/[" + pathchars + "]*[^]'.}>) \t\r\n,\\\"]"
self.matches['full_uri'] = self._vte.match_add(lboundry + schemes + "//(" + user + "@)?[" + hostchars +".]+(:[0-9]+)?(" + urlpath + ")?" + rboundry + "/?")
2007-02-24 14:53:10 +00:00
2008-04-09 07:06:33 +00:00
# FreeBSD works with [[:<:]], Linux works with \<
if self.matches['full_uri'] == -1:
if lboundry != "\\<":
self.add_matches(lboundry = "\\<", rboundry = "\\>")
else:
self.matches['addr_only'] = self._vte.match_add (lboundry + "(www|ftp)[" + hostchars + "]*\.[" + hostchars + ".]+(:[0-9]+)?(" + urlpath + ")?" + rboundry + "/?")
self.matches['email'] = self._vte.match_add (lboundry + "(mailto:)?[a-z0-9][a-z0-9.-]*@[a-z0-9][a-z0-9-]*(\.[a-z0-9][a-z0-9-]*)+" + rboundry)
self.matches['nntp'] = self._vte.match_add (lboundry + '''news:[-A-Z\^_a-z{|}~!"#$%&'()*+,./0-9;:=?`]+@[-A-Za-z0-9.]+(:[0-9]+)?''' + rboundry)
2007-07-28 01:17:37 +00:00
2008-01-14 17:04:13 +00:00
def spawn_child (self, event=None):
2008-04-02 21:10:32 +00:00
update_records = self.conf.update_records
login = self.conf.login_shell
2008-05-20 15:21:34 +00:00
args = []
shell = ''
2007-08-26 23:47:42 +00:00
2008-01-28 04:27:18 +00:00
if self.command:
2008-06-13 00:28:29 +00:00
dbg ('spawn_child: using self.command: %s'%self.command)
2008-02-12 22:06:19 +00:00
args = self.command
shell = self.command[0]
2008-04-02 21:10:32 +00:00
elif self.conf.use_custom_command:
2008-06-15 10:15:45 +00:00
dbg ('spawn_child: using custom command: %s'%self.conf.custom_command)
2008-04-02 21:10:32 +00:00
args = self.conf.custom_command.split ()
2007-08-31 19:27:27 +00:00
shell = args[0]
2008-05-20 15:21:34 +00:00
2008-06-09 17:51:37 +00:00
try:
2008-06-15 10:15:45 +00:00
if os.environ['PATH'] == "":
raise (ValueError)
2008-06-09 17:51:37 +00:00
paths = os.environ['PATH'].split(':')
except:
2008-06-15 10:15:45 +00:00
paths = ['/usr/local/bin', '/usr/bin', '/bin']
dbg ('spawn_child: found paths: "%s"'%paths)
if self.conf.use_custom_command and shell[0] != '/':
for path in paths:
2008-06-15 18:03:54 +00:00
dbg ('spawn_child: looking for "%s"'%os.path.join (path, shell))
2008-06-15 10:15:45 +00:00
if os.path.exists (os.path.join (path, shell)):
shell = os.path.join (path, shell)
break
2008-06-09 17:51:37 +00:00
2008-06-13 00:28:29 +00:00
if not self.command and not os.path.exists (shell):
dbg ('spawn_child: hunting for a command')
2008-05-20 15:21:34 +00:00
shell = os.getenv ('SHELL') or ''
2008-06-15 10:15:45 +00:00
args = []
2008-05-20 15:21:34 +00:00
if not os.path.exists (shell):
shell = pwd.getpwuid (os.getuid ())[6] or ''
if not os.path.exists (shell):
for i in ['bash','zsh','tcsh','ksh','csh','sh']:
2008-06-09 17:51:37 +00:00
for p in paths:
shell = os.path.join(p, 'bin', i)
2008-05-20 15:21:34 +00:00
if not os.path.exists (shell):
continue
else:
break
2008-06-09 17:51:37 +00:00
2008-06-13 00:28:29 +00:00
if not self.command and not os.path.exists (shell):
2008-05-20 15:21:34 +00:00
# Give up, we're completely stuck
2008-06-16 23:10:54 +00:00
err (_('Unable to find a shell'))
2008-05-20 15:21:34 +00:00
gobject.timeout_add (100, self.terminator.closeterm, self)
return (-1)
2008-06-09 17:51:37 +00:00
2008-05-20 15:21:34 +00:00
if not args:
args.append (shell)
2008-06-16 23:26:45 +00:00
if self.conf.login_shell:
args[0] = "-%s"%args[0]
2008-06-13 23:42:04 +00:00
dbg ('SEGBUG: Setting WINDOWID')
2008-05-20 15:21:34 +00:00
os.putenv ('WINDOWID', '%s'%self._vte.get_parent_window().xid)
2007-08-26 23:47:42 +00:00
2008-06-16 23:10:54 +00:00
dbg ('SEGBUG: Forking command: "%s" with args "%s", loglastlog = "%s", logwtmp = "%s", logutmp = "%s" and cwd "%s"'%(shell, args, login, update_records, update_records, self.cwd))
self._pid = self._vte.fork_command (command = shell, argv = args, envv = [], loglastlog = login, logwtmp = update_records, logutmp = update_records, directory=self.cwd)
2008-02-24 22:33:26 +00:00
2008-06-13 23:42:04 +00:00
dbg ('SEGBUG: Forked command')
2008-05-20 15:21:34 +00:00
if self._pid == -1:
2008-06-16 23:10:54 +00:00
err (_('Unable to start shell: ') + shell)
2008-05-20 15:21:34 +00:00
return (-1)
2008-02-24 22:33:26 +00:00
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-05-23 21:30:53 +00:00
cwd = pid_get_cwd (self._pid)
dbg ('get_cwd found: %s'%cwd)
return (cwd)
2006-11-10 05:18:31 +00:00
def reconfigure_vte (self):
2006-11-10 20:15:33 +00:00
# Set our emulation
2008-04-02 21:10:32 +00:00
self._vte.set_emulation (self.conf.emulation)
2006-11-10 05:18:31 +00:00
2007-07-16 22:59:17 +00:00
# Set our wordchars
2008-04-02 21:10:32 +00:00
self._vte.set_word_chars (self.conf.word_chars)
2007-07-16 22:59:17 +00:00
# Set our mouselation
2008-04-02 21:10:32 +00:00
self._vte.set_mouse_autohide (self.conf.mouse_autohide)
2007-07-16 22:59:17 +00:00
# Set our compatibility
2008-04-02 21:10:32 +00:00
backspace = self.conf.backspace_binding
delete = self.conf.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
2008-04-02 21:10:32 +00:00
# Set our font
2006-11-10 05:18:31 +00:00
try:
2008-04-02 21:10:32 +00:00
self._vte.set_font (pango.FontDescription (self.conf.font))
2006-11-10 05:18:31 +00:00
except:
pass
2006-11-10 20:15:33 +00:00
# Set our boldness
2008-04-02 21:10:32 +00:00
self._vte.set_allow_bold (self.conf.allow_bold)
2006-11-10 20:15:33 +00:00
2008-04-02 21:10:32 +00:00
# Set our color scheme
palette = self.conf.palette
if self.conf.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-04-02 21:10:32 +00:00
fg_color = gtk.gdk.color_parse (self.conf.foreground_color)
bg_color = gtk.gdk.color_parse (self.conf.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.
2008-04-02 21:10:32 +00:00
background_type = self.conf.background_type
2008-02-25 11:02:02 +00:00
# set background image settings
if background_type == "image":
2008-04-02 21:10:32 +00:00
self._vte.set_background_image_file (self.conf.background_image)
self._vte.set_scroll_background (self.conf.scroll_background)
2008-02-25 11:02:02 +00:00
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)
2008-04-02 21:10:32 +00:00
self._vte.set_background_saturation(1 - (self.conf.background_darkness))
self._vte.set_opacity(int(self.conf.background_darkness * 65535))
2008-02-25 11:02:02 +00:00
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-04-02 21:10:32 +00:00
self._vte.set_cursor_blinks = (self.conf.cursor_blink)
2006-11-10 20:15:33 +00:00
# Set our audible belliness
2008-04-02 21:10:32 +00:00
silent_bell = self.conf.silent_bell
2008-05-29 12:25:46 +00:00
self._vte.set_audible_bell (not silent_bell)
2008-06-16 23:36:55 +00:00
# Set our visual flashiness
2008-05-29 12:25:46 +00:00
self._vte.set_visible_bell (silent_bell)
2006-11-10 20:15:33 +00:00
2008-06-16 23:36:55 +00:00
# Override our flashybelliness
if self.conf.force_no_bell:
self._vte.set_visible_bell (False)
self._vte.set_audible_bell (False)
2006-11-10 20:15:33 +00:00
# Set our scrolliness
2008-04-02 21:10:32 +00:00
self._vte.set_scrollback_lines (self.conf.scrollback_lines)
self._vte.set_scroll_on_keystroke (self.conf.scroll_on_keystroke)
self._vte.set_scroll_on_output (self.conf.scroll_on_output)
2006-11-10 20:15:33 +00:00
2008-04-02 21:10:32 +00:00
if self.scrollbar_position != self.conf.scrollbar_position:
self.scrollbar_position = self.conf.scrollbar_position
2008-02-02 01:49:32 +00:00
2008-04-02 21:10:32 +00:00
if self.scrollbar_position == 'hidden' or self.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-04-07 22:50:01 +00:00
if self.scrollbar_position == 'right':
self._termbox.reorder_child (self._vte, 0)
elif self.scrollbar_position == 'left':
self._termbox.reorder_child (self._scrollbar, 0)
2008-02-02 01:49:32 +00:00
2007-05-08 23:15:26 +00:00
# Set our sloppiness
2008-04-02 21:10:32 +00:00
self.focus = self.conf.focus
2006-11-10 05:18:31 +00:00
2008-06-09 07:49:28 +00:00
self._vte.queue_draw ()
2008-06-03 22:38:17 +00:00
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):
2008-05-20 15:21:34 +00:00
# Left mouse button + Ctrl while over a link should open it
mask = gtk.gdk.CONTROL_MASK
if (event.state & mask) == mask:
if event.button == 1:
url = self._vte.match_check (int (event.x / self._vte.get_char_width ()), int (event.y / self._vte.get_char_height ()))
if url:
if (url[0][0:7] != "mailto:") & (url[1] == self.matches['email']):
address = "mailto:" + url[0]
else:
address = url[0]
openurl ( address )
return False
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 ()
2006-11-17 04:34:15 +00:00
return False
2006-11-10 20:15:33 +00:00
2008-05-22 14:37:59 +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):
2008-03-29 01:06:42 +00:00
self.toggle_widget_visibility (self._scrollbar)
2007-02-24 14:53:10 +00:00
2008-03-01 01:14:32 +00:00
def do_title_toggle (self):
2008-03-29 01:06:42 +00:00
self.toggle_widget_visibility (self._titlebox)
def toggle_widget_visibility (self, widget):
if not isinstance (widget, gtk.Widget):
raise TypeError
if widget.get_property ('visible'):
widget.hide ()
2008-03-01 01:14:32 +00:00
else:
2008-03-29 01:06:42 +00:00
widget.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)
2008-05-29 12:25:46 +00:00
mask = gtk.gdk.CONTROL_MASK | gtk.gdk.SHIFT_MASK | gtk.gdk.MOD1_MASK
if (event.state & mask) == mask:
#Top level tab
if keyname == 'T':
self.terminator.newtab (self, True)
return (True)
2008-01-14 22:45:43 +00:00
# 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-05-22 14:37:59 +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-05-23 21:30:53 +00:00
elif keyname == 'Page_Down':
self.terminator.move_tab(self, 'right')
return (True)
elif keyname == 'Page_Up':
self.terminator.move_tab(self, 'left')
return (True)
2008-05-30 12:19:35 +00:00
elif keyname == 'Z':
2008-06-14 01:27:50 +00:00
self.terminator.toggle_zoom (self, True)
return (True)
elif keyname == 'X':
2008-05-30 12:19:35 +00:00
self.terminator.toggle_zoom (self)
return (True)
2008-05-23 21:30:53 +00:00
2008-05-22 14:37:59 +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')):
2008-05-29 12:25:46 +00:00
mask = gtk.gdk.CONTROL_MASK | gtk.gdk.SHIFT_MASK
2007-11-16 00:19:05 +00:00
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)
2008-05-29 12:25:46 +00:00
mask = gtk.gdk.CONTROL_MASK
if (event.state & mask) == mask:
self.terminator.go_next (self)
return (True)
# Warning, mask value is either gtk.gdk.CONTROL_MASK or gtk.gdk.CONTROL_MASK | gtk.gdk.SHIFT_MASK
# if you intend to use it, reinit it
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-04-07 23:22:01 +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)
2008-05-20 15:21:34 +00:00
def on_vte_popup_menu (self, term, event):
self.do_popup (event)
2006-11-17 07:23:04 +00:00
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")
2008-06-03 23:52:19 +00:00
iconopen = gtk.image_new_from_stock(gtk.STOCK_JUMP_TO, gtk.ICON_SIZE_MENU)
item = gtk.ImageMenuItem (nameopen)
item.set_property('image', iconopen)
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
2008-06-03 23:52:19 +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
2008-03-29 01:06:42 +00:00
item = gtk.CheckMenuItem (_("Show _titlebar"))
item.set_active (self._titlebox.get_property ('visible'))
2008-03-01 01:14:32 +00:00
item.connect ("toggled", lambda menu_item: self.do_title_toggle ())
menu.append (item)
2007-02-24 14:53:10 +00:00
2008-05-22 14:37:59 +00:00
self._do_encoding_items (menu)
2007-07-29 02:06:52 +00:00
item = gtk.MenuItem ()
menu.append (item)
2008-05-30 12:09:24 +00:00
if not self.terminator._zoomed:
2008-06-04 10:18:59 +00:00
str_horiz = _("Split H_orizontally")
str_vert = _("Split V_ertically")
2008-06-04 11:34:33 +00:00
item = gtk.ImageMenuItem (str_horiz)
item_image = gtk.Image ()
item_image.set_from_icon_name (APP_NAME + '_horiz', gtk.ICON_SIZE_MENU)
item.set_image (item_image)
2008-05-30 12:09:24 +00:00
item.connect ("activate", lambda menu_item: self.terminator.splitaxis (self, False))
menu.append (item)
2007-07-29 02:06:52 +00:00
2008-06-04 11:34:33 +00:00
item = gtk.ImageMenuItem (str_vert)
item_image = gtk.Image ()
item_image.set_from_icon_name (APP_NAME + '_vert', gtk.ICON_SIZE_MENU)
item.set_image (item_image)
2008-05-30 12:09:24 +00:00
item.connect ("activate", lambda menu_item: self.terminator.splitaxis (self, True))
menu.append (item)
2008-05-22 14:37:59 +00:00
2008-05-30 12:09:24 +00:00
item = gtk.MenuItem (_("Open _Tab"))
item.connect ("activate", lambda menu_item: self.terminator.newtab (self))
2008-05-29 12:25:46 +00:00
menu.append (item)
2008-05-30 12:09:24 +00:00
if self.conf.extreme_tabs:
item = gtk.MenuItem (_("Open Top Level Tab"))
item.connect ("activate", lambda menu_item: self.terminator.newtab (self, True))
menu.append (item)
2008-05-29 12:25:46 +00:00
2008-05-30 12:09:24 +00:00
item = gtk.MenuItem ()
menu.append (item)
2008-05-23 21:41:49 +00:00
2008-05-30 12:09:24 +00:00
if len (self.terminator.term_list) > 1:
if not self.terminator._zoomed:
item = gtk.MenuItem (_("_Zoom terminal"))
2008-06-14 01:27:50 +00:00
item.connect ("activate", lambda menu_item: self.terminator.toggle_zoom (self, True))
menu.append (item)
item = gtk.MenuItem (_("_Maximise terminal"))
2008-05-30 12:09:24 +00:00
item.connect ("activate", lambda menu_item: self.terminator.toggle_zoom (self))
2008-06-14 01:27:50 +00:00
menu.append (item)
2008-05-30 12:09:24 +00:00
else:
2008-06-14 01:27:50 +00:00
if self.terminator._zoomed and not self.terminator._maximised:
item = gtk.MenuItem (_("_Unzoom terminal"))
item.connect ("activate", lambda menu_item: self.terminator.toggle_zoom (self, True))
menu.append (item)
if self.terminator._zoomed and self.terminator._maximised:
2008-06-14 11:46:29 +00:00
item = gtk.MenuItem (_("U_nmaximise terminal"))
2008-06-14 01:27:50 +00:00
item.connect ("activate", lambda menu_item: self.terminator.toggle_zoom (self))
menu.append (item)
2008-05-29 12:40:58 +00:00
2008-05-30 12:09:24 +00:00
item = gtk.MenuItem ()
menu.append (item)
2008-05-29 12:40:58 +00:00
2007-07-29 15:41:59 +00:00
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-05-22 14:37:59 +00:00
def on_encoding_change (self, widget, encoding):
current = self._vte.get_encoding ()
if current != encoding:
dbg ('Setting Encoding to: %s'%encoding)
self._vte.set_encoding (encoding)
def _do_encoding_items (self, menu):
active_encodings = self.conf.active_encodings
item = gtk.MenuItem (_("Encodings"))
menu.append (item)
submenu = gtk.Menu ()
item.set_submenu (submenu)
current_encoding = self._vte.get_encoding ()
group = None
for encoding in active_encodings:
radioitem = gtk.RadioMenuItem (group, _(encoding))
if group is None:
group = radioitem
if encoding == current_encoding:
radioitem.set_active (True)
radioitem.connect ('activate', self.on_encoding_change, encoding)
submenu.append (radioitem)
item = gtk.MenuItem (_("Other Encodings"))
submenu.append (item)
#second level
submenu = gtk.Menu ()
item.set_submenu (submenu)
encodings = TerminatorEncoding ().get_list ()
encodings.sort (lambda x, y: cmp (x[2].lower (), y[2].lower ()))
group = None
for encoding in encodings:
if encoding[1] in active_encodings:
continue
if encoding[1] is None:
label = "%s %s"%(encoding[2], self._vte.get_encoding ())
else:
label = "%s %s"%(encoding[2], encoding[1])
radioitem = gtk.RadioMenuItem (group, label)
if group is None:
group = radioitem
if encoding[1] == current_encoding:
radioitem.set_active (True)
radioitem.connect ('activate', self.on_encoding_change, encoding[1])
submenu.append (radioitem)
2008-01-06 02:28:21 +00:00
def on_vte_title_change(self, vte):
2008-04-02 21:10:32 +00:00
if self.conf.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-29 01:36:23 +00:00
self.terminator.set_window_title("%s: %s" %(APP_NAME.capitalize(), vte.get_window_title ()))
2008-05-22 14:37:59 +00:00
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-13 16:41:25 +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-03-02 11:46:21 +00:00
def on_vte_focus(self, vte):
if vte.get_window_title ():
2008-03-29 01:36:23 +00:00
self.terminator.set_window_title("%s: %s" %(APP_NAME.capitalize(), vte.get_window_title ()))
2008-05-22 14:37:59 +00:00
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])
def destroy(self):
self._vte.destroy()
2006-11-10 05:18:31 +00:00
class Terminator:
2008-06-10 23:21:05 +00:00
def __init__ (self, profile = None, command = None, fullscreen = False, maximise = False, borderless = False):
2007-08-31 19:27:27 +00:00
self.profile = profile
2008-01-28 04:27:18 +00:00
self.command = command
2008-05-23 21:30:53 +00:00
2008-05-30 12:09:24 +00:00
self._zoomed = False
2008-06-14 01:27:50 +00:00
self._maximised = False
2007-12-27 17:44:38 +00:00
self._fullscreen = False
2008-06-09 18:01:16 +00:00
self._f11_modifier = False
2008-05-10 12:08:20 +00:00
self.term_list = []
2008-04-02 21:10:32 +00:00
stores = []
2008-05-22 14:37:59 +00:00
stores.append (config.TerminatorConfValuestoreRC ())
2008-04-02 21:10:32 +00:00
2008-04-08 09:08:51 +00:00
try:
import gconf
2008-06-15 10:15:45 +00:00
if self.profile:
self.profile = gconf.escape_key (self.profile, -1)
store = config.TerminatorConfValuestoreGConf (self.profile)
2008-04-08 09:08:51 +00:00
store.set_reconfigure_callback (self.reconfigure_vtes)
2008-06-15 10:15:45 +00:00
dbg ('Terminator__init__: comparing %s and %s'%(self.profile, store.profile.split ('/').pop ()))
if self.profile == store.profile.split ('/').pop ():
2008-06-10 23:21:05 +00:00
# If we have been given a profile, and we loaded it, we should be higher priority than RC
dbg ('Terminator__init__: placing GConf before RC')
stores.insert (0, store)
else:
stores.append (store)
2008-04-08 09:08:51 +00:00
except:
pass
2008-04-02 21:10:32 +00:00
2008-05-22 14:37:59 +00:00
self.conf = config.TerminatorConfig (stores)
2008-06-03 23:10:09 +00:00
self.icon_theme = gtk.IconTheme ()
2008-06-09 18:01:16 +00:00
if self.conf.f11_modifier:
self._f11_modifier = True
2008-05-29 12:25:46 +00:00
if self.conf.handle_size in range (0,6):
gtk.rc_parse_string("""
style "terminator-paned-style" {
GtkPaned::handle_size = %s
}
class "GtkPaned" style "terminator-paned-style"
""" % self.conf.handle_size)
2006-11-10 05:18:31 +00:00
self.window = gtk.Window ()
2008-03-29 01:36:23 +00:00
self.window.set_title (APP_NAME.capitalize())
2008-02-08 11:05:19 +00:00
try:
2008-06-03 23:10:09 +00:00
self.window.set_icon (self.icon_theme.load_icon (APP_NAME, 48, 0))
2008-02-08 11:05:19 +00:00
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)
2008-05-29 12:25:46 +00:00
self.window.connect ("window-state-event", self.on_window_state_changed)
2007-11-09 03:19:25 +00:00
2007-11-07 19:57:56 +00:00
self.window.set_property ('allow-shrink', True)
2008-05-29 12:25:46 +00:00
if fullscreen or self.conf.fullscreen:
2008-03-01 00:09:49 +00:00
self.fullscreen_toggle ()
2008-05-29 12:25:46 +00:00
if maximise or self.conf.maximise:
2008-03-01 00:09:49 +00:00
self.maximize ()
2008-05-29 12:25:46 +00:00
if borderless or self.conf.borderless:
2008-03-01 00:09:49 +00:00
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]
2008-05-20 15:21:34 +00:00
self.window.add (term)
2008-05-29 12:25:46 +00:00
term._titlebox.hide()
2008-02-02 01:49:32 +00:00
self.window.show ()
2008-05-20 15:21:34 +00:00
term.spawn_child ()
2007-07-29 02:06:52 +00:00
2007-12-27 17:44:38 +00:00
def maximize (self):
2008-03-29 01:36:23 +00:00
""" Maximize the Terminator window."""
2007-12-27 17:44:38 +00:00
self.window.maximize ()
2008-05-15 00:59:54 +00:00
def fullscreen_toggle (self):
2007-12-27 17:44:38 +00:00
""" 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 ()
2008-05-29 12:25:46 +00:00
def on_window_state_changed (self, window, event):
state = event.new_window_state & gtk.gdk.WINDOW_STATE_FULLSCREEN
self._fullscreen = bool (state)
return (False)
2007-12-27 17:44:38 +00:00
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-06-09 18:01:16 +00:00
if (keyname == 'F11' and (self._f11_modifier == False or event.state & mask)):
2008-06-09 17:51:37 +00:00
self.fullscreen_toggle ()
return (True)
2008-01-14 15:41:52 +00:00
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))
2008-05-22 14:37:59 +00:00
2008-03-02 11:46:21 +00:00
def set_window_title(self, title):
2008-05-22 14:37:59 +00:00
"""
Modifies Terminator window title
"""
2008-03-02 11:46:21 +00:00
self.window.set_title(title)
2008-05-22 14:37:59 +00:00
def add(self, widget, terminal, pos = "bottom"):
"""
Add a term to another at position pos
"""
vertical = pos in ("top", "bottom")
2008-01-14 22:45:43 +00:00
pane = (vertical) and gtk.VPaned () or gtk.HPaned ()
2008-05-29 12:25:46 +00:00
2008-01-14 22:45:43 +00:00
# get the parent of the provided terminal
2008-06-13 10:30:28 +00:00
parent = widget.get_parent ()
2008-06-13 23:42:04 +00:00
dbg ('SEGBUG: add() Got parent')
2007-08-10 21:35:06 +00:00
if isinstance (parent, gtk.Window):
2008-06-13 23:42:04 +00:00
dbg ('SEGBUG: parent is a gtk.Window')
2007-08-10 21:35:06 +00:00
# We have just one term
2008-06-13 11:21:14 +00:00
parent.remove(widget)
2008-06-13 23:42:04 +00:00
dbg ('SEGBUG: removed widget from window')
2008-05-22 14:37:59 +00:00
if pos in ("top", "left"):
2008-06-13 23:42:04 +00:00
dbg ('SEGBUG: pos is in top/left')
2008-05-22 14:37:59 +00:00
pane.pack1 (terminal, True, True)
2008-06-13 23:42:04 +00:00
dbg ('SEGBUG: packed terminal')
2008-05-22 14:37:59 +00:00
pane.pack2 (widget, True, True)
2008-06-13 23:42:04 +00:00
dbg ('SEGBUG: packed widget')
2008-05-22 14:37:59 +00:00
else:
2008-06-13 23:42:04 +00:00
dbg ('SEGBUG: pos is not in top/left')
2008-05-22 14:37:59 +00:00
pane.pack1 (widget, True, True)
2008-06-13 23:42:04 +00:00
dbg ('SEGBUG: packed widget')
2008-05-22 14:37:59 +00:00
pane.pack2 (terminal, True, True)
2008-06-13 23:42:04 +00:00
dbg ('SEGBUG: packed terminal')
2007-08-10 23:33:00 +00:00
parent.add (pane)
2008-06-13 23:42:04 +00:00
dbg ('SEGBUG: added pane to parent')
2008-05-20 15:21:34 +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-06-13 10:30:28 +00:00
if (isinstance (parent, gtk.Notebook) or isinstance (parent, gtk.Window)) and widget.conf.titlebars:
2008-05-29 12:25:46 +00:00
#not the only term in the notebook/window anymore, need to reshow the title
2008-06-13 23:42:04 +00:00
dbg ('SEGBUG: Showing _titlebox')
2008-05-29 12:25:46 +00:00
widget._titlebox.show()
2008-05-22 14:37:59 +00:00
if isinstance (parent, gtk.Notebook):
2008-06-13 23:42:04 +00:00
dbg ('SEGBUG: Parent is a notebook')
2008-05-22 14:37:59 +00:00
page = -1
2008-05-29 12:25:46 +00:00
2008-05-22 14:37:59 +00:00
for i in range(0, parent.get_n_pages()):
if parent.get_nth_page(i) == widget:
page = i
break
widget.reparent (pane)
if pos in ("top", "left"):
pane.remove(widget)
pane.pack1 (terminal, True, True)
pane.pack2 (widget, True, True)
else:
pane.pack1 (widget, True, True)
pane.pack2 (terminal, 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)
2008-05-23 21:30:53 +00:00
parent.set_tab_reorderable(pane, True)
2008-05-22 14:37:59 +00:00
parent.set_current_page(page)
position = (vertical) and parent.allocation.height \
or parent.allocation.width
2008-06-13 10:30:28 +00:00
2007-08-10 21:35:06 +00:00
if isinstance (parent, gtk.Paned):
2008-06-13 23:42:04 +00:00
dbg ('SEGBUG: parent is a Paned')
2007-08-10 21:35:06 +00:00
# We are inside a split term
2008-05-20 15:21:34 +00:00
position = (vertical) and widget.allocation.height \
or widget.allocation.width
2008-06-13 23:42:04 +00:00
dbg ('SEGBUG: Preparing to reparent sibling')
2008-05-20 15:21:34 +00:00
if (widget == parent.get_child1 ()):
widget.reparent (pane)
2007-11-07 19:05:15 +00:00
parent.pack1 (pane, True, True)
2007-08-10 21:35:06 +00:00
else:
2008-05-20 15:21:34 +00:00
widget.reparent (pane)
2007-11-07 19:05:15 +00:00
parent.pack2 (pane, True, True)
2007-08-10 21:35:06 +00:00
2008-05-22 14:37:59 +00:00
if pos in ("top", "left"):
2008-06-13 23:42:04 +00:00
dbg ('SEGBUG: pos is in top/left. Removing and re-ordering')
2008-05-22 14:37:59 +00:00
pane.remove(widget)
pane.pack1 (terminal, True, True)
pane.pack2 (widget, True, True)
else:
2008-06-13 23:42:04 +00:00
dbg ('SEGBUG: pos is not in top/left. Packing')
2008-05-22 14:37:59 +00:00
pane.pack1 (widget, True, True)
pane.pack2 (terminal, True, True)
2008-06-13 10:30:28 +00:00
2008-06-13 23:42:04 +00:00
dbg ('SEGBUG: packing widget and terminal')
2008-05-20 15:21:34 +00:00
pane.pack1 (widget, True, True)
pane.pack2 (terminal, True, True)
2008-06-13 23:42:04 +00:00
dbg ('SEGBUG: packed widget and terminal')
2008-05-15 00:59:54 +00:00
2008-01-14 22:45:43 +00:00
# show all, set position of the divider
2008-06-13 23:42:04 +00:00
dbg ('SEGBUG: Showing pane')
2007-12-16 16:49:49 +00:00
pane.show ()
2008-06-13 23:42:04 +00:00
dbg ('SEGBUG: Showed pane')
2008-05-29 12:25:46 +00:00
pane.set_position (position / 2)
2008-06-13 23:42:04 +00:00
dbg ('SEGBUG: Set position')
2008-05-20 15:21:34 +00:00
terminal.show ()
2007-10-13 23:11:29 +00:00
# insert the term reference into the list
index = self.term_list.index (widget)
2008-05-29 12:25:46 +00:00
if pos in ('bottom', 'right'):
index = index + 1
self.term_list.insert (index, terminal)
2008-01-14 22:45:43 +00:00
# make the new terminal grab the focus
terminal._vte.grab_focus ()
2008-05-20 15:21:34 +00:00
2008-01-14 22:45:43 +00:00
return (terminal)
2007-08-10 21:35:06 +00:00
2008-05-22 14:37:59 +00:00
def on_page_reordered(self, notebook, child, page_num):
2008-05-23 21:30:53 +00:00
#page has been reordered, we need to get the
# first term and last term
2008-05-22 14:37:59 +00:00
dbg ("Reordered: %d"%page_num)
2008-05-23 21:30:53 +00:00
nbpages = notebook.get_n_pages()
if nbpages == 1:
dbg("[ERROR] only one page in on_page_reordered")
first = self._notebook_first_term(notebook.get_nth_page(page_num))
last = self._notebook_last_term(notebook.get_nth_page(page_num))
firstidx = self.term_list.index(first)
lastidx = self.term_list.index(last)
termslice = self.term_list[firstidx:lastidx+1]
#remove them from the list
for term in termslice:
self.term_list.remove(term)
2008-05-22 14:37:59 +00:00
2008-05-23 21:30:53 +00:00
if page_num == 0:
#first page, we insert before the first term of next page
nexttab = notebook.get_nth_page(1)
sibling = self._notebook_first_term(nexttab)
siblingindex = self.term_list.index(sibling)
for term in termslice:
self.term_list.insert(siblingindex, term)
siblingindex += 1
else:
#other pages, we insert after the last term of previous page
previoustab = notebook.get_nth_page(page_num - 1)
sibling = self._notebook_last_term(previoustab)
siblingindex = self.term_list.index(sibling)
for term in termslice:
siblingindex += 1
self.term_list.insert(siblingindex, term)
#for page reorder, we need to get the first term of a notebook
def notebook_first_term(self, notebook):
return self._notebook_first_term(notebook.get_nth_page(0))
def _notebook_first_term(self, child):
if isinstance(child, TerminatorTerm):
return child
elif isinstance(child, gtk.Paned):
return self._notebook_first_term(child.get_child1())
elif isinstance(child, gtk.Notebook):
return self._notebook_first_term(child.get_nth_page(0))
dbg("[ERROR] unsupported class %s in _notebook_first_term" % child.__class__.__name__)
return None
#for page reorder, we need to get the last term of a notebook
def notebook_last_term(self, notebook):
return self._notebook_last_term(notebook.get_nth_page(notebook.get_n_pages()-1))
def _notebook_last_term(self, child):
if isinstance(child, TerminatorTerm):
return child
elif isinstance(child, gtk.Paned):
2008-05-29 12:25:46 +00:00
return self._notebook_last_term(child.get_child2())
2008-05-23 21:30:53 +00:00
elif isinstance(child, gtk.Notebook):
2008-05-29 12:25:46 +00:00
return self._notebook_last_term(child.get_nth_page(child.get_n_pages()-1))
2008-05-23 21:30:53 +00:00
dbg("[ERROR] unsupported class %s in _notebook_last_term" % child.__class__.__name__)
return None
2008-05-29 12:25:46 +00:00
def newtab(self,widget, toplevel = False):
2008-05-30 12:09:24 +00:00
if self._zoomed:
# We don't want to add a new tab while we are zoomed in on a terminal
dbg ("newtab function called, but Terminator was in zoomed terminal mode.")
2008-05-29 12:40:58 +00:00
return
2008-05-22 14:37:59 +00:00
terminal = TerminatorTerm (self, self.profile, None, widget.get_cwd())
2008-05-29 12:25:46 +00:00
#only one term, we don't show the title
terminal._titlebox.hide()
if self.conf.extreme_tabs and not toplevel:
parent = widget.get_parent ()
child = widget
else:
child = self.window.get_children()[0]
parent = child.get_parent()
if isinstance(parent, gtk.Paned) or (isinstance(parent, gtk.Window)
and
((self.conf.extreme_tabs and not toplevel) or not isinstance(child, gtk.Notebook))):
2008-05-22 14:37:59 +00:00
#no notebook yet.
notebook = gtk.Notebook()
2008-05-23 21:30:53 +00:00
notebook.set_tab_pos(gtk.POS_TOP)
2008-05-22 14:37:59 +00:00
notebook.connect('page-reordered',self.on_page_reordered)
notebook.set_property('homogeneous', True)
2008-05-23 21:30:53 +00:00
notebook.set_tab_reorderable(widget, True)
2008-05-22 14:37:59 +00:00
if isinstance(parent, gtk.Paned):
2008-05-29 12:25:46 +00:00
if parent.get_child1() == child:
child.reparent(notebook)
2008-05-22 14:37:59 +00:00
parent.pack1(notebook)
else:
2008-05-29 12:25:46 +00:00
child.reparent(notebook)
2008-05-22 14:37:59 +00:00
parent.pack2(notebook)
elif isinstance(parent, gtk.Window):
2008-05-29 12:25:46 +00:00
child.reparent(notebook)
2008-05-22 14:37:59 +00:00
parent.add(notebook)
2008-05-29 12:25:46 +00:00
notebook.set_tab_reorderable(child,True)
2008-05-22 14:37:59 +00:00
notebooklabel = ""
2008-05-29 12:25:46 +00:00
if isinstance(child, TerminatorTerm):
child._titlebox.hide()
2008-05-22 14:37:59 +00:00
if widget._vte.get_window_title() is not None:
notebooklabel = widget._vte.get_window_title()
2008-05-29 12:25:46 +00:00
notebook.set_tab_label_text(child, notebooklabel)
notebook. set_tab_label_packing(child, True, True, gtk.PACK_START)
2008-05-22 14:37:59 +00:00
notebook.show()
elif isinstance(parent, gtk.Notebook):
notebook = parent
2008-05-29 12:25:46 +00:00
elif isinstance(parent, gtk.Window) and isinstance(child, gtk.Notebook):
notebook = child
2008-05-22 14:37:59 +00:00
else:
return (False)
2008-05-23 21:30:53 +00:00
## NOTE
## Here we need to append to the notebook before we can
## spawn the terminal (WINDOW_ID needs to be set)
notebook.append_page(terminal,None)
2008-05-22 14:37:59 +00:00
terminal.show ()
terminal.spawn_child ()
2008-05-23 21:30:53 +00:00
## Some gtk/vte weirdness
## If we don't use this silly test,
## terminal._vte.get_window_title() might return
## bogus values
notebooklabel = ""
if terminal._vte.get_window_title() is not None:
notebooklabel = terminal._vte.get_window_title()
notebook.set_tab_label_text(terminal, notebooklabel)
notebook.set_tab_label_packing(terminal, True, True, gtk.PACK_START)
notebook.set_tab_reorderable(terminal,True)
## Now, we set focus on the new term
notebook.set_current_page(-1)
2008-05-22 14:37:59 +00:00
terminal._vte.grab_focus ()
2008-05-23 21:30:53 +00:00
#adding a new tab, thus we need to get the
# last term of the previous tab and add
# the new term just after
sibling = self._notebook_last_term(notebook.get_nth_page(notebook.page_num(terminal)-1))
index = self.term_list.index(sibling)
self.term_list.insert (index + 1, terminal)
2008-05-22 14:37:59 +00:00
return (True)
return terminal
def splitaxis (self, widget, vertical=True):
""" Split the provided widget on the horizontal or vertical axis. """
2008-05-30 12:09:24 +00:00
if self._zoomed:
# We don't want to split the terminal while we are in zoomed mode
dbg ("splitaxis function called, but Terminator was in zoomed mode.")
2008-05-29 12:40:58 +00:00
return
2008-05-22 14:37:59 +00:00
# create a new terminal and parent pane.
2008-06-13 23:42:04 +00:00
dbg ('SEGBUG: Creating TerminatorTerm')
2008-05-22 14:37:59 +00:00
terminal = TerminatorTerm (self, self.profile, None, widget.get_cwd())
2008-06-13 23:42:04 +00:00
dbg ('SEGBUG: Created TerminatorTerm')
2008-06-04 11:34:33 +00:00
pos = vertical and "right" or "bottom"
2008-06-13 23:42:04 +00:00
dbg ('SEGBUG: Position is: %s'%pos)
2008-05-22 14:37:59 +00:00
self.add(widget, terminal, pos)
2008-06-13 23:42:04 +00:00
dbg ('SEGBUG: added TerminatorTerm to container')
2008-05-22 14:37:59 +00:00
terminal.show ()
2008-06-13 23:42:04 +00:00
dbg ('SEGBUG: showed TerminatorTerm')
2008-05-22 14:37:59 +00:00
terminal.spawn_child ()
2008-06-13 23:42:04 +00:00
dbg ('SEGBUG: spawned child')
2008-05-22 14:37:59 +00:00
return terminal
def remove(self, widget):
"""Remove a TerminatorTerm from the Terminator view and terms list
Returns True on success, False on failure"""
2008-05-20 15:21:34 +00:00
parent = widget.get_parent ()
2007-07-29 15:41:59 +00:00
sibling = None
2008-06-06 18:13:54 +00:00
focus_on_close = 'prev'
2007-07-29 15:41:59 +00:00
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
2008-05-20 15:21:34 +00:00
if widget == parent.get_child1 ():
2007-07-29 15:41:59 +00:00
sibling = parent.get_child2 ()
2008-06-06 18:13:54 +00:00
focus_on_close = 'next'
2008-05-20 15:21:34 +00:00
if widget == parent.get_child2 ():
2007-07-29 15:41:59 +00:00
sibling = parent.get_child1 ()
if not sibling:
# something is wrong, give up
2008-06-16 23:10:54 +00:00
err ("Error: %s is not a child of %s"%(widget, parent))
2008-05-22 14:37:59 +00:00
return False
parent.remove(widget)
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)
2008-05-23 21:30:53 +00:00
grandparent.set_tab_reorderable(sibling, True)
2008-05-29 12:25:46 +00:00
grandparent.set_current_page(page)
2008-06-17 00:58:41 +00:00
2008-05-22 14:37:59 +00:00
else:
grandparent.remove (parent)
sibling.reparent (grandparent)
2008-05-30 12:09:24 +00:00
if not self._zoomed:
2008-05-29 12:40:58 +00:00
grandparent.resize_children()
2007-12-28 10:48:26 +00:00
parent.destroy ()
2008-05-29 12:25:46 +00:00
if isinstance(sibling, TerminatorTerm) and isinstance(sibling.get_parent(), gtk.Notebook):
sibling._titlebox.hide()
2008-05-22 14:37:59 +00:00
self.term_list.remove (widget)
2008-06-06 18:13:54 +00:00
2008-05-22 14:37:59 +00:00
elif isinstance (parent, gtk.Notebook):
parent.remove(widget)
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)
2008-05-29 12:25:46 +00:00
if isinstance(sibling, TerminatorTerm) and sibling.conf.titlebars and sibling.conf.extreme_tabs:
sibling._titlebox.show()
2008-05-22 14:37:59 +00:00
parent.destroy()
2008-06-06 18:13:54 +00:00
if self.conf.focus_on_close == 'prev' or ( self.conf.focus_on_close == 'auto' and focus_on_close == 'prev'):
if index == 0: index = 1
self.term_list[index - 1]._vte.grab_focus ()
self._set_current_notebook_page_recursive(self.term_list[index - 1])
elif self.conf.focus_on_close == 'next' or ( self.conf.focus_on_close == 'auto' and focus_on_close == 'next'):
if index == len(self.term_list): index = index - 1
self.term_list[index]._vte.grab_focus ()
self._set_current_notebook_page_recursive(self.term_list[index])
2008-05-22 14:37:59 +00:00
if len(self.term_list) == 1:
self.term_list[0]._titlebox.hide()
2007-10-13 23:11:29 +00:00
2008-05-22 14:37:59 +00:00
return True
def closeterm (self, widget):
2008-05-30 12:09:24 +00:00
if self._zoomed:
# We are zoomed, pop back out to normal layout before closing
dbg ("closeterm function called while in zoomed mode. Restoring previous layout before closing.")
2008-06-14 01:37:01 +00:00
self.toggle_zoom(widget, not self._maximised)
2008-05-29 12:40:58 +00:00
2008-05-22 14:37:59 +00:00
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-05-22 14:37:59 +00:00
nextterm = self.term_list[next]
2008-05-29 12:25:46 +00:00
##we need to set the current page of each notebook
self._set_current_notebook_page_recursive(nextterm)
2008-05-22 14:37:59 +00:00
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-05-22 14:37:59 +00:00
previousterm = self.term_list[previous]
2008-05-29 12:25:46 +00:00
##we need to set the current page of each notebook
self._set_current_notebook_page_recursive(previousterm)
2008-05-22 14:37:59 +00:00
previousterm._vte.grab_focus ()
2008-05-29 12:25:46 +00:00
def _set_current_notebook_page_recursive(self, widget):
page = self.get_first_notebook_page(widget)
while page:
child = None
page_num = page[0].page_num(page[1])
page[0].set_current_page(page_num)
page = self.get_first_notebook_page(page[0])
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-05-20 15:21:34 +00:00
parent = self.get_first_parent_paned(widget,vertical)
2008-02-22 23:39:59 +00:00
if parent == None:
return
#We have a corresponding parent pane
#
#allocation = parent.get_allocation()
2008-04-02 21:10:32 +00:00
2008-02-22 23:39:59 +00:00
if keyname in ('Up', 'Down'):
maxi = parent.get_child1().get_allocation().height + parent.get_child2().get_allocation().height - 1
2008-05-29 12:25:46 +00:00
2008-02-22 23:39:59 +00:00
else:
maxi = parent.get_child1().get_allocation().width + parent.get_child2().get_allocation().width - 1
move = 10
if keyname in ('Up', 'Left'):
move = -10
2008-04-02 21:10:32 +00:00
2008-05-20 15:21:34 +00:00
move = max(2, parent.get_position() + move)
2008-05-14 02:19:41 +00:00
move = min(maxi, move)
2008-05-13 01:06:16 +00:00
2008-05-14 02:19:41 +00:00
parent.set_position(move)
2008-05-13 01:06:16 +00:00
2008-05-22 14:37:59 +00:00
def previous_tab(self, term):
notebook = self.get_first_parent_notebook(term)
notebook.prev_page()
return
def next_tab(self, term):
notebook = self.get_first_parent_notebook(term)
notebook.next_page()
return
2008-05-23 21:30:53 +00:00
def move_tab(self, term, direction):
dbg("moving to direction %s" % direction)
(notebook, page) = self.get_first_notebook_page(term)
page_num = notebook.page_num(page)
nbpages = notebook.get_n_pages()
#dbg ("%s %s %s %s" % (page_num, nbpages,notebook, page))
if page_num == 0 and direction == 'left':
new_page_num = nbpages
elif page_num == nbpages - 1 and direction == 'right':
new_page_num = 0
elif direction == 'left':
new_page_num = page_num - 1
elif direction == 'right':
new_page_num = page_num + 1
else:
dbg("[ERROR] unhandled combination in move_tab: direction = %s page_num = %d" % (direction, page_num))
return False
notebook.reorder_child(page, new_page_num)
return True
2008-05-22 14:37:59 +00:00
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-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-04-02 21:10:32 +00:00
2008-05-22 14:37:59 +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-04-02 21:10:32 +00:00
def reconfigure_vtes (self):
for term in self.term_list:
term.reconfigure_vte ()
2008-06-14 01:27:50 +00:00
def toggle_zoom(self, widget, fontscale = False):
2008-05-30 12:09:24 +00:00
if not self._zoomed:
2008-06-14 01:27:50 +00:00
self.zoom_term (widget, fontscale)
2008-06-07 13:56:30 +00:00
if self.conf.titlebars:
widget._titlebox.hide()
2008-05-29 12:40:58 +00:00
else:
2008-06-14 01:27:50 +00:00
self.unzoom_term (widget, fontscale)
2008-06-07 13:56:30 +00:00
if self.conf.titlebars and \
2008-06-07 14:07:26 +00:00
len(self.term_list) > 1 \
and \
(isinstance(widget, TerminatorTerm) and isinstance(widget.get_parent(),gtk.Paned))\
:
2008-06-07 13:56:30 +00:00
widget._titlebox.show()
2008-06-07 14:03:44 +00:00
widget._vte.grab_focus()
2008-05-29 12:40:58 +00:00
2008-06-14 01:27:50 +00:00
def zoom_term (self, widget, fontscale = False):
2008-05-29 12:40:58 +00:00
"""Proof of concept: Maximize to full window
an instance of TerminatorTerm.
"""
2008-06-14 01:27:50 +00:00
self.old_font = widget._vte.get_font ()
self.old_columns = widget._vte.get_column_count ()
self.old_rows = widget._vte.get_row_count ()
2008-05-29 12:40:58 +00:00
self.old_parent = widget.get_parent()
2008-06-14 01:27:50 +00:00
2008-05-29 12:40:58 +00:00
if isinstance(self.old_parent, gtk.Window):
return
if isinstance(self.old_parent, gtk.Notebook):
self.old_page = self.old_parent.get_current_page()
2008-06-14 01:27:50 +00:00
2008-05-29 12:40:58 +00:00
self.window_child = self.window.get_children()[0]
self.window.remove(self.window_child)
self.old_parent.remove(widget)
self.window.add(widget)
2008-05-30 12:09:24 +00:00
self._zoomed = True
2008-05-29 12:40:58 +00:00
2008-06-14 01:27:50 +00:00
if fontscale:
self.cnid = widget.connect ("size-allocate", self.zoom_scale_font)
else:
self._maximised = True
def zoom_scale_font (self, widget, allocation):
new_columns = widget._vte.get_column_count ()
new_rows = widget._vte.get_row_count ()
new_font = widget._vte.get_font ()
dbg ('zoom_term: I just went from %dx%d to %dx%d. Raa!'%(self.old_columns, self.old_rows, new_columns, new_rows))
old_area = self.old_columns * self.old_rows
new_area = new_columns * new_rows
area_factor = new_area / old_area
dbg ('zoom_term: My area changed from %d characters to %d characters, a factor of %f.'%(old_area, new_area, area_factor))
new_font.set_size (self.old_font.get_size() * (area_factor / 2))
dbg ('zoom_term: Scaled font from %f to %f'%(self.old_font.get_size () / pango.SCALE, new_font.get_size () / pango.SCALE))
widget._vte.set_font (new_font)
widget.disconnect (self.cnid)
def unzoom_term (self, widget, fontscale = False):
2008-05-29 12:40:58 +00:00
"""Proof of concept: Go back to previous application
widget structure.
"""
2008-05-30 12:09:24 +00:00
if self._zoomed:
2008-06-14 01:27:50 +00:00
if fontscale:
widget._vte.set_font (self.old_font)
self._zoomed = False
self._maximised = False
2008-05-29 12:40:58 +00:00
self.window.remove(widget)
self.window.add(self.window_child)
self.old_parent.add(widget)
if isinstance(self.old_parent, gtk.Notebook):
self.old_parent.set_current_page(self.old_page)
2007-07-29 02:06:52 +00:00
if __name__ == '__main__':
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-06-15 10:15:45 +00:00
def profile_cb (option, opt, value, parser):
assert value is None
value = ''
while parser.rargs:
arg = parser.rargs[0]
if arg[0] != '-':
if len (value) > 0:
value = '%s %s'%(value, arg)
else:
value = '%s'%arg
del (parser.rargs[0])
else:
break
setattr (parser.values, option.dest, value)
2008-02-12 13:49:36 +00:00
usage = "usage: %prog [options]"
parser = OptionParser (usage)
2008-03-29 01:40:49 +00:00
parser.add_option ("-v", "--version", action="store_true", dest="version", help="Display program version")
2008-02-12 13:49:36 +00:00
parser.add_option ("-d", "--debug", action="store_true", dest="debug", help="Enable debugging information")
2008-03-29 01:36:23 +00:00
parser.add_option ("-m", "--maximise", action="store_true", dest="maximise", help="Open the %s window maximised"%APP_NAME.capitalize())
2008-02-12 13:49:36 +00:00
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")
2008-06-15 10:15:45 +00:00
parser.add_option ("-p", "--profile", dest="profile", action="callback", callback=profile_cb, help="Specify a GNOME Terminal profile to emulate")
2008-02-12 13:49:36 +00:00
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")
(options, args) = parser.parse_args ()
if len (args) != 0:
parser.error("Expecting zero additional arguments, found: %d"%len (args))
2008-03-29 01:40:49 +00:00
if options.version:
print "%s %s"%(APP_NAME, APP_VERSION)
sys.exit (0)
2008-02-12 22:06:19 +00:00
command = []
if (options.command):
command.append (options.command)
if (options.execute):
command = options.execute
2008-05-29 12:25:46 +00:00
if gtk.gdk.display_get_default() == None:
2008-06-16 23:10:54 +00:00
err (_("You need to run terminator in an X environment. " \
"Make sure DISPLAY is properly set"))
2008-05-29 12:25:46 +00:00
sys.exit(1)
2008-06-13 00:28:29 +00:00
2008-06-17 00:58:41 +00:00
try:
open (os.path.expanduser ('~/.config/terminator/config'))
except:
try:
open (os.path.expanduser ('~/.terminatorrc'))
error = gtk.MessageDialog (None, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK,
('''You have a configuration file:
~/.terminatorrc.
Please be aware that this file needs to be moved to:
~/.config/terminator/config.
See the following bug report for more details:
https://bugs.launchpad.net/bugs/238070'''))
error.run ()
error.destroy ()
except:
pass
2008-06-15 10:15:45 +00:00
print 'profile_cb: settled on profile: "%s"'%options.profile
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 ()