~ubuntu-branches/ubuntu/saucy/terminator/saucy

« back to all changes in this revision

Viewing changes to terminatorlib/terminatorterm.py

  • Committer: Bazaar Package Importer
  • Author(s): Nicolas Valcárcel Scerpella (Canonical)
  • Date: 2010-04-07 17:10:31 UTC
  • mfrom: (1.1.7 upstream)
  • Revision ID: james.westby@ubuntu.com-20100407171031-35nsuj0tmbub0bj5
Tags: 0.92-0ubuntu1
* New upstream release
* Remove python-xdg from Recommends. (Closes: #567967)
* Downgrade python-gnome2 to Recommends.
* Update python-gtk2 dependency to (>= 2.14.0)
* Add python-keybinder to Recommends

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#!/usr/bin/python
2
 
#    Terminator - multiple gnome terminals in one window
3
 
#    Copyright (C) 2006-2008  cmsj@tenshu.net
4
 
#
5
 
#    This program is free software; you can redistribute it and/or modify
6
 
#    it under the terms of the GNU General Public License as published by
7
 
#    the Free Software Foundation, version 2 only.
8
 
#
9
 
#    This program is distributed in the hope that it will be useful,
10
 
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 
#    GNU General Public License for more details.
13
 
#
14
 
#    You should have received a copy of the GNU General Public License
15
 
#    along with this program; if not, write to the Free Software
16
 
#    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
17
 
 
18
 
"""Terminator by Chris Jones <cmsj@tenshu.net>"""
19
 
import pygtk
20
 
pygtk.require ("2.0")
21
 
import gobject, gtk, pango
22
 
import os, signal, sys, subprocess, pwd, re, urllib2
23
 
 
24
 
#import version details
25
 
from terminatorlib.version import *
26
 
 
27
 
# import our configuration loader
28
 
from terminatorlib import config
29
 
from terminatorlib.config import dbg, err, debug
30
 
 
31
 
#import encoding list
32
 
from terminatorlib.encoding import TerminatorEncoding
33
 
from terminatorlib.terminatoreditablelabel import TerminatorEditableLabel
34
 
# import translation support
35
 
from terminatorlib import translation
36
 
 
37
 
# import vte-bindings
38
 
try:
39
 
  import vte
40
 
except ImportError:
41
 
  error = gtk.MessageDialog (None, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK,
42
 
    _('You need to install python bindings for libvte ("python-vte" in debian/ubuntu)'))
43
 
  error.run()
44
 
  sys.exit (1)
45
 
 
46
 
class TerminatorTermTitle (gtk.EventBox):
47
 
  wanted = None
48
 
  _title = None
49
 
  _termtext = ""
50
 
  _sizetext = ""
51
 
  _group = None
52
 
  _separator = None
53
 
  _hbox = None
54
 
  _ebox = None
55
 
  _grouphbox = None
56
 
  _icon = None
57
 
  _parent = None
58
 
  _unzoomed_title = None
59
 
  _terminal = None
60
 
  terminator = None
61
 
 
62
 
  def __init__ (self, terminal, terminator, configwanted = False):
63
 
    gtk.EventBox.__init__ (self)
64
 
 
65
 
    self._title = TerminatorEditableLabel()
66
 
    self._group = gtk.Label ()
67
 
    self._separator = gtk.VSeparator ()
68
 
    self._ebox = gtk.EventBox ()
69
 
    self._grouphbox = gtk.HBox ()
70
 
    self._icon = gtk.Image ()
71
 
    self._hbox = gtk.HBox ()
72
 
    self._terminal = terminal
73
 
 
74
 
    self.terminator = terminator
75
 
    if self.terminator.groupsend == 2:
76
 
      self.set_from_icon_name (APP_NAME + \
77
 
              '_active_broadcast_all', gtk.ICON_SIZE_MENU)
78
 
    elif self.terminator.groupsend == 1:
79
 
      self.set_from_icon_name (APP_NAME + \
80
 
              '_active_broadcast_group', gtk.ICON_SIZE_MENU)
81
 
    else:
82
 
      self.set_from_icon_name (APP_NAME + \
83
 
              '_active_broadcast_off', gtk.ICON_SIZE_MENU)
84
 
 
85
 
    self._grouphbox.pack_start (self._icon, False, True, 2)
86
 
    self._grouphbox.pack_start (self._group, False, True, 2)
87
 
    self._ebox.add (self._grouphbox)
88
 
    self._ebox.show_all ()
89
 
 
90
 
    self._hbox.pack_start (self._ebox, False, True, 0)
91
 
    self._hbox.pack_start (self._separator, False, True, 0)
92
 
    self._hbox.pack_start (self._title, True, True)
93
 
    self.add (self._hbox)
94
 
 
95
 
    self._title.show_all ()
96
 
    self._hbox.show ()
97
 
 
98
 
    self.wanted = configwanted
99
 
 
100
 
    self.connect ("button-press-event", self.on_clicked)
101
 
 
102
 
  def connect_icon (self, func):
103
 
    self._ebox.connect ("button-release-event", func)
104
 
 
105
 
  def on_clicked (self, widget, event):
106
 
    if self._parent is not None:
107
 
      self._parent._vte.grab_focus ()
108
 
 
109
 
  def set_group_label (self, name):
110
 
    """If 'name' is None, hide the group name object, otherwise set it as the group label"""
111
 
    if name:
112
 
      self._group.set_text (name)
113
 
      self._group.show ()
114
 
    else:
115
 
      self._group.hide ()
116
 
    self._separator.show ()
117
 
 
118
 
  def set_terminal_title (self, name):
119
 
    """Set the title text shown in the titlebar"""
120
 
    self._termtext = name
121
 
    self.update_label ()
122
 
 
123
 
  def set_terminal_size (self, width, height):
124
 
    """Set the terminal size shown in the titlebar"""
125
 
    self._sizetext = "%sx%s" % (width, height)
126
 
    self.update_label ()
127
 
 
128
 
  def update_label (self):
129
 
    """Update the gtk label with values previously set"""
130
 
    self._title.set_text ("%s %s" % (self._termtext, self._sizetext))
131
 
 
132
 
  def get_terminal_title (self):
133
 
    """Return the text showin in the titlebar"""
134
 
    return (self._termtext)
135
 
 
136
 
  def set_from_icon_name (self, name, size = gtk.ICON_SIZE_MENU):
137
 
    """Set an icon for the group label"""
138
 
    if not name:
139
 
      self._icon.hide ()
140
 
      return
141
 
 
142
 
    self._icon.set_from_icon_name (APP_NAME + name, size)
143
 
    self._icon.show ()
144
 
    
145
 
  def update_colors(self, source):
146
 
    """Update terminals titlebar colours based on grouping"""
147
 
    term = self._terminal
148
 
    if term != source and term._group != None and term._group == source._group:
149
 
      # Not active, group is not none, and in active's group
150
 
      if self.terminator.groupsend == 0:
151
 
        title_fg = term.conf.title_ia_txt_color
152
 
        title_bg = term.conf.title_ia_bg_color
153
 
        icon = '_receive_off'
154
 
      else:
155
 
        title_fg = term.conf.title_rx_txt_color
156
 
        title_bg = term.conf.title_rx_bg_color
157
 
        icon = '_receive_on'
158
 
      group_fg = term.conf.title_rx_txt_color
159
 
      group_bg = term.conf.title_rx_bg_color
160
 
    elif term != source and term._group == None or term._group != source._group:
161
 
      # Not active, group is not none, not in active's group
162
 
      if self.terminator.groupsend == 2:
163
 
        title_fg = term.conf.title_rx_txt_color
164
 
        title_bg = term.conf.title_rx_bg_color
165
 
        icon = '_receive_on'
166
 
      else:
167
 
        title_fg = term.conf.title_ia_txt_color
168
 
        title_bg = term.conf.title_ia_bg_color
169
 
        icon = '_receive_off'
170
 
      group_fg = term.conf.title_ia_txt_color
171
 
      group_bg = term.conf.title_ia_bg_color
172
 
    else:
173
 
      title_fg = term.conf.title_tx_txt_color
174
 
      title_bg = term.conf.title_tx_bg_color
175
 
      if self.terminator.groupsend == 2:
176
 
        icon = '_active_broadcast_all'
177
 
      elif self.terminator.groupsend == 1:
178
 
        icon = '_active_broadcast_group'
179
 
      else:
180
 
        icon = '_active_broadcast_off'
181
 
      group_fg = term.conf.title_tx_txt_color
182
 
      group_bg = term.conf.title_tx_bg_color
183
 
 
184
 
    self._title.modify_fg (gtk.STATE_NORMAL, gtk.gdk.color_parse (title_fg))
185
 
    self._group.modify_fg (gtk.STATE_NORMAL, gtk.gdk.color_parse (group_fg))
186
 
    self.modify_bg (gtk.STATE_NORMAL, gtk.gdk.color_parse (title_bg))
187
 
    self._ebox.modify_bg (gtk.STATE_NORMAL, gtk.gdk.color_parse (group_bg))
188
 
    self.set_from_icon_name(icon, gtk.ICON_SIZE_MENU)
189
 
    return
190
 
 
191
 
  def update (self):
192
 
    """Update our state"""
193
 
    if not self._parent:
194
 
      self._parent = self.get_parent ()
195
 
 
196
 
    if self._parent.terminator._zoomed and len (self._parent.terminator.term_list):
197
 
      if not self._unzoomed_title:
198
 
        self._unzoomed_title = self.get_terminal_title ()
199
 
      if self._parent.conf.zoomedtitlebar:
200
 
        self.set_terminal_title ("Zoomed/Maximised terminal, %d hidden" % (len (self._parent.terminator.term_list) - 1))
201
 
        self.show()
202
 
      else:
203
 
        self.hide()
204
 
      return
205
 
    else:
206
 
      if self._unzoomed_title:
207
 
        self.set_terminal_title (self._unzoomed_title)
208
 
        self._unzoomed_title = None
209
 
 
210
 
    if isinstance (self._parent.get_parent (), gtk.Window):
211
 
      self.hide()
212
 
      return
213
 
 
214
 
    if (self._parent.conf.titlebars and self.wanted) or self._parent._group:
215
 
      self.show ()
216
 
    else:
217
 
      self.hide ()
218
 
 
219
 
    if self._parent._group:
220
 
      self.set_group_label (self._parent._group)
221
 
    else:
222
 
      self.set_group_label (None)
223
 
 
224
 
class TerminatorTerm (gtk.VBox):
225
 
 
226
 
  matches = None
227
 
  TARGET_TYPE_VTE = 8
228
 
  _custom_font_size = None
229
 
  _custom_encoding = None
230
 
  _default_encoding = None
231
 
  _group = None
232
 
  focus = None
233
 
  _urgent_bell_cnid = None
234
 
 
235
 
  def __init__ (self, terminator, profile = None, command = None, cwd = None):
236
 
    gtk.VBox.__init__ (self)
237
 
    self.terminator = terminator
238
 
    self.conf = terminator.conf
239
 
    self.command = command
240
 
    self._oldtitle = ""
241
 
    self.matches = {}
242
 
 
243
 
    self.cwd = cwd or os.getcwd();
244
 
    if not os.path.exists(self.cwd) or not os.path.isdir(self.cwd):
245
 
      self.cwd = pwd.getpwuid(os.getuid ())[5]
246
 
 
247
 
    self.clipboard = gtk.clipboard_get (gtk.gdk.SELECTION_CLIPBOARD)
248
 
    self.scrollbar_position = self.conf.scrollbar_position
249
 
 
250
 
    self._composited_support = True
251
 
    self._vte = vte.Terminal ()
252
 
    self._default_encoding = self._vte.get_encoding()
253
 
    if not hasattr(self._vte, "set_opacity") or not hasattr(self._vte, "is_composited"):
254
 
      self._composited_support = False
255
 
    dbg ('H9TRANS: composited_support: %s' % self._composited_support)
256
 
    #self._vte.set_double_buffered(True)
257
 
    self._vte.set_size (80, 24)
258
 
    self._vte._expose_data = None
259
 
    self._vte.show ()
260
 
 
261
 
    self._termbox = gtk.HBox ()
262
 
    self._termbox.show()
263
 
    
264
 
    self._titlebox = TerminatorTermTitle (self, self.terminator, self.conf.titlebars)
265
 
 
266
 
    self._search_string = None
267
 
    self._searchbox = gtk.HBox()
268
 
    self._searchinput = gtk.Entry()
269
 
    self._searchinput.set_activates_default(True)
270
 
    self._searchinput.show()
271
 
 
272
 
    self._searchinput.connect('activate', self.do_search)
273
 
    self._searchinput.connect('key-press-event', self.search_keypress)
274
 
 
275
 
    slabel = gtk.Label()
276
 
    slabel.set_text(_("Search:"))
277
 
    slabel.show()
278
 
 
279
 
    sclose = gtk.Button()
280
 
    sclose.set_relief(gtk.RELIEF_NONE)
281
 
    sclose.set_focus_on_click(False)
282
 
    sclose.set_relief(gtk.RELIEF_NONE)
283
 
    sclose_icon = gtk.Image()
284
 
    sclose_icon.set_from_stock(gtk.STOCK_CLOSE, gtk.ICON_SIZE_MENU)
285
 
    sclose.add(sclose_icon)
286
 
    sclose.set_name("terminator-search-close-button")
287
 
    if hasattr(sclose, "set_tooltip_text"):
288
 
      sclose.set_tooltip_text("Close Search Bar")
289
 
    sclose.connect('clicked', self.end_search)
290
 
    sclose.show_all()
291
 
 
292
 
    # Button for the next result. Explicitly not show()n by default.
293
 
    self._search_next = gtk.Button(_("Next"))
294
 
    self._search_next.connect('clicked', self.next_search)
295
 
 
296
 
    self._searchbox.pack_start(slabel, False)
297
 
    self._search_result_label = gtk.Label()
298
 
    self._search_result_label.set_text("")
299
 
    self._search_result_label.show()
300
 
    self._searchbox.pack_start(self._searchinput)
301
 
    self._searchbox.pack_start(self._search_result_label, False)
302
 
    self._searchbox.pack_start(self._search_next, False, False)
303
 
    self._searchbox.pack_end(sclose, False, False)
304
 
 
305
 
    self.show()
306
 
    self.pack_start(self._titlebox, False)
307
 
    self.pack_start(self._termbox)
308
 
    self.pack_end(self._searchbox)
309
 
 
310
 
    self._titlebox.update ()
311
 
 
312
 
    self._scrollbar = gtk.VScrollbar (self._vte.get_adjustment ())
313
 
    if self.scrollbar_position != "hidden" and self.scrollbar_position != "disabled":
314
 
      self._scrollbar.show ()
315
 
 
316
 
    if self.scrollbar_position == 'left':
317
 
      packfunc = self._termbox.pack_end
318
 
    else:
319
 
      packfunc = self._termbox.pack_start
320
 
 
321
 
    packfunc (self._vte)
322
 
    packfunc (self._scrollbar, False)
323
 
 
324
 
    self._vte.connect ("key-press-event", self.on_vte_key_press)
325
 
    self._vte.connect ("button-press-event", self.on_vte_button_press)
326
 
    self._vte.connect ("popup-menu", self.create_popup_menu)
327
 
    """drag and drop"""
328
 
    srcvtetargets = [ ( "vte", gtk.TARGET_SAME_APP, self.TARGET_TYPE_VTE ) ]
329
 
    dsttargets = [ ( "vte", gtk.TARGET_SAME_APP, self.TARGET_TYPE_VTE ), ('text/plain', 0, 0) , ("STRING", 0, 0), ("COMPOUND_TEXT", 0, 0)]
330
 
    self._vte.drag_source_set( gtk.gdk.CONTROL_MASK | gtk.gdk.BUTTON3_MASK, srcvtetargets, gtk.gdk.ACTION_MOVE)
331
 
    self._titlebox.drag_source_set( gtk.gdk.BUTTON1_MASK, srcvtetargets, gtk.gdk.ACTION_MOVE)
332
 
    #self._vte.drag_dest_set(gtk.DEST_DEFAULT_MOTION | gtk.DEST_DEFAULT_HIGHLIGHT |gtk.DEST_DEFAULT_DROP ,dsttargets, gtk.gdk.ACTION_MOVE)
333
 
    self._vte.drag_dest_set(gtk.DEST_DEFAULT_MOTION | gtk.DEST_DEFAULT_HIGHLIGHT |gtk.DEST_DEFAULT_DROP ,dsttargets, gtk.gdk.ACTION_MOVE)
334
 
    self._vte.connect("drag-begin", self.on_drag_begin, self)
335
 
    self._titlebox.connect("drag-begin", self.on_drag_begin, self)
336
 
    self._vte.connect("drag-data-get", self.on_drag_data_get, self)
337
 
    self._titlebox.connect("drag-data-get", self.on_drag_data_get, self)
338
 
    #for testing purpose: drag-motion
339
 
    self._vte.connect("drag-motion", self.on_drag_motion, self)
340
 
    self._vte.connect("drag-data-received", self.on_drag_data_received, self)
341
 
 
342
 
    if self.conf.copy_on_selection:
343
 
      self._vte.connect ("selection-changed", lambda widget: self._vte.copy_clipboard ())
344
 
    if self._composited_support :
345
 
      self._vte.connect ("composited-changed", self.on_composited_changed)
346
 
    self._vte.connect ("window-title-changed", self.on_vte_title_change)
347
 
    self._vte.connect ("grab-focus", self.on_vte_focus)
348
 
    self._vte.connect ("focus-out-event", self.on_vte_focus_out)
349
 
    self._vte.connect ("focus-in-event", self.on_vte_focus_in)
350
 
    self._vte.connect ("resize-window", self.on_resize_window)
351
 
    self._vte.connect ("size-allocate", self.on_vte_size_allocate)
352
 
 
353
 
    self._titlebox.connect_icon (self.on_group_button_press)
354
 
 
355
 
    exit_action = self.conf.exit_action
356
 
    if exit_action == "restart":
357
 
      self._vte.connect ("child-exited", self.spawn_child)
358
 
    # We need to support "left" because some buggy versions of gnome-terminal
359
 
    #  set it in some situations
360
 
    elif exit_action in ("close", "left"):
361
 
      self._vte.connect ("child-exited", lambda close_term: self.terminator.closeterm (self))
362
 
 
363
 
    self._vte.add_events (gtk.gdk.ENTER_NOTIFY_MASK)
364
 
    self._vte.connect ("enter_notify_event", self.on_vte_notify_enter)
365
 
 
366
 
    self._vte.connect_after ("realize", self.reconfigure_vte)
367
 
 
368
 
    self.add_matches(posix = self.conf.try_posix_regexp)
369
 
 
370
 
    env_proxy = os.getenv ('http_proxy')
371
 
    if not env_proxy and self.conf.http_proxy and self.conf.http_proxy != '':
372
 
      os.putenv ('http_proxy', self.conf.http_proxy)
373
 
 
374
 
    os.putenv ('COLORTERM', 'gnome-terminal')
375
 
 
376
 
  def prepareurl (self, url, match):
377
 
    dbg ("prepareurl: Checking '%s' with a match of '%s'" % (url, match))
378
 
    if match == self.matches['email'] and url[0:7] != 'mailto:':
379
 
      url = 'mailto:' + url
380
 
    elif match == self.matches['addr_only'] and url[0:3] == 'ftp':
381
 
      url = 'ftp://' + url
382
 
    elif match == self.matches['addr_only']:
383
 
      url = 'http://' + url
384
 
    elif match == self.matches['launchpad']:
385
 
      for item in re.findall(r'[0-9]+',url):
386
 
        url = 'https://bugs.launchpad.net/bugs/%s' % item
387
 
        return url
388
 
    
389
 
    return url
390
 
 
391
 
  def openurl (self, url):
392
 
    dbg ('openurl: viewing %s'%url)
393
 
    try:
394
 
      dbg ('openurl: calling xdg-open')
395
 
      subprocess.Popen(["xdg-open", url])
396
 
    except:
397
 
      dbg ('openurl: xdg-open failed')
398
 
      try:
399
 
        dbg ('openurl: calling url_show')
400
 
        self.terminator.url_show (url)
401
 
      except:
402
 
        dbg ('openurl: url_show failed. No URL for you')
403
 
        pass
404
 
 
405
 
  def on_resize_window(self, widget, width, height):
406
 
    dbg ('Resize window triggered on %s: %dx%d' % (widget, width, height))
407
 
 
408
 
  def on_vte_size_allocate(self, widget, allocation):
409
 
    dbg ('Terminal resized to %dx%d' % (self._vte.get_column_count (), self._vte.get_row_count ()))
410
 
    self._titlebox.set_terminal_size (self._vte.get_column_count (), self._vte.get_row_count ())
411
 
    if self._vte.window != None and (self.conf.geometry_hinting):
412
 
        self.terminator.on_term_resized ()
413
 
 
414
 
  def get_pixbuf(self, maxsize= None):
415
 
    pixmap = self.get_snapshot()
416
 
    (width, height) = pixmap.get_size()
417
 
    pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False, 8, width, height)
