~bobbo/ssherminator/trunk

« back to all changes in this revision

Viewing changes to ssherminatorlib/terminatorterm.py

  • Committer: bobbo at ubuntu
  • Date: 2009-03-29 17:04:42 UTC
  • mfrom: (617.1.98 trunk)
  • Revision ID: bobbo@ubuntu.com-20090329170442-rhcgnpne99x8fk5n
Merge in latest terminator trunk. Bump ssherminator versioning to 0.2.0. Time to get hacking on 0.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
45
45
  error.run()
46
46
  sys.exit (1)
47
47
 
 
48
class TerminatorTermTitle (gtk.EventBox):
 
49
  wanted = None
 
50
  _title = None
 
51
  _group = None
 
52
  _separator = None
 
53
  _hbox = None
 
54
  _icon = None
 
55
  _parent = None
 
56
  _unzoomed_title = None
 
57
 
 
58
  def __init__ (self, configwanted = False):
 
59
    gtk.EventBox.__init__ (self)
 
60
 
 
61
    self._title = gtk.Label ()
 
62
    self._group = gtk.Label ()
 
63
    self._separator = gtk.VSeparator ()
 
64
    self._icon = gtk.Image ()
 
65
    self._hbox = gtk.HBox ()
 
66
 
 
67
    self._hbox.pack_start (self._icon, False, True, 2)
 
68
    self._hbox.pack_start (self._group, False, True, 2)
 
69
    self._hbox.pack_start (self._separator, False, True, 2)
 
70
    self._hbox.pack_start (self._title, True, True)
 
71
    self.add (self._hbox)
 
72
 
 
73
    self._title.show ()
 
74
    self._hbox.show ()
 
75
 
 
76
    self.wanted = configwanted
 
77
 
 
78
  def set_group_label (self, name):
 
79
    """If 'name' is None, hide the group name object, otherwise set it as the group label"""
 
80
    if name:
 
81
      self._group.set_text (name)
 
82
      self._group.show ()
 
83
      self._separator.show ()
 
84
    else:
 
85
      self._group.hide ()
 
86
      self._separator.hide ()
 
87
 
 
88
  def set_terminal_title (self, name):
 
89
    """Set the text shown in the titlebar"""
 
90
    self._title.set_text (name)
 
91
 
 
92
  def get_terminal_title (self):
 
93
    """Return the text showin in the titlebar"""
 
94
    return (self._title.get_text ())
 
95
 
 
96
  def set_background_color (self, color):
 
97
    """Set the background color of the titlebar"""
 
98
    self.modify_bg (gtk.STATE_NORMAL, color)
 
99
 
 
100
  def set_foreground_color (self, color):
 
101
    """Set the foreground color of the titlebar"""
 
102
    self._title.modify_fg (color)
 
103
 
 
104
  def set_from_icon_name (self, name, size = gtk.ICON_SIZE_MENU):
 
105
    """Set an icon for the group label"""
 
106
    if not name:
 
107
      self._icon.hide ()
 
108
      return
 
109
 
 
110
    self._icon.set_from_icon_name (APP_NAME + name, size)
 
111
    self._icon.show ()
 
112
 
 
113
  def update (self):
 
114
    """Update our state"""
 
115
    if not self._parent:
 
116
      self._parent = self.get_parent ()
 
117
 
 
118
    if self._parent.terminator._zoomed and len (self._parent.terminator.term_list):
 
119
      self._unzoomed_title = self.get_terminal_title ()
 
120
      self.set_terminal_title ("Zoomed/Maximised terminal, %d hidden" % (len (self._parent.terminator.term_list) - 1))
 
121
      self.show()
 
122
      return
 
123
    else:
 
124
      if self._unzoomed_title:
 
125
        self.set_terminal_title (self._unzoomed_title)
 
126
        self._unzoomed_title = None
 
127
 
 
128
    if isinstance (self._parent.get_parent (), gtk.Window):
 
129
      self.hide()
 
130
      return
 
131
 
 
132
    if (self._parent.conf.titlebars and self.wanted) or self._parent._group:
 
133
      self.show ()
 
134
    else:
 
135
      self.hide ()
 
136
 
 
137
    if self._parent._group:
 
138
      self.set_group_label (self._parent._group)
 
139
    else:
 
140
      self.set_group_label (None)
 
141
 
48
142
class TerminatorTerm (gtk.VBox):
49
143
 
50
 
  matches = {}
 
144
  matches = None
51
145
  TARGET_TYPE_VTE = 8
52
146
  _custom_font_size = None
53
147
  _group = None
54
 
  _want_titlebar = False
 
148
  focus = None
 
149
  _urgent_bell_cnid = None
55
150
 
56
151
  def __init__ (self, terminator, profile = None, command = None, cwd = None, ssh=None):
57
152
    gtk.VBox.__init__ (self)
59
154
    self.conf = terminator.conf
60
155
    self.command = command
61
156
    self._oldtitle = ""
 
157
    self.matches = {}
62
158
 
63
159
    self.cwd = cwd or os.getcwd();
64
160
    if not os.path.exists(self.cwd) or not os.path.isdir(self.cwd):
71
167
    self._vte = vte.Terminal ()
72
168
    if not hasattr(self._vte, "set_opacity") or not hasattr(self._vte, "is_composited"):
73
169
      self._composited_support = False
 
170
    dbg ('H9TRANS: composited_support: %s' % self._composited_support)
74
171
    #self._vte.set_double_buffered(True)
75
172
    self._vte.set_size (80, 24)
76
 
    self.reconfigure_vte ()
77
173
    self._vte._expose_data = None
78
174
    self._vte.show ()
79
175
 
80
176
    self._termbox = gtk.HBox ()
81
177
    self._termbox.show()
82
 
    self._title = gtk.Label()
83
 
    self._title.show()
84
 
    self._titlegroup = gtk.Label()
85
 
    self._titlesep = gtk.VSeparator ()
86
 
    self._titlebox = gtk.EventBox ()
87
 
    self._titlehbox = gtk.HBox()
88
 
    self._titlehbox.pack_start (self._titlegroup, False, True)
89
 
    self._titlehbox.pack_start (self._titlesep, False, True, 2)
90
 
    self._titlehbox.pack_start (self._title, True, True)
91
 
    self._titlehbox.show ()
92
 
    self._titlebox.add (self._titlehbox)
 
178
    
 
179
    self._titlebox = TerminatorTermTitle (self.conf.titlebars)
93
180
 
94
181
    self._search_string = None
95
182
    self._searchbox = gtk.HBox()
135
222
    self.pack_start(self._termbox)
136
223
    self.pack_end(self._searchbox)
137
224
 
138
 
    if self.conf.titlebars:
139
 
      self._titlebox.show()
140
 
      self._want_titlebar = True
141
 
    else:
142
 
      self._titlebox.hide()
 
225
    self._titlebox.update ()
143
226
 
144
227
    self._scrollbar = gtk.VScrollbar (self._vte.get_adjustment ())
145
228
    if self.scrollbar_position != "hidden" and self.scrollbar_position != "disabled":
192
275
    self._vte.add_events (gtk.gdk.ENTER_NOTIFY_MASK)
193
276
    self._vte.connect ("enter_notify_event", self.on_vte_notify_enter)
194
277
 
 
278
    self._vte.connect_after ("realize", self.reconfigure_vte)
 
279
 
195
280
    self.add_matches(posix = self.conf.try_posix_regexp)
196
281
 
197
282
    dbg ('SEGBUG: Setting http_proxy')
211
296
      
212
297
    dbg ('SEGBUG: TerminatorTerm __init__ complete')
213
298
 
 
299
  def prepareurl (self, url, match):
 
300
    dbg ("prepareurl: Checking '%s' with a match of '%s'" % (url, match))
 
301
    if match == self.matches['email'] and url[0:7] != 'mailto:':
 
302
      url = 'mailto:' + url
 
303
    elif match == self.matches['addr_only'] and url[0:3] == 'ftp':
 
304
      url = 'ftp://' + url
 
305
    elif match == self.matches['addr_only']:
 
306
      url = 'http://' + url
 
307
    elif match == self.matches['launchpad']:
 
308
      url = 'https://bugs.launchpad.net/bugs/%s' % re.sub (r'[^0-9]+', '', url)
 
309
    
 
310
    return url
 
311
 
214
312
  def openurl (self, url):
215
313
    dbg ('openurl: viewing %s'%url)
216
314
    try:
313
411
    #here, we define some widget internal values
314
412
    widget._expose_data = { 'color': color, 'coord' : coord }
315
413
    #redraw by forcing an event
316
 
    connec = widget.connect('expose-event', self.on_expose_event)
 
414
    connec = widget.connect_after('expose-event', self.on_expose_event)
317
415
    widget.window.invalidate_rect(rect, True)
318
416
    widget.window.process_updates(True)
319
417
    #finaly reset the values
413
511
      else:
414
512
        err ('add_matches: Failed adding URL match patterns')
415
513
    else:
 
514
      self.matches['voip'] = self._vte.match_add(lboundry + '(callto:|h323:|sip:)' + "[" + userchars + "+][" + userchars + ".]*(:[0-9]+)?@?[" + pathchars + "]+" + rboundry)
416
515
      self.matches['addr_only'] = self._vte.match_add (lboundry + "(www|ftp)[" + hostchars + "]*\.[" + hostchars + ".]+(:[0-9]+)?(" + urlpath + ")?" + rboundry + "/?")
417
516
      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)
418
517
      self.matches['nntp'] = self._vte.match_add (lboundry + '''news:[-A-Z\^_a-z{|}~!"#$%&'()*+,./0-9;:=?`]+@[-A-Za-z0-9.]+(:[0-9]+)?''' + rboundry)
511
610
    dbg ('SEGBUG: Forked command')
512
611
 
513
612
    self.on_vte_title_change(self._vte) # Force an initial update of our titles
514
 
    self._title.show()
 
613
    self._titlebox.update ()
515
614
 
516
615
    if self._pid == -1:
517
616
      err (_('Unable to start shell: ') + shell)
530
629
    dbg ('get_cwd found: %s'%cwd)
531
630
    return (cwd)
532
631
 
533
 
  def reconfigure_vte (self):
 
632
  def reconfigure_vte (self, widget = None):
534
633
    # Set our emulation
535
634
    self._vte.set_emulation (self.conf.emulation)
536
635
 
588
687
        palette.append (gtk.gdk.color_parse (color))
589
688
    self._vte.set_colors (fg_color, bg_color, palette)
590
689
 
 
690
    cursor_color = self.conf.cursor_color
 
691
    if cursor_color != '':
 
692
      self._vte.set_color_cursor (gtk.gdk.color_parse (cursor_color))
 
693
 
 
694
    # Set cursor shape
 
695
    if hasattr (self._vte, "set_cursor_shape"):
 
696
      self._vte.set_cursor_shape (getattr (vte, "CURSOR_SHAPE_" + self.conf.cursor_shape.upper ()))
 
697
 
591
698
    # Set our background image, transparency and type
592
699
    # Many thanks to the authors of gnome-terminal, on which this code is based.
593
700
    background_type = self.conf.background_type
621
728
      dbg ('H9TRANS: Set opacity to: %s' % opacity)
622
729
      self._vte.set_opacity(opacity)
623
730
 
624
 
    if self._composited_support and not self._vte.is_composited():
625
 
      dbg ('H9TRANS: Set background transparency to: %s' % (background_type == "transparent"))
626
 
      self._vte.set_background_transparent (background_type == "transparent")
627
 
    else:
628
 
      dbg ('H9TRANS: Set background transparency to: False, hard')
629
 
      self._vte.set_background_transparent (False)
 
731
    if background_type == "transparent":
 
732
      if not self.conf.enable_real_transparency:
 
733
        self._vte.set_background_transparent (True)
 
734
      else:
 
735
        self._vte.set_background_transparent (False)
630
736
 
631
737
    # Set our cursor blinkiness
632
738
    self._vte.set_cursor_blinks (self.conf.cursor_blink)
633
739
 
634
 
    # Set our audible belliness
635
 
    silent_bell = self.conf.silent_bell
636
 
    self._vte.set_audible_bell (not silent_bell)
637
 
 
638
 
    # Set our visual flashiness
639
 
    self._vte.set_visible_bell (silent_bell)
640
 
 
641
 
    # Override our flashybelliness
642
740
    if self.conf.force_no_bell:
 
741
      self._vte.set_audible_bell (False)
643
742
      self._vte.set_visible_bell (False)
644
 
      self._vte.set_audible_bell (False)
 
743
      if self._urgent_bell_cnid:
 
744
        self._vte.disconnect (self._urgent_bell_cnid)
 
745
        self._urgent_bell_cnid = None
 
746
    else:
 
747
      # Set our audible belliness
 
748
      self._vte.set_audible_bell (self.conf.audible_bell)
 
749
 
 
750
      # Set our visual flashiness
 
751
      self._vte.set_visible_bell (self.conf.visible_bell)
 
752
 
 
753
      # Set our urgent belliness
 
754
      if self.conf.urgent_bell:
 
755
        try:
 
756
          self._urgent_bell_cnid = self._vte.connect ("beep", self.terminator.on_beep)
 
757
        except TypeError:
 
758
          err ("beep signal not supported by your VTE, urgent handler not available")
 
759
      elif self._urgent_bell_cnid:
 
760
        self._vte.disconnect (self._urgent_bell_cnid)
 
761
        self._urgent_bell_cnid = None
645
762
 
646
763
    # Set our scrolliness
647
764
    self._vte.set_scrollback_lines (self.conf.scrollback_lines)
660
777
        elif self.scrollbar_position == 'left':
661
778
          self._termbox.reorder_child (self._scrollbar, 0)
662
779
 
 
780
    if hasattr (self._vte, "set_alternate_screen_scroll"):
 
781
      self._vte.set_alternate_screen_scroll (self.conf.alternate_screen_scroll)
 
782
 
663
783
    # Set our sloppiness
664
784
    self.focus = self.conf.focus
665
785
 
675
795
      if event.button == 1:
676
796
        url = self._vte.match_check (int (event.x / self._vte.get_char_width ()), int (event.y / self._vte.get_char_height ()))
677
797
        if url:
678
 
          if (url[0][0:7] != "mailto:") & (url[1] == self.matches['email']):
679
 
            address = "mailto:" + url[0]
680
 
          elif url[1] == self.matches['launchpad']:
681
 
            # the only part of 'launchpad' we need are the actual numbers for the bug
682
 
            address = "https://bugs.launchpad.net/bugs/%s" % re.sub(r'[^0-9]+', '', url[0])
683
 
          else:
684
 
            address = url[0]
685
 
          self.openurl ( address )
 
798
          self.openurl (self.prepareurl (url[0], url[1]))
686
799
      return False
687
800
 
688
801
    # Left mouse button should transfer focus to this vte widget
708
821
    self.toggle_widget_visibility (self._scrollbar)
709
822
 
710
823
  def do_title_toggle (self):
711
 
    self._want_titlebar = not self._titlebox.get_property ('visible')
 
824
    self._titlebox.wanted = not self._titlebox.get_property ('visible')
712
825
    self.toggle_widget_visibility (self._titlebox)
713
826
 
714
827
  def toggle_widget_visibility (self, widget):
743
856
      dbg ('on_vte_key_press: Called on %s with no event' % term)
744
857
      return False
745
858
    mapping = self.terminator.keybindings.lookup(event)
 
859
    
 
860
    if mapping == "hide_window":
 
861
      return False
746
862
 
747
863
    if mapping and mapping not in self.UnhandledKeybindings:
748
 
      dbg("on_vte_key_press: lookup found %r" % mapping)
749
 
      getattr(self, "key_" + mapping)()
750
 
      return True
 
864
      dbg("on_vte_key_press: lookup found %r" % mapping) 
 
865
      # handle the case where user has re-bound copy to ctrl+<key>
 
866
      # we only copy if there is a selection otherwise let it fall through to ^<key>
 
867
      if (mapping == "copy" and event.state & gtk.gdk.CONTROL_MASK):
 
868
        if self._vte.get_has_selection ():
 
869
          getattr(self, "key_" + mapping)()
 
870
          return True
 
871
      else:
 
872
        getattr(self, "key_" + mapping)()
 
873
        return True
751
874
 
752
875
    if self._group and self._vte.is_focus ():
753
876
      self.terminator.group_emit (self, self._group, 'key-press-event', event)
981
1104
  def create_popup_menu (self, widget, event = None):
982
1105
    menu = gtk.Menu ()
983
1106
    url = None
 
1107
    address = None
984
1108
 
985
1109
    if event:
986
1110
      url = self._vte.match_check (int (event.x / self._vte.get_char_width ()), int (event.y / self._vte.get_char_height ()))
991
1115
      time = 0
992
1116
 
993
1117
    if url:
994
 
      if url[1] != self.matches['email']:
995
 
        # Add protocol if we launch a URL without it, otherwise xdg-open won't open it
996
 
        if url[1] == self.matches['addr_only']:
997
 
          if url[0][0:3] == "ftp":
998
 
              # "ftp.foo.bar" -> "ftp://ftp.foo.bar"
999
 
              address = "ftp://" + url[0]
1000
 
          else:
1001
 
              # Assume http
1002
 
              address = "http://" + url[0]
1003
 
        elif url[1] == self.matches['launchpad']:
1004
 
          # the only part of 'launchpad' we need are the actual numbers for the bug 
1005
 
          address = "https://bugs.launchpad.net/bugs/%s" % re.sub(r'[^0-9]+', '', url[0])
1006
 
        else:
1007
 
          address = url[0]
 
1118
      address = self.prepareurl (url[0], url[1])
 
