Exemplo n.º 1
0
class PrefsDialog(SimpleGladeApp):

    """The Guake Preferences dialog.
    """

    def __init__(self):
        """Setup the preferences dialog interface, loading images,
        adding filters to file choosers and connecting some signals.
        """
        super(PrefsDialog, self).__init__(gladefile('prefs.glade'),
                                          root='config-window')
        self.add_callbacks(PrefsCallbacks(self))

        self.client = gconf.client_get_default()

        # window cleanup handler
        self.get_widget('config-window').connect('destroy', self.on_destroy)

        # setting evtbox title bg
        eventbox = self.get_widget('eventbox-title')
        eventbox.modify_bg(gtk.STATE_NORMAL,
                           eventbox.get_colormap().alloc_color("#ffffff"))

        # images
        ipath = pixmapfile('guake-notification.png')
        self.get_widget('image_logo').set_from_file(ipath)
        ipath = pixmapfile('quick-open.png')
        self.get_widget('image_quick_open').set_from_file(ipath)

        # the first position in tree will store the keybinding path in gconf,
        # and the user doesn't worry with this, let's hide that =D
        model = gtk.TreeStore(str, str, object, bool)
        treeview = self.get_widget('treeview-keys')
        treeview.set_model(model)
        treeview.set_rules_hint(True)
        treeview.connect('button-press-event', self.start_editing)

        renderer = gtk.CellRendererText()
        column = gtk.TreeViewColumn('keypath', renderer, text=0)
        column.set_visible(False)
        treeview.append_column(column)

        renderer = gtk.CellRendererText()
        column = gtk.TreeViewColumn(_('Action'), renderer, text=1)
        column.set_property('expand', True)
        treeview.append_column(column)

        renderer = gtk.CellRendererAccel()
        renderer.set_property('editable', True)

        renderer.connect('accel-edited', self.on_key_edited, model)
        renderer.connect('accel-cleared', self.on_key_cleared, model)

        column = gtk.TreeViewColumn(_('Shortcut'), renderer)
        column.set_cell_data_func(renderer, self.cell_data_func)
        column.set_property('expand', False)
        treeview.append_column(column)

        self.demo_terminal = GuakeTerminal()
        demo_terminal_box = self.get_widget('demo_terminal_box')
        demo_terminal_box.add(self.demo_terminal)

        default_params = {}
        pid = self.demo_terminal.fork_command(**default_params)
        self.demo_terminal.pid = pid

        self.populate_shell_combo()
        self.populate_keys_tree()
        self.populate_display_n()
        self.load_configs()
        self.get_widget('config-window').hide()

        # Preview when selecting a bgimage
        self.selection_preview = gtk.Image()
        self.file_filter = gtk.FileFilter()
        self.file_filter.add_pattern("*.jpg")
        self.file_filter.add_pattern("*.png")
        self.file_filter.add_pattern("*.svg")
        self.file_filter.add_pattern("*.jpeg")
        self.bgfilechooser = self.get_widget('background_image')
        self.bgfilechooser.set_preview_widget(self.selection_preview)
        self.bgfilechooser.set_filter(self.file_filter)
        self.bgfilechooser.connect('update-preview', self.update_preview,
                                   self.selection_preview)

    def show(self):
        """Calls the main window show_all method and presents the
        window in the desktop.
        """
        self.get_widget('config-window').show_all()
        self.get_widget('config-window').present()

    def hide(self):
        """Calls the main window hide function.
        """
        self.get_widget('config-window').hide()

    def on_destroy(self, window):
        self.demo_terminal.kill()
        self.demo_terminal.destroy()

    def update_preview(self, file_chooser, preview):
        """Used by filechooser to preview image files
        """
        filename = file_chooser.get_preview_filename()
        if filename and os.path.isfile(filename or ''):
            try:
                mkpb = gtk.gdk.pixbuf_new_from_file_at_size
                pixbuf = mkpb(filename, 256, 256)
                preview.set_from_pixbuf(pixbuf)
                file_chooser.set_preview_widget_active(True)
            except gobject.GError:
                # this exception is raised when user chooses a
                # non-image file or a directory
                warnings.warn('File %s is not an image' % filename)
        else:
            file_chooser.set_preview_widget_active(False)

    def toggle_prompt_on_quit_sensitivity(self, combo):
        """If toggle_on_close_tabs is set to 2 (Always), prompt_on_quit has no
        effect.
        """
        self.get_widget('prompt_on_quit').set_sensitive(combo.get_active() != 2)

    def toggle_style_sensitivity(self, chk):
        """If the user chooses to use the gnome default font
        configuration it means that he will not be able to use the
        font selector.
        """
        self.get_widget('font_style').set_sensitive(not chk.get_active())

    def toggle_use_font_background_sensitivity(self, chk):
        """If the user chooses to use the gnome default font
        configuration it means that he will not be able to use the
        font selector.
        """
        self.get_widget('palette_16').set_sensitive(chk.get_active())
        self.get_widget('palette_17').set_sensitive(chk.get_active())

    def toggle_display_n_sensitivity(self, chk):
        """When the user unchecks 'on mouse display', the option to select an
        alternate display should be enabeld.
        """
        self.get_widget('display_n').set_sensitive(not chk.get_active())

    def toggle_quick_open_command_line_sensitivity(self, chk):
        """When the user unchecks 'enable quick open', the command line should be disabled
        """
        self.get_widget('quick_open_command_line').set_sensitive(chk.get_active())
        self.get_widget('quick_open_in_current_terminal').set_sensitive(chk.get_active())

    def toggle_use_vte_titles(self, chk):
        """When vte titles aren't used, there is nothing to abbreviate
        """
        self.update_vte_subwidgets_states()

    def update_vte_subwidgets_states(self):
        do_use_vte_titles = self.get_widget('use_vte_titles').get_active()
        max_tab_name_length_wdg = self.get_widget('max_tab_name_length')
        max_tab_name_length_wdg.set_sensitive(do_use_vte_titles)
        self.get_widget('lbl_max_tab_name_length').set_sensitive(do_use_vte_titles)
        self.get_widget('abbreviate_tab_names').set_sensitive(do_use_vte_titles)

    def clear_background_image(self, btn):
        """Unset the gconf variable that holds the name of the
        background image of all terminals.
        """
        self.client.unset(KEY('/style/background/image'))
        self.bgfilechooser.unselect_all()

    def on_reset_compat_defaults_clicked(self, bnt):
        """Reset default values to compat_{backspace,delete} gconf
        keys. The default values are retrivied from the guake.schemas
        file.
        """
        self.client.unset(KEY('/general/compat_backspace'))
        self.client.unset(KEY('/general/compat_delete'))
        self.reload_erase_combos()

    def on_palette_name_changed(self, combo):
        """Changes the value of palette in gconf
        """
        palette_name = combo.get_active_text()
        if palette_name not in PALETTES:
            return
        self.client.set_string(KEY('/style/font/palette'),
                               PALETTES[palette_name])
        self.client.set_string(KEY('/style/font/palette_name'), palette_name)
        self.set_palette_colors(PALETTES[palette_name])
        self.update_demo_palette(PALETTES[palette_name])

    def on_cursor_shape_changed(self, combo):
        """Changes the value of cursor_shape in gconf
        """
        index = combo.get_active()
        self.client.set_int(KEY('/style/cursor_shape'), index)

    def on_blink_cursor_toggled(self, chk):
        """Changes the value of blink_cursor in gconf
        """
        self.client.set_int(KEY('/style/cursor_blink_mode'), chk.get_active())

    def on_palette_color_set(self, btn):
        """Changes the value of palette in gconf
        """
        palette = []
        for i in range(18):
            palette.append(hexify_color(
                self.get_widget('palette_%d' % i).get_color()))
        palette = ':'.join(palette)
        self.client.set_string(KEY('/style/font/palette'), palette)
        self.client.set_string(KEY('/style/font/palette_name'), _('Custom'))
        self.set_palette_name('Custom')
        self.update_demo_palette(palette)

    def set_palette_name(self, palette_name):
        """If the given palette matches an existing one, shows it in the
        combobox
        """
        combo = self.get_widget('palette_name')
        found = False
        log.debug("wanting palette: %r", palette_name)
        for i in combo.get_model():
            if i[0] == palette_name:
                combo.set_active_iter(i.iter)
                found = True
                break
        if not found:
            combo.set_active(self.custom_palette_index)

    def update_demo_palette(self, palette):
        fgcolor = gtk.gdk.color_parse(
            self.client.get_string(KEY('/style/font/color')))
        bgcolor = gtk.gdk.color_parse(
            self.client.get_string(KEY('/style/background/color')))
        palette = [gtk.gdk.color_parse(color) for color in palette.split(':')]
        font_name = self.client.get_string(KEY('/style/font/style'))
        font = FontDescription(font_name)

        use_palette_font_and_background_color = self.client.get_bool(
            KEY('/general/use_palette_font_and_background_color'))
        if use_palette_font_and_background_color and len(palette) > 16:
            fgcolor = palette[16]
            bgcolor = palette[17]
        self.demo_terminal.set_color_dim(fgcolor)
        self.demo_terminal.set_color_foreground(fgcolor)
        self.demo_terminal.set_color_bold(fgcolor)
        self.demo_terminal.set_color_background(bgcolor)
        self.demo_terminal.set_background_tint_color(bgcolor)
        self.demo_terminal.set_colors(fgcolor, bgcolor, palette[:16])
        self.demo_terminal.set_font(font)

    def fill_palette_names(self):
        combo = self.get_widget('palette_name')
        for palette_name in sorted(PALETTES.keys()):
            combo.append_text(palette_name)
        self.custom_palette_index = len(PALETTES)
        combo.append_text(_('Custom'))

    def set_cursor_shape(self, shape_index):
        self.get_widget('cursor_shape').set_active(shape_index)

    def set_cursor_blink_mode(self, mode_index):
        self.get_widget('cursor_blink_mode').set_active(mode_index)

    def set_palette_colors(self, palette):
        """Updates the color buttons with the given palette
        """
        palette = palette.split(':')
        for i, pal in enumerate(palette):
            color = gtk.gdk.color_parse(pal)
            self.get_widget('palette_%d' % i).set_color(color)

    def reload_erase_combos(self, btn=None):
        """Read from gconf the value of compat_{backspace,delete} vars
        and select the right option in combos.
        """
        # backspace erase binding
        combo = self.get_widget('backspace-binding-combobox')
        binding = self.client.get_string(KEY('/general/compat_backspace'))
        for i in combo.get_model():
            if ERASE_BINDINGS.get(i[0]) == binding:
                combo.set_active_iter(i.iter)

        # delete erase binding
        combo = self.get_widget('delete-binding-combobox')
        binding = self.client.get_string(KEY('/general/compat_delete'))
        for i in combo.get_model():
            if ERASE_BINDINGS.get(i[0]) == binding:
                combo.set_active_iter(i.iter)

    def load_configs(self):
        """Load configurations for all widgets in General, Scrolling
        and Appearance tabs from gconf.
        """
        # default_shell

        combo = self.get_widget('default_shell')
        # get the value for defualt shell. If unset, set to USER_SHELL_VALUE.
        value = self.client.get_string(KEY('/general/default_shell')) or USER_SHELL_VALUE
        for i in combo.get_model():
            if i[0] == value:
                combo.set_active_iter(i.iter)

        # login shell
        value = self.client.get_bool(KEY('/general/use_login_shell'))
        self.get_widget('use_login_shell').set_active(value)

        # tray icon
        value = self.client.get_bool(KEY('/general/use_trayicon'))
        self.get_widget('use_trayicon').set_active(value)

        # popup
        value = self.client.get_bool(KEY('/general/use_popup'))
        self.get_widget('use_popup').set_active(value)

        # prompt on quit
        value = self.client.get_bool(KEY('/general/prompt_on_quit'))
        self.get_widget('prompt_on_quit').set_active(value)

        # prompt on close_tab
        value = self.client.get_int(KEY('/general/prompt_on_close_tab'))
        self.get_widget('prompt_on_close_tab').set_active(value)
        self.get_widget('prompt_on_quit').set_sensitive(value != 2)

        # ontop
        value = self.client.get_bool(KEY('/general/window_ontop'))
        self.get_widget('window_ontop').set_active(value)

        # tab ontop
        value = self.client.get_bool(KEY('/general/tab_ontop'))
        self.get_widget('tab_ontop').set_active(value)

        # losefocus
        value = self.client.get_bool(KEY('/general/window_losefocus'))
        self.get_widget('window_losefocus').set_active(value)

        # use VTE titles
        value = self.client.get_bool(KEY('/general/use_vte_titles'))
        self.get_widget('use_vte_titles').set_active(value)

        # abbreviate tab names
        self.get_widget('abbreviate_tab_names').set_sensitive(value)
        value = self.client.get_bool(KEY('/general/abbreviate_tab_names'))
        self.get_widget('abbreviate_tab_names').set_active(value)

        # max tab name length
        value = self.client.get_int(KEY('/general/max_tab_name_length'))
        self.get_widget('max_tab_name_length').set_value(value)

        self.update_vte_subwidgets_states()

        value = self.client.get_float(KEY('/general/window_height_f'))
        if not value:
            value = self.client.get_int(KEY('/general/window_height'))
        self.get_widget('window_height').set_value(value)

        value = self.client.get_float(KEY('/general/window_width_f'))
        if not value:
            value = self.client.get_int(KEY('/general/window_width'))
        self.get_widget('window_width').set_value(value)

        value = self.client.get_int(KEY('/general/window_halignment'))
        which_button = {
            ALIGN_RIGHT: 'radiobutton_align_right',
            ALIGN_LEFT: 'radiobutton_align_left',
            ALIGN_CENTER: 'radiobutton_align_center'
        }
        self.get_widget(which_button[value]).set_active(True)

        value = self.client.get_bool(KEY('/general/open_tab_cwd'))
        self.get_widget('open_tab_cwd').set_active(value)

        # tab bar
        value = self.client.get_bool(KEY('/general/window_tabbar'))
        self.get_widget('window_tabbar').set_active(value)

        # start fullscreen
        value = self.client.get_bool(KEY('/general/start_fullscreen'))
        self.get_widget('start_fullscreen').set_active(value)

        # use visible bell
        value = self.client.get_bool(KEY('/general/use_visible_bell'))
        self.get_widget('use_visible_bell').set_active(value)

        # use audible bell
        value = self.client.get_bool(KEY('/general/use_audible_bell'))
        self.get_widget('use_audible_bell').set_active(value)

        # display number / use primary display
        combo = self.get_widget('display_n')
        dest_screen = self.client.get_int(KEY('/general/display_n'))

        value = self.client.get_bool(KEY('/general/quick_open_enable'))
        self.get_widget('quick_open_enable').set_active(value)
        self.get_widget('quick_open_command_line').set_sensitive(value)
        self.get_widget('quick_open_in_current_terminal').set_sensitive(value)
        text = gtk.TextBuffer()
        text = self.get_widget('quick_open_supported_patterns').get_buffer()
        for title, matcher, _useless in QUICK_OPEN_MATCHERS:
            text.insert_at_cursor("%s: %s\n" % (title, matcher))
        self.get_widget('quick_open_supported_patterns').set_buffer(text)

        value = self.client.get_string(KEY('/general/quick_open_command_line'))
        if value is None:
            value = "subl %(file_path)s:%(line_number)s"
        self.get_widget('quick_open_command_line').set_text(value)

        value = self.client.get_bool(KEY('/general/quick_open_in_current_terminal'))
        self.get_widget('quick_open_in_current_terminal').set_active(value)

        value = self.client.get_string(KEY('/general/startup_script'))
        self.get_widget('startup_script').set_text(value)

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

        if dest_screen == ALWAYS_ON_PRIMARY:
            first_item = combo.get_model().get_iter_first()
            combo.set_active_iter(first_item)
        else:
            seen_first = False  # first item "always on primary" is special
            for i in combo.get_model():
                if seen_first:
                    i_int = int(i[0].split()[0])  # extracts 1 from '1' or from '1 (primary)'
                    if i_int == dest_screen:
                        combo.set_active_iter(i.iter)
                else:
                    seen_first = True

        # use display where the mouse is currently
        value = self.client.get_bool(KEY('/general/mouse_display'))
        self.get_widget('mouse_display').set_active(value)

        # scrollbar
        value = self.client.get_bool(KEY('/general/use_scrollbar'))
        self.get_widget('use_scrollbar').set_active(value)

        # history size
        value = self.client.get_int(KEY('/general/history_size'))
        self.get_widget('history_size').set_value(value)

        # scroll output
        value = self.client.get_bool(KEY('/general/scroll_output'))
        self.get_widget('scroll_output').set_active(value)

        # scroll keystroke
        value = self.client.get_bool(KEY('/general/scroll_keystroke'))
        self.get_widget('scroll_keystroke').set_active(value)

        # default font
        value = self.client.get_bool(KEY('/general/use_default_font'))
        self.get_widget('use_default_font').set_active(value)
        self.get_widget('font_style').set_sensitive(not value)

        # use font and background color
        value = self.client.get_bool(KEY('/general/use_palette_font_and_background_color'))
        self.get_widget('use_palette_font_and_background_color').set_active(value)
        self.get_widget('palette_16').set_sensitive(value)
        self.get_widget('palette_17').set_sensitive(value)

        # font
        value = self.client.get_string(KEY('/style/font/style'))
        if value:
            self.get_widget('font_style').set_font_name(value)

        # font color
        val = self.client.get_string(KEY('/style/font/color'))
        try:
            color = gtk.gdk.color_parse(val)
            self.get_widget('font_color').set_color(color)
        except (ValueError, TypeError):
            warnings.warn('Unable to parse color %s' % val, Warning)

        # background color
        value = self.client.get_string(KEY('/style/background/color'))
        try:
            color = gtk.gdk.color_parse(value)
            self.get_widget('background_color').set_color(color)
        except (ValueError, TypeError):
            warnings.warn('Unable to parse color %s' % val, Warning)

        # allow bold font
        value = self.client.get_bool(KEY('/style/font/allow_bold'))
        self.get_widget('allow_bold').set_active(value)

        # palette
        self.fill_palette_names()
        value = self.client.get_string(KEY('/style/font/palette_name'))
        self.set_palette_name(value)
        value = self.client.get_string(KEY('/style/font/palette'))
        self.set_palette_colors(value)
        self.update_demo_palette(value)

        # cursor shape
        value = self.client.get_int(KEY('/style/cursor_shape'))
        self.set_cursor_shape(value)

        # cursor blink
        value = self.client.get_int(KEY('/style/cursor_blink_mode'))
        self.set_cursor_blink_mode(value)

        # background image
        value = self.client.get_string(KEY('/style/background/image'))
        if os.path.isfile(value or ''):
            self.get_widget('background_image').set_filename(value)

        value = self.client.get_int(KEY('/style/background/transparency'))
        self.get_widget('background_transparency').set_value(value)

        value = self.client.get_int(KEY('/general/window_valignment'))
        self.get_widget('top_align').set_active(value)

        # it's a separated method, to be reused.
        self.reload_erase_combos()

        # custom command context-menu configuration file
        custom_command_file = self.client.get_string(KEY('/general/custom_command_file'))
        if custom_command_file:
            custom_command_file_name = os.path.expanduser(custom_command_file)
        else:
            custom_command_file_name = None
        custom_cmd_filter = gtk.FileFilter()
        custom_cmd_filter.set_name(_("JSON files"))
        custom_cmd_filter.add_pattern("*.json")
        self.get_widget('custom_command_file_chooser').add_filter(custom_cmd_filter)
        all_files_filter = gtk.FileFilter()
        all_files_filter.set_name(_("All files"))
        all_files_filter.add_pattern("*")
        self.get_widget('custom_command_file_chooser').add_filter(all_files_filter)
        if custom_command_file_name:
            self.get_widget('custom_command_file_chooser').set_filename(custom_command_file_name)

    # -- populate functions --

    def populate_shell_combo(self):
        """Read the /etc/shells and looks for installed shells to
        fill the default_shell combobox.
        """
        cb = self.get_widget('default_shell')
        # append user shell as first option
        cb.append_text(USER_SHELL_VALUE)
        if os.path.exists(SHELLS_FILE):
            lines = open(SHELLS_FILE).readlines()
            for i in lines:
                possible = i.strip()
                if possible and not possible.startswith('#') and os.path.exists(possible):
                    cb.append_text(possible)

        for i in get_binaries_from_path(PYTHONS):
            cb.append_text(i)

    def populate_keys_tree(self):
        """Reads the HOTKEYS global variable and insert all data in
        the TreeStore used by the preferences window treeview.
        """
        model = self.get_widget('treeview-keys').get_model()
        for group in HOTKEYS:
            giter = model.append(None)
            model.set(giter, 0, '', 1, _(group['label']))
            for item in group['keys']:
                child = model.append(giter)
                accel = self.client.get_string(item['key'])
                if accel:
                    params = gtk.accelerator_parse(accel)
                    hotkey = KeyEntry(*params)
                else:
                    hotkey = KeyEntry(0, 0)
                model.set(child,
                          0, item['key'],
                          1, _(item['label']),
                          2, hotkey,
                          3, True)
        self.get_widget('treeview-keys').expand_all()

    def populate_display_n(self):
        """Get the number of displays and populate this drop-down box
        with them all. Prepend the "always on primary" option.
        """
        cb = self.get_widget('display_n')
        screen = self.get_widget('config-window').get_screen()

        cb.append_text("always on primary")

        for m in range(0, int(screen.get_n_monitors())):
            if m == int(screen.get_primary_monitor()):
                # TODO l10n
                cb.append_text(str(m) + ' ' + '(primary)')
            else:
                cb.append_text(str(m))

    # -- key handling --

    def on_key_edited(self, renderer, path, keycode, mask, keyval, model):
        """Callback that handles key edition in cellrenderer. It makes
        some tests to validate the key, like looking for already in
        use keys and look for [A-Z][a-z][0-9] to avoid problems with
        these common keys. If all tests are ok, the value will be
        stored in gconf.
        """
        giter = model.get_iter(path)
        gconf_path = model.get_value(giter, 0)

        oldkey = model.get_value(giter, 2)
        hotkey = KeyEntry(keycode, mask)
        key = gtk.accelerator_name(keycode, mask)
        keylabel = gtk.accelerator_get_label(keycode, mask)

        # we needn't to change anything, the user is trying to set the
        # same key that is already set.
        if oldkey == hotkey:
            return False

        # looking for already used keybindings
        def each_key(model, path, subiter):
            keyentry = model.get_value(subiter, 2)
            if keyentry and keyentry == hotkey:
                msg = _("The shortcut \"%s\" is already in use.") % keylabel
                raise ShowableError(_('Error setting keybinding.'), msg, -1)
        model.foreach(each_key)

        # avoiding problems with common keys
        if ((mask == 0 and keycode != 0) and (
            (keycode >= ord('a') and keycode <= ord('z')) or
            (keycode >= ord('A') and keycode <= ord('Z')) or
                (keycode >= ord('0') and keycode <= ord('9')))):
            dialog = gtk.MessageDialog(
                self.get_widget('config-window'),
                gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
                gtk.MESSAGE_WARNING, gtk.BUTTONS_OK,
                _("The shortcut \"%s\" cannot be used "
                  "because it will become impossible to "
                  "type using this key.\n\n"
                  "Please try with a key such as "
                  "Control, Alt or Shift at the same "
                  "time.\n") % key)
            dialog.run()
            dialog.destroy()
            return False

        # setting new value in ui
        giter = model.get_iter(path)
        model.set_value(giter, 2, hotkey)

        # setting the new value in gconf
        self.client.set_string(gconf_path, key)

    def on_key_cleared(self, renderer, path, model):
        """If the user tries to clear a keybinding with the backspace
        key this callback will be called and it just fill the model
        with an empty key and set the 'disabled' string in gconf path.
        """
        giter = model.get_iter(path)
        gconf_path = model.get_value(giter, 0)

        self.client.get_string(gconf_path)
        model.set_value(giter, 2, KeyEntry(0, 0))

        self.client.set_string(gconf_path, 'disabled')

    def cell_data_func(self, column, renderer, model, giter):
        """Defines the way that each renderer will handle the key
        object and the mask it sets the properties for a cellrenderer
        key.
        """
        obj = model.get_value(giter, 2)
        if obj:
            renderer.set_property('visible', True)
            renderer.set_property('accel-key', obj.keycode)
            renderer.set_property('accel-mods', obj.mask)
        else:
            renderer.set_property('visible', False)
            renderer.set_property('accel-key', 0)
            renderer.set_property('accel-mods', 0)

    def start_editing(self, treeview, event):
        """Make the treeview grab the focus and start editing the cell
        that the user has clicked to avoid confusion with two or three
        clicks before editing a keybinding.

        Thanks to gnome-keybinding-properties.c =)
        """
        if event.window != treeview.get_bin_window():
            return False

        x, y = int(event.x), int(event.y)
        ret = treeview.get_path_at_pos(x, y)
        if not ret:
            return False

        path, column, cellx, celly = ret
        if path and len(path) > 1:
            def real_cb():
                treeview.grab_focus()
                treeview.set_cursor(path, column, True)
            treeview.stop_emission('button-press-event')
            gobject.idle_add(real_cb)

        return True
