diff --git a/app/settings.py b/app/settings.py index 5f42ff24..d3851147 100644 --- a/app/settings.py +++ b/app/settings.py @@ -9,10 +9,11 @@ from pathlib import Path from pprint import pformat from textwrap import dedent +SEP = os.sep HOME_PATH = str(Path.home()) -CONFIG_PATH = HOME_PATH + "/.config/demon-editor/" +CONFIG_PATH = HOME_PATH + "{}.config{}demon-editor{}".format(SEP, SEP, SEP) CONFIG_FILE = CONFIG_PATH + "config.json" -DATA_PATH = HOME_PATH + "/DemonEditor/data/" +DATA_PATH = HOME_PATH + "{}DemonEditor{}data{}".format(SEP, SEP, SEP) GTK_PATH = os.environ.get("GTK_PATH", None) IS_DARWIN = sys.platform == "darwin" @@ -27,7 +28,7 @@ class Defaults(Enum): BACKUP_BEFORE_SAVE = True V5_SUPPORT = False FORCE_BQ_NAMES = False - HTTP_API_SUPPORT = False + HTTP_API_SUPPORT = True ENABLE_YT_DL = False ENABLE_SEND_TO = False USE_COLORS = True @@ -37,18 +38,18 @@ class Defaults(Enum): LIST_PICON_SIZE = 32 FAV_CLICK_MODE = 0 PLAY_STREAMS_MODE = 1 if IS_DARWIN else 0 - STREAM_LIB = "vlc" + STREAM_LIB = "mpv" if IS_WIN else "vlc" PROFILE_FOLDER_DEFAULT = False - RECORDS_PATH = DATA_PATH + "records/" + RECORDS_PATH = DATA_PATH + "records{}".format(SEP) ACTIVATE_TRANSCODING = False - ACTIVE_TRANSCODING_PRESET = "720p TV/device" + ACTIVE_TRANSCODING_PRESET = "720p TV{}device".format(SEP) def get_settings(): if not os.path.isfile(CONFIG_FILE) or os.stat(CONFIG_FILE).st_size == 0: write_settings(get_default_settings()) - with open(CONFIG_FILE, "r") as config_file: + with open(CONFIG_FILE, "r", encoding="utf-8") as config_file: return json.load(config_file) @@ -82,18 +83,18 @@ def get_default_transcoding_presets(): def write_settings(config): os.makedirs(os.path.dirname(CONFIG_PATH), exist_ok=True) - with open(CONFIG_FILE, "w") as config_file: + with open(CONFIG_FILE, "w", encoding="utf-8") as config_file: json.dump(config, config_file, indent=" ") def set_local_paths(settings, profile_name, data_path=DATA_PATH, use_profile_folder=False): - settings["data_local_path"] = "{}{}/".format(data_path, profile_name) + settings["data_local_path"] = "{}{}{}".format(data_path, profile_name, SEP) if use_profile_folder: - settings["picons_local_path"] = "{}{}/{}/".format(data_path, profile_name, "picons") - settings["backup_local_path"] = "{}{}/{}/".format(data_path, profile_name, "backup") + settings["picons_local_path"] = "{}{}{}{}{}".format(data_path, profile_name, SEP, "picons", SEP) + settings["backup_local_path"] = "{}{}{}{}{}".format(data_path, profile_name, SEP, "backup", SEP) else: - settings["picons_local_path"] = "{}{}/{}/".format(data_path, "picons", profile_name) - settings["backup_local_path"] = "{}{}/{}/".format(data_path, "backup", profile_name) + settings["picons_local_path"] = "{}{}{}{}{}".format(data_path, "picons", SEP, profile_name, SEP) + settings["backup_local_path"] = "{}{}{}{}{}".format(data_path, "backup", SEP, profile_name, SEP) class SettingsType(IntEnum): @@ -110,11 +111,11 @@ class SettingsType(IntEnum): "http_port": "80", "http_timeout": 5, "http_use_ssl": False, "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/", + "satellites_xml_path": "/etc/tuxbox/", "data_local_path": "{}enigma2{}".format(DATA_PATH, SEP), "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: + "picons_local_path": "{}enigma2{}picons{}".format(DATA_PATH, SEP, SEP), + "backup_local_path": "{}enigma2{}backup{}".format(DATA_PATH, SEP, SEP)} + elif self is SettingsType.NEUTRINO_MP: return {"setting_type": self, "host": "127.0.0.1", "port": "21", "timeout": 5, "user": "root", "password": "root", diff --git a/app/ui/dialogs.glade b/app/ui/dialogs.glade index d36fb7b9..1b2a0599 100644 --- a/app/ui/dialogs.glade +++ b/app/ui/dialogs.glade @@ -30,7 +30,7 @@ Author: Dmitriy Yefremov - + @@ -43,7 +43,7 @@ Author: Dmitriy Yefremov 2.0.0 Alpha 2018-2021 Dmitriy Yefremov - Enigma2 channel and satellite list editor for GNU/Linux. + Enigma2 channel and satellite list editor. https://dyefremov.github.io/DemonEditor/ Это приложение распространяется без каких-либо гарантий. Подробнее в <a href="http://opensource.org/licenses/mit-license.php">The MIT License (MIT)</a>. @@ -63,9 +63,11 @@ Author: Dmitriy Yefremov vertical 2 - + False - end + 50 + 50 + expand False @@ -86,36 +88,61 @@ Author: Dmitriy Yefremov center 320 True - dialog + utility True True - center - - - gtk-cancel - True - True - True - True - True - - - - - gtk-ok - True - True - True - True - True - - + + False + 5 + 5 + 4 vertical 2 + + + False + center + end + + + Cancel + True + True + True + center + + + False + False + 0 + + + + + OK + True + True + True + center + + + + False + False + 1 + + + + + False + False + 0 + + True @@ -124,7 +151,7 @@ Author: Dmitriy Yefremov 2 2 2 - gtk-edit + document-edit-symbolic False False False @@ -132,7 +159,7 @@ Author: Dmitriy Yefremov False True - 1 + 0 @@ -142,7 +169,7 @@ Author: Dmitriy Yefremov input_dialog_ok_button - + False False True @@ -153,68 +180,43 @@ Author: Dmitriy Yefremov True False - - - - - 120 + + 100 + True False + 5 + 5 vertical - - + + + 150 + 45 + True False - 0 - end + True False - False + True 0 - + True False - 2 - vertical - - - 150 - 45 - True - False - True - - - False - True - 0 - - - - - True - False - 10 - 10 - Loading data... - - - False - True - 1 - - + 10 + 10 + Loading data... - True + False True 1 - + diff --git a/app/ui/dialogs.py b/app/ui/dialogs.py index e02f6aa3..6062be45 100644 --- a/app/ui/dialogs.py +++ b/app/ui/dialogs.py @@ -1,10 +1,40 @@ +# -*- coding: utf-8 -*- +# +# The MIT License (MIT) +# +# Copyright (c) 2018-2021 Dmitriy Yefremov +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# +# Author: Dmitriy Yefremov +# + + """ Common module for showing dialogs """ import gettext from enum import Enum from functools import lru_cache from pathlib import Path +import xml.etree.ElementTree as ET from app.commons import run_idle +from app.settings import SEP, IS_WIN from .uicommons import Gtk, UI_RESOURCES_PATH, TEXT_DOMAIN, IS_GNOME_SESSION @@ -17,12 +47,11 @@ class Dialog(Enum): {use_header} False True - 320 + 250 True dialog True True - center {message_type} {buttons_type} @@ -104,12 +133,10 @@ def get_chooser_dialog(transient, settings, name, patterns, title=None): def get_file_chooser_dialog(transient, text, settings, action_type, file_filter, buttons=None, title=None, dirs=False): - text = get_message(text) if text else "" action_type = Gtk.FileChooserAction.SELECT_FOLDER if action_type is None else action_type - buttons = buttons or (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK) - dialog = Gtk.FileChooserDialog(text, transient, action_type, buttons, use_header_bar=IS_GNOME_SESSION) - dialog.set_title(get_message(title) if title else "") + dialog = Gtk.FileChooserNative.new(get_message(title) if title else "", transient, action_type) dialog.set_create_folders(dirs) + dialog.set_modal(True) if file_filter is not None: dialog.add_filter(file_filter) @@ -117,10 +144,10 @@ def get_file_chooser_dialog(transient, text, settings, action_type, file_filter, dialog.set_current_folder(settings.data_local_path) response = dialog.run() - if response not in (Gtk.ResponseType.CANCEL, Gtk.ResponseType.DELETE_EVENT): + if response == Gtk.ResponseType.ACCEPT: path = Path(dialog.get_filename() or dialog.get_current_folder()) if path.is_dir(): - response = "{}/".format(path.resolve()) + response = "{}{}".format(path.resolve(), SEP) elif path.is_file(): response = str(path.resolve()) dialog.destroy() @@ -180,31 +207,49 @@ def get_message(message): @lru_cache(maxsize=5) -def get_dialogs_string(path): - with open(path, "r") as f: - return "".join(f) +def get_dialogs_string(path, tag="property"): + if IS_WIN: + return translate_xml(path, tag) + else: + with open(path, "r", encoding="utf-8") as f: + return "".join(f) -def get_builder(path, handlers=None, use_str=False, objects=None): +def get_builder(path, handlers=None, use_str=False, objects=None, tag="property"): """ Creates and returns a Gtk.Builder instance. """ builder = Gtk.Builder() builder.set_translation_domain(TEXT_DOMAIN) if use_str: if objects: - builder.add_objects_from_string(get_dialogs_string(path).format(use_header=IS_GNOME_SESSION), objects) + builder.add_objects_from_string(get_dialogs_string(path, tag).format(use_header=IS_GNOME_SESSION), objects) else: - builder.add_from_string(get_dialogs_string(path).format(use_header=IS_GNOME_SESSION)) + builder.add_from_string(get_dialogs_string(path, tag).format(use_header=IS_GNOME_SESSION)) else: if objects: - builder.add_objects_from_file(path, objects) + builder.add_objects_from_string(get_dialogs_string(path, tag), objects) else: - builder.add_from_file(path) + builder.add_from_string(get_dialogs_string(path, tag)) builder.connect_signals(handlers or {}) return builder +def translate_xml(path, tag="property"): + """ + Used to translate GUI from * .glade files in MS Windows. + + More info: https://gitlab.gnome.org/GNOME/gtk/-/issues/569 + """ + et = ET.parse(path) + root = et.getroot() + for e in root.iter(tag): + if e.attrib.get("translatable", None) == "yes": + e.text = get_message(e.text) + + return ET.tostring(root, encoding="unicode", method="xml") + + if __name__ == "__main__": pass