1119
 
 
1120
      if url[1] == self.matches['email']:
 
1121
        nameopen = _("_Send Mail To...")
 
1122
        namecopy = _("_Copy Email Address")
 
1123
        item = gtk.MenuItem (nameopen)
 
1124
      elif url[1] == self.matches['voip']:
 
1125
        nameopen = _("Ca_ll To...")
 
1126
        namecopy = _("_Copy Call Address")
 
1127
        item = gtk.MenuItem (nameopen)
 
1128
      else:
1008
1129
        nameopen = _("_Open Link")
1009
1130
        namecopy = _("_Copy Link Address")
1010
1131
        iconopen = gtk.image_new_from_stock(gtk.STOCK_JUMP_TO, gtk.ICON_SIZE_MENU)
1011
1132
 
1012
1133
        item = gtk.ImageMenuItem (nameopen)
1013
1134
        item.set_property('image', iconopen)
1014
 
      else:
1015
 
        if url[0][0:7] != "mailto:":
1016
 
          address = "mailto:" + url[0]
1017
 
        else:
1018
 
          address = url[0]
1019
 
        nameopen = _("_Send Mail To...")
1020
 
        namecopy = _("_Copy Email Address")
1021
 
 
1022
 
        item = gtk.MenuItem (nameopen)
1023
1135
 
1024
1136
      item.connect ("activate", lambda menu_item: self.openurl (address))
1025
1137
      menu.append (item)
1222
1334
    if self._group == data:
1223
1335
      # No action needed
1224
1336
      return
1225
 
      
1226
 
    if data:
1227
 
      self._titlegroup.set_text (data)
1228
 
      self._titlegroup.show()
1229
 
      self._titlesep.show ()
1230
1337
    else:
1231
 
      self._titlegroup.hide()
1232
 
      self._titlesep.hide ()
 
1338
       self._group = data
 
1339
    
 
1340
    self._titlebox.set_group_label (data)
 
1341
    self._titlebox.update ()
1233
1342
 
1234
 
    if not self._group:
1235
 
      # We were not previously in a group
1236
 
      self._titlebox.show ()
1237
 
      self._group = data
1238
 
    else:
1239
 
      # We were previously in a group
1240
 
      self._group = data
1241
 
      if data == None:
1242
 
        # We have been removed from a group
1243
 
        if not self.conf.titlebars and not self._want_titlebar:
1244
 
          self._titlebox.hide ()
1245
 
      self.terminator.group_hoover ()
 
1343
    self.terminator.group_hoover ()
1246
1344
 
1247
1345
  def group_all (self, widget):
1248
1346
    allname = _("All")
1462
1560
      vte.set_property ("has-tooltip", True)
1463
1561
      vte.set_property ("tooltip-text", title)
1464
1562
    #set the title anyhow, titlebars setting only show/hide the label
1465
 
    self._title.set_text(title)
 
1563
    self._titlebox.set_terminal_title (title)
1466
1564
    self.terminator.set_window_title("%s - %s" % (re.sub(' - %s' % APP_NAME.capitalize(), '', title), APP_NAME.capitalize()))
1467
1565
    notebookpage = self.terminator.get_first_notebook_page(vte)
1468
1566
    while notebookpage != None:
1473
1571
      notebookpage = self.terminator.get_first_notebook_page(notebookpage[0])
1474
1572
 
1475
1573
  def on_vte_focus_in(self, vte, event):
1476
 
    self._titlebox.modify_bg(gtk.STATE_NORMAL,self.terminator.window.get_style().bg[gtk.STATE_SELECTED])
 
1574
    self._titlebox.set_background_color (self.terminator.window.get_style().bg[gtk.STATE_SELECTED])
1477
1575
    return
1478
1576
 
1479
1577
  def on_vte_focus_out(self, vte, event):
1480
 
    self._titlebox.modify_bg(gtk.STATE_NORMAL, self.terminator.window.get_style().bg[gtk.STATE_NORMAL])
 
1578
    self._titlebox.set_background_color (self.terminator.window.get_style().bg[gtk.STATE_NORMAL])
1481
1579
    return
1482
1580
 
1483
1581
  def on_vte_focus(self, vte):
1490
1588
        label.set_title(title)
1491
1589
        notebookpage[0].set_tab_label(notebookpage[1], label)
1492
1590
      notebookpage = self.terminator.get_first_notebook_page(notebookpage[0])
 
1591
 
 
1592
  def is_scrollbar_present(self):
 
1593
          return self._scrollbar.get_property('visible')
1493
1594