418
 
    pixbuf.get_from_drawable(pixmap, pixmap.get_colormap(), 0, 0, 0, 0, width, height)
419
 
 
420
 
    longest = max(width, height)
421
 
 
422
 
    if maxsize is not None:
423
 
      factor = float(maxsize) / float(longest)
424
 
 
425
 
    if not maxsize or (width * factor) > width or (height * factor) > height:
426
 
      factor = 1
427
 
 
428
 
    scaledpixbuf = pixbuf.scale_simple (int(width * factor), int(height * factor), gtk.gdk.INTERP_BILINEAR)
429
 
 
430
 
    return(scaledpixbuf)
431
 
 
432
 
  def on_drag_begin(self, widget, drag_context, data):
433
 
    dbg ('Drag begins')
434
 
    widget.drag_source_set_icon_pixbuf(self.get_pixbuf (512))
435
 
 
436
 
  def on_drag_data_get(self,widget, drag_context, selection_data, info, time, data):
437
 
    dbg ("Drag data get")
438
 
    selection_data.set("vte",info, str(data.terminator.term_list.index (self)))
439
 
 
440
 
  def on_expose_event(self, widget, event):
441
 
    if widget._expose_data is None:
442
 
      return False
443
 
 
444
 
    color = widget._expose_data['color']
445
 
    coord = widget._expose_data['coord']
446
 
    
447
 
    context = widget.window.cairo_create()
448
 
    #leaving those xxx_group as they could be usefull
449
 
    ##http://macslow.thepimp.net/?p=153
450
 
    #context.push_group()
451
 
    context.set_source_rgba(color.red, color.green, color.blue, 0.5)
452
 
    if len(coord) > 0 :
453
 
      context.move_to(coord[len(coord)-1][0],coord[len(coord)-1][1])