Exemplo n.º 2
0
class PrefsDialog(SimpleGladeApp):

    """The Guake Preferences dialog.
    """

    def __init__(self):
        """Setup the preferences dialog interface, loading images,
        adding filters to file choosers and connecting some signals.
        """
        super(PrefsDialog, self).__init__(gladefile('prefs.glade'),
                                          root='config-window')
        self.add_callbacks(PrefsCallbacks())

        self.client = gconf.client_get_default()

        # setting evtbox title bg
        eventbox = self.get_widget('eventbox-title')
        eventbox.modify_bg(gtk.STATE_NORMAL,
                           eventbox.get_colormap().alloc_color("#ffffff"))

        # images
        ipath = pixmapfile('guake-notification.png')
        self.get_widget('image_logo').set_from_file(ipath)
        ipath = pixmapfile('quick-open.png')
        self.get_widget('image_quick_open').set_from_file(ipath)

        # the first position in tree will store the keybinding path in gconf,
        # and the user doesn't worry with this, let's hide that =D
        model = gtk.TreeStore(str, str, object, bool)
        treeview = self.get_widget('treeview-keys')
        treeview.set_model(model)
        treeview.set_rules_hint(True)
        treeview.connect('button-press-event', self.start_editing)

        renderer = gtk.CellRendererText()
        column = gtk.TreeViewColumn('keypath', renderer, text=0)
        column.set_visible(False)
        treeview.append_column(column)

        renderer = gtk.CellRendererText()
        column = gtk.TreeViewColumn(_('Action'), renderer, text=1)
        column.set_property('expand', True)
        treeview.append_column(column)

        renderer = gtk.CellRendererAccel()
        renderer.set_property('editable', True)

        renderer.connect('accel-edited', self.on_key_edited, model)
        renderer.connect('accel-cleared', self.on_key_cleared, model)

        column = gtk.TreeViewColumn(_('Shortcut'), renderer)
        column.set_cell_data_func(renderer, self.cell_data_func)
        column.set_property('expand', False)
        treeview.append_column(column)

        self.demo_terminal = GuakeTerminal()
        demo_terminal_box = self.get_widget('demo_terminal_box')
        demo_terminal_box.add(self.demo_terminal)

        default_params = {}
        pid = self.demo_terminal.fork_command(**default_params)
        self.demo_terminal.pid = pid

        self.populate_shell_combo()
        self.populate_keys_tree()
        self.populate_display_n()
        self.load_configs()
        self.get_widget('config-window').hide()

        # Preview when selecting a bgimage
        self.selection_preview = gtk.Image()
        self.file_filter = gtk.FileFilter()
        self.file_filter.add_pattern("*.jpg")
        self.file_filter.add_pattern("*.png")
        self.file_filter.add_pattern("*.svg")
        self.file_filter.add_pattern("*.jpeg")
        self.bgfilechooser = self.get_widget('background_image')
        self.bgfilechooser.set_preview_widget(self.selection_preview)
        self.bgfilechooser.set_filter(self.file_filter)
        self.bgfilechooser.connect('update-preview', self.update_preview,
                                   self.selection_preview)

    def show(self):
        """Calls the main window show_all method and presents the
        window in the desktop.
        """
        self.get_widget('config-window').show_all()
        self.get_widget('config-window').present()

    def hide(self):
        """Calls the main window hide function.
        """
        self.get_widget('config-window').hide()

    def update_preview(self, file_chooser, preview):
        """Used by filechooser to preview image files
        """
        filename = file_chooser.get_preview_filename()
        if filename and os.path.isfile(filename or ''):
            try:
                mkpb = gtk.gdk.pixbuf_new_from_file_at_size
                pixbuf = mkpb(filename, 256, 256)
                preview.set_from_pixbuf(pixbuf)
                file_chooser.set_preview_widget_active(True)
            except gobject.GError:
                # this exception is raised when user chooses a
                # non-image file or a directory
                warnings.warn('File %s is not an image' % filename)
        else:
            file_chooser.set_preview_widget_active(False)

    def toggle_prompt_on_quit_sensitivity(self, combo):
        """If toggle_on_close_tabs is set to 2 (Always), prompt_on_quit has no
        effect.
        """
        self.get_widget('prompt_on_quit').set_sensitive(combo.get_active() != 2)

    def toggle_style_sensitivity(self, chk):
        """If the user chooses to use the gnome default font
        configuration it means that he will not be able to use the
        font selector.
        """
        self.get_widget('font_style').set_sensitive(not chk.get_active())

    def toggle_use_font_background_sensitivity(self, chk):
        """If the user chooses to use the gnome default font
        configuration it means that he will not be able to use the
        font selector.
        """
        self.get_widget('palette_16').set_sensitive(chk.get_active())
        self.get_widget('palette_17').set_sensitive(chk.get_active())

    def toggle_display_n_sensitivity(self, chk):
        """When the user unchecks 'on mouse display', the option to select an
        alternate display should be enabeld.
        """
        self.get_widget('display_n').set_sensitive(not chk.get_active())

    def toggle_quick_open_command_line_sensitivity(self, chk):
        """When the user unchecks 'enable quick open', the command line should be disabled
        """
        self.get_widget('quick_open_command_line').set_sensitive(chk.get_active())
        self.get_widget('quick_open_in_current_terminal').set_sensitive(chk.get_active())

    def clear_background_image(self, btn):
        """Unset the gconf variable that holds the name of the
        background image of all terminals.
        """
        self.client.unset(KEY('/style/background/image'))
        self.bgfilechooser.unselect_all()

    def on_reset_compat_defaults_clicked(self, bnt):
        """Reset default values to compat_{backspace,delete} gconf
        keys. The default values are retrivied from the guake.schemas
        file.
        """
        self.client.unset(KEY('/general/compat_backspace'))
        self.client.unset(KEY('/general/compat_delete'))
        self.reload_erase_combos()

    def on_palette_name_changed(self, combo):
        """Changes the value of palette in gconf
        """
        palette_name = combo.get_active_text()
        if palette_name not in PALETTES:
            return
        self.client.set_string(KEY('/style/font/palette'),
                               PALETTES[palette_name])
        self.client.set_string(KEY('/style/font/palette_name'), palette_name)
        self.set_palette_colors(PALETTES[palette_name])
        self.update_demo_palette(PALETTES[palette_name])

    def on_cursor_shape_changed(self, combo):
        """Changes the value of cursor_shape in gconf
        """
        index = combo.get_active()
        self.client.set_int(KEY('/style/cursor_shape'), index)

    def on_blink_cursor_toggled(self, chk):
        """Changes the value of blink_cursor in gconf
        """
        self.client.set_int(KEY('/style/cursor_blink_mode'), chk.get_active())

    def on_palette_color_set(self, btn):
        """Changes the value of palette in gconf
        """
        palette = []
        for i in range(18):
            palette.append(hexify_color(
                self.get_widget('palette_%d' % i).get_color()))
        palette = ':'.join(palette)
        self.client.set_string(KEY('/style/font/palette'), palette)
        self.client.set_string(KEY('/style/font/palette_name'), _('Custom'))
        self.set_palette_name('Custom')
        self.update_demo_palette(palette)

    def set_palette_name(self, palette_name):
        """If the given palette matches an existing one, shows it in the
        combobox
        """
        combo = self.get_widget('palette_name')
        found = False
        log.debug("wanting palette: %r", palette_name)
        for i in combo.get_model():
            if i[0] == palette_name:
                combo.set_active_iter(i.iter)
                found = True
                break
        if not found:
            combo.set_active(self.custom_palette_index)

    def update_demo_palette(self, palette):
        fgcolor = gtk.gdk.color_parse(
            self.client.get_string(KEY('/style/font/color')))
        bgcolor = gtk.gdk.color_parse(
            self.client.get_string(KEY('/style/background/color')))
        palette = [gtk.gdk.color_parse(color) for color in palette.split(':')]
        font_name = self.client.get_string(KEY('/style/font/style'))
        font = FontDescription(font_name)

        use_palette_font_and_background_color = self.client.get_bool(
            KEY('/general/use_palette_font_and_background_color'))
        if use_palette_font_and_background_color and len(palette) > 16:
            fgcolor = palette[16]
            bgcolor = palette[17]
        self.demo_terminal.set_color_dim(fgcolor)
        self.demo_terminal.set_color_foreground(fgcolor)
        self.demo_terminal.set_color_bold(fgcolor)
        self.demo_terminal.set_color_background(bgcolor)
        self.demo_terminal.set_background_tint_color(bgcolor)
        self.demo_terminal.set_colors(fgcolor, bgcolor, palette[:16])
        self.demo_terminal.set_font(font)

    def fill_palette_names(self):
        combo = self.get_widget('palette_name')
        for palette_name in sorted(PALETTES.keys()):
            combo.append_text(palette_name)
        self.custom_palette_index = len(PALETTES)
        combo.append_text(_('Custom'))

    def set_cursor_shape(self, shape_index):
        self.get_widget('cursor_shape').set_active(shape_index)

    def set_cursor_blink_mode(self, mode_index):
        self.get_widget('cursor_blink_mode').set_active(mode_index)

    def set_palette_colors(self, palette):
        """Updates the color buttons with the given palette
        """
        palette = palette.split(':')
        for i in range(len(palette)):
            color = gtk.gdk.color_parse(palette[i])
            self.get_widget('palette_%d' % i).set_color(color)

    def reload_erase_combos(self, btn=None):
        """Read from gconf the value of compat_{backspace,delete} vars
        and select the right option in combos.
        """
        # backspace erase binding
        combo = self.get_widget('backspace-binding-combobox')
        binding = self.client.get_string(KEY('/general/compat_backspace'))
        for i in combo.get_model():
            if ERASE_BINDINGS.get(i[0]) == binding:
                combo.set_active_iter(i.iter)

        # delete erase binding
        combo = self.get_widget('delete-binding-combobox')
        binding = self.client.get_string(KEY('/general/compat_delete'))
        for i in combo.get_model():
            if ERASE_BINDINGS.get(i[0]) == binding:
                combo.set_active_iter(i.iter)

    def load_configs(self):
        """Load configurations for all widgets in General, Scrolling
        and Appearance tabs from gconf.
        """
        # default_shell

        combo = self.get_widget('default_shell')
        # get the value for defualt shell. If unset, set to USER_SHELL_VALUE.
        value = self.client.get_string(KEY('/general/default_shell')) or USER_SHELL_VALUE
        for i in combo.get_model():
            if i[0] == value:
                combo.set_active_iter(i.iter)

        # login shell
        value = self.client.get_bool(KEY('/general/use_login_shell'))
        self.get_widget('use_login_shell').set_active(value)

        # tray icon
        value = self.client.get_bool(KEY('/general/use_trayicon'))
        self.get_widget('use_trayicon').set_active(value)

        # popup
        value = self.client.get_bool(KEY('/general/use_popup'))
        self.get_widget('use_popup').set_active(value)

        # prompt on quit
        value = self.client.get_bool(KEY('/general/prompt_on_quit'))
        self.get_widget('prompt_on_quit').set_active(value)

        # prompt on close_tab
        value = self.client.get_int(KEY('/general/prompt_on_close_tab'))
        self.get_widget('prompt_on_close_tab').set_active(value)
        self.get_widget('prompt_on_quit').set_sensitive(value != 2)

        # ontop
        value = self.client.get_bool(KEY('/general/window_ontop'))
        self.get_widget('window_ontop').set_active(value)

        # tab ontop
        value = self.client.get_bool(KEY('/general/tab_ontop'))
        self.get_widget('tab_ontop').set_active(value)

        # losefocus
        value = self.client.get_bool(KEY('/general/window_losefocus'))
        self.get_widget('window_losefocus').set_active(value)

        # use VTE titles
        value = self.client.get_bool(KEY('/general/use_vte_titles'))
        self.get_widget('use_vte_titles').set_active(value)

        # max tab name length
        value = self.client.get_int(KEY('/general/max_tab_name_length'))
        self.get_widget('max_tab_name_length').set_value(value)

        value = self.client.get_float(KEY('/general/window_height_f'))
        if not value:
            value = self.client.get_int(KEY('/general/window_height'))
        self.get_widget('window_height').set_value(value)

        value = self.client.get_float(KEY('/general/window_width_f'))
        if not value:
            value = self.client.get_int(KEY('/general/window_width'))
        self.get_widget('window_width').set_value(value)

        value = self.client.get_int(KEY('/general/window_halignment'))
        which_button = {
            ALIGN_RIGHT: 'radiobutton_align_right',
            ALIGN_LEFT: 'radiobutton_align_left',
            ALIGN_CENTER: 'radiobutton_align_center'
        }
        self.get_widget(which_button[value]).set_active(True)

        value = self.client.get_bool(KEY('/general/open_tab_cwd'))
        self.get_widget('open_tab_cwd').set_active(value)

        # tab bar
        value = self.client.get_bool(KEY('/general/window_tabbar'))
        self.get_widget('window_tabbar').set_active(value)

        # start fullscreen
        value = self.client.get_bool(KEY('/general/start_fullscreen'))
        self.get_widget('start_fullscreen').set_active(value)

        # use visible bell
        value = self.client.get_bool(KEY('/general/use_visible_bell'))
        self.get_widget('use_visible_bell').set_active(value)

        # use audible bell
        value = self.client.get_bool(KEY('/general/use_audible_bell'))
        self.get_widget('use_audible_bell').set_active(value)

        # display number / use primary display
        combo = self.get_widget('display_n')
        dest_screen = self.client.get_int(KEY('/general/display_n'))

        value = self.client.get_bool(KEY('/general/quick_open_enable'))
        self.get_widget('quick_open_enable').set_active(value)
        self.get_widget('quick_open_command_line').set_sensitive(value)
        self.get_widget('quick_open_in_current_terminal').set_sensitive(value)
        text = gtk.TextBuffer()
        text = self.get_widget('quick_open_supported_patterns').get_buffer()
        for title, matcher, _useless in QUICK_OPEN_MATCHERS:
            text.insert_at_cursor("%s: %s\n" % (title, matcher))
        self.get_widget('quick_open_supported_patterns').set_buffer(text)

        value = self.client.get_string(KEY('/general/quick_open_command_line'))
        if value is None:
            value = "subl %(file_path)s:%(line_number)s"
        self.get_widget('quick_open_command_line').set_text(value)

        value = self.client.get_bool(KEY('/general/quick_open_in_current_terminal'))
        self.get_widget('quick_open_in_current_terminal').set_active(value)

        value = self.client.get_string(KEY('/general/startup_script'))
        self.get_widget('startup_script').set_text(value)

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

        if dest_screen == ALWAYS_ON_PRIMARY:
            first_item = combo.get_model().get_iter_first()
            combo.set_active_iter(first_item)
        else:
            seen_first = False  # first item "always on primary" is special
            for i in combo.get_model():
                if seen_first:
                    i_int = int(i[0].split()[0])  # extracts 1 from '1' or from '1 (primary)'
                    if i_int == dest_screen:
                        combo.set_active_iter(i.iter)
                else:
                    seen_first = True

        # use display where the mouse is currently
        value = self.client.get_bool(KEY('/general/mouse_display'))
        self.get_widget('mouse_display').set_active(value)

        # scrollbar
        value = self.client.get_bool(KEY('/general/use_scrollbar'))
        self.get_widget('use_scrollbar').set_active(value)

        # history size
        value = self.client.get_int(KEY('/general/history_size'))
        self.get_widget('history_size').set_value(value)

        # scroll output
        value = self.client.get_bool(KEY('/general/scroll_output'))
        self.get_widget('scroll_output').set_active(value)

        # scroll keystroke
        value = self.client.get_bool(KEY('/general/scroll_keystroke'))
        self.get_widget('scroll_keystroke').set_active(value)

        # default font
        value = self.client.get_bool(KEY('/general/use_default_font'))
        self.get_widget('use_default_font').set_active(value)
        self.get_widget('font_style').set_sensitive(not value)

        # use font and background color
        value = self.client.get_bool(KEY('/general/use_palette_font_and_background_color'))
        self.get_widget('use_palette_font_and_background_color').set_active(value)
        self.get_widget('palette_16').set_sensitive(value)
        self.get_widget('palette_17').set_sensitive(value)

        # font
        value = self.client.get_string(KEY('/style/font/style'))
        if value:
            self.get_widget('font_style').set_font_name(value)

        # font color
        val = self.client.get_string(KEY('/style/font/color'))
        try:
            color = gtk.gdk.color_parse(val)
            self.get_widget('font_color').set_color(color)
        except (ValueError, TypeError):
            warnings.warn('Unable to parse color %s' % val, Warning)

        # background color
        value = self.client.get_string(KEY('/style/background/color'))
        try:
            color = gtk.gdk.color_parse(value)
            self.get_widget('background_color').set_color(color)
        except (ValueError, TypeError):
            warnings.warn('Unable to parse color %s' % val, Warning)

        # allow bold font
        value = self.client.get_bool(KEY('/style/font/allow_bold'))
        self.get_widget('allow_bold').set_active(value)

        # palette
        self.fill_palette_names()
        value = self.client.get_string(KEY('/style/font/palette_name'))
        self.set_palette_name(value)
        value = self.client.get_string(KEY('/style/font/palette'))
        self.set_palette_colors(value)
        self.update_demo_palette(value)

        # cursor shape
        value = self.client.get_int(KEY('/style/cursor_shape'))
        self.set_cursor_shape(value)

        # cursor blink
        value = self.client.get_int(KEY('/style/cursor_blink_mode'))
        self.set_cursor_blink_mode(value)

        # background image
        value = self.client.get_string(KEY('/style/background/image'))
        if os.path.isfile(value or ''):
            self.get_widget('background_image').set_filename(value)

        value = self.client.get_int(KEY('/style/background/transparency'))
        self.get_widget('background_transparency').set_value(value)

        value = self.client.get_int(KEY('/general/window_valignment'))
        self.get_widget('top_align').set_active(value)

        # it's a separated method, to be reused.
        self.reload_erase_combos()

        # custom command context-menu configuration file
        custom_command_file = self.client.get_string(KEY('/general/custom_command_file'))
        if custom_command_file:
            custom_command_file_name = os.path.expanduser(custom_command_file)
        else:
            custom_command_file_name = None
        custom_cmd_filter = gtk.FileFilter()
        custom_cmd_filter.set_name(_("JSON files"))
        custom_cmd_filter.add_pattern("*.json")
        self.get_widget('custom_command_file_chooser').add_filter(custom_cmd_filter)
        all_files_filter = gtk.FileFilter()
        all_files_filter.set_name(_("All files"))
        all_files_filter.add_pattern("*")
        self.get_widget('custom_command_file_chooser').add_filter(all_files_filter)
        if custom_command_file_name:
            self.get_widget('custom_command_file_chooser').set_filename(custom_command_file_name)

    # -- populate functions --

    def populate_shell_combo(self):
        """Read the /etc/shells and looks for installed shells to
        fill the default_shell combobox.
        """
        cb = self.get_widget('default_shell')
        # append user shell as first option
        cb.append_text(USER_SHELL_VALUE)
        if os.path.exists(SHELLS_FILE):
            lines = open(SHELLS_FILE).readlines()
            for i in lines:
                possible = i.strip()
                if possible and not possible.startswith('#') and os.path.exists(possible):
                    cb.append_text(possible)

        for i in get_binaries_from_path(PYTHONS):
            cb.append_text(i)

    def populate_keys_tree(self):
        """Reads the HOTKEYS global variable and insert all data in
        the TreeStore used by the preferences window treeview.
        """
        model = self.get_widget('treeview-keys').get_model()
        for group in HOTKEYS:
            giter = model.append(None)
            model.set(giter, 0, '', 1, _(group['label']))
            for item in group['keys']:
                child = model.append(giter)
                accel = self.client.get_string(item['key'])
                if accel:
                    params = gtk.accelerator_parse(accel)
                    hotkey = KeyEntry(*params)
                else:
                    hotkey = KeyEntry(0, 0)
                model.set(child,
                          0, item['key'],
                          1, _(item['label']),
                          2, hotkey,
                          3, True)
        self.get_widget('treeview-keys').expand_all()

    def populate_display_n(self):
        """Get the number of displays and populate this drop-down box
        with them all. Prepend the "always on primary" option.
        """
        cb = self.get_widget('display_n')
        screen = self.get_widget('config-window').get_screen()

        cb.append_text("always on primary")

        for m in range(0, int(screen.get_n_monitors())):
            if m == int(screen.get_primary_monitor()):
                # TODO l10n
                cb.append_text(str(m) + ' ' + '(primary)')
            else:
                cb.append_text(str(m))

    # -- key handling --

    def on_key_edited(self, renderer, path, keycode, mask, keyval, model):
        """Callback that handles key edition in cellrenderer. It makes
        some tests to validate the key, like looking for already in
        use keys and look for [A-Z][a-z][0-9] to avoid problems with
        these common keys. If all tests are ok, the value will be
        stored in gconf.
        """
        giter = model.get_iter(path)
        gconf_path = model.get_value(giter, 0)

        oldkey = model.get_value(giter, 2)
        hotkey = KeyEntry(keycode, mask)
        key = gtk.accelerator_name(keycode, mask)
        keylabel = gtk.accelerator_get_label(keycode, mask)

        # we needn't to change anything, the user is trying to set the
        # same key that is already set.
        if oldkey == hotkey:
            return False

        # looking for already used keybindings
        def each_key(model, path, subiter):
            keyentry = model.get_value(subiter, 2)
            if keyentry and keyentry == hotkey:
                msg = _("The shortcut \"%s\" is already in use.") % keylabel
                raise ShowableError(_('Error setting keybinding.'), msg, -1)
        model.foreach(each_key)

        # avoiding problems with common keys
        if ((mask == 0 and keycode != 0) and (
            (keycode >= ord('a') and keycode <= ord('z')) or
            (keycode >= ord('A') and keycode <= ord('Z')) or
                (keycode >= ord('0') and keycode <= ord('9')))):
            dialog = gtk.MessageDialog(
                self.get_widget('config-window'),
                gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
                gtk.MESSAGE_WARNING, gtk.BUTTONS_OK,
                _("The shortcut \"%s\" cannot be used "
                  "because it will become impossible to "
                  "type using this key.\n\n"
                  "Please try with a key such as "
                  "Control, Alt or Shift at the same "
                  "time.\n") % key)
            dialog.run()
            dialog.destroy()
            return False

        # setting new value in ui
        giter = model.get_iter(path)
        model.set_value(giter, 2, hotkey)

        # setting the new value in gconf
        self.client.set_string(gconf_path, key)

    def on_key_cleared(self, renderer, path, model):
        """If the user tries to clear a keybinding with the backspace
        key this callback will be called and it just fill the model
        with an empty key and set the 'disabled' string in gconf path.
        """
        giter = model.get_iter(path)
        gconf_path = model.get_value(giter, 0)

        self.client.get_string(gconf_path)
        model.set_value(giter, 2, KeyEntry(0, 0))

        self.client.set_string(gconf_path, 'disabled')

    def cell_data_func(self, column, renderer, model, giter):
        """Defines the way that each renderer will handle the key
        object and the mask it sets the properties for a cellrenderer
        key.
        """
        obj = model.get_value(giter, 2)
        if obj:
            renderer.set_property('visible', True)
            renderer.set_property('accel-key', obj.keycode)
            renderer.set_property('accel-mods', obj.mask)
        else:
            renderer.set_property('visible', False)
            renderer.set_property('accel-key', 0)
            renderer.set_property('accel-mods', 0)

    def start_editing(self, treeview, event):
        """Make the treeview grab the focus and start editing the cell
        that the user has clicked to avoid confusion with two or three
        clicks before editing a keybinding.

        Thanks to gnome-keybinding-properties.c =)
        """
        if event.window != treeview.get_bin_window():
            return False

        x, y = int(event.x), int(event.y)
        ret = treeview.get_path_at_pos(x, y)
        if not ret:
            return False

        path, column, cellx, celly = ret
        if path and len(path) > 1:
            def real_cb():
                treeview.grab_focus()
                treeview.set_cursor(path, column, True)
            treeview.stop_emission('button-press-event')
            gobject.idle_add(real_cb)

        return True