diff --git a/vanilla_installer/core/timezones.py b/vanilla_installer/core/timezones.py
index 83f60858..c6f5e411 100644
--- a/vanilla_installer/core/timezones.py
+++ b/vanilla_installer/core/timezones.py
@@ -1,11 +1,13 @@
import datetime
+import logging
import requests
-from gi.repository import GnomeDesktop
-from gi.repository import GWeather
+from gi.repository import GLib, GWeather
from zoneinfo import ZoneInfo
-regions = {}
+logger = logging.getLogger("VanillaInstaller::Timezones")
+
+regions: dict[str, dict[str, dict[str, str]]] = {}
world = GWeather.Location.get_world()
parents = []
base = world
@@ -20,7 +22,9 @@
regions[current_region][child.get_name()] = {}
current_country = child.get_name()
elif child.get_level() == GWeather.LocationLevel.CITY:
- regions[current_region][current_country][child.get_city_name()] = child.get_timezone_str()
+ regions[current_region][current_country][child.get_city_name()] = (
+ child.get_timezone_str()
+ )
if child.next_child(None) is not None:
parents.append(child)
@@ -34,21 +38,37 @@
all_timezones = dict(sorted(regions.items()))
-def get_location():
+
+def get_location(callback=None):
+ logger.info("Trying to retrieve timezone automatically")
try:
res = requests.get("http://ip-api.com/json?fields=49344", timeout=3).json()
if res["status"] != "success":
raise Exception(f"get_location: request failed with message '{res['message']}'")
nearest = world.find_nearest_city(res["lat"], res["lon"])
except Exception as e:
- print(e)
+ logger.error(f"Failed to retrieve timezone: {e}")
nearest = None
- return nearest
+ logger.info("Done retrieving timezone")
+ if callback:
+ logger.info("Running callback")
+ GLib.idle_add(callback, nearest)
-def get_timezone_preview(tzname):
- timezone = ZoneInfo(tzname)
- now = datetime.datetime.now(timezone)
- return now.strftime("%H:%M"), now.strftime("%A, %d %B %Y")
+tz_preview_cache: dict[str, tuple[str, str]] = {}
+
+
+def get_timezone_preview(tzname):
+ if tzname in tz_preview_cache:
+ return tz_preview_cache[tzname]
+ else:
+ timezone = ZoneInfo(tzname)
+ now = datetime.datetime.now(timezone)
+ now_str = (
+ "%02d:%02d" % (now.hour, now.minute),
+ now.strftime("%A, %d %B %Y"),
+ )
+ tz_preview_cache[tzname] = now_str
+ return now_str
diff --git a/vanilla_installer/defaults/disk.py b/vanilla_installer/defaults/disk.py
index da565d63..b5589e8a 100644
--- a/vanilla_installer/defaults/disk.py
+++ b/vanilla_installer/defaults/disk.py
@@ -578,7 +578,7 @@ def __init__(self, window, partition_recipe, **kwargs):
)
)
self.group_partitions.add(entry)
-
+
if "auto" in partition_recipe:
for vg in partition_recipe["auto"]["vgs_to_remove"]:
entry = Adw.ActionRow()
diff --git a/vanilla_installer/defaults/network.py b/vanilla_installer/defaults/network.py
index c744f72c..29cbf5db 100644
--- a/vanilla_installer/defaults/network.py
+++ b/vanilla_installer/defaults/network.py
@@ -22,7 +22,7 @@
from operator import attrgetter
from threading import Lock, Timer
-from gi.repository import NM, NMA4, Adw, Gtk, GLib
+from gi.repository import NM, NMA4, Adw, GLib, Gtk
from vanilla_installer.utils.run_async import RunAsync
@@ -115,9 +115,7 @@ def refresh_ui(self):
if not secure:
self.secure_icon.set_from_icon_name("warning-small-symbolic")
else:
- self.secure_icon.set_from_icon_name(
- "network-wireless-encrypted-symbolic"
- )
+ self.secure_icon.set_from_icon_name("network-wireless-encrypted-symbolic")
self.secure_icon.set_visible(secure is not None)
if tooltip is not None:
@@ -254,6 +252,7 @@ def __init__(self, window, distro_info, key, step, **kwargs):
self.__key = key
self.__step = step
self.__nm_client = NM.Client.new()
+ self.__step_num = step["num"]
self.__devices = []
self.__wired_children = []
@@ -280,9 +279,12 @@ def __init__(self, window, distro_info, key, step, **kwargs):
self.__nm_client.connect("device-added", self.__add_new_device)
self.__nm_client.connect("device-added", self.__remove_device)
self.btn_next.connect("clicked", self.__window.next)
- self.connect("realize", self.__try_skip_page)
+ self.__window.carousel.connect("page-changed", self.__try_skip_page)
+
+ def __try_skip_page(self, carousel=None, idx=None):
+ if idx is not None and idx != self.__step_num:
+ return
- def __try_skip_page(self, data):
# Skip page if already connected to the internet
if self.has_eth_connection or self.has_wifi_connection:
self.__window.next()
@@ -316,9 +318,7 @@ def __get_network_devices(self):
eth_devices += 1
elif device_type == NM.DeviceType.WIFI:
device.connect("state-changed", self.__on_state_changed)
- self.has_wifi_connection = (
- device.get_active_connection() is not None
- )
+ self.has_wifi_connection = device.get_active_connection() is not None
self.__refresh_wifi_list(device)
wifi_devices += 1
else:
@@ -419,7 +419,7 @@ def __poll_wifi_scan(self, conn: NM.DeviceWifi, last_known_scan: int):
GLib.idle_add(self.__refresh_wifi_list, conn)
def __refresh_wifi_list(self, conn: NM.DeviceWifi):
- networks = {}
+ networks: dict[str, list[NM.AccessPoint]] = {}
for ap in conn.get_access_points():
ssid = ap.get_ssid()
if ssid is None:
diff --git a/vanilla_installer/defaults/timezone.py b/vanilla_installer/defaults/timezone.py
index 8e0ef7ce..b8210944 100644
--- a/vanilla_installer/defaults/timezone.py
+++ b/vanilla_installer/defaults/timezone.py
@@ -14,18 +14,21 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
+import logging
import re
+import threading
import unicodedata
-
-from gi.repository import Adw, Gtk, GLib
from gettext import gettext as _
+from gi.repository import Adw, GLib, Gtk
+
from vanilla_installer.core.timezones import (
all_timezones,
get_location,
get_timezone_preview,
)
-from vanilla_installer.utils.run_async import RunAsync
+
+logger = logging.getLogger("VanillaInstaller::Timezone")
@Gtk.Template(resource_path="/org/vanillaos/Installer/gtk/widget-timezone.ui")
@@ -35,30 +38,22 @@ class TimezoneRow(Adw.ActionRow):
select_button = Gtk.Template.Child()
country_label = Gtk.Template.Child()
- def __init__(self, title, subtitle, tz_name, parent, **kwargs):
+ def __init__(self, title, subtitle, tz_name, toggled_callback, parent_expander, **kwargs):
super().__init__(**kwargs)
self.title = title
self.subtitle = subtitle
- self.parent = parent
self.tz_name = tz_name
-
- tz_time, tz_date = get_timezone_preview(tz_name)
+ self.parent_expander = parent_expander
self.set_title(title)
- self.set_subtitle(f"{tz_time} • {tz_date}")
self.country_label.set_label(tz_name)
- self.select_button.connect("toggled", self.__on_check_button_toggled)
+ self.select_button.connect("toggled", toggled_callback, self)
+ self.parent_expander.connect("notify::expanded", self.update_time_preview)
- def __on_check_button_toggled(self, widget):
- tz_split = self.tz_name.split("/", 1)
- self.parent.selected_timezone["region"] = tz_split[0]
- self.parent.selected_timezone["zone"] = tz_split[1]
- self.parent.current_tz_label.set_label(self.tz_name)
- self.parent.current_location_label.set_label(
- _("(at %s, %s)") % (self.title, self.subtitle)
- )
- self.parent.timezone_verify()
+ def update_time_preview(self, *args):
+ tz_time, tz_date = get_timezone_preview(self.tz_name)
+ self.set_subtitle(f"{tz_time} • {tz_date}")
@Gtk.Template(resource_path="/org/vanillaos/Installer/gtk/default-timezone.ui")
@@ -74,11 +69,15 @@ class VanillaDefaultTimezone(Adw.Bin):
search_controller = Gtk.EventControllerKey.new()
selected_timezone = {"region": "Europe", "zone": None}
- expanders_list = {
- country: region
- for region, countries in all_timezones.items()
- for country in countries.keys()
- }
+ expanders_list = dict(
+ sorted(
+ {
+ country: region
+ for region, countries in all_timezones.items()
+ for country in countries.keys()
+ }.items()
+ )
+ )
def __init__(self, window, distro_info, key, step, **kwargs):
super().__init__(**kwargs)
@@ -86,6 +85,7 @@ def __init__(self, window, distro_info, key, step, **kwargs):
self.__distro_info = distro_info
self.__key = key
self.__step = step
+ self.__step_num = step["num"]
self.__expanders = []
self.__tz_entries = []
@@ -98,12 +98,24 @@ def __init__(self, window, distro_info, key, step, **kwargs):
self.search_controller.connect("key-released", self.__on_search_key_pressed)
self.entry_search_timezone.add_controller(self.search_controller)
- def timezone_verify(self, *args):
- valid = (
- self.selected_timezone["region"]
- and self.selected_timezone["zone"] is not None
- )
- self.btn_next.set_sensitive(valid)
+ def timezone_verify(self, carousel=None, idx=None):
+ if idx is not None and idx != self.__step_num:
+ return
+
+ def timezone_verify_callback(result, *args):
+ if result:
+ current_city = result.get_city_name()
+ current_country = result.get_country_name()
+ for entry in self.__tz_entries:
+ if current_city == entry.title and current_country == entry.subtitle:
+ self.selected_timezone["zone"] = current_city
+ self.selected_timezone["region"] = current_country
+ entry.select_button.set_active(True)
+ return
+ self.btn_next.set_sensitive(True)
+
+ thread = threading.Thread(target=get_location, args=(timezone_verify_callback,))
+ thread.start()
def get_finals(self):
try:
@@ -118,11 +130,7 @@ def get_finals(self):
def __on_search_key_pressed(self, *args):
def remove_accents(msg: str):
- out = (
- unicodedata.normalize("NFD", msg)
- .encode("ascii", "ignore")
- .decode("utf-8")
- )
+ out = unicodedata.normalize("NFD", msg).encode("ascii", "ignore").decode("utf-8")
return str(out)
search_entry = self.entry_search_timezone.get_text().lower()
@@ -147,48 +155,30 @@ def remove_accents(msg: str):
visible_entries = 0
current_country = entry.subtitle
current_expander += 1
- visible_entries += 1 if match else 0
+ visible_entries += match
+
+ def __on_row_toggle(self, __check_button, widget):
+ tz_split = widget.tz_name.split("/", 1)
+ self.selected_timezone["region"] = tz_split[0]
+ self.selected_timezone["zone"] = tz_split[1]
+ self.current_tz_label.set_label(widget.tz_name)
+ self.current_location_label.set_label(_("(at %s, %s)") % (widget.title, widget.subtitle))
+ self.btn_next.set_sensitive(True)
def __generate_timezone_list_widgets(self):
- def __populate_expanders():
- widgets = {}
- first_elem = None
- for country, region in dict(sorted(self.expanders_list.items())).items():
- if len(all_timezones[region][country]) > 0:
- country_tz_expander_row = Adw.ExpanderRow.new()
- country_tz_expander_row.set_title(country)
- country_tz_expander_row.set_subtitle(region)
- widgets[country_tz_expander_row] = []
- for city, tzname in sorted(all_timezones[region][country].items()):
- timezone_row = TimezoneRow(city, country, tzname, self)
- if first_elem is None:
- first_elem = timezone_row
- else:
- timezone_row.select_button.set_group(
- first_elem.select_button
- )
- widgets[country_tz_expander_row].append(timezone_row)
- return widgets
-
- def __set_located_timezone(result, *args):
- if not result:
- return
- current_city = result.get_city_name()
- current_country = result.get_country_name()
- for entry in self.__tz_entries:
- if current_city == entry.title and current_country == entry.subtitle:
- self.selected_timezone["zone"] = current_city
- self.selected_timezone["region"] = current_country
- entry.select_button.set_active(True)
- return
-
- def __callback(widgets, *args):
- for expander, timezone_rows in widgets.items():
+ def __populate_expander(expander, region, country, *args):
+ for city, tzname in all_timezones[region][country].items():
+ timezone_row = TimezoneRow(city, country, tzname, self.__on_row_toggle, expander)
+ self.__tz_entries.append(timezone_row)
+ if len(self.__tz_entries) > 0:
+ timezone_row.select_button.set_group(self.__tz_entries[0].select_button)
+ expander.add_row(timezone_row)
+
+ for country, region in self.expanders_list.items():
+ if len(all_timezones[region][country]) > 0:
+ expander = Adw.ExpanderRow.new()
+ expander.set_title(country)
+ expander.set_subtitle(region)
self.all_timezones_group.add(expander)
self.__expanders.append(expander)
- for timezone_row in timezone_rows:
- expander.add_row(timezone_row)
- self.__tz_entries.append(timezone_row)
- RunAsync(get_location, __set_located_timezone)
-
- RunAsync(__populate_expanders, __callback)
+ GLib.idle_add(__populate_expander, expander, region, country)
diff --git a/vanilla_installer/defaults/welcome.py b/vanilla_installer/defaults/welcome.py
index fba45711..2b9f6305 100644
--- a/vanilla_installer/defaults/welcome.py
+++ b/vanilla_installer/defaults/welcome.py
@@ -19,6 +19,7 @@
from vanilla_installer.windows.dialog_recovery import VanillaRecoveryDialog
from vanilla_installer.windows.dialog_poweroff import VanillaPoweroffDialog
+
@Gtk.Template(resource_path="/org/vanillaos/Installer/gtk/default-welcome.ui")
class VanillaDefaultWelcome(Adw.Bin):
__gtype_name__ = "VanillaDefaultWelcome"
@@ -37,7 +38,7 @@ def __init__(self, window, distro_info, key, step, **kwargs):
distro_name = self.__distro_info.get("name", "Vanilla OS")
distro_logo = self.__distro_info.get("logo", "org.vanillaos.Installer-flower")
-
+
self.status_page.set_icon_name(distro_logo)
self.status_page.set_title(f"Welcome to {distro_name}!")
diff --git a/vanilla_installer/windows/main_window.py b/vanilla_installer/windows/main_window.py
index ee178ab4..8f10a59d 100644
--- a/vanilla_installer/windows/main_window.py
+++ b/vanilla_installer/windows/main_window.py
@@ -16,8 +16,8 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
-import logging
import json
+import logging
import os
from gi.repository import Adw, Gtk
@@ -62,14 +62,12 @@ def __connect_signals(self):
self.btn_back.connect("clicked", self.back)
self.carousel.connect("page-changed", self.__on_page_changed)
self.__builder.widgets[-1].btn_next.connect("clicked", self.update_finals)
- self.__view_confirm.connect(
- "installation-confirmed", self.on_installation_confirmed
- )
+ self.__view_confirm.connect("installation-confirmed", self.on_installation_confirmed)
def __build_ui(self):
if "VANILLA_FORCE_TOUR" not in os.environ:
for widget in self.__builder.widgets:
- logger.info(f"(%s) Adding widget to carousel", widget)
+ logger.info("(%s) Adding widget to carousel", widget.__gtype_name__)
self.carousel.append(widget)
else:
self.__on_page_changed()
@@ -82,16 +80,16 @@ def __on_page_changed(self, *args):
cur_index = self.carousel.get_position()
page = self.carousel.get_nth_page(cur_index)
- logger.info(f"(%s) Page is changing...", page)
+ logger.info("(%s) Page is changing...", page.__gtype_name__)
if page not in [self.__view_progress, self.__view_done]:
- logger.info(f"(%s) It is not a final page", page)
+ logger.info("(%s) It is not a final page", page.__gtype_name__)
self.btn_back.set_visible(cur_index != 0.0)
self.btn_back.set_sensitive(cur_index != 0.0)
self.carousel_indicator_dots.set_visible(cur_index != 0.0)
return
- logger.info(f"(%s) It is a final page", page)
+ logger.info("(%s) It is a final page", page.__gtype_name__)
self.btn_back.set_visible(False)
self.btn_back.set_sensitive(False)
@@ -99,7 +97,7 @@ def __on_page_changed(self, *args):
# keep the btn_back button locked if this is the last page
if page == self.__view_done:
- logger.info(f"(%s) It is the DONE page", page)
+ logger.info("(%s) It is the DONE page", page.__gtype_name__)
return
def update_finals(self, *args):
@@ -123,10 +121,14 @@ def on_installation_confirmed(self, *args):
def next(self, widget=None, fn=None, *args):
logger.info("Going to next page")
+ cur_index = self.carousel.get_position()
+ logger.info(f"Next page is {int(cur_index + 1)}")
+
if fn is not None:
+ logger.info("Executing page's custom function")
fn()
+ logger.info("Finished executing page's custom function")
- cur_index = self.carousel.get_position()
page = self.carousel.get_nth_page(cur_index + 1)
self.carousel.scroll_to(page, True)
@@ -134,6 +136,8 @@ def back(self, *args):
logger.info("Going to previous page")
cur_index = self.carousel.get_position()
+ logger.info(f"Previous page is {int(cur_index - 1)}")
+
page = self.carousel.get_nth_page(cur_index - 1)
self.carousel.scroll_to(page, True)