454
 
      for i in coord:
455
 
        context.line_to(i[0],i[1])
456
 
    
457
 
    context.fill()
458
 
    #context.pop_group_to_source()
459
 
    #context.paint()
460
 
    return False
461
 
 
462
 
  def on_drag_motion(self, widget, drag_context, x, y, time, data): 
463
 
    dbg ("Drag Motion on ")
464
 
    """
465
 
x-special/gnome-icon-list
466
 
text/uri-list
467
 
UTF8_STRING
468
 
COMPOUND_TEXT
469
 
TEXT
470
 
STRING
471
 
text/plain;charset=utf-8
472
 
text/plain;charset=UTF-8
473
 
text/plain
474
 
    """
475
 
      
476
 
    if 'text/plain' in drag_context.targets:
477
 
      #copy text from another widget
478
 
      return
479
 
    srcwidget = drag_context.get_source_widget()
480
 
    if (isinstance(srcwidget, gtk.EventBox) and srcwidget == self._titlebox) or widget == srcwidget:
481
 
      #on self
482
 
      return
483
 
 
484
 
    alloc = widget.allocation
485
 
    rect = gtk.gdk.Rectangle(0, 0, alloc.width, alloc.height)
486
 
    
487
 
    if self.conf.use_theme_colors:
488
 
      color = self._vte.get_style ().text[gtk.STATE_NORMAL]
489
 
    else:
490
 
      color = gtk.gdk.color_parse (self.conf.foreground_color)
491
 
     
492
 
    pos = self.get_location(widget, x, y)
493
 
    topleft = (0,0)
494
 
    topright = (alloc.width,0)
495
 
    topmiddle = (alloc.width/2,0)
496
 
    bottomleft = (0, alloc.height)
497
 
    bottomright = (alloc.width,alloc.height)
498
 
    bottommiddle = (alloc.width/2, alloc.height)
499
 
    middle = (alloc.width/2, alloc.height/2)
500
 
    middleleft = (0, alloc.height/2)
501
 
    middleright = (alloc.width, alloc.height/2)
502
 
    #print "%f %f %d %d" %(coef1, coef2, b1,b2)
503
 
    coord = ()
504
 
    if pos == "right":
505
 
      coord = (topright, topmiddle, bottommiddle, bottomright)
506
 
    if pos == "top":
507
 
      coord = (topleft, topright, middleright , middleleft) 
508
 
    if pos == "left":
509
 
      coord = (topleft, topmiddle, bottommiddle, bottomleft)
510
 
    if pos == "bottom":
511
 
      coord = (bottomleft, bottomright, middleright , middleleft) 
512
 
    
513
 
    
514
 
    #here, we define some widget internal values
515
 
    widget._expose_data = { 'color': color, 'coord' : coord }
516
 
    #redraw by forcing an event
517
 
    connec = widget.connect_after('expose-event', self.on_expose_event)
518
 
    widget.window.invalidate_rect(rect, True)
519
 
    widget.window.process_updates(True)
520
 
    #finaly reset the values
521
 
    widget.disconnect(connec)
522
 
    widget._expose_data = None
523
 
 
524
 
  def on_drag_drop(self, widget, drag_context, x, y, time):
525
 
    parent = widget.get_parent()
526
 
    dbg ('Drag drop on %s'%parent)
527
 
 
528
 
  def get_target_terms(self):
529
 
    if self.terminator.groupsend == 2:
530
 
      return self.terminator.term_list
531
 
    elif self.terminator.groupsend == 1:
532
 
      term_subset = []
533
 
      for term in self.terminator.term_list:
534
 
        if term == self or (term._group != None and term._group == self._group):
535
 
          term_subset.append(term)
536
 
      return term_subset
537
 
    else:
538
 
      return [self]
539
 
 
540
 
  def on_drag_data_received(self, widget, drag_context, x, y, selection_data, info, time, data):
541
 
    dbg ("Drag Data Received")
542
 
    if selection_data.type == 'text/plain':
543
 
      #copy text to destination
544
 
      #print "%s %s" % (selection_data.type, selection_data.target)
545
 
      txt = selection_data.data.strip()
546
 
      if txt[0:7] == "file://":
547
 
        txt = "'%s'" % urllib2.unquote(txt[7:])
548
 
      for term in self.get_target_terms():
549
 
          term._vte.feed_child(txt)
550
 
      return
551
 
      
552
 
    widgetsrc = data.terminator.term_list[int(selection_data.data)]
553
 
    srcvte = drag_context.get_source_widget()
554
 
    #check if computation requireds
555
 
    if (isinstance(srcvte, gtk.EventBox) and srcvte == self._titlebox) or srcvte == widget:
556
 
      dbg ("  on itself")
557
 
      return
558
 
    
559
 
    srchbox = widgetsrc
560
 
    dsthbox = widget.get_parent().get_parent()
561
 
    
562
 
    dstpaned = dsthbox.get_parent()
563
 
    srcpaned = srchbox.get_parent()
564
 
    if isinstance(dstpaned, gtk.Window) and isinstance(srcpaned, gtk.Window):
565
 
      dbg ("  Only one terminal")
566
 
      return
567
 
    pos = self.get_location(widget, x, y)
568
 
    
569
 
    data.terminator.remove(widgetsrc, True)
570
 
    data.terminator.add(self, widgetsrc,pos)
571
 
    return
572
 
 
573
 
  def get_location(self, vte, x, y):
574
 
    pos = ""
575
 
    #get the diagonales function for the receiving widget
576
 
    coef1 = float(vte.allocation.height)/float(vte.allocation.width)
577
 
    coef2 = -float(vte.allocation.height)/float(vte.allocation.width)
578
 
    b1 = 0
579
 
    b2 = vte.allocation.height
580
 
    #determine position in rectangle
581
 
    """
582
 
    --------
583
 
    |\    /|
584
 
    | \  / |
585
 
    |  \/  |
586
 
    |  /\  |
587
 
    | /  \ |
588
 
    |/    \|
589
 
    --------
590
 
    """
591
 
    if (x*coef1 + b1 > y ) and (x*coef2 + b2 < y ):
592
 
      pos =  "right"
593
 
    if (x*coef1 + b1 > y ) and (x*coef2 + b2 > y ):
594
 
      pos = "top"
595
 
    if (x*coef1 + b1 < y ) and (x*coef2 + b2 > y ):
596
 
      pos = "left"
597
 
    if (x*coef1 + b1 < y ) and (x*coef2 + b2 < y ):
598
 
      pos = "bottom"
599
 
    return pos
600
 
 
601
 
  def add_matches (self, posix = True):
602
 
    userchars = "-A-Za-z0-9"
603
 
    passchars = "-A-Za-z0-9,?;.:/!%$^*&~\"#'"
604
 
    hostchars = "-A-Za-z0-9"
605
 
    pathchars = "-A-Za-z0-9_$.+!*(),;:@&=?/~#%'\""
606
 
    schemes   = "(news:|telnet:|nntp:|file:/|https?:|ftps?:|webcal:)"
607
 
    user      = "[" + userchars + "]+(:[" + passchars + "]+)?"
608
 
    urlpath   = "/[" + pathchars + "]*[^]'.}>) \t\r\n,\\\"]"
609
 
 
610
 
    if posix:
611
 
      dbg ('add_matches: Trying POSIX URL regexps.  Set try_posix_regexp = False in config to only try GNU if you get (harmless) VTE warnings.')
612
 
      lboundry = "[[:<:]]"
613
 
      rboundry = "[[:>:]]"
614
 
    else: # GNU
615
 
      dbg ('add_matches: Trying GNU URL regexps.  Set try_posix_regexp = True in config if URLs are not detected.')
616
 
      lboundry = "\\<"
617
 
      rboundry = "\\>"
618
 
 
619
 
    self.matches['full_uri'] = self._vte.match_add(lboundry + schemes + "//(" + user + "@)?[" + hostchars  +".]+(:[0-9]+)?(" + urlpath + ")?" + rboundry + "/?")
620
 
 
621
 
    if self.matches['full_uri'] == -1:
622
 
      if posix:
623
 
        err ('add_matches: POSIX match failed, trying GNU')
624
 
        self.add_matches(posix = False)
625
 
      else:
626
 
        err ('add_matches: Failed adding URL match patterns')
627
 
    else:
628
 
      self.matches['voip'] = self._vte.match_add(lboundry + '(callto:|h323:|sip:)' + "[" + userchars + "+][" + userchars + ".]*(:[0-9]+)?@?[" + pathchars + "]+" + rboundry)
629
 
      self.matches['addr_only'] = self._vte.match_add (lboundry + "(www|ftp)[" + hostchars + "]*\.[" + hostchars + ".]+(:[0-9]+)?(" + urlpath + ")?" + rboundry + "/?")
630
 
      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)
631
 
      self.matches['nntp'] = self._vte.match_add (lboundry + '''news:[-A-Z\^_a-z{|}~!"#$%&'()*+,./0-9;:=?`]+@[-A-Za-z0-9.]+(:[0-9]+)?''' + rboundry)
632
 
      # if the url looks like a Launchpad changelog closure entry LP: #92953 - make it a url to http://bugs.launchpad.net
633
 
      # the regular expression is similar to the perl one specified in the Ubuntu Policy Manual - /lp:\s+\#\d+(?:,\s*\#\d+)*/i
634
 
      self.matches['launchpad'] = self._vte.match_add ('\\b(lp|LP):?\s?#?[0-9]+(,\s*#?[0-9]+)*\\b')
635
 
 
636
 
  def _path_lookup(self, command):
637
 
    if os.path.isabs (command):
638
 
      if os.path.isfile (command):
639
 
        return command
640
 
      else:
641
 
        return None
642
 
    elif command[:2] == './' and os.path.isfile(command):
643
 
      dbg('path_lookup: Relative filename "%s" found in cwd' % command)
644
 
      return command
645
 
 
646
 
    try:
647
 
      paths = os.environ['PATH'].split(':')
648
 
      if len(paths[0]) == 0: raise (ValueError)
649
 
    except (ValueError, NameError):
650
 
      dbg('path_lookup: PATH not set in environment, using fallbacks')
651
 
      paths = ['/usr/local/bin', '/usr/bin', '/bin']
652
 
 
653
 
    dbg('path_lookup: Using %d paths: %s' % (len(paths), paths))
654
 
 
655
 
    for path in paths:
656
 
      target = os.path.join (path, command)
657
 
      if os.path.isfile (target):
658
 
        dbg('path_lookup: found "%s"' % target)
659
 
        return target
660
 
 
661
 
    dbg('path_lookup: Unable to locate "%s"' % command)
662
 
 
663
 
  def _shell_lookup(self):
664
 
    shells = [os.getenv('SHELL'), pwd.getpwuid(os.getuid())[6],
665
 
              'bash', 'zsh', 'tcsh', 'ksh', 'csh', 'sh']
666
 
 
667
 
    for shell in shells:
668
 
      if shell is None: continue
669
 
      elif os.path.isfile (shell):
670
 
        return shell
671
 
      else:
672
 
        rshell = self._path_lookup(shell)
673
 
        if rshell is not None:
674
 
          dbg('shell_lookup: Found "%s" at "%s"' % (shell, rshell))
675
 
          return rshell
676
 
 
677
 
    dbg('shell_lookup: Unable to locate a shell')
678
 
 
679
 
  def spawn_child (self, event=None):
680
 
    update_records = self.conf.update_records
681
 
    login = self.conf.login_shell
682
 
    args = []
683
 
    shell = None
684
 
    command = None
