From ffcdc7d51689772b5ab67c978fd2fb1e32bed772 Mon Sep 17 00:00:00 2001 From: gmmcosta15 Date: Wed, 19 Nov 2025 16:49:55 +0000 Subject: [PATCH 01/19] refactor: change network list to listview --- BlocksScreen/lib/panels/networkWindow.py | 497 ++++++++++++----------- 1 file changed, 262 insertions(+), 235 deletions(-) diff --git a/BlocksScreen/lib/panels/networkWindow.py b/BlocksScreen/lib/panels/networkWindow.py index 57617efe..0e7a32d1 100644 --- a/BlocksScreen/lib/panels/networkWindow.py +++ b/BlocksScreen/lib/panels/networkWindow.py @@ -1,6 +1,7 @@ import logging import typing -import subprocess # nosec: B404 +import copy +import subprocess from functools import partial from lib.network import SdbusNetworkManagerAsync @@ -8,7 +9,14 @@ from lib.ui.wifiConnectivityWindow_ui import Ui_wifi_stacked_page from lib.utils.list_button import ListCustomButton from lib.panels.widgets.keyboardPage import CustomQwertyKeyboard +from lib.utils.blocks_button import BlocksCustomButton +from lib.utils.blocks_frame import BlocksCustomFrame +from lib.panels.widgets.loadPage import LoadScreen +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 QVariant +from PyQt6.QtWidgets import QScroller, QScrollerProperties logger = logging.getLogger("logs/BlocksScreen.log") @@ -138,23 +146,41 @@ class NetworkControlWindow(QtWidgets.QStackedWidget): 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 + if parent: + super().__init__(parent) + else: + super().__init__() + self.panel = Ui_wifi_stacked_page() self.panel.setupUi(self) + + self._setupUI() + #self.background: typing.Optional[QtGui.QPixmap] = None + self.ongoing_update: bool = False + self.popup = Popup(self) self.sdbus_network = SdbusNetworkManagerAsync() self.start: bool = True - self.saved_network = dict + self.saved_network = {} + + self.load_popup: LoadScreen = LoadScreen(self) + self.repeated_request_status = QtCore.QTimer() + self.repeated_request_status.setInterval(2000) # every 2 seconds self._load_timer = QtCore.QTimer() self._load_timer.setSingleShot(True) self._load_timer.timeout.connect(self._handle_load_timeout) + + #View Models and Controllers + self.model = EntryListModel() + self.model.setParent(self.network_list_widget) + self.entry_delegate = EntryDelegate() + self.network_list_widget.setModel(self.model) + self.network_list_widget.setItemDelegate(self.entry_delegate) + self.entry_delegate.item_selected.connect(self.ssid_item_clicked) + self.panel.network_backButton.clicked.connect(self.reset_view_model) # Network Scan - self.network_list_widget = QtWidgets.QListWidget( - parent=self.panel.network_list_page - ) self.build_network_list() self.network_list_worker = BuildNetworkList() @@ -167,10 +193,7 @@ def __init__(self, parent: typing.Optional[QtWidgets.QWidget], /) -> None: 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), - ) + partial(self.setCurrentIndex, self.indexOf(self.panel.network_list_page)) ) self.panel.hotspot_button.clicked.connect( partial(self.setCurrentIndex, self.indexOf(self.panel.hotspot_page)) @@ -322,6 +345,7 @@ def __init__(self, parent: typing.Optional[QtWidgets.QWidget], /) -> None: self.network_list_worker.build() self.request_network_scan.emit() self.hide() + self.info_box_load() self.qwerty = CustomQwertyKeyboard(self) @@ -351,6 +375,54 @@ def __init__(self, parent: typing.Optional[QtWidgets.QWidget], /) -> None: ) ) + def handle_update_end(self) -> None: + """Handles update end signal + (closes loading page, returns to normal operation) + """ + if self.load_popup.isVisible(): + self.load_popup.close() + self.repeated_request_status.stop() + self.request_network_scan.emit() + self.build_model_list() + + def handle_ongoing_update(self) -> None: + """Handled ongoing update signal, + calls loading page (blocks user interaction) + """ + self.load_popup.set_status_message("Updating...") + self.load_popup.show() + self.repeated_request_status.start(2000) + + # View Model Methods + def reset_view_model(self) -> None: + """Clears items from ListView + (Resets `QAbstractListModel` by clearing entries) + """ + self.model.clear() + self.entry_delegate.clear() + + def deleteLater(self) -> None: + """Schedule the object for deletion, resets the list model first""" + self.reset_view_model() + return super().deleteLater() + + def showEvent(self, event: QtGui.QShowEvent | None) -> None: + """Re-add clients to update list""" + self.build_model_list() + return super().showEvent(event) + + def build_model_list(self) -> None: + """Builds the model list (`self.model`) containing updatable clients""" + self.network_list_widget.blockSignals(True) + self.model.clear() + #logger.debug(f"len saved: {len(self.saved_network.items())}") + test:dict = copy.copy(self.saved_network) + if test.items(): + for ssid,(signal,is_saved) in test.items(): + self.add_network_entry(ssid=ssid, signal=signal, is_saved=is_saved) + + self.network_list_widget.blockSignals(False) + def saved_wifi_option_selected(self): """Handle connect/delete network button clicks""" _sender = self.sender() @@ -764,32 +836,39 @@ def add_network(self) -> None: 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) +<<<<<<< HEAD @QtCore.pyqtSlot(QtWidgets.QListWidgetItem, name="ssid_item_clicked") def ssid_item_clicked(self, item: QtWidgets.QListWidgetItem) -> None: +======= + + @QtCore.pyqtSlot(ListItem, name="ssid_item_clicked") + def ssid_item_clicked(self, item: ListItem) -> None: +>>>>>>> da41c34 (refactor: change network list to listview) """Handles when a network is clicked on the QListWidget. 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 + if not item: + return + + _current_ssid_name = item.text + #_current_ssid_name = self.saved_network.get(item.text, {}) + self.selected_item = copy.copy(item) + 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 + def update_network( self, @@ -810,29 +889,25 @@ def update_network( self.setCurrentIndex(self.indexOf(self.panel.network_list_page)) @QtCore.pyqtSlot(list, name="finished-network-list-build") +<<<<<<< HEAD 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() +======= + def handle_network_list(self, data: typing.Dict) -> None: +>>>>>>> da41c34 (refactor: change network list to listview) 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() + if entry[0] == self.sdbus_network.hotspot_ssid: continue - elif entry == "blank": - self.blank_space_item() + if entry == "blank": continue - if entry[0] == self.sdbus_network.hotspot_ssid: + if entry == "separator": continue - self.network_button_item(*entry) - - 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.saved_network[entry[0]] = (entry[1], entry[2] == "Saved" or entry[2] == "Active") + self.build_model_list() + self.network_list_widget.blockSignals(False) + self.evaluate_network_state() QtCore.QTimer().singleShot(10000, lambda: self.network_list_worker.build()) @@ -852,21 +927,6 @@ def handle_button_click(self, ssid: str): self.setCurrentIndex(self.indexOf(self.panel.add_network_page)) self.panel.add_network_network_label.setText(str(ssid)) - def event(self, event: QtCore.QEvent) -> bool: - """Receives PyQt eEvents, this method is reimplemented from the QEvent class - - Args: - event (QtCore.QEvent) - - 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 @@ -936,198 +996,165 @@ def show_network_panel( self.updateGeometry() self.update() self.show() - + + def add_network_entry(self, ssid: str, signal: int, is_saved:str) -> None: + """Adds a new item to the list model""" + + 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") + + item = ListItem( + text=ssid, + left_icon=wifi_pixmap, + right_text=f"Signal - {signal} % | {'Active' if is_saved else 'Protect'} ", + selected=False, + allow_check=False, + _lfontsize=17, + _rfontsize=13, + height=60, + ) + self.model.add_item(item) + + def _setupUI(self) -> None: + + """Sets up the UI components and layout for the network window.""" + + font_id = QtGui.QFontDatabase.addApplicationFont( + ":/font/media/fonts for text/Momcake-Bold.ttf" + ) + font_family = QtGui.QFontDatabase.applicationFontFamilies(font_id)[0] + sizePolicy = QtWidgets.QSizePolicy( + QtWidgets.QSizePolicy.Policy.MinimumExpanding, + QtWidgets.QSizePolicy.Policy.MinimumExpanding, + ) + sizePolicy.setHorizontalStretch(1) + #sizePolicy.setVerticalStretch(1) + self.setSizePolicy(sizePolicy) + self.setMinimumSize(QtCore.QSize(800, 500)) + #self.setMaximumSize(QtCore.QSize(800, 500)) + self.setLayoutDirection(QtCore.Qt.LayoutDirection.LeftToRight) + self.main_content_layout = QtWidgets.QHBoxLayout() + self.main_content_layout.setAlignment(QtCore.Qt.AlignmentFlag.AlignTop) + + font = QtGui.QFont() + font.setFamily(font_family) + font.setPointSize(24) + + self.header_title = QtWidgets.QLabel(self) + self.header_title.setMinimumSize(QtCore.QSize(100, 60)) + self.header_title.setMaximumSize(QtCore.QSize(16777215, 60)) + self.header_title.setFont(font) + + # Timer for loading screen timeout + self._load_timer = QtCore.QTimer(self) + + #Buttons frame for update buttons + self.network_buttons_frame = BlocksCustomFrame() + #self.network_buttons_frame.setMinimumSize(QtCore.QSize(100, 100)) + #self.network_buttons_frame.setMaximumSize(QtCore.QSize(800, 450)) + + #List widget for update buttons + self.network_list_widget = QtWidgets.QListView(self.network_buttons_frame) + + self.network_buttons_layout = QtWidgets.QVBoxLayout() + self.network_buttons_layout.setContentsMargins(15, 20, 20, 5) + self.network_buttons_layout.addWidget(self.network_list_widget, 0, QtCore.Qt.AlignmentFlag.AlignBottom) + self.network_buttons_frame.setLayout(self.network_buttons_layout) + + self.main_content_layout.addWidget(self.network_buttons_frame, 0) + self.setLayout(self.main_content_layout) + def build_network_list(self) -> None: - """Build available/saved network list""" + """Build available/saved network list with optimized palette setup.""" + def set_brush_for_all_groups(palette, role, color, style=QtCore.Qt.BrushStyle.SolidPattern): + """Helper to set a brush for Active, Inactive, and Disabled states.""" + brush = QtGui.QBrush(QtGui.QColor(*color)) + brush.setStyle(style) + for group in [ + QtGui.QPalette.ColorGroup.Active, + QtGui.QPalette.ColorGroup.Inactive, + QtGui.QPalette.ColorGroup.Disabled, + ]: + palette.setBrush(group, role, brush) + 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, - ) + + # Transparent backgrounds + set_brush_for_all_groups(palette, QtGui.QPalette.ColorRole.Button, (0, 0, 0, 0)) + set_brush_for_all_groups(palette, QtGui.QPalette.ColorRole.Window, (0, 0, 0, 0)) + + # Base (black, no brush) + set_brush_for_all_groups(palette, QtGui.QPalette.ColorRole.Base, (0, 0, 0), QtCore.Qt.BrushStyle.NoBrush) + + # Highlight & link + set_brush_for_all_groups(palette, QtGui.QPalette.ColorRole.Highlight, (0, 120, 215, 0)) + set_brush_for_all_groups(palette, QtGui.QPalette.ColorRole.Link, (0, 0, 255, 0)) + + # Apply palette self.network_list_widget.setPalette(palette) + + # General QListView setup 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.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarPolicy.ScrollBarAsNeeded) + self.network_list_widget.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarPolicy.ScrollBarAlwaysOff) + self.network_list_widget.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.SizeAdjustPolicy.AdjustIgnored) 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.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) + self.network_list_widget.setUniformItemSizes(True) + + #self.network_list_widget.setStyleSheet("QListView { padding-bottom: 8px; }") + + # inside build_network_list + + # ... (your palette / list-view setup) + + # Grab gesture on the viewport + viewport = self.network_list_widget.viewport() + QScroller.grabGesture(viewport, QScroller.ScrollerGestureType.TouchGesture) + QScroller.grabGesture(viewport, QScroller.ScrollerGestureType.LeftMouseButtonGesture) + + scroller = QScroller.scroller(viewport) + props = scroller.scrollerProperties() + + props.setScrollMetric( + QScrollerProperties.ScrollMetric.VerticalOvershootPolicy, + QVariant(QScrollerProperties.OvershootPolicy.OvershootAlwaysOff) ) - self.network_list_widget.setVerticalScrollMode( - QtWidgets.QAbstractItemView.ScrollMode.ScrollPerPixel + props.setScrollMetric( + QScrollerProperties.ScrollMetric.OvershootDragResistanceFactor, + QVariant(1.0) ) - self.network_list_widget.setHorizontalScrollMode( - QtWidgets.QAbstractItemView.ScrollMode.ScrollPerPixel + props.setScrollMetric( + QScrollerProperties.ScrollMetric.OvershootDragDistanceFactor, + QVariant(0.0) ) - QtWidgets.QScroller.grabGesture( - self.network_list_widget, - QtWidgets.QScroller.ScrollerGestureType.TouchGesture, + props.setScrollMetric( + QScrollerProperties.ScrollMetric.OvershootScrollDistanceFactor, + QVariant(0.0) ) - QtWidgets.QScroller.grabGesture( - self.network_list_widget, - QtWidgets.QScroller.ScrollerGestureType.LeftMouseButtonGesture, + props.setScrollMetric( + QScrollerProperties.ScrollMetric.OvershootScrollTime, + QVariant(0.0) ) - self.network_list_widget.setObjectName("network_list_widget") - self.panel.nl_content_layout.addWidget(self.network_list_widget) + scroller.setScrollerProperties(props) + + # ... add widget to layout, etc. - 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) + self.network_list_widget.setObjectName("network_list_widget") + self.panel.nl_content_layout.addWidget(self.network_list_widget) From afe33b2b872289c1610dbe1917650bcb0bb4f101 Mon Sep 17 00:00:00 2001 From: gmmcosta15 Date: Thu, 20 Nov 2025 16:19:05 +0000 Subject: [PATCH 02/19] Refactor: Refac to MVC view with Controller being runnables on a threadpoll --- BlocksScreen/lib/panels/networkWindow.py | 336 ++++++++++-------- .../lib/ui/resources/icon_resources.qrc | 13 +- .../resources/media/btn_icons/0bar_wifi.svg | 1 + .../media/btn_icons/0bar_wifi_protected.svg | 1 + .../resources/media/btn_icons/1bar_wifi.svg | 2 +- .../media/btn_icons/1bar_wifi_protected.svg | 1 + .../resources/media/btn_icons/2bar_wifi.svg | 2 +- .../media/btn_icons/2bar_wifi_protected.svg | 1 + .../resources/media/btn_icons/3bar_wifi.svg | 2 +- .../media/btn_icons/3bar_wifi_protected.svg | 1 + .../resources/media/btn_icons/4bar_wifi.svg | 1 + .../media/btn_icons/4bar_wifi_protected.svg | 1 + 12 files changed, 215 insertions(+), 147 deletions(-) create mode 100644 BlocksScreen/lib/ui/resources/media/btn_icons/0bar_wifi.svg create mode 100644 BlocksScreen/lib/ui/resources/media/btn_icons/0bar_wifi_protected.svg create mode 100644 BlocksScreen/lib/ui/resources/media/btn_icons/1bar_wifi_protected.svg create mode 100644 BlocksScreen/lib/ui/resources/media/btn_icons/2bar_wifi_protected.svg create mode 100644 BlocksScreen/lib/ui/resources/media/btn_icons/3bar_wifi_protected.svg create mode 100644 BlocksScreen/lib/ui/resources/media/btn_icons/4bar_wifi.svg create mode 100644 BlocksScreen/lib/ui/resources/media/btn_icons/4bar_wifi_protected.svg diff --git a/BlocksScreen/lib/panels/networkWindow.py b/BlocksScreen/lib/panels/networkWindow.py index 0e7a32d1..d6b70c13 100644 --- a/BlocksScreen/lib/panels/networkWindow.py +++ b/BlocksScreen/lib/panels/networkWindow.py @@ -4,139 +4,208 @@ import subprocess from functools import partial +from PyQt6 import QtCore, QtGui, QtWidgets +from PyQt6.QtCore import QRunnable, QThreadPool, QObject, pyqtSignal, QVariant +from PyQt6.QtWidgets import QScroller, QScrollerProperties + from lib.network import SdbusNetworkManagerAsync 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.panels.widgets.keyboardPage import CustomQwertyKeyboard -from lib.utils.blocks_button import BlocksCustomButton from lib.utils.blocks_frame import BlocksCustomFrame from lib.panels.widgets.loadPage import LoadScreen -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 QVariant -from PyQt6.QtWidgets import QScroller, QScrollerProperties + logger = logging.getLogger("logs/BlocksScreen.log") +<<<<<<< HEAD class BuildNetworkList(QtCore.QThread): """Retrieves information from sdbus interface about scanned networks""" - - 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" - ) - - def __init__(self) -> None: +======= +class NetworkScanRunnable(QRunnable): + """QRunnable task that performs network scanning using SdbusNetworkManagerAsync +>>>>>>> 9285fb7 (Refactor: Refac to MVC view with Controller being runnables on a threadpoll) + + This runnable: + - Triggers a network rescan via SdbusNetworkManagerAsync + - collects SSIDs, signal strenght and saved status + - emits signal with raw scan data and a processed lisgs + + Signals: + - scan_results (dict): Emitted with raw scan results mapping SSID to properties + - finished_network_list_build (list): Emitted with processed list of networks + - error (str): Emitted if an error occurs during scanning + + """ + + class Signals(QObject): + scan_results = pyqtSignal(dict, name="scan-results") + finished_network_list_build = pyqtSignal(list, name="finished-network-list-build") + error = pyqtSignal(str) + + def __init__(self): 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.signals = NetworkScanRunnable.Signals() - 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() - - def stop(self): - """Stops QThread execution""" - self.mutex.lock() - self.condition.wakeOne() - self.mutex.unlock() - self.deleteLater() - - 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() + def run(self): + try: + logger.debug("NetworkScanRunnable: scanning networks") 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() + saved = self.nm.get_saved_ssid_names() + available = self.nm.get_available_networks() if self.nm.check_wifi_interface() else {} + + data_dict: dict[str, dict] = {} + for ssid, props in available.items(): + signal = int(props.get("signal_level", 0)) + data_dict[ssid] = { + "signal_level": signal, + "is_saved": ssid in saved, + } + + # Emit scan_result (same name) + self.signals.scan_results.emit(data_dict) + + # Transform into your “list of tuples + blank / separator” format + items: list[typing.Union[tuple[str,int,str], str]] = [] + saved_nets = [ (ssid, info["signal_level"]) for ssid, info in data_dict.items() if info["is_saved"] ] + unsaved_nets = [ (ssid, info["signal_level"]) 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]) + + # Build your list with statuses + for ssid, sig in saved_nets: + status = "Active" if ssid == self.nm.get_current_ssid() else "Saved" + items.append((ssid, sig, status)) + + for ssid, sig in unsaved_nets: + items.append((ssid, sig, "Protected")) + + 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)) + +class BuildNetworkList(QtCore.QObject): + """ + Controller class that schedules and manages repeted network scans + + Uses a QThreadPool to un NetworkScanRunnable tasks periodically. with a QTimer to trigger scans. + Prevents overlapping scans by tracking whether a scan is already in progress. + + Args: + poll_interval_ms: (int) Milliseconds between scans (default: 10000) + _timer (QtCore.QTimer): Timer that schedules next scan + _is_scanning (bool): Flag indicating if a scan is currently in progress + + Signals: + scan_results (dict): Emitted with raw scan results mapping SSID to properties + finished_network_list_build (list): Emitted with processed list of networks + error (str): Emitted if an error occurs during scanning + """ + scan_results = pyqtSignal(dict, name="scan-results") + finished_network_list_build = pyqtSignal(list, name="finished-network-list-build") + error = pyqtSignal(str) + + def __init__(self, poll_interval_ms: int = 10000): + super().__init__() + self.threadpool = QThreadPool.globalInstance() + self.poll_interval_ms = poll_interval_ms + self._is_scanning = False + + self._timer = QtCore.QTimer(self) + self._timer.setSingleShot(True) + self._timer.timeout.connect(self._do_scan) + + def start_polling(self): + self._schedule_next_scan() + + def stop_polling(self): + self._timer.stop() + + def build(self): + self._do_scan() + def _schedule_next_scan(self): + self._timer.start(self.poll_interval_ms) + def _on_task_finished(self, items): + self._is_scanning = False + self.finished_network_list_build.emit(items) + self._schedule_next_scan() + + def _on_task_scan_results(self, data_dict): + self.scan_results.emit(data_dict) + + def _on_task_error(self, err): + self._is_scanning = False + self.error.emit(err) + self._schedule_next_scan() + + def _do_scan(self): + if self._is_scanning: + logger.debug("Already scanning, skip scheduling.") + self._schedule_next_scan() + return + + self._is_scanning = True + task = NetworkScanRunnable() + 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) + logger.debug("Submitted scan task to thread pool") + +class WifiIconProvider: + """Simple provider: loads QPixmap for WiFi bars + protection without caching.""" + + def __init__(self): + # Map from (bars, is_protected) to resource path + self.paths = { + ("no", False): ":/network/media/btn_icons/0bar_wifi.svg", + (4, False): ":/network/media/btn_icons/4bar_wifi.svg", + (3, False): ":/network/media/btn_icons/3bar_wifi.svg", + (2, False): ":/network/media/btn_icons/2bar_wifi.svg", + (1, False): ":/network/media/btn_icons/1bar_wifi.svg", + + ("no", True): ":/network/media/btn_icons/0bar_wifi_protected.svg", + (4, True): ":/network/media/btn_icons/4bar_wifi_protected.svg", + (3, True): ":/network/media/btn_icons/3bar_wifi_protected.svg", + (2, True): ":/network/media/btn_icons/2bar_wifi_protected.svg", + (1, True): ":/network/media/btn_icons/1bar_wifi_protected.svg", + } + + + def get_pixmap(self, signal: int, state: str) -> QtGui.QPixmap: + """Return a QPixmap for the given signal (0-100) and state ("Protected" or not).""" + # Normalize signal + if signal <= 0: + bars = "no" + elif signal >= 75: + bars = 4 + elif signal >= 50: + bars = 3 + elif signal >= 25: + bars = 2 + else: + bars = 1 + + is_protected = (state == "Protected") + key = (bars, is_protected) + + path = self.paths.get(key) + if path is None: + logger.warning(f"No icon path for key {key}, falling back to no-signal unprotected") + path = self.paths[("no", False)] + + pm = QtGui.QPixmap(path) + if pm.isNull(): + logger.error(f"Failed to load pixmap from '{path}' for key {key}") + return pm class NetworkControlWindow(QtWidgets.QStackedWidget): """Network Control panel Widget""" @@ -155,7 +224,8 @@ def __init__(self, parent: typing.Optional[QtWidgets.QWidget], /) -> None: self.panel.setupUi(self) self._setupUI() - #self.background: typing.Optional[QtGui.QPixmap] = None + + self._provider = WifiIconProvider() self.ongoing_update: bool = False self.popup = Popup(self) @@ -171,7 +241,7 @@ def __init__(self, parent: typing.Optional[QtWidgets.QWidget], /) -> None: self._load_timer.setSingleShot(True) self._load_timer.timeout.connect(self._handle_load_timeout) - #View Models and Controllers + #View Models and Delegates self.model = EntryListModel() self.model.setParent(self.network_list_widget) self.entry_delegate = EntryDelegate() @@ -187,9 +257,8 @@ def __init__(self, parent: typing.Optional[QtWidgets.QWidget], /) -> None: self.network_list_worker.finished_network_list_build.connect( self.handle_network_list ) - self.panel.rescan_button.clicked.connect( - lambda: QtCore.QTimer.singleShot(100, self.network_list_worker.build) - ) + self.network_list_worker.start_polling() + self.panel.rescan_button.clicked.connect(self.network_list_worker.build) self.sdbus_network.nm_state_change.connect(self.evaluate_network_state) self.panel.wifi_button.clicked.connect( @@ -415,7 +484,7 @@ def build_model_list(self) -> None: """Builds the model list (`self.model`) containing updatable clients""" self.network_list_widget.blockSignals(True) self.model.clear() - #logger.debug(f"len saved: {len(self.saved_network.items())}") + test:dict = copy.copy(self.saved_network) if test.items(): for ssid,(signal,is_saved) in test.items(): @@ -904,7 +973,7 @@ def handle_network_list(self, data: typing.Dict) -> None: continue if entry == "separator": continue - self.saved_network[entry[0]] = (entry[1], entry[2] == "Saved" or entry[2] == "Active") + self.saved_network[entry[0]] = (entry[1], entry[2]) self.build_model_list() self.network_list_widget.blockSignals(False) @@ -1000,23 +1069,17 @@ def show_network_panel( def add_network_entry(self, ssid: str, signal: int, is_saved:str) -> None: """Adds a new item to the list model""" - 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") + wifi_pixmap = self._provider.get_pixmap(signal=signal, state=is_saved) item = ListItem( text=ssid, left_icon=wifi_pixmap, - right_text=f"Signal - {signal} % | {'Active' if is_saved else 'Protect'} ", + right_text=is_saved, selected=False, allow_check=False, _lfontsize=17, _rfontsize=13, - height=60, + height=70, ) self.model.add_item(item) @@ -1115,14 +1178,8 @@ def set_brush_for_all_groups(palette, role, color, style=QtCore.Qt.BrushStyle.So self.network_list_widget.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollMode.ScrollPerPixel) self.network_list_widget.setHorizontalScrollMode(QtWidgets.QAbstractItemView.ScrollMode.ScrollPerPixel) self.network_list_widget.setUniformItemSizes(True) - - #self.network_list_widget.setStyleSheet("QListView { padding-bottom: 8px; }") - - # inside build_network_list - - # ... (your palette / list-view setup) - - # Grab gesture on the viewport + self.network_list_widget.setSpacing(3) + viewport = self.network_list_widget.viewport() QScroller.grabGesture(viewport, QScroller.ScrollerGestureType.TouchGesture) QScroller.grabGesture(viewport, QScroller.ScrollerGestureType.LeftMouseButtonGesture) @@ -1153,8 +1210,5 @@ def set_brush_for_all_groups(palette, role, color, style=QtCore.Qt.BrushStyle.So scroller.setScrollerProperties(props) - # ... add widget to layout, etc. - - self.network_list_widget.setObjectName("network_list_widget") self.panel.nl_content_layout.addWidget(self.network_list_widget) diff --git a/BlocksScreen/lib/ui/resources/icon_resources.qrc b/BlocksScreen/lib/ui/resources/icon_resources.qrc index 3022fd4d..7c9abcdd 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/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 From c1ee388876ae13b368adae597fdd98c3c3f1f3f7 Mon Sep 17 00:00:00 2001 From: Roberto Martins Date: Wed, 26 Nov 2025 12:09:41 +0000 Subject: [PATCH 03/19] UPD: Regenerated icon_resources_rc --- .../lib/ui/resources/icon_resources_rc.py | 2085 +++++++++++++---- 1 file changed, 1632 insertions(+), 453 deletions(-) diff --git a/BlocksScreen/lib/ui/resources/icon_resources_rc.py b/BlocksScreen/lib/ui/resources/icon_resources_rc.py index 14285978..4d2e5ab4 100644 --- a/BlocksScreen/lib/ui/resources/icon_resources_rc.py +++ b/BlocksScreen/lib/ui/resources/icon_resources_rc.py @@ -19093,7 +19093,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\ @@ -19102,42 +19102,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\ @@ -19874,7 +20022,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\ @@ -19886,86 +20034,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\ @@ -20125,7 +20363,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\ @@ -20134,61 +20372,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\ @@ -20291,6 +20726,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\ @@ -25625,6 +26750,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\ @@ -25642,6 +26771,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\ @@ -25654,10 +26787,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\ @@ -25876,9 +27034,9 @@ qt_resource_struct_v1 = b"\ \x00\x00\x00\x00\x00\x02\x00\x00\x00\x0f\x00\x00\x00\x01\ -\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x94\ -\x00\x00\x00\x0a\x00\x02\x00\x00\x00\x01\x00\x00\x00\x91\ -\x00\x00\x00\x1a\x00\x02\x00\x00\x00\x01\x00\x00\x00\x85\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x9b\ +\x00\x00\x00\x0a\x00\x02\x00\x00\x00\x01\x00\x00\x00\x98\ +\x00\x00\x00\x1a\x00\x02\x00\x00\x00\x01\x00\x00\x00\x8c\ \x00\x00\x00\x46\x00\x02\x00\x00\x00\x01\x00\x00\x00\x7a\ \x00\x00\x00\x5a\x00\x02\x00\x00\x00\x01\x00\x00\x00\x76\ \x00\x00\x00\x6c\x00\x02\x00\x00\x00\x01\x00\x00\x00\x70\ @@ -25998,83 +27156,90 @@ \x00\x00\x0e\x70\x00\x00\x00\x00\x00\x01\x00\x04\x8c\xaa\ \x00\x00\x0e\x9c\x00\x00\x00\x00\x00\x01\x00\x04\x93\x91\ \x00\x00\x01\x88\x00\x02\x00\x00\x00\x01\x00\x00\x00\x7b\ -\x00\x00\x01\x98\x00\x02\x00\x00\x00\x09\x00\x00\x00\x7c\ +\x00\x00\x01\x98\x00\x02\x00\x00\x00\x10\x00\x00\x00\x7c\ \x00\x00\x0e\xc0\x00\x00\x00\x00\x00\x01\x00\x04\x9c\xf5\ -\x00\x00\x0e\xe0\x00\x01\x00\x00\x00\x01\x00\x04\x9f\xa1\ -\x00\x00\x0f\x04\x00\x00\x00\x00\x00\x01\x00\x04\xaa\xf2\ -\x00\x00\x0f\x26\x00\x00\x00\x00\x00\x01\x00\x04\xb2\x97\ -\x00\x00\x0f\x42\x00\x00\x00\x00\x00\x01\x00\x04\xc3\x97\ -\x00\x00\x0f\x66\x00\x00\x00\x00\x00\x01\x00\x04\xcd\x18\ -\x00\x00\x0f\x86\x00\x00\x00\x00\x00\x01\x00\x04\xd2\xb5\ -\x00\x00\x0f\xae\x00\x00\x00\x00\x00\x01\x00\x04\xdc\x7e\ -\x00\x00\x0f\xce\x00\x00\x00\x00\x00\x01\x00\x04\xe0\x61\ -\x00\x00\x01\x88\x00\x02\x00\x00\x00\x01\x00\x00\x00\x86\ -\x00\x00\x01\x98\x00\x02\x00\x00\x00\x0a\x00\x00\x00\x87\ -\x00\x00\x0f\xea\x00\x00\x00\x00\x00\x01\x00\x04\xe6\x9c\ -\x00\x00\x10\x1a\x00\x00\x00\x00\x00\x01\x00\x04\xf0\x32\ -\x00\x00\x10\x4a\x00\x00\x00\x00\x00\x01\x00\x04\xfc\x0e\ -\x00\x00\x10\x6e\x00\x00\x00\x00\x00\x01\x00\x05\x02\x52\ -\x00\x00\x10\x98\x00\x00\x00\x00\x00\x01\x00\x05\x09\xdb\ -\x00\x00\x10\xc4\x00\x00\x00\x00\x00\x01\x00\x05\x10\x39\ -\x00\x00\x10\xfa\x00\x00\x00\x00\x00\x01\x00\x05\x18\x28\ -\x00\x00\x08\xaa\x00\x00\x00\x00\x00\x01\x00\x05\x25\xcb\ -\x00\x00\x11\x18\x00\x00\x00\x00\x00\x01\x00\x05\x2b\x00\ -\x00\x00\x11\x32\x00\x00\x00\x00\x00\x01\x00\x05\x34\xd5\ -\x00\x00\x01\x88\x00\x02\x00\x00\x00\x01\x00\x00\x00\x92\ -\x00\x00\x01\x98\x00\x02\x00\x00\x00\x01\x00\x00\x00\x93\ -\x00\x00\x11\x5a\x00\x00\x00\x00\x00\x01\x00\x05\x3c\x9f\ -\x00\x00\x01\x88\x00\x02\x00\x00\x00\x01\x00\x00\x00\x95\ -\x00\x00\x01\x98\x00\x02\x00\x00\x00\x28\x00\x00\x00\x96\ -\x00\x00\x11\x7a\x00\x00\x00\x00\x00\x01\x00\x05\x41\x65\ -\x00\x00\x11\x90\x00\x00\x00\x00\x00\x01\x00\x05\x49\x19\ -\x00\x00\x11\xa8\x00\x00\x00\x00\x00\x01\x00\x05\x4b\x3e\ -\x00\x00\x11\xe0\x00\x00\x00\x00\x00\x01\x00\x05\x4c\xbe\ -\x00\x00\x11\xfc\x00\x00\x00\x00\x00\x01\x00\x05\x54\x6b\ -\x00\x00\x12\x1c\x00\x00\x00\x00\x00\x01\x00\x05\x59\x40\ -\x00\x00\x12\x32\x00\x00\x00\x00\x00\x01\x00\x05\x5a\x30\ -\x00\x00\x12\x48\x00\x00\x00\x00\x00\x01\x00\x05\x5d\x14\ -\x00\x00\x12\x6e\x00\x00\x00\x00\x00\x01\x00\x05\x63\x4b\ -\x00\x00\x12\x88\x00\x00\x00\x00\x00\x01\x00\x05\x78\x58\ -\x00\x00\x12\xaa\x00\x00\x00\x00\x00\x01\x00\x05\x7d\x48\ -\x00\x00\x12\xc0\x00\x00\x00\x00\x00\x01\x00\x05\x80\x47\ -\x00\x00\x12\xd6\x00\x00\x00\x00\x00\x01\x00\x05\x86\x51\ -\x00\x00\x13\x08\x00\x00\x00\x00\x00\x01\x00\x05\x89\xa0\ -\x00\x00\x13\x20\x00\x00\x00\x00\x00\x01\x00\x05\x93\x21\ -\x00\x00\x13\x36\x00\x00\x00\x00\x00\x01\x00\x05\x99\x18\ -\x00\x00\x13\x4a\x00\x00\x00\x00\x00\x01\x00\x05\x9b\x29\ -\x00\x00\x13\x62\x00\x00\x00\x00\x00\x01\x00\x05\x9e\xec\ -\x00\x00\x13\x88\x00\x00\x00\x00\x00\x01\x00\x05\xa8\xa0\ -\x00\x00\x13\xb2\x00\x00\x00\x00\x00\x01\x00\x05\xab\xee\ -\x00\x00\x13\xd4\x00\x00\x00\x00\x00\x01\x00\x05\xaf\x7c\ -\x00\x00\x13\xfa\x00\x00\x00\x00\x00\x01\x00\x05\xb4\x1d\ -\x00\x00\x14\x0e\x00\x00\x00\x00\x00\x01\x00\x05\xbd\xef\ -\x00\x00\x14\x3a\x00\x00\x00\x00\x00\x01\x00\x05\xc3\x39\ -\x00\x00\x14\x62\x00\x00\x00\x00\x00\x01\x00\x05\xc9\x40\ -\x00\x00\x14\x78\x00\x00\x00\x00\x00\x01\x00\x05\xca\x24\ -\x00\x00\x14\xa4\x00\x00\x00\x00\x00\x01\x00\x05\xcc\x6d\ -\x00\x00\x14\xba\x00\x00\x00\x00\x00\x01\x00\x05\xd3\x1d\ -\x00\x00\x14\xd6\x00\x00\x00\x00\x00\x01\x00\x05\xd6\x61\ -\x00\x00\x14\xee\x00\x00\x00\x00\x00\x01\x00\x05\xd7\x8d\ -\x00\x00\x15\x08\x00\x00\x00\x00\x00\x01\x00\x05\xdd\x4e\ -\x00\x00\x15\x2a\x00\x00\x00\x00\x00\x01\x00\x05\xde\x70\ -\x00\x00\x15\x48\x00\x00\x00\x00\x00\x01\x00\x05\xe4\x63\ -\x00\x00\x15\x68\x00\x00\x00\x00\x00\x01\x00\x05\xe7\x67\ -\x00\x00\x15\x8a\x00\x00\x00\x00\x00\x01\x00\x05\xe8\x88\ -\x00\x00\x15\xaa\x00\x00\x00\x00\x00\x01\x00\x05\xeb\x5c\ -\x00\x00\x15\xd8\x00\x00\x00\x00\x00\x01\x00\x05\xf3\xc8\ -\x00\x00\x15\xfc\x00\x00\x00\x00\x00\x01\x00\x05\xfb\x88\ -\x00\x00\x16\x20\x00\x00\x00\x00\x00\x01\x00\x06\x00\xa3\ -\x00\x00\x16\x48\x00\x00\x00\x00\x00\x01\x00\x06\x01\xf6\ +\x00\x00\x0e\xe0\x00\x00\x00\x00\x00\x01\x00\x04\xa2\x8e\ +\x00\x00\x0f\x00\x00\x01\x00\x00\x00\x01\x00\x04\xa8\xb5\ +\x00\x00\x0f\x24\x00\x00\x00\x00\x00\x01\x00\x04\xb4\x06\ +\x00\x00\x0f\x46\x00\x00\x00\x00\x00\x01\x00\x04\xbb\xab\ +\x00\x00\x0f\x62\x00\x00\x00\x00\x00\x01\x00\x04\xcc\xab\ +\x00\x00\x0f\x86\x00\x00\x00\x00\x00\x01\x00\x04\xd6\x2c\ +\x00\x00\x0f\xa6\x00\x00\x00\x00\x00\x01\x00\x04\xdb\xb0\ +\x00\x00\x0f\xc6\x00\x00\x00\x00\x00\x01\x00\x04\xe1\x49\ +\x00\x00\x0f\xee\x00\x00\x00\x00\x00\x01\x00\x04\xeb\x12\ +\x00\x00\x10\x0e\x00\x00\x00\x00\x00\x01\x00\x04\xf0\xab\ +\x00\x00\x10\x42\x00\x00\x00\x00\x00\x01\x00\x04\xfb\x1f\ +\x00\x00\x10\x5e\x00\x00\x00\x00\x00\x01\x00\x05\x01\x5a\ +\x00\x00\x10\x92\x00\x00\x00\x00\x00\x01\x00\x05\x0b\xd4\ +\x00\x00\x10\xc6\x00\x00\x00\x00\x00\x01\x00\x05\x16\x33\ +\x00\x00\x10\xfa\x00\x00\x00\x00\x00\x01\x00\x05\x21\x7e\ +\x00\x00\x01\x88\x00\x02\x00\x00\x00\x01\x00\x00\x00\x8d\ +\x00\x00\x01\x98\x00\x02\x00\x00\x00\x0a\x00\x00\x00\x8e\ +\x00\x00\x11\x2e\x00\x00\x00\x00\x00\x01\x00\x05\x2b\xf2\ +\x00\x00\x11\x5e\x00\x00\x00\x00\x00\x01\x00\x05\x35\x88\ +\x00\x00\x11\x8e\x00\x00\x00\x00\x00\x01\x00\x05\x41\x64\ +\x00\x00\x11\xb2\x00\x00\x00\x00\x00\x01\x00\x05\x47\xa8\ +\x00\x00\x11\xdc\x00\x00\x00\x00\x00\x01\x00\x05\x4f\x31\ +\x00\x00\x12\x08\x00\x00\x00\x00\x00\x01\x00\x05\x55\x8f\ +\x00\x00\x12\x3e\x00\x00\x00\x00\x00\x01\x00\x05\x5d\x7e\ +\x00\x00\x08\xaa\x00\x00\x00\x00\x00\x01\x00\x05\x6b\x21\ +\x00\x00\x12\x5c\x00\x00\x00\x00\x00\x01\x00\x05\x70\x56\ +\x00\x00\x12\x76\x00\x00\x00\x00\x00\x01\x00\x05\x7a\x2b\ +\x00\x00\x01\x88\x00\x02\x00\x00\x00\x01\x00\x00\x00\x99\ +\x00\x00\x01\x98\x00\x02\x00\x00\x00\x01\x00\x00\x00\x9a\ +\x00\x00\x12\x9e\x00\x00\x00\x00\x00\x01\x00\x05\x81\xf5\ +\x00\x00\x01\x88\x00\x02\x00\x00\x00\x01\x00\x00\x00\x9c\ +\x00\x00\x01\x98\x00\x02\x00\x00\x00\x28\x00\x00\x00\x9d\ +\x00\x00\x12\xbe\x00\x00\x00\x00\x00\x01\x00\x05\x86\xbb\ +\x00\x00\x12\xd4\x00\x00\x00\x00\x00\x01\x00\x05\x8e\x6f\ +\x00\x00\x12\xec\x00\x00\x00\x00\x00\x01\x00\x05\x90\x94\ +\x00\x00\x13\x24\x00\x00\x00\x00\x00\x01\x00\x05\x92\x14\ +\x00\x00\x13\x40\x00\x00\x00\x00\x00\x01\x00\x05\x99\xc1\ +\x00\x00\x13\x60\x00\x00\x00\x00\x00\x01\x00\x05\x9e\x96\ +\x00\x00\x13\x76\x00\x00\x00\x00\x00\x01\x00\x05\x9f\x86\ +\x00\x00\x13\x8c\x00\x00\x00\x00\x00\x01\x00\x05\xa2\x6a\ +\x00\x00\x13\xb2\x00\x00\x00\x00\x00\x01\x00\x05\xa8\xa1\ +\x00\x00\x13\xcc\x00\x00\x00\x00\x00\x01\x00\x05\xbd\xae\ +\x00\x00\x13\xee\x00\x00\x00\x00\x00\x01\x00\x05\xc2\x9e\ +\x00\x00\x14\x04\x00\x00\x00\x00\x00\x01\x00\x05\xc5\x9d\ +\x00\x00\x14\x1a\x00\x00\x00\x00\x00\x01\x00\x05\xcb\xa7\ +\x00\x00\x14\x4c\x00\x00\x00\x00\x00\x01\x00\x05\xce\xf6\ +\x00\x00\x14\x64\x00\x00\x00\x00\x00\x01\x00\x05\xd8\x77\ +\x00\x00\x14\x7a\x00\x00\x00\x00\x00\x01\x00\x05\xde\x6e\ +\x00\x00\x14\x8e\x00\x00\x00\x00\x00\x01\x00\x05\xe0\x7f\ +\x00\x00\x14\xa6\x00\x00\x00\x00\x00\x01\x00\x05\xe4\x42\ +\x00\x00\x14\xcc\x00\x00\x00\x00\x00\x01\x00\x05\xed\xf6\ +\x00\x00\x14\xf6\x00\x00\x00\x00\x00\x01\x00\x05\xf1\x44\ +\x00\x00\x15\x18\x00\x00\x00\x00\x00\x01\x00\x05\xf4\xd2\ +\x00\x00\x15\x3e\x00\x00\x00\x00\x00\x01\x00\x05\xf9\x73\ +\x00\x00\x15\x52\x00\x00\x00\x00\x00\x01\x00\x06\x03\x45\ +\x00\x00\x15\x7e\x00\x00\x00\x00\x00\x01\x00\x06\x08\x8f\ +\x00\x00\x15\xa6\x00\x00\x00\x00\x00\x01\x00\x06\x0e\x96\ +\x00\x00\x15\xbc\x00\x00\x00\x00\x00\x01\x00\x06\x0f\x7a\ +\x00\x00\x15\xe8\x00\x00\x00\x00\x00\x01\x00\x06\x11\xc3\ +\x00\x00\x15\xfe\x00\x00\x00\x00\x00\x01\x00\x06\x18\x73\ +\x00\x00\x16\x1a\x00\x00\x00\x00\x00\x01\x00\x06\x1b\xb7\ +\x00\x00\x16\x32\x00\x00\x00\x00\x00\x01\x00\x06\x1c\xe3\ +\x00\x00\x16\x4c\x00\x00\x00\x00\x00\x01\x00\x06\x22\xa4\ +\x00\x00\x16\x6e\x00\x00\x00\x00\x00\x01\x00\x06\x23\xc6\ +\x00\x00\x16\x8c\x00\x00\x00\x00\x00\x01\x00\x06\x29\xb9\ +\x00\x00\x16\xac\x00\x00\x00\x00\x00\x01\x00\x06\x2c\xbd\ +\x00\x00\x16\xce\x00\x00\x00\x00\x00\x01\x00\x06\x2d\xde\ +\x00\x00\x16\xee\x00\x00\x00\x00\x00\x01\x00\x06\x30\xb2\ +\x00\x00\x17\x1c\x00\x00\x00\x00\x00\x01\x00\x06\x39\x1e\ +\x00\x00\x17\x40\x00\x00\x00\x00\x00\x01\x00\x06\x40\xde\ +\x00\x00\x17\x64\x00\x00\x00\x00\x00\x01\x00\x06\x45\xf9\ +\x00\x00\x17\x8c\x00\x00\x00\x00\x00\x01\x00\x06\x47\x4c\ " qt_resource_struct_v2 = b"\ \x00\x00\x00\x00\x00\x02\x00\x00\x00\x0f\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\x94\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x9b\ \x00\x00\x00\x00\x00\x00\x00\x00\ -\x00\x00\x00\x0a\x00\x02\x00\x00\x00\x01\x00\x00\x00\x91\ +\x00\x00\x00\x0a\x00\x02\x00\x00\x00\x01\x00\x00\x00\x98\ \x00\x00\x00\x00\x00\x00\x00\x00\ -\x00\x00\x00\x1a\x00\x02\x00\x00\x00\x01\x00\x00\x00\x85\ +\x00\x00\x00\x1a\x00\x02\x00\x00\x00\x01\x00\x00\x00\x8c\ \x00\x00\x00\x00\x00\x00\x00\x00\ \x00\x00\x00\x46\x00\x02\x00\x00\x00\x01\x00\x00\x00\x7a\ \x00\x00\x00\x00\x00\x00\x00\x00\ @@ -26105,75 +27270,75 @@ \x00\x00\x01\x98\x00\x02\x00\x00\x00\x0d\x00\x00\x00\x12\ \x00\x00\x00\x00\x00\x00\x00\x00\ \x00\x00\x01\xb0\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\ -\x00\x00\x01\x98\xe1\xb8\x63\x02\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x01\xd0\x00\x00\x00\x00\x00\x01\x00\x00\x08\x66\ -\x00\x00\x01\x98\xe1\xb8\x62\xfe\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x01\xf8\x00\x00\x00\x00\x00\x01\x00\x00\x09\x52\ -\x00\x00\x01\x98\xe1\xb8\x62\xfe\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x02\x20\x00\x00\x00\x00\x00\x01\x00\x00\x0a\x35\ -\x00\x00\x01\x98\xe1\xb8\x62\xfe\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x02\x48\x00\x00\x00\x00\x00\x01\x00\x00\x0b\x18\ -\x00\x00\x01\x98\xe1\xb8\x62\xfe\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x02\x70\x00\x00\x00\x00\x00\x01\x00\x00\x0c\x06\ -\x00\x00\x01\x98\xe1\xb8\x63\x02\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x02\xa4\x00\x00\x00\x00\x00\x01\x00\x00\x14\x42\ -\x00\x00\x01\x98\xe1\xb8\x63\x02\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x02\xd0\x00\x00\x00\x00\x00\x01\x00\x00\x1d\xf7\ -\x00\x00\x01\x98\xe1\xb8\x62\xfe\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x02\xfa\x00\x00\x00\x00\x00\x01\x00\x00\x22\x41\ -\x00\x00\x01\x98\xe1\xb8\x62\xfe\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x03\x1a\x00\x00\x00\x00\x00\x01\x00\x00\x26\x90\ -\x00\x00\x01\x98\xe1\xb8\x62\xfe\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x03\x36\x00\x00\x00\x00\x00\x01\x00\x00\x2c\x6e\ -\x00\x00\x01\x98\xe1\xb8\x63\x02\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x03\x52\x00\x00\x00\x00\x00\x01\x00\x00\x30\xca\ -\x00\x00\x01\x98\xe1\xb8\x62\xfe\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x03\x7a\x00\x00\x00\x00\x00\x01\x00\x00\x32\xb1\ -\x00\x00\x01\x98\xe1\xb8\x63\x02\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x01\x88\x00\x02\x00\x00\x00\x01\x00\x00\x00\x20\ \x00\x00\x00\x00\x00\x00\x00\x00\ \x00\x00\x01\x98\x00\x02\x00\x00\x00\x04\x00\x00\x00\x21\ \x00\x00\x00\x00\x00\x00\x00\x00\ \x00\x00\x03\x9a\x00\x00\x00\x00\x00\x01\x00\x00\x3a\x55\ -\x00\x00\x01\x98\xe1\xb8\x63\x0a\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x03\xbe\x00\x00\x00\x00\x00\x01\x00\x00\x3c\xcf\ -\x00\x00\x01\x98\xe1\xb8\x63\x02\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x03\xe0\x00\x00\x00\x00\x00\x01\x00\x00\x3f\x43\ -\x00\x00\x01\x98\xe1\xb8\x63\x0e\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x03\xfe\x00\x00\x00\x00\x00\x01\x00\x00\x41\xbf\ -\x00\x00\x01\x98\xe1\xb8\x63\x06\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x01\x88\x00\x02\x00\x00\x00\x01\x00\x00\x00\x26\ \x00\x00\x00\x00\x00\x00\x00\x00\ \x00\x00\x01\x98\x00\x02\x00\x00\x00\x0a\x00\x00\x00\x27\ \x00\x00\x00\x00\x00\x00\x00\x00\ \x00\x00\x04\x20\x00\x00\x00\x00\x00\x01\x00\x00\x44\x3b\ -\x00\x00\x01\x98\xe1\xb8\x63\x06\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x04\x3a\x00\x00\x00\x00\x00\x01\x00\x00\x45\x3c\ -\x00\x00\x01\x98\xe1\xb8\x63\x02\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x04\x6a\x00\x00\x00\x00\x00\x01\x00\x00\x4a\x54\ -\x00\x00\x01\x98\xe1\xb8\x63\x0a\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x04\x9a\x00\x00\x00\x00\x00\x01\x00\x00\x56\x16\ -\x00\x00\x01\x98\xe1\xb8\x63\x06\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x04\xc4\x00\x00\x00\x00\x00\x01\x00\x00\x58\x86\ -\x00\x00\x01\x98\xe1\xb8\x63\x0a\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x04\xea\x00\x00\x00\x00\x00\x01\x00\x00\x5e\x1e\ -\x00\x00\x01\x98\xe1\xb8\x63\x0a\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x05\x0e\x00\x00\x00\x00\x00\x01\x00\x00\x62\x7a\ -\x00\x00\x01\x98\xe1\xb8\x63\x06\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x05\x48\x00\x00\x00\x00\x00\x01\x00\x00\x69\x07\ -\x00\x00\x01\x98\xe1\xb8\x63\x02\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x05\x64\x00\x00\x00\x00\x00\x01\x00\x00\x6c\x03\ -\x00\x00\x01\x98\xe1\xb8\x63\x06\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x05\x8c\x00\x00\x00\x00\x00\x01\x00\x00\x6d\x01\ -\x00\x00\x01\x98\xe1\xb8\x63\x06\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x01\x88\x00\x02\x00\x00\x00\x01\x00\x00\x00\x32\ \x00\x00\x00\x00\x00\x00\x00\x00\ \x00\x00\x01\x98\x00\x02\x00\x00\x00\x02\x00\x00\x00\x33\ \x00\x00\x00\x00\x00\x00\x00\x00\ \x00\x00\x05\xbe\x00\x01\x00\x00\x00\x01\x00\x00\x77\x6b\ -\x00\x00\x01\x98\xe1\xb8\x63\x0a\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x05\xd0\x00\x00\x00\x00\x00\x01\x00\x02\x3a\xc0\ -\x00\x00\x01\x98\xe1\xb8\x63\x02\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x01\x88\x00\x02\x00\x00\x00\x02\x00\x00\x00\x36\ \x00\x00\x00\x00\x00\x00\x00\x00\ \x00\x00\x01\x98\x00\x02\x00\x00\x00\x06\x00\x00\x00\x3f\ @@ -26181,273 +27346,287 @@ \x00\x00\x05\xe4\x00\x02\x00\x00\x00\x07\x00\x00\x00\x38\ \x00\x00\x00\x00\x00\x00\x00\x00\ \x00\x00\x05\xf6\x00\x00\x00\x00\x00\x01\x00\x02\x3f\x46\ -\x00\x00\x01\x98\xe1\xb8\x63\x96\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x06\x2a\x00\x00\x00\x00\x00\x01\x00\x02\x49\xaa\ -\x00\x00\x01\x98\xe1\xb8\x63\x96\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x06\x5e\x00\x00\x00\x00\x00\x01\x00\x02\x53\xd3\ -\x00\x00\x01\x98\xe1\xb8\x63\x96\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x06\x98\x00\x00\x00\x00\x00\x01\x00\x02\x5e\x2b\ -\x00\x00\x01\x98\xe1\xb8\x63\x9a\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x06\xcc\x00\x00\x00\x00\x00\x01\x00\x02\x68\x43\ -\x00\x00\x01\x98\xe1\xb8\x63\x96\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x07\x02\x00\x00\x00\x00\x00\x01\x00\x02\x72\x6c\ -\x00\x00\x01\x98\xe1\xb8\x63\x96\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x07\x3a\x00\x00\x00\x00\x00\x01\x00\x02\x7c\x82\ -\x00\x00\x01\x98\xe1\xb8\x63\x96\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x07\x70\x00\x00\x00\x00\x00\x01\x00\x02\x86\xe8\ -\x00\x00\x01\x98\xe1\xb8\x63\x06\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x07\x98\x00\x00\x00\x00\x00\x01\x00\x02\x91\x17\ -\x00\x00\x01\x98\xe1\xb8\x63\x0e\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x07\xc4\x00\x00\x00\x00\x00\x01\x00\x02\x9b\x5e\ -\x00\x00\x01\x98\xe1\xb8\x63\x02\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x08\x00\x00\x00\x00\x00\x00\x01\x00\x02\x9d\x5f\ -\x00\x00\x01\x98\xe1\xb8\x62\xfe\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x08\x2c\x00\x00\x00\x00\x00\x01\x00\x02\xb8\xa4\ -\x00\x00\x01\x98\xe1\xb8\x63\x02\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x08\x58\x00\x00\x00\x00\x00\x01\x00\x02\xbd\xed\ -\x00\x00\x01\x98\xe1\xb8\x63\x02\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x01\x88\x00\x02\x00\x00\x00\x01\x00\x00\x00\x46\ \x00\x00\x00\x00\x00\x00\x00\x00\ \x00\x00\x01\x98\x00\x02\x00\x00\x00\x02\x00\x00\x00\x47\ \x00\x00\x00\x00\x00\x00\x00\x00\ \x00\x00\x08\x8c\x00\x00\x00\x00\x00\x01\x00\x02\xc1\x65\ -\x00\x00\x01\x98\xe1\xb8\x63\x02\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x08\xaa\x00\x00\x00\x00\x00\x01\x00\x02\xca\x47\ -\x00\x00\x01\x98\xe1\xb8\x63\x02\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x01\x88\x00\x02\x00\x00\x00\x01\x00\x00\x00\x4a\ \x00\x00\x00\x00\x00\x00\x00\x00\ \x00\x00\x01\x98\x00\x02\x00\x00\x00\x0d\x00\x00\x00\x4b\ \x00\x00\x00\x00\x00\x00\x00\x00\ \x00\x00\x08\xbe\x00\x00\x00\x00\x00\x01\x00\x02\xcf\x7c\ -\x00\x00\x01\x98\xe1\xb8\x62\xfe\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x08\xe6\x00\x00\x00\x00\x00\x01\x00\x02\xd4\x77\ -\x00\x00\x01\x98\xe1\xb8\x63\x06\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x09\x1e\x00\x00\x00\x00\x00\x01\x00\x02\xdd\x4b\ -\x00\x00\x01\x98\xe1\xb8\x63\x06\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x09\x56\x00\x00\x00\x00\x00\x01\x00\x02\xe5\xef\ -\x00\x00\x01\x98\xe1\xb8\x63\x02\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x09\x86\x00\x00\x00\x00\x00\x01\x00\x02\xed\x8e\ -\x00\x00\x01\x98\xe1\xb8\x63\x02\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x09\xaa\x00\x00\x00\x00\x00\x01\x00\x02\xf5\x6a\ -\x00\x00\x01\x98\xe1\xb8\x63\x02\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x09\xce\x00\x00\x00\x00\x00\x01\x00\x02\xfd\x20\ -\x00\x00\x01\x98\xe1\xb8\x63\x02\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x0a\x02\x00\x00\x00\x00\x00\x01\x00\x03\x05\x2e\ -\x00\x00\x01\x98\xe1\xb8\x63\x02\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x0a\x36\x00\x00\x00\x00\x00\x01\x00\x03\x0d\x10\ -\x00\x00\x01\x98\xe1\xb8\x63\x06\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x0a\x6a\x00\x00\x00\x00\x00\x01\x00\x03\x14\xda\ -\x00\x00\x01\x98\xe1\xb8\x63\x02\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x0a\xa4\x00\x00\x00\x00\x00\x01\x00\x03\x1c\x41\ -\x00\x00\x01\x98\xe1\xb8\x62\xfe\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x0a\xc8\x00\x00\x00\x00\x00\x01\x00\x03\x1f\xfe\ -\x00\x00\x01\x98\xe1\xb8\x62\xfe\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x0a\xf6\x00\x00\x00\x00\x00\x01\x00\x03\x23\xd7\ -\x00\x00\x01\x98\xe1\xb8\x63\x02\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x01\x88\x00\x02\x00\x00\x00\x01\x00\x00\x00\x59\ \x00\x00\x00\x00\x00\x00\x00\x00\ \x00\x00\x01\x98\x00\x02\x00\x00\x00\x08\x00\x00\x00\x5a\ \x00\x00\x00\x00\x00\x00\x00\x00\ \x00\x00\x0b\x1c\x00\x00\x00\x00\x00\x01\x00\x03\x29\xe0\ -\x00\x00\x01\x98\xe1\xb8\x63\x0a\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x0b\x48\x00\x00\x00\x00\x00\x01\x00\x03\x4c\x64\ -\x00\x00\x01\x98\xe1\xb8\x63\x0a\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x0b\x76\x00\x00\x00\x00\x00\x01\x00\x03\x52\x51\ -\x00\x00\x01\x98\xe1\xb8\x63\x02\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x0b\xa0\x00\x00\x00\x00\x00\x01\x00\x03\x54\x71\ -\x00\x00\x01\x98\xe1\xb8\x63\x02\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x0b\xc8\x00\x00\x00\x00\x00\x01\x00\x03\x5d\x09\ -\x00\x00\x01\x98\xe1\xb8\x63\x0a\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x0b\xe2\x00\x00\x00\x00\x00\x01\x00\x03\x6c\x52\ -\x00\x00\x01\x98\xe1\xb8\x63\x0a\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x0c\x0e\x00\x00\x00\x00\x00\x01\x00\x03\x72\xd0\ -\x00\x00\x01\x98\xe1\xb8\x63\x0e\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x0c\x36\x00\x00\x00\x00\x00\x01\x00\x03\x7d\x4a\ -\x00\x00\x01\x99\xe8\x24\xab\x4a\ +\x00\x00\x01\x9a\x3a\xfe\x5d\xa0\ \x00\x00\x01\x88\x00\x02\x00\x00\x00\x01\x00\x00\x00\x63\ \x00\x00\x00\x00\x00\x00\x00\x00\ \x00\x00\x01\x98\x00\x02\x00\x00\x00\x0c\x00\x00\x00\x64\ \x00\x00\x00\x00\x00\x00\x00\x00\ \x00\x00\x0c\x6c\x00\x00\x00\x00\x00\x01\x00\x03\x86\x91\ -\x00\x00\x01\x98\xe1\xb8\x62\xfe\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x0c\x9a\x00\x00\x00\x00\x00\x01\x00\x03\x89\x38\ -\x00\x00\x01\x98\xe1\xb8\x63\x02\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x0c\xc8\x00\x01\x00\x00\x00\x01\x00\x03\x95\xb5\ -\x00\x00\x01\x98\xe1\xb8\x62\xfe\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x0c\xf4\x00\x00\x00\x00\x00\x01\x00\x03\xc3\x34\ -\x00\x00\x01\x98\xe1\xb8\x62\xfe\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x0d\x14\x00\x00\x00\x00\x00\x01\x00\x03\xc7\xeb\ -\x00\x00\x01\x98\xe1\xb8\x62\xfe\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x0d\x46\x00\x01\x00\x00\x00\x01\x00\x04\x20\xe4\ -\x00\x00\x01\x98\xe1\xb8\x62\xfe\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x0d\x78\x00\x00\x00\x00\x00\x01\x00\x04\x55\x7e\ -\x00\x00\x01\x98\xe1\xb8\x63\x02\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x0d\x92\x00\x00\x00\x00\x00\x01\x00\x04\x5a\xc8\ -\x00\x00\x01\x98\xe1\xb8\x63\x02\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x0d\xac\x00\x00\x00\x00\x00\x01\x00\x04\x60\x57\ -\x00\x00\x01\x98\xe1\xb8\x63\x02\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x0d\xc6\x00\x00\x00\x00\x00\x01\x00\x04\x65\xc0\ -\x00\x00\x01\x98\xe1\xb8\x63\x0a\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x0d\xde\x00\x00\x00\x00\x00\x01\x00\x04\x71\x9e\ -\x00\x00\x01\x98\xe1\xb8\x63\x02\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x0d\xfc\x00\x00\x00\x00\x00\x01\x00\x04\x77\xa2\ -\x00\x00\x01\x98\xe1\xb8\x63\x02\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x01\x88\x00\x02\x00\x00\x00\x01\x00\x00\x00\x71\ \x00\x00\x00\x00\x00\x00\x00\x00\ \x00\x00\x01\x98\x00\x02\x00\x00\x00\x04\x00\x00\x00\x72\ \x00\x00\x00\x00\x00\x00\x00\x00\ \x00\x00\x0e\x24\x00\x00\x00\x00\x00\x01\x00\x04\x7c\xd7\ -\x00\x00\x01\x98\xe1\xb8\x63\x06\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x0e\x38\x00\x00\x00\x00\x00\x01\x00\x04\x82\xd4\ -\x00\x00\x01\x98\xe1\xb8\x63\x06\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x0e\x4a\x00\x00\x00\x00\x00\x01\x00\x04\x84\x5a\ -\x00\x00\x01\x98\xe1\xb8\x63\x06\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x0e\x5c\x00\x00\x00\x00\x00\x01\x00\x04\x8a\x54\ -\x00\x00\x01\x98\xe1\xb8\x63\x0e\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x01\x88\x00\x02\x00\x00\x00\x01\x00\x00\x00\x77\ \x00\x00\x00\x00\x00\x00\x00\x00\ \x00\x00\x01\x98\x00\x02\x00\x00\x00\x02\x00\x00\x00\x78\ \x00\x00\x00\x00\x00\x00\x00\x00\ \x00\x00\x0e\x70\x00\x00\x00\x00\x00\x01\x00\x04\x8c\xaa\ -\x00\x00\x01\x98\xe1\xb8\x62\xfe\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x0e\x9c\x00\x00\x00\x00\x00\x01\x00\x04\x93\x91\ -\x00\x00\x01\x98\xe1\xb8\x63\x06\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ \x00\x00\x01\x88\x00\x02\x00\x00\x00\x01\x00\x00\x00\x7b\ \x00\x00\x00\x00\x00\x00\x00\x00\ -\x00\x00\x01\x98\x00\x02\x00\x00\x00\x09\x00\x00\x00\x7c\ +\x00\x00\x01\x98\x00\x02\x00\x00\x00\x10\x00\x00\x00\x7c\ \x00\x00\x00\x00\x00\x00\x00\x00\ \x00\x00\x0e\xc0\x00\x00\x00\x00\x00\x01\x00\x04\x9c\xf5\ -\x00\x00\x01\x98\xe1\xb8\x62\xfe\ -\x00\x00\x0e\xe0\x00\x01\x00\x00\x00\x01\x00\x04\x9f\xa1\ -\x00\x00\x01\x98\xe1\xb8\x63\x0e\ -\x00\x00\x0f\x04\x00\x00\x00\x00\x00\x01\x00\x04\xaa\xf2\ -\x00\x00\x01\x98\xe1\xb8\x63\x0a\ -\x00\x00\x0f\x26\x00\x00\x00\x00\x00\x01\x00\x04\xb2\x97\ -\x00\x00\x01\x98\xe1\xb8\x63\x02\ -\x00\x00\x0f\x42\x00\x00\x00\x00\x00\x01\x00\x04\xc3\x97\ -\x00\x00\x01\x98\xe1\xb8\x63\x0e\ -\x00\x00\x0f\x66\x00\x00\x00\x00\x00\x01\x00\x04\xcd\x18\ -\x00\x00\x01\x98\xe1\xb8\x62\xfe\ -\x00\x00\x0f\x86\x00\x00\x00\x00\x00\x01\x00\x04\xd2\xb5\ -\x00\x00\x01\x98\xe1\xb8\x63\x0e\ -\x00\x00\x0f\xae\x00\x00\x00\x00\x00\x01\x00\x04\xdc\x7e\ -\x00\x00\x01\x98\xe1\xb8\x62\xfe\ -\x00\x00\x0f\xce\x00\x00\x00\x00\x00\x01\x00\x04\xe0\x61\ -\x00\x00\x01\x98\xe1\xb8\x63\x06\ -\x00\x00\x01\x88\x00\x02\x00\x00\x00\x01\x00\x00\x00\x86\ +\x00\x00\x01\x9a\xc0\x0b\xf8\x0b\ +\x00\x00\x0e\xe0\x00\x00\x00\x00\x00\x01\x00\x04\xa2\x8e\ +\x00\x00\x01\x9a\xc0\x0b\xf8\x07\ +\x00\x00\x0f\x00\x00\x01\x00\x00\x00\x01\x00\x04\xa8\xb5\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ +\x00\x00\x0f\x24\x00\x00\x00\x00\x00\x01\x00\x04\xb4\x06\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ +\x00\x00\x0f\x46\x00\x00\x00\x00\x00\x01\x00\x04\xbb\xab\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ +\x00\x00\x0f\x62\x00\x00\x00\x00\x00\x01\x00\x04\xcc\xab\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ +\x00\x00\x0f\x86\x00\x00\x00\x00\x00\x01\x00\x04\xd6\x2c\ +\x00\x00\x01\x9a\xc0\x0b\xf8\x0b\ +\x00\x00\x0f\xa6\x00\x00\x00\x00\x00\x01\x00\x04\xdb\xb0\ +\x00\x00\x01\x9a\xc0\x0b\xf8\x0b\ +\x00\x00\x0f\xc6\x00\x00\x00\x00\x00\x01\x00\x04\xe1\x49\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ +\x00\x00\x0f\xee\x00\x00\x00\x00\x00\x01\x00\x04\xeb\x12\ +\x00\x00\x01\x9a\xc0\x0b\xf8\x0b\ +\x00\x00\x10\x0e\x00\x00\x00\x00\x00\x01\x00\x04\xf0\xab\ +\x00\x00\x01\x9a\xc0\x0b\xf8\x0b\ +\x00\x00\x10\x42\x00\x00\x00\x00\x00\x01\x00\x04\xfb\x1f\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ +\x00\x00\x10\x5e\x00\x00\x00\x00\x00\x01\x00\x05\x01\x5a\ +\x00\x00\x01\x9a\xc0\x0b\xf8\x0b\ +\x00\x00\x10\x92\x00\x00\x00\x00\x00\x01\x00\x05\x0b\xd4\ +\x00\x00\x01\x9a\xc0\x0b\xf8\x0b\ +\x00\x00\x10\xc6\x00\x00\x00\x00\x00\x01\x00\x05\x16\x33\ +\x00\x00\x01\x9a\xc0\x0b\xf8\x07\ +\x00\x00\x10\xfa\x00\x00\x00\x00\x00\x01\x00\x05\x21\x7e\ +\x00\x00\x01\x9a\xc0\x0b\xf8\x0b\ +\x00\x00\x01\x88\x00\x02\x00\x00\x00\x01\x00\x00\x00\x8d\ \x00\x00\x00\x00\x00\x00\x00\x00\ -\x00\x00\x01\x98\x00\x02\x00\x00\x00\x0a\x00\x00\x00\x87\ +\x00\x00\x01\x98\x00\x02\x00\x00\x00\x0a\x00\x00\x00\x8e\ \x00\x00\x00\x00\x00\x00\x00\x00\ -\x00\x00\x0f\xea\x00\x00\x00\x00\x00\x01\x00\x04\xe6\x9c\ -\x00\x00\x01\x98\xe1\xb8\x63\x0a\ -\x00\x00\x10\x1a\x00\x00\x00\x00\x00\x01\x00\x04\xf0\x32\ -\x00\x00\x01\x98\xe1\xb8\x63\x0a\ -\x00\x00\x10\x4a\x00\x00\x00\x00\x00\x01\x00\x04\xfc\x0e\ -\x00\x00\x01\x98\xe1\xb8\x63\x0a\ -\x00\x00\x10\x6e\x00\x00\x00\x00\x00\x01\x00\x05\x02\x52\ -\x00\x00\x01\x98\xe1\xb8\x63\x0e\ -\x00\x00\x10\x98\x00\x00\x00\x00\x00\x01\x00\x05\x09\xdb\ -\x00\x00\x01\x98\xe1\xb8\x63\x02\ -\x00\x00\x10\xc4\x00\x00\x00\x00\x00\x01\x00\x05\x10\x39\ -\x00\x00\x01\x98\xe1\xb8\x63\x0a\ -\x00\x00\x10\xfa\x00\x00\x00\x00\x00\x01\x00\x05\x18\x28\ -\x00\x00\x01\x98\xe1\xb8\x62\xfe\ -\x00\x00\x08\xaa\x00\x00\x00\x00\x00\x01\x00\x05\x25\xcb\ -\x00\x00\x01\x98\xe1\xb8\x63\x02\ -\x00\x00\x11\x18\x00\x00\x00\x00\x00\x01\x00\x05\x2b\x00\ -\x00\x00\x01\x98\xe1\xb8\x62\xfe\ -\x00\x00\x11\x32\x00\x00\x00\x00\x00\x01\x00\x05\x34\xd5\ -\x00\x00\x01\x99\xe8\x24\xab\x4a\ -\x00\x00\x01\x88\x00\x02\x00\x00\x00\x01\x00\x00\x00\x92\ +\x00\x00\x11\x2e\x00\x00\x00\x00\x00\x01\x00\x05\x2b\xf2\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ +\x00\x00\x11\x5e\x00\x00\x00\x00\x00\x01\x00\x05\x35\x88\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ +\x00\x00\x11\x8e\x00\x00\x00\x00\x00\x01\x00\x05\x41\x64\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ +\x00\x00\x11\xb2\x00\x00\x00\x00\x00\x01\x00\x05\x47\xa8\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ +\x00\x00\x11\xdc\x00\x00\x00\x00\x00\x01\x00\x05\x4f\x31\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ +\x00\x00\x12\x08\x00\x00\x00\x00\x00\x01\x00\x05\x55\x8f\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ +\x00\x00\x12\x3e\x00\x00\x00\x00\x00\x01\x00\x05\x5d\x7e\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ +\x00\x00\x08\xaa\x00\x00\x00\x00\x00\x01\x00\x05\x6b\x21\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ +\x00\x00\x12\x5c\x00\x00\x00\x00\x00\x01\x00\x05\x70\x56\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ +\x00\x00\x12\x76\x00\x00\x00\x00\x00\x01\x00\x05\x7a\x2b\ +\x00\x00\x01\x9a\x3a\xfe\x5d\xa0\ +\x00\x00\x01\x88\x00\x02\x00\x00\x00\x01\x00\x00\x00\x99\ \x00\x00\x00\x00\x00\x00\x00\x00\ -\x00\x00\x01\x98\x00\x02\x00\x00\x00\x01\x00\x00\x00\x93\ +\x00\x00\x01\x98\x00\x02\x00\x00\x00\x01\x00\x00\x00\x9a\ \x00\x00\x00\x00\x00\x00\x00\x00\ -\x00\x00\x11\x5a\x00\x00\x00\x00\x00\x01\x00\x05\x3c\x9f\ -\x00\x00\x01\x98\xe1\xb8\x63\x02\ -\x00\x00\x01\x88\x00\x02\x00\x00\x00\x01\x00\x00\x00\x95\ +\x00\x00\x12\x9e\x00\x00\x00\x00\x00\x01\x00\x05\x81\xf5\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ +\x00\x00\x01\x88\x00\x02\x00\x00\x00\x01\x00\x00\x00\x9c\ \x00\x00\x00\x00\x00\x00\x00\x00\ -\x00\x00\x01\x98\x00\x02\x00\x00\x00\x28\x00\x00\x00\x96\ +\x00\x00\x01\x98\x00\x02\x00\x00\x00\x28\x00\x00\x00\x9d\ \x00\x00\x00\x00\x00\x00\x00\x00\ -\x00\x00\x11\x7a\x00\x00\x00\x00\x00\x01\x00\x05\x41\x65\ -\x00\x00\x01\x98\xe1\xb8\x63\x0e\ -\x00\x00\x11\x90\x00\x00\x00\x00\x00\x01\x00\x05\x49\x19\ -\x00\x00\x01\x98\xe1\xb8\x63\x06\ -\x00\x00\x11\xa8\x00\x00\x00\x00\x00\x01\x00\x05\x4b\x3e\ -\x00\x00\x01\x98\xe1\xb8\x63\x02\ -\x00\x00\x11\xe0\x00\x00\x00\x00\x00\x01\x00\x05\x4c\xbe\ -\x00\x00\x01\x98\xe1\xb8\x63\x0a\ -\x00\x00\x11\xfc\x00\x00\x00\x00\x00\x01\x00\x05\x54\x6b\ -\x00\x00\x01\x98\xe1\xb8\x63\x0a\ -\x00\x00\x12\x1c\x00\x00\x00\x00\x00\x01\x00\x05\x59\x40\ -\x00\x00\x01\x98\xe1\xb8\x63\x06\ -\x00\x00\x12\x32\x00\x00\x00\x00\x00\x01\x00\x05\x5a\x30\ -\x00\x00\x01\x98\xe1\xb8\x63\x02\ -\x00\x00\x12\x48\x00\x00\x00\x00\x00\x01\x00\x05\x5d\x14\ -\x00\x00\x01\x98\xe1\xb8\x63\x0e\ -\x00\x00\x12\x6e\x00\x00\x00\x00\x00\x01\x00\x05\x63\x4b\ -\x00\x00\x01\x99\x96\xf9\x85\x18\ -\x00\x00\x12\x88\x00\x00\x00\x00\x00\x01\x00\x05\x78\x58\ -\x00\x00\x01\x99\xe8\x24\xab\x4a\ -\x00\x00\x12\xaa\x00\x00\x00\x00\x00\x01\x00\x05\x7d\x48\ -\x00\x00\x01\x98\xe1\xb8\x62\xfe\ -\x00\x00\x12\xc0\x00\x00\x00\x00\x00\x01\x00\x05\x80\x47\ -\x00\x00\x01\x98\xe1\xb8\x63\x0a\ -\x00\x00\x12\xd6\x00\x00\x00\x00\x00\x01\x00\x05\x86\x51\ -\x00\x00\x01\x98\xe1\xb8\x62\xfe\ -\x00\x00\x13\x08\x00\x00\x00\x00\x00\x01\x00\x05\x89\xa0\ -\x00\x00\x01\x98\xe1\xb8\x63\x02\ -\x00\x00\x13\x20\x00\x00\x00\x00\x00\x01\x00\x05\x93\x21\ -\x00\x00\x01\x98\xe1\xb8\x62\xfe\ -\x00\x00\x13\x36\x00\x00\x00\x00\x00\x01\x00\x05\x99\x18\ -\x00\x00\x01\x99\x96\xf9\x85\x18\ -\x00\x00\x13\x4a\x00\x00\x00\x00\x00\x01\x00\x05\x9b\x29\ -\x00\x00\x01\x99\x96\xf9\x85\x18\ -\x00\x00\x13\x62\x00\x00\x00\x00\x00\x01\x00\x05\x9e\xec\ -\x00\x00\x01\x98\xe1\xb8\x62\xfe\ -\x00\x00\x13\x88\x00\x00\x00\x00\x00\x01\x00\x05\xa8\xa0\ -\x00\x00\x01\x98\xe1\xb8\x63\x0a\ -\x00\x00\x13\xb2\x00\x00\x00\x00\x00\x01\x00\x05\xab\xee\ -\x00\x00\x01\x98\xe1\xb8\x63\x0a\ -\x00\x00\x13\xd4\x00\x00\x00\x00\x00\x01\x00\x05\xaf\x7c\ -\x00\x00\x01\x99\xed\x4d\xf1\x14\ -\x00\x00\x13\xfa\x00\x00\x00\x00\x00\x01\x00\x05\xb4\x1d\ -\x00\x00\x01\x98\xe1\xb8\x63\x0a\ -\x00\x00\x14\x0e\x00\x00\x00\x00\x00\x01\x00\x05\xbd\xef\ -\x00\x00\x01\x98\xe1\xb8\x63\x06\ -\x00\x00\x14\x3a\x00\x00\x00\x00\x00\x01\x00\x05\xc3\x39\ -\x00\x00\x01\x98\xe1\xb8\x63\x0a\ -\x00\x00\x14\x62\x00\x00\x00\x00\x00\x01\x00\x05\xc9\x40\ -\x00\x00\x01\x98\xe1\xb8\x63\x0a\ -\x00\x00\x14\x78\x00\x00\x00\x00\x00\x01\x00\x05\xca\x24\ -\x00\x00\x01\x98\xe1\xb8\x62\xfe\ -\x00\x00\x14\xa4\x00\x00\x00\x00\x00\x01\x00\x05\xcc\x6d\ -\x00\x00\x01\x98\xe1\xb8\x63\x0e\ -\x00\x00\x14\xba\x00\x00\x00\x00\x00\x01\x00\x05\xd3\x1d\ -\x00\x00\x01\x98\xe1\xb8\x63\x0a\ -\x00\x00\x14\xd6\x00\x00\x00\x00\x00\x01\x00\x05\xd6\x61\ -\x00\x00\x01\x98\xe1\xb8\x63\x06\ -\x00\x00\x14\xee\x00\x00\x00\x00\x00\x01\x00\x05\xd7\x8d\ -\x00\x00\x01\x98\xe1\xb8\x63\x06\ -\x00\x00\x15\x08\x00\x00\x00\x00\x00\x01\x00\x05\xdd\x4e\ -\x00\x00\x01\x98\xe1\xb8\x63\x0a\ -\x00\x00\x15\x2a\x00\x00\x00\x00\x00\x01\x00\x05\xde\x70\ -\x00\x00\x01\x98\xe1\xb8\x63\x0a\ -\x00\x00\x15\x48\x00\x00\x00\x00\x00\x01\x00\x05\xe4\x63\ -\x00\x00\x01\x98\xe1\xb8\x63\x06\ -\x00\x00\x15\x68\x00\x00\x00\x00\x00\x01\x00\x05\xe7\x67\ -\x00\x00\x01\x98\xe1\xb8\x63\x0a\ -\x00\x00\x15\x8a\x00\x00\x00\x00\x00\x01\x00\x05\xe8\x88\ -\x00\x00\x01\x98\xe1\xb8\x63\x0a\ -\x00\x00\x15\xaa\x00\x00\x00\x00\x00\x01\x00\x05\xeb\x5c\ -\x00\x00\x01\x98\xe1\xb8\x63\x06\ -\x00\x00\x15\xd8\x00\x00\x00\x00\x00\x01\x00\x05\xf3\xc8\ +\x00\x00\x12\xbe\x00\x00\x00\x00\x00\x01\x00\x05\x86\xbb\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ +\x00\x00\x12\xd4\x00\x00\x00\x00\x00\x01\x00\x05\x8e\x6f\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ +\x00\x00\x12\xec\x00\x00\x00\x00\x00\x01\x00\x05\x90\x94\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ +\x00\x00\x13\x24\x00\x00\x00\x00\x00\x01\x00\x05\x92\x14\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ +\x00\x00\x13\x40\x00\x00\x00\x00\x00\x01\x00\x05\x99\xc1\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ +\x00\x00\x13\x60\x00\x00\x00\x00\x00\x01\x00\x05\x9e\x96\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ +\x00\x00\x13\x76\x00\x00\x00\x00\x00\x01\x00\x05\x9f\x86\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ +\x00\x00\x13\x8c\x00\x00\x00\x00\x00\x01\x00\x05\xa2\x6a\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ +\x00\x00\x13\xb2\x00\x00\x00\x00\x00\x01\x00\x05\xa8\xa1\ +\x00\x00\x01\x9a\x3a\xfe\x5d\xa0\ +\x00\x00\x13\xcc\x00\x00\x00\x00\x00\x01\x00\x05\xbd\xae\ +\x00\x00\x01\x9a\x3a\xfe\x5d\xa0\ +\x00\x00\x13\xee\x00\x00\x00\x00\x00\x01\x00\x05\xc2\x9e\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ +\x00\x00\x14\x04\x00\x00\x00\x00\x00\x01\x00\x05\xc5\x9d\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ +\x00\x00\x14\x1a\x00\x00\x00\x00\x00\x01\x00\x05\xcb\xa7\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ +\x00\x00\x14\x4c\x00\x00\x00\x00\x00\x01\x00\x05\xce\xf6\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ +\x00\x00\x14\x64\x00\x00\x00\x00\x00\x01\x00\x05\xd8\x77\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ +\x00\x00\x14\x7a\x00\x00\x00\x00\x00\x01\x00\x05\xde\x6e\ +\x00\x00\x01\x9a\x3a\xfe\x5d\xa0\ +\x00\x00\x14\x8e\x00\x00\x00\x00\x00\x01\x00\x05\xe0\x7f\ +\x00\x00\x01\x9a\x3a\xfe\x5d\xa0\ +\x00\x00\x14\xa6\x00\x00\x00\x00\x00\x01\x00\x05\xe4\x42\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ +\x00\x00\x14\xcc\x00\x00\x00\x00\x00\x01\x00\x05\xed\xf6\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ +\x00\x00\x14\xf6\x00\x00\x00\x00\x00\x01\x00\x05\xf1\x44\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ +\x00\x00\x15\x18\x00\x00\x00\x00\x00\x01\x00\x05\xf4\xd2\ +\x00\x00\x01\x9a\x3a\xfe\x5d\xa0\ +\x00\x00\x15\x3e\x00\x00\x00\x00\x00\x01\x00\x05\xf9\x73\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ +\x00\x00\x15\x52\x00\x00\x00\x00\x00\x01\x00\x06\x03\x45\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ +\x00\x00\x15\x7e\x00\x00\x00\x00\x00\x01\x00\x06\x08\x8f\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ +\x00\x00\x15\xa6\x00\x00\x00\x00\x00\x01\x00\x06\x0e\x96\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ +\x00\x00\x15\xbc\x00\x00\x00\x00\x00\x01\x00\x06\x0f\x7a\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ +\x00\x00\x15\xe8\x00\x00\x00\x00\x00\x01\x00\x06\x11\xc3\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ +\x00\x00\x15\xfe\x00\x00\x00\x00\x00\x01\x00\x06\x18\x73\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ +\x00\x00\x16\x1a\x00\x00\x00\x00\x00\x01\x00\x06\x1b\xb7\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ +\x00\x00\x16\x32\x00\x00\x00\x00\x00\x01\x00\x06\x1c\xe3\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ +\x00\x00\x16\x4c\x00\x00\x00\x00\x00\x01\x00\x06\x22\xa4\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ +\x00\x00\x16\x6e\x00\x00\x00\x00\x00\x01\x00\x06\x23\xc6\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ +\x00\x00\x16\x8c\x00\x00\x00\x00\x00\x01\x00\x06\x29\xb9\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ +\x00\x00\x16\xac\x00\x00\x00\x00\x00\x01\x00\x06\x2c\xbd\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ +\x00\x00\x16\xce\x00\x00\x00\x00\x00\x01\x00\x06\x2d\xde\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ +\x00\x00\x16\xee\x00\x00\x00\x00\x00\x01\x00\x06\x30\xb2\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ +\x00\x00\x17\x1c\x00\x00\x00\x00\x00\x01\x00\x06\x39\x1e\ \x00\x00\x01\x99\x7d\x04\xc2\x80\ -\x00\x00\x15\xfc\x00\x00\x00\x00\x00\x01\x00\x05\xfb\x88\ -\x00\x00\x01\x98\xe1\xb8\x63\x02\ -\x00\x00\x16\x20\x00\x00\x00\x00\x00\x01\x00\x06\x00\xa3\ -\x00\x00\x01\x99\x4c\xf0\xd6\xbc\ -\x00\x00\x16\x48\x00\x00\x00\x00\x00\x01\x00\x06\x01\xf6\ -\x00\x00\x01\x98\xe1\xb8\x62\xfe\ +\x00\x00\x17\x40\x00\x00\x00\x00\x00\x01\x00\x06\x40\xde\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ +\x00\x00\x17\x64\x00\x00\x00\x00\x00\x01\x00\x06\x45\xf9\ +\x00\x00\x01\x99\x4c\xf0\xd6\x60\ +\x00\x00\x17\x8c\x00\x00\x00\x00\x00\x01\x00\x06\x47\x4c\ +\x00\x00\x01\x98\xe1\xb8\x61\xb0\ " qt_version = [int(v) for v in QtCore.qVersion().split('.')] From 635cfd3d3758ac7184ced62035605137704703b8 Mon Sep 17 00:00:00 2001 From: Guilherme Costa Date: Fri, 28 Nov 2025 15:44:45 +0000 Subject: [PATCH 04/19] networkWindow.py: refactor to include listview wifiConnectivityWindow.ui: change horizontalLayout to a vertical layout with a listview and a vertical scrollbar wifiConnectivityWindow.py: generated file from QtDesigner with some optimizations Signed-off-by: Guilherme Costa --- BlocksScreen/lib/panels/networkWindow.py | 191 +++++------------- BlocksScreen/lib/ui/wifiConnectivityWindow.ui | 65 +++++- .../lib/ui/wifiConnectivityWindow_ui.py | 142 +++++++++---- 3 files changed, 218 insertions(+), 180 deletions(-) diff --git a/BlocksScreen/lib/panels/networkWindow.py b/BlocksScreen/lib/panels/networkWindow.py index d6b70c13..d7803955 100644 --- a/BlocksScreen/lib/panels/networkWindow.py +++ b/BlocksScreen/lib/panels/networkWindow.py @@ -1,32 +1,24 @@ +import copy import logging -import typing -import copy import subprocess +import typing from functools import partial from PyQt6 import QtCore, QtGui, QtWidgets -from PyQt6.QtCore import QRunnable, QThreadPool, QObject, pyqtSignal, QVariant -from PyQt6.QtWidgets import QScroller, QScrollerProperties +from PyQt6.QtCore import QRunnable, QThreadPool, QObject, pyqtSignal from lib.network import SdbusNetworkManagerAsync from lib.panels.widgets.popupDialogWidget import Popup from lib.ui.wifiConnectivityWindow_ui import Ui_wifi_stacked_page from lib.panels.widgets.keyboardPage import CustomQwertyKeyboard -from lib.utils.blocks_frame import BlocksCustomFrame from lib.panels.widgets.loadPage import LoadScreen from lib.utils.list_model import EntryDelegate, EntryListModel, ListItem logger = logging.getLogger("logs/BlocksScreen.log") -<<<<<<< HEAD - -class BuildNetworkList(QtCore.QThread): - """Retrieves information from sdbus interface about scanned networks""" -======= class NetworkScanRunnable(QRunnable): """QRunnable task that performs network scanning using SdbusNetworkManagerAsync ->>>>>>> 9285fb7 (Refactor: Refac to MVC view with Controller being runnables on a threadpoll) This runnable: - Triggers a network rescan via SdbusNetworkManagerAsync @@ -222,9 +214,6 @@ def __init__(self, parent: typing.Optional[QtWidgets.QWidget], /) -> None: self.panel = Ui_wifi_stacked_page() self.panel.setupUi(self) - - self._setupUI() - self._provider = WifiIconProvider() self.ongoing_update: bool = False @@ -243,16 +232,14 @@ def __init__(self, parent: typing.Optional[QtWidgets.QWidget], /) -> None: #View Models and Delegates self.model = EntryListModel() - self.model.setParent(self.network_list_widget) + self.model.setParent(self.panel.listView) self.entry_delegate = EntryDelegate() - self.network_list_widget.setModel(self.model) - self.network_list_widget.setItemDelegate(self.entry_delegate) + self.panel.listView.setModel(self.model) + self.panel.listView.setItemDelegate(self.entry_delegate) self.entry_delegate.item_selected.connect(self.ssid_item_clicked) - self.panel.network_backButton.clicked.connect(self.reset_view_model) # Network Scan self.build_network_list() - self.network_list_worker = BuildNetworkList() self.network_list_worker.finished_network_list_build.connect( self.handle_network_list @@ -413,8 +400,15 @@ def __init__(self, parent: typing.Optional[QtWidgets.QWidget], /) -> None: self.network_list_worker.build() self.request_network_scan.emit() + self.panel.listView.verticalScrollBar().valueChanged.connect( + self._handle_scrollbar + ) + self.panel.verticalScrollBar.valueChanged.connect(self._handle_scrollbar) + self.panel.verticalScrollBar.valueChanged.connect( + lambda value: self.panel.listView.verticalScrollBar().setValue(value) + ) + self.panel.verticalScrollBar.show() self.hide() - self.info_box_load() self.qwerty = CustomQwertyKeyboard(self) @@ -477,20 +471,28 @@ def deleteLater(self) -> None: def showEvent(self, event: QtGui.QShowEvent | None) -> None: """Re-add clients to update list""" + # Block all touch events so multitouch is ignored + if event.type() in ( + QtCore.QEvent.Type.TouchBegin, + QtCore.QEvent.Type.TouchUpdate, + QtCore.QEvent.Type.TouchEnd + ): + return True # ignore the event entirely + self.build_model_list() return super().showEvent(event) def build_model_list(self) -> None: """Builds the model list (`self.model`) containing updatable clients""" - self.network_list_widget.blockSignals(True) - self.model.clear() - - test:dict = copy.copy(self.saved_network) - if test.items(): - for ssid,(signal,is_saved) in test.items(): + self.panel.listView.blockSignals(True) + self.reset_view_model() + saved_networks:dict = copy.copy(self.saved_network) + if saved_networks.items(): + for ssid,(signal,is_saved) in saved_networks.items(): self.add_network_entry(ssid=ssid, signal=signal, is_saved=is_saved) - - self.network_list_widget.blockSignals(False) + self._setup_scrollbar() + + self.panel.listView.blockSignals(False) def saved_wifi_option_selected(self): """Handle connect/delete network button clicks""" @@ -739,6 +741,7 @@ def evaluate_network_state(self, nm_state: str = "") -> None: self.panel.hotspot_button.setEnabled(True) self.repaint() + if ( wifi_btn.state == wifi_btn.State.OFF and hotspot_btn.state == hotspot_btn.State.OFF @@ -905,15 +908,9 @@ def add_network(self) -> None: 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) -<<<<<<< HEAD - - @QtCore.pyqtSlot(QtWidgets.QListWidgetItem, name="ssid_item_clicked") - def ssid_item_clicked(self, item: QtWidgets.QListWidgetItem) -> None: -======= @QtCore.pyqtSlot(ListItem, name="ssid_item_clicked") def ssid_item_clicked(self, item: ListItem) -> None: ->>>>>>> da41c34 (refactor: change network list to listview) """Handles when a network is clicked on the QListWidget. Args: @@ -923,7 +920,6 @@ def ssid_item_clicked(self, item: ListItem) -> None: return _current_ssid_name = item.text - #_current_ssid_name = self.saved_network.get(item.text, {}) self.selected_item = copy.copy(item) if ( _current_ssid_name in self.sdbus_network.get_saved_ssid_names() @@ -964,7 +960,6 @@ def handle_network_list(self, data: typing.List[typing.Tuple]) -> None: scroll_bar_position = self.network_list_widget.verticalScrollBar().value() ======= def handle_network_list(self, data: typing.Dict) -> None: ->>>>>>> da41c34 (refactor: change network list to listview) self.network_list_widget.blockSignals(True) for entry in data: if entry[0] == self.sdbus_network.hotspot_ssid: @@ -975,8 +970,6 @@ def handle_network_list(self, data: typing.Dict) -> None: continue self.saved_network[entry[0]] = (entry[1], entry[2]) self.build_model_list() - self.network_list_widget.blockSignals(False) - self.evaluate_network_state() QtCore.QTimer().singleShot(10000, lambda: self.network_list_worker.build()) @@ -990,7 +983,7 @@ def handle_button_click(self, ssid: str): self.panel.network_activate_btn.show() else: self.panel.network_activate_btn.hide() - self.panel.frame.repaint() + #self.panel.frame.repaint() else: self.setCurrentIndex(self.indexOf(self.panel.add_network_page)) @@ -1070,7 +1063,7 @@ def add_network_entry(self, ssid: str, signal: int, is_saved:str) -> None: """Adds a new item to the list model""" wifi_pixmap = self._provider.get_pixmap(signal=signal, state=is_saved) - + ssid = ssid if ssid is not "" else "UNKOWN" item = ListItem( text=ssid, left_icon=wifi_pixmap, @@ -1078,59 +1071,27 @@ def add_network_entry(self, ssid: str, signal: int, is_saved:str) -> None: selected=False, allow_check=False, _lfontsize=17, - _rfontsize=13, - height=70, + _rfontsize=12, + height=80, ) self.model.add_item(item) + + def _handle_scrollbar(self, value): + # Block signals to avoid recursion + self.panel.verticalScrollBar.blockSignals(True) + self.panel.verticalScrollBar.setValue(value) + self.panel.verticalScrollBar.blockSignals(False) - def _setupUI(self) -> None: - - """Sets up the UI components and layout for the network window.""" - - font_id = QtGui.QFontDatabase.addApplicationFont( - ":/font/media/fonts for text/Momcake-Bold.ttf" + def _setup_scrollbar(self) -> None: + self.panel.verticalScrollBar.setMinimum( + self.panel.listView.verticalScrollBar().minimum() ) - font_family = QtGui.QFontDatabase.applicationFontFamilies(font_id)[0] - sizePolicy = QtWidgets.QSizePolicy( - QtWidgets.QSizePolicy.Policy.MinimumExpanding, - QtWidgets.QSizePolicy.Policy.MinimumExpanding, + self.panel.verticalScrollBar.setMaximum( + self.panel.listView.verticalScrollBar().maximum() + ) + self.panel.verticalScrollBar.setPageStep( + self.panel.listView.verticalScrollBar().pageStep() ) - sizePolicy.setHorizontalStretch(1) - #sizePolicy.setVerticalStretch(1) - self.setSizePolicy(sizePolicy) - self.setMinimumSize(QtCore.QSize(800, 500)) - #self.setMaximumSize(QtCore.QSize(800, 500)) - self.setLayoutDirection(QtCore.Qt.LayoutDirection.LeftToRight) - self.main_content_layout = QtWidgets.QHBoxLayout() - self.main_content_layout.setAlignment(QtCore.Qt.AlignmentFlag.AlignTop) - - font = QtGui.QFont() - font.setFamily(font_family) - font.setPointSize(24) - - self.header_title = QtWidgets.QLabel(self) - self.header_title.setMinimumSize(QtCore.QSize(100, 60)) - self.header_title.setMaximumSize(QtCore.QSize(16777215, 60)) - self.header_title.setFont(font) - - # Timer for loading screen timeout - self._load_timer = QtCore.QTimer(self) - - #Buttons frame for update buttons - self.network_buttons_frame = BlocksCustomFrame() - #self.network_buttons_frame.setMinimumSize(QtCore.QSize(100, 100)) - #self.network_buttons_frame.setMaximumSize(QtCore.QSize(800, 450)) - - #List widget for update buttons - self.network_list_widget = QtWidgets.QListView(self.network_buttons_frame) - - self.network_buttons_layout = QtWidgets.QVBoxLayout() - self.network_buttons_layout.setContentsMargins(15, 20, 20, 5) - self.network_buttons_layout.addWidget(self.network_list_widget, 0, QtCore.Qt.AlignmentFlag.AlignBottom) - self.network_buttons_frame.setLayout(self.network_buttons_layout) - - self.main_content_layout.addWidget(self.network_buttons_frame, 0) - self.setLayout(self.main_content_layout) def build_network_list(self) -> None: """Build available/saved network list with optimized palette setup.""" @@ -1159,56 +1120,4 @@ def set_brush_for_all_groups(palette, role, color, style=QtCore.Qt.BrushStyle.So set_brush_for_all_groups(palette, QtGui.QPalette.ColorRole.Link, (0, 0, 255, 0)) # Apply palette - self.network_list_widget.setPalette(palette) - - # General QListView setup - 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.ScrollBarAsNeeded) - self.network_list_widget.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarPolicy.ScrollBarAlwaysOff) - self.network_list_widget.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.SizeAdjustPolicy.AdjustIgnored) - 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) - self.network_list_widget.setUniformItemSizes(True) - self.network_list_widget.setSpacing(3) - - viewport = self.network_list_widget.viewport() - QScroller.grabGesture(viewport, QScroller.ScrollerGestureType.TouchGesture) - QScroller.grabGesture(viewport, QScroller.ScrollerGestureType.LeftMouseButtonGesture) - - scroller = QScroller.scroller(viewport) - props = scroller.scrollerProperties() - - props.setScrollMetric( - QScrollerProperties.ScrollMetric.VerticalOvershootPolicy, - QVariant(QScrollerProperties.OvershootPolicy.OvershootAlwaysOff) - ) - props.setScrollMetric( - QScrollerProperties.ScrollMetric.OvershootDragResistanceFactor, - QVariant(1.0) - ) - props.setScrollMetric( - QScrollerProperties.ScrollMetric.OvershootDragDistanceFactor, - QVariant(0.0) - ) - props.setScrollMetric( - QScrollerProperties.ScrollMetric.OvershootScrollDistanceFactor, - QVariant(0.0) - ) - props.setScrollMetric( - QScrollerProperties.ScrollMetric.OvershootScrollTime, - QVariant(0.0) - ) - - scroller.setScrollerProperties(props) - - self.network_list_widget.setObjectName("network_list_widget") - self.panel.nl_content_layout.addWidget(self.network_list_widget) + self.panel.listView.setPalette(palette) \ No newline at end of file diff --git a/BlocksScreen/lib/ui/wifiConnectivityWindow.ui b/BlocksScreen/lib/ui/wifiConnectivityWindow.ui index 289cbe08..f069ea17 100644 --- a/BlocksScreen/lib/ui/wifiConnectivityWindow.ui +++ b/BlocksScreen/lib/ui/wifiConnectivityWindow.ui @@ -6,7 +6,7 @@ 0 0 - 800 + 852 480 @@ -40,7 +40,7 @@ - 2 + 1 @@ -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 + + + + @@ -2754,6 +2808,11 @@ Type QLabel
lib.panels.widgets.loadWidget
+ + CustomScrollBar + QScrollBar +
lib.utils.blocks_Scrollbar
+
diff --git a/BlocksScreen/lib/ui/wifiConnectivityWindow_ui.py b/BlocksScreen/lib/ui/wifiConnectivityWindow_ui.py index fccaf1ae..a5316106 100644 --- a/BlocksScreen/lib/ui/wifiConnectivityWindow_ui.py +++ b/BlocksScreen/lib/ui/wifiConnectivityWindow_ui.py @@ -1,4 +1,4 @@ -# Form implementation generated from reading ui file '/home/levi/main/BlocksScreen/BlocksScreen/lib/ui/wifiConnectivityWindow.ui' +# Form implementation generated from reading ui file '~/home/main/BlocksScreen/BlocksScreen/lib/ui/wifiConnectivityWindow.ui' # # Created by: PyQt6 UI code generator 6.7.1 # @@ -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) @@ -1017,70 +1086,71 @@ def setupUi(self, wifi_stacked_page): wifi_stacked_page.addWidget(self.hotspot_page) self.retranslateUi(wifi_stacked_page) - wifi_stacked_page.setCurrentIndex(2) + wifi_stacked_page.setCurrentIndex(1) 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")) - self.saved_connection_delete_network_button.setText(_translate("wifi_stacked_page", "Delete")) + self.add_network_validation_button.setText("Activate") + self.saved_connection_delete_network_button.setText("Delete") self.saved_connection_delete_network_button.setProperty("class", _translate("wifi_stacked_page", "back_btn")) self.saved_connection_delete_network_button.setProperty("button_type", _translate("wifi_stacked_page", "icon")) - self.saved_connection_network_name.setText(_translate("wifi_stacked_page", "SSID")) - self.saved_connection_back_button.setText(_translate("wifi_stacked_page", "Back")) + self.saved_connection_network_name.setText("SSID") + self.saved_connection_back_button.setText("Back") self.saved_connection_back_button.setProperty("class", _translate("wifi_stacked_page", "back_btn")) self.saved_connection_back_button.setProperty("button_type", _translate("wifi_stacked_page", "icon")) self.saved_connection_change_password_label_2.setText(_translate("wifi_stacked_page", "Change\n" "Password")) - self.saved_connection_change_password_view.setText(_translate("wifi_stacked_page", "View")) + self.saved_connection_change_password_view.setText("View") self.saved_connection_change_password_view.setProperty("class", _translate("wifi_stacked_page", "back_btn")) self.saved_connection_change_password_view.setProperty("button_type", _translate("wifi_stacked_page", "icon")) self.sabed_connection_signal_strength_label.setText(_translate("wifi_stacked_page", "Signal\n" "Strength")) - self.saved_connection_signal_strength_info_frame.setText(_translate("wifi_stacked_page", "TextLabel")) + self.saved_connection_signal_strength_info_frame.setText("TextLabel") self.saved_connection_security_type_label.setText(_translate("wifi_stacked_page", "Security\n" "Type")) - self.saved_connection_security_type_info_label.setText(_translate("wifi_stacked_page", "TextLabel")) - self.hotspot_header_title.setText(_translate("wifi_stacked_page", "Hotspot")) - self.hotspot_back_button.setText(_translate("wifi_stacked_page", "Back")) + self.saved_connection_security_type_info_label.setText("TextLabel") + self.hotspot_header_title.setText("Hotspot") + self.hotspot_back_button.setText("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 From 7a023f28c967eb2f09ad2a035b9604f63df5887c Mon Sep 17 00:00:00 2001 From: Guilherme Costa Date: Thu, 11 Dec 2025 11:24:23 +0000 Subject: [PATCH 05/19] networkWindow: rebase merge conflits fix and cleanup Signed-off-by: Guilherme Costa --- BlocksScreen/lib/panels/networkWindow.py | 143 +++++++++++++---------- 1 file changed, 81 insertions(+), 62 deletions(-) diff --git a/BlocksScreen/lib/panels/networkWindow.py b/BlocksScreen/lib/panels/networkWindow.py index d7803955..85ba5b58 100644 --- a/BlocksScreen/lib/panels/networkWindow.py +++ b/BlocksScreen/lib/panels/networkWindow.py @@ -17,24 +17,27 @@ logger = logging.getLogger("logs/BlocksScreen.log") + class NetworkScanRunnable(QRunnable): """QRunnable task that performs network scanning using SdbusNetworkManagerAsync - This runnable: - - Triggers a network rescan via SdbusNetworkManagerAsync - - collects SSIDs, signal strenght and saved status - - emits signal with raw scan data and a processed lisgs - - Signals: - - scan_results (dict): Emitted with raw scan results mapping SSID to properties - - finished_network_list_build (list): Emitted with processed list of networks - - error (str): Emitted if an error occurs during scanning - + This runnable: + - Triggers a network rescan via SdbusNetworkManagerAsync + - collects SSIDs, signal strenght and saved status + - emits signal with raw scan data and a processed lisgs + + Signals: + - scan_results (dict): Emitted with raw scan results mapping SSID to properties + - finished_network_list_build (list): Emitted with processed list of networks + - error (str): Emitted if an error occurs during scanning + """ class Signals(QObject): scan_results = pyqtSignal(dict, name="scan-results") - finished_network_list_build = pyqtSignal(list, name="finished-network-list-build") + finished_network_list_build = pyqtSignal( + list, name="finished-network-list-build" + ) error = pyqtSignal(str) def __init__(self): @@ -47,7 +50,11 @@ def run(self): logger.debug("NetworkScanRunnable: scanning networks") self.nm.rescan_networks() saved = self.nm.get_saved_ssid_names() - available = self.nm.get_available_networks() if self.nm.check_wifi_interface() else {} + available = ( + self.nm.get_available_networks() + if self.nm.check_wifi_interface() + else {} + ) data_dict: dict[str, dict] = {} for ssid, props in available.items(): @@ -61,9 +68,17 @@ def run(self): self.signals.scan_results.emit(data_dict) # Transform into your “list of tuples + blank / separator” format - items: list[typing.Union[tuple[str,int,str], str]] = [] - saved_nets = [ (ssid, info["signal_level"]) for ssid, info in data_dict.items() if info["is_saved"] ] - unsaved_nets = [ (ssid, info["signal_level"]) for ssid, info in data_dict.items() if not info["is_saved"] ] + items: list[typing.Union[tuple[str, int, str], str]] = [] + saved_nets = [ + (ssid, info["signal_level"]) + for ssid, info in data_dict.items() + if info["is_saved"] + ] + unsaved_nets = [ + (ssid, info["signal_level"]) + 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]) @@ -81,10 +96,11 @@ def run(self): logger.error("Error scanning networks", exc_info=True) self.signals.error.emit(str(e)) + class BuildNetworkList(QtCore.QObject): """ Controller class that schedules and manages repeted network scans - + Uses a QThreadPool to un NetworkScanRunnable tasks periodically. with a QTimer to trigger scans. Prevents overlapping scans by tracking whether a scan is already in progress. @@ -92,12 +108,13 @@ class BuildNetworkList(QtCore.QObject): poll_interval_ms: (int) Milliseconds between scans (default: 10000) _timer (QtCore.QTimer): Timer that schedules next scan _is_scanning (bool): Flag indicating if a scan is currently in progress - + Signals: scan_results (dict): Emitted with raw scan results mapping SSID to properties finished_network_list_build (list): Emitted with processed list of networks error (str): Emitted if an error occurs during scanning """ + scan_results = pyqtSignal(dict, name="scan-results") finished_network_list_build = pyqtSignal(list, name="finished-network-list-build") error = pyqtSignal(str) @@ -152,6 +169,7 @@ def _do_scan(self): self.threadpool.start(task) logger.debug("Submitted scan task to thread pool") + class WifiIconProvider: """Simple provider: loads QPixmap for WiFi bars + protection without caching.""" @@ -163,7 +181,6 @@ def __init__(self): (3, False): ":/network/media/btn_icons/3bar_wifi.svg", (2, False): ":/network/media/btn_icons/2bar_wifi.svg", (1, False): ":/network/media/btn_icons/1bar_wifi.svg", - ("no", True): ":/network/media/btn_icons/0bar_wifi_protected.svg", (4, True): ":/network/media/btn_icons/4bar_wifi_protected.svg", (3, True): ":/network/media/btn_icons/3bar_wifi_protected.svg", @@ -171,7 +188,6 @@ def __init__(self): (1, True): ":/network/media/btn_icons/1bar_wifi_protected.svg", } - def get_pixmap(self, signal: int, state: str) -> QtGui.QPixmap: """Return a QPixmap for the given signal (0-100) and state ("Protected" or not).""" # Normalize signal @@ -186,18 +202,22 @@ def get_pixmap(self, signal: int, state: str) -> QtGui.QPixmap: else: bars = 1 - is_protected = (state == "Protected") + is_protected = state == "Protected" key = (bars, is_protected) path = self.paths.get(key) if path is None: - logger.warning(f"No icon path for key {key}, falling back to no-signal unprotected") + logger.warning( + f"No icon path for key {key}, falling back to no-signal unprotected" + ) path = self.paths[("no", False)] pm = QtGui.QPixmap(path) if pm.isNull(): logger.error(f"Failed to load pixmap from '{path}' for key {key}") return pm + + class NetworkControlWindow(QtWidgets.QStackedWidget): """Network Control panel Widget""" @@ -211,17 +231,17 @@ def __init__(self, parent: typing.Optional[QtWidgets.QWidget], /) -> None: super().__init__(parent) else: super().__init__() - + self.panel = Ui_wifi_stacked_page() self.panel.setupUi(self) self._provider = WifiIconProvider() self.ongoing_update: bool = False - + self.popup = Popup(self) self.sdbus_network = SdbusNetworkManagerAsync() self.start: bool = True self.saved_network = {} - + self.load_popup: LoadScreen = LoadScreen(self) self.repeated_request_status = QtCore.QTimer() self.repeated_request_status.setInterval(2000) # every 2 seconds @@ -229,8 +249,8 @@ def __init__(self, parent: typing.Optional[QtWidgets.QWidget], /) -> None: self._load_timer = QtCore.QTimer() self._load_timer.setSingleShot(True) self._load_timer.timeout.connect(self._handle_load_timeout) - - #View Models and Delegates + + # View Models and Delegates self.model = EntryListModel() self.model.setParent(self.panel.listView) self.entry_delegate = EntryDelegate() @@ -455,8 +475,7 @@ def handle_ongoing_update(self) -> None: self.load_popup.set_status_message("Updating...") self.load_popup.show() self.repeated_request_status.start(2000) - - # View Model Methods + def reset_view_model(self) -> None: """Clears items from ListView (Resets `QAbstractListModel` by clearing entries) @@ -475,10 +494,10 @@ def showEvent(self, event: QtGui.QShowEvent | None) -> None: if event.type() in ( QtCore.QEvent.Type.TouchBegin, QtCore.QEvent.Type.TouchUpdate, - QtCore.QEvent.Type.TouchEnd + QtCore.QEvent.Type.TouchEnd, ): return True # ignore the event entirely - + self.build_model_list() return super().showEvent(event) @@ -486,14 +505,14 @@ def build_model_list(self) -> None: """Builds the model list (`self.model`) containing updatable clients""" self.panel.listView.blockSignals(True) self.reset_view_model() - saved_networks:dict = copy.copy(self.saved_network) + saved_networks: dict = copy.copy(self.saved_network) if saved_networks.items(): - for ssid,(signal,is_saved) in saved_networks.items(): + for ssid, (signal, is_saved) in saved_networks.items(): self.add_network_entry(ssid=ssid, signal=signal, is_saved=is_saved) self._setup_scrollbar() self.panel.listView.blockSignals(False) - + def saved_wifi_option_selected(self): """Handle connect/delete network button clicks""" _sender = self.sender() @@ -741,7 +760,6 @@ def evaluate_network_state(self, nm_state: str = "") -> None: self.panel.hotspot_button.setEnabled(True) self.repaint() - if ( wifi_btn.state == wifi_btn.State.OFF and hotspot_btn.state == hotspot_btn.State.OFF @@ -908,7 +926,7 @@ def add_network(self) -> None: 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) - + @QtCore.pyqtSlot(ListItem, name="ssid_item_clicked") def ssid_item_clicked(self, item: ListItem) -> None: """Handles when a network is clicked on the QListWidget. @@ -917,24 +935,21 @@ def ssid_item_clicked(self, item: ListItem) -> None: item (QListWidgetItem): The list entry that was clicked """ if not item: - return - + return + _current_ssid_name = item.text self.selected_item = copy.copy(item) 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) - ) + 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 - def update_network( self, ssid: str, @@ -954,13 +969,8 @@ def update_network( self.setCurrentIndex(self.indexOf(self.panel.network_list_page)) @QtCore.pyqtSlot(list, name="finished-network-list-build") -<<<<<<< HEAD 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() -======= - def handle_network_list(self, data: typing.Dict) -> None: - self.network_list_widget.blockSignals(True) for entry in data: if entry[0] == self.sdbus_network.hotspot_ssid: continue @@ -983,7 +993,6 @@ def handle_button_click(self, ssid: str): self.panel.network_activate_btn.show() else: self.panel.network_activate_btn.hide() - #self.panel.frame.repaint() else: self.setCurrentIndex(self.indexOf(self.panel.add_network_page)) @@ -1058,12 +1067,12 @@ def show_network_panel( self.updateGeometry() self.update() self.show() - - def add_network_entry(self, ssid: str, signal: int, is_saved:str) -> None: + + def add_network_entry(self, ssid: str, signal: int, is_saved: str) -> None: """Adds a new item to the list model""" - + wifi_pixmap = self._provider.get_pixmap(signal=signal, state=is_saved) - ssid = ssid if ssid is not "" else "UNKOWN" + ssid = ssid if ssid != "" else "UNKOWN" item = ListItem( text=ssid, left_icon=wifi_pixmap, @@ -1075,13 +1084,13 @@ def add_network_entry(self, ssid: str, signal: int, is_saved:str) -> None: height=80, ) self.model.add_item(item) - + def _handle_scrollbar(self, value): # Block signals to avoid recursion self.panel.verticalScrollBar.blockSignals(True) self.panel.verticalScrollBar.setValue(value) self.panel.verticalScrollBar.blockSignals(False) - + def _setup_scrollbar(self) -> None: self.panel.verticalScrollBar.setMinimum( self.panel.listView.verticalScrollBar().minimum() @@ -1092,10 +1101,13 @@ def _setup_scrollbar(self) -> None: self.panel.verticalScrollBar.setPageStep( self.panel.listView.verticalScrollBar().pageStep() ) - + def build_network_list(self) -> None: """Build available/saved network list with optimized palette setup.""" - def set_brush_for_all_groups(palette, role, color, style=QtCore.Qt.BrushStyle.SolidPattern): + + def set_brush_for_all_groups( + palette, role, color, style=QtCore.Qt.BrushStyle.SolidPattern + ): """Helper to set a brush for Active, Inactive, and Disabled states.""" brush = QtGui.QBrush(QtGui.QColor(*color)) brush.setStyle(style) @@ -1107,17 +1119,24 @@ def set_brush_for_all_groups(palette, role, color, style=QtCore.Qt.BrushStyle.So palette.setBrush(group, role, brush) palette = QtGui.QPalette() - + # Transparent backgrounds set_brush_for_all_groups(palette, QtGui.QPalette.ColorRole.Button, (0, 0, 0, 0)) set_brush_for_all_groups(palette, QtGui.QPalette.ColorRole.Window, (0, 0, 0, 0)) - + # Base (black, no brush) - set_brush_for_all_groups(palette, QtGui.QPalette.ColorRole.Base, (0, 0, 0), QtCore.Qt.BrushStyle.NoBrush) - + set_brush_for_all_groups( + palette, + QtGui.QPalette.ColorRole.Base, + (0, 0, 0), + QtCore.Qt.BrushStyle.NoBrush, + ) + # Highlight & link - set_brush_for_all_groups(palette, QtGui.QPalette.ColorRole.Highlight, (0, 120, 215, 0)) + set_brush_for_all_groups( + palette, QtGui.QPalette.ColorRole.Highlight, (0, 120, 215, 0) + ) set_brush_for_all_groups(palette, QtGui.QPalette.ColorRole.Link, (0, 0, 255, 0)) - + # Apply palette - self.panel.listView.setPalette(palette) \ No newline at end of file + self.panel.listView.setPalette(palette) From 01d42a4103b203213b7f76511cba6279cd1cbe09 Mon Sep 17 00:00:00 2001 From: Guilherme Costa Date: Fri, 12 Dec 2025 13:07:36 +0000 Subject: [PATCH 06/19] networkWindow.py: added missing right icon Signed-off-by: Guilherme Costa --- BlocksScreen/lib/panels/networkWindow.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/BlocksScreen/lib/panels/networkWindow.py b/BlocksScreen/lib/panels/networkWindow.py index 85ba5b58..303784d4 100644 --- a/BlocksScreen/lib/panels/networkWindow.py +++ b/BlocksScreen/lib/panels/networkWindow.py @@ -281,6 +281,7 @@ def __init__(self, parent: typing.Optional[QtWidgets.QWidget], /) -> None: self.panel.wifi_button.setPixmap( QtGui.QPixmap(":/network/media/btn_icons/wifi_config.svg") ) + self.right_icon = QtGui.QPixmap(":/arrow_icons/media/btn_icons/right_arrow.svg") self.panel.nl_back_button.clicked.connect( partial( @@ -1077,6 +1078,7 @@ def add_network_entry(self, ssid: str, signal: int, is_saved: str) -> None: text=ssid, left_icon=wifi_pixmap, right_text=is_saved, + right_icon=self.right_icon, selected=False, allow_check=False, _lfontsize=17, @@ -1120,23 +1122,17 @@ def set_brush_for_all_groups( palette = QtGui.QPalette() - # Transparent backgrounds set_brush_for_all_groups(palette, QtGui.QPalette.ColorRole.Button, (0, 0, 0, 0)) set_brush_for_all_groups(palette, QtGui.QPalette.ColorRole.Window, (0, 0, 0, 0)) - # Base (black, no brush) set_brush_for_all_groups( palette, QtGui.QPalette.ColorRole.Base, (0, 0, 0), QtCore.Qt.BrushStyle.NoBrush, ) - - # Highlight & link set_brush_for_all_groups( palette, QtGui.QPalette.ColorRole.Highlight, (0, 120, 215, 0) ) set_brush_for_all_groups(palette, QtGui.QPalette.ColorRole.Link, (0, 0, 255, 0)) - - # Apply palette self.panel.listView.setPalette(palette) From cc61cfd2f1fe227351fe2b2edb61a9cc3025214c Mon Sep 17 00:00:00 2001 From: HugoCLSC Date: Mon, 15 Dec 2025 16:44:40 +0000 Subject: [PATCH 07/19] Fix typo --- BlocksScreen/lib/panels/networkWindow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BlocksScreen/lib/panels/networkWindow.py b/BlocksScreen/lib/panels/networkWindow.py index 303784d4..5f227aee 100644 --- a/BlocksScreen/lib/panels/networkWindow.py +++ b/BlocksScreen/lib/panels/networkWindow.py @@ -1073,7 +1073,7 @@ def add_network_entry(self, ssid: str, signal: int, is_saved: str) -> None: """Adds a new item to the list model""" wifi_pixmap = self._provider.get_pixmap(signal=signal, state=is_saved) - ssid = ssid if ssid != "" else "UNKOWN" + ssid = ssid if ssid != "" else "UNKNOWN" item = ListItem( text=ssid, left_icon=wifi_pixmap, From 44e74d4030752b91810bd5aaad6fa9bb6a3694c7 Mon Sep 17 00:00:00 2001 From: Guilherme Costa Date: Tue, 16 Dec 2025 11:16:00 +0000 Subject: [PATCH 08/19] networkWindow.py: optimize and bugfix on self.paths Signed-off-by: Guilherme Costa --- BlocksScreen/lib/panels/networkWindow.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/BlocksScreen/lib/panels/networkWindow.py b/BlocksScreen/lib/panels/networkWindow.py index 5f227aee..0ee8d715 100644 --- a/BlocksScreen/lib/panels/networkWindow.py +++ b/BlocksScreen/lib/panels/networkWindow.py @@ -176,12 +176,12 @@ class WifiIconProvider: def __init__(self): # Map from (bars, is_protected) to resource path self.paths = { - ("no", False): ":/network/media/btn_icons/0bar_wifi.svg", + (0, False): ":/network/media/btn_icons/0bar_wifi.svg", (4, False): ":/network/media/btn_icons/4bar_wifi.svg", (3, False): ":/network/media/btn_icons/3bar_wifi.svg", (2, False): ":/network/media/btn_icons/2bar_wifi.svg", (1, False): ":/network/media/btn_icons/1bar_wifi.svg", - ("no", True): ":/network/media/btn_icons/0bar_wifi_protected.svg", + (0, True): ":/network/media/btn_icons/0bar_wifi_protected.svg", (4, True): ":/network/media/btn_icons/4bar_wifi_protected.svg", (3, True): ":/network/media/btn_icons/3bar_wifi_protected.svg", (2, True): ":/network/media/btn_icons/2bar_wifi_protected.svg", @@ -191,13 +191,13 @@ def __init__(self): def get_pixmap(self, signal: int, state: str) -> QtGui.QPixmap: """Return a QPixmap for the given signal (0-100) and state ("Protected" or not).""" # Normalize signal - if signal <= 0: - bars = "no" + if signal <= 25: + bars = 0 elif signal >= 75: bars = 4 elif signal >= 50: bars = 3 - elif signal >= 25: + elif signal > 25: bars = 2 else: bars = 1 @@ -207,14 +207,14 @@ def get_pixmap(self, signal: int, state: str) -> QtGui.QPixmap: path = self.paths.get(key) if path is None: - logger.warning( + logger.debug( f"No icon path for key {key}, falling back to no-signal unprotected" ) - path = self.paths[("no", False)] + path = self.paths[(0, False)] pm = QtGui.QPixmap(path) if pm.isNull(): - logger.error(f"Failed to load pixmap from '{path}' for key {key}") + logger.debug(f"Failed to load pixmap from '{path}' for key {key}") return pm From 13f91d7370d3a2f0180aca1c1c99a3b325ae63d0 Mon Sep 17 00:00:00 2001 From: Guilherme Costa Date: Tue, 16 Dec 2025 14:27:18 +0000 Subject: [PATCH 09/19] networkWindow.py: comments cleanup --- BlocksScreen/lib/panels/networkWindow.py | 28 +++++++----------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/BlocksScreen/lib/panels/networkWindow.py b/BlocksScreen/lib/panels/networkWindow.py index 0ee8d715..75271327 100644 --- a/BlocksScreen/lib/panels/networkWindow.py +++ b/BlocksScreen/lib/panels/networkWindow.py @@ -64,10 +64,8 @@ def run(self): "is_saved": ssid in saved, } - # Emit scan_result (same name) self.signals.scan_results.emit(data_dict) - # Transform into your “list of tuples + blank / separator” format items: list[typing.Union[tuple[str, int, str], str]] = [] saved_nets = [ (ssid, info["signal_level"]) @@ -82,7 +80,6 @@ def run(self): saved_nets.sort(key=lambda x: -x[1]) unsaved_nets.sort(key=lambda x: -x[1]) - # Build your list with statuses for ssid, sig in saved_nets: status = "Active" if ssid == self.nm.get_current_ssid() else "Saved" items.append((ssid, sig, status)) @@ -174,7 +171,6 @@ class WifiIconProvider: """Simple provider: loads QPixmap for WiFi bars + protection without caching.""" def __init__(self): - # Map from (bars, is_protected) to resource path self.paths = { (0, False): ":/network/media/btn_icons/0bar_wifi.svg", (4, False): ":/network/media/btn_icons/4bar_wifi.svg", @@ -190,7 +186,6 @@ def __init__(self): def get_pixmap(self, signal: int, state: str) -> QtGui.QPixmap: """Return a QPixmap for the given signal (0-100) and state ("Protected" or not).""" - # Normalize signal if signal <= 25: bars = 0 elif signal >= 75: @@ -244,13 +239,12 @@ def __init__(self, parent: typing.Optional[QtWidgets.QWidget], /) -> None: self.load_popup: LoadScreen = LoadScreen(self) self.repeated_request_status = QtCore.QTimer() - self.repeated_request_status.setInterval(2000) # every 2 seconds + self.repeated_request_status.setInterval(2000) self._load_timer = QtCore.QTimer() self._load_timer.setSingleShot(True) self._load_timer.timeout.connect(self._handle_load_timeout) - # View Models and Delegates self.model = EntryListModel() self.model.setParent(self.panel.listView) self.entry_delegate = EntryDelegate() @@ -258,7 +252,6 @@ def __init__(self, parent: typing.Optional[QtWidgets.QWidget], /) -> None: self.panel.listView.setItemDelegate(self.entry_delegate) self.entry_delegate.item_selected.connect(self.ssid_item_clicked) - # Network Scan self.build_network_list() self.network_list_worker = BuildNetworkList() self.network_list_worker.finished_network_list_build.connect( @@ -491,13 +484,12 @@ def deleteLater(self) -> None: def showEvent(self, event: QtGui.QShowEvent | None) -> None: """Re-add clients to update list""" - # Block all touch events so multitouch is ignored if event.type() in ( QtCore.QEvent.Type.TouchBegin, QtCore.QEvent.Type.TouchUpdate, QtCore.QEvent.Type.TouchEnd, ): - return True # ignore the event entirely + return True self.build_model_list() return super().showEvent(event) @@ -838,7 +830,7 @@ def _expand_infobox(self, toggle: bool = False) -> None: 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) @@ -878,7 +870,6 @@ def add_network(self) -> None: - add_network_confirmation(pyqtSignal): Signal with a dict that contains the result of adding a new network to the machine. """ - # Check if a password was inserted self.panel.add_network_validation_button.setEnabled(False) self.panel.add_network_validation_button.repaint() @@ -898,7 +889,6 @@ def add_network(self) -> None: 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, @@ -942,15 +932,14 @@ def ssid_item_clicked(self, item: ListItem) -> None: self.selected_item = copy.copy(item) 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 + else: 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 - + ) def update_network( self, ssid: str, @@ -1009,14 +998,14 @@ def setCurrentIndex(self, index: int): if not self.isVisible(): return _cur = self.currentIndex() - if index == self.indexOf(self.panel.add_network_page): # Add network page 2 + if index == self.indexOf(self.panel.add_network_page): 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" @@ -1088,7 +1077,6 @@ def add_network_entry(self, ssid: str, signal: int, is_saved: str) -> None: self.model.add_item(item) def _handle_scrollbar(self, value): - # Block signals to avoid recursion self.panel.verticalScrollBar.blockSignals(True) self.panel.verticalScrollBar.setValue(value) self.panel.verticalScrollBar.blockSignals(False) From a7d4981f379736e165cf5ed2ce6789e927404298 Mon Sep 17 00:00:00 2001 From: Guilherme Costa Date: Wed, 17 Dec 2025 12:29:24 +0000 Subject: [PATCH 10/19] networkWindow.py: fix missing formatting --- BlocksScreen/lib/panels/networkWindow.py | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/BlocksScreen/lib/panels/networkWindow.py b/BlocksScreen/lib/panels/networkWindow.py index 75271327..16d89c91 100644 --- a/BlocksScreen/lib/panels/networkWindow.py +++ b/BlocksScreen/lib/panels/networkWindow.py @@ -489,7 +489,7 @@ def showEvent(self, event: QtGui.QShowEvent | None) -> None: QtCore.QEvent.Type.TouchUpdate, QtCore.QEvent.Type.TouchEnd, ): - return True + return True self.build_model_list() return super().showEvent(event) @@ -930,16 +930,13 @@ def ssid_item_clicked(self, item: ListItem) -> None: _current_ssid_name = item.text self.selected_item = copy.copy(item) - if ( - _current_ssid_name in self.sdbus_network.get_saved_ssid_names() - ): + if _current_ssid_name in self.sdbus_network.get_saved_ssid_names(): self.setCurrentIndex(self.indexOf(self.panel.saved_connection_page)) self.panel.saved_connection_network_name.setText(str(_current_ssid_name)) - else: + else: self.setCurrentIndex(self.indexOf(self.panel.add_network_page)) - self.panel.add_network_network_label.setText( - str(_current_ssid_name) - ) + self.panel.add_network_network_label.setText(str(_current_ssid_name)) + def update_network( self, ssid: str, @@ -998,14 +995,12 @@ def setCurrentIndex(self, index: int): if not self.isVisible(): return _cur = self.currentIndex() - if index == self.indexOf(self.panel.add_network_page): + if index == self.indexOf(self.panel.add_network_page): 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 - ): + elif index == self.indexOf(self.panel.saved_connection_page): self.panel.saved_connection_change_password_field.clear() self.panel.saved_connection_change_password_field.setPlaceholderText( "Change network password" From 3f51cfe70c60ed55bd5bf6ad6fadd3e2204c6c0a Mon Sep 17 00:00:00 2001 From: Guilherme Costa Date: Fri, 2 Jan 2026 15:17:51 +0000 Subject: [PATCH 11/19] Revert accidental changes to requirements.txt --- scripts/requirements.txt | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/scripts/requirements.txt b/scripts/requirements.txt index ed52a007..9dfdbd57 100644 --- a/scripts/requirements.txt +++ b/scripts/requirements.txt @@ -1,14 +1,14 @@ altgraph==0.17.4 -certifi==2024.7.4 -charset-normalizer==3.3.2 -idna==3.8 -numpy==2.1.0 -pefile==2023.2.7 -PyQt6==6.7.1 -PyQt6-Qt6==6.7.2 -PyQt6_sip==13.8.0 -requests==2.32.4 -sdbus==0.12.0 +certifi==2025.10.5 +charset-normalizer==3.4.4 +idna==3.11 +numpy==2.3.4 +pefile==2024.8.26 +PyQt6==6.10.0 +PyQt6-Qt6==6.10.0 +PyQt6_sip==13.10.2 +requests>=2.32.5 +sdbus==0.14.1 sdbus-networkmanager==2.0.0 typing==3.7.4.3 websocket-client==1.9.0 From a66c95b59316066e14bdeb10b39dc5a1667c8866 Mon Sep 17 00:00:00 2001 From: Guilherme Costa Date: Fri, 2 Jan 2026 15:58:15 +0000 Subject: [PATCH 12/19] networkwindo.py: between 5 and 25 show only one bar icon --- BlocksScreen/lib/panels/networkWindow.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/BlocksScreen/lib/panels/networkWindow.py b/BlocksScreen/lib/panels/networkWindow.py index 16d89c91..ed970ef8 100644 --- a/BlocksScreen/lib/panels/networkWindow.py +++ b/BlocksScreen/lib/panels/networkWindow.py @@ -4,16 +4,14 @@ import typing from functools import partial -from PyQt6 import QtCore, QtGui, QtWidgets -from PyQt6.QtCore import QRunnable, QThreadPool, QObject, pyqtSignal - from lib.network import SdbusNetworkManagerAsync -from lib.panels.widgets.popupDialogWidget import Popup -from lib.ui.wifiConnectivityWindow_ui import Ui_wifi_stacked_page from lib.panels.widgets.keyboardPage import CustomQwertyKeyboard from lib.panels.widgets.loadPage import LoadScreen +from lib.panels.widgets.popupDialogWidget import Popup +from lib.ui.wifiConnectivityWindow_ui import Ui_wifi_stacked_page 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") @@ -186,7 +184,7 @@ def __init__(self): def get_pixmap(self, signal: int, state: str) -> QtGui.QPixmap: """Return a QPixmap for the given signal (0-100) and state ("Protected" or not).""" - if signal <= 25: + if signal < 5: bars = 0 elif signal >= 75: bars = 4 From c1aa80f25417dfd643a170437236cce0d4fa413a Mon Sep 17 00:00:00 2001 From: Roberto Date: Wed, 14 Jan 2026 12:06:44 +0000 Subject: [PATCH 13/19] bugfix: fixed wrong imports --- BlocksScreen/lib/ui/wifiConnectivityWindow_ui.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/BlocksScreen/lib/ui/wifiConnectivityWindow_ui.py b/BlocksScreen/lib/ui/wifiConnectivityWindow_ui.py index 1af9f761..e66053a9 100644 --- a/BlocksScreen/lib/ui/wifiConnectivityWindow_ui.py +++ b/BlocksScreen/lib/ui/wifiConnectivityWindow_ui.py @@ -781,7 +781,7 @@ def setupUi(self, wifi_stacked_page): self.verticalLayout_17.addItem(spacerItem8) self.horizontalLayout_4 = QtWidgets.QHBoxLayout() self.horizontalLayout_4.setObjectName("horizontalLayout_4") - self.low_priority_btn = GroupButton(parent=self.frame_12) + 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) @@ -793,7 +793,7 @@ def setupUi(self, wifi_stacked_page): self.priority_btn_group.setObjectName("priority_btn_group") self.priority_btn_group.addButton(self.low_priority_btn) self.horizontalLayout_4.addWidget(self.low_priority_btn) - self.med_priority_btn = GroupButton(parent=self.frame_12) + 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) @@ -804,7 +804,7 @@ def setupUi(self, wifi_stacked_page): self.med_priority_btn.setObjectName("med_priority_btn") self.priority_btn_group.addButton(self.med_priority_btn) self.horizontalLayout_4.addWidget(self.med_priority_btn) - self.high_priority_btn = GroupButton(parent=self.frame_12) + 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) @@ -1227,5 +1227,5 @@ def retranslateUi(self, wifi_stacked_page): from lib.utils.blocks_frame import BlocksCustomFrame from lib.utils.blocks_linedit import BlocksCustomLinEdit from lib.utils.blocks_togglebutton import NetworkWidgetbuttons -from lib.utils.group_button import GroupButton +from lib.utils.check_button import BlocksCustomCheckButton from lib.utils.icon_button import IconButton From 393d384743decada8e8e76d64a12458dfa15d0ca Mon Sep 17 00:00:00 2001 From: Roberto Date: Wed, 14 Jan 2026 12:39:19 +0000 Subject: [PATCH 14/19] bugfix: wrong button name --- BlocksScreen/lib/panels/networkWindow.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/BlocksScreen/lib/panels/networkWindow.py b/BlocksScreen/lib/panels/networkWindow.py index 125d07ba..c15b4f75 100644 --- a/BlocksScreen/lib/panels/networkWindow.py +++ b/BlocksScreen/lib/panels/networkWindow.py @@ -874,12 +874,11 @@ def handle_button_click(self, ssid: str): priority = entry.get("priority") if priority == 90: - self.panel.hig_priorrity_btn.setChecked(True) + self.panel.high_priority_btn.setChecked(True) elif priority == 20: - self.panel.low_priorrity_btn.setChecked(True) + self.panel.low_priority_btn.setChecked(True) else: - self.panel.med_priorrity_btn.setChecked(True) - + 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) From 52f64f71fe17ef9b2ed8b79c3652a25a63f7a7f1 Mon Sep 17 00:00:00 2001 From: Guilherme Costa Date: Wed, 14 Jan 2026 17:06:36 +0000 Subject: [PATCH 15/19] resolve merge conflits --- BlocksScreen/lib/panels/networkWindow.py | 10 - .../lib/ui/resources/icon_resources_rc.py | 562 +++++++++--------- 2 files changed, 294 insertions(+), 278 deletions(-) diff --git a/BlocksScreen/lib/panels/networkWindow.py b/BlocksScreen/lib/panels/networkWindow.py index 4e78069e..61fcefdb 100644 --- a/BlocksScreen/lib/panels/networkWindow.py +++ b/BlocksScreen/lib/panels/networkWindow.py @@ -6,7 +6,6 @@ from lib.network import SdbusNetworkManagerAsync from lib.panels.widgets.keyboardPage import CustomQwertyKeyboard -from lib.panels.widgets.loadPage import LoadScreen from lib.panels.widgets.popupDialogWidget import Popup from lib.ui.wifiConnectivityWindow_ui import Ui_wifi_stacked_page from lib.utils.list_model import EntryDelegate, EntryListModel, ListItem @@ -235,7 +234,6 @@ def __init__(self, parent: typing.Optional[QtWidgets.QWidget], /) -> None: self.start: bool = True self.saved_network = {} - self.load_popup: LoadScreen = LoadScreen(self) self.repeated_request_status = QtCore.QTimer() self.repeated_request_status.setInterval(2000) @@ -469,8 +467,6 @@ def handle_update_end(self) -> None: """Handles update end signal (closes loading page, returns to normal operation) """ - if self.load_popup.isVisible(): - self.load_popup.close() self.repeated_request_status.stop() self.request_network_scan.emit() self.build_model_list() @@ -479,8 +475,6 @@ def handle_ongoing_update(self) -> None: """Handled ongoing update signal, calls loading page (blocks user interaction) """ - self.load_popup.set_status_message("Updating...") - self.load_popup.show() self.repeated_request_status.start(2000) def reset_view_model(self) -> None: @@ -980,10 +974,6 @@ def handle_network_list(self, data: typing.List[typing.Tuple]) -> None: for entry in data: if entry[0] == self.sdbus_network.hotspot_ssid: continue - if entry == "blank": - continue - if entry == "separator": - continue self.saved_network[entry[0]] = (entry[1], entry[2]) self.build_model_list() self.evaluate_network_state() diff --git a/BlocksScreen/lib/ui/resources/icon_resources_rc.py b/BlocksScreen/lib/ui/resources/icon_resources_rc.py index 9c88b518..a7aca514 100644 --- a/BlocksScreen/lib/ui/resources/icon_resources_rc.py +++ b/BlocksScreen/lib/ui/resources/icon_resources_rc.py @@ -27214,207 +27214,219 @@ " qt_resource_struct_v1 = b"\ -\x00\x00\x00\x00\x00\x02\x00\x00\x00\x0f\x00\x00\x00\x01\ -\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x95\ -\x00\x00\x00\x0a\x00\x02\x00\x00\x00\x01\x00\x00\x00\x92\ -\x00\x00\x00\x1a\x00\x02\x00\x00\x00\x01\x00\x00\x00\x86\ -\x00\x00\x00\x46\x00\x02\x00\x00\x00\x01\x00\x00\x00\x7b\ -\x00\x00\x00\x5a\x00\x02\x00\x00\x00\x01\x00\x00\x00\x77\ -\x00\x00\x00\x6c\x00\x02\x00\x00\x00\x01\x00\x00\x00\x71\ -\x00\x00\x00\x7e\x00\x02\x00\x00\x00\x01\x00\x00\x00\x63\ -\x00\x00\x00\x90\x00\x02\x00\x00\x00\x01\x00\x00\x00\x59\ -\x00\x00\x00\xa2\x00\x02\x00\x00\x00\x01\x00\x00\x00\x4a\ -\x00\x00\x00\xc0\x00\x02\x00\x00\x00\x01\x00\x00\x00\x45\ -\x00\x00\x00\xdc\x00\x02\x00\x00\x00\x01\x00\x00\x00\x35\ -\x00\x00\x01\x02\x00\x02\x00\x00\x00\x01\x00\x00\x00\x31\ -\x00\x00\x01\x2a\x00\x02\x00\x00\x00\x01\x00\x00\x00\x25\ -\x00\x00\x01\x50\x00\x02\x00\x00\x00\x01\x00\x00\x00\x1f\ -\x00\x00\x01\x6c\x00\x02\x00\x00\x00\x01\x00\x00\x00\x10\ -\x00\x00\x01\x88\x00\x02\x00\x00\x00\x01\x00\x00\x00\x11\ -\x00\x00\x01\x98\x00\x02\x00\x00\x00\x0d\x00\x00\x00\x12\ -\x00\x00\x01\xb0\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\ -\x00\x00\x01\xd0\x00\x00\x00\x00\x00\x01\x00\x00\x08\x66\ -\x00\x00\x01\xf8\x00\x00\x00\x00\x00\x01\x00\x00\x09\x52\ -\x00\x00\x02\x20\x00\x00\x00\x00\x00\x01\x00\x00\x0a\x35\ -\x00\x00\x02\x48\x00\x00\x00\x00\x00\x01\x00\x00\x0b\x18\ -\x00\x00\x02\x70\x00\x00\x00\x00\x00\x01\x00\x00\x0c\x06\ -\x00\x00\x02\xa4\x00\x00\x00\x00\x00\x01\x00\x00\x14\x42\ -\x00\x00\x02\xd0\x00\x00\x00\x00\x00\x01\x00\x00\x1d\xf7\ -\x00\x00\x02\xfa\x00\x00\x00\x00\x00\x01\x00\x00\x22\x41\ -\x00\x00\x03\x1a\x00\x00\x00\x00\x00\x01\x00\x00\x26\x90\ -\x00\x00\x03\x36\x00\x00\x00\x00\x00\x01\x00\x00\x2c\x6e\ -\x00\x00\x03\x52\x00\x00\x00\x00\x00\x01\x00\x00\x30\xca\ -\x00\x00\x03\x7a\x00\x00\x00\x00\x00\x01\x00\x00\x32\xb1\ -\x00\x00\x01\x88\x00\x02\x00\x00\x00\x01\x00\x00\x00\x20\ -\x00\x00\x01\x98\x00\x02\x00\x00\x00\x04\x00\x00\x00\x21\ -\x00\x00\x03\x9a\x00\x00\x00\x00\x00\x01\x00\x00\x3a\x55\ -\x00\x00\x03\xbe\x00\x00\x00\x00\x00\x01\x00\x00\x3c\xcf\ -\x00\x00\x03\xe0\x00\x00\x00\x00\x00\x01\x00\x00\x3f\x43\ -\x00\x00\x03\xfe\x00\x00\x00\x00\x00\x01\x00\x00\x41\xbf\ -\x00\x00\x01\x88\x00\x02\x00\x00\x00\x01\x00\x00\x00\x26\ -\x00\x00\x01\x98\x00\x02\x00\x00\x00\x0a\x00\x00\x00\x27\ -\x00\x00\x04\x20\x00\x00\x00\x00\x00\x01\x00\x00\x44\x3b\ -\x00\x00\x04\x3a\x00\x00\x00\x00\x00\x01\x00\x00\x45\x3c\ -\x00\x00\x04\x6a\x00\x00\x00\x00\x00\x01\x00\x00\x4a\x54\ -\x00\x00\x04\x9a\x00\x00\x00\x00\x00\x01\x00\x00\x56\x16\ -\x00\x00\x04\xc4\x00\x00\x00\x00\x00\x01\x00\x00\x58\x86\ -\x00\x00\x04\xea\x00\x00\x00\x00\x00\x01\x00\x00\x5e\x1e\ -\x00\x00\x05\x0e\x00\x00\x00\x00\x00\x01\x00\x00\x62\x7a\ -\x00\x00\x05\x48\x00\x00\x00\x00\x00\x01\x00\x00\x69\x07\ -\x00\x00\x05\x64\x00\x00\x00\x00\x00\x01\x00\x00\x6c\x03\ -\x00\x00\x05\x8c\x00\x00\x00\x00\x00\x01\x00\x00\x6d\x01\ -\x00\x00\x01\x88\x00\x02\x00\x00\x00\x01\x00\x00\x00\x32\ -\x00\x00\x01\x98\x00\x02\x00\x00\x00\x02\x00\x00\x00\x33\ -\x00\x00\x05\xbe\x00\x01\x00\x00\x00\x01\x00\x00\x77\x6b\ -\x00\x00\x05\xd0\x00\x00\x00\x00\x00\x01\x00\x02\x3a\xc0\ -\x00\x00\x01\x88\x00\x02\x00\x00\x00\x02\x00\x00\x00\x36\ -\x00\x00\x01\x98\x00\x02\x00\x00\x00\x06\x00\x00\x00\x3f\ -\x00\x00\x05\xe4\x00\x02\x00\x00\x00\x07\x00\x00\x00\x38\ -\x00\x00\x05\xf6\x00\x00\x00\x00\x00\x01\x00\x02\x3f\x46\ -\x00\x00\x06\x2a\x00\x00\x00\x00\x00\x01\x00\x02\x49\xaa\ -\x00\x00\x06\x5e\x00\x00\x00\x00\x00\x01\x00\x02\x53\xd3\ -\x00\x00\x06\x98\x00\x00\x00\x00\x00\x01\x00\x02\x5e\x2b\ -\x00\x00\x06\xcc\x00\x00\x00\x00\x00\x01\x00\x02\x68\x43\ -\x00\x00\x07\x02\x00\x00\x00\x00\x00\x01\x00\x02\x72\x6c\ -\x00\x00\x07\x3a\x00\x00\x00\x00\x00\x01\x00\x02\x7c\x82\ -\x00\x00\x07\x70\x00\x00\x00\x00\x00\x01\x00\x02\x86\xe8\ -\x00\x00\x07\x98\x00\x00\x00\x00\x00\x01\x00\x02\x91\x17\ -\x00\x00\x07\xc4\x00\x00\x00\x00\x00\x01\x00\x02\x9b\x5e\ -\x00\x00\x08\x00\x00\x00\x00\x00\x00\x01\x00\x02\x9d\x5f\ -\x00\x00\x08\x2c\x00\x00\x00\x00\x00\x01\x00\x02\xb8\xa4\ -\x00\x00\x08\x58\x00\x00\x00\x00\x00\x01\x00\x02\xbd\xed\ -\x00\x00\x01\x88\x00\x02\x00\x00\x00\x01\x00\x00\x00\x46\ -\x00\x00\x01\x98\x00\x02\x00\x00\x00\x03\x00\x00\x00\x47\ -\x00\x00\x08\x8c\x00\x00\x00\x00\x00\x01\x00\x02\xc1\x65\ -\x00\x00\x08\xaa\x00\x00\x00\x00\x00\x01\x00\x02\xca\x47\ -\x00\x00\x08\xbe\x00\x00\x00\x00\x00\x01\x00\x02\xcf\x7c\ -\x00\x00\x01\x88\x00\x02\x00\x00\x00\x01\x00\x00\x00\x4b\ -\x00\x00\x01\x98\x00\x02\x00\x00\x00\x0d\x00\x00\x00\x4c\ -\x00\x00\x08\xd8\x00\x00\x00\x00\x00\x01\x00\x02\xd9\x51\ -\x00\x00\x09\x00\x00\x00\x00\x00\x00\x01\x00\x02\xde\x4c\ -\x00\x00\x09\x38\x00\x00\x00\x00\x00\x01\x00\x02\xe7\x20\ -\x00\x00\x09\x70\x00\x00\x00\x00\x00\x01\x00\x02\xef\xc4\ -\x00\x00\x09\xa0\x00\x00\x00\x00\x00\x01\x00\x02\xf7\x63\ -\x00\x00\x09\xc4\x00\x00\x00\x00\x00\x01\x00\x02\xff\x3f\ -\x00\x00\x09\xe8\x00\x00\x00\x00\x00\x01\x00\x03\x06\xf5\ -\x00\x00\x0a\x1c\x00\x00\x00\x00\x00\x01\x00\x03\x0f\x03\ -\x00\x00\x0a\x50\x00\x00\x00\x00\x00\x01\x00\x03\x16\xe5\ -\x00\x00\x0a\x84\x00\x00\x00\x00\x00\x01\x00\x03\x1e\xaf\ -\x00\x00\x0a\xbe\x00\x00\x00\x00\x00\x01\x00\x03\x26\x16\ -\x00\x00\x0a\xe2\x00\x00\x00\x00\x00\x01\x00\x03\x29\xd3\ -\x00\x00\x0b\x10\x00\x00\x00\x00\x00\x01\x00\x03\x2d\xac\ -\x00\x00\x01\x88\x00\x02\x00\x00\x00\x01\x00\x00\x00\x5a\ -\x00\x00\x01\x98\x00\x02\x00\x00\x00\x08\x00\x00\x00\x5b\ -\x00\x00\x0b\x36\x00\x00\x00\x00\x00\x01\x00\x03\x33\xb5\ -\x00\x00\x0b\x62\x00\x00\x00\x00\x00\x01\x00\x03\x56\x39\ -\x00\x00\x0b\x90\x00\x00\x00\x00\x00\x01\x00\x03\x5c\x26\ -\x00\x00\x0b\xba\x00\x00\x00\x00\x00\x01\x00\x03\x5e\x46\ -\x00\x00\x0b\xe2\x00\x00\x00\x00\x00\x01\x00\x03\x66\xde\ -\x00\x00\x0b\xfc\x00\x00\x00\x00\x00\x01\x00\x03\x76\x27\ -\x00\x00\x0c\x28\x00\x00\x00\x00\x00\x01\x00\x03\x7c\xa5\ -\x00\x00\x0c\x50\x00\x00\x00\x00\x00\x01\x00\x03\x87\x1f\ -\x00\x00\x01\x88\x00\x02\x00\x00\x00\x01\x00\x00\x00\x64\ -\x00\x00\x01\x98\x00\x02\x00\x00\x00\x0c\x00\x00\x00\x65\ -\x00\x00\x0c\x86\x00\x00\x00\x00\x00\x01\x00\x03\x90\x66\ -\x00\x00\x0c\xb4\x00\x00\x00\x00\x00\x01\x00\x03\x93\x0d\ -\x00\x00\x0c\xe2\x00\x01\x00\x00\x00\x01\x00\x03\x9f\x8a\ -\x00\x00\x0d\x0e\x00\x00\x00\x00\x00\x01\x00\x03\xcd\x09\ -\x00\x00\x0d\x2e\x00\x00\x00\x00\x00\x01\x00\x03\xd1\xc0\ -\x00\x00\x0d\x60\x00\x01\x00\x00\x00\x01\x00\x04\x2a\xb9\ -\x00\x00\x0d\x92\x00\x00\x00\x00\x00\x01\x00\x04\x5f\x53\ -\x00\x00\x0d\xac\x00\x00\x00\x00\x00\x01\x00\x04\x64\x9d\ -\x00\x00\x0d\xc6\x00\x00\x00\x00\x00\x01\x00\x04\x6a\x2c\ -\x00\x00\x0d\xe0\x00\x00\x00\x00\x00\x01\x00\x04\x6f\x95\ -\x00\x00\x0d\xf8\x00\x00\x00\x00\x00\x01\x00\x04\x7b\x73\ -\x00\x00\x0e\x16\x00\x00\x00\x00\x00\x01\x00\x04\x81\x77\ -\x00\x00\x01\x88\x00\x02\x00\x00\x00\x01\x00\x00\x00\x72\ -\x00\x00\x01\x98\x00\x02\x00\x00\x00\x04\x00\x00\x00\x73\ -\x00\x00\x0e\x3e\x00\x00\x00\x00\x00\x01\x00\x04\x86\xac\ -\x00\x00\x0e\x52\x00\x00\x00\x00\x00\x01\x00\x04\x8c\xa9\ -\x00\x00\x0e\x64\x00\x00\x00\x00\x00\x01\x00\x04\x8e\x2f\ -\x00\x00\x0e\x76\x00\x00\x00\x00\x00\x01\x00\x04\x94\x29\ -\x00\x00\x01\x88\x00\x02\x00\x00\x00\x01\x00\x00\x00\x78\ -\x00\x00\x01\x98\x00\x02\x00\x00\x00\x02\x00\x00\x00\x79\ -\x00\x00\x0e\x8a\x00\x00\x00\x00\x00\x01\x00\x04\x96\x7f\ -\x00\x00\x0e\xb6\x00\x00\x00\x00\x00\x01\x00\x04\x9d\x66\ -\x00\x00\x01\x88\x00\x02\x00\x00\x00\x01\x00\x00\x00\x7c\ -\x00\x00\x01\x98\x00\x02\x00\x00\x00\x09\x00\x00\x00\x7d\ -\x00\x00\x0e\xda\x00\x00\x00\x00\x00\x01\x00\x04\xa6\xca\ -\x00\x00\x0e\xfa\x00\x01\x00\x00\x00\x01\x00\x04\xa9\x76\ -\x00\x00\x0f\x1e\x00\x00\x00\x00\x00\x01\x00\x04\xb4\xc7\ -\x00\x00\x0f\x40\x00\x00\x00\x00\x00\x01\x00\x04\xbc\x6c\ -\x00\x00\x0f\x5c\x00\x00\x00\x00\x00\x01\x00\x04\xcd\x6c\ -\x00\x00\x0f\x80\x00\x00\x00\x00\x00\x01\x00\x04\xd6\xed\ -\x00\x00\x0f\xa0\x00\x00\x00\x00\x00\x01\x00\x04\xdc\x8a\ -\x00\x00\x0f\xc8\x00\x00\x00\x00\x00\x01\x00\x04\xe6\x53\ -\x00\x00\x0f\xe8\x00\x00\x00\x00\x00\x01\x00\x04\xea\x36\ -\x00\x00\x01\x88\x00\x02\x00\x00\x00\x01\x00\x00\x00\x87\ -\x00\x00\x01\x98\x00\x02\x00\x00\x00\x0a\x00\x00\x00\x88\ -\x00\x00\x10\x04\x00\x00\x00\x00\x00\x01\x00\x04\xf0\x71\ -\x00\x00\x10\x34\x00\x00\x00\x00\x00\x01\x00\x04\xfa\x07\ -\x00\x00\x10\x64\x00\x00\x00\x00\x00\x01\x00\x05\x05\xe3\ -\x00\x00\x10\x88\x00\x00\x00\x00\x00\x01\x00\x05\x0c\x27\ -\x00\x00\x10\xb2\x00\x00\x00\x00\x00\x01\x00\x05\x13\xb0\ -\x00\x00\x10\xde\x00\x00\x00\x00\x00\x01\x00\x05\x1a\x0e\ -\x00\x00\x11\x14\x00\x00\x00\x00\x00\x01\x00\x05\x21\xfd\ -\x00\x00\x08\xaa\x00\x00\x00\x00\x00\x01\x00\x05\x2f\xa0\ -\x00\x00\x08\xbe\x00\x00\x00\x00\x00\x01\x00\x05\x34\xd5\ -\x00\x00\x11\x32\x00\x00\x00\x00\x00\x01\x00\x05\x3e\xaa\ -\x00\x00\x01\x88\x00\x02\x00\x00\x00\x01\x00\x00\x00\x93\ -\x00\x00\x01\x98\x00\x02\x00\x00\x00\x01\x00\x00\x00\x94\ -\x00\x00\x11\x5a\x00\x00\x00\x00\x00\x01\x00\x05\x46\x74\ -\x00\x00\x01\x88\x00\x02\x00\x00\x00\x01\x00\x00\x00\x96\ -\x00\x00\x01\x98\x00\x02\x00\x00\x00\x28\x00\x00\x00\x97\ -\x00\x00\x11\x7a\x00\x00\x00\x00\x00\x01\x00\x05\x4b\x3a\ -\x00\x00\x11\x90\x00\x00\x00\x00\x00\x01\x00\x05\x52\xee\ -\x00\x00\x11\xa8\x00\x00\x00\x00\x00\x01\x00\x05\x55\x13\ -\x00\x00\x11\xe0\x00\x00\x00\x00\x00\x01\x00\x05\x56\x93\ -\x00\x00\x11\xfc\x00\x00\x00\x00\x00\x01\x00\x05\x5e\x40\ -\x00\x00\x12\x1c\x00\x00\x00\x00\x00\x01\x00\x05\x63\x15\ -\x00\x00\x12\x32\x00\x00\x00\x00\x00\x01\x00\x05\x64\x05\ -\x00\x00\x12\x48\x00\x00\x00\x00\x00\x01\x00\x05\x66\xe9\ -\x00\x00\x12\x6e\x00\x00\x00\x00\x00\x01\x00\x05\x6d\x20\ -\x00\x00\x12\x88\x00\x00\x00\x00\x00\x01\x00\x05\x82\x2d\ -\x00\x00\x12\xaa\x00\x00\x00\x00\x00\x01\x00\x05\x87\x1d\ -\x00\x00\x12\xc0\x00\x00\x00\x00\x00\x01\x00\x05\x8a\x1c\ -\x00\x00\x12\xd6\x00\x00\x00\x00\x00\x01\x00\x05\x90\x26\ -\x00\x00\x13\x08\x00\x00\x00\x00\x00\x01\x00\x05\x93\x75\ -\x00\x00\x13\x20\x00\x00\x00\x00\x00\x01\x00\x05\x9c\xf6\ -\x00\x00\x13\x36\x00\x00\x00\x00\x00\x01\x00\x05\xa2\xed\ -\x00\x00\x13\x4a\x00\x00\x00\x00\x00\x01\x00\x05\xa4\xfe\ -\x00\x00\x13\x62\x00\x00\x00\x00\x00\x01\x00\x05\xa8\xc1\ -\x00\x00\x13\x88\x00\x00\x00\x00\x00\x01\x00\x05\xb2\x75\ -\x00\x00\x13\xb2\x00\x00\x00\x00\x00\x01\x00\x05\xb5\xc3\ -\x00\x00\x13\xd4\x00\x00\x00\x00\x00\x01\x00\x05\xb9\x51\ -\x00\x00\x13\xfa\x00\x00\x00\x00\x00\x01\x00\x05\xbd\xf2\ -\x00\x00\x14\x0e\x00\x00\x00\x00\x00\x01\x00\x05\xc7\xc4\ -\x00\x00\x14\x3a\x00\x00\x00\x00\x00\x01\x00\x05\xcd\x0e\ -\x00\x00\x14\x62\x00\x00\x00\x00\x00\x01\x00\x05\xd3\x15\ -\x00\x00\x14\x78\x00\x00\x00\x00\x00\x01\x00\x05\xd3\xf9\ -\x00\x00\x14\xa4\x00\x00\x00\x00\x00\x01\x00\x05\xd6\x42\ -\x00\x00\x14\xba\x00\x00\x00\x00\x00\x01\x00\x05\xdc\xf2\ -\x00\x00\x14\xd6\x00\x00\x00\x00\x00\x01\x00\x05\xe0\x36\ -\x00\x00\x14\xee\x00\x00\x00\x00\x00\x01\x00\x05\xe1\x62\ -\x00\x00\x15\x08\x00\x00\x00\x00\x00\x01\x00\x05\xe7\x23\ -\x00\x00\x15\x2a\x00\x00\x00\x00\x00\x01\x00\x05\xe8\x45\ -\x00\x00\x15\x48\x00\x00\x00\x00\x00\x01\x00\x05\xee\x38\ -\x00\x00\x15\x68\x00\x00\x00\x00\x00\x01\x00\x05\xf1\x3c\ -\x00\x00\x15\x8a\x00\x00\x00\x00\x00\x01\x00\x05\xf2\x5d\ -\x00\x00\x15\xaa\x00\x00\x00\x00\x00\x01\x00\x05\xf5\x31\ -\x00\x00\x15\xd8\x00\x00\x00\x00\x00\x01\x00\x05\xfd\x9d\ -\x00\x00\x15\xfc\x00\x00\x00\x00\x00\x01\x00\x06\x05\x5d\ -\x00\x00\x16\x20\x00\x00\x00\x00\x00\x01\x00\x06\x0a\x78\ -\x00\x00\x16\x48\x00\x00\x00\x00\x00\x01\x00\x06\x0b\xcb\ +\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\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\ +\x00\x00\x00\x7e\x00\x02\x00\x00\x00\x01\x00\x00\x00\x68\ +\x00\x00\x00\x90\x00\x02\x00\x00\x00\x01\x00\x00\x00\x5e\ +\x00\x00\x00\xa2\x00\x02\x00\x00\x00\x01\x00\x00\x00\x4f\ +\x00\x00\x00\xc0\x00\x02\x00\x00\x00\x01\x00\x00\x00\x4a\ +\x00\x00\x00\xdc\x00\x02\x00\x00\x00\x01\x00\x00\x00\x3a\ +\x00\x00\x01\x02\x00\x02\x00\x00\x00\x01\x00\x00\x00\x36\ +\x00\x00\x01\x1a\x00\x02\x00\x00\x00\x01\x00\x00\x00\x32\ +\x00\x00\x01\x42\x00\x02\x00\x00\x00\x01\x00\x00\x00\x26\ +\x00\x00\x01\x68\x00\x02\x00\x00\x00\x01\x00\x00\x00\x20\ +\x00\x00\x01\x84\x00\x02\x00\x00\x00\x01\x00\x00\x00\x11\ +\x00\x00\x01\xa0\x00\x02\x00\x00\x00\x01\x00\x00\x00\x12\ +\x00\x00\x01\xb0\x00\x02\x00\x00\x00\x0d\x00\x00\x00\x13\ +\x00\x00\x01\xc8\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\ +\x00\x00\x01\xe8\x00\x00\x00\x00\x00\x01\x00\x00\x08\x66\ +\x00\x00\x02\x10\x00\x00\x00\x00\x00\x01\x00\x00\x09\x52\ +\x00\x00\x02\x38\x00\x00\x00\x00\x00\x01\x00\x00\x0a\x35\ +\x00\x00\x02\x60\x00\x00\x00\x00\x00\x01\x00\x00\x0b\x18\ +\x00\x00\x02\x88\x00\x00\x00\x00\x00\x01\x00\x00\x0c\x06\ +\x00\x00\x02\xbc\x00\x00\x00\x00\x00\x01\x00\x00\x14\x42\ +\x00\x00\x02\xe8\x00\x00\x00\x00\x00\x01\x00\x00\x1d\xf7\ +\x00\x00\x03\x12\x00\x00\x00\x00\x00\x01\x00\x00\x22\x41\ +\x00\x00\x03\x32\x00\x00\x00\x00\x00\x01\x00\x00\x26\x90\ +\x00\x00\x03\x4e\x00\x00\x00\x00\x00\x01\x00\x00\x2c\x6e\ +\x00\x00\x03\x6a\x00\x00\x00\x00\x00\x01\x00\x00\x30\xca\ +\x00\x00\x03\x92\x00\x00\x00\x00\x00\x01\x00\x00\x32\xb1\ +\x00\x00\x01\xa0\x00\x02\x00\x00\x00\x01\x00\x00\x00\x21\ +\x00\x00\x01\xb0\x00\x02\x00\x00\x00\x04\x00\x00\x00\x22\ +\x00\x00\x03\xb2\x00\x00\x00\x00\x00\x01\x00\x00\x3a\x55\ +\x00\x00\x03\xd6\x00\x00\x00\x00\x00\x01\x00\x00\x3c\xcf\ +\x00\x00\x03\xf8\x00\x00\x00\x00\x00\x01\x00\x00\x3f\x43\ +\x00\x00\x04\x16\x00\x00\x00\x00\x00\x01\x00\x00\x41\xbf\ +\x00\x00\x01\xa0\x00\x02\x00\x00\x00\x01\x00\x00\x00\x27\ +\x00\x00\x01\xb0\x00\x02\x00\x00\x00\x0a\x00\x00\x00\x28\ +\x00\x00\x04\x38\x00\x00\x00\x00\x00\x01\x00\x00\x44\x3b\ +\x00\x00\x04\x52\x00\x00\x00\x00\x00\x01\x00\x00\x45\x3c\ +\x00\x00\x04\x82\x00\x00\x00\x00\x00\x01\x00\x00\x4a\x54\ +\x00\x00\x04\xb2\x00\x00\x00\x00\x00\x01\x00\x00\x56\x16\ +\x00\x00\x04\xdc\x00\x00\x00\x00\x00\x01\x00\x00\x58\x86\ +\x00\x00\x05\x02\x00\x00\x00\x00\x00\x01\x00\x00\x5e\x1e\ +\x00\x00\x05\x26\x00\x00\x00\x00\x00\x01\x00\x00\x62\x7a\ +\x00\x00\x05\x60\x00\x00\x00\x00\x00\x01\x00\x00\x69\x07\ +\x00\x00\x05\x7c\x00\x00\x00\x00\x00\x01\x00\x00\x6c\x03\ +\x00\x00\x05\xa4\x00\x00\x00\x00\x00\x01\x00\x00\x6d\x01\ +\x00\x00\x01\xa0\x00\x02\x00\x00\x00\x01\x00\x00\x00\x33\ +\x00\x00\x01\xb0\x00\x02\x00\x00\x00\x02\x00\x00\x00\x34\ +\x00\x00\x05\xd6\x00\x01\x00\x00\x00\x01\x00\x00\x77\x6b\ +\x00\x00\x05\xe8\x00\x00\x00\x00\x00\x01\x00\x02\x3a\xc0\ +\x00\x00\x01\xa0\x00\x02\x00\x00\x00\x01\x00\x00\x00\x37\ +\x00\x00\x01\xb0\x00\x02\x00\x00\x00\x02\x00\x00\x00\x38\ +\x00\x00\x05\xfc\x00\x00\x00\x00\x00\x01\x00\x02\x3f\x46\ +\x00\x00\x06\x2a\x00\x00\x00\x00\x00\x01\x00\x02\x41\x71\ +\x00\x00\x01\xa0\x00\x02\x00\x00\x00\x02\x00\x00\x00\x3b\ +\x00\x00\x01\xb0\x00\x02\x00\x00\x00\x06\x00\x00\x00\x44\ +\x00\x00\x06\x5a\x00\x02\x00\x00\x00\x07\x00\x00\x00\x3d\ +\x00\x00\x06\x6c\x00\x00\x00\x00\x00\x01\x00\x02\x43\x77\ +\x00\x00\x06\xa0\x00\x00\x00\x00\x00\x01\x00\x02\x4d\xdb\ +\x00\x00\x06\xd4\x00\x00\x00\x00\x00\x01\x00\x02\x58\x04\ +\x00\x00\x07\x0e\x00\x00\x00\x00\x00\x01\x00\x02\x62\x5c\ +\x00\x00\x07\x42\x00\x00\x00\x00\x00\x01\x00\x02\x6c\x74\ +\x00\x00\x07\x78\x00\x00\x00\x00\x00\x01\x00\x02\x76\x9d\ +\x00\x00\x07\xb0\x00\x00\x00\x00\x00\x01\x00\x02\x80\xb3\ +\x00\x00\x07\xe6\x00\x00\x00\x00\x00\x01\x00\x02\x8b\x19\ +\x00\x00\x08\x0e\x00\x00\x00\x00\x00\x01\x00\x02\x95\x48\ +\x00\x00\x08\x3a\x00\x00\x00\x00\x00\x01\x00\x02\x9f\x8f\ +\x00\x00\x08\x76\x00\x00\x00\x00\x00\x01\x00\x02\xa1\x90\ +\x00\x00\x08\xa2\x00\x00\x00\x00\x00\x01\x00\x02\xbc\xd5\ +\x00\x00\x08\xce\x00\x00\x00\x00\x00\x01\x00\x02\xc2\x1e\ +\x00\x00\x01\xa0\x00\x02\x00\x00\x00\x01\x00\x00\x00\x4b\ +\x00\x00\x01\xb0\x00\x02\x00\x00\x00\x03\x00\x00\x00\x4c\ +\x00\x00\x09\x02\x00\x00\x00\x00\x00\x01\x00\x02\xc5\x96\ +\x00\x00\x09\x20\x00\x00\x00\x00\x00\x01\x00\x02\xce\x78\ +\x00\x00\x09\x34\x00\x00\x00\x00\x00\x01\x00\x02\xd3\xad\ +\x00\x00\x01\xa0\x00\x02\x00\x00\x00\x01\x00\x00\x00\x50\ +\x00\x00\x01\xb0\x00\x02\x00\x00\x00\x0d\x00\x00\x00\x51\ +\x00\x00\x09\x4e\x00\x00\x00\x00\x00\x01\x00\x02\xdd\x82\ +\x00\x00\x09\x76\x00\x00\x00\x00\x00\x01\x00\x02\xe2\x7d\ +\x00\x00\x09\xae\x00\x00\x00\x00\x00\x01\x00\x02\xeb\x51\ +\x00\x00\x09\xe6\x00\x00\x00\x00\x00\x01\x00\x02\xf3\xf5\ +\x00\x00\x0a\x16\x00\x00\x00\x00\x00\x01\x00\x02\xfb\x94\ +\x00\x00\x0a\x3a\x00\x00\x00\x00\x00\x01\x00\x03\x03\x70\ +\x00\x00\x0a\x5e\x00\x00\x00\x00\x00\x01\x00\x03\x0b\x26\ +\x00\x00\x0a\x92\x00\x00\x00\x00\x00\x01\x00\x03\x13\x34\ +\x00\x00\x0a\xc6\x00\x00\x00\x00\x00\x01\x00\x03\x1b\x16\ +\x00\x00\x0a\xfa\x00\x00\x00\x00\x00\x01\x00\x03\x22\xe0\ +\x00\x00\x0b\x34\x00\x00\x00\x00\x00\x01\x00\x03\x2a\x47\ +\x00\x00\x0b\x58\x00\x00\x00\x00\x00\x01\x00\x03\x2e\x04\ +\x00\x00\x0b\x86\x00\x00\x00\x00\x00\x01\x00\x03\x31\xdd\ +\x00\x00\x01\xa0\x00\x02\x00\x00\x00\x01\x00\x00\x00\x5f\ +\x00\x00\x01\xb0\x00\x02\x00\x00\x00\x08\x00\x00\x00\x60\ +\x00\x00\x0b\xac\x00\x00\x00\x00\x00\x01\x00\x03\x37\xe6\ +\x00\x00\x0b\xd8\x00\x00\x00\x00\x00\x01\x00\x03\x5a\x6a\ +\x00\x00\x0c\x06\x00\x00\x00\x00\x00\x01\x00\x03\x60\x57\ +\x00\x00\x0c\x30\x00\x00\x00\x00\x00\x01\x00\x03\x62\x77\ +\x00\x00\x0c\x58\x00\x00\x00\x00\x00\x01\x00\x03\x6b\x0f\ +\x00\x00\x0c\x72\x00\x00\x00\x00\x00\x01\x00\x03\x7a\x58\ +\x00\x00\x0c\x9e\x00\x00\x00\x00\x00\x01\x00\x03\x80\xd6\ +\x00\x00\x0c\xc6\x00\x00\x00\x00\x00\x01\x00\x03\x8b\x50\ +\x00\x00\x01\xa0\x00\x02\x00\x00\x00\x01\x00\x00\x00\x69\ +\x00\x00\x01\xb0\x00\x02\x00\x00\x00\x0c\x00\x00\x00\x6a\ +\x00\x00\x0c\xfc\x00\x00\x00\x00\x00\x01\x00\x03\x94\x97\ +\x00\x00\x0d\x2a\x00\x00\x00\x00\x00\x01\x00\x03\x97\x3e\ +\x00\x00\x0d\x58\x00\x01\x00\x00\x00\x01\x00\x03\xa3\xbb\ +\x00\x00\x0d\x84\x00\x00\x00\x00\x00\x01\x00\x03\xd1\x3a\ +\x00\x00\x0d\xa4\x00\x00\x00\x00\x00\x01\x00\x03\xd5\xf1\ +\x00\x00\x0d\xd6\x00\x01\x00\x00\x00\x01\x00\x04\x2e\xea\ +\x00\x00\x0e\x08\x00\x00\x00\x00\x00\x01\x00\x04\x63\x84\ +\x00\x00\x0e\x22\x00\x00\x00\x00\x00\x01\x00\x04\x68\xce\ +\x00\x00\x0e\x3c\x00\x00\x00\x00\x00\x01\x00\x04\x6e\x5d\ +\x00\x00\x0e\x56\x00\x00\x00\x00\x00\x01\x00\x04\x73\xc6\ +\x00\x00\x0e\x6e\x00\x00\x00\x00\x00\x01\x00\x04\x7f\xa4\ +\x00\x00\x0e\x8c\x00\x00\x00\x00\x00\x01\x00\x04\x85\xa8\ +\x00\x00\x01\xa0\x00\x02\x00\x00\x00\x01\x00\x00\x00\x77\ +\x00\x00\x01\xb0\x00\x02\x00\x00\x00\x04\x00\x00\x00\x78\ +\x00\x00\x0e\xb4\x00\x00\x00\x00\x00\x01\x00\x04\x8a\xdd\ +\x00\x00\x0e\xc8\x00\x00\x00\x00\x00\x01\x00\x04\x90\xda\ +\x00\x00\x0e\xda\x00\x00\x00\x00\x00\x01\x00\x04\x92\x60\ +\x00\x00\x0e\xec\x00\x00\x00\x00\x00\x01\x00\x04\x98\x5a\ +\x00\x00\x01\xa0\x00\x02\x00\x00\x00\x01\x00\x00\x00\x7d\ +\x00\x00\x01\xb0\x00\x02\x00\x00\x00\x02\x00\x00\x00\x7e\ +\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\x10\x00\x00\x00\x82\ +\x00\x00\x0f\x50\x00\x00\x00\x00\x00\x01\x00\x04\xaa\xfb\ +\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\x95\ +\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\x92\ +\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\x86\ +\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\ @@ -27521,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\ @@ -27666,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\x98\x00\x02\x00\x00\x00\x09\x00\x00\x00\x7d\ +\x00\x00\x01\xb0\x00\x02\x00\x00\x00\x10\x00\x00\x00\x82\ \x00\x00\x00\x00\x00\x00\x00\x00\ -\x00\x00\x0e\xda\x00\x00\x00\x00\x00\x01\x00\x04\xa6\xca\ -\x00\x00\x01\x9b\x13\x3e\xbb\x62\ -\x00\x00\x0e\xfa\x00\x01\x00\x00\x00\x01\x00\x04\xa9\x76\ +\x00\x00\x0f\x50\x00\x00\x00\x00\x00\x01\x00\x04\xaa\xfb\ +\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\x1e\x00\x00\x00\x00\x00\x01\x00\x04\xb4\xc7\ +\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\x40\x00\x00\x00\x00\x00\x01\x00\x04\xbc\x6c\ +\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\x5c\x00\x00\x00\x00\x00\x01\x00\x04\xcd\x6c\ +\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\x80\x00\x00\x00\x00\x00\x01\x00\x04\xd6\xed\ -\x00\x00\x01\x9b\x13\x3e\xbb\x62\ -\x00\x00\x0f\xa0\x00\x00\x00\x00\x00\x01\x00\x04\xdc\x8a\ +\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\x0f\xc8\x00\x00\x00\x00\x00\x01\x00\x04\xe6\x53\ -\x00\x00\x01\x9b\x13\x3e\xbb\x62\ -\x00\x00\x0f\xe8\x00\x00\x00\x00\x00\x01\x00\x04\xea\x36\ +\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\x88\x00\x02\x00\x00\x00\x01\x00\x00\x00\x87\ +\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\x98\x00\x02\x00\x00\x00\x0a\x00\x00\x00\x88\ +\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\x04\x00\x00\x00\x00\x00\x01\x00\x04\xf0\x71\ +\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\x34\x00\x00\x00\x00\x00\x01\x00\x04\xfa\x07\ +\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\x64\x00\x00\x00\x00\x00\x01\x00\x05\x05\xe3\ +\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\x88\x00\x00\x00\x00\x00\x01\x00\x05\x0c\x27\ +\x00\x00\x12\x42\x00\x00\x00\x00\x00\x01\x00\x05\x55\xae\ \x00\x00\x01\x9a\x72\xe1\x94\x5b\ -\x00\x00\x10\xb2\x00\x00\x00\x00\x00\x01\x00\x05\x13\xb0\ +\x00\x00\x12\x6c\x00\x00\x00\x00\x00\x01\x00\x05\x5d\x37\ \x00\x00\x01\x9a\x72\xe1\x94\x53\ -\x00\x00\x10\xde\x00\x00\x00\x00\x00\x01\x00\x05\x1a\x0e\ +\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\x14\x00\x00\x00\x00\x00\x01\x00\x05\x21\xfd\ +\x00\x00\x12\xce\x00\x00\x00\x00\x00\x01\x00\x05\x6b\x84\ \x00\x00\x01\x9a\x72\xe1\x94\x4f\ -\x00\x00\x08\xaa\x00\x00\x00\x00\x00\x01\x00\x05\x2f\xa0\ +\x00\x00\x09\x20\x00\x00\x00\x00\x00\x01\x00\x05\x79\x27\ \x00\x00\x01\x9a\x72\xe1\x94\x4f\ -\x00\x00\x08\xbe\x00\x00\x00\x00\x00\x01\x00\x05\x34\xd5\ +\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\x32\x00\x00\x00\x00\x00\x01\x00\x05\x3e\xaa\ +\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\x88\x00\x02\x00\x00\x00\x01\x00\x00\x00\x93\ +\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\x98\x00\x02\x00\x00\x00\x01\x00\x00\x00\x94\ +\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\x5a\x00\x00\x00\x00\x00\x01\x00\x05\x46\x74\ +\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\x88\x00\x02\x00\x00\x00\x01\x00\x00\x00\x96\ +\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\x98\x00\x02\x00\x00\x00\x28\x00\x00\x00\x97\ +\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\x7a\x00\x00\x00\x00\x00\x01\x00\x05\x4b\x3a\ +\x00\x00\x13\x34\x00\x00\x00\x00\x00\x01\x00\x05\x94\xc1\ \x00\x00\x01\x9a\x72\xe1\x94\x5b\ -\x00\x00\x11\x90\x00\x00\x00\x00\x00\x01\x00\x05\x52\xee\ +\x00\x00\x13\x4a\x00\x00\x00\x00\x00\x01\x00\x05\x9c\x75\ \x00\x00\x01\x9a\x72\xe1\x94\x53\ -\x00\x00\x11\xa8\x00\x00\x00\x00\x00\x01\x00\x05\x55\x13\ +\x00\x00\x13\x62\x00\x00\x00\x00\x00\x01\x00\x05\x9e\x9a\ \x00\x00\x01\x9a\x72\xe1\x94\x53\ -\x00\x00\x11\xe0\x00\x00\x00\x00\x00\x01\x00\x05\x56\x93\ +\x00\x00\x13\x9a\x00\x00\x00\x00\x00\x01\x00\x05\xa0\x1a\ \x00\x00\x01\x9a\x72\xe1\x94\x57\ -\x00\x00\x11\xfc\x00\x00\x00\x00\x00\x01\x00\x05\x5e\x40\ +\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\x1c\x00\x00\x00\x00\x00\x01\x00\x05\x63\x15\ +\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\x32\x00\x00\x00\x00\x00\x01\x00\x05\x64\x05\ -\x00\x00\x01\x9a\x72\xe1\x94\x53\ -\x00\x00\x12\x48\x00\x00\x00\x00\x00\x01\x00\x05\x66\xe9\ +\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\x6e\x00\x00\x00\x00\x00\x01\x00\x05\x6d\x20\ +\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\x88\x00\x00\x00\x00\x00\x01\x00\x05\x82\x2d\ +\x00\x00\x14\x42\x00\x00\x00\x00\x00\x01\x00\x05\xcc\xf9\ \x00\x00\x01\x9a\x72\xe1\x94\x4f\ -\x00\x00\x12\xaa\x00\x00\x00\x00\x00\x01\x00\x05\x87\x1d\ +\x00\x00\x14\x64\x00\x00\x00\x00\x00\x01\x00\x05\xd1\xe9\ \x00\x00\x01\x9a\x72\xe1\x94\x4b\ -\x00\x00\x12\xc0\x00\x00\x00\x00\x00\x01\x00\x05\x8a\x1c\ +\x00\x00\x14\x7a\x00\x00\x00\x00\x00\x01\x00\x05\xd4\xe8\ \x00\x00\x01\x9a\x72\xe1\x94\x57\ -\x00\x00\x12\xd6\x00\x00\x00\x00\x00\x01\x00\x05\x90\x26\ -\x00\x00\x01\x9a\x72\xe1\x94\x4f\ -\x00\x00\x13\x08\x00\x00\x00\x00\x00\x01\x00\x05\x93\x75\ +\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\x20\x00\x00\x00\x00\x00\x01\x00\x05\x9c\xf6\ +\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\x36\x00\x00\x00\x00\x00\x01\x00\x05\xa2\xed\ +\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\x4a\x00\x00\x00\x00\x00\x01\x00\x05\xa4\xfe\ +\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\x62\x00\x00\x00\x00\x00\x01\x00\x05\xa8\xc1\ +\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\x88\x00\x00\x00\x00\x00\x01\x00\x05\xb2\x75\ +\x00\x00\x15\x42\x00\x00\x00\x00\x00\x01\x00\x05\xf7\xe1\ \x00\x00\x01\x9a\x72\xe1\x94\x57\ -\x00\x00\x13\xb2\x00\x00\x00\x00\x00\x01\x00\x05\xb5\xc3\ +\x00\x00\x15\x6c\x00\x00\x00\x00\x00\x01\x00\x05\xfb\x2f\ \x00\x00\x01\x9a\x72\xe1\x94\x57\ -\x00\x00\x13\xd4\x00\x00\x00\x00\x00\x01\x00\x05\xb9\x51\ +\x00\x00\x15\x8e\x00\x00\x00\x00\x00\x01\x00\x05\xfe\xbd\ \x00\x00\x01\x9a\x72\xe1\x94\x4f\ -\x00\x00\x13\xfa\x00\x00\x00\x00\x00\x01\x00\x05\xbd\xf2\ +\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\x0e\x00\x00\x00\x00\x00\x01\x00\x05\xc7\xc4\ +\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\x3a\x00\x00\x00\x00\x00\x01\x00\x05\xcd\x0e\ +\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\x62\x00\x00\x00\x00\x00\x01\x00\x05\xd3\x15\ +\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\x78\x00\x00\x00\x00\x00\x01\x00\x05\xd3\xf9\ +\x00\x00\x16\x32\x00\x00\x00\x00\x00\x01\x00\x06\x19\x65\ \x00\x00\x01\x9a\x72\xe1\x94\x4f\ -\x00\x00\x14\xa4\x00\x00\x00\x00\x00\x01\x00\x05\xd6\x42\ +\x00\x00\x16\x5e\x00\x00\x00\x00\x00\x01\x00\x06\x1b\xae\ \x00\x00\x01\x9a\x72\xe1\x94\x5b\ -\x00\x00\x14\xba\x00\x00\x00\x00\x00\x01\x00\x05\xdc\xf2\ +\x00\x00\x16\x74\x00\x00\x00\x00\x00\x01\x00\x06\x22\x5e\ \x00\x00\x01\x9a\x72\xe1\x94\x57\ -\x00\x00\x14\xd6\x00\x00\x00\x00\x00\x01\x00\x05\xe0\x36\ +\x00\x00\x16\x90\x00\x00\x00\x00\x00\x01\x00\x06\x25\xa2\ \x00\x00\x01\x9a\x72\xe1\x94\x53\ -\x00\x00\x14\xee\x00\x00\x00\x00\x00\x01\x00\x05\xe1\x62\ +\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\x08\x00\x00\x00\x00\x00\x01\x00\x05\xe7\x23\ +\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\x2a\x00\x00\x00\x00\x00\x01\x00\x05\xe8\x45\ +\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\x48\x00\x00\x00\x00\x00\x01\x00\x05\xee\x38\ +\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\x68\x00\x00\x00\x00\x00\x01\x00\x05\xf1\x3c\ +\x00\x00\x17\x22\x00\x00\x00\x00\x00\x01\x00\x06\x36\xa8\ \x00\x00\x01\x9a\x72\xe1\x94\x57\ -\x00\x00\x15\x8a\x00\x00\x00\x00\x00\x01\x00\x05\xf2\x5d\ +\x00\x00\x17\x44\x00\x00\x00\x00\x00\x01\x00\x06\x37\xc9\ \x00\x00\x01\x9a\x72\xe1\x94\x57\ -\x00\x00\x15\xaa\x00\x00\x00\x00\x00\x01\x00\x05\xf5\x31\ +\x00\x00\x17\x64\x00\x00\x00\x00\x00\x01\x00\x06\x3a\x9d\ \x00\x00\x01\x9a\x72\xe1\x94\x53\ -\x00\x00\x15\xd8\x00\x00\x00\x00\x00\x01\x00\x05\xfd\x9d\ +\x00\x00\x17\x92\x00\x00\x00\x00\x00\x01\x00\x06\x43\x09\ \x00\x00\x01\x9a\x72\xe1\x94\x4f\ -\x00\x00\x15\xfc\x00\x00\x00\x00\x00\x01\x00\x06\x05\x5d\ +\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\x20\x00\x00\x00\x00\x00\x01\x00\x06\x0a\x78\ +\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\x48\x00\x00\x00\x00\x00\x01\x00\x06\x0b\xcb\ +\x00\x00\x18\x02\x00\x00\x00\x00\x00\x01\x00\x06\x51\x37\ \x00\x00\x01\x9a\x72\xe1\x94\x4b\ " From 97a881e5fe8408a06f324c12d6a5dfde7b428e1e Mon Sep 17 00:00:00 2001 From: Guilherme Costa Date: Wed, 14 Jan 2026 17:41:36 +0000 Subject: [PATCH 16/19] add missing docstrings --- BlocksScreen/lib/panels/networkWindow.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/BlocksScreen/lib/panels/networkWindow.py b/BlocksScreen/lib/panels/networkWindow.py index 61fcefdb..df9e39cc 100644 --- a/BlocksScreen/lib/panels/networkWindow.py +++ b/BlocksScreen/lib/panels/networkWindow.py @@ -124,26 +124,33 @@ def __init__(self, poll_interval_ms: int = 10000): self._timer.timeout.connect(self._do_scan) def start_polling(self): + """Schedule scan folowwing the `poll_interval_ms`""" self._schedule_next_scan() def stop_polling(self): + """Start Polling for future development""" self._timer.stop() def build(self): + """Method called by rescan button""" self._do_scan() def _schedule_next_scan(self): + """Start next rescan timer""" self._timer.start(self.poll_interval_ms) def _on_task_finished(self, items): + """Update data after scan finish, update saved networks list and schedule next scan""" self._is_scanning = False self.finished_network_list_build.emit(items) self._schedule_next_scan() def _on_task_scan_results(self, data_dict): + """Emit results to main thread""" self.scan_results.emit(data_dict) def _on_task_error(self, err): + """Emit errors to main thread""" self._is_scanning = False self.error.emit(err) self._schedule_next_scan() From 58152951c0c2e07ae19a55402d1004a5e0d3f058 Mon Sep 17 00:00:00 2001 From: Guilherme Costa Date: Wed, 14 Jan 2026 17:44:20 +0000 Subject: [PATCH 17/19] add missing docstrings --- BlocksScreen/lib/panels/networkWindow.py | 1 + 1 file changed, 1 insertion(+) diff --git a/BlocksScreen/lib/panels/networkWindow.py b/BlocksScreen/lib/panels/networkWindow.py index df9e39cc..3c190928 100644 --- a/BlocksScreen/lib/panels/networkWindow.py +++ b/BlocksScreen/lib/panels/networkWindow.py @@ -156,6 +156,7 @@ def _on_task_error(self, err): self._schedule_next_scan() def _do_scan(self): + """Start wifi scanning threadpool""" if self._is_scanning: logger.debug("Already scanning, skip scheduling.") self._schedule_next_scan() From 21cae0204bef141e28c12729de82d1f9475fd010 Mon Sep 17 00:00:00 2001 From: Guilherme Costa Date: Fri, 16 Jan 2026 18:29:39 +0000 Subject: [PATCH 18/19] separation between saved and unsaved network and update code --- BlocksScreen/lib/panels/networkWindow.py | 223 +++++++++++++---------- BlocksScreen/lib/utils/list_model.py | 9 + 2 files changed, 135 insertions(+), 97 deletions(-) diff --git a/BlocksScreen/lib/panels/networkWindow.py b/BlocksScreen/lib/panels/networkWindow.py index 1e2dc0e9..0f72c869 100644 --- a/BlocksScreen/lib/panels/networkWindow.py +++ b/BlocksScreen/lib/panels/networkWindow.py @@ -8,6 +8,7 @@ from lib.panels.widgets.keyboardPage import CustomQwertyKeyboard from lib.panels.widgets.popupDialogWidget import Popup from lib.ui.wifiConnectivityWindow_ui import Ui_wifi_stacked_page +from lib.utils.list_model import EntryDelegate, EntryListModel, ListItem from PyQt6 import QtCore, QtGui, QtWidgets from PyQt6.QtCore import QObject, QRunnable, QThreadPool, pyqtSignal @@ -79,6 +80,8 @@ def run(self): for ssid, sig in saved_nets: status = "Active" if ssid == self.nm.get_current_ssid() else "Saved" items.append((ssid, sig, status)) + if saved_nets: + items.append(("separator", -10, "separator")) for ssid, sig in unsaved_nets: items.append((ssid, sig, "Protected")) @@ -207,7 +210,7 @@ def get_pixmap(self, signal: int, state: str) -> QtGui.QPixmap: path = self.paths.get(key) if path is None: logger.debug( - f"No icon path for key {key}, falling back to no-signal unprotected" + "No icon path for key %s, falling back to no-signal unprotected", key ) path = self.paths[(0, False)] @@ -241,9 +244,12 @@ def __init__(self, parent: typing.Optional[QtWidgets.QWidget], /) -> None: self.start: bool = True self.saved_network = {} - self.repeated_request_status = QtCore.QTimer() + self.repeated_request_status = QtCore.QTimer(self) self.repeated_request_status.setInterval(2000) + self._delayed_action_timer = QtCore.QTimer(self) + self._delayed_action_timer.setSingleShot(True) + self._load_timer = QtCore.QTimer() self._load_timer.setSingleShot(True) self._load_timer.timeout.connect(self._handle_load_timeout) @@ -287,9 +293,6 @@ def __init__(self, parent: typing.Optional[QtWidgets.QWidget], /) -> None: ) self.panel.network_backButton.clicked.connect(self.hide) - self.panel.rescan_button.clicked.connect( - lambda: self.sdbus_network.rescan_networks() - ) self.request_network_scan.connect(self.rescan_networks) self.panel.add_network_validation_button.clicked.connect(self.add_network) @@ -423,12 +426,8 @@ def __init__(self, parent: typing.Optional[QtWidgets.QWidget], /) -> None: lambda: self.setCurrentIndex(self.indexOf(self.panel.saved_details_page)) ) - self.panel.network_activate_btn.clicked.connect( - lambda: self.saved_wifi_option_selected() - ) - self.panel.network_delete_btn.clicked.connect( - lambda: self.saved_wifi_option_selected() - ) + self.panel.network_activate_btn.clicked.connect(self.saved_wifi_option_selected) + self.panel.network_delete_btn.clicked.connect(self.saved_wifi_option_selected) self.network_list_worker.build() self.request_network_scan.emit() @@ -491,49 +490,63 @@ def reset_view_model(self) -> None: self.model.clear() self.entry_delegate.clear() - def deleteLater(self) -> None: - """Schedule the object for deletion, resets the list model first""" - self.reset_view_model() - return super().deleteLater() + def closeEvent(self, event: QtGui.QShowEvent | None) -> None: + """Clean up resources before closing it""" + if hasattr(self, "_load_timer") and self._load_timer.isActive(): + self._load_timer.stop() - def showEvent(self, event: QtGui.QShowEvent | None) -> None: - """Re-add clients to update list""" - if event.type() in ( - QtCore.QEvent.Type.TouchBegin, - QtCore.QEvent.Type.TouchUpdate, - QtCore.QEvent.Type.TouchEnd, + if ( + hasattr(self, "repeated_request_status") + and self.repeated_request_status.isActive() ): - return True + self.repeated_request_status.stop() + if ( + hasattr(self, "_delayed_action_timer") + and self._delayed_action_timer.isActive() + ): + self._delayed_action_timer.stop() + super().closeEvent(event) + + def showEvent(self, event: QtGui.QShowEvent | None) -> None: + """Re-add clients to update list""" self.build_model_list() + self.evaluate_network_state() return super().showEvent(event) def build_model_list(self) -> None: """Builds the model list (`self.model`) containing updatable clients""" self.panel.listView.blockSignals(True) + saved_networks: dict = copy.deepcopy(self.saved_network) self.reset_view_model() - saved_networks: dict = copy.copy(self.saved_network) - if saved_networks.items(): - for ssid, (signal, is_saved) in saved_networks.items(): - self.add_network_entry(ssid=ssid, signal=signal, is_saved=is_saved) - self._setup_scrollbar() - + networks = list(saved_networks.items()) + for ssid, (signal, is_saved) in networks: + self.add_network_entry(ssid=ssid, signal=signal, is_saved=is_saved) + self._setup_scrollbar() self.panel.listView.blockSignals(False) 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 - ) - self.panel.hotspot_button.toggle_button.state = ( - self.panel.hotspot_button.toggle_button.State.OFF + + wifi_toggle = self.panel.wifi_button.toggle_button + hotspot_toggle = self.panel.hotspot_button.toggle_button + + blockers = ( + QtCore.QSignalBlocker(wifi_toggle), + QtCore.QSignalBlocker(hotspot_toggle), ) + wifi_toggle.state = wifi_toggle.State.ON + hotspot_toggle.state = hotspot_toggle.State.OFF + if _sender == self.panel.network_delete_btn: - self.sdbus_network.delete_network( - self.panel.saved_connection_network_name.text() - ) + ssid_to_delete = self.panel.saved_connection_network_name.text() + self.sdbus_network.delete_network(ssid_to_delete) + if ssid_to_delete in self.saved_network: + del self.saved_network[ssid_to_delete] + self.build_model_list() + self.evaluate_network_state() self.setCurrentIndex(self.indexOf(self.panel.main_network_page)) elif _sender == self.panel.network_activate_btn: @@ -626,9 +639,8 @@ def on_toggle_state(self, new_state) -> None: wifi_btn = self.panel.wifi_button.toggle_button hotspot_btn = self.panel.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() + saved_network = self.sdbus_network.get_saved_networks_with_for() if sender_button is wifi_btn: if is_sender_now_on: @@ -652,6 +664,7 @@ def on_toggle_state(self, new_state) -> None: elif sender_button is hotspot_btn: if is_sender_now_on: wifi_btn.state = wifi_btn.State.OFF + _old_hotspot = None for n in saved_network: if "ap" in n.get("mode", ""): @@ -696,6 +709,7 @@ def evaluate_network_state(self, nm_state: str = "") -> None: wifi_btn = self.panel.wifi_button.toggle_button hotspot_btn = self.panel.hotspot_button.toggle_button _nm_state = nm_state + _old_hotspot = None if not _nm_state: _nm_state = self.sdbus_network.check_nm_state() @@ -704,7 +718,7 @@ def evaluate_network_state(self, nm_state: str = "") -> None: if self.start: self.start = False - saved_network = self.sdbus_network.get_saved_networks() + saved_network = self.sdbus_network.get_saved_networks_with_for() for n in saved_network: if "ap" in n.get("mode", ""): _old_hotspot = n @@ -747,25 +761,30 @@ def evaluate_network_state(self, nm_state: str = "") -> None: 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 - ) + sec_type = "--" + signal_strength = "--" + curr_ip_text = "No IP Address" + curr_status = "Disconnected" + if current_ssid != "": + sec_type = self.sdbus_network.get_security_type_by_ssid(current_ssid) + signal_strength = self.sdbus_network.get_connection_signal_by_ssid( + current_ssid + ) + curr_ip_text = f"IP: {ipv4_addr}" + curr_status = "Connected" 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_ip.setText(curr_ip_text) + self.panel.netlist_security.setText(str(sec_type).upper()) self.panel.netlist_strength.setText( str(signal_strength if signal_strength != -1 else "--") ) - self.panel.mn_info_box.setText("Connected") + self.panel.mn_info_box.setText(curr_status) self._expand_infobox(False) self.info_box_load(False) self.panel.wifi_button.setEnabled(True) self.panel.hotspot_button.setEnabled(True) - self.repaint() + self.update() if ( wifi_btn.state == wifi_btn.State.OFF @@ -879,7 +898,7 @@ def add_network(self) -> None: """ self.panel.add_network_validation_button.setEnabled(False) - self.panel.add_network_validation_button.repaint() + self.panel.add_network_validation_button.update() if not self.panel.add_network_password_field.text(): self.popup.new_message( @@ -896,7 +915,7 @@ def add_network(self) -> None: error_msg = result.get("error", "") self.panel.add_network_password_field.clear() if not error_msg: - QtCore.QTimer().singleShot(5000, self.network_list_worker.build) + self._schedule_delayed_action(self.network_list_worker.build, 5000) QtCore.QTimer().singleShot( 5000, lambda: self.sdbus_network.connect_network( @@ -910,7 +929,7 @@ def add_network(self) -> None: self.panel.wifi_button.setEnabled(False) self.panel.hotspot_button.setEnabled(False) - self.panel.add_network_validation_button.repaint() + self.panel.add_network_validation_button.update() return if error_msg == "Invalid password": @@ -922,27 +941,18 @@ def add_network(self) -> None: 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.panel.add_network_validation_button.update() self.popup.new_message(message_type=Popup.MessageType.ERROR, message=message) - @QtCore.pyqtSlot(ListItem, name="ssid_item_clicked") - def ssid_item_clicked(self, item: ListItem) -> None: - """Handles when a network is clicked on the QListWidget. - - Args: - item (QListWidgetItem): The list entry that was clicked - """ - if not item: - return + def _schedule_delayed_action(self, callback, delay_ms: int) -> None: + """Schedule a reschedule timer""" + try: + self._delayed_action_timer.timeout.disconnect() + except TypeError: + pass - _current_ssid_name = item.text - self.selected_item = copy.copy(item) - if _current_ssid_name in self.sdbus_network.get_saved_ssid_names(): - self.setCurrentIndex(self.indexOf(self.panel.saved_connection_page)) - self.panel.saved_connection_network_name.setText(str(_current_ssid_name)) - else: - self.setCurrentIndex(self.indexOf(self.panel.add_network_page)) - self.panel.add_network_network_label.setText(str(_current_ssid_name)) + self._delayed_action_timer.timeout.connect(callback) + self._delayed_action_timer.start(delay_ms) def update_network( self, @@ -965,23 +975,30 @@ def update_network( 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._schedule_delayed_action(self.network_list_worker.build, 10000) self.setCurrentIndex(self.indexOf(self.panel.network_list_page)) @QtCore.pyqtSlot(list, name="finished-network-list-build") def handle_network_list(self, data: typing.List[typing.Tuple]) -> None: """Handle available network list update""" + self.saved_network.clear() for entry in data: if entry[0] == self.sdbus_network.hotspot_ssid: continue self.saved_network[entry[0]] = (entry[1], entry[2]) - self.build_model_list() self.evaluate_network_state() - QtCore.QTimer().singleShot(10000, lambda: self.network_list_worker.build()) + self.build_model_list() + self._schedule_delayed_action(self.network_list_worker.build, 10000) + + @QtCore.pyqtSlot(ListItem, name="ssid_item_clicked") + def ssid_item_clicked(self, item: ListItem) -> None: + """Handles when a network is clicked on the QListWidget. - def handle_button_click(self, ssid: str): - """Handles pressing a network""" - _saved_ssids = self.sdbus_network.get_saved_networks() + Args: + item (QListWidgetItem): The list entry that was clicked + """ + ssid = item.text + _saved_ssids = self.sdbus_network.get_saved_networks_with_for() 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)) @@ -1038,11 +1055,6 @@ def setCurrentIndex(self, index: int): _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() ) @@ -1052,7 +1064,10 @@ def setCurrentIndex(self, index: int): self.panel.saved_connection_signal_strength_info_frame.setText( _signal_string ) - self.update() + self.panel.saved_connection_security_type_info_label.setText( + str(_security_type or "--").upper() + ) + self.repaint() super().setCurrentIndex(index) def setProperty(self, name: str, value: typing.Any) -> bool: @@ -1080,25 +1095,39 @@ def show_network_panel( _parent_size = self.parent().size() # type: ignore self.setGeometry(0, 0, _parent_size.width(), _parent_size.height()) self.updateGeometry() - self.update() + self.repaint() self.show() def add_network_entry(self, ssid: str, signal: int, is_saved: str) -> None: """Adds a new item to the list model""" - - wifi_pixmap = self._provider.get_pixmap(signal=signal, state=is_saved) - ssid = ssid if ssid != "" else "UNKNOWN" - item = ListItem( - text=ssid, - left_icon=wifi_pixmap, - right_text=is_saved, - right_icon=self.right_icon, - selected=False, - allow_check=False, - _lfontsize=17, - _rfontsize=12, - height=80, - ) + if signal != -10: + wifi_pixmap = self._provider.get_pixmap(signal=signal, state=is_saved) + ssid = ssid if ssid != "" else "UNKNOWN" + item = ListItem( + text=ssid, + left_icon=wifi_pixmap, + right_text=is_saved, + right_icon=self.right_icon, + selected=False, + allow_check=False, + _lfontsize=17, + _rfontsize=12, + height=80, + not_clickable=False, + ) + else: + item = ListItem( + text="", + left_icon=None, + right_text="", + right_icon=None, + selected=False, + allow_check=False, + _lfontsize=17, + _rfontsize=12, + height=80, + not_clickable=True, + ) self.model.add_item(item) def _handle_scrollbar(self, value): 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() From a904b85bc6865d2b926f4b5a224935704cbe0e08 Mon Sep 17 00:00:00 2001 From: Guilherme Costa Date: Wed, 21 Jan 2026 16:32:24 +0000 Subject: [PATCH 19/19] refactor network window file --- BlocksScreen/lib/panels/networkWindow.py | 1935 +++++++++++----------- 1 file changed, 1007 insertions(+), 928 deletions(-) diff --git a/BlocksScreen/lib/panels/networkWindow.py b/BlocksScreen/lib/panels/networkWindow.py index 0f72c869..2b64cf5a 100644 --- a/BlocksScreen/lib/panels/networkWindow.py +++ b/BlocksScreen/lib/panels/networkWindow.py @@ -1,9 +1,17 @@ -import copy import logging -import subprocess # nosec: B404 -import typing +import socket +import threading from functools import partial - +from typing import ( + Any, + Callable, + Dict, + List, + NamedTuple, + Optional, +) + +import psutil from lib.network import SdbusNetworkManagerAsync from lib.panels.widgets.keyboardPage import CustomQwertyKeyboard from lib.panels.widgets.popupDialogWidget import Popup @@ -15,1166 +23,1237 @@ logger = logging.getLogger("logs/BlocksScreen.log") -class NetworkScanRunnable(QRunnable): - """QRunnable task that performs network scanning using SdbusNetworkManagerAsync +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 + +SIGNAL_EXCELLENT_THRESHOLD = 75 +SIGNAL_GOOD_THRESHOLD = 50 +SIGNAL_FAIR_THRESHOLD = 25 +SIGNAL_MINIMUM_THRESHOLD = 5 + +PRIORITY_HIGH = 90 +PRIORITY_MEDIUM = 50 +PRIORITY_LOW = 20 + +SEPARATOR_SIGNAL_VALUE = -10 +PRIVACY_BIT = 1 + - This runnable: - - Triggers a network rescan via SdbusNetworkManagerAsync - - collects SSIDs, signal strenght and saved status - - emits signal with raw scan data and a processed lisgs +class NetworkInfo(NamedTuple): + signal: int + status: str + is_open: bool = False + is_saved: bool = False - Signals: - - scan_results (dict): Emitted with raw scan results mapping SSID to properties - - finished_network_list_build (list): Emitted with processed list of networks - - error (str): Emitted if an error occurs during scanning - """ +class NetworkScanResult(NamedTuple): + ssid: str + signal: int + status: str + is_open: bool = False + + +class NetworkScanRunnable(QRunnable): class Signals(QObject): scan_results = pyqtSignal(dict, name="scan-results") - finished_network_list_build = pyqtSignal( - list, name="finished-network-list-build" - ) + finished_network_list_build = pyqtSignal(list, name="finished-network-list-build") error = pyqtSignal(str) - def __init__(self): + def __init__(self, nm: SdbusNetworkManagerAsync) -> None: super().__init__() - self.nm = SdbusNetworkManagerAsync() + self._nm = nm self.signals = NetworkScanRunnable.Signals() - def run(self): + def run(self) -> None: try: - logger.debug("NetworkScanRunnable: scanning networks") - self.nm.rescan_networks() - saved = self.nm.get_saved_ssid_names() - available = ( - self.nm.get_available_networks() - if self.nm.check_wifi_interface() - else {} - ) - - data_dict: dict[str, dict] = {} - for ssid, props in available.items(): - signal = int(props.get("signal_level", 0)) - data_dict[ssid] = { - "signal_level": signal, - "is_saved": ssid in saved, - } - + 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: list[typing.Union[tuple[str, int, str], str]] = [] - saved_nets = [ - (ssid, info["signal_level"]) - for ssid, info in data_dict.items() - if info["is_saved"] - ] - unsaved_nets = [ - (ssid, info["signal_level"]) - 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]) - - for ssid, sig in saved_nets: - status = "Active" if ssid == self.nm.get_current_ssid() else "Saved" - items.append((ssid, sig, status)) - if saved_nets: - items.append(("separator", -10, "separator")) - - for ssid, sig in unsaved_nets: - items.append((ssid, sig, "Protected")) - + 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]: + if self._nm.check_wifi_interface(): + return self._nm.get_available_networks() or {} + return {} -class BuildNetworkList(QtCore.QObject): - """ - Controller class that schedules and manages repeted network scans - - Uses a QThreadPool to un NetworkScanRunnable tasks periodically. with a QTimer to trigger scans. - Prevents overlapping scans by tracking whether a scan is already in progress. + def _build_data_dict( + self, + available: Dict[str, Dict], + saved_ssids: List[str] + ) -> Dict[str, Dict]: + 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 + data_dict[ssid] = { + "signal_level": signal, + "is_saved": ssid in saved_ssids, + "is_open": is_open, + } + return data_dict + + def _build_network_list(self, data_dict: Dict[str, Dict]) -> List[tuple]: + current_ssid = self._nm.get_current_ssid() + + saved_nets = [ + (ssid, info["signal_level"], info["is_open"]) + for ssid, info in data_dict.items() + if info["is_saved"] + ] + unsaved_nets = [ + (ssid, info["signal_level"], info["is_open"]) + 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 in saved_nets: + status = "Active" if ssid == current_ssid else "Saved" + items.append((ssid, signal, status, is_open, True)) + + for ssid, signal, is_open in unsaved_nets: + status = "Open" if is_open else "Protected" + items.append((ssid, signal, status, is_open, False)) + + return items - Args: - poll_interval_ms: (int) Milliseconds between scans (default: 10000) - _timer (QtCore.QTimer): Timer that schedules next scan - _is_scanning (bool): Flag indicating if a scan is currently in progress - Signals: - scan_results (dict): Emitted with raw scan results mapping SSID to properties - finished_network_list_build (list): Emitted with processed list of networks - error (str): Emitted if an error occurs during scanning - """ +class BuildNetworkList(QtCore.QObject): scan_results = pyqtSignal(dict, name="scan-results") finished_network_list_build = pyqtSignal(list, name="finished-network-list-build") error = pyqtSignal(str) - def __init__(self, poll_interval_ms: int = 10000): + def __init__( + self, + nm: SdbusNetworkManagerAsync, + poll_interval_ms: int = DEFAULT_POLL_INTERVAL_MS, + ) -> None: super().__init__() - self.threadpool = QThreadPool.globalInstance() - self.poll_interval_ms = poll_interval_ms + 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): - """Schedule scan folowwing the `poll_interval_ms`""" + def start_polling(self) -> None: self._schedule_next_scan() - def stop_polling(self): - """Start Polling for future development""" + def stop_polling(self) -> None: self._timer.stop() - def build(self): - """Method called by rescan button""" + def build(self) -> None: self._do_scan() - def _schedule_next_scan(self): - """Start next rescan timer""" - self._timer.start(self.poll_interval_ms) + def _schedule_next_scan(self) -> None: + self._timer.start(self._poll_interval_ms) - def _on_task_finished(self, items): - """Update data after scan finish, update saved networks list and schedule next scan""" - self._is_scanning = False + def _on_task_finished(self, items: List) -> None: + 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): - """Emit results to main thread""" + def _on_task_scan_results(self, data_dict: Dict) -> None: self.scan_results.emit(data_dict) - def _on_task_error(self, err): - """Emit errors to main thread""" - self._is_scanning = False + def _on_task_error(self, err: str) -> None: + with self._scan_lock: + self._is_scanning = False self.error.emit(err) self._schedule_next_scan() - def _do_scan(self): - """Start wifi scanning threadpool""" - if self._is_scanning: - logger.debug("Already scanning, skip scheduling.") - self._schedule_next_scan() - return + def _do_scan(self) -> None: + with self._scan_lock: + if self._is_scanning: + return + self._is_scanning = True - self._is_scanning = True - task = NetworkScanRunnable() + 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) - logger.debug("Submitted scan task to thread pool") + self._threadpool.start(task) class WifiIconProvider: - """Simple provider: loads QPixmap for WiFi bars + protection without caching.""" - def __init__(self): - self.paths = { + def __init__(self) -> None: + self._paths = { (0, False): ":/network/media/btn_icons/0bar_wifi.svg", - (4, False): ":/network/media/btn_icons/4bar_wifi.svg", - (3, False): ":/network/media/btn_icons/3bar_wifi.svg", - (2, False): ":/network/media/btn_icons/2bar_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", - (4, True): ":/network/media/btn_icons/4bar_wifi_protected.svg", - (3, True): ":/network/media/btn_icons/3bar_wifi_protected.svg", - (2, True): ":/network/media/btn_icons/2bar_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, state: str) -> QtGui.QPixmap: - """Return a QPixmap for the given signal (0-100) and state ("Protected" or not).""" - if signal < 5: - bars = 0 - elif signal >= 75: - bars = 4 - elif signal >= 50: - bars = 3 - elif signal > 25: - bars = 2 - else: - bars = 1 - - is_protected = state == "Protected" + def get_pixmap(self, signal: int, status: str) -> QtGui.QPixmap: + bars = self._signal_to_bars(signal) + is_protected = status == "Protected" key = (bars, is_protected) - - path = self.paths.get(key) - if path is None: - logger.debug( - "No icon path for key %s, falling back to no-signal unprotected", key - ) - path = self.paths[(0, False)] - - pm = QtGui.QPixmap(path) - if pm.isNull(): - logger.debug(f"Failed to load pixmap from '{path}' for key {key}") - return pm + path = self._paths.get(key, self._paths[(0, False)]) + return QtGui.QPixmap(path) + + @staticmethod + def _signal_to_bars(signal: int) -> int: + 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: - if parent: - super().__init__(parent) - else: - super().__init__() - - self.panel = Ui_wifi_stacked_page() - self.panel.setupUi(self) - self._provider = WifiIconProvider() - self.ongoing_update: bool = False - - self.popup = Popup(self) - self.sdbus_network = SdbusNetworkManagerAsync() - self.start: bool = True - self.saved_network = {} + 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: + super().__init__(parent) if parent else super().__init__() + + self._init_instance_variables() + self._init_ui() + 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() + self._set_loading_state(False) + + def _init_instance_variables(self) -> None: + 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._cached_hotspot_ip = "" + self._current_network_is_open = False + + def _init_ui(self) -> None: + self._panel = Ui_wifi_stacked_page() + self._panel.setupUi(self) + self._popup = Popup(self) + self._sdbus_network = SdbusNetworkManagerAsync() + self._right_arrow_icon = QtGui.QPixmap( + ":/arrow_icons/media/btn_icons/right_arrow.svg" + ) - self.repeated_request_status = QtCore.QTimer(self) - self.repeated_request_status.setInterval(2000) + def _init_timers(self) -> None: + 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._load_timer = QtCore.QTimer(self) self._load_timer.setSingleShot(True) self._load_timer.timeout.connect(self._handle_load_timeout) - self.model = EntryListModel() - self.model.setParent(self.panel.listView) - self.entry_delegate = EntryDelegate() - self.panel.listView.setModel(self.model) - self.panel.listView.setItemDelegate(self.entry_delegate) - self.entry_delegate.item_selected.connect(self.ssid_item_clicked) - - self.build_network_list() - self.network_list_worker = BuildNetworkList() - self.network_list_worker.finished_network_list_build.connect( - self.handle_network_list - ) - self.network_list_worker.start_polling() - self.panel.rescan_button.clicked.connect(self.network_list_worker.build) - - 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)) + def _init_model_view(self) -> None: + self._model = EntryListModel() + self._model.setParent(self._panel.listView) + self._entry_delegate = EntryDelegate() + self._panel.listView.setModel(self._model) + self._panel.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: + self._network_list_worker = BuildNetworkList( + nm=self._sdbus_network, + poll_interval_ms=DEFAULT_POLL_INTERVAL_MS ) - self.panel.hotspot_button.clicked.connect( - partial(self.setCurrentIndex, self.indexOf(self.panel.hotspot_page)) + self._network_list_worker.finished_network_list_build.connect( + self._handle_network_list ) + self._network_list_worker.start_polling() + self._panel.rescan_button.clicked.connect(self._network_list_worker.build) - self.panel.hotspot_button.setPixmap( - QtGui.QPixmap(":/network/media/btn_icons/hotspot.svg") + def _setup_navigation_signals(self) -> None: + self._panel.wifi_button.clicked.connect( + partial(self.setCurrentIndex, self.indexOf(self._panel.network_list_page)) ) - self.panel.wifi_button.setPixmap( - QtGui.QPixmap(":/network/media/btn_icons/wifi_config.svg") + self._panel.hotspot_button.clicked.connect( + partial(self.setCurrentIndex, self.indexOf(self._panel.hotspot_page)) ) - self.right_icon = QtGui.QPixmap(":/arrow_icons/media/btn_icons/right_arrow.svg") - - self.panel.nl_back_button.clicked.connect( - partial( - self.setCurrentIndex, - self.indexOf(self.panel.main_network_page), - ) + self._panel.nl_back_button.clicked.connect( + partial(self.setCurrentIndex, self.indexOf(self._panel.main_network_page)) ) + self._panel.network_backButton.clicked.connect(self.hide) - self.panel.network_backButton.clicked.connect(self.hide) - - 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), - ) - ) - self.panel.add_network_password_view.pressed.connect( - partial( - self.panel.add_network_password_field.setEchoMode, - QtWidgets.QLineEdit.EchoMode.Normal, - ) - ) - self.panel.add_network_password_view.released.connect( - partial( - self.panel.add_network_password_field.setEchoMode, - QtWidgets.QLineEdit.EchoMode.Password, - ) + self._panel.add_network_page_backButton.clicked.connect( + partial(self.setCurrentIndex, self.indexOf(self._panel.network_list_page)) ) - self.panel.saved_connection_back_button.clicked.connect( - partial( - self.setCurrentIndex, - self.indexOf(self.panel.network_list_page), - ) - ) - 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, - ) + self._panel.saved_connection_back_button.clicked.connect( + partial(self.setCurrentIndex, self.indexOf(self._panel.network_list_page)) ) - self.panel.saved_connection_change_password_view.pressed.connect( - partial( - self.panel.saved_connection_change_password_field.setEchoMode, - QtWidgets.QLineEdit.EchoMode.Normal, - ) + self._panel.snd_back.clicked.connect( + partial(self.setCurrentIndex, self.indexOf(self._panel.saved_connection_page)) ) - self.panel.saved_connection_change_password_view.released.connect( - partial( - self.panel.saved_connection_change_password_field.setEchoMode, - QtWidgets.QLineEdit.EchoMode.Password, - ) - ) - self.panel.hotspot_back_button.clicked.connect( - lambda: self.setCurrentIndex(self.indexOf(self.panel.main_network_page)) + self._panel.network_details_btn.clicked.connect( + partial(self.setCurrentIndex, self.indexOf(self._panel.saved_details_page)) ) - self.panel.hotspot_password_input_field.setPlaceholderText( - "Defaults to: 123456789" + self._panel.hotspot_back_button.clicked.connect( + partial(self.setCurrentIndex, self.indexOf(self._panel.main_network_page)) ) - self.panel.hotspot_change_confirm.clicked.connect( - lambda: self.setCurrentIndex(self.indexOf(self.panel.main_network_page)) + self._panel.hotspot_change_confirm.clicked.connect( + partial(self.setCurrentIndex, self.indexOf(self._panel.main_network_page)) ) - 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.panel.hotspot_password_view_button.released.connect( - partial(self.panel.hotspot_password_input_field.setHidden, True) - ) - self.panel.hotspot_name_input_field.setText( - str(self.sdbus_network.get_hotspot_ssid()) - ) - self.panel.hotspot_password_input_field.setText( - str(self.sdbus_network.hotspot_password) + def _setup_action_signals(self) -> None: + 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._panel.add_network_validation_button.clicked.connect(self._add_network) + + self._panel.snd_back.clicked.connect(self._on_save_network_settings) + self._panel.network_activate_btn.clicked.connect(self._on_saved_wifi_option_selected) + self._panel.network_delete_btn.clicked.connect(self._on_saved_wifi_option_selected) + + def _setup_toggle_signals(self) -> None: + self._panel.wifi_button.toggle_button.stateChange.connect(self._on_toggle_state) + self._panel.hotspot_button.toggle_button.stateChange.connect(self._on_toggle_state) + + def _setup_password_visibility_signals(self) -> None: + self._setup_password_visibility_toggle( + self._panel.add_network_password_view, + self._panel.add_network_password_field, ) - 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._setup_password_visibility_toggle( + self._panel.saved_connection_change_password_view, + self._panel.saved_connection_change_password_field, ) - 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._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.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") - ) + self._panel.hotspot_password_view_button.released.connect( + partial(self._panel.hotspot_password_input_field.setHidden, True) ) - self.panel.add_network_password_view.released.connect( - lambda: self.panel.add_network_password_view.setPixmap( - QtGui.QPixmap(":/ui/media/btn_icons/unsee.svg") - ) + + see_icon = QtGui.QPixmap(":/ui/media/btn_icons/see.svg") + unsee_icon = QtGui.QPixmap(":/ui/media/btn_icons/unsee.svg") + self._panel.hotspot_password_view_button.pressed.connect( + lambda: self._panel.hotspot_password_view_button.setPixmap(see_icon) ) - 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._panel.hotspot_password_view_button.released.connect( + lambda: self._panel.hotspot_password_view_button.setPixmap(unsee_icon) ) - self.panel.hotspot_password_view_button.released.connect( - lambda: self.panel.hotspot_password_view_button.setPixmap( - QtGui.QPixmap(":/ui/media/btn_icons/unsee.svg") - ) + + def _setup_password_visibility_toggle( + self, + view_button: QtWidgets.QWidget, + password_field: QtWidgets.QLineEdit + ) -> None: + see_icon = QtGui.QPixmap(":/ui/media/btn_icons/see.svg") + unsee_icon = QtGui.QPixmap(":/ui/media/btn_icons/unsee.svg") + + view_button.pressed.connect( + lambda: password_field.setEchoMode(QtWidgets.QLineEdit.EchoMode.Normal) ) - self.panel.hotspot_password_view_button.pressed.connect( - lambda: self.panel.hotspot_password_view_button.setPixmap( - QtGui.QPixmap(":/ui/media/btn_icons/see.svg") - ) + view_button.released.connect( + lambda: password_field.setEchoMode(QtWidgets.QLineEdit.EchoMode.Password) ) + view_button.pressed.connect(lambda: view_button.setPixmap(see_icon)) + view_button.released.connect(lambda: view_button.setPixmap(unsee_icon)) - self.panel.add_network_password_field.setCursor( - QtCore.Qt.CursorShape.BlankCursor + def _setup_icons(self) -> None: + self._panel.hotspot_button.setPixmap( + QtGui.QPixmap(":/network/media/btn_icons/hotspot.svg") ) - self.panel.hotspot_name_input_field.setCursor(QtCore.Qt.CursorShape.BlankCursor) - self.panel.hotspot_password_input_field.setCursor( - QtCore.Qt.CursorShape.BlankCursor + self._panel.wifi_button.setPixmap( + QtGui.QPixmap(":/network/media/btn_icons/wifi_config.svg") ) - self.panel.network_delete_btn.setPixmap( + self._panel.network_delete_btn.setPixmap( QtGui.QPixmap(":/ui/media/btn_icons/garbage-icon.svg") ) - - self.panel.network_activate_btn.setPixmap( + self._panel.network_activate_btn.setPixmap( QtGui.QPixmap(":/dialog/media/btn_icons/yes.svg") ) - - self.panel.network_details_btn.setPixmap( + self._panel.network_details_btn.setPixmap( QtGui.QPixmap(":/ui/media/btn_icons/printer_settings.svg") ) - self.panel.snd_back.clicked.connect( - lambda: self.setCurrentIndex(self.indexOf(self.panel.saved_connection_page)) - ) - self.panel.network_details_btn.clicked.connect( - lambda: self.setCurrentIndex(self.indexOf(self.panel.saved_details_page)) - ) + def _setup_input_fields(self) -> None: + self._panel.add_network_password_field.setCursor(QtCore.Qt.CursorShape.BlankCursor) + self._panel.hotspot_name_input_field.setCursor(QtCore.Qt.CursorShape.BlankCursor) + self._panel.hotspot_password_input_field.setCursor(QtCore.Qt.CursorShape.BlankCursor) - self.panel.network_activate_btn.clicked.connect(self.saved_wifi_option_selected) - self.panel.network_delete_btn.clicked.connect(self.saved_wifi_option_selected) - - self.network_list_worker.build() - self.request_network_scan.emit() - self.panel.listView.verticalScrollBar().valueChanged.connect( - self._handle_scrollbar + self._panel.hotspot_password_input_field.setPlaceholderText("Defaults to: 123456789") + self._panel.hotspot_name_input_field.setText( + str(self._sdbus_network.get_hotspot_ssid()) ) - self.panel.verticalScrollBar.valueChanged.connect(self._handle_scrollbar) - self.panel.verticalScrollBar.valueChanged.connect( - lambda value: self.panel.listView.verticalScrollBar().setValue(value) + self._panel.hotspot_password_input_field.setText( + str(self._sdbus_network.hotspot_password) ) - self.panel.verticalScrollBar.show() - self.hide() - self.info_box_load() - 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) + def _setup_keyboard(self) -> None: + 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.panel.add_network_password_field.clicked.connect( - lambda: self.on_show_keyboard( - self.panel.add_network_page, self.panel.add_network_password_field + self._panel.add_network_password_field.clicked.connect( + lambda: self._on_show_keyboard( + self._panel.add_network_page, + self._panel.add_network_password_field ) ) - self.panel.hotspot_password_input_field.clicked.connect( - lambda: self.on_show_keyboard( - self.panel.hotspot_page, self.panel.hotspot_password_input_field + self._panel.hotspot_password_input_field.clicked.connect( + lambda: self._on_show_keyboard( + self._panel.hotspot_page, + self._panel.hotspot_password_input_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._panel.hotspot_name_input_field.clicked.connect( + lambda: self._on_show_keyboard( + self._panel.hotspot_page, + self._panel.hotspot_name_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._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, ) ) - def handle_update_end(self) -> None: - """Handles update end signal - (closes loading page, returns to normal operation) - """ - self.repeated_request_status.stop() - self.request_network_scan.emit() - self.build_model_list() - - def handle_ongoing_update(self) -> None: - """Handled ongoing update signal, - calls loading page (blocks user interaction) - """ - self.repeated_request_status.start(2000) - - def reset_view_model(self) -> None: - """Clears items from ListView - (Resets `QAbstractListModel` by clearing entries) - """ - self.model.clear() - self.entry_delegate.clear() - - def closeEvent(self, event: QtGui.QShowEvent | None) -> None: - """Clean up resources before closing it""" - if hasattr(self, "_load_timer") and self._load_timer.isActive(): - self._load_timer.stop() + def _setup_scrollbar_signals(self) -> None: + self._panel.listView.verticalScrollBar().valueChanged.connect( + self._handle_scrollbar_change + ) + self._panel.verticalScrollBar.valueChanged.connect(self._handle_scrollbar_change) + self._panel.verticalScrollBar.valueChanged.connect( + lambda value: self._panel.listView.verticalScrollBar().setValue(value) + ) + self._panel.verticalScrollBar.show() - if ( - hasattr(self, "repeated_request_status") - and self.repeated_request_status.isActive() - ): - self.repeated_request_status.stop() + def _configure_list_view_palette(self) -> None: + palette = QtGui.QPalette() - if ( - hasattr(self, "_delayed_action_timer") - and self._delayed_action_timer.isActive() - ): - self._delayed_action_timer.stop() - super().closeEvent(event) + 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._panel.listView.setPalette(palette) + + def _show_error_popup(self, message: str, timeout: int = 6000) -> None: + self._popup.new_message( + message_type=Popup.MessageType.ERROR, + message=message, + timeout=timeout, + userInput=False, + ) - def showEvent(self, event: QtGui.QShowEvent | None) -> None: - """Re-add clients to update list""" - self.build_model_list() - self.evaluate_network_state() - return super().showEvent(event) - - def build_model_list(self) -> None: - """Builds the model list (`self.model`) containing updatable clients""" - self.panel.listView.blockSignals(True) - saved_networks: dict = copy.deepcopy(self.saved_network) - self.reset_view_model() - networks = list(saved_networks.items()) - for ssid, (signal, is_saved) in networks: - self.add_network_entry(ssid=ssid, signal=signal, is_saved=is_saved) - self._setup_scrollbar() - self.panel.listView.blockSignals(False) - - def saved_wifi_option_selected(self): - """Handle connect/delete network button clicks""" - _sender = self.sender() - - wifi_toggle = self.panel.wifi_button.toggle_button - hotspot_toggle = self.panel.hotspot_button.toggle_button - - blockers = ( - QtCore.QSignalBlocker(wifi_toggle), - QtCore.QSignalBlocker(hotspot_toggle), + def _show_info_popup(self, message: str, timeout: int = 4000) -> None: + self._popup.new_message( + message_type=Popup.MessageType.INFO, + message=message, + timeout=timeout, + userInput=False, ) - wifi_toggle.state = wifi_toggle.State.ON - hotspot_toggle.state = hotspot_toggle.State.OFF - - if _sender == self.panel.network_delete_btn: - ssid_to_delete = self.panel.saved_connection_network_name.text() - self.sdbus_network.delete_network(ssid_to_delete) - if ssid_to_delete in self.saved_network: - del self.saved_network[ssid_to_delete] - self.build_model_list() - self.evaluate_network_state() - self.setCurrentIndex(self.indexOf(self.panel.main_network_page)) - - 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. - """ - self._show_loadscreen(toggle) + def _show_warning_popup(self, message: str, timeout: int = 5000) -> None: + self._popup.new_message( + message_type=Popup.MessageType.WARNING, + message=message, + timeout=timeout, + userInput=False, + ) - self.panel.wifi_button.setEnabled(not toggle) - self.panel.hotspot_button.setEnabled(not toggle) + def closeEvent(self, event: Optional[QtGui.QCloseEvent]) -> None: + self._stop_all_timers() + self._network_list_worker.stop_polling() + super().closeEvent(event) - if toggle: + def showEvent(self, event: Optional[QtGui.QShowEvent]) -> None: + if self._networks: + self._build_model_list() + self._evaluate_network_state() + super().showEvent(event) + + def _stop_all_timers(self) -> None: + 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: + 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: + if self._previous_panel: + self.setCurrentIndex(self.indexOf(self._previous_panel)) + + def _on_qwerty_value_selected(self, value: str) -> None: + 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: + self._set_info_panel_visibility(show_details=not loading, show_loading=loading) + self._panel.wifi_button.setEnabled(not loading) + self._panel.hotspot_button.setEnabled(not loading) + + if loading: + if self._load_timer.isActive(): + self._load_timer.stop() + self._load_timer.start(LOAD_TIMEOUT_MS) + else: if self._load_timer.isActive(): self._load_timer.stop() - self._load_timer.start(30000) - 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 + def _handle_load_timeout(self) -> None: + if not self._panel.loadingwidget.isVisible(): + return - 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._panel.wifi_button + hotspot_btn = self._panel.hotspot_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." - else: - message = "Loading timed out.\n Please check your connection \n and try again." + message = self._get_timeout_message(wifi_btn, hotspot_btn) - self.panel.mn_info_box.setText(message) - self._show_loadscreen(False) - self._expand_infobox(True) + self._panel.mn_info_box.setText(message) + self._set_info_panel_visibility(show_details=False, show_loading=False) + self._configure_info_box_centered() 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.panel.line_3.setVisible(not toggle) - self.panel.netlist_security.setVisible(not toggle) - self.panel.netlist_security_label.setVisible(not toggle) - - self.panel.mn_info_box.setVisible(not toggle) + def _get_timeout_message(self, wifi_btn, hotspot_btn) -> str: + if wifi_btn.toggle_button.state == wifi_btn.toggle_button.State.ON: + return ( + "Wi-Fi Connection Failed.\nThe connection attempt timed out." + ) + elif hotspot_btn.toggle_button.state == hotspot_btn.toggle_button.State.ON: + return ( + "Hotspot Setup Failed.\n" + "The local network sharing timed out.\n" + "Please restart the hotspot." + ) + else: + return "Loading timed out.\nPlease check your connection and try again." - self.panel.loadingwidget.setVisible(toggle) + def _set_info_panel_visibility( + self, + show_details: bool, + show_loading: bool = False + ) -> None: + self._panel.netlist_ip.setVisible(show_details) + self._panel.netlist_ssuid.setVisible(show_details) + self._panel.mn_info_seperator.setVisible(show_details) + self._panel.line_2.setVisible(show_details) + self._panel.netlist_strength.setVisible(show_details) + self._panel.netlist_strength_label.setVisible(show_details) + self._panel.line_3.setVisible(show_details) + self._panel.netlist_security.setVisible(show_details) + self._panel.netlist_security_label.setVisible(show_details) + self._panel.mn_info_box.setVisible(not show_loading) + self._panel.loadingwidget.setVisible(show_loading) + + def _configure_info_box_centered(self) -> None: + self._panel.mn_info_box.setWordWrap(True) + self._panel.mn_info_box.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) @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: sender_button = self.sender() - wifi_btn = self.panel.wifi_button.toggle_button - hotspot_btn = self.panel.hotspot_button.toggle_button + wifi_btn = self._panel.wifi_button.toggle_button + hotspot_btn = self._panel.hotspot_button.toggle_button is_sender_now_on = new_state == sender_button.State.ON - saved_network = self.sdbus_network.get_saved_networks_with_for() + saved_networks = self._sdbus_network.get_saved_networks_with_for() 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}") - + 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 - _old_hotspot = None - - 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._set_loading_state(False) - self.info_box_load(False) if ( hotspot_btn.state == hotspot_btn.State.OFF and wifi_btn.state == wifi_btn.State.OFF ): - self.evaluate_network_state() + self._evaluate_network_state() else: - self.info_box_load(True) - - @QtCore.pyqtSlot(str, name="nm-state-changed") - def evaluate_network_state(self, nm_state: str = "") -> None: - """Handles or Reloads network state + self._set_loading_state(True) - 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 + def _handle_wifi_toggle( + self, + is_on: bool, + hotspot_btn, + saved_networks: List[Dict] + ) -> None: + if not is_on: + return - wifi_btn = self.panel.wifi_button.toggle_button - hotspot_btn = self.panel.hotspot_button.toggle_button - _nm_state = nm_state - _old_hotspot = None + hotspot_btn.state = hotspot_btn.State.OFF + self._sdbus_network.toggle_hotspot(False) + self._clear_hotspot_ip_cache() - if not _nm_state: - _nm_state = self.sdbus_network.check_nm_state() - if not _nm_state: - return + if not saved_networks: + return - if self.start: - self.start = False - saved_network = self.sdbus_network.get_saved_networks_with_for() - 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 - ) + try: + ssid = next( + ( + n["ssid"] + for n in saved_networks + if "ap" not in n.get("mode", "") and n.get("signal", 0) != 0 + ), + None, + ) + if ssid: + self._sdbus_network.connect_network(str(ssid)) + except Exception as e: + logger.error("Error when turning ON wifi: %s",e) - if not self.sdbus_network.check_wifi_interface(): + def _handle_hotspot_toggle( + self, + is_on: bool, + wifi_btn, + saved_networks: List[Dict] + ) -> None: + if not is_on: + self._clear_hotspot_ip_cache() return - if hotspot_btn.state == hotspot_btn.State.ON: - ipv4_addr = self.get_hotspot_ip_via_shell() + wifi_btn.state = wifi_btn.State.OFF - self.panel.netlist_ssuid.setText(self.panel.hotspot_name_input_field.text()) + old_hotspot = self._find_hotspot_in_saved(saved_networks) - self.panel.netlist_ip.setText(f"IP: {ipv4_addr or 'No IP Address'}") + new_hotspot_name = self._panel.hotspot_name_input_field.text() + if old_hotspot and old_hotspot["ssid"] != new_hotspot_name: + self._sdbus_network.delete_network(old_hotspot["ssid"]) - self.panel.netlist_strength.setText("--") + self._sdbus_network.create_hotspot( + new_hotspot_name, + self._panel.hotspot_password_input_field.text(), + ) + self._sdbus_network.toggle_hotspot(True) + try: + self._sdbus_network.connect_network(new_hotspot_name) + except Exception as e: + logger.error(e) - self.panel.netlist_security.setText("--") + def _find_hotspot_in_saved(self, saved_networks: List[Dict]) -> Optional[Dict]: + return next( + (n for n in saved_networks if "ap" in n.get("mode", "")), + None + ) - self.panel.mn_info_box.setText("Hotspot On") + def _find_wifi_networks_in_saved(self, saved_networks: List[Dict]) -> List[Dict]: + return [ + n for n in saved_networks + if "ap" not in n.get("mode", "") and n.get("signal", 0) != 0 + ] - 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() - sec_type = "--" - signal_strength = "--" - curr_ip_text = "No IP Address" - curr_status = "Disconnected" - if current_ssid != "": - sec_type = self.sdbus_network.get_security_type_by_ssid(current_ssid) - signal_strength = self.sdbus_network.get_connection_signal_by_ssid( - current_ssid - ) - curr_ip_text = f"IP: {ipv4_addr}" - curr_status = "Connected" - self.panel.netlist_ssuid.setText(current_ssid) - self.panel.netlist_ip.setText(curr_ip_text) - self.panel.netlist_security.setText(str(sec_type).upper()) - self.panel.netlist_strength.setText( - str(signal_strength if signal_strength != -1 else "--") - ) - self.panel.mn_info_box.setText(curr_status) + @QtCore.pyqtSlot(str, name="nm-state-changed") + def _evaluate_network_state(self, nm_state: str = "") -> None: + wifi_btn = self._panel.wifi_button.toggle_button + hotspot_btn = self._panel.hotspot_button.toggle_button + + state = nm_state or self._sdbus_network.check_nm_state() + if not state: + return + + if self._is_first_run: + self._handle_first_run_state() + self._is_first_run = False + + if not self._sdbus_network.check_wifi_interface(): + return + + if hotspot_btn.state == hotspot_btn.State.ON: + self._update_hotspot_display() + elif wifi_btn.state == wifi_btn.State.ON: + self._update_wifi_display() - self._expand_infobox(False) - self.info_box_load(False) - self.panel.wifi_button.setEnabled(True) - self.panel.hotspot_button.setEnabled(True) - self.update() + self._set_info_panel_visibility(show_details=True) + self._set_loading_state(False) + self._panel.wifi_button.setEnabled(True) + self._panel.hotspot_button.setEnabled(True) 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( + self._sdbus_network.disconnect_network() + self._set_info_panel_visibility(show_details=False) + self._configure_info_box_centered() + self._panel.mn_info_box.setText( "Network connection required.\n\nConnect to Wi-Fi\nor\nTurn on Hotspot" ) + self.repaint() - def get_hotspot_ip_via_shell(self): - """ - Executes a shell command to retrieve the IPv4 address for a specified interface. + def _handle_first_run_state(self) -> None: + saved_networks = self._sdbus_network.get_saved_networks_with_for() + + old_hotspot = self._find_hotspot_in_saved(saved_networks) + if old_hotspot: + self._panel.hotspot_name_input_field.setText(old_hotspot["ssid"]) + + connection = self._sdbus_network.check_connectivity() + wifi_btn = self._panel.wifi_button.toggle_button + hotspot_btn = self._panel.hotspot_button.toggle_button + + if connection == "FULL": + wifi_btn.state = wifi_btn.State.ON + hotspot_btn.state = hotspot_btn.State.OFF + elif connection == "LIMITED": + wifi_btn.state = wifi_btn.State.OFF + hotspot_btn.state = hotspot_btn.State.ON + + def _update_hotspot_display(self) -> None: + ipv4_addr = self._get_hotspot_ip_via_psutil() + + self._panel.netlist_ssuid.setText(self._panel.hotspot_name_input_field.text()) + self._panel.netlist_ip.setText(f"IP: {ipv4_addr or 'No IP Address'}") + self._panel.netlist_strength.setText("--") + self._panel.netlist_security.setText("--") + self._panel.mn_info_box.setText("Hotspot On") + + def _update_wifi_display(self) -> None: + 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) + + self._panel.netlist_ssuid.setText(current_ssid) + self._panel.netlist_ip.setText(f"IP: {ipv4_addr}") + 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") + else: + self._panel.netlist_ssuid.setText("") + self._panel.netlist_ip.setText("No IP Address") + self._panel.netlist_security.setText("--") + self._panel.netlist_strength.setText("--") + self._panel.mn_info_box.setText("Disconnected") - Returns: - The IP address string (e.g., '10.42.0.1') or None if not found. + def _get_hotspot_ip_via_psutil(self, iface: str = "wlan0") -> str: + """ + Get the IPv4 address for the given interface using psutil. + Returns an empty string if no address is found. """ - command = ["ip", "-4", "addr", "show", "wlan0"] + if self._cached_hotspot_ip: + return self._cached_hotspot_ip + 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") + addrs = psutil.net_if_addrs().get(iface, []) + for addr in addrs: + # Only look at IPv4 addresses + if addr.family == socket.AF_INET: + ip = addr.address + self._cached_hotspot_ip = ip + return ip + except Exception: + # If psutil isn't installed or something goes wrong, ignore + pass + return "" - def close(self) -> bool: - """Close class, close network module""" - self.sdbus_network.close() - return super().close() + def _clear_hotspot_ip_cache(self) -> None: + self._cached_hotspot_ip = "" - def _expand_infobox(self, toggle: bool = False) -> None: - """Expand information box on the main network panel + @QtCore.pyqtSlot(str, name="delete-network") + def _delete_network(self, ssid: str) -> None: + 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") - 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) + @QtCore.pyqtSlot(name="rescan-networks") + def _rescan_networks(self) -> None: + self._sdbus_network.rescan_networks() - self.panel.line_3.setVisible(not toggle) - self.panel.netlist_security.setVisible(not toggle) - self.panel.netlist_security_label.setVisible(not toggle) + @QtCore.pyqtSlot(name="add-network") + def _add_network(self) -> None: + self._panel.add_network_validation_button.setEnabled(False) + self._panel.add_network_validation_button.update() - self.panel.mn_info_box.setWordWrap(True) - self.panel.mn_info_box.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) + password = self._panel.add_network_password_field.text() + ssid = self._panel.add_network_network_label.text() - @QtCore.pyqtSlot(str, name="delete-network") - def delete_network(self, ssid: str) -> None: - """Delete network""" - self.sdbus_network.delete_network(ssid=ssid) + if not password and not self._current_network_is_open: + self._show_error_popup("Password field cannot be empty.") + self._panel.add_network_validation_button.setEnabled(True) + return - @QtCore.pyqtSlot(name="rescan-networks") - def rescan_networks(self) -> None: - """Rescan for networks""" - self.sdbus_network.rescan_networks() + result = self._sdbus_network.add_wifi_network(ssid=ssid, psk=password) + self._panel.add_network_password_field.clear() + + error_msg = result.get("error", "") if result else "" + + if not error_msg: + self._handle_successful_network_add(ssid) + else: + self._handle_failed_network_add(error_msg) + + def _handle_successful_network_add(self, ssid: str) -> None: + self._schedule_delayed_action( + self._network_list_worker.build, + NETWORK_CONNECT_DELAY_MS + ) + QtCore.QTimer.singleShot( + NETWORK_CONNECT_DELAY_MS, + lambda: self._sdbus_network.connect_network(ssid) + ) + + self._set_loading_state(True) + self.setCurrentIndex(self.indexOf(self._panel.main_network_page)) + self._panel.add_network_validation_button.setEnabled(True) + self._panel.wifi_button.setEnabled(False) + self._panel.hotspot_button.setEnabled(False) + self._panel.add_network_validation_button.update() + + def _handle_failed_network_add(self, error_msg: str) -> None: + 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._panel.add_network_validation_button.setEnabled(True) + self._panel.add_network_validation_button.update() + self._show_error_popup(message) + + def _on_save_network_settings(self) -> None: + self._update_network( + ssid=self._panel.saved_connection_network_name.text(), + password=self._panel.saved_connection_change_password_field.text(), + new_ssid=None, + ) + + def _update_network( + self, + ssid: str, + password: Optional[str], + new_ssid: Optional[str], + ) -> None: + 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 + ) + except Exception as e: + logger.error("Failed to update network settings: %s",e) + self._show_error_popup("Failed to update network settings") + + self.setCurrentIndex(self.indexOf(self._panel.network_list_page)) + + def _get_selected_priority(self) -> int: + checked_btn = self._panel.priority_btn_group.checkedButton() + + if checked_btn == self._panel.high_priority_btn: + return PRIORITY_HIGH + elif checked_btn == self._panel.low_priority_btn: + return PRIORITY_LOW + else: + return PRIORITY_MEDIUM + + def _on_saved_wifi_option_selected(self) -> None: + sender = self.sender() + + wifi_toggle = self._panel.wifi_button.toggle_button + hotspot_toggle = self._panel.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 + + ssid = self._panel.saved_connection_network_name.text() + + if sender == self._panel.network_delete_btn: + self._handle_network_delete(ssid) + elif sender == self._panel.network_activate_btn: + self._handle_network_activate(ssid) + + def _handle_network_delete(self, ssid: str) -> None: + try: + self._sdbus_network.delete_network(ssid) + if ssid in self._networks: + network_was_saved = self._networks[ssid].is_saved + del self._networks[ssid] + if network_was_saved: + self._network_list_worker.build() + self.setCurrentIndex(self.indexOf(self._panel.network_list_page)) + self._build_model_list() + 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: + self.setCurrentIndex(self.indexOf(self._panel.main_network_page)) + try: + self._sdbus_network.connect_network(ssid) + self._set_loading_state(True) + except Exception as e: + logger.error("Failed to connect to %s: %s",ssid,e) + self._set_loading_state(False) + self._show_error_popup("Failed to connect to network") @QtCore.pyqtSlot(name="handle-hotspot-back") - def handle_hotspot_back(self) -> None: - """Handle go back a page from hotspot page""" + def _handle_hotspot_back(self) -> None: if ( - self.panel.hotspot_password_input_field.text() - != self.sdbus_network.hotspot_password + self._panel.hotspot_password_input_field.text() + != self._sdbus_network.hotspot_password ): - self.panel.hotspot_password_input_field.setText( - self.sdbus_network.hotspot_password + self._panel.hotspot_password_input_field.setText( + self._sdbus_network.hotspot_password ) if ( - self.panel.hotspot_name_input_field.text() - != self.sdbus_network.hotspot_ssid + 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)) + self._panel.hotspot_name_input_field.setText( + self._sdbus_network.hotspot_ssid + ) - @QtCore.pyqtSlot(name="add_network") - def add_network(self) -> None: - """Slot for adding a new network + self.setCurrentIndex(self.indexOf(self._panel.main_network_page)) - Emitted Signals: - - add_network_confirmation(pyqtSignal): Signal with a dict that contains the result of adding a new network to the machine. + @QtCore.pyqtSlot(list, name="finished-network-list-build") + def _handle_network_list(self, data: List[tuple]) -> None: + self._networks.clear() + hotspot_ssid = self._sdbus_network.hotspot_ssid - """ + for entry in data: + if len(entry) >= 5: + ssid, signal, status, is_open, is_saved = entry + elif len(entry) >= 4: + ssid, signal, status, is_open = entry + is_saved = status in ("Active", "Saved") + else: + ssid, signal, status = entry[0], entry[1], entry[2] + is_open = status == "Open" + is_saved = status in ("Active", "Saved") - self.panel.add_network_validation_button.setEnabled(False) - self.panel.add_network_validation_button.update() + if ssid == hotspot_ssid: + continue - if not self.panel.add_network_password_field.text(): - self.popup.new_message( - message_type=Popup.MessageType.ERROR, - message="Password field cannot be empty.", + self._networks[ssid] = NetworkInfo( + signal=signal, + status=status, + is_open=is_open, + is_saved=is_saved ) - 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 - ) + self._build_model_list() - error_msg = result.get("error", "") - self.panel.add_network_password_field.clear() - if not error_msg: - self._schedule_delayed_action(self.network_list_worker.build, 5000) - QtCore.QTimer().singleShot( - 5000, - lambda: self.sdbus_network.connect_network( - self.panel.add_network_network_label.text() - ), - ) - self.info_box_load(True) - self.setCurrentIndex(self.indexOf(self.panel.main_network_page)) - self.panel.add_network_validation_button.setEnabled(True) + def _build_model_list(self) -> None: + self._panel.listView.blockSignals(True) + self._reset_view_model() - self.panel.wifi_button.setEnabled(False) - self.panel.hotspot_button.setEnabled(False) + saved_networks = [] + unsaved_networks = [] - self.panel.add_network_validation_button.update() - return + for ssid, info in self._networks.items(): + if info.is_saved: + saved_networks.append((ssid, info)) + else: + unsaved_networks.append((ssid, info)) - 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" - else: - message = "Error while adding network. Please try again" - self.panel.add_network_validation_button.setEnabled(True) - self.panel.add_network_validation_button.update() - self.popup.new_message(message_type=Popup.MessageType.ERROR, message=message) + saved_networks.sort(key=lambda x: -x[1].signal) + unsaved_networks.sort(key=lambda x: -x[1].signal) - def _schedule_delayed_action(self, callback, delay_ms: int) -> None: - """Schedule a reschedule timer""" - try: - self._delayed_action_timer.timeout.disconnect() - except TypeError: - pass + for ssid, info in saved_networks: + self._add_network_entry( + ssid=ssid, + signal=info.signal, + status=info.status, + is_open=info.is_open + ) - self._delayed_action_timer.timeout.connect(callback) - self._delayed_action_timer.start(delay_ms) + if saved_networks and unsaved_networks: + self._add_separator_entry() - def update_network( + for ssid, info in unsaved_networks: + self._add_network_entry( + ssid=ssid, + signal=info.signal, + status=info.status, + is_open=info.is_open + ) + + self._sync_scrollbar() + self._panel.listView.blockSignals(False) + self._panel.listView.update() + + def _reset_view_model(self) -> None: + self._model.clear() + self._entry_delegate.clear() + + def _add_separator_entry(self) -> None: + 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_network_entry( self, ssid: str, - password: typing.Union[str, None], - new_ssid: typing.Union[str, None], + signal: int, + status: str, + is_open: bool = False ) -> None: - """Update network information""" - if not self.sdbus_network.is_known(ssid): + wifi_pixmap = self._icon_provider.get_pixmap(signal=signal, status=status) + display_ssid = ssid if ssid else "UNKNOWN" + 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, + ) + self._model.add_item(item) + + @QtCore.pyqtSlot(ListItem, name="ssid-item-clicked") + def _on_ssid_item_clicked(self, item: ListItem) -> None: + ssid = item.text + if not ssid: 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 + network_info = self._networks.get(ssid) + if network_info is None: + return + + 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 - ) - self._schedule_delayed_action(self.network_list_worker.build, 10000) - self.setCurrentIndex(self.indexOf(self.panel.network_list_page)) + def _show_saved_network_page(self, ssid: str, saved_networks: List[Dict]) -> None: + self._panel.saved_connection_network_name.setText(str(ssid)) + self._panel.snd_name.setText(str(ssid)) - @QtCore.pyqtSlot(list, name="finished-network-list-build") - def handle_network_list(self, data: typing.List[typing.Tuple]) -> None: - """Handle available network list update""" - self.saved_network.clear() - for entry in data: - if entry[0] == self.sdbus_network.hotspot_ssid: - continue - self.saved_network[entry[0]] = (entry[1], entry[2]) - self.evaluate_network_state() - self.build_model_list() - self._schedule_delayed_action(self.network_list_worker.build, 10000) + entry = next((net for net in saved_networks if net["ssid"] == ssid), None) - @QtCore.pyqtSlot(ListItem, name="ssid_item_clicked") - def ssid_item_clicked(self, item: ListItem) -> None: - """Handles when a network is clicked on the QListWidget. + if entry is not None: + self._set_priority_button(entry.get("priority")) - Args: - item (QListWidgetItem): The list entry that was clicked - """ - ssid = item.text - _saved_ssids = self.sdbus_network.get_saved_networks_with_for() - 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") + network_info = self._networks.get(ssid) + if network_info: + signal_text = f"{network_info.signal}%" if network_info.signal >= 0 else "--%" + self._panel.saved_connection_signal_strength_info_frame.setText(signal_text) + + if network_info.is_open: + self._panel.saved_connection_security_type_info_label.setText("OPEN") else: - self.panel.network_activate_btn.setDisabled(True) - self.panel.sn_info.setText("Active Network") + sec_type = self._sdbus_network.get_security_type_by_ssid(ssid) + self._panel.saved_connection_security_type_info_label.setText( + str(sec_type or "WPA").upper() + ) + else: + self._panel.saved_connection_signal_strength_info_frame.setText("--%") + self._panel.saved_connection_security_type_info_label.setText("--") + + current_ssid = self._sdbus_network.get_current_ssid() + if current_ssid != 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.panel.frame.repaint() + self.setCurrentIndex(self.indexOf(self._panel.saved_connection_page)) + self._panel.frame.repaint() + def _set_priority_button(self, priority: Optional[int]) -> None: + if priority == PRIORITY_HIGH: + self._panel.high_priority_btn.setChecked(True) + elif priority == PRIORITY_LOW: + self._panel.low_priority_btn.setChecked(True) else: - self.setCurrentIndex(self.indexOf(self.panel.add_network_page)) - self.panel.add_network_network_label.setText(str(ssid)) + self._panel.med_priority_btn.setChecked(True) - 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 _show_add_network_page(self, ssid: str, is_open: bool = False) -> None: + self._current_network_is_open = is_open + self._panel.add_network_network_label.setText(str(ssid)) + self.setCurrentIndex(self.indexOf(self._panel.add_network_page)) - """ + def _set_add_network_page_password_visibility(self, show_password: bool) -> None: + self._panel.frame_2.setVisible(show_password) + + def _handle_scrollbar_change(self, value: int) -> None: + self._panel.verticalScrollBar.blockSignals(True) + self._panel.verticalScrollBar.setValue(value) + self._panel.verticalScrollBar.blockSignals(False) + + def _sync_scrollbar(self) -> None: + list_scrollbar = self._panel.listView.verticalScrollBar() + self._panel.verticalScrollBar.setMinimum(list_scrollbar.minimum()) + self._panel.verticalScrollBar.setMaximum(list_scrollbar.maximum()) + self._panel.verticalScrollBar.setPageStep(list_scrollbar.pageStep()) + + def _schedule_delayed_action(self, callback: Callable, delay_ms: int) -> None: + 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: + self._network_list_worker.stop_polling() + self._sdbus_network.close() + return super().close() + + def setCurrentIndex(self, index: int) -> None: if not self.isVisible(): return - _cur = self.currentIndex() - if index == self.indexOf(self.panel.add_network_page): - 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): - 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() - ) - _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.panel.saved_connection_security_type_info_label.setText( - str(_security_type or "--").upper() - ) + + if index == self.indexOf(self._panel.add_network_page): + self._setup_add_network_page() + elif index == self.indexOf(self._panel.saved_connection_page): + self._setup_saved_connection_page() + 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(self) -> None: + self._panel.add_network_password_field.clear() + + if self._current_network_is_open: + self._panel.frame_2.setVisible(False) + self._panel.add_network_validation_button.setText("Connect") + else: + self._panel.frame_2.setVisible(True) + self._panel.add_network_password_field.setPlaceholderText( + "Insert password here, press enter when finished." + ) + self._panel.add_network_validation_button.setText("Activate") - Args: - name (str): Name of the dynamic property - value (typing.Any): Value for the dynamic property + def _setup_saved_connection_page(self) -> None: + self._panel.saved_connection_change_password_field.clear() + self._panel.saved_connection_change_password_field.setPlaceholderText( + "Change network password" + ) - Returns: - bool: Returns to the super class - """ + def setProperty(self, name: str, value: Any) -> bool: 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: 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._panel.network_list_page)) + parent_size = self.parent().size() + self.setGeometry(0, 0, parent_size.width(), parent_size.height()) self.updateGeometry() self.repaint() - self.show() - - def add_network_entry(self, ssid: str, signal: int, is_saved: str) -> None: - """Adds a new item to the list model""" - if signal != -10: - wifi_pixmap = self._provider.get_pixmap(signal=signal, state=is_saved) - ssid = ssid if ssid != "" else "UNKNOWN" - item = ListItem( - text=ssid, - left_icon=wifi_pixmap, - right_text=is_saved, - right_icon=self.right_icon, - selected=False, - allow_check=False, - _lfontsize=17, - _rfontsize=12, - height=80, - not_clickable=False, - ) - else: - item = ListItem( - text="", - left_icon=None, - right_text="", - right_icon=None, - selected=False, - allow_check=False, - _lfontsize=17, - _rfontsize=12, - height=80, - not_clickable=True, - ) - self.model.add_item(item) - - def _handle_scrollbar(self, value): - self.panel.verticalScrollBar.blockSignals(True) - self.panel.verticalScrollBar.setValue(value) - self.panel.verticalScrollBar.blockSignals(False) - - def _setup_scrollbar(self) -> None: - self.panel.verticalScrollBar.setMinimum( - self.panel.listView.verticalScrollBar().minimum() - ) - self.panel.verticalScrollBar.setMaximum( - self.panel.listView.verticalScrollBar().maximum() - ) - self.panel.verticalScrollBar.setPageStep( - self.panel.listView.verticalScrollBar().pageStep() - ) - - def build_network_list(self) -> None: - """Build available/saved network list with optimized palette setup.""" - - def set_brush_for_all_groups( - palette, role, color, style=QtCore.Qt.BrushStyle.SolidPattern - ): - """Helper to set a brush for Active, Inactive, and Disabled states.""" - brush = QtGui.QBrush(QtGui.QColor(*color)) - brush.setStyle(style) - for group in [ - QtGui.QPalette.ColorGroup.Active, - QtGui.QPalette.ColorGroup.Inactive, - QtGui.QPalette.ColorGroup.Disabled, - ]: - palette.setBrush(group, role, brush) - - palette = QtGui.QPalette() - - set_brush_for_all_groups(palette, QtGui.QPalette.ColorRole.Button, (0, 0, 0, 0)) - set_brush_for_all_groups(palette, QtGui.QPalette.ColorRole.Window, (0, 0, 0, 0)) - - set_brush_for_all_groups( - palette, - QtGui.QPalette.ColorRole.Base, - (0, 0, 0), - QtCore.Qt.BrushStyle.NoBrush, - ) - set_brush_for_all_groups( - palette, QtGui.QPalette.ColorRole.Highlight, (0, 120, 215, 0) - ) - set_brush_for_all_groups(palette, QtGui.QPalette.ColorRole.Link, (0, 0, 255, 0)) - self.panel.listView.setPalette(palette) + self.show() \ No newline at end of file