dnd: can move marks in directories

This commit is contained in:
boyska 2021-03-28 20:17:47 +02:00
parent beddb0b2d5
commit c5999cac00
3 changed files with 87 additions and 15 deletions

View file

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

View file

@ -14,11 +14,14 @@
<object class="GtkTreeView" id="tree_dirs">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="rubber_banding">True</property>
<signal name="drag-data-received" handler="on_tree_dirs_drag_data_received" swapped="no"/>
<signal name="focus" handler="on_tree_dirs_focus" swapped="no"/>
<signal name="key-press-event" handler="on_tree_dirs_key_press_event" swapped="no"/>
<signal name="row-activated" handler="on_tree_dirs_row_activated" swapped="no"/>
<child internal-child="selection">
<object class="GtkTreeSelection" id="tree_dirs_sel">
<property name="mode">multiple</property>
<signal name="changed" handler="on_tree_dirs_sel_changed" swapped="no"/>
</object>
</child>
@ -49,9 +52,13 @@
<property name="vexpand">False</property>
<property name="reorderable">True</property>
<property name="enable_search">False</property>
<property name="rubber_banding">True</property>
<signal name="drag-data-get" handler="on_tree_marks_drag_data_get" swapped="no"/>
<signal name="row-activated" handler="on_tree_marks_row_activated" swapped="no"/>
<child internal-child="selection">
<object class="GtkTreeSelection"/>
<object class="GtkTreeSelection">
<property name="mode">multiple</property>
</object>
</child>
</object>
</child>
@ -70,6 +77,7 @@
<property name="primary_icon_name">edit-find-symbolic</property>
<property name="primary_icon_activatable">False</property>
<property name="primary_icon_sensitive">False</property>
<signal name="activate" handler="on_search_activate" swapped="no"/>
<signal name="changed" handler="on_search_changed" swapped="no"/>
</object>
<packing>

View file

@ -63,6 +63,17 @@ class Store:
def folder(self, folder: str):
return Store(self.basedir / folder)
def move(self, path: str, dest_dir: str):
dest = self.basedir / dest_dir
if dest.exists() and not dest.is_dir():
raise ValueError(
"destination '%s' already exists and is not a directory" % dest_dir
)
if not dest.exists():
dest.mkdir(parents=True, exist_ok=True)
fpath = self.basedir / path
fpath.rename(dest / fpath.name)
HEADER_LINE = re.compile(r"^([^:]+): (.*)$")