685
 
 
686
 
    if self.command:
687
 
      dbg ('spawn_child: using self.command: %s' % self.command)
688
 
      command = self.command
689
 
    elif self.conf.use_custom_command:
690
 
      dbg ('spawn_child: using custom command: %s' % self.conf.custom_command)
691
 
      command = self.conf.custom_command
692
 
 
693
 
    if type(command) is list:
694
 
      # List of arguments from -x
695
 
      dbg('spawn_child: Bypassing shell and trying to run "%s" directly' % command[0])
696
 
      shell = self._path_lookup(command[0])
697
 
      args = command
698
 
    else:
699
 
      shell = self._shell_lookup()
700
 
 
701
 
      if self.conf.login_shell:
702
 
        args.insert(0, "-%s" % shell)
703
 
      else:
704
 
        args.insert(0, shell)
705
 
 
706
 
      if command is not None:
707
 
        args += ['-c', command]
708
 
 
709
 
    if shell is None:
710
 
      # Give up, we're completely stuck
711
 
      err (_('Unable to find a shell'))
712
 
      gobject.timeout_add (100, self.terminator.closeterm, self)
713
 
      return (-1)
714
 
 
715
 
    os.putenv ('WINDOWID', '%s' % self._vte.get_parent_window().xid)
716
 
 
717
 
    self._pid = self._vte.fork_command (command = shell, argv = args,
718
 
        envv = [], loglastlog = login, logwtmp = update_records,
719
 
        logutmp = update_records, directory=self.cwd)
720
 
 
721
 
    self.on_vte_title_change(self._vte) # Force an initial update of our titles
722
 
    self._titlebox.update ()
723
 
 
724
 
    if self._pid == -1:
725
 
      err (_('Unable to start shell: ') + shell)
726
 
      return (-1)
727
 
 
728
 
  def get_cwd (self):
729
 
    """ Return the current working directory of the subprocess.
730
 
        This function requires OS specific behaviours
731
 
    """
732
 
    try:
733
 
      cwd = self.terminator.pid_get_cwd (self._pid)
734
 
    except OSError:
735
 
      err ('get_cwd: unable to get cwd of %d' % self._pid)
736
 
      cwd = '~'
737
 
      pass
738
 
    dbg ('get_cwd found: %s'%cwd)
739
 
    return (cwd)
740
 
 
741
 
  def reconfigure_vte (self, widget = None):
742
 
    # Set our emulation
743
 
    self._vte.set_emulation (self.conf.emulation)
744
 
 
745
 
    # Set our charset
746
 
    if self._custom_encoding == False or self._custom_encoding == None:
747
 
      self._vte.set_encoding (self.conf.encoding)
748
 
 
749
 
    # Set our wordchars
750
 
    self._vte.set_word_chars (self.conf.word_chars)
751
 
 
752
 
    # Set our mouselation
753
 
    self._vte.set_mouse_autohide (self.conf.mouse_autohide)
754
 
 
755
 
    # Set our compatibility
756
 
    backspace = self.conf.backspace_binding
757
 
    delete = self.conf.delete_binding
758
 
 
759
 
# 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 :/
760
 
    if backspace == "ascii-del":
761
 
#      backbind = vte.ERASE_ASCII_BACKSPACE
762
 
      backbind = 2
763
 
    else:
764
 
#      backbind = vte.ERASE_AUTO_BACKSPACE
765
 
      backbind = 1
766
 
 
767
 
    if delete == "escape-sequence":
768
 
#      delbind = vte.ERASE_DELETE_SEQUENCE
769
 
      delbind = 3
770
 
    else:
771
 
#      delbind = vte.ERASE_AUTO
772
 
      delbind = 0
773
 
 
774
 
    self._vte.set_backspace_binding (backbind)
775
 
    self._vte.set_delete_binding (delbind)
776
 
 
777
 
    # Set our font
778
 
    if not self._custom_font_size:
779
 
      try:
780
 
        self._vte.set_font (pango.FontDescription (self.conf.font))
781
 
      except:
782
 
        pass
783
 
 
784
 
    # Set our boldness
785
 
    self._vte.set_allow_bold (self.conf.allow_bold)
786
 
 
787
 
    # Set our color scheme
788
 
    palette = self.conf.palette
789
 
    if self.conf.use_theme_colors:
790
 
      fg_color = self._vte.get_style ().text[gtk.STATE_NORMAL]
791
 
      bg_color = self._vte.get_style ().base[gtk.STATE_NORMAL]
792
 
    else:
793
 
      fg_color = gtk.gdk.color_parse (self.conf.foreground_color)
794
 
      bg_color = gtk.gdk.color_parse (self.conf.background_color)
795
 
      
796
 
    colors = palette.split (':')
797
 
    palette = []
798
 
    for color in colors:
799
 
      if color:
800
 
        palette.append (gtk.gdk.color_parse (color))
801
 
    self._vte.set_colors (fg_color, bg_color, palette)
802
 
 
803
 
    cursor_color = self.conf.cursor_color
804
 
    if cursor_color != '':
805
 
      self._vte.set_color_cursor (gtk.gdk.color_parse (cursor_color))
806
 
 
807
 
    # Set cursor shape
808
 
    if hasattr (self._vte, "set_cursor_shape"):
809
 
      self._vte.set_cursor_shape (getattr (vte, "CURSOR_SHAPE_" + self.conf.cursor_shape.upper ()))
810
 
 
811
 
    # Set our background image, transparency and type
812
 
    # Many thanks to the authors of gnome-terminal, on which this code is based.
813
 
    background_type = self.conf.background_type
814
 
    dbg ('H9TRANS: Configuring background type as: %s' % background_type)
815
 
 
816
 
    # set background image settings
817
 
    if background_type == "image" and self.conf.background_image is not None and self.conf.background_image != '':
818
 
      dbg ('H9TRANS: Setting background image to: %s' % self.conf.background_image)
819
 
      self._vte.set_background_image_file (self.conf.background_image)
820
 
      dbg ('H9TRANS: Setting background image scroll to: %s' % self.conf.scroll_background)
821
 
      self._vte.set_scroll_background (self.conf.scroll_background)
822
 
    else:
823
 
      dbg ('H9TRANS: Unsetting background image')
824
 
      self._vte.set_background_image_file('')
825
 
      dbg ('H9TRANS: Unsetting background image scrolling')
826
 
      self._vte.set_scroll_background(False)
827
 
 
828
 
    # set transparency for the background (image)
829
 
    opacity = 65535
830
 
    if background_type in ("image", "transparent"):
831
 
      self._vte.set_background_tint_color (gtk.gdk.color_parse (self.conf.background_color))
832
 
      self._vte.set_background_saturation(1 - (self.conf.background_darkness))
833
 
      opacity = int(self.conf.background_darkness * 65535)
834
 
      dbg ('H9TRANS: Set background tint color to: %s' % self.conf.background_color)
835
 
      dbg ('H9TRANS: Set background saturation to: %s' % (1 - (self.conf.background_darkness)))
836
 
    else:
837
 
      dbg ('H9TRANS: Set background saturation to: 1')
838
 
      self._vte.set_background_saturation(1)
839
 
      
840
 
    if self._composited_support:
841
 
      dbg ('H9TRANS: Set opacity to: %s' % opacity)
842
 
      self._vte.set_opacity(opacity)
843
 
 
844
 
    if background_type == "transparent":
845
 
      if not self.conf.enable_real_transparency:
846
 
        self._vte.set_background_transparent (True)
847
 
      else:
848
 
        self._vte.set_background_transparent (False)
849
 
 
850
 
    # Set our cursor blinkiness
851
 
    self._vte.set_cursor_blinks (self.conf.cursor_blink)
852
 
 
853
 
    if self.conf.force_no_bell:
854
 
      self._vte.set_audible_bell (False)
855
 
      self._vte.set_visible_bell (False)
856
 
      if self._urgent_bell_cnid:
857
 
        self._vte.disconnect (self._urgent_bell_cnid)
858
 
        self._urgent_bell_cnid = None
859
 
    else:
860
 
      # Set our audible belliness
861
 
      self._vte.set_audible_bell (self.conf.audible_bell)
862
 
 
863
 
      # Set our visual flashiness
864
 
      self._vte.set_visible_bell (self.conf.visible_bell)
865
 
 
866
 
      # Set our urgent belliness
867
 
      if self.conf.urgent_bell:
868
 
        try:
869
 
          self._urgent_bell_cnid = self._vte.connect ("beep", self.terminator.on_beep)
870
 
        except TypeError:
871
 
          err ("beep signal not supported by your VTE, urgent handler not available")
872
 
      elif self._urgent_bell_cnid:
873
 
        self._vte.disconnect (self._urgent_bell_cnid)
874
 
        self._urgent_bell_cnid = None
875
 
 
876
 
    # Set our scrolliness
877
 
    self._vte.set_scrollback_lines (self.conf.scrollback_lines)
878
 
    self._vte.set_scroll_on_keystroke (self.conf.scroll_on_keystroke)
879
 
    self._vte.set_scroll_on_output (self.conf.scroll_on_output)
880
 
 
881
 
    if self.scrollbar_position != self.conf.scrollbar_position:
882
 
      self.scrollbar_position = self.conf.scrollbar_position
883
 
 
884
 
      if self.scrollbar_position == 'hidden' or self.scrollbar_position == 'disabled':
885
 
        self._scrollbar.hide ()
886
 
      else:
887
 
        self._scrollbar.show ()
888
 
        if self.scrollbar_position == 'right':
889
 
          self._termbox.reorder_child (self._vte, 0)
890
 
        elif self.scrollbar_position == 'left':
891
 
          self._termbox.reorder_child (self._scrollbar, 0)
892
 
 
893
 
    if hasattr (self._vte, "set_alternate_screen_scroll"):
894
 
      self._vte.set_alternate_screen_scroll (self.conf.alternate_screen_scroll)
895
 
 
896
 
    # Set our sloppiness
897
 
    self.focus = self.conf.focus
898
 
 
899
 
    # Sync our titlebar state
900
 
    self._titlebox.update ()
901
 
    self._vte.queue_draw ()
902
 
 
903
 
  def get_size_details(self):
904
 
    font_width = self._vte.get_char_width ()
905
 
    font_height = self._vte.get_char_height ()
906
 
    columns = self._vte.get_column_count ()
907
 
    rows = self._vte.get_row_count ()
908
 
 
909
 
    return (font_width, font_height, columns, rows)
910
 
 
911
 
  def on_composited_changed (self, widget):
912
 
    self.reconfigure_vte ()
913
 
 
914
 
  def on_vte_button_press (self, term, event):
915
 
    # Left mouse button + Ctrl while over a link should open it
916
 
    mask = gtk.gdk.CONTROL_MASK
917
 
    if (event.state & mask) == mask:
918
 
      if event.button == 1:
919
 
        url = self._vte.match_check (int (event.x / self._vte.get_char_width ()), int (event.y / self._vte.get_char_height ()))
920
 
        if url:
921
 
          self.openurl (self.prepareurl (url[0], url[1]))
922
 
      return False
923
 
 
924
 
    # Left mouse button should transfer focus to this vte widget
925
 
    # we also need to give focus on the widget where the paste occured
926
 
    if event.button in (1 ,2):
927
 
      if event.button == 2:
928
 
        self.paste_clipboard (True)
929
 
        return True
930
 
      self._vte.grab_focus ()
931
 
      return False
932
 
 
933
 
    # Right mouse button should display a context menu if ctrl not pressed
