#!/usr/bin/python
#    Terminator - multiple gnome terminals in one window
#    Copyright (C) 2006-2008  cmsj@tenshu.net
#
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, version 2 only.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program; if not, write to the Free Software
#    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

"""Terminator by Chris Jones <cmsj@tenshu.net>"""

# Global defines
APP_NAME = 'terminator'
APP_VERSION = '0.9'

# import standard python libs
import os, platform, sys, string, time, math
from optparse import OptionParser

try:
  import gettext
  gettext.install (APP_NAME)
except:
  def _ (text):
    return text

# import unix-lib
import pwd

# import our configuration loader
import terminatorconfig

# import gtk libs
# check just in case anyone runs it on a non-gnome system.
try:
  import gobject, gtk, pango
except:
  print >> sys.stderr, _("You need to install the python bindings for " \
    "gobject, gtk and pango to run Terminator.")
  sys.exit(1)

# 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:
    url_show (url)
  except:
    pass

class TerminatorTerm:

  matches = {}

  def __init__ (self, terminator, profile = None, command = None, cwd = None):
    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._box = gtk.VBox ()
    self._box.show()
    self._box.pack_start(self._titlebox, False)
    self._box.pack_start(self._termbox)
    if  len(self.terminator.term_list) > 0 and self.conf.titlebars:
      if len(self.terminator.term_list) == 1:
        self.terminator.term_list[0]._titlebox.show()
      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 == 'right':
      packfunc = self._termbox.pack_start
    else:
      packfunc = self._termbox.pack_end

    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)
    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()

    env_proxy = os.getenv ('http_proxy')
    if not env_proxy:
      os.putenv ('http_proxy', self.conf.http_proxy)

    self.spawn_child ()

  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-z0-9][a-z0-9.-]*@[a-z0-9][a-z0-9-]*(\.[a-z0-9][a-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

    if self.command:
      args = self.command
      shell = self.command[0]
    elif self.conf.use_custom_command:
      args = self.conf.custom_command.split ()
      shell = args[0]
    else:
      shell = pwd.getpwuid (os.getuid ())[6]
      args = [os.path.basename (shell)]

    self._pid = self._vte.fork_command (command = shell, argv = args, envv = [], directory=self.cwd, loglastlog = login, logwtmp = update_records, logutmp = update_records)

  def get_cwd (self):
    """ Return the current working directory of the subprocess.
        This function requires OS specific behaviours
    """
    system = platform.system ()

    if system == 'Linux':
      cwd = os.path.realpath ('/proc/%s/cwd' % self._pid)
    else:
      # We don't have a child cwd getter for this platform, so let
      # TerminatorTerm use its default
      cwd = None
    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
    self._vte.set_visible_bell = silent_bell

    # 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

  def on_composited_changed (self, widget):
    self.reconfigure_vte ()

  def on_vte_button_press (self, term, event):
    # Left mouse button should transfer focus to this vte widget
    if event.button == 1:
      self._vte.grab_focus ()
      return False

    # Right mouse button should display a context menu
    if event.button == 3:
      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 ()

  #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)

    # 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._vte.paste_clipboard ()
        return (True)
      elif keyname == 'S':
        self.do_scrollbar_toggle ()
        return (True)
      elif keyname in ('Up', 'Down', 'Left', 'Right'):
          self.terminator.resizeterm (self, keyname)
          return (True)

    if keyname and (keyname == 'Tab' or keyname.endswith('_Tab')):
        if event.state == gtk.gdk.CONTROL_MASK:
            self.terminator.go_next (self)
            return (True)
        if (event.state & mask) == mask:
            self.terminator.go_prev (self)
            return (True)

    return (False)

  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):
    self.do_popup ()

  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")
      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._vte.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)

    item = gtk.MenuItem ()
    menu.append (item)

    item = gtk.MenuItem (_("Split H_orizontally"))
    item.connect ("activate", lambda menu_item: self.terminator.splitaxis (self, False))
    menu.append (item)

    item = gtk.MenuItem (_("Split V_ertically"))
    item.connect ("activate", lambda menu_item: self.terminator.splitaxis (self, True))
    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_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 ()))

  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 ()))
  
  def get_box (self):
    return self._box

