2008-06-26 23:24:52 +00:00
#!/usr/bin/python
# Terminator - multiple gnome terminals in one window
# Copyright (C) 2006-2008 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
""" Terminator by Chris Jones <cmsj@tenshu.net> """
2008-07-02 09:16:52 +00:00
import pygtk
pygtk . require ( " 2.0 " )
2008-06-26 23:24:52 +00:00
import gobject , gtk , pango
2008-07-20 21:15:14 +00:00
import os , platform , sys , subprocess , pwd
2008-06-26 23:24:52 +00:00
#import version details
from terminatorlib . version import *
# import our configuration loader
from terminatorlib import config
2008-06-28 20:20:49 +00:00
from terminatorlib . config import dbg , err , debug
2008-06-26 23:24:52 +00:00
#import encoding list
from terminatorlib . encoding import TerminatorEncoding
# import vte-bindings
try :
import vte
2008-07-17 17:44:21 +00:00
except ImportError :
2008-06-26 23:24:52 +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 ( )
sys . exit ( 1 )
class TerminatorTerm ( gtk . VBox ) :
matches = { }
2008-07-03 22:48:29 +00:00
TARGET_TYPE_VTE = 8
2008-06-26 23:24:52 +00:00
def __init__ ( self , terminator , profile = None , command = None , cwd = None ) :
gtk . VBox . __init__ ( self )
self . terminator = terminator
self . conf = terminator . conf
self . command = command
2008-07-03 22:48:29 +00:00
# Sort out cwd detection code, if available
self . pid_get_cwd = lambda pid : None
if platform . system ( ) == ' FreeBSD ' :
try :
from terminatorlib import freebsd
self . pid_get_cwd = lambda pid : freebsd . get_process_cwd ( pid )
dbg ( ' Using FreeBSD self.pid_get_cwd ' )
2008-07-17 17:44:21 +00:00
except ( OSError , NotImplementedError , ImportError ) :
2008-07-03 22:48:29 +00:00
dbg ( ' FreeBSD version too old for self.pid_get_cwd ' )
pass
elif platform . system ( ) == ' Linux ' :
dbg ( ' Using Linux self.pid_get_cwd ' )
self . pid_get_cwd = lambda pid : os . path . realpath ( ' /proc/ %s /cwd ' % pid )
else :
dbg ( ' Unable to set a self.pid_get_cwd, unknown system: %s ' % platform . system )
# import a library for viewing URLs
try :
# gnome.url_show() is really useful
dbg ( ' url_show: importing gnome module ' )
import gnome
2008-07-06 15:22:22 +00:00
gnome . init ( ' terminator ' , ' terminator ' )
2008-07-04 06:29:32 +00:00
self . url_show = gnome . url_show
2008-07-17 17:44:21 +00:00
except ImportError :
2008-07-03 22:48:29 +00:00
# webbrowser.open() is not really useful, but will do as a fallback
dbg ( ' url_show: gnome module failed, using webbrowser ' )
import webbrowser
2008-07-04 06:29:32 +00:00
self . url_show = webbrowser . open
2008-07-03 22:48:29 +00:00
2008-06-26 23:24:52 +00:00
self . cwd = cwd or os . getcwd ( ) ;
if not os . path . exists ( self . cwd ) or not os . path . isdir ( self . cwd ) :
self . cwd = pwd . getpwuid ( os . getuid ( ) ) [ 5 ]
self . clipboard = gtk . clipboard_get ( gtk . gdk . SELECTION_CLIPBOARD )
self . scrollbar_position = self . conf . scrollbar_position
self . _vte = vte . Terminal ( )
2008-07-16 19:27:16 +00:00
#self._vte.set_double_buffered(True)
2008-06-26 23:24:52 +00:00
self . _vte . set_size ( 80 , 24 )
self . reconfigure_vte ( )
2008-07-17 21:09:41 +00:00
self . _vte . _expose_data = None
2008-06-26 23:24:52 +00:00
self . _vte . show ( )
self . _termbox = gtk . HBox ( )
self . _termbox . show ( )
self . _title = gtk . Label ( )
self . _title . show ( )
self . _titlebox = gtk . EventBox ( )
self . _titlebox . add ( self . _title )
self . show ( )
self . pack_start ( self . _titlebox , False )
self . pack_start ( self . _termbox )
if self . conf . titlebars :
self . _titlebox . show ( )
else :
self . _titlebox . hide ( )
self . _scrollbar = gtk . VScrollbar ( self . _vte . get_adjustment ( ) )
if self . scrollbar_position != " hidden " and self . scrollbar_position != " disabled " :
self . _scrollbar . show ( )
if self . scrollbar_position == ' left ' :
packfunc = self . _termbox . pack_end
else :
packfunc = self . _termbox . pack_start
packfunc ( self . _vte )
packfunc ( self . _scrollbar , False )
self . _vte . connect ( " key-press-event " , self . on_vte_key_press )
self . _vte . connect ( " button-press-event " , self . on_vte_button_press )
2008-08-06 22:12:45 +00:00
self . _vte . connect ( " popup-menu " , self . create_popup_menu )
2008-06-26 23:24:52 +00:00
""" drag and drop """
2008-07-03 22:48:29 +00:00
srcvtetargets = [ ( " vte " , gtk . TARGET_SAME_APP , self . TARGET_TYPE_VTE ) ]
dsttargets = [ ( " vte " , gtk . TARGET_SAME_APP , self . TARGET_TYPE_VTE ) , ( ' text/plain ' , 0 , 0 ) , ( " STRING " , 0 , 0 ) , ( " COMPOUND_TEXT " , 0 , 0 ) ]
2008-06-26 23:24:52 +00:00
self . _vte . drag_source_set ( gtk . gdk . CONTROL_MASK | gtk . gdk . BUTTON3_MASK , srcvtetargets , gtk . gdk . ACTION_MOVE )
self . _titlebox . drag_source_set ( gtk . gdk . BUTTON1_MASK , srcvtetargets , gtk . gdk . ACTION_MOVE )
#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 )
if self . conf . copy_on_selection :
self . _vte . connect ( " selection-changed " , lambda widget : self . _vte . copy_clipboard ( ) )
self . _vte . connect ( " composited-changed " , self . on_composited_changed )
self . _vte . connect ( " window-title-changed " , self . on_vte_title_change )
self . _vte . connect ( " grab-focus " , self . on_vte_focus )
self . _vte . connect ( " focus-out-event " , self . on_vte_focus_out )
self . _vte . connect ( " focus-in-event " , self . on_vte_focus_in )
2008-08-03 02:09:38 +00:00
self . _vte . connect ( " resize-window " , self . on_resize_window )
2008-07-03 22:48:29 +00:00
2008-06-26 23:24:52 +00:00
exit_action = self . conf . exit_action
if exit_action == " restart " :
self . _vte . connect ( " child-exited " , self . spawn_child )
# We need to support "left" because some buggy versions of gnome-terminal
# set it in some situations
elif exit_action in ( " close " , " left " ) :
self . _vte . connect ( " child-exited " , lambda close_term : self . terminator . closeterm ( self ) )
self . _vte . add_events ( gtk . gdk . ENTER_NOTIFY_MASK )
self . _vte . connect ( " enter_notify_event " , self . on_vte_notify_enter )
2008-08-03 02:09:38 +00:00
self . add_matches ( posix = self . conf . try_posix_regexp )
2008-06-26 23:24:52 +00:00
dbg ( ' SEGBUG: Setting http_proxy ' )
env_proxy = os . getenv ( ' http_proxy ' )
if not env_proxy and self . conf . http_proxy and self . conf . http_proxy != ' ' :
os . putenv ( ' http_proxy ' , self . conf . http_proxy )
dbg ( ' SEGBUG: Setting COLORTERM ' )
os . putenv ( ' COLORTERM ' , ' gnome-terminal ' )
dbg ( ' SEGBUG: TerminatorTerm __init__ complete ' )
2008-07-03 22:48:29 +00:00
2008-07-04 06:29:32 +00:00
def openurl ( self , url ) :
2008-07-03 22:48:29 +00:00
dbg ( ' openurl: viewing %s ' % url )
try :
2008-07-16 23:28:04 +00:00
dbg ( ' openurl: calling xdg-open ' )
2008-07-03 22:48:29 +00:00
if subprocess . call ( [ " xdg-open " , url ] ) != 0 :
dbg ( ' openurl: xdg-open failed ' )
raise
except :
try :
dbg ( ' openurl: calling url_show ' )
2008-07-04 06:29:32 +00:00
self . url_show ( url )
2008-07-03 22:48:29 +00:00
except :
dbg ( ' openurl: url_show failed. No URL for you ' )
pass
2008-08-03 02:09:38 +00:00
def on_resize_window ( self , widget , width , height ) :
dbg ( ' Resize window triggered on %s : %d x %d ' % ( widget , width , height ) )
2008-06-26 23:24:52 +00:00
def on_drag_begin ( self , widget , drag_context , data ) :
dbg ( ' Drag begins ' )
widget . drag_source_set_icon_pixbuf ( self . terminator . icon_theme . load_icon ( APP_NAME , 48 , 0 ) )
2008-08-03 02:09:38 +00:00
2008-06-26 23:24:52 +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 ) ) )
2008-07-03 22:48:29 +00:00
2008-07-16 20:00:43 +00:00
def on_expose_event ( self , widget , event ) :
if widget . _expose_data is None :
return False
color = widget . _expose_data [ ' color ' ]
coord = widget . _expose_data [ ' coord ' ]
context = widget . window . cairo_create ( )
#leaving those xxx_group as they could be usefull
##http://macslow.thepimp.net/?p=153
#context.push_group()
context . set_source_rgba ( color . red , color . green , color . blue , 0.5 )
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 ( )
#context.pop_group_to_source()
#context.paint()
return False
2008-08-03 02:09:38 +00:00
2008-06-26 23:24:52 +00:00
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 )
2008-07-16 20:00:43 +00:00
2008-06-26 23:24:52 +00:00
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 )
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 )
2008-07-16 19:17:53 +00:00
2008-07-16 20:00:43 +00:00
#here, we define some widget internal values
widget . _expose_data = { ' color ' : color , ' coord ' : coord }
#redraw by forcing an event
2008-07-17 21:09:41 +00:00
connec = widget . connect ( ' expose-event ' , self . on_expose_event )
2008-07-16 20:00:43 +00:00
widget . window . invalidate_rect ( rect , True )
widget . window . process_updates ( True )
#finaly reset the values
2008-07-17 21:09:41 +00:00
widget . disconnect ( connec )
2008-07-16 20:00:43 +00:00
widget . _expose_data = None
2008-07-16 19:17:53 +00:00
2008-08-03 02:09:38 +00:00
2008-06-26 23:24:52 +00:00
def on_drag_drop ( self , widget , drag_context , x , y , time ) :
parent = widget . get_parent ( )
dbg ( ' Drag drop on %s ' % parent )
2008-08-03 02:09:38 +00:00
2008-06-26 23:24:52 +00:00
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
2008-08-03 02:09:38 +00:00
2008-06-26 23:24:52 +00:00
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
2008-07-03 22:48:29 +00:00
#determine position in rectangle
2008-06-26 23:24:52 +00:00
"""
- - - - - - - -
| \ / |
| \ / |
| \/ |
| / \ |
| / \ |
| / \|
- - - - - - - -
"""
if ( x * coef1 + b1 > y ) and ( x * coef2 + b2 < y ) :
pos = " right "
if ( x * coef1 + b1 > y ) and ( x * coef2 + b2 > y ) :
pos = " top "
if ( x * coef1 + b1 < y ) and ( x * coef2 + b2 > y ) :
pos = " left "
if ( x * coef1 + b1 < y ) and ( x * coef2 + b2 < y ) :
pos = " bottom "
return pos
2008-08-03 02:09:38 +00:00
def add_matches ( self , posix = True ) :
2008-06-26 23:24:52 +00:00
userchars = " -A-Za-z0-9 "
passchars = " -A-Za-z0-9,?;.:/! % $^*&~ \" # ' "
hostchars = " -A-Za-z0-9 "
pathchars = " -A-Za-z0-9_$.+!*(),;:@&=?/~# % ' "
schemes = " (news:|telnet:|nntp:|file:/|https?:|ftps?:|webcal:) "
user = " [ " + userchars + " ]+(:[ " + passchars + " ]+)? "
urlpath = " /[ " + pathchars + " ]*[^] ' .}>) \t \r \n , \\ \" ] "
2008-08-02 22:32:10 +00:00
2008-08-03 02:09:38 +00:00
if posix :
dbg ( ' add_matches: Trying POSIX URL regexps. Set try_posix_regexp = False in config to only try GNU if you get (harmless) VTE warnings. ' )
lboundry = " [[:<:]] "
rboundry = " [[:>:]] "
else : # GNU
dbg ( ' add_matches: Trying GNU URL regexps. Set try_posix_regexp = True in config if URLs are not detected. ' )
lboundry = " \\ < "
rboundry = " \\ > "
2008-07-13 22:16:19 +00:00
self . matches [ ' full_uri ' ] = self . _vte . match_add ( lboundry + schemes + " //( " + user + " @)?[ " + hostchars + " .]+(:[0-9]+)?( " + urlpath + " )? " + rboundry + " /? " )
2008-06-26 23:24:52 +00:00
2008-07-13 22:16:19 +00:00
if self . matches [ ' full_uri ' ] == - 1 :
2008-08-03 02:09:38 +00:00
if posix :
err ( ' add_matches: POSIX match failed, trying GNU ' )
self . add_matches ( posix = False )
else :
err ( ' add_matches: Failed adding URL match patterns ' )
2008-06-26 23:24:52 +00:00
else :
2008-07-13 22:16:19 +00:00
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-zA-Z0-9][a-zA-Z0-9.+-]*@[a-zA-Z0-9][a-zA-Z0-9-]* \ .[a-zA-Z0-9][a-zA-Z0-9-]+[.a-zA-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 )
2008-06-26 23:24:52 +00:00
2008-07-17 17:32:24 +00:00
2008-07-20 21:15:14 +00:00
def _path_lookup ( self , command ) :
if os . path . isabs ( command ) :
if os . path . isfile ( command ) :
return command
else :
return None
elif command [ : 2 ] == ' ./ ' and os . path . isfile ( command ) :
dbg ( ' path_lookup: Relative filename " %s " found in cwd ' % command )
return command
try :
paths = os . environ [ ' PATH ' ] . split ( ' : ' )
if len ( paths [ 0 ] ) == 0 : raise ( ValueError )
except ( ValueError , NameError ) :
dbg ( ' path_lookup: PATH not set in environment, using fallbacks ' )
paths = [ ' /usr/local/bin ' , ' /usr/bin ' , ' /bin ' ]
dbg ( ' path_lookup: Using %d paths: %s ' % ( len ( paths ) , paths ) )
for path in paths :
target = os . path . join ( path , command )
if os . path . isfile ( target ) :
dbg ( ' path_lookup: found " %s " ' % target )
return target
dbg ( ' path_lookup: Unable to locate " %s " ' % command )
def _shell_lookup ( self ) :
shells = [ os . getenv ( ' SHELL ' ) , pwd . getpwuid ( os . getuid ( ) ) [ 6 ] ,
' bash ' , ' zsh ' , ' tcsh ' , ' ksh ' , ' csh ' , ' sh ' ]
for shell in shells :
if shell is None : continue
elif os . path . isfile ( shell ) :
return shell
else :
rshell = self . _path_lookup ( shell )
if rshell is not None :
dbg ( ' shell_lookup: Found " %s " at " %s " ' % ( shell , rshell ) )
return rshell
dbg ( ' shell_lookup: Unable to locate a shell ' )
2008-06-26 23:24:52 +00:00
def spawn_child ( self , event = None ) :
update_records = self . conf . update_records
login = self . conf . login_shell
args = [ ]
2008-07-20 21:15:14 +00:00
shell = None
command = None
2008-06-26 23:24:52 +00:00
if self . command :
2008-07-17 17:32:24 +00:00
dbg ( ' spawn_child: using self.command: %s ' % self . command )
2008-07-20 21:15:14 +00:00
command = self . command
2008-06-26 23:24:52 +00:00
elif self . conf . use_custom_command :
2008-07-17 17:32:24 +00:00
dbg ( ' spawn_child: using custom command: %s ' % self . conf . custom_command )
2008-07-20 21:15:14 +00:00
command = self . conf . custom_command
2008-06-26 23:24:52 +00:00
2008-07-20 21:15:14 +00:00
if type ( command ) is list :
# List of arguments from -x
dbg ( ' spawn_child: Bypassing shell and trying to run " %s " directly ' % command [ 0 ] )
shell = self . _path_lookup ( command [ 0 ] )
args = command
else :
shell = self . _shell_lookup ( )
if self . conf . login_shell :
args . insert ( 0 , " - %s " % shell )
else :
args . insert ( 0 , shell )
if command is not None :
args + = [ ' -c ' , command ]
if shell is None :
2008-06-26 23:24:52 +00:00
# Give up, we're completely stuck
err ( _ ( ' Unable to find a shell ' ) )
gobject . timeout_add ( 100 , self . terminator . closeterm , self )
return ( - 1 )
dbg ( ' SEGBUG: Setting WINDOWID ' )
2008-07-17 17:32:24 +00:00
os . putenv ( ' WINDOWID ' , ' %s ' % self . _vte . get_parent_window ( ) . xid )
2008-06-26 23:24:52 +00:00
2008-07-17 17:32:24 +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-06-26 23:24:52 +00:00
2008-08-07 20:34:47 +00:00
dbg ( ' SEGBUG: Forked command ' )
self . on_vte_title_change ( self . _vte ) # Force an initial update of our titles
2008-06-26 23:24:52 +00:00
if self . _pid == - 1 :
err ( _ ( ' Unable to start shell: ' ) + shell )
return ( - 1 )
2008-07-17 17:32:24 +00:00
2008-06-26 23:24:52 +00:00
def get_cwd ( self ) :
""" Return the current working directory of the subprocess.
This function requires OS specific behaviours
"""
2008-07-03 22:48:29 +00:00
cwd = self . pid_get_cwd ( self . _pid )
2008-06-26 23:24:52 +00:00
dbg ( ' get_cwd found: %s ' % cwd )
return ( cwd )
def reconfigure_vte ( self ) :
# Set our emulation
self . _vte . set_emulation ( self . conf . emulation )
# Set our wordchars
self . _vte . set_word_chars ( self . conf . word_chars )
# Set our mouselation
self . _vte . set_mouse_autohide ( self . conf . mouse_autohide )
# Set our compatibility
backspace = self . conf . backspace_binding
delete = self . conf . delete_binding
# 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 )
# Set our font
try :
self . _vte . set_font ( pango . FontDescription ( self . conf . font ) )
except :
pass
# Set our boldness
self . _vte . set_allow_bold ( self . conf . allow_bold )
# Set our color scheme
palette = self . conf . palette
if self . conf . use_theme_colors :
fg_color = self . _vte . get_style ( ) . text [ gtk . STATE_NORMAL ]
bg_color = self . _vte . get_style ( ) . base [ gtk . STATE_NORMAL ]
else :
fg_color = gtk . gdk . color_parse ( self . conf . foreground_color )
bg_color = gtk . gdk . color_parse ( self . conf . background_color )
colors = palette . split ( ' : ' )
palette = [ ]
for color in colors :
if color :
palette . append ( gtk . gdk . color_parse ( color ) )
self . _vte . set_colors ( fg_color , bg_color , palette )
# Set our background image, transparency and type
# Many thanks to the authors of gnome-terminal, on which this code is based.
background_type = self . conf . background_type
# set background image settings
if background_type == " image " :
self . _vte . set_background_image_file ( self . conf . background_image )
self . _vte . set_scroll_background ( self . conf . scroll_background )
else :
self . _vte . set_background_image_file ( ' ' )
self . _vte . set_scroll_background ( False )
# set transparency for the background (image)
if background_type in ( " image " , " transparent " ) :
self . _vte . set_background_tint_color ( bg_color )
self . _vte . set_background_saturation ( 1 - ( self . conf . background_darkness ) )
self . _vte . set_opacity ( int ( self . conf . background_darkness * 65535 ) )
else :
self . _vte . set_background_saturation ( 1 )
self . _vte . set_opacity ( 65535 )
if not self . _vte . is_composited ( ) :
self . _vte . set_background_transparent ( background_type == " transparent " )
else :
self . _vte . set_background_transparent ( False )
# Set our cursor blinkiness
2008-07-12 15:26:33 +00:00
self . _vte . set_cursor_blinks ( self . conf . cursor_blink )
2008-06-26 23:24:52 +00:00
# Set our audible belliness
silent_bell = self . conf . silent_bell
self . _vte . set_audible_bell ( not silent_bell )
# Set our visual flashiness
self . _vte . set_visible_bell ( silent_bell )
# Override our flashybelliness
if self . conf . force_no_bell :
self . _vte . set_visible_bell ( False )
self . _vte . set_audible_bell ( False )
# Set our scrolliness
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 )
if self . scrollbar_position != self . conf . scrollbar_position :
self . scrollbar_position = self . conf . scrollbar_position
if self . scrollbar_position == ' hidden ' or self . scrollbar_position == ' disabled ' :
self . _scrollbar . hide ( )
else :
self . _scrollbar . show ( )
if self . scrollbar_position == ' right ' :
self . _termbox . reorder_child ( self . _vte , 0 )
elif self . scrollbar_position == ' left ' :
self . _termbox . reorder_child ( self . _scrollbar , 0 )
# Set our sloppiness
self . focus = self . conf . focus
self . _vte . queue_draw ( )
def on_composited_changed ( self , widget ) :
self . reconfigure_vte ( )
def on_vte_button_press ( self , term , event ) :
# 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 ]
2008-07-04 06:29:32 +00:00
self . openurl ( address )
2008-06-26 23:24:52 +00:00
return False
# Left mouse button should transfer focus to this vte widget
# we also need to give focus on the widget where the paste occured
if event . button in ( 1 , 2 ) :
self . _vte . grab_focus ( )
return False
# Right mouse button should display a context menu if ctrl not pressed
if event . button == 3 and event . state & gtk . gdk . CONTROL_MASK == 0 :
2008-08-06 22:12:45 +00:00
self . create_popup_menu ( self . _vte , event )
2008-06-26 23:24:52 +00:00
return True
def on_vte_notify_enter ( self , term , event ) :
if ( self . focus == " sloppy " or self . focus == " mouse " ) :
term . grab_focus ( )
return False
def do_scrollbar_toggle ( self ) :
self . toggle_widget_visibility ( self . _scrollbar )
def do_title_toggle ( self ) :
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 ( )
else :
widget . show ( )
def paste_clipboard ( self ) :
self . _vte . paste_clipboard ( )
self . _vte . grab_focus ( )
#keybindings for the individual splited terminals (affects only the
#the selected terminal)
2008-08-11 18:02:14 +00:00
UnhandledKeybindings = ( ' close_window ' , ' full_screen ' )
2008-06-26 23:24:52 +00:00
def on_vte_key_press ( self , term , event ) :
2008-08-11 18:02:14 +00:00
mapping = self . terminator . keybindings . lookup ( event )
2008-06-26 23:24:52 +00:00
2008-08-11 18:02:14 +00:00
if mapping and mapping not in self . UnhandledKeybindings :
dbg ( " on_vte_key_press: lookup found %s " % repr ( mapping ) )
getattr ( self , " key_ " + mapping ) ( )
return True
return False
# Key events
def key_zoom_in ( self ) : self . zoom ( True )
def key_zoom_out ( self ) : self . zoom ( False )
def key_copy ( self ) : self . _vte . copy_clipboard ( )
def key_paste ( self ) : self . paste_clipboard ( )
def key_toggle_scrollbar ( self ) : self . do_scrollbar_toggle ( )
def key_zoom_normal ( self ) : self . zoom_orig ( )
# bindings that should be moved to Terminator as they all just call
# a function of Terminator. It would be cleaner if TerminatorTerm
# has absolutely no reference to Terminator.
# N (next) - P (previous) - O (horizontal) - E (vertical) - W (close)
def key_new_root_tab ( self ) :
self . terminator . newtab ( self , True )
def key_go_next ( self ) :
self . terminator . go_next ( self )
def key_go_prev ( self ) :
self . terminator . go_prev ( self )
def key_split_horiz ( self ) :
self . terminator . splitaxis ( self , False )
def key_split_vert ( self ) :
self . terminator . splitaxis ( self , True )
def key_close_term ( self ) :
self . terminator . closeterm ( self )
def key_new_tab ( self ) :
self . terminator . newtab ( self )
def key_resize_up ( self ) :
self . terminator . resizeterm ( self , ' Up ' )
def key_resize_down ( self ) :
self . terminator . resizeterm ( self , ' Down ' )
def key_resize_left ( self ) :
self . terminator . resizeterm ( self , ' Left ' )
def key_resize_right ( self ) :
self . terminator . resizeterm ( self , ' Right ' )
def key_move_tab_right ( self ) :
self . terminator . move_tab ( self , ' right ' )
def key_move_tab_right ( self ) :
self . terminator . move_tab ( self , ' left ' )
def key_toggle_zoom ( self ) :
self . terminator . toggle_zoom ( self )
def key_scaled_zoom ( self ) :
self . terminator . toggle_zoom ( self , True )
def key_go_prev ( self ) :
self . terminator . go_prev ( self )
def key_go_next ( self ) :
self . terminator . go_next ( self )
# End key events
2008-06-26 23:24:52 +00:00
def zoom_orig ( self ) :
self . _vte . set_font ( pango . FontDescription ( self . conf . font ) )
def zoom ( self , zoom_in ) :
pangodesc = self . _vte . get_font ( )
fontsize = pangodesc . get_size ( )
if fontsize > pango . SCALE and not zoom_in :
fontsize - = pango . SCALE
elif zoom_in :
fontsize + = pango . SCALE
pangodesc . set_size ( fontsize )
self . _vte . set_font ( pangodesc )
2008-08-06 22:12:45 +00:00
def create_popup_menu ( self , widget , event = None ) :
2008-06-26 23:24:52 +00:00
menu = gtk . Menu ( )
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 ( ) ) )
2008-08-06 22:12:45 +00:00
button = event . button
time = event . time
else :
button = 0
time = 0
2008-06-26 23:24:52 +00:00
if url :
if url [ 1 ] != self . matches [ ' email ' ] :
address = url [ 0 ]
nameopen = _ ( " _Open Link " )
namecopy = _ ( " _Copy Link Address " )
iconopen = gtk . image_new_from_stock ( gtk . STOCK_JUMP_TO , gtk . ICON_SIZE_MENU )
item = gtk . ImageMenuItem ( nameopen )
item . set_property ( ' image ' , iconopen )
else :
if url [ 0 ] [ 0 : 7 ] != " mailto: " :
address = " mailto: " + url [ 0 ]
else :
address = url [ 0 ]
nameopen = _ ( " _Send Mail To... " )
namecopy = _ ( " _Copy Email Address " )
item = gtk . MenuItem ( nameopen )
2008-07-04 06:29:32 +00:00
item . connect ( " activate " , lambda menu_item : self . openurl ( address ) )
2008-06-26 23:24:52 +00:00
menu . append ( item )
item = gtk . MenuItem ( namecopy )
item . connect ( " activate " , lambda menu_item : self . clipboard . set_text ( url [ 0 ] ) )
menu . append ( item )
item = gtk . MenuItem ( )
menu . append ( item )
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 )
item = gtk . ImageMenuItem ( gtk . STOCK_PASTE )
item . connect ( " activate " , lambda menu_item : self . paste_clipboard ( ) )
menu . append ( item )
item = gtk . MenuItem ( )
menu . append ( item )
item = gtk . CheckMenuItem ( _ ( " Show _scrollbar " ) )
item . set_active ( self . _scrollbar . get_property ( ' visible ' ) )
item . connect ( " toggled " , lambda menu_item : self . do_scrollbar_toggle ( ) )
menu . append ( item )
item = gtk . CheckMenuItem ( _ ( " Show _titlebar " ) )
item . set_active ( self . _titlebox . get_property ( ' visible ' ) )
item . connect ( " toggled " , lambda menu_item : self . do_title_toggle ( ) )
menu . append ( item )
self . _do_encoding_items ( menu )
item = gtk . MenuItem ( )
menu . append ( item )
if not self . terminator . _zoomed :
str_horiz = _ ( " Split H_orizontally " )
str_vert = _ ( " Split V_ertically " )
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 )
item . connect ( " activate " , lambda menu_item : self . terminator . splitaxis ( self , False ) )
menu . append ( item )
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 )
item . connect ( " activate " , lambda menu_item : self . terminator . splitaxis ( self , True ) )
menu . append ( item )
2008-08-07 20:34:47 +00:00
2008-06-26 23:24:52 +00:00
item = gtk . MenuItem ( _ ( " Open _Tab " ) )
item . connect ( " activate " , lambda menu_item : self . terminator . newtab ( self ) )
menu . append ( item )
2008-08-07 20:34:47 +00:00
if self . terminator . debugaddress :
item = gtk . MenuItem ( _ ( " Open _Debug Tab " ) )
item . connect ( " activate " , lambda menu_item : self . terminator . newtab ( self , command = " telnet %s " % ' ' . join ( [ str ( x ) for x in self . terminator . debugaddress ] ) ) )
menu . append ( item )
2008-06-26 23:24:52 +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 )
item = gtk . MenuItem ( )
menu . append ( item )
if len ( self . terminator . term_list ) > 1 :
if not self . terminator . _zoomed :
item = gtk . MenuItem ( _ ( " _Zoom terminal " ) )
item . connect ( " activate " , lambda menu_item : self . terminator . toggle_zoom ( self , True ) )
menu . append ( item )
2008-07-11 23:56:44 +00:00
item = gtk . MenuItem ( _ ( " Ma_ximise terminal " ) )
2008-06-26 23:24:52 +00:00
item . connect ( " activate " , lambda menu_item : self . terminator . toggle_zoom ( self ) )
menu . append ( item )
else :
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-07-11 23:56:44 +00:00
item = gtk . MenuItem ( _ ( " Unma_ximise terminal " ) )
2008-06-26 23:24:52 +00:00
item . connect ( " activate " , lambda menu_item : self . terminator . toggle_zoom ( self ) )
menu . append ( item )
item = gtk . MenuItem ( )
menu . append ( item )
item = gtk . ImageMenuItem ( gtk . STOCK_CLOSE )
item . connect ( " activate " , lambda menu_item : self . terminator . closeterm ( self ) )
menu . append ( item )
menu . show_all ( )
2008-08-06 22:12:45 +00:00
menu . popup ( None , None , None , button , time )
return True
2008-06-26 23:24:52 +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-08-05 10:28:46 +00:00
2008-08-07 22:07:21 +00:00
def get_window_title ( self , vte = None ) :
if vte is None :
vte = self . _vte
2008-08-07 20:34:47 +00:00
title = vte . get_window_title ( )
if title is None :
title = str ( self . command )
return title
2008-06-26 23:24:52 +00:00
def on_vte_title_change ( self , vte ) :
2008-08-07 22:07:21 +00:00
title = self . get_window_title ( vte )
2008-06-26 23:24:52 +00:00
if self . conf . titletips :
vte . set_property ( " has-tooltip " , True )
2008-08-07 20:34:47 +00:00
vte . set_property ( " tooltip-text " , title )
2008-06-26 23:24:52 +00:00
#set the title anyhow, titlebars setting only show/hide the label
2008-08-07 20:34:47 +00:00
self . _title . set_text ( title )
self . terminator . set_window_title ( " %s - %s " % ( title , APP_NAME . capitalize ( ) ) )
2008-06-26 23:24:52 +00:00
notebookpage = self . terminator . get_first_notebook_page ( vte )
while notebookpage != None :
2008-06-27 17:11:24 +00:00
if notebookpage [ 0 ] . get_tab_label ( notebookpage [ 1 ] ) :
label = notebookpage [ 0 ] . get_tab_label ( notebookpage [ 1 ] )
2008-08-07 20:34:47 +00:00
label . set_title ( title )
2008-06-27 17:11:24 +00:00
notebookpage [ 0 ] . set_tab_label ( notebookpage [ 1 ] , label )
2008-06-26 23:24:52 +00:00
notebookpage = self . terminator . get_first_notebook_page ( notebookpage [ 0 ] )
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
2008-08-05 10:28:46 +00:00
2008-06-26 23:24:52 +00:00
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
def on_vte_focus ( self , vte ) :
2008-08-07 22:07:21 +00:00
title = self . get_window_title ( vte )
2008-08-07 20:34:47 +00:00
self . terminator . set_window_title ( " %s - %s " % ( title , APP_NAME . capitalize ( ) ) )
notebookpage = self . terminator . get_first_notebook_page ( vte )
while notebookpage != None :
if notebookpage [ 0 ] . get_tab_label ( notebookpage [ 1 ] ) :
label = notebookpage [ 0 ] . get_tab_label ( notebookpage [ 1 ] )
label . set_title ( title )
notebookpage [ 0 ] . set_tab_label ( notebookpage [ 1 ] , label )
notebookpage = self . terminator . get_first_notebook_page ( notebookpage [ 0 ] )
2008-06-26 23:24:52 +00:00
def destroy ( self ) :
self . _vte . destroy ( )