diff --git a/app/settings.py b/app/settings.py index 0f6430e8..29e98777 100644 --- a/app/settings.py +++ b/app/settings.py @@ -619,8 +619,13 @@ class Settings: self._settings["extra_color"] = value @property + @lru_cache(1) def dark_mode(self): - return self._settings.get("dark_mode", False) + import subprocess + + cmd = ["defaults", "read", "-g", "AppleInterfaceStyle"] + p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate() + return "Dark" in str(p[0]) @dark_mode.setter def dark_mode(self, value): diff --git a/app/tools/media.py b/app/tools/media.py index e5c964de..62e2665b 100644 --- a/app/tools/media.py +++ b/app/tools/media.py @@ -3,7 +3,9 @@ import sys from abc import ABC, abstractmethod from datetime import datetime -from app.commons import run_task, log, _DATE_FORMAT +from gi.repository import Gdk, Gtk + +from app.commons import run_task, log, _DATE_FORMAT, run_with_delay class Player(ABC): @@ -69,11 +71,10 @@ class Player(ABC): return get_pointer(gpointer) def get_video_widget(self, widget): - from gi.repository import Gtk, Gdk - area = Gtk.DrawingArea(visible=True) area.connect("draw", self.on_drawing_area_draw) - area.set_events(Gdk.ModifierType.BUTTON1_MASK) + area.connect("motion-notify-event", self.on_mouse_motion) + area.set_events(Gdk.EventMask.BUTTON_PRESS_MASK | Gdk.EventMask.POINTER_MOTION_MASK) widget.add(area) return area @@ -83,6 +84,19 @@ class Player(ABC): cr.set_source_rgb(0, 0, 0) cr.paint() + def on_mouse_motion(self, widget, event): + display = widget.get_display() + window = widget.get_window() + cursor = Gdk.Cursor.new_from_name(display, "default") + window.set_cursor(cursor) + + self.hide_mouse_cursor(window, display) + + @run_with_delay(3) + def hide_mouse_cursor(self, window, display): + cursor = Gdk.Cursor.new_for_display(display, Gdk.CursorType.BLANK_CURSOR) + window.set_cursor(cursor) + @staticmethod def make(name, mode, widget, buf_cb=None, position_cb=None, error_cb=None, playing_cb=None): """ Factory method. We will not use a separate factory to return a specific implementation. @@ -118,7 +132,10 @@ class MpvPlayer(Player): try: from app.tools import mpv - self._player = mpv.MPV(wid=str(self.get_window_handle(self.get_video_widget(widget)))) + self._player = mpv.MPV(wid=str(self.get_window_handle(self.get_video_widget(widget), )), + input_default_bindings=False, + input_cursor=False, + cursor_autohide="no") except OSError as e: log("{}: Load library error: {}".format(__class__.__name__, e)) raise ImportError("No libmpv is found. Check that it is installed!") @@ -189,6 +206,11 @@ class GstPlayer(Player): from gi.repository import Gst, GstVideo # Initialization of GStreamer. Gst.init(sys.argv) + gtk_sink = Gst.ElementFactory.make("gtksink") + if not gtk_sink: + msg = "GStreamer error: gtksink plugin not installed!" + log(msg) + raise ImportError(msg) except (OSError, ValueError) as e: log("{}: Load library error: {}".format(__class__.__name__, e)) raise ImportError("No GStreamer is found. Check that it is installed!") @@ -203,10 +225,11 @@ class GstPlayer(Player): self._is_playing = False self._player = Gst.ElementFactory.make("playbin", "player") # Initialization of the playback widget. - vid_widget = self.get_video_widget(widget) + self._player.set_property("video-sink", gtk_sink) + vid_widget = gtk_sink.get_property("widget") + vid_widget.connect("motion-notify-event", self.on_mouse_motion) widget.add(vid_widget) vid_widget.show() - self._player.set_window_handle(self.get_window_handle(vid_widget)) bus = self._player.get_bus() bus.add_signal_watch() @@ -316,6 +339,8 @@ class VlcPlayer(Player): args = "--quiet {}".format("" if sys.platform == "darwin" else "--no-xlib") self._player = vlc.Instance(args).media_player_new() + vlc.libvlc_video_set_key_input(self._player, False) + vlc.libvlc_video_set_mouse_input(self._player, False) except (OSError, AttributeError) as e: log("{}: Load library error: {}".format(__class__.__name__, e)) raise ImportError("No VLC is found. Check that it is installed!") diff --git a/app/tools/picons.py b/app/tools/picons.py index 39d47d57..7fce4992 100644 --- a/app/tools/picons.py +++ b/app/tools/picons.py @@ -173,20 +173,11 @@ class ProviderParser(HTMLParser): url = attrs[0][1] if any(d in url for d in self._DOMAINS): self._current_row.append(url) - if tag == "font" and len(attrs) == 1: - atr = attrs[0] - if len(atr) == 2 and atr[1] == "darkgreen": - self._is_onid_tid = True def handle_data(self, data): """ Save content to a cell """ if self._is_td or self._is_th: self._current_cell.append(data.strip()) - if self._is_onid_tid: - m = self._ONID_TID_PATTERN.match(data) - if m: - self._on_id, tid = m.group().split("-") - self._is_onid_tid = False def handle_endtag(self, tag): if tag == 'td': @@ -208,32 +199,34 @@ class ProviderParser(HTMLParser): len_row = len(row) if len_row > 2: - m = self._TRANSPONDER_FREQUENCY_PATTERN.match(row[1]) + m = self._TRANSPONDER_FREQUENCY_PATTERN.match(row[0]) if m: self._freq = m.group().split()[0] - if len_row == 14: + if len_row > 12: # Providers - name = row[6] + name = row[5] self._prv_names.add(name) - m = self._ONID_TID_PATTERN.match(str(row[9])) + m = self._ONID_TID_PATTERN.match(str(row[-5])) if m: on_id, tid = m.group().split("-") if on_id not in self._ids: + self._on_id = on_id row[-2] = on_id self._ids.add(on_id) row[0] = self._positon if name + on_id not in self._prv_names: self._prv_names.add(name + on_id) logo_data = None - req = requests.get(self._BASE_URL + row[3], timeout=5) - if req.status_code == 200: - logo_data = req.content - else: - log("Downloading provider logo error: {}".format(req.reason)) - self.rows.append(Provider(logo=logo_data, name=name, pos=self._positon, url=row[5], on_id=on_id, + if row[2].startswith("/logo/"): + req = requests.get(self._BASE_URL + row[2], timeout=5) + if req.status_code == 200: + logo_data = req.content + else: + log("Downloading provider logo error: {}".format(req.reason)) + self.rows.append(Provider(logo=logo_data, name=name, pos=self._positon, url=row[6], on_id=on_id, ssid=None, single=False, selected=True)) - elif 6 < len_row < 14: + elif 6 < len_row < 12: # Single services name, url, ssid = None, None, None if row[0].startswith("http"): diff --git a/app/tools/satellites.py b/app/tools/satellites.py index c0df73f6..7ca596bc 100644 --- a/app/tools/satellites.py +++ b/app/tools/satellites.py @@ -77,6 +77,8 @@ class Cell: class SatellitesParser(HTMLParser): """ Parser for satellite html page. """ + POS_PAT = re.compile(r".*?(\d+\.\d°?[EW]).*") + def __init__(self, source=SatelliteSource.FLYSAT, entities=False, separator=' '): HTMLParser.__init__(self) @@ -150,40 +152,21 @@ class SatellitesParser(HTMLParser): return list(map(get_sat, filter(lambda x: all(x) and len(x) == 5, self._rows))) elif self._source is SatelliteSource.LYNGSAT: - extra_pattern = re.compile(r"^https://www\.lyngsat\.com/[\w-]+\.html") base_url = "https://www.lyngsat.com/" sats = [] - names = set() - current_pos = "0" - for row in filter(lambda x: len(x) in (5, 7, 8), self._rows): - r_len = len(row) - if r_len == 7: - current_pos = self.parse_position(row[2]) - name = row[1].rsplit("/")[-1].rstrip(".html").replace("-", " ") - if name not in names: - # [all in one] satellites - sats.append((name, current_pos, row[5], base_url + row[1], False)) - names.add(name) - name = row[4] - if name not in names: - sats.append((name, current_pos, row[5], base_url + row[3], False)) - names.add(name) - if r_len == 8: # for a very limited number of satellites - data = list(filter(None, row)) - urls = set() - sat_type = "" - for d in data: - url = re.match(extra_pattern, d) - if url: - urls.add(url.group(0)) - if d in ("C", "Ku", "CKu"): - sat_type = d - current_pos = self.parse_position(data[1]) - for url in urls: - name = url.rsplit("/")[-1].rstrip(".html").replace("-", " ") - sats.append((name, current_pos, sat_type, base_url + url, False)) - elif r_len == 5: - sats.append((row[2], current_pos, row[3], base_url + row[1], False)) + cur_pos = "0" + for row in filter(lambda x: 3 < len(x) < 8, self._rows): + if not row[0]: + row = row[1:] + + pos = self.parse_position(row[1]) + if not self.POS_PAT.match(pos): + if len(row) == 4 and row[0].endswith(".html"): + sats.append((row[1], cur_pos, row[-2], base_url + row[0], False)) + continue + + sats.append((row[-3], pos, row[-2], base_url + row[0], False)) + cur_pos = pos return sats elif source is SatelliteSource.KINGOFSAT: def get_sat(r): @@ -321,11 +304,10 @@ class SatellitesParser(HTMLParser): Since the *.ini file contains incomplete information, it is not used. """ zeros = "000" - pos_pat = re.compile(r".*?(\d+\.\d°[EW]).*") pat = re.compile( r"(\d+).00\s+([RLHV])\s+(DVB-S[2]?)\s+(?:T2-MI, PLP (\d+)\s+)?(.*PSK).*?(?:Stream\s+(\d+))?\s+(\d+)\s+(\d+/\d+)$") - for row in filter(lambda r: len(r) == 16 and pos_pat.match(r[0]), self._rows): + for row in filter(lambda r: len(r) == 16 and self.POS_PAT.match(r[0]), self._rows): res = pat.search(" ".join((row[0], row[2], row[3], row[8], row[9], row[10]))) if res: freq, sr, pol, fec, sys = res.group(1), res.group(7), res.group(2), res.group(8), res.group(3) @@ -343,9 +325,10 @@ class ServicesParser(HTMLParser): HTMLParser.__init__(self) - self._S_TYPES = {"": "2", "MPEG-2 SD": "1", "SD": "1", "MPEG-4 SD": "22", "HEVC SD": "22", "MPEG-4 HD": "25", - "MPEG-4 HD 1080": "25", "MPEG-4 HD 720": "25", "HEVC HD": "25", "HEVC UHD": "31", - "HEVC UHD 4K": "31"} + self._S_TYPES = {"": "2", "MPEG-2 SD": "1", "MPEG-2/SD": "1", "SD": "1", "MPEG-4 SD": "22", "MPEG-4/SD": "22", + "MPEG-4": "22", "HEVC SD": "22", "MPEG-4/HD": "25", "MPEG-4 HD": "25", "MPEG-4 HD 1080": "25", + "MPEG-4 HD 720": "25", "HEVC HD": "25", "HEVC/HD": "25", "HEVC": "31", "HEVC/UHD": "31", + "HEVC UHD": "31", "HEVC UHD 4K": "31"} self._TR_PAT = re.compile( r".*?(\d+)\s+([RLHV]).*(DVB-S[2]?)/?(.*PSK)?\s(T2-MI)?\s?SR-FEC:\s(\d+)-(\d/\d)\s+.*ONID-TID:\s+(\d+)-(\d+).*") self._POS_PAT = re.compile(r".*?(\d+\.\d°[EW]).*") @@ -421,8 +404,8 @@ class ServicesParser(HTMLParser): log(e) else: url = "https://www.lyngsat.com/muxes/" - return [row[1] for row in - filter(lambda x: x and len(x) > 8 and x[1].url and x[1].url.startswith(url), self._rows)] + return [row[0] for row in + filter(lambda x: x and len(x) > 8 and x[0].url and x[0].url.startswith(url), self._rows)] return [] def get_transponder_services(self, tr_url, sat_position=None, use_pids=False): diff --git a/app/ui/app_menu_bar.ui b/app/ui/app_menu_bar.ui index 44694623..cb770e21 100644 --- a/app/ui/app_menu_bar.ui +++ b/app/ui/app_menu_bar.ui @@ -1,8 +1,30 @@ + +
+ + About + app.on_about_app + +
+
+ + Settings + app.on_settings + +
+
+ + Exit + app.on_close_app + +
+
File + app.hide_menu_bar + action-disabled
Import @@ -46,34 +68,26 @@ app.on_download
-
- - Settings - app.on_settings - -
-
- - Exit - app.on_close_app - -
Edit + app.hide_menu_bar + action-disabled
- - Lock - app.on_locked - - - Hide - app.on_hide - -
+ + Lock + app.on_locked + + + Hide + app.on_hide + +
View + app.hide_menu_bar + action-disabled
Search @@ -87,6 +101,8 @@ Tools + app.hide_menu_bar + action-disabled
Satellites editor @@ -103,52 +119,52 @@
-
- - IPTV - - Add IPTV or stream service - app.on_iptv - - - Import YouTube playlist - app.on_import_yt_list - - - Import m3u - app.on_import_m3u - - - Export to m3u - app.on_export_to_m3u - -
- - EPG configuration - app.on_epg_list_configuration - - - List configuration - app.on_iptv_list_configuration - -
-
- - Remove all unavailable - app.on_remove_all_unavailable - -
-
-
- Help + IPTV + app.hide_menu_bar + action-disabled + + Add IPTV or stream service + app.on_iptv + + + Import YouTube playlist + app.on_import_yt_list + + + Import m3u + app.on_import_m3u + + + Export to m3u + app.on_export_to_m3u +
- About - app.on_about_app + EPG configuration + app.on_epg_list_configuration + + + List configuration + app.on_iptv_list_configuration + +
+
+ + Remove all unavailable + app.on_remove_all_unavailable
+ + FTP client + app.show_ftp_menu + action-disabled + + Close + app.on_ftp_client_close + +
\ No newline at end of file diff --git a/app/ui/ftp.glade b/app/ui/ftp.glade index 3019ac8c..f81e836b 100644 --- a/app/ui/ftp.glade +++ b/app/ui/ftp.glade @@ -93,27 +93,97 @@ Author: Dmitriy Yefremov 0 in - - 320 - 240 + True - True - 5 - 5 - 5 - 5 + False vertical - True + 2 - + True False - vertical + 10 + 10 + 5 + 5 2 - + True False + True + Connect + + + + True + False + gtk-connect + + + + + False + True + 0 + + + + + False + True + Disconnect + + + + True + False + gtk-disconnect + + + + + False + True + 1 + + + + + False + bookmarks_list_store + 0 + + + False + True + 2 + + + + + False + True + 0 + + + + + 320 + 240 + True + True + 5 + 5 + 5 + 5 + True + + + True + False + vertical + 2 True @@ -123,9 +193,7 @@ Author: Dmitriy Yefremov True False - 10 FTP: - 1 @@ -141,7 +209,7 @@ Author: Dmitriy Yefremov True False end - 25 + 75 1 @@ -158,24 +226,158 @@ Author: Dmitriy Yefremov - + True - False - 2 + True + in + 100 - + True - False - True - Connect - - - - True - False - gtk-connect + True + ftp_list_store + 1 + True + + + + + + + + + + + + multiple + + + True + 100 + Name + True + 0.5 + 1 + + + 0.019999999552965164 + + + 0 + + + + + 0.019999999552965164 + end + + + + 1 + + + + + + + fixed + 75 + Size + 0.5 + 2 + + + 0.94999998807907104 + + + 2 + + + + + + + 75 + Date + 0.5 + 3 + + + + 3 + + + + + + + fixed + 75 + Attr. + 0.5 + 4 + + + 0.50999999046325684 + end + + + 4 + + + + + + + False + Extra + + + + 5 + + + + + + + + + True + True + 2 + + + + + True + True + + + + + True + False + vertical + 2 + + + True + False + 5 + 5 + + + True + False + start + 10 + PC: + + + False @@ -184,18 +386,11 @@ Author: Dmitriy Yefremov - + + True False - True - Disconnect - - - - True - False - gtk-disconnect - - + end + 75 False @@ -203,189 +398,6 @@ Author: Dmitriy Yefremov 1 - - - False - bookmarks_list_store - 0 - - - False - True - end - 2 - - - - - False - True - end - 1 - - - - - - - - False - True - 1 - - - - - True - True - in - 100 - - - True - True - ftp_list_store - 1 - True - - - - - - - - - - - - multiple - - - - - True - 100 - Name - True - 0.5 - 1 - - - 0.019999999552965164 - - - 0 - - - - - 0.019999999552965164 - end - - - - 1 - - - - - - - fixed - 75 - Size - 0.5 - 2 - - - 0.94999998807907104 - - - 2 - - - - - - - 75 - Date - 0.5 - 3 - - - - 3 - - - - - - - fixed - 50 - Attr. - 0.5 - 4 - - - 0.50999999046325684 - end - - - 4 - - - - - - - False - Extra - - - - 5 - - - - - - - - - True - True - 2 - - - - - True - True - - - - - True - False - vertical - 2 - - - True - False - 5 - - - True - False - start - 10 - PC: - - - False @@ -394,150 +406,137 @@ Author: Dmitriy Yefremov - + True - False - end - 32 + True + in + 100 + + + True + True + file_list_store + 1 + True + + + + + + + + + + + + multiple + + + + + True + 100 + Name + True + 0.5 + 1 + + + 0.20000000298023224 + + + 0 + + + + + end + + + + 1 + + + + + + + fixed + 75 + Size + 0.5 + 2 + + + 0.94999998807907104 + + + 2 + + + + + + + 75 + Date + 0.5 + 3 + + + + 3 + + + + + + + False + fixed + 50 + Path + 0.5 + + + + 4 + + + + + + + False + Extra + + + + 5 + + + + + + - False + True True 1 - False - True - 0 - - - - - True - True - in - 100 - - - True - True - file_list_store - 1 - True - - - - - - - - - - - - multiple - - - - - True - 100 - Name - True - 0.5 - 1 - - - 0.20000000298023224 - - - 0 - - - - - end - - - - 1 - - - - - - - fixed - 75 - Size - 0.5 - 2 - - - 0.94999998807907104 - - - 2 - - - - - - - 75 - Date - 0.5 - 3 - - - - 3 - - - - - - - False - fixed - 50 - Path - 0.5 - - - - 4 - - - - - - - False - Extra - - - - 5 - - - - - - - - - True - True - 1 + True + True - True - True + True + True + 1 diff --git a/app/ui/imports.py b/app/ui/imports.py index 595751dd..3d380199 100644 --- a/app/ui/imports.py +++ b/app/ui/imports.py @@ -128,8 +128,15 @@ class ImportDialog: for bq in bqs.bouquets: self._main_model.append((bq.name, bq.type, True)) self._bq_services[(bq.name, bq.type)] = bq.services - # Note! Getting default format ver. 4 - services = get_services(path, self._profile, 4 if self._profile is SettingsType.ENIGMA_2 else 0) + + if self._profile is SettingsType.ENIGMA_2: + services = get_services(path, self._profile, 5 if self._settings.v5_support else 4) + elif self._profile is SettingsType.NEUTRINO_MP: + services = get_services(path, self._profile, 0) + else: + self.show_info_message("Setting format not supported!", Gtk.MessageType.ERROR) + return + for srv in services: self._services[srv.fav_id] = srv except FileNotFoundError as e: diff --git a/app/ui/main_app_window.py b/app/ui/main_app_window.py index 522df341..fdb8820a 100644 --- a/app/ui/main_app_window.py +++ b/app/ui/main_app_window.py @@ -17,7 +17,7 @@ from app.eparser.ecommons import CAS, Flag, BouquetService from app.eparser.enigma.bouquets import BqServiceType from app.eparser.iptv import export_to_m3u from app.eparser.neutrino.bouquets import BqType -from app.settings import SettingsType, Settings, SettingsException, PlayStreamsMode, SettingsReadException, IS_WIN +from app.settings import SettingsType, Settings, SettingsException, PlayStreamsMode, SettingsReadException from app.tools.media import Player, Recorder from app.ui.epg_dialog import EpgDialog from app.ui.transmitter import LinksTransmitter @@ -36,7 +36,8 @@ 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, MOD_MASK, TEXT_DOMAIN, APP_FONT) + FavClickMode, MOD_MASK, APP_FONT) + class Application(Gtk.Application): SERVICE_MODEL_NAME = "services_list_store" @@ -57,8 +58,8 @@ class Application(Gtk.Application): _FAV_ELEMENTS = ("fav_cut_popup_item", "fav_paste_popup_item", "fav_locate_popup_item", "fav_iptv_popup_item", "fav_insert_marker_popup_item", "fav_insert_space_popup_item", "fav_edit_sub_menu_popup_item", - "fav_edit_popup_item", "fav_picon_popup_item", "fav_copy_popup_item", - "fav_epg_configuration_popup_item", "fav_add_alt_popup_item") + "fav_edit_popup_item", "fav_picon_popup_item", "fav_copy_popup_item", "fav_add_alt_popup_item", + "fav_epg_configuration_popup_item", "fav_mark_dup_popup_item") _BOUQUET_ELEMENTS = ("bouquets_new_popup_item", "bouquets_edit_popup_item", "bouquets_cut_popup_item", "bouquets_copy_popup_item", "bouquets_paste_popup_item", "bouquet_import_popup_item", @@ -134,6 +135,7 @@ class Application(Gtk.Application): "on_insert_space": self.on_insert_space, "on_fav_press": self.on_fav_press, "on_locate_in_services": self.on_locate_in_services, + "on_mark_duplicates": self.on_mark_duplicates, "on_picons_manager_show": self.on_picons_manager_show, "on_filter_changed": self.on_filter_changed, "on_filter_type_toggled": self.on_filter_type_toggled, @@ -234,19 +236,27 @@ class Application(Gtk.Application): self._status_bar_box = builder.get_object("status_bar_box") self._services_main_box = builder.get_object("services_main_box") self._bq_name_label = builder.get_object("bq_name_label") - tool_bar = builder.get_object("top_toolbar") - self._main_data_box.bind_property("visible", tool_bar, "visible") + self._main_data_box.bind_property("visible", builder.get_object("top_toolbar"), "visible") self._telnet_tool_button = builder.get_object("telnet_tool_button") self._top_box = builder.get_object("top_box") + self._toolbar_extra_tools_box = builder.get_object("toolbar_extra_tools_box") + self._add_bouquet_button = builder.get_object("add_bouquet_tool_button") # Setting custom sort function for position column. self._services_view.get_model().set_sort_func(Column.SRV_POS, self.position_sort_func, Column.SRV_POS) + # Tool bar elements. + self._main_box = builder.get_object("main_box") + self._toolbar_search_box = builder.get_object("toolbar_search_box") + toolbar_tools_box = builder.get_object("toolbar_tools_box") + self._toolbar_search_box.bind_property("visible", toolbar_tools_box, "visible") + self._toolbar_search_box.bind_property("visible", self._toolbar_extra_tools_box, "visible") # App info self._app_info_box = builder.get_object("app_info_box") self._app_info_box.bind_property("visible", builder.get_object("main_paned"), "visible", 4) - self._app_info_box.bind_property("visible", builder.get_object("toolbar_extra_box"), "visible", 4) - self._app_info_box.bind_property("visible", builder.get_object("toolbar_tools_box"), "visible", 4) + self._app_info_box.bind_property("visible", self._toolbar_search_box, "visible", 4) + self._app_info_box.bind_property("visible", self._toolbar_extra_tools_box, "visible", 4) + self._app_info_box.bind_property("visible", toolbar_tools_box, "visible", 4) self._app_info_box.bind_property("visible", builder.get_object("save_tool_button"), "visible", 4) - self._app_info_box.bind_property("visible", builder.get_object("add_bouquet_tool_button"), "visible", 4) + self._app_info_box.bind_property("visible", self._add_bouquet_button, "visible", 4) # Status bar self._profile_combo_box = builder.get_object("profile_combo_box") self._receiver_info_box = builder.get_object("receiver_info_box") @@ -264,7 +274,7 @@ class Application(Gtk.Application): self._radio_count_label = builder.get_object("radio_count_label") self._data_count_label = builder.get_object("data_count_label") self._signal_level_bar.bind_property("visible", builder.get_object("play_current_service_button"), "visible") - # self._signal_level_bar.bind_property("visible", builder.get_object("record_button"), "visible") + self._signal_level_bar.bind_property("visible", builder.get_object("record_button"), "visible") self._receiver_info_box.bind_property("visible", self._http_status_image, "visible", 4) self._receiver_info_box.bind_property("visible", self._signal_box, "visible") # Alternatives @@ -280,6 +290,9 @@ class Application(Gtk.Application): self._ftp_button = builder.get_object("ftp_button") self._ftp_revealer = builder.get_object("ftp_revealer") self._ftp_button.bind_property("active", self._ftp_revealer, "visible") + self._ftp_revealer.bind_property("visible", self._main_box, "visible", 4) + self._ftp_revealer.bind_property("visible", builder.get_object("toolbar_main_box"), "visible", 4) + self._ftp_button.connect("toggled", self.on_ftp_toggle) # Force Ctrl press event for view. Multiple selections in lists only with Space key(as in file managers)!!! self._services_view.connect("key-press-event", self.force_ctrl) self._fav_view.connect("key-press-event", self.force_ctrl) @@ -291,7 +304,7 @@ class Application(Gtk.Application): self._services_model_filter = builder.get_object("services_model_filter") self._services_model_filter.set_visible_func(self.services_filter_function) self._filter_entry = builder.get_object("filter_entry") - self._filter_bar = builder.get_object("filter_bar") + self._filter_box = builder.get_object("filter_box") self._filter_types_model = builder.get_object("filter_types_list_store") self._filter_sat_pos_model = builder.get_object("filter_sat_pos_list_store") self._filter_only_free_button = builder.get_object("filter_only_free_button") @@ -305,18 +318,18 @@ class Application(Gtk.Application): self._player_tool_bar = builder.get_object("player_tool_bar") self._player_prev_button = builder.get_object("player_prev_button") self._player_next_button = builder.get_object("player_next_button") + self._player_box.bind_property("visible", self._top_box, "visible", 4) self._player_box.bind_property("visible", self._services_main_box, "visible", 4) self._fav_bouquets_paned = builder.get_object("fav_bouquets_paned") self._player_box.bind_property("visible", builder.get_object("fav_pos_column"), "visible", 4) self._player_box.bind_property("visible", builder.get_object("fav_pos_column"), "visible", 4) self._player_box.bind_property("visible", self._player_event_box, "visible") - self._player_box.bind_property("visible", tool_bar, "sensitive", 4) self._fav_view.bind_property("sensitive", self._player_prev_button, "sensitive") self._fav_view.bind_property("sensitive", self._player_next_button, "sensitive") # Record self._record_image = builder.get_object("record_button_image") # Search - self._search_bar = builder.get_object("search_bar") + self._search_box = builder.get_object("search_box") self._search_entry = builder.get_object("search_entry") self._search_provider = SearchProvider((self._services_view, self._fav_view, self._bouquets_view), builder.get_object("search_down_button"), @@ -330,25 +343,78 @@ class Application(Gtk.Application): style_provider.load_from_path(UI_RESOURCES_PATH + "style.css") self._status_bar_box.get_style_context().add_provider_for_screen(Gdk.Screen.get_default(), style_provider, Gtk.STYLE_PROVIDER_PRIORITY_USER) + # Layout + if self._settings.is_darwin and self._settings.alternate_layout: + self._main_paned = builder.get_object("main_data_paned") + self._fav_paned = builder.get_object("fav_bouquets_paned") + self._fav_box = self._fav_paned.get_child1() + self._bouquets_box = self._fav_paned.get_child2() + self._left_ar_bq_button = builder.get_object("left_arrow_bq_button") + self._left_ar_bq_button.bind_property("visible", builder.get_object("right_arrow_bq_button"), "visible", 4) + self._left_ar_bq_button.set_visible(True) + self.init_layout(builder) - # Menu bar - main_box = builder.get_object("main_window_box") - builder.set_translation_domain(TEXT_DOMAIN) - builder.add_from_file(UI_RESOURCES_PATH + "app_menu_bar.ui") - menu_bar = Gtk.MenuBar.new_from_model(builder.get_object("menu_bar")) - menu_bar.set_visible(True) - main_box.pack_start(menu_bar, False, False, 0) - main_box.reorder_child(menu_bar, 0) - self._main_data_box.bind_property("visible", menu_bar, "visible") - self._player_box.bind_property("visible", menu_bar, "sensitive", 4) - if self._settings.get("telnet"): - self.init_telnet(builder) + def init_layout(self, builder): + """ Initializes an alternate layout, if enabled. """ + control_box = builder.get_object("control_button_box") + control_box.set_child_packing(self._control_button, False, True, 0, Gtk.PackType.END) + + extra_box = builder.get_object("toolbar_extra_box") + extra_box.set_child_packing(self._toolbar_extra_tools_box, False, True, 0, Gtk.PackType.END) + self._toolbar_search_box.reorder_child(builder.get_object("search_tool_button"), 0) + + self._top_box.set_child_packing(extra_box, False, True, 0, Gtk.PackType.START) + self._top_box.set_child_packing(self._toolbar_search_box, False, True, 0, Gtk.PackType.END) + self._top_box.reorder_child(extra_box, 0) + + center_box = builder.get_object("center_box") + center_box.reorder_child(self._control_revealer, 0) + center_box.reorder_child(self._ftp_revealer, 1) + center_box.reorder_child(self._main_box, 2) + center_box.set_child_packing(self._control_revealer, False, True, 0, Gtk.PackType.START) + + builder.get_object("fs_box").set_child_packing(self._filter_box, False, True, 0, Gtk.PackType.END) + top_toolbar = builder.get_object("top_toolbar") + top_toolbar.set_child_packing(self._toolbar_search_box, False, True, 0, Gtk.PackType.END) + + services_box = self._main_paned.get_child1() + self._main_paned.remove(services_box) + self._main_paned.remove(self._fav_paned) + self._main_paned.pack1(self._fav_paned, True, True) + self._main_paned.pack2(services_box, True, True) + + self._left_ar_bq_button.set_visible(not self._settings.bq_details_first) + self.init_bq_position() + + def init_bq_position(self): + self._fav_paned.remove(self._fav_box) + self._fav_paned.remove(self._bouquets_box) + + if self._settings.bq_details_first: + self._fav_paned.pack1(self._fav_box, False, False) + self._fav_paned.pack2(self._bouquets_box, False, False) + else: + self._fav_paned.pack1(self._bouquets_box, False, False) + self._fav_paned.pack2(self._fav_box, False, False) + + pack = Gtk.PackType.END if self._settings.bq_details_first else Gtk.PackType.START + self._toolbar_extra_tools_box.set_child_packing(self._add_bouquet_button, False, True, 0, pack) def do_startup(self): Gtk.Application.do_startup(self) self.init_keys() self.set_accels() + + builder = Gtk.Builder() + builder.set_translation_domain("demon-editor") + builder.add_from_file(UI_RESOURCES_PATH + "app_menu_bar.ui") + self.set_menubar(builder.get_object("menu_bar")) + self.set_app_menu(builder.get_object("app-menu")) + + if self._settings.get("telnet"): + self.init_telnet(builder) + self.update_profile_label() self.init_drag_and_drop() self.init_appearance() @@ -419,6 +485,16 @@ class Application(Gtk.Application): remote_action = Gio.SimpleAction.new_stateful("on_remote", None, GLib.Variant.new_boolean(False)) remote_action.connect("change-state", self.on_control) self.add_action(remote_action) + # FTP client. Hiding the app menu bar when the client is shown. + # We are working with the "hidden-when" submenu attribute. See 'app_menu_bar.ui' file. + hide_bar_action = Gio.SimpleAction.new("hide_menu_bar", None) + self._ftp_revealer.bind_property("visible", hide_bar_action, "enabled", 4) + self.add_action(hide_bar_action) + show_ftp_menu_action = Gio.SimpleAction.new("show_ftp_menu", None) + show_ftp_menu_action.set_enabled(False) + self._ftp_revealer.bind_property("visible", show_ftp_menu_action, "enabled") + self.add_action(show_ftp_menu_action) + self.set_action("on_ftp_client_close", lambda a, v: self._ftp_button.set_active(False)) # Layout self.set_action("on_switch_fav_position", self.on_switch_fav_position) @@ -617,7 +693,9 @@ class Application(Gtk.Application): def on_close_app(self, *args): """ Performing operations before closing the application. """ # Saving the current size of the application window. - self._settings.add("window_size", self._main_window.get_size()) + self._main_window.unfullscreen() + if not self._main_window.is_maximized(): + self._settings.add("window_size", self._main_window.get_size()) if self._recorder: if self._recorder.is_record(): @@ -665,10 +743,9 @@ class Application(Gtk.Application): model, paths = view.get_selection().get_selected_rows() if target is ViewTarget.FAV: - rows = [] - for in_itr in [model.get_iter(path) for path in paths]: - v1, v2, v3, v4, v5, v6, v7, v8 = model.get(in_itr, 2, 3, 4, 5, 7, 16, 18, 8) - rows.append((0, v1, v2, v3, v4, v5, v6, v7, v8)) + self._rows_buffer.extend((0, *model.get(model.get_iter(path), Column.SRV_CODED, Column.SRV_SERVICE, + Column.SRV_LOCKED, Column.SRV_HIDE, Column.SRV_TYPE, Column.SRV_POS, + Column.SRV_FAV_ID, Column.SRV_PICON), None, None) for path in paths) elif target is ViewTarget.SERVICES: self._rows_buffer.extend(model[path][:] for path in paths) elif target is ViewTarget.BOUQUET: @@ -1380,7 +1457,7 @@ class Application(Gtk.Application): self.delete_selection(self._services_view, self._fav_view) self.on_view_focus(self._bouquets_view) - menu.popup(None, None, None, None, event.button, event.time) + menu.popup_at_pointer(None) return True def on_satellite_editor_show(self, action, value=None): @@ -1577,7 +1654,7 @@ class Application(Gtk.Application): yield True self._data_hash = self.get_data_hash() yield True - if self._filter_bar.get_visible(): + if self._filter_box.get_visible(): self.on_filter_changed() yield True @@ -1620,7 +1697,7 @@ class Application(Gtk.Application): bq_id = "{}:{}".format(name, bq_type) services = [] extra_services = {} # for services with different names in bouquet and main list - + agr = [None] * 7 for srv in bq.services: fav_id = srv.data # IPTV and MARKER services @@ -1639,13 +1716,12 @@ class Application(Gtk.Application): picon_id = "{}_{}_{}_{}_{}_{}_{}_{}_{}_{}.png".format(*fav_id_data[:10]) locked = LOCKED_ICON if data_id in self._blacklist else None srv = Service(None, None, icon, srv.name, locked, None, None, s_type.name, - self._picons.get(picon_id, None), picon_id, None, None, None, None, None, None, None, - data_id, fav_id, None) + self._picons.get(picon_id, None), picon_id, *agr, data_id, fav_id, None) self._services[fav_id] = srv elif s_type is BqServiceType.ALT: self._alt_file.add("{}:{}".format(srv.data, bq_type)) srv = Service(None, None, None, srv.name, locked, None, None, s_type.name, - None, None, None, None, None, None, None, None, None, srv.data, fav_id, srv.num) + None, None, *agr, srv.data, fav_id, srv.num) self._services[fav_id] = srv elif srv.name: extra_services[fav_id] = srv.name @@ -1767,7 +1843,7 @@ class Application(Gtk.Application): bq = Bouquet(bq_name, bq_type, bq_s, locked, hidden, self._bq_file.get(bq_id, None)) bqs.append(bq) if len(b_path) == 1: - bouquets.append(Bouquets(model.get_value(itr, 0), model.get_value(itr, 3), bqs if bqs else [])) + bouquets.append(Bouquets(*model.get(itr, Column.BQ_NAME, Column.BQ_TYPE), bqs if bqs else [])) # Getting bouquets self._bouquets_view.get_model().foreach(parse_bouquets) @@ -1962,9 +2038,7 @@ class Application(Gtk.Application): def delete_selection(self, view, *args): """ Used for clear selection on given view(s) """ - views = [view, ] - views.extend(args) - for v in views: + for v in [view, *args]: v.get_selection().unselect_all() def on_settings(self, action, value=None): @@ -2902,6 +2976,10 @@ class Application(Gtk.Application): # ****************** FTP client ********************* # + def on_ftp_toggle(self, button): + if not self._app_info_box.get_visible(): + self._toolbar_search_box.set_visible(not button.get_active()) + def on_ftp_realize(self, revealer): if not self._ftp_client: from app.ui.ftp import FtpClientBox @@ -2919,7 +2997,7 @@ class Application(Gtk.Application): self._filter_entry.grab_focus() if value else self.on_filter_changed() self.filter_set_default() - self._filter_bar.set_visible(value) + self._filter_box.set_visible(value) @run_idle def filter_set_default(self): @@ -2980,7 +3058,7 @@ class Application(Gtk.Application): self._services_view.set_model(model) def services_filter_function(self, model, itr, data): - if not self._filter_bar.is_visible(): + if not self._filter_box.is_visible(): return True else: r_txt = str(model.get(itr, Column.SRV_SERVICE, Column.SRV_PACKAGE, Column.SRV_TYPE, Column.SRV_SSID, @@ -3020,7 +3098,7 @@ class Application(Gtk.Application): return True action.set_state(value) - self._search_bar.set_visible(value) + self._search_box.set_visible(value) if value: self._search_entry.grab_focus() else: @@ -3178,6 +3256,17 @@ class Application(Gtk.Application): def on_locate_in_services(self, view): locate_in_services(view, self._services_view, self._main_window) + def on_mark_duplicates(self, item): + """ Marks services with duplicate [names] in the fav list. """ + from collections import Counter + + dup = Counter(r[Column.FAV_SERVICE] for r in self._fav_model if r[Column.FAV_TYPE] not in self._marker_types) + dup = {k for k, v in dup.items() if v > 1} + + for r in self._fav_model: + if r[Column.FAV_SERVICE] in dup: + r[Column.FAV_BACKGROUND] = self._NEW_COLOR + # ***************** Picons *********************# def on_picons_manager_show(self, action, value=None): diff --git a/app/ui/main_window.glade b/app/ui/main_window.glade index 08133e56..e0ce9579 100644 --- a/app/ui/main_window.glade +++ b/app/ui/main_window.glade @@ -1,5 +1,5 @@ - - - + + @@ -110,7 +110,7 @@ Author: Dmitriy Yefremov Cut True - + @@ -121,7 +121,7 @@ Author: Dmitriy Yefremov Copy True - + @@ -132,7 +132,7 @@ Author: Dmitriy Yefremov Paste True - + @@ -149,7 +149,7 @@ Author: Dmitriy Yefremov Edit True - + @@ -184,6 +184,11 @@ Author: Dmitriy Yefremov + + True + False + input-gaming-symbolic + @@ -223,7 +228,7 @@ Author: Dmitriy Yefremov Cut True - + @@ -234,7 +239,7 @@ Author: Dmitriy Yefremov Copy True - + @@ -245,7 +250,7 @@ Author: Dmitriy Yefremov Paste True - + @@ -261,7 +266,7 @@ Author: Dmitriy Yefremov Edit True - + @@ -308,6 +313,15 @@ Author: Dmitriy Yefremov + + + True + False + False + Mark duplicates + + + True @@ -566,7 +580,6 @@ Author: Dmitriy Yefremov 135 - 150 True False 5 @@ -574,6 +587,8 @@ Author: Dmitriy Yefremov 5 5 never + 350 + True True @@ -595,7 +610,7 @@ Author: Dmitriy Yefremov - 0.98000001907348633 + 0.98 @@ -693,6 +708,11 @@ Author: Dmitriy Yefremov edit-find-symbolic 1 + + True + False + network-server-symbolic + True False @@ -716,8 +736,6 @@ Author: Dmitriy Yefremov True False 5 - 2 - 2 folder-open-symbolic 1 @@ -735,7 +753,7 @@ Author: Dmitriy Yefremov True False - view-sort-ascending-symbolic + document-edit-symbolic 1 @@ -907,7 +925,7 @@ Author: Dmitriy Yefremov Copy True - + @@ -917,7 +935,7 @@ Author: Dmitriy Yefremov Edit True - + @@ -1098,6 +1116,7 @@ Author: Dmitriy Yefremov center demon-editor center + DemonEditor @@ -1108,128 +1127,18 @@ Author: Dmitriy Yefremov False vertical - + True False - center - 10 - 10 - 10 10 - - True + False - False - Profile - 0 - - - - False - True - 0 - - - - - True - False - expand - - - True - False - True - Open - app.on_data_open - open_image - True - - - True - True - 0 - - - - - - True - False - True - FTP-transfer - app.on_download - ftp_image - True - - - True - True - 1 - - - - - - True - True - Telnet - app.on_telnet_client_show - telnet_image - True - - - True - True - 2 - - - - - - False - True - Save - app.on_data_save - save_image - True - - - True - True - 3 - - - - - - True - False - True - Backups - app.on_backup_tool_show - backups_image - True - - - True - True - 4 - - - - - False - True - 1 - - - - - False - 0.99000000022351742 - immediate + center + center + 15 + 15 + True expand @@ -1238,6 +1147,7 @@ Author: Dmitriy Yefremov False True Filter + center win.filter filter_image True @@ -1255,44 +1165,11 @@ Author: Dmitriy Yefremov True True Search + center win.search find_image True - - True - True - 1 - - - - - Lock - True - False - True - Parent lock - app.on_locked - lock_image - True - - - True - True - 2 - - - - - Hide - True - False - True - Hide/Skip - app.on_hide - hide_image - True - True True @@ -1303,126 +1180,318 @@ Author: Dmitriy Yefremov False True - 2 + 0 - - + + + True False - expand + center + center + 5 + 5 + 10 + 10 + 5 - - Satellites + True False - True - Satellites editor - app.on_satellite_editor_show - sat_editor_image - True + False + Profile + center + 0 + - True + False True 0 - - Picons + True False - True - Picons manager - app.on_picons_manager_show - picons_image - True + center + center + expand + + + True + False + True + Open + center + app.on_data_open + open_image + True + + + True + True + 0 + + + + + + 48 + True + False + True + FTP-transfer + center + app.on_download + ftp_image + True + + + True + True + 1 + + + + + + True + True + Telnet + app.on_telnet_client_show + telnet_image + True + + + True + True + 2 + + + + + + False + True + Save + center + app.on_data_save + save_image + True + + + True + True + 3 + + + + + + True + False + True + Backups + center + app.on_backup_tool_show + backups_image + True + + + True + True + 4 + + - True + False True 1 - - True + False - True - IPTV tools - toolbar_iptv_menu + center + center + expand - + + Satellites True False - center - 2 + True + Satellites editor + center + app.on_satellite_editor_show + sat_editor_image + True + + + True + True + 0 + + + + + Picons + True + False + True + Picons manager + center + app.on_picons_manager_show + picons_image + True + + + True + True + 1 + + + + + True + False + True + IPTV tools + center + toolbar_iptv_menu - + True False center - center - network-wired-symbolic - 1 + 2 + + + True + False + center + center + network-wired-symbolic + 1 + + + False + True + 0 + + + + + True + False + IPTV + + + False + True + 1 + + - - False - True - 0 - - - - - True - False - IPTV - - - False - True - 1 - + + True + True + 2 + - True + False True - 5 + 3 False True - 3 + 0 - + True False - 10 - immediate - True - expand + center + center + 15 + 15 - - False + False - True - Create bouquet - True - + center + 5 + 5 + True + expand - + True False - bookmark-new-symbolic - 1 + True + Parent lock + center + app.on_locked + lock_image + True + + True + True + 0 + + + + + True + False + True + Hide/Skip + center + app.on_hide + hide_image + True + + + True + True + 1 + + + + + 32 + False + False + True + Create bouquet + True + + + + True + False + bookmark-new-symbolic + 1 + + + + + False + True + end + 2 + True + @@ -1432,19 +1501,47 @@ Author: Dmitriy Yefremov - + + True False - True - Control - app.on_remote + center + center + True + expand - - True - False - input-gaming-symbolic + + 32 + True + True + Control + app.on_remote + control_image + True + + + False + True + 1 + + + + + FTP + 32 + True + True + True + FTP client + ftp_client_image + True + + + True + True + 2 + - False @@ -1452,39 +1549,18 @@ Author: Dmitriy Yefremov 1 - - - True - True - True - True - - - True - False - FTP client - network-server-symbolic - - - - - True - True - 2 - - False True end - 4 + 1 False - True + False 0 @@ -1497,203 +1573,6 @@ Author: Dmitriy Yefremov True False vertical - - - 32 - True - False - 1 - 1 - 2 - - - False - start - 10 - 5 - - - True - False - gtk-info - - - False - True - 0 - - - - - True - False - Receiver info - Receiver info - - - - - - False - True - 1 - - - - - True - False - Current IP: - - - - - - False - True - 2 - - - - - True - True - 0 - - - - - False - No connection to the receiver! - 10 - 10 - network-offline-symbolic - - - False - True - end - 2 - - - - - False - end - center - 10 - 10 - 5 - - - True - False - center - end - 0 - - - False - True - 1 - - - - - 28 - False - True - Record - - - - True - False - gtk-media-record - - - - - False - True - 2 - - - - - 28 - False - False - True - Play - center - center - - - - True - False - center - center - gtk-media-play - - - - - False - True - 3 - - - - - True - False - Current service - fill - end - 35 - 1 - - - - - - True - True - 4 - - - - - 70 - False - Signal level - center - 100 - - - False - False - 5 - - - - - True - True - end - 2 - - - - - False - True - end - 0 - - False @@ -1905,12 +1784,12 @@ Author: Dmitriy Yefremov False 10 10 - 5 - 5 10 - + False + 5 + 5 Only free @@ -1994,9 +1873,11 @@ Author: Dmitriy Yefremov 0 - - + + False + 5 + 5 True @@ -2012,34 +1893,13 @@ Author: Dmitriy Yefremov 0 - - - True - False - True - True - - - - True - False - center - up - - - - - False - False - 1 - - True False - True + False True + half @@ -2053,11 +1913,31 @@ Author: Dmitriy Yefremov False False - 2 + 1 - + + True + False + False + True + half + + + + True + False + center + up + + + + + False + False + 2 + + + + False + True + 0 + + + True False - center 2 2 Bouquet details @@ -2660,11 +2564,38 @@ Author: Dmitriy Yefremov - True + False True 3 + + + False + True + Toggle display position + app.on_switch_fav_position + none + True + + + True + False + 12 + pan-end-symbolic + + + + + + False + True + end + 2 + + False @@ -3073,16 +3004,36 @@ Author: Dmitriy Yefremov - + True False - label - end + + + False + True + 2 + + + + + True + False + center + + + True + False + True + end + 12 + 20 + + True True - 2 + 3 @@ -3294,13 +3245,10 @@ Author: Dmitriy Yefremov 2 - + True True - 0.93000000000000005 - center - immediate vertical @@ -3338,7 +3286,7 @@ Author: Dmitriy Yefremov True False - 1.0.7 Alpha + 1.0.7 Beta @@ -3351,11 +3299,209 @@ Author: Dmitriy Yefremov - True + False True 2 + + + 28 + True + False + 1 + 1 + + + False + start + 10 + 5 + + + True + False + gtk-info + + + False + True + 0 + + + + + True + False + Receiver info + Receiver info + + + + + + False + True + 1 + + + + + True + False + Current IP: + + + + + + False + True + 2 + + + + + True + True + 0 + + + + + False + No connection to the receiver! + 10 + 10 + network-offline-symbolic + + + False + True + end + 2 + + + + + False + end + center + 10 + 10 + 5 + + + True + False + center + end + 0 + + + False + True + 1 + + + + + status-bar-button + 28 + False + True + Record + + + + True + False + gtk-media-record + + + + + False + True + 2 + + + + + status-bar-button + 28 + False + False + True + Play + center + center + + + + True + False + center + center + gtk-media-play + + + + + False + True + 3 + + + + + True + False + Current service + fill + end + 35 + 1 + + + + + + True + True + 4 + + + + + 70 + False + Signal level + center + 100 + + + False + False + 5 + + + + + True + True + end + 2 + + + + + False + True + end + 0 + + True @@ -3376,6 +3522,7 @@ Author: Dmitriy Yefremov False True + end 1 @@ -3390,7 +3537,7 @@ Author: Dmitriy Yefremov - False + True True 2 diff --git a/app/ui/settings_dialog.glade b/app/ui/settings_dialog.glade index 000211fa..513b9305 100644 --- a/app/ui/settings_dialog.glade +++ b/app/ui/settings_dialog.glade @@ -2676,7 +2676,7 @@ Author: Dmitriy Yefremov 5 - True + False False diff --git a/app/ui/settings_dialog.py b/app/ui/settings_dialog.py index f4ec9a9f..07dae1f4 100644 --- a/app/ui/settings_dialog.py +++ b/app/ui/settings_dialog.py @@ -358,7 +358,6 @@ class SettingsDialog: self._ext_settings.list_font = self._list_font_button.get_font() if IS_WIN: - self._ext_settings.dark_mode = self._dark_mode_switch.get_active() self._ext_settings.alternate_layout = self._layout_switch.get_active() self._ext_settings.is_themes_support = self._themes_support_switch.get_active() self._ext_settings.theme = self._theme_combo_box.get_active_id() @@ -796,7 +795,6 @@ class SettingsDialog: @run_idle def init_themes(self): - self._dark_mode_switch.set_active(self._ext_settings.dark_mode) t_support = self._ext_settings.is_themes_support self._themes_support_switch.set_active(t_support) if t_support: