diff --git a/BlocksScreen/lib/network.py b/BlocksScreen/lib/network.py index 51b87074..61ea4078 100644 --- a/BlocksScreen/lib/network.py +++ b/BlocksScreen/lib/network.py @@ -354,13 +354,15 @@ def get_wired_interfaces(self) -> typing.List[dbusNm.NetworkDeviceWired]: filter( lambda path: path, filter( - lambda device: asyncio.run_coroutine_threadsafe( - dbusNm.NetworkDeviceGeneric( - bus=self.system_dbus, device_path=device - ).device_type.get_async(), - self.loop, - ).result(timeout=2) - == dbusNm.enums.DeviceType.ETHERNET, + lambda device: ( + asyncio.run_coroutine_threadsafe( + dbusNm.NetworkDeviceGeneric( + bus=self.system_dbus, device_path=device + ).device_type.get_async(), + self.loop, + ).result(timeout=2) + == dbusNm.enums.DeviceType.ETHERNET + ), devices, ), ), @@ -386,13 +388,15 @@ def get_wireless_interfaces( filter( lambda path: path, filter( - lambda device: asyncio.run_coroutine_threadsafe( - dbusNm.NetworkDeviceGeneric( - bus=self.system_dbus, device_path=device - ).device_type.get_async(), - self.loop, - ).result(timeout=3) - == dbusNm.enums.DeviceType.WIFI, + lambda device: ( + asyncio.run_coroutine_threadsafe( + dbusNm.NetworkDeviceGeneric( + bus=self.system_dbus, device_path=device + ).device_type.get_async(), + self.loop, + ).result(timeout=3) + == dbusNm.enums.DeviceType.WIFI + ), devices, ), ), @@ -472,6 +476,68 @@ def get_current_ip_addr(self) -> str: return [address_data["address"][1] for address_data in addr_data][0] except IndexError as e: logger.error("List out of index %s", e) + except Exception as e: + logger.error("Error getting current IP address: %s", e) + return "" + + def get_device_ip_by_interface(self, interface_name: str = "wlan0") -> str: + """Get IPv4 address for a specific interface via NetworkManager D-Bus. + + This method retrieves the IP address directly from a specific network + interface, useful for getting hotspot IP when it's the active connection + on that interface. + + Args: + interface_name: The network interface name (e.g., "wlan0", "eth0") + + Returns: + str: The IPv4 address or empty string if not found + """ + if not self.nm: + return "" + + try: + devices_future = asyncio.run_coroutine_threadsafe( + self.nm.get_devices(), self.loop + ) + devices = devices_future.result(timeout=2) + + for device_path in devices: + device = dbusNm.NetworkDeviceGeneric( + bus=self.system_dbus, device_path=device_path + ) + + # Check if this is the interface we want + iface_future = asyncio.run_coroutine_threadsafe( + device.interface.get_async(), self.loop + ) + iface = iface_future.result(timeout=2) + + if iface != interface_name: + continue + + # Get IP4Config path + ip4_path_future = asyncio.run_coroutine_threadsafe( + device.ip4_config.get_async(), self.loop + ) + ip4_path = ip4_path_future.result(timeout=2) + + if not ip4_path or ip4_path == "/": + return "" + + # Get address data + ip4_config = dbusNm.IPv4Config(bus=self.system_dbus, ip4_path=ip4_path) + addr_data_future = asyncio.run_coroutine_threadsafe( + ip4_config.address_data.get_async(), self.loop + ) + addr_data = addr_data_future.result(timeout=2) + + if addr_data and len(addr_data) > 0: + return addr_data[0]["address"][1] + + except Exception as e: + logger.error("Failed to get IP for interface %s: %s", interface_name, e) + return "" async def _gather_primary_interface( @@ -821,7 +887,7 @@ def get_saved_ssid_names(self) -> typing.List[str]: return [] return list( map( - lambda saved_network: (saved_network.get("ssid", None)), + lambda saved_network: saved_network.get("ssid", None), _saved_networks, ) ) diff --git a/BlocksScreen/lib/panels/networkWindow.py b/BlocksScreen/lib/panels/networkWindow.py index f9636a82..19574cf5 100644 --- a/BlocksScreen/lib/panels/networkWindow.py +++ b/BlocksScreen/lib/panels/networkWindow.py @@ -1,1167 +1,3235 @@ import logging -import subprocess # nosec: B404 -import typing +import threading from functools import partial +from typing import ( + Any, + Callable, + Dict, + List, + NamedTuple, + Optional, +) from lib.network import SdbusNetworkManagerAsync from lib.panels.widgets.keyboardPage import CustomQwertyKeyboard +from lib.panels.widgets.loadWidget import LoadingOverlayWidget from lib.panels.widgets.popupDialogWidget import Popup -from lib.ui.wifiConnectivityWindow_ui import Ui_wifi_stacked_page -from lib.utils.list_button import ListCustomButton +from lib.utils.blocks_button import BlocksCustomButton +from lib.utils.blocks_frame import BlocksCustomFrame +from lib.utils.blocks_linedit import BlocksCustomLinEdit +from lib.utils.blocks_Scrollbar import CustomScrollBar +from lib.utils.blocks_togglebutton import NetworkWidgetbuttons +from lib.utils.check_button import BlocksCustomCheckButton +from lib.utils.icon_button import IconButton +from lib.utils.list_model import EntryDelegate, EntryListModel, ListItem from PyQt6 import QtCore, QtGui, QtWidgets +from PyQt6.QtCore import QObject, QRunnable, QThreadPool, pyqtSignal logger = logging.getLogger("logs/BlocksScreen.log") -class BuildNetworkList(QtCore.QThread): - """Retrieves information from sdbus interface about scanned networks""" +LOAD_TIMEOUT_MS = 30_000 +NETWORK_CONNECT_DELAY_MS = 5_000 +NETWORK_LIST_REFRESH_MS = 10_000 +STATUS_CHECK_INTERVAL_MS = 2_000 +DEFAULT_POLL_INTERVAL_MS = 10_000 - scan_result: typing.ClassVar[QtCore.pyqtSignal] = QtCore.pyqtSignal( - dict, name="scan-results" - ) - finished_network_list_build: typing.ClassVar[QtCore.pyqtSignal] = QtCore.pyqtSignal( - list, name="finished-network-list-build" - ) +SIGNAL_EXCELLENT_THRESHOLD = 75 +SIGNAL_GOOD_THRESHOLD = 50 +SIGNAL_FAIR_THRESHOLD = 25 +SIGNAL_MINIMUM_THRESHOLD = 5 - def __init__(self) -> None: +PRIORITY_HIGH = 90 +PRIORITY_MEDIUM = 50 +PRIORITY_LOW = 20 + +SEPARATOR_SIGNAL_VALUE = -10 +PRIVACY_BIT = 1 + +# SSIDs that indicate hidden networks +HIDDEN_NETWORK_INDICATORS = ("", "UNKNOWN", "", None) + + +class NetworkInfo(NamedTuple): + """Information about a network.""" + + signal: int + status: str + is_open: bool = False + is_saved: bool = False + is_hidden: bool = False # Added flag for hidden networks + + +class NetworkScanResult(NamedTuple): + """Result of a network scan.""" + + ssid: str + signal: int + status: str + is_open: bool = False + + +class NetworkScanRunnable(QRunnable): + """Runnable for scanning networks in background thread.""" + + class Signals(QObject): + """Signals for network scan results.""" + + scan_results = pyqtSignal(dict, name="scan-results") + finished_network_list_build = pyqtSignal( + list, name="finished-network-list-build" + ) + error = pyqtSignal(str) + + def __init__(self, nm: SdbusNetworkManagerAsync) -> None: + """Initialize the network scan runnable.""" super().__init__() - self.mutex = QtCore.QMutex() - self.condition = QtCore.QWaitCondition() - self.restart = False - self.mutex.unlock() - self.network_items_list = [] - self.nm = SdbusNetworkManagerAsync() - if not self.nm: - logger.error( - "Cannot scan for networks, parent does not have \ - sdbus_network ('SdbusNetworkManagerAsync' instance class)" - ) - return - logger.info("Network Scanner Thread Initiated") + self._nm = nm + self.signals = NetworkScanRunnable.Signals() + + def run(self) -> None: + """Execute the network scan.""" + try: + self._nm.rescan_networks() + saved_ssids = self._nm.get_saved_ssid_names() + available = self._get_available_networks() + data_dict = self._build_data_dict(available, saved_ssids) + self.signals.scan_results.emit(data_dict) + items = self._build_network_list(data_dict) + self.signals.finished_network_list_build.emit(items) + except Exception as e: + logger.error("Error scanning networks", exc_info=True) + self.signals.error.emit(str(e)) + + def _get_available_networks(self) -> Dict[str, Dict]: + """Get available networks from NetworkManager.""" + if self._nm.check_wifi_interface(): + return self._nm.get_available_networks() or {} + return {} + + def _build_data_dict( + self, available: Dict[str, Dict], saved_ssids: List[str] + ) -> Dict[str, Dict]: + """Build data dictionary from available networks.""" + data_dict: Dict[str, Dict] = {} + for ssid, props in available.items(): + signal = int(props.get("signal_level", 0)) + sec_tuple = props.get("security", (0, 0, 0)) + caps_value = sec_tuple[2] if len(sec_tuple) > 2 else 0 + is_open = (caps_value & PRIVACY_BIT) == 0 + # Check if this is a hidden network + is_hidden = ssid in HIDDEN_NETWORK_INDICATORS or not ssid.strip() + data_dict[ssid] = { + "signal_level": signal, + "is_saved": ssid in saved_ssids, + "is_open": is_open, + "is_hidden": is_hidden, + } + return data_dict + + def _build_network_list(self, data_dict: Dict[str, Dict]) -> List[tuple]: + """Build sorted network list for display.""" + current_ssid = self._nm.get_current_ssid() + + saved_nets = [ + (ssid, info["signal_level"], info["is_open"], info.get("is_hidden", False)) + for ssid, info in data_dict.items() + if info["is_saved"] + ] + unsaved_nets = [ + (ssid, info["signal_level"], info["is_open"], info.get("is_hidden", False)) + for ssid, info in data_dict.items() + if not info["is_saved"] + ] + + saved_nets.sort(key=lambda x: -x[1]) + unsaved_nets.sort(key=lambda x: -x[1]) + + items: List[tuple] = [] + + for ssid, signal, is_open, is_hidden in saved_nets: + status = "Active" if ssid == current_ssid else "Saved" + items.append((ssid, signal, status, is_open, True, is_hidden)) + + for ssid, signal, is_open, is_hidden in unsaved_nets: + status = "Open" if is_open else "Protected" + items.append((ssid, signal, status, is_open, False, is_hidden)) + + return items + + +class BuildNetworkList(QtCore.QObject): + """Worker class for building network lists with polling support.""" + + scan_results = pyqtSignal(dict, name="scan-results") + finished_network_list_build = pyqtSignal(list, name="finished-network-list-build") + error = pyqtSignal(str) + + def __init__( + self, + nm: SdbusNetworkManagerAsync, + poll_interval_ms: int = DEFAULT_POLL_INTERVAL_MS, + ) -> None: + """Initialize the network list builder.""" + super().__init__() + self._nm = nm + self._threadpool = QThreadPool.globalInstance() + self._poll_interval_ms = poll_interval_ms + self._is_scanning = False + self._scan_lock = threading.Lock() + self._timer = QtCore.QTimer(self) + self._timer.setSingleShot(True) + self._timer.timeout.connect(self._do_scan) + + def start_polling(self) -> None: + """Start periodic network scanning.""" + self._schedule_next_scan() + + def stop_polling(self) -> None: + """Stop periodic network scanning.""" + self._timer.stop() def build(self) -> None: - """Starts QThread""" - with QtCore.QMutexLocker(self.mutex): - if not self.isRunning(): - self.start(QtCore.QThread.Priority.LowPriority) - else: - self.restart = True - self.condition.wakeOne() + """Trigger immediate network scan.""" + self._do_scan() + + def _schedule_next_scan(self) -> None: + """Schedule the next network scan.""" + self._timer.start(self._poll_interval_ms) + + def _on_task_finished(self, items: List) -> None: + """Handle scan completion.""" + with self._scan_lock: + self._is_scanning = False + self.finished_network_list_build.emit(items) + self._schedule_next_scan() + + def _on_task_scan_results(self, data_dict: Dict) -> None: + """Handle scan results.""" + self.scan_results.emit(data_dict) + + def _on_task_error(self, err: str) -> None: + """Handle scan error.""" + with self._scan_lock: + self._is_scanning = False + self.error.emit(err) + self._schedule_next_scan() + + def _do_scan(self) -> None: + """Execute network scan in background thread.""" + with self._scan_lock: + if self._is_scanning: + return + self._is_scanning = True - def stop(self): - """Stops QThread execution""" - self.mutex.lock() - self.condition.wakeOne() - self.mutex.unlock() - self.deleteLater() + task = NetworkScanRunnable(self._nm) + task.signals.finished_network_list_build.connect(self._on_task_finished) + task.signals.scan_results.connect(self._on_task_scan_results) + task.signals.error.connect(self._on_task_error) + self._threadpool.start(task) - def run(self) -> None: - """BuildNetworkList main thread logic""" - logger.debug("Scanning and building network list") - while True: - self.mutex.lock() - self.network_items_list.clear() - self.nm.rescan_networks() - saved_ssids = self.nm.get_saved_ssid_names() - saved_networks = self.nm.get_saved_networks() - unsaved_networks = [] - networks = [] - if self.nm.check_wifi_interface(): - available_networks = self.nm.get_available_networks() - if not available_networks: # Skip everything if no networks exist - logger.debug("No available networks after scan") - self.finished_network_list_build.emit(self.network_items_list) - return - for ssid_key in available_networks: - properties = available_networks.get(ssid_key, {}) - signal = int(properties.get("signal_level", 0)) - networks.append( - { - "ssid": ssid_key if ssid_key else "UNKNOWN", - "signal": signal, - "is_saved": bool(ssid_key in saved_ssids), - } - ) - if networks: - saved_networks = sorted( - [n for n in networks if n["is_saved"]], - key=lambda x: -x["signal"], - ) - unsaved_networks = sorted( - [n for n in networks if not n["is_saved"]], - key=lambda x: -x["signal"], - ) - elif saved_networks: - saved_networks = sorted([n for n in saved_networks], key=lambda x: -1) - if saved_networks: - for net in saved_networks: - if "ap" in net.get("mode", ""): - return - ssid = net.get("ssid", "UNKNOWN") - signal = ( - self.nm.get_connection_signal_by_ssid(ssid) - if ssid != "UNKNOWN" - else 0 - ) - if ssid == self.nm.get_current_ssid(): - self.network_items_list.append((ssid, signal, "Active")) - else: - self.network_items_list.append((ssid, signal, "Saved")) - if saved_networks and unsaved_networks: # Separator - self.network_items_list.append("separator") - if unsaved_networks: - for net in unsaved_networks: - ssid = net.get("ssid", "UNKNOWN") - signal = ( - self.nm.get_connection_signal_by_ssid(ssid) - if ssid != "UNKNOWN" - else 0 - ) - self.network_items_list.append((ssid, signal, "Protected")) - # Add a dummy blank space at the end if there are any unsaved networks - if unsaved_networks: - self.network_items_list.append("blank") - - self.finished_network_list_build.emit(self.network_items_list) - if not self.restart: - self.condition.wait(self.mutex) - self.restart = False - self.mutex.unlock() + +class WifiIconProvider: + """Provider for Wi-Fi signal strength icons.""" + + def __init__(self) -> None: + """Initialize icon paths.""" + self._paths = { + (0, False): ":/network/media/btn_icons/0bar_wifi.svg", + (1, False): ":/network/media/btn_icons/1bar_wifi.svg", + (2, False): ":/network/media/btn_icons/2bar_wifi.svg", + (3, False): ":/network/media/btn_icons/3bar_wifi.svg", + (4, False): ":/network/media/btn_icons/4bar_wifi.svg", + (0, True): ":/network/media/btn_icons/0bar_wifi_protected.svg", + (1, True): ":/network/media/btn_icons/1bar_wifi_protected.svg", + (2, True): ":/network/media/btn_icons/2bar_wifi_protected.svg", + (3, True): ":/network/media/btn_icons/3bar_wifi_protected.svg", + (4, True): ":/network/media/btn_icons/4bar_wifi_protected.svg", + } + + def get_pixmap(self, signal: int, status: str) -> QtGui.QPixmap: + """Get pixmap for given signal strength and status.""" + bars = self._signal_to_bars(signal) + is_protected = status == "Protected" + key = (bars, is_protected) + path = self._paths.get(key, self._paths[(0, False)]) + return QtGui.QPixmap(path) + + @staticmethod + def _signal_to_bars(signal: int) -> int: + """Convert signal strength to bar count.""" + if signal < SIGNAL_MINIMUM_THRESHOLD: + return 0 + elif signal >= SIGNAL_EXCELLENT_THRESHOLD: + return 4 + elif signal >= SIGNAL_GOOD_THRESHOLD: + return 3 + elif signal > SIGNAL_FAIR_THRESHOLD: + return 2 + else: + return 1 class NetworkControlWindow(QtWidgets.QStackedWidget): - """Network Control panel Widget""" - - request_network_scan = QtCore.pyqtSignal(name="scan-network") - new_ip_signal = QtCore.pyqtSignal(str, name="ip-address-change") - get_hotspot_ssid = QtCore.pyqtSignal(str, name="hotspot-ssid-name") - delete_network_signal = QtCore.pyqtSignal(str, name="delete-network") - - def __init__(self, parent: typing.Optional[QtWidgets.QWidget], /) -> None: - super(NetworkControlWindow, self).__init__(parent) - self.background: typing.Optional[QtGui.QPixmap] = None - self.panel = Ui_wifi_stacked_page() - self.panel.setupUi(self) - self.popup = Popup(self) - self.sdbus_network = SdbusNetworkManagerAsync() - self.start: bool = True - self.saved_network = dict - - self._load_timer = QtCore.QTimer() - self._load_timer.setSingleShot(True) - self._load_timer.timeout.connect(self._handle_load_timeout) + """Main network control window widget.""" + + request_network_scan = pyqtSignal(name="scan-network") + new_ip_signal = pyqtSignal(str, name="ip-address-change") + get_hotspot_ssid = pyqtSignal(str, name="hotspot-ssid-name") + delete_network_signal = pyqtSignal(str, name="delete-network") + + def __init__(self, parent: Optional[QtWidgets.QWidget] = None, /) -> None: + """Initialize the network control window.""" + super().__init__(parent) if parent else super().__init__() + + self._init_instance_variables() + self._setupUI() + self._init_timers() + self._init_model_view() + self._init_network_worker() + self._setup_navigation_signals() + self._setup_action_signals() + self._setup_toggle_signals() + self._setup_password_visibility_signals() + self._setup_icons() + self._setup_input_fields() + self._setup_keyboard() + self._setup_scrollbar_signals() + + self._network_list_worker.build() + self.request_network_scan.emit() + self.hide() - # Network Scan - self.network_list_widget = QtWidgets.QListWidget( - parent=self.panel.network_list_page - ) - self.build_network_list() + # Initialize UI state + self._init_ui_state() + + def _init_ui_state(self) -> None: + """Initialize UI to a clean disconnected state.""" + self.loadingwidget.setVisible(False) + self._hide_all_info_elements() + self._configure_info_box_centered() + self.mn_info_box.setVisible(True) + self.mn_info_box.setText( + "Network connection required.\n\nConnect to Wi-Fi\nor\nTurn on Hotspot" + ) + + def _hide_all_info_elements(self) -> None: + """Hide ALL elements in the info panel (details, loading, info box).""" + # Hide network details + self.netlist_ip.setVisible(False) + self.netlist_ssuid.setVisible(False) + self.mn_info_seperator.setVisible(False) + self.line_2.setVisible(False) + self.netlist_strength.setVisible(False) + self.netlist_strength_label.setVisible(False) + self.line_3.setVisible(False) + self.netlist_security.setVisible(False) + self.netlist_security_label.setVisible(False) + # Hide loading + self.loadingwidget.setVisible(False) + # Hide info box + self.mn_info_box.setVisible(False) + + def _init_instance_variables(self) -> None: + """Initialize all instance variables.""" + self._icon_provider = WifiIconProvider() + self._ongoing_update = False + self._is_first_run = True + self._networks: Dict[str, NetworkInfo] = {} + self._previous_panel: Optional[QtWidgets.QWidget] = None + self._current_field: Optional[QtWidgets.QLineEdit] = None + self._current_network_is_open = False + self._current_network_is_hidden = False + self._is_connecting = False + self._target_ssid: Optional[str] = None + self._last_displayed_ssid: Optional[str] = None + self._current_network_ssid: Optional[str] = ( + None # Track current network for priority + ) + + def _setupUI(self) -> None: + """Setup all UI elements programmatically.""" + self.setObjectName("wifi_stacked_page") + self.resize(800, 480) + + size_policy = QtWidgets.QSizePolicy( + QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Minimum + ) + size_policy.setHorizontalStretch(0) + size_policy.setVerticalStretch(0) + size_policy.setHeightForWidth(self.sizePolicy().hasHeightForWidth()) + self.setSizePolicy(size_policy) + self.setMinimumSize(QtCore.QSize(0, 400)) + self.setMaximumSize(QtCore.QSize(16777215, 575)) + self.setStyleSheet( + "#wifi_stacked_page{\n" + " background-image: url(:/background/media/1st_background.png);\n" + "}\n" + ) + + self._sdbus_network = SdbusNetworkManagerAsync() + self._popup = Popup(self) + self._right_arrow_icon = QtGui.QPixmap( + ":/arrow_icons/media/btn_icons/right_arrow.svg" + ) + + # Create all pages + self._setup_main_network_page() + self._setup_network_list_page() + self._setup_add_network_page() + self._setup_saved_connection_page() + self._setup_saved_details_page() + self._setup_hotspot_page() + self._setup_hidden_network_page() + + self.setCurrentIndex(0) + + def _create_white_palette(self) -> QtGui.QPalette: + """Create a palette with white text.""" + palette = QtGui.QPalette() + white_brush = QtGui.QBrush(QtGui.QColor(255, 255, 255)) + white_brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern) + grey_brush = QtGui.QBrush(QtGui.QColor(120, 120, 120)) + grey_brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern) + + for group in [ + QtGui.QPalette.ColorGroup.Active, + QtGui.QPalette.ColorGroup.Inactive, + ]: + palette.setBrush(group, QtGui.QPalette.ColorRole.WindowText, white_brush) + palette.setBrush(group, QtGui.QPalette.ColorRole.Text, white_brush) - self.network_list_worker = BuildNetworkList() - self.network_list_worker.finished_network_list_build.connect( - self.handle_network_list + palette.setBrush( + QtGui.QPalette.ColorGroup.Disabled, + QtGui.QPalette.ColorRole.WindowText, + grey_brush, ) - self.panel.rescan_button.clicked.connect( - lambda: QtCore.QTimer.singleShot(100, self.network_list_worker.build) + palette.setBrush( + QtGui.QPalette.ColorGroup.Disabled, + QtGui.QPalette.ColorRole.Text, + grey_brush, + ) + + return palette + + def _setup_main_network_page(self) -> None: + """Setup the main network page.""" + self.main_network_page = QtWidgets.QWidget() + size_policy = QtWidgets.QSizePolicy( + QtWidgets.QSizePolicy.Policy.Expanding, + QtWidgets.QSizePolicy.Policy.Expanding, ) + self.main_network_page.setSizePolicy(size_policy) + self.main_network_page.setObjectName("main_network_page") - self.sdbus_network.nm_state_change.connect(self.evaluate_network_state) - self.panel.wifi_button.clicked.connect( - partial( - self.setCurrentIndex, - self.indexOf(self.panel.network_list_page), + main_layout = QtWidgets.QVBoxLayout(self.main_network_page) + main_layout.setObjectName("verticalLayout_14") + + # Header layout + header_layout = QtWidgets.QHBoxLayout() + header_layout.setObjectName("main_network_header_layout") + + header_layout.addItem( + QtWidgets.QSpacerItem( + 60, + 60, + QtWidgets.QSizePolicy.Policy.Minimum, + QtWidgets.QSizePolicy.Policy.Minimum, ) ) - self.panel.hotspot_button.clicked.connect( - partial(self.setCurrentIndex, self.indexOf(self.panel.hotspot_page)) + + self.network_main_title = QtWidgets.QLabel(parent=self.main_network_page) + title_policy = QtWidgets.QSizePolicy( + QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum + ) + self.network_main_title.setSizePolicy(title_policy) + self.network_main_title.setMinimumSize(QtCore.QSize(300, 0)) + self.network_main_title.setMaximumSize(QtCore.QSize(16777215, 60)) + font = QtGui.QFont() + font.setPointSize(20) + self.network_main_title.setFont(font) + self.network_main_title.setStyleSheet("color:white") + self.network_main_title.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) + self.network_main_title.setText("Networks") + self.network_main_title.setObjectName("network_main_title") + header_layout.addWidget(self.network_main_title) + + self.network_backButton = IconButton(parent=self.main_network_page) + self.network_backButton.setMinimumSize(QtCore.QSize(60, 60)) + self.network_backButton.setMaximumSize(QtCore.QSize(60, 60)) + self.network_backButton.setText("") + self.network_backButton.setFlat(True) + self.network_backButton.setProperty( + "icon_pixmap", QtGui.QPixmap(":/ui/media/btn_icons/back.svg") + ) + self.network_backButton.setObjectName("network_backButton") + header_layout.addWidget(self.network_backButton) + + main_layout.addLayout(header_layout) + + # Content layout + content_layout = QtWidgets.QHBoxLayout() + content_layout.setObjectName("main_network_content_layout") + + # Information frame + self.mn_information_layout = BlocksCustomFrame(parent=self.main_network_page) + info_policy = QtWidgets.QSizePolicy( + QtWidgets.QSizePolicy.Policy.Expanding, + QtWidgets.QSizePolicy.Policy.Expanding, + ) + self.mn_information_layout.setSizePolicy(info_policy) + self.mn_information_layout.setFrameShape(QtWidgets.QFrame.Shape.StyledPanel) + self.mn_information_layout.setFrameShadow(QtWidgets.QFrame.Shadow.Raised) + self.mn_information_layout.setObjectName("mn_information_layout") + + info_layout = QtWidgets.QVBoxLayout(self.mn_information_layout) + info_layout.setObjectName("verticalLayout_3") + + # SSID label + self.netlist_ssuid = QtWidgets.QLabel(parent=self.mn_information_layout) + ssid_policy = QtWidgets.QSizePolicy( + QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Minimum + ) + self.netlist_ssuid.setSizePolicy(ssid_policy) + font = QtGui.QFont() + font.setPointSize(17) + self.netlist_ssuid.setFont(font) + self.netlist_ssuid.setStyleSheet("color: rgb(255, 255, 255);") + self.netlist_ssuid.setText("") + self.netlist_ssuid.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) + self.netlist_ssuid.setObjectName("netlist_ssuid") + info_layout.addWidget(self.netlist_ssuid) + + # Separator + self.mn_info_seperator = QtWidgets.QFrame(parent=self.mn_information_layout) + self.mn_info_seperator.setFrameShape(QtWidgets.QFrame.Shape.HLine) + self.mn_info_seperator.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken) + self.mn_info_seperator.setObjectName("mn_info_seperator") + info_layout.addWidget(self.mn_info_seperator) + + # IP label + self.netlist_ip = QtWidgets.QLabel(parent=self.mn_information_layout) + font = QtGui.QFont() + font.setPointSize(15) + self.netlist_ip.setFont(font) + self.netlist_ip.setStyleSheet("color: rgb(255, 255, 255);") + self.netlist_ip.setText("") + self.netlist_ip.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) + self.netlist_ip.setObjectName("netlist_ip") + info_layout.addWidget(self.netlist_ip) + + # Connection info layout + conn_info_layout = QtWidgets.QHBoxLayout() + conn_info_layout.setObjectName("mn_conn_info") + + # Signal strength section + sg_info_layout = QtWidgets.QVBoxLayout() + sg_info_layout.setObjectName("mn_sg_info_layout") + + self.netlist_strength_label = QtWidgets.QLabel( + parent=self.mn_information_layout + ) + self.netlist_strength_label.setPalette(self._create_white_palette()) + font = QtGui.QFont() + font.setPointSize(15) + self.netlist_strength_label.setFont(font) + self.netlist_strength_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) + self.netlist_strength_label.setText("Signal\nStrength") + self.netlist_strength_label.setObjectName("netlist_strength_label") + sg_info_layout.addWidget(self.netlist_strength_label) + + self.line_2 = QtWidgets.QFrame(parent=self.mn_information_layout) + self.line_2.setFrameShape(QtWidgets.QFrame.Shape.HLine) + self.line_2.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken) + self.line_2.setObjectName("line_2") + sg_info_layout.addWidget(self.line_2) + + self.netlist_strength = QtWidgets.QLabel(parent=self.mn_information_layout) + font = QtGui.QFont() + font.setPointSize(11) + self.netlist_strength.setFont(font) + self.netlist_strength.setStyleSheet("color: rgb(255, 255, 255);") + self.netlist_strength.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) + self.netlist_strength.setText("") + self.netlist_strength.setObjectName("netlist_strength") + sg_info_layout.addWidget(self.netlist_strength) + + conn_info_layout.addLayout(sg_info_layout) + + # Security section + sec_info_layout = QtWidgets.QVBoxLayout() + sec_info_layout.setObjectName("mn_sec_info_layout") + + self.netlist_security_label = QtWidgets.QLabel( + parent=self.mn_information_layout + ) + self.netlist_security_label.setPalette(self._create_white_palette()) + font = QtGui.QFont() + font.setPointSize(15) + self.netlist_security_label.setFont(font) + self.netlist_security_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) + self.netlist_security_label.setText("Security\nType") + self.netlist_security_label.setObjectName("netlist_security_label") + sec_info_layout.addWidget(self.netlist_security_label) + + self.line_3 = QtWidgets.QFrame(parent=self.mn_information_layout) + self.line_3.setFrameShape(QtWidgets.QFrame.Shape.HLine) + self.line_3.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken) + self.line_3.setObjectName("line_3") + sec_info_layout.addWidget(self.line_3) + + self.netlist_security = QtWidgets.QLabel(parent=self.mn_information_layout) + font = QtGui.QFont() + font.setPointSize(11) + self.netlist_security.setFont(font) + self.netlist_security.setStyleSheet("color: rgb(255, 255, 255);") + self.netlist_security.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) + self.netlist_security.setText("") + self.netlist_security.setObjectName("netlist_security") + sec_info_layout.addWidget(self.netlist_security) + + conn_info_layout.addLayout(sec_info_layout) + info_layout.addLayout(conn_info_layout) + + # Info box + self.mn_info_box = QtWidgets.QLabel(parent=self.mn_information_layout) + self.mn_info_box.setEnabled(False) + font = QtGui.QFont() + font.setPointSize(17) + self.mn_info_box.setFont(font) + self.mn_info_box.setStyleSheet("color: white") + self.mn_info_box.setTextFormat(QtCore.Qt.TextFormat.PlainText) + self.mn_info_box.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) + self.mn_info_box.setText( + "No network connection.\n\n" + "Try connecting to Wi-Fi \n" + "or turn on the hotspot\n" + "using the buttons on the side." + ) + self.mn_info_box.setObjectName("mn_info_box") + info_layout.addWidget(self.mn_info_box) + + # Loading widget + self.loadingwidget = LoadingOverlayWidget(parent=self.mn_information_layout) + self.loadingwidget.setEnabled(True) + loading_policy = QtWidgets.QSizePolicy( + QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Minimum + ) + self.loadingwidget.setSizePolicy(loading_policy) + self.loadingwidget.setText("") + self.loadingwidget.setObjectName("loadingwidget") + info_layout.addWidget(self.loadingwidget) + + content_layout.addWidget(self.mn_information_layout) + + # Option buttons layout + option_layout = QtWidgets.QVBoxLayout() + option_layout.setObjectName("mn_option_button_layout") + + self.wifi_button = NetworkWidgetbuttons(parent=self.main_network_page) + wifi_policy = QtWidgets.QSizePolicy( + QtWidgets.QSizePolicy.Policy.Expanding, + QtWidgets.QSizePolicy.Policy.Expanding, + ) + self.wifi_button.setSizePolicy(wifi_policy) + self.wifi_button.setMaximumSize(QtCore.QSize(400, 9999)) + font = QtGui.QFont() + font.setPointSize(20) + self.wifi_button.setFont(font) + self.wifi_button.setText("Wi-Fi") + self.wifi_button.setObjectName("wifi_button") + option_layout.addWidget(self.wifi_button) + + self.hotspot_button = NetworkWidgetbuttons(parent=self.main_network_page) + self.hotspot_button.setSizePolicy(wifi_policy) + self.hotspot_button.setMaximumSize(QtCore.QSize(400, 9999)) + font = QtGui.QFont() + font.setPointSize(20) + self.hotspot_button.setFont(font) + self.hotspot_button.setText("Hotspot") + self.hotspot_button.setObjectName("hotspot_button") + option_layout.addWidget(self.hotspot_button) + + content_layout.addLayout(option_layout) + main_layout.addLayout(content_layout) + + self.addWidget(self.main_network_page) + + def _setup_network_list_page(self) -> None: + """Setup the network list page.""" + self.network_list_page = QtWidgets.QWidget() + self.network_list_page.setObjectName("network_list_page") + + main_layout = QtWidgets.QVBoxLayout(self.network_list_page) + main_layout.setObjectName("verticalLayout_9") + + # Header layout + header_layout = QtWidgets.QHBoxLayout() + header_layout.setObjectName("nl_header_layout") + + self.rescan_button = IconButton(parent=self.network_list_page) + self.rescan_button.setMinimumSize(QtCore.QSize(60, 60)) + self.rescan_button.setMaximumSize(QtCore.QSize(60, 60)) + self.rescan_button.setText("Reload") + self.rescan_button.setFlat(True) + self.rescan_button.setProperty( + "icon_pixmap", QtGui.QPixmap(":/ui/media/btn_icons/refresh.svg") + ) + self.rescan_button.setProperty("button_type", "icon") + self.rescan_button.setObjectName("rescan_button") + header_layout.addWidget(self.rescan_button) + + self.network_list_title = QtWidgets.QLabel(parent=self.network_list_page) + self.network_list_title.setMaximumSize(QtCore.QSize(16777215, 60)) + self.network_list_title.setPalette(self._create_white_palette()) + font = QtGui.QFont() + font.setPointSize(20) + self.network_list_title.setFont(font) + self.network_list_title.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) + self.network_list_title.setText("Wi-Fi List") + self.network_list_title.setObjectName("network_list_title") + header_layout.addWidget(self.network_list_title) + + self.nl_back_button = IconButton(parent=self.network_list_page) + self.nl_back_button.setMinimumSize(QtCore.QSize(60, 60)) + self.nl_back_button.setMaximumSize(QtCore.QSize(60, 60)) + self.nl_back_button.setText("Back") + self.nl_back_button.setFlat(True) + self.nl_back_button.setProperty( + "icon_pixmap", QtGui.QPixmap(":/ui/media/btn_icons/back.svg") + ) + self.nl_back_button.setProperty("class", "back_btn") + self.nl_back_button.setProperty("button_type", "icon") + self.nl_back_button.setObjectName("nl_back_button") + header_layout.addWidget(self.nl_back_button) + + main_layout.addLayout(header_layout) + + # List view layout + list_layout = QtWidgets.QHBoxLayout() + list_layout.setObjectName("horizontalLayout_2") + + self.listView = QtWidgets.QListView(self.network_list_page) + list_policy = QtWidgets.QSizePolicy( + QtWidgets.QSizePolicy.Policy.MinimumExpanding, + QtWidgets.QSizePolicy.Policy.MinimumExpanding, + ) + list_policy.setHorizontalStretch(1) + list_policy.setVerticalStretch(1) + self.listView.setSizePolicy(list_policy) + self.listView.setMinimumSize(QtCore.QSize(0, 0)) + self.listView.setStyleSheet("background-color: rgba(255, 255, 255, 0);") + self.listView.setFrameShape(QtWidgets.QFrame.Shape.NoFrame) + self.listView.setFrameShadow(QtWidgets.QFrame.Shadow.Plain) + self.listView.setVerticalScrollBarPolicy( + QtCore.Qt.ScrollBarPolicy.ScrollBarAlwaysOff + ) + self.listView.setHorizontalScrollBarPolicy( + QtCore.Qt.ScrollBarPolicy.ScrollBarAlwaysOff + ) + self.listView.setSelectionBehavior( + QtWidgets.QAbstractItemView.SelectionBehavior.SelectItems + ) + self.listView.setVerticalScrollMode( + QtWidgets.QAbstractItemView.ScrollMode.ScrollPerPixel ) + self.listView.setEditTriggers( + QtWidgets.QAbstractItemView.EditTrigger.NoEditTriggers + ) + self.listView.setUniformItemSizes(True) + self.listView.setSpacing(5) - self.panel.hotspot_button.setPixmap( - QtGui.QPixmap(":/network/media/btn_icons/hotspot.svg") + # Setup touch scrolling + QtWidgets.QScroller.grabGesture( + self.listView, + QtWidgets.QScroller.ScrollerGestureType.TouchGesture, ) - self.panel.wifi_button.setPixmap( - QtGui.QPixmap(":/network/media/btn_icons/wifi_config.svg") + QtWidgets.QScroller.grabGesture( + self.listView, + QtWidgets.QScroller.ScrollerGestureType.LeftMouseButtonGesture, ) - self.panel.nl_back_button.clicked.connect( - partial( - self.setCurrentIndex, - self.indexOf(self.panel.main_network_page), + scroller_instance = QtWidgets.QScroller.scroller(self.listView) + scroller_props = scroller_instance.scrollerProperties() + scroller_props.setScrollMetric( + QtWidgets.QScrollerProperties.ScrollMetric.DragVelocitySmoothingFactor, + 0.05, + ) + scroller_props.setScrollMetric( + QtWidgets.QScrollerProperties.ScrollMetric.DecelerationFactor, + 0.4, + ) + QtWidgets.QScroller.scroller(self.listView).setScrollerProperties( + scroller_props + ) + + self.verticalScrollBar = CustomScrollBar(parent=self.network_list_page) + scrollbar_policy = QtWidgets.QSizePolicy( + QtWidgets.QSizePolicy.Policy.Fixed, QtWidgets.QSizePolicy.Policy.Minimum + ) + self.verticalScrollBar.setSizePolicy(scrollbar_policy) + self.verticalScrollBar.setOrientation(QtCore.Qt.Orientation.Vertical) + self.verticalScrollBar.setObjectName("verticalScrollBar") + self.verticalScrollBar.setAttribute( + QtCore.Qt.WidgetAttribute.WA_TransparentForMouseEvents, True + ) + + self.listView.setVerticalScrollBar(self.verticalScrollBar) + self.listView.setVerticalScrollBarPolicy( + QtCore.Qt.ScrollBarPolicy.ScrollBarAsNeeded + ) + + list_layout.addWidget(self.listView) + list_layout.addWidget(self.verticalScrollBar) + + main_layout.addLayout(list_layout) + + self.scroller = QtWidgets.QScroller.scroller(self.listView) + + self.addWidget(self.network_list_page) + + def _setup_add_network_page(self) -> None: + """Setup the add network page.""" + self.add_network_page = QtWidgets.QWidget() + self.add_network_page.setObjectName("add_network_page") + + main_layout = QtWidgets.QVBoxLayout(self.add_network_page) + main_layout.setObjectName("verticalLayout_10") + + # Header layout + header_layout = QtWidgets.QHBoxLayout() + header_layout.setObjectName("add_np_header_layout") + + header_layout.addItem( + QtWidgets.QSpacerItem( + 40, + 60, + QtWidgets.QSizePolicy.Policy.Minimum, + QtWidgets.QSizePolicy.Policy.Minimum, ) ) - self.panel.network_backButton.clicked.connect(self.hide) - self.panel.rescan_button.clicked.connect( - lambda: self.sdbus_network.rescan_networks() + self.add_network_network_label = QtWidgets.QLabel(parent=self.add_network_page) + label_policy = QtWidgets.QSizePolicy( + QtWidgets.QSizePolicy.Policy.Expanding, + QtWidgets.QSizePolicy.Policy.Preferred, + ) + self.add_network_network_label.setSizePolicy(label_policy) + self.add_network_network_label.setMinimumSize(QtCore.QSize(0, 60)) + self.add_network_network_label.setMaximumSize(QtCore.QSize(16777215, 60)) + font = QtGui.QFont() + font.setPointSize(20) + self.add_network_network_label.setFont(font) + self.add_network_network_label.setStyleSheet("color:white") + self.add_network_network_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) + self.add_network_network_label.setText("TextLabel") + self.add_network_network_label.setObjectName("add_network_network_label") + header_layout.addWidget(self.add_network_network_label) + + self.add_network_page_backButton = IconButton(parent=self.add_network_page) + self.add_network_page_backButton.setMinimumSize(QtCore.QSize(60, 60)) + self.add_network_page_backButton.setMaximumSize(QtCore.QSize(60, 60)) + self.add_network_page_backButton.setText("Back") + self.add_network_page_backButton.setFlat(True) + self.add_network_page_backButton.setProperty( + "icon_pixmap", QtGui.QPixmap(":/ui/media/btn_icons/back.svg") + ) + self.add_network_page_backButton.setProperty("class", "back_btn") + self.add_network_page_backButton.setProperty("button_type", "icon") + self.add_network_page_backButton.setObjectName("add_network_page_backButton") + header_layout.addWidget(self.add_network_page_backButton) + + main_layout.addLayout(header_layout) + + # Content layout + content_layout = QtWidgets.QVBoxLayout() + content_layout.setSizeConstraint( + QtWidgets.QLayout.SizeConstraint.SetMinimumSize + ) + content_layout.setObjectName("add_np_content_layout") + + content_layout.addItem( + QtWidgets.QSpacerItem( + 20, + 50, + QtWidgets.QSizePolicy.Policy.Minimum, + QtWidgets.QSizePolicy.Policy.Minimum, + ) ) - self.request_network_scan.connect(self.rescan_networks) - self.panel.add_network_validation_button.clicked.connect(self.add_network) - self.panel.add_network_page_backButton.clicked.connect( - partial( - self.setCurrentIndex, - self.indexOf(self.panel.network_list_page), + # Password frame + self.frame_2 = BlocksCustomFrame(parent=self.add_network_page) + frame_policy = QtWidgets.QSizePolicy( + QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Minimum + ) + frame_policy.setVerticalStretch(2) + self.frame_2.setSizePolicy(frame_policy) + self.frame_2.setMinimumSize(QtCore.QSize(0, 80)) + self.frame_2.setMaximumSize(QtCore.QSize(16777215, 90)) + self.frame_2.setFrameShape(QtWidgets.QFrame.Shape.StyledPanel) + self.frame_2.setFrameShadow(QtWidgets.QFrame.Shadow.Raised) + self.frame_2.setObjectName("frame_2") + + frame_layout_widget = QtWidgets.QWidget(parent=self.frame_2) + frame_layout_widget.setGeometry(QtCore.QRect(10, 10, 761, 82)) + frame_layout_widget.setObjectName("layoutWidget_2") + + password_layout = QtWidgets.QHBoxLayout(frame_layout_widget) + password_layout.setSizeConstraint( + QtWidgets.QLayout.SizeConstraint.SetMaximumSize + ) + password_layout.setContentsMargins(0, 0, 0, 0) + password_layout.setObjectName("horizontalLayout_5") + + self.add_network_password_label = QtWidgets.QLabel(parent=frame_layout_widget) + self.add_network_password_label.setPalette(self._create_white_palette()) + font = QtGui.QFont() + font.setPointSize(15) + self.add_network_password_label.setFont(font) + self.add_network_password_label.setAlignment( + QtCore.Qt.AlignmentFlag.AlignCenter + ) + self.add_network_password_label.setText("Password") + self.add_network_password_label.setObjectName("add_network_password_label") + password_layout.addWidget(self.add_network_password_label) + + self.add_network_password_field = BlocksCustomLinEdit( + parent=frame_layout_widget + ) + self.add_network_password_field.setHidden(True) + self.add_network_password_field.setMinimumSize(QtCore.QSize(500, 60)) + font = QtGui.QFont() + font.setPointSize(12) + self.add_network_password_field.setFont(font) + self.add_network_password_field.setObjectName("add_network_password_field") + password_layout.addWidget(self.add_network_password_field) + + self.add_network_password_view = IconButton(parent=frame_layout_widget) + self.add_network_password_view.setMinimumSize(QtCore.QSize(60, 60)) + self.add_network_password_view.setMaximumSize(QtCore.QSize(60, 60)) + self.add_network_password_view.setText("View") + self.add_network_password_view.setFlat(True) + self.add_network_password_view.setProperty( + "icon_pixmap", QtGui.QPixmap(":/ui/media/btn_icons/unsee.svg") + ) + self.add_network_password_view.setProperty("class", "back_btn") + self.add_network_password_view.setProperty("button_type", "icon") + self.add_network_password_view.setObjectName("add_network_password_view") + password_layout.addWidget(self.add_network_password_view) + + content_layout.addWidget(self.frame_2) + + content_layout.addItem( + QtWidgets.QSpacerItem( + 20, + 150, + QtWidgets.QSizePolicy.Policy.Minimum, + QtWidgets.QSizePolicy.Policy.Minimum, ) ) - self.panel.add_network_password_view.pressed.connect( - partial( - self.panel.add_network_password_field.setEchoMode, - QtWidgets.QLineEdit.EchoMode.Normal, + + # Validation button layout + button_layout = QtWidgets.QHBoxLayout() + button_layout.setSizeConstraint(QtWidgets.QLayout.SizeConstraint.SetMinimumSize) + button_layout.setObjectName("horizontalLayout_6") + + self.add_network_validation_button = BlocksCustomButton( + parent=self.add_network_page + ) + self.add_network_validation_button.setEnabled(True) + btn_policy = QtWidgets.QSizePolicy( + QtWidgets.QSizePolicy.Policy.MinimumExpanding, + QtWidgets.QSizePolicy.Policy.MinimumExpanding, + ) + btn_policy.setHorizontalStretch(1) + btn_policy.setVerticalStretch(1) + self.add_network_validation_button.setSizePolicy(btn_policy) + self.add_network_validation_button.setMinimumSize(QtCore.QSize(250, 80)) + self.add_network_validation_button.setMaximumSize(QtCore.QSize(250, 80)) + font = QtGui.QFont() + font.setFamily("Momcake") + font.setPointSize(15) + self.add_network_validation_button.setFont(font) + self.add_network_validation_button.setIconSize(QtCore.QSize(16, 16)) + self.add_network_validation_button.setCheckable(False) + self.add_network_validation_button.setChecked(False) + self.add_network_validation_button.setFlat(True) + self.add_network_validation_button.setProperty( + "icon_pixmap", QtGui.QPixmap(":/dialog/media/btn_icons/yes.svg") + ) + self.add_network_validation_button.setText("Activate") + self.add_network_validation_button.setObjectName( + "add_network_validation_button" + ) + button_layout.addWidget( + self.add_network_validation_button, + 0, + QtCore.Qt.AlignmentFlag.AlignHCenter | QtCore.Qt.AlignmentFlag.AlignTop, + ) + + content_layout.addLayout(button_layout) + main_layout.addLayout(content_layout) + + self.addWidget(self.add_network_page) + + def _setup_hidden_network_page(self) -> None: + """Setup the hidden network page for connecting to networks with hidden SSID.""" + self.hidden_network_page = QtWidgets.QWidget() + self.hidden_network_page.setObjectName("hidden_network_page") + + main_layout = QtWidgets.QVBoxLayout(self.hidden_network_page) + main_layout.setObjectName("hidden_network_layout") + + # Header layout + header_layout = QtWidgets.QHBoxLayout() + header_layout.addItem( + QtWidgets.QSpacerItem( + 40, + 60, + QtWidgets.QSizePolicy.Policy.Minimum, + QtWidgets.QSizePolicy.Policy.Minimum, ) ) - self.panel.add_network_password_view.released.connect( - partial( - self.panel.add_network_password_field.setEchoMode, - QtWidgets.QLineEdit.EchoMode.Password, + + self.hidden_network_title = QtWidgets.QLabel(parent=self.hidden_network_page) + self.hidden_network_title.setPalette(self._create_white_palette()) + font = QtGui.QFont() + font.setPointSize(20) + self.hidden_network_title.setFont(font) + self.hidden_network_title.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) + self.hidden_network_title.setText("Hidden Network") + header_layout.addWidget(self.hidden_network_title) + + self.hidden_network_back_button = IconButton(parent=self.hidden_network_page) + self.hidden_network_back_button.setMinimumSize(QtCore.QSize(60, 60)) + self.hidden_network_back_button.setMaximumSize(QtCore.QSize(60, 60)) + self.hidden_network_back_button.setFlat(True) + self.hidden_network_back_button.setProperty( + "icon_pixmap", QtGui.QPixmap(":/ui/media/btn_icons/back.svg") + ) + self.hidden_network_back_button.setProperty("button_type", "icon") + header_layout.addWidget(self.hidden_network_back_button) + + main_layout.addLayout(header_layout) + + # Content + content_layout = QtWidgets.QVBoxLayout() + content_layout.addItem( + QtWidgets.QSpacerItem( + 20, + 30, + QtWidgets.QSizePolicy.Policy.Minimum, + QtWidgets.QSizePolicy.Policy.Minimum, ) ) - self.panel.saved_connection_back_button.clicked.connect( - partial( - self.setCurrentIndex, - self.indexOf(self.panel.network_list_page), + # SSID Frame + ssid_frame = BlocksCustomFrame(parent=self.hidden_network_page) + ssid_frame.setMinimumSize(QtCore.QSize(0, 80)) + ssid_frame.setMaximumSize(QtCore.QSize(16777215, 90)) + ssid_frame_layout = QtWidgets.QHBoxLayout(ssid_frame) + + ssid_label = QtWidgets.QLabel("Network\nName", parent=ssid_frame) + ssid_label.setPalette(self._create_white_palette()) + font = QtGui.QFont() + font.setPointSize(15) + ssid_label.setFont(font) + ssid_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) + ssid_frame_layout.addWidget(ssid_label) + + self.hidden_network_ssid_field = BlocksCustomLinEdit(parent=ssid_frame) + self.hidden_network_ssid_field.setMinimumSize(QtCore.QSize(500, 60)) + font = QtGui.QFont() + font.setPointSize(12) + self.hidden_network_ssid_field.setFont(font) + self.hidden_network_ssid_field.setPlaceholderText("Enter network name") + ssid_frame_layout.addWidget(self.hidden_network_ssid_field) + + content_layout.addWidget(ssid_frame) + + content_layout.addItem( + QtWidgets.QSpacerItem( + 20, + 20, + QtWidgets.QSizePolicy.Policy.Minimum, + QtWidgets.QSizePolicy.Policy.Minimum, ) ) - self.delete_network_signal.connect(self.delete_network) - self.panel.snd_back.clicked.connect( - lambda: self.update_network( - ssid=self.panel.saved_connection_network_name.text(), - password=self.panel.saved_connection_change_password_field.text(), - new_ssid=None, + + # Password Frame + password_frame = BlocksCustomFrame(parent=self.hidden_network_page) + password_frame.setMinimumSize(QtCore.QSize(0, 80)) + password_frame.setMaximumSize(QtCore.QSize(16777215, 90)) + password_frame_layout = QtWidgets.QHBoxLayout(password_frame) + + password_label = QtWidgets.QLabel("Password", parent=password_frame) + password_label.setPalette(self._create_white_palette()) + font = QtGui.QFont() + font.setPointSize(15) + password_label.setFont(font) + password_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) + password_frame_layout.addWidget(password_label) + + self.hidden_network_password_field = BlocksCustomLinEdit(parent=password_frame) + self.hidden_network_password_field.setHidden(True) + self.hidden_network_password_field.setMinimumSize(QtCore.QSize(500, 60)) + font = QtGui.QFont() + font.setPointSize(12) + self.hidden_network_password_field.setFont(font) + self.hidden_network_password_field.setPlaceholderText( + "Enter password (leave empty for open networks)" + ) + self.hidden_network_password_field.setEchoMode( + QtWidgets.QLineEdit.EchoMode.Password + ) + password_frame_layout.addWidget(self.hidden_network_password_field) + + self.hidden_network_password_view = IconButton(parent=password_frame) + self.hidden_network_password_view.setMinimumSize(QtCore.QSize(60, 60)) + self.hidden_network_password_view.setMaximumSize(QtCore.QSize(60, 60)) + self.hidden_network_password_view.setFlat(True) + self.hidden_network_password_view.setProperty( + "icon_pixmap", QtGui.QPixmap(":/ui/media/btn_icons/unsee.svg") + ) + self.hidden_network_password_view.setProperty("button_type", "icon") + password_frame_layout.addWidget(self.hidden_network_password_view) + + content_layout.addWidget(password_frame) + + content_layout.addItem( + QtWidgets.QSpacerItem( + 20, + 50, + QtWidgets.QSizePolicy.Policy.Minimum, + QtWidgets.QSizePolicy.Policy.Minimum, ) ) - self.panel.saved_connection_change_password_view.pressed.connect( - partial( - self.panel.saved_connection_change_password_field.setEchoMode, - QtWidgets.QLineEdit.EchoMode.Normal, + + # Connect button + self.hidden_network_connect_button = BlocksCustomButton( + parent=self.hidden_network_page + ) + self.hidden_network_connect_button.setMinimumSize(QtCore.QSize(250, 80)) + self.hidden_network_connect_button.setMaximumSize(QtCore.QSize(250, 80)) + font = QtGui.QFont() + font.setPointSize(15) + self.hidden_network_connect_button.setFont(font) + self.hidden_network_connect_button.setFlat(True) + self.hidden_network_connect_button.setProperty( + "icon_pixmap", QtGui.QPixmap(":/dialog/media/btn_icons/yes.svg") + ) + self.hidden_network_connect_button.setText("Connect") + content_layout.addWidget( + self.hidden_network_connect_button, 0, QtCore.Qt.AlignmentFlag.AlignHCenter + ) + + main_layout.addLayout(content_layout) + self.addWidget(self.hidden_network_page) + + # Connect signals + self.hidden_network_back_button.clicked.connect( + partial(self.setCurrentIndex, self.indexOf(self.network_list_page)) + ) + self.hidden_network_connect_button.clicked.connect( + self._on_hidden_network_connect + ) + self.hidden_network_ssid_field.clicked.connect( + lambda: self._on_show_keyboard( + self.hidden_network_page, self.hidden_network_ssid_field ) ) - self.panel.saved_connection_change_password_view.released.connect( - partial( - self.panel.saved_connection_change_password_field.setEchoMode, - QtWidgets.QLineEdit.EchoMode.Password, + self.hidden_network_password_field.clicked.connect( + lambda: self._on_show_keyboard( + self.hidden_network_page, self.hidden_network_password_field ) ) - self.panel.hotspot_back_button.clicked.connect( - lambda: self.setCurrentIndex(self.indexOf(self.panel.main_network_page)) + self._setup_password_visibility_toggle( + self.hidden_network_password_view, self.hidden_network_password_field ) - self.panel.hotspot_password_input_field.setPlaceholderText( - "Defaults to: 123456789" + def _on_hidden_network_connect(self) -> None: + """Handle connection to hidden network.""" + ssid = self.hidden_network_ssid_field.text().strip() + password = self.hidden_network_password_field.text() + + if not ssid: + self._show_error_popup("Please enter a network name.") + return + + self._current_network_is_hidden = True + self._current_network_is_open = not password + + result = self._sdbus_network.add_wifi_network(ssid=ssid, psk=password) + + if result is None: + self._handle_failed_network_add("Failed to add network") + return + + error_msg = result.get("error", "") if isinstance(result, dict) else "" + + if not error_msg: + self.hidden_network_ssid_field.clear() + self.hidden_network_password_field.clear() + self._handle_successful_network_add(ssid) + else: + self._handle_failed_network_add(error_msg) + + def _setup_saved_connection_page(self) -> None: + """Setup the saved connection page.""" + self.saved_connection_page = QtWidgets.QWidget() + self.saved_connection_page.setObjectName("saved_connection_page") + + main_layout = QtWidgets.QVBoxLayout(self.saved_connection_page) + main_layout.setObjectName("verticalLayout_11") + + # Header layout + header_layout = QtWidgets.QHBoxLayout() + header_layout.setObjectName("horizontalLayout_7") + + header_layout.addItem( + QtWidgets.QSpacerItem( + 60, + 20, + QtWidgets.QSizePolicy.Policy.Minimum, + QtWidgets.QSizePolicy.Policy.Minimum, + ) + ) + + self.saved_connection_network_name = QtWidgets.QLabel( + parent=self.saved_connection_page ) - self.panel.hotspot_change_confirm.clicked.connect( - lambda: self.setCurrentIndex(self.indexOf(self.panel.main_network_page)) + name_policy = QtWidgets.QSizePolicy( + QtWidgets.QSizePolicy.Policy.Expanding, + QtWidgets.QSizePolicy.Policy.Expanding, ) + self.saved_connection_network_name.setSizePolicy(name_policy) + self.saved_connection_network_name.setMaximumSize(QtCore.QSize(16777215, 60)) + font = QtGui.QFont() + font.setPointSize(20) + self.saved_connection_network_name.setFont(font) + self.saved_connection_network_name.setStyleSheet("color: rgb(255, 255, 255);") + self.saved_connection_network_name.setText("") + self.saved_connection_network_name.setAlignment( + QtCore.Qt.AlignmentFlag.AlignCenter + ) + self.saved_connection_network_name.setObjectName( + "saved_connection_network_name" + ) + header_layout.addWidget(self.saved_connection_network_name) - self.panel.hotspot_password_input_field.setHidden(True) - self.panel.hotspot_password_view_button.pressed.connect( - partial(self.panel.hotspot_password_input_field.setHidden, False) + self.saved_connection_back_button = IconButton( + parent=self.saved_connection_page + ) + self.saved_connection_back_button.setMinimumSize(QtCore.QSize(60, 60)) + self.saved_connection_back_button.setMaximumSize(QtCore.QSize(60, 60)) + self.saved_connection_back_button.setText("Back") + self.saved_connection_back_button.setFlat(True) + self.saved_connection_back_button.setProperty( + "icon_pixmap", QtGui.QPixmap(":/ui/media/btn_icons/back.svg") ) - self.panel.hotspot_password_view_button.released.connect( - partial(self.panel.hotspot_password_input_field.setHidden, True) + self.saved_connection_back_button.setProperty("class", "back_btn") + self.saved_connection_back_button.setProperty("button_type", "icon") + self.saved_connection_back_button.setObjectName("saved_connection_back_button") + header_layout.addWidget( + self.saved_connection_back_button, 0, QtCore.Qt.AlignmentFlag.AlignRight ) - self.panel.hotspot_name_input_field.setText( - str(self.sdbus_network.get_hotspot_ssid()) + + main_layout.addLayout(header_layout) + + # Content layout + content_layout = QtWidgets.QVBoxLayout() + content_layout.setObjectName("verticalLayout_5") + + content_layout.addItem( + QtWidgets.QSpacerItem( + 20, + 20, + QtWidgets.QSizePolicy.Policy.Minimum, + QtWidgets.QSizePolicy.Policy.Minimum, + ) ) - self.panel.hotspot_password_input_field.setText( - str(self.sdbus_network.hotspot_password) + + # Main content horizontal layout + main_content_layout = QtWidgets.QHBoxLayout() + main_content_layout.setObjectName("horizontalLayout_9") + + # Info frame layout + info_layout = QtWidgets.QVBoxLayout() + info_layout.setObjectName("verticalLayout_2") + + self.frame = BlocksCustomFrame(parent=self.saved_connection_page) + frame_policy = QtWidgets.QSizePolicy( + QtWidgets.QSizePolicy.Policy.Expanding, + QtWidgets.QSizePolicy.Policy.Expanding, ) - self.panel.wifi_button.toggle_button.stateChange.connect(self.on_toggle_state) - self.panel.hotspot_button.toggle_button.stateChange.connect( - self.on_toggle_state + self.frame.setSizePolicy(frame_policy) + self.frame.setMaximumSize(QtCore.QSize(400, 16777215)) + self.frame.setFrameShape(QtWidgets.QFrame.Shape.StyledPanel) + self.frame.setFrameShadow(QtWidgets.QFrame.Shadow.Raised) + self.frame.setObjectName("frame") + + frame_inner_layout = QtWidgets.QVBoxLayout(self.frame) + frame_inner_layout.setObjectName("verticalLayout_6") + + # Signal strength row + signal_layout = QtWidgets.QHBoxLayout() + signal_layout.setObjectName("horizontalLayout") + + self.netlist_strength_label_2 = QtWidgets.QLabel(parent=self.frame) + self.netlist_strength_label_2.setPalette(self._create_white_palette()) + font = QtGui.QFont() + font.setPointSize(15) + self.netlist_strength_label_2.setFont(font) + self.netlist_strength_label_2.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) + self.netlist_strength_label_2.setText("Signal\nStrength") + self.netlist_strength_label_2.setObjectName("netlist_strength_label_2") + signal_layout.addWidget(self.netlist_strength_label_2) + + self.saved_connection_signal_strength_info_frame = QtWidgets.QLabel( + parent=self.frame + ) + self.saved_connection_signal_strength_info_frame.setMinimumSize( + QtCore.QSize(250, 0) + ) + font = QtGui.QFont() + font.setPointSize(11) + self.saved_connection_signal_strength_info_frame.setFont(font) + self.saved_connection_signal_strength_info_frame.setStyleSheet( + "color: rgb(255, 255, 255);" + ) + self.saved_connection_signal_strength_info_frame.setAlignment( + QtCore.Qt.AlignmentFlag.AlignCenter + ) + self.saved_connection_signal_strength_info_frame.setText("TextLabel") + self.saved_connection_signal_strength_info_frame.setObjectName( + "saved_connection_signal_strength_info_frame" + ) + signal_layout.addWidget(self.saved_connection_signal_strength_info_frame) + + frame_inner_layout.addLayout(signal_layout) + + self.line_4 = QtWidgets.QFrame(parent=self.frame) + self.line_4.setFrameShape(QtWidgets.QFrame.Shape.HLine) + self.line_4.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken) + self.line_4.setObjectName("line_4") + frame_inner_layout.addWidget(self.line_4) + + # Security type row + security_layout = QtWidgets.QHBoxLayout() + security_layout.setObjectName("horizontalLayout_2") + + self.netlist_security_label_2 = QtWidgets.QLabel(parent=self.frame) + self.netlist_security_label_2.setPalette(self._create_white_palette()) + font = QtGui.QFont() + font.setPointSize(15) + self.netlist_security_label_2.setFont(font) + self.netlist_security_label_2.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) + self.netlist_security_label_2.setText("Security\nType") + self.netlist_security_label_2.setObjectName("netlist_security_label_2") + security_layout.addWidget(self.netlist_security_label_2) + + self.saved_connection_security_type_info_label = QtWidgets.QLabel( + parent=self.frame + ) + self.saved_connection_security_type_info_label.setMinimumSize( + QtCore.QSize(250, 0) + ) + font = QtGui.QFont() + font.setPointSize(11) + self.saved_connection_security_type_info_label.setFont(font) + self.saved_connection_security_type_info_label.setStyleSheet( + "color: rgb(255, 255, 255);" + ) + self.saved_connection_security_type_info_label.setAlignment( + QtCore.Qt.AlignmentFlag.AlignCenter + ) + self.saved_connection_security_type_info_label.setText("TextLabel") + self.saved_connection_security_type_info_label.setObjectName( + "saved_connection_security_type_info_label" + ) + security_layout.addWidget(self.saved_connection_security_type_info_label) + + frame_inner_layout.addLayout(security_layout) + + self.line_5 = QtWidgets.QFrame(parent=self.frame) + self.line_5.setFrameShape(QtWidgets.QFrame.Shape.HLine) + self.line_5.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken) + self.line_5.setObjectName("line_5") + frame_inner_layout.addWidget(self.line_5) + + # Status row + status_layout = QtWidgets.QHBoxLayout() + status_layout.setObjectName("horizontalLayout_8") + + self.netlist_security_label_4 = QtWidgets.QLabel(parent=self.frame) + self.netlist_security_label_4.setPalette(self._create_white_palette()) + font = QtGui.QFont() + font.setPointSize(15) + self.netlist_security_label_4.setFont(font) + self.netlist_security_label_4.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) + self.netlist_security_label_4.setText("Status") + self.netlist_security_label_4.setObjectName("netlist_security_label_4") + status_layout.addWidget(self.netlist_security_label_4) + + self.sn_info = QtWidgets.QLabel(parent=self.frame) + self.sn_info.setMinimumSize(QtCore.QSize(250, 0)) + font = QtGui.QFont() + font.setPointSize(11) + self.sn_info.setFont(font) + self.sn_info.setStyleSheet("color: rgb(255, 255, 255);") + self.sn_info.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) + self.sn_info.setText("TextLabel") + self.sn_info.setObjectName("sn_info") + status_layout.addWidget(self.sn_info) + + frame_inner_layout.addLayout(status_layout) + info_layout.addWidget(self.frame) + main_content_layout.addLayout(info_layout) + + # Action buttons frame + self.frame_8 = BlocksCustomFrame(parent=self.saved_connection_page) + self.frame_8.setFrameShape(QtWidgets.QFrame.Shape.StyledPanel) + self.frame_8.setFrameShadow(QtWidgets.QFrame.Shadow.Raised) + self.frame_8.setObjectName("frame_8") + + buttons_layout = QtWidgets.QVBoxLayout(self.frame_8) + buttons_layout.setObjectName("verticalLayout_4") + + self.network_activate_btn = BlocksCustomButton(parent=self.frame_8) + self.network_activate_btn.setMinimumSize(QtCore.QSize(250, 80)) + self.network_activate_btn.setMaximumSize(QtCore.QSize(250, 80)) + font = QtGui.QFont() + font.setPointSize(15) + self.network_activate_btn.setFont(font) + self.network_activate_btn.setFlat(True) + self.network_activate_btn.setText("Connect") + self.network_activate_btn.setObjectName("network_activate_btn") + buttons_layout.addWidget( + self.network_activate_btn, + 0, + QtCore.Qt.AlignmentFlag.AlignHCenter | QtCore.Qt.AlignmentFlag.AlignVCenter, + ) + + self.network_details_btn = BlocksCustomButton(parent=self.frame_8) + self.network_details_btn.setMinimumSize(QtCore.QSize(250, 80)) + self.network_details_btn.setMaximumSize(QtCore.QSize(250, 80)) + font = QtGui.QFont() + font.setPointSize(15) + self.network_details_btn.setFont(font) + self.network_details_btn.setFlat(True) + self.network_details_btn.setText("Details") + self.network_details_btn.setObjectName("network_details_btn") + buttons_layout.addWidget( + self.network_details_btn, 0, QtCore.Qt.AlignmentFlag.AlignHCenter + ) + + self.network_delete_btn = BlocksCustomButton(parent=self.frame_8) + self.network_delete_btn.setMinimumSize(QtCore.QSize(250, 80)) + self.network_delete_btn.setMaximumSize(QtCore.QSize(250, 80)) + font = QtGui.QFont() + font.setPointSize(15) + self.network_delete_btn.setFont(font) + self.network_delete_btn.setFlat(True) + self.network_delete_btn.setText("Forget") + self.network_delete_btn.setObjectName("network_delete_btn") + buttons_layout.addWidget( + self.network_delete_btn, + 0, + QtCore.Qt.AlignmentFlag.AlignHCenter | QtCore.Qt.AlignmentFlag.AlignVCenter, + ) + + main_content_layout.addWidget(self.frame_8) + content_layout.addLayout(main_content_layout) + main_layout.addLayout(content_layout) + + self.addWidget(self.saved_connection_page) + + def _setup_saved_details_page(self) -> None: + """Setup the saved network details page.""" + self.saved_details_page = QtWidgets.QWidget() + self.saved_details_page.setObjectName("saved_details_page") + + main_layout = QtWidgets.QVBoxLayout(self.saved_details_page) + main_layout.setObjectName("verticalLayout_19") + + # Header layout + header_layout = QtWidgets.QHBoxLayout() + header_layout.setObjectName("horizontalLayout_14") + + header_layout.addItem( + QtWidgets.QSpacerItem( + 60, + 60, + QtWidgets.QSizePolicy.Policy.Minimum, + QtWidgets.QSizePolicy.Policy.Minimum, + ) ) - self.panel.saved_connection_change_password_view.pressed.connect( - lambda: self.panel.saved_connection_change_password_view.setPixmap( - QtGui.QPixmap(":/ui/media/btn_icons/see.svg") + + self.snd_name = QtWidgets.QLabel(parent=self.saved_details_page) + name_policy = QtWidgets.QSizePolicy( + QtWidgets.QSizePolicy.Policy.Expanding, + QtWidgets.QSizePolicy.Policy.Expanding, + ) + self.snd_name.setSizePolicy(name_policy) + self.snd_name.setMaximumSize(QtCore.QSize(16777215, 60)) + self.snd_name.setPalette(self._create_white_palette()) + font = QtGui.QFont() + font.setPointSize(20) + self.snd_name.setFont(font) + self.snd_name.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) + self.snd_name.setText("SSID") + self.snd_name.setObjectName("snd_name") + header_layout.addWidget(self.snd_name) + + self.snd_back = IconButton(parent=self.saved_details_page) + self.snd_back.setMinimumSize(QtCore.QSize(60, 60)) + self.snd_back.setMaximumSize(QtCore.QSize(60, 60)) + self.snd_back.setText("Back") + self.snd_back.setFlat(True) + self.snd_back.setProperty( + "icon_pixmap", QtGui.QPixmap(":/ui/media/btn_icons/back.svg") + ) + self.snd_back.setProperty("class", "back_btn") + self.snd_back.setProperty("button_type", "icon") + self.snd_back.setObjectName("snd_back") + header_layout.addWidget(self.snd_back) + + main_layout.addLayout(header_layout) + + # Content layout + content_layout = QtWidgets.QVBoxLayout() + content_layout.setObjectName("verticalLayout_8") + + content_layout.addItem( + QtWidgets.QSpacerItem( + 20, + 20, + QtWidgets.QSizePolicy.Policy.Minimum, + QtWidgets.QSizePolicy.Policy.Minimum, ) ) - self.panel.saved_connection_change_password_view.released.connect( - lambda: self.panel.saved_connection_change_password_view.setPixmap( - QtGui.QPixmap(":/ui/media/btn_icons/unsee.svg") + + # Password change frame + self.frame_9 = BlocksCustomFrame(parent=self.saved_details_page) + frame_policy = QtWidgets.QSizePolicy( + QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Minimum + ) + self.frame_9.setSizePolicy(frame_policy) + self.frame_9.setMinimumSize(QtCore.QSize(0, 70)) + self.frame_9.setMaximumSize(QtCore.QSize(16777215, 70)) + self.frame_9.setFrameShape(QtWidgets.QFrame.Shape.StyledPanel) + self.frame_9.setFrameShadow(QtWidgets.QFrame.Shadow.Raised) + self.frame_9.setObjectName("frame_9") + + frame_layout_widget = QtWidgets.QWidget(parent=self.frame_9) + frame_layout_widget.setGeometry(QtCore.QRect(0, 0, 776, 62)) + frame_layout_widget.setObjectName("layoutWidget_8") + + password_layout = QtWidgets.QHBoxLayout(frame_layout_widget) + password_layout.setContentsMargins(0, 0, 0, 0) + password_layout.setObjectName("horizontalLayout_10") + + self.saved_connection_change_password_label_3 = QtWidgets.QLabel( + parent=frame_layout_widget + ) + self.saved_connection_change_password_label_3.setPalette( + self._create_white_palette() + ) + font = QtGui.QFont() + font.setPointSize(15) + self.saved_connection_change_password_label_3.setFont(font) + self.saved_connection_change_password_label_3.setAlignment( + QtCore.Qt.AlignmentFlag.AlignCenter + ) + self.saved_connection_change_password_label_3.setText("Change\nPassword") + self.saved_connection_change_password_label_3.setObjectName( + "saved_connection_change_password_label_3" + ) + password_layout.addWidget( + self.saved_connection_change_password_label_3, + 0, + QtCore.Qt.AlignmentFlag.AlignHCenter | QtCore.Qt.AlignmentFlag.AlignVCenter, + ) + + self.saved_connection_change_password_field = BlocksCustomLinEdit( + parent=frame_layout_widget + ) + self.saved_connection_change_password_field.setHidden(True) + self.saved_connection_change_password_field.setMinimumSize( + QtCore.QSize(500, 60) + ) + self.saved_connection_change_password_field.setMaximumSize( + QtCore.QSize(500, 16777215) + ) + font = QtGui.QFont() + font.setPointSize(12) + self.saved_connection_change_password_field.setFont(font) + self.saved_connection_change_password_field.setObjectName( + "saved_connection_change_password_field" + ) + password_layout.addWidget( + self.saved_connection_change_password_field, + 0, + QtCore.Qt.AlignmentFlag.AlignHCenter, + ) + + self.saved_connection_change_password_view = IconButton( + parent=frame_layout_widget + ) + self.saved_connection_change_password_view.setMinimumSize(QtCore.QSize(60, 60)) + self.saved_connection_change_password_view.setMaximumSize(QtCore.QSize(60, 60)) + self.saved_connection_change_password_view.setText("View") + self.saved_connection_change_password_view.setFlat(True) + self.saved_connection_change_password_view.setProperty( + "icon_pixmap", QtGui.QPixmap(":/ui/media/btn_icons/unsee.svg") + ) + self.saved_connection_change_password_view.setProperty("class", "back_btn") + self.saved_connection_change_password_view.setProperty("button_type", "icon") + self.saved_connection_change_password_view.setObjectName( + "saved_connection_change_password_view" + ) + password_layout.addWidget( + self.saved_connection_change_password_view, + 0, + QtCore.Qt.AlignmentFlag.AlignHCenter | QtCore.Qt.AlignmentFlag.AlignVCenter, + ) + + content_layout.addWidget(self.frame_9) + + # Priority buttons layout + priority_outer_layout = QtWidgets.QHBoxLayout() + priority_outer_layout.setObjectName("horizontalLayout_13") + + priority_inner_layout = QtWidgets.QVBoxLayout() + priority_inner_layout.setObjectName("verticalLayout_13") + + self.frame_12 = BlocksCustomFrame(parent=self.saved_details_page) + frame_policy = QtWidgets.QSizePolicy( + QtWidgets.QSizePolicy.Policy.Expanding, + QtWidgets.QSizePolicy.Policy.Expanding, + ) + self.frame_12.setSizePolicy(frame_policy) + self.frame_12.setMinimumSize(QtCore.QSize(400, 160)) + self.frame_12.setMaximumSize(QtCore.QSize(400, 99999)) + self.frame_12.setFrameShape(QtWidgets.QFrame.Shape.StyledPanel) + self.frame_12.setFrameShadow(QtWidgets.QFrame.Shadow.Raised) + self.frame_12.setProperty("text", "Network priority") + self.frame_12.setObjectName("frame_12") + + frame_inner_layout = QtWidgets.QVBoxLayout(self.frame_12) + frame_inner_layout.setObjectName("verticalLayout_17") + + frame_inner_layout.addItem( + QtWidgets.QSpacerItem( + 10, + 10, + QtWidgets.QSizePolicy.Policy.Minimum, + QtWidgets.QSizePolicy.Policy.Minimum, ) ) - self.panel.add_network_password_view.released.connect( - lambda: self.panel.add_network_password_view.setPixmap( - QtGui.QPixmap(":/ui/media/btn_icons/unsee.svg") + + # Priority buttons + buttons_layout = QtWidgets.QHBoxLayout() + buttons_layout.setObjectName("horizontalLayout_4") + + self.priority_btn_group = QtWidgets.QButtonGroup(self) + self.priority_btn_group.setObjectName("priority_btn_group") + + self.low_priority_btn = BlocksCustomCheckButton(parent=self.frame_12) + self.low_priority_btn.setMinimumSize(QtCore.QSize(100, 100)) + self.low_priority_btn.setMaximumSize(QtCore.QSize(100, 100)) + self.low_priority_btn.setCheckable(True) + self.low_priority_btn.setAutoExclusive(True) + self.low_priority_btn.setFlat(True) + self.low_priority_btn.setProperty( + "icon_pixmap", QtGui.QPixmap(":/ui/media/btn_icons/indf_svg.svg") + ) + self.low_priority_btn.setText("Low") + self.low_priority_btn.setProperty("class", "back_btn") + self.low_priority_btn.setProperty("button_type", "icon") + self.low_priority_btn.setObjectName("low_priority_btn") + self.priority_btn_group.addButton(self.low_priority_btn) + buttons_layout.addWidget(self.low_priority_btn) + + self.med_priority_btn = BlocksCustomCheckButton(parent=self.frame_12) + self.med_priority_btn.setMinimumSize(QtCore.QSize(100, 100)) + self.med_priority_btn.setMaximumSize(QtCore.QSize(100, 100)) + self.med_priority_btn.setCheckable(True) + self.med_priority_btn.setChecked(False) # Don't set default checked + self.med_priority_btn.setAutoExclusive(True) + self.med_priority_btn.setFlat(True) + self.med_priority_btn.setProperty( + "icon_pixmap", QtGui.QPixmap(":/ui/media/btn_icons/indf_svg.svg") + ) + self.med_priority_btn.setText("Medium") + self.med_priority_btn.setProperty("class", "back_btn") + self.med_priority_btn.setProperty("button_type", "icon") + self.med_priority_btn.setObjectName("med_priority_btn") + self.priority_btn_group.addButton(self.med_priority_btn) + buttons_layout.addWidget(self.med_priority_btn) + + self.high_priority_btn = BlocksCustomCheckButton(parent=self.frame_12) + self.high_priority_btn.setMinimumSize(QtCore.QSize(100, 100)) + self.high_priority_btn.setMaximumSize(QtCore.QSize(100, 100)) + self.high_priority_btn.setCheckable(True) + self.high_priority_btn.setChecked(False) + self.high_priority_btn.setAutoExclusive(True) + self.high_priority_btn.setFlat(True) + self.high_priority_btn.setProperty( + "icon_pixmap", QtGui.QPixmap(":/ui/media/btn_icons/indf_svg.svg") + ) + self.high_priority_btn.setText("High") + self.high_priority_btn.setProperty("class", "back_btn") + self.high_priority_btn.setProperty("button_type", "icon") + self.high_priority_btn.setObjectName("high_priority_btn") + self.priority_btn_group.addButton(self.high_priority_btn) + buttons_layout.addWidget(self.high_priority_btn) + + frame_inner_layout.addLayout(buttons_layout) + + priority_inner_layout.addWidget( + self.frame_12, + 0, + QtCore.Qt.AlignmentFlag.AlignHCenter | QtCore.Qt.AlignmentFlag.AlignVCenter, + ) + + priority_outer_layout.addLayout(priority_inner_layout) + content_layout.addLayout(priority_outer_layout) + main_layout.addLayout(content_layout) + + self.addWidget(self.saved_details_page) + + def _setup_hotspot_page(self) -> None: + """Setup the hotspot configuration page.""" + self.hotspot_page = QtWidgets.QWidget() + self.hotspot_page.setObjectName("hotspot_page") + + main_layout = QtWidgets.QVBoxLayout(self.hotspot_page) + main_layout.setObjectName("verticalLayout_12") + + # Header layout + header_layout = QtWidgets.QHBoxLayout() + header_layout.setObjectName("hospot_page_header_layout") + + header_layout.addItem( + QtWidgets.QSpacerItem( + 40, + 20, + QtWidgets.QSizePolicy.Policy.Minimum, + QtWidgets.QSizePolicy.Policy.Minimum, ) ) - self.panel.add_network_password_view.pressed.connect( - lambda: self.panel.add_network_password_view.setPixmap( - QtGui.QPixmap(":/ui/media/btn_icons/see.svg") + + self.hotspot_header_title = QtWidgets.QLabel(parent=self.hotspot_page) + self.hotspot_header_title.setPalette(self._create_white_palette()) + font = QtGui.QFont() + font.setPointSize(20) + self.hotspot_header_title.setFont(font) + self.hotspot_header_title.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) + self.hotspot_header_title.setText("Hotspot") + self.hotspot_header_title.setObjectName("hotspot_header_title") + header_layout.addWidget(self.hotspot_header_title) + + self.hotspot_back_button = IconButton(parent=self.hotspot_page) + self.hotspot_back_button.setMinimumSize(QtCore.QSize(60, 60)) + self.hotspot_back_button.setMaximumSize(QtCore.QSize(60, 60)) + self.hotspot_back_button.setText("Back") + self.hotspot_back_button.setFlat(True) + self.hotspot_back_button.setProperty( + "icon_pixmap", QtGui.QPixmap(":/ui/media/btn_icons/back.svg") + ) + self.hotspot_back_button.setProperty("class", "back_btn") + self.hotspot_back_button.setProperty("button_type", "icon") + self.hotspot_back_button.setObjectName("hotspot_back_button") + header_layout.addWidget(self.hotspot_back_button) + + main_layout.addLayout(header_layout) + + # Content layout + content_layout = QtWidgets.QVBoxLayout() + content_layout.setContentsMargins(-1, 5, -1, 5) + content_layout.setObjectName("hotspot_page_content_layout") + + content_layout.addItem( + QtWidgets.QSpacerItem( + 20, + 50, + QtWidgets.QSizePolicy.Policy.Minimum, + QtWidgets.QSizePolicy.Policy.Minimum, ) ) - self.panel.hotspot_password_view_button.released.connect( - lambda: self.panel.hotspot_password_view_button.setPixmap( - QtGui.QPixmap(":/ui/media/btn_icons/unsee.svg") + + # Hotspot name frame + self.frame_6 = BlocksCustomFrame(parent=self.hotspot_page) + frame_policy = QtWidgets.QSizePolicy( + QtWidgets.QSizePolicy.Policy.Expanding, + QtWidgets.QSizePolicy.Policy.Expanding, + ) + self.frame_6.setSizePolicy(frame_policy) + self.frame_6.setMinimumSize(QtCore.QSize(70, 80)) + self.frame_6.setFrameShape(QtWidgets.QFrame.Shape.StyledPanel) + self.frame_6.setFrameShadow(QtWidgets.QFrame.Shadow.Raised) + self.frame_6.setObjectName("frame_6") + + frame_layout_widget = QtWidgets.QWidget(parent=self.frame_6) + frame_layout_widget.setGeometry(QtCore.QRect(0, 10, 776, 61)) + frame_layout_widget.setObjectName("layoutWidget_6") + + name_layout = QtWidgets.QHBoxLayout(frame_layout_widget) + name_layout.setContentsMargins(0, 0, 0, 0) + name_layout.setObjectName("horizontalLayout_11") + + self.hotspot_info_name_label = QtWidgets.QLabel(parent=frame_layout_widget) + label_policy = QtWidgets.QSizePolicy( + QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Maximum + ) + self.hotspot_info_name_label.setSizePolicy(label_policy) + self.hotspot_info_name_label.setMaximumSize(QtCore.QSize(150, 16777215)) + self.hotspot_info_name_label.setPalette(self._create_white_palette()) + font = QtGui.QFont() + font.setFamily("Momcake") + font.setPointSize(10) + self.hotspot_info_name_label.setFont(font) + self.hotspot_info_name_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) + self.hotspot_info_name_label.setText("Hotspot Name: ") + self.hotspot_info_name_label.setObjectName("hotspot_info_name_label") + name_layout.addWidget(self.hotspot_info_name_label) + + self.hotspot_name_input_field = BlocksCustomLinEdit(parent=frame_layout_widget) + field_policy = QtWidgets.QSizePolicy( + QtWidgets.QSizePolicy.Policy.Expanding, + QtWidgets.QSizePolicy.Policy.MinimumExpanding, + ) + self.hotspot_name_input_field.setSizePolicy(field_policy) + self.hotspot_name_input_field.setMinimumSize(QtCore.QSize(500, 40)) + self.hotspot_name_input_field.setMaximumSize(QtCore.QSize(500, 60)) + font = QtGui.QFont() + font.setPointSize(12) + self.hotspot_name_input_field.setFont(font) + # Name should be visible, not masked + self.hotspot_name_input_field.setEchoMode(QtWidgets.QLineEdit.EchoMode.Normal) + self.hotspot_name_input_field.setObjectName("hotspot_name_input_field") + name_layout.addWidget( + self.hotspot_name_input_field, 0, QtCore.Qt.AlignmentFlag.AlignHCenter + ) + + name_layout.addItem( + QtWidgets.QSpacerItem( + 60, + 20, + QtWidgets.QSizePolicy.Policy.Minimum, + QtWidgets.QSizePolicy.Policy.Minimum, ) ) - self.panel.hotspot_password_view_button.pressed.connect( - lambda: self.panel.hotspot_password_view_button.setPixmap( - QtGui.QPixmap(":/ui/media/btn_icons/see.svg") + + content_layout.addWidget(self.frame_6) + + content_layout.addItem( + QtWidgets.QSpacerItem( + 773, + 128, + QtWidgets.QSizePolicy.Policy.Minimum, + QtWidgets.QSizePolicy.Policy.Minimum, ) ) - self.panel.add_network_password_field.setCursor( - QtCore.Qt.CursorShape.BlankCursor + # Hotspot password frame + self.frame_7 = BlocksCustomFrame(parent=self.hotspot_page) + frame_policy = QtWidgets.QSizePolicy( + QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum + ) + self.frame_7.setSizePolicy(frame_policy) + self.frame_7.setMinimumSize(QtCore.QSize(0, 80)) + self.frame_7.setFrameShape(QtWidgets.QFrame.Shape.StyledPanel) + self.frame_7.setFrameShadow(QtWidgets.QFrame.Shadow.Raised) + self.frame_7.setObjectName("frame_7") + + password_layout_widget = QtWidgets.QWidget(parent=self.frame_7) + password_layout_widget.setGeometry(QtCore.QRect(0, 10, 776, 62)) + password_layout_widget.setObjectName("layoutWidget_7") + + password_layout = QtWidgets.QHBoxLayout(password_layout_widget) + password_layout.setContentsMargins(0, 0, 0, 0) + password_layout.setObjectName("horizontalLayout_12") + + self.hotspot_info_password_label = QtWidgets.QLabel( + parent=password_layout_widget + ) + self.hotspot_info_password_label.setSizePolicy(label_policy) + self.hotspot_info_password_label.setMaximumSize(QtCore.QSize(150, 16777215)) + self.hotspot_info_password_label.setPalette(self._create_white_palette()) + font = QtGui.QFont() + font.setFamily("Momcake") + font.setPointSize(10) + self.hotspot_info_password_label.setFont(font) + self.hotspot_info_password_label.setAlignment( + QtCore.Qt.AlignmentFlag.AlignCenter + ) + self.hotspot_info_password_label.setText("Hotspot Password:") + self.hotspot_info_password_label.setObjectName("hotspot_info_password_label") + password_layout.addWidget(self.hotspot_info_password_label) + + self.hotspot_password_input_field = BlocksCustomLinEdit( + parent=password_layout_widget + ) + self.hotspot_password_input_field.setHidden(True) + self.hotspot_password_input_field.setSizePolicy(field_policy) + self.hotspot_password_input_field.setMinimumSize(QtCore.QSize(500, 40)) + self.hotspot_password_input_field.setMaximumSize(QtCore.QSize(500, 60)) + font = QtGui.QFont() + font.setPointSize(12) + self.hotspot_password_input_field.setFont(font) + self.hotspot_password_input_field.setEchoMode( + QtWidgets.QLineEdit.EchoMode.Password + ) + self.hotspot_password_input_field.setObjectName("hotspot_password_input_field") + password_layout.addWidget( + self.hotspot_password_input_field, 0, QtCore.Qt.AlignmentFlag.AlignHCenter + ) + + self.hotspot_password_view_button = IconButton(parent=password_layout_widget) + self.hotspot_password_view_button.setMinimumSize(QtCore.QSize(60, 60)) + self.hotspot_password_view_button.setMaximumSize(QtCore.QSize(60, 60)) + self.hotspot_password_view_button.setText("View") + self.hotspot_password_view_button.setFlat(True) + self.hotspot_password_view_button.setProperty( + "icon_pixmap", QtGui.QPixmap(":/ui/media/btn_icons/unsee.svg") + ) + self.hotspot_password_view_button.setProperty("class", "back_btn") + self.hotspot_password_view_button.setProperty("button_type", "icon") + self.hotspot_password_view_button.setObjectName("hotspot_password_view_button") + password_layout.addWidget(self.hotspot_password_view_button) + + content_layout.addWidget(self.frame_7) + + # Save button + self.hotspot_change_confirm = BlocksCustomButton(parent=self.hotspot_page) + self.hotspot_change_confirm.setMinimumSize(QtCore.QSize(200, 80)) + self.hotspot_change_confirm.setMaximumSize(QtCore.QSize(250, 100)) + font = QtGui.QFont() + font.setPointSize(18) + font.setBold(True) + font.setWeight(75) + self.hotspot_change_confirm.setFont(font) + self.hotspot_change_confirm.setProperty( + "icon_pixmap", QtGui.QPixmap(":/ui/media/btn_icons/save.svg") + ) + self.hotspot_change_confirm.setText("Save") + self.hotspot_change_confirm.setObjectName("hotspot_change_confirm") + content_layout.addWidget( + self.hotspot_change_confirm, + 0, + QtCore.Qt.AlignmentFlag.AlignHCenter | QtCore.Qt.AlignmentFlag.AlignVCenter, + ) + + main_layout.addLayout(content_layout) + + self.addWidget(self.hotspot_page) + + def _init_timers(self) -> None: + """Initialize all timers.""" + self._status_check_timer = QtCore.QTimer(self) + self._status_check_timer.setInterval(STATUS_CHECK_INTERVAL_MS) + + self._delayed_action_timer = QtCore.QTimer(self) + self._delayed_action_timer.setSingleShot(True) + + self._load_timer = QtCore.QTimer(self) + self._load_timer.setSingleShot(True) + self._load_timer.timeout.connect(self._handle_load_timeout) + + def _init_model_view(self) -> None: + """Initialize the model and view for network list.""" + self._model = EntryListModel() + self._model.setParent(self.listView) + self._entry_delegate = EntryDelegate() + self.listView.setModel(self._model) + self.listView.setItemDelegate(self._entry_delegate) + self._entry_delegate.item_selected.connect(self._on_ssid_item_clicked) + self._configure_list_view_palette() + + def _init_network_worker(self) -> None: + """Initialize the network list worker.""" + self._network_list_worker = BuildNetworkList( + nm=self._sdbus_network, poll_interval_ms=DEFAULT_POLL_INTERVAL_MS ) - self.panel.hotspot_name_input_field.setCursor(QtCore.Qt.CursorShape.BlankCursor) - self.panel.hotspot_password_input_field.setCursor( - QtCore.Qt.CursorShape.BlankCursor + self._network_list_worker.finished_network_list_build.connect( + self._handle_network_list ) - self.panel.network_delete_btn.setPixmap( - QtGui.QPixmap(":/ui/media/btn_icons/garbage-icon.svg") + self._network_list_worker.start_polling() + self.rescan_button.clicked.connect(self._network_list_worker.build) + + def _setup_navigation_signals(self) -> None: + """Setup navigation button signals.""" + self.wifi_button.clicked.connect( + partial(self.setCurrentIndex, self.indexOf(self.network_list_page)) + ) + self.hotspot_button.clicked.connect( + partial(self.setCurrentIndex, self.indexOf(self.hotspot_page)) + ) + self.nl_back_button.clicked.connect( + partial(self.setCurrentIndex, self.indexOf(self.main_network_page)) ) + self.network_backButton.clicked.connect(self.hide) - self.panel.network_activate_btn.setPixmap( - QtGui.QPixmap(":/dialog/media/btn_icons/yes.svg") + self.add_network_page_backButton.clicked.connect( + partial(self.setCurrentIndex, self.indexOf(self.network_list_page)) ) - self.panel.network_details_btn.setPixmap( - QtGui.QPixmap(":/ui/media/btn_icons/printer_settings.svg") + self.saved_connection_back_button.clicked.connect( + partial(self.setCurrentIndex, self.indexOf(self.network_list_page)) + ) + self.snd_back.clicked.connect( + partial(self.setCurrentIndex, self.indexOf(self.saved_connection_page)) + ) + self.network_details_btn.clicked.connect( + partial(self.setCurrentIndex, self.indexOf(self.saved_details_page)) ) - self.panel.snd_back.clicked.connect( - lambda: self.setCurrentIndex(self.indexOf(self.panel.saved_connection_page)) + self.hotspot_back_button.clicked.connect( + partial(self.setCurrentIndex, self.indexOf(self.main_network_page)) ) - self.panel.network_details_btn.clicked.connect( - lambda: self.setCurrentIndex(self.indexOf(self.panel.saved_details_page)) + self.hotspot_change_confirm.clicked.connect( + partial(self.setCurrentIndex, self.indexOf(self.main_network_page)) ) - self.panel.network_activate_btn.clicked.connect( - lambda: self.saved_wifi_option_selected() + def _setup_action_signals(self) -> None: + """Setup action button signals.""" + self._sdbus_network.nm_state_change.connect(self._evaluate_network_state) + self.request_network_scan.connect(self._rescan_networks) + self.delete_network_signal.connect(self._delete_network) + + self.add_network_validation_button.clicked.connect(self._add_network) + + self.snd_back.clicked.connect(self._on_save_network_settings) + self.network_activate_btn.clicked.connect(self._on_saved_wifi_option_selected) + self.network_delete_btn.clicked.connect(self._on_saved_wifi_option_selected) + + self._status_check_timer.timeout.connect(self._check_connection_status) + + def _setup_toggle_signals(self) -> None: + """Setup toggle button signals.""" + self.wifi_button.toggle_button.stateChange.connect(self._on_toggle_state) + self.hotspot_button.toggle_button.stateChange.connect(self._on_toggle_state) + + def _setup_password_visibility_signals(self) -> None: + """Setup password visibility toggle signals.""" + self._setup_password_visibility_toggle( + self.add_network_password_view, + self.add_network_password_field, + ) + self._setup_password_visibility_toggle( + self.saved_connection_change_password_view, + self.saved_connection_change_password_field, ) - self.panel.network_delete_btn.clicked.connect( - lambda: self.saved_wifi_option_selected() + self._setup_password_visibility_toggle( + self.hotspot_password_view_button, + self.hotspot_password_input_field, ) - self.network_list_worker.build() - self.request_network_scan.emit() - self.hide() - self.info_box_load() + def _setup_password_visibility_toggle( + self, view_button: QtWidgets.QWidget, password_field: QtWidgets.QLineEdit + ) -> None: + """Setup password visibility toggle for a button/field pair.""" + view_button.setCheckable(True) - self.qwerty = CustomQwertyKeyboard(self) - self.addWidget(self.qwerty) - self.qwerty.value_selected.connect(self.on_qwerty_value_selected) - self.qwerty.request_back.connect(self.on_qwerty_go_back) + see_icon = QtGui.QPixmap(":/ui/media/btn_icons/see.svg") + unsee_icon = QtGui.QPixmap(":/ui/media/btn_icons/unsee.svg") - self.panel.add_network_password_field.clicked.connect( - lambda: self.on_show_keyboard( - self.panel.add_network_page, self.panel.add_network_password_field + # Connect toggle signal + view_button.toggled.connect( + lambda checked: password_field.setHidden(not checked) + ) + + # Update icon based on toggle state + view_button.toggled.connect( + lambda checked: view_button.setPixmap( + unsee_icon if not checked else see_icon ) ) - self.panel.hotspot_password_input_field.clicked.connect( - lambda: self.on_show_keyboard( - self.panel.hotspot_page, self.panel.hotspot_password_input_field + + def _setup_icons(self) -> None: + """Setup button icons.""" + self.hotspot_button.setPixmap( + QtGui.QPixmap(":/network/media/btn_icons/hotspot.svg") + ) + self.wifi_button.setPixmap( + QtGui.QPixmap(":/network/media/btn_icons/wifi_config.svg") + ) + self.network_delete_btn.setPixmap( + QtGui.QPixmap(":/ui/media/btn_icons/garbage-icon.svg") + ) + self.network_activate_btn.setPixmap( + QtGui.QPixmap(":/dialog/media/btn_icons/yes.svg") + ) + self.network_details_btn.setPixmap( + QtGui.QPixmap(":/ui/media/btn_icons/printer_settings.svg") + ) + + def _setup_input_fields(self) -> None: + """Setup input field properties.""" + self.add_network_password_field.setCursor(QtCore.Qt.CursorShape.BlankCursor) + self.hotspot_name_input_field.setCursor(QtCore.Qt.CursorShape.BlankCursor) + self.hotspot_password_input_field.setCursor(QtCore.Qt.CursorShape.BlankCursor) + + self.hotspot_password_input_field.setPlaceholderText("Defaults to: 123456789") + self.hotspot_name_input_field.setText( + str(self._sdbus_network.get_hotspot_ssid() or "PrinterHotspot") + ) + self.hotspot_password_input_field.setText( + str(self._sdbus_network.hotspot_password or "123456789") + ) + + def _setup_keyboard(self) -> None: + """Setup the on-screen keyboard.""" + self._qwerty = CustomQwertyKeyboard(self) + self.addWidget(self._qwerty) + self._qwerty.value_selected.connect(self._on_qwerty_value_selected) + self._qwerty.request_back.connect(self._on_qwerty_go_back) + + self.add_network_password_field.clicked.connect( + lambda: self._on_show_keyboard( + self.add_network_page, self.add_network_password_field ) ) - self.panel.hotspot_name_input_field.clicked.connect( - lambda: self.on_show_keyboard( - self.panel.hotspot_page, self.panel.hotspot_name_input_field + self.hotspot_password_input_field.clicked.connect( + lambda: self._on_show_keyboard( + self.hotspot_page, self.hotspot_password_input_field ) ) - self.panel.saved_connection_change_password_field.clicked.connect( - lambda: self.on_show_keyboard( - self.panel.saved_connection_page, - self.panel.saved_connection_change_password_field, + self.hotspot_name_input_field.clicked.connect( + lambda: self._on_show_keyboard( + self.hotspot_page, self.hotspot_name_input_field + ) + ) + self.saved_connection_change_password_field.clicked.connect( + lambda: self._on_show_keyboard( + self.saved_connection_page, + self.saved_connection_change_password_field, ) ) - def saved_wifi_option_selected(self): - """Handle connect/delete network button clicks""" - _sender = self.sender() - self.panel.wifi_button.toggle_button.state = ( - self.panel.wifi_button.toggle_button.State.ON + def _setup_scrollbar_signals(self) -> None: + """Setup scrollbar synchronization signals.""" + self.listView.verticalScrollBar().valueChanged.connect( + self._handle_scrollbar_change ) - self.panel.hotspot_button.toggle_button.state = ( - self.panel.hotspot_button.toggle_button.State.OFF + self.verticalScrollBar.valueChanged.connect(self._handle_scrollbar_change) + self.verticalScrollBar.valueChanged.connect( + lambda value: self.listView.verticalScrollBar().setValue(value) ) + self.verticalScrollBar.show() - if _sender == self.panel.network_delete_btn: - self.sdbus_network.delete_network( - self.panel.saved_connection_network_name.text() - ) - self.setCurrentIndex(self.indexOf(self.panel.main_network_page)) + def _configure_list_view_palette(self) -> None: + """Configure the list view palette for transparency.""" + palette = QtGui.QPalette() - elif _sender == self.panel.network_activate_btn: - self.setCurrentIndex(self.indexOf(self.panel.main_network_page)) - self.sdbus_network.connect_network( - self.panel.saved_connection_network_name.text() - ) - self.info_box_load(True) - - def on_show_keyboard(self, panel: QtWidgets.QWidget, field: QtWidgets.QLineEdit): - """Handle keyboard show""" - self.previousPanel = panel - self.currentField = field - self.qwerty.set_value(field.text()) - self.setCurrentIndex(self.indexOf(self.qwerty)) - - def on_qwerty_go_back(self): - """Hide keyboard""" - self.setCurrentIndex(self.indexOf(self.previousPanel)) - - def on_qwerty_value_selected(self, value: str): - """Handle keyboard value input""" - self.setCurrentIndex(self.indexOf(self.previousPanel)) - if hasattr(self, "currentField") and self.currentField: - self.currentField.setText(value) - - def info_box_load(self, toggle: bool = False) -> None: - """ - Shows or hides the loading screen. - Sets a 30-second timeout to handle loading failures. + for group in [ + QtGui.QPalette.ColorGroup.Active, + QtGui.QPalette.ColorGroup.Inactive, + QtGui.QPalette.ColorGroup.Disabled, + ]: + transparent = QtGui.QBrush(QtGui.QColor(0, 0, 0, 0)) + transparent.setStyle(QtCore.Qt.BrushStyle.SolidPattern) + palette.setBrush(group, QtGui.QPalette.ColorRole.Button, transparent) + palette.setBrush(group, QtGui.QPalette.ColorRole.Window, transparent) + + no_brush = QtGui.QBrush(QtGui.QColor(0, 0, 0)) + no_brush.setStyle(QtCore.Qt.BrushStyle.NoBrush) + palette.setBrush(group, QtGui.QPalette.ColorRole.Base, no_brush) + + highlight = QtGui.QBrush(QtGui.QColor(0, 120, 215, 0)) + highlight.setStyle(QtCore.Qt.BrushStyle.SolidPattern) + palette.setBrush(group, QtGui.QPalette.ColorRole.Highlight, highlight) + + link = QtGui.QBrush(QtGui.QColor(0, 0, 255, 0)) + link.setStyle(QtCore.Qt.BrushStyle.SolidPattern) + palette.setBrush(group, QtGui.QPalette.ColorRole.Link, link) + + self.listView.setPalette(palette) + + def _show_error_popup(self, message: str, timeout: int = 6000) -> None: + """Show an error popup message.""" + self._popup.raise_() + self._popup.new_message( + message_type=Popup.MessageType.ERROR, + message=message, + timeout=timeout, + userInput=False, + ) + + def _show_info_popup(self, message: str, timeout: int = 4000) -> None: + """Show an info popup message.""" + self._popup.raise_() + self._popup.new_message( + message_type=Popup.MessageType.INFO, + message=message, + timeout=timeout, + userInput=False, + ) + + def _show_warning_popup(self, message: str, timeout: int = 5000) -> None: + """Show a warning popup message.""" + self._popup.raise_() + self._popup.new_message( + message_type=Popup.MessageType.WARNING, + message=message, + timeout=timeout, + userInput=False, + ) + + def closeEvent(self, event: Optional[QtGui.QCloseEvent]) -> None: + """Handle close event.""" + self._stop_all_timers() + self._network_list_worker.stop_polling() + super().closeEvent(event) + + def showEvent(self, event: Optional[QtGui.QShowEvent]) -> None: + """Handle show event.""" + if self._networks: + self._build_model_list() + self._evaluate_network_state() + super().showEvent(event) + + def _stop_all_timers(self) -> None: + """Stop all active timers.""" + timers = [ + self._load_timer, + self._status_check_timer, + self._delayed_action_timer, + ] + for timer in timers: + if timer.isActive(): + timer.stop() + + def _on_show_keyboard( + self, panel: QtWidgets.QWidget, field: QtWidgets.QLineEdit + ) -> None: + """Show the on-screen keyboard for a field.""" + self._previous_panel = panel + self._current_field = field + self._qwerty.set_value(field.text()) + self.setCurrentIndex(self.indexOf(self._qwerty)) + + def _on_qwerty_go_back(self) -> None: + """Handle keyboard back button.""" + if self._previous_panel: + self.setCurrentIndex(self.indexOf(self._previous_panel)) + + def _on_qwerty_value_selected(self, value: str) -> None: + """Handle keyboard value selection.""" + if self._previous_panel: + self.setCurrentIndex(self.indexOf(self._previous_panel)) + if self._current_field: + self._current_field.setText(value) + + def _set_loading_state(self, loading: bool) -> None: + """Set loading state - controls loading widget visibility. + + This method ensures mutual exclusivity between + loading widget, network details, and info box. """ - self._show_loadscreen(toggle) + self.wifi_button.setEnabled(not loading) + self.hotspot_button.setEnabled(not loading) + + if loading: + self._is_connecting = True + # + # Hide ALL other elements first before showing loading + # This prevents the dual panel visibility bug + self._hide_all_info_elements() + # Force UI update to ensure elements are hidden + self.repaint() + # Now show loading + self.loadingwidget.setVisible(True) - self.panel.wifi_button.setEnabled(not toggle) - self.panel.hotspot_button.setEnabled(not toggle) + if self._load_timer.isActive(): + self._load_timer.stop() + self._load_timer.start(LOAD_TIMEOUT_MS) + if not self._status_check_timer.isActive(): + self._status_check_timer.start() + else: + self._is_connecting = False + self._target_ssid = None + # Just hide loading - caller decides what to show next + self.loadingwidget.setVisible(False) - if toggle: if self._load_timer.isActive(): self._load_timer.stop() - self._load_timer.start(30000) + if self._status_check_timer.isActive(): + self._status_check_timer.stop() + + def _show_network_details(self) -> None: + """Show network details panel - HIDES everything else first.""" + # Hide everything else first to prevent dual panel + self.loadingwidget.setVisible(False) + self.mn_info_box.setVisible(False) + # Force UI update + self.repaint() - def _handle_load_timeout(self): - """ - Logic to execute if the loading screen is still visible after 30 seconds.< - """ - wifi_btn = self.panel.wifi_button - hotspot_btn = self.panel.hotspot_button + # Then show only the details + self.netlist_ip.setVisible(True) + self.netlist_ssuid.setVisible(True) + self.mn_info_seperator.setVisible(True) + self.line_2.setVisible(True) + self.netlist_strength.setVisible(True) + self.netlist_strength_label.setVisible(True) + self.line_3.setVisible(True) + self.netlist_security.setVisible(True) + self.netlist_security_label.setVisible(True) + + def _show_disconnected_message(self) -> None: + """Show the disconnected state message - HIDES everything else first.""" + # Hide everything else first to prevent dual panel + self.loadingwidget.setVisible(False) + self._hide_network_detail_labels() + # Force UI update + self.repaint() + + # Then show info box + self._configure_info_box_centered() + self.mn_info_box.setVisible(True) + self.mn_info_box.setText( + "Network connection required.\n\nConnect to Wi-Fi\nor\nTurn on Hotspot" + ) + + def _hide_network_detail_labels(self) -> None: + """Hide only the network detail labels (not loading or info box).""" + self.netlist_ip.setVisible(False) + self.netlist_ssuid.setVisible(False) + self.mn_info_seperator.setVisible(False) + self.line_2.setVisible(False) + self.netlist_strength.setVisible(False) + self.netlist_strength_label.setVisible(False) + self.line_3.setVisible(False) + self.netlist_security.setVisible(False) + self.netlist_security_label.setVisible(False) + + def _check_connection_status(self) -> None: + """Backup periodic check to detect successful connections.""" + if not self.loadingwidget.isVisible(): + if self._status_check_timer.isActive(): + self._status_check_timer.stop() + return + + connectivity = self._sdbus_network.check_connectivity() + is_connected = connectivity in ("FULL", "LIMITED") - if self.panel.loadingwidget.isVisible(): - if wifi_btn.toggle_button.state == wifi_btn.toggle_button.State.ON: - message = "Wi-Fi Connection Failed.\nThe connection attempt \ntimed out.\n Please check\nyour network stability \nor\n try reconnecting." + wifi_btn = self.wifi_button.toggle_button + hotspot_btn = self.hotspot_button.toggle_button - elif hotspot_btn.toggle_button.state == hotspot_btn.toggle_button.State.ON: - message = "Hotspot Setup Failed.\nThe local network sharing\n timed out.\n\n restart the hotspot." + if hotspot_btn.state == hotspot_btn.State.ON: + hotspot_ip = self._sdbus_network.get_device_ip_by_interface("wlan0") + if hotspot_ip: + logger.debug("Hotspot connection detected via status check") + # Stop loading first, then show details + self._set_loading_state(False) + self._update_hotspot_display() + self._show_network_details() + return + + if wifi_btn.state == wifi_btn.State.ON: + current_ssid = self._sdbus_network.get_current_ssid() + + if self._target_ssid: + if current_ssid == self._target_ssid and is_connected: + logger.debug("Target Wi-Fi connection detected: %s", current_ssid) + # Stop loading first, then show details + self._set_loading_state(False) + self._update_wifi_display() + self._show_network_details() + return else: - message = "Loading timed out.\n Please check your connection \n and try again." + if current_ssid and is_connected: + logger.debug("Wi-Fi connection detected: %s", current_ssid) + # Stop loading first, then show details + self._set_loading_state(False) + self._update_wifi_display() + self._show_network_details() + return + + def _handle_load_timeout(self) -> None: + """Handle connection timeout.""" + if not self.loadingwidget.isVisible(): + return + + connectivity = self._sdbus_network.check_connectivity() + is_connected = connectivity in ("FULL", "LIMITED") - self.panel.mn_info_box.setText(message) - self._show_loadscreen(False) - self._expand_infobox(True) + wifi_btn = self.wifi_button + hotspot_btn = self.hotspot_button + + # Final check if connection succeeded + if wifi_btn.toggle_button.state == wifi_btn.toggle_button.State.ON: + current_ssid = self._sdbus_network.get_current_ssid() + + if self._target_ssid: + if current_ssid == self._target_ssid and is_connected: + logger.debug("Target connection succeeded on timeout check") + self._set_loading_state(False) + self._update_wifi_display() + self._show_network_details() + return + else: + if current_ssid and is_connected: + logger.debug("Connection succeeded on timeout check") + self._set_loading_state(False) + self._update_wifi_display() + self._show_network_details() + return + + elif hotspot_btn.toggle_button.state == hotspot_btn.toggle_button.State.ON: + hotspot_ip = self._sdbus_network.get_device_ip_by_interface("wlan0") + if hotspot_ip: + logger.debug("Hotspot succeeded on timeout check") + self._set_loading_state(False) + self._update_hotspot_display() + self._show_network_details() + return + + # Connection actually failed + self._is_connecting = False + self._target_ssid = None + self._set_loading_state(False) + + # Show error message + self._hide_all_info_elements() + self._configure_info_box_centered() + self.mn_info_box.setVisible(True) + self.mn_info_box.setText(self._get_timeout_message(wifi_btn, hotspot_btn)) hotspot_btn.setEnabled(True) wifi_btn.setEnabled(True) - def _show_loadscreen(self, toggle: bool = False): - """Expand LOAD box on the main network panel - - Args: - toggle (bool, optional): show or not (Defaults to False) - """ - self.panel.netlist_ip.setVisible(not toggle) - self.panel.netlist_ssuid.setVisible(not toggle) - self.panel.mn_info_seperator.setVisible(not toggle) - self.panel.line_2.setVisible(not toggle) - self.panel.netlist_strength.setVisible(not toggle) - self.panel.netlist_strength_label.setVisible(not toggle) + self._show_error_popup("Connection timed out. Please try again.") - self.panel.line_3.setVisible(not toggle) - self.panel.netlist_security.setVisible(not toggle) - self.panel.netlist_security_label.setVisible(not toggle) + def _get_timeout_message(self, wifi_btn, hotspot_btn) -> str: + """Get appropriate timeout message based on state.""" + if wifi_btn.toggle_button.state == wifi_btn.toggle_button.State.ON: + return "Wi-Fi Connection Failed.\nThe connection attempt\n timed out." + elif hotspot_btn.toggle_button.state == hotspot_btn.toggle_button.State.ON: + return "Hotspot Setup Failed.\nPlease restart the hotspot." + else: + return "Loading timed out.\nPlease check your connection\n and try again." - self.panel.mn_info_box.setVisible(not toggle) + def _configure_info_box_centered(self) -> None: + """Configure info box for centered text.""" + self.mn_info_box.setWordWrap(True) + self.mn_info_box.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) - self.panel.loadingwidget.setVisible(toggle) + def _clear_network_display(self) -> None: + """Clear all network display labels.""" + self.netlist_ssuid.setText("") + self.netlist_ip.setText("") + self.netlist_strength.setText("") + self.netlist_security.setText("") + self._last_displayed_ssid = None @QtCore.pyqtSlot(object, name="stateChange") - def on_toggle_state(self, new_state) -> None: - """Handle toggle button changes""" + def _on_toggle_state(self, new_state) -> None: + """Handle toggle button state change.""" sender_button = self.sender() - wifi_btn = self.panel.wifi_button.toggle_button - hotspot_btn = self.panel.hotspot_button.toggle_button + wifi_btn = self.wifi_button.toggle_button + hotspot_btn = self.hotspot_button.toggle_button is_sender_now_on = new_state == sender_button.State.ON - _old_hotspot = None - saved_network = self.sdbus_network.get_saved_networks() + # Show loading IMMEDIATELY when turning something on + if is_sender_now_on: + self._set_loading_state(True) + self.repaint() - if sender_button is wifi_btn: - if is_sender_now_on: - hotspot_btn.state = hotspot_btn.State.OFF - self.sdbus_network.toggle_hotspot(False) - if saved_network: - try: - ssid = next( - ( - n["ssid"] - for n in saved_network - if "ap" not in n["mode"] and n["signal"] != 0 - ), - None, - ) - self.sdbus_network.connect_network(str(ssid)) - - except Exception as e: - logger.error(f"error when turning ON wifi on_toggle_state:{e}") + saved_networks = self._sdbus_network.get_saved_networks_with_for() + if sender_button is wifi_btn: + self._handle_wifi_toggle(is_sender_now_on, hotspot_btn, saved_networks) elif sender_button is hotspot_btn: - if is_sender_now_on: - wifi_btn.state = wifi_btn.State.OFF - - for n in saved_network: - if "ap" in n.get("mode", ""): - _old_hotspot = n - break - - if ( - _old_hotspot - and _old_hotspot["ssid"] - != self.panel.hotspot_name_input_field.text() - ): - self.sdbus_network.delete_network(_old_hotspot["ssid"]) - - self.sdbus_network.create_hotspot( - self.panel.hotspot_name_input_field.text(), - self.panel.hotspot_password_input_field.text(), - ) - self.sdbus_network.toggle_hotspot(True) - self.sdbus_network.connect_network( - self.panel.hotspot_name_input_field.text() - ) + self._handle_hotspot_toggle(is_sender_now_on, wifi_btn, saved_networks) - self.info_box_load(False) + # Handle both OFF if ( hotspot_btn.state == hotspot_btn.State.OFF and wifi_btn.state == wifi_btn.State.OFF ): - self.evaluate_network_state() - else: - self.info_box_load(True) + self._set_loading_state(False) + self._show_disconnected_message() - @QtCore.pyqtSlot(str, name="nm-state-changed") - def evaluate_network_state(self, nm_state: str = "") -> None: - """Handles or Reloads network state + def _handle_wifi_toggle( + self, is_on: bool, hotspot_btn, saved_networks: List[Dict] + ) -> None: + """Handle Wi-Fi toggle state change.""" + if not is_on: + self._target_ssid = None + return - Args: - nm_state (str, optional): Handles Network state depending on state - """ - # NM State Constants: UNKNOWN=0, ASLEEP=10, DISCONNECTED=20, DISCONNECTING=30, - # CONNECTING=40, CONNECTED_LOCAL=50, CONNECTED_SITE=60, GLOBAL=70 + hotspot_btn.state = hotspot_btn.State.OFF + self._sdbus_network.toggle_hotspot(False) - wifi_btn = self.panel.wifi_button.toggle_button - hotspot_btn = self.panel.hotspot_button.toggle_button - _nm_state = nm_state + # Check if already connected + current_ssid = self._sdbus_network.get_current_ssid() + connectivity = self._sdbus_network.check_connectivity() - if not _nm_state: - _nm_state = self.sdbus_network.check_nm_state() - if not _nm_state: - return + if current_ssid and connectivity == "FULL": + # Already connected - show immediately + self._target_ssid = current_ssid + self._set_loading_state(False) + self._update_wifi_display() + self._show_network_details() + return - if self.start: - self.start = False - saved_network = self.sdbus_network.get_saved_networks() - for n in saved_network: - if "ap" in n.get("mode", ""): - _old_hotspot = n - break - if _old_hotspot: - self.panel.hotspot_name_input_field.setText(_old_hotspot["ssid"]) - - connection = self.sdbus_network.check_connectivity() - if connection == "FULL": - self.panel.wifi_button.toggle_button.state = ( - self.panel.wifi_button.toggle_button.State.ON - ) - self.panel.hotspot_button.toggle_button.state = ( - self.panel.hotspot_button.toggle_button.State.OFF - ) - if connection == "LIMITED": - self.panel.wifi_button.toggle_button.state = ( - self.panel.wifi_button.toggle_button.State.OFF - ) - self.panel.hotspot_button.toggle_button.state = ( - self.panel.hotspot_button.toggle_button.State.ON - ) + # Filter wifi networks (not hotspots) + wifi_networks = [ + n for n in saved_networks if "ap" not in str(n.get("mode", "")) + ] - if not self.sdbus_network.check_wifi_interface(): + if not wifi_networks: + self._set_loading_state(False) + self._show_warning_popup( + "No saved Wi-Fi networks. Please add a network first." + ) + self._show_disconnected_message() return - if hotspot_btn.state == hotspot_btn.State.ON: - ipv4_addr = self.get_hotspot_ip_via_shell() + try: + ssid = wifi_networks[0]["ssid"] + self._target_ssid = ssid + self._sdbus_network.connect_network(str(ssid)) + except Exception as e: + logger.error("Error when turning ON wifi: %s", e) + self._set_loading_state(False) + self._show_error_popup("Failed to connect to Wi-Fi") + + def _handle_hotspot_toggle( + self, is_on: bool, wifi_btn, saved_networks: List[Dict] + ) -> None: + """Handle hotspot toggle state change.""" + if not is_on: + self._target_ssid = None + return - self.panel.netlist_ssuid.setText(self.panel.hotspot_name_input_field.text()) + wifi_btn.state = wifi_btn.State.OFF + self._target_ssid = None - self.panel.netlist_ip.setText(f"IP: {ipv4_addr or 'No IP Address'}") + new_hotspot_name = self.hotspot_name_input_field.text() or "PrinterHotspot" + new_hotspot_password = self.hotspot_password_input_field.text() or "123456789" - self.panel.netlist_strength.setText("--") + # Use QTimer to defer async operations + def setup_hotspot(): + try: + self._sdbus_network.create_hotspot( + new_hotspot_name, new_hotspot_password + ) + self._sdbus_network.toggle_hotspot(True) + except Exception as e: + logger.error("Error creating/activating hotspot: %s", e) + self._show_error_popup("Failed to start hotspot") + self._set_loading_state(False) - self.panel.netlist_security.setText("--") + QtCore.QTimer.singleShot(100, setup_hotspot) - self.panel.mn_info_box.setText("Hotspot On") + @QtCore.pyqtSlot(str, name="nm-state-changed") + def _evaluate_network_state(self, nm_state: str = "") -> None: + """Evaluate and update network state.""" + wifi_btn = self.wifi_button.toggle_button + hotspot_btn = self.hotspot_button.toggle_button - if wifi_btn.state == wifi_btn.State.ON: - ipv4_addr = self.sdbus_network.get_current_ip_addr() - current_ssid = self.sdbus_network.get_current_ssid() - if current_ssid == "": - return - sec_type = self.sdbus_network.get_security_type_by_ssid(current_ssid) - signal_strength = self.sdbus_network.get_connection_signal_by_ssid( - current_ssid - ) - self.panel.netlist_ssuid.setText(current_ssid) - self.panel.netlist_ip.setText(f"IP: {ipv4_addr or 'No IP Address'}") - self.panel.netlist_security.setText(str(sec_type or "--").upper()) - self.panel.netlist_strength.setText( - str(signal_strength if signal_strength != -1 else "--") - ) - self.panel.mn_info_box.setText("Connected") + state = nm_state or self._sdbus_network.check_nm_state() + if not state: + return - self._expand_infobox(False) - self.info_box_load(False) - self.panel.wifi_button.setEnabled(True) - self.panel.hotspot_button.setEnabled(True) - self.repaint() + if self._is_first_run: + self._handle_first_run_state() + self._is_first_run = False + return + if not self._sdbus_network.check_wifi_interface(): + return + + # Handle both OFF first if ( wifi_btn.state == wifi_btn.State.OFF and hotspot_btn.state == hotspot_btn.State.OFF ): - self.sdbus_network.disconnect_network() - self._expand_infobox(True) - self.panel.mn_info_box.setText( - "Network connection required.\n\nConnect to Wi-Fi\nor\nTurn on Hotspot" - ) + self._sdbus_network.disconnect_network() + self._clear_network_display() + self._set_loading_state(False) + self._show_disconnected_message() + return - def get_hotspot_ip_via_shell(self): - """ - Executes a shell command to retrieve the IPv4 address for a specified interface. + connectivity = self._sdbus_network.check_connectivity() + is_connected = connectivity in ("FULL", "LIMITED") - Returns: - The IP address string (e.g., '10.42.0.1') or None if not found. - """ - command = ["ip", "-4", "addr", "show", "wlan0"] - try: - result = subprocess.run( # nosec: B603 - command, - capture_output=True, - text=True, - check=True, - timeout=5, - ) - except subprocess.CalledProcessError as e: - logging.error( - "Caught exception (exit code %d) failed to run command: %s \nStderr: %s", - e.returncode, - command, - e.stderr.strip(), - ) - return "" - except FileNotFoundError: - logging.error("Command not found") - return "" - except subprocess.TimeoutExpired as e: - logging.error("Caught exception, failed to run command %s", e) - return "" - - for line in result.stdout.splitlines(): - line = line.strip() - if line.startswith("inet "): - ip_address = line.split()[1].split("/")[0] - return ip_address - logging.error("No IPv4 address found in output for wlan0") - return "" + # Handle hotspot + if hotspot_btn.state == hotspot_btn.State.ON: + hotspot_ip = self._sdbus_network.get_device_ip_by_interface("wlan0") + if hotspot_ip or is_connected: + # Stop loading first, then update display, then show details + self._set_loading_state(False) + self._update_hotspot_display() + self._show_network_details() + self.wifi_button.setEnabled(True) + self.hotspot_button.setEnabled(True) + return - def close(self) -> bool: - """Close class, close network module""" - self.sdbus_network.close() - return super().close() + # Handle wifi + if wifi_btn.state == wifi_btn.State.ON: + current_ssid = self._sdbus_network.get_current_ssid() + + if self._target_ssid: + if current_ssid == self._target_ssid and is_connected: + logger.debug("Connected to target: %s", current_ssid) + # Stop loading first, then update display, then show details + self._set_loading_state(False) + self._update_wifi_display() + self._show_network_details() + self.wifi_button.setEnabled(True) + self.hotspot_button.setEnabled(True) + else: + if current_ssid and is_connected: + # Stop loading first, then update display, then show details + self._set_loading_state(False) + self._update_wifi_display() + self._show_network_details() + self.wifi_button.setEnabled(True) + self.hotspot_button.setEnabled(True) + self.update() - def _expand_infobox(self, toggle: bool = False) -> None: - """Expand information box on the main network panel + def _handle_first_run_state(self) -> None: + """Handle initial state on first run.""" + saved_networks = self._sdbus_network.get_saved_networks_with_for() - Args: - toggle (bool, optional): Expand or not (Defaults to False) - """ - self.panel.netlist_ip.setVisible(not toggle) - self.panel.netlist_ssuid.setVisible(not toggle) - self.panel.mn_info_seperator.setVisible(not toggle) - self.panel.line_2.setVisible(not toggle) - self.panel.netlist_strength.setVisible(not toggle) - self.panel.netlist_strength_label.setVisible(not toggle) - - self.panel.line_3.setVisible(not toggle) - self.panel.netlist_security.setVisible(not toggle) - self.panel.netlist_security_label.setVisible(not toggle) - # Align text - self.panel.mn_info_box.setWordWrap(True) - self.panel.mn_info_box.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) + old_hotspot = next( + (n for n in saved_networks if "ap" in str(n.get("mode", ""))), None + ) + if old_hotspot: + self.hotspot_name_input_field.setText(old_hotspot["ssid"]) - @QtCore.pyqtSlot(str, name="delete-network") - def delete_network(self, ssid: str) -> None: - """Delete network""" - self.sdbus_network.delete_network(ssid=ssid) + connectivity = self._sdbus_network.check_connectivity() + wifi_btn = self.wifi_button.toggle_button + hotspot_btn = self.hotspot_button.toggle_button + current_ssid = self._sdbus_network.get_current_ssid() - @QtCore.pyqtSlot(name="rescan-networks") - def rescan_networks(self) -> None: - """Rescan for networks""" - self.sdbus_network.rescan_networks() + self._is_connecting = False + self.loadingwidget.setVisible(False) - @QtCore.pyqtSlot(name="handle-hotspot-back") - def handle_hotspot_back(self) -> None: - """Handle go back a page from hotspot page""" - if ( - self.panel.hotspot_password_input_field.text() - != self.sdbus_network.hotspot_password - ): - self.panel.hotspot_password_input_field.setText( - self.sdbus_network.hotspot_password + with QtCore.QSignalBlocker(wifi_btn), QtCore.QSignalBlocker(hotspot_btn): + if connectivity == "FULL" and current_ssid: + wifi_btn.state = wifi_btn.State.ON + hotspot_btn.state = hotspot_btn.State.OFF + self._update_wifi_display() + self._show_network_details() + self.wifi_button.setEnabled(True) + self.hotspot_button.setEnabled(True) + elif connectivity == "LIMITED": + wifi_btn.state = wifi_btn.State.OFF + hotspot_btn.state = hotspot_btn.State.ON + self._update_hotspot_display() + self._show_network_details() + self.wifi_button.setEnabled(True) + self.hotspot_button.setEnabled(True) + else: + wifi_btn.state = wifi_btn.State.OFF + hotspot_btn.state = hotspot_btn.State.OFF + self._clear_network_display() + self._show_disconnected_message() + self.wifi_button.setEnabled(True) + self.hotspot_button.setEnabled(True) + + def _update_hotspot_display(self) -> None: + """Update display for hotspot mode.""" + ipv4_addr = self._sdbus_network.get_device_ip_by_interface("wlan0") + if not ipv4_addr: + ipv4_addr = self._sdbus_network.get_current_ip_addr() + + hotspot_name = self.hotspot_name_input_field.text() + if not hotspot_name: + hotspot_name = self._sdbus_network.hotspot_ssid or "Hotspot" + self.hotspot_name_input_field.setText(hotspot_name) + + self.netlist_ssuid.setText(hotspot_name) + # Handle empty IP properly + if ipv4_addr and ipv4_addr.strip(): + self.netlist_ip.setText(f"IP: {ipv4_addr}") + else: + self.netlist_ip.setText("IP: Obtaining...") + self.netlist_strength.setText("--") + self.netlist_security.setText("WPA2") + self._last_displayed_ssid = hotspot_name + + def _update_wifi_display(self) -> None: + """Update display for wifi connection.""" + current_ssid = self._sdbus_network.get_current_ssid() + + if current_ssid: + ipv4_addr = self._sdbus_network.get_current_ip_addr() + sec_type = self._sdbus_network.get_security_type_by_ssid(current_ssid) + signal_strength = self._sdbus_network.get_connection_signal_by_ssid( + current_ssid ) - if ( - self.panel.hotspot_name_input_field.text() - != self.sdbus_network.hotspot_ssid - ): - self.panel.hotspot_name_input_field.setText(self.sdbus_network.hotspot_ssid) - self.setCurrentIndex(self.indexOf(self.panel.main_network_page)) - - @QtCore.pyqtSlot(name="add_network") - def add_network(self) -> None: - """Slot for adding a new network + self.netlist_ssuid.setText(current_ssid) + # Handle empty IP properly + if ipv4_addr and ipv4_addr.strip(): + self.netlist_ip.setText(f"IP: {ipv4_addr}") + else: + self.netlist_ip.setText("IP: Obtaining...") + self.netlist_security.setText(str(sec_type or "OPEN").upper()) + self.netlist_strength.setText( + f"{signal_strength}%" + if signal_strength and signal_strength != -1 + else "--" + ) + self._last_displayed_ssid = current_ssid + else: + self._clear_network_display() - Emitted Signals: - - add_network_confirmation(pyqtSignal): Signal with a dict that contains the result of adding a new network to the machine. + @QtCore.pyqtSlot(str, name="delete-network") + def _delete_network(self, ssid: str) -> None: + """Delete a network.""" + try: + self._sdbus_network.delete_network(ssid=ssid) + except Exception as e: + logger.error("Failed to delete network %s: %s", ssid, e) + self._show_error_popup("Failed to delete network") - """ - # Check if a password was inserted + @QtCore.pyqtSlot(name="rescan-networks") + def _rescan_networks(self) -> None: + """Trigger network rescan.""" + self._sdbus_network.rescan_networks() + + @QtCore.pyqtSlot(name="add-network") + def _add_network(self) -> None: + """Add a new network.""" + self.add_network_validation_button.setEnabled(False) + self.add_network_validation_button.update() + + password = self.add_network_password_field.text() + ssid = self.add_network_network_label.text() + + if not password and not self._current_network_is_open: + self._show_error_popup("Password field cannot be empty.") + self.add_network_validation_button.setEnabled(True) + return - self.panel.add_network_validation_button.setEnabled(False) - self.panel.add_network_validation_button.repaint() + result = self._sdbus_network.add_wifi_network(ssid=ssid, psk=password) + self.add_network_password_field.clear() - if not self.panel.add_network_password_field.text(): - self.popup.new_message( - message_type=Popup.MessageType.ERROR, - message="Password field cannot be empty.", - ) + if result is None: + self._handle_failed_network_add("Failed to add network") return - _network_psk = self.panel.add_network_password_field.text() - result = self.sdbus_network.add_wifi_network( - ssid=self.panel.add_network_network_label.text(), psk=_network_psk - ) + error_msg = result.get("error", "") if isinstance(result, dict) else "" - error_msg = result.get("error", "") - self.panel.add_network_password_field.clear() if not error_msg: - # Assume it was a success - QtCore.QTimer().singleShot(5000, self.network_list_worker.build) - QtCore.QTimer().singleShot( - 5000, - lambda: self.sdbus_network.connect_network( - self.panel.add_network_network_label.text() - ), + self._handle_successful_network_add(ssid) + else: + self._handle_failed_network_add(error_msg) + + def _handle_successful_network_add(self, ssid: str) -> None: + """Handle successful network addition.""" + self._target_ssid = ssid + self._set_loading_state(True) + self.setCurrentIndex(self.indexOf(self.main_network_page)) + + wifi_btn = self.wifi_button.toggle_button + hotspot_btn = self.hotspot_button.toggle_button + with QtCore.QSignalBlocker(wifi_btn), QtCore.QSignalBlocker(hotspot_btn): + wifi_btn.state = wifi_btn.State.ON + hotspot_btn.state = hotspot_btn.State.OFF + + self._schedule_delayed_action( + self._network_list_worker.build, NETWORK_CONNECT_DELAY_MS + ) + + def connect_and_refresh(): + try: + self._sdbus_network.connect_network(ssid) + except Exception as e: + logger.error("Failed to connect to %s: %s", ssid, e) + self._show_error_popup(f"Failed to connect to {ssid}") + self._set_loading_state(False) + + QtCore.QTimer.singleShot(NETWORK_CONNECT_DELAY_MS, connect_and_refresh) + + self.add_network_validation_button.setEnabled(True) + self.wifi_button.setEnabled(False) + self.hotspot_button.setEnabled(False) + self.add_network_validation_button.update() + + def _handle_failed_network_add(self, error_msg: str) -> None: + """Handle failed network addition.""" + logging.error(error_msg) + error_messages = { + "Invalid password": "Invalid password. Please try again", + "Network connection properties error": ( + "Network connection properties error. Please try again" + ), + "Permission Denied": "Permission Denied. Please try again", + } + + message = error_messages.get( + error_msg, "Error while adding network. Please try again" + ) + + self.add_network_validation_button.setEnabled(True) + self.add_network_validation_button.update() + self._show_error_popup(message) + + def _on_save_network_settings(self) -> None: + """Save network settings.""" + self._update_network( + ssid=self.saved_connection_network_name.text(), + password=self.saved_connection_change_password_field.text(), + new_ssid=None, + ) + + def _update_network( + self, + ssid: str, + password: Optional[str], + new_ssid: Optional[str], + ) -> None: + """Update network settings.""" + if not self._sdbus_network.is_known(ssid): + return + + priority = self._get_selected_priority() + + try: + self._sdbus_network.update_connection_settings( + ssid=ssid, password=password, new_ssid=new_ssid, priority=priority ) - self.info_box_load(True) - self.setCurrentIndex(self.indexOf(self.panel.main_network_page)) - self.panel.add_network_validation_button.setEnabled(True) + except Exception as e: + logger.error("Failed to update network settings: %s", e) + self._show_error_popup("Failed to update network settings") - self.panel.wifi_button.setEnabled(False) - self.panel.hotspot_button.setEnabled(False) + self.setCurrentIndex(self.indexOf(self.network_list_page)) - self.panel.add_network_validation_button.repaint() - return + def _get_selected_priority(self) -> int: + """Get selected priority from radio buttons.""" + checked_btn = self.priority_btn_group.checkedButton() - if error_msg == "Invalid password": - message = "Invalid password. Please try again" - elif error_msg == "Network connection properties error": - message = "Network connection properties error. Please try again" - elif error_msg == "Permission Denied": - message = "Permission Denied. Please try again" + if checked_btn == self.high_priority_btn: + return PRIORITY_HIGH + elif checked_btn == self.low_priority_btn: + return PRIORITY_LOW else: - message = "Error while adding network. Please try again" - self.panel.add_network_validation_button.setEnabled(True) - self.panel.add_network_validation_button.repaint() - self.popup.new_message(message_type=Popup.MessageType.ERROR, message=message) + return PRIORITY_MEDIUM - @QtCore.pyqtSlot(QtWidgets.QListWidgetItem, name="ssid_item_clicked") - def ssid_item_clicked(self, item: QtWidgets.QListWidgetItem) -> None: - """Handles when a network is clicked on the QListWidget. + def _on_saved_wifi_option_selected(self) -> None: + """Handle saved wifi option selection.""" + sender = self.sender() - Args: - item (QListWidgetItem): The list entry that was clicked - """ - _current_item: QtWidgets.QWidget = ( - self.panel.network_list_widget.itemWidget(item) # type: ignore - ) - if _current_item: - _current_ssid_name = _current_item.findChild(QtWidgets.QLabel).text() - - if ( - _current_ssid_name in self.sdbus_network.get_saved_ssid_names() - ): # Network already saved go to the information page - self.setCurrentIndex(self.indexOf(self.panel.saved_connection_page)) - self.panel.saved_connection_network_name.setText( - str(_current_ssid_name) - ) - else: # Network not saved go to the add network page - self.setCurrentIndex(self.indexOf(self.panel.add_network_page)) - self.panel.add_network_network_label.setText( - str(_current_ssid_name) - ) # Add the network name to the title + wifi_toggle = self.wifi_button.toggle_button + hotspot_toggle = self.hotspot_button.toggle_button + + with QtCore.QSignalBlocker(wifi_toggle), QtCore.QSignalBlocker(hotspot_toggle): + wifi_toggle.state = wifi_toggle.State.ON + hotspot_toggle.state = hotspot_toggle.State.OFF - def update_network( + ssid = self.saved_connection_network_name.text() + + if sender == self.network_delete_btn: + self._handle_network_delete(ssid) + elif sender == self.network_activate_btn: + self._handle_network_activate(ssid) + + def _handle_network_delete(self, ssid: str) -> None: + """Handle network deletion.""" + try: + self._sdbus_network.delete_network(ssid) + if ssid in self._networks: + del self._networks[ssid] + self.setCurrentIndex(self.indexOf(self.network_list_page)) + self._build_model_list() + self._network_list_worker.build() + self._show_info_popup(f"Network '{ssid}' deleted") + except Exception as e: + logger.error("Failed to delete network %s: %s", ssid, e) + self._show_error_popup("Failed to delete network") + + def _handle_network_activate(self, ssid: str) -> None: + """Handle network activation.""" + self._target_ssid = ssid + # Show loading IMMEDIATELY + self._set_loading_state(True) + self.repaint() + + self.setCurrentIndex(self.indexOf(self.main_network_page)) + + try: + self._sdbus_network.connect_network(ssid) + except Exception as e: + logger.error("Failed to connect to %s: %s", ssid, e) + self._set_loading_state(False) + self._show_disconnected_message() + self._show_error_popup("Failed to connect to network") + + @QtCore.pyqtSlot(list, name="finished-network-list-build") + def _handle_network_list(self, data: List[tuple]) -> None: + """Handle network list build completion.""" + self._networks.clear() + hotspot_ssid = self._sdbus_network.hotspot_ssid + + for entry in data: + # Handle different tuple lengths + if len(entry) >= 6: + ssid, signal, status, is_open, is_saved, is_hidden = entry + elif len(entry) >= 5: + ssid, signal, status, is_open, is_saved = entry + is_hidden = self._is_hidden_ssid(ssid) + elif len(entry) >= 4: + ssid, signal, status, is_open = entry + is_saved = status in ("Active", "Saved") + is_hidden = self._is_hidden_ssid(ssid) + else: + ssid, signal, status = entry[0], entry[1], entry[2] + is_open = status == "Open" + is_saved = status in ("Active", "Saved") + is_hidden = self._is_hidden_ssid(ssid) + + if ssid == hotspot_ssid: + continue + + self._networks[ssid] = NetworkInfo( + signal=signal, + status=status, + is_open=is_open, + is_saved=is_saved, + is_hidden=is_hidden, + ) + + self._build_model_list() + + # Update main panel if connected + if self._last_displayed_ssid and self._last_displayed_ssid in self._networks: + network_info = self._networks[self._last_displayed_ssid] + self.netlist_strength.setText( + f"{network_info.signal}%" if network_info.signal != -1 else "--" + ) + + def _is_hidden_ssid(self, ssid: str) -> bool: + """Check if an SSID indicates a hidden network.""" + if ssid is None: + return True + ssid_stripped = ssid.strip() + ssid_lower = ssid_stripped.lower() + # Check for empty, unknown, or hidden indicators + return ( + ssid_stripped == "" + or ssid_lower == "unknown" + or ssid_lower == "" + or ssid_lower == "hidden" + or not ssid_stripped + ) + + def _build_model_list(self) -> None: + """Build the network list model.""" + self.listView.blockSignals(True) + self._reset_view_model() + + saved_networks = [] + unsaved_networks = [] + + for ssid, info in self._networks.items(): + if info.is_saved: + saved_networks.append((ssid, info)) + else: + unsaved_networks.append((ssid, info)) + + saved_networks.sort(key=lambda x: -x[1].signal) + unsaved_networks.sort(key=lambda x: -x[1].signal) + + for ssid, info in saved_networks: + self._add_network_entry( + ssid=ssid, + signal=info.signal, + status=info.status, + is_open=info.is_open, + is_hidden=info.is_hidden, + ) + + if saved_networks and unsaved_networks: + self._add_separator_entry() + + for ssid, info in unsaved_networks: + self._add_network_entry( + ssid=ssid, + signal=info.signal, + status=info.status, + is_open=info.is_open, + is_hidden=info.is_hidden, + ) + + # Add "Connect to Hidden Network" entry at the end + self._add_hidden_network_entry() + + self._sync_scrollbar() + self.listView.blockSignals(False) + self.listView.update() + + def _reset_view_model(self) -> None: + """Reset the view model.""" + self._model.clear() + self._entry_delegate.clear() + + def _add_separator_entry(self) -> None: + """Add a separator entry to the list.""" + item = ListItem( + text="", + left_icon=None, + right_text="", + right_icon=None, + selected=False, + allow_check=False, + _lfontsize=17, + _rfontsize=12, + height=20, + not_clickable=True, + ) + self._model.add_item(item) + + def _add_hidden_network_entry(self) -> None: + """Add a 'Connect to Hidden Network' entry at the end of the list.""" + wifi_pixmap = QtGui.QPixmap(":/network/media/btn_icons/0bar_wifi_protected.svg") + item = ListItem( + text="Connect to Hidden Network...", + left_icon=wifi_pixmap, + right_text="", + right_icon=self._right_arrow_icon, + selected=False, + allow_check=False, + _lfontsize=17, + _rfontsize=12, + height=80, + not_clickable=False, + ) + self._model.add_item(item) + + def _add_network_entry( self, ssid: str, - password: typing.Union[str, None], - new_ssid: typing.Union[str, None], + signal: int, + status: str, + is_open: bool = False, + is_hidden: bool = False, ) -> None: - """Update network information""" - if not self.sdbus_network.is_known(ssid): + """Add a network entry to the list.""" + wifi_pixmap = self._icon_provider.get_pixmap(signal=signal, status=status) + + # Skipping hidden networks + # Check both the is_hidden flag AND the ssid content + if is_hidden or self._is_hidden_ssid(ssid): + return + display_ssid = ssid + + item = ListItem( + text=display_ssid, + left_icon=wifi_pixmap, + right_text=status, + right_icon=self._right_arrow_icon, + selected=False, + allow_check=False, + _lfontsize=17, + _rfontsize=12, + height=80, + not_clickable=False, # All entries are clickable + ) + self._model.add_item(item) + + @QtCore.pyqtSlot(ListItem, name="ssid-item-clicked") + def _on_ssid_item_clicked(self, item: ListItem) -> None: + """Handle network item click.""" + ssid = item.text + + # Handle hidden network entries - check for various hidden indicators + if ( + self._is_hidden_ssid(ssid) + or ssid == "Hidden Network" + or ssid == "Connect to Hidden Network..." + ): + self.setCurrentIndex(self.indexOf(self.hidden_network_page)) + return + + network_info = self._networks.get(ssid) + if network_info is None: + # Also check if it might be a hidden network in the _networks dict + # Hidden networks might have empty or UNKNOWN as key + for key, info in self._networks.items(): + if info.is_hidden: + self.setCurrentIndex(self.indexOf(self.hidden_network_page)) + return return - checked_btn = self.panel.priority_btn_group.checkedButton() - if checked_btn == self.panel.high_priority_btn: - priority = 90 - elif checked_btn == self.panel.low_priority_btn: - priority = 20 + if network_info.is_saved: + saved_networks = self._sdbus_network.get_saved_networks_with_for() + self._show_saved_network_page(ssid, saved_networks) else: - priority = 50 + self._show_add_network_page(ssid, is_open=network_info.is_open) - self.sdbus_network.update_connection_settings( - ssid=ssid, password=password, new_ssid=new_ssid, priority=priority - ) - QtCore.QTimer().singleShot(10000, lambda: self.network_list_worker.build()) - self.setCurrentIndex(self.indexOf(self.panel.network_list_page)) + def _show_saved_network_page(self, ssid: str, saved_networks: List[Dict]) -> None: + """Show the saved network page.""" + self.saved_connection_network_name.setText(str(ssid)) + self.snd_name.setText(str(ssid)) + self._current_network_ssid = ssid # Track for priority lookup - @QtCore.pyqtSlot(list, name="finished-network-list-build") - def handle_network_list(self, data: typing.List[typing.Tuple]) -> None: - """Handle available network list update""" - scroll_bar_position = self.network_list_widget.verticalScrollBar().value() - self.network_list_widget.blockSignals(True) - self.network_list_widget.clear() - self.network_list_widget.setSpacing(35) - for entry in data: - if entry == "separator": - self.separator_item() - continue - elif entry == "blank": - self.blank_space_item() - continue - if entry[0] == self.sdbus_network.hotspot_ssid: - continue - self.network_button_item(*entry) + # Fetch priority from get_saved_networks() which includes priority + # get_saved_networks_with_for() does NOT include priority field + priority = None + try: + full_saved_networks = self._sdbus_network.get_saved_networks() + if full_saved_networks: + for net in full_saved_networks: + if net.get("ssid") == ssid: + priority = net.get("priority") + logger.debug("Found priority %s for network %s", priority, ssid) + break + except Exception as e: + logger.error("Failed to get priority for %s: %s", ssid, e) - max_v = self.network_list_widget.verticalScrollBar().maximum() - if scroll_bar_position > max_v: - self.network_list_widget.verticalScrollBar().setValue(max_v) - else: - self.network_list_widget.verticalScrollBar().setValue(scroll_bar_position) - self.network_list_widget.verticalScrollBar().update() - self.evaluate_network_state() - QtCore.QTimer().singleShot(10000, lambda: self.network_list_worker.build()) - - def handle_button_click(self, ssid: str): - """Handles pressing a network""" - _saved_ssids = self.sdbus_network.get_saved_networks() - if any(item["ssid"] == ssid for item in _saved_ssids): - self.setCurrentIndex(self.indexOf(self.panel.saved_connection_page)) - self.panel.saved_connection_network_name.setText(str(ssid)) - self.panel.snd_name.setText(str(ssid)) - - # find the entry for this SSID - entry = next((item for item in _saved_ssids if item["ssid"] == ssid), None) - - logger.debug(_saved_ssids) - - if entry is not None: - priority = entry.get("priority") - - if priority == 90: - self.panel.high_priority_btn.setChecked(True) - elif priority == 20: - self.panel.low_priority_btn.setChecked(True) - else: - self.panel.med_priority_btn.setChecked(True) - _curr_ssid = self.sdbus_network.get_current_ssid() - if _curr_ssid != str(ssid): - self.panel.network_activate_btn.setDisabled(False) - self.panel.sn_info.setText("Saved Network") - else: - self.panel.network_activate_btn.setDisabled(True) - self.panel.sn_info.setText("Active Network") + self._set_priority_button(priority) - self.panel.frame.repaint() + network_info = self._networks.get(ssid) + if network_info: + signal_text = ( + f"{network_info.signal}%" if network_info.signal >= 0 else "--%" + ) + self.saved_connection_signal_strength_info_frame.setText(signal_text) + if network_info.is_open: + self.saved_connection_security_type_info_label.setText("OPEN") + else: + sec_type = self._sdbus_network.get_security_type_by_ssid(ssid) + self.saved_connection_security_type_info_label.setText( + str(sec_type or "WPA").upper() + ) else: - self.setCurrentIndex(self.indexOf(self.panel.add_network_page)) - self.panel.add_network_network_label.setText(str(ssid)) + self.saved_connection_signal_strength_info_frame.setText("--%") + self.saved_connection_security_type_info_label.setText("--") - def event(self, event: QtCore.QEvent) -> bool: - """Receives PyQt eEvents, this method is reimplemented from the QEvent class + current_ssid = self._sdbus_network.get_current_ssid() + if current_ssid != ssid: + self.network_activate_btn.setDisabled(False) + self.sn_info.setText("Saved Network") + else: + self.network_activate_btn.setDisabled(True) + self.sn_info.setText("Active Network") - Args: - event (QtCore.QEvent) + self.setCurrentIndex(self.indexOf(self.saved_connection_page)) + self.frame.repaint() - Returns: - bool: Event has been handled or not 1 - """ - if event.type() == QtCore.QEvent.Type.ApplicationActivated: - # Request a networks scan right at the start of the application - self.request_network_scan.emit() - return False - return super().event(event) - - def setCurrentIndex(self, index: int): - """Re-implementation of the QStackedWidget setCurrentIndex method - in order to clear and display text as needed for each panel on the StackedWidget - Args: - index (int): The index we want to change to + def _set_priority_button(self, priority: Optional[int]) -> None: + """Set the priority button based on value. + Block signals while setting to prevent unwanted triggers. """ + # Block signals to prevent any side effects + with ( + QtCore.QSignalBlocker(self.high_priority_btn), + QtCore.QSignalBlocker(self.med_priority_btn), + QtCore.QSignalBlocker(self.low_priority_btn), + ): + # Uncheck all first + self.high_priority_btn.setChecked(False) + self.med_priority_btn.setChecked(False) + self.low_priority_btn.setChecked(False) + + # Then check the correct one + if priority is not None: + if priority >= PRIORITY_HIGH: + self.high_priority_btn.setChecked(True) + elif priority <= PRIORITY_LOW: + self.low_priority_btn.setChecked(True) + else: + self.med_priority_btn.setChecked(True) + else: + # Default to medium if no priority set + self.med_priority_btn.setChecked(True) + + def _show_add_network_page(self, ssid: str, is_open: bool = False) -> None: + """Show the add network page.""" + self._current_network_is_open = is_open + self._current_network_is_hidden = False + self.add_network_network_label.setText(str(ssid)) + self.setCurrentIndex(self.indexOf(self.add_network_page)) + + def _handle_scrollbar_change(self, value: int) -> None: + """Handle scrollbar value change.""" + self.verticalScrollBar.blockSignals(True) + self.verticalScrollBar.setValue(value) + self.verticalScrollBar.blockSignals(False) + + def _sync_scrollbar(self) -> None: + """Synchronize scrollbar with list view.""" + list_scrollbar = self.listView.verticalScrollBar() + self.verticalScrollBar.setMinimum(list_scrollbar.minimum()) + self.verticalScrollBar.setMaximum(list_scrollbar.maximum()) + self.verticalScrollBar.setPageStep(list_scrollbar.pageStep()) + + def _schedule_delayed_action(self, callback: Callable, delay_ms: int) -> None: + """Schedule a delayed action.""" + try: + self._delayed_action_timer.timeout.disconnect() + except TypeError: + pass + + self._delayed_action_timer.timeout.connect(callback) + self._delayed_action_timer.start(delay_ms) + + def close(self) -> bool: + """Close the window.""" + self._network_list_worker.stop_polling() + self._sdbus_network.close() + return super().close() + + def setCurrentIndex(self, index: int) -> None: + """Set the current page index.""" if not self.isVisible(): return - _cur = self.currentIndex() - if index == self.indexOf(self.panel.add_network_page): # Add network page 2 - self.panel.add_network_password_field.clear() - self.panel.add_network_password_field.setPlaceholderText( - "Insert password here, press enter when finished." - ) - elif index == self.indexOf( - self.panel.saved_connection_page - ): # Network information page 3 - self.panel.saved_connection_change_password_field.clear() - self.panel.saved_connection_change_password_field.setPlaceholderText( - "Change network password" - ) - _security_type = self.sdbus_network.get_security_type_by_ssid( - ssid=self.panel.saved_connection_network_name.text() - ) - if not _security_type: - _security_type = "--" - self.panel.saved_connection_security_type_info_label.setText( - str(_security_type) - ) - _signal = self.sdbus_network.get_connection_signal_by_ssid( - self.panel.saved_connection_network_name.text() - ) - if _signal == -1: - _signal = "--" - _signal_string = f"{_signal}%" - self.panel.saved_connection_signal_strength_info_frame.setText( - _signal_string - ) - self.update() + + if index == self.indexOf(self.add_network_page): + self._setup_add_network_page_state() + elif index == self.indexOf(self.saved_connection_page): + self._setup_saved_connection_page_state() + + self.repaint() super().setCurrentIndex(index) - def setProperty(self, name: str, value: typing.Any) -> bool: - """setProperty-> Intercept the set property method + def _setup_add_network_page_state(self) -> None: + """Setup add network page state.""" + self.add_network_password_field.clear() - Args: - name (str): Name of the dynamic property - value (typing.Any): Value for the dynamic property + if self._current_network_is_open: + self.frame_2.setVisible(False) + self.add_network_validation_button.setText("Connect") + else: + self.frame_2.setVisible(True) + self.add_network_password_field.setPlaceholderText( + "Insert password here, press enter when finished." + ) + self.add_network_validation_button.setText("Activate") - Returns: - bool: Returns to the super class - """ + def _setup_saved_connection_page_state(self) -> None: + """Setup saved connection page state.""" + self.saved_connection_change_password_field.clear() + self.saved_connection_change_password_field.setPlaceholderText( + "Change network password" + ) + + def setProperty(self, name: str, value: Any) -> bool: + """Set a property value.""" if name == "backgroundPixmap": - self.background = value + self._background = value return super().setProperty(name, value) @QtCore.pyqtSlot(name="call-network-panel") - def show_network_panel( - self, - ) -> None: - """Slot for displaying networkWindow Panel""" + def show_network_panel(self) -> None: + """Show the network panel.""" if not self.parent(): return - self.setCurrentIndex(self.indexOf(self.panel.network_list_page)) - _parent_size = self.parent().size() # type: ignore - self.setGeometry(0, 0, _parent_size.width(), _parent_size.height()) + + self.setCurrentIndex(self.indexOf(self.network_list_page)) + parent_size = self.parent().size() + self.setGeometry(0, 0, parent_size.width(), parent_size.height()) self.updateGeometry() - self.update() + self.repaint() self.show() - - def build_network_list(self) -> None: - """Build available/saved network list""" - palette = QtGui.QPalette() - brush = QtGui.QBrush(QtGui.QColor(0, 0, 0, 0)) - brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern) - palette.setBrush( - QtGui.QPalette.ColorGroup.Active, - QtGui.QPalette.ColorRole.Button, - brush, - ) - brush = QtGui.QBrush(QtGui.QColor(0, 0, 0)) - brush.setStyle(QtCore.Qt.BrushStyle.NoBrush) - palette.setBrush( - QtGui.QPalette.ColorGroup.Active, - QtGui.QPalette.ColorRole.Base, - brush, - ) - brush = QtGui.QBrush(QtGui.QColor(0, 0, 0, 0)) - brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern) - palette.setBrush( - QtGui.QPalette.ColorGroup.Active, - QtGui.QPalette.ColorRole.Window, - brush, - ) - brush = QtGui.QBrush(QtGui.QColor(0, 120, 215, 0)) - brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern) - palette.setBrush( - QtGui.QPalette.ColorGroup.Active, - QtGui.QPalette.ColorRole.Highlight, - brush, - ) - brush = QtGui.QBrush(QtGui.QColor(0, 0, 255, 0)) - brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern) - palette.setBrush( - QtGui.QPalette.ColorGroup.Active, - QtGui.QPalette.ColorRole.Link, - brush, - ) - brush = QtGui.QBrush(QtGui.QColor(0, 0, 0, 0)) - brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern) - palette.setBrush( - QtGui.QPalette.ColorGroup.Inactive, - QtGui.QPalette.ColorRole.Button, - brush, - ) - brush = QtGui.QBrush(QtGui.QColor(0, 0, 0)) - brush.setStyle(QtCore.Qt.BrushStyle.NoBrush) - palette.setBrush( - QtGui.QPalette.ColorGroup.Inactive, - QtGui.QPalette.ColorRole.Base, - brush, - ) - brush = QtGui.QBrush(QtGui.QColor(0, 0, 0, 0)) - brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern) - palette.setBrush( - QtGui.QPalette.ColorGroup.Inactive, - QtGui.QPalette.ColorRole.Window, - brush, - ) - brush = QtGui.QBrush(QtGui.QColor(0, 120, 215, 0)) - brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern) - palette.setBrush( - QtGui.QPalette.ColorGroup.Inactive, - QtGui.QPalette.ColorRole.Highlight, - brush, - ) - brush = QtGui.QBrush(QtGui.QColor(0, 0, 255, 0)) - brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern) - palette.setBrush( - QtGui.QPalette.ColorGroup.Inactive, - QtGui.QPalette.ColorRole.Link, - brush, - ) - brush = QtGui.QBrush(QtGui.QColor(0, 0, 0, 0)) - brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern) - palette.setBrush( - QtGui.QPalette.ColorGroup.Disabled, - QtGui.QPalette.ColorRole.Button, - brush, - ) - brush = QtGui.QBrush(QtGui.QColor(0, 0, 0)) - brush.setStyle(QtCore.Qt.BrushStyle.NoBrush) - palette.setBrush( - QtGui.QPalette.ColorGroup.Disabled, - QtGui.QPalette.ColorRole.Base, - brush, - ) - brush = QtGui.QBrush(QtGui.QColor(0, 0, 0, 0)) - brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern) - palette.setBrush( - QtGui.QPalette.ColorGroup.Disabled, - QtGui.QPalette.ColorRole.Window, - brush, - ) - brush = QtGui.QBrush(QtGui.QColor(0, 120, 215, 0)) - brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern) - palette.setBrush( - QtGui.QPalette.ColorGroup.Disabled, - QtGui.QPalette.ColorRole.Highlight, - brush, - ) - brush = QtGui.QBrush(QtGui.QColor(0, 0, 255, 0)) - brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern) - palette.setBrush( - QtGui.QPalette.ColorGroup.Disabled, - QtGui.QPalette.ColorRole.Link, - brush, - ) - self.network_list_widget.setPalette(palette) - self.network_list_widget.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus) - self.network_list_widget.setStyleSheet("background-color:transparent") - self.network_list_widget.setFrameShape(QtWidgets.QFrame.Shape.NoFrame) - self.network_list_widget.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken) - self.network_list_widget.setVerticalScrollBarPolicy( - QtCore.Qt.ScrollBarPolicy.ScrollBarAlwaysOff - ) - self.network_list_widget.setHorizontalScrollBarPolicy( - QtCore.Qt.ScrollBarPolicy.ScrollBarAlwaysOff - ) - self.network_list_widget.setSizeAdjustPolicy( - QtWidgets.QAbstractScrollArea.SizeAdjustPolicy.AdjustToContents - ) - self.network_list_widget.setAutoScroll(False) - self.network_list_widget.setProperty("showDropIndicator", False) - self.network_list_widget.setDefaultDropAction(QtCore.Qt.DropAction.IgnoreAction) - self.network_list_widget.setAlternatingRowColors(False) - self.network_list_widget.setSelectionMode( - QtWidgets.QAbstractItemView.SelectionMode.NoSelection - ) - self.network_list_widget.setSelectionBehavior( - QtWidgets.QAbstractItemView.SelectionBehavior.SelectItems - ) - self.network_list_widget.setVerticalScrollMode( - QtWidgets.QAbstractItemView.ScrollMode.ScrollPerPixel - ) - self.network_list_widget.setHorizontalScrollMode( - QtWidgets.QAbstractItemView.ScrollMode.ScrollPerPixel - ) - QtWidgets.QScroller.grabGesture( - self.network_list_widget, - QtWidgets.QScroller.ScrollerGestureType.TouchGesture, - ) - QtWidgets.QScroller.grabGesture( - self.network_list_widget, - QtWidgets.QScroller.ScrollerGestureType.LeftMouseButtonGesture, - ) - - self.network_list_widget.setObjectName("network_list_widget") - self.panel.nl_content_layout.addWidget(self.network_list_widget) - - def separator_item(self) -> None: - """Add separator item to network list""" - separator_item = QtWidgets.QListWidgetItem() - separator_widget = QtWidgets.QLabel() - separator_widget.setStyleSheet( - "background-color: gray; margin: 1px 1px; min-height: 1px; max-height: 1px;" - ) - separator_item.setSizeHint(QtCore.QSize(0, 2)) # Total vertical space: 2px - self.network_list_widget.addItem(separator_item) - self.network_list_widget.setItemWidget(separator_item, separator_widget) - - def blank_space_item(self) -> None: - """Add blank space item to network list""" - spacer_item = QtWidgets.QListWidgetItem() - spacer_widget = QtWidgets.QWidget() - spacer_widget.setFixedHeight(10) # Adjust height as needed - spacer_item.setSizeHint(spacer_widget.sizeHint()) - self.network_list_widget.addItem(spacer_item) - self.network_list_widget.setItemWidget(spacer_item, spacer_widget) - - def network_button_item(self, ssid, signal, right_text, /) -> None: - """Add a network entry to network list""" - wifi_pixmap = QtGui.QPixmap(":/network/media/btn_icons/no_wifi.svg") - if 70 <= signal <= 100: - wifi_pixmap = QtGui.QPixmap(":/network/media/btn_icons/3bar_wifi.svg") - elif signal >= 40: - wifi_pixmap = QtGui.QPixmap(":/network/media/btn_icons/2bar_wifi.svg") - elif 1 < signal < 40: - wifi_pixmap = QtGui.QPixmap(":/network/media/btn_icons/1bar_wifi.svg") - - button = ListCustomButton(parent=self.network_list_widget) - button.setText(ssid) - button.setRightText(right_text) - button.setPixmap(QtGui.QPixmap(":/arrow_icons/media/btn_icons/right_arrow.svg")) - button.setSecondPixmap(wifi_pixmap) - button.setFixedHeight(80) - button.setLeftFontSize(17) - button.setRightFontSize(12) - - button.clicked.connect(lambda checked, s=ssid: self.handle_button_click(s)) - item = QtWidgets.QListWidgetItem() - item.setSizeHint(button.sizeHint()) - self.network_list_widget.addItem(item) - self.network_list_widget.setItemWidget(item, button) diff --git a/BlocksScreen/lib/panels/widgets/sensorsPanel.py b/BlocksScreen/lib/panels/widgets/sensorsPanel.py index 29eea5e7..df63cfb5 100644 --- a/BlocksScreen/lib/panels/widgets/sensorsPanel.py +++ b/BlocksScreen/lib/panels/widgets/sensorsPanel.py @@ -1,8 +1,8 @@ import typing +from lib.panels.widgets.sensorWidget import SensorWidget from lib.utils.blocks_frame import BlocksCustomFrame from lib.utils.icon_button import IconButton -from lib.panels.widgets.sensorWidget import SensorWidget from lib.utils.list_model import EntryDelegate, EntryListModel, ListItem from PyQt6 import QtCore, QtGui, QtWidgets @@ -44,16 +44,13 @@ def handle_available_fil_sensors(self, sensors: dict) -> None: if not isinstance(sensors, dict): return self.reset_view_model() - filtered_sensors = list( - filter( - lambda printer_obj: str(printer_obj).startswith( - "filament_switch_sensor" - ) - or str(printer_obj).startswith("filament_motion_sensor") - or str(printer_obj).startswith("cutter_sensor"), - sensors.keys(), + filtered_sensors = [ + sensor + for sensor in sensors.keys() + if sensor.startswith( + ("filament_switch_sensor", "filament_motion_sensor", "cutter_sensor") ) - ) + ] if filtered_sensors: self.sensor_list = [ self.create_sensor_widget(name=sensor) for sensor in filtered_sensors diff --git a/BlocksScreen/lib/ui/connectionWindow.ui b/BlocksScreen/lib/ui/connectionWindow.ui deleted file mode 100644 index f2bd4899..00000000 --- a/BlocksScreen/lib/ui/connectionWindow.ui +++ /dev/null @@ -1,938 +0,0 @@ - - - ConnectivityForm - - - Qt::WindowModal - - - - 0 - 0 - 800 - 480 - - - - - 0 - 0 - - - - - 800 - 480 - - - - - 800 - 480 - - - - Form - - - 1.000000000000000 - - - false - - - #ConnectivityForm{ -background-image: url(:/background/media/1st_background.png); -} - - - - - - - - 10 - 380 - 780 - 124 - - - - - 0 - 0 - - - - - 780 - 124 - - - - - 780 - 150 - - - - - 800 - 80 - - - - - - - - - - - - PreferAntialias - - - - true - - - false - - - - - - QFrame::NoFrame - - - QFrame::Plain - - - 0 - - - - 0 - - - 0 - - - 5 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - - 100 - 80 - - - - - 100 - 80 - - - - - 160 - 80 - - - - - - - - - 66 - 66 - 66 - - - - - - - 66 - 66 - 66 - - - - - - - 66 - 66 - 66 - - - - - - - - - 66 - 66 - 66 - - - - - - - 66 - 66 - 66 - - - - - - - 66 - 66 - 66 - - - - - - - - - 66 - 66 - 66 - - - - - - - 66 - 66 - 66 - - - - - - - 66 - 66 - 66 - - - - - - - - - false - PreferAntialias - false - - - - BlankCursor - - - true - - - Qt::NoFocus - - - Qt::NoContextMenu - - - Qt::LeftToRight - - - false - - - - - - Restart Klipper - - - - :/system_icons/media/btn_icons/restart_klipper.svg - - - - - 46 - 42 - - - - false - - - false - - - false - - - 0 - - - 0 - - - false - - - true - - - :/system/media/btn_icons/restart_klipper.svg - - - true - - - bottom - - - - 255 - 255 - 255 - - - - - - - - - 0 - 0 - - - - - 100 - 80 - - - - - 100 - 80 - - - - - 80 - 80 - - - - BlankCursor - - - true - - - Qt::NoFocus - - - Qt::NoContextMenu - - - false - - - Reboot - - - - :/system_icons/media/btn_icons/firmware_restart.svg:/system_icons/media/btn_icons/firmware_restart.svg - - - false - - - false - - - true - - - :/system/media/btn_icons/reboot.svg - - - bottom - - - - 255 - 255 - 255 - - - - true - - - - - - - - 0 - 0 - - - - - 100 - 80 - - - - - 100 - 80 - - - - - 160 - 80 - - - - BlankCursor - - - true - - - Qt::NoFocus - - - Qt::NoContextMenu - - - false - - - Firmware Restart - - - - :/system_icons/media/btn_icons/firmware_restart.svg:/system_icons/media/btn_icons/firmware_restart.svg - - - false - - - false - - - true - - - :/system/media/btn_icons/restart_firmware.svg - - - true - - - bottom - - - - 255 - 255 - 255 - - - - - - - - - 0 - 0 - - - - - 100 - 80 - - - - - 100 - 80 - - - - - 80 - 80 - - - - - - - - - - - - 13 - - - - true - - - Qt::ClickFocus - - - false - - - - - - Retry - - - - :/system_icons/media/btn_icons/retry_connection.svg:/system_icons/media/btn_icons/retry_connection.svg - - - - 16 - 16 - - - - false - - - 0 - - - 0 - - - false - - - false - - - true - - - bottom - - - :/system/media/btn_icons/restart_printer.svg - - - - 255 - 255 - 255 - - - - true - - - - - - - - 0 - 0 - - - - - 100 - 80 - - - - - 100 - 80 - - - - - 80 - 80 - - - - BlankCursor - - - true - - - Qt::NoFocus - - - Qt::NoContextMenu - - - false - - - Update page - - - - :/system_icons/media/btn_icons/firmware_restart.svg:/system_icons/media/btn_icons/firmware_restart.svg - - - false - - - false - - - true - - - :/system/media/btn_icons/update-software-icon.svg - - - bottom - - - - 255 - 255 - 255 - - - - true - - - - - - - - 0 - 0 - - - - - 100 - 80 - - - - - 100 - 80 - - - - - 80 - 80 - - - - true - - - Qt::NoFocus - - - Qt::NoContextMenu - - - false - - - Wifi Settings - - - - :/system_icons/media/btn_icons/retry_connection.svg:/system_icons/media/btn_icons/retry_connection.svg - - - false - - - false - - - true - - - system_control_btn - - - :/network/media/btn_icons/wifi_config.svg - - - true - - - bottom - - - - - - - - - 0 - 0 - 800 - 380 - - - - - 0 - 0 - - - - - 800 - 380 - - - - - 800 - 380 - - - - false - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - - - Qt::Vertical - - - QSizePolicy::Minimum - - - - 775 - 70 - - - - - - - - - 0 - 0 - - - - - 800 - 380 - - - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - - - Momcake - 17 - 75 - false - true - - - - color:white - - - - - - Qt::AutoText - - - false - - - Qt::AlignCenter - - - true - - - Qt::NoTextInteraction - - - - - - - - - IconButton - QPushButton -
lib.utils.icon_button
-
- - BlocksCustomFrame - QFrame -
lib.utils.blocks_frame
- 1 -
-
- - - - - - - -
diff --git a/BlocksScreen/lib/ui/connectionWindow_ui.py b/BlocksScreen/lib/ui/connectionWindow_ui.py deleted file mode 100644 index 772dc227..00000000 --- a/BlocksScreen/lib/ui/connectionWindow_ui.py +++ /dev/null @@ -1,338 +0,0 @@ -# Form implementation generated from reading ui file '/home/levi/BlocksScreen/BlocksScreen/lib/ui/connectionWindow.ui' -# -# Created by: PyQt6 UI code generator 6.7.1 -# -# WARNING: Any manual changes made to this file will be lost when pyuic6 is -# run again. Do not edit this file unless you know what you are doing. - - -from PyQt6 import QtCore, QtGui, QtWidgets - - -class Ui_ConnectivityForm(object): - def setupUi(self, ConnectivityForm): - ConnectivityForm.setObjectName("ConnectivityForm") - ConnectivityForm.setWindowModality(QtCore.Qt.WindowModality.WindowModal) - ConnectivityForm.resize(800, 480) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Fixed, QtWidgets.QSizePolicy.Policy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(ConnectivityForm.sizePolicy().hasHeightForWidth()) - ConnectivityForm.setSizePolicy(sizePolicy) - ConnectivityForm.setMinimumSize(QtCore.QSize(800, 480)) - ConnectivityForm.setMaximumSize(QtCore.QSize(800, 480)) - ConnectivityForm.setWindowOpacity(1.0) - ConnectivityForm.setAutoFillBackground(False) - ConnectivityForm.setStyleSheet("#ConnectivityForm{\n" -"background-image: url(:/background/media/1st_background.png);\n" -"}") - ConnectivityForm.setProperty("class", "") - self.cw_buttonFrame = BlocksCustomFrame(parent=ConnectivityForm) - self.cw_buttonFrame.setGeometry(QtCore.QRect(10, 380, 780, 124)) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.cw_buttonFrame.sizePolicy().hasHeightForWidth()) - self.cw_buttonFrame.setSizePolicy(sizePolicy) - self.cw_buttonFrame.setMinimumSize(QtCore.QSize(780, 124)) - self.cw_buttonFrame.setMaximumSize(QtCore.QSize(780, 150)) - self.cw_buttonFrame.setBaseSize(QtCore.QSize(800, 80)) - palette = QtGui.QPalette() - self.cw_buttonFrame.setPalette(palette) - font = QtGui.QFont() - font.setStyleStrategy(QtGui.QFont.StyleStrategy.PreferAntialias) - self.cw_buttonFrame.setFont(font) - self.cw_buttonFrame.setTabletTracking(True) - self.cw_buttonFrame.setAutoFillBackground(False) - self.cw_buttonFrame.setStyleSheet("") - self.cw_buttonFrame.setFrameShape(QtWidgets.QFrame.Shape.NoFrame) - self.cw_buttonFrame.setFrameShadow(QtWidgets.QFrame.Shadow.Plain) - self.cw_buttonFrame.setLineWidth(0) - self.cw_buttonFrame.setObjectName("cw_buttonFrame") - self.horizontalLayout = QtWidgets.QHBoxLayout(self.cw_buttonFrame) - self.horizontalLayout.setContentsMargins(0, 5, 0, 0) - self.horizontalLayout.setSpacing(0) - self.horizontalLayout.setObjectName("horizontalLayout") - self.RestartKlipperButton = IconButton(parent=self.cw_buttonFrame) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.MinimumExpanding, QtWidgets.QSizePolicy.Policy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.RestartKlipperButton.sizePolicy().hasHeightForWidth()) - self.RestartKlipperButton.setSizePolicy(sizePolicy) - self.RestartKlipperButton.setMinimumSize(QtCore.QSize(100, 80)) - self.RestartKlipperButton.setMaximumSize(QtCore.QSize(100, 80)) - self.RestartKlipperButton.setBaseSize(QtCore.QSize(160, 80)) - palette = QtGui.QPalette() - brush = QtGui.QBrush(QtGui.QColor(66, 66, 66)) - brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern) - palette.setBrush(QtGui.QPalette.ColorGroup.Active, QtGui.QPalette.ColorRole.WindowText, brush) - brush = QtGui.QBrush(QtGui.QColor(66, 66, 66)) - brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern) - palette.setBrush(QtGui.QPalette.ColorGroup.Active, QtGui.QPalette.ColorRole.Text, brush) - brush = QtGui.QBrush(QtGui.QColor(66, 66, 66)) - brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern) - palette.setBrush(QtGui.QPalette.ColorGroup.Active, QtGui.QPalette.ColorRole.ButtonText, brush) - brush = QtGui.QBrush(QtGui.QColor(66, 66, 66)) - brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern) - palette.setBrush(QtGui.QPalette.ColorGroup.Inactive, QtGui.QPalette.ColorRole.WindowText, brush) - brush = QtGui.QBrush(QtGui.QColor(66, 66, 66)) - brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern) - palette.setBrush(QtGui.QPalette.ColorGroup.Inactive, QtGui.QPalette.ColorRole.Text, brush) - brush = QtGui.QBrush(QtGui.QColor(66, 66, 66)) - brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern) - palette.setBrush(QtGui.QPalette.ColorGroup.Inactive, QtGui.QPalette.ColorRole.ButtonText, brush) - brush = QtGui.QBrush(QtGui.QColor(66, 66, 66)) - brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern) - palette.setBrush(QtGui.QPalette.ColorGroup.Disabled, QtGui.QPalette.ColorRole.WindowText, brush) - brush = QtGui.QBrush(QtGui.QColor(66, 66, 66)) - brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern) - palette.setBrush(QtGui.QPalette.ColorGroup.Disabled, QtGui.QPalette.ColorRole.Text, brush) - brush = QtGui.QBrush(QtGui.QColor(66, 66, 66)) - brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern) - palette.setBrush(QtGui.QPalette.ColorGroup.Disabled, QtGui.QPalette.ColorRole.ButtonText, brush) - self.RestartKlipperButton.setPalette(palette) - font = QtGui.QFont() - font.setStrikeOut(False) - font.setKerning(False) - font.setStyleStrategy(QtGui.QFont.StyleStrategy.PreferAntialias) - self.RestartKlipperButton.setFont(font) - self.RestartKlipperButton.setCursor(QtGui.QCursor(QtCore.Qt.CursorShape.BlankCursor)) - self.RestartKlipperButton.setTabletTracking(True) - self.RestartKlipperButton.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus) - self.RestartKlipperButton.setContextMenuPolicy(QtCore.Qt.ContextMenuPolicy.NoContextMenu) - self.RestartKlipperButton.setLayoutDirection(QtCore.Qt.LayoutDirection.LeftToRight) - self.RestartKlipperButton.setAutoFillBackground(False) - self.RestartKlipperButton.setStyleSheet("") - icon = QtGui.QIcon() - icon.addPixmap(QtGui.QPixmap(":/system_icons/media/btn_icons/restart_klipper.svg"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.On) - self.RestartKlipperButton.setIcon(icon) - self.RestartKlipperButton.setIconSize(QtCore.QSize(46, 42)) - self.RestartKlipperButton.setCheckable(False) - self.RestartKlipperButton.setAutoRepeat(False) - self.RestartKlipperButton.setAutoExclusive(False) - self.RestartKlipperButton.setAutoRepeatDelay(0) - self.RestartKlipperButton.setAutoRepeatInterval(0) - self.RestartKlipperButton.setAutoDefault(False) - self.RestartKlipperButton.setFlat(True) - self.RestartKlipperButton.setProperty("icon_pixmap", QtGui.QPixmap(":/system/media/btn_icons/restart_klipper.svg")) - self.RestartKlipperButton.setProperty("has_text", True) - self.RestartKlipperButton.setProperty("text_color", QtGui.QColor(255, 255, 255)) - self.RestartKlipperButton.setObjectName("RestartKlipperButton") - self.horizontalLayout.addWidget(self.RestartKlipperButton, 0, QtCore.Qt.AlignmentFlag.AlignHCenter|QtCore.Qt.AlignmentFlag.AlignTop) - self.RebootSystemButton = IconButton(parent=self.cw_buttonFrame) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.MinimumExpanding, QtWidgets.QSizePolicy.Policy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.RebootSystemButton.sizePolicy().hasHeightForWidth()) - self.RebootSystemButton.setSizePolicy(sizePolicy) - self.RebootSystemButton.setMinimumSize(QtCore.QSize(100, 80)) - self.RebootSystemButton.setMaximumSize(QtCore.QSize(100, 80)) - self.RebootSystemButton.setBaseSize(QtCore.QSize(80, 80)) - self.RebootSystemButton.setCursor(QtGui.QCursor(QtCore.Qt.CursorShape.BlankCursor)) - self.RebootSystemButton.setTabletTracking(True) - self.RebootSystemButton.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus) - self.RebootSystemButton.setContextMenuPolicy(QtCore.Qt.ContextMenuPolicy.NoContextMenu) - self.RebootSystemButton.setAutoFillBackground(False) - icon1 = QtGui.QIcon() - icon1.addPixmap(QtGui.QPixmap(":/system_icons/media/btn_icons/firmware_restart.svg"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off) - self.RebootSystemButton.setIcon(icon1) - self.RebootSystemButton.setAutoDefault(False) - self.RebootSystemButton.setDefault(False) - self.RebootSystemButton.setFlat(True) - self.RebootSystemButton.setProperty("icon_pixmap", QtGui.QPixmap(":/system/media/btn_icons/reboot.svg")) - self.RebootSystemButton.setProperty("text_color", QtGui.QColor(255, 255, 255)) - self.RebootSystemButton.setProperty("has_text", True) - self.RebootSystemButton.setObjectName("RebootSystemButton") - self.horizontalLayout.addWidget(self.RebootSystemButton, 0, QtCore.Qt.AlignmentFlag.AlignHCenter|QtCore.Qt.AlignmentFlag.AlignTop) - self.FirmwareRestartButton = IconButton(parent=self.cw_buttonFrame) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.MinimumExpanding, QtWidgets.QSizePolicy.Policy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.FirmwareRestartButton.sizePolicy().hasHeightForWidth()) - self.FirmwareRestartButton.setSizePolicy(sizePolicy) - self.FirmwareRestartButton.setMinimumSize(QtCore.QSize(100, 80)) - self.FirmwareRestartButton.setMaximumSize(QtCore.QSize(100, 80)) - self.FirmwareRestartButton.setBaseSize(QtCore.QSize(160, 80)) - self.FirmwareRestartButton.setCursor(QtGui.QCursor(QtCore.Qt.CursorShape.BlankCursor)) - self.FirmwareRestartButton.setTabletTracking(True) - self.FirmwareRestartButton.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus) - self.FirmwareRestartButton.setContextMenuPolicy(QtCore.Qt.ContextMenuPolicy.NoContextMenu) - self.FirmwareRestartButton.setAutoFillBackground(False) - self.FirmwareRestartButton.setIcon(icon1) - self.FirmwareRestartButton.setAutoDefault(False) - self.FirmwareRestartButton.setDefault(False) - self.FirmwareRestartButton.setFlat(True) - self.FirmwareRestartButton.setProperty("icon_pixmap", QtGui.QPixmap(":/system/media/btn_icons/restart_firmware.svg")) - self.FirmwareRestartButton.setProperty("has_text", True) - self.FirmwareRestartButton.setProperty("text_color", QtGui.QColor(255, 255, 255)) - self.FirmwareRestartButton.setObjectName("FirmwareRestartButton") - self.horizontalLayout.addWidget(self.FirmwareRestartButton, 0, QtCore.Qt.AlignmentFlag.AlignHCenter|QtCore.Qt.AlignmentFlag.AlignTop) - self.RetryConnectionButton = IconButton(parent=self.cw_buttonFrame) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.MinimumExpanding, QtWidgets.QSizePolicy.Policy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.RetryConnectionButton.sizePolicy().hasHeightForWidth()) - self.RetryConnectionButton.setSizePolicy(sizePolicy) - self.RetryConnectionButton.setMinimumSize(QtCore.QSize(100, 80)) - self.RetryConnectionButton.setMaximumSize(QtCore.QSize(100, 80)) - self.RetryConnectionButton.setBaseSize(QtCore.QSize(80, 80)) - palette = QtGui.QPalette() - self.RetryConnectionButton.setPalette(palette) - font = QtGui.QFont() - font.setPointSize(13) - self.RetryConnectionButton.setFont(font) - self.RetryConnectionButton.setTabletTracking(True) - self.RetryConnectionButton.setFocusPolicy(QtCore.Qt.FocusPolicy.ClickFocus) - self.RetryConnectionButton.setAutoFillBackground(False) - self.RetryConnectionButton.setStyleSheet("") - icon2 = QtGui.QIcon() - icon2.addPixmap(QtGui.QPixmap(":/system_icons/media/btn_icons/retry_connection.svg"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off) - self.RetryConnectionButton.setIcon(icon2) - self.RetryConnectionButton.setIconSize(QtCore.QSize(16, 16)) - self.RetryConnectionButton.setCheckable(False) - self.RetryConnectionButton.setAutoRepeatDelay(0) - self.RetryConnectionButton.setAutoRepeatInterval(0) - self.RetryConnectionButton.setAutoDefault(False) - self.RetryConnectionButton.setDefault(False) - self.RetryConnectionButton.setFlat(True) - self.RetryConnectionButton.setProperty("icon_pixmap", QtGui.QPixmap(":/system/media/btn_icons/restart_printer.svg")) - self.RetryConnectionButton.setProperty("text_color", QtGui.QColor(255, 255, 255)) - self.RetryConnectionButton.setProperty("has_text", True) - self.RetryConnectionButton.setObjectName("RetryConnectionButton") - self.horizontalLayout.addWidget(self.RetryConnectionButton, 0, QtCore.Qt.AlignmentFlag.AlignHCenter|QtCore.Qt.AlignmentFlag.AlignTop) - self.updatepageButton = IconButton(parent=self.cw_buttonFrame) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.MinimumExpanding, QtWidgets.QSizePolicy.Policy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.updatepageButton.sizePolicy().hasHeightForWidth()) - self.updatepageButton.setSizePolicy(sizePolicy) - self.updatepageButton.setMinimumSize(QtCore.QSize(100, 80)) - self.updatepageButton.setMaximumSize(QtCore.QSize(100, 80)) - self.updatepageButton.setBaseSize(QtCore.QSize(80, 80)) - self.updatepageButton.setCursor(QtGui.QCursor(QtCore.Qt.CursorShape.BlankCursor)) - self.updatepageButton.setTabletTracking(True) - self.updatepageButton.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus) - self.updatepageButton.setContextMenuPolicy(QtCore.Qt.ContextMenuPolicy.NoContextMenu) - self.updatepageButton.setAutoFillBackground(False) - self.updatepageButton.setIcon(icon1) - self.updatepageButton.setAutoDefault(False) - self.updatepageButton.setDefault(False) - self.updatepageButton.setFlat(True) - self.updatepageButton.setProperty("icon_pixmap", QtGui.QPixmap(":/system/media/btn_icons/update-software-icon.svg")) - self.updatepageButton.setProperty("text_color", QtGui.QColor(255, 255, 255)) - self.updatepageButton.setProperty("has_text", True) - self.updatepageButton.setObjectName("updatepageButton") - self.horizontalLayout.addWidget(self.updatepageButton, 0, QtCore.Qt.AlignmentFlag.AlignHCenter|QtCore.Qt.AlignmentFlag.AlignTop) - self.wifi_button = IconButton(parent=self.cw_buttonFrame) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.MinimumExpanding, QtWidgets.QSizePolicy.Policy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.wifi_button.sizePolicy().hasHeightForWidth()) - self.wifi_button.setSizePolicy(sizePolicy) - self.wifi_button.setMinimumSize(QtCore.QSize(100, 80)) - self.wifi_button.setMaximumSize(QtCore.QSize(100, 80)) - self.wifi_button.setBaseSize(QtCore.QSize(80, 80)) - self.wifi_button.setTabletTracking(True) - self.wifi_button.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus) - self.wifi_button.setContextMenuPolicy(QtCore.Qt.ContextMenuPolicy.NoContextMenu) - self.wifi_button.setAutoFillBackground(False) - self.wifi_button.setIcon(icon2) - self.wifi_button.setAutoDefault(False) - self.wifi_button.setDefault(False) - self.wifi_button.setFlat(True) - self.wifi_button.setProperty("icon_pixmap", QtGui.QPixmap(":/network/media/btn_icons/wifi_config.svg")) - self.wifi_button.setProperty("has_text", True) - self.wifi_button.setObjectName("wifi_button") - self.horizontalLayout.addWidget(self.wifi_button, 0, QtCore.Qt.AlignmentFlag.AlignHCenter|QtCore.Qt.AlignmentFlag.AlignTop) - self.cw_Frame = QtWidgets.QFrame(parent=ConnectivityForm) - self.cw_Frame.setGeometry(QtCore.QRect(0, 0, 800, 380)) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Minimum) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.cw_Frame.sizePolicy().hasHeightForWidth()) - self.cw_Frame.setSizePolicy(sizePolicy) - self.cw_Frame.setMinimumSize(QtCore.QSize(800, 380)) - self.cw_Frame.setMaximumSize(QtCore.QSize(800, 380)) - self.cw_Frame.setAutoFillBackground(False) - self.cw_Frame.setStyleSheet("") - self.cw_Frame.setFrameShape(QtWidgets.QFrame.Shape.NoFrame) - self.cw_Frame.setFrameShadow(QtWidgets.QFrame.Shadow.Raised) - self.cw_Frame.setObjectName("cw_Frame") - self.verticalLayout = QtWidgets.QVBoxLayout(self.cw_Frame) - self.verticalLayout.setObjectName("verticalLayout") - spacerItem = QtWidgets.QSpacerItem(775, 70, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Minimum) - self.verticalLayout.addItem(spacerItem) - self.connectionTextBox = QtWidgets.QLabel(parent=self.cw_Frame) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.connectionTextBox.sizePolicy().hasHeightForWidth()) - self.connectionTextBox.setSizePolicy(sizePolicy) - self.connectionTextBox.setMaximumSize(QtCore.QSize(800, 380)) - palette = QtGui.QPalette() - brush = QtGui.QBrush(QtGui.QColor(255, 255, 255)) - brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern) - palette.setBrush(QtGui.QPalette.ColorGroup.Active, QtGui.QPalette.ColorRole.WindowText, brush) - brush = QtGui.QBrush(QtGui.QColor(255, 255, 255)) - brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern) - palette.setBrush(QtGui.QPalette.ColorGroup.Active, QtGui.QPalette.ColorRole.Text, brush) - brush = QtGui.QBrush(QtGui.QColor(255, 255, 255)) - brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern) - palette.setBrush(QtGui.QPalette.ColorGroup.Active, QtGui.QPalette.ColorRole.ButtonText, brush) - brush = QtGui.QBrush(QtGui.QColor(255, 255, 255)) - brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern) - palette.setBrush(QtGui.QPalette.ColorGroup.Inactive, QtGui.QPalette.ColorRole.WindowText, brush) - brush = QtGui.QBrush(QtGui.QColor(255, 255, 255)) - brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern) - palette.setBrush(QtGui.QPalette.ColorGroup.Inactive, QtGui.QPalette.ColorRole.Text, brush) - brush = QtGui.QBrush(QtGui.QColor(255, 255, 255)) - brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern) - palette.setBrush(QtGui.QPalette.ColorGroup.Inactive, QtGui.QPalette.ColorRole.ButtonText, brush) - brush = QtGui.QBrush(QtGui.QColor(255, 255, 255)) - brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern) - palette.setBrush(QtGui.QPalette.ColorGroup.Disabled, QtGui.QPalette.ColorRole.WindowText, brush) - brush = QtGui.QBrush(QtGui.QColor(255, 255, 255)) - brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern) - palette.setBrush(QtGui.QPalette.ColorGroup.Disabled, QtGui.QPalette.ColorRole.Text, brush) - brush = QtGui.QBrush(QtGui.QColor(255, 255, 255)) - brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern) - palette.setBrush(QtGui.QPalette.ColorGroup.Disabled, QtGui.QPalette.ColorRole.ButtonText, brush) - self.connectionTextBox.setPalette(palette) - font = QtGui.QFont() - font.setFamily("Momcake") - font.setPointSize(17) - font.setBold(True) - font.setItalic(False) - font.setWeight(75) - self.connectionTextBox.setFont(font) - self.connectionTextBox.setStyleSheet("color:white") - self.connectionTextBox.setText("") - self.connectionTextBox.setTextFormat(QtCore.Qt.TextFormat.AutoText) - self.connectionTextBox.setScaledContents(False) - self.connectionTextBox.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) - self.connectionTextBox.setWordWrap(True) - self.connectionTextBox.setTextInteractionFlags(QtCore.Qt.TextInteractionFlag.NoTextInteraction) - self.connectionTextBox.setObjectName("connectionTextBox") - self.verticalLayout.addWidget(self.connectionTextBox) - - self.retranslateUi(ConnectivityForm) - QtCore.QMetaObject.connectSlotsByName(ConnectivityForm) - - def retranslateUi(self, ConnectivityForm): - _translate = QtCore.QCoreApplication.translate - ConnectivityForm.setWindowTitle(_translate("ConnectivityForm", "Form")) - self.RestartKlipperButton.setText(_translate("ConnectivityForm", "Restart Klipper")) - self.RestartKlipperButton.setProperty("text_formatting", _translate("ConnectivityForm", "bottom")) - self.RebootSystemButton.setText(_translate("ConnectivityForm", "Reboot")) - self.RebootSystemButton.setProperty("text_formatting", _translate("ConnectivityForm", "bottom")) - self.FirmwareRestartButton.setText(_translate("ConnectivityForm", "Firmware Restart")) - self.FirmwareRestartButton.setProperty("text_formatting", _translate("ConnectivityForm", "bottom")) - self.RetryConnectionButton.setText(_translate("ConnectivityForm", "Retry ")) - self.RetryConnectionButton.setProperty("text_formatting", _translate("ConnectivityForm", "bottom")) - self.updatepageButton.setText(_translate("ConnectivityForm", "Update page")) - self.updatepageButton.setProperty("text_formatting", _translate("ConnectivityForm", "bottom")) - self.wifi_button.setText(_translate("ConnectivityForm", "Wifi Settings")) - self.wifi_button.setProperty("class", _translate("ConnectivityForm", "system_control_btn")) - self.wifi_button.setProperty("text_formatting", _translate("ConnectivityForm", "bottom")) -from lib.utils.blocks_frame import BlocksCustomFrame -from lib.utils.icon_button import IconButton diff --git a/BlocksScreen/lib/ui/resources/icon_resources.qrc b/BlocksScreen/lib/ui/resources/icon_resources.qrc index a62dda06..5239a1fd 100644 --- a/BlocksScreen/lib/ui/resources/icon_resources.qrc +++ b/BlocksScreen/lib/ui/resources/icon_resources.qrc @@ -1,11 +1,18 @@ - media/btn_icons/wifi_config.svg - media/btn_icons/wifi_locked.svg - media/btn_icons/wifi_unlocked.svg + media/btn_icons/0bar_wifi.svg + media/btn_icons/0bar_wifi_protected.svg media/btn_icons/1bar_wifi.svg + media/btn_icons/1bar_wifi_protected.svg media/btn_icons/2bar_wifi.svg + media/btn_icons/2bar_wifi_protected.svg media/btn_icons/3bar_wifi.svg + media/btn_icons/3bar_wifi_protected.svg + media/btn_icons/4bar_wifi.svg + media/btn_icons/4bar_wifi_protected.svg + media/btn_icons/wifi_config.svg + media/btn_icons/wifi_locked.svg + media/btn_icons/wifi_unlocked.svg media/btn_icons/hotspot.svg media/btn_icons/no_wifi.svg media/btn_icons/retry_wifi.svg diff --git a/BlocksScreen/lib/ui/resources/icon_resources_rc.py b/BlocksScreen/lib/ui/resources/icon_resources_rc.py index 9df24546..a7aca514 100644 --- a/BlocksScreen/lib/ui/resources/icon_resources_rc.py +++ b/BlocksScreen/lib/ui/resources/icon_resources_rc.py @@ -19325,7 +19325,7 @@ \x22\x32\x34\x36\x2e\x32\x38\x22\x20\x77\x69\x64\x74\x68\x3d\x22\ \x35\x32\x35\x2e\x39\x31\x22\x20\x68\x65\x69\x67\x68\x74\x3d\x22\ \x31\x30\x37\x2e\x34\x35\x22\x2f\x3e\x3c\x2f\x73\x76\x67\x3e\ -\x00\x00\x02\xa8\ +\x00\x00\x05\x95\ \x3c\ \x73\x76\x67\x20\x69\x64\x3d\x22\x4c\x61\x79\x65\x72\x5f\x31\x22\ \x20\x64\x61\x74\x61\x2d\x6e\x61\x6d\x65\x3d\x22\x4c\x61\x79\x65\ @@ -19334,42 +19334,190 @@ \x30\x30\x2f\x73\x76\x67\x22\x20\x76\x69\x65\x77\x42\x6f\x78\x3d\ \x22\x30\x20\x30\x20\x36\x30\x30\x20\x36\x30\x30\x22\x3e\x3c\x64\ \x65\x66\x73\x3e\x3c\x73\x74\x79\x6c\x65\x3e\x2e\x63\x6c\x73\x2d\ -\x31\x7b\x6f\x70\x61\x63\x69\x74\x79\x3a\x30\x2e\x37\x35\x3b\x7d\ -\x2e\x63\x6c\x73\x2d\x32\x7b\x66\x69\x6c\x6c\x3a\x23\x65\x30\x65\ -\x30\x64\x66\x3b\x7d\x3c\x2f\x73\x74\x79\x6c\x65\x3e\x3c\x2f\x64\ -\x65\x66\x73\x3e\x3c\x67\x20\x63\x6c\x61\x73\x73\x3d\x22\x63\x6c\ -\x73\x2d\x31\x22\x3e\x3c\x70\x61\x74\x68\x20\x63\x6c\x61\x73\x73\ -\x3d\x22\x63\x6c\x73\x2d\x32\x22\x20\x64\x3d\x22\x4d\x31\x38\x36\ -\x2e\x31\x38\x2c\x34\x30\x36\x2e\x32\x33\x63\x30\x2d\x39\x2e\x30\ -\x39\x2c\x33\x2e\x35\x32\x2d\x31\x36\x2e\x34\x31\x2c\x39\x2e\x34\ -\x37\x2d\x32\x32\x2e\x34\x38\x2c\x32\x33\x2e\x34\x38\x2d\x32\x34\ -\x2c\x35\x31\x2e\x30\x37\x2d\x33\x39\x2e\x32\x33\x2c\x38\x33\x2e\ -\x33\x39\x2d\x34\x33\x2e\x37\x38\x2c\x34\x37\x2e\x39\x34\x2d\x36\ -\x2e\x37\x36\x2c\x38\x39\x2e\x35\x2c\x38\x2e\x31\x33\x2c\x31\x32\ -\x34\x2e\x37\x37\x2c\x34\x33\x2e\x33\x31\x2c\x31\x30\x2c\x31\x30\ -\x2c\x31\x32\x2e\x36\x32\x2c\x32\x33\x2e\x36\x35\x2c\x37\x2e\x33\ -\x34\x2c\x33\x35\x2e\x35\x32\x2d\x38\x2e\x33\x32\x2c\x31\x38\x2e\ -\x37\x31\x2d\x33\x30\x2e\x35\x34\x2c\x32\x33\x2e\x31\x2d\x34\x34\ -\x2e\x36\x36\x2c\x38\x2e\x35\x34\x2d\x31\x33\x2e\x32\x2d\x31\x33\ -\x2e\x36\x33\x2d\x32\x38\x2e\x33\x38\x2d\x32\x33\x2e\x32\x39\x2d\ -\x34\x36\x2e\x34\x31\x2d\x32\x37\x2e\x31\x35\x2d\x33\x32\x2e\x37\ -\x34\x2d\x37\x2d\x36\x31\x2e\x34\x35\x2c\x31\x2e\x36\x33\x2d\x38\ -\x35\x2e\x36\x39\x2c\x32\x36\x2e\x33\x38\x2d\x31\x36\x2e\x34\x32\ -\x2c\x31\x36\x2e\x37\x36\x2d\x34\x31\x2e\x36\x39\x2c\x39\x2e\x38\ -\x36\x2d\x34\x37\x2e\x32\x33\x2d\x31\x33\x2e\x30\x37\x41\x36\x37\ -\x2e\x35\x36\x2c\x36\x37\x2e\x35\x36\x2c\x30\x2c\x30\x2c\x31\x2c\ -\x31\x38\x36\x2e\x31\x38\x2c\x34\x30\x36\x2e\x32\x33\x5a\x22\x2f\ +\x31\x7b\x66\x69\x6c\x6c\x3a\x23\x64\x30\x64\x32\x64\x33\x3b\x7d\ +\x2e\x63\x6c\x73\x2d\x32\x7b\x66\x69\x6c\x6c\x3a\x23\x38\x63\x63\ +\x35\x34\x30\x3b\x7d\x3c\x2f\x73\x74\x79\x6c\x65\x3e\x3c\x2f\x64\ +\x65\x66\x73\x3e\x3c\x70\x61\x74\x68\x20\x63\x6c\x61\x73\x73\x3d\ +\x22\x63\x6c\x73\x2d\x31\x22\x20\x64\x3d\x22\x4d\x32\x39\x30\x2e\ +\x31\x38\x2c\x37\x37\x43\x34\x30\x32\x2c\x37\x38\x2e\x37\x37\x2c\ +\x34\x39\x30\x2e\x31\x2c\x31\x31\x37\x2e\x30\x37\x2c\x35\x36\x33\ +\x2e\x39\x2c\x31\x39\x33\x2e\x30\x39\x63\x31\x34\x2e\x38\x2c\x31\ +\x35\x2e\x32\x35\x2c\x31\x31\x2e\x38\x31\x2c\x33\x33\x2e\x39\x33\ +\x2c\x33\x2e\x32\x36\x2c\x34\x34\x2d\x31\x31\x2e\x31\x32\x2c\x31\ +\x33\x2e\x31\x37\x2d\x32\x38\x2e\x36\x32\x2c\x31\x33\x2d\x34\x31\ +\x2e\x34\x36\x2e\x38\x33\x2d\x31\x35\x2e\x30\x38\x2d\x31\x34\x2e\ +\x32\x37\x2d\x33\x30\x2e\x32\x2d\x32\x38\x2e\x37\x31\x2d\x34\x36\ +\x2e\x36\x31\x2d\x34\x31\x2e\x31\x2d\x33\x38\x2e\x34\x2d\x32\x39\ +\x2d\x38\x31\x2e\x33\x33\x2d\x34\x36\x2e\x37\x35\x2d\x31\x32\x37\ +\x2e\x37\x35\x2d\x35\x34\x2e\x36\x35\x2d\x35\x34\x2d\x39\x2e\x31\ +\x39\x2d\x31\x30\x36\x2e\x39\x32\x2d\x34\x2e\x33\x31\x2d\x31\x35\ +\x38\x2e\x35\x2c\x31\x35\x2e\x32\x33\x2d\x34\x35\x2c\x31\x37\x2e\ +\x30\x35\x2d\x38\x34\x2e\x32\x39\x2c\x34\x33\x2e\x39\x33\x2d\x31\ +\x31\x38\x2e\x31\x36\x2c\x37\x39\x2e\x39\x33\x2d\x38\x2e\x35\x37\ +\x2c\x39\x2e\x31\x31\x2d\x31\x38\x2e\x36\x35\x2c\x31\x32\x2e\x33\ +\x32\x2d\x33\x30\x2e\x31\x39\x2c\x38\x2d\x32\x30\x2e\x30\x39\x2d\ +\x37\x2e\x35\x37\x2d\x32\x35\x2e\x32\x2d\x33\x34\x2e\x30\x39\x2d\ +\x39\x2e\x37\x34\x2d\x35\x30\x2e\x36\x31\x61\x33\x38\x30\x2c\x33\ +\x38\x30\x2c\x30\x2c\x30\x2c\x31\x2c\x35\x37\x2e\x36\x32\x2d\x35\ +\x30\x2e\x33\x38\x63\x34\x33\x2d\x33\x30\x2e\x35\x38\x2c\x38\x39\ +\x2e\x39\x33\x2d\x35\x31\x2e\x31\x2c\x31\x34\x30\x2e\x37\x37\x2d\ +\x36\x30\x2e\x34\x36\x43\x32\x35\x35\x2e\x30\x37\x2c\x37\x39\x2e\ +\x38\x37\x2c\x32\x37\x37\x2e\x34\x33\x2c\x37\x38\x2e\x34\x38\x2c\ +\x32\x39\x30\x2e\x31\x38\x2c\x37\x37\x5a\x22\x2f\x3e\x3c\x70\x61\ +\x74\x68\x20\x63\x6c\x61\x73\x73\x3d\x22\x63\x6c\x73\x2d\x31\x22\ +\x20\x64\x3d\x22\x4d\x34\x36\x39\x2e\x38\x37\x2c\x33\x33\x32\x2e\ +\x32\x31\x63\x2d\x31\x31\x2c\x2e\x31\x38\x2d\x31\x37\x2e\x38\x33\ +\x2d\x33\x2e\x30\x37\x2d\x32\x33\x2e\x35\x32\x2d\x39\x2e\x31\x32\ +\x43\x34\x31\x34\x2c\x32\x38\x38\x2e\x36\x35\x2c\x33\x37\x35\x2e\ +\x32\x32\x2c\x32\x36\x37\x2e\x31\x36\x2c\x33\x33\x30\x2e\x31\x2c\ +\x32\x36\x30\x2e\x36\x38\x63\x2d\x36\x37\x2e\x33\x37\x2d\x39\x2e\ +\x36\x37\x2d\x31\x32\x36\x2e\x31\x37\x2c\x31\x30\x2e\x38\x33\x2d\ +\x31\x37\x35\x2e\x33\x39\x2c\x36\x31\x2e\x34\x31\x2d\x31\x36\x2e\ +\x33\x35\x2c\x31\x36\x2e\x38\x2d\x34\x30\x2e\x36\x37\x2c\x31\x32\ +\x2d\x34\x37\x2e\x39\x31\x2d\x31\x30\x2d\x33\x2e\x39\x2d\x31\x31\ +\x2e\x39\x2d\x31\x2e\x33\x38\x2d\x32\x32\x2e\x38\x2c\x36\x2e\x38\ +\x39\x2d\x33\x31\x2e\x35\x32\x2c\x34\x31\x2d\x34\x33\x2e\x32\x34\ +\x2c\x38\x39\x2e\x37\x35\x2d\x37\x30\x2e\x34\x38\x2c\x31\x34\x36\ +\x2e\x38\x34\x2d\x37\x39\x2e\x32\x38\x2c\x37\x35\x2e\x36\x2d\x31\ +\x31\x2e\x36\x36\x2c\x31\x34\x33\x2e\x36\x39\x2c\x38\x2e\x30\x35\ +\x2c\x32\x30\x33\x2e\x39\x31\x2c\x35\x38\x2e\x33\x36\x61\x32\x30\ +\x35\x2e\x37\x34\x2c\x32\x30\x35\x2e\x37\x34\x2c\x30\x2c\x30\x2c\ +\x31\x2c\x32\x33\x2e\x32\x35\x2c\x32\x32\x2e\x36\x39\x63\x38\x2e\ +\x30\x38\x2c\x39\x2e\x33\x2c\x39\x2e\x35\x2c\x32\x30\x2e\x36\x32\ +\x2c\x34\x2e\x35\x39\x2c\x33\x32\x2e\x33\x34\x53\x34\x37\x38\x2e\ +\x36\x32\x2c\x33\x33\x31\x2e\x36\x36\x2c\x34\x36\x39\x2e\x38\x37\ +\x2c\x33\x33\x32\x2e\x32\x31\x5a\x22\x2f\x3e\x3c\x70\x61\x74\x68\ +\x20\x63\x6c\x61\x73\x73\x3d\x22\x63\x6c\x73\x2d\x31\x22\x20\x64\ +\x3d\x22\x4d\x31\x38\x34\x2e\x35\x32\x2c\x33\x38\x37\x2e\x34\x63\ +\x30\x2d\x39\x2e\x32\x32\x2c\x33\x2e\x35\x37\x2d\x31\x36\x2e\x36\ +\x35\x2c\x39\x2e\x36\x31\x2d\x32\x32\x2e\x38\x31\x43\x32\x31\x38\ +\x2c\x33\x34\x30\x2e\x32\x34\x2c\x32\x34\x36\x2c\x33\x32\x34\x2e\ +\x37\x38\x2c\x32\x37\x38\x2e\x37\x37\x2c\x33\x32\x30\x2e\x31\x35\ +\x63\x34\x38\x2e\x36\x36\x2d\x36\x2e\x38\x36\x2c\x39\x30\x2e\x38\ +\x33\x2c\x38\x2e\x32\x35\x2c\x31\x32\x36\x2e\x36\x33\x2c\x34\x34\ +\x2c\x31\x30\x2e\x31\x38\x2c\x31\x30\x2e\x31\x35\x2c\x31\x32\x2e\ +\x38\x31\x2c\x32\x34\x2c\x37\x2e\x34\x35\x2c\x33\x36\x2e\x30\x35\ +\x2d\x38\x2e\x34\x34\x2c\x31\x39\x2d\x33\x31\x2c\x32\x33\x2e\x34\ +\x35\x2d\x34\x35\x2e\x33\x32\x2c\x38\x2e\x36\x36\x2d\x31\x33\x2e\ +\x34\x2d\x31\x33\x2e\x38\x33\x2d\x32\x38\x2e\x38\x2d\x32\x33\x2e\ +\x36\x33\x2d\x34\x37\x2e\x31\x31\x2d\x32\x37\x2e\x35\x34\x2d\x33\ +\x33\x2e\x32\x32\x2d\x37\x2e\x31\x2d\x36\x32\x2e\x33\x36\x2c\x31\ +\x2e\x36\x35\x2d\x38\x37\x2c\x32\x36\x2e\x37\x37\x2d\x31\x36\x2e\ +\x36\x36\x2c\x31\x37\x2d\x34\x32\x2e\x33\x2c\x31\x30\x2d\x34\x37\ +\x2e\x39\x33\x2d\x31\x33\x2e\x32\x37\x41\x36\x39\x2e\x32\x38\x2c\ +\x36\x39\x2e\x32\x38\x2c\x30\x2c\x30\x2c\x31\x2c\x31\x38\x34\x2e\ +\x35\x32\x2c\x33\x38\x37\x2e\x34\x5a\x22\x2f\x3e\x3c\x70\x61\x74\ +\x68\x20\x63\x6c\x61\x73\x73\x3d\x22\x63\x6c\x73\x2d\x32\x22\x20\ +\x64\x3d\x22\x4d\x33\x30\x30\x2c\x35\x32\x33\x63\x2d\x32\x32\x2c\ +\x30\x2d\x33\x39\x2e\x31\x35\x2d\x31\x38\x2e\x34\x36\x2d\x33\x39\ +\x2e\x31\x31\x2d\x34\x32\x2e\x31\x31\x2c\x30\x2d\x32\x33\x2e\x33\ +\x38\x2c\x31\x37\x2e\x31\x39\x2d\x34\x31\x2e\x38\x33\x2c\x33\x38\ +\x2e\x38\x36\x2d\x34\x31\x2e\x38\x2c\x32\x32\x2e\x32\x35\x2c\x30\ +\x2c\x33\x39\x2e\x33\x32\x2c\x31\x38\x2e\x31\x38\x2c\x33\x39\x2e\ +\x33\x34\x2c\x34\x31\x2e\x38\x31\x53\x33\x32\x32\x2e\x31\x2c\x35\ +\x32\x33\x2c\x33\x30\x30\x2c\x35\x32\x33\x5a\x22\x2f\x3e\x3c\x2f\ +\x73\x76\x67\x3e\ +\x00\x00\x06\x23\ +\x3c\ +\x73\x76\x67\x20\x69\x64\x3d\x22\x4c\x61\x79\x65\x72\x5f\x31\x22\ +\x20\x64\x61\x74\x61\x2d\x6e\x61\x6d\x65\x3d\x22\x4c\x61\x79\x65\ +\x72\x20\x31\x22\x20\x78\x6d\x6c\x6e\x73\x3d\x22\x68\x74\x74\x70\ +\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x32\x30\ +\x30\x30\x2f\x73\x76\x67\x22\x20\x76\x69\x65\x77\x42\x6f\x78\x3d\ +\x22\x30\x20\x30\x20\x36\x30\x30\x20\x36\x30\x30\x22\x3e\x3c\x64\ +\x65\x66\x73\x3e\x3c\x73\x74\x79\x6c\x65\x3e\x2e\x63\x6c\x73\x2d\ +\x31\x7b\x66\x69\x6c\x6c\x3a\x23\x64\x30\x64\x32\x64\x33\x3b\x7d\ +\x2e\x63\x6c\x73\x2d\x32\x7b\x66\x69\x6c\x6c\x3a\x23\x35\x65\x36\ +\x30\x36\x31\x3b\x7d\x3c\x2f\x73\x74\x79\x6c\x65\x3e\x3c\x2f\x64\ +\x65\x66\x73\x3e\x3c\x70\x61\x74\x68\x20\x63\x6c\x61\x73\x73\x3d\ +\x22\x63\x6c\x73\x2d\x31\x22\x20\x64\x3d\x22\x4d\x32\x39\x30\x2e\ +\x32\x36\x2c\x39\x34\x2e\x37\x63\x31\x31\x30\x2e\x39\x34\x2c\x31\ +\x2e\x37\x37\x2c\x31\x39\x38\x2e\x33\x39\x2c\x33\x39\x2e\x37\x37\ +\x2c\x32\x37\x31\x2e\x36\x32\x2c\x31\x31\x35\x2e\x32\x31\x2c\x31\ +\x34\x2e\x36\x39\x2c\x31\x35\x2e\x31\x33\x2c\x31\x31\x2e\x37\x32\ +\x2c\x33\x33\x2e\x36\x37\x2c\x33\x2e\x32\x34\x2c\x34\x33\x2e\x37\ +\x2d\x31\x31\x2c\x31\x33\x2e\x30\x37\x2d\x32\x38\x2e\x34\x2c\x31\ +\x32\x2e\x38\x39\x2d\x34\x31\x2e\x31\x35\x2e\x38\x33\x2d\x31\x35\ +\x2d\x31\x34\x2e\x31\x36\x2d\x33\x30\x2d\x32\x38\x2e\x35\x2d\x34\ +\x36\x2e\x32\x35\x2d\x34\x30\x2e\x37\x39\x2d\x33\x38\x2e\x31\x31\ +\x2d\x32\x38\x2e\x37\x38\x2d\x38\x30\x2e\x37\x31\x2d\x34\x36\x2e\ +\x33\x39\x2d\x31\x32\x36\x2e\x37\x38\x2d\x35\x34\x2e\x32\x33\x2d\ +\x35\x33\x2e\x36\x2d\x39\x2e\x31\x32\x2d\x31\x30\x36\x2e\x30\x39\ +\x2d\x34\x2e\x32\x38\x2d\x31\x35\x37\x2e\x32\x38\x2c\x31\x35\x2e\ +\x31\x31\x43\x31\x34\x39\x2c\x31\x39\x31\x2e\x34\x35\x2c\x31\x31\ +\x30\x2c\x32\x31\x38\x2e\x31\x32\x2c\x37\x36\x2e\x34\x2c\x32\x35\ +\x33\x2e\x38\x35\x63\x2d\x38\x2e\x35\x2c\x39\x2d\x31\x38\x2e\x35\ +\x2c\x31\x32\x2e\x32\x33\x2d\x33\x30\x2c\x37\x2e\x39\x31\x2d\x31\ +\x39\x2e\x39\x33\x2d\x37\x2e\x35\x2d\x32\x35\x2d\x33\x33\x2e\x38\ +\x32\x2d\x39\x2e\x36\x36\x2d\x35\x30\x2e\x32\x31\x61\x33\x37\x37\ +\x2e\x33\x2c\x33\x37\x37\x2e\x33\x2c\x30\x2c\x30\x2c\x31\x2c\x35\ +\x37\x2e\x31\x38\x2d\x35\x30\x63\x34\x32\x2e\x37\x2d\x33\x30\x2e\ +\x33\x35\x2c\x38\x39\x2e\x32\x34\x2d\x35\x30\x2e\x37\x31\x2c\x31\ +\x33\x39\x2e\x36\x39\x2d\x36\x30\x43\x32\x35\x35\x2e\x34\x31\x2c\ +\x39\x37\x2e\x35\x35\x2c\x32\x37\x37\x2e\x36\x2c\x39\x36\x2e\x31\ +\x38\x2c\x32\x39\x30\x2e\x32\x36\x2c\x39\x34\x2e\x37\x5a\x22\x2f\ \x3e\x3c\x70\x61\x74\x68\x20\x63\x6c\x61\x73\x73\x3d\x22\x63\x6c\ -\x73\x2d\x32\x22\x20\x64\x3d\x22\x4d\x33\x30\x30\x2c\x35\x33\x39\ -\x2e\x38\x34\x63\x2d\x32\x31\x2e\x36\x37\x2c\x30\x2d\x33\x38\x2e\ -\x35\x37\x2d\x31\x38\x2e\x31\x38\x2d\x33\x38\x2e\x35\x33\x2d\x34\ -\x31\x2e\x34\x39\x2c\x30\x2d\x32\x33\x2c\x31\x36\x2e\x39\x34\x2d\ -\x34\x31\x2e\x32\x32\x2c\x33\x38\x2e\x32\x38\x2d\x34\x31\x2e\x31\ -\x38\x2c\x32\x31\x2e\x39\x33\x2c\x30\x2c\x33\x38\x2e\x37\x35\x2c\ -\x31\x37\x2e\x39\x31\x2c\x33\x38\x2e\x37\x36\x2c\x34\x31\x2e\x32\ -\x53\x33\x32\x31\x2e\x37\x36\x2c\x35\x33\x39\x2e\x38\x33\x2c\x33\ -\x30\x30\x2c\x35\x33\x39\x2e\x38\x34\x5a\x22\x2f\x3e\x3c\x2f\x67\ -\x3e\x3c\x2f\x73\x76\x67\x3e\ +\x73\x2d\x31\x22\x20\x64\x3d\x22\x4d\x34\x36\x38\x2e\x35\x31\x2c\ +\x33\x34\x38\x63\x2d\x31\x30\x2e\x39\x31\x2e\x31\x38\x2d\x31\x37\ +\x2e\x36\x39\x2d\x33\x2e\x30\x35\x2d\x32\x33\x2e\x33\x34\x2d\x39\ +\x2e\x30\x36\x2d\x33\x32\x2e\x31\x32\x2d\x33\x34\x2e\x31\x38\x2d\ +\x37\x30\x2e\x35\x39\x2d\x35\x35\x2e\x35\x2d\x31\x31\x35\x2e\x33\ +\x36\x2d\x36\x31\x2e\x39\x33\x2d\x36\x36\x2e\x38\x35\x2d\x39\x2e\ +\x35\x39\x2d\x31\x32\x35\x2e\x32\x2c\x31\x30\x2e\x37\x35\x2d\x31\ +\x37\x34\x2c\x36\x30\x2e\x39\x34\x2d\x31\x36\x2e\x32\x32\x2c\x31\ +\x36\x2e\x36\x37\x2d\x34\x30\x2e\x33\x36\x2c\x31\x31\x2e\x39\x31\ +\x2d\x34\x37\x2e\x35\x34\x2d\x31\x30\x2d\x33\x2e\x38\x38\x2d\x31\ +\x31\x2e\x38\x2d\x31\x2e\x33\x37\x2d\x32\x32\x2e\x36\x32\x2c\x36\ +\x2e\x38\x34\x2d\x33\x31\x2e\x32\x37\x2c\x34\x30\x2e\x36\x38\x2d\ +\x34\x32\x2e\x39\x31\x2c\x38\x39\x2e\x30\x36\x2d\x36\x39\x2e\x39\ +\x34\x2c\x31\x34\x35\x2e\x37\x31\x2d\x37\x38\x2e\x36\x38\x2c\x37\ +\x35\x2d\x31\x31\x2e\x35\x37\x2c\x31\x34\x32\x2e\x35\x39\x2c\x38\ +\x2c\x32\x30\x32\x2e\x33\x35\x2c\x35\x37\x2e\x39\x32\x61\x32\x30\ +\x33\x2e\x34\x37\x2c\x32\x30\x33\x2e\x34\x37\x2c\x30\x2c\x30\x2c\ +\x31\x2c\x32\x33\x2e\x30\x37\x2c\x32\x32\x2e\x35\x32\x63\x38\x2c\ +\x39\x2e\x32\x32\x2c\x39\x2e\x34\x33\x2c\x32\x30\x2e\x34\x36\x2c\ +\x34\x2e\x35\x36\x2c\x33\x32\x2e\x30\x38\x53\x34\x37\x37\x2e\x31\ +\x39\x2c\x33\x34\x37\x2e\x34\x31\x2c\x34\x36\x38\x2e\x35\x31\x2c\ +\x33\x34\x38\x5a\x22\x2f\x3e\x3c\x70\x61\x74\x68\x20\x63\x6c\x61\ +\x73\x73\x3d\x22\x63\x6c\x73\x2d\x31\x22\x20\x64\x3d\x22\x4d\x31\ +\x38\x35\x2e\x33\x36\x2c\x34\x30\x32\x2e\x37\x33\x63\x30\x2d\x39\ +\x2e\x31\x35\x2c\x33\x2e\x35\x35\x2d\x31\x36\x2e\x35\x32\x2c\x39\ +\x2e\x35\x34\x2d\x32\x32\x2e\x36\x34\x2c\x32\x33\x2e\x36\x36\x2d\ +\x32\x34\x2e\x31\x35\x2c\x35\x31\x2e\x34\x34\x2d\x33\x39\x2e\x35\ +\x2c\x38\x34\x2d\x34\x34\x2e\x30\x39\x2c\x34\x38\x2e\x32\x38\x2d\ +\x36\x2e\x38\x31\x2c\x39\x30\x2e\x31\x34\x2c\x38\x2e\x31\x39\x2c\ +\x31\x32\x35\x2e\x36\x37\x2c\x34\x33\x2e\x36\x32\x2c\x31\x30\x2e\ +\x30\x39\x2c\x31\x30\x2e\x30\x37\x2c\x31\x32\x2e\x37\x2c\x32\x33\ +\x2e\x38\x32\x2c\x37\x2e\x33\x39\x2c\x33\x35\x2e\x37\x37\x2d\x38\ +\x2e\x33\x39\x2c\x31\x38\x2e\x38\x35\x2d\x33\x30\x2e\x37\x37\x2c\ +\x32\x33\x2e\x32\x37\x2d\x34\x35\x2c\x38\x2e\x36\x2d\x31\x33\x2e\ +\x33\x2d\x31\x33\x2e\x37\x33\x2d\x32\x38\x2e\x35\x38\x2d\x32\x33\ +\x2e\x34\x35\x2d\x34\x36\x2e\x37\x35\x2d\x32\x37\x2e\x33\x33\x2d\ +\x33\x33\x2d\x37\x2e\x30\x35\x2d\x36\x31\x2e\x38\x38\x2c\x31\x2e\ +\x36\x34\x2d\x38\x36\x2e\x33\x2c\x32\x36\x2e\x35\x36\x2d\x31\x36\ +\x2e\x35\x34\x2c\x31\x36\x2e\x38\x38\x2d\x34\x32\x2c\x39\x2e\x39\ +\x33\x2d\x34\x37\x2e\x35\x37\x2d\x31\x33\x2e\x31\x37\x41\x37\x30\ +\x2e\x34\x31\x2c\x37\x30\x2e\x34\x31\x2c\x30\x2c\x30\x2c\x31\x2c\ +\x31\x38\x35\x2e\x33\x36\x2c\x34\x30\x32\x2e\x37\x33\x5a\x22\x2f\ +\x3e\x3c\x70\x61\x74\x68\x20\x63\x6c\x61\x73\x73\x3d\x22\x63\x6c\ +\x73\x2d\x31\x22\x20\x64\x3d\x22\x4d\x33\x30\x30\x2c\x35\x33\x37\ +\x2e\x33\x63\x2d\x32\x31\x2e\x38\x33\x2c\x30\x2d\x33\x38\x2e\x38\ +\x34\x2d\x31\x38\x2e\x33\x31\x2d\x33\x38\x2e\x38\x31\x2d\x34\x31\ +\x2e\x37\x39\x2c\x30\x2d\x32\x33\x2e\x31\x39\x2c\x31\x37\x2e\x30\ +\x36\x2d\x34\x31\x2e\x35\x31\x2c\x33\x38\x2e\x35\x36\x2d\x34\x31\ +\x2e\x34\x37\x2c\x32\x32\x2e\x30\x39\x2c\x30\x2c\x33\x39\x2c\x31\ +\x38\x2c\x33\x39\x2c\x34\x31\x2e\x34\x39\x53\x33\x32\x31\x2e\x39\ +\x31\x2c\x35\x33\x37\x2e\x32\x39\x2c\x33\x30\x30\x2c\x35\x33\x37\ +\x2e\x33\x5a\x22\x2f\x3e\x3c\x70\x61\x74\x68\x20\x63\x6c\x61\x73\ +\x73\x3d\x22\x63\x6c\x73\x2d\x32\x22\x20\x64\x3d\x22\x4d\x31\x35\ +\x36\x2e\x37\x36\x2c\x36\x31\x2c\x33\x30\x30\x2c\x32\x35\x39\x2e\ +\x38\x2c\x34\x34\x33\x2e\x32\x35\x2c\x36\x31\x68\x35\x37\x2e\x39\ +\x33\x4c\x33\x32\x39\x2c\x33\x30\x30\x2c\x35\x30\x31\x2e\x31\x39\ +\x2c\x35\x33\x39\x48\x34\x34\x33\x2e\x32\x35\x4c\x33\x30\x30\x2c\ +\x33\x34\x30\x2e\x31\x39\x2c\x31\x35\x36\x2e\x37\x36\x2c\x35\x33\ +\x39\x48\x39\x38\x2e\x38\x31\x4c\x32\x37\x31\x2c\x33\x30\x30\x2c\ +\x39\x38\x2e\x38\x33\x2c\x36\x31\x5a\x22\x2f\x3e\x3c\x2f\x73\x76\ +\x67\x3e\ \x00\x00\x0b\x4d\ \x00\ \x00\x38\xfa\x78\x9c\xed\x9b\x49\x6f\x1d\xc7\x15\x85\xff\x0a\xc1\ @@ -20106,7 +20254,7 @@ \x61\x6e\x73\x6c\x61\x74\x65\x28\x31\x30\x39\x36\x2e\x38\x36\x20\ \x34\x34\x35\x2e\x35\x33\x29\x20\x72\x6f\x74\x61\x74\x65\x28\x31\ \x33\x35\x29\x22\x2f\x3e\x3c\x2f\x73\x76\x67\x3e\ -\x00\x00\x05\x99\ +\x00\x00\x05\x80\ \x3c\ \x73\x76\x67\x20\x69\x64\x3d\x22\x4c\x61\x79\x65\x72\x5f\x31\x22\ \x20\x64\x61\x74\x61\x2d\x6e\x61\x6d\x65\x3d\x22\x4c\x61\x79\x65\ @@ -20118,86 +20266,176 @@ \x31\x7b\x66\x69\x6c\x6c\x3a\x23\x38\x63\x63\x35\x34\x30\x3b\x7d\ \x3c\x2f\x73\x74\x79\x6c\x65\x3e\x3c\x2f\x64\x65\x66\x73\x3e\x3c\ \x70\x61\x74\x68\x20\x63\x6c\x61\x73\x73\x3d\x22\x63\x6c\x73\x2d\ -\x31\x22\x20\x64\x3d\x22\x4d\x32\x39\x30\x2e\x31\x38\x2c\x39\x33\ -\x2e\x38\x32\x43\x34\x30\x32\x2c\x39\x35\x2e\x36\x31\x2c\x34\x39\ -\x30\x2e\x31\x2c\x31\x33\x33\x2e\x39\x31\x2c\x35\x36\x33\x2e\x39\ -\x2c\x32\x30\x39\x2e\x39\x32\x63\x31\x34\x2e\x38\x2c\x31\x35\x2e\ -\x32\x35\x2c\x31\x31\x2e\x38\x31\x2c\x33\x33\x2e\x39\x33\x2c\x33\ -\x2e\x32\x36\x2c\x34\x34\x2e\x30\x35\x2d\x31\x31\x2e\x31\x32\x2c\ -\x31\x33\x2e\x31\x36\x2d\x32\x38\x2e\x36\x32\x2c\x31\x33\x2d\x34\ -\x31\x2e\x34\x36\x2e\x38\x33\x2d\x31\x35\x2e\x30\x38\x2d\x31\x34\ -\x2e\x32\x37\x2d\x33\x30\x2e\x32\x2d\x32\x38\x2e\x37\x32\x2d\x34\ -\x36\x2e\x36\x31\x2d\x34\x31\x2e\x31\x31\x2d\x33\x38\x2e\x34\x2d\ -\x32\x39\x2d\x38\x31\x2e\x33\x33\x2d\x34\x36\x2e\x37\x35\x2d\x31\ -\x32\x37\x2e\x37\x35\x2d\x35\x34\x2e\x36\x35\x2d\x35\x34\x2d\x39\ -\x2e\x31\x39\x2d\x31\x30\x36\x2e\x39\x32\x2d\x34\x2e\x33\x31\x2d\ -\x31\x35\x38\x2e\x35\x2c\x31\x35\x2e\x32\x33\x2d\x34\x35\x2c\x31\ -\x37\x2d\x38\x34\x2e\x32\x39\x2c\x34\x33\x2e\x39\x33\x2d\x31\x31\ -\x38\x2e\x31\x36\x2c\x37\x39\x2e\x39\x34\x2d\x38\x2e\x35\x37\x2c\ -\x39\x2e\x31\x2d\x31\x38\x2e\x36\x35\x2c\x31\x32\x2e\x33\x32\x2d\ -\x33\x30\x2e\x31\x39\x2c\x38\x2d\x32\x30\x2e\x30\x39\x2d\x37\x2e\ -\x35\x36\x2d\x32\x35\x2e\x32\x2d\x33\x34\x2e\x30\x39\x2d\x39\x2e\ -\x37\x34\x2d\x35\x30\x2e\x36\x31\x61\x33\x38\x30\x2e\x35\x31\x2c\ -\x33\x38\x30\x2e\x35\x31\x2c\x30\x2c\x30\x2c\x31\x2c\x35\x37\x2e\ -\x36\x32\x2d\x35\x30\x2e\x33\x38\x63\x34\x33\x2d\x33\x30\x2e\x35\ -\x38\x2c\x38\x39\x2e\x39\x33\x2d\x35\x31\x2e\x31\x2c\x31\x34\x30\ -\x2e\x37\x37\x2d\x36\x30\x2e\x34\x35\x43\x32\x35\x35\x2e\x30\x37\ -\x2c\x39\x36\x2e\x37\x2c\x32\x37\x37\x2e\x34\x33\x2c\x39\x35\x2e\ -\x33\x31\x2c\x32\x39\x30\x2e\x31\x38\x2c\x39\x33\x2e\x38\x32\x5a\ -\x22\x2f\x3e\x3c\x70\x61\x74\x68\x20\x63\x6c\x61\x73\x73\x3d\x22\ -\x63\x6c\x73\x2d\x31\x22\x20\x64\x3d\x22\x4d\x34\x36\x39\x2e\x38\ -\x37\x2c\x33\x34\x39\x2e\x30\x35\x63\x2d\x31\x31\x2c\x2e\x31\x38\ -\x2d\x31\x37\x2e\x38\x33\x2d\x33\x2e\x30\x38\x2d\x32\x33\x2e\x35\ -\x32\x2d\x39\x2e\x31\x33\x43\x34\x31\x34\x2c\x33\x30\x35\x2e\x34\ -\x38\x2c\x33\x37\x35\x2e\x32\x32\x2c\x32\x38\x34\x2c\x33\x33\x30\ -\x2e\x31\x2c\x32\x37\x37\x2e\x35\x32\x63\x2d\x36\x37\x2e\x33\x37\ -\x2d\x39\x2e\x36\x37\x2d\x31\x32\x36\x2e\x31\x37\x2c\x31\x30\x2e\ -\x38\x32\x2d\x31\x37\x35\x2e\x33\x39\x2c\x36\x31\x2e\x34\x2d\x31\ -\x36\x2e\x33\x35\x2c\x31\x36\x2e\x38\x2d\x34\x30\x2e\x36\x37\x2c\ -\x31\x32\x2d\x34\x37\x2e\x39\x31\x2d\x31\x30\x2d\x33\x2e\x39\x2d\ -\x31\x31\x2e\x39\x2d\x31\x2e\x33\x38\x2d\x32\x32\x2e\x38\x2c\x36\ -\x2e\x38\x39\x2d\x33\x31\x2e\x35\x32\x2c\x34\x31\x2d\x34\x33\x2e\ -\x32\x34\x2c\x38\x39\x2e\x37\x35\x2d\x37\x30\x2e\x34\x38\x2c\x31\ -\x34\x36\x2e\x38\x34\x2d\x37\x39\x2e\x32\x38\x2c\x37\x35\x2e\x36\ -\x2d\x31\x31\x2e\x36\x36\x2c\x31\x34\x33\x2e\x36\x39\x2c\x38\x2c\ -\x32\x30\x33\x2e\x39\x31\x2c\x35\x38\x2e\x33\x36\x61\x32\x30\x35\ -\x2c\x32\x30\x35\x2c\x30\x2c\x30\x2c\x31\x2c\x32\x33\x2e\x32\x35\ -\x2c\x32\x32\x2e\x37\x63\x38\x2e\x30\x38\x2c\x39\x2e\x32\x39\x2c\ -\x39\x2e\x35\x2c\x32\x30\x2e\x36\x31\x2c\x34\x2e\x35\x39\x2c\x33\ -\x32\x2e\x33\x33\x43\x34\x38\x37\x2e\x34\x34\x2c\x33\x34\x33\x2e\ -\x30\x35\x2c\x34\x37\x38\x2e\x36\x32\x2c\x33\x34\x38\x2e\x34\x39\ -\x2c\x34\x36\x39\x2e\x38\x37\x2c\x33\x34\x39\x2e\x30\x35\x5a\x22\ -\x2f\x3e\x3c\x70\x61\x74\x68\x20\x63\x6c\x61\x73\x73\x3d\x22\x63\ -\x6c\x73\x2d\x31\x22\x20\x64\x3d\x22\x4d\x31\x38\x34\x2e\x35\x32\ -\x2c\x34\x30\x34\x2e\x32\x33\x63\x30\x2d\x39\x2e\x32\x32\x2c\x33\ -\x2e\x35\x37\x2d\x31\x36\x2e\x36\x34\x2c\x39\x2e\x36\x31\x2d\x32\ -\x32\x2e\x38\x31\x43\x32\x31\x38\x2c\x33\x35\x37\x2e\x30\x38\x2c\ -\x32\x34\x36\x2c\x33\x34\x31\x2e\x36\x31\x2c\x32\x37\x38\x2e\x37\ -\x37\x2c\x33\x33\x37\x63\x34\x38\x2e\x36\x36\x2d\x36\x2e\x38\x36\ -\x2c\x39\x30\x2e\x38\x33\x2c\x38\x2e\x32\x35\x2c\x31\x32\x36\x2e\ -\x36\x33\x2c\x34\x33\x2e\x39\x35\x2c\x31\x30\x2e\x31\x38\x2c\x31\ -\x30\x2e\x31\x35\x2c\x31\x32\x2e\x38\x31\x2c\x32\x34\x2c\x37\x2e\ -\x34\x35\x2c\x33\x36\x2e\x30\x35\x2d\x38\x2e\x34\x34\x2c\x31\x39\ -\x2d\x33\x31\x2c\x32\x33\x2e\x34\x35\x2d\x34\x35\x2e\x33\x32\x2c\ -\x38\x2e\x36\x37\x2d\x31\x33\x2e\x34\x2d\x31\x33\x2e\x38\x34\x2d\ -\x32\x38\x2e\x38\x2d\x32\x33\x2e\x36\x34\x2d\x34\x37\x2e\x31\x31\ -\x2d\x32\x37\x2e\x35\x35\x2d\x33\x33\x2e\x32\x32\x2d\x37\x2e\x30\ -\x39\x2d\x36\x32\x2e\x33\x36\x2c\x31\x2e\x36\x35\x2d\x38\x37\x2c\ -\x32\x36\x2e\x37\x37\x2d\x31\x36\x2e\x36\x36\x2c\x31\x37\x2d\x34\ -\x32\x2e\x33\x2c\x31\x30\x2d\x34\x37\x2e\x39\x33\x2d\x31\x33\x2e\ -\x32\x37\x41\x36\x38\x2e\x39\x33\x2c\x36\x38\x2e\x39\x33\x2c\x30\ -\x2c\x30\x2c\x31\x2c\x31\x38\x34\x2e\x35\x32\x2c\x34\x30\x34\x2e\ -\x32\x33\x5a\x22\x2f\x3e\x3c\x70\x61\x74\x68\x20\x63\x6c\x61\x73\ -\x73\x3d\x22\x63\x6c\x73\x2d\x31\x22\x20\x64\x3d\x22\x4d\x33\x30\ -\x30\x2c\x35\x33\x39\x2e\x38\x34\x63\x2d\x32\x32\x2c\x30\x2d\x33\ -\x39\x2e\x31\x35\x2d\x31\x38\x2e\x34\x35\x2d\x33\x39\x2e\x31\x31\ -\x2d\x34\x32\x2e\x31\x31\x2c\x30\x2d\x32\x33\x2e\x33\x38\x2c\x31\ -\x37\x2e\x31\x39\x2d\x34\x31\x2e\x38\x33\x2c\x33\x38\x2e\x38\x36\ -\x2d\x34\x31\x2e\x37\x39\x2c\x32\x32\x2e\x32\x35\x2c\x30\x2c\x33\ -\x39\x2e\x33\x32\x2c\x31\x38\x2e\x31\x37\x2c\x33\x39\x2e\x33\x34\ -\x2c\x34\x31\x2e\x38\x31\x53\x33\x32\x32\x2e\x31\x2c\x35\x33\x39\ -\x2e\x38\x33\x2c\x33\x30\x30\x2c\x35\x33\x39\x2e\x38\x34\x5a\x22\ -\x2f\x3e\x3c\x2f\x73\x76\x67\x3e\ +\x31\x22\x20\x64\x3d\x22\x4d\x32\x39\x30\x2e\x31\x38\x2c\x37\x37\ +\x43\x34\x30\x32\x2c\x37\x38\x2e\x37\x37\x2c\x34\x39\x30\x2e\x31\ +\x2c\x31\x31\x37\x2e\x30\x37\x2c\x35\x36\x33\x2e\x39\x2c\x31\x39\ +\x33\x2e\x30\x39\x63\x31\x34\x2e\x38\x2c\x31\x35\x2e\x32\x35\x2c\ +\x31\x31\x2e\x38\x31\x2c\x33\x33\x2e\x39\x33\x2c\x33\x2e\x32\x36\ +\x2c\x34\x34\x2d\x31\x31\x2e\x31\x32\x2c\x31\x33\x2e\x31\x37\x2d\ +\x32\x38\x2e\x36\x32\x2c\x31\x33\x2d\x34\x31\x2e\x34\x36\x2e\x38\ +\x33\x2d\x31\x35\x2e\x30\x38\x2d\x31\x34\x2e\x32\x37\x2d\x33\x30\ +\x2e\x32\x2d\x32\x38\x2e\x37\x31\x2d\x34\x36\x2e\x36\x31\x2d\x34\ +\x31\x2e\x31\x2d\x33\x38\x2e\x34\x2d\x32\x39\x2d\x38\x31\x2e\x33\ +\x33\x2d\x34\x36\x2e\x37\x35\x2d\x31\x32\x37\x2e\x37\x35\x2d\x35\ +\x34\x2e\x36\x35\x2d\x35\x34\x2d\x39\x2e\x31\x39\x2d\x31\x30\x36\ +\x2e\x39\x32\x2d\x34\x2e\x33\x31\x2d\x31\x35\x38\x2e\x35\x2c\x31\ +\x35\x2e\x32\x33\x2d\x34\x35\x2c\x31\x37\x2e\x30\x35\x2d\x38\x34\ +\x2e\x32\x39\x2c\x34\x33\x2e\x39\x33\x2d\x31\x31\x38\x2e\x31\x36\ +\x2c\x37\x39\x2e\x39\x33\x2d\x38\x2e\x35\x37\x2c\x39\x2e\x31\x31\ +\x2d\x31\x38\x2e\x36\x35\x2c\x31\x32\x2e\x33\x32\x2d\x33\x30\x2e\ +\x31\x39\x2c\x38\x2d\x32\x30\x2e\x30\x39\x2d\x37\x2e\x35\x37\x2d\ +\x32\x35\x2e\x32\x2d\x33\x34\x2e\x30\x39\x2d\x39\x2e\x37\x34\x2d\ +\x35\x30\x2e\x36\x31\x61\x33\x38\x30\x2c\x33\x38\x30\x2c\x30\x2c\ +\x30\x2c\x31\x2c\x35\x37\x2e\x36\x32\x2d\x35\x30\x2e\x33\x38\x63\ +\x34\x33\x2d\x33\x30\x2e\x35\x38\x2c\x38\x39\x2e\x39\x33\x2d\x35\ +\x31\x2e\x31\x2c\x31\x34\x30\x2e\x37\x37\x2d\x36\x30\x2e\x34\x36\ +\x43\x32\x35\x35\x2e\x30\x37\x2c\x37\x39\x2e\x38\x37\x2c\x32\x37\ +\x37\x2e\x34\x33\x2c\x37\x38\x2e\x34\x38\x2c\x32\x39\x30\x2e\x31\ +\x38\x2c\x37\x37\x5a\x22\x2f\x3e\x3c\x70\x61\x74\x68\x20\x63\x6c\ +\x61\x73\x73\x3d\x22\x63\x6c\x73\x2d\x31\x22\x20\x64\x3d\x22\x4d\ +\x34\x36\x39\x2e\x38\x37\x2c\x33\x33\x32\x2e\x32\x31\x63\x2d\x31\ +\x31\x2c\x2e\x31\x38\x2d\x31\x37\x2e\x38\x33\x2d\x33\x2e\x30\x37\ +\x2d\x32\x33\x2e\x35\x32\x2d\x39\x2e\x31\x32\x43\x34\x31\x34\x2c\ +\x32\x38\x38\x2e\x36\x35\x2c\x33\x37\x35\x2e\x32\x32\x2c\x32\x36\ +\x37\x2e\x31\x36\x2c\x33\x33\x30\x2e\x31\x2c\x32\x36\x30\x2e\x36\ +\x38\x63\x2d\x36\x37\x2e\x33\x37\x2d\x39\x2e\x36\x37\x2d\x31\x32\ +\x36\x2e\x31\x37\x2c\x31\x30\x2e\x38\x33\x2d\x31\x37\x35\x2e\x33\ +\x39\x2c\x36\x31\x2e\x34\x31\x2d\x31\x36\x2e\x33\x35\x2c\x31\x36\ +\x2e\x38\x2d\x34\x30\x2e\x36\x37\x2c\x31\x32\x2d\x34\x37\x2e\x39\ +\x31\x2d\x31\x30\x2d\x33\x2e\x39\x2d\x31\x31\x2e\x39\x2d\x31\x2e\ +\x33\x38\x2d\x32\x32\x2e\x38\x2c\x36\x2e\x38\x39\x2d\x33\x31\x2e\ +\x35\x32\x2c\x34\x31\x2d\x34\x33\x2e\x32\x34\x2c\x38\x39\x2e\x37\ +\x35\x2d\x37\x30\x2e\x34\x38\x2c\x31\x34\x36\x2e\x38\x34\x2d\x37\ +\x39\x2e\x32\x38\x2c\x37\x35\x2e\x36\x2d\x31\x31\x2e\x36\x36\x2c\ +\x31\x34\x33\x2e\x36\x39\x2c\x38\x2e\x30\x35\x2c\x32\x30\x33\x2e\ +\x39\x31\x2c\x35\x38\x2e\x33\x36\x61\x32\x30\x35\x2e\x37\x34\x2c\ +\x32\x30\x35\x2e\x37\x34\x2c\x30\x2c\x30\x2c\x31\x2c\x32\x33\x2e\ +\x32\x35\x2c\x32\x32\x2e\x36\x39\x63\x38\x2e\x30\x38\x2c\x39\x2e\ +\x33\x2c\x39\x2e\x35\x2c\x32\x30\x2e\x36\x32\x2c\x34\x2e\x35\x39\ +\x2c\x33\x32\x2e\x33\x34\x53\x34\x37\x38\x2e\x36\x32\x2c\x33\x33\ +\x31\x2e\x36\x36\x2c\x34\x36\x39\x2e\x38\x37\x2c\x33\x33\x32\x2e\ +\x32\x31\x5a\x22\x2f\x3e\x3c\x70\x61\x74\x68\x20\x63\x6c\x61\x73\ +\x73\x3d\x22\x63\x6c\x73\x2d\x31\x22\x20\x64\x3d\x22\x4d\x31\x38\ +\x34\x2e\x35\x32\x2c\x33\x38\x37\x2e\x34\x63\x30\x2d\x39\x2e\x32\ +\x32\x2c\x33\x2e\x35\x37\x2d\x31\x36\x2e\x36\x35\x2c\x39\x2e\x36\ +\x31\x2d\x32\x32\x2e\x38\x31\x43\x32\x31\x38\x2c\x33\x34\x30\x2e\ +\x32\x34\x2c\x32\x34\x36\x2c\x33\x32\x34\x2e\x37\x38\x2c\x32\x37\ +\x38\x2e\x37\x37\x2c\x33\x32\x30\x2e\x31\x35\x63\x34\x38\x2e\x36\ +\x36\x2d\x36\x2e\x38\x36\x2c\x39\x30\x2e\x38\x33\x2c\x38\x2e\x32\ +\x35\x2c\x31\x32\x36\x2e\x36\x33\x2c\x34\x34\x2c\x31\x30\x2e\x31\ +\x38\x2c\x31\x30\x2e\x31\x35\x2c\x31\x32\x2e\x38\x31\x2c\x32\x34\ +\x2c\x37\x2e\x34\x35\x2c\x33\x36\x2e\x30\x35\x2d\x38\x2e\x34\x34\ +\x2c\x31\x39\x2d\x33\x31\x2c\x32\x33\x2e\x34\x35\x2d\x34\x35\x2e\ +\x33\x32\x2c\x38\x2e\x36\x36\x2d\x31\x33\x2e\x34\x2d\x31\x33\x2e\ +\x38\x33\x2d\x32\x38\x2e\x38\x2d\x32\x33\x2e\x36\x33\x2d\x34\x37\ +\x2e\x31\x31\x2d\x32\x37\x2e\x35\x34\x2d\x33\x33\x2e\x32\x32\x2d\ +\x37\x2e\x31\x2d\x36\x32\x2e\x33\x36\x2c\x31\x2e\x36\x35\x2d\x38\ +\x37\x2c\x32\x36\x2e\x37\x37\x2d\x31\x36\x2e\x36\x36\x2c\x31\x37\ +\x2d\x34\x32\x2e\x33\x2c\x31\x30\x2d\x34\x37\x2e\x39\x33\x2d\x31\ +\x33\x2e\x32\x37\x41\x36\x39\x2e\x32\x38\x2c\x36\x39\x2e\x32\x38\ +\x2c\x30\x2c\x30\x2c\x31\x2c\x31\x38\x34\x2e\x35\x32\x2c\x33\x38\ +\x37\x2e\x34\x5a\x22\x2f\x3e\x3c\x70\x61\x74\x68\x20\x63\x6c\x61\ +\x73\x73\x3d\x22\x63\x6c\x73\x2d\x31\x22\x20\x64\x3d\x22\x4d\x33\ +\x30\x30\x2c\x35\x32\x33\x63\x2d\x32\x32\x2c\x30\x2d\x33\x39\x2e\ +\x31\x35\x2d\x31\x38\x2e\x34\x36\x2d\x33\x39\x2e\x31\x31\x2d\x34\ +\x32\x2e\x31\x31\x2c\x30\x2d\x32\x33\x2e\x33\x38\x2c\x31\x37\x2e\ +\x31\x39\x2d\x34\x31\x2e\x38\x33\x2c\x33\x38\x2e\x38\x36\x2d\x34\ +\x31\x2e\x38\x2c\x32\x32\x2e\x32\x35\x2c\x30\x2c\x33\x39\x2e\x33\ +\x32\x2c\x31\x38\x2e\x31\x38\x2c\x33\x39\x2e\x33\x34\x2c\x34\x31\ +\x2e\x38\x31\x53\x33\x32\x32\x2e\x31\x2c\x35\x32\x33\x2c\x33\x30\ +\x30\x2c\x35\x32\x33\x5a\x22\x2f\x3e\x3c\x2f\x73\x76\x67\x3e\ +\x00\x00\x05\x95\ +\x3c\ +\x73\x76\x67\x20\x69\x64\x3d\x22\x4c\x61\x79\x65\x72\x5f\x31\x22\ +\x20\x64\x61\x74\x61\x2d\x6e\x61\x6d\x65\x3d\x22\x4c\x61\x79\x65\ +\x72\x20\x31\x22\x20\x78\x6d\x6c\x6e\x73\x3d\x22\x68\x74\x74\x70\ +\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x32\x30\ +\x30\x30\x2f\x73\x76\x67\x22\x20\x76\x69\x65\x77\x42\x6f\x78\x3d\ +\x22\x30\x20\x30\x20\x36\x30\x30\x20\x36\x30\x30\x22\x3e\x3c\x64\ +\x65\x66\x73\x3e\x3c\x73\x74\x79\x6c\x65\x3e\x2e\x63\x6c\x73\x2d\ +\x31\x7b\x66\x69\x6c\x6c\x3a\x23\x64\x30\x64\x32\x64\x33\x3b\x7d\ +\x2e\x63\x6c\x73\x2d\x32\x7b\x66\x69\x6c\x6c\x3a\x23\x38\x63\x63\ +\x35\x34\x30\x3b\x7d\x3c\x2f\x73\x74\x79\x6c\x65\x3e\x3c\x2f\x64\ +\x65\x66\x73\x3e\x3c\x70\x61\x74\x68\x20\x63\x6c\x61\x73\x73\x3d\ +\x22\x63\x6c\x73\x2d\x31\x22\x20\x64\x3d\x22\x4d\x32\x39\x30\x2e\ +\x31\x38\x2c\x37\x37\x43\x34\x30\x32\x2c\x37\x38\x2e\x37\x37\x2c\ +\x34\x39\x30\x2e\x31\x2c\x31\x31\x37\x2e\x30\x37\x2c\x35\x36\x33\ +\x2e\x39\x2c\x31\x39\x33\x2e\x30\x39\x63\x31\x34\x2e\x38\x2c\x31\ +\x35\x2e\x32\x35\x2c\x31\x31\x2e\x38\x31\x2c\x33\x33\x2e\x39\x33\ +\x2c\x33\x2e\x32\x36\x2c\x34\x34\x2d\x31\x31\x2e\x31\x32\x2c\x31\ +\x33\x2e\x31\x37\x2d\x32\x38\x2e\x36\x32\x2c\x31\x33\x2d\x34\x31\ +\x2e\x34\x36\x2e\x38\x33\x2d\x31\x35\x2e\x30\x38\x2d\x31\x34\x2e\ +\x32\x37\x2d\x33\x30\x2e\x32\x2d\x32\x38\x2e\x37\x31\x2d\x34\x36\ +\x2e\x36\x31\x2d\x34\x31\x2e\x31\x2d\x33\x38\x2e\x34\x2d\x32\x39\ +\x2d\x38\x31\x2e\x33\x33\x2d\x34\x36\x2e\x37\x35\x2d\x31\x32\x37\ +\x2e\x37\x35\x2d\x35\x34\x2e\x36\x35\x2d\x35\x34\x2d\x39\x2e\x31\ +\x39\x2d\x31\x30\x36\x2e\x39\x32\x2d\x34\x2e\x33\x31\x2d\x31\x35\ +\x38\x2e\x35\x2c\x31\x35\x2e\x32\x33\x2d\x34\x35\x2c\x31\x37\x2e\ +\x30\x35\x2d\x38\x34\x2e\x32\x39\x2c\x34\x33\x2e\x39\x33\x2d\x31\ +\x31\x38\x2e\x31\x36\x2c\x37\x39\x2e\x39\x33\x2d\x38\x2e\x35\x37\ +\x2c\x39\x2e\x31\x31\x2d\x31\x38\x2e\x36\x35\x2c\x31\x32\x2e\x33\ +\x32\x2d\x33\x30\x2e\x31\x39\x2c\x38\x2d\x32\x30\x2e\x30\x39\x2d\ +\x37\x2e\x35\x37\x2d\x32\x35\x2e\x32\x2d\x33\x34\x2e\x30\x39\x2d\ +\x39\x2e\x37\x34\x2d\x35\x30\x2e\x36\x31\x61\x33\x38\x30\x2c\x33\ +\x38\x30\x2c\x30\x2c\x30\x2c\x31\x2c\x35\x37\x2e\x36\x32\x2d\x35\ +\x30\x2e\x33\x38\x63\x34\x33\x2d\x33\x30\x2e\x35\x38\x2c\x38\x39\ +\x2e\x39\x33\x2d\x35\x31\x2e\x31\x2c\x31\x34\x30\x2e\x37\x37\x2d\ +\x36\x30\x2e\x34\x36\x43\x32\x35\x35\x2e\x30\x37\x2c\x37\x39\x2e\ +\x38\x37\x2c\x32\x37\x37\x2e\x34\x33\x2c\x37\x38\x2e\x34\x38\x2c\ +\x32\x39\x30\x2e\x31\x38\x2c\x37\x37\x5a\x22\x2f\x3e\x3c\x70\x61\ +\x74\x68\x20\x63\x6c\x61\x73\x73\x3d\x22\x63\x6c\x73\x2d\x32\x22\ +\x20\x64\x3d\x22\x4d\x34\x36\x39\x2e\x38\x37\x2c\x33\x33\x32\x2e\ +\x32\x31\x63\x2d\x31\x31\x2c\x2e\x31\x38\x2d\x31\x37\x2e\x38\x33\ +\x2d\x33\x2e\x30\x37\x2d\x32\x33\x2e\x35\x32\x2d\x39\x2e\x31\x32\ +\x43\x34\x31\x34\x2c\x32\x38\x38\x2e\x36\x35\x2c\x33\x37\x35\x2e\ +\x32\x32\x2c\x32\x36\x37\x2e\x31\x36\x2c\x33\x33\x30\x2e\x31\x2c\ +\x32\x36\x30\x2e\x36\x38\x63\x2d\x36\x37\x2e\x33\x37\x2d\x39\x2e\ +\x36\x37\x2d\x31\x32\x36\x2e\x31\x37\x2c\x31\x30\x2e\x38\x33\x2d\ +\x31\x37\x35\x2e\x33\x39\x2c\x36\x31\x2e\x34\x31\x2d\x31\x36\x2e\ +\x33\x35\x2c\x31\x36\x2e\x38\x2d\x34\x30\x2e\x36\x37\x2c\x31\x32\ +\x2d\x34\x37\x2e\x39\x31\x2d\x31\x30\x2d\x33\x2e\x39\x2d\x31\x31\ +\x2e\x39\x2d\x31\x2e\x33\x38\x2d\x32\x32\x2e\x38\x2c\x36\x2e\x38\ +\x39\x2d\x33\x31\x2e\x35\x32\x2c\x34\x31\x2d\x34\x33\x2e\x32\x34\ +\x2c\x38\x39\x2e\x37\x35\x2d\x37\x30\x2e\x34\x38\x2c\x31\x34\x36\ +\x2e\x38\x34\x2d\x37\x39\x2e\x32\x38\x2c\x37\x35\x2e\x36\x2d\x31\ +\x31\x2e\x36\x36\x2c\x31\x34\x33\x2e\x36\x39\x2c\x38\x2e\x30\x35\ +\x2c\x32\x30\x33\x2e\x39\x31\x2c\x35\x38\x2e\x33\x36\x61\x32\x30\ +\x35\x2e\x37\x34\x2c\x32\x30\x35\x2e\x37\x34\x2c\x30\x2c\x30\x2c\ +\x31\x2c\x32\x33\x2e\x32\x35\x2c\x32\x32\x2e\x36\x39\x63\x38\x2e\ +\x30\x38\x2c\x39\x2e\x33\x2c\x39\x2e\x35\x2c\x32\x30\x2e\x36\x32\ +\x2c\x34\x2e\x35\x39\x2c\x33\x32\x2e\x33\x34\x53\x34\x37\x38\x2e\ +\x36\x32\x2c\x33\x33\x31\x2e\x36\x36\x2c\x34\x36\x39\x2e\x38\x37\ +\x2c\x33\x33\x32\x2e\x32\x31\x5a\x22\x2f\x3e\x3c\x70\x61\x74\x68\ +\x20\x63\x6c\x61\x73\x73\x3d\x22\x63\x6c\x73\x2d\x32\x22\x20\x64\ +\x3d\x22\x4d\x31\x38\x34\x2e\x35\x32\x2c\x33\x38\x37\x2e\x34\x63\ +\x30\x2d\x39\x2e\x32\x32\x2c\x33\x2e\x35\x37\x2d\x31\x36\x2e\x36\ +\x35\x2c\x39\x2e\x36\x31\x2d\x32\x32\x2e\x38\x31\x43\x32\x31\x38\ +\x2c\x33\x34\x30\x2e\x32\x34\x2c\x32\x34\x36\x2c\x33\x32\x34\x2e\ +\x37\x38\x2c\x32\x37\x38\x2e\x37\x37\x2c\x33\x32\x30\x2e\x31\x35\ +\x63\x34\x38\x2e\x36\x36\x2d\x36\x2e\x38\x36\x2c\x39\x30\x2e\x38\ +\x33\x2c\x38\x2e\x32\x35\x2c\x31\x32\x36\x2e\x36\x33\x2c\x34\x34\ +\x2c\x31\x30\x2e\x31\x38\x2c\x31\x30\x2e\x31\x35\x2c\x31\x32\x2e\ +\x38\x31\x2c\x32\x34\x2c\x37\x2e\x34\x35\x2c\x33\x36\x2e\x30\x35\ +\x2d\x38\x2e\x34\x34\x2c\x31\x39\x2d\x33\x31\x2c\x32\x33\x2e\x34\ +\x35\x2d\x34\x35\x2e\x33\x32\x2c\x38\x2e\x36\x36\x2d\x31\x33\x2e\ +\x34\x2d\x31\x33\x2e\x38\x33\x2d\x32\x38\x2e\x38\x2d\x32\x33\x2e\ +\x36\x33\x2d\x34\x37\x2e\x31\x31\x2d\x32\x37\x2e\x35\x34\x2d\x33\ +\x33\x2e\x32\x32\x2d\x37\x2e\x31\x2d\x36\x32\x2e\x33\x36\x2c\x31\ +\x2e\x36\x35\x2d\x38\x37\x2c\x32\x36\x2e\x37\x37\x2d\x31\x36\x2e\ +\x36\x36\x2c\x31\x37\x2d\x34\x32\x2e\x33\x2c\x31\x30\x2d\x34\x37\ +\x2e\x39\x33\x2d\x31\x33\x2e\x32\x37\x41\x36\x39\x2e\x32\x38\x2c\ +\x36\x39\x2e\x32\x38\x2c\x30\x2c\x30\x2c\x31\x2c\x31\x38\x34\x2e\ +\x35\x32\x2c\x33\x38\x37\x2e\x34\x5a\x22\x2f\x3e\x3c\x70\x61\x74\ +\x68\x20\x63\x6c\x61\x73\x73\x3d\x22\x63\x6c\x73\x2d\x32\x22\x20\ +\x64\x3d\x22\x4d\x33\x30\x30\x2c\x35\x32\x33\x63\x2d\x32\x32\x2c\ +\x30\x2d\x33\x39\x2e\x31\x35\x2d\x31\x38\x2e\x34\x36\x2d\x33\x39\ +\x2e\x31\x31\x2d\x34\x32\x2e\x31\x31\x2c\x30\x2d\x32\x33\x2e\x33\ +\x38\x2c\x31\x37\x2e\x31\x39\x2d\x34\x31\x2e\x38\x33\x2c\x33\x38\ +\x2e\x38\x36\x2d\x34\x31\x2e\x38\x2c\x32\x32\x2e\x32\x35\x2c\x30\ +\x2c\x33\x39\x2e\x33\x32\x2c\x31\x38\x2e\x31\x38\x2c\x33\x39\x2e\ +\x33\x34\x2c\x34\x31\x2e\x38\x31\x53\x33\x32\x32\x2e\x31\x2c\x35\ +\x32\x33\x2c\x33\x30\x30\x2c\x35\x32\x33\x5a\x22\x2f\x3e\x3c\x2f\ +\x73\x76\x67\x3e\ \x00\x00\x09\xc5\ \x3c\ \x73\x76\x67\x20\x69\x64\x3d\x22\x4c\x61\x79\x65\x72\x5f\x31\x22\ @@ -20357,7 +20595,7 @@ \x32\x37\x2e\x37\x35\x2c\x34\x31\x36\x2e\x31\x31\x2c\x35\x32\x38\ \x2e\x32\x39\x2c\x34\x31\x36\x2e\x36\x38\x5a\x22\x2f\x3e\x3c\x2f\ \x73\x76\x67\x3e\ -\x00\x00\x03\xdf\ +\x00\x00\x05\x95\ \x3c\ \x73\x76\x67\x20\x69\x64\x3d\x22\x4c\x61\x79\x65\x72\x5f\x31\x22\ \x20\x64\x61\x74\x61\x2d\x6e\x61\x6d\x65\x3d\x22\x4c\x61\x79\x65\ @@ -20366,61 +20604,258 @@ \x30\x30\x2f\x73\x76\x67\x22\x20\x76\x69\x65\x77\x42\x6f\x78\x3d\ \x22\x30\x20\x30\x20\x36\x30\x30\x20\x36\x30\x30\x22\x3e\x3c\x64\ \x65\x66\x73\x3e\x3c\x73\x74\x79\x6c\x65\x3e\x2e\x63\x6c\x73\x2d\ -\x31\x7b\x66\x69\x6c\x6c\x3a\x23\x33\x38\x62\x33\x34\x61\x3b\x7d\ -\x3c\x2f\x73\x74\x79\x6c\x65\x3e\x3c\x2f\x64\x65\x66\x73\x3e\x3c\ -\x70\x61\x74\x68\x20\x63\x6c\x61\x73\x73\x3d\x22\x63\x6c\x73\x2d\ -\x31\x22\x20\x64\x3d\x22\x4d\x34\x36\x38\x2e\x35\x31\x2c\x33\x35\ -\x30\x2e\x35\x31\x63\x2d\x31\x30\x2e\x39\x31\x2e\x31\x38\x2d\x31\ -\x37\x2e\x36\x39\x2d\x33\x2e\x30\x35\x2d\x32\x33\x2e\x33\x34\x2d\ -\x39\x2e\x30\x36\x2d\x33\x32\x2e\x31\x32\x2d\x33\x34\x2e\x31\x38\ -\x2d\x37\x30\x2e\x35\x39\x2d\x35\x35\x2e\x35\x2d\x31\x31\x35\x2e\ -\x33\x36\x2d\x36\x31\x2e\x39\x33\x2d\x36\x36\x2e\x38\x35\x2d\x39\ -\x2e\x35\x39\x2d\x31\x32\x35\x2e\x32\x2c\x31\x30\x2e\x37\x35\x2d\ -\x31\x37\x34\x2c\x36\x30\x2e\x39\x34\x2d\x31\x36\x2e\x32\x32\x2c\ -\x31\x36\x2e\x36\x37\x2d\x34\x30\x2e\x33\x36\x2c\x31\x31\x2e\x39\ -\x31\x2d\x34\x37\x2e\x35\x34\x2d\x31\x30\x2d\x33\x2e\x38\x38\x2d\ -\x31\x31\x2e\x38\x2d\x31\x2e\x33\x37\x2d\x32\x32\x2e\x36\x32\x2c\ -\x36\x2e\x38\x34\x2d\x33\x31\x2e\x32\x37\x2c\x34\x30\x2e\x36\x38\ -\x2d\x34\x32\x2e\x39\x31\x2c\x38\x39\x2e\x30\x36\x2d\x36\x39\x2e\ -\x39\x34\x2c\x31\x34\x35\x2e\x37\x31\x2d\x37\x38\x2e\x36\x38\x2c\ -\x37\x35\x2d\x31\x31\x2e\x35\x37\x2c\x31\x34\x32\x2e\x35\x39\x2c\ -\x38\x2c\x32\x30\x32\x2e\x33\x35\x2c\x35\x37\x2e\x39\x32\x41\x32\ -\x30\x33\x2e\x34\x37\x2c\x32\x30\x33\x2e\x34\x37\x2c\x30\x2c\x30\ -\x2c\x31\x2c\x34\x38\x36\x2e\x31\x39\x2c\x33\x30\x31\x63\x38\x2c\ -\x39\x2e\x32\x32\x2c\x39\x2e\x34\x33\x2c\x32\x30\x2e\x34\x36\x2c\ -\x34\x2e\x35\x36\x2c\x33\x32\x2e\x30\x38\x53\x34\x37\x37\x2e\x31\ -\x39\x2c\x33\x35\x30\x2c\x34\x36\x38\x2e\x35\x31\x2c\x33\x35\x30\ -\x2e\x35\x31\x5a\x22\x2f\x3e\x3c\x70\x61\x74\x68\x20\x63\x6c\x61\ -\x73\x73\x3d\x22\x63\x6c\x73\x2d\x31\x22\x20\x64\x3d\x22\x4d\x31\ -\x38\x35\x2e\x33\x36\x2c\x34\x30\x35\x2e\x32\x37\x63\x30\x2d\x39\ -\x2e\x31\x35\x2c\x33\x2e\x35\x35\x2d\x31\x36\x2e\x35\x32\x2c\x39\ -\x2e\x35\x34\x2d\x32\x32\x2e\x36\x34\x2c\x32\x33\x2e\x36\x36\x2d\ -\x32\x34\x2e\x31\x35\x2c\x35\x31\x2e\x34\x34\x2d\x33\x39\x2e\x35\ -\x2c\x38\x34\x2d\x34\x34\x2e\x30\x39\x2c\x34\x38\x2e\x32\x38\x2d\ -\x36\x2e\x38\x31\x2c\x39\x30\x2e\x31\x34\x2c\x38\x2e\x31\x39\x2c\ -\x31\x32\x35\x2e\x36\x37\x2c\x34\x33\x2e\x36\x32\x2c\x31\x30\x2e\ -\x30\x39\x2c\x31\x30\x2e\x30\x37\x2c\x31\x32\x2e\x37\x2c\x32\x33\ -\x2e\x38\x32\x2c\x37\x2e\x33\x39\x2c\x33\x35\x2e\x37\x37\x2d\x38\ -\x2e\x33\x39\x2c\x31\x38\x2e\x38\x34\x2d\x33\x30\x2e\x37\x37\x2c\ -\x32\x33\x2e\x32\x37\x2d\x34\x35\x2c\x38\x2e\x36\x2d\x31\x33\x2e\ -\x33\x2d\x31\x33\x2e\x37\x33\x2d\x32\x38\x2e\x35\x38\x2d\x32\x33\ -\x2e\x34\x35\x2d\x34\x36\x2e\x37\x35\x2d\x32\x37\x2e\x33\x33\x2d\ -\x33\x33\x2d\x37\x2e\x30\x35\x2d\x36\x31\x2e\x38\x38\x2c\x31\x2e\ -\x36\x34\x2d\x38\x36\x2e\x33\x2c\x32\x36\x2e\x35\x36\x2d\x31\x36\ -\x2e\x35\x34\x2c\x31\x36\x2e\x38\x38\x2d\x34\x32\x2c\x39\x2e\x39\ -\x33\x2d\x34\x37\x2e\x35\x37\x2d\x31\x33\x2e\x31\x37\x41\x37\x30\ -\x2e\x34\x31\x2c\x37\x30\x2e\x34\x31\x2c\x30\x2c\x30\x2c\x31\x2c\ -\x31\x38\x35\x2e\x33\x36\x2c\x34\x30\x35\x2e\x32\x37\x5a\x22\x2f\ -\x3e\x3c\x70\x61\x74\x68\x20\x63\x6c\x61\x73\x73\x3d\x22\x63\x6c\ -\x73\x2d\x31\x22\x20\x64\x3d\x22\x4d\x33\x30\x30\x2c\x35\x33\x39\ -\x2e\x38\x34\x63\x2d\x32\x31\x2e\x38\x33\x2c\x30\x2d\x33\x38\x2e\ -\x38\x34\x2d\x31\x38\x2e\x33\x31\x2d\x33\x38\x2e\x38\x31\x2d\x34\ -\x31\x2e\x37\x39\x2c\x30\x2d\x32\x33\x2e\x31\x39\x2c\x31\x37\x2e\ -\x30\x36\x2d\x34\x31\x2e\x35\x31\x2c\x33\x38\x2e\x35\x36\x2d\x34\ -\x31\x2e\x34\x37\x2c\x32\x32\x2e\x30\x39\x2c\x30\x2c\x33\x39\x2c\ -\x31\x38\x2c\x33\x39\x2c\x34\x31\x2e\x34\x39\x53\x33\x32\x31\x2e\ -\x39\x31\x2c\x35\x33\x39\x2e\x38\x33\x2c\x33\x30\x30\x2c\x35\x33\ -\x39\x2e\x38\x34\x5a\x22\x2f\x3e\x3c\x2f\x73\x76\x67\x3e\ +\x31\x7b\x66\x69\x6c\x6c\x3a\x23\x64\x30\x64\x32\x64\x33\x3b\x7d\ +\x2e\x63\x6c\x73\x2d\x32\x7b\x66\x69\x6c\x6c\x3a\x23\x38\x63\x63\ +\x35\x34\x30\x3b\x7d\x3c\x2f\x73\x74\x79\x6c\x65\x3e\x3c\x2f\x64\ +\x65\x66\x73\x3e\x3c\x70\x61\x74\x68\x20\x63\x6c\x61\x73\x73\x3d\ +\x22\x63\x6c\x73\x2d\x31\x22\x20\x64\x3d\x22\x4d\x32\x39\x30\x2e\ +\x31\x38\x2c\x37\x37\x43\x34\x30\x32\x2c\x37\x38\x2e\x37\x37\x2c\ +\x34\x39\x30\x2e\x31\x2c\x31\x31\x37\x2e\x30\x37\x2c\x35\x36\x33\ +\x2e\x39\x2c\x31\x39\x33\x2e\x30\x39\x63\x31\x34\x2e\x38\x2c\x31\ +\x35\x2e\x32\x35\x2c\x31\x31\x2e\x38\x31\x2c\x33\x33\x2e\x39\x33\ +\x2c\x33\x2e\x32\x36\x2c\x34\x34\x2d\x31\x31\x2e\x31\x32\x2c\x31\ +\x33\x2e\x31\x37\x2d\x32\x38\x2e\x36\x32\x2c\x31\x33\x2d\x34\x31\ +\x2e\x34\x36\x2e\x38\x33\x2d\x31\x35\x2e\x30\x38\x2d\x31\x34\x2e\ +\x32\x37\x2d\x33\x30\x2e\x32\x2d\x32\x38\x2e\x37\x31\x2d\x34\x36\ +\x2e\x36\x31\x2d\x34\x31\x2e\x31\x2d\x33\x38\x2e\x34\x2d\x32\x39\ +\x2d\x38\x31\x2e\x33\x33\x2d\x34\x36\x2e\x37\x35\x2d\x31\x32\x37\ +\x2e\x37\x35\x2d\x35\x34\x2e\x36\x35\x2d\x35\x34\x2d\x39\x2e\x31\ +\x39\x2d\x31\x30\x36\x2e\x39\x32\x2d\x34\x2e\x33\x31\x2d\x31\x35\ +\x38\x2e\x35\x2c\x31\x35\x2e\x32\x33\x2d\x34\x35\x2c\x31\x37\x2e\ +\x30\x35\x2d\x38\x34\x2e\x32\x39\x2c\x34\x33\x2e\x39\x33\x2d\x31\ +\x31\x38\x2e\x31\x36\x2c\x37\x39\x2e\x39\x33\x2d\x38\x2e\x35\x37\ +\x2c\x39\x2e\x31\x31\x2d\x31\x38\x2e\x36\x35\x2c\x31\x32\x2e\x33\ +\x32\x2d\x33\x30\x2e\x31\x39\x2c\x38\x2d\x32\x30\x2e\x30\x39\x2d\ +\x37\x2e\x35\x37\x2d\x32\x35\x2e\x32\x2d\x33\x34\x2e\x30\x39\x2d\ +\x39\x2e\x37\x34\x2d\x35\x30\x2e\x36\x31\x61\x33\x38\x30\x2c\x33\ +\x38\x30\x2c\x30\x2c\x30\x2c\x31\x2c\x35\x37\x2e\x36\x32\x2d\x35\ +\x30\x2e\x33\x38\x63\x34\x33\x2d\x33\x30\x2e\x35\x38\x2c\x38\x39\ +\x2e\x39\x33\x2d\x35\x31\x2e\x31\x2c\x31\x34\x30\x2e\x37\x37\x2d\ +\x36\x30\x2e\x34\x36\x43\x32\x35\x35\x2e\x30\x37\x2c\x37\x39\x2e\ +\x38\x37\x2c\x32\x37\x37\x2e\x34\x33\x2c\x37\x38\x2e\x34\x38\x2c\ +\x32\x39\x30\x2e\x31\x38\x2c\x37\x37\x5a\x22\x2f\x3e\x3c\x70\x61\ +\x74\x68\x20\x63\x6c\x61\x73\x73\x3d\x22\x63\x6c\x73\x2d\x31\x22\ +\x20\x64\x3d\x22\x4d\x34\x36\x39\x2e\x38\x37\x2c\x33\x33\x32\x2e\ +\x32\x31\x63\x2d\x31\x31\x2c\x2e\x31\x38\x2d\x31\x37\x2e\x38\x33\ +\x2d\x33\x2e\x30\x37\x2d\x32\x33\x2e\x35\x32\x2d\x39\x2e\x31\x32\ +\x43\x34\x31\x34\x2c\x32\x38\x38\x2e\x36\x35\x2c\x33\x37\x35\x2e\ +\x32\x32\x2c\x32\x36\x37\x2e\x31\x36\x2c\x33\x33\x30\x2e\x31\x2c\ +\x32\x36\x30\x2e\x36\x38\x63\x2d\x36\x37\x2e\x33\x37\x2d\x39\x2e\ +\x36\x37\x2d\x31\x32\x36\x2e\x31\x37\x2c\x31\x30\x2e\x38\x33\x2d\ +\x31\x37\x35\x2e\x33\x39\x2c\x36\x31\x2e\x34\x31\x2d\x31\x36\x2e\ +\x33\x35\x2c\x31\x36\x2e\x38\x2d\x34\x30\x2e\x36\x37\x2c\x31\x32\ +\x2d\x34\x37\x2e\x39\x31\x2d\x31\x30\x2d\x33\x2e\x39\x2d\x31\x31\ +\x2e\x39\x2d\x31\x2e\x33\x38\x2d\x32\x32\x2e\x38\x2c\x36\x2e\x38\ +\x39\x2d\x33\x31\x2e\x35\x32\x2c\x34\x31\x2d\x34\x33\x2e\x32\x34\ +\x2c\x38\x39\x2e\x37\x35\x2d\x37\x30\x2e\x34\x38\x2c\x31\x34\x36\ +\x2e\x38\x34\x2d\x37\x39\x2e\x32\x38\x2c\x37\x35\x2e\x36\x2d\x31\ +\x31\x2e\x36\x36\x2c\x31\x34\x33\x2e\x36\x39\x2c\x38\x2e\x30\x35\ +\x2c\x32\x30\x33\x2e\x39\x31\x2c\x35\x38\x2e\x33\x36\x61\x32\x30\ +\x35\x2e\x37\x34\x2c\x32\x30\x35\x2e\x37\x34\x2c\x30\x2c\x30\x2c\ +\x31\x2c\x32\x33\x2e\x32\x35\x2c\x32\x32\x2e\x36\x39\x63\x38\x2e\ +\x30\x38\x2c\x39\x2e\x33\x2c\x39\x2e\x35\x2c\x32\x30\x2e\x36\x32\ +\x2c\x34\x2e\x35\x39\x2c\x33\x32\x2e\x33\x34\x53\x34\x37\x38\x2e\ +\x36\x32\x2c\x33\x33\x31\x2e\x36\x36\x2c\x34\x36\x39\x2e\x38\x37\ +\x2c\x33\x33\x32\x2e\x32\x31\x5a\x22\x2f\x3e\x3c\x70\x61\x74\x68\ +\x20\x63\x6c\x61\x73\x73\x3d\x22\x63\x6c\x73\x2d\x32\x22\x20\x64\ +\x3d\x22\x4d\x31\x38\x34\x2e\x35\x32\x2c\x33\x38\x37\x2e\x34\x63\ +\x30\x2d\x39\x2e\x32\x32\x2c\x33\x2e\x35\x37\x2d\x31\x36\x2e\x36\ +\x35\x2c\x39\x2e\x36\x31\x2d\x32\x32\x2e\x38\x31\x43\x32\x31\x38\ +\x2c\x33\x34\x30\x2e\x32\x34\x2c\x32\x34\x36\x2c\x33\x32\x34\x2e\ +\x37\x38\x2c\x32\x37\x38\x2e\x37\x37\x2c\x33\x32\x30\x2e\x31\x35\ +\x63\x34\x38\x2e\x36\x36\x2d\x36\x2e\x38\x36\x2c\x39\x30\x2e\x38\ +\x33\x2c\x38\x2e\x32\x35\x2c\x31\x32\x36\x2e\x36\x33\x2c\x34\x34\ +\x2c\x31\x30\x2e\x31\x38\x2c\x31\x30\x2e\x31\x35\x2c\x31\x32\x2e\ +\x38\x31\x2c\x32\x34\x2c\x37\x2e\x34\x35\x2c\x33\x36\x2e\x30\x35\ +\x2d\x38\x2e\x34\x34\x2c\x31\x39\x2d\x33\x31\x2c\x32\x33\x2e\x34\ +\x35\x2d\x34\x35\x2e\x33\x32\x2c\x38\x2e\x36\x36\x2d\x31\x33\x2e\ +\x34\x2d\x31\x33\x2e\x38\x33\x2d\x32\x38\x2e\x38\x2d\x32\x33\x2e\ +\x36\x33\x2d\x34\x37\x2e\x31\x31\x2d\x32\x37\x2e\x35\x34\x2d\x33\ +\x33\x2e\x32\x32\x2d\x37\x2e\x31\x2d\x36\x32\x2e\x33\x36\x2c\x31\ +\x2e\x36\x35\x2d\x38\x37\x2c\x32\x36\x2e\x37\x37\x2d\x31\x36\x2e\ +\x36\x36\x2c\x31\x37\x2d\x34\x32\x2e\x33\x2c\x31\x30\x2d\x34\x37\ +\x2e\x39\x33\x2d\x31\x33\x2e\x32\x37\x41\x36\x39\x2e\x32\x38\x2c\ +\x36\x39\x2e\x32\x38\x2c\x30\x2c\x30\x2c\x31\x2c\x31\x38\x34\x2e\ +\x35\x32\x2c\x33\x38\x37\x2e\x34\x5a\x22\x2f\x3e\x3c\x70\x61\x74\ +\x68\x20\x63\x6c\x61\x73\x73\x3d\x22\x63\x6c\x73\x2d\x32\x22\x20\ +\x64\x3d\x22\x4d\x33\x30\x30\x2c\x35\x32\x33\x63\x2d\x32\x32\x2c\ +\x30\x2d\x33\x39\x2e\x31\x35\x2d\x31\x38\x2e\x34\x36\x2d\x33\x39\ +\x2e\x31\x31\x2d\x34\x32\x2e\x31\x31\x2c\x30\x2d\x32\x33\x2e\x33\ +\x38\x2c\x31\x37\x2e\x31\x39\x2d\x34\x31\x2e\x38\x33\x2c\x33\x38\ +\x2e\x38\x36\x2d\x34\x31\x2e\x38\x2c\x32\x32\x2e\x32\x35\x2c\x30\ +\x2c\x33\x39\x2e\x33\x32\x2c\x31\x38\x2e\x31\x38\x2c\x33\x39\x2e\ +\x33\x34\x2c\x34\x31\x2e\x38\x31\x53\x33\x32\x32\x2e\x31\x2c\x35\ +\x32\x33\x2c\x33\x30\x30\x2c\x35\x32\x33\x5a\x22\x2f\x3e\x3c\x2f\ +\x73\x76\x67\x3e\ +\x00\x00\x0a\x70\ +\x3c\ +\x73\x76\x67\x20\x69\x64\x3d\x22\x4c\x61\x79\x65\x72\x5f\x31\x22\ +\x20\x64\x61\x74\x61\x2d\x6e\x61\x6d\x65\x3d\x22\x4c\x61\x79\x65\ +\x72\x20\x31\x22\x20\x78\x6d\x6c\x6e\x73\x3d\x22\x68\x74\x74\x70\ +\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x32\x30\ +\x30\x30\x2f\x73\x76\x67\x22\x20\x76\x69\x65\x77\x42\x6f\x78\x3d\ +\x22\x30\x20\x30\x20\x36\x30\x30\x20\x36\x30\x30\x22\x3e\x3c\x64\ +\x65\x66\x73\x3e\x3c\x73\x74\x79\x6c\x65\x3e\x2e\x63\x6c\x73\x2d\ +\x31\x7b\x66\x69\x6c\x6c\x3a\x23\x64\x30\x64\x32\x64\x33\x3b\x7d\ +\x2e\x63\x6c\x73\x2d\x32\x7b\x66\x69\x6c\x6c\x3a\x23\x38\x63\x63\ +\x35\x34\x30\x3b\x7d\x2e\x63\x6c\x73\x2d\x33\x7b\x66\x69\x6c\x6c\ +\x3a\x23\x39\x32\x39\x34\x39\x37\x3b\x7d\x3c\x2f\x73\x74\x79\x6c\ +\x65\x3e\x3c\x2f\x64\x65\x66\x73\x3e\x3c\x70\x61\x74\x68\x20\x63\ +\x6c\x61\x73\x73\x3d\x22\x63\x6c\x73\x2d\x31\x22\x20\x64\x3d\x22\ +\x4d\x32\x39\x30\x2e\x31\x38\x2c\x37\x31\x2e\x33\x35\x43\x34\x30\ +\x32\x2c\x37\x33\x2e\x31\x34\x2c\x34\x39\x30\x2e\x31\x2c\x31\x31\ +\x31\x2e\x34\x34\x2c\x35\x36\x33\x2e\x39\x2c\x31\x38\x37\x2e\x34\ +\x35\x63\x31\x34\x2e\x38\x2c\x31\x35\x2e\x32\x35\x2c\x31\x31\x2e\ +\x38\x31\x2c\x33\x33\x2e\x39\x33\x2c\x33\x2e\x32\x36\x2c\x34\x34\ +\x2e\x30\x35\x2d\x31\x31\x2e\x31\x32\x2c\x31\x33\x2e\x31\x36\x2d\ +\x32\x38\x2e\x36\x32\x2c\x31\x33\x2d\x34\x31\x2e\x34\x36\x2e\x38\ +\x33\x2d\x31\x35\x2e\x30\x38\x2d\x31\x34\x2e\x32\x37\x2d\x33\x30\ +\x2e\x32\x2d\x32\x38\x2e\x37\x32\x2d\x34\x36\x2e\x36\x31\x2d\x34\ +\x31\x2e\x31\x31\x2d\x33\x38\x2e\x34\x2d\x32\x39\x2d\x38\x31\x2e\ +\x33\x33\x2d\x34\x36\x2e\x37\x34\x2d\x31\x32\x37\x2e\x37\x35\x2d\ +\x35\x34\x2e\x36\x34\x2d\x35\x34\x2d\x39\x2e\x32\x2d\x31\x30\x36\ +\x2e\x39\x32\x2d\x34\x2e\x33\x32\x2d\x31\x35\x38\x2e\x35\x2c\x31\ +\x35\x2e\x32\x32\x2d\x34\x35\x2c\x31\x37\x2d\x38\x34\x2e\x32\x39\ +\x2c\x34\x33\x2e\x39\x33\x2d\x31\x31\x38\x2e\x31\x36\x2c\x37\x39\ +\x2e\x39\x34\x2d\x38\x2e\x35\x37\x2c\x39\x2e\x31\x2d\x31\x38\x2e\ +\x36\x35\x2c\x31\x32\x2e\x33\x32\x2d\x33\x30\x2e\x31\x39\x2c\x38\ +\x2d\x32\x30\x2e\x30\x39\x2d\x37\x2e\x35\x36\x2d\x32\x35\x2e\x32\ +\x2d\x33\x34\x2e\x30\x38\x2d\x39\x2e\x37\x34\x2d\x35\x30\x2e\x36\ +\x31\x61\x33\x38\x30\x2e\x35\x31\x2c\x33\x38\x30\x2e\x35\x31\x2c\ +\x30\x2c\x30\x2c\x31\x2c\x35\x37\x2e\x36\x32\x2d\x35\x30\x2e\x33\ +\x38\x63\x34\x33\x2d\x33\x30\x2e\x35\x38\x2c\x38\x39\x2e\x39\x33\ +\x2d\x35\x31\x2e\x31\x2c\x31\x34\x30\x2e\x37\x37\x2d\x36\x30\x2e\ +\x34\x35\x43\x32\x35\x35\x2e\x30\x37\x2c\x37\x34\x2e\x32\x33\x2c\ +\x32\x37\x37\x2e\x34\x33\x2c\x37\x32\x2e\x38\x35\x2c\x32\x39\x30\ +\x2e\x31\x38\x2c\x37\x31\x2e\x33\x35\x5a\x22\x2f\x3e\x3c\x70\x61\ +\x74\x68\x20\x63\x6c\x61\x73\x73\x3d\x22\x63\x6c\x73\x2d\x32\x22\ +\x20\x64\x3d\x22\x4d\x33\x30\x30\x2c\x35\x31\x37\x2e\x33\x37\x63\ +\x2d\x32\x32\x2c\x30\x2d\x33\x39\x2e\x31\x35\x2d\x31\x38\x2e\x34\ +\x35\x2d\x33\x39\x2e\x31\x31\x2d\x34\x32\x2e\x31\x31\x2c\x30\x2d\ +\x32\x33\x2e\x33\x37\x2c\x31\x37\x2e\x31\x39\x2d\x34\x31\x2e\x38\ +\x33\x2c\x33\x38\x2e\x38\x36\x2d\x34\x31\x2e\x37\x39\x2c\x32\x32\ +\x2e\x32\x35\x2c\x30\x2c\x33\x39\x2e\x33\x32\x2c\x31\x38\x2e\x31\ +\x37\x2c\x33\x39\x2e\x33\x34\x2c\x34\x31\x2e\x38\x31\x53\x33\x32\ +\x32\x2e\x31\x2c\x35\x31\x37\x2e\x33\x37\x2c\x33\x30\x30\x2c\x35\ +\x31\x37\x2e\x33\x37\x5a\x22\x2f\x3e\x3c\x70\x61\x74\x68\x20\x63\ +\x6c\x61\x73\x73\x3d\x22\x63\x6c\x73\x2d\x33\x22\x20\x64\x3d\x22\ +\x4d\x34\x34\x39\x2e\x30\x37\x2c\x32\x38\x30\x2e\x33\x37\x68\x31\ +\x34\x2e\x33\x35\x61\x31\x30\x2e\x36\x34\x2c\x31\x30\x2e\x36\x34\ +\x2c\x30\x2c\x30\x2c\x30\x2c\x32\x2e\x34\x32\x2c\x31\x63\x32\x37\ +\x2e\x34\x37\x2c\x34\x2c\x34\x39\x2e\x36\x2c\x32\x36\x2e\x36\x37\ +\x2c\x35\x31\x2e\x36\x38\x2c\x35\x34\x2e\x32\x36\x2c\x31\x2e\x31\ +\x37\x2c\x31\x35\x2e\x35\x33\x2e\x35\x38\x2c\x33\x31\x2e\x32\x2e\ +\x37\x38\x2c\x34\x36\x2e\x38\x2c\x30\x2c\x32\x2e\x31\x32\x2c\x30\ +\x2c\x34\x2e\x32\x35\x2c\x30\x2c\x36\x2e\x38\x36\x68\x31\x31\x2e\ +\x37\x35\x63\x31\x33\x2e\x39\x34\x2c\x30\x2c\x31\x39\x2e\x33\x36\ +\x2c\x35\x2e\x33\x39\x2c\x31\x39\x2e\x33\x36\x2c\x31\x39\x2e\x32\ +\x32\x2c\x30\x2c\x33\x33\x2e\x32\x35\x2d\x2e\x31\x38\x2c\x36\x36\ +\x2e\x35\x2e\x31\x32\x2c\x39\x39\x2e\x37\x35\x2e\x30\x39\x2c\x39\ +\x2e\x36\x38\x2d\x32\x2e\x39\x33\x2c\x31\x36\x2e\x36\x37\x2d\x31\ +\x32\x2e\x31\x36\x2c\x32\x30\x2e\x33\x39\x48\x33\x37\x35\x2e\x31\ +\x32\x63\x2d\x37\x2e\x32\x34\x2d\x33\x2e\x31\x37\x2d\x31\x32\x2d\ +\x38\x2e\x31\x31\x2d\x31\x32\x2d\x31\x36\x2e\x35\x33\x2c\x30\x2d\ +\x33\x35\x2e\x34\x36\x2d\x2e\x30\x38\x2d\x37\x30\x2e\x39\x31\x2c\ +\x30\x2d\x31\x30\x36\x2e\x33\x37\x2c\x30\x2d\x31\x30\x2e\x32\x39\ +\x2c\x36\x2e\x32\x34\x2d\x31\x36\x2e\x32\x35\x2c\x31\x36\x2e\x35\ +\x36\x2d\x31\x36\x2e\x34\x34\x2c\x34\x2e\x36\x39\x2d\x2e\x30\x39\ +\x2c\x39\x2e\x33\x39\x2c\x30\x2c\x31\x34\x2e\x35\x31\x2c\x30\x2c\ +\x30\x2d\x31\x35\x2e\x37\x33\x2d\x2e\x30\x38\x2d\x33\x30\x2e\x35\ +\x39\x2c\x30\x2d\x34\x35\x2e\x34\x35\x2e\x31\x39\x2d\x32\x38\x2e\ +\x37\x39\x2c\x31\x37\x2e\x32\x38\x2d\x35\x32\x2e\x33\x36\x2c\x34\ +\x33\x2e\x38\x39\x2d\x36\x30\x2e\x35\x38\x43\x34\x34\x31\x2e\x37\ +\x31\x2c\x32\x38\x32\x2e\x31\x34\x2c\x34\x34\x35\x2e\x34\x31\x2c\ +\x32\x38\x31\x2e\x33\x33\x2c\x34\x34\x39\x2e\x30\x37\x2c\x32\x38\ +\x30\x2e\x33\x37\x5a\x6d\x33\x38\x2e\x33\x31\x2c\x31\x30\x38\x2e\ +\x34\x39\x63\x30\x2d\x31\x36\x2e\x35\x34\x2e\x39\x34\x2d\x33\x32\ +\x2e\x37\x33\x2d\x2e\x32\x35\x2d\x34\x38\x2e\x37\x37\x2d\x31\x2e\ +\x33\x31\x2d\x31\x37\x2e\x38\x32\x2d\x31\x35\x2e\x39\x33\x2d\x32\ +\x39\x2e\x37\x35\x2d\x33\x32\x2e\x37\x37\x2d\x32\x38\x2e\x37\x37\ +\x2d\x31\x36\x2e\x36\x35\x2c\x31\x2d\x32\x39\x2e\x32\x31\x2c\x31\ +\x34\x2e\x37\x33\x2d\x32\x39\x2e\x34\x32\x2c\x33\x32\x2e\x35\x32\ +\x2d\x2e\x31\x36\x2c\x31\x33\x2e\x37\x37\x2c\x30\x2c\x32\x37\x2e\ +\x35\x34\x2c\x30\x2c\x34\x31\x2e\x33\x31\x61\x33\x33\x2e\x31\x39\ +\x2c\x33\x33\x2e\x31\x39\x2c\x30\x2c\x30\x2c\x30\x2c\x2e\x34\x32\ +\x2c\x33\x2e\x37\x31\x5a\x6d\x2d\x33\x31\x2e\x33\x33\x2c\x34\x36\ +\x2e\x37\x31\x61\x31\x34\x2e\x39\x31\x2c\x31\x34\x2e\x39\x31\x2c\ +\x30\x2c\x30\x2c\x30\x2d\x31\x33\x2e\x36\x38\x2c\x38\x2e\x36\x36\ +\x63\x2d\x32\x2e\x38\x32\x2c\x35\x2e\x35\x37\x2d\x32\x2e\x38\x2c\ +\x31\x31\x2e\x35\x39\x2c\x31\x2e\x36\x2c\x31\x35\x2e\x38\x35\x2c\ +\x34\x2e\x31\x36\x2c\x34\x2c\x34\x2e\x34\x34\x2c\x38\x2e\x33\x35\ +\x2c\x34\x2e\x32\x32\x2c\x31\x33\x2e\x33\x32\x61\x36\x38\x2e\x33\ +\x38\x2c\x36\x38\x2e\x33\x38\x2c\x30\x2c\x30\x2c\x30\x2c\x30\x2c\ +\x37\x2e\x37\x31\x63\x2e\x33\x37\x2c\x35\x2e\x33\x33\x2c\x33\x2e\ +\x36\x2c\x38\x2e\x38\x33\x2c\x38\x2c\x38\x2e\x38\x37\x73\x37\x2e\ +\x37\x35\x2d\x33\x2e\x34\x36\x2c\x38\x2e\x30\x37\x2d\x38\x2e\x37\ +\x36\x61\x31\x31\x31\x2e\x34\x34\x2c\x31\x31\x31\x2e\x34\x34\x2c\ +\x30\x2c\x30\x2c\x30\x2c\x30\x2d\x31\x31\x2e\x35\x36\x2c\x39\x2e\ +\x36\x38\x2c\x39\x2e\x36\x38\x2c\x30\x2c\x30\x2c\x31\x2c\x33\x2e\ +\x32\x2d\x38\x2e\x31\x33\x63\x34\x2e\x37\x31\x2d\x34\x2e\x36\x34\ +\x2c\x35\x2e\x36\x31\x2d\x31\x30\x2e\x35\x33\x2c\x33\x2d\x31\x36\ +\x2e\x37\x32\x41\x31\x35\x2e\x34\x38\x2c\x31\x35\x2e\x34\x38\x2c\ +\x30\x2c\x30\x2c\x30\x2c\x34\x35\x36\x2e\x30\x35\x2c\x34\x33\x35\ +\x2e\x35\x37\x5a\x22\x2f\x3e\x3c\x70\x61\x74\x68\x20\x63\x6c\x61\ +\x73\x73\x3d\x22\x63\x6c\x73\x2d\x32\x22\x20\x64\x3d\x22\x4d\x33\ +\x37\x39\x2e\x35\x38\x2c\x33\x38\x34\x2e\x33\x31\x63\x33\x2e\x31\ +\x38\x2d\x2e\x30\x36\x2c\x36\x2e\x33\x31\x2d\x2e\x30\x35\x2c\x39\ +\x2e\x36\x31\x2c\x30\x2c\x30\x2d\x33\x2e\x32\x34\x2c\x30\x2d\x36\ +\x2e\x34\x33\x2c\x30\x2d\x39\x2e\x36\x31\x2c\x30\x2d\x31\x30\x2e\ +\x35\x33\x2c\x30\x2d\x32\x30\x2e\x34\x38\x2c\x30\x2d\x33\x30\x2e\ +\x35\x38\x2d\x33\x32\x2e\x31\x34\x2d\x32\x35\x2e\x32\x37\x2d\x36\ +\x38\x2e\x39\x33\x2d\x33\x35\x2e\x34\x32\x2d\x31\x31\x30\x2e\x34\ +\x34\x2d\x32\x39\x2e\x35\x37\x43\x32\x34\x36\x2c\x33\x31\x39\x2e\ +\x31\x34\x2c\x32\x31\x38\x2c\x33\x33\x34\x2e\x36\x31\x2c\x31\x39\ +\x34\x2e\x31\x33\x2c\x33\x35\x39\x63\x2d\x36\x2c\x36\x2e\x31\x37\ +\x2d\x39\x2e\x36\x33\x2c\x31\x33\x2e\x35\x39\x2d\x39\x2e\x36\x31\ +\x2c\x32\x32\x2e\x38\x31\x61\x36\x38\x2e\x39\x33\x2c\x36\x38\x2e\ +\x39\x33\x2c\x30\x2c\x30\x2c\x30\x2c\x31\x2c\x37\x2e\x33\x38\x63\ +\x35\x2e\x36\x33\x2c\x32\x33\x2e\x32\x38\x2c\x33\x31\x2e\x32\x37\ +\x2c\x33\x30\x2e\x32\x38\x2c\x34\x37\x2e\x39\x33\x2c\x31\x33\x2e\ +\x32\x37\x2c\x32\x34\x2e\x36\x31\x2d\x32\x35\x2e\x31\x31\x2c\x35\ +\x33\x2e\x37\x35\x2d\x33\x33\x2e\x38\x36\x2c\x38\x37\x2d\x32\x36\ +\x2e\x37\x37\x2c\x31\x35\x2c\x33\x2e\x32\x2c\x32\x38\x2c\x31\x30\ +\x2e\x33\x35\x2c\x33\x39\x2e\x36\x2c\x32\x30\x2e\x34\x32\x43\x33\ +\x36\x33\x2e\x31\x37\x2c\x33\x38\x38\x2e\x38\x32\x2c\x33\x37\x30\ +\x2e\x30\x37\x2c\x33\x38\x34\x2e\x34\x39\x2c\x33\x37\x39\x2e\x35\ +\x38\x2c\x33\x38\x34\x2e\x33\x31\x5a\x22\x2f\x3e\x3c\x70\x61\x74\ +\x68\x20\x63\x6c\x61\x73\x73\x3d\x22\x63\x6c\x73\x2d\x31\x22\x20\ +\x64\x3d\x22\x4d\x34\x35\x34\x2e\x36\x35\x2c\x33\x31\x36\x2e\x33\ +\x31\x61\x32\x36\x2e\x31\x32\x2c\x32\x36\x2e\x31\x32\x2c\x30\x2c\ +\x30\x2c\x30\x2d\x37\x2e\x37\x39\x2c\x31\x2e\x36\x36\x63\x35\x2e\ +\x35\x39\x2c\x35\x2e\x37\x32\x2c\x31\x32\x2e\x33\x34\x2c\x38\x2e\ +\x37\x38\x2c\x32\x33\x2c\x38\x2e\x36\x31\x61\x32\x34\x2c\x32\x34\ +\x2c\x30\x2c\x30\x2c\x30\x2c\x36\x2e\x32\x35\x2d\x31\x2e\x32\x37\ +\x41\x32\x35\x2e\x36\x38\x2c\x32\x35\x2e\x36\x38\x2c\x30\x2c\x30\ +\x2c\x30\x2c\x34\x35\x34\x2e\x36\x35\x2c\x33\x31\x36\x2e\x33\x31\ +\x5a\x22\x2f\x3e\x3c\x70\x61\x74\x68\x20\x63\x6c\x61\x73\x73\x3d\ +\x22\x63\x6c\x73\x2d\x31\x22\x20\x64\x3d\x22\x4d\x34\x33\x36\x2e\ +\x36\x32\x2c\x32\x37\x38\x2e\x34\x38\x63\x32\x2e\x36\x31\x2d\x2e\ +\x38\x2c\x35\x2e\x32\x33\x2d\x31\x2e\x34\x36\x2c\x37\x2e\x37\x37\ +\x2d\x32\x2e\x30\x39\x2c\x31\x2e\x31\x34\x2d\x2e\x32\x38\x2c\x32\ +\x2e\x32\x38\x2d\x2e\x35\x36\x2c\x33\x2e\x34\x32\x2d\x2e\x38\x36\ +\x6c\x2e\x36\x32\x2d\x2e\x31\x36\x68\x31\x36\x6c\x2e\x39\x33\x2e\ +\x33\x39\x63\x2e\x33\x36\x2e\x31\x35\x2e\x37\x32\x2e\x33\x33\x2c\ +\x31\x2e\x30\x37\x2e\x35\x6c\x2e\x33\x37\x2e\x31\x39\x61\x36\x36\ +\x2e\x31\x38\x2c\x36\x36\x2e\x31\x38\x2c\x30\x2c\x30\x2c\x31\x2c\ +\x32\x36\x2e\x38\x33\x2c\x31\x30\x2e\x33\x38\x2c\x33\x30\x2e\x33\ +\x35\x2c\x33\x30\x2e\x33\x35\x2c\x30\x2c\x30\x2c\x30\x2d\x35\x2e\ +\x39\x34\x2d\x31\x30\x2e\x31\x35\x41\x32\x30\x35\x2c\x32\x30\x35\ +\x2c\x30\x2c\x30\x2c\x30\x2c\x34\x36\x34\x2e\x34\x34\x2c\x32\x35\ +\x34\x63\x2d\x36\x30\x2e\x32\x32\x2d\x35\x30\x2e\x33\x31\x2d\x31\ +\x32\x38\x2e\x33\x31\x2d\x37\x30\x2d\x32\x30\x33\x2e\x39\x31\x2d\ +\x35\x38\x2e\x33\x36\x2d\x35\x37\x2e\x30\x39\x2c\x38\x2e\x38\x2d\ +\x31\x30\x35\x2e\x38\x34\x2c\x33\x36\x2d\x31\x34\x36\x2e\x38\x34\ +\x2c\x37\x39\x2e\x32\x38\x2d\x38\x2e\x32\x37\x2c\x38\x2e\x37\x32\ +\x2d\x31\x30\x2e\x37\x39\x2c\x31\x39\x2e\x36\x32\x2d\x36\x2e\x38\ +\x39\x2c\x33\x31\x2e\x35\x32\x2c\x37\x2e\x32\x34\x2c\x32\x32\x2c\ +\x33\x31\x2e\x35\x36\x2c\x32\x36\x2e\x38\x33\x2c\x34\x37\x2e\x39\ +\x31\x2c\x31\x30\x2c\x34\x39\x2e\x32\x32\x2d\x35\x30\x2e\x35\x37\ +\x2c\x31\x30\x38\x2d\x37\x31\x2e\x30\x37\x2c\x31\x37\x35\x2e\x33\ +\x39\x2d\x36\x31\x2e\x34\x41\x31\x38\x37\x2e\x39\x32\x2c\x31\x38\ +\x37\x2e\x39\x32\x2c\x30\x2c\x30\x2c\x31\x2c\x34\x31\x35\x2c\x32\ +\x38\x39\x2e\x36\x38\x2c\x36\x37\x2e\x34\x39\x2c\x36\x37\x2e\x34\ +\x39\x2c\x30\x2c\x30\x2c\x31\x2c\x34\x33\x36\x2e\x36\x32\x2c\x32\ +\x37\x38\x2e\x34\x38\x5a\x22\x2f\x3e\x3c\x2f\x73\x76\x67\x3e\ \x00\x00\x06\x37\ \x3c\ \x73\x76\x67\x20\x69\x64\x3d\x22\x4c\x61\x79\x65\x72\x5f\x31\x22\ @@ -20523,6 +20958,696 @@ \x39\x38\x2e\x38\x31\x4c\x32\x37\x31\x2c\x33\x30\x30\x2e\x38\x38\ \x2c\x39\x38\x2e\x38\x33\x2c\x36\x31\x2e\x39\x31\x5a\x22\x2f\x3e\ \x3c\x2f\x73\x76\x67\x3e\ +\x00\x00\x0a\x76\ +\x3c\ +\x73\x76\x67\x20\x69\x64\x3d\x22\x4c\x61\x79\x65\x72\x5f\x31\x22\ +\x20\x64\x61\x74\x61\x2d\x6e\x61\x6d\x65\x3d\x22\x4c\x61\x79\x65\ +\x72\x20\x31\x22\x20\x78\x6d\x6c\x6e\x73\x3d\x22\x68\x74\x74\x70\ +\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x32\x30\ +\x30\x30\x2f\x73\x76\x67\x22\x20\x76\x69\x65\x77\x42\x6f\x78\x3d\ +\x22\x30\x20\x30\x20\x36\x30\x30\x20\x36\x30\x30\x22\x3e\x3c\x64\ +\x65\x66\x73\x3e\x3c\x73\x74\x79\x6c\x65\x3e\x2e\x63\x6c\x73\x2d\ +\x31\x7b\x66\x69\x6c\x6c\x3a\x23\x38\x63\x63\x35\x34\x30\x3b\x7d\ +\x2e\x63\x6c\x73\x2d\x32\x7b\x66\x69\x6c\x6c\x3a\x23\x64\x30\x64\ +\x32\x64\x33\x3b\x7d\x2e\x63\x6c\x73\x2d\x33\x7b\x66\x69\x6c\x6c\ +\x3a\x23\x39\x32\x39\x34\x39\x37\x3b\x7d\x3c\x2f\x73\x74\x79\x6c\ +\x65\x3e\x3c\x2f\x64\x65\x66\x73\x3e\x3c\x70\x61\x74\x68\x20\x63\ +\x6c\x61\x73\x73\x3d\x22\x63\x6c\x73\x2d\x31\x22\x20\x64\x3d\x22\ +\x4d\x34\x34\x37\x2e\x31\x37\x2c\x33\x33\x34\x2e\x38\x35\x61\x32\ +\x36\x2e\x31\x32\x2c\x32\x36\x2e\x31\x32\x2c\x30\x2c\x30\x2c\x30\ +\x2d\x37\x2e\x37\x39\x2c\x31\x2e\x36\x36\x63\x35\x2e\x35\x39\x2c\ +\x35\x2e\x37\x32\x2c\x31\x32\x2e\x33\x34\x2c\x38\x2e\x37\x38\x2c\ +\x32\x33\x2c\x38\x2e\x36\x31\x61\x32\x33\x2e\x35\x35\x2c\x32\x33\ +\x2e\x35\x35\x2c\x30\x2c\x30\x2c\x30\x2c\x36\x2e\x32\x35\x2d\x31\ +\x2e\x32\x37\x41\x32\x35\x2e\x37\x31\x2c\x32\x35\x2e\x37\x31\x2c\ +\x30\x2c\x30\x2c\x30\x2c\x34\x34\x37\x2e\x31\x37\x2c\x33\x33\x34\ +\x2e\x38\x35\x5a\x22\x2f\x3e\x3c\x70\x61\x74\x68\x20\x63\x6c\x61\ +\x73\x73\x3d\x22\x63\x6c\x73\x2d\x32\x22\x20\x64\x3d\x22\x4d\x32\ +\x39\x30\x2e\x31\x38\x2c\x37\x31\x2e\x33\x35\x43\x34\x30\x32\x2c\ +\x37\x33\x2e\x31\x34\x2c\x34\x39\x30\x2e\x31\x2c\x31\x31\x31\x2e\ +\x34\x34\x2c\x35\x36\x33\x2e\x39\x2c\x31\x38\x37\x2e\x34\x35\x63\ +\x31\x34\x2e\x38\x2c\x31\x35\x2e\x32\x35\x2c\x31\x31\x2e\x38\x31\ +\x2c\x33\x33\x2e\x39\x33\x2c\x33\x2e\x32\x36\x2c\x34\x34\x2e\x30\ +\x35\x2d\x31\x31\x2e\x31\x32\x2c\x31\x33\x2e\x31\x36\x2d\x32\x38\ +\x2e\x36\x32\x2c\x31\x33\x2d\x34\x31\x2e\x34\x36\x2e\x38\x33\x2d\ +\x31\x35\x2e\x30\x38\x2d\x31\x34\x2e\x32\x37\x2d\x33\x30\x2e\x32\ +\x2d\x32\x38\x2e\x37\x32\x2d\x34\x36\x2e\x36\x31\x2d\x34\x31\x2e\ +\x31\x31\x2d\x33\x38\x2e\x34\x2d\x32\x39\x2d\x38\x31\x2e\x33\x33\ +\x2d\x34\x36\x2e\x37\x34\x2d\x31\x32\x37\x2e\x37\x35\x2d\x35\x34\ +\x2e\x36\x34\x2d\x35\x34\x2d\x39\x2e\x32\x2d\x31\x30\x36\x2e\x39\ +\x32\x2d\x34\x2e\x33\x32\x2d\x31\x35\x38\x2e\x35\x2c\x31\x35\x2e\ +\x32\x32\x2d\x34\x35\x2c\x31\x37\x2d\x38\x34\x2e\x32\x39\x2c\x34\ +\x33\x2e\x39\x33\x2d\x31\x31\x38\x2e\x31\x36\x2c\x37\x39\x2e\x39\ +\x34\x2d\x38\x2e\x35\x37\x2c\x39\x2e\x31\x2d\x31\x38\x2e\x36\x35\ +\x2c\x31\x32\x2e\x33\x32\x2d\x33\x30\x2e\x31\x39\x2c\x38\x2d\x32\ +\x30\x2e\x30\x39\x2d\x37\x2e\x35\x36\x2d\x32\x35\x2e\x32\x2d\x33\ +\x34\x2e\x30\x38\x2d\x39\x2e\x37\x34\x2d\x35\x30\x2e\x36\x31\x61\ +\x33\x38\x30\x2e\x35\x31\x2c\x33\x38\x30\x2e\x35\x31\x2c\x30\x2c\ +\x30\x2c\x31\x2c\x35\x37\x2e\x36\x32\x2d\x35\x30\x2e\x33\x38\x63\ +\x34\x33\x2d\x33\x30\x2e\x35\x38\x2c\x38\x39\x2e\x39\x33\x2d\x35\ +\x31\x2e\x31\x2c\x31\x34\x30\x2e\x37\x37\x2d\x36\x30\x2e\x34\x35\ +\x43\x32\x35\x35\x2e\x30\x37\x2c\x37\x34\x2e\x32\x33\x2c\x32\x37\ +\x37\x2e\x34\x33\x2c\x37\x32\x2e\x38\x35\x2c\x32\x39\x30\x2e\x31\ +\x38\x2c\x37\x31\x2e\x33\x35\x5a\x22\x2f\x3e\x3c\x70\x61\x74\x68\ +\x20\x63\x6c\x61\x73\x73\x3d\x22\x63\x6c\x73\x2d\x31\x22\x20\x64\ +\x3d\x22\x4d\x33\x30\x30\x2c\x35\x31\x37\x2e\x33\x37\x63\x2d\x32\ +\x32\x2c\x30\x2d\x33\x39\x2e\x31\x35\x2d\x31\x38\x2e\x34\x35\x2d\ +\x33\x39\x2e\x31\x31\x2d\x34\x32\x2e\x31\x31\x2c\x30\x2d\x32\x33\ +\x2e\x33\x37\x2c\x31\x37\x2e\x31\x39\x2d\x34\x31\x2e\x38\x33\x2c\ +\x33\x38\x2e\x38\x36\x2d\x34\x31\x2e\x37\x39\x2c\x32\x32\x2e\x32\ +\x35\x2c\x30\x2c\x33\x39\x2e\x33\x32\x2c\x31\x38\x2e\x31\x37\x2c\ +\x33\x39\x2e\x33\x34\x2c\x34\x31\x2e\x38\x31\x53\x33\x32\x32\x2e\ +\x31\x2c\x35\x31\x37\x2e\x33\x37\x2c\x33\x30\x30\x2c\x35\x31\x37\ +\x2e\x33\x37\x5a\x22\x2f\x3e\x3c\x70\x61\x74\x68\x20\x63\x6c\x61\ +\x73\x73\x3d\x22\x63\x6c\x73\x2d\x33\x22\x20\x64\x3d\x22\x4d\x34\ +\x34\x39\x2e\x30\x37\x2c\x32\x38\x30\x2e\x33\x37\x68\x31\x34\x2e\ +\x33\x35\x61\x31\x30\x2e\x36\x34\x2c\x31\x30\x2e\x36\x34\x2c\x30\ +\x2c\x30\x2c\x30\x2c\x32\x2e\x34\x32\x2c\x31\x63\x32\x37\x2e\x34\ +\x37\x2c\x34\x2c\x34\x39\x2e\x36\x2c\x32\x36\x2e\x36\x37\x2c\x35\ +\x31\x2e\x36\x38\x2c\x35\x34\x2e\x32\x36\x2c\x31\x2e\x31\x37\x2c\ +\x31\x35\x2e\x35\x33\x2e\x35\x38\x2c\x33\x31\x2e\x32\x2e\x37\x38\ +\x2c\x34\x36\x2e\x38\x2c\x30\x2c\x32\x2e\x31\x32\x2c\x30\x2c\x34\ +\x2e\x32\x35\x2c\x30\x2c\x36\x2e\x38\x36\x68\x31\x31\x2e\x37\x35\ +\x63\x31\x33\x2e\x39\x34\x2c\x30\x2c\x31\x39\x2e\x33\x36\x2c\x35\ +\x2e\x33\x39\x2c\x31\x39\x2e\x33\x36\x2c\x31\x39\x2e\x32\x32\x2c\ +\x30\x2c\x33\x33\x2e\x32\x35\x2d\x2e\x31\x38\x2c\x36\x36\x2e\x35\ +\x2e\x31\x32\x2c\x39\x39\x2e\x37\x35\x2e\x30\x39\x2c\x39\x2e\x36\ +\x38\x2d\x32\x2e\x39\x33\x2c\x31\x36\x2e\x36\x37\x2d\x31\x32\x2e\ +\x31\x36\x2c\x32\x30\x2e\x33\x39\x48\x33\x37\x35\x2e\x31\x32\x63\ +\x2d\x37\x2e\x32\x34\x2d\x33\x2e\x31\x37\x2d\x31\x32\x2d\x38\x2e\ +\x31\x31\x2d\x31\x32\x2d\x31\x36\x2e\x35\x33\x2c\x30\x2d\x33\x35\ +\x2e\x34\x36\x2d\x2e\x30\x38\x2d\x37\x30\x2e\x39\x31\x2c\x30\x2d\ +\x31\x30\x36\x2e\x33\x37\x2c\x30\x2d\x31\x30\x2e\x32\x39\x2c\x36\ +\x2e\x32\x34\x2d\x31\x36\x2e\x32\x35\x2c\x31\x36\x2e\x35\x36\x2d\ +\x31\x36\x2e\x34\x34\x2c\x34\x2e\x36\x39\x2d\x2e\x30\x39\x2c\x39\ +\x2e\x33\x39\x2c\x30\x2c\x31\x34\x2e\x35\x31\x2c\x30\x2c\x30\x2d\ +\x31\x35\x2e\x37\x33\x2d\x2e\x30\x38\x2d\x33\x30\x2e\x35\x39\x2c\ +\x30\x2d\x34\x35\x2e\x34\x35\x2e\x31\x39\x2d\x32\x38\x2e\x37\x39\ +\x2c\x31\x37\x2e\x32\x38\x2d\x35\x32\x2e\x33\x36\x2c\x34\x33\x2e\ +\x38\x39\x2d\x36\x30\x2e\x35\x38\x43\x34\x34\x31\x2e\x37\x31\x2c\ +\x32\x38\x32\x2e\x31\x34\x2c\x34\x34\x35\x2e\x34\x31\x2c\x32\x38\ +\x31\x2e\x33\x33\x2c\x34\x34\x39\x2e\x30\x37\x2c\x32\x38\x30\x2e\ +\x33\x37\x5a\x6d\x33\x38\x2e\x33\x31\x2c\x31\x30\x38\x2e\x34\x39\ +\x63\x30\x2d\x31\x36\x2e\x35\x34\x2e\x39\x34\x2d\x33\x32\x2e\x37\ +\x33\x2d\x2e\x32\x35\x2d\x34\x38\x2e\x37\x37\x2d\x31\x2e\x33\x31\ +\x2d\x31\x37\x2e\x38\x32\x2d\x31\x35\x2e\x39\x33\x2d\x32\x39\x2e\ +\x37\x35\x2d\x33\x32\x2e\x37\x37\x2d\x32\x38\x2e\x37\x37\x2d\x31\ +\x36\x2e\x36\x35\x2c\x31\x2d\x32\x39\x2e\x32\x31\x2c\x31\x34\x2e\ +\x37\x33\x2d\x32\x39\x2e\x34\x32\x2c\x33\x32\x2e\x35\x32\x2d\x2e\ +\x31\x36\x2c\x31\x33\x2e\x37\x37\x2c\x30\x2c\x32\x37\x2e\x35\x34\ +\x2c\x30\x2c\x34\x31\x2e\x33\x31\x61\x33\x33\x2e\x31\x39\x2c\x33\ +\x33\x2e\x31\x39\x2c\x30\x2c\x30\x2c\x30\x2c\x2e\x34\x32\x2c\x33\ +\x2e\x37\x31\x5a\x6d\x2d\x33\x31\x2e\x33\x33\x2c\x34\x36\x2e\x37\ +\x31\x61\x31\x34\x2e\x39\x31\x2c\x31\x34\x2e\x39\x31\x2c\x30\x2c\ +\x30\x2c\x30\x2d\x31\x33\x2e\x36\x38\x2c\x38\x2e\x36\x36\x63\x2d\ +\x32\x2e\x38\x32\x2c\x35\x2e\x35\x37\x2d\x32\x2e\x38\x2c\x31\x31\ +\x2e\x35\x39\x2c\x31\x2e\x36\x2c\x31\x35\x2e\x38\x35\x2c\x34\x2e\ +\x31\x36\x2c\x34\x2c\x34\x2e\x34\x34\x2c\x38\x2e\x33\x35\x2c\x34\ +\x2e\x32\x32\x2c\x31\x33\x2e\x33\x32\x61\x36\x38\x2e\x33\x38\x2c\ +\x36\x38\x2e\x33\x38\x2c\x30\x2c\x30\x2c\x30\x2c\x30\x2c\x37\x2e\ +\x37\x31\x63\x2e\x33\x37\x2c\x35\x2e\x33\x33\x2c\x33\x2e\x36\x2c\ +\x38\x2e\x38\x33\x2c\x38\x2c\x38\x2e\x38\x37\x73\x37\x2e\x37\x35\ +\x2d\x33\x2e\x34\x36\x2c\x38\x2e\x30\x37\x2d\x38\x2e\x37\x36\x61\ +\x31\x31\x31\x2e\x34\x34\x2c\x31\x31\x31\x2e\x34\x34\x2c\x30\x2c\ +\x30\x2c\x30\x2c\x30\x2d\x31\x31\x2e\x35\x36\x2c\x39\x2e\x36\x38\ +\x2c\x39\x2e\x36\x38\x2c\x30\x2c\x30\x2c\x31\x2c\x33\x2e\x32\x2d\ +\x38\x2e\x31\x33\x63\x34\x2e\x37\x31\x2d\x34\x2e\x36\x34\x2c\x35\ +\x2e\x36\x31\x2d\x31\x30\x2e\x35\x33\x2c\x33\x2d\x31\x36\x2e\x37\ +\x32\x41\x31\x35\x2e\x34\x38\x2c\x31\x35\x2e\x34\x38\x2c\x30\x2c\ +\x30\x2c\x30\x2c\x34\x35\x36\x2e\x30\x35\x2c\x34\x33\x35\x2e\x35\ +\x37\x5a\x22\x2f\x3e\x3c\x70\x61\x74\x68\x20\x63\x6c\x61\x73\x73\ +\x3d\x22\x63\x6c\x73\x2d\x31\x22\x20\x64\x3d\x22\x4d\x33\x37\x39\ +\x2e\x35\x38\x2c\x33\x38\x34\x2e\x33\x31\x63\x33\x2e\x31\x38\x2d\ +\x2e\x30\x36\x2c\x36\x2e\x33\x31\x2d\x2e\x30\x35\x2c\x39\x2e\x36\ +\x31\x2c\x30\x2c\x30\x2d\x33\x2e\x32\x34\x2c\x30\x2d\x36\x2e\x34\ +\x33\x2c\x30\x2d\x39\x2e\x36\x31\x2c\x30\x2d\x31\x30\x2e\x35\x33\ +\x2c\x30\x2d\x32\x30\x2e\x34\x38\x2c\x30\x2d\x33\x30\x2e\x35\x38\ +\x2d\x33\x32\x2e\x31\x34\x2d\x32\x35\x2e\x32\x37\x2d\x36\x38\x2e\ +\x39\x33\x2d\x33\x35\x2e\x34\x32\x2d\x31\x31\x30\x2e\x34\x34\x2d\ +\x32\x39\x2e\x35\x37\x43\x32\x34\x36\x2c\x33\x31\x39\x2e\x31\x34\ +\x2c\x32\x31\x38\x2c\x33\x33\x34\x2e\x36\x31\x2c\x31\x39\x34\x2e\ +\x31\x33\x2c\x33\x35\x39\x63\x2d\x36\x2c\x36\x2e\x31\x37\x2d\x39\ +\x2e\x36\x33\x2c\x31\x33\x2e\x35\x39\x2d\x39\x2e\x36\x31\x2c\x32\ +\x32\x2e\x38\x31\x61\x36\x38\x2e\x39\x33\x2c\x36\x38\x2e\x39\x33\ +\x2c\x30\x2c\x30\x2c\x30\x2c\x31\x2c\x37\x2e\x33\x38\x63\x35\x2e\ +\x36\x33\x2c\x32\x33\x2e\x32\x38\x2c\x33\x31\x2e\x32\x37\x2c\x33\ +\x30\x2e\x32\x38\x2c\x34\x37\x2e\x39\x33\x2c\x31\x33\x2e\x32\x37\ +\x2c\x32\x34\x2e\x36\x31\x2d\x32\x35\x2e\x31\x31\x2c\x35\x33\x2e\ +\x37\x35\x2d\x33\x33\x2e\x38\x36\x2c\x38\x37\x2d\x32\x36\x2e\x37\ +\x37\x2c\x31\x35\x2c\x33\x2e\x32\x2c\x32\x38\x2c\x31\x30\x2e\x33\ +\x35\x2c\x33\x39\x2e\x36\x2c\x32\x30\x2e\x34\x32\x43\x33\x36\x33\ +\x2e\x31\x37\x2c\x33\x38\x38\x2e\x38\x32\x2c\x33\x37\x30\x2e\x30\ +\x37\x2c\x33\x38\x34\x2e\x34\x39\x2c\x33\x37\x39\x2e\x35\x38\x2c\ +\x33\x38\x34\x2e\x33\x31\x5a\x22\x2f\x3e\x3c\x70\x61\x74\x68\x20\ +\x63\x6c\x61\x73\x73\x3d\x22\x63\x6c\x73\x2d\x31\x22\x20\x64\x3d\ +\x22\x4d\x34\x33\x36\x2e\x36\x32\x2c\x32\x37\x38\x2e\x34\x38\x63\ +\x32\x2e\x36\x31\x2d\x2e\x38\x2c\x35\x2e\x32\x33\x2d\x31\x2e\x34\ +\x36\x2c\x37\x2e\x37\x37\x2d\x32\x2e\x30\x39\x2c\x31\x2e\x31\x34\ +\x2d\x2e\x32\x38\x2c\x32\x2e\x32\x38\x2d\x2e\x35\x36\x2c\x33\x2e\ +\x34\x32\x2d\x2e\x38\x36\x6c\x2e\x36\x32\x2d\x2e\x31\x36\x68\x31\ +\x36\x6c\x2e\x39\x33\x2e\x33\x39\x63\x2e\x33\x36\x2e\x31\x35\x2e\ +\x37\x32\x2e\x33\x33\x2c\x31\x2e\x30\x37\x2e\x35\x6c\x2e\x33\x37\ +\x2e\x31\x39\x61\x36\x36\x2e\x31\x38\x2c\x36\x36\x2e\x31\x38\x2c\ +\x30\x2c\x30\x2c\x31\x2c\x32\x36\x2e\x38\x33\x2c\x31\x30\x2e\x33\ +\x38\x2c\x33\x30\x2e\x33\x35\x2c\x33\x30\x2e\x33\x35\x2c\x30\x2c\ +\x30\x2c\x30\x2d\x35\x2e\x39\x34\x2d\x31\x30\x2e\x31\x35\x41\x32\ +\x30\x35\x2c\x32\x30\x35\x2c\x30\x2c\x30\x2c\x30\x2c\x34\x36\x34\ +\x2e\x34\x34\x2c\x32\x35\x34\x63\x2d\x36\x30\x2e\x32\x32\x2d\x35\ +\x30\x2e\x33\x31\x2d\x31\x32\x38\x2e\x33\x31\x2d\x37\x30\x2d\x32\ +\x30\x33\x2e\x39\x31\x2d\x35\x38\x2e\x33\x36\x2d\x35\x37\x2e\x30\ +\x39\x2c\x38\x2e\x38\x2d\x31\x30\x35\x2e\x38\x34\x2c\x33\x36\x2d\ +\x31\x34\x36\x2e\x38\x34\x2c\x37\x39\x2e\x32\x38\x2d\x38\x2e\x32\ +\x37\x2c\x38\x2e\x37\x32\x2d\x31\x30\x2e\x37\x39\x2c\x31\x39\x2e\ +\x36\x32\x2d\x36\x2e\x38\x39\x2c\x33\x31\x2e\x35\x32\x2c\x37\x2e\ +\x32\x34\x2c\x32\x32\x2c\x33\x31\x2e\x35\x36\x2c\x32\x36\x2e\x38\ +\x33\x2c\x34\x37\x2e\x39\x31\x2c\x31\x30\x2c\x34\x39\x2e\x32\x32\ +\x2d\x35\x30\x2e\x35\x37\x2c\x31\x30\x38\x2d\x37\x31\x2e\x30\x37\ +\x2c\x31\x37\x35\x2e\x33\x39\x2d\x36\x31\x2e\x34\x41\x31\x38\x37\ +\x2e\x39\x32\x2c\x31\x38\x37\x2e\x39\x32\x2c\x30\x2c\x30\x2c\x31\ +\x2c\x34\x31\x35\x2c\x32\x38\x39\x2e\x36\x38\x2c\x36\x37\x2e\x34\ +\x39\x2c\x36\x37\x2e\x34\x39\x2c\x30\x2c\x30\x2c\x31\x2c\x34\x33\ +\x36\x2e\x36\x32\x2c\x32\x37\x38\x2e\x34\x38\x5a\x22\x2f\x3e\x3c\ +\x2f\x73\x76\x67\x3e\ +\x00\x00\x0a\x5b\ +\x3c\ +\x73\x76\x67\x20\x69\x64\x3d\x22\x4c\x61\x79\x65\x72\x5f\x31\x22\ +\x20\x64\x61\x74\x61\x2d\x6e\x61\x6d\x65\x3d\x22\x4c\x61\x79\x65\ +\x72\x20\x31\x22\x20\x78\x6d\x6c\x6e\x73\x3d\x22\x68\x74\x74\x70\ +\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x32\x30\ +\x30\x30\x2f\x73\x76\x67\x22\x20\x76\x69\x65\x77\x42\x6f\x78\x3d\ +\x22\x30\x20\x30\x20\x36\x30\x30\x20\x36\x30\x30\x22\x3e\x3c\x64\ +\x65\x66\x73\x3e\x3c\x73\x74\x79\x6c\x65\x3e\x2e\x63\x6c\x73\x2d\ +\x31\x7b\x66\x69\x6c\x6c\x3a\x23\x38\x63\x63\x35\x34\x30\x3b\x7d\ +\x2e\x63\x6c\x73\x2d\x32\x7b\x66\x69\x6c\x6c\x3a\x23\x39\x32\x39\ +\x34\x39\x37\x3b\x7d\x3c\x2f\x73\x74\x79\x6c\x65\x3e\x3c\x2f\x64\ +\x65\x66\x73\x3e\x3c\x70\x61\x74\x68\x20\x63\x6c\x61\x73\x73\x3d\ +\x22\x63\x6c\x73\x2d\x31\x22\x20\x64\x3d\x22\x4d\x32\x39\x30\x2e\ +\x31\x38\x2c\x37\x31\x2e\x33\x35\x43\x34\x30\x32\x2c\x37\x33\x2e\ +\x31\x34\x2c\x34\x39\x30\x2e\x31\x2c\x31\x31\x31\x2e\x34\x34\x2c\ +\x35\x36\x33\x2e\x39\x2c\x31\x38\x37\x2e\x34\x35\x63\x31\x34\x2e\ +\x38\x2c\x31\x35\x2e\x32\x35\x2c\x31\x31\x2e\x38\x31\x2c\x33\x33\ +\x2e\x39\x33\x2c\x33\x2e\x32\x36\x2c\x34\x34\x2e\x30\x35\x2d\x31\ +\x31\x2e\x31\x32\x2c\x31\x33\x2e\x31\x36\x2d\x32\x38\x2e\x36\x32\ +\x2c\x31\x33\x2d\x34\x31\x2e\x34\x36\x2e\x38\x33\x2d\x31\x35\x2e\ +\x30\x38\x2d\x31\x34\x2e\x32\x37\x2d\x33\x30\x2e\x32\x2d\x32\x38\ +\x2e\x37\x32\x2d\x34\x36\x2e\x36\x31\x2d\x34\x31\x2e\x31\x31\x2d\ +\x33\x38\x2e\x34\x2d\x32\x39\x2d\x38\x31\x2e\x33\x33\x2d\x34\x36\ +\x2e\x37\x34\x2d\x31\x32\x37\x2e\x37\x35\x2d\x35\x34\x2e\x36\x34\ +\x2d\x35\x34\x2d\x39\x2e\x32\x2d\x31\x30\x36\x2e\x39\x32\x2d\x34\ +\x2e\x33\x32\x2d\x31\x35\x38\x2e\x35\x2c\x31\x35\x2e\x32\x32\x2d\ +\x34\x35\x2c\x31\x37\x2d\x38\x34\x2e\x32\x39\x2c\x34\x33\x2e\x39\ +\x33\x2d\x31\x31\x38\x2e\x31\x36\x2c\x37\x39\x2e\x39\x34\x2d\x38\ +\x2e\x35\x37\x2c\x39\x2e\x31\x2d\x31\x38\x2e\x36\x35\x2c\x31\x32\ +\x2e\x33\x32\x2d\x33\x30\x2e\x31\x39\x2c\x38\x2d\x32\x30\x2e\x30\ +\x39\x2d\x37\x2e\x35\x36\x2d\x32\x35\x2e\x32\x2d\x33\x34\x2e\x30\ +\x38\x2d\x39\x2e\x37\x34\x2d\x35\x30\x2e\x36\x31\x61\x33\x38\x30\ +\x2e\x35\x31\x2c\x33\x38\x30\x2e\x35\x31\x2c\x30\x2c\x30\x2c\x31\ +\x2c\x35\x37\x2e\x36\x32\x2d\x35\x30\x2e\x33\x38\x63\x34\x33\x2d\ +\x33\x30\x2e\x35\x38\x2c\x38\x39\x2e\x39\x33\x2d\x35\x31\x2e\x31\ +\x2c\x31\x34\x30\x2e\x37\x37\x2d\x36\x30\x2e\x34\x35\x43\x32\x35\ +\x35\x2e\x30\x37\x2c\x37\x34\x2e\x32\x33\x2c\x32\x37\x37\x2e\x34\ +\x33\x2c\x37\x32\x2e\x38\x35\x2c\x32\x39\x30\x2e\x31\x38\x2c\x37\ +\x31\x2e\x33\x35\x5a\x22\x2f\x3e\x3c\x70\x61\x74\x68\x20\x63\x6c\ +\x61\x73\x73\x3d\x22\x63\x6c\x73\x2d\x31\x22\x20\x64\x3d\x22\x4d\ +\x33\x30\x30\x2c\x35\x31\x37\x2e\x33\x37\x63\x2d\x32\x32\x2c\x30\ +\x2d\x33\x39\x2e\x31\x35\x2d\x31\x38\x2e\x34\x35\x2d\x33\x39\x2e\ +\x31\x31\x2d\x34\x32\x2e\x31\x31\x2c\x30\x2d\x32\x33\x2e\x33\x37\ +\x2c\x31\x37\x2e\x31\x39\x2d\x34\x31\x2e\x38\x33\x2c\x33\x38\x2e\ +\x38\x36\x2d\x34\x31\x2e\x37\x39\x2c\x32\x32\x2e\x32\x35\x2c\x30\ +\x2c\x33\x39\x2e\x33\x32\x2c\x31\x38\x2e\x31\x37\x2c\x33\x39\x2e\ +\x33\x34\x2c\x34\x31\x2e\x38\x31\x53\x33\x32\x32\x2e\x31\x2c\x35\ +\x31\x37\x2e\x33\x37\x2c\x33\x30\x30\x2c\x35\x31\x37\x2e\x33\x37\ +\x5a\x22\x2f\x3e\x3c\x70\x61\x74\x68\x20\x63\x6c\x61\x73\x73\x3d\ +\x22\x63\x6c\x73\x2d\x32\x22\x20\x64\x3d\x22\x4d\x34\x34\x39\x2e\ +\x30\x37\x2c\x32\x38\x30\x2e\x33\x37\x68\x31\x34\x2e\x33\x35\x61\ +\x31\x30\x2e\x36\x34\x2c\x31\x30\x2e\x36\x34\x2c\x30\x2c\x30\x2c\ +\x30\x2c\x32\x2e\x34\x32\x2c\x31\x63\x32\x37\x2e\x34\x37\x2c\x34\ +\x2c\x34\x39\x2e\x36\x2c\x32\x36\x2e\x36\x37\x2c\x35\x31\x2e\x36\ +\x38\x2c\x35\x34\x2e\x32\x36\x2c\x31\x2e\x31\x37\x2c\x31\x35\x2e\ +\x35\x33\x2e\x35\x38\x2c\x33\x31\x2e\x32\x2e\x37\x38\x2c\x34\x36\ +\x2e\x38\x2c\x30\x2c\x32\x2e\x31\x32\x2c\x30\x2c\x34\x2e\x32\x35\ +\x2c\x30\x2c\x36\x2e\x38\x36\x68\x31\x31\x2e\x37\x35\x63\x31\x33\ +\x2e\x39\x34\x2c\x30\x2c\x31\x39\x2e\x33\x36\x2c\x35\x2e\x33\x39\ +\x2c\x31\x39\x2e\x33\x36\x2c\x31\x39\x2e\x32\x32\x2c\x30\x2c\x33\ +\x33\x2e\x32\x35\x2d\x2e\x31\x38\x2c\x36\x36\x2e\x35\x2e\x31\x32\ +\x2c\x39\x39\x2e\x37\x35\x2e\x30\x39\x2c\x39\x2e\x36\x38\x2d\x32\ +\x2e\x39\x33\x2c\x31\x36\x2e\x36\x37\x2d\x31\x32\x2e\x31\x36\x2c\ +\x32\x30\x2e\x33\x39\x48\x33\x37\x35\x2e\x31\x32\x63\x2d\x37\x2e\ +\x32\x34\x2d\x33\x2e\x31\x37\x2d\x31\x32\x2d\x38\x2e\x31\x31\x2d\ +\x31\x32\x2d\x31\x36\x2e\x35\x33\x2c\x30\x2d\x33\x35\x2e\x34\x36\ +\x2d\x2e\x30\x38\x2d\x37\x30\x2e\x39\x31\x2c\x30\x2d\x31\x30\x36\ +\x2e\x33\x37\x2c\x30\x2d\x31\x30\x2e\x32\x39\x2c\x36\x2e\x32\x34\ +\x2d\x31\x36\x2e\x32\x35\x2c\x31\x36\x2e\x35\x36\x2d\x31\x36\x2e\ +\x34\x34\x2c\x34\x2e\x36\x39\x2d\x2e\x30\x39\x2c\x39\x2e\x33\x39\ +\x2c\x30\x2c\x31\x34\x2e\x35\x31\x2c\x30\x2c\x30\x2d\x31\x35\x2e\ +\x37\x33\x2d\x2e\x30\x38\x2d\x33\x30\x2e\x35\x39\x2c\x30\x2d\x34\ +\x35\x2e\x34\x35\x2e\x31\x39\x2d\x32\x38\x2e\x37\x39\x2c\x31\x37\ +\x2e\x32\x38\x2d\x35\x32\x2e\x33\x36\x2c\x34\x33\x2e\x38\x39\x2d\ +\x36\x30\x2e\x35\x38\x43\x34\x34\x31\x2e\x37\x31\x2c\x32\x38\x32\ +\x2e\x31\x34\x2c\x34\x34\x35\x2e\x34\x31\x2c\x32\x38\x31\x2e\x33\ +\x33\x2c\x34\x34\x39\x2e\x30\x37\x2c\x32\x38\x30\x2e\x33\x37\x5a\ +\x6d\x33\x38\x2e\x33\x31\x2c\x31\x30\x38\x2e\x34\x39\x63\x30\x2d\ +\x31\x36\x2e\x35\x34\x2e\x39\x34\x2d\x33\x32\x2e\x37\x33\x2d\x2e\ +\x32\x35\x2d\x34\x38\x2e\x37\x37\x2d\x31\x2e\x33\x31\x2d\x31\x37\ +\x2e\x38\x32\x2d\x31\x35\x2e\x39\x33\x2d\x32\x39\x2e\x37\x35\x2d\ +\x33\x32\x2e\x37\x37\x2d\x32\x38\x2e\x37\x37\x2d\x31\x36\x2e\x36\ +\x35\x2c\x31\x2d\x32\x39\x2e\x32\x31\x2c\x31\x34\x2e\x37\x33\x2d\ +\x32\x39\x2e\x34\x32\x2c\x33\x32\x2e\x35\x32\x2d\x2e\x31\x36\x2c\ +\x31\x33\x2e\x37\x37\x2c\x30\x2c\x32\x37\x2e\x35\x34\x2c\x30\x2c\ +\x34\x31\x2e\x33\x31\x61\x33\x33\x2e\x31\x39\x2c\x33\x33\x2e\x31\ +\x39\x2c\x30\x2c\x30\x2c\x30\x2c\x2e\x34\x32\x2c\x33\x2e\x37\x31\ +\x5a\x6d\x2d\x33\x31\x2e\x33\x33\x2c\x34\x36\x2e\x37\x31\x61\x31\ +\x34\x2e\x39\x31\x2c\x31\x34\x2e\x39\x31\x2c\x30\x2c\x30\x2c\x30\ +\x2d\x31\x33\x2e\x36\x38\x2c\x38\x2e\x36\x36\x63\x2d\x32\x2e\x38\ +\x32\x2c\x35\x2e\x35\x37\x2d\x32\x2e\x38\x2c\x31\x31\x2e\x35\x39\ +\x2c\x31\x2e\x36\x2c\x31\x35\x2e\x38\x35\x2c\x34\x2e\x31\x36\x2c\ +\x34\x2c\x34\x2e\x34\x34\x2c\x38\x2e\x33\x35\x2c\x34\x2e\x32\x32\ +\x2c\x31\x33\x2e\x33\x32\x61\x36\x38\x2e\x33\x38\x2c\x36\x38\x2e\ +\x33\x38\x2c\x30\x2c\x30\x2c\x30\x2c\x30\x2c\x37\x2e\x37\x31\x63\ +\x2e\x33\x37\x2c\x35\x2e\x33\x33\x2c\x33\x2e\x36\x2c\x38\x2e\x38\ +\x33\x2c\x38\x2c\x38\x2e\x38\x37\x73\x37\x2e\x37\x35\x2d\x33\x2e\ +\x34\x36\x2c\x38\x2e\x30\x37\x2d\x38\x2e\x37\x36\x61\x31\x31\x31\ +\x2e\x34\x34\x2c\x31\x31\x31\x2e\x34\x34\x2c\x30\x2c\x30\x2c\x30\ +\x2c\x30\x2d\x31\x31\x2e\x35\x36\x2c\x39\x2e\x36\x38\x2c\x39\x2e\ +\x36\x38\x2c\x30\x2c\x30\x2c\x31\x2c\x33\x2e\x32\x2d\x38\x2e\x31\ +\x33\x63\x34\x2e\x37\x31\x2d\x34\x2e\x36\x34\x2c\x35\x2e\x36\x31\ +\x2d\x31\x30\x2e\x35\x33\x2c\x33\x2d\x31\x36\x2e\x37\x32\x41\x31\ +\x35\x2e\x34\x38\x2c\x31\x35\x2e\x34\x38\x2c\x30\x2c\x30\x2c\x30\ +\x2c\x34\x35\x36\x2e\x30\x35\x2c\x34\x33\x35\x2e\x35\x37\x5a\x22\ +\x2f\x3e\x3c\x70\x61\x74\x68\x20\x63\x6c\x61\x73\x73\x3d\x22\x63\ +\x6c\x73\x2d\x31\x22\x20\x64\x3d\x22\x4d\x33\x37\x39\x2e\x35\x38\ +\x2c\x33\x38\x34\x2e\x33\x31\x63\x33\x2e\x31\x38\x2d\x2e\x30\x36\ +\x2c\x36\x2e\x33\x31\x2d\x2e\x30\x35\x2c\x39\x2e\x36\x31\x2c\x30\ +\x2c\x30\x2d\x33\x2e\x32\x34\x2c\x30\x2d\x36\x2e\x34\x33\x2c\x30\ +\x2d\x39\x2e\x36\x31\x2c\x30\x2d\x31\x30\x2e\x35\x33\x2c\x30\x2d\ +\x32\x30\x2e\x34\x38\x2c\x30\x2d\x33\x30\x2e\x35\x38\x2d\x33\x32\ +\x2e\x31\x34\x2d\x32\x35\x2e\x32\x37\x2d\x36\x38\x2e\x39\x33\x2d\ +\x33\x35\x2e\x34\x32\x2d\x31\x31\x30\x2e\x34\x34\x2d\x32\x39\x2e\ +\x35\x37\x43\x32\x34\x36\x2c\x33\x31\x39\x2e\x31\x34\x2c\x32\x31\ +\x38\x2c\x33\x33\x34\x2e\x36\x31\x2c\x31\x39\x34\x2e\x31\x33\x2c\ +\x33\x35\x39\x63\x2d\x36\x2c\x36\x2e\x31\x37\x2d\x39\x2e\x36\x33\ +\x2c\x31\x33\x2e\x35\x39\x2d\x39\x2e\x36\x31\x2c\x32\x32\x2e\x38\ +\x31\x61\x36\x38\x2e\x39\x33\x2c\x36\x38\x2e\x39\x33\x2c\x30\x2c\ +\x30\x2c\x30\x2c\x31\x2c\x37\x2e\x33\x38\x63\x35\x2e\x36\x33\x2c\ +\x32\x33\x2e\x32\x38\x2c\x33\x31\x2e\x32\x37\x2c\x33\x30\x2e\x32\ +\x38\x2c\x34\x37\x2e\x39\x33\x2c\x31\x33\x2e\x32\x37\x2c\x32\x34\ +\x2e\x36\x31\x2d\x32\x35\x2e\x31\x31\x2c\x35\x33\x2e\x37\x35\x2d\ +\x33\x33\x2e\x38\x36\x2c\x38\x37\x2d\x32\x36\x2e\x37\x37\x2c\x31\ +\x35\x2c\x33\x2e\x32\x2c\x32\x38\x2c\x31\x30\x2e\x33\x35\x2c\x33\ +\x39\x2e\x36\x2c\x32\x30\x2e\x34\x32\x43\x33\x36\x33\x2e\x31\x37\ +\x2c\x33\x38\x38\x2e\x38\x32\x2c\x33\x37\x30\x2e\x30\x37\x2c\x33\ +\x38\x34\x2e\x34\x39\x2c\x33\x37\x39\x2e\x35\x38\x2c\x33\x38\x34\ +\x2e\x33\x31\x5a\x22\x2f\x3e\x3c\x70\x61\x74\x68\x20\x63\x6c\x61\ +\x73\x73\x3d\x22\x63\x6c\x73\x2d\x31\x22\x20\x64\x3d\x22\x4d\x34\ +\x35\x34\x2e\x36\x35\x2c\x33\x31\x36\x2e\x33\x31\x61\x32\x36\x2e\ +\x31\x32\x2c\x32\x36\x2e\x31\x32\x2c\x30\x2c\x30\x2c\x30\x2d\x37\ +\x2e\x37\x39\x2c\x31\x2e\x36\x36\x63\x35\x2e\x35\x39\x2c\x35\x2e\ +\x37\x32\x2c\x31\x32\x2e\x33\x34\x2c\x38\x2e\x37\x38\x2c\x32\x33\ +\x2c\x38\x2e\x36\x31\x61\x32\x34\x2c\x32\x34\x2c\x30\x2c\x30\x2c\ +\x30\x2c\x36\x2e\x32\x35\x2d\x31\x2e\x32\x37\x41\x32\x35\x2e\x36\ +\x38\x2c\x32\x35\x2e\x36\x38\x2c\x30\x2c\x30\x2c\x30\x2c\x34\x35\ +\x34\x2e\x36\x35\x2c\x33\x31\x36\x2e\x33\x31\x5a\x22\x2f\x3e\x3c\ +\x70\x61\x74\x68\x20\x63\x6c\x61\x73\x73\x3d\x22\x63\x6c\x73\x2d\ +\x31\x22\x20\x64\x3d\x22\x4d\x34\x33\x36\x2e\x36\x32\x2c\x32\x37\ +\x38\x2e\x34\x38\x63\x32\x2e\x36\x31\x2d\x2e\x38\x2c\x35\x2e\x32\ +\x33\x2d\x31\x2e\x34\x36\x2c\x37\x2e\x37\x37\x2d\x32\x2e\x30\x39\ +\x2c\x31\x2e\x31\x34\x2d\x2e\x32\x38\x2c\x32\x2e\x32\x38\x2d\x2e\ +\x35\x36\x2c\x33\x2e\x34\x32\x2d\x2e\x38\x36\x6c\x2e\x36\x32\x2d\ +\x2e\x31\x36\x68\x31\x36\x6c\x2e\x39\x33\x2e\x33\x39\x63\x2e\x33\ +\x36\x2e\x31\x35\x2e\x37\x32\x2e\x33\x33\x2c\x31\x2e\x30\x37\x2e\ +\x35\x6c\x2e\x33\x37\x2e\x31\x39\x61\x36\x36\x2e\x31\x38\x2c\x36\ +\x36\x2e\x31\x38\x2c\x30\x2c\x30\x2c\x31\x2c\x32\x36\x2e\x38\x33\ +\x2c\x31\x30\x2e\x33\x38\x2c\x33\x30\x2e\x33\x35\x2c\x33\x30\x2e\ +\x33\x35\x2c\x30\x2c\x30\x2c\x30\x2d\x35\x2e\x39\x34\x2d\x31\x30\ +\x2e\x31\x35\x41\x32\x30\x35\x2c\x32\x30\x35\x2c\x30\x2c\x30\x2c\ +\x30\x2c\x34\x36\x34\x2e\x34\x34\x2c\x32\x35\x34\x63\x2d\x36\x30\ +\x2e\x32\x32\x2d\x35\x30\x2e\x33\x31\x2d\x31\x32\x38\x2e\x33\x31\ +\x2d\x37\x30\x2d\x32\x30\x33\x2e\x39\x31\x2d\x35\x38\x2e\x33\x36\ +\x2d\x35\x37\x2e\x30\x39\x2c\x38\x2e\x38\x2d\x31\x30\x35\x2e\x38\ +\x34\x2c\x33\x36\x2d\x31\x34\x36\x2e\x38\x34\x2c\x37\x39\x2e\x32\ +\x38\x2d\x38\x2e\x32\x37\x2c\x38\x2e\x37\x32\x2d\x31\x30\x2e\x37\ +\x39\x2c\x31\x39\x2e\x36\x32\x2d\x36\x2e\x38\x39\x2c\x33\x31\x2e\ +\x35\x32\x2c\x37\x2e\x32\x34\x2c\x32\x32\x2c\x33\x31\x2e\x35\x36\ +\x2c\x32\x36\x2e\x38\x33\x2c\x34\x37\x2e\x39\x31\x2c\x31\x30\x2c\ +\x34\x39\x2e\x32\x32\x2d\x35\x30\x2e\x35\x37\x2c\x31\x30\x38\x2d\ +\x37\x31\x2e\x30\x37\x2c\x31\x37\x35\x2e\x33\x39\x2d\x36\x31\x2e\ +\x34\x41\x31\x38\x37\x2e\x39\x32\x2c\x31\x38\x37\x2e\x39\x32\x2c\ +\x30\x2c\x30\x2c\x31\x2c\x34\x31\x35\x2c\x32\x38\x39\x2e\x36\x38\ +\x2c\x36\x37\x2e\x34\x39\x2c\x36\x37\x2e\x34\x39\x2c\x30\x2c\x30\ +\x2c\x31\x2c\x34\x33\x36\x2e\x36\x32\x2c\x32\x37\x38\x2e\x34\x38\ +\x5a\x22\x2f\x3e\x3c\x2f\x73\x76\x67\x3e\ +\x00\x00\x0b\x47\ +\x3c\ +\x73\x76\x67\x20\x69\x64\x3d\x22\x4c\x61\x79\x65\x72\x5f\x31\x22\ +\x20\x64\x61\x74\x61\x2d\x6e\x61\x6d\x65\x3d\x22\x4c\x61\x79\x65\ +\x72\x20\x31\x22\x20\x78\x6d\x6c\x6e\x73\x3d\x22\x68\x74\x74\x70\ +\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x32\x30\ +\x30\x30\x2f\x73\x76\x67\x22\x20\x76\x69\x65\x77\x42\x6f\x78\x3d\ +\x22\x30\x20\x30\x20\x36\x30\x30\x20\x36\x30\x30\x22\x3e\x3c\x64\ +\x65\x66\x73\x3e\x3c\x73\x74\x79\x6c\x65\x3e\x2e\x63\x6c\x73\x2d\ +\x31\x7b\x66\x69\x6c\x6c\x3a\x23\x64\x30\x64\x32\x64\x33\x3b\x7d\ +\x2e\x63\x6c\x73\x2d\x32\x7b\x66\x69\x6c\x6c\x3a\x23\x39\x32\x39\ +\x34\x39\x37\x3b\x7d\x2e\x63\x6c\x73\x2d\x33\x7b\x66\x69\x6c\x6c\ +\x3a\x23\x35\x65\x36\x30\x36\x31\x3b\x7d\x3c\x2f\x73\x74\x79\x6c\ +\x65\x3e\x3c\x2f\x64\x65\x66\x73\x3e\x3c\x70\x61\x74\x68\x20\x63\ +\x6c\x61\x73\x73\x3d\x22\x63\x6c\x73\x2d\x31\x22\x20\x64\x3d\x22\ +\x4d\x32\x39\x30\x2e\x31\x38\x2c\x38\x32\x2e\x36\x32\x43\x34\x30\ +\x32\x2c\x38\x34\x2e\x34\x2c\x34\x39\x30\x2e\x31\x2c\x31\x32\x32\ +\x2e\x37\x2c\x35\x36\x33\x2e\x39\x2c\x31\x39\x38\x2e\x37\x31\x63\ +\x31\x34\x2e\x38\x2c\x31\x35\x2e\x32\x35\x2c\x31\x31\x2e\x38\x31\ +\x2c\x33\x33\x2e\x39\x33\x2c\x33\x2e\x32\x36\x2c\x34\x34\x2d\x31\ +\x31\x2e\x31\x32\x2c\x31\x33\x2e\x31\x36\x2d\x32\x38\x2e\x36\x32\ +\x2c\x31\x33\x2d\x34\x31\x2e\x34\x36\x2e\x38\x33\x2d\x31\x35\x2e\ +\x30\x38\x2d\x31\x34\x2e\x32\x37\x2d\x33\x30\x2e\x32\x2d\x32\x38\ +\x2e\x37\x31\x2d\x34\x36\x2e\x36\x31\x2d\x34\x31\x2e\x31\x2d\x33\ +\x38\x2e\x34\x2d\x32\x39\x2d\x38\x31\x2e\x33\x33\x2d\x34\x36\x2e\ +\x37\x35\x2d\x31\x32\x37\x2e\x37\x35\x2d\x35\x34\x2e\x36\x35\x2d\ +\x35\x34\x2d\x39\x2e\x31\x39\x2d\x31\x30\x36\x2e\x39\x32\x2d\x34\ +\x2e\x33\x32\x2d\x31\x35\x38\x2e\x35\x2c\x31\x35\x2e\x32\x33\x43\ +\x31\x34\x37\x2e\x38\x34\x2c\x31\x38\x30\x2e\x31\x32\x2c\x31\x30\ +\x38\x2e\x35\x35\x2c\x32\x30\x37\x2c\x37\x34\x2e\x36\x38\x2c\x32\ +\x34\x33\x63\x2d\x38\x2e\x35\x37\x2c\x39\x2e\x31\x2d\x31\x38\x2e\ +\x36\x35\x2c\x31\x32\x2e\x33\x32\x2d\x33\x30\x2e\x31\x39\x2c\x38\ +\x2d\x32\x30\x2e\x30\x39\x2d\x37\x2e\x35\x36\x2d\x32\x35\x2e\x32\ +\x2d\x33\x34\x2e\x30\x38\x2d\x39\x2e\x37\x34\x2d\x35\x30\x2e\x36\ +\x41\x33\x38\x30\x2c\x33\x38\x30\x2c\x30\x2c\x30\x2c\x31\x2c\x39\ +\x32\x2e\x33\x37\x2c\x31\x35\x30\x63\x34\x33\x2d\x33\x30\x2e\x35\ +\x37\x2c\x38\x39\x2e\x39\x33\x2d\x35\x31\x2e\x30\x39\x2c\x31\x34\ +\x30\x2e\x37\x37\x2d\x36\x30\x2e\x34\x35\x43\x32\x35\x35\x2e\x30\ +\x37\x2c\x38\x35\x2e\x34\x39\x2c\x32\x37\x37\x2e\x34\x33\x2c\x38\ +\x34\x2e\x31\x31\x2c\x32\x39\x30\x2e\x31\x38\x2c\x38\x32\x2e\x36\ +\x32\x5a\x22\x2f\x3e\x3c\x70\x61\x74\x68\x20\x63\x6c\x61\x73\x73\ +\x3d\x22\x63\x6c\x73\x2d\x31\x22\x20\x64\x3d\x22\x4d\x33\x30\x30\ +\x2c\x35\x32\x38\x2e\x36\x34\x63\x2d\x32\x32\x2c\x30\x2d\x33\x39\ +\x2e\x31\x35\x2d\x31\x38\x2e\x34\x36\x2d\x33\x39\x2e\x31\x31\x2d\ +\x34\x32\x2e\x31\x32\x2c\x30\x2d\x32\x33\x2e\x33\x37\x2c\x31\x37\ +\x2e\x31\x39\x2d\x34\x31\x2e\x38\x33\x2c\x33\x38\x2e\x38\x36\x2d\ +\x34\x31\x2e\x37\x39\x2c\x32\x32\x2e\x32\x35\x2c\x30\x2c\x33\x39\ +\x2e\x33\x32\x2c\x31\x38\x2e\x31\x37\x2c\x33\x39\x2e\x33\x34\x2c\ +\x34\x31\x2e\x38\x31\x53\x33\x32\x32\x2e\x31\x2c\x35\x32\x38\x2e\ +\x36\x33\x2c\x33\x30\x30\x2c\x35\x32\x38\x2e\x36\x34\x5a\x22\x2f\ +\x3e\x3c\x70\x61\x74\x68\x20\x63\x6c\x61\x73\x73\x3d\x22\x63\x6c\ +\x73\x2d\x31\x22\x20\x64\x3d\x22\x4d\x33\x37\x39\x2e\x35\x38\x2c\ +\x33\x39\x35\x2e\x35\x37\x63\x33\x2e\x31\x38\x2d\x2e\x30\x36\x2c\ +\x36\x2e\x33\x31\x2d\x2e\x30\x35\x2c\x39\x2e\x36\x31\x2c\x30\x2c\ +\x30\x2d\x33\x2e\x32\x33\x2c\x30\x2d\x36\x2e\x34\x33\x2c\x30\x2d\ +\x39\x2e\x36\x31\x2c\x30\x2d\x31\x30\x2e\x35\x32\x2c\x30\x2d\x32\ +\x30\x2e\x34\x38\x2c\x30\x2d\x33\x30\x2e\x35\x38\x2d\x33\x32\x2e\ +\x31\x34\x2d\x32\x35\x2e\x32\x37\x2d\x36\x38\x2e\x39\x33\x2d\x33\ +\x35\x2e\x34\x32\x2d\x31\x31\x30\x2e\x34\x34\x2d\x32\x39\x2e\x35\ +\x37\x2d\x33\x32\x2e\x38\x2c\x34\x2e\x36\x32\x2d\x36\x30\x2e\x38\ +\x2c\x32\x30\x2e\x30\x39\x2d\x38\x34\x2e\x36\x34\x2c\x34\x34\x2e\ +\x34\x33\x2d\x36\x2c\x36\x2e\x31\x37\x2d\x39\x2e\x36\x33\x2c\x31\ +\x33\x2e\x36\x2d\x39\x2e\x36\x31\x2c\x32\x32\x2e\x38\x32\x61\x36\ +\x39\x2e\x32\x38\x2c\x36\x39\x2e\x32\x38\x2c\x30\x2c\x30\x2c\x30\ +\x2c\x31\x2c\x37\x2e\x33\x38\x63\x35\x2e\x36\x33\x2c\x32\x33\x2e\ +\x32\x37\x2c\x33\x31\x2e\x32\x37\x2c\x33\x30\x2e\x32\x37\x2c\x34\ +\x37\x2e\x39\x33\x2c\x31\x33\x2e\x32\x36\x2c\x32\x34\x2e\x36\x31\ +\x2d\x32\x35\x2e\x31\x31\x2c\x35\x33\x2e\x37\x35\x2d\x33\x33\x2e\ +\x38\x36\x2c\x38\x37\x2d\x32\x36\x2e\x37\x36\x2c\x31\x35\x2c\x33\ +\x2e\x32\x2c\x32\x38\x2c\x31\x30\x2e\x33\x35\x2c\x33\x39\x2e\x36\ +\x2c\x32\x30\x2e\x34\x32\x43\x33\x36\x33\x2e\x31\x37\x2c\x34\x30\ +\x30\x2e\x30\x38\x2c\x33\x37\x30\x2e\x30\x37\x2c\x33\x39\x35\x2e\ +\x37\x35\x2c\x33\x37\x39\x2e\x35\x38\x2c\x33\x39\x35\x2e\x35\x37\ +\x5a\x22\x2f\x3e\x3c\x70\x61\x74\x68\x20\x63\x6c\x61\x73\x73\x3d\ +\x22\x63\x6c\x73\x2d\x31\x22\x20\x64\x3d\x22\x4d\x34\x35\x34\x2e\ +\x36\x35\x2c\x33\x32\x37\x2e\x35\x37\x61\x32\x36\x2e\x31\x32\x2c\ +\x32\x36\x2e\x31\x32\x2c\x30\x2c\x30\x2c\x30\x2d\x37\x2e\x37\x39\ +\x2c\x31\x2e\x36\x36\x63\x35\x2e\x35\x39\x2c\x35\x2e\x37\x32\x2c\ +\x31\x32\x2e\x33\x34\x2c\x38\x2e\x37\x39\x2c\x32\x33\x2c\x38\x2e\ +\x36\x31\x61\x32\x33\x2e\x35\x35\x2c\x32\x33\x2e\x35\x35\x2c\x30\ +\x2c\x30\x2c\x30\x2c\x36\x2e\x32\x35\x2d\x31\x2e\x32\x37\x41\x32\ +\x35\x2e\x37\x31\x2c\x32\x35\x2e\x37\x31\x2c\x30\x2c\x30\x2c\x30\ +\x2c\x34\x35\x34\x2e\x36\x35\x2c\x33\x32\x37\x2e\x35\x37\x5a\x22\ +\x2f\x3e\x3c\x70\x61\x74\x68\x20\x63\x6c\x61\x73\x73\x3d\x22\x63\ +\x6c\x73\x2d\x31\x22\x20\x64\x3d\x22\x4d\x34\x33\x36\x2e\x36\x32\ +\x2c\x32\x38\x39\x2e\x37\x34\x63\x32\x2e\x36\x31\x2d\x2e\x38\x2c\ +\x35\x2e\x32\x33\x2d\x31\x2e\x34\x35\x2c\x37\x2e\x37\x37\x2d\x32\ +\x2e\x30\x38\x6c\x33\x2e\x34\x32\x2d\x2e\x38\x37\x2e\x36\x32\x2d\ +\x2e\x31\x36\x68\x31\x36\x6c\x2e\x39\x33\x2e\x33\x39\x63\x2e\x33\ +\x36\x2e\x31\x36\x2e\x37\x32\x2e\x33\x33\x2c\x31\x2e\x30\x37\x2e\ +\x35\x31\x6c\x2e\x33\x37\x2e\x31\x38\x61\x36\x36\x2e\x31\x38\x2c\ +\x36\x36\x2e\x31\x38\x2c\x30\x2c\x30\x2c\x31\x2c\x32\x36\x2e\x38\ +\x33\x2c\x31\x30\x2e\x33\x38\x2c\x33\x30\x2e\x34\x35\x2c\x33\x30\ +\x2e\x34\x35\x2c\x30\x2c\x30\x2c\x30\x2d\x35\x2e\x39\x34\x2d\x31\ +\x30\x2e\x31\x35\x2c\x32\x30\x35\x2e\x38\x33\x2c\x32\x30\x35\x2e\ +\x38\x33\x2c\x30\x2c\x30\x2c\x30\x2d\x32\x33\x2e\x32\x35\x2d\x32\ +\x32\x2e\x37\x63\x2d\x36\x30\x2e\x32\x32\x2d\x35\x30\x2e\x33\x2d\ +\x31\x32\x38\x2e\x33\x31\x2d\x37\x30\x2d\x32\x30\x33\x2e\x39\x31\ +\x2d\x35\x38\x2e\x33\x36\x2d\x35\x37\x2e\x30\x39\x2c\x38\x2e\x38\ +\x31\x2d\x31\x30\x35\x2e\x38\x34\x2c\x33\x36\x2e\x30\x35\x2d\x31\ +\x34\x36\x2e\x38\x34\x2c\x37\x39\x2e\x32\x38\x2d\x38\x2e\x32\x37\ +\x2c\x38\x2e\x37\x33\x2d\x31\x30\x2e\x37\x39\x2c\x31\x39\x2e\x36\ +\x33\x2d\x36\x2e\x38\x39\x2c\x33\x31\x2e\x35\x32\x2c\x37\x2e\x32\ +\x34\x2c\x32\x32\x2c\x33\x31\x2e\x35\x36\x2c\x32\x36\x2e\x38\x33\ +\x2c\x34\x37\x2e\x39\x31\x2c\x31\x30\x2c\x34\x39\x2e\x32\x32\x2d\ +\x35\x30\x2e\x35\x37\x2c\x31\x30\x38\x2d\x37\x31\x2e\x30\x37\x2c\ +\x31\x37\x35\x2e\x33\x39\x2d\x36\x31\x2e\x34\x41\x31\x38\x38\x2e\ +\x31\x34\x2c\x31\x38\x38\x2e\x31\x34\x2c\x30\x2c\x30\x2c\x31\x2c\ +\x34\x31\x35\x2c\x33\x30\x30\x2e\x39\x34\x2c\x36\x37\x2e\x34\x39\ +\x2c\x36\x37\x2e\x34\x39\x2c\x30\x2c\x30\x2c\x31\x2c\x34\x33\x36\ +\x2e\x36\x32\x2c\x32\x38\x39\x2e\x37\x34\x5a\x22\x2f\x3e\x3c\x70\ +\x61\x74\x68\x20\x63\x6c\x61\x73\x73\x3d\x22\x63\x6c\x73\x2d\x32\ +\x22\x20\x64\x3d\x22\x4d\x34\x34\x39\x2e\x30\x37\x2c\x32\x39\x31\ +\x2e\x36\x33\x68\x31\x34\x2e\x33\x35\x61\x31\x30\x2e\x36\x34\x2c\ +\x31\x30\x2e\x36\x34\x2c\x30\x2c\x30\x2c\x30\x2c\x32\x2e\x34\x32\ +\x2c\x31\x63\x32\x37\x2e\x34\x37\x2c\x34\x2c\x34\x39\x2e\x36\x2c\ +\x32\x36\x2e\x36\x37\x2c\x35\x31\x2e\x36\x38\x2c\x35\x34\x2e\x32\ +\x36\x2c\x31\x2e\x31\x37\x2c\x31\x35\x2e\x35\x33\x2e\x35\x38\x2c\ +\x33\x31\x2e\x32\x2e\x37\x38\x2c\x34\x36\x2e\x38\x2c\x30\x2c\x32\ +\x2e\x31\x33\x2c\x30\x2c\x34\x2e\x32\x35\x2c\x30\x2c\x36\x2e\x38\ +\x37\x68\x31\x31\x2e\x37\x35\x63\x31\x33\x2e\x39\x34\x2c\x30\x2c\ +\x31\x39\x2e\x33\x36\x2c\x35\x2e\x33\x39\x2c\x31\x39\x2e\x33\x36\ +\x2c\x31\x39\x2e\x32\x32\x2c\x30\x2c\x33\x33\x2e\x32\x35\x2d\x2e\ +\x31\x38\x2c\x36\x36\x2e\x35\x2e\x31\x32\x2c\x39\x39\x2e\x37\x35\ +\x2e\x30\x39\x2c\x39\x2e\x36\x38\x2d\x32\x2e\x39\x33\x2c\x31\x36\ +\x2e\x36\x37\x2d\x31\x32\x2e\x31\x36\x2c\x32\x30\x2e\x33\x39\x48\ +\x33\x37\x35\x2e\x31\x32\x63\x2d\x37\x2e\x32\x34\x2d\x33\x2e\x31\ +\x37\x2d\x31\x32\x2d\x38\x2e\x31\x31\x2d\x31\x32\x2d\x31\x36\x2e\ +\x35\x33\x2c\x30\x2d\x33\x35\x2e\x34\x36\x2d\x2e\x30\x38\x2d\x37\ +\x30\x2e\x39\x31\x2c\x30\x2d\x31\x30\x36\x2e\x33\x37\x2c\x30\x2d\ +\x31\x30\x2e\x32\x39\x2c\x36\x2e\x32\x34\x2d\x31\x36\x2e\x32\x34\ +\x2c\x31\x36\x2e\x35\x36\x2d\x31\x36\x2e\x34\x34\x2c\x34\x2e\x36\ +\x39\x2d\x2e\x30\x39\x2c\x39\x2e\x33\x39\x2c\x30\x2c\x31\x34\x2e\ +\x35\x31\x2c\x30\x2c\x30\x2d\x31\x35\x2e\x37\x33\x2d\x2e\x30\x38\ +\x2d\x33\x30\x2e\x35\x39\x2c\x30\x2d\x34\x35\x2e\x34\x34\x2e\x31\ +\x39\x2d\x32\x38\x2e\x38\x2c\x31\x37\x2e\x32\x38\x2d\x35\x32\x2e\ +\x33\x37\x2c\x34\x33\x2e\x38\x39\x2d\x36\x30\x2e\x35\x39\x43\x34\ +\x34\x31\x2e\x37\x31\x2c\x32\x39\x33\x2e\x34\x31\x2c\x34\x34\x35\ +\x2e\x34\x31\x2c\x32\x39\x32\x2e\x35\x39\x2c\x34\x34\x39\x2e\x30\ +\x37\x2c\x32\x39\x31\x2e\x36\x33\x5a\x6d\x33\x38\x2e\x33\x31\x2c\ +\x31\x30\x38\x2e\x34\x39\x63\x30\x2d\x31\x36\x2e\x35\x34\x2e\x39\ +\x34\x2d\x33\x32\x2e\x37\x33\x2d\x2e\x32\x35\x2d\x34\x38\x2e\x37\ +\x37\x2d\x31\x2e\x33\x31\x2d\x31\x37\x2e\x38\x32\x2d\x31\x35\x2e\ +\x39\x33\x2d\x32\x39\x2e\x37\x35\x2d\x33\x32\x2e\x37\x37\x2d\x32\ +\x38\x2e\x37\x37\x2d\x31\x36\x2e\x36\x35\x2c\x31\x2d\x32\x39\x2e\ +\x32\x31\x2c\x31\x34\x2e\x37\x33\x2d\x32\x39\x2e\x34\x32\x2c\x33\ +\x32\x2e\x35\x32\x2d\x2e\x31\x36\x2c\x31\x33\x2e\x37\x37\x2c\x30\ +\x2c\x32\x37\x2e\x35\x34\x2c\x30\x2c\x34\x31\x2e\x33\x31\x61\x33\ +\x33\x2e\x31\x39\x2c\x33\x33\x2e\x31\x39\x2c\x30\x2c\x30\x2c\x30\ +\x2c\x2e\x34\x32\x2c\x33\x2e\x37\x31\x5a\x6d\x2d\x33\x31\x2e\x33\ +\x33\x2c\x34\x36\x2e\x37\x31\x61\x31\x34\x2e\x39\x32\x2c\x31\x34\ +\x2e\x39\x32\x2c\x30\x2c\x30\x2c\x30\x2d\x31\x33\x2e\x36\x38\x2c\ +\x38\x2e\x36\x36\x63\x2d\x32\x2e\x38\x32\x2c\x35\x2e\x35\x37\x2d\ +\x32\x2e\x38\x2c\x31\x31\x2e\x35\x39\x2c\x31\x2e\x36\x2c\x31\x35\ +\x2e\x38\x36\x2c\x34\x2e\x31\x36\x2c\x34\x2c\x34\x2e\x34\x34\x2c\ +\x38\x2e\x33\x34\x2c\x34\x2e\x32\x32\x2c\x31\x33\x2e\x33\x31\x61\ +\x36\x38\x2e\x33\x38\x2c\x36\x38\x2e\x33\x38\x2c\x30\x2c\x30\x2c\ +\x30\x2c\x30\x2c\x37\x2e\x37\x31\x63\x2e\x33\x37\x2c\x35\x2e\x33\ +\x34\x2c\x33\x2e\x36\x2c\x38\x2e\x38\x33\x2c\x38\x2c\x38\x2e\x38\ +\x37\x73\x37\x2e\x37\x35\x2d\x33\x2e\x34\x36\x2c\x38\x2e\x30\x37\ +\x2d\x38\x2e\x37\x36\x61\x31\x31\x31\x2e\x34\x33\x2c\x31\x31\x31\ +\x2e\x34\x33\x2c\x30\x2c\x30\x2c\x30\x2c\x30\x2d\x31\x31\x2e\x35\ +\x36\x2c\x39\x2e\x36\x39\x2c\x39\x2e\x36\x39\x2c\x30\x2c\x30\x2c\ +\x31\x2c\x33\x2e\x32\x2d\x38\x2e\x31\x33\x63\x34\x2e\x37\x31\x2d\ +\x34\x2e\x36\x34\x2c\x35\x2e\x36\x31\x2d\x31\x30\x2e\x35\x33\x2c\ +\x33\x2d\x31\x36\x2e\x37\x32\x41\x31\x35\x2e\x34\x38\x2c\x31\x35\ +\x2e\x34\x38\x2c\x30\x2c\x30\x2c\x30\x2c\x34\x35\x36\x2e\x30\x35\ +\x2c\x34\x34\x36\x2e\x38\x33\x5a\x22\x2f\x3e\x3c\x70\x61\x74\x68\ +\x20\x63\x6c\x61\x73\x73\x3d\x22\x63\x6c\x73\x2d\x33\x22\x20\x64\ +\x3d\x22\x4d\x33\x35\x38\x2e\x32\x34\x2c\x34\x31\x35\x2e\x38\x38\ +\x63\x30\x2d\x31\x32\x2e\x30\x35\x2c\x37\x2e\x37\x2d\x31\x39\x2e\ +\x36\x39\x2c\x32\x30\x2d\x31\x39\x2e\x39\x33\x2c\x33\x2e\x32\x35\ +\x2d\x2e\x30\x36\x2c\x36\x2e\x34\x35\x2c\x30\x2c\x39\x2e\x38\x34\ +\x2c\x30\x68\x31\x2e\x32\x34\x63\x30\x2d\x32\x2e\x35\x32\x2c\x30\ +\x2d\x35\x2c\x30\x2d\x37\x2e\x34\x38\x6c\x2d\x36\x31\x2e\x37\x31\ +\x2d\x38\x35\x2e\x36\x32\x2c\x31\x37\x32\x2e\x32\x31\x2d\x32\x33\ +\x39\x48\x34\x34\x31\x2e\x38\x37\x4c\x32\x39\x38\x2e\x36\x32\x2c\ +\x32\x36\x32\x2e\x36\x33\x2c\x31\x35\x35\x2e\x33\x38\x2c\x36\x33\ +\x2e\x38\x36\x48\x39\x37\x2e\x34\x35\x6c\x31\x37\x32\x2e\x32\x31\ +\x2c\x32\x33\x39\x2d\x31\x37\x32\x2e\x32\x33\x2c\x32\x33\x39\x68\ +\x35\x37\x2e\x39\x35\x4c\x32\x39\x38\x2e\x36\x32\x2c\x33\x34\x33\ +\x6c\x35\x39\x2e\x35\x39\x2c\x38\x32\x2e\x36\x39\x5a\x22\x2f\x3e\ +\x3c\x2f\x73\x76\x67\x3e\ +\x00\x00\x0a\x70\ +\x3c\ +\x73\x76\x67\x20\x69\x64\x3d\x22\x4c\x61\x79\x65\x72\x5f\x31\x22\ +\x20\x64\x61\x74\x61\x2d\x6e\x61\x6d\x65\x3d\x22\x4c\x61\x79\x65\ +\x72\x20\x31\x22\x20\x78\x6d\x6c\x6e\x73\x3d\x22\x68\x74\x74\x70\ +\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x32\x30\ +\x30\x30\x2f\x73\x76\x67\x22\x20\x76\x69\x65\x77\x42\x6f\x78\x3d\ +\x22\x30\x20\x30\x20\x36\x30\x30\x20\x36\x30\x30\x22\x3e\x3c\x64\ +\x65\x66\x73\x3e\x3c\x73\x74\x79\x6c\x65\x3e\x2e\x63\x6c\x73\x2d\ +\x31\x7b\x66\x69\x6c\x6c\x3a\x23\x64\x30\x64\x32\x64\x33\x3b\x7d\ +\x2e\x63\x6c\x73\x2d\x32\x7b\x66\x69\x6c\x6c\x3a\x23\x38\x63\x63\ +\x35\x34\x30\x3b\x7d\x2e\x63\x6c\x73\x2d\x33\x7b\x66\x69\x6c\x6c\ +\x3a\x23\x39\x32\x39\x34\x39\x37\x3b\x7d\x3c\x2f\x73\x74\x79\x6c\ +\x65\x3e\x3c\x2f\x64\x65\x66\x73\x3e\x3c\x70\x61\x74\x68\x20\x63\ +\x6c\x61\x73\x73\x3d\x22\x63\x6c\x73\x2d\x31\x22\x20\x64\x3d\x22\ +\x4d\x32\x39\x30\x2e\x31\x38\x2c\x37\x31\x2e\x33\x35\x43\x34\x30\ +\x32\x2c\x37\x33\x2e\x31\x34\x2c\x34\x39\x30\x2e\x31\x2c\x31\x31\ +\x31\x2e\x34\x34\x2c\x35\x36\x33\x2e\x39\x2c\x31\x38\x37\x2e\x34\ +\x35\x63\x31\x34\x2e\x38\x2c\x31\x35\x2e\x32\x35\x2c\x31\x31\x2e\ +\x38\x31\x2c\x33\x33\x2e\x39\x33\x2c\x33\x2e\x32\x36\x2c\x34\x34\ +\x2e\x30\x35\x2d\x31\x31\x2e\x31\x32\x2c\x31\x33\x2e\x31\x36\x2d\ +\x32\x38\x2e\x36\x32\x2c\x31\x33\x2d\x34\x31\x2e\x34\x36\x2e\x38\ +\x33\x2d\x31\x35\x2e\x30\x38\x2d\x31\x34\x2e\x32\x37\x2d\x33\x30\ +\x2e\x32\x2d\x32\x38\x2e\x37\x32\x2d\x34\x36\x2e\x36\x31\x2d\x34\ +\x31\x2e\x31\x31\x2d\x33\x38\x2e\x34\x2d\x32\x39\x2d\x38\x31\x2e\ +\x33\x33\x2d\x34\x36\x2e\x37\x34\x2d\x31\x32\x37\x2e\x37\x35\x2d\ +\x35\x34\x2e\x36\x34\x2d\x35\x34\x2d\x39\x2e\x32\x2d\x31\x30\x36\ +\x2e\x39\x32\x2d\x34\x2e\x33\x32\x2d\x31\x35\x38\x2e\x35\x2c\x31\ +\x35\x2e\x32\x32\x2d\x34\x35\x2c\x31\x37\x2d\x38\x34\x2e\x32\x39\ +\x2c\x34\x33\x2e\x39\x33\x2d\x31\x31\x38\x2e\x31\x36\x2c\x37\x39\ +\x2e\x39\x34\x2d\x38\x2e\x35\x37\x2c\x39\x2e\x31\x2d\x31\x38\x2e\ +\x36\x35\x2c\x31\x32\x2e\x33\x32\x2d\x33\x30\x2e\x31\x39\x2c\x38\ +\x2d\x32\x30\x2e\x30\x39\x2d\x37\x2e\x35\x36\x2d\x32\x35\x2e\x32\ +\x2d\x33\x34\x2e\x30\x38\x2d\x39\x2e\x37\x34\x2d\x35\x30\x2e\x36\ +\x31\x61\x33\x38\x30\x2e\x35\x31\x2c\x33\x38\x30\x2e\x35\x31\x2c\ +\x30\x2c\x30\x2c\x31\x2c\x35\x37\x2e\x36\x32\x2d\x35\x30\x2e\x33\ +\x38\x63\x34\x33\x2d\x33\x30\x2e\x35\x38\x2c\x38\x39\x2e\x39\x33\ +\x2d\x35\x31\x2e\x31\x2c\x31\x34\x30\x2e\x37\x37\x2d\x36\x30\x2e\ +\x34\x35\x43\x32\x35\x35\x2e\x30\x37\x2c\x37\x34\x2e\x32\x33\x2c\ +\x32\x37\x37\x2e\x34\x33\x2c\x37\x32\x2e\x38\x35\x2c\x32\x39\x30\ +\x2e\x31\x38\x2c\x37\x31\x2e\x33\x35\x5a\x22\x2f\x3e\x3c\x70\x61\ +\x74\x68\x20\x63\x6c\x61\x73\x73\x3d\x22\x63\x6c\x73\x2d\x32\x22\ +\x20\x64\x3d\x22\x4d\x33\x30\x30\x2c\x35\x31\x37\x2e\x33\x37\x63\ +\x2d\x32\x32\x2c\x30\x2d\x33\x39\x2e\x31\x35\x2d\x31\x38\x2e\x34\ +\x35\x2d\x33\x39\x2e\x31\x31\x2d\x34\x32\x2e\x31\x31\x2c\x30\x2d\ +\x32\x33\x2e\x33\x37\x2c\x31\x37\x2e\x31\x39\x2d\x34\x31\x2e\x38\ +\x33\x2c\x33\x38\x2e\x38\x36\x2d\x34\x31\x2e\x37\x39\x2c\x32\x32\ +\x2e\x32\x35\x2c\x30\x2c\x33\x39\x2e\x33\x32\x2c\x31\x38\x2e\x31\ +\x37\x2c\x33\x39\x2e\x33\x34\x2c\x34\x31\x2e\x38\x31\x53\x33\x32\ +\x32\x2e\x31\x2c\x35\x31\x37\x2e\x33\x37\x2c\x33\x30\x30\x2c\x35\ +\x31\x37\x2e\x33\x37\x5a\x22\x2f\x3e\x3c\x70\x61\x74\x68\x20\x63\ +\x6c\x61\x73\x73\x3d\x22\x63\x6c\x73\x2d\x33\x22\x20\x64\x3d\x22\ +\x4d\x34\x34\x39\x2e\x30\x37\x2c\x32\x38\x30\x2e\x33\x37\x68\x31\ +\x34\x2e\x33\x35\x61\x31\x30\x2e\x36\x34\x2c\x31\x30\x2e\x36\x34\ +\x2c\x30\x2c\x30\x2c\x30\x2c\x32\x2e\x34\x32\x2c\x31\x63\x32\x37\ +\x2e\x34\x37\x2c\x34\x2c\x34\x39\x2e\x36\x2c\x32\x36\x2e\x36\x37\ +\x2c\x35\x31\x2e\x36\x38\x2c\x35\x34\x2e\x32\x36\x2c\x31\x2e\x31\ +\x37\x2c\x31\x35\x2e\x35\x33\x2e\x35\x38\x2c\x33\x31\x2e\x32\x2e\ +\x37\x38\x2c\x34\x36\x2e\x38\x2c\x30\x2c\x32\x2e\x31\x32\x2c\x30\ +\x2c\x34\x2e\x32\x35\x2c\x30\x2c\x36\x2e\x38\x36\x68\x31\x31\x2e\ +\x37\x35\x63\x31\x33\x2e\x39\x34\x2c\x30\x2c\x31\x39\x2e\x33\x36\ +\x2c\x35\x2e\x33\x39\x2c\x31\x39\x2e\x33\x36\x2c\x31\x39\x2e\x32\ +\x32\x2c\x30\x2c\x33\x33\x2e\x32\x35\x2d\x2e\x31\x38\x2c\x36\x36\ +\x2e\x35\x2e\x31\x32\x2c\x39\x39\x2e\x37\x35\x2e\x30\x39\x2c\x39\ +\x2e\x36\x38\x2d\x32\x2e\x39\x33\x2c\x31\x36\x2e\x36\x37\x2d\x31\ +\x32\x2e\x31\x36\x2c\x32\x30\x2e\x33\x39\x48\x33\x37\x35\x2e\x31\ +\x32\x63\x2d\x37\x2e\x32\x34\x2d\x33\x2e\x31\x37\x2d\x31\x32\x2d\ +\x38\x2e\x31\x31\x2d\x31\x32\x2d\x31\x36\x2e\x35\x33\x2c\x30\x2d\ +\x33\x35\x2e\x34\x36\x2d\x2e\x30\x38\x2d\x37\x30\x2e\x39\x31\x2c\ +\x30\x2d\x31\x30\x36\x2e\x33\x37\x2c\x30\x2d\x31\x30\x2e\x32\x39\ +\x2c\x36\x2e\x32\x34\x2d\x31\x36\x2e\x32\x35\x2c\x31\x36\x2e\x35\ +\x36\x2d\x31\x36\x2e\x34\x34\x2c\x34\x2e\x36\x39\x2d\x2e\x30\x39\ +\x2c\x39\x2e\x33\x39\x2c\x30\x2c\x31\x34\x2e\x35\x31\x2c\x30\x2c\ +\x30\x2d\x31\x35\x2e\x37\x33\x2d\x2e\x30\x38\x2d\x33\x30\x2e\x35\ +\x39\x2c\x30\x2d\x34\x35\x2e\x34\x35\x2e\x31\x39\x2d\x32\x38\x2e\ +\x37\x39\x2c\x31\x37\x2e\x32\x38\x2d\x35\x32\x2e\x33\x36\x2c\x34\ +\x33\x2e\x38\x39\x2d\x36\x30\x2e\x35\x38\x43\x34\x34\x31\x2e\x37\ +\x31\x2c\x32\x38\x32\x2e\x31\x34\x2c\x34\x34\x35\x2e\x34\x31\x2c\ +\x32\x38\x31\x2e\x33\x33\x2c\x34\x34\x39\x2e\x30\x37\x2c\x32\x38\ +\x30\x2e\x33\x37\x5a\x6d\x33\x38\x2e\x33\x31\x2c\x31\x30\x38\x2e\ +\x34\x39\x63\x30\x2d\x31\x36\x2e\x35\x34\x2e\x39\x34\x2d\x33\x32\ +\x2e\x37\x33\x2d\x2e\x32\x35\x2d\x34\x38\x2e\x37\x37\x2d\x31\x2e\ +\x33\x31\x2d\x31\x37\x2e\x38\x32\x2d\x31\x35\x2e\x39\x33\x2d\x32\ +\x39\x2e\x37\x35\x2d\x33\x32\x2e\x37\x37\x2d\x32\x38\x2e\x37\x37\ +\x2d\x31\x36\x2e\x36\x35\x2c\x31\x2d\x32\x39\x2e\x32\x31\x2c\x31\ +\x34\x2e\x37\x33\x2d\x32\x39\x2e\x34\x32\x2c\x33\x32\x2e\x35\x32\ +\x2d\x2e\x31\x36\x2c\x31\x33\x2e\x37\x37\x2c\x30\x2c\x32\x37\x2e\ +\x35\x34\x2c\x30\x2c\x34\x31\x2e\x33\x31\x61\x33\x33\x2e\x31\x39\ +\x2c\x33\x33\x2e\x31\x39\x2c\x30\x2c\x30\x2c\x30\x2c\x2e\x34\x32\ +\x2c\x33\x2e\x37\x31\x5a\x6d\x2d\x33\x31\x2e\x33\x33\x2c\x34\x36\ +\x2e\x37\x31\x61\x31\x34\x2e\x39\x31\x2c\x31\x34\x2e\x39\x31\x2c\ +\x30\x2c\x30\x2c\x30\x2d\x31\x33\x2e\x36\x38\x2c\x38\x2e\x36\x36\ +\x63\x2d\x32\x2e\x38\x32\x2c\x35\x2e\x35\x37\x2d\x32\x2e\x38\x2c\ +\x31\x31\x2e\x35\x39\x2c\x31\x2e\x36\x2c\x31\x35\x2e\x38\x35\x2c\ +\x34\x2e\x31\x36\x2c\x34\x2c\x34\x2e\x34\x34\x2c\x38\x2e\x33\x35\ +\x2c\x34\x2e\x32\x32\x2c\x31\x33\x2e\x33\x32\x61\x36\x38\x2e\x33\ +\x38\x2c\x36\x38\x2e\x33\x38\x2c\x30\x2c\x30\x2c\x30\x2c\x30\x2c\ +\x37\x2e\x37\x31\x63\x2e\x33\x37\x2c\x35\x2e\x33\x33\x2c\x33\x2e\ +\x36\x2c\x38\x2e\x38\x33\x2c\x38\x2c\x38\x2e\x38\x37\x73\x37\x2e\ +\x37\x35\x2d\x33\x2e\x34\x36\x2c\x38\x2e\x30\x37\x2d\x38\x2e\x37\ +\x36\x61\x31\x31\x31\x2e\x34\x34\x2c\x31\x31\x31\x2e\x34\x34\x2c\ +\x30\x2c\x30\x2c\x30\x2c\x30\x2d\x31\x31\x2e\x35\x36\x2c\x39\x2e\ +\x36\x38\x2c\x39\x2e\x36\x38\x2c\x30\x2c\x30\x2c\x31\x2c\x33\x2e\ +\x32\x2d\x38\x2e\x31\x33\x63\x34\x2e\x37\x31\x2d\x34\x2e\x36\x34\ +\x2c\x35\x2e\x36\x31\x2d\x31\x30\x2e\x35\x33\x2c\x33\x2d\x31\x36\ +\x2e\x37\x32\x41\x31\x35\x2e\x34\x38\x2c\x31\x35\x2e\x34\x38\x2c\ +\x30\x2c\x30\x2c\x30\x2c\x34\x35\x36\x2e\x30\x35\x2c\x34\x33\x35\ +\x2e\x35\x37\x5a\x22\x2f\x3e\x3c\x70\x61\x74\x68\x20\x63\x6c\x61\ +\x73\x73\x3d\x22\x63\x6c\x73\x2d\x31\x22\x20\x64\x3d\x22\x4d\x33\ +\x37\x39\x2e\x35\x38\x2c\x33\x38\x34\x2e\x33\x31\x63\x33\x2e\x31\ +\x38\x2d\x2e\x30\x36\x2c\x36\x2e\x33\x31\x2d\x2e\x30\x35\x2c\x39\ +\x2e\x36\x31\x2c\x30\x2c\x30\x2d\x33\x2e\x32\x34\x2c\x30\x2d\x36\ +\x2e\x34\x33\x2c\x30\x2d\x39\x2e\x36\x31\x2c\x30\x2d\x31\x30\x2e\ +\x35\x33\x2c\x30\x2d\x32\x30\x2e\x34\x38\x2c\x30\x2d\x33\x30\x2e\ +\x35\x38\x2d\x33\x32\x2e\x31\x34\x2d\x32\x35\x2e\x32\x37\x2d\x36\ +\x38\x2e\x39\x33\x2d\x33\x35\x2e\x34\x32\x2d\x31\x31\x30\x2e\x34\ +\x34\x2d\x32\x39\x2e\x35\x37\x43\x32\x34\x36\x2c\x33\x31\x39\x2e\ +\x31\x34\x2c\x32\x31\x38\x2c\x33\x33\x34\x2e\x36\x31\x2c\x31\x39\ +\x34\x2e\x31\x33\x2c\x33\x35\x39\x63\x2d\x36\x2c\x36\x2e\x31\x37\ +\x2d\x39\x2e\x36\x33\x2c\x31\x33\x2e\x35\x39\x2d\x39\x2e\x36\x31\ +\x2c\x32\x32\x2e\x38\x31\x61\x36\x38\x2e\x39\x33\x2c\x36\x38\x2e\ +\x39\x33\x2c\x30\x2c\x30\x2c\x30\x2c\x31\x2c\x37\x2e\x33\x38\x63\ +\x35\x2e\x36\x33\x2c\x32\x33\x2e\x32\x38\x2c\x33\x31\x2e\x32\x37\ +\x2c\x33\x30\x2e\x32\x38\x2c\x34\x37\x2e\x39\x33\x2c\x31\x33\x2e\ +\x32\x37\x2c\x32\x34\x2e\x36\x31\x2d\x32\x35\x2e\x31\x31\x2c\x35\ +\x33\x2e\x37\x35\x2d\x33\x33\x2e\x38\x36\x2c\x38\x37\x2d\x32\x36\ +\x2e\x37\x37\x2c\x31\x35\x2c\x33\x2e\x32\x2c\x32\x38\x2c\x31\x30\ +\x2e\x33\x35\x2c\x33\x39\x2e\x36\x2c\x32\x30\x2e\x34\x32\x43\x33\ +\x36\x33\x2e\x31\x37\x2c\x33\x38\x38\x2e\x38\x32\x2c\x33\x37\x30\ +\x2e\x30\x37\x2c\x33\x38\x34\x2e\x34\x39\x2c\x33\x37\x39\x2e\x35\ +\x38\x2c\x33\x38\x34\x2e\x33\x31\x5a\x22\x2f\x3e\x3c\x70\x61\x74\ +\x68\x20\x63\x6c\x61\x73\x73\x3d\x22\x63\x6c\x73\x2d\x31\x22\x20\ +\x64\x3d\x22\x4d\x34\x35\x34\x2e\x36\x35\x2c\x33\x31\x36\x2e\x33\ +\x31\x61\x32\x36\x2e\x31\x32\x2c\x32\x36\x2e\x31\x32\x2c\x30\x2c\ +\x30\x2c\x30\x2d\x37\x2e\x37\x39\x2c\x31\x2e\x36\x36\x63\x35\x2e\ +\x35\x39\x2c\x35\x2e\x37\x32\x2c\x31\x32\x2e\x33\x34\x2c\x38\x2e\ +\x37\x38\x2c\x32\x33\x2c\x38\x2e\x36\x31\x61\x32\x34\x2c\x32\x34\ +\x2c\x30\x2c\x30\x2c\x30\x2c\x36\x2e\x32\x35\x2d\x31\x2e\x32\x37\ +\x41\x32\x35\x2e\x36\x38\x2c\x32\x35\x2e\x36\x38\x2c\x30\x2c\x30\ +\x2c\x30\x2c\x34\x35\x34\x2e\x36\x35\x2c\x33\x31\x36\x2e\x33\x31\ +\x5a\x22\x2f\x3e\x3c\x70\x61\x74\x68\x20\x63\x6c\x61\x73\x73\x3d\ +\x22\x63\x6c\x73\x2d\x31\x22\x20\x64\x3d\x22\x4d\x34\x33\x36\x2e\ +\x36\x32\x2c\x32\x37\x38\x2e\x34\x38\x63\x32\x2e\x36\x31\x2d\x2e\ +\x38\x2c\x35\x2e\x32\x33\x2d\x31\x2e\x34\x36\x2c\x37\x2e\x37\x37\ +\x2d\x32\x2e\x30\x39\x2c\x31\x2e\x31\x34\x2d\x2e\x32\x38\x2c\x32\ +\x2e\x32\x38\x2d\x2e\x35\x36\x2c\x33\x2e\x34\x32\x2d\x2e\x38\x36\ +\x6c\x2e\x36\x32\x2d\x2e\x31\x36\x68\x31\x36\x6c\x2e\x39\x33\x2e\ +\x33\x39\x63\x2e\x33\x36\x2e\x31\x35\x2e\x37\x32\x2e\x33\x33\x2c\ +\x31\x2e\x30\x37\x2e\x35\x6c\x2e\x33\x37\x2e\x31\x39\x61\x36\x36\ +\x2e\x31\x38\x2c\x36\x36\x2e\x31\x38\x2c\x30\x2c\x30\x2c\x31\x2c\ +\x32\x36\x2e\x38\x33\x2c\x31\x30\x2e\x33\x38\x2c\x33\x30\x2e\x33\ +\x35\x2c\x33\x30\x2e\x33\x35\x2c\x30\x2c\x30\x2c\x30\x2d\x35\x2e\ +\x39\x34\x2d\x31\x30\x2e\x31\x35\x41\x32\x30\x35\x2c\x32\x30\x35\ +\x2c\x30\x2c\x30\x2c\x30\x2c\x34\x36\x34\x2e\x34\x34\x2c\x32\x35\ +\x34\x63\x2d\x36\x30\x2e\x32\x32\x2d\x35\x30\x2e\x33\x31\x2d\x31\ +\x32\x38\x2e\x33\x31\x2d\x37\x30\x2d\x32\x30\x33\x2e\x39\x31\x2d\ +\x35\x38\x2e\x33\x36\x2d\x35\x37\x2e\x30\x39\x2c\x38\x2e\x38\x2d\ +\x31\x30\x35\x2e\x38\x34\x2c\x33\x36\x2d\x31\x34\x36\x2e\x38\x34\ +\x2c\x37\x39\x2e\x32\x38\x2d\x38\x2e\x32\x37\x2c\x38\x2e\x37\x32\ +\x2d\x31\x30\x2e\x37\x39\x2c\x31\x39\x2e\x36\x32\x2d\x36\x2e\x38\ +\x39\x2c\x33\x31\x2e\x35\x32\x2c\x37\x2e\x32\x34\x2c\x32\x32\x2c\ +\x33\x31\x2e\x35\x36\x2c\x32\x36\x2e\x38\x33\x2c\x34\x37\x2e\x39\ +\x31\x2c\x31\x30\x2c\x34\x39\x2e\x32\x32\x2d\x35\x30\x2e\x35\x37\ +\x2c\x31\x30\x38\x2d\x37\x31\x2e\x30\x37\x2c\x31\x37\x35\x2e\x33\ +\x39\x2d\x36\x31\x2e\x34\x41\x31\x38\x37\x2e\x39\x32\x2c\x31\x38\ +\x37\x2e\x39\x32\x2c\x30\x2c\x30\x2c\x31\x2c\x34\x31\x35\x2c\x32\ +\x38\x39\x2e\x36\x38\x2c\x36\x37\x2e\x34\x39\x2c\x36\x37\x2e\x34\ +\x39\x2c\x30\x2c\x30\x2c\x31\x2c\x34\x33\x36\x2e\x36\x32\x2c\x32\ +\x37\x38\x2e\x34\x38\x5a\x22\x2f\x3e\x3c\x2f\x73\x76\x67\x3e\ \x00\x00\x09\x92\ \x3c\ \x73\x76\x67\x20\x69\x64\x3d\x22\x4c\x61\x79\x65\x72\x5f\x31\x22\ @@ -25810,6 +26935,10 @@ \x00\xdd\x57\xa7\ \x00\x31\ \x00\x62\x00\x61\x00\x72\x00\x5f\x00\x77\x00\x69\x00\x66\x00\x69\x00\x2e\x00\x73\x00\x76\x00\x67\ +\x00\x0d\ +\x02\xdd\x57\xa7\ +\x00\x30\ +\x00\x62\x00\x61\x00\x72\x00\x5f\x00\x77\x00\x69\x00\x66\x00\x69\x00\x2e\x00\x73\x00\x76\x00\x67\ \x00\x0f\ \x05\x15\x19\xa7\ \x00\x77\ @@ -25827,6 +26956,10 @@ \x00\x77\ \x00\x69\x00\x66\x00\x69\x00\x5f\x00\x6c\x00\x6f\x00\x63\x00\x6b\x00\x65\x00\x64\x00\x2e\x00\x73\x00\x76\x00\x67\ \x00\x0d\ +\x0a\xdd\x57\x87\ +\x00\x34\ +\x00\x62\x00\x61\x00\x72\x00\x5f\x00\x77\x00\x69\x00\x66\x00\x69\x00\x2e\x00\x73\x00\x76\x00\x67\ +\x00\x0d\ \x0c\xdd\x57\x87\ \x00\x33\ \x00\x62\x00\x61\x00\x72\x00\x5f\x00\x77\x00\x69\x00\x66\x00\x69\x00\x2e\x00\x73\x00\x76\x00\x67\ @@ -25839,10 +26972,35 @@ \x0e\xdd\x57\x87\ \x00\x32\ \x00\x62\x00\x61\x00\x72\x00\x5f\x00\x77\x00\x69\x00\x66\x00\x69\x00\x2e\x00\x73\x00\x76\x00\x67\ +\x00\x17\ +\x0f\x21\x74\x27\ +\x00\x32\ +\x00\x62\x00\x61\x00\x72\x00\x5f\x00\x77\x00\x69\x00\x66\x00\x69\x00\x5f\x00\x70\x00\x72\x00\x6f\x00\x74\x00\x65\x00\x63\x00\x74\ +\x00\x65\x00\x64\x00\x2e\x00\x73\x00\x76\x00\x67\ \x00\x0b\ \x0f\x22\xf7\x67\ \x00\x6e\ \x00\x6f\x00\x5f\x00\x77\x00\x69\x00\x66\x00\x69\x00\x2e\x00\x73\x00\x76\x00\x67\ +\x00\x17\ +\x0f\x29\x74\x27\ +\x00\x33\ +\x00\x62\x00\x61\x00\x72\x00\x5f\x00\x77\x00\x69\x00\x66\x00\x69\x00\x5f\x00\x70\x00\x72\x00\x6f\x00\x74\x00\x65\x00\x63\x00\x74\ +\x00\x65\x00\x64\x00\x2e\x00\x73\x00\x76\x00\x67\ +\x00\x17\ +\x0f\x31\x74\x27\ +\x00\x34\ +\x00\x62\x00\x61\x00\x72\x00\x5f\x00\x77\x00\x69\x00\x66\x00\x69\x00\x5f\x00\x70\x00\x72\x00\x6f\x00\x74\x00\x65\x00\x63\x00\x74\ +\x00\x65\x00\x64\x00\x2e\x00\x73\x00\x76\x00\x67\ +\x00\x17\ +\x0f\x51\x74\x27\ +\x00\x30\ +\x00\x62\x00\x61\x00\x72\x00\x5f\x00\x77\x00\x69\x00\x66\x00\x69\x00\x5f\x00\x70\x00\x72\x00\x6f\x00\x74\x00\x65\x00\x63\x00\x74\ +\x00\x65\x00\x64\x00\x2e\x00\x73\x00\x76\x00\x67\ +\x00\x17\ +\x0f\x59\x74\x27\ +\x00\x31\ +\x00\x62\x00\x61\x00\x72\x00\x5f\x00\x77\x00\x69\x00\x66\x00\x69\x00\x5f\x00\x70\x00\x72\x00\x6f\x00\x74\x00\x65\x00\x63\x00\x74\ +\x00\x65\x00\x64\x00\x2e\x00\x73\x00\x76\x00\x67\ \x00\x15\ \x00\x03\x60\x47\ \x00\x74\ @@ -26057,9 +27215,9 @@ qt_resource_struct_v1 = b"\ \x00\x00\x00\x00\x00\x02\x00\x00\x00\x10\x00\x00\x00\x01\ -\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x9a\ -\x00\x00\x00\x0a\x00\x02\x00\x00\x00\x01\x00\x00\x00\x97\ -\x00\x00\x00\x1a\x00\x02\x00\x00\x00\x01\x00\x00\x00\x8b\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\xa1\ +\x00\x00\x00\x0a\x00\x02\x00\x00\x00\x01\x00\x00\x00\x9e\ +\x00\x00\x00\x1a\x00\x02\x00\x00\x00\x01\x00\x00\x00\x92\ \x00\x00\x00\x46\x00\x02\x00\x00\x00\x01\x00\x00\x00\x80\ \x00\x00\x00\x5a\x00\x02\x00\x00\x00\x01\x00\x00\x00\x7c\ \x00\x00\x00\x6c\x00\x02\x00\x00\x00\x01\x00\x00\x00\x76\ @@ -26185,83 +27343,90 @@ \x00\x00\x0f\x00\x00\x00\x00\x00\x00\x01\x00\x04\x9a\xb0\ \x00\x00\x0f\x2c\x00\x00\x00\x00\x00\x01\x00\x04\xa1\x97\ \x00\x00\x01\xa0\x00\x02\x00\x00\x00\x01\x00\x00\x00\x81\ -\x00\x00\x01\xb0\x00\x02\x00\x00\x00\x09\x00\x00\x00\x82\ +\x00\x00\x01\xb0\x00\x02\x00\x00\x00\x10\x00\x00\x00\x82\ \x00\x00\x0f\x50\x00\x00\x00\x00\x00\x01\x00\x04\xaa\xfb\ -\x00\x00\x0f\x70\x00\x01\x00\x00\x00\x01\x00\x04\xad\xa7\ -\x00\x00\x0f\x94\x00\x00\x00\x00\x00\x01\x00\x04\xb8\xf8\ -\x00\x00\x0f\xb6\x00\x00\x00\x00\x00\x01\x00\x04\xc0\x9d\ -\x00\x00\x0f\xd2\x00\x00\x00\x00\x00\x01\x00\x04\xd1\x9d\ -\x00\x00\x0f\xf6\x00\x00\x00\x00\x00\x01\x00\x04\xdb\x1e\ -\x00\x00\x10\x16\x00\x00\x00\x00\x00\x01\x00\x04\xe0\xbb\ -\x00\x00\x10\x3e\x00\x00\x00\x00\x00\x01\x00\x04\xea\x84\ -\x00\x00\x10\x5e\x00\x00\x00\x00\x00\x01\x00\x04\xee\x67\ -\x00\x00\x01\xa0\x00\x02\x00\x00\x00\x01\x00\x00\x00\x8c\ -\x00\x00\x01\xb0\x00\x02\x00\x00\x00\x0a\x00\x00\x00\x8d\ -\x00\x00\x10\x7a\x00\x00\x00\x00\x00\x01\x00\x04\xf4\xa2\ -\x00\x00\x10\xaa\x00\x00\x00\x00\x00\x01\x00\x04\xfe\x38\ -\x00\x00\x10\xda\x00\x00\x00\x00\x00\x01\x00\x05\x0a\x14\ -\x00\x00\x10\xfe\x00\x00\x00\x00\x00\x01\x00\x05\x10\x58\ -\x00\x00\x11\x28\x00\x00\x00\x00\x00\x01\x00\x05\x17\xe1\ -\x00\x00\x11\x54\x00\x00\x00\x00\x00\x01\x00\x05\x1e\x3f\ -\x00\x00\x11\x8a\x00\x00\x00\x00\x00\x01\x00\x05\x26\x2e\ -\x00\x00\x09\x20\x00\x00\x00\x00\x00\x01\x00\x05\x33\xd1\ -\x00\x00\x09\x34\x00\x00\x00\x00\x00\x01\x00\x05\x39\x06\ -\x00\x00\x11\xa8\x00\x00\x00\x00\x00\x01\x00\x05\x42\xdb\ -\x00\x00\x01\xa0\x00\x02\x00\x00\x00\x01\x00\x00\x00\x98\ -\x00\x00\x01\xb0\x00\x02\x00\x00\x00\x01\x00\x00\x00\x99\ -\x00\x00\x11\xd0\x00\x00\x00\x00\x00\x01\x00\x05\x4a\xa5\ -\x00\x00\x01\xa0\x00\x02\x00\x00\x00\x01\x00\x00\x00\x9b\ -\x00\x00\x01\xb0\x00\x02\x00\x00\x00\x28\x00\x00\x00\x9c\ -\x00\x00\x11\xf0\x00\x00\x00\x00\x00\x01\x00\x05\x4f\x6b\ -\x00\x00\x12\x06\x00\x00\x00\x00\x00\x01\x00\x05\x57\x1f\ -\x00\x00\x12\x1e\x00\x00\x00\x00\x00\x01\x00\x05\x59\x44\ -\x00\x00\x12\x56\x00\x00\x00\x00\x00\x01\x00\x05\x5a\xc4\ -\x00\x00\x12\x72\x00\x00\x00\x00\x00\x01\x00\x05\x62\x71\ -\x00\x00\x12\x92\x00\x00\x00\x00\x00\x01\x00\x05\x67\x46\ -\x00\x00\x12\xa8\x00\x00\x00\x00\x00\x01\x00\x05\x68\x36\ -\x00\x00\x12\xbe\x00\x00\x00\x00\x00\x01\x00\x05\x6c\x5f\ -\x00\x00\x12\xe4\x00\x00\x00\x00\x00\x01\x00\x05\x72\x96\ -\x00\x00\x12\xfe\x00\x00\x00\x00\x00\x01\x00\x05\x87\xa3\ -\x00\x00\x13\x20\x00\x00\x00\x00\x00\x01\x00\x05\x8c\x93\ -\x00\x00\x13\x36\x00\x00\x00\x00\x00\x01\x00\x05\x8f\x92\ -\x00\x00\x13\x4c\x00\x00\x00\x00\x00\x01\x00\x05\x95\x9c\ -\x00\x00\x13\x7e\x00\x00\x00\x00\x00\x01\x00\x05\x98\xeb\ -\x00\x00\x13\x96\x00\x00\x00\x00\x00\x01\x00\x05\x9d\x0c\ -\x00\x00\x13\xac\x00\x00\x00\x00\x00\x01\x00\x05\xa3\x03\ -\x00\x00\x13\xc0\x00\x00\x00\x00\x00\x01\x00\x05\xa5\x14\ -\x00\x00\x13\xd8\x00\x00\x00\x00\x00\x01\x00\x05\xa8\xd7\ -\x00\x00\x13\xfe\x00\x00\x00\x00\x00\x01\x00\x05\xb2\x8b\ -\x00\x00\x14\x28\x00\x00\x00\x00\x00\x01\x00\x05\xb5\xd9\ -\x00\x00\x14\x4a\x00\x00\x00\x00\x00\x01\x00\x05\xb9\x67\ -\x00\x00\x14\x70\x00\x00\x00\x00\x00\x01\x00\x05\xbe\x08\ -\x00\x00\x14\x84\x00\x00\x00\x00\x00\x01\x00\x05\xc7\xda\ -\x00\x00\x14\xb0\x00\x00\x00\x00\x00\x01\x00\x05\xcd\x24\ -\x00\x00\x14\xd8\x00\x00\x00\x00\x00\x01\x00\x05\xd3\x2b\ -\x00\x00\x14\xee\x00\x00\x00\x00\x00\x01\x00\x05\xd4\x0f\ -\x00\x00\x15\x1a\x00\x00\x00\x00\x00\x01\x00\x05\xd6\x58\ -\x00\x00\x15\x30\x00\x00\x00\x00\x00\x01\x00\x05\xdd\x08\ -\x00\x00\x15\x4c\x00\x00\x00\x00\x00\x01\x00\x05\xe0\x4c\ -\x00\x00\x15\x64\x00\x00\x00\x00\x00\x01\x00\x05\xe1\x78\ -\x00\x00\x15\x7e\x00\x00\x00\x00\x00\x01\x00\x05\xe7\x39\ -\x00\x00\x15\xa0\x00\x00\x00\x00\x00\x01\x00\x05\xe8\x5b\ -\x00\x00\x15\xbe\x00\x00\x00\x00\x00\x01\x00\x05\xee\x4e\ -\x00\x00\x15\xde\x00\x00\x00\x00\x00\x01\x00\x05\xf1\x52\ -\x00\x00\x16\x00\x00\x00\x00\x00\x00\x01\x00\x05\xf2\x73\ -\x00\x00\x16\x20\x00\x00\x00\x00\x00\x01\x00\x05\xf5\x47\ -\x00\x00\x16\x4e\x00\x00\x00\x00\x00\x01\x00\x05\xfd\xb3\ -\x00\x00\x16\x72\x00\x00\x00\x00\x00\x01\x00\x06\x05\x73\ -\x00\x00\x16\x96\x00\x00\x00\x00\x00\x01\x00\x06\x0a\x8e\ -\x00\x00\x16\xbe\x00\x00\x00\x00\x00\x01\x00\x06\x0b\xe1\ +\x00\x00\x0f\x70\x00\x00\x00\x00\x00\x01\x00\x04\xb0\x94\ +\x00\x00\x0f\x90\x00\x01\x00\x00\x00\x01\x00\x04\xb6\xbb\ +\x00\x00\x0f\xb4\x00\x00\x00\x00\x00\x01\x00\x04\xc2\x0c\ +\x00\x00\x0f\xd6\x00\x00\x00\x00\x00\x01\x00\x04\xc9\xb1\ +\x00\x00\x0f\xf2\x00\x00\x00\x00\x00\x01\x00\x04\xda\xb1\ +\x00\x00\x10\x16\x00\x00\x00\x00\x00\x01\x00\x04\xe4\x32\ +\x00\x00\x10\x36\x00\x00\x00\x00\x00\x01\x00\x04\xe9\xb6\ +\x00\x00\x10\x56\x00\x00\x00\x00\x00\x01\x00\x04\xef\x4f\ +\x00\x00\x10\x7e\x00\x00\x00\x00\x00\x01\x00\x04\xf9\x18\ +\x00\x00\x10\x9e\x00\x00\x00\x00\x00\x01\x00\x04\xfe\xb1\ +\x00\x00\x10\xd2\x00\x00\x00\x00\x00\x01\x00\x05\x09\x25\ +\x00\x00\x10\xee\x00\x00\x00\x00\x00\x01\x00\x05\x0f\x60\ +\x00\x00\x11\x22\x00\x00\x00\x00\x00\x01\x00\x05\x19\xda\ +\x00\x00\x11\x56\x00\x00\x00\x00\x00\x01\x00\x05\x24\x39\ +\x00\x00\x11\x8a\x00\x00\x00\x00\x00\x01\x00\x05\x2f\x84\ +\x00\x00\x01\xa0\x00\x02\x00\x00\x00\x01\x00\x00\x00\x93\ +\x00\x00\x01\xb0\x00\x02\x00\x00\x00\x0a\x00\x00\x00\x94\ +\x00\x00\x11\xbe\x00\x00\x00\x00\x00\x01\x00\x05\x39\xf8\ +\x00\x00\x11\xee\x00\x00\x00\x00\x00\x01\x00\x05\x43\x8e\ +\x00\x00\x12\x1e\x00\x00\x00\x00\x00\x01\x00\x05\x4f\x6a\ +\x00\x00\x12\x42\x00\x00\x00\x00\x00\x01\x00\x05\x55\xae\ +\x00\x00\x12\x6c\x00\x00\x00\x00\x00\x01\x00\x05\x5d\x37\ +\x00\x00\x12\x98\x00\x00\x00\x00\x00\x01\x00\x05\x63\x95\ +\x00\x00\x12\xce\x00\x00\x00\x00\x00\x01\x00\x05\x6b\x84\ +\x00\x00\x09\x20\x00\x00\x00\x00\x00\x01\x00\x05\x79\x27\ +\x00\x00\x09\x34\x00\x00\x00\x00\x00\x01\x00\x05\x7e\x5c\ +\x00\x00\x12\xec\x00\x00\x00\x00\x00\x01\x00\x05\x88\x31\ +\x00\x00\x01\xa0\x00\x02\x00\x00\x00\x01\x00\x00\x00\x9f\ +\x00\x00\x01\xb0\x00\x02\x00\x00\x00\x01\x00\x00\x00\xa0\ +\x00\x00\x13\x14\x00\x00\x00\x00\x00\x01\x00\x05\x8f\xfb\ +\x00\x00\x01\xa0\x00\x02\x00\x00\x00\x01\x00\x00\x00\xa2\ +\x00\x00\x01\xb0\x00\x02\x00\x00\x00\x28\x00\x00\x00\xa3\ +\x00\x00\x13\x34\x00\x00\x00\x00\x00\x01\x00\x05\x94\xc1\ +\x00\x00\x13\x4a\x00\x00\x00\x00\x00\x01\x00\x05\x9c\x75\ +\x00\x00\x13\x62\x00\x00\x00\x00\x00\x01\x00\x05\x9e\x9a\ +\x00\x00\x13\x9a\x00\x00\x00\x00\x00\x01\x00\x05\xa0\x1a\ +\x00\x00\x13\xb6\x00\x00\x00\x00\x00\x01\x00\x05\xa7\xc7\ +\x00\x00\x13\xd6\x00\x00\x00\x00\x00\x01\x00\x05\xac\x9c\ +\x00\x00\x13\xec\x00\x00\x00\x00\x00\x01\x00\x05\xad\x8c\ +\x00\x00\x14\x02\x00\x00\x00\x00\x00\x01\x00\x05\xb1\xb5\ +\x00\x00\x14\x28\x00\x00\x00\x00\x00\x01\x00\x05\xb7\xec\ +\x00\x00\x14\x42\x00\x00\x00\x00\x00\x01\x00\x05\xcc\xf9\ +\x00\x00\x14\x64\x00\x00\x00\x00\x00\x01\x00\x05\xd1\xe9\ +\x00\x00\x14\x7a\x00\x00\x00\x00\x00\x01\x00\x05\xd4\xe8\ +\x00\x00\x14\x90\x00\x00\x00\x00\x00\x01\x00\x05\xda\xf2\ +\x00\x00\x14\xc2\x00\x00\x00\x00\x00\x01\x00\x05\xde\x41\ +\x00\x00\x14\xda\x00\x00\x00\x00\x00\x01\x00\x05\xe2\x62\ +\x00\x00\x14\xf0\x00\x00\x00\x00\x00\x01\x00\x05\xe8\x59\ +\x00\x00\x15\x04\x00\x00\x00\x00\x00\x01\x00\x05\xea\x6a\ +\x00\x00\x15\x1c\x00\x00\x00\x00\x00\x01\x00\x05\xee\x2d\ +\x00\x00\x15\x42\x00\x00\x00\x00\x00\x01\x00\x05\xf7\xe1\ +\x00\x00\x15\x6c\x00\x00\x00\x00\x00\x01\x00\x05\xfb\x2f\ +\x00\x00\x15\x8e\x00\x00\x00\x00\x00\x01\x00\x05\xfe\xbd\ +\x00\x00\x15\xb4\x00\x00\x00\x00\x00\x01\x00\x06\x03\x5e\ +\x00\x00\x15\xc8\x00\x00\x00\x00\x00\x01\x00\x06\x0d\x30\ +\x00\x00\x15\xf4\x00\x00\x00\x00\x00\x01\x00\x06\x12\x7a\ +\x00\x00\x16\x1c\x00\x00\x00\x00\x00\x01\x00\x06\x18\x81\ +\x00\x00\x16\x32\x00\x00\x00\x00\x00\x01\x00\x06\x19\x65\ +\x00\x00\x16\x5e\x00\x00\x00\x00\x00\x01\x00\x06\x1b\xae\ +\x00\x00\x16\x74\x00\x00\x00\x00\x00\x01\x00\x06\x22\x5e\ +\x00\x00\x16\x90\x00\x00\x00\x00\x00\x01\x00\x06\x25\xa2\ +\x00\x00\x16\xa8\x00\x00\x00\x00\x00\x01\x00\x06\x26\xce\ +\x00\x00\x16\xc2\x00\x00\x00\x00\x00\x01\x00\x06\x2c\x8f\ +\x00\x00\x16\xe4\x00\x00\x00\x00\x00\x01\x00\x06\x2d\xb1\ +\x00\x00\x17\x02\x00\x00\x00\x00\x00\x01\x00\x06\x33\xa4\ +\x00\x00\x17\x22\x00\x00\x00\x00\x00\x01\x00\x06\x36\xa8\ +\x00\x00\x17\x44\x00\x00\x00\x00\x00\x01\x00\x06\x37\xc9\ +\x00\x00\x17\x64\x00\x00\x00\x00\x00\x01\x00\x06\x3a\x9d\ +\x00\x00\x17\x92\x00\x00\x00\x00\x00\x01\x00\x06\x43\x09\ +\x00\x00\x17\xb6\x00\x00\x00\x00\x00\x01\x00\x06\x4a\xc9\ +\x00\x00\x17\xda\x00\x00\x00\x00\x00\x01\x00\x06\x4f\xe4\ +\x00\x00\x18\x02\x00\x00\x00\x00\x00\x01\x00\x06\x51\x37\ " qt_resource_struct_v2 = b"\ \x00\x00\x00\x00\x00\x02\x00\x00\x00\x10\x00\x00\x00\x01\ \x00\x00\x00\x00\x00\x00\x00\x00\ -\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x9a\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\xa1\ \x00\x00\x00\x00\x00\x00\x00\x00\ -\x00\x00\x00\x0a\x00\x02\x00\x00\x00\x01\x00\x00\x00\x97\ +\x00\x00\x00\x0a\x00\x02\x00\x00\x00\x01\x00\x00\x00\x9e\ \x00\x00\x00\x00\x00\x00\x00\x00\ -\x00\x00\x00\x1a\x00\x02\x00\x00\x00\x01\x00\x00\x00\x8b\ +\x00\x00\x00\x1a\x00\x02\x00\x00\x00\x01\x00\x00\x00\x92\ \x00\x00\x00\x00\x00\x00\x00\x00\ \x00\x00\x00\x46\x00\x02\x00\x00\x00\x01\x00\x00\x00\x80\ \x00\x00\x00\x00\x00\x00\x00\x00\ @@ -26368,9 +27533,9 @@ \x00\x00\x01\xb0\x00\x02\x00\x00\x00\x02\x00\x00\x00\x38\ \x00\x00\x00\x00\x00\x00\x00\x00\ \x00\x00\x05\xfc\x00\x00\x00\x00\x00\x01\x00\x02\x3f\x46\ -\x00\x00\x01\x9b\xbc\x28\x2f\x35\ +\x00\x00\x01\x9b\xbc\x55\x7b\x5e\ \x00\x00\x06\x2a\x00\x00\x00\x00\x00\x01\x00\x02\x41\x71\ -\x00\x00\x01\x9b\xbc\x28\x2f\x35\ +\x00\x00\x01\x9b\xbc\x55\x7b\x5e\ \x00\x00\x01\xa0\x00\x02\x00\x00\x00\x02\x00\x00\x00\x3b\ \x00\x00\x00\x00\x00\x00\x00\x00\ \x00\x00\x01\xb0\x00\x02\x00\x00\x00\x06\x00\x00\x00\x44\ @@ -26513,139 +27678,153 @@ \x00\x00\x01\x9a\x72\xe1\x94\x53\ \x00\x00\x01\xa0\x00\x02\x00\x00\x00\x01\x00\x00\x00\x81\ \x00\x00\x00\x00\x00\x00\x00\x00\ -\x00\x00\x01\xb0\x00\x02\x00\x00\x00\x09\x00\x00\x00\x82\ +\x00\x00\x01\xb0\x00\x02\x00\x00\x00\x10\x00\x00\x00\x82\ \x00\x00\x00\x00\x00\x00\x00\x00\ \x00\x00\x0f\x50\x00\x00\x00\x00\x00\x01\x00\x04\xaa\xfb\ -\x00\x00\x01\x9b\x7f\x73\xe2\xad\ -\x00\x00\x0f\x70\x00\x01\x00\x00\x00\x01\x00\x04\xad\xa7\ +\x00\x00\x01\x9b\xbc\x55\x65\x5e\ +\x00\x00\x0f\x70\x00\x00\x00\x00\x00\x01\x00\x04\xb0\x94\ +\x00\x00\x01\x9b\xbc\x55\x65\x5e\ +\x00\x00\x0f\x90\x00\x01\x00\x00\x00\x01\x00\x04\xb6\xbb\ \x00\x00\x01\x9a\x72\xe1\x94\x5b\ -\x00\x00\x0f\x94\x00\x00\x00\x00\x00\x01\x00\x04\xb8\xf8\ +\x00\x00\x0f\xb4\x00\x00\x00\x00\x00\x01\x00\x04\xc2\x0c\ \x00\x00\x01\x9a\x72\xe1\x94\x57\ -\x00\x00\x0f\xb6\x00\x00\x00\x00\x00\x01\x00\x04\xc0\x9d\ +\x00\x00\x0f\xd6\x00\x00\x00\x00\x00\x01\x00\x04\xc9\xb1\ \x00\x00\x01\x9a\x72\xe1\x94\x53\ -\x00\x00\x0f\xd2\x00\x00\x00\x00\x00\x01\x00\x04\xd1\x9d\ +\x00\x00\x0f\xf2\x00\x00\x00\x00\x00\x01\x00\x04\xda\xb1\ \x00\x00\x01\x9a\x72\xe1\x94\x5b\ -\x00\x00\x0f\xf6\x00\x00\x00\x00\x00\x01\x00\x04\xdb\x1e\ -\x00\x00\x01\x9b\x7f\x73\xe2\xad\ -\x00\x00\x10\x16\x00\x00\x00\x00\x00\x01\x00\x04\xe0\xbb\ +\x00\x00\x10\x16\x00\x00\x00\x00\x00\x01\x00\x04\xe4\x32\ +\x00\x00\x01\x9b\xbc\x55\x65\x5e\ +\x00\x00\x10\x36\x00\x00\x00\x00\x00\x01\x00\x04\xe9\xb6\ +\x00\x00\x01\x9b\xbc\x55\x65\x5e\ +\x00\x00\x10\x56\x00\x00\x00\x00\x00\x01\x00\x04\xef\x4f\ \x00\x00\x01\x9a\x72\xe1\x94\x5b\ -\x00\x00\x10\x3e\x00\x00\x00\x00\x00\x01\x00\x04\xea\x84\ -\x00\x00\x01\x9b\x7f\x73\xe2\xad\ -\x00\x00\x10\x5e\x00\x00\x00\x00\x00\x01\x00\x04\xee\x67\ +\x00\x00\x10\x7e\x00\x00\x00\x00\x00\x01\x00\x04\xf9\x18\ +\x00\x00\x01\x9b\xbc\x55\x65\x5e\ +\x00\x00\x10\x9e\x00\x00\x00\x00\x00\x01\x00\x04\xfe\xb1\ +\x00\x00\x01\x9b\xbc\x55\x65\x5e\ +\x00\x00\x10\xd2\x00\x00\x00\x00\x00\x01\x00\x05\x09\x25\ \x00\x00\x01\x9a\x72\xe1\x94\x53\ -\x00\x00\x01\xa0\x00\x02\x00\x00\x00\x01\x00\x00\x00\x8c\ +\x00\x00\x10\xee\x00\x00\x00\x00\x00\x01\x00\x05\x0f\x60\ +\x00\x00\x01\x9b\xbc\x55\x65\x5e\ +\x00\x00\x11\x22\x00\x00\x00\x00\x00\x01\x00\x05\x19\xda\ +\x00\x00\x01\x9b\xbc\x55\x65\x5e\ +\x00\x00\x11\x56\x00\x00\x00\x00\x00\x01\x00\x05\x24\x39\ +\x00\x00\x01\x9b\xbc\x55\x65\x5e\ +\x00\x00\x11\x8a\x00\x00\x00\x00\x00\x01\x00\x05\x2f\x84\ +\x00\x00\x01\x9b\xbc\x55\x65\x5e\ +\x00\x00\x01\xa0\x00\x02\x00\x00\x00\x01\x00\x00\x00\x93\ \x00\x00\x00\x00\x00\x00\x00\x00\ -\x00\x00\x01\xb0\x00\x02\x00\x00\x00\x0a\x00\x00\x00\x8d\ +\x00\x00\x01\xb0\x00\x02\x00\x00\x00\x0a\x00\x00\x00\x94\ \x00\x00\x00\x00\x00\x00\x00\x00\ -\x00\x00\x10\x7a\x00\x00\x00\x00\x00\x01\x00\x04\xf4\xa2\ +\x00\x00\x11\xbe\x00\x00\x00\x00\x00\x01\x00\x05\x39\xf8\ \x00\x00\x01\x9a\x72\xe1\x94\x5b\ -\x00\x00\x10\xaa\x00\x00\x00\x00\x00\x01\x00\x04\xfe\x38\ +\x00\x00\x11\xee\x00\x00\x00\x00\x00\x01\x00\x05\x43\x8e\ \x00\x00\x01\x9a\x72\xe1\x94\x5b\ -\x00\x00\x10\xda\x00\x00\x00\x00\x00\x01\x00\x05\x0a\x14\ +\x00\x00\x12\x1e\x00\x00\x00\x00\x00\x01\x00\x05\x4f\x6a\ \x00\x00\x01\x9a\x72\xe1\x94\x5b\ -\x00\x00\x10\xfe\x00\x00\x00\x00\x00\x01\x00\x05\x10\x58\ +\x00\x00\x12\x42\x00\x00\x00\x00\x00\x01\x00\x05\x55\xae\ \x00\x00\x01\x9a\x72\xe1\x94\x5b\ -\x00\x00\x11\x28\x00\x00\x00\x00\x00\x01\x00\x05\x17\xe1\ +\x00\x00\x12\x6c\x00\x00\x00\x00\x00\x01\x00\x05\x5d\x37\ \x00\x00\x01\x9a\x72\xe1\x94\x53\ -\x00\x00\x11\x54\x00\x00\x00\x00\x00\x01\x00\x05\x1e\x3f\ +\x00\x00\x12\x98\x00\x00\x00\x00\x00\x01\x00\x05\x63\x95\ \x00\x00\x01\x9a\x72\xe1\x94\x57\ -\x00\x00\x11\x8a\x00\x00\x00\x00\x00\x01\x00\x05\x26\x2e\ +\x00\x00\x12\xce\x00\x00\x00\x00\x00\x01\x00\x05\x6b\x84\ \x00\x00\x01\x9a\x72\xe1\x94\x4f\ -\x00\x00\x09\x20\x00\x00\x00\x00\x00\x01\x00\x05\x33\xd1\ +\x00\x00\x09\x20\x00\x00\x00\x00\x00\x01\x00\x05\x79\x27\ \x00\x00\x01\x9a\x72\xe1\x94\x4f\ -\x00\x00\x09\x34\x00\x00\x00\x00\x00\x01\x00\x05\x39\x06\ +\x00\x00\x09\x34\x00\x00\x00\x00\x00\x01\x00\x05\x7e\x5c\ \x00\x00\x01\x9a\x72\xe1\x94\x4f\ -\x00\x00\x11\xa8\x00\x00\x00\x00\x00\x01\x00\x05\x42\xdb\ +\x00\x00\x12\xec\x00\x00\x00\x00\x00\x01\x00\x05\x88\x31\ \x00\x00\x01\x9a\x72\xe1\x94\x4f\ -\x00\x00\x01\xa0\x00\x02\x00\x00\x00\x01\x00\x00\x00\x98\ +\x00\x00\x01\xa0\x00\x02\x00\x00\x00\x01\x00\x00\x00\x9f\ \x00\x00\x00\x00\x00\x00\x00\x00\ -\x00\x00\x01\xb0\x00\x02\x00\x00\x00\x01\x00\x00\x00\x99\ +\x00\x00\x01\xb0\x00\x02\x00\x00\x00\x01\x00\x00\x00\xa0\ \x00\x00\x00\x00\x00\x00\x00\x00\ -\x00\x00\x11\xd0\x00\x00\x00\x00\x00\x01\x00\x05\x4a\xa5\ +\x00\x00\x13\x14\x00\x00\x00\x00\x00\x01\x00\x05\x8f\xfb\ \x00\x00\x01\x9a\x72\xe1\x94\x4f\ -\x00\x00\x01\xa0\x00\x02\x00\x00\x00\x01\x00\x00\x00\x9b\ +\x00\x00\x01\xa0\x00\x02\x00\x00\x00\x01\x00\x00\x00\xa2\ \x00\x00\x00\x00\x00\x00\x00\x00\ -\x00\x00\x01\xb0\x00\x02\x00\x00\x00\x28\x00\x00\x00\x9c\ +\x00\x00\x01\xb0\x00\x02\x00\x00\x00\x28\x00\x00\x00\xa3\ \x00\x00\x00\x00\x00\x00\x00\x00\ -\x00\x00\x11\xf0\x00\x00\x00\x00\x00\x01\x00\x05\x4f\x6b\ +\x00\x00\x13\x34\x00\x00\x00\x00\x00\x01\x00\x05\x94\xc1\ \x00\x00\x01\x9a\x72\xe1\x94\x5b\ -\x00\x00\x12\x06\x00\x00\x00\x00\x00\x01\x00\x05\x57\x1f\ +\x00\x00\x13\x4a\x00\x00\x00\x00\x00\x01\x00\x05\x9c\x75\ \x00\x00\x01\x9a\x72\xe1\x94\x53\ -\x00\x00\x12\x1e\x00\x00\x00\x00\x00\x01\x00\x05\x59\x44\ +\x00\x00\x13\x62\x00\x00\x00\x00\x00\x01\x00\x05\x9e\x9a\ \x00\x00\x01\x9a\x72\xe1\x94\x53\ -\x00\x00\x12\x56\x00\x00\x00\x00\x00\x01\x00\x05\x5a\xc4\ +\x00\x00\x13\x9a\x00\x00\x00\x00\x00\x01\x00\x05\xa0\x1a\ \x00\x00\x01\x9a\x72\xe1\x94\x57\ -\x00\x00\x12\x72\x00\x00\x00\x00\x00\x01\x00\x05\x62\x71\ +\x00\x00\x13\xb6\x00\x00\x00\x00\x00\x01\x00\x05\xa7\xc7\ \x00\x00\x01\x9a\x72\xe1\x94\x57\ -\x00\x00\x12\x92\x00\x00\x00\x00\x00\x01\x00\x05\x67\x46\ +\x00\x00\x13\xd6\x00\x00\x00\x00\x00\x01\x00\x05\xac\x9c\ \x00\x00\x01\x9a\x72\xe1\x94\x53\ -\x00\x00\x12\xa8\x00\x00\x00\x00\x00\x01\x00\x05\x68\x36\ -\x00\x00\x01\x9b\xbc\x0f\x8a\x2e\ -\x00\x00\x12\xbe\x00\x00\x00\x00\x00\x01\x00\x05\x6c\x5f\ +\x00\x00\x13\xec\x00\x00\x00\x00\x00\x01\x00\x05\xad\x8c\ +\x00\x00\x01\x9b\xbc\x55\x7b\x5e\ +\x00\x00\x14\x02\x00\x00\x00\x00\x00\x01\x00\x05\xb1\xb5\ \x00\x00\x01\x9a\x72\xe1\x94\x5b\ -\x00\x00\x12\xe4\x00\x00\x00\x00\x00\x01\x00\x05\x72\x96\ +\x00\x00\x14\x28\x00\x00\x00\x00\x00\x01\x00\x05\xb7\xec\ \x00\x00\x01\x9a\x72\xe1\x94\x57\ -\x00\x00\x12\xfe\x00\x00\x00\x00\x00\x01\x00\x05\x87\xa3\ +\x00\x00\x14\x42\x00\x00\x00\x00\x00\x01\x00\x05\xcc\xf9\ \x00\x00\x01\x9a\x72\xe1\x94\x4f\ -\x00\x00\x13\x20\x00\x00\x00\x00\x00\x01\x00\x05\x8c\x93\ +\x00\x00\x14\x64\x00\x00\x00\x00\x00\x01\x00\x05\xd1\xe9\ \x00\x00\x01\x9a\x72\xe1\x94\x4b\ -\x00\x00\x13\x36\x00\x00\x00\x00\x00\x01\x00\x05\x8f\x92\ +\x00\x00\x14\x7a\x00\x00\x00\x00\x00\x01\x00\x05\xd4\xe8\ \x00\x00\x01\x9a\x72\xe1\x94\x57\ -\x00\x00\x13\x4c\x00\x00\x00\x00\x00\x01\x00\x05\x95\x9c\ +\x00\x00\x14\x90\x00\x00\x00\x00\x00\x01\x00\x05\xda\xf2\ \x00\x00\x01\x9a\x72\xe1\x94\x4f\ -\x00\x00\x13\x7e\x00\x00\x00\x00\x00\x01\x00\x05\x98\xeb\ -\x00\x00\x01\x9b\xbc\x0f\x8a\x2e\ -\x00\x00\x13\x96\x00\x00\x00\x00\x00\x01\x00\x05\x9d\x0c\ +\x00\x00\x14\xc2\x00\x00\x00\x00\x00\x01\x00\x05\xde\x41\ +\x00\x00\x01\x9b\xbc\x55\x7b\x5e\ +\x00\x00\x14\xda\x00\x00\x00\x00\x00\x01\x00\x05\xe2\x62\ \x00\x00\x01\x9a\x72\xe1\x94\x4b\ -\x00\x00\x13\xac\x00\x00\x00\x00\x00\x01\x00\x05\xa3\x03\ +\x00\x00\x14\xf0\x00\x00\x00\x00\x00\x01\x00\x05\xe8\x59\ \x00\x00\x01\x9a\x72\xe1\x94\x57\ -\x00\x00\x13\xc0\x00\x00\x00\x00\x00\x01\x00\x05\xa5\x14\ +\x00\x00\x15\x04\x00\x00\x00\x00\x00\x01\x00\x05\xea\x6a\ \x00\x00\x01\x9a\x72\xe1\x94\x5b\ -\x00\x00\x13\xd8\x00\x00\x00\x00\x00\x01\x00\x05\xa8\xd7\ +\x00\x00\x15\x1c\x00\x00\x00\x00\x00\x01\x00\x05\xee\x2d\ \x00\x00\x01\x9a\x72\xe1\x94\x4b\ -\x00\x00\x13\xfe\x00\x00\x00\x00\x00\x01\x00\x05\xb2\x8b\ +\x00\x00\x15\x42\x00\x00\x00\x00\x00\x01\x00\x05\xf7\xe1\ \x00\x00\x01\x9a\x72\xe1\x94\x57\ -\x00\x00\x14\x28\x00\x00\x00\x00\x00\x01\x00\x05\xb5\xd9\ +\x00\x00\x15\x6c\x00\x00\x00\x00\x00\x01\x00\x05\xfb\x2f\ \x00\x00\x01\x9a\x72\xe1\x94\x57\ -\x00\x00\x14\x4a\x00\x00\x00\x00\x00\x01\x00\x05\xb9\x67\ +\x00\x00\x15\x8e\x00\x00\x00\x00\x00\x01\x00\x05\xfe\xbd\ \x00\x00\x01\x9a\x72\xe1\x94\x4f\ -\x00\x00\x14\x70\x00\x00\x00\x00\x00\x01\x00\x05\xbe\x08\ +\x00\x00\x15\xb4\x00\x00\x00\x00\x00\x01\x00\x06\x03\x5e\ \x00\x00\x01\x9a\x72\xe1\x94\x57\ -\x00\x00\x14\x84\x00\x00\x00\x00\x00\x01\x00\x05\xc7\xda\ +\x00\x00\x15\xc8\x00\x00\x00\x00\x00\x01\x00\x06\x0d\x30\ \x00\x00\x01\x9a\x72\xe1\x94\x53\ -\x00\x00\x14\xb0\x00\x00\x00\x00\x00\x01\x00\x05\xcd\x24\ +\x00\x00\x15\xf4\x00\x00\x00\x00\x00\x01\x00\x06\x12\x7a\ \x00\x00\x01\x9a\x72\xe1\x94\x57\ -\x00\x00\x14\xd8\x00\x00\x00\x00\x00\x01\x00\x05\xd3\x2b\ +\x00\x00\x16\x1c\x00\x00\x00\x00\x00\x01\x00\x06\x18\x81\ \x00\x00\x01\x9a\x72\xe1\x94\x57\ -\x00\x00\x14\xee\x00\x00\x00\x00\x00\x01\x00\x05\xd4\x0f\ +\x00\x00\x16\x32\x00\x00\x00\x00\x00\x01\x00\x06\x19\x65\ \x00\x00\x01\x9a\x72\xe1\x94\x4f\ -\x00\x00\x15\x1a\x00\x00\x00\x00\x00\x01\x00\x05\xd6\x58\ +\x00\x00\x16\x5e\x00\x00\x00\x00\x00\x01\x00\x06\x1b\xae\ \x00\x00\x01\x9a\x72\xe1\x94\x5b\ -\x00\x00\x15\x30\x00\x00\x00\x00\x00\x01\x00\x05\xdd\x08\ +\x00\x00\x16\x74\x00\x00\x00\x00\x00\x01\x00\x06\x22\x5e\ \x00\x00\x01\x9a\x72\xe1\x94\x57\ -\x00\x00\x15\x4c\x00\x00\x00\x00\x00\x01\x00\x05\xe0\x4c\ +\x00\x00\x16\x90\x00\x00\x00\x00\x00\x01\x00\x06\x25\xa2\ \x00\x00\x01\x9a\x72\xe1\x94\x53\ -\x00\x00\x15\x64\x00\x00\x00\x00\x00\x01\x00\x05\xe1\x78\ +\x00\x00\x16\xa8\x00\x00\x00\x00\x00\x01\x00\x06\x26\xce\ \x00\x00\x01\x9a\x72\xe1\x94\x53\ -\x00\x00\x15\x7e\x00\x00\x00\x00\x00\x01\x00\x05\xe7\x39\ +\x00\x00\x16\xc2\x00\x00\x00\x00\x00\x01\x00\x06\x2c\x8f\ \x00\x00\x01\x9a\x72\xe1\x94\x57\ -\x00\x00\x15\xa0\x00\x00\x00\x00\x00\x01\x00\x05\xe8\x5b\ +\x00\x00\x16\xe4\x00\x00\x00\x00\x00\x01\x00\x06\x2d\xb1\ \x00\x00\x01\x9a\x72\xe1\x94\x57\ -\x00\x00\x15\xbe\x00\x00\x00\x00\x00\x01\x00\x05\xee\x4e\ +\x00\x00\x17\x02\x00\x00\x00\x00\x00\x01\x00\x06\x33\xa4\ \x00\x00\x01\x9a\x72\xe1\x94\x53\ -\x00\x00\x15\xde\x00\x00\x00\x00\x00\x01\x00\x05\xf1\x52\ +\x00\x00\x17\x22\x00\x00\x00\x00\x00\x01\x00\x06\x36\xa8\ \x00\x00\x01\x9a\x72\xe1\x94\x57\ -\x00\x00\x16\x00\x00\x00\x00\x00\x00\x01\x00\x05\xf2\x73\ +\x00\x00\x17\x44\x00\x00\x00\x00\x00\x01\x00\x06\x37\xc9\ \x00\x00\x01\x9a\x72\xe1\x94\x57\ -\x00\x00\x16\x20\x00\x00\x00\x00\x00\x01\x00\x05\xf5\x47\ +\x00\x00\x17\x64\x00\x00\x00\x00\x00\x01\x00\x06\x3a\x9d\ \x00\x00\x01\x9a\x72\xe1\x94\x53\ -\x00\x00\x16\x4e\x00\x00\x00\x00\x00\x01\x00\x05\xfd\xb3\ +\x00\x00\x17\x92\x00\x00\x00\x00\x00\x01\x00\x06\x43\x09\ \x00\x00\x01\x9a\x72\xe1\x94\x4f\ -\x00\x00\x16\x72\x00\x00\x00\x00\x00\x01\x00\x06\x05\x73\ +\x00\x00\x17\xb6\x00\x00\x00\x00\x00\x01\x00\x06\x4a\xc9\ \x00\x00\x01\x9a\x72\xe1\x94\x53\ -\x00\x00\x16\x96\x00\x00\x00\x00\x00\x01\x00\x06\x0a\x8e\ +\x00\x00\x17\xda\x00\x00\x00\x00\x00\x01\x00\x06\x4f\xe4\ \x00\x00\x01\x9a\x72\xe1\x94\x4f\ -\x00\x00\x16\xbe\x00\x00\x00\x00\x00\x01\x00\x06\x0b\xe1\ +\x00\x00\x18\x02\x00\x00\x00\x00\x00\x01\x00\x06\x51\x37\ \x00\x00\x01\x9a\x72\xe1\x94\x4b\ " diff --git a/BlocksScreen/lib/ui/resources/media/btn_icons/0bar_wifi.svg b/BlocksScreen/lib/ui/resources/media/btn_icons/0bar_wifi.svg new file mode 100644 index 00000000..ceaff53d --- /dev/null +++ b/BlocksScreen/lib/ui/resources/media/btn_icons/0bar_wifi.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/BlocksScreen/lib/ui/resources/media/btn_icons/0bar_wifi_protected.svg b/BlocksScreen/lib/ui/resources/media/btn_icons/0bar_wifi_protected.svg new file mode 100644 index 00000000..a10ea388 --- /dev/null +++ b/BlocksScreen/lib/ui/resources/media/btn_icons/0bar_wifi_protected.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/BlocksScreen/lib/ui/resources/media/btn_icons/1bar_wifi.svg b/BlocksScreen/lib/ui/resources/media/btn_icons/1bar_wifi.svg index debc48b2..3258893d 100644 --- a/BlocksScreen/lib/ui/resources/media/btn_icons/1bar_wifi.svg +++ b/BlocksScreen/lib/ui/resources/media/btn_icons/1bar_wifi.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/BlocksScreen/lib/ui/resources/media/btn_icons/1bar_wifi_protected.svg b/BlocksScreen/lib/ui/resources/media/btn_icons/1bar_wifi_protected.svg new file mode 100644 index 00000000..8793447e --- /dev/null +++ b/BlocksScreen/lib/ui/resources/media/btn_icons/1bar_wifi_protected.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/BlocksScreen/lib/ui/resources/media/btn_icons/2bar_wifi.svg b/BlocksScreen/lib/ui/resources/media/btn_icons/2bar_wifi.svg index d9ba78c1..203b70bb 100644 --- a/BlocksScreen/lib/ui/resources/media/btn_icons/2bar_wifi.svg +++ b/BlocksScreen/lib/ui/resources/media/btn_icons/2bar_wifi.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/BlocksScreen/lib/ui/resources/media/btn_icons/2bar_wifi_protected.svg b/BlocksScreen/lib/ui/resources/media/btn_icons/2bar_wifi_protected.svg new file mode 100644 index 00000000..a9f3233b --- /dev/null +++ b/BlocksScreen/lib/ui/resources/media/btn_icons/2bar_wifi_protected.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/BlocksScreen/lib/ui/resources/media/btn_icons/3bar_wifi.svg b/BlocksScreen/lib/ui/resources/media/btn_icons/3bar_wifi.svg index 7c694b76..8d98855c 100644 --- a/BlocksScreen/lib/ui/resources/media/btn_icons/3bar_wifi.svg +++ b/BlocksScreen/lib/ui/resources/media/btn_icons/3bar_wifi.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/BlocksScreen/lib/ui/resources/media/btn_icons/3bar_wifi_protected.svg b/BlocksScreen/lib/ui/resources/media/btn_icons/3bar_wifi_protected.svg new file mode 100644 index 00000000..458c1ac5 --- /dev/null +++ b/BlocksScreen/lib/ui/resources/media/btn_icons/3bar_wifi_protected.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/BlocksScreen/lib/ui/resources/media/btn_icons/4bar_wifi.svg b/BlocksScreen/lib/ui/resources/media/btn_icons/4bar_wifi.svg new file mode 100644 index 00000000..9aadd8e7 --- /dev/null +++ b/BlocksScreen/lib/ui/resources/media/btn_icons/4bar_wifi.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/BlocksScreen/lib/ui/resources/media/btn_icons/4bar_wifi_protected.svg b/BlocksScreen/lib/ui/resources/media/btn_icons/4bar_wifi_protected.svg new file mode 100644 index 00000000..639762e7 --- /dev/null +++ b/BlocksScreen/lib/ui/resources/media/btn_icons/4bar_wifi_protected.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/BlocksScreen/lib/ui/wifiConnectivityWindow.ui b/BlocksScreen/lib/ui/wifiConnectivityWindow.ui index 871ba2dd..688da18c 100644 --- a/BlocksScreen/lib/ui/wifiConnectivityWindow.ui +++ b/BlocksScreen/lib/ui/wifiConnectivityWindow.ui @@ -6,7 +6,7 @@ 0 0 - 800 + 852 480 @@ -697,7 +697,61 @@ using the buttons on the side. - + + + + + + 1 + 1 + + + + + 0 + 0 + + + + BlankCursor + + + background-color: rgba(255, 255, 255, 0); + + + QFrame::NoFrame + + + QFrame::Plain + + + Qt::ScrollBarAlwaysOff + + + Qt::ScrollBarAlwaysOff + + + QAbstractItemView::ScrollPerPixel + + + QAbstractItemView::ScrollPerPixel + + + + + + + + 0 + 0 + + + + Qt::Vertical + + + + @@ -3116,11 +3170,6 @@ Password QLabel
lib.panels.widgets.loadWidget
- - GroupButton - QPushButton -
lib.utils.group_button
-
diff --git a/BlocksScreen/lib/ui/wifiConnectivityWindow_ui.py b/BlocksScreen/lib/ui/wifiConnectivityWindow_ui.py index e66053a9..4a41aae5 100644 --- a/BlocksScreen/lib/ui/wifiConnectivityWindow_ui.py +++ b/BlocksScreen/lib/ui/wifiConnectivityWindow_ui.py @@ -300,14 +300,83 @@ def setupUi(self, wifi_stacked_page): self.nl_back_button.setObjectName("nl_back_button") self.nl_header_layout.addWidget(self.nl_back_button) self.verticalLayout_9.addLayout(self.nl_header_layout) - self.nl_content_layout = QtWidgets.QVBoxLayout() - self.nl_content_layout.setObjectName("nl_content_layout") - self.verticalLayout_9.addLayout(self.nl_content_layout) + self.horizontalLayout_2 = QtWidgets.QHBoxLayout() + self.horizontalLayout_2.setObjectName("horizontalLayout_2") + self.listView = QtWidgets.QListView(self.network_list_page) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.MinimumExpanding, QtWidgets.QSizePolicy.Policy.MinimumExpanding) + sizePolicy.setHorizontalStretch(1) + sizePolicy.setVerticalStretch(1) + sizePolicy.setHeightForWidth(self.listView.sizePolicy().hasHeightForWidth()) + self.listView.setSizePolicy(sizePolicy) + self.listView.setMinimumSize(QtCore.QSize(0, 0)) + self.listView.setStyleSheet("background-color: rgba(255, 255, 255, 0);") + self.listView.setFrameShape(QtWidgets.QFrame.Shape.NoFrame) + self.listView.setFrameShadow(QtWidgets.QFrame.Shadow.Plain) + self.listView.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarPolicy.ScrollBarAlwaysOff) + self.listView.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarPolicy.ScrollBarAlwaysOff) + self.listView.setSelectionBehavior( + QtWidgets.QAbstractItemView.SelectionBehavior.SelectItems + ) + self.listView.setHorizontalScrollBarPolicy( # No horizontal scroll + QtCore.Qt.ScrollBarPolicy.ScrollBarAlwaysOff + ) + self.listView.setVerticalScrollMode( + QtWidgets.QAbstractItemView.ScrollMode.ScrollPerPixel + ) + self.listView.setVerticalScrollBarPolicy( + QtCore.Qt.ScrollBarPolicy.ScrollBarAlwaysOff + ) + QtWidgets.QScroller.grabGesture( + self.listView, + QtWidgets.QScroller.ScrollerGestureType.TouchGesture, + ) + QtWidgets.QScroller.grabGesture( + self.listView, + QtWidgets.QScroller.ScrollerGestureType.LeftMouseButtonGesture, + ) + self.listView.setEditTriggers( + QtWidgets.QAbstractItemView.EditTrigger.NoEditTriggers + ) + + scroller_instance = QtWidgets.QScroller.scroller(self.listView) + scroller_props = scroller_instance.scrollerProperties() + scroller_props.setScrollMetric( + QtWidgets.QScrollerProperties.ScrollMetric.DragVelocitySmoothingFactor, + 0.05, # Lower = more responsive + ) + scroller_props.setScrollMetric( + QtWidgets.QScrollerProperties.ScrollMetric.DecelerationFactor, + 0.4, # higher = less inertia + ) + QtWidgets.QScroller.scroller(self.listView).setScrollerProperties( + scroller_props + ) + self.verticalScrollBar = CustomScrollBar(parent=self.network_list_page) + self.listView.setVerticalScrollBar(self.verticalScrollBar) + self.listView.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarPolicy.ScrollBarAsNeeded) + self.horizontalLayout_2.addWidget(self.listView) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Fixed, QtWidgets.QSizePolicy.Policy.Minimum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.verticalScrollBar.sizePolicy().hasHeightForWidth()) + self.verticalScrollBar.setSizePolicy(sizePolicy) + self.verticalScrollBar.setOrientation(QtCore.Qt.Orientation.Vertical) + self.verticalScrollBar.setObjectName("verticalScrollBar") + self.listView.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollMode.ScrollPerPixel) + self.listView.setUniformItemSizes(True) + #self.listView.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarPolicy.ScrollBarAsNeeded) + self.listView.setSpacing(5) + self.horizontalLayout_2.addWidget(self.verticalScrollBar) + self.verticalLayout_9.addLayout(self.horizontalLayout_2) wifi_stacked_page.addWidget(self.network_list_page) self.add_network_page = QtWidgets.QWidget() self.add_network_page.setObjectName("add_network_page") self.verticalLayout_10 = QtWidgets.QVBoxLayout(self.add_network_page) self.verticalLayout_10.setObjectName("verticalLayout_10") + self.verticalScrollBar.setAttribute( + QtCore.Qt.WidgetAttribute.WA_TransparentForMouseEvents, True + ) + self.scroller = QtWidgets.QScroller.scroller(self.listView) self.add_np_header_layout = QtWidgets.QHBoxLayout() self.add_np_header_layout.setObjectName("add_np_header_layout") spacerItem1 = QtWidgets.QSpacerItem(40, 60, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Minimum) @@ -1144,38 +1213,38 @@ def setupUi(self, wifi_stacked_page): wifi_stacked_page.addWidget(self.hotspot_page) self.retranslateUi(wifi_stacked_page) - wifi_stacked_page.setCurrentIndex(4) + wifi_stacked_page.setCurrentIndex(2) QtCore.QMetaObject.connectSlotsByName(wifi_stacked_page) def retranslateUi(self, wifi_stacked_page): _translate = QtCore.QCoreApplication.translate wifi_stacked_page.setWindowTitle(_translate("wifi_stacked_page", "StackedWidget")) - self.network_main_title.setText(_translate("wifi_stacked_page", "Networks")) - self.netlist_strength_label.setText(_translate("wifi_stacked_page", "Signal\n" -"Strength")) - self.netlist_strength.setText(_translate("wifi_stacked_page", "TextLabel")) - self.netlist_security_label.setText(_translate("wifi_stacked_page", "Security\n" -"Type")) - self.netlist_security.setText(_translate("wifi_stacked_page", "TextLabel")) - self.mn_info_box.setText(_translate("wifi_stacked_page", "No network connection.\n" + self.network_main_title.setText("Networks") + self.netlist_strength_label.setText( "Signal\n" +"Strength") + self.netlist_strength.setText( "TextLabel") + self.netlist_security_label.setText( "Security\n" +"Type") + self.netlist_security.setText("TextLabel") + self.mn_info_box.setText( "No network connection.\n" "\n" "Try connecting to Wi-Fi \n" "or turn on the hotspot\n" -"using the buttons on the side.")) - self.wifi_button.setText(_translate("wifi_stacked_page", "Wi-Fi")) - self.hotspot_button.setText(_translate("wifi_stacked_page", "Hotspot")) - self.rescan_button.setText(_translate("wifi_stacked_page", "Reload")) +"using the buttons on the side.") + self.wifi_button.setText( "Wi-Fi") + self.hotspot_button.setText( "Hotspot") + self.rescan_button.setText( "Reload") self.rescan_button.setProperty("button_type", _translate("wifi_stacked_page", "icon")) - self.network_list_title.setText(_translate("wifi_stacked_page", "Wi-Fi List")) - self.nl_back_button.setText(_translate("wifi_stacked_page", "Back")) + self.network_list_title.setText("Wi-Fi List") + self.nl_back_button.setText("Back") self.nl_back_button.setProperty("class", _translate("wifi_stacked_page", "back_btn")) self.nl_back_button.setProperty("button_type", _translate("wifi_stacked_page", "icon")) - self.add_network_network_label.setText(_translate("wifi_stacked_page", "TextLabel")) - self.add_network_page_backButton.setText(_translate("wifi_stacked_page", "Back")) + self.add_network_network_label.setText("TextLabel") + self.add_network_page_backButton.setText("Back") self.add_network_page_backButton.setProperty("class", _translate("wifi_stacked_page", "back_btn")) self.add_network_page_backButton.setProperty("button_type", _translate("wifi_stacked_page", "icon")) - self.add_network_password_label.setText(_translate("wifi_stacked_page", "Password")) - self.add_network_password_view.setText(_translate("wifi_stacked_page", "View")) + self.add_network_password_label.setText("Password") + self.add_network_password_view.setText("View") self.add_network_password_view.setProperty("class", _translate("wifi_stacked_page", "back_btn")) self.add_network_password_view.setProperty("button_type", _translate("wifi_stacked_page", "icon")) self.add_network_validation_button.setText(_translate("wifi_stacked_page", "Activate")) @@ -1216,13 +1285,14 @@ def retranslateUi(self, wifi_stacked_page): self.hotspot_back_button.setText(_translate("wifi_stacked_page", "Back")) self.hotspot_back_button.setProperty("class", _translate("wifi_stacked_page", "back_btn")) self.hotspot_back_button.setProperty("button_type", _translate("wifi_stacked_page", "icon")) - self.hotspot_info_name_label.setText(_translate("wifi_stacked_page", "Hotspot Name: ")) - self.hotspot_info_password_label.setText(_translate("wifi_stacked_page", "Hotspot Password:")) - self.hotspot_password_view_button.setText(_translate("wifi_stacked_page", "View")) + self.hotspot_info_name_label.setText("Hotspot Name: ") + self.hotspot_info_password_label.setText("Hotspot Password:") + self.hotspot_password_view_button.setText("View") self.hotspot_password_view_button.setProperty("class", _translate("wifi_stacked_page", "back_btn")) self.hotspot_password_view_button.setProperty("button_type", _translate("wifi_stacked_page", "icon")) - self.hotspot_change_confirm.setText(_translate("wifi_stacked_page", "Save")) + self.hotspot_change_confirm.setText("Save") from lib.panels.widgets.loadWidget import LoadingOverlayWidget +from lib.utils.blocks_Scrollbar import CustomScrollBar from lib.utils.blocks_button import BlocksCustomButton from lib.utils.blocks_frame import BlocksCustomFrame from lib.utils.blocks_linedit import BlocksCustomLinEdit diff --git a/BlocksScreen/lib/utils/blocks_Scrollbar.py b/BlocksScreen/lib/utils/blocks_Scrollbar.py index 38e571e0..df2e436d 100644 --- a/BlocksScreen/lib/utils/blocks_Scrollbar.py +++ b/BlocksScreen/lib/utils/blocks_Scrollbar.py @@ -1,5 +1,5 @@ -from PyQt6 import QtCore, QtGui, QtWidgets import numpy as np +from PyQt6 import QtCore, QtGui, QtWidgets class CustomScrollBar(QtWidgets.QScrollBar): @@ -26,34 +26,16 @@ def paintEvent(self, event): handle_percentage = int((self.value() / max_val) * 100) - if handle_percentage < 15: - base_handle_length = int( - (groove.height() * page_step / (max_val - min_val + page_step)) - + np.interp(handle_percentage, [0, 15], [0, 40]) - ) - handle_pos = 0 - - elif handle_percentage > 85: - base_handle_length = int( - (groove.height() * page_step / (max_val - min_val + page_step)) - + np.interp(handle_percentage, [85, 100], [40, 0]) - ) - handle_pos = int( - (groove.height() - base_handle_length) - * (max_val - min_val) - / (max_val - min_val) - ) - else: - val = np.interp((handle_percentage), [15, 85], [0, 100]) / 100 * max_val + val = np.interp((handle_percentage), [15, 85], [0, 100]) / 100 * max_val - base_handle_length = int( - (groove.height() * page_step / (max_val - min_val + page_step)) + 40 - ) - handle_pos = int( - (groove.height() - base_handle_length) - * (val - min_val) - / (max_val - min_val) - ) + base_handle_length = int( + (groove.height() * page_step / (max_val - min_val + page_step)) + 40 + ) + handle_pos = int( + (groove.height() - base_handle_length) + * (val - min_val) + / (max_val - min_val) + ) handle_rect = QtCore.QRect( groove.x(), diff --git a/BlocksScreen/lib/utils/blocks_linedit.py b/BlocksScreen/lib/utils/blocks_linedit.py index 242e4b0d..a4342fe3 100644 --- a/BlocksScreen/lib/utils/blocks_linedit.py +++ b/BlocksScreen/lib/utils/blocks_linedit.py @@ -1,78 +1,139 @@ import typing + from PyQt6 import QtCore, QtGui, QtWidgets class BlocksCustomLinEdit(QtWidgets.QLineEdit): clicked = QtCore.pyqtSignal() - def __init__( - self, - parent: QtWidgets.QWidget, - ) -> None: - super(BlocksCustomLinEdit, self).__init__(parent) - - self.button_background = None - self.button_ellipse = None - self._text: str = "" - self.placeholder_str = "Type here" - self._name: str = "" - self.text_color: QtGui.QColor = QtGui.QColor(0, 0, 0) - self.secret: bool = False + # Layout constants + TEXT_MARGIN = 10 + CORNER_RADIUS = 8 + + def __init__(self, parent: typing.Optional[QtWidgets.QWidget] = None) -> None: + super().__init__(parent) + + # State + self._placeholder_str = "Type here" + self._name = "" + self._secret = False # True = show bullets, False = show text + self._show_toggle = False + self._is_password_visible = False + + # Pre-allocated colors (avoid allocation in paint) + self._bg_color = QtGui.QColor(223, 223, 223) + self._bg_pressed_color = QtGui.QColor(200, 200, 200) + self._text_color = QtGui.QColor(0, 0, 0) + self._placeholder_color = QtGui.QColor(130, 130, 130) + + # Touch support self.setAttribute(QtCore.Qt.WidgetAttribute.WA_AcceptTouchEvents, True) + # Cursor + self.setCursor(QtCore.Qt.CursorShape.BlankCursor) + @property - def name(self): - """Widget name""" + def name(self) -> str: + """Widget name property.""" return self._name @name.setter - def name(self, new_name) -> None: - self._name = new_name - self.setObjectName(new_name) + def name(self, value: str) -> None: + self._name = value + self.setObjectName(value) + + def placeholderText(self) -> str: + """Get placeholder text.""" + return self._placeholder_str - def setText(self, text: str) -> None: - """Set widget text""" - super().setText(text) + def setPlaceholderText(self, text: str) -> None: + """Set placeholder text displayed when empty.""" + self._placeholder_str = text + self.update() + + def showToggleButton(self) -> bool: + """Check if toggle button is enabled.""" + return self._show_toggle def setHidden(self, hidden: bool) -> None: - """Hide widget text""" - self.secret = hidden + """ + Set whether text is hidden (password mode). + + Args: + hidden: True to show bullets, False to show actual text + """ + if self._secret == hidden: + return + + self._secret = hidden + self._is_password_visible = not hidden self.update() + def isPasswordVisible(self) -> bool: + """Check if password is currently visible.""" + return self._is_password_visible + + def _get_text_rect(self) -> QtCore.QRect: + """Calculate the rectangle available for text rendering.""" + left_margin = self.TEXT_MARGIN + right_margin = self.TEXT_MARGIN + + return self.rect().adjusted(left_margin, 0, -right_margin, 0) + def mousePressEvent(self, event: QtGui.QMouseEvent) -> None: - """Re-implemented method, handle mouse press events""" + """Handle mouse press""" self.clicked.emit() super().mousePressEvent(event) - def paintEvent(self, e: typing.Optional[QtGui.QPaintEvent]): - """Re-implemented method, paint widget""" + def mouseReleaseEvent(self, event: QtGui.QMouseEvent) -> None: + """Handle mouse release""" + super().mouseReleaseEvent(event) + + def focusInEvent(self, event: QtGui.QFocusEvent) -> None: + """Handle focus in - emit clicked for virtual keyboard.""" + self.clicked.emit() + super().focusInEvent(event) + + def paintEvent(self, event: typing.Optional[QtGui.QPaintEvent]) -> None: + """Custom paint with embedded toggle button.""" painter = QtGui.QPainter(self) - painter.setRenderHint(painter.RenderHint.Antialiasing, True) + painter.setRenderHint(QtGui.QPainter.RenderHint.Antialiasing, True) - # Draw background - bg_color = QtGui.QColor(223, 223, 223) - painter.setBrush(bg_color) + # Background + painter.setBrush(self._bg_color) painter.setPen(QtCore.Qt.PenStyle.NoPen) - painter.drawRoundedRect(self.rect(), 8, 8) + painter.drawRoundedRect(self.rect(), self.CORNER_RADIUS, self.CORNER_RADIUS) + + # Text + self._draw_text(painter) - margin = 5 + painter.end() + + def _draw_text(self, painter: QtGui.QPainter) -> None: + """Draw the text or placeholder.""" + text_rect = self._get_text_rect() display_text = self.text() - if self.secret and display_text: + + # Apply password masking + if self._secret and display_text: display_text = "*" * len(display_text) - if self.text(): - painter.setPen(self.text_color) + if display_text: + painter.setPen(self._text_color) + painter.setFont(self.font()) painter.drawText( - self.rect().adjusted(margin, 0, 0, 0), + text_rect, QtCore.Qt.AlignmentFlag.AlignLeft | QtCore.Qt.AlignmentFlag.AlignVCenter, display_text, ) else: - painter.setPen(QtGui.QColor(150, 150, 150)) + # Placeholder text + painter.setPen(self._placeholder_color) + painter.setFont(self.font()) painter.drawText( - self.rect().adjusted(margin, 0, 0, 0), + text_rect, QtCore.Qt.AlignmentFlag.AlignLeft | QtCore.Qt.AlignmentFlag.AlignVCenter, - self.placeholder_str, + self._placeholder_str, ) diff --git a/BlocksScreen/lib/utils/list_model.py b/BlocksScreen/lib/utils/list_model.py index 2f4cbc3a..a622f094 100644 --- a/BlocksScreen/lib/utils/list_model.py +++ b/BlocksScreen/lib/utils/list_model.py @@ -19,6 +19,7 @@ class ListItem: _rfontsize: int = 0 height: int = 60 # Change has needed notificate: bool = False # render red dot + not_clickable: bool = False class EntryListModel(QtCore.QAbstractListModel): @@ -59,6 +60,8 @@ def flags(self, index) -> QtCore.Qt.ItemFlag: """Models item flags, re-implemented method""" item = self.entries[index.row()] flags = QtCore.Qt.ItemFlag.ItemIsEnabled | QtCore.Qt.ItemFlag.ItemIsSelectable + if item.not_clickable: + return QtCore.Qt.ItemFlag.NoItemFlags if item.allow_check: flags |= QtCore.Qt.ItemFlag.ItemIsUserCheckable return flags @@ -152,6 +155,10 @@ def paint( path.addRoundedRect(QtCore.QRectF(rect), radius, radius) # Gradient background (left to right) + if item.not_clickable: + painter.restore() + return + if not item.selected: pressed_color = QtGui.QColor("#1A8FBF") pressed_color.setAlpha(20) @@ -327,6 +334,8 @@ def editorEvent( """Capture view model events, re-implemented method""" item = index.data(QtCore.Qt.ItemDataRole.UserRole) if event.type() == QtCore.QEvent.Type.MouseButtonPress: + if item and item.not_clickable: + return True if item.callback: if callable(item.callback): item.callback()