Update translations and new strings in po files

- Adjust line mappings in .po files for translation strings
- Add new translations for custom partitioning, password strength, keyboard setup, installer steps, and network setup strings
- Update POT-Creation-Date in Romanian and Slovak .po files
- Include various new UI strings for installation and try-live options
This commit is contained in:
ericbsd
2025-07-12 21:24:33 -03:00
parent 9db6c513c9
commit 391904b744
58 changed files with 10263 additions and 5540 deletions
+5 -9
View File
@@ -2,12 +2,8 @@ import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk
from install_station.partition import bios_or_uefi
from install_station.data import InstallationData
import gettext
from install_station.data import InstallationData, get_text
gettext.bindtextdomain('install-station', '/usr/local/share/locale')
gettext.textdomain('install-station')
_ = gettext.gettext
cssProvider = Gtk.CssProvider()
cssProvider.load_from_path('/usr/local/lib/install-station/ghostbsd-style.css')
@@ -122,7 +118,7 @@ class BootManager:
scheme = cls._get_partition_scheme()
# Create title header
title = Gtk.Label(label=_('Boot Option'), name="Header")
title = Gtk.Label(label=get_text('Boot Option'), name="Header")
title.set_property("height-request", 50)
cls.vbox1.pack_start(title, False, False, 0)
@@ -136,7 +132,7 @@ class BootManager:
bbox1.show()
# rEFInd boot manager option
cls.refind = Gtk.RadioButton(label=_("Setup rEFInd boot manager"))
cls.refind = Gtk.RadioButton(label=get_text("Setup rEFInd boot manager"))
bbox1.pack_start(cls.refind, False, True, 10)
cls.refind.connect("toggled", cls.boot_manager_selection, "refind")
cls.refind.show()
@@ -150,7 +146,7 @@ class BootManager:
# FreeBSD boot manager option
cls.bsd = Gtk.RadioButton.new_with_label_from_widget(
cls.refind,
_("Setup FreeBSD boot manager")
get_text("Setup FreeBSD boot manager")
)
bbox1.pack_start(cls.bsd, False, True, 10)
cls.bsd.connect("toggled", cls.boot_manager_selection, "bsd")
@@ -165,7 +161,7 @@ class BootManager:
# Native loader option (always available)
cls.none = Gtk.RadioButton.new_with_label_from_widget(
cls.bsd,
_("FreeBSD {loader} loader only").format(loader=loader)
get_text("FreeBSD {loader} loader only").format(loader=loader)
)
bbox1.pack_start(cls.none, False, True, 10)
cls.none.connect("toggled", cls.boot_manager_selection, "none")
+25 -24
View File
@@ -1,5 +1,6 @@
import re
import warnings
from install_station.data import get_text
def lower_case(text: str) -> bool:
@@ -104,53 +105,53 @@ def password_strength(password, label3):
]
)
if ' ' in password or '\t' in password:
label3.set_text("Space not allowed")
label3.set_text(get_text("Space not allowed"))
elif len(password) <= 4:
label3.set_text("Super Weak")
label3.set_text(get_text("Super Weak"))
elif len(password) <= 8 and same_character_type:
label3.set_text("Super Weak")
label3.set_text(get_text("Super Weak"))
elif len(password) <= 8 and mix_character:
label3.set_text("Very Weak")
label3.set_text(get_text("Very Weak"))
elif len(password) <= 8 and lower_upper_number(password):
label3.set_text("Fairly Weak")
label3.set_text(get_text("Fairly Weak"))
elif len(password) <= 8 and all_character(password):
label3.set_text("Weak")
label3.set_text(get_text("Weak"))
elif len(password) <= 12 and same_character_type:
label3.set_text("Very Weak")
label3.set_text(get_text("Very Weak"))
elif len(password) <= 12 and mix_character:
label3.set_text("Fairly Weak")
label3.set_text(get_text("Fairly Weak"))
elif len(password) <= 12 and lower_upper_number(password):
label3.set_text("Weak")
label3.set_text(get_text("Weak"))
elif len(password) <= 12 and all_character(password):
label3.set_text("Strong")
label3.set_text(get_text("Strong"))
elif len(password) <= 16 and same_character_type:
label3.set_text("Fairly Weak")
label3.set_text(get_text("Fairly Weak"))
elif len(password) <= 16 and mix_character:
label3.set_text("Weak")
label3.set_text(get_text("Weak"))
elif len(password) <= 16 and lower_upper_number(password):
label3.set_text("Strong")
label3.set_text(get_text("Strong"))
elif len(password) <= 16 and all_character(password):
label3.set_text("Fairly Strong")
label3.set_text(get_text("Fairly Strong"))
elif len(password) <= 20 and same_character_type:
label3.set_text("Weak")
label3.set_text(get_text("Weak"))
elif len(password) <= 20 and mix_character:
label3.set_text("Strong")
label3.set_text(get_text("Strong"))
elif len(password) <= 20 and lower_upper_number(password):
label3.set_text("Fairly Strong")
label3.set_text(get_text("Fairly Strong"))
elif len(password) <= 20 and all_character(password):
label3.set_text("Very Strong")
label3.set_text(get_text("Very Strong"))
elif len(password) <= 24 and same_character_type:
label3.set_text("Strong")
label3.set_text(get_text("Strong"))
elif len(password) <= 24 and mix_character:
label3.set_text("Fairly Strong")
label3.set_text(get_text("Fairly Strong"))
elif len(password) <= 24 and lower_upper_number(password):
label3.set_text("Very Strong")
label3.set_text(get_text("Very Strong"))
elif len(password) <= 24 and all_character(password):
label3.set_text("Super Strong")
label3.set_text(get_text("Super Strong"))
elif same_character_type:
label3.set_text("Fairly Strong")
label3.set_text(get_text("Fairly Strong"))
else:
label3.set_text("Super Strong")
label3.set_text(get_text("Super Strong"))
def deprecated(*, version: str, reason: str):
+14 -18
View File
@@ -11,13 +11,9 @@ from install_station.partition import (
CreatePartition,
CreateLabel
)
from install_station.data import InstallationData, logo
from install_station.data import InstallationData, logo, get_text
from install_station.interface_controller import Button
import gettext
gettext.bindtextdomain('install-station', '/usr/local/share/locale')
gettext.textdomain('install-station')
_ = gettext.gettext
bios_type = bios_or_uefi()
@@ -249,19 +245,19 @@ class PartitionManager:
bbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, homogeneous=True, spacing=10)
bbox.set_border_width(5)
bbox.set_spacing(10)
cls.create_bt = Gtk.Button(label="Create")
cls.create_bt = Gtk.Button(label=get_text("Create"))
cls.create_bt.connect("clicked", cls.create_partition)
cls.create_bt.set_sensitive(False)
bbox.pack_start(cls.create_bt, True, True, 0)
cls.delete_bt = Gtk.Button(label="Delete")
cls.delete_bt = Gtk.Button(label=get_text("Delete"))
cls.delete_bt.connect("clicked", cls.delete_partition)
cls.delete_bt.set_sensitive(False)
bbox.pack_start(cls.delete_bt, True, True, 0)
cls.revert_bt = Gtk.Button(label="Revert")
cls.revert_bt = Gtk.Button(label=get_text("Revert"))
cls.revert_bt.connect("clicked", cls.revert_change)
cls.revert_bt.set_sensitive(False)
bbox.pack_start(cls.revert_bt, True, True, 0)
cls.auto_bt = Gtk.Button(label="Auto")
cls.auto_bt = Gtk.Button(label=get_text("Auto"))
cls.auto_bt.connect("clicked", cls.auto_partition)
cls.auto_bt.set_sensitive(False)
bbox.pack_start(cls.auto_bt, True, True, 0)
@@ -329,7 +325,7 @@ class PartitionManager:
"""
free_space = int(size)
cls.window = Gtk.Window()
cls.window.set_title(title="Add Partition")
cls.window.set_title(title=get_text("Add Partition"))
cls.window.set_border_width(0)
cls.window.set_size_request(480, 200)
cls.window.set_icon_from_file(logo)
@@ -343,9 +339,9 @@ class PartitionManager:
# Create partition configuration table
table = Gtk.Table(1, 2, True)
label1 = Gtk.Label(label="Type:")
label2 = Gtk.Label(label="Size(MB):")
label3 = Gtk.Label(label="Mount point:")
label1 = Gtk.Label(label=get_text("Type:"))
label2 = Gtk.Label(label=get_text("Size(MB):"))
label3 = Gtk.Label(label=get_text("Mount point:"))
cls.fs_type = Gtk.ComboBoxText()
cls.fs_type.append_text('ZFS')
cls.fs_type.append_text('SWAP')
@@ -497,7 +493,7 @@ class PartitionManager:
partition schemes for the selected disk.
"""
cls.window = Gtk.Window()
cls.window.set_title("Partition Scheme")
cls.window.set_title(get_text("Partition Scheme"))
cls.window.set_border_width(0)
cls.window.set_size_request(400, 150)
cls.window.set_icon_from_file(logo)
@@ -516,8 +512,8 @@ class PartitionManager:
# Adding a combo box to selecting MBR or GPT scheme.
cls.scheme = 'GPT'
scheme_box = Gtk.ComboBoxText()
scheme_box.append_text("GPT: GUID Partition Table")
scheme_box.append_text("MBR: DOS Partition")
scheme_box.append_text(get_text("GPT: GUID Partition Table"))
scheme_box.append_text(get_text("MBR: DOS Partition"))
scheme_box.connect('changed', cls.scheme_selection)
scheme_box.set_active(0)
table = Gtk.Table(1, 2, True)
@@ -567,7 +563,7 @@ class PartitionManager:
"""
free_space = int(cls.size)
cls.window = Gtk.Window()
cls.window.set_title("Add Partition")
cls.window.set_title(get_text("Add Partition"))
cls.window.set_border_width(0)
cls.window.set_size_request(400, 150)
cls.window.set_icon_from_file(logo)
@@ -581,7 +577,7 @@ class PartitionManager:
# Create Partition slice
table = Gtk.Table(1, 2, True)
label1 = Gtk.Label(label="Size(MB):")
label1 = Gtk.Label(label=get_text("Size(MB):"))
adj = Gtk.Adjustment(free_space, 0, free_space, 1, 100, 0)
cls.entry = Gtk.SpinButton(adjustment=adj, numeric=True)
cls.entry.set_numeric(True)
+31
View File
@@ -1,9 +1,12 @@
"""
Contains the data class and some commonly use variables
"""
import os
import gettext
be_name = "default"
logo = "/usr/local/lib/install-station/image/logo.png"
gif_logo = "/usr/local/lib/install-station/image/G_logo.gif"
pc_sysinstall = "/usr/local/sbin/pc-sysinstall"
query = "sh /usr/local/lib/install-station/backend-query"
tmp = "/tmp"
@@ -50,6 +53,13 @@ class InstallationData:
language: str = ""
language_code: str = ""
# Keyboard configuration
keyboard_layout: str = ""
keyboard_layout_code: str = ""
keyboard_variant: str = ""
keyboard_model: str = ""
keyboard_model_code: str = ""
# Boot manager configuration
boot_manager: str = ""
@@ -73,5 +83,26 @@ class InstallationData:
cls.filesystem_type = ""
cls.language = ""
cls.language_code = ""
cls.keyboard_layout = ""
cls.keyboard_layout_code = ""
cls.keyboard_variant = ""
cls.keyboard_model = ""
cls.keyboard_model_code = ""
cls.boot_manager = ""
cls.network_config = {}
def get_text(text):
"""
Global translation function that always returns current language translation.
Args:
text: Text to translate
Returns:
str: Translated text in current language
"""
# Force reload of translations for current language
gettext.bindtextdomain('install-station', '/usr/local/share/locale')
gettext.textdomain('install-station')
return gettext.gettext(text)
+6 -13
View File
@@ -4,20 +4,17 @@ import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
from subprocess import Popen
import gettext
from install_station.data import get_text
gettext.bindtextdomain('install-station', '/usr/local/share/locale')
gettext.textdomain('install-station')
_ = gettext.gettext
lyrics = _("""Installation is complete. You need to restart the
lyrics = get_text("""Installation is complete. You need to restart the
computer in order to use the new installation.
You can continue to use this live media, although
any changes you make or documents you save will
not be preserved on reboot.""")
class PyApp:
class EndWindow:
@classmethod
def on_reboot(cls, _widget):
Popen('shutdown -r now', shell=True)
@@ -31,7 +28,7 @@ class PyApp:
window = Gtk.Window()
window.set_border_width(8)
window.connect("destroy", Gtk.main_quit)
window.set_title(_("Installation Completed"))
window.set_title(get_text("Installation Completed"))
window.set_icon_from_file("/usr/local/lib/install-station/image/logo.png")
box1 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, homogeneous=False, spacing=0)
window.add(box1)
@@ -47,15 +44,11 @@ class PyApp:
box1.pack_start(box2, False, True, 0)
box2.show()
table = Gtk.Table(1, 2, True)
restart = Gtk.Button(label=_("Restart"))
restart = Gtk.Button(label=get_text("Restart"))
restart.connect("clicked", self.on_reboot)
continue_button = Gtk.Button(label=_("Continue"))
continue_button = Gtk.Button(label=get_text("Continue"))
continue_button.connect("clicked", self.on_close)
table.attach(continue_button, 0, 1, 0, 1)
table.attach(restart, 1, 2, 0, 1)
box2.pack_start(table, True, True, 0)
window.show_all()
PyApp()
Gtk.main()
+8 -14
View File
@@ -1,11 +1,7 @@
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
import gettext
gettext.bindtextdomain('install-station', '/usr/local/share/locale')
gettext.textdomain('install-station')
_ = gettext.gettext
from install_station.data import get_text
class ErrorWindow:
@@ -18,7 +14,7 @@ class ErrorWindow:
window = Gtk.Window()
window.set_border_width(8)
window.connect("destroy", Gtk.main_quit)
window.set_title(_("Installation Error"))
window.set_title(get_text("Installation Error"))
# window.set_icon_from_file("/usr/local/lib/install-station/image/logo.png")
box1 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, homogeneous=False, spacing=0)
window.add(box1)
@@ -29,13 +25,15 @@ class ErrorWindow:
box2.show()
title = Gtk.Label()
title.set_use_markup(True)
title_text = _("Installation has failed!")
title_text = get_text("Installation has failed!")
title.set_markup(f'<b><span size="larger">{title_text}</span></b>')
label = Gtk.Label()
label.set_use_markup(True)
url = 'https://github.com/ghostbsd/ghostbsd-src/issues/new/choose'
anchor = f"<a href='{url}'>{_('GhostBSD issue system')}</a>"
message = _("Please report the issue to {anchor}, and \nbe sure to provide /tmp/.pc-sysinstall/pc-sysinstall.log.").format(anchor=anchor)
anchor = f"<a href='{url}'>{get_text('GhostBSD issue system')}</a>"
message = get_text(
"Please report the issue to {anchor}, and \nbe sure to provide /tmp/.pc-sysinstall/pc-sysinstall.log."
).format(anchor=anchor)
label.set_markup(message)
box2.pack_start(title, True, True, 0)
box2.pack_start(label, True, True, 0)
@@ -44,12 +42,8 @@ class ErrorWindow:
box1.pack_start(box2, False, True, 0)
box2.show()
table = Gtk.Table(n_rows=1, n_columns=2, homogeneous=True)
ok = Gtk.Button(label=_("Ok"))
ok = Gtk.Button(label=get_text("Ok"))
ok.connect("clicked", self.on_close)
table.attach(ok, 0, 2, 0, 1)
box2.pack_start(table, True, True, 0)
window.show_all()
ErrorWindow()
Gtk.main()
+23 -16
View File
@@ -4,16 +4,23 @@ from gi.repository import Gtk, GLib, Gdk
import threading
from subprocess import Popen, PIPE, STDOUT
from time import sleep
from install_station.partition import delete_partition, destroy_partition, add_partition
from install_station.partition import (
delete_partition,
destroy_partition,
add_partition
)
from install_station.create_cfg import Configuration
from install_station.end import EndWindow
from install_station.error import ErrorWindow
from install_station.window import Window
from install_station.data import InstallationData, installation_config, pc_sysinstall
import sys
import gettext
from install_station.data import (
gif_logo,
InstallationData,
installation_config,
pc_sysinstall,
get_text
)
gettext.bindtextdomain('install-station', '/usr/local/share/locale')
gettext.textdomain('install-station')
_ = gettext.gettext
cssProvider = Gtk.CssProvider()
cssProvider.load_from_path('/usr/local/lib/install-station/ghostbsd-style.css')
@@ -36,21 +43,21 @@ def update_progress(progressbar, text):
def read_output(command, progressbar):
GLib.idle_add(update_progress, progressbar, _("Creating ghostbsd_installation.cfg"))
GLib.idle_add(update_progress, progressbar, get_text("Creating ghostbsd_installation.cfg"))
Configuration.create_cfg()
sleep(1)
if InstallationData.delete:
GLib.idle_add(update_progress, progressbar, _("Deleting partition"))
GLib.idle_add(update_progress, progressbar, get_text("Deleting partition"))
delete_partition()
sleep(1)
# destroy disk partition and create scheme
if InstallationData.destroy:
GLib.idle_add(update_progress, progressbar, _("Creating disk partition"))
GLib.idle_add(update_progress, progressbar, get_text("Creating disk partition"))
destroy_partition()
sleep(1)
# create partition
if InstallationData.create:
GLib.idle_add(update_progress, progressbar, _("Creating new partitions"))
GLib.idle_add(update_progress, progressbar, get_text("Creating new partitions"))
add_partition()
sleep(1)
progressbar_text = None
@@ -69,9 +76,9 @@ def read_output(command, progressbar):
# filer.close
print(progressbar_text)
if progressbar_text.rstrip() == "Installation finished!":
Popen(f'python {install-station_dir}/end.py', shell=True, close_fds=True)
EndWindow()
else:
Popen(f'python {install-station_dir}/error.py', shell=True, close_fds=True)
ErrorWindow()
Window.hide()
@@ -80,7 +87,7 @@ class InstallWindow:
def __init__(self):
self.vBox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, homogeneous=False, spacing=0)
self.vBox.show()
label = Gtk.Label(label=_("Installation in progress"), name="Header")
label = Gtk.Label(label=get_text("Installation in progress"), name="Header")
label.set_property("height-request", 50)
self.vBox.pack_start(label, False, False, 0)
@@ -91,7 +98,7 @@ class InstallWindow:
vbox2.show()
label2 = Gtk.Label(name="sideText")
label2.set_markup(_(
label2.set_markup(get_text(
"Thank you for choosing GhostBSD!\n\n"
"We believe every computer operating system should "
"be simple, elegant, secure and protect your privacy"
@@ -110,7 +117,7 @@ class InstallWindow:
hbox.pack_start(hbox2, True, True, 0)
hbox2.pack_start(label2, True, True, 30)
image = Gtk.Image()
image.set_from_file(f"{install-station_dir}/image/G_logo.gif")
image.set_from_file(gif_logo)
# image.set_size_request(width=256, height=256)
image.show()
hbox.pack_end(image, True, True, 20)
+10 -7
View File
@@ -4,10 +4,7 @@ Module to create the inner window for select what type of installation.
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk
from install_station.data import InstallationData
import gettext
_ = gettext.gettext
from install_station.data import InstallationData, get_text
cssProvider = Gtk.CssProvider()
cssProvider.load_from_path('/usr/local/lib/install-station/ghostbsd-style.css')
@@ -52,12 +49,15 @@ class InstallTypes:
InstallationData.filesystem_type = cls.ne
cls.vbox1.pack_start(hbox1, True, False, 0)
hbox1.set_halign(Gtk.Align.CENTER)
label = Gtk.Label(label=_("How do you want to install GhostBSD?"))
label = Gtk.Label(label=get_text("How do you want to install GhostBSD?"))
label.set_alignment(0, 0.5)
vbox2.pack_start(label, False, False, 10)
# Create radio button group
cls.full_zfs_button = Gtk.RadioButton(
label=_("<b>Disks Configuration</b>\nInstall GhostBSD using Stripe, Mirror, RAIDZ1, RAIDZ2, or RAIDZ3 configurations.")
label=get_text(
"<b>Disks Configuration</b>"
"\nInstall GhostBSD using Stripe, Mirror, RAIDZ1, RAIDZ2, or RAIDZ3 configurations."
)
)
cls.full_zfs_button.get_child().set_use_markup(True)
cls.full_zfs_button.get_child().set_line_wrap(True)
@@ -67,7 +67,10 @@ class InstallTypes:
cls.custom_button = Gtk.RadioButton.new_with_label_from_widget(
cls.full_zfs_button,
_("<b>Multi-Boot Configuration</b>\nInstall GhostBSD with ZFS alongside other operating systems.")
get_text(
"<b>Multi-Boot Configuration</b>\n"
"Install GhostBSD with ZFS alongside other operating systems."
)
)
cls.custom_button.get_child().set_use_markup(True)
cls.custom_button.get_child().set_line_wrap(True)
+81 -47
View File
@@ -1,24 +1,26 @@
from gi.repository import Gtk
import gettext
from install_station.install import InstallProgress, InstallWindow
from install_station.partition import DiskPartition
from install_station.window import Window
from install_station.data import InstallationData
gettext.bindtextdomain('install-station', '/usr/local/share/locale')
gettext.textdomain('install-station')
_ = gettext.gettext
from install_station.data import InstallationData, get_text
class Button:
back_button = Gtk.Button(label=_('Back'))
back_button = Gtk.Button(label=get_text('Back'))
"""This button is used to go back to the previous page."""
cancel_button = Gtk.Button(label=_('Cancel'))
cancel_button = Gtk.Button(label=get_text('Cancel'))
"""This button is used to quit and clean up."""
next_button = Gtk.Button(label=_('Next'))
next_button = Gtk.Button(label=get_text('Next'))
"""This button is used to go to the next page."""
_box = None
@classmethod
def update_button_labels(cls):
"""Update button labels with current language translations."""
cls.back_button.set_label(get_text('Back'))
cls.cancel_button.set_label(get_text('Cancel'))
cls.next_button.set_label(get_text('Next'))
@classmethod
def hide_all(cls):
"""
@@ -33,6 +35,7 @@ class Button:
"""
This method shows the initial buttons. Cancel and Next.
"""
cls.back_button.hide()
cls.cancel_button.show()
cls.next_button.show()
@@ -48,6 +51,7 @@ class Button:
"""
This method hides the back button.
"""
cls.back_button.hide()
@classmethod
def box(cls):
@@ -77,11 +81,13 @@ class Button:
class Interface:
welcome = None
keyboard = None
network_setup = None
try_isntall = None
installation_type = None
custom_partition = None
full_zfs = None
boot_manager = None
network_setup = None
page = Gtk.Notebook()
@classmethod
@@ -97,8 +103,8 @@ class Interface:
cls.welcome.initialize()
get_types = cls.welcome.get_model()
welcome_box.pack_start(get_types, True, True, 0)
Window.set_title(_("Welcome to GhostBSD"))
label = Gtk.Label(label=_("Welcome to GhostBSD"))
Window.set_title(get_text("Welcome to GhostBSD"))
label = Gtk.Label(label=get_text("Welcome to GhostBSD"))
cls.page.insert_page(welcome_box, label, 0)
# Set what page to start at type of installation
cls.page.set_current_page(0)
@@ -107,7 +113,7 @@ class Interface:
cls.nbButton.show()
cls.nbButton.set_show_tabs(False)
cls.nbButton.set_show_border(False)
label = Gtk.Label(label=_("Button"))
label = Gtk.Label(label=get_text("Button"))
cls.nbButton.insert_page(Button.box(), label, 0)
return interface_box
@@ -119,34 +125,61 @@ class Interface:
@classmethod
def next_page(cls, _widget):
if InstallationData.install_mode == "install":
cls.next_install_page()
else:
cls.next_setup_page()
@classmethod
def next_install_page(cls):
"""Go to the next window."""
page = cls.page.get_current_page()
if page == 0:
type_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, homogeneous=False, spacing=0)
type_box.show()
get_types = cls.installation_type.get_model()
type_box.pack_start(get_types, True, True, 0)
label = Gtk.Label(label=_("Installation Types"))
cls.page.insert_page(type_box, label, 1)
# Check if the keyboard page already exists
if cls.page.get_n_pages() <= 1:
keyboard_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, homogeneous=False, spacing=0)
keyboard_box.show()
get_keyboard = cls.keyboard.get_model()
keyboard_box.pack_start(get_keyboard, True, True, 0)
label = Gtk.Label(label=get_text("Keyboard Setup"))
cls.page.insert_page(keyboard_box, label, 1)
cls.page.next_page()
cls.page.show_all()
Button.show_initial()
Button.show_back()
elif page == 1:
# Check if the network setup page already exists
if cls.page.get_n_pages() <= 2:
network_setup_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, homogeneous=False, spacing=0)
network_setup_box.show()
get_network_setup = cls.network_setup.get_model()
network_setup_box.pack_start(get_network_setup, True, True, 0)
label = Gtk.Label(label=get_text("Network Setup"))
cls.page.insert_page(network_setup_box, label, 2)
cls.page.next_page()
cls.page.show_all()
elif page == 2:
# Check if the try_install page already exists
if cls.page.get_n_pages() <= 3:
try_install_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, homogeneous=False, spacing=0)
try_install_box.show()
get_try_install = cls.try_isntall.get_model()
try_install_box.pack_start(get_try_install, True, True, 0)
label = Gtk.Label(label=get_text("Try Or Install GhostBSD"))
cls.page.insert_page(try_install_box, label, 3)
cls.page.next_page()
cls.page.show_all()
elif page == 3:
if cls.page.get_n_pages() <= 4:
type_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, homogeneous=False, spacing=0)
type_box.show()
get_types = cls.installation_type.get_model()
type_box.pack_start(get_types, True, True, 0)
label = Gtk.Label(label=get_text("Installation Types"))
cls.page.insert_page(type_box, label, 4)
cls.page.next_page()
cls.page.show_all()
elif page == 4:
Button.show_back()
if InstallationData.filesystem_type == "custom":
custom_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, homogeneous=False, spacing=0)
custom_box.show()
get_part = cls.custom_partition.get_model()
custom_box.pack_start(get_part, True, True, 0)
label = Gtk.Label(label=_("Custom Configuration"))
cls.page.insert_page(custom_box, label, 2)
label = Gtk.Label(label=get_text("Custom Configuration"))
cls.page.insert_page(custom_box, label, 5)
cls.page.next_page()
cls.page.show_all()
Button.next_button.set_sensitive(False)
@@ -155,38 +188,38 @@ class Interface:
zfs_box.show()
get_zfs = cls.full_zfs.get_model()
zfs_box.pack_start(get_zfs, True, True, 0)
label = Gtk.Label(label=_("ZFS Configuration"))
cls.page.insert_page(zfs_box, label, 2)
label = Gtk.Label(label=get_text("ZFS Configuration"))
cls.page.insert_page(zfs_box, label, 5)
cls.page.next_page()
cls.page.show_all()
Button.next_button.set_sensitive(False)
elif page == 2:
elif page == 5:
boot_manager_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, homogeneous=False, spacing=0)
boot_manager_box.show()
get_root = cls.boot_manager.get_model()
boot_manager_box.pack_start(get_root, True, True, 0)
label = Gtk.Label(label=_("Boot Option"))
cls.page.insert_page(boot_manager_box, label, 3)
Button.next_button.set_label(_("Install"))
label = Gtk.Label(label=get_text("Boot Option"))
cls.page.insert_page(boot_manager_box, label, 6)
Button.next_button.set_label(get_text("Install"))
cls.page.next_page()
cls.page.show_all()
Button.next_button.set_sensitive(True)
elif page == 3:
elif page == 6:
installation_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, homogeneous=False, spacing=0)
installation_box.show()
install_window = InstallWindow()
get_install = install_window.get_model()
installation_box.pack_start(get_install, True, True, 0)
label = Gtk.Label(label=_("Installation Progress"))
cls.page.insert_page(installation_box, label, 8)
label = Gtk.Label(label=get_text("Installation Progress"))
cls.page.insert_page(installation_box, label, 7)
cls.page.next_page()
installation_progressbar = InstallProgress()
progressbar = installation_progressbar.get_progressbar()
box1 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, homogeneous=False, spacing=0)
box1.show()
label = Gtk.Label(label=_("Progress Bar"))
label = Gtk.Label(label=get_text("Progress Bar"))
box1.pack_end(progressbar, False, False, 0)
cls.nbButton.insert_page(box1, label, 4)
cls.nbButton.insert_page(box1, label, 1)
cls.nbButton.next_page()
current_page_widget = cls.page.get_nth_page(cls.page.get_current_page())
title_text = cls.page.get_tab_label_text(current_page_widget)
@@ -198,12 +231,12 @@ class Interface:
if page == 0:
Button.next_button.show()
Button.next_button.set_sensitive(False)
Window.set_title(_("Network Setup"))
Window.set_title(get_text("Network Setup"))
net_setup_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, homogeneous=False, spacing=0)
net_setup_box.show()
model = cls.network_setup.get_model()
net_setup_box.pack_start(model, True, True, 0)
label = Gtk.Label(label=_("Network Setup"))
label = Gtk.Label(label=get_text("Network Setup"))
cls.page.insert_page(net_setup_box, label, 1)
cls.page.next_page()
cls.page.show_all()
@@ -220,13 +253,11 @@ class Interface:
current_page = cls.page.get_current_page()
if current_page == 1:
Button.hide_back()
if current_page == 2:
Button.hide_back()
elif current_page == 3:
Button.next_button.set_label(_("Next"))
Button.next_button.set_label(get_text("Next"))
cls.page.prev_page()
new_page = cls.page.get_current_page()
if current_page == 2 and new_page == 1:
if current_page == 1 and new_page == 0:
# Reset partition configuration data when going back
InstallationData.destroy = {}
InstallationData.delete = []
@@ -239,4 +270,7 @@ class Interface:
InstallationData.ufs_config_data = []
# Clean up temporary directory if it exists
DiskPartition.create_partition_database()
Button.next_button.set_sensitive(True)
current_page_widget = cls.page.get_nth_page(cls.page.get_current_page())
title_text = cls.page.get_tab_label_text(current_page_widget)
Window.set_title(title_text)
# Button.next_button.set_sensitive(True)
+308
View File
@@ -0,0 +1,308 @@
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk
import os
from install_station.system_calls import (
keyboard_dictionary,
keyboard_models,
change_keyboard,
set_keyboard
)
from install_station.data import InstallationData, tmp, get_text
# Ensure temp directory exists
if not os.path.exists(tmp):
os.makedirs(tmp)
layout = f'{tmp}layout'
variant = f'{tmp}variant'
KBFile = f'{tmp}keyboard'
kb_dictionary = keyboard_dictionary()
kbm_dictionary = keyboard_models()
cssProvider = Gtk.CssProvider()
cssProvider.load_from_path('/usr/local/lib/install-station/ghostbsd-style.css')
screen = Gdk.Screen.get_default()
styleContext = Gtk.StyleContext()
styleContext.add_provider_for_screen(
screen,
cssProvider,
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
)
# This class is for placeholder for entry.
class PlaceHolderEntry(Gtk.Entry):
def __init__(self, *args, **kwds):
Gtk.Entry.__init__(self, *args, **kwds)
self.placeholder = get_text('Type here to test your keyboard')
self.set_text(self.placeholder)
self._default = True
self.connect('focus-in-event', self._focus_in_event)
self.connect('focus-out-event', self._focus_out_event)
def _focus_in_event(self, _widget, _event):
if self._default:
self.set_text('')
def _focus_out_event(self, _widget, _event):
if Gtk.Entry.get_text(self) == '':
self.set_text(self.placeholder)
self._default = True
else:
self._default = False
def get_text(self):
if self._default:
return ''
return Gtk.Entry.get_text(self)
class Keyboard:
"""
Utility class for the keyboard configuration screen following the utility class pattern.
This class provides a GTK+ interface for keyboard layout and model selection including:
- Keyboard layout selection from available system layouts
- Keyboard model selection from available models
- Real-time keyboard testing with preview text entry
- Integration with InstallationData for persistent configuration
The class follows a utility pattern with class methods and variables for state management,
designed to integrate with the Interface controller for navigation flow.
"""
# Class variables instead of instance variables
kb_layout = None
kb_variant = None
kb_model = None
vbox1 = None
treeView = None
test_entry = None
@classmethod
def layout_columns(cls, treeview):
"""
Configure the keyboard layout treeview with appropriate columns.
Creates a single column with a "Keyboard Layout" header for displaying
available keyboard layouts in the tree view.
Args:
treeview: TreeView widget to configure with layout column
"""
cell = Gtk.CellRendererText()
column = Gtk.TreeViewColumn(None, cell, text=0)
column_header = Gtk.Label(label=f'<b>{get_text("Keyboard Layout")}</b>')
column_header.set_use_markup(True)
column_header.show()
column.set_widget(column_header)
column.set_sort_column_id(0)
treeview.append_column(column)
@classmethod
def variant_columns(cls, treeview):
"""
Configure the keyboard model treeview with appropriate columns.
Creates a single column with a "Keyboard Models" header for displaying
available keyboard models in the tree view.
Args:
treeview: TreeView widget to configure with model column
"""
cell = Gtk.CellRendererText()
column = Gtk.TreeViewColumn(None, cell, text=0)
column_header = Gtk.Label(label=f'<b>{get_text("Keyboard Models")}</b>')
column_header.set_use_markup(True)
column_header.show()
column.set_widget(column_header)
column.set_sort_column_id(0)
treeview.append_column(column)
@classmethod
def layout_selection(cls, tree_selection):
"""
Handle keyboard layout selection from the treeview.
Extracts the selected layout from the tree view and updates both
class variables and InstallationData with the layout information.
Also applies the keyboard layout change immediately for testing.
Args:
tree_selection: TreeSelection widget containing the user's layout choice
"""
model, treeiter = tree_selection.get_selected()
if treeiter is not None:
value = model[treeiter][0]
kb_lv = kb_dictionary[value]
cls.kb_layout = kb_lv['layout']
cls.kb_variant = kb_lv['variant']
# Save to InstallationData
InstallationData.keyboard_layout = value
InstallationData.keyboard_layout_code = cls.kb_layout
InstallationData.keyboard_variant = cls.kb_variant
change_keyboard(cls.kb_layout, cls.kb_variant)
print(f"Keyboard layout selected: {value} ({cls.kb_layout}/{cls.kb_variant})")
@classmethod
def model_selection(cls, tree_selection):
"""
Handle keyboard model selection from the treeview.
Extracts the selected model from the tree view and updates both
class variables and InstallationData with the model information.
Also applies the keyboard model change immediately for testing.
Args:
tree_selection: TreeSelection widget containing the user's model choice
"""
model, treeiter = tree_selection.get_selected()
if treeiter is not None:
value = model[treeiter][0]
cls.kb_model = kbm_dictionary[value]
# Save to InstallationData
InstallationData.keyboard_model = value
InstallationData.keyboard_model_code = cls.kb_model
if cls.kb_layout and cls.kb_variant:
change_keyboard(cls.kb_layout, cls.kb_variant, cls.kb_model)
print(f"Keyboard model selected: {value} ({cls.kb_model})")
@classmethod
def save_selection(cls):
"""
Save the current keyboard selection.
This method saves keyboard configuration to both InstallationData
(for the installer) and temporary files (for compatibility).
"""
# Data is now saved in InstallationData automatically
# Keep file writing for compatibility
if cls.kb_layout and cls.kb_variant and cls.kb_model:
with open(KBFile, 'w') as file:
file.write(f"{cls.kb_layout}\\n")
file.write(f"{cls.kb_variant}\\n")
file.write(f"{cls.kb_model}\\n")
@classmethod
def save_keyboard(cls):
"""
Apply the keyboard configuration to the system.
This method applies the selected keyboard layout, variant, and model
to the current system for immediate use.
"""
if cls.kb_layout and cls.kb_variant and cls.kb_model:
set_keyboard(cls.kb_layout, cls.kb_variant, cls.kb_model)
@classmethod
def initialize(cls):
"""
Initialize the keyboard configuration UI following the utility class pattern.
Creates the main interface including:
- Keyboard layout selection tree view on the left side
- Keyboard model selection tree view on the right side
- Test entry field at the bottom for keyboard testing
- Grid-based layout with proper spacing and margins
This method is called automatically by get_model() when the interface is first accessed.
"""
cls.vbox1 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, homogeneous=False, spacing=0)
cls.vbox1.show()
main_grid = Gtk.Grid()
cls.vbox1.pack_start(main_grid, True, True, 0)
# Create two scrolled windows side by side for layout and model selection
# Left side - Keyboard layouts
sw_layouts = Gtk.ScrolledWindow()
sw_layouts.set_shadow_type(Gtk.ShadowType.ETCHED_IN)
sw_layouts.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
layout_store = Gtk.TreeStore(str)
layout_store.append(None, [get_text('English (US)')])
layout_store.append(None, [get_text('English (Canada)')])
layout_store.append(None, [get_text('French (Canada)')])
for line in sorted(kb_dictionary):
layout_store.append(None, [line.rstrip()])
cls.treeView = Gtk.TreeView()
cls.treeView.set_model(layout_store)
cls.treeView.set_rules_hint(True)
cls.layout_columns(cls.treeView)
layout_selection = cls.treeView.get_selection()
layout_selection.set_mode(Gtk.SelectionMode.SINGLE)
layout_selection.connect("changed", cls.layout_selection)
sw_layouts.add(cls.treeView)
sw_layouts.show()
# Right side - Keyboard models
sw_models = Gtk.ScrolledWindow()
sw_models.set_shadow_type(Gtk.ShadowType.ETCHED_IN)
sw_models.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
model_store = Gtk.TreeStore(str)
for line in sorted(kbm_dictionary):
model_store.append(None, [line.rstrip()])
model_treeview = Gtk.TreeView()
model_treeview.set_model(model_store)
model_treeview.set_rules_hint(True)
cls.variant_columns(model_treeview)
model_selection = model_treeview.get_selection()
model_selection.set_mode(Gtk.SelectionMode.SINGLE)
model_selection.connect("changed", cls.model_selection)
sw_models.add(model_treeview)
sw_models.show()
# Bottom - Test entry
cls.test_entry = PlaceHolderEntry()
# Layout everything in grid
main_grid.set_row_spacing(5)
main_grid.set_column_spacing(10)
main_grid.set_column_homogeneous(True)
main_grid.set_row_homogeneous(True)
main_grid.set_margin_left(10)
main_grid.set_margin_right(10)
main_grid.set_margin_top(10)
main_grid.set_margin_bottom(10)
main_grid.attach(sw_layouts, 0, 0, 1, 8)
main_grid.attach(sw_models, 1, 0, 1, 8)
main_grid.attach(cls.test_entry, 0, 9, 2, 1)
main_grid.show()
# Set default selection
cls.treeView.set_cursor(0)
@classmethod
def get_model(cls):
"""
Return the GTK widget model for the keyboard configuration interface.
Returns the main container widget that was created during initialization.
Returns:
Gtk.Box: The main container widget for the keyboard configuration interface
"""
if cls.vbox1 is None:
cls.initialize()
return cls.vbox1
@classmethod
def get_keyboard_info(cls):
"""
Get the current keyboard configuration information.
Returns:
dict: Dictionary containing keyboard layout, variant, and model information
"""
return {
'layout': InstallationData.keyboard_layout or cls.kb_layout,
'layout_code': InstallationData.keyboard_layout_code or cls.kb_layout,
'variant': InstallationData.keyboard_variant or cls.kb_variant,
'model': InstallationData.keyboard_model or cls.kb_model,
'model_code': InstallationData.keyboard_model_code or cls.kb_model
}
+217 -98
View File
@@ -1,23 +1,19 @@
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk
import os
from install_station.system_calls import (
language_dictionary,
localize_system
)
from install_station.data import InstallationData, tmp, logo
import gettext
gettext.bindtextdomain('install-station', '/usr/local/share/locale')
gettext.textdomain('install-station')
_ = gettext.gettext
from install_station.data import InstallationData, tmp, gif_logo, get_text
from install_station.window import Window
# Ensure temp directory exists
if not os.path.exists(tmp):
os.makedirs(tmp)
lang_dictionary = language_dictionary()
# Text to be replace be multiple language file.
welltext = _("Select the language you want to use with GhostBSD.")
cssProvider = Gtk.CssProvider()
cssProvider.load_from_path('/usr/local/lib/install-station/ghostbsd-style.css')
@@ -31,113 +27,236 @@ styleContext.add_provider_for_screen(
class Language:
_instance = None
"""
Utility class for the language selection screen following the utility class pattern.
This class provides a GTK+ interface for language selection including:
- Language selection from available system languages
- Visual elements with welcome message and logo
- Integration with InstallationData for persistent configuration
- Environment variable setting for immediate translation updates
The class follows a utility pattern with class methods and variables for state management,
designed to integrate with the Interface controller for navigation flow.
"""
# Class variables instead of instance variables
vbox1 = None
language = None
treeview = None
welcome_text = None
language_column_header = None
def __new__(cls):
"""Implement singleton pattern."""
if cls._instance is None:
cls._instance = super(Language, cls).__new__(cls)
cls._instance._initialized = False
return cls._instance
def __init__(self):
"""Initialize only once."""
if not self._initialized:
self._language = None
self._vbox1 = None
self._initialize_ui()
self._initialized = True
# On selection it overwrite the default language file.
def _language_selection(self, tree_selection):
@classmethod
def language_selection(cls, tree_selection):
"""
Handle language selection from the treeview.
Extracts the selected language from the tree view and updates both
class variables and InstallationData with the language name and code.
Also sets environment variables globally so all modules pick up the language.
Args:
tree_selection: TreeSelection widget containing the user's language choice
"""
model, treeiter = tree_selection.get_selected()
if treeiter is not None:
value = model[treeiter][0]
self._language = lang_dictionary[value]
language_code = lang_dictionary[value]
cls.language = language_code
InstallationData.language = value
InstallationData.language_code = lang_dictionary[value]
return
InstallationData.language_code = language_code
print(f"Language selected: {value} ({language_code})")
# Set environment variables globally so all modules pick up the language
import os
os.environ['LANGUAGE'] = language_code
os.environ['LC_ALL'] = f'{language_code}.UTF-8'
os.environ['LANG'] = f'{language_code}.UTF-8'
# Update the UI text with new translations
cls.update_ui_text()
def _setup_language_columns(self, treeView):
@classmethod
def update_ui_text(cls):
"""
Update all UI text elements with new translations after language change.
"""
from install_station.interface_controller import Button
# Update navigation buttons
Button.update_button_labels()
# Update the welcome text
if hasattr(cls, 'welcome_text') and cls.welcome_text:
cls.welcome_text.set_text(
get_text(
"Please select your language:"
)
)
# Update the language column header
if hasattr(cls, 'language_column_header') and cls.language_column_header:
cls.language_column_header.set_text(get_text('Language'))
Window.set_title(get_text("Welcome to GhostBSD"))
@classmethod
def setup_language_columns(cls, treeview):
"""
Configure the language selection treeview with appropriate columns.
Creates a single column with a "Language" header for displaying
available languages in the tree view.
Args:
treeview: TreeView widget to configure with language column
"""
cell = Gtk.CellRendererText()
column = Gtk.TreeViewColumn(None, cell, text=0)
column_header = Gtk.Label(label=_('Language'))
column_header = Gtk.Label(label=get_text('Language'))
column_header.set_use_markup(True)
column_header.show()
column.set_widget(column_header)
# Store reference for updating
cls.language_column_header = column_header
column.set_sort_column_id(0)
treeView.append_column(column)
return
# Initial definition.
def _initialize_ui(self):
# Add a Default vertical box
self._vbox1 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, homogeneous=False, spacing=0)
self._vbox1.show()
# Add a second vertical box
grid = Gtk.Grid()
title = Gtk.Label(label=_('Welcome To GhostBSD!'), name="Header")
title.set_property("height-request", 50)
self._vbox1.pack_start(title, False, False, 0)
self._vbox1.pack_start(grid, True, True, 0)
grid.set_row_spacing(10)
grid.set_column_spacing(3)
grid.set_column_homogeneous(True)
grid.set_row_homogeneous(True)
grid.set_margin_left(10)
grid.set_margin_right(10)
grid.set_margin_top(10)
grid.set_margin_bottom(10)
# Adding a Scrolling Window
sw = Gtk.ScrolledWindow()
sw.set_shadow_type(Gtk.ShadowType.ETCHED_IN)
sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
# Adding a treestore and store language in it.
store = Gtk.TreeStore(str)
for line in lang_dictionary:
store.append(None, [line])
treeview = Gtk.TreeView()
treeview.set_model(store)
treeview.set_rules_hint(True)
self._setup_language_columns(treeview)
tree_selection = treeview.get_selection()
tree_selection.set_mode(Gtk.SelectionMode.SINGLE)
tree_selection.connect("changed", self._language_selection)
sw.add(treeview)
sw.show()
grid.attach(sw, 1, 2, 1, 9)
# add text in a label.
vbox2 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, homogeneous=False, spacing=0)
vbox2.set_border_width(10)
vbox2.show()
wellcome_text = Gtk.Label(label=welltext)
wellcome_text.set_use_markup(True)
table = Gtk.Table()
table.attach(wellcome_text, 0, 1, 3, 4)
vbox2.pack_start(table, False, False, 5)
image = Gtk.Image()
image.set_from_file(logo)
image.show()
# grid.attach(self.wellcome, 1, 1, 3, 1)
vbox2.pack_start(image, True, True, 5)
grid.attach(vbox2, 2, 2, 2, 9)
grid.show()
treeview.append_column(column)
@classmethod
def get_model(cls):
return cls()._vbox1
@classmethod
def get_language(cls):
"""Get the selected language."""
return InstallationData.language_code or cls()._language
def save_selection(self):
def save_selection(cls):
"""
Save the current language selection.
This method is maintained for compatibility but language selection
is now automatically saved to InstallationData when chosen.
"""
# Language is now saved in InstallationData automatically
pass
@classmethod
def save_language(cls):
language_code = InstallationData.language_code or cls()._language
"""
Apply the language configuration to the system.
This method applies the selected language to the system for
permanent configuration during installation.
"""
language_code = InstallationData.language_code or cls.language
if language_code:
localize_system(language_code)
localize_system(language_code)
@classmethod
def initialize(cls):
"""
Initialize the language selection UI following the utility class pattern.
Creates the main interface including:
- Language selection tree view on the left side
- Welcome message and logo on the right side
- Grid-based layout with proper spacing and margins
This method is called automatically by get_model() when the interface is first accessed.
"""
cls.language = None
# Main container
cls.vbox1 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, homogeneous=False, spacing=0)
cls.vbox1.show()
main_grid = Gtk.Grid()
cls.vbox1.pack_start(main_grid, True, True, 0)
# Left side - Language selection
sw = Gtk.ScrolledWindow()
sw.set_shadow_type(Gtk.ShadowType.ETCHED_IN)
sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
store = Gtk.TreeStore(str)
for line in lang_dictionary:
store.append(None, [line])
cls.treeview = Gtk.TreeView()
cls.treeview.set_model(store)
cls.treeview.set_rules_hint(True)
cls.treeview.set_headers_visible(False)
cls.setup_language_columns(cls.treeview)
tree_selection = cls.treeview.get_selection()
tree_selection.set_mode(Gtk.SelectionMode.SINGLE)
tree_selection.connect("changed", cls.language_selection)
sw.add(cls.treeview)
sw.show()
# Right side - Welcome content
right_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, homogeneous=False, spacing=10)
right_box.set_border_width(20)
right_box.show()
# Welcome text
cls.welcome_text = Gtk.Label(
label=get_text(
"Please select your language:"
)
)
cls.welcome_text.set_use_markup(True)
cls.welcome_text.set_line_wrap(True)
cls.welcome_text.set_justify(Gtk.Justification.CENTER)
cls.welcome_text.show()
# Logo
image = Gtk.Image()
image.set_from_file(gif_logo)
image.show()
right_box.pack_start(cls.welcome_text, False, False, 10)
right_box.pack_start(sw, True, True, 10)
# Layout in grid
main_grid.set_row_spacing(10)
main_grid.set_column_spacing(20)
main_grid.set_column_homogeneous(True)
main_grid.set_row_homogeneous(True)
main_grid.set_margin_left(10)
main_grid.set_margin_right(10)
main_grid.set_margin_top(10)
main_grid.set_margin_bottom(10)
main_grid.attach(image, 0, 0, 1, 1)
main_grid.attach(right_box, 1, 0, 1, 1)
main_grid.show()
@classmethod
def get_model(cls):
"""
Return the GTK widget model for the language selection interface.
Returns the main container widget that was created during initialization.
Returns:
Gtk.Box: The main container widget for the language selection interface
"""
if cls.vbox1 is None:
cls.initialize()
return cls.vbox1
@classmethod
def get_language(cls):
"""
Get the selected language code.
Returns:
str: The selected language code
"""
return InstallationData.language_code or cls.language
@classmethod
def get_language_info(cls):
"""
Get the current language configuration information.
Returns:
dict: Dictionary containing language name and code information
"""
return {
'language': InstallationData.language or '',
'language_code': InstallationData.language_code or cls.language or ''
}
+258 -127
View File
@@ -10,11 +10,8 @@ from NetworkMgr.net_api import (
delete_ssid_wpa_supplicant_config,
nic_status
)
import gettext
gettext.bindtextdomain('install-station', '/usr/local/share/locale')
gettext.textdomain('install-station')
_ = gettext.gettext
from install_station.data import get_text
from install_station.interface_controller import Button
logo = "/usr/local/lib/install-station/logo.png"
cssProvider = Gtk.CssProvider()
@@ -29,21 +26,55 @@ styleContext.add_provider_for_screen(
class NetworkSetup:
_instance = None
def __new__(cls, button3=None):
"""Implement singleton pattern."""
if cls._instance is None:
cls._instance = super(NetworkSetup, cls).__new__(cls)
cls._instance._initialized = False
return cls._instance
"""
Utility class for network setup following the utility class pattern.
This class provides a GTK+ interface for network configuration including:
- Wired network detection and status
- WiFi network detection and connection
- Network authentication dialogs
- Integration with Button class for navigation
The class follows a utility pattern with class methods and variables for state management,
designed to integrate with the Interface controller for navigation flow.
"""
# Class variables instead of instance variables
vbox1 = None
network_info = None
wire_connection_label = None
wire_connection_image = None
wifi_connection_label = None
wifi_connection_image = None
connection_box = None
store = None
window = None
password = None
@classmethod
def get_model(cls):
return cls().vbox1
"""
Return the GTK widget model for the network setup interface.
Returns the main container widget that was created during initialization.
Returns:
Gtk.Box: The main container widget for the network setup interface
"""
if cls.vbox1 is None:
cls.initialize()
return cls.vbox1
@staticmethod
def wifi_stat(bar):
"""
Get WiFi signal strength icon name based on signal bar percentage.
Args:
bar (int): Signal strength percentage
Returns:
str: Icon name for the signal strength
"""
if bar > 75:
return 'nm-signal-100'
elif bar > 50:
@@ -55,121 +86,147 @@ class NetworkSetup:
else:
return 'nm-signal-00'
def update_network_detection(self):
cards = self.network_info['cards']
@classmethod
def update_network_detection(cls):
"""
Update network detection status and UI elements.
Checks both wired and wireless network connections and updates
the UI with current status and enables/disables next button.
"""
cards = cls.network_info['cards']
card_list = list(cards.keys())
r = re.compile("wlan")
wlan_list = list(filter(r.match, card_list))
wire_list = list(set(card_list).difference(wlan_list))
cards = self.network_info['cards']
# Update wired connection status
if wire_list:
for card in wire_list:
if cards[card]['state']['connection'] == 'Connected':
wire_text = _('Network card connected to the internet')
self.wire_connection_image.set_from_stock(Gtk.STOCK_YES, 5)
wire_text = get_text('Network card connected to the internet')
cls.wire_connection_image.set_from_stock(Gtk.STOCK_YES, 5)
print('Connected True')
self.next_button.set_sensitive(True)
Button.next_button.set_sensitive(True)
break
else:
wire_text = _('Network card not connected to the internet')
self.wire_connection_image.set_from_stock(Gtk.STOCK_NO, 5)
wire_text = get_text('Network card not connected to the internet')
cls.wire_connection_image.set_from_stock(Gtk.STOCK_NO, 5)
else:
wire_text = _('No network card detected')
self.wire_connection_image.set_from_stock(Gtk.STOCK_NO, 5)
wire_text = get_text('No network card detected')
cls.wire_connection_image.set_from_stock(Gtk.STOCK_NO, 5)
self.wire_connection_label.set_label(wire_text)
cls.wire_connection_label.set_label(wire_text)
# Update WiFi connection status
if wlan_list:
for wlan_card in wlan_list:
if cards[wlan_card]['state']['connection'] == 'Connected':
wifi_text = _('WiFi card detected and connected to an access point')
self.wifi_connection_image.set_from_stock(Gtk.STOCK_YES, 5)
wifi_text = get_text('WiFi card detected and connected to an access point')
cls.wifi_connection_image.set_from_stock(Gtk.STOCK_YES, 5)
break
else:
wifi_text = _('WiFi card detected but not connected to an access point')
self.wifi_connection_image.set_from_stock(Gtk.STOCK_NO, 5)
wifi_text = get_text('WiFi card detected but not connected to an access point')
cls.wifi_connection_image.set_from_stock(Gtk.STOCK_NO, 5)
else:
wifi_text = _("WiFi card not detected or not supported")
self.wifi_connection_image.set_from_stock(Gtk.STOCK_NO, 5)
wifi_text = get_text("WiFi card not detected or not supported")
cls.wifi_connection_image.set_from_stock(Gtk.STOCK_NO, 5)
self.wifi_connection_label.set_label(wifi_text)
cls.wifi_connection_label.set_label(wifi_text)
def __init__(self, next_button=None):
"""Initialize only once."""
if not self._initialized:
self.next_button = next_button
self._initialize_ui()
self._initialized = True
@classmethod
def update_ui_text(cls):
"""
Update all UI text elements with new translations after language change.
"""
# Update button labels
Button.update_button_labels()
# Update network status if elements exist
if cls.network_info:
cls.update_network_detection()
def _initialize_ui(self):
self.network_info = networkdictionary()
print(self.network_info)
self.vbox1 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, homogeneous=False, spacing=0)
self.vbox1.show()
title = Gtk.Label(label=_('Network Setup'), name="Header")
title.set_property("height-request", 50)
self.vbox1.pack_start(title, False, False, 0)
cards = self.network_info['cards']
@classmethod
def initialize(cls):
"""
Initialize the network setup UI following the utility class pattern.
Creates the main interface including:
- Network status indicators for wired and wireless
- WiFi access point list if available
- Grid-based layout with proper spacing and margins
This method is called automatically by get_model() when the interface is first accessed.
"""
cls.network_info = networkdictionary()
print(cls.network_info)
cls.vbox1 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, homogeneous=False, spacing=0)
cls.vbox1.show()
cards = cls.network_info['cards']
card_list = list(cards.keys())
r = re.compile("wlan")
wlan_list = list(filter(r.match, card_list))
wire_list = list(set(card_list).difference(wlan_list))
self.wire_connection_label = Gtk.Label()
self.wire_connection_label.set_xalign(0.01)
self.wire_connection_image = Gtk.Image()
self.wifi_connection_label = Gtk.Label()
self.wifi_connection_label.set_xalign(0.01)
self.wifi_connection_image = Gtk.Image()
cls.wire_connection_label = Gtk.Label()
cls.wire_connection_label.set_xalign(0.01)
cls.wire_connection_image = Gtk.Image()
cls.wifi_connection_label = Gtk.Label()
cls.wifi_connection_label.set_xalign(0.01)
cls.wifi_connection_image = Gtk.Image()
# Check wired connection status
if wire_list:
for card in wire_list:
if cards[card]['state']['connection'] == 'Connected':
wire_text = _('Network card connected to the internet')
self.wire_connection_image.set_from_stock(Gtk.STOCK_YES, 5)
wire_text = get_text('Network card connected to the internet')
cls.wire_connection_image.set_from_stock(Gtk.STOCK_YES, 5)
print('Connected True')
self.next_button.set_sensitive(True)
Button.next_button.set_sensitive(True)
break
else:
wire_text = _('Network card not connected to the internet')
self.wire_connection_image.set_from_stock(Gtk.STOCK_NO, 5)
wire_text = get_text('Network card not connected to the internet')
cls.wire_connection_image.set_from_stock(Gtk.STOCK_NO, 5)
else:
wire_text = _('No network card detected')
self.wire_connection_image.set_from_stock(Gtk.STOCK_NO, 5)
wire_text = get_text('No network card detected')
cls.wire_connection_image.set_from_stock(Gtk.STOCK_NO, 5)
self.wire_connection_label.set_label(wire_text)
cls.wire_connection_label.set_label(wire_text)
# Check WiFi status and setup WiFi list if available
wlan_card = ""
if wlan_list:
for wlan_card in wlan_list:
if cards[wlan_card]['state']['connection'] == 'Connected':
wifi_text = _('WiFi card detected and connected to an access point')
self.wifi_connection_image.set_from_stock(Gtk.STOCK_YES, 5)
wifi_text = get_text('WiFi card detected and connected to an access point')
cls.wifi_connection_image.set_from_stock(Gtk.STOCK_YES, 5)
break
else:
wifi_text = _('WiFi card detected but not connected to an access point')
self.wifi_connection_image.set_from_stock(Gtk.STOCK_NO, 5)
wifi_text = get_text('WiFi card detected but not connected to an access point')
cls.wifi_connection_image.set_from_stock(Gtk.STOCK_NO, 5)
else:
wifi_text = _('WiFi card not detected or not supported')
self.wifi_connection_image.set_from_stock(Gtk.STOCK_NO, 5)
wifi_text = get_text('WiFi card not detected or not supported')
cls.wifi_connection_image.set_from_stock(Gtk.STOCK_NO, 5)
self.wifi_connection_label.set_label(wifi_text)
cls.wifi_connection_label.set_label(wifi_text)
self.connection_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, homogeneous=True, spacing=20)
cls.connection_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, homogeneous=True, spacing=20)
if wlan_card:
# add a default card variable
# Setup WiFi access point list
sw = Gtk.ScrolledWindow()
sw.set_shadow_type(Gtk.ShadowType.ETCHED_IN)
sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
self.store = Gtk.ListStore(GdkPixbuf.Pixbuf, str, str)
for ssid in self.network_info['cards'][wlan_card]['info']:
ssid_info = self.network_info['cards'][wlan_card]['info'][ssid]
cls.store = Gtk.ListStore(GdkPixbuf.Pixbuf, str, str)
for ssid in cls.network_info['cards'][wlan_card]['info']:
ssid_info = cls.network_info['cards'][wlan_card]['info'][ssid]
bar = ssid_info[4]
stat = NetworkSetup.wifi_stat(bar)
pixbuf = Gtk.IconTheme.get_default().load_icon(stat, 32, 0)
self.store.append([pixbuf, ssid, f'{ssid_info}'])
cls.store.append([pixbuf, ssid, f'{ssid_info}'])
treeview = Gtk.TreeView()
treeview.set_model(self.store)
treeview.set_model(cls.store)
treeview.set_rules_hint(True)
pixbuf_cell = Gtk.CellRendererPixbuf()
pixbuf_column = Gtk.TreeViewColumn('Stat', pixbuf_cell)
@@ -182,129 +239,197 @@ class NetworkSetup:
treeview.append_column(column)
tree_selection = treeview.get_selection()
tree_selection.set_mode(Gtk.SelectionMode.SINGLE)
tree_selection.connect("changed", self.wifi_setup, wlan_card)
tree_selection.connect("changed", cls.wifi_setup, wlan_card)
sw.add(treeview)
self.connection_box.pack_start(sw, True, True, 50)
cls.connection_box.pack_start(sw, True, True, 50)
# Layout the interface
main_grid = Gtk.Grid()
main_grid.set_row_spacing(10)
main_grid.set_column_spacing(10)
main_grid.set_column_homogeneous(True)
main_grid.set_row_homogeneous(True)
self.vbox1.pack_start(main_grid, True, True, 10)
main_grid.attach(self.wire_connection_image, 2, 1, 1, 1)
main_grid.attach(self.wire_connection_label, 3, 1, 8, 1)
main_grid.attach(self.wifi_connection_image, 2, 2, 1, 1)
main_grid.attach(self.wifi_connection_label, 3, 2, 8, 1)
main_grid.attach(self.connection_box, 1, 4, 10, 5)
cls.vbox1.pack_start(main_grid, True, True, 10)
main_grid.attach(cls.wire_connection_image, 2, 1, 1, 1)
main_grid.attach(cls.wire_connection_label, 3, 1, 8, 1)
main_grid.attach(cls.wifi_connection_image, 2, 2, 1, 1)
main_grid.attach(cls.wifi_connection_label, 3, 2, 8, 1)
main_grid.attach(cls.connection_box, 1, 4, 10, 5)
def wifi_setup(self, tree_selection, wifi_card):
@classmethod
def wifi_setup(cls, tree_selection, wifi_card):
"""
Handle WiFi access point selection and connection setup.
Args:
tree_selection: TreeSelection widget containing the selected access point
wifi_card: WiFi card interface name
"""
model, treeiter = tree_selection.get_selected()
if treeiter is not None:
ssid = model[treeiter][1]
ssid_info = self.network_info['cards'][wifi_card]['info'][ssid]
ssid_info = cls.network_info['cards'][wifi_card]['info'][ssid]
caps = ssid_info[6]
print(ssid) # added the code to authenticate.
print(ssid)
print(ssid_info)
if caps == 'E' or caps == 'ES':
if f'"{ssid}"' in open("/etc/wpa_supplicant.conf").read():
self.try_to_connect_to_ssid(ssid, ssid_info, wifi_card)
cls.try_to_connect_to_ssid(ssid, ssid_info, wifi_card)
else:
NetworkSetup.open_wpa_supplicant(ssid)
self.try_to_connect_to_ssid(ssid, ssid_info, wifi_card)
cls.try_to_connect_to_ssid(ssid, ssid_info, wifi_card)
else:
if f'"{ssid}"' in open('/etc/wpa_supplicant.conf').read():
self.try_to_connect_to_ssid(ssid, ssid_info, wifi_card)
cls.try_to_connect_to_ssid(ssid, ssid_info, wifi_card)
else:
self.authentication(ssid_info, wifi_card, False)
cls.authentication(ssid_info, wifi_card, False)
def add_to_wpa_supplicant(self, _widget, ssid_info, card):
pwd = self.password.get_text()
@classmethod
def add_to_wpa_supplicant(cls, _widget, ssid_info, card):
"""
Add WiFi credentials to wpa_supplicant configuration and connect.
Args:
_widget: Button widget that triggered the action (unused)
ssid_info: WiFi network information
card: WiFi card interface name
"""
pwd = cls.password.get_text()
NetworkSetup.setup_wpa_supplicant(ssid_info[0], ssid_info, pwd)
_thread.start_new_thread(
self.try_to_connect_to_ssid,
cls.try_to_connect_to_ssid,
(ssid_info[0], ssid_info, card)
)
self.window.hide()
cls.window.hide()
def try_to_connect_to_ssid(self, ssid, ssid_info, card):
@classmethod
def try_to_connect_to_ssid(cls, ssid, ssid_info, card):
"""
Attempt to connect to the specified WiFi network.
Args:
ssid: WiFi network SSID
ssid_info: WiFi network information
card: WiFi card interface name
"""
if connectToSsid(ssid, card) is False:
delete_ssid_wpa_supplicant_config(ssid)
GLib.idle_add(self.restart_authentication, ssid_info, card)
GLib.idle_add(cls.restart_authentication, ssid_info, card)
else:
for _ in list(range(30)):
if nic_status(card) == 'associated':
self.network_info = networkdictionary()
print(self.network_info)
self.update_network_detection()
cls.network_info = networkdictionary()
print(cls.network_info)
cls.update_network_detection()
break
sleep(1)
else:
delete_ssid_wpa_supplicant_config(ssid)
GLib.idle_add(self.restart_authentication, ssid_info, card)
GLib.idle_add(cls.restart_authentication, ssid_info, card)
return
def restart_authentication(self, ssid_info, card):
self.authentication(ssid_info, card, True)
@classmethod
def restart_authentication(cls, ssid_info, card):
"""
Restart WiFi authentication after a failed connection attempt.
Args:
ssid_info: WiFi network information
card: WiFi card interface name
"""
cls.authentication(ssid_info, card, True)
def on_check(self, widget):
@classmethod
def on_check(cls, widget):
"""
Toggle password visibility in authentication dialog.
Args:
widget: CheckButton widget for show/hide password
"""
if widget.get_active():
self.password.set_visibility(True)
cls.password.set_visibility(True)
else:
self.password.set_visibility(False)
cls.password.set_visibility(False)
def authentication(self, ssid_info, card, failed):
self.window = Gtk.Window()
self.window.set_title(_("Wi-Fi Network Authentication Required"))
self.window.set_border_width(0)
self.window.set_size_request(500, 200)
@classmethod
def authentication(cls, ssid_info, card, failed):
"""
Show WiFi authentication dialog.
Args:
ssid_info: WiFi network information
card: WiFi card interface name
failed: Boolean indicating if this is a retry after failed authentication
"""
cls.window = Gtk.Window()
cls.window.set_title(get_text("Wi-Fi Network Authentication Required"))
cls.window.set_border_width(0)
cls.window.set_size_request(500, 200)
box1 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, homogeneous=False, spacing=0)
self.window.add(box1)
cls.window.add(box1)
box1.show()
box2 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, homogeneous=False, spacing=10)
box2.set_border_width(10)
box1.pack_start(box2, True, True, 0)
box2.show()
# Creating MBR or GPT drive
# Set dialog title based on authentication status
if failed:
title = _("{ssid} Wi-Fi Network Authentication failed").format(ssid=ssid_info[0])
title = get_text("{ssid} Wi-Fi Network Authentication failed").format(ssid=ssid_info[0])
else:
title = _("Authentication required by {ssid} Wi-Fi Network").format(ssid=ssid_info[0])
title = get_text("Authentication required by {ssid} Wi-Fi Network").format(ssid=ssid_info[0])
label = Gtk.Label(label=f"<b><span size='large'>{title}</span></b>")
label.set_use_markup(True)
pwd_label = Gtk.Label(label=_("Password:"))
self.password = Gtk.Entry()
self.password.set_visibility(False)
check = Gtk.CheckButton(label=_("Show password"))
check.connect("toggled", self.on_check)
pwd_label = Gtk.Label(label=get_text("Password:"))
cls.password = Gtk.Entry()
cls.password.set_visibility(False)
check = Gtk.CheckButton(label=get_text("Show password"))
check.connect("toggled", cls.on_check)
table = Gtk.Table(1, 2, True)
table.attach(label, 0, 5, 0, 1)
table.attach(pwd_label, 1, 2, 2, 3)
table.attach(self.password, 2, 4, 2, 3)
table.attach(cls.password, 2, 4, 2, 3)
table.attach(check, 2, 4, 3, 4)
box2.pack_start(table, False, False, 0)
box2 = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, homogeneous=False, spacing=10)
box2.set_border_width(5)
box1.pack_start(box2, False, True, 0)
box2.show()
# Add create_scheme button
# Add authentication buttons
cancel = Gtk.Button(stock=Gtk.STOCK_CANCEL)
cancel.connect("clicked", self.close)
cancel.connect("clicked", cls.close)
connect = Gtk.Button(stock=Gtk.STOCK_CONNECT)
connect.connect("clicked", self.add_to_wpa_supplicant, ssid_info, card)
connect.connect("clicked", cls.add_to_wpa_supplicant, ssid_info, card)
table = Gtk.Table(1, 2, True)
table.set_col_spacings(10)
table.attach(connect, 4, 5, 0, 1)
table.attach(cancel, 3, 4, 0, 1)
box2.pack_end(table, True, True, 5)
self.window.show_all()
cls.window.show_all()
return 'Done'
def close(self, _widget):
self.window.hide()
@classmethod
def close(cls, _widget):
"""
Close the authentication dialog.
Args:
_widget: Button widget that triggered the action (unused)
"""
cls.window.hide()
@staticmethod
def setup_wpa_supplicant(ssid, ssid_info, pwd):
"""
Setup wpa_supplicant configuration for WiFi network.
Args:
ssid: WiFi network SSID
ssid_info: WiFi network information
pwd: WiFi network password
"""
if 'RSN' in ssid_info[-1]:
ws = '\nnetwork={'
ws += f'\n ssid="{ssid}"'
@@ -332,6 +457,12 @@ class NetworkSetup:
@staticmethod
def open_wpa_supplicant(ssid):
"""
Add open network entry to wpa_supplicant configuration.
Args:
ssid: WiFi network SSID
"""
ws = '\nnetwork={'
ws += f'\n ssid={ssid}'
ws += '\n key_mgmt=NONE\n}\n'
+1 -1
View File
@@ -1015,7 +1015,7 @@ class CreateLabel:
# class modifyLabel():
# def __init__(self, path, size_left, create_size, mount_point, fs,
# def __init_get_text(self, path, size_left, create_size, mount_point, fs,
# data, disk):
# if not os.path.exists(disk_file):
# file_disk = open(disk_file, 'w')
+3 -1
View File
@@ -58,7 +58,9 @@ def keyboard_dictionary():
kb_name = keyboard_list[1].strip()
kb_layouts = keyboard_list[0].strip()
kb_variant = None
dictionary[kb_name] = {'layout': kb_layouts, 'variant': kb_variant}
# Skip the "custom" layout as it's not a real keyboard layout
if kb_layouts != 'custom':
dictionary[kb_name] = {'layout': kb_layouts, 'variant': kb_variant}
xkeyboard_variants = Popen(f'{pc_sysinstall} xkeyboard-variants',
shell=True, stdout=PIPE,
+161
View File
@@ -0,0 +1,161 @@
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk
from install_station.data import InstallationData, get_text, gif_logo
cssProvider = Gtk.CssProvider()
cssProvider.load_from_path('/usr/local/lib/install-station/ghostbsd-style.css')
screen = Gdk.Screen.get_default()
styleContext = Gtk.StyleContext()
styleContext.add_provider_for_screen(
screen,
cssProvider,
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
)
class TryOrInstall:
"""
Utility class for the welcome screen and initial mode selection following the utility class pattern.
This class provides a GTK+ interface for the initial GhostBSD welcome screen including:
- Mode selection between "Install GhostBSD" and "Try GhostBSD" using radio buttons
- Visual elements with GhostBSD logo and instructional text
- Integration with InstallationData for persistent configuration
The class follows a utility pattern with class methods and variables for state management,
designed to integrate with the Interface controller for navigation flow.
"""
# Class variables instead of instance variables
what = None
install_button = None
try_button = None
instruction_label = None
vbox1 = None
@classmethod
def mode_selection(cls, widget, val):
"""
Handle mode selection from radio buttons.
Only responds to activation, not deactivation. Updates both
class variables and InstallationData with the selected mode.
Args:
widget: RadioButton widget that triggered the action
val: Mode value ('install' or 'try')
"""
# Only respond to activation, not deactivation
if widget.get_active():
cls.what = val
InstallationData.install_mode = val
print(f"Mode selected: {val}")
@classmethod
def get_what(cls):
"""
Get the current installation mode.
Returns the installation mode from InstallationData if available,
otherwise falls back to the class variable.
Returns:
str: Current installation mode ('install' or 'try')
"""
return InstallationData.install_mode or cls.what
@classmethod
def initialize(cls):
"""
Initialize the welcome screen UI following the utility class pattern.
Creates the main interface including:
- GhostBSD logo on the left side
- Radio buttons for Install/Try options on the right side
- Instructional text explaining the options
- Grid-based layout with proper spacing and margins
This method is called automatically by get_model() when the interface is first accessed.
"""
cls.what = 'install' # Default to install mode
InstallationData.install_mode = cls.what
cls.vbox1 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, homogeneous=False, spacing=0)
cls.vbox1.show()
main_grid = Gtk.Grid()
cls.vbox1.pack_start(main_grid, True, True, 0)
# Left side - Logo
logo_image = Gtk.Image()
logo_image.set_from_file(gif_logo)
logo_image.show()
# Right side - Radio button options
right_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, homogeneous=False, spacing=0)
right_box.set_border_width(20)
right_box.set_halign(Gtk.Align.CENTER)
right_box.set_valign(Gtk.Align.CENTER)
right_box.show()
# Instruction label
cls.instruction_label = Gtk.Label(label=get_text("What would you like to do?"))
cls.instruction_label.set_alignment(0.0, 0.5)
right_box.pack_start(cls.instruction_label, False, False, 10)
# Create radio button group
cls.install_button = Gtk.RadioButton(
label=get_text(
"<b>Install GhostBSD</b>\n"
"Install GhostBSD on your computer."
)
)
cls.install_button.get_child().set_use_markup(True)
cls.install_button.get_child().set_line_wrap(True)
right_box.pack_start(cls.install_button, False, False, 10)
cls.install_button.connect("toggled", cls.mode_selection, "install")
cls.install_button.show()
cls.try_button = Gtk.RadioButton.new_with_label_from_widget(
cls.install_button,
get_text(
"<b>Try GhostBSD</b>\n"
"Run GhostBSD without installing to your computer."
)
)
cls.try_button.get_child().set_use_markup(True)
cls.try_button.get_child().set_line_wrap(True)
right_box.pack_start(cls.try_button, False, False, 10)
cls.try_button.connect("toggled", cls.mode_selection, "try")
cls.try_button.show()
# Layout in grid
main_grid.set_row_spacing(20)
main_grid.set_column_spacing(20)
main_grid.set_column_homogeneous(True)
main_grid.set_row_homogeneous(True)
main_grid.set_margin_left(10)
main_grid.set_margin_right(10)
main_grid.set_margin_top(10)
main_grid.set_margin_bottom(10)
main_grid.attach(logo_image, 0, 0, 1, 1)
main_grid.attach(right_box, 1, 0, 1, 1)
main_grid.show()
# Set default selection
cls.install_button.set_active(True)
@classmethod
def get_model(cls):
"""
Return the GTK widget model for the welcome screen interface.
Returns the main container widget created during initialization.
Returns:
Gtk.Box: The main container widget for the welcome screen interface
"""
if cls.vbox1 is None:
cls.initialize()
return cls.vbox1
+24 -28
View File
@@ -1,7 +1,6 @@
from gi.repository import Gtk, Gdk
import gettext
from install_station.common import password_strength
from install_station.data import InstallationData, zfs_datasets, be_name, logo
from install_station.data import InstallationData, zfs_datasets, be_name, logo, get_text
from install_station.partition import bios_or_uefi
from install_station.system_calls import (
zfs_disk_query,
@@ -9,9 +8,6 @@ from install_station.system_calls import (
)
from install_station.interface_controller import Button
gettext.bindtextdomain('install-station', '/usr/local/share/locale')
gettext.textdomain('install-station')
_ = gettext.gettext
cssProvider = Gtk.CssProvider()
cssProvider.load_from_path('/usr/local/lib/install-station/ghostbsd-style.css')
@@ -161,14 +157,14 @@ class ZFS:
if cls.mirror == "1+ disks Stripe":
cls.pool_type = 'stripe'
cls.mirrorTips.set_text(
_("Please select 1 or more drive for stripe (select the smallest disk first)"))
get_text("Please select 1 or more drive for stripe (select the smallest disk first)"))
if len(cls.zfs_disk_list) >= 1:
Button.next_button.set_sensitive(True)
else:
Button.next_button.set_sensitive(False)
elif cls.mirror == "2+ disks Mirror":
cls.pool_type = 'mirror'
mir_msg1 = _("Please select 2 drive for mirroring (select the smallest disk first)")
mir_msg1 = get_text("Please select 2 drive for mirroring (select the smallest disk first)")
cls.mirrorTips.set_text(mir_msg1)
if len(cls.zfs_disk_list) >= 2:
Button.next_button.set_sensitive(True)
@@ -176,21 +172,21 @@ class ZFS:
Button.next_button.set_sensitive(False)
elif cls.mirror == "3 disks RAIDZ1":
cls.pool_type = 'raidz1'
cls.mirrorTips.set_text(_("Please select 3 drive for RAIDZ1 (select the smallest disk first)"))
cls.mirrorTips.set_text(get_text("Please select 3 drive for RAIDZ1 (select the smallest disk first)"))
if len(cls.zfs_disk_list) == 3:
Button.next_button.set_sensitive(True)
else:
Button.next_button.set_sensitive(False)
elif cls.mirror == "4 disks RAIDZ2":
cls.pool_type = 'raidz2'
cls.mirrorTips.set_text(_("Please select 4 drive for RAIDZ2 (select the smallest disk first)"))
cls.mirrorTips.set_text(get_text("Please select 4 drive for RAIDZ2 (select the smallest disk first)"))
if len(cls.zfs_disk_list) == 4:
Button.next_button.set_sensitive(True)
else:
Button.next_button.set_sensitive(False)
elif cls.mirror == "5 disks RAIDZ3":
cls.pool_type = 'raidz3'
cls.mirrorTips.set_text(_("Please select 5 drive for RAIDZ3 (select the smallest disk first)"))
cls.mirrorTips.set_text(get_text("Please select 5 drive for RAIDZ3 (select the smallest disk first)"))
if len(cls.zfs_disk_list) == 5:
Button.next_button.set_sensitive(True)
else:
@@ -293,24 +289,24 @@ class ZFS:
cls.check_cell.connect('toggled', cls.col1_toggled_cb, cls.store)
cell = Gtk.CellRendererText()
column = Gtk.TreeViewColumn(None, cell, text=0)
column_header = Gtk.Label(label=_('Disk'))
column_header = Gtk.Label(label=get_text('Disk'))
column_header.set_use_markup(True)
column_header.show()
column.set_widget(column_header)
column.set_sort_column_id(0)
cell2 = Gtk.CellRendererText()
column2 = Gtk.TreeViewColumn(None, cell2, text=0)
column_header2 = Gtk.Label(label=_('Size(MB)'))
column_header2 = Gtk.Label(label=get_text('Size(MB)'))
column_header2.set_use_markup(True)
column_header2.show()
column2.set_widget(column_header2)
cell3 = Gtk.CellRendererText()
column3 = Gtk.TreeViewColumn(None, cell3, text=0)
column_header3 = Gtk.Label(label=_('Name'))
column_header3 = Gtk.Label(label=get_text('Name'))
column_header3.set_use_markup(True)
column_header3.show()
column3.set_widget(column_header3)
column1 = Gtk.TreeViewColumn(_("Check"), cls.check_cell)
column1 = Gtk.TreeViewColumn(get_text("Check"), cls.check_cell)
column1.add_attribute(cls.check_cell, "active", 3)
column.set_attributes(cell, text=0)
column2.set_attributes(cell2, text=1)
@@ -323,20 +319,20 @@ class ZFS:
tree_selection.set_mode(Gtk.SelectionMode.SINGLE)
sw.add(treeview)
sw.show()
cls.mirrorTips = Gtk.Label(label=_('Please select one drive'))
cls.mirrorTips = Gtk.Label(label=get_text('Please select one drive'))
cls.mirrorTips.set_justify(Gtk.Justification.LEFT)
cls.mirrorTips.set_alignment(0.01, 0.5)
# Mirror, raidz and stripe
cls.mirror = 'none'
mirror_label = Gtk.Label(label=_('<b>Pool Type</b>'))
mirror_label = Gtk.Label(label=get_text('<b>Pool Type</b>'))
mirror_label.set_use_markup(True)
mirror_box = Gtk.ComboBox()
mirror_store = Gtk.ListStore(str, str) # value, display_text
mirror_store.append(["1+ disks Stripe", _("1+ disks Stripe")])
mirror_store.append(["2+ disks Mirror", _("2+ disks Mirror")])
mirror_store.append(["3 disks RAIDZ1", _("3 disks RAIDZ1")])
mirror_store.append(["4 disks RAIDZ2", _("4 disks RAIDZ2")])
mirror_store.append(["5 disks RAIDZ3", _("5 disks RAIDZ3")])
mirror_store.append(["1+ disks Stripe", get_text("1+ disks Stripe")])
mirror_store.append(["2+ disks Mirror", get_text("2+ disks Mirror")])
mirror_store.append(["3 disks RAIDZ1", get_text("3 disks RAIDZ1")])
mirror_store.append(["4 disks RAIDZ2", get_text("4 disks RAIDZ2")])
mirror_store.append(["5 disks RAIDZ3", get_text("5 disks RAIDZ3")])
mirror_box.set_model(mirror_store)
renderer = Gtk.CellRendererText()
mirror_box.pack_start(renderer, True)
@@ -346,7 +342,7 @@ class ZFS:
# Pool Name
cls.zpool = False
pool_name_label = Gtk.Label(label=_('<b>Pool Name</b>'))
pool_name_label = Gtk.Label(label=get_text('<b>Pool Name</b>'))
pool_name_label.set_use_markup(True)
cls.pool = Gtk.Entry()
cls.pool.set_text('zroot')
@@ -366,18 +362,18 @@ class ZFS:
shemebox.set_sensitive(True)
# GELI Disk encryption
cls.disk_encrypt = False
encrypt_check = Gtk.CheckButton(label=_("Encrypt Disk"))
encrypt_check = Gtk.CheckButton(label=get_text("Encrypt Disk"))
encrypt_check.connect("toggled", cls.on_check_encrypt)
encrypt_check.set_sensitive(True)
# password
cls.passwd_label = Gtk.Label(label=_("Password"))
cls.passwd_label = Gtk.Label(label=get_text("Password"))
cls.password = Gtk.Entry()
cls.password.set_sensitive(False)
cls.password.set_visibility(False)
cls.password.connect("changed", password_strength)
cls.strenght_label = Gtk.Label()
cls.strenght_label.set_alignment(0.1, 0.5)
cls.vpasswd_label = Gtk.Label(label=_("Verify it"))
cls.vpasswd_label = Gtk.Label(label=get_text("Verify it"))
cls.repassword = Gtk.Entry()
cls.repassword.set_sensitive(False)
cls.repassword.set_visibility(False)
@@ -538,7 +534,7 @@ class ZFS:
selected first and offers to reset all selections.
"""
window = Gtk.Window()
window.set_title(_("Warning"))
window.set_title(get_text("Warning"))
window.set_border_width(0)
# window.set_size_request(480, 200)
window.set_icon_from_file(logo)
@@ -549,8 +545,8 @@ class ZFS:
box2.set_border_width(10)
box1.pack_start(box2, True, True, 0)
box2.show()
warning_text = _("Smallest disk need to be SELECTED first!\n")
warning_text += _("All the disk selected will reset.")
warning_text = get_text("Smallest disk need to be SELECTED first!\n")
warning_text += get_text("All the disk selected will reset.")
label = Gtk.Label(label=warning_text)
# Add button
box2.pack_start(label, True, True, 0)
-293
View File
@@ -1,293 +0,0 @@
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk, GdkPixbuf
import gettext
from install_station.system_calls import language_dictionary
from install_station.interface_controller import Interface
from install_station.data import InstallationData
from install_station.window import Window
gettext.bindtextdomain('install-station', '/usr/local/share/locale')
gettext.textdomain('install-station')
_ = gettext.gettext
lang_dictionary = language_dictionary()
Messages = _("""To run GhostBSD without installing, select "Try GhostBSD."
To install GhostBSD on your computer hard disk drive, click "Install GhostBSD."
Note: Language selection only works when selecting "Try GhostBSD."
When installing GhostBSD, the installation program is only in English.""")
cssProvider = Gtk.CssProvider()
cssProvider.load_from_path('/usr/local/lib/install-station/ghostbsd-style.css')
screen = Gdk.Screen.get_default()
styleContext = Gtk.StyleContext()
styleContext.add_provider_for_screen(
screen,
cssProvider,
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
)
class Welcome:
"""
Utility class for the welcome screen and initial mode selection following the utility class pattern.
This class provides a GTK+ interface for the initial GhostBSD welcome screen including:
- Language selection from available system languages
- Mode selection between "Install GhostBSD" and "Try GhostBSD"
- Visual elements with images and instructional text
- Integration with InstallationData for persistent configuration
The class follows a utility pattern with class methods and variables for state management,
designed to integrate with the Interface controller for navigation flow.
"""
# Class variables instead of instance variables
what = None
language = None
install_ghostbsd = None
try_ghostbsd = None
vbox1 = None
@classmethod
def language_selection(cls, tree_selection):
"""
Handle language selection from the treeview.
Extracts the selected language from the tree view and updates both
class variables and InstallationData with the language name and code.
Also updates the UI text to reflect the new language selection.
Args:
tree_selection: TreeSelection widget containing the user's language choice
"""
model, tree_iter = tree_selection.get_selected()
if tree_iter is not None:
value = model[tree_iter][0]
language_code = lang_dictionary[value]
cls.language = language_code
InstallationData.language = value
InstallationData.language_code = language_code
print(f"Language selected: {value} ({language_code})")
# Update gettext to use the new language
import os
os.environ['LANGUAGE'] = language_code
gettext.bindtextdomain('install-station', '/usr/local/share/locale')
gettext.textdomain('install-station')
# Update the UI text with new translations
cls.update_ui_text()
return
@classmethod
def update_ui_text(cls):
"""
Update all UI text elements with new translations after language change.
"""
# Reload the gettext function to pick up new locale
_ = gettext.gettext
# Update UI elements if they exist
if hasattr(cls, 'install_button') and cls.install_button:
cls.install_button.set_label(_('Install GhostBSD'))
if hasattr(cls, 'try_button') and cls.try_button:
cls.try_button.set_label(_('Try GhostBSD'))
if hasattr(cls, 'text_label') and cls.text_label:
cls.text_label.set_text(_("""To run GhostBSD without installing, select "Try GhostBSD."
To install GhostBSD on your computer hard disk drive, click "Install GhostBSD."
Note: Language selection only works when selecting "Try GhostBSD."
When installing GhostBSD, the installation program is only in English."""))
if hasattr(cls, 'language_column_header') and cls.language_column_header:
cls.language_column_header.set_text(_('Language'))
Window.set_title(_("Welcome to GhostBSD"))
@classmethod
def language_columns(cls, treeview):
"""
Configure the language selection treeview with appropriate columns.
Creates a single column with a "Language" header for displaying
available languages in the tree view.
Args:
treeview: TreeView widget to configure with language column
"""
cell = Gtk.CellRendererText()
column = Gtk.TreeViewColumn(None, cell, text=0)
column_header = Gtk.Label(label=_('Language'))
column_header.set_use_markup(True)
column_header.show()
column.set_widget(column_header)
# Store reference for updating
cls.language_column_header = column_header
column.set_sort_column_id(0)
treeview.append_column(column)
@classmethod
def save_selection(cls):
"""
Save the current language selection.
This method is maintained for compatibility but language selection
is now automatically saved to InstallationData when chosen.
"""
# Language is now saved in InstallationData automatically
pass
@classmethod
def install_system(cls, _widget):
"""
Handle "Install GhostBSD" button click.
Sets the installation mode to 'install' and navigates to the
installation workflow pages.
Args:
_widget: Button widget that triggered the action (unused)
"""
cls.what = 'install'
InstallationData.install_mode = 'install'
Interface.next_install_page()
@classmethod
def try_system(cls, _widget):
"""
Handle "Try GhostBSD" button click.
Sets the installation mode to 'try' and navigates to the
live system setup pages (typically network configuration).
Args:
_widget: Button widget that triggered the action (unused)
"""
cls.what = 'try'
InstallationData.install_mode = 'try'
Interface.next_setup_page()
@classmethod
def get_what(cls):
"""
Get the current installation mode.
Returns the installation mode from InstallationData if available,
otherwise falls back to the class variable.
Returns:
str: Current installation mode ('install' or 'try')
"""
return InstallationData.install_mode or cls.what
@classmethod
def initialize(cls):
"""
Initialize the welcome screen UI following the utility class pattern.
Creates the main interface including:
- Language selection tree view on the left side
- Install and Try buttons with images on the right side
- Instructional text explaining the options
- Grid-based layout with proper spacing and margins
This method is called automatically by get_model() when the interface is first accessed.
"""
cls.what = None
cls.language = None
cls.vbox1 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, homogeneous=False, spacing=0)
cls.vbox1.show()
main_grid = Gtk.Grid()
cls.vbox1.pack_start(main_grid, True, True, 0)
sw = Gtk.ScrolledWindow()
sw.set_shadow_type(Gtk.ShadowType.ETCHED_IN)
sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
store = Gtk.TreeStore(str)
for line in lang_dictionary:
store.append(None, [line])
treeview = Gtk.TreeView()
treeview.set_model(store)
treeview.set_rules_hint(True)
cls.language_columns(treeview)
tree_selection = treeview.get_selection()
tree_selection.set_mode(Gtk.SelectionMode.SINGLE)
tree_selection.connect("changed", cls.language_selection)
sw.add(treeview)
sw.show()
vbox2 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, homogeneous=False, spacing=0)
vbox2.set_border_width(10)
vbox2.show()
pix_buf1 = GdkPixbuf.Pixbuf().new_from_file_at_scale(
filename='/usr/local/lib/install-station/laptop.png',
width=190,
height=190,
preserve_aspect_ratio=True
)
image1 = Gtk.Image.new_from_pixbuf(pix_buf1)
image1.show()
pix_buf2 = GdkPixbuf.Pixbuf().new_from_file_at_scale(
filename='/usr/local/lib/install-station/disk.png',
width=120,
height=120,
preserve_aspect_ratio=True)
image2 = Gtk.Image.new_from_pixbuf(pix_buf2)
image2.show()
install_button = Gtk.Button(label=_('Install GhostBSD'), image=image1,
image_position=2)
install_button.set_always_show_image(True)
install_button.connect("clicked", cls.install_system)
try_button = Gtk.Button(label=_('Try GhostBSD'), image=image2,
image_position=2)
try_button.set_always_show_image(True)
try_button.connect("clicked", cls.try_system)
text_label = Gtk.Label(label=Messages)
text_label.set_line_wrap(True)
# Store references for updating
cls.install_button = install_button
cls.try_button = try_button
cls.text_label = text_label
right_grid = Gtk.Grid()
right_grid.set_row_spacing(10)
right_grid.set_column_spacing(2)
right_grid.set_column_homogeneous(True)
right_grid.set_row_homogeneous(True)
right_grid.set_margin_left(10)
right_grid.set_margin_right(10)
right_grid.set_margin_top(10)
right_grid.set_margin_bottom(10)
right_grid.attach(install_button, 1, 1, 1, 5)
right_grid.attach(try_button, 2, 1, 1, 5)
right_grid.attach(text_label, 1, 6, 2, 5)
main_grid.set_row_spacing(10)
main_grid.set_column_spacing(4)
main_grid.set_column_homogeneous(True)
main_grid.set_row_homogeneous(True)
main_grid.set_margin_left(10)
main_grid.set_margin_right(10)
main_grid.set_margin_top(10)
main_grid.set_margin_bottom(10)
main_grid.attach(sw, 1, 1, 1, 10)
main_grid.attach(right_grid, 2, 1, 3, 10)
main_grid.show()
@classmethod
def get_model(cls):
"""
Return the GTK widget model for the welcome screen interface.
Returns the main container widget that was created during initialization.
Returns:
Gtk.Box: The main container widget for the welcome screen interface
"""
if cls.vbox1 is None:
cls.initialize()
return cls.vbox1