# -*- coding: utf-8; -*-
"""
Copyright (C) 2007-2012 Lincoln de Sousa <lincoln@minaslivre.org>
Copyright (C) 2007 Gabriel Falcão <gabrielteratos@gmail.com>

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; either version 2 of the
License, or (at your option) any later version.

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
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

try:
    from colorlog import ColoredFormatter
except:
    ColoredFormatter = None

import gconf
import gobject
import gtk
import logging
import logging.config
import os
import platform
import pygtk
import subprocess
import sys
import xdg.Exceptions

from urllib import quote_plus
from urllib import url2pathname
from urlparse import urlsplit
from xdg.DesktopEntry import DesktopEntry
from xml.sax.saxutils import escape as xml_escape

import guake.notifier
import keybinder

from guake.about import AboutDialog
from guake.common import _
from guake.common import gladefile
from guake.common import pixmapfile
from guake.common import shell_quote
from guake.gconfhandler import GConfHandler
from guake.gconfhandler import GConfKeyHandler
from guake.globals import ALIGN_BOTTOM
from guake.globals import ALIGN_CENTER
from guake.globals import ALIGN_LEFT
from guake.globals import ALIGN_RIGHT
from guake.globals import ALWAYS_ON_PRIMARY
from guake.globals import GKEY
from guake.globals import KEY
from guake.globals import LOCALE_DIR
from guake.globals import NAME
from guake.guake_notebook import GuakeNotebook
from guake.prefs import PrefsDialog
from guake.simplegladeapp import SimpleGladeApp
from guake.simplegladeapp import bindtextdomain
from guake.terminal import GuakeTerminalBox


libutempter = None
try:
    # this allow to run some commands that requires libuterm to
    # be injected in current process, as: wall
    from atexit import register as at_exit_call
    from ctypes import cdll
    libutempter = cdll.LoadLibrary('libutempter.so.0')
    if libutempter is not None:
        # We absolutly need to remove the old tty from the utmp !!!!!!!!!!
        at_exit_call(libutempter.utempter_remove_added_record)
except Exception as e:
    libutempter = None
    sys.stderr.write('[WARN] Unable to load the library libutempter !\n')
    sys.stderr.write('[WARN] The <wall> command will not work in guake !\n')
    sys.stderr.write('[WARN] ' + str(e) + '\n')

instance = None
RESPONSE_FORWARD = 0
RESPONSE_BACKWARD = 1

# Disable find feature until python-vte hasn't been updated
enable_find = False

pygtk.require('2.0')
gobject.threads_init()

# Loading translation
bindtextdomain(NAME, LOCALE_DIR)

# Setting gobject program name
gobject.set_prgname(NAME)

GDK_WINDOW_STATE_WITHDRAWN = 1
GDK_WINDOW_STATE_ICONIFIED = 2
GDK_WINDOW_STATE_STICKY = 8
GDK_WINDOW_STATE_ABOVE = 32


log = logging.getLogger(__name__)


class PromptQuitDialog(gtk.MessageDialog):

    """Prompts the user whether to quit/close a tab.
    """

    def __init__(self, parent, procs, tabs):
        super(PromptQuitDialog, self).__init__(
            parent,
            gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
            gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO)

        if tabs == -1:
            primary_msg = _('Do you want to close the tab?')
            tab_str = ''
        else:
            primary_msg = _('Do you really want to quit Guake?')
            if tabs == 1:
                tab_str = _(" and one tab open")
            else:
                tab_str = _(" and {0} tabs open").format(tabs)

        if procs == 0:
            proc_str = _("There are no processes running")
        elif procs == 1:
            proc_str = _("There is a process still running")
        else:
            proc_str = _("There are {0} processes still running").format(procs)

        self.set_keep_above(True)
        self.set_markup(primary_msg)
        self.format_secondary_markup("<b>{0}{1}.</b>".format(proc_str, tab_str))


class Guake(SimpleGladeApp):

    """Guake main class. Handles specialy the main window.
    """

    def __init__(self):
        super(Guake, self).__init__(gladefile('guake.glade'))
        self.client = gconf.client_get_default()

        self.debug_mode = self.client.get_bool(KEY('/general/debug_mode'))
        self.setupLogging()

        # Cannot use "getattr(gtk.Window().get_style(), "base")[int(gtk.STATE_SELECTED)]"
        # since theme has not been applied before first show_all
        self.selected_color = None

        self.prompt_dialog = None
        self.hidden = True
        self.forceHide = False
        self.preventHide = False

        # trayicon!
        try:
            import appindicator
        except ImportError:
            img = pixmapfile('guake-tray.png')
            self.tray_icon = gtk.status_icon_new_from_file(img)
            self.tray_icon.set_tooltip(_('Guake Terminal'))
            self.tray_icon.connect('popup-menu', self.show_menu)
            self.tray_icon.connect('activate', self.show_hide)
        else:
            self.tray_icon = appindicator.Indicator(
                _("guake-indicator"), _("guake-tray"), appindicator.CATEGORY_OTHER)
            self.tray_icon.set_icon("guake-tray")
            self.tray_icon.set_status(appindicator.STATUS_ACTIVE)
            menu = self.get_widget('tray-menu')
            show = gtk.MenuItem(_('Show'))
            show.set_sensitive(True)
            show.connect('activate', self.show_hide)
            show.show()
            menu.prepend(show)
            self.tray_icon.set_menu(menu)

        # important widgets
        self.window = self.get_widget('window-root')
        self.mainframe = self.get_widget('mainframe')
        self.mainframe.remove(self.get_widget('notebook-teminals'))
        self.notebook = GuakeNotebook()
        self.notebook.set_name("notebook-teminals")
        self.notebook.set_property("tab_pos", "bottom")
        self.notebook.set_property("show_tabs", False)
        self.notebook.set_property("show_border", False)
        self.notebook.set_property("visible", True)
        self.notebook.set_property("has_focus", True)
        self.notebook.set_property("can_focus", True)
        self.notebook.set_property("is_focus", True)
        self.notebook.set_property("enable_popup", True)
        self.notebook.connect("switch_page", self.select_current_tab)
        self.mainframe.add(self.notebook)
        self.set_tab_position()

        self.tabs = self.get_widget('hbox-tabs')
        self.toolbar = self.get_widget('toolbar')
        self.mainframe = self.get_widget('mainframe')
        self.resizer = self.get_widget('resizer')

        # check and set ARGB for real transparency
        screen = self.window.get_screen()
        colormap = screen.get_rgba_colormap()
        if colormap is None:
            self.has_argb = False
        else:
            self.window.set_colormap(colormap)
            self.has_argb = self.window.get_screen().is_composited()

            def composited_changed(screen):
                self.has_argb = screen.is_composited()
                self.set_background_transparency(
                    self.client.get_int(KEY('/style/background/transparency')))
                self.set_background_image(
                    self.client.get_string(KEY('/style/background/image')))

            self.window.get_screen().connect("composited-changed",
                                             composited_changed)

        # It's intended to know which tab was selected to
        # close/rename. This attribute will be set in
        # self.show_tab_menu
        self.selected_tab = None

        # holds fullscreen status
        self.is_fullscreen = False

        # holds the timestamp of the losefocus event
        self.losefocus_time = 0

        # holds the timestamp of the previous show/hide action
        self.prev_showhide_time = 0

        # double click stuff
        def double_click(hbox, event):
            """Handles double clicks on tabs area and when receive
            one, calls add_tab.
            """
            if event.button == 1 and event.type == gtk.gdk._2BUTTON_PRESS:
                self.add_tab()
        evtbox = self.get_widget('event-tabs')
        evtbox.connect('button-press-event', double_click)

        # Flag to prevent guake hide when window_losefocus is true and
        # user tries to use the context menu.
        self.showing_context_menu = False

        def hide_context_menu(menu):
            """Turn context menu flag off to make sure it is not being
            shown.
            """
            self.showing_context_menu = False

        self.get_widget('context-menu').connect('hide', hide_context_menu)
        self.get_widget('tab-menu').connect('hide', hide_context_menu)
        self.window.connect('focus-out-event', self.on_window_losefocus)

        # Handling the delete-event of the main window to avoid
        # problems when closing it.
        def destroy(*args):
            self.hide()
            return True

        def window_event(*args):
            return self.window_event(*args)

        self.window.connect('delete-event', destroy)
        self.window.connect('window-state-event', window_event)

        # Flag to completely disable losefocus hiding
        self.disable_losefocus_hiding = False

        # this line is important to resize the main window and make it
        # smaller.
        self.window.set_geometry_hints(min_width=1, min_height=1)

        # special trick to avoid the "lost guake on Ubuntu 'Show Desktop'" problem.
        # DOCK makes the window foundable after having being "lost" after "Show Desktop"
        self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DOCK)
        # Restore back to normal behavior
        self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_NORMAL)

        # resizer stuff
        self.resizer.connect('motion-notify-event', self.on_resizer_drag)

        # adding the first tab on guake
        self.add_tab()

        # loading and setting up configuration stuff
        GConfHandler(self)
        self.hotkeys = keybinder
        GConfKeyHandler(self)
        self.load_config()

        key = self.client.get_string(GKEY('show_hide'))
        keyval, mask = gtk.accelerator_parse(key)
        label = gtk.accelerator_get_label(keyval, mask)
        filename = pixmapfile('guake-notification.png')

        self.get_widget("context_find_tab").set_visible(enable_find)

        if self.client.get_bool(KEY('/general/start_fullscreen')):
            self.fullscreen()

        if self.client.get_bool(KEY('/general/use_popup')):
            # Pop-up that shows that guake is working properly (if not
            # unset in the preferences windows)
            guake.notifier.show_message(
                _('Guake Terminal'),
                _('Guake is now running,\n'
                  'press <b>%s</b> to use it.') % xml_escape(label), filename)

    def setupLogging(self):
        if self.debug_mode:
            base_logging_level = logging.DEBUG
        else:
            base_logging_level = logging.INFO

        if ColoredFormatter:
            logging.config.dictConfig({
                'version': 1,
                'disable_existing_loggers': False,
                'loggers': {
                    '': {
                        'handlers': ['default'],
                        'level': 'DEBUG',
                        'propagate': True
                    },
                },
                'handlers': {
                    'default': {
                        'level': 'DEBUG',
                        'class': 'logging.StreamHandler',
                        'formatter': "default",
                    },
                },
                'formatters': {
                    'default': {
                        '()': 'colorlog.ColoredFormatter',
                        'format': "%(log_color)s%(levelname)-8s%(reset)s %(message)s",
                        'log_colors': {
                            'DEBUG': 'cyan',
                            'INFO': 'green',
                            'WARNING': 'yellow',
                            'ERROR': 'red',
                            'CRITICAL': 'red,bg_white',
                        },
                    }
                },
            })
        else:
            logging.basicConfig(level=base_logging_level)
        log.setLevel(base_logging_level)
        log.info("Logging configuration complete")
        log.debug("Debug mode enabled")

    def printDebug(self, text):
        log.debug(text)

    def printInfo(self, text):
        log.debug(text)

    def set_background_transparency(self, transparency):
        for t in self.notebook.iter_terminals():
            t.set_background_saturation(transparency / 100.0)
            if self.has_argb:
                t.set_opacity(int((100 - transparency) / 100.0 * 65535))

    def set_background_image(self, image):
        for t in self.notebook.iter_terminals():
            if image and os.path.exists(image):
                t.set_background_image_file(image)
                t.set_background_transparent(False)
            else:
                """We need to clear the image if it's not set but there is
                a bug in vte python bindings which doesn't allow None to be
                passed to set_background_image (C GTK function expects NULL).
                The user will need to restart Guake after clearing the image.
                r.set_background_image(None)
                """
                if self.has_argb:
                    t.set_background_transparent(False)
                else:
                    t.set_background_transparent(True)

    def set_bgcolor(self, bgcolor, tab=None):
        """Set the background color of `tab' or the current tab to `bgcolor'."""
        if not self.notebook.has_term():
            self.add_tab()
        index = tab or self.notebook.get_current_page()
        for terminal in self.notebook.get_terminals_for_tab(index):
            terminal.custom_bgcolor = gtk.gdk.color_parse(bgcolor)

    def set_fgcolor(self, fgcolor, tab=None):
        """Set the foreground color of `tab' or the current tab to `fgcolor'."""
        if not self.notebook.has_term():
            self.add_tab()
        index = tab or self.notebook.get_current_page()
        for terminal in self.notebook.get_terminals_for_tab(index):
            terminal.custom_fgcolor = gtk.gdk.color_parse(fgcolor)

    def execute_command(self, command, tab=None):
        """Execute the `command' in the `tab'. If tab is None, the
        command will be executed in the currently selected
        tab. Command should end with '\n', otherwise it will be
        appended to the string.
        """
        if not self.notebook.has_term():
            self.add_tab()

        if command[-1] != '\n':
            command += '\n'

        index = self.notebook.get_current_page()
        index = tab or self.notebook.get_current_page()
        for terminal in self.notebook.get_terminals_for_tab(index):
            terminal.feed_child(command)
            break

    def on_resizer_drag(self, widget, event):
        """Method that handles the resize drag. It does not actuall
        moves the main window. It just set the new window size in
        gconf.
        """
        (x, y), mod = event.device.get_state(widget.window)
        if 'GDK_BUTTON1_MASK' not in mod.value_names:
            return

        screen = self.window.get_screen()
        x, y, _ = screen.get_root_window().get_pointer()
        screen_no = screen.get_monitor_at_point(x, y)

        max_height = screen.get_monitor_geometry(screen_no).height
        percent = y / (max_height / 100)

        if percent < 1:
            percent = 1

        window_rect = self.window.get_size()
        self.window.resize(window_rect[0], y)
        # self.client.set_int(KEY('/general/window_height'), int(percent))

    def on_window_losefocus(self, window, event):
        """Hides terminal main window when it loses the focus and if
        the window_losefocus gconf variable is True.
        """
        if self.disable_losefocus_hiding or self.showing_context_menu:
            return

        if self.prompt_dialog is not None:
            return

        if self.preventHide:
            return

        value = self.client.get_bool(KEY('/general/window_losefocus'))
        visible = window.get_property('visible')
        if value and visible:
            self.losefocus_time = \
                gtk.gdk.x11_get_server_time(self.window.window)
            self.hide()

    def show_menu(self, status_icon, button, activate_time):
        """Show the tray icon menu.
        """
        menu = self.get_widget('tray-menu')
        menu.popup(None, None, gtk.status_icon_position_menu,
                   button, activate_time, status_icon)

    def show_context_menu(self, terminal, event):
        """Show the context menu, only with a right click on a vte
        Terminal.
        """
        if event.button != 3:
            return False

        self.showing_context_menu = True

        guake_clipboard = gtk.clipboard_get()
        if not guake_clipboard.wait_is_text_available():
            self.get_widget('context_paste').set_sensitive(False)
        else:
            self.get_widget('context_paste').set_sensitive(True)

        current_selection = ''
        current_term = self.notebook.get_current_terminal()
        if current_term.get_has_selection():
            current_term.copy_clipboard()
            guake_clipboard = gtk.clipboard_get()
            current_selection = guake_clipboard.wait_for_text()
            if current_selection:
                current_selection.rstrip()

            if current_selection and len(current_selection) > 20:
                current_selection = current_selection[:17] + "..."

        self.get_widget('separator_search').set_visible(False)
        if current_selection:
            self.get_widget('context_search_on_web').set_label(
                _("Search on Web: '%s'") % current_selection)
            self.get_widget('context_search_on_web').set_visible(True)
            self.get_widget('separator_search').set_visible(True)
        else:
            self.get_widget('context_search_on_web').set_label(_("Search on Web (no selection)"))
            self.get_widget('context_search_on_web').set_visible(False)

        link = self.getCurrentTerminalLinkUnderCursor()
        if link:
            self.get_widget('context_browse_on_web').set_visible(True)
            self.get_widget('context_browse_on_web').set_label(_("Open Link: {}".format(link)))
            self.get_widget('separator_search').set_visible(True)
        else:
            self.get_widget('context_browse_on_web').set_label(_("Open Link..."))
            self.get_widget('context_browse_on_web').set_visible(False)

        context_menu = self.get_widget('context-menu')
        context_menu.popup(None, None, None, 3, gtk.get_current_event_time())
        return True

    def show_rename_current_tab_dialog(self, target, event):
        """On double-click over a tab, show the rename dialog.
        """
        if event.button == 1:
            if event.type == gtk.gdk._2BUTTON_PRESS:
                self.accel_rename_current_tab()
                self.set_terminal_focus()
                self.selected_tab.pressed()
                return

    def show_tab_menu(self, target, event):
        """Shows the tab menu with a right click. After that, the
        focus come back to the terminal.
        """
        if event.button == 3:
            self.showing_context_menu = True
            self.selected_tab = target
            menu = self.get_widget('tab-menu')
            menu.popup(None, None, None, 3, event.get_time())
        self.set_terminal_focus()

    def show_about(self, *args):
        """Hides the main window and creates an instance of the About
        Dialog.
        """
        self.hide()
        AboutDialog()

    def show_prefs(self, *args):
        """Hides the main window and creates an instance of the
        Preferences window.
        """
        self.hide()
        PrefsDialog().show()

    def is_iconified(self):
        if self.window.window:
            cur_state = int(self.window.window.get_state())
            return bool(cur_state & GDK_WINDOW_STATE_ICONIFIED)
        return False

    def window_event(self, window, event):
        state = event.new_window_state
        log.debug("Received window state event. Window: %s. Event: %s", window, state)

    def show_hide(self, *args):
        """Toggles the main window visibility
        """
        if self.forceHide:
            self.forceHide = False
            return

        if self.preventHide:
            return

        event_time = self.hotkeys.get_current_event_time()

        if self.losefocus_time and \
                self.losefocus_time >= event_time and \
                (self.losefocus_time - event_time) < 10:
            self.losefocus_time = 0
            return

        # limit rate at which the visibility can be toggled.
        if self.prev_showhide_time and event_time and \
                (event_time - self.prev_showhide_time) < 65:
            return
        self.prev_showhide_time = event_time

        log.debug("Window display")
        if self.window.window:
            cur_state = int(self.window.window.get_state())
            is_sticky = bool(cur_state & GDK_WINDOW_STATE_STICKY)
            is_withdrawn = bool(cur_state & GDK_WINDOW_STATE_WITHDRAWN)
            is_above = bool(cur_state & GDK_WINDOW_STATE_ABOVE)
            is_iconified = self.is_iconified()
            log.debug("gtk.gdk.WindowState = %s", cur_state)
            log.debug("GDK_WINDOW_STATE_STICKY? %s", is_sticky)
            log.debug("GDK_WINDOW_STATE_WITHDRAWN? %s", is_withdrawn)
            log.debug("GDK_WINDOW_STATE_ABOVE? %s", is_above)
            log.debug("GDK_WINDOW_STATE_ICONIFIED? %s", is_iconified)

        if not self.window.get_property('visible'):
            log.debug("Showing the terminal")
            self.show()
            self.set_terminal_focus()
            return

        # Disable the focus_if_open feature
        #  - if doesn't work seamlessly on all system
        #  - self.window.window.get_state doesn't provides us the right information on all
        #    systems, especially on MATE/XFCE
        #
        # if self.client.get_bool(KEY('/general/focus_if_open')):
        #     restore_focus = False
        #     if self.window.window:
        #         state = int(self.window.window.get_state())
        #         if ((state & GDK_WINDOW_STATE_STICKY or
        #                 state & GDK_WINDOW_STATE_WITHDRAWN
        #              )):
        #             restore_focus = True
        #     else:
        #         restore_focus = True
        # if not self.hidden:
        # restore_focus = True
        #     if restore_focus:
        #         log.debug("DBG: Restoring the focus to the terminal")
        #         self.hide()
        #         self.show()
        #         self.window.window.focus()
        #         self.set_terminal_focus()
        #         return

        log.debug("hiding the terminal")
        self.hide()

    def show(self):
        """Shows the main window and grabs the focus on it.
        """
        self.hidden = False

        # setting window in all desktops
        if not self.is_fullscreen:
            window_rect = self.set_final_window_rect()
        self.get_widget('window-root').stick()

        # add tab must be called before window.show to avoid a
        # blank screen before adding the tab.
        if not self.notebook.has_term():
            self.add_tab()

        self.window.set_keep_below(False)
        self.window.show_all()

        if self.selected_color is None:
            self.selected_color = getattr(self.window.get_style(), "light")[int(gtk.STATE_SELECTED)]

            # Reapply the tab color to all button in the tab list, since at least one don't have the
            # select color set. This needs to happen AFTER the first show_all, since before the gtk
            # has not loaded the right colors yet.
            for tab in self.tabs.get_children():
                if isinstance(tab, gtk.RadioButton):
                    tab.modify_bg(gtk.STATE_ACTIVE, gtk.gdk.Color(str(self.selected_color)))

        # this work arround an issue in fluxbox
        if not self.is_fullscreen:
            self.window.move(window_rect.x, window_rect.y)
            self.client.notify(KEY('/general/window_height'))

        try:
            # does it work in other gtk backends
            time = gtk.gdk.x11_get_server_time(self.window.window)
        except AttributeError:
            time = 0

        # When minized, the window manager seems to refuse to resume
        log.debug("self.window: %s. Dir=%s", type(self.window), dir(self.window))
        # is_iconified = self.is_iconified()
        # if is_iconified:
        #     log.debug("Is iconified. Ubuntu Trick => removing skip_taskbar_hint and skip_pager_hint "
        #           "so deiconify can work!")
        #     self.get_widget('window-root').set_skip_taskbar_hint(False)
        #     self.get_widget('window-root').set_skip_pager_hint(False)
        #     self.get_widget('window-root').set_urgency_hint(False)
        #     log.debug("get_skip_taskbar_hint: {}".format(
        #         self.get_widget('window-root').get_skip_taskbar_hint()))
        #     log.debug("get_skip_pager_hint: {}".format(
        #         self.get_widget('window-root').get_skip_pager_hint()))
        #     log.debug("get_urgency_hint: {}".format(
        #         self.get_widget('window-root').get_urgency_hint()))
        #     glib.timeout_add_seconds(1, lambda: self.timeout_restore(time))

        log.debug("order to present and deiconify")
        self.window.present()
        self.window.deiconify()
        self.window.window.deiconify()
        self.window.window.show()
        self.window.window.focus(time)
        self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DOCK)
        self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_NORMAL)

        # log.debug("Restoring skip_taskbar_hint and skip_pager_hint")
        # if is_iconified:
        #     self.get_widget('window-root').set_skip_taskbar_hint(False)
        #     self.get_widget('window-root').set_skip_pager_hint(False)
        #     self.get_widget('window-root').set_urgency_hint(False)

        # This is here because vte color configuration works only after the widget is shown.
        self.client.notify(KEY('/style/font/color'))
        self.client.notify(KEY('/style/background/color'))

    def hide_from_remote(self):
        """
        Hides the main window of the terminal and sets the visible
        flag to False.
        """
        log.debug("hide from remote")
        self.forceHide = True
        self.hide()

    def show_from_remote(self):
        """Show the main window of the terminal and sets the visible
        flag to False.
        """
        log.debug("show from remote")
        self.forceHide = True
        self.show()

    def hide(self):
        """Hides the main window of the terminal and sets the visible
        flag to False.
        """
        if self.preventHide:
            return
        self.hidden = True
        self.get_widget('window-root').unstick()
        self.window.hide()  # Don't use hide_all here!

    def get_final_window_monitor(self):
        """Gets the final screen number for the main window of guake.
        """

        screen = self.window.get_screen()

        # fetch settings
        use_mouse = self.client.get_bool(KEY('/general/mouse_display'))
        dest_screen = self.client.get_int(KEY('/general/display_n'))

        if use_mouse:
            x, y, _ = screen.get_root_window().get_pointer()
            dest_screen = screen.get_monitor_at_point(x, y)

        # If Guake is configured to use a screen that is not currently attached,
        # default to 'primary display' option.
        n_screens = screen.get_n_monitors()
        if dest_screen > n_screens - 1:
            self.client.set_bool(KEY('/general/mouse_display'), False)
            self.client.set_int(KEY('/general/display_n'), dest_screen)
            dest_screen = screen.get_primary_monitor()

        # Use primary display if configured
        if dest_screen == ALWAYS_ON_PRIMARY:
            dest_screen = screen.get_primary_monitor()

        return dest_screen

    def is_using_unity(self):
        linux_distrib = platform.linux_distribution()
        if linux_distrib[0].lower() != "ubuntu":
            return False

        # http://askubuntu.com/questions/70296/is-there-an-environment-variable-that-is-set-for-unity
        if float(linux_distrib[1]) - 0.01 < 11.10:
            if os.environ.get('DESKTOP_SESSION').lower() == "gnome".lower():
                log.debug("Unity detected")
                return True
        else:
            if os.environ.get('XDG_CURRENT_DESKTOP').lower() == "unity".lower():
                log.debug("Unity detected")
                return True
        return False

    def set_final_window_rect(self):
        """Sets the final size and location of the main window of guake. The height
        is the window_height property, width is window_width and the
        horizontal alignment is given by window_alignment.
        """

        # fetch settings
        height_percents = self.client.get_float(KEY('/general/window_height_f'))
        if not height_percents:
            height_percents = self.client.get_int(KEY('/general/window_height'))

        width_percents = self.client.get_float(KEY('/general/window_width_f'))
        if not width_percents:
            width_percents = self.client.get_int(KEY('/general/window_width'))
        halignment = self.client.get_int(KEY('/general/window_halignment'))
        valignment = self.client.get_int(KEY('/general/window_valignment'))

        # log.debug("height_percents", height_percents)
        # log.debug("width_percents", width_percents)
        # log.debug("halignment", halignment)
        # log.debug("valignment", valignment)

        # get the rectangle just from the destination monitor
        screen = self.window.get_screen()
        monitor = self.get_final_window_monitor()
        window_rect = screen.get_monitor_geometry(monitor)

        if self.is_using_unity():

            # For Ubuntu 12.10 and above, try to use dconf:
            # see if unity dock is hiden => unity_hide
            # and the width of unity dock. => unity_dock
            found = False
            unity_hide = 0
            # float() conversion might mess things up. Add 0.01 so the comparison will always be
            # valid, even in case of float("10.10") = 10.099999999999999
            if float(platform.linux_distribution()[1]) + 0.01 >= 12.10:
                try:
                    unity_hide = int(subprocess.check_output(
                        ['/usr/bin/dconf', 'read',
                         '/org/compiz/profiles/unity/plugins/unityshell/launcher-hide-mode']))
                    unity_dock = int(subprocess.check_output(
                        ['/usr/bin/dconf', 'read',
                         '/org/compiz/profiles/unity/plugins/unityshell/icon-size']))
                    found = True
                except:
                    # in case of error, just ignore it, 'found' will not be set to True and so
                    # we execute the fallback
                    pass
            if not found:
                # Fallback: try to bet from gconf
                unity_hide = self.client.get_int(KEY(
                    '/apps/compiz-1/plugins/unityshell/screen0/options/launcher_hide_mode'))
                unity_icon_size = self.client.get_int(KEY(
                    '/apps/compiz-1/plugins/unityshell/screen0/options/icon_size'))
                unity_dock = unity_icon_size + 17

            # launcher_hide_mode = 1 => autohide
            if unity_hide != 1:
                log.debug("correcting window width because of launcher width %s "
                          "(from %s to %s)",
                          unity_dock,
                          window_rect.width,
                          window_rect.width - unity_dock)

                window_rect.width = window_rect.width - unity_dock

        total_width = window_rect.width
        total_height = window_rect.height

        # log.debug("total_width", total_width)
        # log.debug("total_height", total_height)

        window_rect.height = window_rect.height * height_percents / 100
        window_rect.width = window_rect.width * width_percents / 100

        # log.debug("window_rect.x", window_rect.x)
        # log.debug("window_rect.y", window_rect.y)
        # log.debug("window_rect.height", window_rect.height)
        # log.debug("window_rect.width", window_rect.width)

        if window_rect.width < total_width:
            if halignment == ALIGN_CENTER:
                # log.debug("aligning to center!")
                window_rect.x += (total_width - window_rect.width) / 2
            elif halignment == ALIGN_LEFT:
                # log.debug("aligning to left!")
                window_rect.x += 0
            elif halignment == ALIGN_RIGHT:
                # log.debug("aligning to right!")
                window_rect.x += total_width - window_rect.width
        if window_rect.height < total_height:
            if valignment == ALIGN_BOTTOM:
                window_rect.y += (total_height - window_rect.height)

        self.window.resize(window_rect.width, window_rect.height)
        self.window.move(window_rect.x, window_rect.y)
        # log.debug("Moving/Resizing to: window_rect", window_rect)

        return window_rect

    # -- configuration --

    def load_config(self):
        """"Just a proxy for all the configuration stuff.
        """
        self.client.notify(KEY('/general/use_trayicon'))
        self.client.notify(KEY('/general/prompt_on_quit'))
        self.client.notify(KEY('/general/prompt_on_close_tab'))
        self.client.notify(KEY('/general/window_tabbar'))
        self.client.notify(KEY('/general/mouse_display'))
        self.client.notify(KEY('/general/display_n'))
        self.client.notify(KEY('/general/window_ontop'))
        if not self.is_fullscreen:
            self.client.notify(KEY('/general/window_height'))
            self.client.notify(KEY('/general/window_width'))
        self.client.notify(KEY('/general/use_scrollbar'))
        self.client.notify(KEY('/general/history_size'))
        self.client.notify(KEY('/general/show_resizer'))
        self.client.notify(KEY('/general/use_vte_titles'))
        self.client.notify(KEY('/general/quick_open_enable'))
        self.client.notify(KEY('/general/quick_open_command_line'))
        self.client.notify(KEY('/style/cursor_shape'))
        self.client.notify(KEY('/style/font/style'))
        self.client.notify(KEY('/style/font/color'))
        self.client.notify(KEY('/style/font/palette'))
        self.client.notify(KEY('/style/font/palette_name'))
        self.client.notify(KEY('/style/background/color'))
        self.client.notify(KEY('/style/background/image'))
        self.client.notify(KEY('/style/background/transparency'))
        self.client.notify(KEY('/general/use_default_font'))
        self.client.notify(KEY('/general/use_palette_font_and_background_color'))
        self.client.notify(KEY('/general/compat_backspace'))
        self.client.notify(KEY('/general/compat_delete'))

    def run_quit_dialog(self, procs, tab):
        """Run the "are you sure" dialog for closing a tab, or quitting Guake
        """
        # Stop an open "close tab" dialog from obstructing a quit
        if self.prompt_dialog is not None:
            self.prompt_dialog.destroy()
        self.prompt_dialog = PromptQuitDialog(self.window, procs, tab)
        response = self.prompt_dialog.run() == gtk.RESPONSE_YES
        self.prompt_dialog.destroy()
        self.prompt_dialog = None
        # Keep Guake focussed after dismissing tab-close prompt
        if tab == -1:
            self.window.present()
        return response

    def accel_quit(self, *args):
        """Callback to prompt the user whether to quit Guake or not.
        """
        procs = self.notebook.get_running_fg_processes()
        tabs = self.notebook.get_tab_count()
        prompt_cfg = self.client.get_bool(KEY('/general/prompt_on_quit'))
        prompt_tab_cfg = self.client.get_int(KEY('/general/prompt_on_close_tab'))
        # "Prompt on tab close" config overrides "prompt on quit" config
        if prompt_cfg or (prompt_tab_cfg == 1 and procs > 0) or (prompt_tab_cfg == 2):
            if self.run_quit_dialog(procs, tabs):
                gtk.main_quit()
        else:
            gtk.main_quit()

    def accel_zoom_in(self, *args):
        """Callback to zoom in.
        """
        for term in self.notebook.iter_terminals():
            term.increase_font_size()
        return True

    def accel_zoom_out(self, *args):
        """Callback to zoom out.
        """
        for term in self.notebook.iter_terminals():
            term.decrease_font_size()
        return True

    def accel_increase_height(self, *args):
        """Callback to increase height.
        """
        try:
            height = self.client.get_float(KEY('/general/window_height'))
        except:
            height = self.client.get_int(KEY('/general/window_height'))

        self.client.set_int(KEY('/general/window_height'), int(height) + 2)
        return True

    def accel_decrease_height(self, *args):
        """Callback to decrease height.
        """
        try:
            height = self.client.get_float(KEY('/general/window_height'))
        except:
            height = self.client.get_int(KEY('/general/window_height'))

        self.client.set_int(KEY('/general/window_height'), int(height) - 2)
        return True

    def accel_increase_transparency(self, *args):
        """Callback to increase transparency.
        """
        transparency = self.client.get_int(KEY('/style/background/transparency'))
        self.client.set_int(KEY('/style/background/transparency'), int(transparency) + 2)
        return True

    def accel_decrease_transparency(self, *args):
        """Callback to decrease transparency.
        """
        transparency = self.client.get_int(KEY('/style/background/transparency'))
        self.client.set_int(KEY('/style/background/transparency'), int(transparency) - 2)
        return True

    def accel_add(self, *args):
        """Callback to add a new tab. Called by the accel key.
        """
        self.add_tab()
        return True

    def accel_prev(self, *args):
        """Callback to go to the previous tab. Called by the accel key.
        """
        if self.notebook.get_current_page() == 0:
            self.notebook.set_current_page(self.notebook.get_n_pages() - 1)
        else:
            self.notebook.prev_page()
        return True

    def accel_next(self, *args):
        """Callback to go to the next tab. Called by the accel key.
        """
        if self.notebook.get_current_page() + 1 == self.notebook.get_n_pages():
            self.notebook.set_current_page(0)
        else:
            self.notebook.next_page()
        return True

    def accel_move_tab_left(self, *args):
        """ Callback to move a tab to the left """
        pos = self.notebook.get_current_page()
        if pos != 0:
            self.move_tab(pos, pos - 1)
        return True

    def accel_move_tab_right(self, *args):
        """ Callback to move a tab to the right """
        pos = self.notebook.get_current_page()
        if pos != self.notebook.get_n_pages() - 1:
            self.move_tab(pos, pos + 1)
        return True

    def gen_accel_switch_tabN(self, N):
        """Generates callback (which called by accel key) to go to the Nth tab.
        """
        def callback(*args):
            if N >= 0 and N < self.notebook.get_n_pages():
                self.notebook.set_current_page(N)
            return True

        return callback

    def accel_rename_current_tab(self, *args):
        """Callback to show the rename tab dialog. Called by the accel
        key.
        """
        pagepos = self.notebook.get_current_page()
        self.selected_tab = self.tabs.get_children()[pagepos]
        self.on_rename_current_tab_activate()
        return True

    def accel_copy_clipboard(self, *args):
        """Callback to copy text in the shown terminal. Called by the
        accel key.
        """
        current_term = self.notebook.get_current_terminal()

        if current_term.get_has_selection():
            current_term.copy_clipboard()
        elif current_term.matched_value:
            guake_clipboard = gtk.clipboard_get()
            guake_clipboard.set_text(current_term.matched_value)

        return True

    def accel_paste_clipboard(self, *args):
        """Callback to paste text in the shown terminal. Called by the
        accel key.
        """
        self.notebook.get_current_terminal().paste_clipboard()
        return True

    def accel_toggle_fullscreen(self, *args):
        """Callback toggle the fullscreen status of the main
        window. Called by the accel key.
        """
        if not self.is_fullscreen:
            self.fullscreen()
        else:
            self.unfullscreen()
        return True

    def accel_toggle_hide_on_lose_focus(self, *args):
        """Callback toggle whether the window should hide when it loses
        focus. Called by the accel key.
        """
        # use temporary setting -- don't change conf key
        self.disable_losefocus_hiding = not self.disable_losefocus_hiding
        return True

    def fullscreen(self):
        self.window.fullscreen()
        self.is_fullscreen = True

        # The resizer widget really don't need to be shown in
        # fullscreen mode, but tabbar will only be shown if a
        # hidden gconf key is false.
        self.resizer.hide()
        if not self.client.get_bool(KEY('/general/toolbar_visible_in_fullscreen')):
            self.toolbar.hide()

    def unfullscreen(self):
        self.set_final_window_rect()
        self.window.unfullscreen()
        self.is_fullscreen = False

        # making sure that tabbar and resizer will come back to
        # their default state.
        self.client.notify(KEY('/general/window_tabbar'))
        self.client.notify(KEY('/general/show_resizer'))

        # make sure the window size is correct after returning
        # from fullscreen. broken on old compiz/metacity versions :C
        self.client.notify(KEY('/general/window_height'))

    # -- callbacks --

    def on_terminal_exited(self, term, widget):
        """When a terminal is closed, shell process should be killed,
        this is the method that does that, or, at least calls
        `delete_tab' method to do the work.
        """
        log.debug("Terminal exited: %s", term)
        if libutempter is not None:
            libutempter.utempter_remove_record(term.get_pty())
        self.delete_tab(self.notebook.page_num(widget), kill=False)

    def on_terminal_title_changed(self, vte, box):
        use_them = self.client.get_bool(KEY("/general/use_vte_titles"))
        if not use_them:
            return
        page = self.notebook.page_num(box)
        tab = self.tabs.get_children()[page]
        # if tab has been renamed by user, don't override.
        if not getattr(tab, 'custom_label_set', False):
            tab.set_label(vte.get_window_title())

    def on_rename_current_tab_activate(self, *args):
        """Shows a dialog to rename the current tab.
        """
        entry = gtk.Entry()
        entry.set_text(self.selected_tab.get_label())
        entry.set_property('can-default', True)
        entry.show()

        vbox = gtk.VBox()
        vbox.set_border_width(6)
        vbox.show()

        dialog = gtk.Dialog(_("Rename tab"),
                            self.window,
                            gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
                            (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT,
                             gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))

        dialog.set_size_request(300, -1)
        dialog.vbox.pack_start(vbox)
        dialog.set_border_width(4)
        dialog.set_has_separator(False)
        dialog.set_default_response(gtk.RESPONSE_ACCEPT)
        dialog.add_action_widget(entry, gtk.RESPONSE_ACCEPT)
        entry.reparent(vbox)

        # don't hide on lose focus until the rename is finished
        current_hide_setting = self.disable_losefocus_hiding
        self.disable_losefocus_hiding = True
        response = dialog.run()
        self.disable_losefocus_hiding = current_hide_setting

        if response == gtk.RESPONSE_ACCEPT:
            new_text = entry.get_text()
            self.selected_tab.set_label(new_text)
            # if user sets empty name, consider he wants default behavior.
            setattr(self.selected_tab, 'custom_label_set', bool(new_text))
            # trigger titling handler in case that custom label has been reset
            current_vte = self.notebook.get_current_terminal()
            current_vte.emit('window-title-changed')

        dialog.destroy()
        self.set_terminal_focus()

    def on_close_activate(self, *args):
        """Tab context menu close handler
        """
        tabs = self.tabs.get_children()
        pagepos = tabs.index(self.selected_tab)
        self.delete_tab(pagepos)

    def on_drag_data_received(self, widget, context,
                              x, y,
                              selection,
                              target,
                              timestamp,
                              box):
        droppeduris = selection.get_uris()

        # url-unquote the list, strip file:// schemes, handle .desktop-s
        pathlist = []
        app = None
        for uri in droppeduris:
            scheme, _, path, _, _ = urlsplit(uri)

            if scheme != "file":
                pathlist.append(uri)
            else:
                filename = url2pathname(path)

                desktopentry = DesktopEntry()
                try:
                    desktopentry.parse(filename)
                except xdg.Exceptions.ParsingError:
                    pathlist.append(filename)
                    continue

                if desktopentry.getType() == 'Link':
                    pathlist.append(desktopentry.getURL())

                if desktopentry.getType() == 'Application':
                    app = desktopentry.getExec()

        if app and len(droppeduris) == 1:
            text = app
        else:
            text = str.join("", (shell_quote(path) + " " for path in pathlist))

        box.terminal.feed_child(text)
        return True

    # -- tab related functions --

    def close_tab(self, *args):
        """Closes the current tab.
        """
        pagepos = self.notebook.get_current_page()
        self.delete_tab(pagepos)

    def rename_tab(self, tab_index, new_text):
        """Rename an already added tab by its index.
        """
        try:
            tab = self.tabs.get_children()[tab_index]
        except IndexError:
            pass
        else:
            tab.set_label(new_text)
            setattr(tab, 'custom_label_set', new_text != "-")
            terminals = self.notebook.get_terminals_for_tab(tab_index)
            for current_vte in terminals:
                current_vte.emit('window-title-changed')

    def rename_current_tab(self, new_text):
        """Sets the `self.selected_tab' var with the selected radio
        button and change its label to `new_text'.
        """
        pagepos = self.notebook.get_current_page()
        self.selected_tab = self.tabs.get_children()[pagepos]
        self.selected_tab.set_label(new_text)

        # it's hard to pass an empty string as a command line argument,
        # so we'll interpret single dash "-" as a "reset custom title" request
        setattr(self.selected_tab, 'custom_label_set', new_text != "-")

        # trigger titling handler in case that custom label has been reset
        current_vte = self.notebook.get_current_terminal()
        current_vte.emit('window-title-changed')
        self.notebook.get_current_terminal().grab_focus()

    def get_current_dir(self):
        """Gets the working directory of the current tab to create a
        new one in the same dir.
        """
        active_terminal = self.notebook.get_current_terminal()
        directory = os.path.expanduser('~')
        if active_terminal:
            cwd = os.readlink("/proc/%d/cwd" % active_terminal.get_pid())
            if os.path.exists(cwd):
                directory = cwd
        return directory

    def get_fork_params(self, default_params=None):
        """Return all parameters to be passed to the fork_command
        method of a vte terminal. Params returned can be expanded by
        the `params' parameter that receive a dictionary.
        """
        # use dictionary to pass named params to work around command
        # parameter in fork_command not accepting None as argument.
        # When we pass None as command, vte starts the default user
        # shell.
        params = {}

        shell = self.client.get_string(KEY('/general/default_shell'))
        if shell and os.path.exists(shell):
            params['command'] = shell

        login_shell = self.client.get_bool(KEY('/general/use_login_shell'))
        if login_shell:
            params['argv'] = ['-']

        if self.client.get_bool(KEY('/general/open_tab_cwd')):
            params['directory'] = self.get_current_dir()
        params['loglastlog'] = login_shell

        # Leting caller change/add values to fork params.
        if default_params:
            params.update(default_params)

        # Environment variables are not actually parameters but they
        # need to be set before calling terminal.fork_command()
        # method. So I found this place good to do it.
        self.update_proxy_vars()
        return params

    def update_proxy_vars(self):
        """This method updates http{s,}_proxy environment variables
        with values found in gconf.
        """
        proxy = '/system/http_proxy/'
        if self.client.get_bool(proxy + 'use_http_proxy'):
            host = self.client.get_string(proxy + 'host')
            port = self.client.get_int(proxy + 'port')
            if self.client.get_bool(proxy + 'use_same_proxy'):
                ssl_host = host
                ssl_port = port
            else:
                ssl_host = self.client.get_string('/system/proxy/secure_host')
                ssl_port = self.client.get_int('/system/proxy/secure_port')

            if self.client.get_bool(proxy + 'use_authentication'):
                auth_user = self.client.get_string(
                    proxy + 'authentication_user')
                auth_pass = self.client.get_string(
                    proxy + 'authentication_password')
                auth_pass = quote_plus(auth_pass, '')
                os.environ['http_proxy'] = 'http://%s:%s@%s:%d' % (
                    auth_user, auth_pass, host, port)
                os.environ['https_proxy'] = 'http://%s:%s@%s:%d' % (
                    auth_user, auth_pass, ssl_host, ssl_port)
            else:
                os.environ['http_proxy'] = 'http://%s:%d' % (host, port)
                os.environ['https_proxy'] = 'http://%s:%d' % (
                    ssl_host, ssl_port)

    def add_tab(self, directory=None):
        """Adds a new tab to the terminal notebook.
        """
        box = GuakeTerminalBox()
        box.terminal.grab_focus()
        box.terminal.drag_dest_set(gtk.DEST_DEFAULT_MOTION |
                                   gtk.DEST_DEFAULT_DROP |
                                   gtk.DEST_DEFAULT_HIGHLIGHT,
                                   [('text/uri-list', gtk.TARGET_OTHER_APP, 0)],
                                   gtk.gdk.ACTION_COPY
                                   )
        box.terminal.connect('button-press-event', self.show_context_menu)
        box.terminal.connect('child-exited', self.on_terminal_exited, box)
        box.terminal.connect('window-title-changed',
                             self.on_terminal_title_changed, box)
        box.terminal.connect('drag-data-received',
                             self.on_drag_data_received,
                             box)

        # -- Ubuntu has a patch to libvte which disables mouse scrolling in apps
        # -- like vim and less by default. If this is the case, enable it back.
        if hasattr(box.terminal, "set_alternate_screen_scroll"):
            box.terminal.set_alternate_screen_scroll(True)

        box.show()

        self.notebook.append_tab(box.terminal)

        # We can choose the directory to vte launch. It is important
        # to be used by dbus interface. I'm testing if directory is a
        # string because when binded to a signal, the first param can
        # be a button not a directory.
        default_params = {}
        if isinstance(directory, basestring):
            default_params['directory'] = directory

        final_params = self.get_fork_params(default_params)
        pid = box.terminal.fork_command(**final_params)
        if libutempter is not None:
            # After the fork_command we add this new tty to utmp !
            libutempter.utempter_add_record(box.terminal.get_pty(), os.uname()[1])
        box.terminal.pid = pid

        # Adding a new radio button to the tabbar
        label = box.terminal.get_window_title() or _("Terminal")
        tabs = self.tabs.get_children()
        parent = tabs and tabs[0] or None

        bnt = gtk.RadioButton(group=parent, label=label, use_underline=False)
        bnt.set_property('can-focus', False)
        bnt.set_property('draw-indicator', False)
        bnt.connect('button-press-event', self.show_tab_menu)
        bnt.connect('button-press-event', self.show_rename_current_tab_dialog)
        bnt.connect('clicked',
                    lambda *x: self.notebook.set_current_page(
                        self.notebook.page_num(box)
                    ))
        if self.selected_color is not None:
            bnt.modify_bg(gtk.STATE_ACTIVE, gtk.gdk.Color(str(self.selected_color)))
        drag_drop_type = ("text/plain", gtk.TARGET_SAME_APP, 80)
        bnt.drag_dest_set(gtk.DEST_DEFAULT_ALL, [drag_drop_type], gtk.gdk.ACTION_MOVE)
        bnt.connect("drag_data_received", self.on_drop_tab)
        bnt.drag_source_set(gtk.gdk.BUTTON1_MASK, [drag_drop_type], gtk.gdk.ACTION_MOVE)
        bnt.connect("drag_data_get", self.on_drag_tab)
        bnt.show()

        self.tabs.pack_start(bnt, expand=False, padding=1)

        self.notebook.append_page(box, None)
        self.notebook.set_current_page(self.notebook.page_num(box))
        box.terminal.grab_focus()
        self.load_config()

        if self.is_fullscreen:
            self.fullscreen()

    def save_tab(self, directory=None):
        self.preventHide = True
        current_term = self.notebook.get_current_terminal()
        current_term.select_all()
        current_term.copy_clipboard()
        current_term.select_none()
        guake_clipboard = gtk.clipboard_get()
        current_selection = guake_clipboard.wait_for_text().rstrip()

        dialog = gtk.FileChooserDialog(_("Save to..."),
                                       self.window,
                                       gtk.FILE_CHOOSER_ACTION_SAVE,
                                       (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
                                        gtk.STOCK_OPEN, gtk.RESPONSE_OK))
        dialog.set_default_response(gtk.RESPONSE_OK)
        filter = gtk.FileFilter()
        filter.set_name(_("All files"))
        filter.add_pattern("*")
        dialog.add_filter(filter)

        filter = gtk.FileFilter()
        filter.set_name(_("Text and Logs"))
        filter.add_pattern("*.log")
        filter.add_pattern("*.txt")
        dialog.add_filter(filter)

        response = dialog.run()
        if response == gtk.RESPONSE_OK:
            filename = dialog.get_filename()
            with open(filename, "w") as f:
                f.write(current_selection)
        dialog.destroy()
        self.preventHide = False

    def find_tab(self, directory=None):
        log.debug("find")
        self.preventHide = True
        search_text = gtk.TextView()

        dialog = gtk.Dialog(_("Find"), self.window,
                            gtk.DIALOG_DESTROY_WITH_PARENT,
                            (_("Forward"), RESPONSE_FORWARD,
                             _("Backward"), RESPONSE_BACKWARD,
                             gtk.STOCK_CANCEL, gtk.RESPONSE_NONE))
        dialog.vbox.pack_end(search_text, True, True, 0)
        dialog.buffer = search_text.get_buffer()
        dialog.connect("response", self._dialog_response_callback)

        search_text.show()
        search_text.grab_focus()
        dialog.show_all()
        # Note: beware to reset preventHide when closing the find dialog

    def _dialog_response_callback(self, dialog, response_id):
        if (response_id != RESPONSE_FORWARD and
                response_id != RESPONSE_BACKWARD):
            dialog.destroy()
            self.preventHide = False
            return

        start, end = dialog.buffer.get_bounds()
        search_string = start.get_text(end)

        log.debug("Searching for %r %s\n",
                  search_string,
                  "forward" if response_id == RESPONSE_FORWARD else "backward")

        current_term = self.notebook.get_current_terminal()
        log.debug("type: %r", type(current_term))
        log.debug("dir: %r", dir(current_term))
        current_term.search_set_gregex()
        current_term.search_get_gregex()

        # buffer = self.text_view.get_buffer()
        # if response_id == RESPONSE_FORWARD:
        #     buffer.search_forward(search_string, self)
        # elif response_id == RESPONSE_BACKWARD:
        #     buffer.search_backward(search_string, self)

    def on_drag_tab(self, widget, context, selection, targetType, eventTime):
        tab_pos = self.tabs.get_children().index(widget)
        selection.set(selection.target, 32, str(tab_pos))

    def on_drop_tab(self, widget, context, x, y, selection, targetType, data):
        old_tab_pos = int(selection.get_text())
        new_tab_pos = self.tabs.get_children().index(widget)
        self.move_tab(old_tab_pos, new_tab_pos)

    def move_tab(self, old_tab_pos, new_tab_pos):
        self.notebook.reorder_child(self.notebook.get_nth_page(old_tab_pos), new_tab_pos)
        self.tabs.reorder_child(self.tabs.get_children()[old_tab_pos], new_tab_pos)
        self.notebook.set_current_page(new_tab_pos)

    def delete_tab(self, pagepos, kill=True):
        """This function will destroy the notebook page, terminal and
        tab widgets and will call the function to kill interpreter
        forked by vte.
        """
        # Run prompt if necessary
        procs = self.notebook.get_running_fg_processes_tab(pagepos)
        prompt_cfg = self.client.get_int(KEY('/general/prompt_on_close_tab'))
        if (prompt_cfg == 1 and procs > 0) or (prompt_cfg == 2):
            if not self.run_quit_dialog(procs, -1):
                return

        self.tabs.get_children()[pagepos].destroy()
        self.notebook.delete_tab(pagepos, kill=kill)

        if not self.notebook.has_term():
            self.hide()
            # avoiding the delay on next Guake show request
            self.add_tab()
        else:
            self.set_terminal_focus()

    def set_terminal_focus(self):
        """Grabs the focus on the current tab.
        """
        self.notebook.get_current_terminal().grab_focus()
        self.notebook.set_current_page(self.get_selected_tab())
        # Hack to fix "Not focused on openning if tab was moved" (#441)
        pos = self.get_selected_tab()
        self.select_tab(0)
        self.select_tab(pos)

    def select_current_tab(self, notebook, user_data, page):
        """When current self.notebook page is changed, the tab bar
        made with radio buttons must be updated and this method does
        this work.
        """
        self.tabs.get_children()[page].set_active(True)

    def select_tab(self, tab_index):
        """Select an already added tab by its index.
        """
        try:
            self.tabs.get_children()[tab_index].set_active(True)
            return tab_index
        except IndexError:
            pass

    def get_selected_tab(self):
        """return the selected tab index, it also set the
        self.selected_tab var.
        """
        pagepos = self.notebook.get_current_page()
        self.selected_tab = self.tabs.get_children()[pagepos]
        return pagepos

    def search_on_web(self, *args):
        """search on web the selected text
        """
        current_term = self.notebook.get_current_terminal()

        if current_term.get_has_selection():
            current_term.copy_clipboard()
            guake_clipboard = gtk.clipboard_get()
            search_query = guake_clipboard.wait_for_text()
            search_query = quote_plus(search_query)
            if search_query:
                search_url = "https://www.google.com/#q=%s&safe=off" % (search_query,)
                gtk.show_uri(current_term.window.get_screen(), search_url,
                             gtk.gdk.x11_get_server_time(current_term.window))
        return True

    def getCurrentTerminalLinkUnderCursor(self):
        current_term = self.notebook.get_current_terminal()
        l = current_term.found_link
        log.debug("Current link under cursor: %s", l)
        if l:
            return l

    def browse_on_web(self, *args):
        log.debug("browsing %s...", self.getCurrentTerminalLinkUnderCursor())
        self.notebook.get_current_terminal().browse_link_under_cursor()

    def set_tab_position(self, *args):
        if self.client.get_bool(KEY('/general/tab_ontop')):
            self.mainframe.reorder_child(self.notebook, 2)
        else:
            self.mainframe.reorder_child(self.notebook, 0)
        self.mainframe.pack_start(self.mainframe, expand=True, fill=True, padding=0)
