dnd: can move marks in directories
This commit is contained in:
parent
beddb0b2d5
commit
c5999cac00
3 changed files with 87 additions and 15 deletions
|
@ -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()
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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"^([^:]+): (.*)$")
|
||||
|
||||
|
|
Loading…
Reference in a new issue