class Terminator:
  def __init__ (self, profile, command = None, fullscreen = False, maximise = False, borderless = False):
    self.profile = profile
    self.command = command

    self._fullscreen = False
    self.term_list = []
    stores = []
    stores.append (terminatorconfig.TerminatorConfValuestoreRC ())

    try:
      import gconf
      store = terminatorconfig.TerminatorConfValuestoreGConf ()
      store.set_reconfigure_callback (self.reconfigure_vtes)
      stores.append (store)
    except:
      pass

    self.conf = terminatorconfig.TerminatorConfig (stores)

    self.window = gtk.Window ()
    self.window.set_title (APP_NAME.capitalize())

    # FIXME: This really shouldn't be a hardcoded path
    try:
      self.window.set_icon_from_file ("/usr/share/icons/hicolor/48x48/apps/" + APP_NAME + ".png")
    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.set_property ('allow-shrink', True)

    if fullscreen:
      self.fullscreen_toggle ()

    if maximise:
      self.maximize ()

    if 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.get_box ())
    self.window.show ()

  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 ()
    self._fullscreen = not self._fullscreen

  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 (_('<big><b>Close all terminals?</b></big>'))
    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'):
      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):
    self.window.set_title(title)
    
  def splitaxis (self, widget, vertical=True):
    """ Split the provided widget on the horizontal or vertical axis. """

    # create a new terminal and parent pane.
    terminal = TerminatorTerm (self, self.profile, None, widget.get_cwd())
    pane = (vertical) and gtk.VPaned () or gtk.HPaned ()

    # get the parent of the provided terminal
    parent   = widget.get_box ().get_parent ()

    if isinstance (parent, gtk.Window):
      # We have just one term
      widget.get_box ().reparent (pane)

      pane.pack1 (widget.get_box (), True, True)
      pane.pack2 (terminal.get_box (), True, True)

      parent.add (pane)

      position = (vertical) and parent.allocation.height \
                             or parent.allocation.width

    if isinstance (parent, gtk.Paned):
      # We are inside a split term
      position = (vertical) and widget.get_box().allocation.height \
                             or widget.get_box().allocation.width

      if (widget.get_box () == parent.get_child1 ()):
        widget.get_box ().reparent (pane)
        parent.pack1 (pane, True, True)
      else:
        widget.get_box ().reparent (pane)
        parent.pack2 (pane, True, True)

      pane.pack1 (widget.get_box (), True, True)
      pane.pack2 (terminal.get_box (), True, True)

    # show all, set position of the divider
    pane.show ()
    pane.set_position (position / 2)
    terminal.get_box ().show ()

    # insert the term reference into the list
    index = self.term_list.index (widget)
    self.term_list.insert (index + 1, terminal)
    
    # make the new terminal grab the focus
    terminal._vte.grab_focus ()

    return (terminal)

  def closeterm (self, widget):
    parent = widget.get_box ().get_parent ()
    sibling = None

    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.get_box () == parent.get_child1 ():
        sibling = parent.get_child2 ()
      if widget.get_box () == parent.get_child2 ():
        sibling = parent.get_child1 ()

      if not sibling:
        # something is wrong, give up
        print >> sys.stderr, "Error: %s is not a child of %s"%(widget, parent)
        return

      self.term_list.remove (widget)
      grandparent.remove (parent)
      sibling.reparent (grandparent)
      widget.get_box ().destroy ()
      parent.destroy ()

      if not isinstance (sibling, gtk.Paned):
        for term in self.term_list:
          if term.get_box () == sibling:
            term._vte.grab_focus ()
            break
      else:
        if index == 0: index = 1
        self.term_list[index - 1]._vte.grab_focus ()
      if len(self.term_list) == 1:
        self.term_list[0]._titlebox.hide()

    return

  def go_next (self, term):
    current = self.term_list.index (term)
    next = current

    if current == len (self.term_list) - 1:
      next = 0
    else:
      next += 1

    self.term_list[next]._vte.grab_focus ()

  def go_prev (self, term):
    current = self.term_list.index (term)
    previous = current

    if current == 0:
      previous = len (self.term_list) - 1
    else:
      previous -= 1

    #self.window.set_title(self.term_list[previous]._vte.get_window_title())
    self.term_list[previous]._vte.grab_focus ()

  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.get_box (),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 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 reconfigure_vtes (self):
    for term in self.term_list:
      term.reconfigure_vte ()

if __name__ == '__main__':

  def execute_cb (option, opt, value, parser):
    assert value is None
    value = []
    while parser.rargs:
      arg = parser.rargs[0]
      value.append (arg)
      del (parser.rargs[0])
    setattr(parser.values, option.dest, value)

  usage = "usage: %prog [options]"
  parser = OptionParser (usage)
  parser.add_option ("-v", "--version", action="store_true", dest="version", help="Display program version")
  parser.add_option ("-d", "--debug", action="store_true", dest="debug", help="Enable debugging information")
  parser.add_option ("-m", "--maximise", action="store_true", dest="maximise", help="Open the %s window maximised"%APP_NAME.capitalize())
  parser.add_option ("-f", "--fullscreen", action="store_true", dest="fullscreen", help="Set the window into fullscreen mode")
  parser.add_option ("-b", "--borderless", action="store_true", dest="borderless", help="Turn off the window's borders")
  parser.add_option ("-p", "--profile", dest="profile", help="Specify a GNOME Terminal profile to emulate")
  parser.add_option ("-e", "--command", dest="command", help="Execute the argument to this option inside the terminal")
  parser.add_option ("-x", "--execute", dest="execute", action="callback", callback=execute_cb, help="Execute the remainder of the command line inside the terminal")

  (options, args) = parser.parse_args ()
  if len (args) != 0:
    parser.error("Expecting zero additional arguments, found: %d"%len (args))

  if options.version:
    print "%s %s"%(APP_NAME, APP_VERSION)
    sys.exit (0)

  command = []
  if (options.command):
    command.append (options.command)
  if (options.execute):
    command = options.execute

  term = Terminator (options.profile, command, options.fullscreen, options.maximise, options.borderless)

  gtk.main ()