934
 
    if event.button == 3 and event.state & gtk.gdk.CONTROL_MASK == 0:
935
 
      self.create_popup_menu (self._vte, event)
936
 
      return True
937
 
 
938
 
  def on_vte_notify_enter (self, term, event):
939
 
    if (self.focus == "sloppy" or self.focus == "mouse"):
940
 
      term.grab_focus ()
941
 
      return False
942
 
 
943
 
  def do_autocleangroups_toggle (self):
944
 
    self.terminator.autocleangroups = not self.terminator.autocleangroups
945
 
    if self.terminator.autocleangroups:
946
 
      self.terminator.group_hoover()
947
 
    
948
 
  def do_scrollbar_toggle (self):
949
 
    self.toggle_widget_visibility (self._scrollbar)
950
 
 
951
 
  def do_splittogroup_toggle (self):
952
 
    self.terminator.splittogroup = not self.terminator.splittogroup
953
 
    
954
 
  def do_title_toggle (self):
955
 
    self._titlebox.wanted = not self._titlebox.get_property ('visible')
956
 
    self.toggle_widget_visibility (self._titlebox)
957
 
 
958
 
  def toggle_widget_visibility (self, widget):
959
 
    if not isinstance (widget, gtk.Widget):
960
 
      raise TypeError
961
 
 
962
 
    if widget.get_property ('visible'):
963
 
      widget.hide ()
964
 
    else:
965
 
      widget.show ()
966
 
 
967
 
  def paste_clipboard(self, primary = False):
968
 
    for term in self.get_target_terms():
969
 
      if primary:
970
 
        term._vte.paste_primary ()
971
 
      else:
972
 
        term._vte.paste_clipboard ()
973
 
    self._vte.grab_focus()
974
 
 
975
 
  def do_enumerate(self, pad=False):
976
 
    if pad:
977
 
      numstr='%0'+str(len(str(len(self.terminator.term_list))))+'d'
978
 
    else:
979
 
      numstr='%d'
980
 
    for term in self.get_target_terms():
981
 
      idx=self.terminator.term_list.index(term)
982
 
      term._vte.feed_child(numstr % (idx+1))
983
 
 
984
 
  #keybindings for the individual splited terminals (affects only the
985
 
  #the selected terminal)
986
 
  UnhandledKeybindings = ('close_window', 'full_screen')
987
 
  def on_vte_key_press (self, term, event):
988
 
    if not event:
989
 
      dbg ('on_vte_key_press: Called on %s with no event' % term)
990
 
      return False
991
 
    mapping = self.terminator.keybindings.lookup(event)
992
 
    
993
 
    if mapping == "hide_window":
994
 
      return False
995
 
 
996
 
    if mapping and mapping not in self.UnhandledKeybindings:
997
 
      dbg("on_vte_key_press: lookup found %r" % mapping) 
998
 
      # handle the case where user has re-bound copy to ctrl+<key>
999
 
      # we only copy if there is a selection otherwise let it fall through to ^<key>
1000
 
      if (mapping == "copy" and event.state & gtk.gdk.CONTROL_MASK):
1001
 
        if self._vte.get_has_selection ():
1002
 
          getattr(self, "key_" + mapping)()
1003
 
          return True
1004
 
      else:
1005
 
        getattr(self, "key_" + mapping)()
1006
 
        return True
1007
 
 
1008
 
    if self.terminator.groupsend != 0 and self._vte.is_focus ():
1009
 
      if self._group and self.terminator.groupsend == 1:
1010
 
        self.terminator.group_emit (self, self._group, 'key-press-event', event)
1011
 
      if self.terminator.groupsend == 2:
1012
 
        self.terminator.all_emit (self, 'key-press-event', event)
1013
 
    return False
1014
 
 
1015
 
  # Key events
1016
 
  def key_zoom_in(self):
1017
 
    self.zoom (True)
1018
 
 
1019
 
  def key_zoom_out(self):
1020
 
    self.zoom (False)
1021
 
 
1022
 
  def key_copy(self):
1023
 
    self._vte.copy_clipboard ()
1024
 
 
1025
 
  def key_paste(self):
1026
 
    self.paste_clipboard ()
1027
 
 
1028
 
  def key_toggle_scrollbar(self):
1029
 
    self.do_scrollbar_toggle ()
1030
 
 
1031
 
  def key_zoom_normal(self):
1032
 
    self.zoom_orig ()
1033
 
 
1034
 
  def key_search(self):
1035
 
    self.start_search()
1036
 
 
1037
 
  # bindings that should be moved to Terminator as they all just call
1038
 
  # a function of Terminator. It would be cleaner if TerminatorTerm
1039
 
  # has absolutely no reference to Terminator.
1040
 
  # N (next) - P (previous) - O (horizontal) - E (vertical) - W (close)
1041
 
  def key_new_root_tab(self):
1042
 
    self.terminator.newtab (self, True)
1043
 
 
1044
 
  def key_go_next(self):
1045
 
    self.terminator.go_next (self)
1046
 
 
1047
 
  def key_go_prev(self):
1048
 
    self.terminator.go_prev (self)
1049
 
 
1050
 
  def key_go_up(self):
1051
 
    self.terminator.go_up (self)
1052
 
 
1053
 
  def key_go_down(self):
1054
 
    self.terminator.go_down (self)
1055
 
 
1056
 
  def key_go_left(self):
1057
 
    self.terminator.go_left (self)
1058
 
 
1059
 
  def key_go_right(self):
1060
 
    self.terminator.go_right (self)
1061
 
 
1062
 
  def key_split_horiz(self):
1063
 
    self.terminator.splitaxis (self, False)
1064
 
 
1065
 
  def key_split_vert(self):
1066
 
    self.terminator.splitaxis (self, True)
1067
 
 
1068
 
  def key_close_term(self):
1069
 
    self.terminator.closeterm (self)
1070
 
 
1071
 
  def key_new_tab(self):
1072
 
    self.terminator.newtab(self)
1073
 
 
1074
 
  def key_resize_up(self):
1075
 
    self.terminator.resizeterm (self, 'Up')
1076
 
 
1077
 
  def key_resize_down(self):
1078
 
    self.terminator.resizeterm (self, 'Down')
1079
 
 
1080
 
  def key_resize_left(self):
1081
 
    self.terminator.resizeterm (self, 'Left')
1082
 
 
1083
 
  def key_resize_right(self):
1084
 
    self.terminator.resizeterm (self, 'Right')
1085
 
 
1086
 
  def key_move_tab_right(self):
1087
 
    self.terminator.move_tab (self, 'right')
1088
 
 
1089
 
  def key_move_tab_left(self):
1090
 
    self.terminator.move_tab (self, 'left')
1091
 
 
1092
 
  def key_toggle_zoom(self):
1093
 
    self.terminator.toggle_zoom (self)
1094
 
 
1095
 
  def key_scaled_zoom(self):
1096
 
    self.terminator.toggle_zoom (self, True)
1097
 
 
1098
 
  def key_next_tab(self):
1099
 
    self.terminator.next_tab (self)
1100
 
 
1101
 
  def key_prev_tab(self):
1102
 
    self.terminator.previous_tab (self)
1103
 
 
1104
 
  def key_switch_to_tab_1(self):
1105
 
    self.terminator.switch_to_tab (self, 0)
1106
 
 
1107
 
  def key_switch_to_tab_2(self):
1108
 
    self.terminator.switch_to_tab (self, 1)
1109
 
 
1110
 
  def key_switch_to_tab_3(self):
1111
 
    self.terminator.switch_to_tab (self, 2)
1112
 
 
1113
 
  def key_switch_to_tab_4(self):
1114
 
    self.terminator.switch_to_tab (self, 3)
1115
 
 
1116
 
  def key_switch_to_tab_5(self):
1117
 
    self.terminator.switch_to_tab (self, 4)
1118
 
 
1119
 
  def key_switch_to_tab_6(self):
1120
 
    self.terminator.switch_to_tab (self, 5)
1121
 
 
1122
 
  def key_switch_to_tab_7(self):
1123
 
    self.terminator.switch_to_tab (self, 6)
1124
 
 
1125
 
  def key_switch_to_tab_8(self):
1126
 
    self.terminator.switch_to_tab (self, 7)
1127
 
 
1128
 
  def key_switch_to_tab_9(self):
1129
 
    self.terminator.switch_to_tab (self, 8)
1130
 
 
1131
 
  def key_switch_to_tab_10(self):
1132
 
    self.terminator.switch_to_tab (self, 9)
1133
 
 
1134
 
  def key_reset(self):
1135
 
    self._vte.reset (True, False)
1136
 
 
1137
 
  def key_reset_clear(self):
1138
 
    self._vte.reset (True, True)
1139
 
 
1140
 
  def key_group_all(self):
1141
 
    self.group_all(self)
1142
 
 
1143
 
  def key_ungroup_all(self):
1144
 
    self.ungroup_all(self)
1145
 
 
1146
 
  def key_group_tab(self):
1147
 
    self.group_tab(self)
1148
 
 
1149
 
  def key_ungroup_tab(self):
1150
 
    self.ungroup_tab(self)
1151
 
 
1152
 
  def key_new_window(self):
1153
 
    cmd = sys.argv[0]
1154
 
    
1155
 
    if not os.path.isabs(cmd):
1156
 
      # Command is not an absolute path. Figure out where we are
1157
 
      cmd = os.path.join (self.terminator.origcwd, sys.argv[0])
1158
 
      if not os.path.isfile(cmd):
1159
 
        # we weren't started as ./terminator in a path. Give up
1160
 
        err('Unable to locate Terminator')
1161
 
        return False
1162
 
          
1163
 
    dbg("Spawning: %s" % cmd)
1164
 
    subprocess.Popen([cmd,])
1165
 
  # End key events
1166
 
 
1167
 
  def zoom_orig (self):
1168
 
    self._custom_font_size = None
1169
 
    self._vte.set_font (pango.FontDescription (self.conf.font))
1170
 
 
1171
 
  def zoom (self, zoom_in):
1172
 
    pangodesc = self._vte.get_font ()
1173
 
    fontsize = pangodesc.get_size ()
1174
 
 
1175
 
    if fontsize > pango.SCALE and not zoom_in:
1176
 
      fontsize -= pango.SCALE
1177
 
    elif zoom_in:
1178
 
      fontsize += pango.SCALE
1179
 
 
1180
 
    pangodesc.set_size (fontsize)
1181
 
    self._custom_font_size = fontsize
1182
 
    self._vte.set_font (pangodesc)
1183
 
 
1184
 
  def start_search(self):
1185
 
    self._searchbox.show()
1186
 
    self._searchinput.grab_focus()
1187
 
 
1188
 
  def search_keypress(self, widget, event):
1189
 
    key = gtk.gdk.keyval_name(event.keyval)
1190
 
    if key == 'Escape':
1191
 
      self.end_search()
1192
 
 
1193
 
  def end_search(self, widget = None):
1194
 
    self._search_row = 0
1195
 
    self._search_string = None
1196
 
    self._search_result_label.set_text("")
1197
 
    self._searchbox.hide()
1198
 
    self._scrollbar.set_value(self._vte.get_cursor_position()[1])
1199
 
    self._vte.grab_focus()
1200
 
 
1201
 
  def do_search(self, widget):
1202
 
    string = widget.get_text()
1203
 
    dbg("do_search: Looking for %r" % string)
1204
 
    if string == '':
1205
 
      return
1206
 
 
1207
 
    if string != self._search_string:
1208
 
      self._search_row = self._get_vte_buffer_range()[0]
1209
 
      self._search_string = string
