From 14459b8e7e50dc4b16ccfc8d641526f905e88ecf Mon Sep 17 00:00:00 2001 From: DYefremov Date: Sat, 18 Jun 2022 21:29:10 +0300 Subject: [PATCH] satellite tools decoupling --- app/ui/main.py | 3 +- app/ui/satellites.glade | 2452 ---------------------- app/ui/xml/__init__.py | 0 app/ui/xml/dialogs.glade | 769 +++++++ app/ui/{satellites.py => xml/dialogs.py} | 277 +-- app/ui/xml/edit.py | 288 +++ app/ui/xml/editor.glade | 601 ++++++ app/ui/xml/update.glade | 1173 +++++++++++ 8 files changed, 2844 insertions(+), 2719 deletions(-) delete mode 100644 app/ui/satellites.glade create mode 100644 app/ui/xml/__init__.py create mode 100644 app/ui/xml/dialogs.glade rename app/ui/{satellites.py => xml/dialogs.py} (71%) create mode 100644 app/ui/xml/edit.py create mode 100644 app/ui/xml/editor.glade create mode 100644 app/ui/xml/update.glade diff --git a/app/ui/main.py b/app/ui/main.py index cdae756e..06400418 100644 --- a/app/ui/main.py +++ b/app/ui/main.py @@ -64,7 +64,8 @@ from .imports import ImportDialog, import_bouquet from .iptv import IptvDialog, SearchUnavailableDialog, IptvListConfigurationDialog, YtListImportDialog, M3uImportDialog from .main_helper import * from .picons import PiconManager -from .satellites import SatellitesTool, ServicesUpdateDialog +from .xml.dialogs import ServicesUpdateDialog +from .xml.edit import SatellitesTool from .search import SearchProvider from .service_details_dialog import ServiceDetailsDialog, Action from .settings_dialog import SettingsDialog diff --git a/app/ui/satellites.glade b/app/ui/satellites.glade deleted file mode 100644 index 3472bf36..00000000 --- a/app/ui/satellites.glade +++ /dev/null @@ -1,2452 +0,0 @@ - - - - - - - - - - - - - - - - - 1/2 - - - 2/3 - - - 3/4 - - - 5/6 - - - 6/7 - - - 7/8 - - - 8/9 - - - 3/5 - - - 4/5 - - - 9/10 - - - Auto - - - - - - - - - - - Auto - - - QPSK - - - 8PSK - - - 16APSK - - - 32APSK - - - - - - - - - - - Root - - - Gold - - - Combo - - - - - - - - - - - H - - - V - - - R - - - L - - - - - True - False - gtk-add - - - True - False - - - Add - True - False - popup_menu_add_image - False - - - - - - True - False - - - - - gtk-edit - True - False - True - True - - - - - - - True - False - - - - - gtk-remove - True - False - True - True - - - - - - - True - False - gtk-add - - - True - False - - - Add - True - False - popup_menu_add_image_2 - False - - - - - - True - False - - - - - gtk-edit - True - False - True - True - - - - - - - True - False - - - - - gtk-remove - True - False - True - True - - - - - - - 180 - 0.10000000000000001 - 10 - - - 180 - 0.10000000000000001 - 10 - - - True - False - edit-undo - - - True - False - - - gtk-select-all - True - False - True - True - - - - - - Remove selection - True - False - remove_selection_image - False - - - - - - True - False - network-receive-symbolic - 1 - - - True - False - window-close-symbolic - 1 - - - True - False - emblem-synchronizing-symbolic - 1 - - - - - - - - - - - - - - - - - - - - - E - - - W - - - - - False - Satellite - False - True - center-on-parent - True - dialog - True - True - - - - - - False - vertical - 2 - - - False - end - - - Cancel - True - True - True - center - - - True - True - 0 - - - - - OK - 90 - True - True - True - center - - - True - True - 1 - - - - - False - False - 0 - - - - - True - False - 5 - 5 - 5 - 5 - 0.019999999552965164 - in - - - True - False - 5 - 5 - 5 - 5 - - - True - False - Name - - - 0 - 0 - - - - - True - False - Position - - - 1 - 0 - - - - - True - True - document-edit-symbolic - False - False - False - satellite name - name - - - 0 - 1 - - - - - True - True - 5 - False - False - number - pos_adjustment - 1 - True - - - 1 - 1 - - - - - True - False - side_store - 0 - 0 - - - - 0 - - - - - 2 - 1 - - - - - - - - - - True - False - Satellite properties: - - - - - True - True - 1 - - - - - - sat_cancel_button - sat_ok_button - - - - - - - - - - DVB-S - - - DVB-S2 - - - - - 320 - False - Transponder - False - True - center-on-parent - 320 - True - dialog - True - True - - - False - vertical - 2 - - - False - end - - - Cancel - True - True - True - center - - - True - True - 0 - - - - - OK - 90 - True - True - True - center - - - True - True - 1 - - - - - False - False - 0 - - - - - True - False - 5 - 5 - 5 - 5 - 0.02 - in - - - True - False - 5 - 5 - 5 - vertical - 2 - - - True - False - 5 - - - True - False - Freq - - - 0 - 0 - - - - - True - False - Rate - - - 1 - 0 - - - - - True - False - Pol - - - 2 - 0 - - - - - True - False - FEC - - - 3 - 0 - - - - - True - False - System - - - 4 - 0 - - - - - True - False - Mod - - - 5 - 0 - - - - - True - True - 12 - 14 - document-edit-symbolic - False - False - False - 11700000 - digits - - - - 0 - 1 - - - - - True - True - 12 - 14 - document-edit-symbolic - 27500000 - digits - - - - 1 - 1 - - - - - True - False - pol_store - 0 - - - - 0 - - - - - 2 - 1 - - - - - True - False - fec_store - 0 - - - - 0 - - - - - 3 - 1 - - - - - True - False - system_store - 0 - - - - 0 - - - - - 4 - 1 - - - - - True - False - mod_store - 0 - - - - 0 - - - - - 5 - 1 - - - - - False - True - 2 - - - - - True - True - True - - - True - False - 5 - - - True - False - Pls mode - - - 0 - 0 - - - - - True - False - Pls code - - - 1 - 0 - - - - - True - False - Is ID - - - 2 - 0 - - - - - True - False - pls_mode_store - 0 - - - - 0 - - - - - 0 - 1 - - - - - True - True - 5 - 12 - document-edit-symbolic - False - 0 - 262142 - digits - - - - 1 - 1 - - - - - True - True - 5 - 12 - document-edit-symbolic - False - 0 - 255 - digits - - - - 2 - 1 - - - - - True - False - T2-MI PLP ID - - - 3 - 0 - - - - - True - True - 5 - 12 - document-edit-symbolic - False - 0 - 255 - digits - - - - 3 - 1 - - - - - - - True - False - Extra: - - - - - False - True - 4 - - - - - - - True - False - Transponder properties: - - - - - False - True - 1 - - - - - - tr_cancel_button - tr_ok_button - - - - - - - - - - - - - - - - - - - - - - - - - - - - True - False - vertical - 5 - - - True - True - True - - - 350 - True - False - 0.49000000953674316 - in - - - True - False - 5 - 5 - 5 - 5 - vertical - 5 - - - True - False - 15 - 15 - 5 - 5 - 5 - - - True - True - True - Add - - - - True - False - document-new-symbolic - - - - - False - True - 0 - - - - - True - True - True - Update - - - - True - False - emblem-synchronizing-symbolic - - - - - False - True - 1 - - - - - False - True - 0 - - - - - True - True - in - - - True - True - satellite_view_model - True - True - - - - - - multiple - - - - - Name - True - 0.49000000953674316 - - - end - - - 0 - - - - - - - 85 - Pos - 0.49000000953674316 - - - 0.49000000953674316 - - - 2 - - - - - - - - - True - True - 1 - - - - - - - True - False - Satellites - - - - - False - False - - - - - True - False - 0.49000000953674316 - in - - - True - False - 5 - 5 - 5 - 5 - vertical - 5 - - - True - False - 15 - 15 - 5 - 5 - 5 - - - True - True - True - Add - - - - True - False - document-new-symbolic - - - - - False - True - 0 - - - - - - - - False - True - 0 - - - - - True - True - in - - - True - True - transponder_view_model - 0 - True - both - - - - - multiple - - - - - True - 20 - Freq - True - 0.5 - - - 0.49000000953674316 - - - 0 - - - - - - - True - 20 - Rate - True - 0.5 - - - 0.49000000953674316 - - - 1 - - - - - - - True - 20 - Pol - True - 0.5 - - - 0.49000000953674316 - - - 2 - - - - - - - True - 20 - FEC - True - 0.5 - - - 0.49000000953674316 - - - 3 - - - - - - - True - 20 - System - True - 0.5 - - - 0.49000000953674316 - - - 4 - - - - - - - True - 20 - Mod - True - 0.5 - - - 0.49000000953674316 - - - 5 - - - - - - - False - True - Pls mode - True - - - - 6 - - - - - - - False - True - Pls code - True - - - - 7 - - - - - - - False - True - Is ID - True - - - - 8 - - - - - - - False - T2-MI PLP ID - - - - 9 - - - - - - - - - True - True - 1 - - - - - - - True - False - Transponder - - - - - True - False - - - - - True - True - 1 - - - - - - - - - - - - - - - - - - - update_sat_list_store - - - update_sat_list_model_filter - - - - - - - - - - - - - - - - - - - - - - - - - - - - - False - Satellites update - True - center-on-parent - True - True - True - - - - - - - True - False - vertical - - - True - False - vertical - - - True - False - 15 - 15 - 5 - 5 - 5 - - - True - False - Source - 0 - - LyngSat - KingOfSat - - - - False - True - 1 - - - - - True - False - expand - - - Update - True - True - True - Update - sat_update_image - True - - - - False - True - 1 - - - - - Receive - True - True - True - Receive - sat_receive_image - True - - - - False - True - 3 - - - - - Cancel - True - True - Cancel - sat_update_cancel_image - True - - - - - False - True - 3 - - - - - False - True - 2 - - - - - True - False - expand - - - True - True - True - Filter - True - - - - True - False - edit-find-replace-symbolic - 1 - - - - - - False - True - 2 - - - - - True - True - True - Find - True - - - - True - False - edit-find-symbolic - 1 - - - - - - False - True - 3 - - - - - False - True - end - 3 - - - - - False - True - 0 - - - - - - False - True - 0 - - - - - False - - - True - False - 2 - 2 - - - 200 - True - True - edit-find-symbolic - False - False - - - False - True - 0 - - - - - True - False - True - True - - - True - False - down - - - - - False - False - 1 - - - - - True - False - True - True - - - True - False - up - - - - - False - False - 2 - - - - - - - - False - True - 1 - - - - - False - - - True - False - 2 - 2 - 2 - - - True - False - From: - - - 0 - 0 - - - - - True - True - number - pos_adjustment - 1 - True - - - - 1 - 0 - - - - - True - False - side_store - 0 - - - - - 0 - - - - - 2 - 0 - - - - - True - False - To: - - - 3 - 0 - - - - - True - True - number - pos_adjustment2 - 1 - True - - - - 4 - 0 - - - - - True - False - side_store - 0 - - - - - 0 - - - - - 5 - 0 - - - - - gtk-apply - True - True - True - True - - - - 6 - 0 - - - - - - - False - True - 2 - - - - - True - True - 5 - 5 - 5 - 5 - True - - - True - False - 0.49000000953674316 - in - - - True - False - 5 - 5 - 5 - 5 - vertical - 5 - - - True - True - in - - - True - True - update_sat_list_model_sort - 0 - True - - - - - multiple - - - - - Satellite - True - True - 0.5 - True - 0 - - - 0.0099999997764825821 - end - - - 0 - - - - - - - Position - True - 0.5 - True - 1 - - - 0.49000000953674316 - - - 1 - - - - - - - Type - True - 0.5 - True - 2 - - - 0.49000000953674316 - - - 2 - - - - - - - False - Url - - - - 3 - - - - - - - Selected - True - True - 4 - - - - - - 4 - - - - - - - - - True - True - 1 - - - - - True - False - 10 - 10 - 5 - - - True - False - document-properties-symbolic - - - False - True - 0 - - - - - True - False - 0 - - - False - True - 1 - - - - - False - True - 2 - - - - - - - True - False - Satellites - - - - - True - True - - - - - True - vertical - True - - - True - False - 0.49000000953674316 - in - - - True - False - 5 - 5 - 5 - 5 - vertical - 5 - - - True - True - in - - - True - True - update_transponder_store - 0 - True - - - - - - Transponder - True - 0.5 - - - 0.0099999997764825821 - - - 0 - - - - - - - False - link - - - - 1 - - - - - - - Selected - - - - - - 2 - - - - - - - - - True - True - 0 - - - - - True - False - 10 - 10 - 5 - - - True - False - document-properties-symbolic - - - False - True - 0 - - - - - True - False - 0 - - - False - True - 1 - - - - - False - True - 2 - - - - - - - True - False - Transponders - - - - - True - True - - - - - True - False - 0.49000000953674316 - in - - - True - False - 5 - 5 - 5 - 5 - vertical - 5 - - - True - True - in - - - True - True - update_service_store - 1 - - - - - - Service - True - 0.5 - - - 2 - - - 0 - - - - - 0.0099999997764825821 - - - 1 - - - - - - - Package - True - 0.5 - - - end - - - 2 - - - - - - - Type - True - 0.5 - - - 0.49000000953674316 - - - 3 - - - - - - - SID - True - 0.5 - - - 0.49000000953674316 - - - 4 - - - - - - - False - CAS - True - 0.5 - - - 0.49000000953674316 - - - - - - - - - True - True - 0 - - - - - True - False - 10 - 10 - 5 - - - True - False - document-properties-symbolic - - - False - True - 0 - - - - - True - False - 0 - - - False - True - 1 - - - - - False - True - 1 - - - - - - - - - - True - False - Services - - - - - True - True - - - - - True - True - - - - - True - True - 3 - - - - - False - 4 - 5 - 5 - 0 - in - - - True - False - other - True - - - False - 6 - end - - - False - False - 0 - - - - - False - - - 120 - True - True - in - - - True - True - False - 5 - 5 - - - - - True - True - 0 - - - - - True - True - 0 - - - - - - - False - True - 4 - - - - - True - False - True - - - - False - 6 - end - - - False - False - 0 - - - - - False - 16 - - - True - False - Info - - - False - True - 0 - - - - - False - False - 0 - - - - - False - True - 9 - - - - - - diff --git a/app/ui/xml/__init__.py b/app/ui/xml/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/app/ui/xml/dialogs.glade b/app/ui/xml/dialogs.glade new file mode 100644 index 00000000..eca58b88 --- /dev/null +++ b/app/ui/xml/dialogs.glade @@ -0,0 +1,769 @@ + + + + + + + + + + + + + + + + + 1/2 + + + 2/3 + + + 3/4 + + + 5/6 + + + 6/7 + + + 7/8 + + + 8/9 + + + 3/5 + + + 4/5 + + + 9/10 + + + Auto + + + + + + + + + + + Auto + + + QPSK + + + 8PSK + + + 16APSK + + + 32APSK + + + + + + + + + + + Root + + + Gold + + + Combo + + + + + + + + + + + H + + + V + + + R + + + L + + + + + 180 + 0.10000000000000001 + 10 + + + + + + + + + E + + + W + + + + + False + Satellite + False + True + center-on-parent + True + dialog + True + True + + + + + + False + vertical + 2 + + + False + end + + + Cancel + True + True + True + center + + + True + True + 0 + + + + + OK + 90 + True + True + True + center + + + True + True + 1 + + + + + False + False + 0 + + + + + True + False + 5 + 5 + 5 + 5 + 0.019999999552965164 + in + + + True + False + 5 + 5 + 5 + 5 + + + True + False + Name + + + 0 + 0 + + + + + True + False + Position + + + 1 + 0 + + + + + True + True + document-edit-symbolic + False + False + False + satellite name + name + + + 0 + 1 + + + + + True + True + 5 + False + False + number + pos_adjustment + 1 + True + + + 1 + 1 + + + + + True + False + side_store + 0 + 0 + + + + 0 + + + + + 2 + 1 + + + + + + + + + + True + False + Satellite properties: + + + + + True + True + 1 + + + + + + sat_cancel_button + sat_ok_button + + + + + + + + + + DVB-S + + + DVB-S2 + + + + + 320 + False + Transponder + False + True + center-on-parent + 320 + True + dialog + True + True + + + False + vertical + 2 + + + False + end + + + Cancel + True + True + True + center + + + True + True + 0 + + + + + OK + 90 + True + True + True + center + + + True + True + 1 + + + + + False + False + 0 + + + + + True + False + 5 + 5 + 5 + 5 + 0.02 + in + + + True + False + 5 + 5 + 5 + vertical + 2 + + + True + False + 5 + + + True + False + Freq + + + 0 + 0 + + + + + True + False + Rate + + + 1 + 0 + + + + + True + False + Pol + + + 2 + 0 + + + + + True + False + FEC + + + 3 + 0 + + + + + True + False + System + + + 4 + 0 + + + + + True + False + Mod + + + 5 + 0 + + + + + True + True + 12 + 14 + document-edit-symbolic + False + False + False + 11700000 + digits + + + + 0 + 1 + + + + + True + True + 12 + 14 + document-edit-symbolic + 27500000 + digits + + + + 1 + 1 + + + + + True + False + pol_store + 0 + + + + 0 + + + + + 2 + 1 + + + + + True + False + fec_store + 0 + + + + 0 + + + + + 3 + 1 + + + + + True + False + system_store + 0 + + + + 0 + + + + + 4 + 1 + + + + + True + False + mod_store + 0 + + + + 0 + + + + + 5 + 1 + + + + + False + True + 2 + + + + + True + True + True + + + True + False + 5 + + + True + False + Pls mode + + + 0 + 0 + + + + + True + False + Pls code + + + 1 + 0 + + + + + True + False + Is ID + + + 2 + 0 + + + + + True + False + pls_mode_store + 0 + + + + 0 + + + + + 0 + 1 + + + + + True + True + 5 + 12 + document-edit-symbolic + False + 0 - 262142 + digits + + + + 1 + 1 + + + + + True + True + 5 + 12 + document-edit-symbolic + False + 0 - 255 + digits + + + + 2 + 1 + + + + + True + False + T2-MI PLP ID + + + 3 + 0 + + + + + True + True + 5 + 12 + document-edit-symbolic + False + 0 - 255 + digits + + + + 3 + 1 + + + + + + + True + False + Extra: + + + + + False + True + 4 + + + + + + + True + False + Transponder properties: + + + + + False + True + 1 + + + + + + tr_cancel_button + tr_ok_button + + + diff --git a/app/ui/satellites.py b/app/ui/xml/dialogs.py similarity index 71% rename from app/ui/satellites.py rename to app/ui/xml/dialogs.py index 7c5e141e..d352afad 100644 --- a/app/ui/satellites.py +++ b/app/ui/xml/dialogs.py @@ -27,276 +27,25 @@ import concurrent.futures +import os import re import time from math import fabs -from pyexpat import ExpatError from gi.repository import GLib from app.commons import run_idle, run_task, log -from app.connections import DownloadType -from app.eparser import get_satellites, write_satellites, Satellite, Transponder +from app.eparser import Satellite, Transponder from app.eparser.ecommons import PLS_MODE, get_key_by_value from app.tools.satellites import SatellitesParser, SatelliteSource, ServicesParser -from .dialogs import show_dialog, DialogType, get_chooser_dialog, get_message, get_builder -from .main_helper import move_items, append_text_to_tview, get_base_model, on_popup_menu -from .search import SearchProvider -from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, MOVE_KEYS, KeyboardKey, MOD_MASK, Page +from ..dialogs import show_dialog, DialogType, get_chooser_dialog, get_message, get_builder +from ..main_helper import append_text_to_tview, get_base_model, on_popup_menu +from ..search import SearchProvider +from ..uicommons import Gtk, Gdk, UI_RESOURCES_PATH -_UI_PATH = UI_RESOURCES_PATH + "satellites.glade" +_DIALOGS_UI_PATH = f"{UI_RESOURCES_PATH}xml{os.sep}dialogs.glade" -class SatellitesTool(Gtk.Box): - """ Class to processing satellite data. """ - _aggr = [None for x in range(9)] # aggregate - - def __init__(self, app, settings, *args, **kwargs): - super().__init__(*args, **kwargs) - - self._app = app - self._app.connect("data-save", self.on_save) - self._app.connect("data-save-as", self.on_save_as) - self._app.connect("data-receive", self.on_download) - self._app.connect("data-send", self.on_upload) - - self._settings = settings - self._current_sat_path = None - - handlers = {"on_remove": self.on_remove, - "on_update": self.on_update, - "on_up": self.on_up, - "on_down": self.on_down, - "on_button_press": self.on_button_press, - "on_satellite_add": self.on_satellite_add, - "on_transponder_add": self.on_transponder_add, - "on_edit": self.on_edit, - "on_key_release": self.on_key_release, - "on_satellite_selection": self.on_satellite_selection} - - builder = get_builder(_UI_PATH, handlers, use_str=True, - objects=("satellite_editor_box", "satellite_view_model", "transponder_view_model", - "satellite_popup_menu", "transponder_popup_menu", "left_header_menu", - "popup_menu_add_image", "popup_menu_add_image_2")) - - self._satellite_view = builder.get_object("satellite_view") - self._transponder_view = builder.get_object("transponder_view") - builder.get_object("sat_pos_column").set_cell_data_func(builder.get_object("sat_pos_renderer"), - self.sat_pos_func) - - self._stores = {3: builder.get_object("pol_store"), - 4: builder.get_object("fec_store"), - 5: builder.get_object("system_store"), - 6: builder.get_object("mod_store")} - - self.pack_start(builder.get_object("satellite_editor_box"), True, True, 0) - self._app.connect("profile-changed", lambda a, m: self.load_satellites_list()) - self.show() - self.load_satellites_list() - - def load_satellites_list(self, path=None): - gen = self.on_satellites_list_load(path) - GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW) - - @run_idle - def on_open(self): - response = get_chooser_dialog(self._app.app_window, self._settings, "satellites.xml", ("*.xml",)) - if response in (Gtk.ResponseType.CANCEL, Gtk.ResponseType.DELETE_EVENT): - return - - if not str(response).endswith("satellites.xml"): - self._app.show_error_message("No satellites.xml file is selected!") - return - - self.load_satellites_list(response) - - def on_satellite_selection(self, view): - model = self._transponder_view.get_model() - model.clear() - - self._current_sat_path, column = view.get_cursor() - if self._current_sat_path: - list(map(model.append, view.get_model()[self._current_sat_path][-1])) - - def on_up(self, item): - move_items(KeyboardKey.UP, self._satellite_view) - - def on_down(self, item): - move_items(KeyboardKey.DOWN, self._satellite_view) - - def on_button_press(self, menu, event): - if event.get_event_type() == Gdk.EventType.DOUBLE_BUTTON_PRESS: - self.on_edit(self._satellite_view if self._satellite_view.is_focus() else self._transponder_view) - else: - on_popup_menu(menu, event) - - def on_key_release(self, view, event): - """ Handling keystrokes """ - key_code = event.hardware_keycode - if not KeyboardKey.value_exist(key_code): - return - key = KeyboardKey(key_code) - ctrl = event.state & MOD_MASK - - if key is KeyboardKey.DELETE: - self.on_remove(view) - elif key is KeyboardKey.INSERT: - pass - elif ctrl and key is KeyboardKey.E: - self.on_edit(view) - elif ctrl and key is KeyboardKey.S: - self.on_satellite() - elif ctrl and key is KeyboardKey.T: - self.on_transponder() - elif ctrl and key in MOVE_KEYS: - move_items(key, self._satellite_view) - elif key is KeyboardKey.LEFT or key is KeyboardKey.RIGHT: - view.do_unselect_all(view) - - def on_satellites_list_load(self, path=None): - """ Load satellites data into model """ - model = self._satellite_view.get_model() - model.clear() - - try: - path = path or self._settings.profile_data_path + "satellites.xml" - satellites = get_satellites(path) - yield True - except FileNotFoundError as e: - msg = get_message("Please, download files from receiver or setup your path for read data!") - self._app.show_error_message(f"{e}\n{msg}") - except ExpatError as e: - msg = f"The file [{path}] is not formatted correctly or contains invalid characters! Cause: {e}" - self._app.show_error_message(msg) - else: - for sat in satellites: - yield model.append(sat) - - def on_add(self, view): - """ Common adding """ - self.on_edit(view, force=True) - - def on_satellite_add(self, item): - self.on_satellite() - - def on_transponder_add(self, item): - self.on_transponder() - - def on_edit(self, view, force=False): - """ Common edit """ - paths = self.check_selection(view, "Please, select only one item!") - if not paths: - return - - model = view.get_model() - row = model[paths][:] - itr = model.get_iter(paths) - - if view is self._satellite_view: - self.on_satellite(None if force else Satellite(*row), itr) - elif view is self._transponder_view: - self.on_transponder(None if force else Transponder(*row), itr) - - def on_satellite(self, satellite=None, edited_itr=None): - """ Create or edit satellite""" - sat_dialog = SatelliteDialog(self._app.get_active_window(), satellite) - sat = sat_dialog.run() - sat_dialog.destroy() - - if sat: - model, paths = self._satellite_view.get_selection().get_selected_rows() - if satellite and edited_itr: - model.set(edited_itr, {i: v for i, v in enumerate(sat)}) - else: - if len(model): - index = paths[0].get_indices()[0] + 1 - model.insert(index, sat) - else: - model.append(sat) - - def on_transponder(self, transponder=None, edited_itr=None): - """ Create or edit transponder """ - - paths = self.check_selection(self._satellite_view, "Please, select only one satellite!") - if paths is None: - return - elif len(paths) == 0: - self._app.show_error_message("No satellite is selected!") - return - - dialog = TransponderDialog(self._app.get_active_window(), transponder) - tr = dialog.run() - dialog.destroy() - - if tr: - sat_model = self._satellite_view.get_model() - transponders = sat_model[paths][-1] - tr_model, tr_paths = self._transponder_view.get_selection().get_selected_rows() - - if transponder and edited_itr: - tr_model.set(edited_itr, {i: v for i, v in enumerate(tr)}) - transponders[tr_model.get_path(edited_itr).get_indices()[0]] = tr - else: - index = paths[0].get_indices()[0] + 1 - tr_model.insert(index, tr) - transponders.insert(index, tr) - - def check_selection(self, view, message): - """ Checks if any row is selected. Shows error dialog if selected more than one. - - Returns selected path or None. - """ - model, paths = view.get_selection().get_selected_rows() - if len(paths) > 1: - self._app.show_error_message(message) - return - - return paths - - def on_remove(self, view): - """ Removes selected satellites and transponders. """ - selection = view.get_selection() - model, paths = selection.get_selected_rows() - - if view is self._satellite_view: - list(map(model.remove, [model.get_iter(path) for path in paths])) - elif view is self._transponder_view: - if self._current_sat_path: - trs = self._satellite_view.get_model()[self._current_sat_path][-1] - list(map(trs.pop, sorted(map(lambda p: p.get_indices()[0], paths), reverse=True))) - list(map(model.remove, [model.get_iter(path) for path in paths])) - else: - self._app.show_error_message("No satellite is selected!") - - def sat_pos_func(self, column, renderer, model, itr, data): - """ Converts and sets the satellite position value to a readable format. """ - pos = int(model.get_value(itr, 2)) - renderer.set_property("text", f"{abs(pos / 10):0.1f}{'W' if pos < 0 else 'E'}") - - @run_idle - def on_save(self, app, page): - if page is Page.SATELLITE and show_dialog(DialogType.QUESTION, self._app.app_window) == Gtk.ResponseType.OK: - write_satellites((Satellite(*r) for r in self._satellite_view.get_model()), - self._settings.profile_data_path + "satellites.xml") - - def on_save_as(self, app, page): - show_dialog(DialogType.ERROR, transient=self._app.app_window, text="Not implemented yet!") - - def on_download(self, app, page): - if page is Page.SATELLITE: - self._app.on_download_data(DownloadType.SATELLITES) - - def on_upload(self, app, page): - if page is Page.SATELLITE: - self._app.upload_data(DownloadType.SATELLITES) - - @run_idle - def on_update(self, item): - SatellitesUpdateDialog(self._app.get_active_window(), self._settings, self._satellite_view.get_model()).show() - - -# ***************** Transponder dialog *******************# - class TransponderDialog: """ Shows dialog for adding or edit transponder """ @@ -304,7 +53,7 @@ class TransponderDialog: handlers = {"on_entry_changed": self.on_entry_changed} objects = ("transponder_dialog", "pol_store", "fec_store", "mod_store", "system_store", "pls_mode_store") - builder = get_builder(_UI_PATH, handlers, use_str=True, objects=objects) + builder = get_builder(_DIALOGS_UI_PATH, handlers, use_str=True, objects=objects) self._dialog = builder.get_object("transponder_dialog") self._dialog.set_transient_for(transient) @@ -388,7 +137,8 @@ class SatelliteDialog: """ Shows dialog for adding or edit satellite. """ def __init__(self, transient, satellite=None): - builder = get_builder(_UI_PATH, use_str=True, objects=("satellite_dialog", "side_store", "pos_adjustment")) + builder = get_builder(_DIALOGS_UI_PATH, use_str=True, + objects=("satellite_dialog", "side_store", "pos_adjustment")) self._dialog = builder.get_object("satellite_dialog") self._dialog.set_transient_for(transient) @@ -448,12 +198,7 @@ class UpdateDialog: self._parser = None self._size_name = f"{'_'.join(re.findall('[A-Z][^A-Z]*', self.__class__.__name__))}_window_size".lower() - builder = get_builder(UI_RESOURCES_PATH + "satellites.glade", handlers, - objects=("satellites_update_window", "update_source_store", "update_sat_list_store", - "update_sat_list_model_filter", "update_sat_list_model_sort", "side_store", - "pos_adjustment", "pos_adjustment2", "satellites_update_popup_menu", - "remove_selection_image", "sat_update_cancel_image", "sat_receive_image", - "sat_update_image", "update_transponder_store", "update_service_store")) + builder = get_builder(f"{UI_RESOURCES_PATH}xml{os.sep}update.glade", handlers) self._window = builder.get_object("satellites_update_window") self._window.set_transient_for(transient) diff --git a/app/ui/xml/edit.py b/app/ui/xml/edit.py new file mode 100644 index 00000000..9ec165df --- /dev/null +++ b/app/ui/xml/edit.py @@ -0,0 +1,288 @@ +# -*- coding: utf-8 -*- +# +# The MIT License (MIT) +# +# Copyright (c) 2018-2022 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 +# + + +from pyexpat import ExpatError + +from gi.repository import GLib + +from app.commons import run_idle +from app.connections import DownloadType +from app.eparser import get_satellites, write_satellites, Satellite, Transponder +from .dialogs import SatelliteDialog, TransponderDialog, SatellitesUpdateDialog +from ..dialogs import show_dialog, DialogType, get_chooser_dialog, get_message, get_builder +from ..main_helper import move_items, on_popup_menu +from ..uicommons import Gtk, Gdk, UI_RESOURCES_PATH, MOVE_KEYS, KeyboardKey, MOD_MASK, Page + + +class SatellitesTool(Gtk.Box): + """ Class to processing satellite data. """ + _aggr = [None for x in range(9)] # aggregate + + def __init__(self, app, settings, *args, **kwargs): + super().__init__(*args, **kwargs) + + self._app = app + self._app.connect("data-save", self.on_save) + self._app.connect("data-save-as", self.on_save_as) + self._app.connect("data-receive", self.on_download) + self._app.connect("data-send", self.on_upload) + + self._settings = settings + self._current_sat_path = None + + handlers = {"on_remove": self.on_remove, + "on_update": self.on_update, + "on_up": self.on_up, + "on_down": self.on_down, + "on_button_press": self.on_button_press, + "on_satellite_add": self.on_satellite_add, + "on_transponder_add": self.on_transponder_add, + "on_edit": self.on_edit, + "on_key_release": self.on_key_release, + "on_satellite_selection": self.on_satellite_selection} + + builder = get_builder(f"{UI_RESOURCES_PATH}xml/editor.glade", handlers) + + self._satellite_view = builder.get_object("satellite_view") + self._transponder_view = builder.get_object("transponder_view") + builder.get_object("sat_pos_column").set_cell_data_func(builder.get_object("sat_pos_renderer"), + self.sat_pos_func) + + self._stores = {3: builder.get_object("pol_store"), + 4: builder.get_object("fec_store"), + 5: builder.get_object("system_store"), + 6: builder.get_object("mod_store")} + + self.pack_start(builder.get_object("satellite_editor_box"), True, True, 0) + self._app.connect("profile-changed", lambda a, m: self.load_satellites_list()) + self.show() + self.load_satellites_list() + + def load_satellites_list(self, path=None): + gen = self.on_satellites_list_load(path) + GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW) + + @run_idle + def on_open(self): + response = get_chooser_dialog(self._app.app_window, self._settings, "satellites.xml", ("*.xml",)) + if response in (Gtk.ResponseType.CANCEL, Gtk.ResponseType.DELETE_EVENT): + return + + if not str(response).endswith("satellites.xml"): + self._app.show_error_message("No satellites.xml file is selected!") + return + + self.load_satellites_list(response) + + def on_satellite_selection(self, view): + model = self._transponder_view.get_model() + model.clear() + + self._current_sat_path, column = view.get_cursor() + if self._current_sat_path: + list(map(model.append, view.get_model()[self._current_sat_path][-1])) + + def on_up(self, item): + move_items(KeyboardKey.UP, self._satellite_view) + + def on_down(self, item): + move_items(KeyboardKey.DOWN, self._satellite_view) + + def on_button_press(self, menu, event): + if event.get_event_type() == Gdk.EventType.DOUBLE_BUTTON_PRESS: + self.on_edit(self._satellite_view if self._satellite_view.is_focus() else self._transponder_view) + else: + on_popup_menu(menu, event) + + def on_key_release(self, view, event): + """ Handling keystrokes """ + key_code = event.hardware_keycode + if not KeyboardKey.value_exist(key_code): + return + key = KeyboardKey(key_code) + ctrl = event.state & MOD_MASK + + if key is KeyboardKey.DELETE: + self.on_remove(view) + elif key is KeyboardKey.INSERT: + pass + elif ctrl and key is KeyboardKey.E: + self.on_edit(view) + elif ctrl and key is KeyboardKey.S: + self.on_satellite() + elif ctrl and key is KeyboardKey.T: + self.on_transponder() + elif ctrl and key in MOVE_KEYS: + move_items(key, self._satellite_view) + elif key is KeyboardKey.LEFT or key is KeyboardKey.RIGHT: + view.do_unselect_all(view) + + def on_satellites_list_load(self, path=None): + """ Load satellites data into model """ + model = self._satellite_view.get_model() + model.clear() + + try: + path = path or self._settings.profile_data_path + "satellites.xml" + satellites = get_satellites(path) + yield True + except FileNotFoundError as e: + msg = get_message("Please, download files from receiver or setup your path for read data!") + self._app.show_error_message(f"{e}\n{msg}") + except ExpatError as e: + msg = f"The file [{path}] is not formatted correctly or contains invalid characters! Cause: {e}" + self._app.show_error_message(msg) + else: + for sat in satellites: + yield model.append(sat) + + def on_add(self, view): + """ Common adding """ + self.on_edit(view, force=True) + + def on_satellite_add(self, item): + self.on_satellite() + + def on_transponder_add(self, item): + self.on_transponder() + + def on_edit(self, view, force=False): + """ Common edit """ + paths = self.check_selection(view, "Please, select only one item!") + if not paths: + return + + model = view.get_model() + row = model[paths][:] + itr = model.get_iter(paths) + + if view is self._satellite_view: + self.on_satellite(None if force else Satellite(*row), itr) + elif view is self._transponder_view: + self.on_transponder(None if force else Transponder(*row), itr) + + def on_satellite(self, satellite=None, edited_itr=None): + """ Create or edit satellite""" + sat_dialog = SatelliteDialog(self._app.get_active_window(), satellite) + sat = sat_dialog.run() + sat_dialog.destroy() + + if sat: + model, paths = self._satellite_view.get_selection().get_selected_rows() + if satellite and edited_itr: + model.set(edited_itr, {i: v for i, v in enumerate(sat)}) + else: + if len(model): + index = paths[0].get_indices()[0] + 1 + model.insert(index, sat) + else: + model.append(sat) + + def on_transponder(self, transponder=None, edited_itr=None): + """ Create or edit transponder """ + + paths = self.check_selection(self._satellite_view, "Please, select only one satellite!") + if paths is None: + return + elif len(paths) == 0: + self._app.show_error_message("No satellite is selected!") + return + + dialog = TransponderDialog(self._app.get_active_window(), transponder) + tr = dialog.run() + dialog.destroy() + + if tr: + sat_model = self._satellite_view.get_model() + transponders = sat_model[paths][-1] + tr_model, tr_paths = self._transponder_view.get_selection().get_selected_rows() + + if transponder and edited_itr: + tr_model.set(edited_itr, {i: v for i, v in enumerate(tr)}) + transponders[tr_model.get_path(edited_itr).get_indices()[0]] = tr + else: + index = paths[0].get_indices()[0] + 1 + tr_model.insert(index, tr) + transponders.insert(index, tr) + + def check_selection(self, view, message): + """ Checks if any row is selected. Shows error dialog if selected more than one. + + Returns selected path or None. + """ + model, paths = view.get_selection().get_selected_rows() + if len(paths) > 1: + self._app.show_error_message(message) + return + + return paths + + def on_remove(self, view): + """ Removes selected satellites and transponders. """ + selection = view.get_selection() + model, paths = selection.get_selected_rows() + + if view is self._satellite_view: + list(map(model.remove, [model.get_iter(path) for path in paths])) + elif view is self._transponder_view: + if self._current_sat_path: + trs = self._satellite_view.get_model()[self._current_sat_path][-1] + list(map(trs.pop, sorted(map(lambda p: p.get_indices()[0], paths), reverse=True))) + list(map(model.remove, [model.get_iter(path) for path in paths])) + else: + self._app.show_error_message("No satellite is selected!") + + def sat_pos_func(self, column, renderer, model, itr, data): + """ Converts and sets the satellite position value to a readable format. """ + pos = int(model.get_value(itr, 2)) + renderer.set_property("text", f"{abs(pos / 10):0.1f}{'W' if pos < 0 else 'E'}") + + @run_idle + def on_save(self, app, page): + if page is Page.SATELLITE and show_dialog(DialogType.QUESTION, self._app.app_window) == Gtk.ResponseType.OK: + write_satellites((Satellite(*r) for r in self._satellite_view.get_model()), + self._settings.profile_data_path + "satellites.xml") + + def on_save_as(self, app, page): + show_dialog(DialogType.ERROR, transient=self._app.app_window, text="Not implemented yet!") + + def on_download(self, app, page): + if page is Page.SATELLITE: + self._app.on_download_data(DownloadType.SATELLITES) + + def on_upload(self, app, page): + if page is Page.SATELLITE: + self._app.upload_data(DownloadType.SATELLITES) + + @run_idle + def on_update(self, item): + SatellitesUpdateDialog(self._app.get_active_window(), self._settings, self._satellite_view.get_model()).show() + + +if __name__ == "__main__": + pass diff --git a/app/ui/xml/editor.glade b/app/ui/xml/editor.glade new file mode 100644 index 00000000..d2285c49 --- /dev/null +++ b/app/ui/xml/editor.glade @@ -0,0 +1,601 @@ + + + + + + + + + + + True + False + gtk-add + + + True + False + + + Add + True + False + popup_menu_add_image + False + + + + + + True + False + + + + + gtk-edit + True + False + True + True + + + + + + + True + False + + + + + gtk-remove + True + False + True + True + + + + + + + True + False + gtk-add + + + True + False + + + Add + True + False + popup_menu_add_image_2 + False + + + + + + True + False + + + + + gtk-edit + True + False + True + True + + + + + + + True + False + + + + + gtk-remove + True + False + True + True + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + True + False + vertical + 5 + + + True + True + True + + + 350 + True + False + 0.49000000953674316 + in + + + True + False + 5 + 5 + 5 + 5 + vertical + 5 + + + True + False + 15 + 15 + 5 + 5 + 5 + + + True + True + True + Add + + + + True + False + document-new-symbolic + + + + + False + True + 0 + + + + + True + True + True + Update + + + + True + False + emblem-synchronizing-symbolic + + + + + False + True + 1 + + + + + False + True + 0 + + + + + True + True + in + + + True + True + satellite_view_model + True + True + + + + + + multiple + + + + + Name + True + 0.49000000953674316 + + + end + + + 0 + + + + + + + 85 + Pos + 0.49000000953674316 + + + 0.49000000953674316 + + + 2 + + + + + + + + + True + True + 1 + + + + + + + True + False + Satellites + + + + + False + False + + + + + True + False + 0.49000000953674316 + in + + + True + False + 5 + 5 + 5 + 5 + vertical + 5 + + + True + False + 15 + 15 + 5 + 5 + 5 + + + True + True + True + Add + + + + True + False + document-new-symbolic + + + + + False + True + 0 + + + + + + + + False + True + 0 + + + + + True + True + in + + + True + True + transponder_view_model + 0 + True + both + + + + + multiple + + + + + True + 20 + Freq + True + 0.5 + + + 0.49000000953674316 + + + 0 + + + + + + + True + 20 + Rate + True + 0.5 + + + 0.49000000953674316 + + + 1 + + + + + + + True + 20 + Pol + True + 0.5 + + + 0.49000000953674316 + + + 2 + + + + + + + True + 20 + FEC + True + 0.5 + + + 0.49000000953674316 + + + 3 + + + + + + + True + 20 + System + True + 0.5 + + + 0.49000000953674316 + + + 4 + + + + + + + True + 20 + Mod + True + 0.5 + + + 0.49000000953674316 + + + 5 + + + + + + + False + True + Pls mode + True + + + + 6 + + + + + + + False + True + Pls code + True + + + + 7 + + + + + + + False + True + Is ID + True + + + + 8 + + + + + + + False + T2-MI PLP ID + + + + 9 + + + + + + + + + True + True + 1 + + + + + + + True + False + Transponder + + + + + True + False + + + + + True + True + 1 + + + + diff --git a/app/ui/xml/update.glade b/app/ui/xml/update.glade new file mode 100644 index 00000000..26cda14b --- /dev/null +++ b/app/ui/xml/update.glade @@ -0,0 +1,1173 @@ + + + + + + + + + + + 180 + 0.10000000000000001 + 10 + + + 180 + 0.10000000000000001 + 10 + + + True + False + edit-undo + + + True + False + + + gtk-select-all + True + False + True + True + + + + + + Remove selection + True + False + remove_selection_image + False + + + + + + True + False + network-receive-symbolic + 1 + + + True + False + window-close-symbolic + 1 + + + True + False + emblem-synchronizing-symbolic + 1 + + + + + + + + + E + + + W + + + + + + + + + + + + + + + + + + + update_sat_list_store + + + update_sat_list_model_filter + + + + + + + + + + + + + + + + + + + + + + + + + + + + + False + Satellites update + True + center-on-parent + True + True + True + + + + + + + True + False + vertical + + + True + False + vertical + + + True + False + 15 + 15 + 5 + 5 + 5 + + + True + False + Source + 0 + + LyngSat + KingOfSat + + + + False + True + 1 + + + + + True + False + expand + + + Update + True + True + True + Update + sat_update_image + True + + + + False + True + 1 + + + + + Receive + True + True + True + Receive + sat_receive_image + True + + + + False + True + 3 + + + + + Cancel + True + True + Cancel + sat_update_cancel_image + True + + + + + False + True + 3 + + + + + False + True + 2 + + + + + True + False + expand + + + True + True + True + Filter + True + + + + True + False + edit-find-replace-symbolic + 1 + + + + + + False + True + 2 + + + + + True + True + True + Find + True + + + + True + False + edit-find-symbolic + 1 + + + + + + False + True + 3 + + + + + False + True + end + 3 + + + + + False + True + 0 + + + + + + False + True + 0 + + + + + False + + + True + False + 2 + 2 + + + 200 + True + True + edit-find-symbolic + False + False + + + False + True + 0 + + + + + True + False + True + True + + + True + False + down + + + + + False + False + 1 + + + + + True + False + True + True + + + True + False + up + + + + + False + False + 2 + + + + + + + + False + True + 1 + + + + + False + + + True + False + 2 + 2 + 2 + + + True + False + From: + + + 0 + 0 + + + + + True + True + number + pos_adjustment + 1 + True + + + + 1 + 0 + + + + + True + False + side_store + 0 + + + + + 0 + + + + + 2 + 0 + + + + + True + False + To: + + + 3 + 0 + + + + + True + True + number + pos_adjustment2 + 1 + True + + + + 4 + 0 + + + + + True + False + side_store + 0 + + + + + 0 + + + + + 5 + 0 + + + + + gtk-apply + True + True + True + True + + + + 6 + 0 + + + + + + + False + True + 2 + + + + + True + True + 5 + 5 + 5 + 5 + True + + + True + False + 0.49000000953674316 + in + + + True + False + 5 + 5 + 5 + 5 + vertical + 5 + + + True + True + in + + + True + True + update_sat_list_model_sort + 0 + True + + + + + multiple + + + + + Satellite + True + True + 0.5 + True + 0 + + + 0.0099999997764825821 + end + + + 0 + + + + + + + Position + True + 0.5 + True + 1 + + + 0.49000000953674316 + + + 1 + + + + + + + Type + True + 0.5 + True + 2 + + + 0.49000000953674316 + + + 2 + + + + + + + False + Url + + + + 3 + + + + + + + Selected + True + True + 4 + + + + + + 4 + + + + + + + + + True + True + 1 + + + + + True + False + 10 + 10 + 5 + + + True + False + document-properties-symbolic + + + False + True + 0 + + + + + True + False + 0 + + + False + True + 1 + + + + + False + True + 2 + + + + + + + True + False + Satellites + + + + + True + True + + + + + True + vertical + True + + + True + False + 0.49000000953674316 + in + + + True + False + 5 + 5 + 5 + 5 + vertical + 5 + + + True + True + in + + + True + True + update_transponder_store + 0 + True + + + + + + Transponder + True + 0.5 + + + 0.0099999997764825821 + + + 0 + + + + + + + False + link + + + + 1 + + + + + + + Selected + + + + + + 2 + + + + + + + + + True + True + 0 + + + + + True + False + 10 + 10 + 5 + + + True + False + document-properties-symbolic + + + False + True + 0 + + + + + True + False + 0 + + + False + True + 1 + + + + + False + True + 2 + + + + + + + True + False + Transponders + + + + + True + True + + + + + True + False + 0.49000000953674316 + in + + + True + False + 5 + 5 + 5 + 5 + vertical + 5 + + + True + True + in + + + True + True + update_service_store + 1 + + + + + + Service + True + 0.5 + + + 2 + + + 0 + + + + + 0.0099999997764825821 + + + 1 + + + + + + + Package + True + 0.5 + + + end + + + 2 + + + + + + + Type + True + 0.5 + + + 0.49000000953674316 + + + 3 + + + + + + + SID + True + 0.5 + + + 0.49000000953674316 + + + 4 + + + + + + + False + CAS + True + 0.5 + + + 0.49000000953674316 + + + + + + + + + True + True + 0 + + + + + True + False + 10 + 10 + 5 + + + True + False + document-properties-symbolic + + + False + True + 0 + + + + + True + False + 0 + + + False + True + 1 + + + + + False + True + 1 + + + + + + + + + + True + False + Services + + + + + True + True + + + + + True + True + + + + + True + True + 3 + + + + + False + 4 + 5 + 5 + 0 + in + + + True + False + other + True + + + False + 6 + end + + + False + False + 0 + + + + + False + + + 120 + True + True + in + + + True + True + False + 5 + 5 + + + + + True + True + 0 + + + + + True + True + 0 + + + + + + + False + True + 4 + + + + + True + False + True + + + + False + 6 + end + + + False + False + 0 + + + + + False + 16 + + + True + False + Info + + + False + True + 0 + + + + + False + False + 0 + + + + + False + True + 9 + + + + + +