aboutsummaryrefslogtreecommitdiff
path: root/src/book_flow.py
diff options
context:
space:
mode:
authorJoris Guyonvarch2025-12-26 18:41:26 +0100
committerJoris Guyonvarch2025-12-27 20:41:44 +0100
commita110c200e86d2325af07167531fac0f61d9681a0 (patch)
tree90e843f915a2e153ba735849afd83710d90560bf /src/book_flow.py
parenta26d92ad5055fa057647158eb79511e7b1841162 (diff)
Switch to GUI to manage the library
Allow to regroup the CLI and the view into one unique tool.
Diffstat (limited to 'src/book_flow.py')
-rw-r--r--src/book_flow.py141
1 files changed, 141 insertions, 0 deletions
diff --git a/src/book_flow.py b/src/book_flow.py
new file mode 100644
index 0000000..59b0218
--- /dev/null
+++ b/src/book_flow.py
@@ -0,0 +1,141 @@
+import gi
+gi.require_version("Gtk", "4.0")
+from gi.repository import Gtk, Gio
+import glob
+import shutil
+import functools
+import os
+import subprocess
+
+import src.utils as utils
+import src.db as db
+from src.book_detail import BookDetail
+from src.book_delete import BookDelete
+from src.book_form import BookForm
+from src.book_transfer import BookTransfer
+from src.picture_cache import PictureCache
+import src.book_files as book_files
+
+class BookFlow(Gtk.FlowBox):
+
+ def __init__(self, window, resources, library, ereader, conn, progress, update_filter_progress):
+ Gtk.FlowBox.__init__(self)
+
+ self._window = window
+ self._resources = resources
+ self._library = library
+ self._ereader = ereader
+ self._conn = conn
+ self._progress = progress
+ self._update_filter_progress = update_filter_progress
+ self._picture_cache = PictureCache()
+
+ self._load_books()
+
+ def add_book(self, book_id, data):
+ self._books[book_id] = data
+ self.update_progress(data['progress'])
+
+ def update_progress(self, progress):
+ self._progress = progress
+ self._reset_view()
+
+ # Private
+
+ def _load_books(self):
+ """Get books from DB, but most importantly load covers to memory."""
+ self._books = db.get_books(self._conn)
+ for book_id in self._books.keys():
+ _ = self._picture_cache.get(f'{self._library}/{book_id}/cover-min.png')
+ self._reset_view()
+
+ def _reset_view(self):
+ self.remove_all()
+ self._flow_box_children = {}
+ for book_id, data in sorted(self._books.items(), key=book_sort):
+ if data['progress'] == self._progress:
+ picture = self._picture_cache.get(f'{self._library}/{book_id}/cover-min.png')
+ picture.set_can_shrink(False)
+
+ gesture_lclick = Gtk.GestureClick()
+ gesture_lclick.connect('released', self._on_left_click, book_id, data)
+ picture.add_controller(gesture_lclick)
+
+ gesture_rclick = Gtk.GestureClick()
+ gesture_rclick.set_button(3)
+ gesture_rclick.connect('released', self._on_right_click, picture, book_id, data)
+ picture.add_controller(gesture_rclick)
+
+ flow_box_child = Gtk.FlowBoxChild()
+ flow_box_child.set_child(picture)
+ self._flow_box_children[book_id] = flow_box_child
+ self.append(flow_box_child)
+
+ def _on_left_click(self, gesture, n_press, x, y, book_id, data):
+ if n_press == 2:
+ self._see_book(book_id, data)
+
+ def _on_right_click(self, gesture, n_press, x, y, picture, book_id, data):
+ if n_press == 1:
+ popover = Gtk.Popover()
+ popover.set_parent(picture)
+ box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5)
+ popover.set_child(box)
+
+ see_button = Gtk.Button(label='Voir')
+ see_button.connect('clicked', lambda _: self._see_book(book_id, data))
+ box.append(see_button)
+
+ update_button = Gtk.Button(label='Modifier')
+ update_button.connect('clicked', lambda _: self._update_book(book_id, data))
+ box.append(update_button)
+
+ delete_button = Gtk.Button(label='Supprimer')
+ delete_button.connect('clicked', lambda _: self._confirm_delete_book(book_id, data))
+ box.append(delete_button)
+
+ if self._ereader and os.path.exists(self._ereader):
+ paths = book_files.get(self._library, book_id)
+ if paths:
+ transfer_button = Gtk.Button(label='Transférer')
+ transfer_button.connect('clicked', lambda _: self._transfer_book(book_id, data, paths))
+ box.append(transfer_button)
+
+ popover.popup()
+
+ def _see_book(self, book_id, data):
+ BookDetail(self._window, self._library, book_id, data).present()
+
+ def _update_book(self, book_id, data):
+ book = {'id': book_id, 'data': data }
+ BookForm(self._window, self._resources, self._library, self._conn, data['progress'], self._on_book_updated, book).present()
+
+ def _on_book_updated(self, book_id, data):
+ self._picture_cache.invalidate(f'{self._library}/{book_id}/cover-min.png')
+ self._books[book_id] = data
+ self.update_progress(data['progress']) # Note: this redraws everything, this is overkill in some cases
+ self._update_filter_progress(data['progress'])
+ self.select_child(self._flow_box_children[book_id])
+
+ def _confirm_delete_book(self, book_id, data):
+ BookDelete(self._window, self._library, book_id, data, lambda: self._delete_book(book_id, data)).present()
+
+ def _delete_book(self, book_id, data):
+ del self._books[book_id]
+ db.delete_book(self._conn, book_id)
+ self._reset_view()
+
+ def _transfer_book(self, book_id, data, paths):
+ BookTransfer(self._window, self._ereader, book_id, data, paths).present()
+
+def book_sort(b):
+ key, data = b
+ author = author_key(data)
+ date = data['date'] if 'date' in data else ''
+ return f'{author}{date}'
+
+def author_key(data):
+ match data['authors']:
+ case [author, *_]:
+ return author.split()[-1]
+ return ''