1210
 
 
1211
 
    self._search_result_label.set_text("Searching scrollback")
1212
 
    self.next_search()
1213
 
 
1214
 
  # Called by get_text_range, once per character.  Argh.
1215
 
  def _search_character(self, widget, col, row, junk):
1216
 
    return True
1217
 
 
1218
 
  def next_search(self, widget=None):
1219
 
    startrow,endrow = self._get_vte_buffer_range()
1220
 
    while True:
1221
 
      if self._search_row == endrow:
1222
 
        self._search_row = startrow
1223
 
        self._search_result_label.set_text("Finished Search")
1224
 
        self._search_next.hide()
1225
 
        return
1226
 
      buffer = self._vte.get_text_range(self._search_row, 0, self._search_row, -1, self._search_character)
1227
 
 
1228
 
      # dbg("Row %d buffer: %r" % (self._search_row, buffer))
1229
 
      index = buffer.find(self._search_string)
1230
 
      if index != -1:
1231
 
        self._search_result_label.set_text("Found at row %d" % self._search_row)
1232
 
        self._scrollbar.set_value(self._search_row)
1233
 
        self._search_row += 1
1234
 
        self._search_next.show()
1235
 
        return
1236
 
      self._search_row += 1
1237
 
 
1238
 
  def _get_vte_buffer_range(self):
1239
 
    column, endrow = self._vte.get_cursor_position()
1240
 
    startrow = max(0, endrow - self.conf.scrollback_lines)
1241
 
    return(startrow, endrow)
1242
 
 
1243
 
  def get_geometry (self):
1244
 
    '''Returns Gdk.Window.get_position(), pixel-based cursor position,
1245
 
       and Gdk.Window.get_geometry()'''
1246
 
    reply = dict()
1247
 
    if not self._vte.window:
1248
 
        return reply
1249
 
    x, y = self._vte.window.get_origin ()
1250
 
    reply.setdefault('origin_x',x)
1251
 
    reply.setdefault('origin_y',y)
1252
 
 
1253
 
    column, row = self._vte.get_cursor_position ()
1254
 
    cursor_x = column * self._vte.get_char_width ()
1255
 
    cursor_y = row    * self._vte.get_char_height ()
1256
 
    reply.setdefault('cursor_x', cursor_x)
1257
 
    reply.setdefault('cursor_y', cursor_y)
1258
 
 
1259
 
    geometry = self._vte.window.get_geometry()
1260
 
    reply.setdefault('offset_x', geometry[0])
1261
 
    reply.setdefault('offset_y', geometry[1])
1262
 
    reply.setdefault('span_x', geometry[2])
1263
 
    reply.setdefault('span_y', geometry[3])
1264
 
    reply.setdefault('depth', geometry[4])
1265
 
 
1266
 
    return reply
1267
 
 
1268
 
  def create_popup_menu (self, widget, event = None):
1269
 
    menu = gtk.Menu ()
1270
 
    url = None
1271
 
    address = None
1272
 
 
1273
 
    if event:
1274
 
      url = self._vte.match_check (int (event.x / self._vte.get_char_width ()), int (event.y / self._vte.get_char_height ()))
1275
 
      button = event.button
1276
 
      time = event.time
1277
 
    else:
1278
 
      button = 0
1279
 
      time = 0
1280
 
 
1281
 
    if url:
1282
 
      address = self.prepareurl (url[0], url[1])
1283
 
 
1284
 
      if url[1] == self.matches['email']:
1285
 
        nameopen = _("_Send Mail To...")
1286
 
        namecopy = _("_Copy Email Address")
1287
 
        item = gtk.MenuItem (nameopen)
1288
 
      elif url[1] == self.matches['voip']:
1289
 
        nameopen = _("Ca_ll To...")
1290
 
        namecopy = _("_Copy Call Address")
1291
 
        item = gtk.MenuItem (nameopen)
1292
 
      else:
1293
 
        nameopen = _("_Open Link")
1294
 
        namecopy = _("_Copy Link Address")
1295
 
        iconopen = gtk.image_new_from_stock(gtk.STOCK_JUMP_TO, gtk.ICON_SIZE_MENU)
1296
 
 
1297
 
        item = gtk.ImageMenuItem (nameopen)
1298
 
        item.set_property('image', iconopen)
1299
 
 
1300
 
      item.connect ("activate", lambda menu_item: self.openurl (address))
1301
 
      menu.append (item)
1302
 
 
1303
 
      item = gtk.MenuItem (namecopy)
1304
 
      item.connect ("activate", lambda menu_item: self.clipboard.set_text (url[0]))
1305
 
      menu.append (item)
1306
 
 
1307
 
      item = gtk.MenuItem ()
1308
 
      menu.append (item)
1309
 
 
1310
 
    item = gtk.ImageMenuItem (gtk.STOCK_COPY)
1311
 
    item.connect ("activate", lambda menu_item: self._vte.copy_clipboard ())
1312
 
    item.set_sensitive (self._vte.get_has_selection ())
1313
 
    menu.append (item)
1314
 
 
1315
 
    item = gtk.ImageMenuItem (gtk.STOCK_PASTE)
1316
 
    item.connect ("activate", lambda menu_item: self.paste_clipboard ())
1317
 
    menu.append (item)
1318
 
 
1319
 
    item = gtk.MenuItem ()
1320
 
    menu.append (item)
1321
 
 
1322
 
    if not self.terminator._zoomed:
1323
 
      str_horiz = _("Split H_orizontally")
1324
 
      str_vert = _("Split V_ertically")
1325
 
 
1326
 
      item = gtk.ImageMenuItem (str_horiz)
1327
 
      item_image = gtk.Image ()
1328
 
      item_image.set_from_icon_name (APP_NAME + '_horiz', gtk.ICON_SIZE_MENU)
1329
 
      item.set_image (item_image)
1330
 
      if hasattr(item, "set_always_show_image"):
1331
 
        item.set_always_show_image (True)
1332
 
 
1333
 
      item.connect ("activate", lambda menu_item: self.terminator.splitaxis (self, False))
1334
 
      menu.append (item)
1335
 
      item = gtk.ImageMenuItem (str_vert)
1336
 
      item_image = gtk.Image ()
1337
 
      item_image.set_from_icon_name (APP_NAME + '_vert', gtk.ICON_SIZE_MENU)
1338
 
      item.set_image (item_image)
1339
 
      if hasattr(item, "set_always_show_image"):
1340
 
        item.set_always_show_image (True)
1341
 
 
1342
 
      item.connect ("activate", lambda menu_item: self.terminator.splitaxis (self, True))
1343
 
      menu.append (item)
1344
 
 
1345
 
      item = gtk.MenuItem (_("Open _Tab"))
1346
 
      item.connect ("activate", lambda menu_item: self.terminator.newtab (self))
1347
 
      menu.append (item)
1348
 
 
1349
 
      if self.terminator.debugaddress:
1350
 
        item = gtk.MenuItem (_("Open _Debug Tab"))
1351
 
        item.connect ("activate", lambda menu_item: self.terminator.newtab (self, command = "telnet %s" % ' '.join([str(x) for x in self.terminator.debugaddress])))
1352
 
        menu.append (item)
1353
 
 
1354
 
 
1355
 
      if self.conf.extreme_tabs:
1356
 
        item = gtk.MenuItem (_("Open Top Level Tab"))
1357
 
        item.connect ("activate", lambda menu_item: self.terminator.newtab (self, True))
1358
 
        menu.append (item)
1359
 
      
1360
 
      item = gtk.MenuItem ()
1361
 
      menu.append (item)
1362
 
 
1363
 
    item = gtk.ImageMenuItem (gtk.STOCK_CLOSE)
1364
 
    item.connect ("activate", lambda menu_item: self.terminator.closeterm (self))
1365
 
    menu.append (item)
1366
 
 
1367
 
    item = gtk.MenuItem ()
1368
 
    menu.append (item)
1369
 
 
1370
 
    if len (self.terminator.term_list) > 1:
1371
 
      if not self.terminator._zoomed:
1372
 
        item = gtk.MenuItem (_("_Zoom terminal"))
1373
 
        item.connect ("activate", lambda menu_item: self.terminator.toggle_zoom (self, True))
1374
 
        menu.append (item)
1375
 
 
1376
 
        item = gtk.MenuItem (_("Ma_ximise terminal"))
1377
 
        item.connect ("activate", lambda menu_item: self.terminator.toggle_zoom (self))
1378
 
        menu.append (item)
1379
 
      else:
1380
 
        if self.terminator._zoomed and not self.terminator._maximised:
1381
 
          item = gtk.MenuItem (_("_Unzoom terminal"))
1382
 
          item.connect ("activate", lambda menu_item: self.terminator.toggle_zoom (self, True))
1383
 
          menu.append (item)
1384
 
 
1385
 
        if self.terminator._zoomed and self.terminator._maximised:
1386
 
          item = gtk.MenuItem (_("Unma_ximise terminal"))
1387
 
          item.connect ("activate", lambda menu_item: self.terminator.toggle_zoom (self))
1388
 
          menu.append (item)
1389
 
 
1390
 
      item = gtk.MenuItem ()
1391
 
      menu.append (item)
1392
 
 
1393
 
    item = gtk.CheckMenuItem (_("Show _scrollbar"))
1394
 
    item.set_active (self._scrollbar.get_property ('visible'))
1395
 
    item.connect ("toggled", lambda menu_item: self.do_scrollbar_toggle ())
1396
 
    menu.append (item)
1397
 
    
1398
 
    item = gtk.CheckMenuItem (_("Show _titlebar"))
1399
 
    item.set_active (self._titlebox.get_property ('visible'))
1400
 
    item.connect ("toggled", lambda menu_item: self.do_title_toggle ())
1401
 
    if self._group:
1402
 
      item.set_sensitive (False)
1403
 
    menu.append (item)
1404
 
 
1405
 
    item = gtk.MenuItem (_("Ed_it profile"))
1406
 
    item.connect ("activate", lambda menu_item: self.terminator.edit_profile (self))
1407
 
    menu.append (item)
1408
 
 
1409
 
    self._do_encoding_items (menu)
1410
 
        
1411
 
    menu.show_all ()
1412
 
    menu.popup (None, None, None, button, time)
1413
 
 
1414
 
    return True
1415
 
 
1416
 
  def create_popup_group_menu (self, widget, event = None):
1417
 
    menu = gtk.Menu ()
1418
 
    url = None
1419
 
 
1420
 
    if event:
1421
 
      url = self._vte.match_check (int (event.x / self._vte.get_char_width ()), int (event.y / self._vte.get_char_height ()))
1422
 
      button = event.button
1423
 
      time = event.time
1424
 
    else:
1425
 
      button = 0
1426
 
      time = 0
1427
 
 
1428
 
    self.populate_grouping_menu (menu)
1429
 
 
1430
 
    menu.show_all ()
1431
 
    if gtk.gtk_version > (2, 14, 0):
1432
 
        menu.popup (None, None, self.position_popup_group_menu, button, time, widget)
1433
 
    else:
1434
 
        menu.popup (None, None, None, button, time, widget)
1435
 
    
1436
 
    return True
1437
 
 
1438
 
  def populate_grouping_menu (self, widget):
1439
 
    groupitem = None
1440
 
 
1441
 
    item = gtk.MenuItem (_("Assign to group..."))
1442
 
    item.connect ("activate", self.create_group)
1443
 
    widget.append (item)
1444
 
 
1445
 
    if len (self.terminator.groupings) > 0:
