#!/usr/bin/python
# Terminator by Chris Jones <cmsj@tenshu.net>
# GPL v2 only
"""terminal.py - classes necessary to provide Terminal widgets"""

from __future__ import division
import sys
import os
import signal
import pygtk
pygtk.require('2.0')
import gtk
import gobject
import pango
import subprocess
import urllib
import uuid

from util import dbg, err, gerr
import util
from config import Config
from cwd import get_default_cwd
from factory import Factory
from terminator import Terminator
from titlebar import Titlebar
from terminal_popup_menu import TerminalPopupMenu
from searchbar import Searchbar
from translation import _
from signalman import Signalman
import plugin

try:
    import vte
except ImportError:
    gerr('You need to install python bindings for libvte')
    sys.exit(1)

# pylint: disable-msg=R0904
class Terminal(gtk.VBox):
    """Class implementing the VTE widget and its wrappings"""

    __gsignals__ = {
        'close-term': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()),
        'title-change': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
            (gobject.TYPE_STRING,)),
        'enumerate': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
            (gobject.TYPE_INT,)),
        'group-tab': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()),
        'ungroup-tab': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()),
        'ungroup-all': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()),
        'split-horiz': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
            (gobject.TYPE_STRING,)),
        'split-vert': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
            (gobject.TYPE_STRING,)),
        'tab-new': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
            (gobject.TYPE_BOOLEAN, gobject.TYPE_OBJECT)),
        'tab-top-new': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()),
        'focus-in': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()),
        'zoom': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()),
        'maximise': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()),
        'unzoom': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()),
        'resize-term': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
            (gobject.TYPE_STRING,)),
        'navigate': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
            (gobject.TYPE_STRING,)),
        'tab-change': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
            (gobject.TYPE_INT,)),
        'group-all': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()),
        'move-tab': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, 
            (gobject.TYPE_STRING,)),
    }

    TARGET_TYPE_VTE = 8

    terminator = None
    vte = None
    terminalbox = None
    scrollbar = None
    scrollbar_position = None
    titlebar = None
    searchbar = None

    group = None
    cwd = None
    origcwd = None
    command = None
    clipboard = None
    pid = None
    uuid = None

    matches = None
    config = None
    default_encoding = None
    custom_encoding = None
    custom_font_size = None
    layout_command = None

    composite_support = None

    cnxids = None

    def __init__(self):
        """Class initialiser"""
        gtk.VBox.__init__(self)
        self.__gobject_init__()

        self.terminator = Terminator()
        self.terminator.register_terminal(self)

        # FIXME: Surely these should happen in Terminator::register_terminal()?
        self.connect('enumerate', self.terminator.do_enumerate)
        self.connect('focus-in', self.terminator.focus_changed)

        self.matches = {}
        self.cnxids = Signalman()

        self.config = Config()

        self.cwd = get_default_cwd()
        self.origcwd = self.terminator.origcwd
        self.clipboard = gtk.clipboard_get(gtk.gdk.SELECTION_CLIPBOARD)

        self.pending_on_vte_size_allocate = False

        self.uuid = uuid.uuid4()
        dbg('assigning Terminal a TERMINATOR_UUID of: %s' % self.uuid.urn)

        self.vte = vte.Terminal()
        self.vte.set_size(80, 24)
        self.vte._expose_data = None
        if not hasattr(self.vte, "set_opacity") or \
           not hasattr(self.vte, "is_composited"):
            self.composite_support = False
        else:
            self.composite_support = True
        dbg('composite_support: %s' % self.composite_support)

        self.vte.show()

        self.default_encoding = self.vte.get_encoding()
        self.update_url_matches(self.config['try_posix_regexp'])

        self.terminalbox = self.create_terminalbox()

        self.titlebar = Titlebar(self)
        self.titlebar.connect_icon(self.on_group_button_press)
        self.titlebar.connect('edit-done', self.on_edit_done)
        self.connect('title-change', self.titlebar.set_terminal_title)
        self.titlebar.connect('create-group', self.really_create_group)
        self.titlebar.show_all()

        self.searchbar = Searchbar()
        self.searchbar.connect('end-search', self.on_search_done)

        self.show()
        self.pack_start(self.titlebar, False)
        self.pack_start(self.terminalbox)
        self.pack_end(self.searchbar)

        self.connect_signals()

        os.putenv('TERM', 'xterm')
        os.putenv('COLORTERM', 'gnome-terminal')

        env_proxy = os.getenv('http_proxy')
        if not env_proxy:
            if self.config['http_proxy'] and self.config['http_proxy'] != '':
                os.putenv('http_proxy', self.config['http_proxy'])

    def get_vte(self):
        """This simply returns the vte widget we are using"""
        return(self.vte)

    def force_set_profile(self, widget, profile):
        """Forcibly set our profile"""
        self.set_profile(widget, profile, True)

    def set_profile(self, _widget, profile, force=False):
        """Set our profile"""
        if profile != self.config.get_profile():
            self.config.set_profile(profile, force)
            self.reconfigure()

    def get_profile(self):
        """Return our profile name"""
        return(self.config.profile)

    def get_cwd(self):
        """Return our cwd"""
        return(self.terminator.pid_cwd(self.pid))

    def close(self):
        """Close ourselves"""
        dbg('Terminal::close: emitting close-term')
        self.emit('close-term')
        try:
            os.kill(self.pid, signal.SIGHUP)
        except:
            # We really don't want to care if this failed. Deep OS voodoo is
            # not what we should be doing.
            pass

    def create_terminalbox(self):
        """Create a GtkHBox containing the terminal and a scrollbar"""

        terminalbox = gtk.HBox()
        self.scrollbar = gtk.VScrollbar(self.vte.get_adjustment())
        self.scrollbar.set_no_show_all(True)
        self.scrollbar_position = self.config['scrollbar_position']

        if self.scrollbar_position not in ('hidden', 'disabled'):
            self.scrollbar.show()

        if self.scrollbar_position == 'left':
            func = terminalbox.pack_end
        else:
            func = terminalbox.pack_start

        func(self.vte)
        func(self.scrollbar, False)
        terminalbox.show_all()

        return(terminalbox)

    def update_url_matches(self, posix = True):
        """Update the regexps used to match URLs"""
        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,\\\"]"

        if posix:
            dbg ('Terminal::update_url_matches: Trying POSIX URL regexps')
            lboundry = "[[:<:]]"
            rboundry = "[[:>:]]"
        else: # GNU
            dbg ('Terminal::update_url_matches: Trying GNU URL regexps')
            lboundry = "\\<"
            rboundry = "\\>"

        self.matches['full_uri'] = self.vte.match_add(lboundry + schemes + 
                "//(" + user + "@)?[" + hostchars  +".]+(:[0-9]+)?(" + 
                urlpath + ")?" + rboundry + "/?")

        if self.matches['full_uri'] == -1:
            if posix:
                err ('Terminal::update_url_matches: POSIX failed, trying GNU')
                self.update_url_matches(posix = False)
            else:
                err ('Terminal::update_url_matches: Failed adding URL matches')
        else:
            self.matches['voip'] = self.vte.match_add(lboundry + 
                    '(callto:|h323:|sip:)' + "[" + userchars + "+][" + 
                    userchars + ".]*(:[0-9]+)?@?[" + pathchars + "]+" + 
                    rboundry)
            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)

            # Now add any matches from plugins
            try:
                registry = plugin.PluginRegistry()
                registry.load_plugins()
                plugins = registry.get_plugins_by_capability('url_handler')

                for urlplugin in plugins:
                    name = urlplugin.handler_name
                    match = urlplugin.match
                    if name in self.matches:
                        dbg('Terminal::update_matches: refusing to add \
duplicate match %s' % name)
                        continue
                    self.matches[name] = self.vte.match_add(match)
                    dbg('Terminal::update_matches: added plugin URL handler \
for %s (%s)' % (name, urlplugin.__class__.__name__))
            except Exception, ex:
                err('Terminal::update_url_matches: %s' % ex)

    def match_add(self, name, match):
        """Register a URL match"""
        if name in self.matches:
            err('Terminal::match_add: Refusing to create duplicate match %s' % name)
            return
        self.matches[name] = self.vte.match_add(match)

    def match_remove(self, name):
        """Remove a previously registered URL match"""
        if name not in self.matches:
            err('Terminal::match_remove: Unable to remove non-existent match %s' % name)
            return
        self.vte.match_remove(self.matches[name])
        del(self.matches[name])

    def connect_signals(self):
        """Connect all the gtk signals and drag-n-drop mechanics"""

        self.vte.connect('key-press-event', self.on_keypress)
        self.vte.connect('button-press-event', self.on_buttonpress)
        self.vte.connect('popup-menu', self.popup_menu)

        srcvtetargets = [("vte", gtk.TARGET_SAME_APP, self.TARGET_TYPE_VTE)]
        dsttargets = [("vte", gtk.TARGET_SAME_APP, self.TARGET_TYPE_VTE), 
                      ('text/x-moz-url', 0, 0), 
                      ('_NETSCAPE_URL', 0, 0)]
        dsttargets = gtk.target_list_add_text_targets(dsttargets)
        dsttargets = gtk.target_list_add_uri_targets(dsttargets)
        dbg('Finalised drag targets: %s' % dsttargets)

        for (widget, mask) in [
            (self.vte, gtk.gdk.CONTROL_MASK | gtk.gdk.BUTTON3_MASK), 
            (self.titlebar, gtk.gdk.BUTTON1_MASK)]:
            widget.drag_source_set(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_COPY | gtk.gdk.ACTION_MOVE)

        for widget in [self.vte, self.titlebar]:
            widget.connect('drag-begin', self.on_drag_begin, self)
            widget.connect('drag-data-get', self.on_drag_data_get,
            self)

        self.vte.connect('drag-motion', self.on_drag_motion, self)
        self.vte.connect('drag-data-received',
            self.on_drag_data_received, self)

        # FIXME: Shouldn't this be in configure()?
        if self.config['copy_on_selection']:
            self.cnxids.new(self.vte, 'selection-changed', 
                    lambda widget: self.vte.copy_clipboard())

        if self.composite_support:
            self.vte.connect('composited-changed', self.reconfigure)

        self.vte.connect('window-title-changed', lambda x:
            self.emit('title-change', self.get_window_title()))
        self.vte.connect('grab-focus', self.on_vte_focus)
        self.vte.connect('focus-in-event', self.on_vte_focus_in)
        self.vte.connect('size-allocate', self.deferred_on_vte_size_allocate)

        self.vte.add_events(gtk.gdk.ENTER_NOTIFY_MASK)
        self.vte.connect('enter_notify_event',
            self.on_vte_notify_enter)

        self.cnxids.new(self.vte, 'realize', self.reconfigure)

    def create_popup_group_menu(self, widget, event = None):
        """Pop up a menu for the group widget"""
        if event:
            button = event.button
            time = event.time
        else:
            button = 0
            time = 0

        menu = self.populate_group_menu()
        menu.show_all()
        menu.popup(None, None, self.position_popup_group_menu, button, time,
                widget)
        return(True)

    def populate_group_menu(self):
        """Fill out a group menu"""
        menu = gtk.Menu()
        groupitem = None

        item = gtk.MenuItem(_('New group...'))
        item.connect('activate', self.create_group)
        menu.append(item)

        if len(self.terminator.groups) > 0:
            groupitem = gtk.RadioMenuItem(groupitem, _('None'))
            groupitem.set_active(self.group == None)
            groupitem.connect('activate', self.set_group, None)
            menu.append(groupitem)

            for group in self.terminator.groups:
                item = gtk.RadioMenuItem(groupitem, group, False)
                item.set_active(self.group == group)
                item.connect('toggled', self.set_group, group)
                menu.append(item)
                groupitem = item

        if self.group != None or len(self.terminator.groups) > 0:
            menu.append(gtk.MenuItem())

        if self.group != None:
            item = gtk.MenuItem(_('Remove group %s') % self.group)
            item.connect('activate', self.ungroup, self.group)
            menu.append(item)

        if util.has_ancestor(self, gtk.Notebook):
            item = gtk.MenuItem(_('G_roup all in tab'))
            item.connect('activate', lambda x: self.emit('group_tab'))
            menu.append(item)

            if len(self.terminator.groups) > 0:
                item = gtk.MenuItem(_('Ungr_oup all in tab'))
                item.connect('activate', lambda x: self.emit('ungroup_tab'))
                menu.append(item)

        if len(self.terminator.groups) > 0:
            item = gtk.MenuItem(_('Remove all groups'))
            item.connect('activate', lambda x: self.emit('ungroup-all'))
            menu.append(item)

        if self.group != None:
            menu.append(gtk.MenuItem())

            item = gtk.MenuItem(_('Close group %s') % self.group)
            item.connect('activate', lambda x:
                         self.terminator.closegroupedterms(self.group))
            menu.append(item)

        menu.append(gtk.MenuItem())

        groupitem = None

        for key, value in {_('Broadcast all'):'all', 
                          _('Broadcast group'):'group',
                          _('Broadcast off'):'off'}.items():
            groupitem = gtk.RadioMenuItem(groupitem, key)
            dbg('Terminal::populate_group_menu: %s active: %s' %
                    (key, self.terminator.groupsend ==
                        self.terminator.groupsend_type[value]))
            groupitem.set_active(self.terminator.groupsend ==
                    self.terminator.groupsend_type[value])
            groupitem.connect('activate', self.set_groupsend,
                    self.terminator.groupsend_type[value])
            menu.append(groupitem)

        menu.append(gtk.MenuItem())

        item = gtk.CheckMenuItem(_('Split to this group'))
        item.set_active(self.config['split_to_group'])
        item.connect('toggled', lambda x: self.do_splittogroup_toggle())
        menu.append(item)

        item = gtk.CheckMenuItem(_('Autoclean groups'))
        item.set_active(self.config['autoclean_groups'])
        item.connect('toggled', lambda x: self.do_autocleangroups_toggle())
        menu.append(item)

        menu.append(gtk.MenuItem())

        item = gtk.MenuItem(_('Insert terminal number'))
        item.connect('activate', lambda x: self.emit('enumerate', False))
        menu.append(item)

        item = gtk.MenuItem(_('Insert padded terminal number'))
        item.connect('activate', lambda x: self.emit('enumerate', True))
        menu.append(item)

        return(menu)

    def position_popup_group_menu(self, menu, widget):
        """Calculate the position of the group popup menu"""
        _screen_w = gtk.gdk.screen_width()
        screen_h = gtk.gdk.screen_height()

        if gtk.gtk_version >= (2, 14):
            widget_win = widget.get_window()
        else:
            widget_win = widget.window
        widget_x, widget_y = widget_win.get_origin()
        _widget_w, widget_h = widget_win.get_size()

        _menu_w, menu_h = menu.size_request()

        if widget_y + widget_h + menu_h > screen_h:
            menu_y = max(widget_y - menu_h, 0)
        else:
            menu_y = widget_y + widget_h

        return(widget_x, menu_y, 1)

    def set_group(self, _item, name):
        """Set a particular group"""
        if self.group == name:
            # already in this group, no action needed
            return
        dbg('Terminal::set_group: Setting group to %s' % name)
        self.group = name
        self.titlebar.set_group_label(name)
        self.terminator.group_hoover()

    def create_group(self, _item):
        """Trigger the creation of a group via the titlebar (because popup 
        windows are really lame)"""
        self.titlebar.create_group()

    def really_create_group(self, _widget, groupname):
        """The titlebar has spoken, let a group be created"""
        self.terminator.create_group(groupname)
        self.set_group(None, groupname)

    def ungroup(self, _widget, data):
        """Remove a group"""
        # FIXME: Could we emit and have Terminator do this?
        for term in self.terminator.terminals:
            if term.group == data:
                term.set_group(None, None)
        self.terminator.group_hoover()

    def set_groupsend(self, _widget, value):
        """Set the groupsend mode"""
        # FIXME: Can we think of a smarter way of doing this than poking?
        if value in self.terminator.groupsend_type.values():
            dbg('Terminal::set_groupsend: setting groupsend to %s' % value)
            self.terminator.groupsend = value

    def do_splittogroup_toggle(self):
        """Toggle the splittogroup mode"""
        self.config['split_to_group'] = not self.config['split_to_group']

    def do_autocleangroups_toggle(self):
        """Toggle the autocleangroups mode"""
        self.config['autoclean_groups'] = not self.config['autoclean_groups']

    def reconfigure(self, _widget=None):
        """Reconfigure our settings"""
        dbg('Terminal::reconfigure')
        self.cnxids.remove_signal(self.vte, 'realize')

        # Handle child command exiting
        self.cnxids.remove_signal(self.vte, 'child-exited')

        if self.config['exit_action'] == 'restart':
            self.cnxids.new(self.vte, 'child-exited', self.spawn_child, True)
        elif self.config['exit_action'] in ('close', 'left'):
            self.cnxids.new(self.vte, 'child-exited', 
                                            lambda x: self.emit('close-term'))

        self.vte.set_emulation(self.config['emulation'])
        if self.custom_encoding != True:
            self.vte.set_encoding(self.config['encoding'])
        self.vte.set_word_chars(self.config['word_chars'])
        self.vte.set_mouse_autohide(self.config['mouse_autohide'])

        backspace = self.config['backspace_binding']
        delete = self.config['delete_binding']

        try:
            if backspace == 'ascii-del':
                backbind = vte.ERASE_ASCII_DELETE
            elif backspace == 'control-h':
                backbind = vte.ERASE_ASCII_BACKSPACE
            elif backspace == 'escape-sequence':
                backbind = vte.ERASE_DELETE_SEQUENCE
            else:
                backbind = vte.ERASE_AUTO
        except AttributeError:
            if backspace == 'ascii-del':
                backbind = 2
            elif backspace == 'control-h':
                backbind = 1
            elif backspace == 'escape-sequence':
                backbind = 3
            else:
                backbind = 0

        try:
            if delete == 'ascii-del':
                delbind = vte.ERASE_ASCII_DELETE
            elif delete == 'control-h':
                delbind = vte.ERASE_ASCII_BACKSPACE
            elif delete == 'escape-sequence':
                delbind = vte.ERASE_DELETE_SEQUENCE
            else:
                delbind = vte.ERASE_AUTO
        except AttributeError:
            if delete == 'ascii-del':
                delbind = 2
            elif delete == 'control-h':
                delbind = 1
            elif delete == 'escape-sequence':
                delbind = 3
            else:
                delbind = 0

        self.vte.set_backspace_binding(backbind)
        self.vte.set_delete_binding(delbind)

        if not self.custom_font_size:
            try:
                if self.config['use_system_font'] == True:
                    font = self.config.get_system_font()
                else:
                    font = self.config['font']
                self.set_font(pango.FontDescription(font))
            except:
                pass
        self.vte.set_allow_bold(self.config['allow_bold'])
        if self.config['use_theme_colors']:
            fgcolor = self.vte.get_style().text[gtk.STATE_NORMAL]
            bgcolor = self.vte.get_style().base[gtk.STATE_NORMAL]
        else:
            fgcolor = gtk.gdk.color_parse(self.config['foreground_color'])
            bgcolor = gtk.gdk.color_parse(self.config['background_color'])

        colors = self.config['palette'].split(':')
        palette = []
        for color in colors:
            if color:
                palette.append(gtk.gdk.color_parse(color))
        self.vte.set_colors(fgcolor, bgcolor, palette)
        if self.config['cursor_color'] == self.config['foreground_color']:
            try:
                self.vte.set_color_cursor(None) 
            except TypeError:
                # FIXME: I think this is only necessary because of
                # https://bugzilla.gnome.org/show_bug.cgi?id=614910
                pass
        elif self.config['cursor_color'] != '':
            self.vte.set_color_cursor(gtk.gdk.color_parse(
                                        self.config['cursor_color']))
        if hasattr(self.vte, 'set_cursor_shape'):
            self.vte.set_cursor_shape(getattr(vte, 'CURSOR_SHAPE_' +
                self.config['cursor_shape'].upper()))

        background_type = self.config['background_type']
        dbg('background_type=%s' % background_type)
        if background_type == 'image' and \
           self.config['background_image'] is not None and \
           self.config['background_image'] != '':
            self.vte.set_background_image_file(self.config['background_image'])
            self.vte.set_scroll_background(self.config['scroll_background'])
        else:
            try:
                self.vte.set_background_image(None)
            except TypeError:
                # FIXME: I think this is only necessary because of
                # https://bugzilla.gnome.org/show_bug.cgi?id=614910
                pass
            self.vte.set_scroll_background(False)

        if background_type in ('image', 'transparent'):
            self.vte.set_background_tint_color(gtk.gdk.color_parse(
                                               self.config['background_color']))
            opacity = int(self.config['background_darkness'] * 65536)
            saturation = 1.0 - float(self.config['background_darkness'])
            dbg('setting background saturation: %f' % saturation)
            self.vte.set_background_saturation(saturation)
        else:
            dbg('setting background_saturation: 1')
            opacity = 65535
            self.vte.set_background_saturation(1)

        if self.composite_support:
            dbg('setting opacity: %d' % opacity)
            self.vte.set_opacity(opacity)

        # This is quite hairy, but the basic explanation is that we should
        # set_background_transparent(True) when we have no compositing and want
        # fake background transparency, otherwise it should be False.
        if not self.composite_support:
            # We have no compositing support, fake background only
            background_transparent = True
        else:
            if self.vte.is_composited() == False:
                # We have compositing and it's enabled. no fake background.
                background_transparent = True
            else:
                # We have compositing, but it's not enabled. fake background
                background_transparent = False

        if self.config['background_type'] == 'transparent':
            dbg('setting background_transparent=%s' % background_transparent)
            self.vte.set_background_transparent(background_transparent)
        else:
            dbg('setting background_transparent=False')
            self.vte.set_background_transparent(False)

        if hasattr(vte, 'VVVVTE_CURSOR_BLINK_ON'):
            if self.config['cursor_blink'] == True:
                self.vte.set_cursor_blink_mode('VTE_CURSOR_BLINK_ON')
            else:
                self.vte.set_cursor_blink_mode('VTE_CURSOR_BLINK_OFF')
        else:
            self.vte.set_cursor_blinks(self.config['cursor_blink'])

        if self.config['force_no_bell'] == True:
            self.vte.set_audible_bell(False)
            self.vte.set_visible_bell(False)
            self.cnxids.remove_signal(self.vte, 'beep')
        else:
            self.vte.set_audible_bell(self.config['audible_bell'])
            self.vte.set_visible_bell(self.config['visible_bell'])
            self.cnxids.remove_signal(self.vte, 'beep')
            if self.config['urgent_bell'] == True or \
               self.config['icon_bell'] == True:
                try:
                    self.cnxids.new(self.vte, 'beep', self.on_beep)
                except TypeError:
                    err('beep signal unavailable with this version of VTE')

        if self.config['scrollback_infinite'] == True:
            scrollback_lines = -1
        else:
            scrollback_lines = self.config['scrollback_lines']
        self.vte.set_scrollback_lines(scrollback_lines)
        self.vte.set_scroll_on_keystroke(self.config['scroll_on_keystroke'])
        self.vte.set_scroll_on_output(self.config['scroll_on_output'])

        if self.scrollbar_position != self.config['scrollbar_position']:
            self.scrollbar_position = self.config['scrollbar_position']
            if self.config['scrollbar_position'] in ['disabled', 'hidden']:
                self.scrollbar.hide()
            else:
                self.scrollbar.show()
                if self.config['scrollbar_position'] == 'left':
                    self.reorder_child(self.scrollbar, 0)
                elif self.config['scrollbar_position'] == 'right':
                    self.reorder_child(self.vte, 0)

        if hasattr(self.vte, 'set_alternate_screen_scroll'):
            self.vte.set_alternate_screen_scroll(
                                        self.config['alternate_screen_scroll'])

        self.titlebar.update()
        self.vte.queue_draw()

    def get_window_title(self):
        """Return the window title"""
        return(self.vte.get_window_title() or str(self.command))

    def on_group_button_press(self, widget, event):
        """Handler for the group button"""
        if event.button == 1:
            self.create_popup_group_menu(widget, event)
        return(False)

    def on_keypress(self, widget, event):
        """Handler for keyboard events"""
        if not event:
            dbg('Terminal::on_keypress: Called on %s with no event' % widget)
            return(False)

        # FIXME: Does keybindings really want to live in Terminator()?
        mapping = self.terminator.keybindings.lookup(event)

        if mapping == "hide_window":
            return(False)

        if mapping and mapping not in ['close_window', 
                                       'full_screen', 
                                       'new_tab']:
            dbg('Terminal::on_keypress: lookup found: %r' % mapping)
            # handle the case where user has re-bound copy to ctrl+<key>
            # we only copy if there is a selection otherwise let it fall through
            # to ^<key>
            if (mapping == "copy" and event.state & gtk.gdk.CONTROL_MASK):
                if self.vte.get_has_selection ():
                    getattr(self, "key_" + mapping)()
                    return(True)
            else:
                getattr(self, "key_" + mapping)()
                return(True)

        # FIXME: This is all clearly wrong. We should be doing this better
        #         maybe we can emit the key event and let Terminator() care?
        groupsend = self.terminator.groupsend
        groupsend_type = self.terminator.groupsend_type
        window_focussed = self.vte.get_toplevel().get_property('has-toplevel-focus')
        if groupsend != groupsend_type['off'] and window_focussed and self.vte.is_focus():
            if self.group and groupsend == groupsend_type['group']:
                self.terminator.group_emit(self, self.group, 'key-press-event',
                        event)
            if groupsend == groupsend_type['all']:
                self.terminator.all_emit(self, 'key-press-event', event)

        return(False)

    def on_buttonpress(self, widget, event):
        """Handler for mouse events"""
        # Any button event should grab focus
        widget.grab_focus()

        if event.button == 1:
            # Ctrl+leftclick on a URL should open it
            if event.state & gtk.gdk.CONTROL_MASK == gtk.gdk.CONTROL_MASK:
                url = self.check_for_url(event)
                if url:
                    self.open_url(url, prepare=True)
        elif event.button == 2:
            # middleclick should paste the clipboard
            self.paste_clipboard(True)
            return(True)
        elif event.button == 3:
            # rightclick should display a context menu if Ctrl is not pressed
            if event.state & gtk.gdk.CONTROL_MASK == 0:
                self.popup_menu(widget, event)
                return(True)

        return(False)
    
    def popup_menu(self, widget, event=None):
        """Display the context menu"""
        menu = TerminalPopupMenu(self)
        menu.show(widget, event)

    def do_scrollbar_toggle(self):
        """Show or hide the terminal scrollbar"""
        self.toggle_widget_visibility(self.scrollbar)

    def toggle_widget_visibility(self, widget):
        """Show or hide a widget"""
        if widget.get_property('visible'):
            widget.hide()
        else:
            widget.show()

    def on_encoding_change(self, _widget, encoding):
        """Handle the encoding changing"""
        current = self.vte.get_encoding()
        if current != encoding:
            dbg('on_encoding_change: setting encoding to: %s' % encoding)
            self.custom_encoding = not (encoding == self.config['encoding'])
            self.vte.set_encoding(encoding)

    def on_drag_begin(self, widget, drag_context, _data):
        """Handle the start of a drag event"""
        widget.drag_source_set_icon_pixbuf(util.widget_pixbuf(self, 512))

    def on_drag_data_get(self, _widget, _drag_context, selection_data, info, 
            _time, data):
        """I have no idea what this does, drag and drop is a mystery. sorry."""
        selection_data.set('vte', info,
                str(data.terminator.terminals.index(self)))

    def on_drag_motion(self, widget, drag_context, x, y, _time, _data):
        """*shrug*"""
        if gtk.targets_include_text(drag_context.targets) or \
           gtk.targets_include_uri(drag_context.targets):
            # copy text from another widget
            return
        srcwidget = drag_context.get_source_widget()
        if(isinstance(srcwidget, gtk.EventBox) and 
           srcwidget == self.titlebar) or widget == srcwidget:
            # on self
            return

        alloc = widget.allocation
        rect = gtk.gdk.Rectangle(0, 0, alloc.width, alloc.height)

        if self.config['use_theme_colors']:
            color = self.vte.get_style().text[gtk.STATE_NORMAL]
        else:
            color = gtk.gdk.color_parse(self.config['foreground_color'])

        pos = self.get_location(widget, x, y)
        topleft = (0, 0)
        topright = (alloc.width, 0)
        topmiddle = (alloc.width/2, 0)
        bottomleft = (0, alloc.height)
        bottomright = (alloc.width, alloc.height)
        bottommiddle = (alloc.width/2, alloc.height)
        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)
        elif pos == "top":
            coord = (topleft, topright, middleright , middleleft)
        elif pos == "left":
            coord = (topleft, topmiddle, bottommiddle, bottomleft)
        elif pos == "bottom":
            coord = (bottomleft, bottomright, middleright , middleleft) 

        #here, we define some widget internal values
        widget._expose_data = { 'color': color, 'coord' : coord }
        #redraw by forcing an event
        connec = widget.connect_after('expose-event', self.on_expose_event)
        widget.window.invalidate_rect(rect, True)
        widget.window.process_updates(True)
        #finaly reset the values
        widget.disconnect(connec)
        widget._expose_data = None

    def on_expose_event(self, widget, _event):
        """Handle an expose event while dragging"""
        if not widget._expose_data:
            return(False)

        color = widget._expose_data['color']
        coord = widget._expose_data['coord']

        context = widget.window.cairo_create()
        context.set_source_rgba(color.red, color.green, color.blue, 0.5)
        if len(coord) > 0 :
            context.move_to(coord[len(coord)-1][0], coord[len(coord)-1][1])
            for i in coord:
                context.line_to(i[0], i[1])

        context.fill()
        return(False)

    def on_drag_data_received(self, widget, drag_context, x, y, selection_data,
            _info, _time, data):
        """Something has been dragged into the terminal. Handle it as either a
        URL or another terminal."""
        dbg('drag data received of type: %s' % selection_data.type)
        if gtk.targets_include_text(drag_context.targets) or \
           gtk.targets_include_uri(drag_context.targets):
            # copy text to destination
            txt = selection_data.data.strip()
            if txt[0:7] == 'file://':
                txt = "'%s'" % urllib.unquote(txt[7:])
            for term in self.terminator.get_target_terms(self):
                term.feed(txt)
            return
        
        widgetsrc = data.terminator.terminals[int(selection_data.data)]
        srcvte = drag_context.get_source_widget()
        #check if computation requireds
        if (isinstance(srcvte, gtk.EventBox) and 
                srcvte == self.titlebar) or srcvte == widget:
            return

        srchbox = widgetsrc

        # The widget argument is actually a vte.Terminal(). Turn that into a
        # terminatorlib Terminal()
        maker = Factory()
        while True:
            widget = widget.get_parent()
            if not widget:
                # We've run out of widgets. Something is wrong.
                err('Failed to find Terminal from vte')
                return
            if maker.isinstance(widget, 'Terminal'):
                break

        dsthbox = widget

        dstpaned = dsthbox.get_parent()
        srcpaned = srchbox.get_parent()

        pos = self.get_location(widget, x, y)

        srcpaned.remove(widgetsrc)
        dstpaned.split_axis(dsthbox, pos in ['top', 'bottom'], None, widgetsrc, pos in ['bottom', 'right'])
        srcpaned.hoover()
        widgetsrc.ensure_visible_and_focussed()

    def get_location(self, term, x, y):
        """Get our location within the terminal"""
        pos = ''
        #get the diagonales function for the receiving widget
        coef1 = float(term.allocation.height)/float(term.allocation.width)
        coef2 = -float(term.allocation.height)/float(term.allocation.width)
        b1 = 0
        b2 = term.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 grab_focus(self):
        """Steal focus for this terminal"""
        if not self.vte.flags()&gtk.HAS_FOCUS:
            self.vte.grab_focus()

    def ensure_visible_and_focussed(self):
        """Make sure that we're visible and focussed"""
        window = self.get_toplevel()
        topchild = window.get_child()
        maker = Factory()

        if maker.isinstance(topchild, 'Notebook'):
            prevtmp = None
            tmp = self.get_parent()
            while tmp != topchild:
                prevtmp = tmp
                tmp = tmp.get_parent() 
            page = topchild.page_num(prevtmp)
            topchild.set_current_page(page)

        self.grab_focus()

    def on_vte_focus(self, _widget):
        """Update our UI when we get focus"""
        self.emit('title-change', self.get_window_title())

    def on_vte_focus_in(self, _widget, _event):
        """Inform other parts of the application when focus is received"""
        self.emit('focus-in')

    def on_window_focus_out(self):
        """Update our UI when the window loses focus"""
        self.titlebar.update('window-focus-out')

    def scrollbar_jump(self, position):
        """Move the scrollbar to a particular row"""
        self.scrollbar.set_value(position)

    def on_search_done(self, _widget):
        """We've finished searching, so clean up"""
        self.searchbar.hide()
        self.scrollbar.set_value(self.vte.get_cursor_position()[1])
        self.vte.grab_focus()

    def on_edit_done(self, _widget):
        """A child widget is done editing a label, return focus to VTE"""
        self.vte.grab_focus()

    def deferred_on_vte_size_allocate(self, widget, allocation):
        # widget & allocation are not used in on_vte_size_allocate, so we
        # can use the on_vte_size_allocate instead of duplicating the code
        if self.pending_on_vte_size_allocate == True:
            return
        self.pending_on_vte_size_allocate = True
        gobject.idle_add(self.do_deferred_on_vte_size_allocate, widget, allocation)

    def do_deferred_on_vte_size_allocate(self, widget, allocation):
        self.pending_on_vte_size_allocate = False
        self.on_vte_size_allocate(widget, allocation)

    def on_vte_size_allocate(self, widget, allocation):
        self.titlebar.update_terminal_size(self.vte.get_column_count(),
                self.vte.get_row_count())
        if self.vte.window and self.config['geometry_hinting']:
            window = self.get_toplevel()
            window.deferred_set_rough_geometry_hints()

    def on_vte_notify_enter(self, term, event):
        """Handle the mouse entering this terminal"""
        # FIXME: This shouldn't be looking up all these values every time
        sloppy = False
        if self.config['focus'] == 'system':
            sloppy = self.config.get_system_focus() in ['sloppy', 'mouse']
        elif self.config['focus'] in ['sloppy', 'mouse']:
            sloppy = True
        if sloppy == True and self.titlebar.editing() == False:
            term.grab_focus()
            return(False)

    def get_zoom_data(self):
        """Return a dict of information for Window"""
        data = {}
        data['old_font'] = self.vte.get_font()
        data['old_char_height'] = self.vte.get_char_height()
        data['old_char_width'] = self.vte.get_char_width()
        data['old_allocation'] = self.vte.get_allocation()
        data['old_padding'] = self.vte.get_padding()
        data['old_columns'] = self.vte.get_column_count()
        data['old_rows'] = self.vte.get_row_count()
        data['old_parent'] = self.get_parent()

        return(data)

    def zoom_scale(self, widget, allocation, old_data):
        """Scale our font correctly based on how big we are not vs before"""
        self.cnxids.remove_signal(self, 'size-allocate')
        # FIXME: Is a zoom signal actualy used anywhere?
        self.cnxids.remove_signal(self, 'zoom')

        new_columns = self.vte.get_column_count()
        new_rows = self.vte.get_row_count()
        new_font = self.vte.get_font()
        new_allocation = self.vte.get_allocation()

        old_alloc = {'x': old_data['old_allocation'].width - \
                          old_data['old_padding'][0],
                     'y': old_data['old_allocation'].height - \
                          old_data['old_padding'][1]
                    }

        dbg('Terminal::zoom_scale: Resized from %dx%d to %dx%d' % (
             old_data['old_columns'],
             old_data['old_rows'],
             new_columns,
             new_rows))

        if new_rows == old_data['old_rows'] or \
           new_columns == old_data['old_columns']:
            dbg('Terminal::zoom_scale: One axis unchanged, not scaling')
            return

        old_area = old_data['old_columns'] * old_data['old_rows']
        new_area = new_columns * new_rows
        area_factor = (new_area / old_area) / 2

        new_size = int(old_data['old_font'].get_size() * area_factor)
        if new_size == 0:
            err('refusing to set a zero sized font')
            return
        new_font.set_size(new_size)
        dbg('setting new font: %s' % new_font)
        self.set_font(new_font)

    def is_zoomed(self):
        """Determine if we are a zoomed terminal"""
        prop = None
        parent = self.get_parent()
        window = self.get_toplevel()

        try:
            prop = window.get_property('term-zoomed')
        except TypeError:
            prop = False

        return(prop)

    def zoom(self, widget=None):
        """Zoom ourself to fill the window"""
        self.emit('zoom')

    def maximise(self, widget=None):
        """Maximise ourself to fill the window"""
        self.emit('maximise')

    def unzoom(self, widget=None):
        """Restore normal layout"""
        self.emit('unzoom')

    def set_cwd(self, cwd=None):
        """Set our cwd"""
        if cwd is not None:
            self.cwd = cwd

    def spawn_child(self, widget=None, respawn=False, debugserver=False):
        update_records = self.config['update_records']
        login = self.config['login_shell']
        args = []
        shell = None
        command = None

        if self.terminator.doing_layout == True:
            dbg('still laying out, refusing to spawn a child')
            return

        if respawn == False:
            self.vte.grab_focus()

        options = self.config.options_get()
        if options and options.command:
            command = options.command
            options.command = None
        elif options and options.execute:
            command = options.execute
            options.execute = None
        elif self.config['use_custom_command']:
            command = self.config['custom_command']
        elif self.layout_command:
            command = self.layout_command
        elif debugserver is True:
            details = self.terminator.debug_address
            dbg('spawning debug session with: %s:%s' % (details[0],
                details[1]))
            command = 'telnet %s %s' % (details[0], details[1])

        if options and options.working_directory and \
           options.working_directory != '':
            self.set_cwd(options.working_directory)
            options.working_directory = ''

        if type(command) is list:
            shell = util.path_lookup(command[0])
            args = command
        else:
            shell = util.shell_lookup()

            if self.config['login_shell']:
                args.insert(0, "-%s" % shell)
            else:
                args.insert(0, shell)

            if command is not None:
                args += ['-c', command]

        if shell is None:
            self.vte.feed(_('Unable to find a shell'))
            return(-1)

        try:
            os.putenv('WINDOWID', '%s' % self.vte.get_parent_window().xid)
        except AttributeError:
            pass

        envv = []
        envv.append('TERMINATOR_UUID=%s' % self.uuid.urn)
        if self.terminator.dbus_name:
            envv.append('TERMINATOR_DBUS_NAME=%s' % self.terminator.dbus_name)
        if self.terminator.dbus_path:
            envv.append('TERMINATOR_DBUS_PATH=%s' % self.terminator.dbus_path)

        dbg('Forking shell: "%s" with args: %s' % (shell, args))
        self.pid = self.vte.fork_command(command=shell, argv=args, envv=envv,
                                         loglastlog=login, 
                                         logwtmp=update_records,
                                         logutmp=update_records, 
                                         directory=self.cwd)
        self.command = shell

        self.titlebar.update()

        if self.pid == -1:
            self.vte.feed(_('Unable to start shell:') + shell)
            return(-1)

    def check_for_url(self, event):
        """Check if the mouse is over a URL"""
        return (self.vte.match_check(int(event.x / self.vte.get_char_width()),
            int(event.y / self.vte.get_char_height())))

    def prepare_url(self, urlmatch):
        """Prepare a URL from a VTE match"""
        url = urlmatch[0]
        match = urlmatch[1]

        if match == self.matches['email'] and url[0:7] != 'mailto:':
            url = 'mailto:' + url
        elif match == self.matches['addr_only'] and url[0:3] == 'ftp':
            url = 'ftp://' + url
        elif match == self.matches['addr_only']:
            url = 'http://' + url
        elif match in self.matches.values():
            # We have a match, but it's not a hard coded one, so it's a plugin
            try:
                registry = plugin.PluginRegistry()
                registry.load_plugins()
                plugins = registry.get_plugins_by_capability('url_handler')

                for urlplugin in plugins:
                    if match == self.matches[urlplugin.handler_name]:
                        newurl = urlplugin.callback(url)
                        if newurl is not None:
                            dbg('Terminal::prepare_url: URL prepared by \
%s plugin' % urlplugin.handler_name)
                            url = newurl
                        break
            except Exception, ex:
                err('Terminal::prepare_url: %s' % ex)

        return(url)

    def open_url(self, url, prepare=False):
        """Open a given URL, conditionally unpacking it from a VTE match"""
        oldstyle = False

        if prepare == True:
            url = self.prepare_url(url)
        dbg('open_url: URL: %s (prepared: %s)' % (url, prepare))

        if gtk.gtk_version < (2, 14, 0) or \
           not hasattr(gtk, 'show_uri') or \
           not hasattr(gtk.gdk, 'CURRENT_TIME'):
            oldstyle = True

        if oldstyle == False:
            try:
                gtk.show_uri(None, url, gtk.gdk.CURRENT_TIME)
            except:
                oldstyle = True

        if oldstyle == True:
            dbg('Old gtk (%s,%s,%s), calling xdg-open' % gtk.gtk_version)
            try:
                subprocess.Popen(["xdg-open", url])
            except:
                dbg('xdg-open did not work, falling back to webbrowser.open')
                import webbrowser
                webbrowser.open(url)

    def paste_clipboard(self, primary=False):
        """Paste one of the two clipboards"""
        for term in self.terminator.get_target_terms(self):
            if primary:
                term.vte.paste_primary()
            else:
                term.vte.paste_clipboard()
        self.vte.grab_focus()

    def feed(self, text):
        """Feed the supplied text to VTE"""
        self.vte.feed_child(text)

    def zoom_in(self):
        """Increase the font size"""
        self.zoom_font(True)

    def zoom_out(self):
        """Decrease the font size"""
        self.zoom_font(False)

    def zoom_font(self, zoom_in):
        """Change the font size"""
        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.set_font(pangodesc)
        self.custom_font_size = fontsize

    def zoom_orig(self):
        """Restore original font size"""
        if self.config['use_system_font'] == True:
            font = self.config.get_system_font()
        else:
            font = self.config['font']
        dbg("Terminal::zoom_orig: restoring font to: %s" % font)
        self.set_font(pango.FontDescription(font))
        self.custom_font_size = None

    def set_font(self, fontdesc):
        """Set the font we want in VTE"""
        antialias = self.config['antialias']
        if antialias:
            try:
                antialias = vte.ANTI_ALIAS_FORCE_ENABLE
            except AttributeError:
                antialias = 1
        else:
            try:
                antialias = vte.ANTI_ALIAS_FORCE_DISABLE
            except AttributeError:
                antialias = 2
        self.vte.set_font_full(fontdesc, antialias)

    def get_cursor_position(self):
        """Return the co-ordinates of our cursor"""
        # FIXME: THIS METHOD IS DEPRECATED AND UNUSED
        col, row = self.vte.get_cursor_position()
        width = self.vte.get_char_width()
        height = self.vte.get_char_height()
        return((col * width, row * height))

    def get_font_size(self):
        """Return the width/height of our font"""
        return((self.vte.get_char_width(), self.vte.get_char_height()))

    def get_size(self):
        """Return the column/rows of the terminal"""
        return((self.vte.get_column_count(), self.vte.get_row_count()))

    def on_beep(self, widget):
        """Set the urgency hint for our window"""
        if self.config['urgent_bell'] == True:
            window = self.get_toplevel()
            window.set_urgency_hint(True)
        if self.config['icon_bell'] == True:
            self.titlebar.icon_bell()

    def describe_layout(self, count, parent, global_layout, child_order):
        """Describe our layout"""
        layout = {}
        layout['type'] = 'Terminal'
        layout['parent'] = parent
        layout['order'] = child_order
        if self.group:
            layout['group'] = self.group
        profile = self.get_profile()
        if layout != "default":
            # There's no point explicitly noting default profiles
            layout['profile'] = profile
        title = self.titlebar.get_custom_string()
        if title:
            layout['title'] = title
        name = 'terminal%d' % count
        count = count + 1
        global_layout[name] = layout
        return(count)

    def create_layout(self, layout):
        """Apply our layout"""
        dbg('Setting layout')
        if layout.has_key('command') and layout['command'] != '':
            self.layout_command = layout['command']
        if layout.has_key('profile') and layout['profile'] != '':
            if layout['profile'] in self.config.list_profiles():
                self.set_profile(self, layout['profile'])
        if layout.has_key('group') and layout['group'] != '':
            # This doesn't need/use self.titlebar, but it's safer than sending
            # None
            self.really_create_group(self.titlebar, layout['group'])
        if layout.has_key('title') and layout['title'] != '':
            self.titlebar.set_custom_string(layout['title'])

    # There now begins a great list of keyboard event handlers
    def key_zoom_in(self):
        self.zoom_in()

    def key_zoom_out(self):
        self.zoom_out()

    def key_copy(self):
        self.vte.copy_clipboard()

    def key_paste(self):
        self.paste_clipboard()

    def key_toggle_scrollbar(self):
        self.do_scrollbar_toggle()

    def key_zoom_normal(self):
        self.zoom_orig ()

    def key_search(self):
        self.searchbar.start_search()

    # bindings that should be moved to Terminator as they all just call
    # a function of Terminator. It would be cleaner if TerminatorTerm
    # has absolutely no reference to Terminator.
    # N (next) - P (previous) - O (horizontal) - E (vertical) - W (close)
    def key_cycle_next(self):
        self.key_go_next()

    def key_cycle_prev(self):
        self.key_go_prev()

    def key_go_next(self):
        self.emit('navigate', 'next')

    def key_go_prev(self):
        self.emit('navigate', 'prev')

    def key_go_up(self):
        self.emit('navigate', 'up')

    def key_go_down(self):
        self.emit('navigate', 'down')

    def key_go_left(self):
        self.emit('navigate', 'left')

    def key_go_right(self):
        self.emit('navigate', 'right')

    def key_split_horiz(self):
        self.emit('split-horiz', self.terminator.pid_cwd(self.pid))

    def key_split_vert(self):
        self.emit('split-vert', self.terminator.pid_cwd(self.pid))

    def key_close_term(self):
        self.close()

    def key_resize_up(self):
        self.emit('resize-term', 'up')

    def key_resize_down(self):
        self.emit('resize-term', 'down')

    def key_resize_left(self):
        self.emit('resize-term', 'left')

    def key_resize_right(self):
        self.emit('resize-term', 'right')

    def key_move_tab_right(self):
        self.emit('move-tab', 'right')

    def key_move_tab_left(self):
        self.emit('move-tab', 'left')

    def key_toggle_zoom(self):
        if self.is_zoomed():
            self.unzoom()
        else:
            self.maximise()

    def key_scaled_zoom(self):
        if self.is_zoomed():
            self.unzoom()
        else:
            self.zoom()

    def key_next_tab(self):
        self.emit('tab-change', -1)

    def key_prev_tab(self):
        self.emit('tab-change', -2)

    def key_switch_to_tab_1(self):
        self.emit('tab-change', 0)

    def key_switch_to_tab_2(self):
        self.emit('tab-change', 1)

    def key_switch_to_tab_3(self):
        self.emit('tab-change', 2)

    def key_switch_to_tab_4(self):
        self.emit('tab-change', 3)

    def key_switch_to_tab_5(self):
        self.emit('tab-change', 4)

    def key_switch_to_tab_6(self):
        self.emit('tab-change', 5)

    def key_switch_to_tab_7(self):
        self.emit('tab-change', 6)

    def key_switch_to_tab_8(self):
        self.emit('tab-change', 7)

    def key_switch_to_tab_9(self):
        self.emit('tab-change', 8)

    def key_switch_to_tab_10(self):
        self.emit('tab-change', 9)

    def key_reset(self):
        self.vte.reset (True, False)

    def key_reset_clear(self):
        self.vte.reset (True, True)

    def key_group_all(self):
        self.emit('group-all')

    def key_ungroup_all(self):
        self.emit('ungroup-all')

    def key_group_tab(self):
        self.emit('group-tab')
    
    def key_ungroup_tab(self):
        self.emit('ungroup-tab')

    def key_new_window(self):
        self.terminator.new_window(self.terminator.pid_cwd(self.pid))

    def key_new_terminator(self):
        cmd = sys.argv[0]
    
        if not os.path.isabs(cmd):
            # Command is not an absolute path. Figure out where we are
            cmd = os.path.join (self.origcwd, sys.argv[0])
            if not os.path.isfile(cmd):
                # we weren't started as ./terminator in a path. Give up
                err('Terminal::key_new_window: Unable to locate Terminator')
                return False
          
        dbg("Terminal::key_new_window: Spawning: %s" % cmd)
        subprocess.Popen([cmd, '-u'])

    def key_broadcast_off(self):
        self.set_groupsend(None, self.terminator.groupsend_type['off'])

    def key_broadcast_group(self):
        self.set_groupsend(None, self.terminator.groupsend_type['group'])

    def key_broadcast_all(self):
        self.set_groupsend(None, self.terminator.groupsend_type['all'])

# End key events

gobject.type_register(Terminal)
# vim: set expandtab ts=4 sw=4:
