示例#1
0
 def on_show_menu(self):
     """Called during the button-press-event to show the actual menu."""
     # HACK: bind to self, since the ref would get lost.
     self._menu = PopupMenu()
     self._menu.simple_add('Toggle all', self.on_toggle_all)
     self._menu.simple_add('Toggle selected', self.on_toggle_selected)
     self._menu.simple_add_separator()
     self._menu.simple_add('Expand all', self.on_expand_all)
     self._menu.simple_add('Collapse all', self.on_collapse_all)
     self._menu.simple_add_separator()
     self._menu.simple_add('Open item', self.on_open_folder)
     self._menu.simple_add('Copy path to clipboard',
                           self.on_copy_to_clipboard)
     return self._menu
示例#2
0
 def on_show_menu(self):
     """Called during the button-press-event to show the actual menu."""
     # HACK: bind to self, since the ref would get lost.
     self._menu = PopupMenu()
     self._menu.simple_add('Toggle all', self.on_toggle_all)
     self._menu.simple_add('Toggle selected', self.on_toggle_selected)
     self._menu.simple_add_separator()
     self._menu.simple_add('Expand all', self.on_expand_all)
     self._menu.simple_add('Collapse all', self.on_collapse_all)
     self._menu.simple_add_separator()
     self._menu.simple_add('Open item', self.on_open_folder)
     self._menu.simple_add(
         'Copy path to clipboard',
         self.on_copy_to_clipboard
     )
     return self._menu
示例#3
0
class PathTreeView(Gtk.TreeView):
    """A GtkTreeView that is readily configured for using PathTreeModel"""
    def __init__(self):
        Gtk.TreeView.__init__(self)

        # Enable separator lines:
        self.set_grid_lines(Gtk.TreeViewGridLines.NONE)
        self.set_enable_tree_lines(True)

        # Make selecting multiple rows possible:
        self.get_selection().set_mode(Gtk.SelectionMode.MULTIPLE)

        # Small spedup:
        self.set_fixed_height_mode(True)

        # Enable querying of the tooltip column:
        self.set_tooltip_column(Column.TOOLTIP)

        # Configure the column rendering:
        self.append_column(_create_column(
            'Path', Column.PATH, [
                (CellRendererLint(), False, False, dict(tag=Column.TAG)),
                (Gtk.CellRendererText(), False, True, dict(text=Column.PATH)),
            ],
            240
        ))
        self.append_column(_create_column(
            'Size', Column.SIZE,
            [(CellRendererSize(), True, False, dict(size=Column.SIZE))],
            70
        ))
        self.append_column(_create_column(
            'Count', Column.COUNT,
            [(CellRendererCount(), True, False, dict(count=Column.COUNT))],
            90
        ))
        self.append_column(
            _create_column(
                'Changed', Column.MTIME, [
                    (CellRendererModifiedTime(), True, False, dict(
                        mtime=Column.MTIME))], 100))

        self.connect(
            'button-press-event',
            PathTreeView.on_button_press_event
        )

        # Shut up, pylint.
        self._menu = None

    def set_model(self, model):
        """Overwrite Gtk.TreeView.set_model, but expand sub root paths"""
        Gtk.TreeView.set_model(self, model)
        self.expand_all()

    def get_selected_nodes(self):
        """Extra convinience method for getting the currently selected nodes"""
        model, rows = self.get_selection().get_selected_rows()
        for tp_path in rows:
            node = model.trie.resolve(tp_path.get_indices())
            yield node

    def get_selected_node(self):
        """Return the first selected node or None."""
        try:
            return next(self.get_selected_nodes())
        except StopIteration:
            return None

    def on_button_press_event(self, event):
        """Callback handler only used for mouse clicks."""
        if event.button != 3:
            return

        menu = self.on_show_menu()
        if menu:
            menu.simple_popup(event)

    #######################
    # MENU ENTRY HANDLING #
    #######################

    def on_show_menu(self):
        """Called during the button-press-event to show the actual menu."""
        # HACK: bind to self, since the ref would get lost.
        self._menu = PopupMenu()
        self._menu.simple_add('Toggle all', self.on_toggle_all)
        self._menu.simple_add('Toggle selected', self.on_toggle_selected)
        self._menu.simple_add_separator()
        self._menu.simple_add('Expand all', self.on_expand_all)
        self._menu.simple_add('Collapse all', self.on_collapse_all)
        self._menu.simple_add_separator()
        self._menu.simple_add('Open item', self.on_open_folder)
        self._menu.simple_add(
            'Copy path to clipboard',
            self.on_copy_to_clipboard
        )
        return self._menu

    def on_open_folder(self, _):
        """Open the selected item or folder via xdg-open."""
        node = self.get_selected_node()
        if node is None:
            return

        try:
            LOGGER.info('Calling xdg-open %s', node.build_path())
            Gio.Subprocess.new(
                ['xdg-open', node.build_path()], 0
            )
        except GLib.Error as err:
            LOGGER.exception('Could not open directory via xdg-open')

    def on_copy_to_clipboard(self, _):
        """Copy the currently selected full path to the clipboard."""
        node = self.get_selected_node()
        if node is None:
            return

        path = node.build_path()
        clipboard = Gtk.Clipboard.get_default(Gdk.Display.get_default())
        clipboard.set_text(path, len(path))

    def _toggle_tag_state(self, node_iter):
        """Iterate over `node_iter` and toggle the `tag` state."""
        model = self.get_model()
        for node in node_iter:
            current, new = node[Column.TAG], NodeState.NONE

            if current is NodeState.DUPLICATE:
                new = NodeState.ORIGINAL
            elif current is NodeState.ORIGINAL:
                new = NodeState.DUPLICATE

            self.update_node(node, Column.TAG, new)

    def on_toggle_all(self, _):
        """Toggle all nodes in the current visible model."""
        model = self.get_model()
        self._toggle_tag_state(model.trie)

    def on_toggle_selected(self, _):
        """Toggle all selected nodes in the current visible model."""
        nodes = list(self.get_selected_nodes())
        self._toggle_tag_state(nodes)

        # Note: this is the *full* trie.
        model = self.get_model()
        trie = model.trie

        for node in nodes:
            # Json documents with all related twins:
            group = trie.group(node[Column.CKSUM])
            if not group:
                continue

            # List of PathNodes which are twins:
            has_original = False
            for twin_node in group:
                if twin_node[Column.TAG] is NodeState.ORIGINAL:
                    has_original = True
                    break

            # All good:
            if has_original:
                continue

            # No original in group, set first twin as original.
            for twin_node in group:
                if twin_node is not node:
                    self.update_node(twin_node, Column.TAG, NodeState.ORIGINAL)
                    break

    def on_expand_all(self, _):
        """Just expand everything in the tree."""
        self.expand_all()

    def on_collapse_all(self, _):
        """Just collpase everything in the tree."""
        self.collapse_all()

    def set_twin(self, view):
        self.twin_view = view

    def update_node(self, node, column_id, value):
        main_model, twin_model = self.get_model(), self.twin_view.get_model()
        for model in [m for m in (main_model, twin_model) if m]:
            model.trie.update_node(node, column_id, value)

            for child in model.trie.group(node[Column.CKSUM]):
                model.mark_for_update(child)
示例#4
0
def on_button_press_event(_, event):
    """Callback handler only used for mouse clicks."""
    if event.button != 3:
        return

    menu = PopupMenu()
    menu.simple_add_checkbox('Pin file', None)
    menu.simple_add('Show History', None)
    menu.simple_add_separator()
    menu.simple_add('Import file', None)
    menu.simple_add('Export file', None)
    menu.simple_add('Remove', None)
    menu.simple_add('New Folder', None)
    menu.simple_add_separator()
    menu.simple_add('Open in file browser', None)
    menu.simple_popup(event)
示例#5
0
class PathTreeView(Gtk.TreeView):
    """A GtkTreeView that is readily configured for using PathTreeModel"""
    def __init__(self):
        Gtk.TreeView.__init__(self)

        # Enable separator lines:
        self.set_grid_lines(Gtk.TreeViewGridLines.NONE)
        self.set_enable_tree_lines(True)

        # Make selecting multiple rows possible:
        self.get_selection().set_mode(Gtk.SelectionMode.MULTIPLE)

        # Small spedup:
        self.set_fixed_height_mode(True)

        # Enable querying of the tooltip column:
        self.set_tooltip_column(Column.TOOLTIP)

        # Configure the column rendering:
        self.append_column(_create_column(
            'Path', Column.PATH, [
                (CellRendererLint(), False, False, dict(tag=Column.TAG)),
                (Gtk.CellRendererText(), False, True, dict(text=Column.PATH)),
            ],
            240
        ))
        self.append_column(_create_column(
            'Size', Column.SIZE,
            [(CellRendererSize(), True, False, dict(size=Column.SIZE))],
            70
        ))
        self.append_column(_create_column(
            'Count', Column.COUNT,
            [(CellRendererCount(), True, False, dict(count=Column.COUNT))],
            90
        ))
        self.append_column(
            _create_column(
                'Changed', Column.MTIME, [
                    (CellRendererModifiedTime(), True, False, dict(
                        mtime=Column.MTIME))], 100))

        self.connect(
            'button-press-event',
            PathTreeView.on_button_press_event
        )

        # Shut up, pylint.
        self._menu = None

    def set_model(self, model):
        """Overwrite Gtk.TreeView.set_model, but expand sub root paths"""
        Gtk.TreeView.set_model(self, model)
        self.expand_all()

    def get_selected_nodes(self):
        """Extra convinience method for getting the currently selected nodes"""
        model, rows = self.get_selection().get_selected_rows()
        for tp_path in rows:
            node = model.trie.resolve(tp_path.get_indices())
            yield node

    def get_selected_node(self):
        """Return thefirst selected node or None."""
        try:
            return next(self.get_selected_nodes())
        except StopIteration:
            return None

    def on_button_press_event(self, event):
        """Callback handler only used for mouse clicks."""
        if event.button != 3:
            return

        menu = self.on_show_menu()
        if menu:
            menu.simple_popup(event)

    #######################
    # MENU ENTRY HANDLING #
    #######################

    def on_show_menu(self):
        """Called during the button-press-event to show the actual menu."""
        # HACK: bind to self, since the ref would get lost.
        self._menu = PopupMenu()
        self._menu.simple_add('Toggle all', self.on_toggle_all)
        self._menu.simple_add('Toggle selected', self.on_toggle_selected)
        self._menu.simple_add_separator()
        self._menu.simple_add('Expand all', self.on_expand_all)
        self._menu.simple_add('Collapse all', self.on_collapse_all)
        self._menu.simple_add_separator()
        self._menu.simple_add('Open item', self.on_open_folder)
        self._menu.simple_add(
            'Copy path to clipboard',
            self.on_copy_to_clipboard
        )
        return self._menu

    def on_open_folder(self, _):
        """Open the selected item or folder via xdg-open."""
        node = self.get_selected_node()
        if node is None:
            return

        try:
            LOGGER.info('Calling xdg-open %s', node.build_path())
            Gio.Subprocess.new(
                ['xdg-open', node.build_path()], 0
            )
        except GLib.Error as err:
            LOGGER.exception('Could not open directory via xdg-open')

    def on_copy_to_clipboard(self, _):
        """Copy the currently selected full path to the clipboard."""
        node = self.get_selected_node()
        if node is None:
            return

        path = node.build_path()
        clipboard = Gtk.Clipboard.get_default(Gdk.Display.get_default())
        clipboard.set_text(path, len(path))

    def _toggle_tag_state(self, node_iter):
        """Iterate over `node_iter` and toggle the `tag` state."""
        model = self.get_model()
        for node in node_iter:
            current, new = node[Column.TAG], NodeState.NONE

            if current is NodeState.DUPLICATE:
                new = NodeState.ORIGINAL
            elif current is NodeState.ORIGINAL:
                new = NodeState.DUPLICATE

            self.update_node(node, Column.TAG, new)

    def on_toggle_all(self, _):
        """Toggle all nodes in the current visible model."""
        model = self.get_model()
        self._toggle_tag_state(model.trie)

    def on_toggle_selected(self, _):
        """Toggle all selected nodes in the current visible model."""
        nodes = list(self.get_selected_nodes())
        self._toggle_tag_state(nodes)

        # Note: this is the *full* trie.
        model = self.get_model()
        trie = model.trie

        for node in nodes:
            # Json documents with all related twins:
            group = trie.group(node[Column.CKSUM])
            if not group:
                continue

            # List of PathNodes which are twins:
            has_original = False
            for twin_node in group:
                if twin_node[Column.TAG] is NodeState.ORIGINAL:
                    has_original = True
                    break

            # All good:
            if has_original:
                continue

            # No original in group, set first twin as original.
            for twin_node in group:
                if twin_node is not node:
                    self.update_node(twin_node, Column.TAG, NodeState.ORIGINAL)
                    break

    def on_expand_all(self, _):
        """Just expand everything in the tree."""
        self.expand_all()

    def on_collapse_all(self, _):
        """Just collpase everything in the tree."""
        self.collapse_all()

    def set_twin(self, view):
        self.twin_view = view

    def update_node(self, node, column_id, value):
        main_model, twin_model = self.get_model(), self.twin_view.get_model()
        for model in [m for m in (main_model, twin_model) if m]:
            model.trie.update_node(node, column_id, value)

            for child in model.trie.group(node[Column.CKSUM]):
                model.mark_for_update(child)