From 113ae17b7913a0e310ebc3dd5fa1f8e2e2ee60f2 Mon Sep 17 00:00:00 2001 From: Emmanuel Bretelle Date: Fri, 4 Jul 2008 12:45:36 +0100 Subject: [PATCH] porting .spec to 0.9 --- terminator | 1664 +++++++++++++++++++++++++++++++++++++++++++++++ terminator.spec | 5 +- 2 files changed, 1668 insertions(+), 1 deletion(-) diff --git a/terminator b/terminator index d367a07d..a6c46847 100755 --- a/terminator +++ b/terminator @@ -52,8 +52,1672 @@ except: "gobject, gtk and pango to run Terminator.")) sys.exit(1) +<<<<<<< TREE +# import a library for viewing URLs +try: + # gnome.url_show() is really useful + import gnome + url_show = gnome.url_show +except: + # webbrowser.open() is not really useful, but will do as a fallback + import webbrowser + url_show = webbrowser.open + +# import vte-bindings +try: + import vte +except: + error = gtk.MessageDialog (None, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, + _('You need to install python bindings for libvte ("python-vte" in debian/ubuntu)')) + error.run() + sys.exit (1) + +def openurl (url): + try: + if subprocess.call(["xdg-open", url]) != 0: + raise + except: + try: + url_show (url) + except: + pass + +class TerminatorTerm (gtk.VBox): + + matches = {} + + def __init__ (self, terminator, profile = None, command = None, cwd = None): + gtk.VBox.__init__ (self) + self.terminator = terminator + self.conf = terminator.conf + self.command = command + + self.cwd = cwd or os.getcwd(); + if not os.path.exists(self.cwd) or not os.path.isdir(self.cwd): + self.cwd = pwd.getpwuid(os.getuid ())[5] + + self.clipboard = gtk.clipboard_get (gtk.gdk.SELECTION_CLIPBOARD) + self.scrollbar_position = self.conf.scrollbar_position + + self._vte = vte.Terminal () + self._vte.set_size (80, 24) + self.reconfigure_vte () + self._vte.show () + + self._termbox = gtk.HBox () + self._termbox.show() + self._title = gtk.Label() + self._title.show() + self._titlebox = gtk.EventBox() + self._titlebox.add(self._title) + self.show() + self.pack_start(self._titlebox, False) + self.pack_start(self._termbox) + + if self.conf.titlebars: + self._titlebox.show() + else: + self._titlebox.hide() + + self._scrollbar = gtk.VScrollbar (self._vte.get_adjustment ()) + if self.scrollbar_position != "hidden" and self.scrollbar_position != "disabled": + self._scrollbar.show () + + if self.scrollbar_position == 'left': + packfunc = self._termbox.pack_end + else: + packfunc = self._termbox.pack_start + + packfunc (self._vte) + packfunc (self._scrollbar, False) + + self._vte.connect ("key-press-event", self.on_vte_key_press) + self._vte.connect ("button-press-event", self.on_vte_button_press) + self._vte.connect ("popup-menu", self.on_vte_popup_menu) + + """drag and drop""" + srcvtetargets = [ ( "vte", gtk.TARGET_SAME_APP, TARGET_TYPE_VTE ) ] + dsttargets = [ ( "vte", gtk.TARGET_SAME_APP, TARGET_TYPE_VTE ), ('text/plain', 0, 0) , ("STRING", 0, 0), ("COMPOUND_TEXT", 0, 0)] + self._vte.drag_source_set( gtk.gdk.CONTROL_MASK | gtk.gdk.BUTTON3_MASK, srcvtetargets, gtk.gdk.ACTION_MOVE) + self._titlebox.drag_source_set( gtk.gdk.BUTTON1_MASK, srcvtetargets, gtk.gdk.ACTION_MOVE) + #self._vte.drag_dest_set(gtk.DEST_DEFAULT_MOTION | gtk.DEST_DEFAULT_HIGHLIGHT |gtk.DEST_DEFAULT_DROP ,dsttargets, gtk.gdk.ACTION_MOVE) + self._vte.drag_dest_set(gtk.DEST_DEFAULT_MOTION | gtk.DEST_DEFAULT_HIGHLIGHT |gtk.DEST_DEFAULT_DROP ,dsttargets, gtk.gdk.ACTION_MOVE) + self._vte.connect("drag-begin", self.on_drag_begin, self) + self._titlebox.connect("drag-begin", self.on_drag_begin, self) + self._vte.connect("drag-data-get", self.on_drag_data_get, self) + self._titlebox.connect("drag-data-get", self.on_drag_data_get, self) + #for testing purpose: drag-motion + self._vte.connect("drag-motion", self.on_drag_motion, self) + self._vte.connect("drag-data-received", self.on_drag_data_received, self) + + if self.conf.copy_on_selection: + self._vte.connect ("selection-changed", lambda widget: self._vte.copy_clipboard ()) + + self._vte.connect ("composited-changed", self.on_composited_changed) + self._vte.connect ("window-title-changed", self.on_vte_title_change) + self._vte.connect ("grab-focus", self.on_vte_focus) + self._vte.connect ("focus-out-event", self.on_vte_focus_out) + self._vte.connect ("focus-in-event", self.on_vte_focus_in) + + + exit_action = self.conf.exit_action + if exit_action == "restart": + self._vte.connect ("child-exited", self.spawn_child) + # We need to support "left" because some buggy versions of gnome-terminal + # set it in some situations + elif exit_action in ("close", "left"): + self._vte.connect ("child-exited", lambda close_term: self.terminator.closeterm (self)) + + self._vte.add_events (gtk.gdk.ENTER_NOTIFY_MASK) + self._vte.connect ("enter_notify_event", self.on_vte_notify_enter) + + self.add_matches() + + dbg ('SEGBUG: Setting http_proxy') + env_proxy = os.getenv ('http_proxy') + if not env_proxy and self.conf.http_proxy and self.conf.http_proxy != '': + os.putenv ('http_proxy', self.conf.http_proxy) + + dbg ('SEGBUG: Setting COLORTERM') + os.putenv ('COLORTERM', 'gnome-terminal') + dbg ('SEGBUG: TerminatorTerm __init__ complete') + + def on_drag_begin(self, widget, drag_context, data): + dbg ('Drag begins') + widget.drag_source_set_icon_pixbuf(self.terminator.icon_theme.load_icon (APP_NAME, 48, 0)) + + def on_drag_data_get(self,widget, drag_context, selection_data, info, time, data): + dbg ("Drag data get") + selection_data.set("vte",info, str(data.terminator.term_list.index (self))) + + + def on_drag_motion(self, widget, drag_context, x, y, time, data): + dbg ("Drag Motion on ") + """ +x-special/gnome-icon-list +text/uri-list +UTF8_STRING +COMPOUND_TEXT +TEXT +STRING +text/plain;charset=utf-8 +text/plain;charset=UTF-8 +text/plain + """ + + if 'text/plain' in drag_context.targets: + #copy text from another widget + return + srcwidget = drag_context.get_source_widget() + if (isinstance(srcwidget, gtk.EventBox) and srcwidget == self._titlebox) or widget == srcwidget: + #on self + return + + + alloc = widget.allocation + rect = gtk.gdk.Rectangle(0, 0, alloc.width, alloc.height) + widget.window.invalidate_rect(rect, True) + widget.window.process_updates(True) + + context = widget.window.cairo_create() + if self.conf.use_theme_colors: + color = self._vte.get_style ().text[gtk.STATE_NORMAL] + else: + color = gtk.gdk.color_parse (self.conf.foreground_color) + + + context.set_source_rgba(color.red, color.green, color.blue, 0.5) + + pos = self.get_location(widget, x, y) + topleft = (0,0) + topright = (alloc.width,0) + topmiddle = (alloc.width/2,0) + bottomleft = (0, alloc.height) + bottomright = (alloc.width,alloc.height) + bottommiddle = (alloc.width/2, alloc.height) + middle = (alloc.width/2, alloc.height/2) + middleleft = (0, alloc.height/2) + middleright = (alloc.width, alloc.height/2) + #print "%f %f %d %d" %(coef1, coef2, b1,b2) + coord = () + if pos == "right": + coord = (topright, topmiddle, bottommiddle, bottomright) + if pos == "top": + coord = (topleft, topright, middleright , middleleft) + if pos == "left": + coord = (topleft, topmiddle, bottommiddle, bottomleft) + if pos == "bottom": + coord = (bottomleft, bottomright, middleright , middleleft) + + if len(coord) > 0 : + context.move_to(coord[len(coord)-1][0],coord[len(coord)-1][1]) + for i in coord: + context.line_to(i[0],i[1]) + + context.fill() + + + def on_drag_drop(self, widget, drag_context, x, y, time): + parent = widget.get_parent() + dbg ('Drag drop on %s'%parent) + + def on_drag_data_received(self, widget, drag_context, x, y, selection_data, info, time, data): + dbg ("Drag Data Received") + if selection_data.type == 'text/plain': + #copy text to destination + #print "%s %s" % (selection_data.type, selection_data.target) + txt = selection_data.data.strip() + if txt[0:7] == "file://": + txt = "'%s'" % txt[7:] + self._vte.feed_child(txt) + return + + widgetsrc = data.terminator.term_list[int(selection_data.data)] + srcvte = drag_context.get_source_widget() + #check if computation requireds + if (isinstance(srcvte, gtk.EventBox) and srcvte == self._titlebox) or srcvte == widget: + dbg (" on itself") + return + + srchbox = widgetsrc + dsthbox = widget.get_parent().get_parent() + + dstpaned = dsthbox.get_parent() + srcpaned = srchbox.get_parent() + if isinstance(dstpaned, gtk.Window) and isinstance(srcpaned, gtk.Window): + dbg (" Only one terminal") + return + pos = self.get_location(widget, x, y) + + data.terminator.remove(widgetsrc) + data.terminator.add(self, widgetsrc,pos) + return + + + def get_location(self, vte, x, y): + pos = "" + #get the diagonales function for the receiving widget + coef1 = float(vte.allocation.height)/float(vte.allocation.width) + coef2 = -float(vte.allocation.height)/float(vte.allocation.width) + b1 = 0 + b2 = vte.allocation.height + #determine position in rectangle + """ + -------- + |\ /| + | \ / | + | \/ | + | /\ | + | / \ | + |/ \| + -------- + """ + if (x*coef1 + b1 > y ) and (x*coef2 + b2 < y ): + pos = "right" + if (x*coef1 + b1 > y ) and (x*coef2 + b2 > y ): + pos = "top" + if (x*coef1 + b1 < y ) and (x*coef2 + b2 > y ): + pos = "left" + if (x*coef1 + b1 < y ) and (x*coef2 + b2 < y ): + pos = "bottom" + return pos + + + def add_matches (self, lboundry="[[:<:]]", rboundry="[[:>:]]"): + userchars = "-A-Za-z0-9" + passchars = "-A-Za-z0-9,?;.:/!%$^*&~\"#'" + hostchars = "-A-Za-z0-9" + pathchars = "-A-Za-z0-9_$.+!*(),;:@&=?/~#%'" + schemes = "(news:|telnet:|nntp:|file:/|https?:|ftps?:|webcal:)" + user = "[" + userchars + "]+(:[" + passchars + "]+)?" + urlpath = "/[" + pathchars + "]*[^]'.}>) \t\r\n,\\\"]" + + self.matches['full_uri'] = self._vte.match_add(lboundry + schemes + "//(" + user + "@)?[" + hostchars +".]+(:[0-9]+)?(" + urlpath + ")?" + rboundry + "/?") + + # FreeBSD works with [[:<:]], Linux works with \< + if self.matches['full_uri'] == -1: + if lboundry != "\\<": + self.add_matches(lboundry = "\\<", rboundry = "\\>") + else: + self.matches['addr_only'] = self._vte.match_add (lboundry + "(www|ftp)[" + hostchars + "]*\.[" + hostchars + ".]+(:[0-9]+)?(" + urlpath + ")?" + rboundry + "/?") + self.matches['email'] = self._vte.match_add (lboundry + "(mailto:)?[a-zA-Z0-9][a-zA-Z0-9.+-]*@[a-zA-Z0-9][a-zA-Z0-9-]*\.[a-zA-Z0-9][a-zA-Z0-9-]+[.a-zA-Z0-9-]*" + rboundry) + self.matches['nntp'] = self._vte.match_add (lboundry + '''news:[-A-Z\^_a-z{|}~!"#$%&'()*+,./0-9;:=?`]+@[-A-Za-z0-9.]+(:[0-9]+)?''' + rboundry) + + def spawn_child (self, event=None): + update_records = self.conf.update_records + login = self.conf.login_shell + args = [] + shell = '' + + if self.command: + dbg ('spawn_child: using self.command: %s'%self.command) + args = self.command + shell = self.command[0] + elif self.conf.use_custom_command: + dbg ('spawn_child: using custom command: %s'%self.conf.custom_command) + args = self.conf.custom_command.split () + shell = args[0] + + try: + if os.environ['PATH'] == "": + raise (ValueError) + paths = os.environ['PATH'].split(':') + except: + paths = ['/usr/local/bin', '/usr/bin', '/bin'] + dbg ('spawn_child: found paths: "%s"'%paths) + + if self.conf.use_custom_command and shell[0] != '/': + for path in paths: + dbg ('spawn_child: looking for pathless custom command "%s"'%os.path.join (path, shell)) + if os.path.exists (os.path.join (path, shell)): + shell = os.path.join (path, shell) + break + + if not self.command and not os.path.exists (shell): + dbg ('spawn_child: hunting for a command') + shell = os.getenv ('SHELL') or '' + args = [] + if not os.path.exists (shell): + dbg ('spawn_child: No usable shell in $SHELL (%s)'%os.getenv('SHELL')) + shell = pwd.getpwuid (os.getuid ())[6] or '' + if not os.path.exists (shell): + for i in ['bash','zsh','tcsh','ksh','csh','sh']: + for p in paths: + shell = os.path.join(p, i) + dbg ('spawn_child: Checking if "%s" exists'%shell) + if not os.path.exists (shell): + dbg ('spawn_child: %s does not exist'%shell) + continue + else: + dbg ('spawn_child: %s does exist'%shell) + break + if os.path.exists (shell): + break + + if not self.command and not os.path.exists (shell): + # Give up, we're completely stuck + err (_('Unable to find a shell')) + gobject.timeout_add (100, self.terminator.closeterm, self) + return (-1) + + if not args: + args.append (shell) + + if self.conf.login_shell: + args[0] = "-%s"%args[0] + + dbg ('SEGBUG: Setting WINDOWID') + os.putenv ('WINDOWID', '%s'%self._vte.get_parent_window().xid) + + dbg ('SEGBUG: Forking command: "%s" with args "%s", loglastlog = "%s", logwtmp = "%s", logutmp = "%s" and cwd "%s"'%(shell, args, login, update_records, update_records, self.cwd)) + self._pid = self._vte.fork_command (command = shell, argv = args, envv = [], loglastlog = login, logwtmp = update_records, logutmp = update_records, directory=self.cwd) + + dbg ('SEGBUG: Forked command') + if self._pid == -1: + err (_('Unable to start shell: ') + shell) + return (-1) + + def get_cwd (self): + """ Return the current working directory of the subprocess. + This function requires OS specific behaviours + """ + cwd = pid_get_cwd (self._pid) + dbg ('get_cwd found: %s'%cwd) + return (cwd) + + def reconfigure_vte (self): + # Set our emulation + self._vte.set_emulation (self.conf.emulation) + + # Set our wordchars + self._vte.set_word_chars (self.conf.word_chars) + + # Set our mouselation + self._vte.set_mouse_autohide (self.conf.mouse_autohide) + + # Set our compatibility + backspace = self.conf.backspace_binding + delete = self.conf.delete_binding + +# Note, each of the 4 following comments should replace the line beneath it, but the python-vte bindings don't appear to support this constant, so the magic values are being assumed from the C enum :/ + if backspace == "ascii-del": +# backbind = vte.ERASE_ASCII_BACKSPACE + backbind = 2 + else: +# backbind = vte.ERASE_AUTO_BACKSPACE + backbind = 1 + + if delete == "escape-sequence": +# delbind = vte.ERASE_DELETE_SEQUENCE + delbind = 3 + else: +# delbind = vte.ERASE_AUTO + delbind = 0 + + self._vte.set_backspace_binding (backbind) + self._vte.set_delete_binding (delbind) + + # Set our font + try: + self._vte.set_font (pango.FontDescription (self.conf.font)) + except: + pass + + # Set our boldness + self._vte.set_allow_bold (self.conf.allow_bold) + + # Set our color scheme + palette = self.conf.palette + if self.conf.use_theme_colors: + fg_color = self._vte.get_style ().text[gtk.STATE_NORMAL] + bg_color = self._vte.get_style ().base[gtk.STATE_NORMAL] + else: + fg_color = gtk.gdk.color_parse (self.conf.foreground_color) + bg_color = gtk.gdk.color_parse (self.conf.background_color) + + colors = palette.split (':') + palette = [] + for color in colors: + if color: + palette.append (gtk.gdk.color_parse (color)) + self._vte.set_colors (fg_color, bg_color, palette) + + # Set our background image, transparency and type + # Many thanks to the authors of gnome-terminal, on which this code is based. + background_type = self.conf.background_type + + # set background image settings + if background_type == "image": + self._vte.set_background_image_file (self.conf.background_image) + self._vte.set_scroll_background (self.conf.scroll_background) + else: + self._vte.set_background_image_file('') + self._vte.set_scroll_background(False) + + # set transparency for the background (image) + if background_type in ("image", "transparent"): + self._vte.set_background_tint_color (bg_color) + self._vte.set_background_saturation(1 - (self.conf.background_darkness)) + self._vte.set_opacity(int(self.conf.background_darkness * 65535)) + else: + self._vte.set_background_saturation(1) + self._vte.set_opacity(65535) + + if not self._vte.is_composited(): + self._vte.set_background_transparent (background_type == "transparent") + else: + self._vte.set_background_transparent (False) + + # Set our cursor blinkiness + self._vte.set_cursor_blinks = (self.conf.cursor_blink) + + # Set our audible belliness + silent_bell = self.conf.silent_bell + self._vte.set_audible_bell (not silent_bell) + + # Set our visual flashiness + self._vte.set_visible_bell (silent_bell) + + # Override our flashybelliness + if self.conf.force_no_bell: + self._vte.set_visible_bell (False) + self._vte.set_audible_bell (False) + + # Set our scrolliness + self._vte.set_scrollback_lines (self.conf.scrollback_lines) + self._vte.set_scroll_on_keystroke (self.conf.scroll_on_keystroke) + self._vte.set_scroll_on_output (self.conf.scroll_on_output) + + if self.scrollbar_position != self.conf.scrollbar_position: + self.scrollbar_position = self.conf.scrollbar_position + + if self.scrollbar_position == 'hidden' or self.scrollbar_position == 'disabled': + self._scrollbar.hide () + else: + self._scrollbar.show () + if self.scrollbar_position == 'right': + self._termbox.reorder_child (self._vte, 0) + elif self.scrollbar_position == 'left': + self._termbox.reorder_child (self._scrollbar, 0) + + # Set our sloppiness + self.focus = self.conf.focus + + self._vte.queue_draw () + + def on_composited_changed (self, widget): + self.reconfigure_vte () + + def on_vte_button_press (self, term, event): + # Left mouse button + Ctrl while over a link should open it + mask = gtk.gdk.CONTROL_MASK + if (event.state & mask) == mask: + if event.button == 1: + url = self._vte.match_check (int (event.x / self._vte.get_char_width ()), int (event.y / self._vte.get_char_height ())) + if url: + if (url[0][0:7] != "mailto:") & (url[1] == self.matches['email']): + address = "mailto:" + url[0] + else: + address = url[0] + openurl ( address ) + return False + + # Left mouse button should transfer focus to this vte widget + #LP#242612: + # we also need to give focus on the widget where the paste occured + if event.button in (1 ,2): + self._vte.grab_focus () + return False + + # Right mouse button should display a context menu if ctrl not pressed + if event.button == 3 and event.state & gtk.gdk.CONTROL_MASK == 0: + self.do_popup (event) + return True + + def on_vte_notify_enter (self, term, event): + if (self.focus == "sloppy" or self.focus == "mouse"): + term.grab_focus () + return False + + def do_scrollbar_toggle (self): + self.toggle_widget_visibility (self._scrollbar) + + def do_title_toggle (self): + self.toggle_widget_visibility (self._titlebox) + + def toggle_widget_visibility (self, widget): + if not isinstance (widget, gtk.Widget): + raise TypeError + + if widget.get_property ('visible'): + widget.hide () + else: + widget.show () + + + def paste_clipboard(self): + self._vte.paste_clipboard() + self._vte.grab_focus() + + + #keybindings for the individual splited terminals (affects only the + #the selected terminal) + def on_vte_key_press (self, term, event): + keyname = gtk.gdk.keyval_name (event.keyval) + + mask = gtk.gdk.CONTROL_MASK + if (event.state & mask) == mask: + if keyname == 'plus': + self.zoom (True) + return (True) + elif keyname == 'minus': + self.zoom (False) + return (True) + elif keyname == 'equal': + self.zoom_orig () + return (True) + + mask = gtk.gdk.CONTROL_MASK | gtk.gdk.SHIFT_MASK | gtk.gdk.MOD1_MASK + if (event.state & mask) == mask: + #Top level tab + if keyname == 'T': + self.terminator.newtab (self, True) + return (True) + # bindings that should be moved to Terminator as they all just call + # a function of Terminator. It would be cleaner is TerminatorTerm + # has absolutely no reference to Terminator. + # N (next) - P (previous) - O (horizontal) - E (vertical) - W (close) + + mask = gtk.gdk.CONTROL_MASK | gtk.gdk.SHIFT_MASK + if (event.state & mask) == mask: + if keyname == 'N': + self.terminator.go_next (self) + return (True) + elif keyname == "P": + self.terminator.go_prev (self) + return (True) + elif keyname == 'O': + self.terminator.splitaxis (self, False) + return (True) + elif keyname == 'E': + self.terminator.splitaxis (self, True) + return (True) + elif keyname == 'W': + self.terminator.closeterm (self) + return (True) + elif keyname == 'C': + self._vte.copy_clipboard () + return (True) + elif keyname == 'V': + self.paste_clipboard () + return (True) + elif keyname == 'S': + self.do_scrollbar_toggle () + return (True) + elif keyname == 'T': + self.terminator.newtab(self) + return (True) + elif keyname in ('Up', 'Down', 'Left', 'Right'): + self.terminator.resizeterm (self, keyname) + return (True) + elif keyname == 'Page_Down': + self.terminator.move_tab(self, 'right') + return (True) + elif keyname == 'Page_Up': + self.terminator.move_tab(self, 'left') + return (True) + elif keyname == 'Z': + self.terminator.toggle_zoom (self, True) + return (True) + elif keyname == 'X': + self.terminator.toggle_zoom (self) + return (True) + + mask = gtk.gdk.CONTROL_MASK + if (event.state & mask) == mask: + if keyname == 'Page_Down': + self.terminator.next_tab(self) + return (True) + elif keyname == 'Page_Up': + self.terminator.previous_tab(self) + return (True) + + if keyname and (keyname == 'Tab' or keyname.endswith('_Tab')): + mask = gtk.gdk.CONTROL_MASK | gtk.gdk.SHIFT_MASK + if (event.state & mask) == mask: + self.terminator.go_prev (self) + return (True) + mask = gtk.gdk.CONTROL_MASK + if (event.state & mask) == mask: + self.terminator.go_next (self) + return (True) + # Warning, mask value is either gtk.gdk.CONTROL_MASK or gtk.gdk.CONTROL_MASK | gtk.gdk.SHIFT_MASK + # if you intend to use it, reinit it + return (False) + + def zoom_orig (self): + self._vte.set_font (pango.FontDescription (self.conf.font)) + + def zoom (self, zoom_in): + pangodesc = self._vte.get_font () + fontsize = pangodesc.get_size () + + if fontsize > pango.SCALE and not zoom_in: + fontsize -= pango.SCALE + elif zoom_in: + fontsize += pango.SCALE + + pangodesc.set_size (fontsize) + self._vte.set_font (pangodesc) + + def on_vte_popup_menu (self, term, event): + self.do_popup (event) + + def do_popup (self, event = None): + menu = self.create_popup_menu (event) + menu.popup (None, None, None, event.button, event.time) + + def create_popup_menu (self, event): + menu = gtk.Menu () + url = None + + if event: + url = self._vte.match_check (int (event.x / self._vte.get_char_width ()), int (event.y / self._vte.get_char_height ())) + + if url: + if url[1] != self.matches['email']: + address = url[0] + nameopen = _("_Open Link") + namecopy = _("_Copy Link Address") + iconopen = gtk.image_new_from_stock(gtk.STOCK_JUMP_TO, gtk.ICON_SIZE_MENU) + + item = gtk.ImageMenuItem (nameopen) + item.set_property('image', iconopen) + else: + if url[0][0:7] != "mailto:": + address = "mailto:" + url[0] + else: + address = url[0] + nameopen = _("_Send Mail To...") + namecopy = _("_Copy Email Address") + + item = gtk.MenuItem (nameopen) + + item.connect ("activate", lambda menu_item: openurl (address)) + menu.append (item) + + item = gtk.MenuItem (namecopy) + item.connect ("activate", lambda menu_item: self.clipboard.set_text (url[0])) + menu.append (item) + + item = gtk.MenuItem () + menu.append (item) + + item = gtk.ImageMenuItem (gtk.STOCK_COPY) + item.connect ("activate", lambda menu_item: self._vte.copy_clipboard ()) + item.set_sensitive (self._vte.get_has_selection ()) + menu.append (item) + + item = gtk.ImageMenuItem (gtk.STOCK_PASTE) + item.connect ("activate", lambda menu_item: self.paste_clipboard ()) + menu.append (item) + + item = gtk.MenuItem () + menu.append (item) + + item = gtk.CheckMenuItem (_("Show _scrollbar")) + item.set_active (self._scrollbar.get_property ('visible')) + item.connect ("toggled", lambda menu_item: self.do_scrollbar_toggle ()) + menu.append (item) + + item = gtk.CheckMenuItem (_("Show _titlebar")) + item.set_active (self._titlebox.get_property ('visible')) + item.connect ("toggled", lambda menu_item: self.do_title_toggle ()) + menu.append (item) + + self._do_encoding_items (menu) + + item = gtk.MenuItem () + menu.append (item) + + if not self.terminator._zoomed: + str_horiz = _("Split H_orizontally") + str_vert = _("Split V_ertically") + + item = gtk.ImageMenuItem (str_horiz) + item_image = gtk.Image () + item_image.set_from_icon_name (APP_NAME + '_horiz', gtk.ICON_SIZE_MENU) + item.set_image (item_image) + + item.connect ("activate", lambda menu_item: self.terminator.splitaxis (self, False)) + menu.append (item) + + item = gtk.ImageMenuItem (str_vert) + item_image = gtk.Image () + item_image.set_from_icon_name (APP_NAME + '_vert', gtk.ICON_SIZE_MENU) + item.set_image (item_image) + + item.connect ("activate", lambda menu_item: self.terminator.splitaxis (self, True)) + menu.append (item) + + item = gtk.MenuItem (_("Open _Tab")) + item.connect ("activate", lambda menu_item: self.terminator.newtab (self)) + menu.append (item) + + if self.conf.extreme_tabs: + item = gtk.MenuItem (_("Open Top Level Tab")) + item.connect ("activate", lambda menu_item: self.terminator.newtab (self, True)) + menu.append (item) + + item = gtk.MenuItem () + menu.append (item) + + if len (self.terminator.term_list) > 1: + if not self.terminator._zoomed: + item = gtk.MenuItem (_("_Zoom terminal")) + item.connect ("activate", lambda menu_item: self.terminator.toggle_zoom (self, True)) + menu.append (item) + + item = gtk.MenuItem (_("_Maximise terminal")) + item.connect ("activate", lambda menu_item: self.terminator.toggle_zoom (self)) + menu.append (item) + else: + if self.terminator._zoomed and not self.terminator._maximised: + item = gtk.MenuItem (_("_Unzoom terminal")) + item.connect ("activate", lambda menu_item: self.terminator.toggle_zoom (self, True)) + menu.append (item) + + if self.terminator._zoomed and self.terminator._maximised: + item = gtk.MenuItem (_("U_nmaximise terminal")) + item.connect ("activate", lambda menu_item: self.terminator.toggle_zoom (self)) + menu.append (item) + + item = gtk.MenuItem () + menu.append (item) + + item = gtk.ImageMenuItem (gtk.STOCK_CLOSE) + item.connect ("activate", lambda menu_item: self.terminator.closeterm (self)) + menu.append (item) + + menu.show_all () + return menu + + def on_encoding_change (self, widget, encoding): + current = self._vte.get_encoding () + if current != encoding: + dbg ('Setting Encoding to: %s'%encoding) + self._vte.set_encoding (encoding) + + def _do_encoding_items (self, menu): + active_encodings = self.conf.active_encodings + item = gtk.MenuItem (_("Encodings")) + menu.append (item) + submenu = gtk.Menu () + item.set_submenu (submenu) + + current_encoding = self._vte.get_encoding () + group = None + for encoding in active_encodings: + radioitem = gtk.RadioMenuItem (group, _(encoding)) + if group is None: + group = radioitem + + if encoding == current_encoding: + radioitem.set_active (True) + + radioitem.connect ('activate', self.on_encoding_change, encoding) + submenu.append (radioitem) + + item = gtk.MenuItem (_("Other Encodings")) + submenu.append (item) + #second level + + submenu = gtk.Menu () + item.set_submenu (submenu) + encodings = TerminatorEncoding ().get_list () + encodings.sort (lambda x, y: cmp (x[2].lower (), y[2].lower ())) + group = None + + for encoding in encodings: + if encoding[1] in active_encodings: + continue + + if encoding[1] is None: + label = "%s %s"%(encoding[2], self._vte.get_encoding ()) + else: + label = "%s %s"%(encoding[2], encoding[1]) + + radioitem = gtk.RadioMenuItem (group, label) + if group is None: + group = radioitem + + if encoding[1] == current_encoding: + radioitem.set_active (True) + + radioitem.connect ('activate', self.on_encoding_change, encoding[1]) + submenu.append (radioitem) + + def on_vte_title_change(self, vte): + if self.conf.titletips: + vte.set_property ("has-tooltip", True) + vte.set_property ("tooltip-text", vte.get_window_title ()) + #set the title anyhow, titlebars setting only show/hide the label + self._title.set_text(vte.get_window_title ()) + self.terminator.set_window_title("%s: %s" %(APP_NAME.capitalize(), vte.get_window_title ())) + notebookpage = self.terminator.get_first_notebook_page(vte) + while notebookpage != None: + notebookpage[0].set_tab_label_text(notebookpage[1], vte.get_window_title ()) + notebookpage = self.terminator.get_first_notebook_page(notebookpage[0]) + + def on_vte_focus_in(self, vte, event): + self._titlebox.modify_bg(gtk.STATE_NORMAL,self.terminator.window.get_style().bg[gtk.STATE_SELECTED]) + self._title.modify_fg(gtk.STATE_NORMAL, self.terminator.window.get_style().fg[gtk.STATE_SELECTED]) + return + + def on_vte_focus_out(self, vte, event): + self._titlebox.modify_bg(gtk.STATE_NORMAL, self.terminator.window.get_style().bg[gtk.STATE_NORMAL]) + self._title.modify_fg(gtk.STATE_NORMAL, self.terminator.window.get_style().fg[gtk.STATE_NORMAL]) + return + + def on_vte_focus(self, vte): + if vte.get_window_title (): + self.terminator.set_window_title("%s: %s" %(APP_NAME.capitalize(), vte.get_window_title ())) + notebookpage = self.terminator.get_first_notebook_page(vte) + while notebookpage != None: + notebookpage[0].set_tab_label_text(notebookpage[1], vte.get_window_title ()) + notebookpage = self.terminator.get_first_notebook_page(notebookpage[0]) + + def destroy(self): + self._vte.destroy() + +class Terminator: + def __init__ (self, profile = None, command = None, fullscreen = False, maximise = False, borderless = False): + self.profile = profile + self.command = command + + self._zoomed = False + self._maximised = False + self._fullscreen = False + self._f11_modifier = False + self.term_list = [] + stores = [] + stores.append (config.TerminatorConfValuestoreRC ()) + + try: + import gconf + if self.profile: + self.profile = gconf.escape_key (self.profile, -1) + store = config.TerminatorConfValuestoreGConf (self.profile) + store.set_reconfigure_callback (self.reconfigure_vtes) + dbg ('Terminator__init__: comparing %s and %s'%(self.profile, store.profile.split ('/').pop ())) + if self.profile == store.profile.split ('/').pop (): + # If we have been given a profile, and we loaded it, we should be higher priority than RC + dbg ('Terminator__init__: placing GConf before RC') + stores.insert (0, store) + else: + stores.append (store) + except: + pass + + self.conf = config.TerminatorConfig (stores) + + self.icon_theme = gtk.IconTheme () + + if self.conf.f11_modifier: + self._f11_modifier = True + + if self.conf.handle_size in range (0,6): + gtk.rc_parse_string(""" + style "terminator-paned-style" { + GtkPaned::handle_size = %s + } + + class "GtkPaned" style "terminator-paned-style" + """ % self.conf.handle_size) + self.window = gtk.Window () + self.window.set_title (APP_NAME.capitalize()) + + try: + self.window.set_icon (self.icon_theme.load_icon (APP_NAME, 48, 0)) + except: + self.icon = self.window.render_icon (gtk.STOCK_DIALOG_INFO, gtk.ICON_SIZE_BUTTON) + self.window.set_icon (self.icon) + + self.window.connect ("key-press-event", self.on_key_press) + self.window.connect ("delete_event", self.on_delete_event) + self.window.connect ("destroy", self.on_destroy_event) + self.window.connect ("window-state-event", self.on_window_state_changed) + + self.window.set_property ('allow-shrink', True) + + if fullscreen or self.conf.fullscreen: + self.fullscreen_toggle () + + if maximise or self.conf.maximise: + self.maximize () + + if borderless or self.conf.borderless: + self.window.set_decorated (False) + + # Set RGBA colormap if possible so VTE can use real alpha + # channels for transparency. + screen = self.window.get_screen() + colormap = screen.get_rgba_colormap() + if colormap: + self.window.set_colormap(colormap) + + # Start out with just one terminal + # FIXME: This should be really be decided from some kind of profile + term = (TerminatorTerm (self, self.profile, self.command)) + self.term_list = [term] + + self.window.add (term) + term._titlebox.hide() + self.window.show () + term.spawn_child () + + def maximize (self): + """ Maximize the Terminator window.""" + self.window.maximize () + + def fullscreen_toggle (self): + """ Toggle the fullscreen state of the window. If it is in + fullscreen state, it will be unfullscreened. If it is not, it + will be set to fullscreen state. + """ + if self._fullscreen: + self.window.unfullscreen () + else: + self.window.fullscreen () + + def on_window_state_changed (self, window, event): + state = event.new_window_state & gtk.gdk.WINDOW_STATE_FULLSCREEN + self._fullscreen = bool (state) + + return (False) + + def on_delete_event (self, window, event, data=None): + if len (self.term_list) == 1: + return False + + # show dialog + dialog = gtk.Dialog (_("Close?"), window, gtk.DIALOG_MODAL, + (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, gtk.STOCK_CLOSE, gtk.RESPONSE_ACCEPT)) + dialog.set_has_separator (False) + dialog.set_resizable (False) + + primairy = gtk.Label (_('Close all terminals?')) + primairy.set_use_markup (True) + primairy.set_alignment (0, 0.5) + secundairy = gtk.Label (_("This window has %s terminals open. Closing the window will also close all terminals.") % len(self.term_list)) + secundairy.set_line_wrap(True) + primairy.set_alignment (0, 0.5) + + labels = gtk.VBox () + labels.pack_start (primairy, False, False, 6) + labels.pack_start (secundairy, False, False, 6) + + image = gtk.image_new_from_stock(gtk.STOCK_DIALOG_WARNING, gtk.ICON_SIZE_DIALOG) + image.set_alignment (0.5, 0) + + box = gtk.HBox() + box.pack_start (image, False, False, 6) + box.pack_start (labels, False, False, 6) + dialog.vbox.pack_start (box, False, False, 12) + + dialog.show_all () + result = dialog.run () + dialog.destroy () + return not (result == gtk.RESPONSE_ACCEPT) + + def on_destroy_event (self, widget, data=None): + gtk.main_quit () + + # keybindings for the whole terminal window (affects the main + # windows containing the splited terminals) + def on_key_press (self, window, event): + """ Callback for the window to determine what to do with special + keys. Currently handled key-combo's: + * F11: toggle fullscreen state of the window. + * CTRL - SHIFT - Q: close all terminals + """ + keyname = gtk.gdk.keyval_name (event.keyval) + mask = gtk.gdk.CONTROL_MASK | gtk.gdk.SHIFT_MASK + + if (keyname == 'F11' and (self._f11_modifier == False or event.state & mask)): + self.fullscreen_toggle () + return (True) + + if (event.state & mask) == mask: + if keyname == 'Q': + if not self.on_delete_event (window, gtk.gdk.Event (gtk.gdk.DELETE)): + self.on_destroy_event (window, gtk.gdk.Event (gtk.gdk.DESTROY)) + + def set_window_title(self, title): + """ + Modifies Terminator window title + """ + self.window.set_title(title) + + def add(self, widget, terminal, pos = "bottom"): + """ + Add a term to another at position pos + """ + vertical = pos in ("top", "bottom") + pane = (vertical) and gtk.VPaned () or gtk.HPaned () + + # get the parent of the provided terminal + parent = widget.get_parent () + dbg ('SEGBUG: add() Got parent') + if isinstance (parent, gtk.Window): + dbg ('SEGBUG: parent is a gtk.Window') + # We have just one term + parent.remove(widget) + dbg ('SEGBUG: removed widget from window') + if pos in ("top", "left"): + dbg ('SEGBUG: pos is in top/left') + pane.pack1 (terminal, True, True) + dbg ('SEGBUG: packed terminal') + pane.pack2 (widget, True, True) + dbg ('SEGBUG: packed widget') + else: + dbg ('SEGBUG: pos is not in top/left') + pane.pack1 (widget, True, True) + dbg ('SEGBUG: packed widget') + pane.pack2 (terminal, True, True) + dbg ('SEGBUG: packed terminal') + parent.add (pane) + dbg ('SEGBUG: added pane to parent') + + position = (vertical) and parent.allocation.height \ + or parent.allocation.width + + if (isinstance (parent, gtk.Notebook) or isinstance (parent, gtk.Window)) and widget.conf.titlebars: + #not the only term in the notebook/window anymore, need to reshow the title + dbg ('SEGBUG: Showing _titlebox') + widget._titlebox.show() + + if isinstance (parent, gtk.Notebook): + dbg ('SEGBUG: Parent is a notebook') + page = -1 + + for i in range(0, parent.get_n_pages()): + if parent.get_nth_page(i) == widget: + page = i + break + widget.reparent (pane) + if pos in ("top", "left"): + pane.remove(widget) + pane.pack1 (terminal, True, True) + pane.pack2 (widget, True, True) + else: + pane.pack1 (widget, True, True) + pane.pack2 (terminal, True, True) + #parent.remove_page(page) + pane.show() + parent.insert_page(pane, None, page) + parent.set_tab_label_text(pane, widget._vte.get_window_title()) + parent.set_tab_label_packing(pane, True, True, gtk.PACK_START) + parent.set_tab_reorderable(pane, True) + parent.set_current_page(page) + + position = (vertical) and parent.allocation.height \ + or parent.allocation.width + + if isinstance (parent, gtk.Paned): + dbg ('SEGBUG: parent is a Paned') + # We are inside a split term + position = (vertical) and widget.allocation.height \ + or widget.allocation.width + + dbg ('SEGBUG: Preparing to reparent sibling') + if (widget == parent.get_child1 ()): + widget.reparent (pane) + parent.pack1 (pane, True, True) + else: + widget.reparent (pane) + parent.pack2 (pane, True, True) + + if pos in ("top", "left"): + dbg ('SEGBUG: pos is in top/left. Removing and re-ordering') + pane.remove(widget) + pane.pack1 (terminal, True, True) + pane.pack2 (widget, True, True) + else: + dbg ('SEGBUG: pos is not in top/left. Packing') + pane.pack1 (widget, True, True) + pane.pack2 (terminal, True, True) + + dbg ('SEGBUG: packing widget and terminal') + pane.pack1 (widget, True, True) + pane.pack2 (terminal, True, True) + dbg ('SEGBUG: packed widget and terminal') + + # show all, set position of the divider + dbg ('SEGBUG: Showing pane') + pane.show () + dbg ('SEGBUG: Showed pane') + pane.set_position (position / 2) + dbg ('SEGBUG: Set position') + terminal.show () + + # insert the term reference into the list + index = self.term_list.index (widget) + if pos in ('bottom', 'right'): + index = index + 1 + self.term_list.insert (index, terminal) + # make the new terminal grab the focus + terminal._vte.grab_focus () + + return (terminal) + + def on_page_reordered(self, notebook, child, page_num): + #page has been reordered, we need to get the + # first term and last term + dbg ("Reordered: %d"%page_num) + nbpages = notebook.get_n_pages() + if nbpages == 1: + dbg("[ERROR] only one page in on_page_reordered") + + first = self._notebook_first_term(notebook.get_nth_page(page_num)) + last = self._notebook_last_term(notebook.get_nth_page(page_num)) + firstidx = self.term_list.index(first) + lastidx = self.term_list.index(last) + termslice = self.term_list[firstidx:lastidx+1] + #remove them from the list + for term in termslice: + self.term_list.remove(term) + + if page_num == 0: + #first page, we insert before the first term of next page + nexttab = notebook.get_nth_page(1) + sibling = self._notebook_first_term(nexttab) + siblingindex = self.term_list.index(sibling) + for term in termslice: + self.term_list.insert(siblingindex, term) + siblingindex += 1 + else: + #other pages, we insert after the last term of previous page + previoustab = notebook.get_nth_page(page_num - 1) + sibling = self._notebook_last_term(previoustab) + siblingindex = self.term_list.index(sibling) + for term in termslice: + siblingindex += 1 + self.term_list.insert(siblingindex, term) + + #for page reorder, we need to get the first term of a notebook + def notebook_first_term(self, notebook): + return self._notebook_first_term(notebook.get_nth_page(0)) + + def _notebook_first_term(self, child): + if isinstance(child, TerminatorTerm): + return child + elif isinstance(child, gtk.Paned): + return self._notebook_first_term(child.get_child1()) + elif isinstance(child, gtk.Notebook): + return self._notebook_first_term(child.get_nth_page(0)) + + dbg("[ERROR] unsupported class %s in _notebook_first_term" % child.__class__.__name__) + return None + + #for page reorder, we need to get the last term of a notebook + def notebook_last_term(self, notebook): + return self._notebook_last_term(notebook.get_nth_page(notebook.get_n_pages()-1)) + + def _notebook_last_term(self, child): + if isinstance(child, TerminatorTerm): + return child + elif isinstance(child, gtk.Paned): + return self._notebook_last_term(child.get_child2()) + elif isinstance(child, gtk.Notebook): + return self._notebook_last_term(child.get_nth_page(child.get_n_pages()-1)) + + dbg("[ERROR] unsupported class %s in _notebook_last_term" % child.__class__.__name__) + return None + + def newtab(self,widget, toplevel = False): + if self._zoomed: + # We don't want to add a new tab while we are zoomed in on a terminal + dbg ("newtab function called, but Terminator was in zoomed terminal mode.") + return + + terminal = TerminatorTerm (self, self.profile, None, widget.get_cwd()) + #only one term, we don't show the title + terminal._titlebox.hide() + if self.conf.extreme_tabs and not toplevel: + parent = widget.get_parent () + child = widget + else: + child = self.window.get_children()[0] + parent = child.get_parent() + + if isinstance(parent, gtk.Paned) or (isinstance(parent, gtk.Window) + and + ((self.conf.extreme_tabs and not toplevel) or not isinstance(child, gtk.Notebook))): + #no notebook yet. + notebook = gtk.Notebook() + notebook.set_tab_pos(gtk.POS_TOP) + notebook.connect('page-reordered',self.on_page_reordered) + notebook.set_property('homogeneous', True) + notebook.set_tab_reorderable(widget, True) + + if isinstance(parent, gtk.Paned): + if parent.get_child1() == child: + child.reparent(notebook) + parent.pack1(notebook) + else: + child.reparent(notebook) + parent.pack2(notebook) + elif isinstance(parent, gtk.Window): + child.reparent(notebook) + parent.add(notebook) + notebook.set_tab_reorderable(child,True) + notebooklabel = "" + if isinstance(child, TerminatorTerm): + child._titlebox.hide() + if widget._vte.get_window_title() is not None: + notebooklabel = widget._vte.get_window_title() + notebook.set_tab_label_text(child, notebooklabel) + notebook.set_tab_label_packing(child, True, True, gtk.PACK_START) + notebook.show() + elif isinstance(parent, gtk.Notebook): + notebook = parent + elif isinstance(parent, gtk.Window) and isinstance(child, gtk.Notebook): + notebook = child + else: + return (False) + + ## NOTE + ## Here we need to append to the notebook before we can + ## spawn the terminal (WINDOW_ID needs to be set) + + notebook.append_page(terminal,None) + terminal.show () + terminal.spawn_child () + ## Some gtk/vte weirdness + ## If we don't use this silly test, + ## terminal._vte.get_window_title() might return + ## bogus values + notebooklabel = "" + if terminal._vte.get_window_title() is not None: + notebooklabel = terminal._vte.get_window_title() + notebook.set_tab_label_text(terminal, notebooklabel) + notebook.set_tab_label_packing(terminal, True, True, gtk.PACK_START) + notebook.set_tab_reorderable(terminal,True) + ## Now, we set focus on the new term + notebook.set_current_page(-1) + terminal._vte.grab_focus () + + #adding a new tab, thus we need to get the + # last term of the previous tab and add + # the new term just after + sibling = self._notebook_last_term(notebook.get_nth_page(notebook.page_num(terminal)-1)) + index = self.term_list.index(sibling) + self.term_list.insert (index + 1, terminal) + return (True) + + + return terminal + + def splitaxis (self, widget, vertical=True): + """ Split the provided widget on the horizontal or vertical axis. """ + if self._zoomed: + # We don't want to split the terminal while we are in zoomed mode + dbg ("splitaxis function called, but Terminator was in zoomed mode.") + return + + # create a new terminal and parent pane. + dbg ('SEGBUG: Creating TerminatorTerm') + terminal = TerminatorTerm (self, self.profile, None, widget.get_cwd()) + dbg ('SEGBUG: Created TerminatorTerm') + pos = vertical and "right" or "bottom" + dbg ('SEGBUG: Position is: %s'%pos) + self.add(widget, terminal, pos) + dbg ('SEGBUG: added TerminatorTerm to container') + terminal.show () + dbg ('SEGBUG: showed TerminatorTerm') + terminal.spawn_child () + dbg ('SEGBUG: spawned child') + return terminal + + def remove(self, widget): + """Remove a TerminatorTerm from the Terminator view and terms list + Returns True on success, False on failure""" + parent = widget.get_parent () + sibling = None + focus_on_close = 'prev' + if isinstance (parent, gtk.Window): + # We are the only term + if not self.on_delete_event (parent, gtk.gdk.Event (gtk.gdk.DELETE)): + self.on_destroy_event (parent, gtk.gdk.Event (gtk.gdk.DESTROY)) + return + + if isinstance (parent, gtk.Paned): + index = self.term_list.index (widget) + grandparent = parent.get_parent () + + # Discover sibling while all objects exist + if widget == parent.get_child1 (): + sibling = parent.get_child2 () + focus_on_close = 'next' + if widget == parent.get_child2 (): + sibling = parent.get_child1 () + + if not sibling: + # something is wrong, give up + err ("Error: %s is not a child of %s"%(widget, parent)) + return False + + parent.remove(widget) + if isinstance(grandparent, gtk.Notebook): + page = -1 + for i in range(0, grandparent.get_n_pages()): + if grandparent.get_nth_page(i) == parent: + page = i + break + parent.remove(sibling) + grandparent.remove_page(page) + grandparent.insert_page(sibling, None,page) + grandparent.set_tab_label_packing(sibling, True, True, gtk.PACK_START) + grandparent.set_tab_reorderable(sibling, True) + grandparent.set_current_page(page) + + else: + grandparent.remove (parent) + sibling.reparent (grandparent) + if not self._zoomed: + grandparent.resize_children() + parent.destroy () + if isinstance(sibling, TerminatorTerm) and isinstance(sibling.get_parent(), gtk.Notebook): + sibling._titlebox.hide() + + self.term_list.remove (widget) + + elif isinstance (parent, gtk.Notebook): + parent.remove(widget) + nbpages = parent.get_n_pages() + index = self.term_list.index (widget) + self.term_list.remove (widget) + if nbpages == 1: + sibling = parent.get_nth_page(0) + parent.remove(sibling) + gdparent = parent.get_parent() + if isinstance(gdparent, gtk.Window): + gdparent.remove(parent) + gdparent.add(sibling) + elif isinstance(gdparent, gtk.Paned): + if gdparent.get_child1() == parent: + gdparent.remove(parent) + gdparent.pack1(sibling) + else: + gdparent.remove(parent) + gdparent.pack2(sibling) + if isinstance(sibling, TerminatorTerm) and sibling.conf.titlebars and sibling.conf.extreme_tabs: + sibling._titlebox.show() + parent.destroy() + if self.conf.focus_on_close == 'prev' or ( self.conf.focus_on_close == 'auto' and focus_on_close == 'prev'): + if index == 0: index = 1 + self.term_list[index - 1]._vte.grab_focus () + self._set_current_notebook_page_recursive(self.term_list[index - 1]) + elif self.conf.focus_on_close == 'next' or ( self.conf.focus_on_close == 'auto' and focus_on_close == 'next'): + if index == len(self.term_list): index = index - 1 + self.term_list[index]._vte.grab_focus () + self._set_current_notebook_page_recursive(self.term_list[index]) + + if len(self.term_list) == 1: + self.term_list[0]._titlebox.hide() + + return True + + def closeterm (self, widget): + if self._zoomed: + # We are zoomed, pop back out to normal layout before closing + dbg ("closeterm function called while in zoomed mode. Restoring previous layout before closing.") + self.toggle_zoom(widget, not self._maximised) + + if self.remove(widget): + widget.destroy () + return True + return False + + + def go_next (self, term): + current = self.term_list.index (term) + next = None + if self.conf.cycle_term_tab: + notebookpage = self.get_first_notebook_page(term) + if notebookpage: + last = self._notebook_last_term(notebookpage[1]) + first = self._notebook_first_term(notebookpage[1]) + if term == last: + next = self.term_list.index(first) + + if next is None: + if current == len (self.term_list) - 1: + next = 0 + else: + next = current + 1 + + + nextterm = self.term_list[next] + ##we need to set the current page of each notebook + self._set_current_notebook_page_recursive(nextterm) + + nextterm._vte.grab_focus () + + + def go_prev (self, term): + current = self.term_list.index (term) + previous = None + + if self.conf.cycle_term_tab: + notebookpage = self.get_first_notebook_page(term) + if notebookpage: + last = self._notebook_last_term(notebookpage[1]) + first = self._notebook_first_term(notebookpage[1]) + if term == first: + previous = self.term_list.index(last) + + if previous is None: + if current == 0: + previous = len (self.term_list) - 1 + else: + previous = current - 1 + + #self.window.set_title(self.term_list[previous]._vte.get_window_title()) + previousterm = self.term_list[previous] + ##we need to set the current page of each notebook + self._set_current_notebook_page_recursive(previousterm) + previousterm._vte.grab_focus () + + + def _set_current_notebook_page_recursive(self, widget): + page = self.get_first_notebook_page(widget) + while page: + child = None + page_num = page[0].page_num(page[1]) + page[0].set_current_page(page_num) + page = self.get_first_notebook_page(page[0]) + + + def resizeterm (self, widget, keyname): + vertical = False + if keyname in ('Up', 'Down'): + vertical = True + elif keyname in ('Left', 'Right'): + vertical = False + else: + return + parent = self.get_first_parent_paned(widget,vertical) + if parent == None: + return + + #We have a corresponding parent pane + # + #allocation = parent.get_allocation() + + if keyname in ('Up', 'Down'): + maxi = parent.get_child1().get_allocation().height + parent.get_child2().get_allocation().height - 1 + + else: + maxi = parent.get_child1().get_allocation().width + parent.get_child2().get_allocation().width - 1 + move = 10 + if keyname in ('Up', 'Left'): + move = -10 + + move = max(2, parent.get_position() + move) + move = min(maxi, move) + + parent.set_position(move) + + def previous_tab(self, term): + notebook = self.get_first_parent_notebook(term) + notebook.prev_page() + return + + def next_tab(self, term): + notebook = self.get_first_parent_notebook(term) + notebook.next_page() + return + + def move_tab(self, term, direction): + dbg("moving to direction %s" % direction) + (notebook, page) = self.get_first_notebook_page(term) + page_num = notebook.page_num(page) + nbpages = notebook.get_n_pages() + #dbg ("%s %s %s %s" % (page_num, nbpages,notebook, page)) + if page_num == 0 and direction == 'left': + new_page_num = nbpages + elif page_num == nbpages - 1 and direction == 'right': + new_page_num = 0 + elif direction == 'left': + new_page_num = page_num - 1 + elif direction == 'right': + new_page_num = page_num + 1 + else: + dbg("[ERROR] unhandled combination in move_tab: direction = %s page_num = %d" % (direction, page_num)) + return False + notebook.reorder_child(page, new_page_num) + return True + + def get_first_parent_notebook(self, widget): + if isinstance (widget, gtk.Window): + return None + parent = widget.get_parent() + if isinstance (parent, gtk.Notebook): + return parent + return self.get_first_parent_notebook(parent) + + def get_first_parent_paned (self, widget, vertical = None): + """This method returns the first parent pane of a widget. + if vertical is True returns the first VPaned + if vertical is False return the first Hpaned + if is None return the First Paned""" + if isinstance (widget, gtk.Window): + return None + parent = widget.get_parent() + if isinstance (parent, gtk.Paned) and vertical is None: + return parent + if isinstance (parent, gtk.VPaned) and vertical: + return parent + elif isinstance (parent, gtk.HPaned) and not vertical: + return parent + return self.get_first_parent_paned(parent, vertical) + + def get_first_notebook_page(self, widget): + if isinstance (widget, gtk.Window): + return None + parent = widget.get_parent() + if isinstance (parent, gtk.Notebook): + page = -1 + for i in range(0, parent.get_n_pages()): + if parent.get_nth_page(i) == widget: + return (parent, widget) + return self.get_first_notebook_page(parent) + + def reconfigure_vtes (self): + for term in self.term_list: + term.reconfigure_vte () + + def toggle_zoom(self, widget, fontscale = False): + if not self._zoomed: + self.zoom_term (widget, fontscale) + if self.conf.titlebars: + widget._titlebox.hide() + else: + self.unzoom_term (widget, fontscale) + if self.conf.titlebars and \ + len(self.term_list) > 1 \ + and \ + (isinstance(widget, TerminatorTerm) and isinstance(widget.get_parent(),gtk.Paned))\ + : + widget._titlebox.show() + widget._vte.grab_focus() + + def zoom_term (self, widget, fontscale = False): + """Maximize to full window an instance of TerminatorTerm.""" + self.old_font = widget._vte.get_font () + self.old_columns = widget._vte.get_column_count () + self.old_rows = widget._vte.get_row_count () + self.old_parent = widget.get_parent() + + if isinstance(self.old_parent, gtk.Window): + return + if isinstance(self.old_parent, gtk.Notebook): + self.old_page = self.old_parent.get_current_page() + + self.window_child = self.window.get_children()[0] + self.window.remove(self.window_child) + self.old_parent.remove(widget) + self.window.add(widget) + self._zoomed = True + + if fontscale: + self.cnid = widget.connect ("size-allocate", self.zoom_scale_font) + else: + self._maximised = True + + widget._vte.grab_focus () + + def zoom_scale_font (self, widget, allocation): + new_columns = widget._vte.get_column_count () + new_rows = widget._vte.get_row_count () + new_font = widget._vte.get_font () + + dbg ('zoom_term: I just went from %dx%d to %dx%d. Raa!'%(self.old_columns, self.old_rows, new_columns, new_rows)) + + old_area = self.old_columns * self.old_rows + new_area = new_columns * new_rows + area_factor = new_area / old_area + + dbg ('zoom_term: My area changed from %d characters to %d characters, a factor of %f.'%(old_area, new_area, area_factor)) + + new_font.set_size (self.old_font.get_size() * (area_factor / 2)) + dbg ('zoom_term: Scaled font from %f to %f'%(self.old_font.get_size () / pango.SCALE, new_font.get_size () / pango.SCALE)) + widget._vte.set_font (new_font) + widget.disconnect (self.cnid) + + def unzoom_term (self, widget, fontscale = False): + """Proof of concept: Go back to previous application + widget structure. + """ + if self._zoomed: + if fontscale: + widget._vte.set_font (self.old_font) + self._zoomed = False + self._maximised = False + + self.window.remove(widget) + self.window.add(self.window_child) + self.old_parent.add(widget) + if isinstance(self.old_parent, gtk.Notebook): + self.old_parent.set_current_page(self.old_page) + + widget._vte.grab_focus () +======= #import Terminator from terminatorlib.terminator import Terminator +>>>>>>> MERGE-SOURCE if __name__ == '__main__': def execute_cb (option, opt, value, parser): diff --git a/terminator.spec b/terminator.spec index a8c53bc1..b184f781 100644 --- a/terminator.spec +++ b/terminator.spec @@ -1,6 +1,6 @@ Summary: Terminator, The robot future of terminals Name: terminator -Version: 0.8.1 +Version: 0.9 Release: 1.fc9.rb License: GPLv2+ Group: Terminals @@ -41,5 +41,8 @@ grep -v man/man1 FILELIST.tmp > FILELIST %{_mandir}/man1/terminator.* %changelog +* Fri Jul 04 2008 - chantra AatT rpm-based DdOoTt org 0.9.fc9.rb +- New upstream release + * Sat May 17 2008 - chantra AatT rpm-based DdOoTt org 0.8.1.fc9.rb - Initial release for Fedora 9.