1446
 
      groupitem = gtk.RadioMenuItem (groupitem, _("None"))
1447
 
      groupitem.set_active (self._group == None)
1448
 
      groupitem.connect ("activate", self.set_group, None)
1449
 
      widget.append (groupitem)
1450
 
 
1451
 
      for group in self.terminator.groupings:
1452
 
        item = gtk.RadioMenuItem (groupitem, group, False)
1453
 
        item.set_active (self._group == group)
1454
 
        item.connect ("toggled", self.set_group, group)
1455
 
        widget.append (item)
1456
 
        groupitem = item
1457
 
 
1458
 
    if self._group != None or len (self.terminator.groupings) > 0:
1459
 
      item = gtk.MenuItem ()
1460
 
      widget.append (item)
1461
 
 
1462
 
    if self._group != None:
1463
 
      item = gtk.MenuItem (_("Remove group %s") % (self._group))
1464
 
      item.connect ("activate", self.ungroup,  self._group)
1465
 
      widget.append (item)
1466
 
 
1467
 
    if self.terminator.get_first_parent_widget (self, gtk.Notebook) is not None and \
1468
 
       not isinstance (self.get_parent(), gtk.Notebook):
1469
 
      item = gtk.MenuItem (_("G_roup all in tab"))
1470
 
      item.connect ("activate", self.group_tab)
1471
 
      widget.append (item)
1472
 
 
1473
 
    if self.terminator.get_first_parent_widget(self, gtk.Notebook) is not None and \
1474
 
       not isinstance(self.get_parent(), gtk.Notebook) and \
1475
 
       len(self.terminator.groupings) > 0:
1476
 
      item = gtk.MenuItem(_("Ungr_oup all in tab"))
1477
 
      item.connect("activate", self.ungroup_tab)
1478
 
      widget.append(item)
1479
 
 
1480
 
    if len (self.terminator.groupings) > 0:
1481
 
      item = gtk.MenuItem (_("Remove all groups"))
1482
 
      item.connect ("activate", self.ungroup_all)
1483
 
      widget.append (item)
1484
 
      
1485
 
    if self._group != None:
1486
 
      item = gtk.MenuItem ()
1487
 
      widget.append (item)
1488
 
 
1489
 
      item = gtk.ImageMenuItem (_("Close group %s") % (self._group))
1490
 
      grp_close_img = gtk.Image()
1491
 
      grp_close_img.set_from_stock(gtk.STOCK_CLOSE, 1)
1492
 
      item.set_image (grp_close_img)
1493
 
      item.connect ("activate", lambda menu_item: self.terminator.closegroupedterms (self))
1494
 
      widget.append (item)
1495
 
 
1496
 
    item = gtk.MenuItem ()
1497
 
    widget.append (item)    
1498
 
 
1499
 
    groupitem = None
1500
 
    
1501
 
    groupitem = gtk.RadioMenuItem (groupitem, _("Broadcast off"))
1502
 
    groupitem.set_active (self.terminator.groupsend == 0)
1503
 
    groupitem.connect ("activate", self.set_groupsend, 0)
1504
 
    widget.append (groupitem)
1505
 
 
1506
 
    groupitem = gtk.RadioMenuItem (groupitem, _("Broadcast to group"))
1507
 
    groupitem.set_active (self.terminator.groupsend == 1)
1508
 
    groupitem.connect ("activate", self.set_groupsend, 1)
1509
 
    widget.append (groupitem)
1510
 
    
1511
 
    groupitem = gtk.RadioMenuItem (groupitem, _("Broadcast to all"))
1512
 
    groupitem.set_active (self.terminator.groupsend == 2)
1513
 
    groupitem.connect ("activate", self.set_groupsend, 2)
1514
 
    widget.append (groupitem)
1515
 
    
1516
 
    item = gtk.MenuItem ()
1517
 
    widget.append (item)
1518
 
    
1519
 
    item = gtk.CheckMenuItem (_("Split to this group"))
1520
 
    item.set_active (self.terminator.splittogroup)
1521
 
    item.connect ("toggled", lambda menu_item: self.do_splittogroup_toggle ())
1522
 
    if self._group == None:
1523
 
      item.set_sensitive(False)
1524
 
    widget.append (item)
1525
 
    
1526
 
    item = gtk.CheckMenuItem (_("Autoclean groups"))
1527
 
    item.set_active (self.terminator.autocleangroups)
1528
 
    item.connect ("toggled", lambda menu_item: self.do_autocleangroups_toggle ())
1529
 
    widget.append (item)
1530
 
 
1531
 
    item = gtk.MenuItem ()
1532
 
    widget.append (item)
1533
 
 
1534
 
    item = gtk.MenuItem (_("Insert terminal number"))
1535
 
    item.connect ("activate", lambda menu_item: self.do_enumerate ())
1536
 
    widget.append (item)
1537
 
 
1538
 
    item = gtk.MenuItem (_("Insert padded terminal number"))
1539
 
    item.connect ("activate", lambda menu_item: self.do_enumerate (pad=True))
1540
 
    widget.append (item)
1541
 
 
1542
 
  def position_popup_group_menu(self, menu, widget):
1543
 
    screen_w = gtk.gdk.screen_width()
1544
 
    screen_h = gtk.gdk.screen_height()
1545
 
 
1546
 
    widget_win = widget.get_window()
1547
 
    widget_x, widget_y = widget_win.get_origin()
1548
 
    widget_w, widget_h = widget_win.get_size()
1549
 
    
1550
 
    menu_w, menu_h = menu.size_request()
1551
 
    
1552
 
    if widget_y + widget_h + menu_h > screen_h:
1553
 
      menu_y = max(widget_y - menu_h, 0)
1554
 
    else:
1555
 
      menu_y = widget_y + widget_h
1556
 
    
1557
 
    return (widget_x, menu_y, 1) 
1558
 
    
1559
 
  def create_group (self, item):
1560
 
    self.groupingscope = 0
1561
 
    grplist=self.terminator.groupings[:]
1562
 
    grplist.sort()
1563
 
    
1564
 
    win = gtk.Window ()
1565
 
    vbox = gtk.VBox (False, 6)
1566
 
    vbox.set_border_width(5)
1567
 
    win.add (vbox)
1568
 
    
1569
 
    # Populate the "Assign..." Section
1570
 
    contentvbox = gtk.VBox (False, 6)
1571
 
    selframe = gtk.Frame()
1572
 
    selframe_label = gtk.Label()
1573
 
    selframe_label.set_markup(_("<b>Assign...</b>"))
1574
 
    selframe.set_shadow_type(gtk.SHADOW_NONE)
1575
 
    selframe.set_label_widget(selframe_label)
1576
 
    selframe_align = gtk.Alignment(0, 0, 1, 1)
1577
 
    selframe_align.set_padding(0, 0, 12, 0)
1578
 
    selframevbox = gtk.VBox ()
1579
 
    selframehbox = gtk.HBox ()
1580
 
    
1581
 
    # Populate the Combo with existing group names (None at the top)
1582
 
    sel_combo = gtk.combo_box_new_text()
1583
 
    sel_combo.append_text(_("Terminals with no group"))
1584
 
    for grp in grplist:
1585
 
      sel_combo.append_text(grp)
1586
 
    sel_combo.set_sensitive(False)
1587
 
    
1588
 
    # Here are the radio buttons
1589
 
    groupitem = None
1590
 
    
1591
 
    groupitem = gtk.RadioButton (groupitem, _("Terminal"))
1592
 
    groupitem.set_active (True)
1593
 
    groupitem.connect ("toggled", self.set_groupingscope, 0, sel_combo)
1594
 
    selframehbox.pack_start (groupitem, False)
1595
 
 
1596
 
    groupitem = gtk.RadioButton (groupitem, _("Group"))
1597
 
    groupitem.connect ("toggled", self.set_groupingscope, 1, sel_combo)
1598
 
    selframehbox.pack_start (groupitem, False)
1599
 
    
1600
 
    groupitem = gtk.RadioButton (groupitem, _("All"))
1601
 
    groupitem.connect ("toggled", self.set_groupingscope, 2, sel_combo)
1602
 
    selframehbox.pack_start (groupitem, False)
1603
 
    
1604
 
    selframevbox.pack_start(selframehbox, True, True)
1605
 
    selframevbox.pack_start(sel_combo, True, True)
1606
 
    selframe_align.add(selframevbox)
1607
 
    selframe.add(selframe_align)
1608
 
    contentvbox.pack_start(selframe)
1609
 
    
1610
 
    # Populate the "To..." Section
1611
 
    tgtframe = gtk.Frame()
1612
 
    tgtframe_label = gtk.Label()
1613
 
    tgtframe_label.set_markup(_("<b>To...</b>"))
1614
 
    tgtframe.set_shadow_type(gtk.SHADOW_NONE)
1615
 
    tgtframe.set_label_widget(tgtframe_label)
1616
 
    tgtframe_align = gtk.Alignment(0, 0, 1, 1)
1617
 
    tgtframe_align.set_padding(0, 0, 12, 0)
1618
 
    tgtframevbox = gtk.VBox ()
1619
 
    
1620
 
    # Populate the Combo with existing group names (None not needed)
1621
 
    tgt_comboentry = gtk.combo_box_entry_new_text()
1622
 
    for grp in grplist:
1623
 
      tgt_comboentry.append_text(grp)
1624
 
    
1625
 
    tgtframevbox.pack_start(tgt_comboentry, True, True)
1626
 
    
1627
 
    tgtframe_align.add(tgtframevbox)
1628
 
    tgtframe.add(tgtframe_align)
1629
 
    contentvbox.pack_start(tgtframe)
1630
 
    
1631
 
    okbut = gtk.Button (stock=gtk.STOCK_OK)
1632
 
    canbut = gtk.Button (stock=gtk.STOCK_CANCEL)
1633
 
    hbuttonbox = gtk.HButtonBox()
1634
 
    hbuttonbox.set_layout(gtk.BUTTONBOX_END)
1635
 
    hbuttonbox.pack_start (canbut, True, True)
1636
 
    hbuttonbox.pack_start (okbut, True, True)
1637
 
    
1638
 
    vbox.pack_start (contentvbox, False, True)
1639
 
    vbox.pack_end (hbuttonbox, False,  True)
1640
 
    
1641
 
    canbut.connect ("clicked", lambda kill: win.destroy())
1642
 
    okbut.connect ("clicked", self.do_create_group, win, sel_combo, tgt_comboentry)
1643
 
    tgt_comboentry.child.connect ("activate", self.do_create_group, win, sel_combo, tgt_comboentry)
1644
 
    
1645
 
    tgt_comboentry.grab_focus()
1646
 
    
1647
 
    # Center it over the current terminal (not perfect?!?)
1648
 
    # This could be replaced by a less bothersome dialog, but then that would
1649
 
    # center over the window, not the terminal
1650
 
    screen_w = gtk.gdk.screen_width()
1651
 
    screen_h = gtk.gdk.screen_height()
1652
 
    local_x, local_y = self.allocation.x, self.allocation.y
1653
 
    local_w, local_h = self.allocation.width, self.allocation.height
1654
 
    window_x, window_y = self.get_window().get_origin()
1655
 
    x = window_x + local_x
1656
 
    y = window_y + local_y
1657
 
    win.realize()
1658
 
    new_x = min(max(0, x+(local_w/2)-(win.allocation.width/2)), screen_w-win.allocation.width)
1659
 
    new_y = min(max(0, y+(local_h/2)-(win.allocation.height/2)), screen_h-win.allocation.height)
