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
# Copyright (C) 2006-2007 cmsj@tenshu.net
#
# 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
2007-12-27 17:44:38 +00:00
"""Terminator by Chris Jones <cmsj@tenshu.net>
Usage: terminator [OPTION] ...
-h, --help Show this usage information
-d, --debug Enable debugging
-m, --maximise Maximise the terminator window when it starts
-f, --fullscreen Place the window in its fullscreen state when it starts
2008-01-05 01:05:41 +00:00
-b, --borderless Draw the terminator window without a window border (useful with -m)
2007-12-27 17:44:38 +00:00
-p, --profile=PROFILE Take settings from gnome-terminal profile PROFILE
2008-01-28 04:27:18 +00:00
-e, --execute=COMMAND Executes the command on the opened terminal window
2007-12-27 17:44:38 +00:00
"""
2008-01-03 08:10:39 +00:00
# import standard python libs
import os, sys, string, time, getopt, math
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
# import gtk/gnome libs
# check just in case anyone runs it on a non-gnome system.
try:
import gobject, gtk, gnome, gconf, pango
except:
print >> sys.stderr, _("You need to install the python bindings for " \
2008-01-03 08:23:20 +00:00
"gobject, gnome, gtk, gconf and pango to run Terminator.")
2008-01-03 08:10:39 +00:00
sys.exit(1)
# 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
class TerminatorTerm:
2007-05-08 23:15:26 +00:00
2007-02-24 14:53:10 +00:00
# Our settings
2007-05-08 23:15:26 +00:00
# FIXME: Add commandline and/or gconf options to change these
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',
2006-11-10 05:18:31 +00:00
'allow_bold' : True,
'audible_bell' : False,
'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',
'font_name' : 'Serif 10',
'foreground_color' : '#AAAAAA',
2007-02-24 14:53:10 +00:00
'scrollbar' : True,
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',
2007-02-24 14:53:10 +00:00
'visible_bell' : False,
2008-01-06 14:41:36 +00:00
'child_restart' : False,
2007-02-24 14:53:10 +00:00
'link_scheme' : '(news|telnet|nttp|file|http|ftp|https)',
'_link_user' : '[%s]+(:[%s]+)?',
'link_hostchars' : '-A-Za-z0-9',
'link_userchars' : '-A-Za-z0-9',
2007-07-27 22:31:58 +00:00
'link_passchars' : '-A-Za-z0-9,?;.:/!%$^*&~"#\'',
2008-01-29 14:00:02 +00:00
'_palette' : '%s/palette',
2008-01-29 15:18:33 +00:00
'default_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-01-29 13:54:16 +00:00
def __init__ (self, terminator, profile, command):
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
self.defaults['link_user'] = self.defaults['_link_user']%(self.defaults['link_userchars'], self.defaults['link_passchars'])
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-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 not self.profile:
print >> sys.stderr, _("Warning: unable to find profile %s. Continue with default values...") % profile
2007-08-31 19:33:39 +00:00
2007-12-03 19:38:55 +00:00
self.defaults['palette'] = self.defaults['_palette']%(self.profile)
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)
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
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
self._box = gtk.HBox ()
self._scrollbar = gtk.VScrollbar (self._vte.get_adjustment ())
2007-02-24 14:53:10 +00:00
if self.defaults['scrollbar']:
self._scrollbar.show ()
2006-11-10 05:18:31 +00:00
self._box.pack_start (self._vte)
self._box.pack_start (self._scrollbar, False)
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-01-14 10:37:35 +00:00
# self._vte.connect ("window-title-changed", self.on_vte_title_change)
2007-07-28 01:17:37 +00:00
2007-11-09 05:58:47 +00:00
exit_action = self.gconf_client.get_string (self.profile + "/exit_action")
2008-01-06 14:41:36 +00:00
if not exit_action:
if self.defaults['child_restart']:
exit_action = "restart"
else:
exit_action = "close"
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)
2007-11-09 05:58:47 +00:00
if exit_action == "close":
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
2007-11-09 03:43:11 +00:00
self.matches['domain'] = self._vte.match_add ('((%s://(%s@)?)|(www|ftp)[%s]*\\.)[%s.]+(:[0-9]*)?'%(self.defaults['link_scheme'], self.defaults['link_user'], self.defaults['link_hostchars'], self.defaults['link_hostchars']))
self.matches['path'] = self._vte.match_add ('((%s://(%s@)?)|(www|ftp)[%s]*\\.)[%s.]+(:[0-9]+)?/[-A-Za-z0-9_$.+!*(),;:@&=?/~#%%]*[^]\'.}>) \t\r\n,\\\]'%(self.defaults['link_scheme'], self.defaults['link_userchars'], self.defaults['link_hostchars'], self.defaults['link_hostchars']))
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-]*)+')
2007-02-24 14:53:10 +00:00
2008-01-14 17:04:13 +00:00
self.spawn_child ()
2007-07-28 01:17:37 +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-01-28 23:28:31 +00:00
args = [self.command]
2008-01-28 04:27:18 +00:00
shell = self.command
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)]
self._vte.fork_command (command = shell, argv = args, envv = [], loglastlog = login, logwtmp = update_records, logutmp = update_records)
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 ()
if ret == None:
print _('Unknown value requested. Unable to find in gconf profile or default settings: ') + property
sys.exit (1)
return ret
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"):
2006-11-10 05:18:31 +00:00
font_name = (self.gconf_client.get_string ("/desktop/gnome/interface/monospace_font_name") or self.defaults['font_name'])
else:
2008-01-29 15:21:58 +00:00
font_name = self.reconf ('font_name')
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-01-29 15:18:33 +00:00
# FIXME: This is wrong, we should be pulling 'palette' out of gconf, but reverting to self.defaults['default_palette'] which means we need to reorganise self.defaults to make this available under the same name as gconf
2008-01-29 15:21:58 +00:00
palette = self.reconf ('default_palette')
2008-01-06 09:31:37 +00:00
if (not self.profile) or 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'))
2007-07-27 22:31:58 +00:00
2008-01-06 03:23:26 +00:00
# Set our background image, transparency and type
2008-01-29 15:21:58 +00:00
background_type = self.reconf ('background_type')
2008-01-06 03:23:26 +00:00
if background_type == "solid":
self._vte.set_background_image_file ('')
self._vte.set_background_transparent (False)
if background_type == "image":
2008-01-29 15:21:58 +00:00
self._vte.set_background_image_file (self.reconf ('background_image'))
self._vte.set_scroll_background (self.reconf ('scroll_background'))
2008-01-06 03:23:26 +00:00
self._vte.set_background_transparent (False)
if background_type == "transparent":
self._vte.set_background_transparent (True)
2008-01-29 15:21:58 +00:00
self._vte.set_background_saturation (1 - (self.reconf ('background_darkness')))
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
# 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-01-29 14:25:24 +00:00
# FIXME: What on earth are we doing here. We should match the profile setting name and try not to set things this way
2007-02-24 14:53:10 +00:00
self._vte.set_audible_bell = not (self.gconf_client.get_bool (self.profile + "/silent_bell") or self.defaults['audible_bell'])
2006-11-10 20:15:33 +00:00
self._vte.set_visible_bell (self.defaults['visible_bell'])
# 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
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 ()
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 ()
2006-11-17 04:34:15 +00:00
return False
2006-11-10 20:15:33 +00:00
2007-02-27 20:47:02 +00:00
# Right mouse button should display a context menu
2006-11-10 05:18:31 +00:00
if event.button == 3:
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-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)
2007-10-13 23:11:29 +00:00
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 ()
2007-08-10 21:44:02 +00:00
url = self._vte.match_check (int (event.x / self._vte.get_char_width ()), int (event.y / self._vte.get_char_height ()))
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)
item.connect ("activate", lambda menu_item: gnome.url_show (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)
2007-12-29 03:01:28 +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)
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)
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):
vte.set_property ("has-tooltip", True)
vte.set_property ("tooltip-text", vte.get_window_title ())
2006-11-10 05:18:31 +00:00
def get_box (self):
return self._box
class Terminator:
2008-01-28 04:27:18 +00:00
def __init__ (self, profile, command):
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")
2006-11-13 06:47:26 +00:00
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
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 ())
self.window.show_all ()
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-01-14 22:45:43 +00:00
def splitaxis (self, widget, vertical=True):
""" Split the provided widget on the horizontal or vertical axis. """
2007-08-10 21:35:06 +00:00
2008-01-14 22:45:43 +00:00
# create a new terminal and parent pane.
2008-01-28 04:27:18 +00:00
terminal = TerminatorTerm (self, self.profile, None)
2008-01-14 22:45:43 +00:00
pane = (vertical) and gtk.VPaned () or gtk.HPaned ()
2007-08-10 21:35:06 +00:00
2008-01-14 22:45:43 +00:00
# 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)
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 23:33:00 +00:00
parent.add (pane)
2008-01-14 22:45:43 +00:00
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-01-14 22:45:43 +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)
2007-08-10 21:35:06 +00:00
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)
2007-08-10 21:35:06 +00:00
2007-07-29 15:41:59 +00:00
def closeterm (self, widget):
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
print "Error: %s is not a child of %s"%(widget, parent)
return
2007-12-28 10:48:26 +00:00
self.term_list.remove (widget)
grandparent.remove (parent)
sibling.reparent (grandparent)
widget.get_box ().destroy ()
parent.destroy ()
2007-07-29 15:41:59 +00:00
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 ()
2007-10-13 23:11:29 +00:00
2007-07-29 15:41:59 +00:00
return
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
self.term_list[next]._vte.grab_focus ()
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())
2007-10-13 23:11:29 +00:00
self.term_list[previous]._vte.grab_focus ()
2007-08-31 19:27:27 +00:00
def usage ():
2007-12-27 17:44:38 +00:00
""" Print information on how to use this program. """
print __doc__
2007-08-31 19:27:27 +00:00
2007-07-29 02:06:52 +00:00
if __name__ == '__main__':
2007-08-31 19:27:27 +00:00
2007-12-27 17:44:38 +00:00
# define the options
2008-01-28 04:27:18 +00:00
short_opts = "hdmfbp:e:"
long_opts = ["help", "debug", "maximise", "fullscreen", "borderless", "profile=", "execute="]
2007-12-27 17:44:38 +00:00
# parse the options
2007-08-31 19:27:27 +00:00
try:
2007-12-27 17:44:38 +00:00
opts, args = getopt.getopt (sys.argv[1:], short_opts, long_opts)
2007-08-31 19:27:27 +00:00
except getopt.GetoptError:
usage ()
sys.exit (2)
2007-12-27 17:44:38 +00:00
# set some default values
debug = 0
2008-01-08 08:59:22 +00:00
profile = None
2007-12-27 17:44:38 +00:00
maximise = False
fullscreen = False
2008-01-05 01:05:41 +00:00
borderless = False
2008-01-28 04:27:18 +00:00
command = None
2007-12-27 17:44:38 +00:00
# check the options
2007-08-31 19:27:27 +00:00
for opt, arg in opts:
if opt in ("-h", "--help"):
usage ()
sys.exit (0)
if opt in ("-d", "--debug"):
debug = 1
2007-11-09 03:19:25 +00:00
if opt in ("-m", "--maximise"):
maximise = True
2007-12-27 17:44:38 +00:00
if opt in ("-f", "--fullscreen"):
fullscreen = True
2008-01-05 01:05:41 +00:00
if opt in ("-b", "--borderless"):
borderless = True
2007-11-09 05:23:26 +00:00
if opt in ("-p", "--profile"):
profile = arg
2008-01-28 04:27:18 +00:00
if opt in ("-e", "--execute"):
command = arg
2007-12-29 03:01:28 +00:00
2008-01-28 04:27:18 +00:00
term = Terminator (profile, command)
2007-12-27 17:44:38 +00:00
# Set the Terminator in fullscreen state or maximize it.
# Fullscreen and maximise are mutually exclusive, with
# fullscreen taking precedence over maximise.
if fullscreen:
term.toggle_fullscreen ()
elif maximise:
term.maximize ()
2008-01-05 01:05:41 +00:00
if borderless:
term.window.set_decorated (False)
2006-11-10 05:18:31 +00:00
gtk.main ()