update: new verified on menu page / other minor updates
330
gui/__main__.py
|
|
@ -14,7 +14,7 @@ import subprocess
|
||||||
import qrcode
|
import qrcode
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from typing import Union
|
from typing import Union
|
||||||
from core.Errors import UnknownConnectionTypeError, CommandNotFoundError, MissingSubscriptionError, InvalidSubscriptionError, ProfileActivationError, UnsupportedApplicationVersionError, FileIntegrityError, ProfileModificationError, ProfileStateConflictError
|
from core.Errors import UnknownConnectionTypeError, CommandNotFoundError, MissingSubscriptionError, InvalidSubscriptionError, ProfileActivationError, UnsupportedApplicationVersionError, FileIntegrityError, ProfileModificationError, ProfileStateConflictError, EndpointVerificationError
|
||||||
from core.controllers.ApplicationVersionController import ApplicationVersionController
|
from core.controllers.ApplicationVersionController import ApplicationVersionController
|
||||||
from core.controllers.ApplicationController import ApplicationController
|
from core.controllers.ApplicationController import ApplicationController
|
||||||
from core.controllers.ClientController import ClientController
|
from core.controllers.ClientController import ClientController
|
||||||
|
|
@ -1188,7 +1188,6 @@ class Worker(QObject):
|
||||||
profile_observer.subscribe('disabled', lambda event: self.handle_profile_status(event.subject, False))
|
profile_observer.subscribe('disabled', lambda event: self.handle_profile_status(event.subject, False))
|
||||||
profile_observer.subscribe('enabled', lambda event: self.handle_profile_status(event.subject, True))
|
profile_observer.subscribe('enabled', lambda event: self.handle_profile_status(event.subject, True))
|
||||||
self.profile_type = None
|
self.profile_type = None
|
||||||
self.force = self.profile_data.get('force', False)
|
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
self.profile = ProfileController.get(int(self.profile_data['id']))
|
self.profile = ProfileController.get(int(self.profile_data['id']))
|
||||||
|
|
@ -1202,9 +1201,17 @@ class Worker(QObject):
|
||||||
return
|
return
|
||||||
if self.profile:
|
if self.profile:
|
||||||
try:
|
try:
|
||||||
ProfileController.enable(self.profile, ignore=self.force, profile_observer=profile_observer,
|
ignore_exceptions = []
|
||||||
|
if self.profile_data.get('ignore_endpoint_verification', False):
|
||||||
|
ignore_exceptions.append(EndpointVerificationError)
|
||||||
|
if self.profile_data.get('ignore_profile_state_conflict', False):
|
||||||
|
ignore_exceptions.append(ProfileStateConflictError)
|
||||||
|
ignore_tuple = tuple(ignore_exceptions)
|
||||||
|
ProfileController.enable(self.profile, ignore=ignore_tuple, profile_observer=profile_observer,
|
||||||
application_version_observer=application_version_observer,
|
application_version_observer=application_version_observer,
|
||||||
connection_observer=connection_observer)
|
connection_observer=connection_observer)
|
||||||
|
except EndpointVerificationError:
|
||||||
|
self.update_signal.emit("ENDPOINT_VERIFICATION_ERROR", False, self.profile_data['id'], None, None)
|
||||||
except (InvalidSubscriptionError, MissingSubscriptionError) as e:
|
except (InvalidSubscriptionError, MissingSubscriptionError) as e:
|
||||||
self.change_page.emit(f"Subscription missing or invalid for profile {self.profile_data['id']}", True)
|
self.change_page.emit(f"Subscription missing or invalid for profile {self.profile_data['id']}", True)
|
||||||
except ProfileActivationError:
|
except ProfileActivationError:
|
||||||
|
|
@ -1216,7 +1223,7 @@ class Worker(QObject):
|
||||||
except ProfileModificationError:
|
except ProfileModificationError:
|
||||||
self.update_signal.emit("WireGuard configuration could not be attached.", False, None, None, None)
|
self.update_signal.emit("WireGuard configuration could not be attached.", False, None, None, None)
|
||||||
except ProfileStateConflictError:
|
except ProfileStateConflictError:
|
||||||
self.update_signal.emit("The profile is already being enabled...", False, None, None, None)
|
self.update_signal.emit("PROFILE_STATE_CONFLICT_ERROR", False, self.profile_data['id'], None, None)
|
||||||
except CommandNotFoundError as e :
|
except CommandNotFoundError as e :
|
||||||
self.update_signal.emit(str(e.subject), False, -1, None, None)
|
self.update_signal.emit(str(e.subject), False, -1, None, None)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
@ -1746,6 +1753,18 @@ class MenuPage(Page):
|
||||||
|
|
||||||
label_principal.show()
|
label_principal.show()
|
||||||
self.additional_labels.append(label_principal)
|
self.additional_labels.append(label_principal)
|
||||||
|
|
||||||
|
if protocol.lower() == "wireguard" and ConfigurationController.get_endpoint_verification_enabled():
|
||||||
|
profile_obj = ProfileController.get(self.reverse_id)
|
||||||
|
is_profile_enabled = self.connection_manager.is_profile_connected(self.reverse_id)
|
||||||
|
if profile_obj and profile_obj.connection and profile_obj.connection.code == 'wireguard' and is_profile_enabled:
|
||||||
|
verified_icon = QLabel(self)
|
||||||
|
verified_icon.setGeometry(0, 60, 400, 40)
|
||||||
|
verified_pixmap = QPixmap(os.path.join(self.btn_path, "verified_profile.png"))
|
||||||
|
verified_icon.setPixmap(verified_pixmap)
|
||||||
|
verified_icon.setScaledContents(True)
|
||||||
|
verified_icon.show()
|
||||||
|
self.additional_labels.append(verified_icon)
|
||||||
|
|
||||||
if protocol.lower() == "hidetor":
|
if protocol.lower() == "hidetor":
|
||||||
|
|
||||||
|
|
@ -2056,19 +2075,59 @@ class MenuPage(Page):
|
||||||
|
|
||||||
def handle_enable_force_result(self, result):
|
def handle_enable_force_result(self, result):
|
||||||
if result:
|
if result:
|
||||||
self.get_billing_code_by_id(force=True)
|
profile_data = {'id': self.reverse_id, 'ignore_profile_state_conflict': True}
|
||||||
|
if hasattr(self, '_pending_endpoint_verification_ignore') and self._pending_endpoint_verification_ignore:
|
||||||
|
profile_data['ignore_endpoint_verification'] = True
|
||||||
|
self.enabling_profile(profile_data)
|
||||||
else:
|
else:
|
||||||
pass
|
pass
|
||||||
|
if hasattr(self, '_pending_profile_state_conflict_ignore'):
|
||||||
|
delattr(self, '_pending_profile_state_conflict_ignore')
|
||||||
|
if hasattr(self, '_pending_endpoint_verification_ignore'):
|
||||||
|
delattr(self, '_pending_endpoint_verification_ignore')
|
||||||
self.popup.close()
|
self.popup.close()
|
||||||
|
|
||||||
|
def handle_endpoint_verification_result(self, result, action, profile_id):
|
||||||
|
if action == "continue":
|
||||||
|
profile_data = {'id': profile_id, 'ignore_endpoint_verification': True}
|
||||||
|
if hasattr(self, '_pending_profile_state_conflict_ignore') and self._pending_profile_state_conflict_ignore:
|
||||||
|
profile_data['ignore_profile_state_conflict'] = True
|
||||||
|
self.enabling_profile(profile_data)
|
||||||
|
elif action == "sync":
|
||||||
|
self._pending_endpoint_verification_ignore = True
|
||||||
|
self.update_status.update_status("Syncing...")
|
||||||
|
self.worker_thread = WorkerThread('SYNC')
|
||||||
|
self.worker_thread.finished.connect(lambda: self.handle_sync_after_verification(profile_id))
|
||||||
|
self.worker_thread.sync_output.connect(self.update_status.update_values)
|
||||||
|
self.worker_thread.start()
|
||||||
|
else:
|
||||||
|
self.update_status.update_status("Profile enable aborted")
|
||||||
|
if hasattr(self, 'popup'):
|
||||||
|
self.popup.close()
|
||||||
|
|
||||||
|
def handle_sync_after_verification(self, profile_id):
|
||||||
|
profile_data = {'id': profile_id, 'ignore_endpoint_verification': True}
|
||||||
|
if hasattr(self, '_pending_profile_state_conflict_ignore') and self._pending_profile_state_conflict_ignore:
|
||||||
|
profile_data['ignore_profile_state_conflict'] = True
|
||||||
|
self.enabling_profile(profile_data)
|
||||||
|
|
||||||
def update_gui_main_thread(self, text, is_enabled, profile_id, profile_type, profile_connection):
|
def update_gui_main_thread(self, text, is_enabled, profile_id, profile_type, profile_connection):
|
||||||
if '...' in text:
|
if text == "ENDPOINT_VERIFICATION_ERROR":
|
||||||
|
message = "Operator verification failed. Do you want to continue anyway?"
|
||||||
|
self.popup = EndpointVerificationPopup(self, message=message)
|
||||||
|
self.popup.finished.connect(lambda result, action: self.handle_endpoint_verification_result(result, action, profile_id))
|
||||||
|
self.popup.show()
|
||||||
|
return
|
||||||
|
|
||||||
|
if text == "PROFILE_STATE_CONFLICT_ERROR":
|
||||||
message = f'The profile is already enabled. Do you want to force enable it anyway?'
|
message = f'The profile is already enabled. Do you want to force enable it anyway?'
|
||||||
|
self._pending_profile_state_conflict_ignore = True
|
||||||
self.popup = self.update_status._create_confirmation_popup(message, button_text='Proceed')
|
self.popup = self.update_status._create_confirmation_popup(message, button_text='Proceed')
|
||||||
self.popup.finished.connect(lambda result: self.handle_enable_force_result(result))
|
self.popup.finished.connect(lambda result: self.handle_enable_force_result(result))
|
||||||
self.popup.show()
|
self.popup.show()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
if profile_id < 0:
|
if profile_id < 0:
|
||||||
self.DisplayInstallScreen(text)
|
self.DisplayInstallScreen(text)
|
||||||
return
|
return
|
||||||
|
|
@ -2115,6 +2174,11 @@ class MenuPage(Page):
|
||||||
|
|
||||||
self.show_disconnect_button(True, profile_type, target_button)
|
self.show_disconnect_button(True, profile_type, target_button)
|
||||||
self.boton_edit.setEnabled(False)
|
self.boton_edit.setEnabled(False)
|
||||||
|
|
||||||
|
if profile_id == self.reverse_id:
|
||||||
|
profile_obj = ProfileController.get(profile_id)
|
||||||
|
if profile_obj and profile_obj.connection and profile_obj.connection.code == 'wireguard' and ConfigurationController.get_endpoint_verification_enabled():
|
||||||
|
self.print_profile_details(f"Profile_{profile_id}")
|
||||||
else:
|
else:
|
||||||
self.connection_manager.remove_connected_profile(profile_id)
|
self.connection_manager.remove_connected_profile(profile_id)
|
||||||
|
|
||||||
|
|
@ -2954,29 +3018,6 @@ class HidetorPage(Page):
|
||||||
boton.location_icon_name = icon_name
|
boton.location_icon_name = icon_name
|
||||||
boton.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
|
boton.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
|
||||||
boton.clicked.connect(lambda checked, loc=icon_name: self.show_location(loc))
|
boton.clicked.connect(lambda checked, loc=icon_name: self.show_location(loc))
|
||||||
|
|
||||||
if locations and locations.operator:
|
|
||||||
verification_icon = QPushButton(self)
|
|
||||||
icon_size = 30
|
|
||||||
button_x, button_y, button_width, button_height = geometry
|
|
||||||
icon_x = button_x + button_width - icon_size + 10
|
|
||||||
icon_y = button_y
|
|
||||||
verification_icon.setGeometry(icon_x, icon_y, icon_size, icon_size)
|
|
||||||
verification_icon.setIcon(QIcon(os.path.join(self.btn_path, "verification_icon.png")))
|
|
||||||
verification_icon.setIconSize(QSize(icon_size - 4, icon_size - 4))
|
|
||||||
verification_icon.setStyleSheet(f"""
|
|
||||||
QPushButton {{
|
|
||||||
background: transparent;
|
|
||||||
border: none;
|
|
||||||
}}
|
|
||||||
QPushButton:hover {{
|
|
||||||
background: rgba(255, 255, 255, 0.2);
|
|
||||||
border-radius: 3px;
|
|
||||||
}}
|
|
||||||
""")
|
|
||||||
verification_icon.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
|
|
||||||
verification_icon.clicked.connect(lambda checked, loc=icon_name: self.show_location_verification(loc))
|
|
||||||
verification_icon.show()
|
|
||||||
|
|
||||||
def update_swarp_json(self):
|
def update_swarp_json(self):
|
||||||
inserted_data = {
|
inserted_data = {
|
||||||
|
|
@ -3250,6 +3291,30 @@ class LocationPage(Page):
|
||||||
self.initial_display.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
self.initial_display.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
self.initial_display.setWordWrap(True)
|
self.initial_display.setWordWrap(True)
|
||||||
|
|
||||||
|
self.verification_button = QPushButton("Verification", self)
|
||||||
|
self.verification_button.setGeometry(330, 40, 130, 40)
|
||||||
|
self.verification_button.setStyleSheet(f"""
|
||||||
|
QPushButton {{
|
||||||
|
background-color: rgba(30, 30, 35, 0.9);
|
||||||
|
border: 1px solid rgba(100, 100, 120, 0.6);
|
||||||
|
border-radius: 5px;
|
||||||
|
color: rgb(255, 255, 255);
|
||||||
|
font-size: 14px;
|
||||||
|
padding: 5px 10px;
|
||||||
|
}}
|
||||||
|
QPushButton:hover {{
|
||||||
|
background-color: rgba(50, 50, 60, 0.95);
|
||||||
|
border: 1px solid rgba(150, 150, 180, 0.8);
|
||||||
|
}}
|
||||||
|
QPushButton:disabled {{
|
||||||
|
background-color: rgba(20, 20, 25, 0.5);
|
||||||
|
border: 1px solid rgba(60, 60, 70, 0.4);
|
||||||
|
opacity: 0.4;
|
||||||
|
}}
|
||||||
|
""")
|
||||||
|
self.verification_button.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
|
||||||
|
self.verification_button.setEnabled(False)
|
||||||
|
self.verification_button.show()
|
||||||
|
|
||||||
def showEvent(self, event):
|
def showEvent(self, event):
|
||||||
super().showEvent(event)
|
super().showEvent(event)
|
||||||
|
|
@ -3294,29 +3359,6 @@ class LocationPage(Page):
|
||||||
boton.location_icon_name = icon_name
|
boton.location_icon_name = icon_name
|
||||||
boton.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
|
boton.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
|
||||||
boton.clicked.connect(lambda checked, loc=icon_name: self.show_location(loc))
|
boton.clicked.connect(lambda checked, loc=icon_name: self.show_location(loc))
|
||||||
|
|
||||||
if locations and locations.operator:
|
|
||||||
verification_icon = QPushButton(self)
|
|
||||||
icon_size = 30
|
|
||||||
button_x, button_y, button_width, button_height = geometry
|
|
||||||
icon_x = button_x + button_width - icon_size + 10
|
|
||||||
icon_y = button_y
|
|
||||||
verification_icon.setGeometry(icon_x, icon_y, icon_size, icon_size)
|
|
||||||
verification_icon.setIcon(QIcon(os.path.join(self.btn_path, "verification_icon.png")))
|
|
||||||
verification_icon.setIconSize(QSize(icon_size - 4, icon_size - 4))
|
|
||||||
verification_icon.setStyleSheet(f"""
|
|
||||||
QPushButton {{
|
|
||||||
background: transparent;
|
|
||||||
border: none;
|
|
||||||
}}
|
|
||||||
QPushButton:hover {{
|
|
||||||
background: rgba(255, 255, 255, 0.2);
|
|
||||||
border-radius: 3px;
|
|
||||||
}}
|
|
||||||
""")
|
|
||||||
verification_icon.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
|
|
||||||
verification_icon.clicked.connect(lambda checked, loc=icon_name: self.show_location_verification(loc))
|
|
||||||
verification_icon.show()
|
|
||||||
|
|
||||||
def update_swarp_json(self, get_connection=False):
|
def update_swarp_json(self, get_connection=False):
|
||||||
profile_data = self.update_status.read_data()
|
profile_data = self.update_status.read_data()
|
||||||
|
|
@ -3336,6 +3378,14 @@ class LocationPage(Page):
|
||||||
self.button_next.setVisible(True)
|
self.button_next.setVisible(True)
|
||||||
self.button_next.clicked.connect(self.go_selected)
|
self.button_next.clicked.connect(self.go_selected)
|
||||||
self.update_swarp_json()
|
self.update_swarp_json()
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.verification_button.clicked.disconnect()
|
||||||
|
except TypeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
self.verification_button.clicked.connect(lambda: self.show_location_verification(location))
|
||||||
|
self.verification_button.setEnabled(True)
|
||||||
|
|
||||||
|
|
||||||
def reverse(self):
|
def reverse(self):
|
||||||
|
|
@ -4888,6 +4938,7 @@ class LocationVerificationPage(Page):
|
||||||
self.verification_copy_buttons = {}
|
self.verification_copy_buttons = {}
|
||||||
self.verification_display_names = {
|
self.verification_display_names = {
|
||||||
"operator_name": "Operator Name",
|
"operator_name": "Operator Name",
|
||||||
|
"provider_name": "Provider Name",
|
||||||
"nostr_public_key": "Nostr Key",
|
"nostr_public_key": "Nostr Key",
|
||||||
"hydraveil_public_key": "HydraVeil Key",
|
"hydraveil_public_key": "HydraVeil Key",
|
||||||
"nostr_attestation_event_reference": "Nostr Verification"
|
"nostr_attestation_event_reference": "Nostr Verification"
|
||||||
|
|
@ -4909,16 +4960,17 @@ class LocationVerificationPage(Page):
|
||||||
location_display.setPixmap(location_pixmap.scaled(390, 520, Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.SmoothTransformation))
|
location_display.setPixmap(location_pixmap.scaled(390, 520, Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.SmoothTransformation))
|
||||||
location_display.show()
|
location_display.show()
|
||||||
|
|
||||||
title = QLabel("Verification Information", self)
|
title = QLabel("Operator Information", self)
|
||||||
title.setGeometry(350, 50, 350, 30)
|
title.setGeometry(350, 50, 350, 30)
|
||||||
title.setStyleSheet(f"color: #808080; font-size: 20px; font-weight: bold; {self.font_style}")
|
title.setStyleSheet(f"color: #808080; font-size: 20px; font-weight: bold; {self.font_style}")
|
||||||
title.show()
|
title.show()
|
||||||
|
|
||||||
info_items = [
|
info_items = [
|
||||||
("Operator Name", "operator_name", 130),
|
("Operator Name", "operator_name", 130),
|
||||||
("Nostr Key", "nostr_public_key", 180),
|
("Provider Name", "provider_name", 180),
|
||||||
("HydraVeil Key", "hydraveil_public_key", 230),
|
("Nostr Key", "nostr_public_key", 230),
|
||||||
("Nostr Verification", "nostr_attestation_event_reference", 280),
|
("HydraVeil Key", "hydraveil_public_key", 280),
|
||||||
|
("Nostr Verification", "nostr_attestation_event_reference", 330),
|
||||||
]
|
]
|
||||||
|
|
||||||
label_x = 350
|
label_x = 350
|
||||||
|
|
@ -5060,6 +5112,11 @@ class LocationVerificationPage(Page):
|
||||||
display_operator = self.truncate_text_by_width(operator_name, self.verification_info["operator_name"], value_width)
|
display_operator = self.truncate_text_by_width(operator_name, self.verification_info["operator_name"], value_width)
|
||||||
self.verification_info["operator_name"].setText(display_operator)
|
self.verification_info["operator_name"].setText(display_operator)
|
||||||
|
|
||||||
|
provider_name = locations.provider_name if locations and hasattr(locations, 'provider_name') and locations.provider_name else "N/A"
|
||||||
|
self.verification_full_values["provider_name"] = provider_name
|
||||||
|
display_provider = self.truncate_text_by_width(provider_name, self.verification_info["provider_name"], value_width)
|
||||||
|
self.verification_info["provider_name"].setText(display_provider)
|
||||||
|
|
||||||
nostr_key = operator.nostr_public_key or "N/A"
|
nostr_key = operator.nostr_public_key or "N/A"
|
||||||
self.verification_full_values["nostr_public_key"] = nostr_key
|
self.verification_full_values["nostr_public_key"] = nostr_key
|
||||||
display_nostr = self.truncate_text_by_width(nostr_key, self.verification_info["nostr_public_key"], value_width)
|
display_nostr = self.truncate_text_by_width(nostr_key, self.verification_info["nostr_public_key"], value_width)
|
||||||
|
|
@ -5077,6 +5134,8 @@ class LocationVerificationPage(Page):
|
||||||
else:
|
else:
|
||||||
self.verification_info["operator_name"].setText("N/A")
|
self.verification_info["operator_name"].setText("N/A")
|
||||||
self.verification_full_values["operator_name"] = "N/A"
|
self.verification_full_values["operator_name"] = "N/A"
|
||||||
|
self.verification_info["provider_name"].setText("N/A")
|
||||||
|
self.verification_full_values["provider_name"] = "N/A"
|
||||||
self.verification_info["nostr_public_key"].setText("N/A")
|
self.verification_info["nostr_public_key"].setText("N/A")
|
||||||
self.verification_full_values["nostr_public_key"] = "N/A"
|
self.verification_full_values["nostr_public_key"] = "N/A"
|
||||||
self.verification_info["hydraveil_public_key"].setText("N/A")
|
self.verification_info["hydraveil_public_key"].setText("N/A")
|
||||||
|
|
@ -6212,6 +6271,25 @@ class Settings(Page):
|
||||||
layout.addLayout(verification_layout)
|
layout.addLayout(verification_layout)
|
||||||
layout.addStretch()
|
layout.addStretch()
|
||||||
|
|
||||||
|
|
||||||
|
endpoint_verification_label = QLabel("Endpoint Verification:", page)
|
||||||
|
endpoint_verification_label.setGeometry(20, 420, 150, 20)
|
||||||
|
endpoint_verification_label.setStyleSheet(f"color: white; font-size: 15px; {self.font_style}")
|
||||||
|
endpoint_verification_label.show()
|
||||||
|
|
||||||
|
self.endpoint_verification_checkbox = QCheckBox(page)
|
||||||
|
self.endpoint_verification_checkbox.setGeometry(180, 415, 30, 30)
|
||||||
|
self.endpoint_verification_checkbox.setChecked(ConfigurationController.get_endpoint_verification_enabled())
|
||||||
|
self.endpoint_verification_checkbox.setStyleSheet(self.get_checkbox_style())
|
||||||
|
self.endpoint_verification_checkbox.show()
|
||||||
|
|
||||||
|
save_button = QPushButton(page)
|
||||||
|
save_button.setGeometry(350, 410, 60, 31)
|
||||||
|
save_button.setIcon(QIcon(os.path.join(self.btn_path, "save.png")))
|
||||||
|
save_button.setIconSize(QSize(60, 31))
|
||||||
|
save_button.clicked.connect(self.save_verification_settings)
|
||||||
|
save_button.show()
|
||||||
|
|
||||||
self.verification_profile_selector.currentIndexChanged.connect(self.update_verification_info)
|
self.verification_profile_selector.currentIndexChanged.connect(self.update_verification_info)
|
||||||
self.verification_profile_selector.activated.connect(self.update_verification_info)
|
self.verification_profile_selector.activated.connect(self.update_verification_info)
|
||||||
if self.verification_profile_selector.count() > 0:
|
if self.verification_profile_selector.count() > 0:
|
||||||
|
|
@ -6287,6 +6365,13 @@ class Settings(Page):
|
||||||
else:
|
else:
|
||||||
self.verification_checkmarks[key].hide()
|
self.verification_checkmarks[key].hide()
|
||||||
|
|
||||||
|
def save_verification_settings(self):
|
||||||
|
try:
|
||||||
|
ConfigurationController.set_endpoint_verification_enabled(self.endpoint_verification_checkbox.isChecked())
|
||||||
|
self.update_status.update_status("Verification settings saved successfully")
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Error saving verification settings: {str(e)}")
|
||||||
|
self.update_status.update_status("Error saving verification settings")
|
||||||
|
|
||||||
def copy_verification_value(self, key):
|
def copy_verification_value(self, key):
|
||||||
"""Copy the full verification value (not truncated) to clipboard."""
|
"""Copy the full verification value (not truncated) to clipboard."""
|
||||||
|
|
@ -7200,11 +7285,131 @@ class ConfirmationPopup(QWidget):
|
||||||
action_button.clicked.connect(self.perform_action)
|
action_button.clicked.connect(self.perform_action)
|
||||||
button_layout.addWidget(action_button)
|
button_layout.addWidget(action_button)
|
||||||
|
|
||||||
content_layout.addStretch()
|
class EndpointVerificationPopup(QWidget):
|
||||||
|
finished = pyqtSignal(bool, str)
|
||||||
|
|
||||||
def perform_action(self):
|
def __init__(self, parent=None, message=""):
|
||||||
self.finished.emit(True)
|
super().__init__(parent)
|
||||||
|
self.parent_window = parent
|
||||||
|
self.message = message
|
||||||
|
self.initUI()
|
||||||
|
|
||||||
|
def initUI(self):
|
||||||
|
self.setMinimumSize(500, 250)
|
||||||
|
self.setWindowFlags(Qt.WindowType.FramelessWindowHint)
|
||||||
|
self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
|
||||||
|
|
||||||
|
main_layout = QVBoxLayout()
|
||||||
|
self.setLayout(main_layout)
|
||||||
|
|
||||||
|
bg_widget = QWidget(self)
|
||||||
|
bg_widget.setStyleSheet("""
|
||||||
|
background-color: white;
|
||||||
|
border-radius: 10px;
|
||||||
|
""")
|
||||||
|
main_layout.addWidget(bg_widget)
|
||||||
|
|
||||||
|
content_layout = QVBoxLayout(bg_widget)
|
||||||
|
|
||||||
|
close_button = QPushButton("✕", self)
|
||||||
|
close_button.setStyleSheet("""
|
||||||
|
QPushButton {
|
||||||
|
background-color: transparent;
|
||||||
|
color: #888888;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
QPushButton:hover {
|
||||||
|
color: #ff4d4d;
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
close_button.setFixedSize(30, 30)
|
||||||
|
close_button.clicked.connect(lambda: self.close_with_action("abort"))
|
||||||
|
content_layout.addWidget(close_button, alignment=Qt.AlignmentFlag.AlignRight)
|
||||||
|
|
||||||
|
scroll_area = QScrollArea()
|
||||||
|
scroll_area.setWidgetResizable(True)
|
||||||
|
scroll_area.setFrameShape(QFrame.Shape.NoFrame)
|
||||||
|
content_layout.addWidget(scroll_area)
|
||||||
|
|
||||||
|
scroll_content = QWidget()
|
||||||
|
scroll_layout = QVBoxLayout(scroll_content)
|
||||||
|
|
||||||
|
message_label = QLabel(self.message)
|
||||||
|
message_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
|
message_label.setFont(QFont("Arial", 14))
|
||||||
|
message_label.setStyleSheet("color: #333333; margin: 10px 0;")
|
||||||
|
message_label.setWordWrap(True)
|
||||||
|
scroll_layout.addWidget(message_label)
|
||||||
|
|
||||||
|
scroll_area.setWidget(scroll_content)
|
||||||
|
|
||||||
|
button_layout = QHBoxLayout()
|
||||||
|
content_layout.addLayout(button_layout)
|
||||||
|
|
||||||
|
sync_button = QPushButton("Sync")
|
||||||
|
sync_button.setFixedSize(120, 50)
|
||||||
|
sync_button.setFont(QFont("Arial", 12))
|
||||||
|
sync_button.setStyleSheet("""
|
||||||
|
QPushButton {
|
||||||
|
background-color: #4CAF50;
|
||||||
|
border: none;
|
||||||
|
color: white;
|
||||||
|
border-radius: 5px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
QPushButton:hover {
|
||||||
|
background-color: #45a049;
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
sync_button.clicked.connect(lambda: self.close_with_action("sync"))
|
||||||
|
button_layout.addWidget(sync_button)
|
||||||
|
|
||||||
|
abort_button = QPushButton("Abort")
|
||||||
|
abort_button.setFixedSize(120, 50)
|
||||||
|
abort_button.setFont(QFont("Arial", 12))
|
||||||
|
abort_button.setStyleSheet("""
|
||||||
|
QPushButton {
|
||||||
|
background-color: #e0e0e0;
|
||||||
|
border: none;
|
||||||
|
color: #333333;
|
||||||
|
border-radius: 5px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
QPushButton:hover {
|
||||||
|
background-color: #d0d0d0;
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
abort_button.clicked.connect(lambda: self.close_with_action("abort"))
|
||||||
|
button_layout.addWidget(abort_button)
|
||||||
|
|
||||||
|
continue_button = QPushButton("Continue Anyway")
|
||||||
|
continue_button.setFixedSize(150, 50)
|
||||||
|
continue_button.setFont(QFont("Arial", 12))
|
||||||
|
continue_button.setStyleSheet("""
|
||||||
|
QPushButton {
|
||||||
|
background-color: #ff9800;
|
||||||
|
border: none;
|
||||||
|
color: white;
|
||||||
|
border-radius: 5px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
QPushButton:hover {
|
||||||
|
background-color: #fb8c00;
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
continue_button.clicked.connect(lambda: self.close_with_action("continue"))
|
||||||
|
button_layout.addWidget(continue_button)
|
||||||
|
|
||||||
|
def close_with_action(self, action):
|
||||||
|
self.finished.emit(True, action)
|
||||||
self.close()
|
self.close()
|
||||||
|
|
||||||
|
def closeEvent(self, event):
|
||||||
|
self.finished.emit(False, "abort")
|
||||||
|
event.accept()
|
||||||
|
|
||||||
def mousePressEvent(self, event):
|
def mousePressEvent(self, event):
|
||||||
self.oldPos = event.globalPosition().toPoint()
|
self.oldPos = event.globalPosition().toPoint()
|
||||||
|
|
||||||
|
|
@ -8140,6 +8345,13 @@ class FastRegistrationPage(Page):
|
||||||
self.buttons.append(next_button)
|
self.buttons.append(next_button)
|
||||||
|
|
||||||
def create_location_section(self):
|
def create_location_section(self):
|
||||||
|
info_label = QLabel("Click the location for more info", self)
|
||||||
|
info_label.setGeometry(520, 80, 230, 20)
|
||||||
|
info_label.setStyleSheet("color: #888888; font-size: 11px; font-style: italic;")
|
||||||
|
info_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight)
|
||||||
|
info_label.show()
|
||||||
|
self.labels.append(info_label)
|
||||||
|
|
||||||
label = QPushButton(self)
|
label = QPushButton(self)
|
||||||
label.setGeometry(435, 250, 185, 75)
|
label.setGeometry(435, 250, 185, 75)
|
||||||
label.setFlat(True)
|
label.setFlat(True)
|
||||||
|
|
|
||||||
0
gui/resources/LICENSE.txt
Normal file → Executable file
0
gui/resources/fonts/open-sans.ttf
Normal file → Executable file
0
gui/resources/fonts/retro-gaming.ttf
Normal file → Executable file
0
gui/resources/images/1-country.png
Normal file → Executable file
|
Before Width: | Height: | Size: 985 B After Width: | Height: | Size: 985 B |
0
gui/resources/images/1024x760.png
Normal file → Executable file
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
0
gui/resources/images/1152x1080.png
Normal file → Executable file
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
0
gui/resources/images/1280x1024.png
Normal file → Executable file
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
0
gui/resources/images/1920x1080.png
Normal file → Executable file
|
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 7.4 KiB |
0
gui/resources/images/400x50_button.png
Normal file → Executable file
|
Before Width: | Height: | Size: 429 B After Width: | Height: | Size: 429 B |
0
gui/resources/images/540x455.png
Normal file → Executable file
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.5 KiB |
0
gui/resources/images/800x600.png
Normal file → Executable file
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
0
gui/resources/images/Dark.png
Normal file → Executable file
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
0
gui/resources/images/Dark2.png
Normal file → Executable file
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
0
gui/resources/images/Default.png
Normal file → Executable file
|
Before Width: | Height: | Size: 194 KiB After Width: | Height: | Size: 194 KiB |
0
gui/resources/images/Mesa de trabajo 1.png
Normal file → Executable file
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
0
gui/resources/images/Mesa de trabajo 10.png
Normal file → Executable file
|
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 75 KiB |
0
gui/resources/images/Mesa de trabajo 2.png
Normal file → Executable file
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 5.7 KiB |
0
gui/resources/images/Mesa de trabajo 4.png
Normal file → Executable file
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
0
gui/resources/images/Mesa de trabajo 8.png
Normal file → Executable file
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
0
gui/resources/images/UP_button.png
Normal file → Executable file
|
Before Width: | Height: | Size: 474 B After Width: | Height: | Size: 474 B |
0
gui/resources/images/app_off.png
Normal file → Executable file
|
Before Width: | Height: | Size: 339 B After Width: | Height: | Size: 339 B |
0
gui/resources/images/app_on.png
Normal file → Executable file
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
0
gui/resources/images/apply.png
Normal file → Executable file
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
0
gui/resources/images/arch.png
Normal file → Executable file
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
0
gui/resources/images/arrow-down.png
Normal file → Executable file
|
Before Width: | Height: | Size: 9.3 KiB After Width: | Height: | Size: 9.3 KiB |
0
gui/resources/images/arrow.png
Normal file → Executable file
|
Before Width: | Height: | Size: 351 B After Width: | Height: | Size: 351 B |
0
gui/resources/images/back.png
Normal file → Executable file
|
Before Width: | Height: | Size: 7.9 KiB After Width: | Height: | Size: 7.9 KiB |
0
gui/resources/images/back_transparente.png
Normal file → Executable file
|
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.8 KiB |
0
gui/resources/images/backgoud800x600.png
Normal file → Executable file
|
Before Width: | Height: | Size: 124 KiB After Width: | Height: | Size: 124 KiB |
0
gui/resources/images/background.png
Normal file → Executable file
|
Before Width: | Height: | Size: 517 KiB After Width: | Height: | Size: 517 KiB |
0
gui/resources/images/background_connected.png
Normal file → Executable file
|
Before Width: | Height: | Size: 325 KiB After Width: | Height: | Size: 325 KiB |
0
gui/resources/images/billing_off.png
Normal file → Executable file
|
Before Width: | Height: | Size: 254 B After Width: | Height: | Size: 254 B |
0
gui/resources/images/billing_on.png
Normal file → Executable file
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
0
gui/resources/images/bitcoin.png
Normal file → Executable file
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
0
gui/resources/images/bitcoin_1year.png
Normal file → Executable file
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
0
gui/resources/images/bov_off.png
Normal file → Executable file
|
Before Width: | Height: | Size: 434 B After Width: | Height: | Size: 434 B |
0
gui/resources/images/bov_on.png
Normal file → Executable file
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.5 KiB |
0
gui/resources/images/brave latest_mini.png
Normal file → Executable file
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
0
gui/resources/images/brave_button.png
Normal file → Executable file
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
0
gui/resources/images/brave_icon.png
Normal file → Executable file
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
0
gui/resources/images/browser only.png
Normal file → Executable file
|
Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 6.1 KiB |
0
gui/resources/images/browser-only.png
Normal file → Executable file
|
Before Width: | Height: | Size: 962 B After Width: | Height: | Size: 962 B |
0
gui/resources/images/browser-only_button.png
Normal file → Executable file
|
Before Width: | Height: | Size: 962 B After Width: | Height: | Size: 962 B |
0
gui/resources/images/browser_just_proxy.png
Normal file → Executable file
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
0
gui/resources/images/browser_tor.png
Normal file → Executable file
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
0
gui/resources/images/browsers_mini.png
Normal file → Executable file
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.3 KiB |
0
gui/resources/images/button230x220.png
Normal file → Executable file
|
Before Width: | Height: | Size: 622 B After Width: | Height: | Size: 622 B |
0
gui/resources/images/button_ch_zh.png
Normal file → Executable file
|
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 5.2 KiB |
0
gui/resources/images/button_fi_01.png
Normal file → Executable file
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
0
gui/resources/images/button_profile.png
Normal file → Executable file
|
Before Width: | Height: | Size: 547 B After Width: | Height: | Size: 547 B |
0
gui/resources/images/button_session_profile.png
Normal file → Executable file
|
Before Width: | Height: | Size: 433 B After Width: | Height: | Size: 433 B |
0
gui/resources/images/button_session_tor.png
Normal file → Executable file
|
Before Width: | Height: | Size: 433 B After Width: | Height: | Size: 433 B |
0
gui/resources/images/button_system_profile.png
Normal file → Executable file
|
Before Width: | Height: | Size: 433 B After Width: | Height: | Size: 433 B |
0
gui/resources/images/check.png
Normal file → Executable file
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
0
gui/resources/images/check_icon.png
Normal file → Executable file
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
0
gui/resources/images/chromium latest_mini.png
Normal file → Executable file
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
0
gui/resources/images/chromium_button.png
Normal file → Executable file
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.2 KiB |
0
gui/resources/images/chromium_icon.png
Normal file → Executable file
|
Before Width: | Height: | Size: 9.9 KiB After Width: | Height: | Size: 9.9 KiB |
0
gui/resources/images/connect.png
Normal file → Executable file
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
0
gui/resources/images/create_profile.png
Normal file → Executable file
|
Before Width: | Height: | Size: 841 B After Width: | Height: | Size: 841 B |
0
gui/resources/images/cuadro150x50.png
Normal file → Executable file
|
Before Width: | Height: | Size: 375 B After Width: | Height: | Size: 375 B |
0
gui/resources/images/cuadro400x50.png
Normal file → Executable file
|
Before Width: | Height: | Size: 429 B After Width: | Height: | Size: 429 B |
0
gui/resources/images/debian.png
Normal file → Executable file
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
0
gui/resources/images/default_browser_button.png
Normal file → Executable file
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.8 KiB |
0
gui/resources/images/default_browser_mini.png
Normal file → Executable file
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
0
gui/resources/images/default_location_button.png
Normal file → Executable file
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
0
gui/resources/images/default_location_mini.png
Normal file → Executable file
|
Before Width: | Height: | Size: 494 B After Width: | Height: | Size: 494 B |
0
gui/resources/images/delete_profile.png
Normal file → Executable file
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
0
gui/resources/images/disconnect.png
Normal file → Executable file
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
0
gui/resources/images/disconnect_system_wide.png
Normal file → Executable file
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
0
gui/resources/images/disconnected.png
Normal file → Executable file
|
Before Width: | Height: | Size: 774 B After Width: | Height: | Size: 774 B |
0
gui/resources/images/edit_profile.png
Normal file → Executable file
|
Before Width: | Height: | Size: 802 B After Width: | Height: | Size: 802 B |
0
gui/resources/images/editv.png
Normal file → Executable file
|
Before Width: | Height: | Size: 808 B After Width: | Height: | Size: 808 B |
0
gui/resources/images/eeuu garaje.png
Normal file → Executable file
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
0
gui/resources/images/eeuu_button.png
Normal file → Executable file
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
0
gui/resources/images/eeuu_mini.png
Normal file → Executable file
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
0
gui/resources/images/fedora.png
Normal file → Executable file
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
0
gui/resources/images/firefox latest_mini.png
Normal file → Executable file
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
0
gui/resources/images/firefox_button.png
Normal file → Executable file
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.5 KiB |
0
gui/resources/images/firefox_icon.png
Normal file → Executable file
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB |
0
gui/resources/images/global-acces.png
Normal file → Executable file
|
Before Width: | Height: | Size: 996 B After Width: | Height: | Size: 996 B |
0
gui/resources/images/hdtor_is_1.png
Normal file → Executable file
|
Before Width: | Height: | Size: 117 KiB After Width: | Height: | Size: 117 KiB |
0
gui/resources/images/hdtor_md_cu.png
Normal file → Executable file
|
Before Width: | Height: | Size: 140 KiB After Width: | Height: | Size: 140 KiB |
0
gui/resources/images/hdtor_mini_md_cu.png
Normal file → Executable file
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
0
gui/resources/images/hdtor_mini_nl_li.png
Normal file → Executable file
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.7 KiB |
0
gui/resources/images/hdtor_mini_us_oh.png
Normal file → Executable file
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
0
gui/resources/images/hdtor_nl_li.png
Normal file → Executable file
|
Before Width: | Height: | Size: 134 KiB After Width: | Height: | Size: 134 KiB |
0
gui/resources/images/hdtor_sg_02.png
Normal file → Executable file
|
Before Width: | Height: | Size: 162 KiB After Width: | Height: | Size: 162 KiB |
0
gui/resources/images/hdtor_us_ny.png
Normal file → Executable file
|
Before Width: | Height: | Size: 120 KiB After Width: | Height: | Size: 120 KiB |
0
gui/resources/images/hdtor_us_oh.png
Normal file → Executable file
|
Before Width: | Height: | Size: 148 KiB After Width: | Height: | Size: 148 KiB |
0
gui/resources/images/hdtor_us_wa.png
Normal file → Executable file
|
Before Width: | Height: | Size: 156 KiB After Width: | Height: | Size: 156 KiB |
0
gui/resources/images/hidetor.png
Normal file → Executable file
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
0
gui/resources/images/hidetor_button.png
Normal file → Executable file
|
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 4.7 KiB |
0
gui/resources/images/hong kong garaje.png
Normal file → Executable file
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
0
gui/resources/images/hong kong_button.png
Normal file → Executable file
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
0
gui/resources/images/hong kong_mini.png
Normal file → Executable file
|
Before Width: | Height: | Size: 772 B After Width: | Height: | Size: 772 B |
0
gui/resources/images/hv-icon-128x128.png
Normal file → Executable file
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
0
gui/resources/images/hv-icon-16x16.png
Normal file → Executable file
|
Before Width: | Height: | Size: 924 B After Width: | Height: | Size: 924 B |
0
gui/resources/images/hv-icon-22x22.png
Normal file → Executable file
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |