From 08c1dca06df43b2d94b9a357e5a7cfdeb0316718 Mon Sep 17 00:00:00 2001 From: DYefremov Date: Sat, 19 Sep 2020 12:32:08 +0300 Subject: [PATCH] added support for loading and importing data via dnd --- app/ui/imports.py | 17 +++++++--- app/ui/main_app_window.py | 68 ++++++++++++++++++++++++++++++++------- app/ui/main_window.glade | 1 + 3 files changed, 69 insertions(+), 17 deletions(-) diff --git a/app/ui/imports.py b/app/ui/imports.py index 4509f325..d4041b1b 100644 --- a/app/ui/imports.py +++ b/app/ui/imports.py @@ -1,7 +1,7 @@ from contextlib import suppress from pathlib import Path -from app.commons import run_idle +from app.commons import run_idle, log from app.eparser import get_bouquets, get_services from app.eparser.ecommons import BqType, BqServiceType, Bouquet from app.eparser.enigma.bouquets import get_bouquet @@ -123,6 +123,7 @@ class ImportDialog: self._services_model.clear() try: if not self._bouquets: + log("Import [init data]: getting bouquets...") self._bouquets = get_bouquets(path, self._profile) for bqs in self._bouquets: for bq in bqs.bouquets: @@ -133,6 +134,7 @@ class ImportDialog: for srv in services: self._services[srv.fav_id] = srv except FileNotFoundError as e: + log("Import error [init data]: {}".format(e)) self.show_info_message(str(e), Gtk.MessageType.ERROR) def on_import(self, item): @@ -143,9 +145,17 @@ class ImportDialog: if not self._bouquets or show_dialog(DialogType.QUESTION, self._dialog_window) == Gtk.ResponseType.CANCEL: return + self.import_data() + + @run_idle + def import_data(self): + """ Importing data into models. """ + if not self._bouquets: + return + + log("Importing data...") services = set() to_delete = set() - for row in self._main_model: bq = (row[0], row[1]) if row[-1]: @@ -155,19 +165,16 @@ class ImportDialog: services.add(srv) else: to_delete.add(bq) - bqs_to_delete = [] for bqs in self._bouquets: for bq in bqs.bouquets: if (bq.name, bq.type) in to_delete: bqs_to_delete.append(bq) - for bqs in self._bouquets: bq = bqs.bouquets for b in bqs_to_delete: with suppress(ValueError): bq.remove(b) - self._append(self._bouquets, list(filter(lambda s: s.fav_id not in self._service_ids, services))) self._dialog_window.destroy() diff --git a/app/ui/main_app_window.py b/app/ui/main_app_window.py index 1578fa38..e3e8db36 100644 --- a/app/ui/main_app_window.py +++ b/app/ui/main_app_window.py @@ -4,6 +4,7 @@ from contextlib import suppress from datetime import datetime from functools import lru_cache from itertools import chain +from urllib.parse import urlparse, unquote from gi.repository import GLib, Gio @@ -476,7 +477,7 @@ class Application(Gtk.Application): self.set_profile(profile) def init_drag_and_drop(self): - """ Enable drag-and-drop """ + """ Enable drag-and-drop. """ target = [] bq_target = [] @@ -496,6 +497,7 @@ class Application(Gtk.Application): self._services_view.drag_source_set_target_list(None) self._services_view.drag_source_add_text_targets() + self._services_view.drag_dest_add_text_targets() self._services_view.drag_dest_add_uri_targets() self._bouquets_view.drag_dest_set_target_list(None) @@ -504,6 +506,9 @@ class Application(Gtk.Application): self._bouquets_view.drag_source_add_text_targets() self._bouquets_view.drag_dest_add_uri_targets() + self._app_info_box.drag_dest_set(Gtk.DestDefaults.ALL, [], Gdk.DragAction.COPY) + self._app_info_box.drag_dest_add_text_targets() + def init_colors(self, update=False): """ Initialisation of background colors for the services. @@ -1035,11 +1040,12 @@ class Application(Gtk.Application): txt = data.get_text() uris = data.get_uris() if txt: - self.receive_selection(view=view, drop_info=view.get_dest_row_at_pos(x, y), data=txt) + if txt.startswith("file://"): + self.on_import_data(urlparse(unquote(txt)).path.strip()) + else: + self.receive_selection(view=view, drop_info=view.get_dest_row_at_pos(x, y), data=txt) if uris: - from urllib.parse import unquote, urlparse - src, sep, dest = uris[0].partition("::::") picon_path = urlparse(unquote(src)).path dest_path = urlparse(unquote(dest)).path + "/" @@ -1060,6 +1066,10 @@ class Application(Gtk.Application): if not data: return + if data.startswith("file://"): + self.on_import_bouquet(None, file_path=urlparse(unquote(data)).path.strip()) + return + itr_str, sep, source = data.partition("::::") if source != self.BQ_MODEL_NAME: return @@ -1269,17 +1279,24 @@ class Application(Gtk.Application): def open_compressed_data(self, data_path): """ Opening archived data. """ + arch_path = self.get_archive_path(data_path) + if arch_path: + gen = self.update_data("{}{}".format(arch_path.name, os.sep), callback=arch_path.cleanup) + GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW) + + def get_archive_path(self, data_path): + """ Returns the temp dir path for the extracted data, or None if the archive format is not supported. """ import zipfile import tarfile import tempfile tmp_path = tempfile.TemporaryDirectory() - tmp_path_name = str(tmp_path.name) + tmp_path_name = tmp_path.name if zipfile.is_zipfile(data_path): with zipfile.ZipFile(data_path) as zip_file: for zip_info in zip_file.infolist(): - if not zip_info.is_dir(): + if not zip_info.filename.endswith(os.sep): zip_info.filename = os.path.basename(zip_info.filename) zip_file.extract(zip_info, path=tmp_path_name) elif tarfile.is_tarfile(data_path): @@ -1289,12 +1306,12 @@ class Application(Gtk.Application): mb.name = os.path.basename(mb.name) tar.extract(mb, path=tmp_path_name) else: - self.show_error_dialog("Unsupported format!") tmp_path.cleanup() + log("Error getting the path for the archive. Unsupported file format: {}".format(data_path)) + self.show_error_dialog("Unsupported format!") return - gen = self.update_data("{}{}".format(tmp_path_name, os.sep), callback=tmp_path.cleanup) - GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW) + return tmp_path def update_data(self, data_path, callback=None): self._profile_combo_box.set_sensitive(False) @@ -2034,6 +2051,16 @@ class Application(Gtk.Application): else: show_dialog(DialogType.INFO, self._main_window, "Done!") + def on_import_data(self, path): + msg = "Combine with the current data?" + if len(self._services_model) > 0 and show_dialog(DialogType.QUESTION, self._main_window, + msg) == Gtk.ResponseType.OK: + self.import_data(path, force=True) + else: + if os.path.isdir(path) and not path.endswith(os.sep): + path += os.sep + self.open_data(path) + def on_import_bouquet(self, action, value=None, file_path=None): model, paths = self._bouquets_view.get_selection().get_selected_rows() if not paths: @@ -2048,17 +2075,34 @@ class Application(Gtk.Application): if response in (Gtk.ResponseType.CANCEL, Gtk.ResponseType.DELETE_EVENT): return + self.import_data(response) + + def import_data(self, path, force=None, callback=None): + if os.path.isdir(path) and not path.endswith(os.sep): + path += os.sep + elif os.path.isfile(path): + arch_path = self.get_archive_path(path) + if not arch_path: + return + + path = arch_path.name + os.sep + callback = arch_path.cleanup + def append(b, s): - gen = self.append_imported_data(b, s) + gen = self.append_imported_data(b, s, callback) GLib.idle_add(lambda: next(gen, False)) - ImportDialog(self._main_window, response, self._settings, self._services.keys(), append).show() + dialog = ImportDialog(self._main_window, path, self._settings, self._services.keys(), append) + dialog.import_data() if force else dialog.show() - def append_imported_data(self, bouquets, services): + def append_imported_data(self, bouquets, services, callback=None): try: self._wait_dialog.show() yield from self.append_data(bouquets, services) finally: + log("Importing data done!") + if callback: + callback() self._wait_dialog.hide() # ***************** Backup ********************# diff --git a/app/ui/main_window.glade b/app/ui/main_window.glade index 24e08ff5..e4e013cd 100644 --- a/app/ui/main_window.glade +++ b/app/ui/main_window.glade @@ -2778,6 +2778,7 @@ Author: Dmitriy Yefremov True True vertical +