1660
 
    win.move(new_x, new_y)
1661
 
    
1662
 
    win.show_all ()
1663
 
 
1664
 
  def set_groupingscope(self, widget, scope=None, sel_combo=None):
1665
 
    if widget.get_active():
1666
 
      self.groupingscope = scope
1667
 
      if self.groupingscope == 1:
1668
 
        sel_combo.set_sensitive(True)
1669
 
      else:
1670
 
        sel_combo.set_sensitive(False)
1671
 
 
1672
 
  def do_create_group (self, widget, window, src, tgt):
1673
 
    tgt_name = tgt.child.get_text()
1674
 
    try:
1675
 
      src_name = src.get_active_text()
1676
 
      src_id = src.get_active()
1677
 
    except:
1678
 
      src_name = None
1679
 
    
1680
 
    if tgt_name == "" or (self.groupingscope == 1 and src_name == None):
1681
 
      return False
1682
 
      
1683
 
    if tgt_name not in self.terminator.groupings:
1684
 
      self.terminator.groupings.append (tgt_name)
1685
 
    
1686
 
    if self.groupingscope == 2:
1687
 
      for term in self.terminator.term_list:
1688
 
        term.set_group (None, tgt_name)
1689
 
    elif self.groupingscope == 1:
1690
 
      for term in self.terminator.term_list:
1691
 
        if term._group == src_name  or (src_id == 0 and term._group == None):
1692
 
          term.set_group (None, tgt_name)
1693
 
    else:
1694
 
      self.set_group (None, tgt_name)
1695
 
      
1696
 
    window.destroy ()
1697
 
 
1698
 
  def add_group (self, groupname):
1699
 
    if not groupname in self.terminator.groupings:
1700
 
      self.terminator.groupings.append(groupname)
1701
 
 
1702
 
  def set_group (self, item, data):
1703
 
    if self._group == data:
1704
 
      # No action needed
1705
 
      return
1706
 
    else:
1707
 
       self._group = data
1708
 
    
1709
 
    self._titlebox.set_group_label (data)
1710
 
    self._titlebox.update ()
1711
 
 
1712
 
    if not self._group:
1713
 
      # We were not previously in a group
1714
 
      self._titlebox.show ()
1715
 
      self._group = data
1716
 
    else:
1717
 
      # We were previously in a group
1718
 
      self._group = data
1719
 
      if data is None:
1720
 
        # We have been removed from a group
1721
 
        if not self.conf.titlebars and not self._want_titlebar:
1722
 
          self._titlebox.hide ()
1723
 
      self.terminator.group_hoover ()
1724
 
 
1725
 
  def set_groupsend (self, item, data):
1726
 
    self.terminator.groupsend = data
1727
 
 
1728
 
  def ungroup (self, widget, data):
1729
 
    for term in self.terminator.term_list:
1730
 
      if term._group == data:
1731
 
        term.set_group (None, None)
1732
 
    self.terminator.group_hoover ()
1733
 
 
1734
 
  def group_all (self, widget):
1735
 
    allname = _("All")
1736
 
    self.add_group(allname)
1737
 
    for term in self.terminator.term_list:
1738
 
      term.set_group (None, allname)
1739
 
    self.on_vte_focus_in(self._vte, None)
1740
 
    self.terminator.group_hoover ()
1741
 
 
1742
 
  def ungroup_all (self, widget):
1743
 
    for term in self.terminator.term_list:
1744
 
      term.set_group (None, None)
1745
 
    self.on_vte_focus_in(self._vte, None)
1746
 
    self.terminator.group_hoover ()
1747
 
 
1748
 
  def find_all_terms_in_tab (self, notebook, pagenum=-1):
1749
 
    if pagenum == -1:
1750
 
      pagenum = notebook.get_current_page()
1751
 
    notebookchild = notebook.get_nth_page(pagenum)
1752
 
 
1753
 
    terms = []
1754
 
 
1755
 
    for term in self.terminator.term_list:
1756
 
      termparent = term.get_parent()
1757
 
      while not isinstance(termparent, gtk.Window):
1758
 
        if termparent == notebookchild:
1759
 
          terms.append(term)
1760
 
        termparent = termparent.get_parent()
1761
 
 
1762
 
    return terms
1763
 
 
1764
 
  def group_tab (self, widget):
1765
 
    groupname = ""
1766
 
    notebook = self.terminator.get_first_parent_widget(self, gtk.Notebook)
1767
 
    pagenum = notebook.get_current_page()
1768
 
    notebookchild = notebook.get_nth_page(pagenum)
1769
 
    terms = self.find_all_terms_in_tab(notebook)
1770
 
 
1771
 
    notebooktablabel = notebook.get_tab_label(notebookchild)
1772
 
    if notebooktablabel._label._custom is True:
1773
 
      groupname = notebooktablabel.get_title()
1774
 
 
1775
 
    if groupname == "":
1776
 
      tmppagenum = pagenum
1777
 
      while True:
1778
 
        groupname = "Tab %d" % (tmppagenum + 1)
1779
 
        if groupname not in self.terminator.groupings:
1780
 
            break
1781
 
        tmppagenum += 1
1782
 
 
1783
 
    self.add_group(groupname)
1784
 
    for term in terms:
1785
 
      term.set_group(None, groupname)
1786
 
    self.on_vte_focus_in(self._vte, None)
1787
 
    self.terminator.group_hoover()
1788
 
 
1789
 
  def ungroup_tab (self, widget):
1790
 
    notebook = self.terminator.get_first_parent_widget(self, gtk.Notebook)
1791
 
    terms = self.find_all_terms_in_tab (notebook)
1792
 
 
1793
 
    for term in terms:
1794
 
      term.set_group (None, None)
1795
 
    self.on_vte_focus_in(self._vte, None)
1796
 
    self.terminator.group_hoover()
1797
 
 
1798
 
  def on_encoding_change (self, widget, encoding):
1799
 
    current = self._vte.get_encoding ()
1800
 
    if current != encoding:
1801
 
      dbg ('Setting Encoding to: %s' % encoding)
1802
 
      if encoding == self.conf.encoding:
1803
 
        self._custom_encoding = False
1804
 
      else:
1805
 
        self._custom_encoding = True
1806
 
      self._vte.set_encoding (encoding)
1807
 
      
1808
 
  def _do_encoding_items (self, menu):
1809
 
    active_encodings = self.conf.active_encodings
1810
 
    item = gtk.MenuItem (_("Encodings"))
1811
 
    menu.append (item)
1812
 
    submenu = gtk.Menu ()
1813
 
    item.set_submenu (submenu)
1814
 
    encodings = TerminatorEncoding ().get_list ()
1815
 
    encodings.sort (lambda x, y: cmp (x[2].lower (), y[2].lower ()))
1816
 
    
1817
 
    current_encoding = self._vte.get_encoding ()
1818
 
    group = None
1819
 
 
1820
 
    if current_encoding not in active_encodings:
1821
 
      active_encodings.insert (0, _(current_encoding))
1822
 
 
1823
 
    for encoding in active_encodings:
1824
 
      if encoding == self._default_encoding:
1825
 
        extratext = " (%s)" % _("Default")
1826
 
      elif encoding == current_encoding and self._custom_encoding == True:
1827
 
        extratext = " (%s)" % _("User defined")
1828
 
      else:
1829
 
        extratext = ""
1830
 
 
1831
 
      radioitem = gtk.RadioMenuItem (group, _(encoding) + extratext)
1832
 
        
1833
 
      if encoding == current_encoding:
1834
 
        radioitem.set_active (True)
1835
 
      
1836
 
      if group is None:
1837
 
        group = radioitem
1838
 
 
1839
 
      radioitem.connect ('activate', self.on_encoding_change, encoding)
1840
 
      submenu.append (radioitem)
1841
 
      
1842
 
    item = gtk.MenuItem (_("Other Encodings"))
1843
 
    submenu.append (item)
1844
 
    #second level
1845
 
 
1846
 
    submenu = gtk.Menu ()
1847
 
    item.set_submenu (submenu)
1848
 
    group = None
1849
 
 
1850
 
    for encoding in encodings:
1851
 
      if encoding[1] in active_encodings:
1852
 
        continue
1853
 
 
1854
 
      if encoding[1] is None:
1855
 
        label = "%s %s"%(encoding[2], self._vte.get_encoding ())
1856
 
      else:
1857
 
        label = "%s %s"%(encoding[2], encoding[1])
1858
 
        
1859
 
      radioitem = gtk.RadioMenuItem (group, label)
1860
 
      if group is None:
1861
 
        group = radioitem
1862
 
        
1863
 
      if encoding[1] == current_encoding:
1864
 
        radioitem.set_active (True)
1865
 
      
1866
 
      radioitem.connect ('activate', self.on_encoding_change, encoding[1])
1867
 
      submenu.append (radioitem)
1868
 
 
1869
 
  def get_window_title(self, vte = None):
1870
 
    if vte is None:
1871
 
      vte = self._vte
1872
 
    title = vte.get_window_title ()
1873
 
    if title is None:
1874
 
      title = str(self.command)
1875
 
    return title
1876
 
 
1877
 
  def on_vte_title_change(self, vte):
1878
 
    title = self.get_window_title(vte)
1879
 
    if title == self._oldtitle:
1880
 
      # Title hasn't changed, don't bother doing anything
1881
 
      return
1882
 
    self._oldtitle = title
1883
 
 
1884
 
    if self.conf.titletips:
1885
 
      vte.set_property ("has-tooltip", True)
1886
 
      vte.set_property ("tooltip-text", title)
1887
 
    #set the title anyhow, titlebars setting only show/hide the label
1888
 
    self._titlebox.set_terminal_title (title)
1889
 
    self.terminator.set_window_title (title)
1890
 
    notebookpage = self.terminator.get_first_notebook_page(vte)
1891
 
    while notebookpage != None:
1892
 
      if notebookpage[0].get_tab_label(notebookpage[1]):
1893
 
        label = notebookpage[0].get_tab_label(notebookpage[1])
1894
 
        label.set_title(title)
1895
 
        # FIXME: Is this necessary? The above line should update the label. LP #369370 might be related
1896
 
        notebookpage[0].set_tab_label(notebookpage[1], label)
1897
 
      notebookpage = self.terminator.get_first_notebook_page(notebookpage[0])
1898
 
 
1899
 
  def on_vte_focus_in(self, vte, event):
1900
 
    for term in self.terminator.term_list:
1901
 
      term._titlebox.update_colors(self)
1902
 
    return
1903
 
 
1904
 
  def on_vte_focus_out(self, vte, event):
1905
 
    return
1906
 
 
1907
 
  def on_vte_focus(self, vte):
1908
 
    title = self.get_window_title(vte)
1909
 
    self.terminator.set_window_title(title)
1910
 
    notebookpage = self.terminator.get_first_notebook_page(vte)
1911
 
    while notebookpage != None:
1912
 
      if notebookpage[0].get_tab_label(notebookpage[1]):
1913
 
        label = notebookpage[0].get_tab_label(notebookpage[1])
1914
 
        label.set_title(title)
1915
 
        notebookpage[0].set_tab_label(notebookpage[1], label)
1916
 
      notebookpage = self.terminator.get_first_notebook_page(notebookpage[0])
1917
 
 
1918
 
  def is_scrollbar_present(self):
1919
 
    return self._scrollbar.get_property('visible')
1920
 
 
1921
 
  def on_group_button_press(self, term, event):
1922
 
    if event.button == 1:
1923
 
      self.create_popup_group_menu(term, event)
1924
 
    return False