|
@@ -1,16 +1,18 @@
|
|
|
import os.path
|
|
|
import json
|
|
|
from subprocess import Popen
|
|
|
+from typing import Set
|
|
|
|
|
|
import gi
|
|
|
|
|
|
from marxbook import Store
|
|
|
|
|
|
gi.require_version("Gtk", "3.0")
|
|
|
-from gi.repository import Gtk, GObject, Gdk
|
|
|
+from gi.repository import Gtk, Gdk # noqa: E402
|
|
|
|
|
|
BROWSER_CMDLINE = ["firefox"]
|
|
|
|
|
|
+
|
|
|
class MyWindow(Gtk.Window):
|
|
|
def __init__(self, mxbstore: Store):
|
|
|
super().__init__()
|
|
@@ -27,7 +29,7 @@ class MyWindow(Gtk.Window):
|
|
|
) # title, description, tags, URL, path
|
|
|
self.mxb_import()
|
|
|
self.filter_marks = self.store_marks.filter_new()
|
|
|
- self.filter_marks_dir = None
|
|
|
+ self.filter_marks_dir: Set[str] = set()
|
|
|
self.filter_marks.set_visible_func(self.filter_func, data=None)
|
|
|
self.builder.get_object("tree_marks").set_model(self.filter_marks)
|
|
|
self.builder.get_object("tree_dirs").set_model(self.store_dirs)
|
|
@@ -55,6 +57,13 @@ class MyWindow(Gtk.Window):
|
|
|
column = Gtk.TreeViewColumn("Path", renderer, text=3)
|
|
|
column.set_visible(False)
|
|
|
self.builder.get_object("tree_marks").append_column(column)
|
|
|
+ targets = [("text/plain", 0, 1), ("TEXT", 0, 2)]
|
|
|
+ self.builder.get_object("tree_marks").enable_model_drag_source(
|
|
|
+ Gdk.ModifierType.BUTTON1_MASK, targets, Gdk.DragAction.COPY
|
|
|
+ )
|
|
|
+ self.builder.get_object("tree_dirs").enable_model_drag_dest(
|
|
|
+ targets, Gdk.DragAction.COPY
|
|
|
+ )
|
|
|
|
|
|
def init_dirs_view(self):
|
|
|
renderer = Gtk.CellRendererText()
|
|
@@ -122,33 +131,37 @@ class MyWindow(Gtk.Window):
|
|
|
iters[d] = self.store_dirs.append(parentiter, [parent, me])
|
|
|
|
|
|
def _add_accelerator(self, accelerator: str, callback):
|
|
|
- """Adds a keyboard shortcut"""
|
|
|
+ """Add a keyboard shortcut."""
|
|
|
key, mod = Gtk.accelerator_parse(accelerator)
|
|
|
self.my_accelerators.connect(key, mod, Gtk.AccelFlags.VISIBLE, callback)
|
|
|
|
|
|
def on_search_changed(self, *args, **kwargs):
|
|
|
self.filter_marks.refilter()
|
|
|
|
|
|
+ def on_search_activate(self, *args, **kwargs):
|
|
|
+ self.builder.get_object("tree_marks").grab_focus()
|
|
|
+
|
|
|
def on_tree_marks_row_activated(self, view, treepath, column):
|
|
|
row = self.filter_marks[treepath]
|
|
|
url = row[3]
|
|
|
- print(url) # XXX: open firefox
|
|
|
cmd = BROWSER_CMDLINE + [url]
|
|
|
Popen(cmd, preexec_fn=os.setpgrp)
|
|
|
|
|
|
- def tree_dirs_select_row(self, treepath):
|
|
|
- row = self.store_dirs[treepath]
|
|
|
- self.filter_marks_dir = "/".join([row[0], row[1]]).lstrip("/")
|
|
|
+ def tree_dirs_select_row(self, treepaths: list):
|
|
|
+ rows = [self.store_dirs[path] for path in treepaths]
|
|
|
+ self.filter_marks_dir = set(
|
|
|
+ filter(bool, ("/".join([row[0], row[1]]).lstrip("/") for row in rows))
|
|
|
+ )
|
|
|
self.filter_marks.refilter()
|
|
|
|
|
|
def on_tree_dirs_sel_changed(self, selection):
|
|
|
model, treepaths = selection.get_selected_rows()
|
|
|
if not treepaths:
|
|
|
return
|
|
|
- self.tree_dirs_select_row(treepaths[0])
|
|
|
+ self.tree_dirs_select_row(treepaths)
|
|
|
|
|
|
def on_tree_dirs_row_activated(self, view, treepath, column):
|
|
|
- self.tree_dirs_select_row(treepath)
|
|
|
+ self.tree_dirs_select_row([treepath])
|
|
|
self.builder.get_object("search").grab_focus()
|
|
|
view.get_parent().hide()
|
|
|
|
|
@@ -175,17 +188,57 @@ class MyWindow(Gtk.Window):
|
|
|
tree_dirs.get_parent().hide()
|
|
|
|
|
|
def on_tree_dirs_focus(self, *args):
|
|
|
- """
|
|
|
- avoid giving focus to tree_dirs
|
|
|
- """
|
|
|
+ """Avoid giving focus to tree_dirs."""
|
|
|
return True
|
|
|
|
|
|
+ # DND {{{
|
|
|
+ def on_tree_marks_drag_data_get(self, widget, drag_context, selection, info, time):
|
|
|
+ source_widget = self.builder.get_object("tree_marks")
|
|
|
+ treeselection = source_widget.get_selection()
|
|
|
+ model, treepaths = treeselection.get_selected_rows()
|
|
|
+ bookmarks = []
|
|
|
+ for path in treepaths:
|
|
|
+ treeiter = model.get_iter(path)
|
|
|
+ bookmark_path = model.get_value(treeiter, 4)
|
|
|
+ bookmarks.append((bookmark_path, path.to_string()))
|
|
|
+ selection.set_text(json.dumps(bookmarks, indent=1), -1)
|
|
|
+ source_widget.stop_emission_by_name("drag-data-get")
|
|
|
+
|
|
|
+ def on_tree_dirs_drag_data_received(
|
|
|
+ self, source_widget, drag_context, x, y, selection_data, info, time
|
|
|
+ ):
|
|
|
+ target_widget = self.builder.get_object("tree_dirs")
|
|
|
+ bookmarks = json.loads(selection_data.get_text())
|
|
|
+ if not bookmarks:
|
|
|
+ return
|
|
|
+ model, treepaths = target_widget.get_selection().get_selected_rows()
|
|
|
+ drop_info = target_widget.get_dest_row_at_pos(x, y)
|
|
|
+ if drop_info:
|
|
|
+ path, position = drop_info
|
|
|
+ treeiter = model.get_iter(path)
|
|
|
+ parent = model.get_value(treeiter, 0)
|
|
|
+ folder = model.get_value(treeiter, 1)
|
|
|
+ if position == Gtk.TreeViewDropPosition.BEFORE:
|
|
|
+ new_folder = parent
|
|
|
+ else:
|
|
|
+ new_folder = os.path.join(parent, folder)
|
|
|
+ for fspath, treepath in bookmarks:
|
|
|
+ self.mxbstore.move(fspath, new_folder)
|
|
|
+ new_path = os.path.join(new_folder, os.path.basename(fspath))
|
|
|
+ treeiter = self.filter_marks.get_iter(treepath)
|
|
|
+ self.filter_marks.set_value(treeiter, 4, new_path)
|
|
|
+ self.filter_marks.refilter()
|
|
|
+ target_widget.stop_emission_by_name("drag-data-received")
|
|
|
+
|
|
|
+ # }}}
|
|
|
+
|
|
|
|
|
|
def main():
|
|
|
s = Store()
|
|
|
- w = MyWindow(s)
|
|
|
- w.show_all()
|
|
|
+ w = MyWindow(s) # noqa: F841
|
|
|
+ # w.show_all()
|
|
|
Gtk.main()
|
|
|
|
|
|
+
|
|
|
if __name__ == "__main__":
|
|
|
main()
|