From 614c87cbf33387c541a6e70ee6ea4e0ee9b89c1a Mon Sep 17 00:00:00 2001 From: DYefremov Date: Fri, 27 Dec 2019 23:05:37 +0300 Subject: [PATCH] base implementation of profiles support --- app/connections.py | 17 ++- app/settings.py | 54 ++++++- app/ui/backup.py | 1 + app/ui/download_dialog.glade | 273 ++++++++++++++++++++--------------- app/ui/download_dialog.py | 35 ++++- app/ui/main_app_window.py | 81 +++++++---- app/ui/main_window.glade | 18 +-- app/ui/settings_dialog.glade | 9 +- app/ui/settings_dialog.py | 108 +++++++++++--- 9 files changed, 396 insertions(+), 200 deletions(-) diff --git a/app/connections.py b/app/connections.py index ea9cff51..2c0bf16b 100644 --- a/app/connections.py +++ b/app/connections.py @@ -11,7 +11,7 @@ from urllib.error import HTTPError, URLError from urllib.parse import urlencode from urllib.request import urlopen, HTTPPasswordMgrWithDefaultRealm, HTTPBasicAuthHandler, build_opener, install_opener -from app.commons import log +from app.commons import log, run_idle from app.settings import SettingsType _BQ_FILES_LIST = ("tv", "radio", # enigma 2 @@ -277,12 +277,15 @@ def telnet(host, port=23, user="", password="", timeout=5): class HttpAPI: - def __init__(self, host, port, user, password): - self._base_url = "http://{}:{}/api/".format(host, port) - init_auth(user, password, self._base_url) + __MAX_WORKERS = 4 + + def __init__(self, settings): + self._settings = settings + self._base_url = None + self.init() from concurrent.futures import ThreadPoolExecutor as PoolExecutor - self._executor = PoolExecutor(max_workers=2) + self._executor = PoolExecutor(max_workers=self.__MAX_WORKERS) def send(self, req_type, ref, callback=print): url = self._base_url + req_type.value @@ -295,6 +298,10 @@ class HttpAPI: future = self._executor.submit(get_json, req_type, url) future.add_done_callback(lambda f: callback(f.result())) + def init(self): + self._base_url = "http://{}:{}/api/".format(self._settings.host, self._settings.http_port) + init_auth(self._settings.http_user, self._settings.http_password, self._base_url) + def close(self): self._executor.shutdown(False) diff --git a/app/settings.py b/app/settings.py index 9fa3633a..18537a63 100644 --- a/app/settings.py +++ b/app/settings.py @@ -1,9 +1,10 @@ +import copy import json import os -from pprint import pformat -from textwrap import dedent from enum import Enum, IntEnum from pathlib import Path +from pprint import pformat +from textwrap import dedent CONFIG_PATH = str(Path.home()) + "/.config/demon-editor/" CONFIG_FILE = CONFIG_PATH + "config.json" @@ -25,11 +26,14 @@ class Defaults(Enum): FAV_CLICK_MODE = 0 -def get_default_settings(): +def get_default_settings(profile_name="default"): + def_settings = SettingsType.ENIGMA_2.get_default_settings() + set_local_paths(def_settings, profile_name) + return { "version": 1, "default_profile": Defaults.DEFAULT_PROFILE.value, - "profiles": {"default": SettingsType.ENIGMA_2.get_default_settings()}, + "profiles": {profile_name: def_settings}, "v5_support": Defaults.V5_SUPPORT.value, "http_api_support": Defaults.HTTP_API_SUPPORT.value, "enable_yt_dl": Defaults.ENABLE_YT_DL.value, @@ -41,6 +45,12 @@ def get_default_settings(): } +def set_local_paths(settings, profile_name): + settings["data_local_path"] = "{}{}/".format(settings["data_local_path"], profile_name) + settings["picons_local_path"] = "{}{}/".format(settings["picons_local_path"], profile_name) + settings["backup_local_path"] = "{}{}/".format(settings["backup_local_path"], profile_name) + + class SettingsType(IntEnum): """ Profiles for settings """ ENIGMA_2 = 0 @@ -49,13 +59,13 @@ class SettingsType(IntEnum): def get_default_settings(self): """ Returns default settings for current type """ if self is self.ENIGMA_2: - return {"setting_type": self, + return {"setting_type": self.value, "host": "127.0.0.1", "port": "21", "user": "root", "password": "root", "timeout": 5, "http_user": "root", "http_password": "", "http_port": "80", "http_timeout": 5, "telnet_user": "root", "telnet_password": "", "telnet_port": "23", "telnet_timeout": 5, "services_path": "/etc/enigma2/", "user_bouquet_path": "/etc/enigma2/", "satellites_xml_path": "/etc/tuxbox/", "data_local_path": DATA_PATH + "enigma2/", - "picons_path": "/usr/share/enigma2/picon", + "picons_path": "/usr/share/enigma2/picon/", "picons_local_path": DATA_PATH + "enigma2/picons/", "backup_local_path": DATA_PATH + "enigma2/backup/"} elif self is self.NEUTRINO_MP: @@ -78,8 +88,8 @@ class Settings: __INSTANCE = None __VERSION = 1 - def __init__(self): - settings = get_settings() + def __init__(self, ext_settings=None): + settings = ext_settings or get_settings() if self.__VERSION > settings.get("version", 0): write_settings(get_default_settings()) @@ -112,6 +122,7 @@ class Settings: def reset(self, force_write=False): for k, v in self.setting_type.get_default_settings().items(): self._cp_settings[k] = v + set_local_paths(self._cp_settings, self._current_profile) if force_write: self.save() @@ -128,6 +139,33 @@ class Settings: """ Returns extra options or None """ return self._settings.get(name, None) + @property + def settings(self): + """ Returns copy of the current settings! """ + return copy.deepcopy(self._settings) + + @settings.setter + def settings(self, value): + """ Sets copy of the settings! """ + self._settings = copy.deepcopy(value) + + @property + def current_profile(self): + return self._current_profile + + @current_profile.setter + def current_profile(self, value): + self._current_profile = value + self._cp_settings = self._profiles.get(self._current_profile) + + @property + def default_profile(self): + return self._settings.get("default_profile", "default") + + @default_profile.setter + def default_profile(self, value): + self._settings["default_profile"] = value + @property def profiles(self): return self._profiles diff --git a/app/ui/backup.py b/app/ui/backup.py index 048ff058..5fb857b4 100644 --- a/app/ui/backup.py +++ b/app/ui/backup.py @@ -192,6 +192,7 @@ def backup_data(path, backup_path, move=True): """ backup_path = "{}{}/".format(backup_path, datetime.now().strftime("%Y-%m-%d_%H-%M-%S")) os.makedirs(os.path.dirname(backup_path), exist_ok=True) + os.makedirs(os.path.dirname(path), exist_ok=True) # backup files in data dir(skipping dirs and satellites.xml) for file in filter(lambda f: f != "satellites.xml" and os.path.isfile(os.path.join(path, f)), os.listdir(path)): src, dst = os.path.join(path, file), backup_path + file diff --git a/app/ui/download_dialog.glade b/app/ui/download_dialog.glade index a441afbd..00116fbf 100644 --- a/app/ui/download_dialog.glade +++ b/app/ui/download_dialog.glade @@ -26,7 +26,7 @@ THE SOFTWARE. Author: Dmitriy Yefremov --> - + @@ -47,6 +47,7 @@ Author: Dmitriy Yefremov True False + FTP-transfer 5 True @@ -56,7 +57,6 @@ Author: Dmitriy Yefremov 2 - 48 True True True @@ -78,7 +78,6 @@ Author: Dmitriy Yefremov - 48 True True True @@ -100,120 +99,13 @@ Author: Dmitriy Yefremov - - - True - False - 5 - 2 - vertical - 5 - - - True - False - FTP-transfer - - - False - True - 0 - - - - - True - False - 5 - 5 - - - True - False - 0 - - - False - True - 0 - - - - - All - True - True - False - True - satellites_radio_button - - - False - True - 1 - - - - - Bouquets - True - True - False - True - satellites_radio_button - - - False - True - 2 - - - - - Satellites - True - True - False - True - all_radio_button - - - False - True - 3 - - - - - WebTV - True - False - True - all_radio_button - - - False - True - 4 - - - - - False - True - 1 - - - - - 48 True True True Options - + True @@ -238,13 +130,156 @@ Author: Dmitriy Yefremov 1 vertical 2 + + + True + False + center + 10 + 5 + 5 + + + True + False + 0 + + + False + True + 0 + + + + + All + True + True + False + True + satellites_radio_button + + + False + True + 1 + + + + + Bouquets + True + True + False + True + satellites_radio_button + + + False + True + 2 + + + + + Satellites + True + True + False + True + all_radio_button + + + False + True + 3 + + + + + WebTV + True + False + True + all_radio_button + + + False + True + 4 + + + + + False + True + 0 + + + + + True + False + center + 5 + 5 + 5 + + + True + False + Profile: + + + False + True + 0 + + + + + True + False + False + center + 1 + 1 + 0 + False + True + + + + True + True + center + 1 + 1 + False + False + 9 + + + + + False + True + 1 + + + + + False + True + 1 + + True False 5 5 - 5 0.019999999552965164 in @@ -253,6 +288,7 @@ Author: Dmitriy Yefremov False 5 5 + 5 5 vertical @@ -319,7 +355,7 @@ Author: Dmitriy Yefremov False True - 0 + 1 @@ -398,7 +434,7 @@ Author: Dmitriy Yefremov False True - 0 + 2 @@ -590,7 +626,7 @@ Author: Dmitriy Yefremov False True - 1 + 3 @@ -629,7 +665,7 @@ Author: Dmitriy Yefremov False True - 3 + 4 @@ -685,9 +721,12 @@ Author: Dmitriy Yefremov False True - 4 + 5 + + + diff --git a/app/ui/download_dialog.py b/app/ui/download_dialog.py index 76fb06d6..5aa5a48f 100644 --- a/app/ui/download_dialog.py +++ b/app/ui/download_dialog.py @@ -1,3 +1,5 @@ +import os + from gi.repository import GLib from app.commons import run_idle, run_task @@ -6,8 +8,8 @@ from app.settings import SettingsType from app.ui.backup import backup_data, restore_data from app.ui.main_helper import append_text_to_tview from app.ui.settings_dialog import show_settings_dialog -from .uicommons import Gtk, UI_RESOURCES_PATH, TEXT_DOMAIN from .dialogs import show_dialog, DialogType, get_message +from .uicommons import Gtk, UI_RESOURCES_PATH class DownloadDialog: @@ -20,11 +22,11 @@ class DownloadDialog: handlers = {"on_receive": self.on_receive, "on_send": self.on_send, "on_settings_button": self.on_settings_button, - "on_preferences": self.on_preferences, + "on_settings": self.on_settings, + "on_profile_changed": self.on_profile_changed, "on_info_bar_close": self.on_info_bar_close} builder = Gtk.Builder() - builder.set_translation_domain(TEXT_DOMAIN) builder.add_from_file(UI_RESOURCES_PATH + "download_dialog.glade") builder.connect_signals(handlers) @@ -35,7 +37,6 @@ class DownloadDialog: self._message_label = builder.get_object("info_bar_message_label") self._text_view = builder.get_object("text_view") self._expander = builder.get_object("expander") - self._host_entry = builder.get_object("host_entry") self._data_path_entry = builder.get_object("data_path_entry") self._remove_unused_check_button = builder.get_object("remove_unused_check_button") @@ -52,12 +53,19 @@ class DownloadDialog: self._use_http_switch = builder.get_object("use_http_switch") self._http_radio_button = builder.get_object("http_radio_button") self._use_http_box = builder.get_object("use_http_box") + self._profile_combo_box = builder.get_object("profile_combo_box") + self.init_settings() def show(self): self._dialog_window.show() def init_settings(self): + self.update_profiles() + self.init_ui_settings() + + @run_idle + def init_ui_settings(self): self._host_entry.set_text(self._settings.host) self._data_path_entry.set_text(self._settings.data_local_path) is_enigma = self._s_type is SettingsType.ENIGMA_2 @@ -66,6 +74,12 @@ class DownloadDialog: self._use_http_box.set_visible(is_enigma) self._use_http_switch.set_active(is_enigma) + def update_profiles(self): + self._profile_combo_box.remove_all() + for p in self._settings.profiles: + self._profile_combo_box.append(p, p) + self._profile_combo_box.set_active_id(self._settings.current_profile) + @run_idle def on_receive(self, item): self.download(True, self.get_download_type()) @@ -108,11 +122,11 @@ class DownloadDialog: self._timeout_entry.set_text("") self._current_property = label - def on_preferences(self, item): + def on_settings(self, item): response = show_settings_dialog(self._dialog_window, self._settings) if response != Gtk.ResponseType.CANCEL: self._s_type = self._settings.setting_type - self.init_settings() + self.update_profiles() gen = self._update_settings_callback() GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW) @@ -121,6 +135,13 @@ class DownloadDialog: self.on_settings_button(button) break + def on_profile_changed(self, box): + active = box.get_active_text() + if active in self._settings.profiles: + self._settings.current_profile = active + self._profile_combo_box.set_active_id(active) + self.init_ui_settings() + def on_info_bar_close(self, bar=None, resp=None): self._info_bar.set_visible(False) @@ -135,8 +156,10 @@ class DownloadDialog: if download: if backup and d_type is not DownloadType.SATELLITES: data_path = self._settings.data_local_path or self._data_path_entry.get_text() + os.makedirs(os.path.dirname(data_path), exist_ok=True) backup_path = self._settings.backup_local_path or data_path + "backup/" backup_src = backup_data(data_path, backup_path, d_type is DownloadType.ALL) + download_data(settings=self._settings, download_type=d_type, callback=self.append_output) else: self.show_info_message(get_message("Please, wait..."), Gtk.MessageType.INFO) diff --git a/app/ui/main_app_window.py b/app/ui/main_app_window.py index e1c3897b..c15a0124 100644 --- a/app/ui/main_app_window.py +++ b/app/ui/main_app_window.py @@ -1,6 +1,5 @@ import os import sys - from contextlib import suppress from functools import lru_cache from itertools import chain @@ -21,21 +20,21 @@ from app.tools.media import Player from app.ui.epg_dialog import EpgDialog from app.ui.transmitter import LinksTransmitter from .backup import BackupDialog, backup_data, clear_data_path -from .imports import ImportDialog, import_bouquet -from .download_dialog import DownloadDialog -from .iptv import IptvDialog, SearchUnavailableDialog, IptvListConfigurationDialog, YtListImportDialog -from .search import SearchProvider -from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, LOCKED_ICON, HIDE_ICON, IPTV_ICON, MOVE_KEYS, KeyboardKey, Column, \ - FavClickMode from .dialogs import show_dialog, DialogType, get_chooser_dialog, WaitDialog, get_message +from .download_dialog import DownloadDialog +from .imports import ImportDialog, import_bouquet +from .iptv import IptvDialog, SearchUnavailableDialog, IptvListConfigurationDialog, YtListImportDialog from .main_helper import insert_marker, move_items, rename, ViewTarget, set_flags, locate_in_services, \ scroll_to, get_base_model, update_picons_data, copy_picon_reference, assign_picon, remove_picon, \ is_only_one_item_selected, gen_bouquets, BqGenType, get_iptv_url, append_picons, get_selection, get_model_data, \ remove_all_unused_picons from .picons_downloader import PiconsDialog from .satellites_dialog import show_satellites_dialog -from .settings_dialog import show_settings_dialog +from .search import SearchProvider from .service_details_dialog import ServiceDetailsDialog, Action +from .settings_dialog import show_settings_dialog +from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, LOCKED_ICON, HIDE_ICON, IPTV_ICON, MOVE_KEYS, KeyboardKey, Column, \ + FavClickMode class Application(Gtk.Application): @@ -75,7 +74,8 @@ class Application(Gtk.Application): handlers = {"on_close_app": self.on_close_app, "on_resize": self.on_resize, "on_about_app": self.on_about_app, - "on_preferences": self.on_preferences, + "on_settings": self.on_settings, + "on_profile_changed": self.on_profile_changed, "on_download": self.on_download, "on_data_open": self.on_data_open, "on_data_save": self.on_data_save, @@ -215,7 +215,6 @@ class Application(Gtk.Application): self._app_info_box.bind_property("visible", builder.get_object("left_header_box"), "sensitive", 4) # Status bar self._profile_combo_box = builder.get_object("profile_combo_box") - self._profile_combo_box.set_tooltip_text(self._profile_combo_box.get_tooltip_text() + self._settings.host) self._receiver_info_box = builder.get_object("receiver_info_box") self._receiver_info_label = builder.get_object("receiver_info_label") self._signal_box = builder.get_object("signal_box") @@ -288,10 +287,11 @@ class Application(Gtk.Application): def do_startup(self): Gtk.Application.do_startup(self) - self.update_profile_label() + self.init_profiles() self.init_drag_and_drop() self.init_colors() - self.init_http_api() + gen = self.init_http_api() + GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW) def do_activate(self): self._main_window.set_application(self) @@ -315,6 +315,11 @@ class Application(Gtk.Application): self.activate() return 0 + @run_idle + def init_profiles(self): + self.update_profiles() + self._profile_combo_box.set_active_id(self._settings.default_profile) + def init_drag_and_drop(self): """ Enable drag-and-drop """ target = [] @@ -807,7 +812,7 @@ class Application(Gtk.Application): DownloadDialog(transient=self._main_window, settings=self._settings, open_data_callback=self.open_data, - update_settings_callback=self.update_options).show() + update_settings_callback=self.update_settings).show() @run_task def on_download_data(self): @@ -1167,25 +1172,46 @@ class Application(Gtk.Application): for v in [view, *args]: v.get_selection().unselect_all() - def on_preferences(self, item): + def on_settings(self, item): response = show_settings_dialog(self._main_window, self._settings) if response != Gtk.ResponseType.CANCEL: - gen = self.update_options() + gen = self.update_settings() GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW) - def update_options(self): - profile = self._settings.setting_type + def update_settings(self): + s_type = self._settings.setting_type - if profile != self._s_type: + if s_type != self._s_type: yield from self.show_app_info(True) - self._s_type = profile + self._s_type = s_type c_gen = self.clear_current_data() yield from c_gen - self.update_profile_label() + self.init_colors(True) + self.init_profiles() yield True - self.init_http_api() - yield True + gen = self.init_http_api() + yield from gen + + def on_profile_changed(self, box): + if self._app_info_box.get_visible(): + self.update_profile_label() + return + + active = box.get_active_text() + if active in self._settings.profiles: + self._settings.current_profile = active + self._s_type = self._settings.setting_type + self._profile_combo_box.set_tooltip_text(self._profile_combo_box.get_tooltip_text() + self._settings.host) + self.update_profile_label() + + if self._http_api and self._settings.http_api_support: + self._http_api.init() + + def update_profiles(self): + self._profile_combo_box.remove_all() + for p in self._settings.profiles: + self._profile_combo_box.append(p, p) def on_tree_view_key_press(self, view, event): """ Handling keystrokes on press """ @@ -1654,7 +1680,7 @@ class Application(Gtk.Application): self._links_transmitter.hide() # ************************ HTTP API ****************************# - @run_task + def init_http_api(self): self._fav_click_mode = FavClickMode(self._settings.fav_click_mode) http_api_enable = self._settings.http_api_support @@ -1663,20 +1689,20 @@ class Application(Gtk.Application): GLib.idle_add(self._http_status_image.set_visible, status) if self._s_type is SettingsType.NEUTRINO_MP or not http_api_enable: - self.update_info_boxes_visible(False) + GLib.idle_add(self.update_info_boxes_visible, False) if self._http_api: self._http_api.close() + yield True self._http_api = None self.init_send_to(False) return if not self._http_api: - self._http_api = HttpAPI(self._settings.host, self._settings.http_port, - self._settings.http_user, self._settings.http_password) - + self._http_api = HttpAPI(self._settings) GLib.timeout_add_seconds(3, self.update_info, priority=GLib.PRIORITY_LOW) self.init_send_to(http_api_enable and self._settings.enable_send_to) + yield True @run_idle def init_send_to(self, enable): @@ -2066,6 +2092,7 @@ class Application(Gtk.Application): # ***************** Profile label *********************# + @run_idle def update_profile_label(self): label, sep, ip = self._profile_combo_box.get_tooltip_text().partition(":") profile_name = self._profile_combo_box.get_active_text() diff --git a/app/ui/main_window.glade b/app/ui/main_window.glade index b9eb54a4..4b95d72c 100644 --- a/app/ui/main_window.glade +++ b/app/ui/main_window.glade @@ -512,7 +512,7 @@ Author: Dmitriy Yefremov True 2 Settings - + True @@ -2718,7 +2718,6 @@ Author: Dmitriy Yefremov True False - 2 False @@ -2799,7 +2798,6 @@ Author: Dmitriy Yefremov - 35 False @@ -2844,6 +2842,7 @@ Author: Dmitriy Yefremov + True False False Current IP: @@ -2852,22 +2851,21 @@ Author: Dmitriy Yefremov 1 1 0 - False True - - default - + - + False True - center + baseline + baseline 1 1 False False 9 - default + True + False gtk-connect diff --git a/app/ui/settings_dialog.glade b/app/ui/settings_dialog.glade index 8c949c1a..7d756a91 100644 --- a/app/ui/settings_dialog.glade +++ b/app/ui/settings_dialog.glade @@ -64,6 +64,7 @@ Author: Dmitriy Yefremov True True center + True @@ -185,6 +186,7 @@ Author: Dmitriy Yefremov True + True False 5 5 @@ -195,7 +197,6 @@ Author: Dmitriy Yefremov 155 True False - Set as default vertical 2 @@ -212,6 +213,7 @@ Author: Dmitriy Yefremov profile_lists_tore False 0 + @@ -265,7 +267,6 @@ Author: Dmitriy Yefremov True - False False Add Add @@ -280,7 +281,6 @@ Author: Dmitriy Yefremov - True False Edit Edit @@ -988,7 +988,6 @@ Author: Dmitriy Yefremov True True - /data/picons gtk-edit folder-open False @@ -1018,7 +1017,6 @@ Author: Dmitriy Yefremov True True - /data gtk-edit folder-open False @@ -1047,7 +1045,6 @@ Author: Dmitriy Yefremov True True - /data/backup gtk-edit folder-open False diff --git a/app/ui/settings_dialog.py b/app/ui/settings_dialog.py index 2376d8b9..8347eeff 100644 --- a/app/ui/settings_dialog.py +++ b/app/ui/settings_dialog.py @@ -1,10 +1,13 @@ +import os from enum import Enum +from pathlib import Path from app.commons import run_task, run_idle from app.connections import test_telnet, test_ftp, TestException, test_http -from app.settings import SettingsType +from app.settings import SettingsType, Settings +from app.ui.dialogs import show_dialog, DialogType +from .main_helper import update_entry_data, scroll_to from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, FavClickMode, DEFAULT_ICON -from .main_helper import update_entry_data def show_settings_dialog(transient, options): @@ -19,10 +22,11 @@ class Property(Enum): class SettingsDialog: - def __init__(self, transient, settings): + def __init__(self, transient, settings: Settings): handlers = {"on_field_icon_press": self.on_field_icon_press, "on_settings_type_changed": self.on_settings_type_changed, "on_reset": self.on_reset, + "on_response": self.on_response, "apply_settings": self.apply_settings, "on_connection_test": self.on_connection_test, "on_info_bar_close": self.on_info_bar_close, @@ -36,6 +40,7 @@ class SettingsDialog: "on_profile_deleted": self.on_profile_deleted, "on_profile_inserted": self.on_profile_inserted, "on_profile_edited": self.on_profile_edited, + "on_profile_selected": self.on_profile_selected, "on_profile_set_default": self.on_profile_set_default} builder = Gtk.Builder() @@ -70,7 +75,7 @@ class SettingsDialog: self._info_bar = builder.get_object("info_bar") self._message_label = builder.get_object("info_bar_message_label") self._test_spinner = builder.get_object("test_spinner") - # Profile + # Settings type self._enigma_radio_button = builder.get_object("enigma_radio_button") self._neutrino_radio_button = builder.get_object("neutrino_radio_button") self._support_ver5_switch = builder.get_object("support_ver5_switch") @@ -97,14 +102,15 @@ class SettingsDialog: self._extra_support_grid.bind_property("sensitive", builder.get_object("v5_support_grid"), "sensitive") # Profiles self._profile_view = builder.get_object("profile_tree_view") + self._profile_add_button = builder.get_object("profile_add_button") self._profile_remove_button = builder.get_object("profile_remove_button") - self._profile_view.get_model().append(("default", DEFAULT_ICON)) # Settings self._settings = settings - self._profiles = settings.profiles - self._s_type = settings.setting_type + self._profiles = self._settings.profiles + self._s_type = self._settings.setting_type self.set_settings() self.init_ui_elements(self._s_type) + self.init_profiles() @run_idle def init_ui_elements(self, s_type): @@ -116,10 +122,15 @@ class SettingsDialog: self._extra_support_grid.set_sensitive(is_enigma_profile) http_active = self._support_http_api_switch.get_active() self._click_mode_zap_button.set_sensitive(is_enigma_profile and http_active) - self._profile_remove_button.set_sensitive(len(self._profile_view.get_model()) > 1) self.on_info_bar_close() if is_enigma_profile else self.show_info_message( "The Neutrino has only experimental support. Not all features are supported!", Gtk.MessageType.WARNING) + def init_profiles(self): + p_def = self._settings.default_profile + for p in self._profiles: + self._profile_view.get_model().append((p, DEFAULT_ICON if p == p_def else None)) + self._profile_remove_button.set_sensitive(len(self._profile_view.get_model()) > 1) + def update_header_bar(self): label, sep, st = self._header_bar.get_subtitle().partition(":") if self._s_type is SettingsType.ENIGMA_2: @@ -128,12 +139,14 @@ class SettingsDialog: self._header_bar.set_subtitle("{}: {}".format(label, self._neutrino_radio_button.get_label())) def show(self): - response = self._dialog.run() - if response == Gtk.ResponseType.OK: - self.apply_settings() - self._dialog.destroy() + self._dialog.run() - return response + def on_response(self, dialog, resp): + if resp == Gtk.ResponseType.OK and not self.apply_settings(): + return + + self._dialog.destroy() + return resp def on_field_icon_press(self, entry, icon, event_button): update_entry_data(entry, self._dialog, self._settings) @@ -149,6 +162,7 @@ class SettingsDialog: self._settings.reset() self.set_settings() + @run_idle def set_settings(self): self._host_field.set_text(self._settings.host) self._port_field.set_text(self._settings.port) @@ -186,6 +200,9 @@ class SettingsDialog: self._extra_color_button.set_rgba(extra_rgb) def apply_settings(self, item=None): + if show_dialog(DialogType.QUESTION, self._dialog) == Gtk.ResponseType.CANCEL: + return + self._s_type = SettingsType.ENIGMA_2 if self._enigma_radio_button.get_active() else SettingsType.NEUTRINO_MP self._settings.setting_type = self._s_type self._settings.host = self._host_field.get_text() @@ -219,7 +236,9 @@ class SettingsDialog: self._settings.enable_yt_dl = self._enable_y_dl_switch.get_active() self._settings.enable_send_to = self._enable_send_to_switch.get_active() + self._settings.default_profile = list(filter(lambda r: r[1], self._profile_view.get_model()))[0][0] self._settings.save() + return True @run_task def on_connection_test(self, item): @@ -300,8 +319,15 @@ class SettingsDialog: while name in self._profiles: count += 1 name = "profile{}".format(count) - self._profiles[name] = {"host": self._host_field.get_text()} - itr = model.append((name, None)) + + self._profiles[name] = self._s_type.get_default_settings() + model.append((name, None)) + scroll_to(len(model) - 1, self._profile_view) + self.on_profile_selected(self._profile_view) + p = name + "/" + self._settings.data_local_path += p + self._settings.picons_local_path += p + self._settings.backup_local_path += p def on_profile_edit(self, item): self.show_info_message("Not implemented yet!", Gtk.MessageType.WARNING) @@ -310,21 +336,61 @@ class SettingsDialog: model, paths = self._profile_view.get_selection().get_selected_rows() if paths: row = model[paths] + is_default = row[1] self._profiles.pop(row[0], None) del model[paths] + if is_default: + model.set_value(model.get_iter_first(), 1, DEFAULT_ICON) def on_profile_deleted(self, model, paths): self._profile_remove_button.set_sensitive(len(model) > 1) - def on_profile_edited(self, render, path, new_text): + def on_profile_edited(self, render, path, new_value): p_name = render.get_property("text") - p_data = self._profiles.pop(p_name, None) - row = self._profile_view.get_model()[path] - row[0] = new_text - self._profiles[new_text] = p_data + p_name = self._profiles.pop(p_name, None) + if p_name: + row = self._profile_view.get_model()[path] + row[0] = new_value + self._profiles[new_value] = p_name + if p_name == self._settings.current_profile: + self._settings.current_profile = new_value + if p_name == self._settings.default_profile: + self._settings.default_profile = new_value + + if p_name != new_value: + self.update_local_paths(new_value) + self.on_profile_selected(self._profile_view) + + def update_local_paths(self, profile_name): + data_path = self._settings.data_local_path + self._settings.data_local_path = "{}/{}/".format(Path(data_path).parent, profile_name) + if os.path.isdir(data_path): + os.rename(data_path, self._settings.data_local_path) + + picons_path = self._settings.picons_local_path + self._settings.picons_local_path = "{}/{}/".format(Path(picons_path).parent, profile_name) + if os.path.isdir(picons_path): + os.rename(picons_path, self._settings.picons_local_path) + + backup_path = self._settings.backup_local_path + self._settings.backup_local_path = "{}/{}/".format(Path(self._settings.backup_local_path).parent, profile_name) + if os.path.isdir(backup_path): + os.rename(backup_path, self._settings.backup_local_path) + + def on_profile_selected(self, view): + model, paths = self._profile_view.get_selection().get_selected_rows() + if paths: + profile = model.get_value(model.get_iter(paths), 0) + self._settings.current_profile = profile + self.set_settings() def on_profile_set_default(self, item): - self.show_info_message("Not implemented yet!", Gtk.MessageType.WARNING) + model, paths = self._profile_view.get_selection().get_selected_rows() + if paths: + itr = model.get_iter(paths) + model.foreach(lambda m, p, i: model.set_value(i, 1, None)) + model.set_value(itr, 1, DEFAULT_ICON) + self._settings.default_profile = model.get_value(itr, 0) def on_profile_inserted(self, model, path, itr): self._profile_remove_button.set_sensitive(len(model) > 1)