diff --git a/ui/FlowBox.py b/ui/FlowBox.py
new file mode 100644
index 0000000..716d914
--- /dev/null
+++ b/ui/FlowBox.py
@@ -0,0 +1,668 @@
+import datetime
+
+import gi
+import os
+import libs.functions as fn
+from ui.ProgressWindow import ProgressWindow
+from ui.MessageWindow import MessageWindow
+
+gi.require_version("Gtk", "4.0")
+from gi.repository import Gtk, Gio, GLib
+
+base_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
+
+
+class FlowBox(Gtk.FlowBox):
+ def __init__(
+ self,
+ kernel,
+ active_kernel,
+ manager_gui,
+ source,
+ ):
+ super(FlowBox, self).__init__()
+
+ self.manager_gui = manager_gui
+
+ # self.set_row_spacing(1)
+ # self.set_column_spacing(1)
+ # self.set_name("hbox_kernel")
+ # self.set_activate_on_single_click(True)
+ # self.connect("child-activated", self.on_child_activated)
+ self.set_valign(Gtk.Align.START)
+
+ self.set_selection_mode(Gtk.SelectionMode.NONE)
+
+ # self.set_homogeneous(True)
+
+ self.set_max_children_per_line(2)
+ self.set_min_children_per_line(2)
+ self.kernel_count = 0
+
+ self.active_kernel_found = False
+ self.kernels = []
+
+ self.kernel = kernel
+ self.source = source
+
+ if self.source == "official":
+ self.flowbox_official()
+
+ if self.source == "community":
+ self.flowbox_community()
+
+ def flowbox_community(self):
+ for community_kernel in self.kernel:
+ self.kernels.append(community_kernel)
+ self.kernel_count += 1
+
+ if len(self.kernels) > 0:
+ installed = False
+
+ for cache in self.kernels:
+ fb_child = Gtk.FlowBoxChild()
+ fb_child.set_name(
+ "%s %s %s" % (cache.name, cache.version, cache.repository)
+ )
+
+ vbox_kernel_widgets = Gtk.Box(
+ orientation=Gtk.Orientation.VERTICAL, spacing=0
+ )
+ vbox_kernel_widgets.set_name("vbox_kernel_widgets")
+ vbox_kernel_widgets.set_homogeneous(True)
+
+ switch_kernel = Gtk.Switch()
+ switch_kernel.set_halign(Gtk.Align.START)
+
+ hbox_kernel_switch = Gtk.Box(
+ orientation=Gtk.Orientation.HORIZONTAL, spacing=0
+ )
+
+ hbox_kernel_switch.append(switch_kernel)
+
+ label_kernel_size = Gtk.Label(xalign=0, yalign=0)
+ label_kernel_size.set_name("label_kernel_flowbox")
+
+ label_kernel_name = Gtk.Label(xalign=0, yalign=0)
+ label_kernel_name.set_name("label_kernel_version")
+ label_kernel_name.set_markup(
+ "%s %s %s"
+ % (cache.name, cache.version, cache.repository)
+ )
+ label_kernel_name.set_selectable(True)
+
+ vbox_kernel_widgets.append(label_kernel_name)
+
+ tux_icon = Gtk.Picture.new_for_file(
+ file=Gio.File.new_for_path(
+ os.path.join(base_dir, "images/48x48/akm-tux.png")
+ )
+ )
+ tux_icon.set_can_shrink(True)
+
+ for installed_kernel in self.manager_gui.installed_kernels:
+ if "{}-{}".format(
+ installed_kernel.name, installed_kernel.version
+ ) == "{}-{}".format(cache.name, cache.version):
+ installed = True
+
+ if cache.name == installed_kernel.name:
+ if (
+ cache.version > installed_kernel.version
+ or cache.version != installed_kernel.version
+ ):
+ fn.logger.info(
+ "Kernel upgrade available - %s %s"
+ % (cache.name, cache.version)
+ )
+
+ tux_icon = Gtk.Picture.new_for_file(
+ file=Gio.File.new_for_path(
+ os.path.join(
+ base_dir, "images/48x48/akm-update.png"
+ )
+ )
+ )
+ tux_icon.set_can_shrink(True)
+
+ label_kernel_name.set_markup(
+ "*%s %s %s"
+ % (cache.name, cache.version, cache.repository)
+ )
+
+ if installed is True:
+ switch_kernel.set_state(True)
+ switch_kernel.set_active(True)
+
+ else:
+ switch_kernel.set_state(False)
+ switch_kernel.set_active(False)
+
+ tux_icon.set_content_fit(content_fit=Gtk.ContentFit.SCALE_DOWN)
+ tux_icon.set_halign(Gtk.Align.START)
+
+ installed = False
+ switch_kernel.connect("state-set", self.kernel_toggle_state, cache)
+
+ hbox_kernel = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=0)
+ hbox_kernel.set_name("hbox_kernel")
+
+ label_kernel_size.set_text("%sM" % str(cache.install_size))
+
+ vbox_kernel_widgets.append(label_kernel_size)
+
+ label_kernel_build_date = Gtk.Label(xalign=0, yalign=0)
+ label_kernel_build_date.set_name("label_kernel_flowbox")
+ label_kernel_build_date.set_text(cache.build_date)
+
+ vbox_kernel_widgets.append(label_kernel_build_date)
+
+ vbox_kernel_widgets.append(hbox_kernel_switch)
+
+ hbox_kernel.append(tux_icon)
+ hbox_kernel.append(vbox_kernel_widgets)
+
+ fb_child.set_child(hbox_kernel)
+
+ self.append(fb_child)
+
+ def flowbox_official(self):
+ for official_kernel in self.manager_gui.official_kernels:
+ if official_kernel.name == self.kernel:
+ self.kernels.append(official_kernel)
+ self.kernel_count += 1
+
+ if len(self.kernels) > 0:
+ installed = False
+
+ latest = sorted(self.kernels)[:-1][0]
+
+ for cache in sorted(self.kernels):
+ fb_child = Gtk.FlowBoxChild()
+ fb_child.set_name("%s %s" % (cache.name, cache.version))
+ if cache == latest:
+ tux_icon = Gtk.Picture.new_for_file(
+ file=Gio.File.new_for_path(
+ os.path.join(base_dir, "images/48x48/akm-new.png")
+ )
+ )
+
+ else:
+ tux_icon = Gtk.Picture.new_for_file(
+ file=Gio.File.new_for_path(
+ os.path.join(base_dir, "images/48x48/akm-tux.png")
+ )
+ )
+
+ tux_icon.set_content_fit(content_fit=Gtk.ContentFit.SCALE_DOWN)
+ tux_icon.set_halign(Gtk.Align.START)
+
+ vbox_kernel_widgets = Gtk.Box(
+ orientation=Gtk.Orientation.VERTICAL, spacing=0
+ )
+ vbox_kernel_widgets.set_homogeneous(True)
+
+ hbox_kernel_switch = Gtk.Box(
+ orientation=Gtk.Orientation.HORIZONTAL, spacing=0
+ )
+
+ switch_kernel = Gtk.Switch()
+ switch_kernel.set_halign(Gtk.Align.START)
+
+ hbox_kernel_switch.append(switch_kernel)
+
+ label_kernel_version = Gtk.Label(xalign=0, yalign=0)
+ label_kernel_version.set_name("label_kernel_version")
+ label_kernel_version.set_selectable(True)
+
+ label_kernel_size = Gtk.Label(xalign=0, yalign=0)
+ label_kernel_size.set_name("label_kernel_flowbox")
+
+ if self.manager_gui.installed_kernels is None:
+ self.manager_gui.installed_kernels = fn.get_installed_kernels()
+
+ for installed_kernel in self.manager_gui.installed_kernels:
+ if (
+ "{}-{}".format(installed_kernel.name, installed_kernel.version)
+ == cache.version
+ ):
+ installed = True
+
+ if installed is True:
+ switch_kernel.set_state(True)
+ switch_kernel.set_active(True)
+
+ else:
+ switch_kernel.set_state(False)
+ switch_kernel.set_active(False)
+
+ installed = False
+ switch_kernel.connect("state-set", self.kernel_toggle_state, cache)
+
+ label_kernel_version.set_markup("%s" % cache.version)
+
+ hbox_kernel = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=0)
+ hbox_kernel.set_name("hbox_kernel")
+
+ label_kernel_size.set_text(cache.size)
+
+ vbox_kernel_widgets.append(label_kernel_version)
+ vbox_kernel_widgets.append(label_kernel_size)
+
+ label_kernel_modified = Gtk.Label(xalign=0, yalign=0)
+ label_kernel_modified.set_name("label_kernel_flowbox")
+ label_kernel_modified.set_text(cache.last_modified)
+
+ vbox_kernel_widgets.append(label_kernel_modified)
+
+ vbox_kernel_widgets.append(hbox_kernel_switch)
+
+ hbox_kernel.append(tux_icon)
+ hbox_kernel.append(vbox_kernel_widgets)
+
+ fb_child.set_child(hbox_kernel)
+
+ self.append(fb_child)
+
+ else:
+ fn.logger.error("Failed to read in kernels.")
+
+ def kernel_toggle_state(self, switch, data, kernel):
+ fn.logger.debug(
+ "Switch toggled, kernel selected = %s %s" % (kernel.name, kernel.version)
+ )
+ message = None
+ title = None
+ downgrade = False
+ community_repo = False
+
+ if fn.check_pacman_lockfile() is False:
+ # switch widget is currently toggled off
+ if switch.get_state() is False: # and switch.get_active() is True:
+ for inst_kernel in fn.get_installed_kernels():
+ if inst_kernel.name == kernel.name:
+ if self.source == "official":
+ if (
+ inst_kernel.version
+ > kernel.version.split("%s-" % inst_kernel.name)[1]
+ ):
+ downgrade = True
+ title = "Downgrading %s kernel" % kernel.name
+ else:
+ downgrade = False
+ title = "Upgrading %s kernel" % kernel.name
+
+ break
+
+ if title is None:
+ title = "Kernel installation"
+
+ if self.source == "community":
+ message = (
+ "Community kernel selected - this may break your system\n"
+ "Confirm the install of %s-%s"
+ % (
+ kernel.name,
+ kernel.version,
+ )
+ )
+
+ # check if the community pacman repo is configured
+ if fn.check_pacman_repo(kernel.repository) is True:
+ community_repo = True
+ else:
+ community_repo = False
+ fn.logger.error(
+ "%s pacman repo is not configured" % kernel.repository
+ )
+
+ elif self.source == "official":
+ message = "Confirm the install of %s-%s" % (
+ kernel.name,
+ kernel.version,
+ )
+
+ if community_repo is False and self.source == "community":
+ mw = MessageWindow(
+ title="Cannot find %s pacman repo" % kernel.repository,
+ message="Enable the pacman repository then retry the installation",
+ transient_for=self.manager_gui,
+ detailed_message=False,
+ )
+ mw.present()
+ else:
+ message_window = FlowBoxMessageWindow(
+ title=title,
+ message=message,
+ action="install",
+ kernel=kernel,
+ transient_for=self.manager_gui,
+ textview=self.manager_gui.textview,
+ textbuffer=self.manager_gui.textbuffer,
+ switch=switch,
+ source=self.source,
+ manager_gui=self.manager_gui,
+ downgrade=downgrade,
+ )
+ message_window.present()
+ return True
+
+ # switch widget is currently toggled on
+ # if widget.get_state() == True and widget.get_active() == False:
+ if switch.get_state() is True:
+ # and switch.get_active() is False:
+ installed_kernels = fn.get_installed_kernels()
+
+ if len(installed_kernels) > 1:
+
+ if self.source == "community":
+ message = "Confirm the removal of %s-%s" % (
+ kernel.name,
+ kernel.version,
+ )
+ elif self.source == "official":
+ message = "Confirm the removal of %s-%s" % (
+ kernel.name,
+ kernel.version,
+ )
+
+ message_window = FlowBoxMessageWindow(
+ title="Kernel uninstallation",
+ message=message,
+ action="uninstall",
+ kernel=kernel,
+ transient_for=self.manager_gui,
+ textview=self.manager_gui.textview,
+ textbuffer=self.manager_gui.textbuffer,
+ switch=switch,
+ source=self.source,
+ manager_gui=self.manager_gui,
+ downgrade=downgrade,
+ )
+ message_window.present()
+ return True
+ else:
+ switch.set_state(True)
+ # switch.set_active(False)
+ fn.logger.warn(
+ "You only have 1 kernel installed, and %s-%s is currently running, uninstall aborted."
+ % (kernel.name, kernel.version)
+ )
+ msg_win = MessageWindow(
+ title="Warning: Uninstall aborted",
+ message=f"You only have 1 kernel installed\n"
+ f"{kernel.name} {kernel.version} is currently active\n",
+ transient_for=self.manager_gui,
+ detailed_message=False,
+ )
+ msg_win.present()
+ return True
+
+ else:
+ fn.logger.error(
+ "Pacman lockfile found, is another pacman process running ?"
+ )
+
+ msg_win = MessageWindow(
+ title="Warning",
+ message="Pacman lockfile found, which indicates another pacman process is running",
+ transient_for=self.manager_gui,
+ detailed_message=False,
+ )
+ msg_win.present()
+ return True
+
+ # while self.manager_gui.default_context.pending():
+ # self.manager_gui.default_context.iteration(True)
+
+
+class FlowBoxInstalled(Gtk.FlowBox):
+ def __init__(self, installed_kernels, manager_gui, **kwargs):
+ super().__init__(**kwargs)
+
+ self.set_selection_mode(Gtk.SelectionMode.NONE)
+
+ self.set_homogeneous(True)
+ self.set_max_children_per_line(2)
+ self.set_min_children_per_line(2)
+
+ self.manager_gui = manager_gui
+
+ for installed_kernel in installed_kernels:
+ tux_icon = Gtk.Picture.new_for_file(
+ file=Gio.File.new_for_path(
+ os.path.join(base_dir, "images/48x48/akm-tux.png")
+ )
+ )
+
+ fb_child = Gtk.FlowBoxChild()
+ fb_child.set_name(
+ "%s %s" % (installed_kernel.name, installed_kernel.version)
+ )
+
+ tux_icon.set_content_fit(content_fit=Gtk.ContentFit.SCALE_DOWN)
+ tux_icon.set_halign(Gtk.Align.START)
+
+ label_installed_kernel_version = Gtk.Label(xalign=0, yalign=0)
+ label_installed_kernel_version.set_name("label_kernel_version")
+ label_installed_kernel_version.set_markup(
+ "%s %s" % (installed_kernel.name, installed_kernel.version)
+ )
+ label_installed_kernel_version.set_selectable(True)
+
+ hbox_installed_version = Gtk.Box(
+ orientation=Gtk.Orientation.HORIZONTAL, spacing=0
+ )
+
+ hbox_installed_version.append(label_installed_kernel_version)
+
+ label_installed_kernel_size = Gtk.Label(xalign=0, yalign=0)
+ label_installed_kernel_size.set_name("label_kernel_flowbox")
+ label_installed_kernel_size.set_text("%sM" % str(installed_kernel.size))
+
+ label_installed_kernel_date = Gtk.Label(xalign=0, yalign=0)
+ label_installed_kernel_date.set_name("label_kernel_flowbox")
+ label_installed_kernel_date.set_text("%s" % installed_kernel.date)
+
+ btn_uninstall_kernel = Gtk.Button.new_with_label("Remove")
+
+ btn_context = btn_uninstall_kernel.get_style_context()
+ btn_context.add_class("destructive-action")
+
+ vbox_uninstall_button = Gtk.Box(
+ orientation=Gtk.Orientation.HORIZONTAL, spacing=0
+ )
+ vbox_uninstall_button.set_name("box_padding_left")
+
+ btn_uninstall_kernel.set_hexpand(False)
+ btn_uninstall_kernel.set_halign(Gtk.Align.CENTER)
+ btn_uninstall_kernel.set_vexpand(False)
+ btn_uninstall_kernel.set_valign(Gtk.Align.CENTER)
+
+ vbox_uninstall_button.append(btn_uninstall_kernel)
+
+ btn_uninstall_kernel.connect(
+ "clicked", self.button_uninstall_kernel, installed_kernel
+ )
+
+ vbox_kernel_widgets = Gtk.Box(
+ orientation=Gtk.Orientation.VERTICAL, spacing=0
+ )
+
+ vbox_kernel_widgets.append(hbox_installed_version)
+ vbox_kernel_widgets.append(label_installed_kernel_size)
+ vbox_kernel_widgets.append(label_installed_kernel_date)
+ vbox_kernel_widgets.append(vbox_uninstall_button)
+
+ hbox_kernel = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=0)
+ hbox_kernel.set_name("hbox_kernel")
+
+ hbox_kernel.append(tux_icon)
+ hbox_kernel.append(vbox_kernel_widgets)
+
+ fb_child.set_child(hbox_kernel)
+
+ self.append(fb_child)
+
+ def button_uninstall_kernel(self, button, installed_kernel):
+ installed_kernels = fn.get_installed_kernels()
+
+ if len(installed_kernels) > 1:
+ fn.logger.info(
+ "Selected kernel to remove = %s %s"
+ % (installed_kernel.name, installed_kernel.version)
+ )
+
+ message_window = FlowBoxMessageWindow(
+ title="Kernel uninstall",
+ message="This will remove %s-%s - Is this ok ?"
+ % (installed_kernel.name, installed_kernel.version),
+ action="uninstall",
+ kernel=installed_kernel,
+ transient_for=self.manager_gui,
+ textview=self.manager_gui.textview,
+ textbuffer=self.manager_gui.textbuffer,
+ switch=None,
+ source=None,
+ manager_gui=self.manager_gui,
+ downgrade=None,
+ )
+ message_window.present()
+ else:
+ fn.logger.warn(
+ "You only have 1 kernel installed %s %s, uninstall aborted."
+ % (installed_kernel.name, installed_kernel.version)
+ )
+ msg_win = MessageWindow(
+ title="Warning: Uninstall aborted",
+ message=f"You only have 1 kernel installed\n"
+ f"{installed_kernel.name} {installed_kernel.version}\n",
+ transient_for=self.manager_gui,
+ detailed_message=False,
+ )
+ msg_win.present()
+
+
+class FlowBoxMessageWindow(Gtk.Window):
+ def __init__(
+ self,
+ title,
+ message,
+ action,
+ kernel,
+ textview,
+ textbuffer,
+ switch,
+ source,
+ manager_gui,
+ downgrade,
+ **kwargs,
+ ):
+ super().__init__(**kwargs)
+
+ self.set_title(title=title)
+ self.set_modal(modal=True)
+ self.set_resizable(False)
+ self.set_icon_name("archlinux-kernel-manager-tux")
+
+ header_bar = Gtk.HeaderBar()
+ header_bar.set_show_title_buttons(False)
+
+ label_title = Gtk.Label(xalign=0.5, yalign=0.5)
+ label_title.set_markup("%s" % title)
+
+ self.set_titlebar(header_bar)
+
+ header_bar.set_title_widget(label_title)
+
+ self.textview = textview
+ self.textbuffer = textbuffer
+ self.manager_gui = manager_gui
+ self.kernel = kernel
+ self.action = action
+ self.switch = switch
+ self.source = source
+ self.downgrade = downgrade
+
+ vbox_flowbox_message = Gtk.Box.new(
+ orientation=Gtk.Orientation.VERTICAL, spacing=10
+ )
+ vbox_flowbox_message.set_name("vbox_flowbox_message")
+
+ self.set_child(child=vbox_flowbox_message)
+
+ label_flowbox_message = Gtk.Label(xalign=0, yalign=0)
+ label_flowbox_message.set_markup("%s" % message)
+ label_flowbox_message.set_name("label_flowbox_message")
+
+ vbox_flowbox_message.set_halign(Gtk.Align.CENTER)
+
+ # Widgets.
+ button_yes = Gtk.Button.new_with_label("Yes")
+ button_yes.set_size_request(100, 30)
+ button_yes.set_halign(Gtk.Align.END)
+ button_yes_context = button_yes.get_style_context()
+ button_yes_context.add_class("destructive-action")
+ button_yes.connect("clicked", self.on_button_yes_clicked)
+
+ button_no = Gtk.Button.new_with_label("No")
+ button_no.set_size_request(100, 30)
+ button_no.set_halign(Gtk.Align.END)
+ button_no.connect("clicked", self.on_button_no_clicked)
+
+ hbox_buttons = Gtk.Box.new(orientation=Gtk.Orientation.HORIZONTAL, spacing=15)
+ hbox_buttons.set_halign(Gtk.Align.CENTER)
+ hbox_buttons.append(button_yes)
+ hbox_buttons.append(button_no)
+
+ vbox_flowbox_message.append(label_flowbox_message)
+ vbox_flowbox_message.append(hbox_buttons)
+
+ def on_button_yes_clicked(self, button):
+ self.hide()
+ self.destroy()
+ progress_window = None
+ if fn.check_pacman_lockfile() is False:
+ if self.action == "uninstall":
+ progress_window = ProgressWindow(
+ title="Removing kernel",
+ action="uninstall",
+ textview=self.textview,
+ textbuffer=self.textbuffer,
+ kernel=self.kernel,
+ switch=self.switch,
+ source=self.source,
+ manager_gui=self.manager_gui,
+ transient_for=self.manager_gui,
+ )
+
+ if self.action == "install":
+ progress_window = ProgressWindow(
+ title="Installing kernel",
+ action="install",
+ textview=self.textview,
+ textbuffer=self.textbuffer,
+ kernel=self.kernel,
+ switch=self.switch,
+ source=self.source,
+ manager_gui=self.manager_gui,
+ transient_for=self.manager_gui,
+ )
+ else:
+ fn.logger.error(
+ "Pacman lockfile found, is another pacman process running ?"
+ )
+
+ def on_button_no_clicked(self, button):
+ if self.action == "uninstall":
+ if self.switch is not None:
+ self.switch.set_state(True)
+
+ elif self.action == "install":
+ if self.switch is not None:
+ self.switch.set_state(False)
+
+ self.hide()
+ self.destroy()
+
+ return True
diff --git a/ui/KernelStack.py b/ui/KernelStack.py
new file mode 100644
index 0000000..2b4d385
--- /dev/null
+++ b/ui/KernelStack.py
@@ -0,0 +1,657 @@
+import gi
+import os
+import libs.functions as fn
+from ui.FlowBox import FlowBox, FlowBoxInstalled
+from ui.Stack import Stack
+from libs.Kernel import Kernel, InstalledKernel, CommunityKernel
+
+gi.require_version("Gtk", "4.0")
+from gi.repository import Gtk, Gio, Gdk
+
+base_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
+
+
+class KernelStack:
+ def __init__(
+ self,
+ manager_gui,
+ **kwargs,
+ ):
+ super().__init__(**kwargs)
+ self.manager_gui = manager_gui
+ self.flowbox_stacks = []
+ self.search_entries = []
+
+ def add_installed_kernels_to_stack(self, reload):
+ vbox_header = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5)
+ vbox_header.set_name("vbox_header")
+
+ lbl_heading = Gtk.Label(xalign=0.5, yalign=0.5)
+ lbl_heading.set_name("label_flowbox_message")
+ lbl_heading.set_text("%s" % "Installed kernels".upper())
+
+ lbl_padding = Gtk.Label(xalign=0.0, yalign=0.0)
+ lbl_padding.set_text(" ")
+
+ grid_banner_img = Gtk.Grid()
+
+ image_settings = Gtk.Image.new_from_file(
+ os.path.join(base_dir, "images/48x48/akm-install.png")
+ )
+
+ image_settings.set_icon_size(Gtk.IconSize.LARGE)
+ image_settings.set_halign(Gtk.Align.START)
+
+ grid_banner_img.attach(image_settings, 0, 1, 1, 1)
+ grid_banner_img.attach_next_to(
+ lbl_padding,
+ image_settings,
+ Gtk.PositionType.RIGHT,
+ 1,
+ 1,
+ )
+
+ grid_banner_img.attach_next_to(
+ lbl_heading,
+ lbl_padding,
+ Gtk.PositionType.RIGHT,
+ 1,
+ 1,
+ )
+
+ vbox_header.append(grid_banner_img)
+
+ label_installed_desc = Gtk.Label(xalign=0, yalign=0)
+ label_installed_desc.set_text("Installed Linux kernel and modules")
+ label_installed_desc.set_name("label_stack_desc")
+
+ label_installed_count = Gtk.Label(xalign=0, yalign=0)
+
+ label_installed_count.set_name("label_stack_count")
+
+ vbox_search_entry = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5)
+
+ search_entry_installed = Gtk.SearchEntry()
+ search_entry_installed.set_name("search_entry_installed")
+ search_entry_installed.set_placeholder_text("Search installed kernels...")
+ search_entry_installed.connect("search_changed", self.flowbox_filter_installed)
+
+ vbox_search_entry.append(search_entry_installed)
+
+ if reload is True:
+ if self.manager_gui.vbox_installed_kernels is not None:
+ for widget in self.manager_gui.vbox_installed_kernels:
+ if widget.get_name() == "label_stack_count":
+ widget.set_markup(
+ "%s Installed kernels"
+ % len(self.manager_gui.installed_kernels)
+ )
+
+ if widget.get_name() == "scrolled_window_installed":
+ self.manager_gui.vbox_installed_kernels.remove(widget)
+
+ scrolled_window_installed = Gtk.ScrolledWindow()
+ scrolled_window_installed.set_name("scrolled_window_installed")
+ scrolled_window_installed.set_policy(
+ Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC
+ )
+ scrolled_window_installed.set_propagate_natural_height(True)
+ scrolled_window_installed.set_propagate_natural_width(True)
+
+ self.flowbox_installed = FlowBoxInstalled(
+ installed_kernels=self.manager_gui.installed_kernels,
+ manager_gui=self.manager_gui,
+ )
+ vbox_installed_flowbox = Gtk.Box(
+ orientation=Gtk.Orientation.VERTICAL, spacing=12
+ )
+
+ # vbox_installed_flowbox.set_halign(align=Gtk.Align.FILL)
+
+ vbox_installed_flowbox.append(self.flowbox_installed)
+
+ scrolled_window_installed.set_child(vbox_installed_flowbox)
+
+ self.manager_gui.vbox_installed_kernels.append(scrolled_window_installed)
+
+ if self.manager_gui.vbox_active_installed_kernel is not None:
+ self.manager_gui.vbox_installed_kernels.reorder_child_after(
+ self.manager_gui.vbox_active_installed_kernel,
+ scrolled_window_installed,
+ )
+ else:
+ self.manager_gui.vbox_installed_kernels = Gtk.Box(
+ orientation=Gtk.Orientation.VERTICAL, spacing=5
+ )
+ self.manager_gui.vbox_installed_kernels.set_name("vbox_installed_kernels")
+
+ self.manager_gui.vbox_active_installed_kernel = Gtk.Box(
+ orientation=Gtk.Orientation.HORIZONTAL, spacing=5
+ )
+ self.manager_gui.vbox_active_installed_kernel.set_name("vbox_active_kernel")
+
+ label_active_installed_kernel = Gtk.Label(xalign=0.5, yalign=0.5)
+ label_active_installed_kernel.set_name("label_active_kernel")
+ label_active_installed_kernel.set_selectable(True)
+
+ label_active_installed_kernel.set_markup(
+ "Active kernel: %s" % self.manager_gui.active_kernel
+ )
+ label_active_installed_kernel.set_halign(Gtk.Align.START)
+ self.manager_gui.vbox_active_installed_kernel.append(
+ label_active_installed_kernel
+ )
+
+ scrolled_window_installed = Gtk.ScrolledWindow()
+ scrolled_window_installed.set_name("scrolled_window_installed")
+ scrolled_window_installed.set_policy(
+ Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC
+ )
+ scrolled_window_installed.set_propagate_natural_height(True)
+ scrolled_window_installed.set_propagate_natural_width(True)
+
+ label_installed_count.set_markup(
+ "%s Installed kernels" % len(self.manager_gui.installed_kernels)
+ )
+
+ self.flowbox_installed = FlowBoxInstalled(
+ installed_kernels=self.manager_gui.installed_kernels,
+ manager_gui=self.manager_gui,
+ )
+ vbox_installed_flowbox = Gtk.Box(
+ orientation=Gtk.Orientation.VERTICAL, spacing=12
+ )
+
+ # vbox_installed_flowbox.set_halign(align=Gtk.Align.FILL)
+
+ vbox_installed_flowbox.append(self.flowbox_installed)
+
+ scrolled_window_installed.set_child(vbox_installed_flowbox)
+
+ # self.manager_gui.vbox_installed_kernels.append(label_installed_title)
+ self.manager_gui.vbox_installed_kernels.append(vbox_header)
+ self.manager_gui.vbox_installed_kernels.append(label_installed_desc)
+ self.manager_gui.vbox_installed_kernels.append(label_installed_count)
+ self.manager_gui.vbox_installed_kernels.append(vbox_search_entry)
+ self.manager_gui.vbox_installed_kernels.append(scrolled_window_installed)
+ self.manager_gui.vbox_installed_kernels.append(
+ self.manager_gui.vbox_active_installed_kernel
+ )
+
+ self.manager_gui.stack.add_titled(
+ self.manager_gui.vbox_installed_kernels, "Installed", "Installed"
+ )
+
+ def add_official_kernels_to_stack(self, reload):
+ if reload is True:
+ self.flowbox_stacks.clear()
+ for kernel in fn.supported_kernels_dict:
+ vbox_flowbox = None
+ stack_child = self.manager_gui.stack.get_child_by_name(kernel)
+
+ if stack_child is not None:
+ for stack_widget in stack_child:
+ if stack_widget.get_name() == "scrolled_window_official":
+ scrolled_window_official = stack_widget
+ vbox_flowbox = (
+ scrolled_window_official.get_child().get_child()
+ )
+
+ for widget in vbox_flowbox:
+ widget.remove_all()
+
+ self.flowbox_official_kernel = FlowBox(
+ kernel,
+ self.manager_gui.active_kernel,
+ self.manager_gui,
+ "official",
+ )
+ self.flowbox_stacks.append(self.flowbox_official_kernel)
+
+ vbox_flowbox.append(self.flowbox_official_kernel)
+
+ # while self.manager_gui.default_context.pending():
+ # self.manager_gui.default_context.iteration(True)
+ else:
+ for kernel in fn.supported_kernels_dict:
+ self.manager_gui.vbox_kernels = Gtk.Box(
+ orientation=Gtk.Orientation.VERTICAL, spacing=5
+ )
+
+ self.manager_gui.vbox_kernels.set_name("stack_%s" % kernel)
+
+ hbox_sep_kernels = Gtk.Box(
+ orientation=Gtk.Orientation.VERTICAL, spacing=10
+ )
+
+ hsep_kernels = Gtk.Separator(orientation=Gtk.Orientation.VERTICAL)
+
+ vbox_active_kernel = Gtk.Box(
+ orientation=Gtk.Orientation.HORIZONTAL, spacing=5
+ )
+ vbox_active_kernel.set_name("vbox_active_kernel")
+
+ label_active_kernel = Gtk.Label(xalign=0.5, yalign=0.5)
+ label_active_kernel.set_name("label_active_kernel")
+ label_active_kernel.set_selectable(True)
+ label_active_kernel.set_markup(
+ "Active kernel: %s" % self.manager_gui.active_kernel
+ )
+ label_active_kernel.set_halign(Gtk.Align.START)
+
+ label_bottom_padding = Gtk.Label(xalign=0, yalign=0)
+ label_bottom_padding.set_text(" ")
+
+ hbox_sep_kernels.append(hsep_kernels)
+
+ self.flowbox_official_kernel = FlowBox(
+ kernel, self.manager_gui.active_kernel, self.manager_gui, "official"
+ )
+
+ self.flowbox_stacks.append(self.flowbox_official_kernel)
+
+ vbox_flowbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
+ vbox_flowbox.set_name("vbox_flowbox_%s" % kernel)
+ # vbox_flowbox.set_halign(align=Gtk.Align.FILL)
+ vbox_flowbox.append(self.flowbox_official_kernel)
+
+ scrolled_window_official = Gtk.ScrolledWindow()
+ scrolled_window_official.set_name("scrolled_window_official")
+ scrolled_window_official.set_policy(
+ Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC
+ )
+ scrolled_window_official.set_propagate_natural_height(True)
+ scrolled_window_official.set_propagate_natural_width(True)
+
+ label_title = Gtk.Label(xalign=0.5, yalign=0.5)
+ label_title.set_text(kernel.upper())
+ label_title.set_name("label_stack_kernel")
+
+ label_desc = Gtk.Label(xalign=0, yalign=0)
+ label_desc.set_text(fn.supported_kernels_dict[kernel][0])
+ label_desc.set_name("label_stack_desc")
+
+ label_count = Gtk.Label(xalign=0, yalign=0)
+ label_count.set_markup(
+ "%s Available kernels"
+ % self.flowbox_official_kernel.kernel_count
+ )
+
+ label_count.set_name("label_stack_count")
+
+ vbox_search_entry = Gtk.Box(
+ orientation=Gtk.Orientation.VERTICAL, spacing=5
+ )
+
+ search_entry_official = Gtk.SearchEntry()
+ search_entry_official.set_name(kernel)
+ search_entry_official.set_placeholder_text(
+ "Search %s kernels..." % kernel
+ )
+ search_entry_official.connect(
+ "search_changed", self.flowbox_filter_official
+ )
+
+ self.search_entries.append(search_entry_official)
+
+ vbox_search_entry.append(search_entry_official)
+
+ vbox_active_kernel.append(label_active_kernel)
+
+ vbox_header = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5)
+ vbox_header.set_name("vbox_header")
+
+ lbl_heading = Gtk.Label(xalign=0.5, yalign=0.5)
+ lbl_heading.set_name("label_flowbox_message")
+ lbl_heading.set_text(
+ "%s - Verified and official kernels" % kernel.upper()
+ )
+
+ lbl_padding = Gtk.Label(xalign=0.0, yalign=0.0)
+ lbl_padding.set_text(" ")
+
+ grid_banner_img = Gtk.Grid()
+
+ image_settings = Gtk.Image.new_from_file(
+ os.path.join(base_dir, "images/48x48/akm-verified.png")
+ )
+
+ image_settings.set_icon_size(Gtk.IconSize.LARGE)
+ image_settings.set_halign(Gtk.Align.START)
+
+ grid_banner_img.attach(image_settings, 0, 1, 1, 1)
+ grid_banner_img.attach_next_to(
+ lbl_padding,
+ image_settings,
+ Gtk.PositionType.RIGHT,
+ 1,
+ 1,
+ )
+
+ grid_banner_img.attach_next_to(
+ lbl_heading,
+ lbl_padding,
+ Gtk.PositionType.RIGHT,
+ 1,
+ 1,
+ )
+
+ vbox_header.append(grid_banner_img)
+
+ # vbox_kernels.append(label_title)
+ self.manager_gui.vbox_kernels.append(vbox_header)
+ # self.manager_gui.vbox_kernels.append(label_title)
+ self.manager_gui.vbox_kernels.append(label_desc)
+ self.manager_gui.vbox_kernels.append(label_count)
+ self.manager_gui.vbox_kernels.append(vbox_search_entry)
+ self.manager_gui.vbox_kernels.append(hbox_sep_kernels)
+
+ scrolled_window_official.set_child(vbox_flowbox)
+ self.manager_gui.vbox_kernels.append(scrolled_window_official)
+ self.manager_gui.vbox_kernels.append(vbox_active_kernel)
+
+ kernel_sidebar_title = None
+
+ if kernel == "linux":
+ kernel_sidebar_title = "Linux"
+ elif kernel == "linux-lts":
+ kernel_sidebar_title = "Linux-LTS"
+ elif kernel == "linux-zen":
+ kernel_sidebar_title = "Linux-ZEN"
+ elif kernel == "linux-hardened":
+ kernel_sidebar_title = "Linux-Hardened"
+ elif kernel == "linux-rt":
+ kernel_sidebar_title = "Linux-RT"
+ elif kernel == "linux-rt-lts":
+ kernel_sidebar_title = "Linux-RT-LTS"
+
+ self.manager_gui.stack.add_titled(
+ self.manager_gui.vbox_kernels, kernel, kernel_sidebar_title
+ )
+
+ def flowbox_filter_official(self, search_entry):
+ def filter_func(fb_child, text):
+ if search_entry.get_name() == fb_child.get_name().split(" ")[0]:
+ if text in fb_child.get_name():
+ return True
+ else:
+ return False
+ else:
+ return True
+
+ text = search_entry.get_text()
+
+ for flowbox in self.flowbox_stacks:
+ flowbox.set_filter_func(filter_func, text)
+
+ def flowbox_filter_community(self, search_entry):
+ def filter_func(fb_child, text):
+ if search_entry.get_name() == "search_entry_community":
+ if text in fb_child.get_name():
+ return True
+ else:
+ return False
+ else:
+ return True
+
+ text = search_entry.get_text()
+
+ self.flowbox_community.set_filter_func(filter_func, text)
+
+ def flowbox_filter_installed(self, search_entry):
+ def filter_func(fb_child, text):
+ if search_entry.get_name() == "search_entry_installed":
+ if text in fb_child.get_name():
+ return True
+ else:
+ return False
+ else:
+ return True
+
+ text = search_entry.get_text()
+
+ self.flowbox_installed.set_filter_func(filter_func, text)
+
+ def add_community_kernels_to_stack(self, reload):
+ vbox_active_kernel = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5)
+ vbox_active_kernel.set_name("vbox_active_kernel")
+ vbox_kernels = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5)
+ hbox_sep_kernels = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10)
+ hsep_kernels = Gtk.Separator(orientation=Gtk.Orientation.VERTICAL)
+ hbox_sep_kernels.append(hsep_kernels)
+
+ label_active_kernel = Gtk.Label(xalign=0.5, yalign=0.5)
+ label_active_kernel.set_name("label_active_kernel")
+ label_active_kernel.set_selectable(True)
+ label_active_kernel.set_markup(
+ "Active kernel: %s" % self.manager_gui.active_kernel
+ )
+ label_active_kernel.set_halign(Gtk.Align.START)
+
+ label_count = Gtk.Label(xalign=0, yalign=0)
+ label_count.set_name("label_stack_count")
+
+ vbox_search_entry = None
+
+ search_entry_community = Gtk.SearchEntry()
+ search_entry_community.set_name("search_entry_community")
+ search_entry_community.set_placeholder_text(
+ "Search %s kernels..." % "community based"
+ )
+ search_entry_community.connect("search_changed", self.flowbox_filter_community)
+
+ hbox_warning_message = Gtk.Box(
+ orientation=Gtk.Orientation.HORIZONTAL, spacing=5
+ )
+ hbox_warning_message.set_name("hbox_warning_message")
+
+ label_pacman_warning = Gtk.Label(xalign=0, yalign=0)
+ label_pacman_warning.set_name("label_community_warning")
+
+ image_warning = Gtk.Image.new_from_file(
+ os.path.join(base_dir, "images/48x48/akm-warning.png")
+ )
+ image_warning.set_name("image_warning")
+
+ image_warning.set_icon_size(Gtk.IconSize.LARGE)
+ image_warning.set_halign(Gtk.Align.CENTER)
+
+ hbox_warning = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5)
+ hbox_warning.set_name("hbox_warning")
+
+ hbox_warning.append(image_warning)
+ # hbox_warning.append(label_pacman_warning)
+
+ label_warning = Gtk.Label(xalign=0, yalign=0)
+ label_warning.set_name("label_community_warning")
+
+ if (
+ self.manager_gui.community_kernels is not None
+ and len(self.manager_gui.community_kernels) == 0
+ ):
+ label_warning.set_markup(
+ f"Cannot find any supported unofficial pacman repository's\n"
+ f"Add the Chaotic-AUR pacman repository to access Community based kernels"
+ )
+ else:
+ label_warning.set_markup(
+ f"These kernels are user produced content\n"
+ f"These kernels may not work on your hardware\n"
+ f"Any use of the provided files is at your own risk"
+ )
+
+ hbox_warning.append(label_warning)
+
+ if reload is True:
+ vbox_flowbox = None
+ stack_child = self.manager_gui.stack.get_child_by_name("Community Kernels")
+
+ if stack_child is not None:
+ for stack_widget in stack_child:
+ if stack_widget.get_name() == "hbox_warning":
+ for w in stack_widget:
+ if w.get_name() == "label_community_warning":
+ if len(self.manager_gui.community_kernels) == 0:
+ w.set_markup(
+ f"Cannot find any supported unofficial pacman repository's\n"
+ f"Add the Chaotic-AUR pacman repository to access Community based kernels"
+ )
+ else:
+ w.set_markup(
+ f"These kernels are user produced content\n"
+ f"These kernels may not work on your hardware\n"
+ f"Any use of the provided files is at your own risk"
+ )
+ break
+ if stack_widget.get_name() == "label_stack_count":
+ stack_widget.set_markup(
+ "%s Available kernels"
+ % len(self.manager_gui.community_kernels)
+ )
+ if stack_widget.get_name() == "vbox_search_entry":
+ if len(self.manager_gui.community_kernels) == 0:
+ for search_entry in stack_widget:
+ search_entry.set_visible(False)
+ else:
+ for search_entry in stack_widget:
+ search_entry.set_visible(True)
+
+ if stack_widget.get_name() == "scrolled_window_community":
+ scrolled_window_community = stack_widget
+ vbox_flowbox = scrolled_window_community.get_child().get_child()
+
+ for widget in vbox_flowbox:
+ widget.remove_all()
+
+ # scrolled_window_community.hide()
+
+ # vbox_search_entry = Gtk.Box(
+ # orientation=Gtk.Orientation.VERTICAL, spacing=5
+ # )
+ #
+ # vbox_search_entry.append(search_entry_community)
+ # widget.append(vbox_search_entry)
+
+ if (
+ self.manager_gui.community_kernels is not None
+ and len(self.manager_gui.community_kernels) > 0
+ ):
+ self.flowbox_community = FlowBox(
+ self.manager_gui.community_kernels,
+ self.manager_gui.active_kernel,
+ self.manager_gui,
+ "community",
+ )
+ vbox_flowbox.append(self.flowbox_community)
+
+ # while self.manager_gui.default_context.pending():
+ # # fn.time.sleep(0.1)
+ # self.manager_gui.default_context.iteration(True)
+ else:
+ self.flowbox_community = None
+
+ vbox_flowbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
+ # vbox_flowbox.set_halign(align=Gtk.Align.FILL)
+
+ if len(self.manager_gui.community_kernels) == 0:
+ label_count.set_markup("%s Available kernels" % 0)
+ else:
+ self.flowbox_community = FlowBox(
+ self.manager_gui.community_kernels,
+ self.manager_gui.active_kernel,
+ self.manager_gui,
+ "community",
+ )
+
+ vbox_flowbox.append(self.flowbox_community)
+
+ label_count.set_markup(
+ "%s Available kernels" % self.flowbox_community.kernel_count
+ )
+
+ vbox_search_entry = Gtk.Box(
+ orientation=Gtk.Orientation.VERTICAL, spacing=5
+ )
+
+ vbox_search_entry.set_name("vbox_search_entry")
+
+ vbox_search_entry.append(search_entry_community)
+
+ if reload is False:
+ scrolled_window_community = Gtk.ScrolledWindow()
+ scrolled_window_community.set_name("scrolled_window_community")
+ scrolled_window_community.set_policy(
+ Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC
+ )
+ scrolled_window_community.set_propagate_natural_height(True)
+ scrolled_window_community.set_propagate_natural_width(True)
+
+ label_title = Gtk.Label(xalign=0.5, yalign=0.5)
+ label_title.set_text("Community Kernels")
+ label_title.set_name("label_stack_kernel")
+
+ label_desc = Gtk.Label(xalign=0, yalign=0)
+ label_desc.set_text("Community Linux kernel and modules")
+ label_desc.set_name("label_stack_desc")
+
+ vbox_active_kernel.append(label_active_kernel)
+
+ vbox_header = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5)
+ vbox_header.set_name("vbox_header")
+
+ lbl_heading = Gtk.Label(xalign=0.5, yalign=0.5)
+ lbl_heading.set_name("label_flowbox_message")
+
+ lbl_heading.set_text(
+ "%s - Unofficial kernels" % "Community based".upper()
+ )
+
+ lbl_padding = Gtk.Label(xalign=0.0, yalign=0.0)
+ lbl_padding.set_text(" ")
+
+ grid_banner_img = Gtk.Grid()
+
+ image_settings = Gtk.Image.new_from_file(
+ os.path.join(base_dir, "images/48x48/akm-community.png")
+ )
+
+ image_settings.set_icon_size(Gtk.IconSize.LARGE)
+ image_settings.set_halign(Gtk.Align.START)
+
+ grid_banner_img.attach(image_settings, 0, 1, 1, 1)
+ grid_banner_img.attach_next_to(
+ lbl_padding,
+ image_settings,
+ Gtk.PositionType.RIGHT,
+ 1,
+ 1,
+ )
+
+ grid_banner_img.attach_next_to(
+ lbl_heading,
+ lbl_padding,
+ Gtk.PositionType.RIGHT,
+ 1,
+ 1,
+ )
+
+ vbox_header.append(grid_banner_img)
+
+ vbox_kernels.append(vbox_header)
+ vbox_kernels.append(label_desc)
+ vbox_kernels.append(hbox_warning)
+ vbox_kernels.append(label_count)
+
+ if vbox_search_entry is not None:
+ vbox_kernels.append(vbox_search_entry)
+
+ vbox_kernels.append(hbox_sep_kernels)
+
+ scrolled_window_community.set_child(vbox_flowbox)
+
+ vbox_kernels.append(scrolled_window_community)
+ vbox_kernels.append(vbox_active_kernel)
+
+ self.manager_gui.stack.add_titled(
+ vbox_kernels, "Community Kernels", "Community"
+ )
diff --git a/ui/ManagerGUI.py b/ui/ManagerGUI.py
new file mode 100644
index 0000000..a4e008e
--- /dev/null
+++ b/ui/ManagerGUI.py
@@ -0,0 +1,573 @@
+import gi
+import os
+from ui.MenuButton import MenuButton
+from ui.Stack import Stack
+from ui.KernelStack import KernelStack
+from ui.FlowBox import FlowBox, FlowBoxInstalled
+from ui.AboutDialog import AboutDialog
+from ui.SplashScreen import SplashScreen
+from ui.MessageWindow import MessageWindow
+from ui.SettingsWindow import SettingsWindow
+import libs.functions as fn
+
+gi.require_version("Gtk", "4.0")
+from gi.repository import Gtk, Gio, Gdk, GLib
+
+
+base_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
+
+
+class ManagerGUI(Gtk.ApplicationWindow):
+ def __init__(self, app_name, default_context, app_version, **kwargs):
+ super().__init__(**kwargs)
+
+ self.default_context = default_context
+
+ self.app_version = app_version
+
+ if self.app_version == "${app_version}":
+ self.app_version = "dev"
+
+ self.set_title(app_name)
+ self.set_resizable(True)
+ self.set_default_size(950, 650)
+
+ # get list of kernels from the arch archive website, aur, then cache
+ self.official_kernels = []
+ self.community_kernels = []
+
+ # splashscreen queue for threading
+ self.queue_load_progress = fn.Queue()
+
+ # official kernels queue for threading
+ self.queue_kernels = fn.Queue()
+
+ # community kernels queue for threading
+ self.queue_community_kernels = fn.Queue()
+
+ self.splash_screen = SplashScreen(app_name)
+
+ try:
+ fn.Thread(
+ target=self.wait_for_gui_load,
+ daemon=True,
+ ).start()
+ except Exception as e:
+ fn.logger.error(e)
+
+ while self.default_context.pending():
+ fn.time.sleep(0.1)
+ self.default_context.iteration(True)
+
+ hbox_notify_revealer = Gtk.Box(
+ orientation=Gtk.Orientation.HORIZONTAL, spacing=20
+ )
+ hbox_notify_revealer.set_name("hbox_notify_revealer")
+ hbox_notify_revealer.set_halign(Gtk.Align.CENTER)
+
+ self.notify_revealer = Gtk.Revealer()
+ self.notify_revealer.set_reveal_child(False)
+ self.label_notify_revealer = Gtk.Label(xalign=0, yalign=0)
+ self.label_notify_revealer.set_name("label_notify_revealer")
+
+ self.notify_revealer.set_child(hbox_notify_revealer)
+
+ hbox_notify_revealer.append(self.label_notify_revealer)
+
+ # while self.default_context.pending():
+ # fn.time.sleep(0.1)
+ # self.default_context.iteration(True)
+
+ self.bootloader = None
+ self.bootloader_grub_cfg = None
+
+ # self.bootloader = fn.get_boot_loader()
+
+ config_data = fn.setup_config(self)
+
+ fn.logger.info("Version = %s" % self.app_version)
+ fn.logger.info("Distro = %s" % fn.distro.id())
+
+ if "bootloader" in config_data.keys():
+ if config_data["bootloader"]["name"] is not None:
+ self.bootloader = config_data["bootloader"]["name"].lower()
+
+ if self.bootloader == "grub":
+ if config_data["bootloader"]["grub_config"] is not None:
+ self.bootloader_grub_cfg = config_data["bootloader"][
+ "grub_config"
+ ]
+ elif self.bootloader != "systemd-boot" or self.bootloader != "grub":
+ fn.logger.warning(
+ "Invalid bootloader config found it should only be systemd-boot or grub"
+ )
+
+ fn.logger.warning("Using bootctl to determine current bootloader")
+ self.bootloader = None
+
+ if self.bootloader is not None or self.bootloader_grub_cfg is not None:
+ fn.logger.info("User provided bootloader options read from config file")
+ fn.logger.info("User bootloader option = %s " % self.bootloader)
+ if self.bootloader_grub_cfg is not None:
+ fn.logger.info(
+ "User bootloader Grub config = %s " % self.bootloader_grub_cfg
+ )
+ else:
+ # no config setting found for bootloader use default method
+ self.bootloader = fn.get_boot_loader()
+ if self.bootloader == "grub":
+ self.bootloader_grub_cfg = "/boot/grub/grub.cfg"
+
+ if self.bootloader is not None:
+ fn.create_cache_dir()
+ fn.create_log_dir()
+ fn.get_pacman_repos()
+
+ self.stack = Stack(transition_type="OVER_DOWN")
+ self.kernel_stack = KernelStack(self)
+
+ header_bar = Gtk.HeaderBar()
+
+ label_title = Gtk.Label(xalign=0.5, yalign=0.5)
+ label_title.set_markup("%s" % app_name)
+
+ header_bar.set_title_widget(label_title)
+ header_bar.set_show_title_buttons(True)
+
+ self.set_titlebar(header_bar)
+
+ menu_outerbox = Gtk.Box(spacing=6, orientation=Gtk.Orientation.VERTICAL)
+ header_bar.pack_end(menu_outerbox)
+
+ menu_outerbox.show()
+
+ menubutton = MenuButton()
+
+ menu_outerbox.append(menubutton)
+
+ menubutton.show()
+
+ action_about = Gio.SimpleAction(name="about")
+ action_about.connect("activate", self.on_about)
+
+ action_settings = Gio.SimpleAction(name="settings")
+ action_settings.connect("activate", self.on_settings, fn)
+
+ self.add_action(action_settings)
+
+ self.add_action(action_about)
+
+ action_refresh = Gio.SimpleAction(name="refresh")
+ action_refresh.connect("activate", self.on_refresh)
+
+ self.add_action(action_refresh)
+
+ action_quit = Gio.SimpleAction(name="quit")
+ action_quit.connect("activate", self.on_quit)
+
+ self.add_action(action_quit)
+
+ # add shortcut keys
+
+ event_controller_key = Gtk.EventControllerKey.new()
+ event_controller_key.connect("key-pressed", self.key_pressed)
+
+ self.add_controller(event_controller_key)
+
+ # overlay = Gtk.Overlay()
+ # self.set_child(child=overlay)
+
+ self.vbox = Gtk.Box.new(orientation=Gtk.Orientation.VERTICAL, spacing=10)
+ self.vbox.set_name("main")
+
+ self.set_child(child=self.vbox)
+
+ self.vbox.append(self.notify_revealer)
+
+ self.installed_kernels = fn.get_installed_kernels()
+ if self.installed_kernels is not None:
+ fn.logger.info("Installed kernels = %s" % len(self.installed_kernels))
+
+ self.active_kernel = fn.get_active_kernel()
+
+ self.refresh_cache = False
+
+ self.refresh_cache = fn.get_latest_kernel_updates(self)
+
+ self.start_get_kernels_threads()
+
+ self.load_kernels_gui()
+
+ # validate bootloader
+ if self.bootloader_grub_cfg and not os.path.exists(
+ self.bootloader_grub_cfg
+ ):
+ mw = MessageWindow(
+ title="Grub config file not found",
+ message=f"The specified Grub config file: {self.bootloader_grub_cfg} does not exist\n"
+ f"This will cause an issue when updating the bootloader\n"
+ f"Update the configuration file/use the Advanced Settings to change this\n",
+ detailed_message=False,
+ transient_for=self,
+ )
+
+ mw.present()
+ if self.bootloader == "systemd-boot":
+ if not os.path.exists(
+ "/sys/firmware/efi/fw_platform_size"
+ ) or not os.path.exists("/sys/firmware/efi/efivars"):
+ mw = MessageWindow(
+ title="Legacy boot detected",
+ message=f"Cannot select systemd-boot, UEFI boot mode is not available\n"
+ f"Update the configuration file\n"
+ f"Or use the Advanced Settings to change this\n",
+ detailed_message=False,
+ transient_for=self,
+ )
+
+ mw.present()
+
+ else:
+ fn.logger.error("Failed to set bootloader, application closing")
+ fn.sys.exit(1)
+
+ def key_pressed(self, keyval, keycode, state, userdata):
+ shortcut = Gtk.accelerator_get_label(
+ keycode, keyval.get_current_event().get_modifier_state()
+ )
+
+ # quit application
+ if shortcut in ("Ctrl+Q", "Ctrl+Mod2+Q"):
+ self.destroy()
+
+ def open_settings(self, fn):
+ settings_win = SettingsWindow(fn, self)
+ settings_win.present()
+
+ def timeout(self):
+ self.hide_notify()
+
+ def hide_notify(self):
+ self.notify_revealer.set_reveal_child(False)
+ if self.timeout_id is not None:
+ GLib.source_remove(self.timeout_id)
+ self.timeout_id = None
+
+ def reveal_notify(self):
+ # reveal = self.notify_revealer.get_reveal_child()
+ self.notify_revealer.set_reveal_child(True)
+ self.timeout_id = GLib.timeout_add(3000, self.timeout)
+
+ def start_get_kernels_threads(self):
+ if self.refresh_cache is False:
+ fn.logger.info("Starting get official Linux kernels thread")
+ try:
+ fn.Thread(
+ name=fn.thread_get_kernels,
+ target=fn.get_official_kernels,
+ daemon=True,
+ args=(self,),
+ ).start()
+
+ except Exception as e:
+ fn.logger.error("Exception in thread fn.get_official_kernels(): %s" % e)
+ finally:
+ self.official_kernels = self.queue_kernels.get()
+ self.queue_kernels.task_done()
+
+ else:
+ self.official_kernels = self.queue_kernels.get()
+ self.queue_kernels.task_done()
+
+ # =====================================================
+ # PACMAN DB SYNC
+ # =====================================================
+
+ def pacman_db_sync(self):
+ sync_err = fn.sync_package_db()
+
+ if sync_err is not None:
+ # fn.logger.error("Pacman db synchronization failed")
+
+ GLib.idle_add(
+ self.show_sync_window,
+ sync_err,
+ priority=GLib.PRIORITY_DEFAULT,
+ )
+
+ return False
+
+ else:
+ fn.logger.info("Pacman DB synchronization completed")
+ return True
+
+ def show_sync_window(self, sync_err):
+ mw = MessageWindow(
+ title="Error - Pacman db synchronization",
+ message=f"Pacman db synchronization failed\n"
+ f"Failed to run 'pacman -Syu'\n"
+ f"{sync_err}\n",
+ transient_for=self,
+ detailed_message=True,
+ )
+
+ mw.present()
+
+ # keep splash screen open, until main gui is loaded
+ def wait_for_gui_load(self):
+ while True:
+ if fn.logger.getEffectiveLevel() == 10:
+ fn.logger.debug("Waiting for GUI to load ..")
+
+ # fn.time.sleep(0.2)
+ status = self.queue_load_progress.get()
+ try:
+
+ if fn.logger.getEffectiveLevel() == 10:
+ fn.logger.debug("Status = %s" % status)
+
+ if status == 1:
+ if fn.logger.getEffectiveLevel() == 10:
+ fn.logger.debug("Destroying splashscreen")
+
+ GLib.idle_add(
+ self.splash_screen.destroy,
+ priority=GLib.PRIORITY_DEFAULT,
+ )
+ break
+ except Exception as e:
+ fn.logger.error("Exception in wait_for_gui_load(): %s" % e)
+ finally:
+ self.queue_load_progress.task_done()
+
+ def on_settings(self, action, param, fn):
+ self.open_settings(fn)
+
+ def on_about(self, action, param):
+ about_dialog = AboutDialog(self)
+ about_dialog.present()
+
+ def on_refresh(self, action, param):
+ if not fn.is_thread_alive(fn.thread_refresh_ui):
+ fn.Thread(
+ name=fn.thread_refresh_ui,
+ target=self.refresh_ui,
+ daemon=True,
+ ).start()
+
+ def refresh_ui(self):
+ if fn.logger.getEffectiveLevel() == 10:
+ fn.logger.debug("Refreshing UI")
+
+ self.label_notify_revealer.set_text("Refreshing UI started")
+ GLib.idle_add(
+ self.reveal_notify,
+ priority=GLib.PRIORITY_DEFAULT,
+ )
+ fn.pacman_repos_list = []
+ fn.get_pacman_repos()
+
+ fn.cached_kernels_list = []
+ fn.community_kernels_list = []
+
+ self.official_kernels = None
+
+ self.community_kernels = None
+
+ self.installed_kernels = None
+
+ self.start_get_kernels_threads()
+
+ if self.pacman_db_sync() is False:
+ fn.logger.error("Pacman DB synchronization failed")
+ else:
+ if fn.logger.getEffectiveLevel() == 10:
+ fn.logger.debug("Adding community kernels to UI")
+
+ try:
+ thread_get_community_kernels = fn.Thread(
+ name=fn.thread_get_community_kernels,
+ target=fn.get_community_kernels,
+ daemon=True,
+ args=(self,),
+ )
+
+ thread_get_community_kernels.start()
+
+ except Exception as e:
+ fn.logger.error("Exception in thread_get_community_kernels: %s" % e)
+ finally:
+ self.community_kernels = self.queue_community_kernels.get()
+ self.queue_community_kernels.task_done()
+
+ self.installed_kernels = fn.get_installed_kernels()
+
+ self.label_notify_revealer.set_text("Refreshing official kernels")
+ GLib.idle_add(
+ self.reveal_notify,
+ priority=GLib.PRIORITY_DEFAULT,
+ )
+
+ GLib.idle_add(
+ self.kernel_stack.add_official_kernels_to_stack,
+ True,
+ priority=GLib.PRIORITY_DEFAULT,
+ )
+
+ self.label_notify_revealer.set_text("Refreshing community kernels")
+ GLib.idle_add(
+ self.reveal_notify,
+ priority=GLib.PRIORITY_DEFAULT,
+ )
+
+ GLib.idle_add(
+ self.kernel_stack.add_community_kernels_to_stack,
+ True,
+ priority=GLib.PRIORITY_DEFAULT,
+ )
+
+ self.label_notify_revealer.set_text("Refreshing installed kernels")
+ GLib.idle_add(
+ self.reveal_notify,
+ priority=GLib.PRIORITY_DEFAULT,
+ )
+
+ GLib.idle_add(
+ self.kernel_stack.add_installed_kernels_to_stack,
+ True,
+ priority=GLib.PRIORITY_DEFAULT,
+ )
+
+ while self.default_context.pending():
+ if fn.logger.getEffectiveLevel() == 10:
+ fn.logger.debug("Waiting for UI loop")
+ fn.time.sleep(0.3)
+ self.default_context.iteration(False)
+
+ # fn.time.sleep(0.5)
+ if fn.logger.getEffectiveLevel() == 10:
+ fn.logger.debug("Refresh UI completed")
+
+ self.label_notify_revealer.set_text("Refreshing UI completed")
+ GLib.idle_add(
+ self.reveal_notify,
+ priority=GLib.PRIORITY_DEFAULT,
+ )
+
+ def on_quit(self, action, param):
+ self.destroy()
+ fn.logger.info("Application quit")
+
+ def on_button_quit_response(self, widget):
+ self.destroy()
+ fn.logger.info("Application quit")
+
+ def load_kernels_gui(self):
+ self.queue_load_progress.put(0)
+ hbox_sep = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10)
+ hsep = Gtk.Separator(orientation=Gtk.Orientation.VERTICAL)
+ hbox_sep.append(hsep)
+
+ # handle error here with message
+ if self.official_kernels is None:
+ fn.logger.error("Failed to retrieve kernel list")
+ self.queue_load_progress.put(1)
+
+ stack_sidebar = Gtk.StackSidebar()
+ stack_sidebar.set_name("stack_sidebar")
+ stack_sidebar.set_stack(self.stack)
+
+ hbox_stack_sidebar = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)
+ hbox_stack_sidebar.set_name("hbox_stack_sidebar")
+ hbox_stack_sidebar.append(stack_sidebar)
+ hbox_stack_sidebar.append(self.stack)
+
+ self.vbox.append(hbox_stack_sidebar)
+
+ button_quit = Gtk.Button.new_with_label("Quit")
+ # button_quit.set_size_request(100, 30)
+ button_quit.connect(
+ "clicked",
+ self.on_button_quit_response,
+ )
+
+ btn_context = button_quit.get_style_context()
+ btn_context.add_class("destructive-action")
+
+ grid_bottom_panel = Gtk.Grid()
+ grid_bottom_panel.set_halign(Gtk.Align.END)
+ grid_bottom_panel.set_row_homogeneous(True)
+
+ grid_bottom_panel.attach(button_quit, 0, 1, 1, 1)
+
+ self.vbox.append(grid_bottom_panel)
+
+ self.textbuffer = Gtk.TextBuffer()
+
+ self.textview = Gtk.TextView()
+ self.textview.set_property("editable", False)
+ self.textview.set_property("monospace", True)
+
+ self.textview.set_vexpand(True)
+ self.textview.set_hexpand(True)
+
+ self.textview.set_buffer(self.textbuffer)
+
+ fn.logger.info("Creating kernel UI")
+
+ # add official kernel flowbox
+ if fn.logger.getEffectiveLevel() == 10:
+ fn.logger.debug("Adding official kernels to UI")
+
+ self.queue_load_progress.put(0)
+
+ self.kernel_stack.add_official_kernels_to_stack(reload=False)
+
+ # fn.logger.debug("Adding community kernels to UI")
+ # self.kernel_stack.add_community_kernels_to_stack(reload=False)
+
+ fn.logger.info("Starting pacman db synchronization")
+
+ if self.pacman_db_sync() is False:
+ fn.logger.error("Pacman DB synchronization failed")
+
+ else:
+ if fn.logger.getEffectiveLevel() == 10:
+ fn.logger.debug("Adding community kernels to UI")
+
+ try:
+ thread_get_community_kernels = fn.Thread(
+ name=fn.thread_get_community_kernels,
+ target=fn.get_community_kernels,
+ daemon=True,
+ args=(self,),
+ )
+
+ thread_get_community_kernels.start()
+
+ except Exception as e:
+ fn.logger.error("Exception in thread_get_community_kernels: %s" % e)
+ finally:
+ self.community_kernels = self.queue_community_kernels.get()
+ self.queue_community_kernels.task_done()
+
+ self.kernel_stack.add_community_kernels_to_stack(reload=False)
+
+ if fn.logger.getEffectiveLevel() == 10:
+ fn.logger.debug("Sending signal to destroy splashscreen")
+
+ # signal to destroy splash screen
+ self.queue_load_progress.put(1)
+
+ if fn.logger.getEffectiveLevel() == 10:
+ fn.logger.debug("Adding installed kernels to UI")
+
+ self.kernel_stack.add_installed_kernels_to_stack(reload=False)
+
+ fn.logger.info("Application started")
+
+ # while self.default_context.pending():
+ # if fn.logger.getEffectiveLevel() == 10:
+ # fn.logger.debug("Waiting for UI loop")
+ # self.default_context.iteration(False)
+ # fn.time.sleep(0.3)
diff --git a/ui/MenuButton.py b/ui/MenuButton.py
new file mode 100644
index 0000000..e72732a
--- /dev/null
+++ b/ui/MenuButton.py
@@ -0,0 +1,45 @@
+import gi
+
+gi.require_version("Gtk", "4.0")
+from gi.repository import Gtk
+
+# Gtk.Builder xml for the application menu
+APP_MENU = """
+
+
+
+
+"""
+
+
+class MenuButton(Gtk.MenuButton):
+ """
+ Wrapper class for at Gtk.Menubutton with a menu defined
+ in a Gtk.Builder xml string
+ """
+
+ def __init__(self, icon_name="open-menu-symbolic"):
+ super(MenuButton, self).__init__()
+ builder = Gtk.Builder.new_from_string(APP_MENU, -1)
+ menu = builder.get_object("app-menu")
+ self.set_menu_model(menu)
+ self.set_icon_name(icon_name)
diff --git a/ui/MessageWindow.py b/ui/MessageWindow.py
new file mode 100644
index 0000000..9e3ea1d
--- /dev/null
+++ b/ui/MessageWindow.py
@@ -0,0 +1,87 @@
+import gi
+import os
+import libs.functions as fn
+
+gi.require_version("Gtk", "4.0")
+from gi.repository import Gtk, Gio, GLib
+
+base_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
+
+
+class MessageWindow(Gtk.Window):
+ def __init__(self, title, message, detailed_message, **kwargs):
+ super().__init__(**kwargs)
+
+ self.set_title(title=title)
+ self.set_modal(modal=True)
+ self.set_resizable(False)
+ icon_name = "akm-tux"
+ self.set_icon_name(icon_name)
+
+ header_bar = Gtk.HeaderBar()
+ header_bar.set_show_title_buttons(True)
+
+ hbox_title = Gtk.Box.new(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)
+
+ label_title = Gtk.Label(xalign=0.5, yalign=0.5)
+ label_title.set_markup("%s" % title)
+
+ hbox_title.append(label_title)
+ header_bar.set_title_widget(hbox_title)
+
+ self.set_titlebar(header_bar)
+
+ vbox_message = Gtk.Box.new(orientation=Gtk.Orientation.VERTICAL, spacing=10)
+ vbox_message.set_name("vbox_flowbox_message")
+
+ hbox_message = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5)
+
+ self.set_child(child=vbox_message)
+
+ if detailed_message is True:
+ scrolled_window = Gtk.ScrolledWindow()
+
+ textview = Gtk.TextView()
+ textview.set_property("editable", False)
+ textview.set_property("monospace", True)
+
+ textview.set_vexpand(True)
+ textview.set_hexpand(True)
+
+ msg_buffer = textview.get_buffer()
+ msg_buffer.insert(
+ msg_buffer.get_end_iter(),
+ "Event timestamp = %s\n"
+ % fn.datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
+ )
+ msg_buffer.insert(msg_buffer.get_end_iter(), "%s\n" % message)
+
+ scrolled_window.set_child(textview)
+
+ hbox_message.append(scrolled_window)
+
+ self.set_size_request(700, 500)
+ self.set_resizable(True)
+ else:
+ label_message = Gtk.Label(xalign=0.5, yalign=0.5)
+ label_message.set_markup("%s" % message)
+ label_message.set_name("label_flowbox_message")
+
+ hbox_message.append(label_message)
+
+ vbox_message.append(hbox_message)
+
+ button_ok = Gtk.Button.new_with_label("OK")
+ button_ok.set_size_request(100, 30)
+ button_ok.set_halign(Gtk.Align.END)
+ button_ok.connect("clicked", self.on_button_ok_clicked)
+
+ hbox_buttons = Gtk.Box.new(orientation=Gtk.Orientation.HORIZONTAL, spacing=50)
+ hbox_buttons.set_halign(Gtk.Align.END)
+ hbox_buttons.append(button_ok)
+
+ vbox_message.append(hbox_buttons)
+
+ def on_button_ok_clicked(self, button):
+ self.hide()
+ self.destroy()
diff --git a/ui/SettingsWindow.py b/ui/SettingsWindow.py
new file mode 100644
index 0000000..ed9b338
--- /dev/null
+++ b/ui/SettingsWindow.py
@@ -0,0 +1,703 @@
+import gi
+import os
+from ui.Stack import Stack
+from ui.MessageWindow import MessageWindow
+import libs.functions as fn
+from gi.repository import Gtk, Gio, GLib, GObject
+
+base_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
+
+
+class SettingsWindow(Gtk.Window):
+ def __init__(self, fn, manager_gui, **kwargs):
+ super().__init__(**kwargs)
+
+ self.set_title("Arch Linux Kernel Manager - Settings")
+ self.set_resizable(False)
+ self.set_size_request(600, 600)
+ stack = Stack(transition_type="CROSSFADE")
+
+ self.set_icon_name("akm-tux")
+ self.manager_gui = manager_gui
+ self.set_modal(True)
+ self.set_transient_for(self.manager_gui)
+
+ self.queue_kernels = self.manager_gui.queue_kernels
+
+ header_bar = Gtk.HeaderBar()
+
+ header_bar.set_show_title_buttons(True)
+
+ self.set_titlebar(header_bar)
+
+ hbox_main = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5)
+ hbox_main.set_name("box")
+ self.set_child(child=hbox_main)
+
+ vbox_header = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5)
+ vbox_header.set_name("vbox_header")
+
+ lbl_heading = Gtk.Label(xalign=0.5, yalign=0.5)
+ lbl_heading.set_name("label_flowbox_message")
+ lbl_heading.set_text("Preferences")
+
+ lbl_padding = Gtk.Label(xalign=0.0, yalign=0.0)
+ lbl_padding.set_text(" ")
+
+ grid_banner_img = Gtk.Grid()
+
+ image_settings = Gtk.Image.new_from_file(
+ os.path.join(base_dir, "images/48x48/akm-settings.png")
+ )
+
+ image_settings.set_icon_size(Gtk.IconSize.LARGE)
+ image_settings.set_halign(Gtk.Align.START)
+
+ grid_banner_img.attach(image_settings, 0, 1, 1, 1)
+ grid_banner_img.attach_next_to(
+ lbl_padding,
+ image_settings,
+ Gtk.PositionType.RIGHT,
+ 1,
+ 1,
+ )
+
+ grid_banner_img.attach_next_to(
+ lbl_heading,
+ lbl_padding,
+ Gtk.PositionType.RIGHT,
+ 1,
+ 1,
+ )
+
+ vbox_header.append(grid_banner_img)
+
+ hbox_main.append(vbox_header)
+
+ stack_switcher = Gtk.StackSwitcher()
+ stack_switcher.set_orientation(Gtk.Orientation.HORIZONTAL)
+ stack_switcher.set_stack(stack)
+
+ button_close = Gtk.Button(label="Close")
+ button_close.connect("clicked", self.on_close_clicked)
+
+ hbox_stack_sidebar = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10)
+ hbox_stack_sidebar.set_name("box")
+
+ hbox_stack_sidebar.append(stack_switcher)
+ hbox_stack_sidebar.append(stack)
+
+ hbox_main.append(hbox_stack_sidebar)
+
+ vbox_button = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)
+ vbox_button.set_halign(Gtk.Align.END)
+ vbox_button.set_name("box")
+
+ vbox_button.append(button_close)
+
+ hbox_stack_sidebar.append(vbox_button)
+
+ vbox_settings = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5)
+ vbox_settings.set_name("box")
+
+ label_official_kernels = Gtk.Label(xalign=0, yalign=0)
+ label_official_kernels.set_markup(
+ "Latest Official kernels (%s)" % len(fn.supported_kernels_dict)
+ )
+
+ label_community_kernels = Gtk.Label(xalign=0, yalign=0)
+ label_community_kernels.set_markup(
+ "Latest Community based kernels (%s)"
+ % len(self.manager_gui.community_kernels)
+ )
+
+ vbox_settings_listbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5)
+
+ self.listbox_official_kernels = Gtk.ListBox()
+ self.listbox_official_kernels.set_selection_mode(Gtk.SelectionMode.NONE)
+
+ self.label_loading_kernels = Gtk.Label(xalign=0, yalign=0)
+ self.label_loading_kernels.set_text("Loading ...")
+
+ self.listbox_official_kernels.append(self.label_loading_kernels)
+
+ listbox_community_kernels = Gtk.ListBox()
+ listbox_community_kernels.set_selection_mode(Gtk.SelectionMode.NONE)
+
+ scrolled_window_community = Gtk.ScrolledWindow()
+ scrolled_window_official = Gtk.ScrolledWindow()
+
+ scrolled_window_community.set_policy(
+ Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC
+ )
+
+ scrolled_window_official.set_policy(
+ Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC
+ )
+
+ scrolled_window_community.set_size_request(0, 150)
+ scrolled_window_official.set_size_request(0, 150)
+
+ scrolled_window_official.set_child(self.listbox_official_kernels)
+ vbox_community_warning = None
+
+ self.kernel_versions_queue = fn.Queue()
+ fn.Thread(
+ target=fn.get_latest_versions,
+ args=(self,),
+ daemon=True,
+ ).start()
+
+ fn.Thread(target=self.check_official_version_queue, daemon=True).start()
+
+ if len(self.manager_gui.community_kernels) > 0:
+ for community_kernel in self.manager_gui.community_kernels:
+ row_community_kernel = Gtk.ListBoxRow()
+ hbox_community_kernel = Gtk.Box(
+ orientation=Gtk.Orientation.VERTICAL, spacing=5
+ )
+ hbox_community_kernel.set_name("box_row")
+
+ hbox_row_official_kernel_row = Gtk.Box(
+ orientation=Gtk.Orientation.HORIZONTAL, spacing=10
+ )
+
+ label_community_kernel = Gtk.Label(xalign=0, yalign=0)
+ label_community_kernel.set_text("%s" % community_kernel.name)
+
+ label_community_kernel_version = Gtk.Label(xalign=0, yalign=0)
+ label_community_kernel_version.set_text("%s" % community_kernel.version)
+
+ hbox_row_official_kernel_row.append(label_community_kernel)
+ hbox_row_official_kernel_row.append(label_community_kernel_version)
+
+ hbox_community_kernel.append(hbox_row_official_kernel_row)
+
+ row_community_kernel.set_child(hbox_community_kernel)
+ listbox_community_kernels.append(row_community_kernel)
+ scrolled_window_community.set_child(listbox_community_kernels)
+ else:
+ vbox_community_warning = Gtk.Box(
+ orientation=Gtk.Orientation.VERTICAL, spacing=10
+ )
+ image_warning = Gtk.Image.new_from_file(
+ os.path.join(base_dir, "images/48x48/akm-warning.png")
+ )
+
+ image_warning.set_icon_size(Gtk.IconSize.LARGE)
+ image_warning.set_halign(Gtk.Align.START)
+
+ hbox_warning = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5)
+ hbox_warning.set_name("hbox_warning")
+
+ hbox_warning.append(image_warning)
+
+ label_pacman_no_community = Gtk.Label(xalign=0, yalign=0)
+ label_pacman_no_community.set_name("label_community_warning")
+ label_pacman_no_community.set_markup(
+ f"Cannot find any supported unofficial pacman repository's\n"
+ f"Add the Chaotic-AUR pacman repository to access Community based kernels"
+ )
+
+ hbox_warning.append(label_pacman_no_community)
+
+ vbox_community_warning.append(hbox_warning)
+
+ vbox_settings_listbox.append(label_official_kernels)
+ vbox_settings_listbox.append(scrolled_window_official)
+ vbox_settings_listbox.append(label_community_kernels)
+
+ if len(self.manager_gui.community_kernels) > 0:
+ vbox_settings_listbox.append(scrolled_window_community)
+ else:
+ vbox_settings_listbox.append(vbox_community_warning)
+
+ vbox_settings.append(vbox_settings_listbox)
+
+ vbox_settings_adv = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5)
+
+ vbox_settings_adv.set_name("box")
+
+ self.listbox_settings_adv = Gtk.ListBox()
+ self.listbox_settings_adv.set_selection_mode(Gtk.SelectionMode.NONE)
+
+ row_settings_adv = Gtk.ListBoxRow()
+ self.listbox_settings_adv.append(row_settings_adv)
+
+ hbox_bootloader_row = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5)
+ hbox_bootloader_row.set_name("box_row")
+ hbox_bootloader_row.set_halign(Gtk.Align.START)
+
+ self.hbox_bootloader_grub_row = Gtk.Box(
+ orientation=Gtk.Orientation.HORIZONTAL, spacing=5
+ )
+ self.hbox_bootloader_grub_row.set_name("box_row")
+ self.hbox_bootloader_grub_row.set_halign(Gtk.Align.START)
+
+ self.text_entry_bootloader_file = Gtk.Entry()
+
+ hbox_switch_row = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5)
+ hbox_switch_row.set_name("box_row")
+ hbox_switch_row.set_halign(Gtk.Align.START)
+
+ hbox_log_row = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5)
+ hbox_log_row.set_name("box_row")
+ hbox_log_row.set_halign(Gtk.Align.START)
+
+ label_bootloader = Gtk.Label(xalign=0, yalign=0)
+ label_bootloader.set_markup("Bootloader")
+
+ hbox_warning = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5)
+ hbox_warning.set_name("hbox_warning")
+
+ label_bootloader_warning = Gtk.Label(xalign=0, yalign=0)
+ label_bootloader_warning.set_markup(
+ f"Only change this setting if you know what you are doing\n"
+ f"The selected Grub/Systemd-boot bootloader entry will be updated\n"
+ f"This may break your system"
+ )
+
+ hbox_warning.append(label_bootloader_warning)
+
+ label_settings_bootloader_title = Gtk.Label(xalign=0.5, yalign=0.5)
+ label_settings_bootloader_title.set_markup("Current Bootloader")
+
+ self.label_settings_bootloader_file = Gtk.Label(xalign=0.5, yalign=0.5)
+ self.label_settings_bootloader_file.set_text("GRUB config file")
+
+ self.button_override_bootloader = Gtk.Button(
+ label="Override bootloader settings"
+ )
+ self.button_override_bootloader.connect("clicked", self.on_override_clicked)
+ self.hbox_bootloader_override_row = Gtk.Box(
+ orientation=Gtk.Orientation.HORIZONTAL, spacing=20
+ )
+ self.hbox_bootloader_override_row.set_name("box_row")
+ self.hbox_bootloader_override_row.append(self.button_override_bootloader)
+
+ boot_loaders = {0: "grub", 1: "systemd-boot"}
+
+ # Set up the factory
+ factory = Gtk.SignalListItemFactory()
+ factory.connect("setup", self._on_factory_setup)
+ factory.connect("bind", self._on_factory_bind)
+
+ self.model = Gio.ListStore(item_type=Bootloader)
+ for bootloader_id in boot_loaders.keys():
+ self.model.append(
+ Bootloader(
+ id=bootloader_id,
+ name=boot_loaders[bootloader_id],
+ )
+ )
+
+ self.dropdown_bootloader = Gtk.DropDown(
+ model=self.model, factory=factory, hexpand=True
+ )
+
+ self.dropdown_bootloader.set_sensitive(False)
+
+ self.selected_bootloader = None
+
+ self._bootloader_grub_config = "/boot/grub/grub.cfg"
+
+ row_settings_override_grub = Gtk.ListBoxRow()
+ row_settings_grub = Gtk.ListBoxRow()
+ self.listbox_settings_adv.append(row_settings_grub)
+
+ self.listbox_settings_adv.append(row_settings_override_grub)
+
+ self.text_entry_bootloader_file.connect("changed", self.on_entry_changed)
+ self.text_entry_bootloader_file.props.editable = False
+ text_entry_buffer_file = Gtk.EntryBuffer()
+
+ if self.manager_gui.bootloader_grub_cfg is not None:
+ text_entry_buffer_file.set_text(
+ self.manager_gui.bootloader_grub_cfg,
+ len(self.manager_gui.bootloader_grub_cfg),
+ )
+ else:
+ text_entry_buffer_file.set_text(
+ self._bootloader_grub_config,
+ len(self._bootloader_grub_config),
+ )
+
+ self.text_entry_bootloader_file.set_buffer(text_entry_buffer_file)
+ self.text_entry_bootloader_file.set_halign(Gtk.Align.END)
+ self.text_entry_bootloader_file.set_sensitive(False)
+
+ label_grub_file_path = Gtk.Label(xalign=0.5, yalign=0.5)
+ label_grub_file_path.set_markup("Grub file path")
+
+ self.hbox_bootloader_grub_row.append(label_grub_file_path)
+ self.hbox_bootloader_grub_row.append(self.text_entry_bootloader_file)
+
+ row_settings_grub.set_child(self.hbox_bootloader_grub_row)
+
+ if manager_gui.bootloader == "grub":
+ self.dropdown_bootloader.set_selected(0)
+ self.selected_bootloader = 0
+ self.hbox_bootloader_grub_row.set_visible(True)
+
+ row_settings_override_grub.set_child(self.hbox_bootloader_override_row)
+
+ if manager_gui.bootloader == "systemd-boot":
+
+ self.selected_bootloader = 1
+
+ self.dropdown_bootloader.set_selected(1)
+ row_settings_override_systemd = Gtk.ListBoxRow()
+ self.listbox_settings_adv.append(row_settings_override_systemd)
+ row_settings_override_systemd.set_child(self.hbox_bootloader_override_row)
+
+ self.hbox_bootloader_grub_row.set_visible(False)
+
+ self.dropdown_bootloader.connect(
+ "notify::selected-item", self._on_selected_item_notify
+ )
+
+ hbox_bootloader_row.append(label_settings_bootloader_title)
+ hbox_bootloader_row.append(self.dropdown_bootloader)
+
+ row_settings_adv.set_child(hbox_bootloader_row)
+
+ vbox_settings_adv.append(label_bootloader)
+ vbox_settings_adv.append(hbox_warning)
+ vbox_settings_adv.append(self.listbox_settings_adv)
+
+ listbox_settings_cache = Gtk.ListBox()
+ listbox_settings_cache.set_selection_mode(Gtk.SelectionMode.NONE)
+
+ row_settings_cache = Gtk.ListBoxRow()
+ listbox_settings_cache.append(row_settings_cache)
+
+ label_cache = Gtk.Label(xalign=0, yalign=0)
+ label_cache.set_markup("Refresh data from Arch Linux Archive")
+
+ label_cache_update = Gtk.Label(xalign=0.5, yalign=0.5)
+ label_cache_update.set_text("Update (this will take some time)")
+
+ self.label_cache_update_status = Gtk.Label(xalign=0.5, yalign=0.5)
+
+ switch_refresh_cache = Gtk.Switch()
+ switch_refresh_cache.connect("state-set", self.refresh_toggle)
+
+ label_cache_file = Gtk.Label(xalign=0, yalign=0)
+ label_cache_file.set_text(fn.cache_file)
+ label_cache_file.set_selectable(True)
+
+ self.label_cache_lastmodified = Gtk.Label(xalign=0, yalign=0)
+ self.label_cache_lastmodified.set_markup(
+ "Last modified date: %s" % fn.get_cache_last_modified()
+ )
+
+ hbox_switch_row.append(label_cache_update)
+ hbox_switch_row.append(switch_refresh_cache)
+ hbox_switch_row.append(self.label_cache_update_status)
+
+ row_settings_cache.set_child(hbox_switch_row)
+
+ label_logfile = Gtk.Label(xalign=0, yalign=0)
+ label_logfile.set_markup("Log file")
+
+ button_logfile = Gtk.Button(label="Open event log file")
+ button_logfile.connect("clicked", self.on_button_logfile_clicked)
+
+ label_logfile_location = Gtk.Label(xalign=0.5, yalign=0.5)
+ label_logfile_location.set_text(fn.event_log_file)
+ label_logfile_location.set_selectable(True)
+ hbox_log_row.append(button_logfile)
+ hbox_log_row.append(label_logfile_location)
+
+ listbox_settings_log = Gtk.ListBox()
+ listbox_settings_log.set_selection_mode(Gtk.SelectionMode.NONE)
+
+ row_settings_log = Gtk.ListBoxRow()
+ listbox_settings_log.append(row_settings_log)
+
+ row_settings_log.set_child(hbox_log_row)
+
+ vbox_settings_adv.append(label_cache)
+ vbox_settings_adv.append(self.label_cache_lastmodified)
+ vbox_settings_adv.append(label_cache_file)
+ vbox_settings_adv.append(listbox_settings_cache)
+ vbox_settings_adv.append(label_logfile)
+ vbox_settings_adv.append(listbox_settings_log)
+
+ stack.add_titled(vbox_settings_adv, "Advanced Settings", "Advanced")
+ stack.add_titled(vbox_settings, "Kernels", "Kernel versions")
+
+ def populate_official_kernels(self):
+ self.label_loading_kernels.hide()
+ for official_kernel in fn.supported_kernels_dict:
+ row_official_kernel = Gtk.ListBoxRow()
+ hbox_row_official = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5)
+
+ hbox_row_official_kernel_row = Gtk.Box(
+ orientation=Gtk.Orientation.HORIZONTAL, spacing=10
+ )
+
+ hbox_row_official.set_name("box_row")
+
+ label_kernel = Gtk.Label(xalign=0, yalign=0)
+ label_kernel.set_text("%s" % official_kernel)
+
+ label_kernel_version = Gtk.Label(xalign=0, yalign=0)
+ label_kernel_version.set_text("%s" % self.kernel_versions[official_kernel])
+
+ hbox_row_official_kernel_row.append(label_kernel)
+ hbox_row_official_kernel_row.append(label_kernel_version)
+
+ hbox_row_official.append(hbox_row_official_kernel_row)
+
+ row_official_kernel.set_child(hbox_row_official)
+
+ self.listbox_official_kernels.append(row_official_kernel)
+
+ def check_official_version_queue(self):
+ while True:
+ self.kernel_versions = self.kernel_versions_queue.get()
+
+ if self.kernel_versions is not None and len(self.kernel_versions) > 0:
+ break
+
+ self.kernel_versions_queue.task_done()
+
+ GLib.idle_add(self.populate_official_kernels, priority=GLib.PRIORITY_DEFAULT)
+
+ def on_entry_changed(self, entry):
+ if (
+ len(entry.get_text()) > 0
+ and entry.get_text() != self.manager_gui.bootloader_grub_cfg
+ ):
+ self.button_override_bootloader.get_child().set_text("Apply changes")
+
+ def _on_factory_setup(self, factory, list_item):
+ label = Gtk.Label()
+ list_item.set_child(label)
+
+ def _on_factory_bind(self, factory, list_item):
+ label = list_item.get_child()
+ bootloader = list_item.get_item()
+ label.set_text(bootloader.name)
+
+ def on_override_clicked(self, widget):
+ if self.button_override_bootloader.get_child().get_text() == "Apply changes":
+ # validate bootloader
+ if self.dropdown_bootloader.get_selected() == 1:
+ if not os.path.exists(
+ "/sys/firmware/efi/fw_platform_size"
+ ) or not os.path.exists("/sys/firmware/efi/efivars"):
+ mw = MessageWindow(
+ title="Legacy boot detected",
+ message="Cannot select systemd-boot, UEFI boot mode is not available",
+ transient_for=self,
+ detailed_message=False,
+ )
+
+ mw.present()
+ self.dropdown_bootloader.set_selected(0)
+ return
+
+ config_data = fn.read_config(self)
+
+ if config_data is not None:
+ # grub
+
+ if (
+ self.dropdown_bootloader.get_selected() == 0
+ and len(
+ self.text_entry_bootloader_file.get_buffer().get_text().strip()
+ )
+ > 0
+ ):
+ if fn.os.path.exists(
+ self.text_entry_bootloader_file.get_buffer().get_text().strip()
+ ):
+ if "bootloader" in config_data.keys():
+ config_data.remove("bootloader")
+
+ bootloader = fn.tomlkit.table(True)
+ bootloader.update({"name": "grub"})
+ bootloader.update(
+ {
+ "grub_config": self.text_entry_bootloader_file.get_buffer()
+ .get_text()
+ .strip()
+ }
+ )
+
+ config_data.append("bootloader", bootloader)
+
+ if fn.update_config(config_data, "grub") is True:
+ self.manager_gui.bootloader = "grub"
+ self.manager_gui.bootloader_grub_cfg = (
+ self.text_entry_bootloader_file.get_buffer()
+ .get_text()
+ .strip()
+ )
+ else:
+ mw = MessageWindow(
+ title="Grub config file",
+ message="The specified Grub config file %s does not exist"
+ % self.text_entry_bootloader_file.get_buffer()
+ .get_text()
+ .strip(),
+ transient_for=self,
+ detailed_message=False,
+ )
+
+ mw.present()
+ self.button_override_bootloader.get_child().set_text(
+ "Override bootloader settings"
+ )
+
+ elif (
+ self.dropdown_bootloader.get_selected() == 1
+ and self.selected_bootloader
+ != self.dropdown_bootloader.get_selected()
+ ):
+ if "bootloader" in config_data.keys():
+ config_data.remove("bootloader")
+
+ self.hbox_bootloader_grub_row.set_visible(True)
+
+ bootloader = fn.tomlkit.table(True)
+ bootloader.update({"name": "systemd-boot"})
+
+ config_data.append("bootloader", bootloader)
+
+ if fn.update_config(config_data, "systemd-boot") is True:
+ self.manager_gui.bootloader = "systemd-boot"
+
+ else:
+ self.dropdown_bootloader.set_sensitive(True)
+
+ if self.dropdown_bootloader.get_selected() == 0:
+ self.hbox_bootloader_grub_row.set_visible(True)
+ self.text_entry_bootloader_file.set_sensitive(True)
+ self.text_entry_bootloader_file.props.editable = True
+ elif self.dropdown_bootloader.get_selected() == 1:
+ self.hbox_bootloader_grub_row.set_visible(False)
+
+ def _on_selected_item_notify(self, dd, _):
+ if self.dropdown_bootloader.get_selected() != self.selected_bootloader:
+ self.button_override_bootloader.get_child().set_text("Apply changes")
+ else:
+ self.button_override_bootloader.get_child().set_text(
+ "Override bootloader settings"
+ )
+ if dd.get_selected() == 1:
+ if self.text_entry_bootloader_file is not None:
+ self.hbox_bootloader_grub_row.set_visible(False)
+ elif dd.get_selected() == 0:
+ if self.text_entry_bootloader_file is not None:
+ self.hbox_bootloader_grub_row.set_visible(True)
+ self.text_entry_bootloader_file.set_sensitive(True)
+ self.text_entry_bootloader_file.props.editable = True
+
+ def monitor_kernels_queue(self, switch):
+ while True:
+ if len(fn.fetched_kernels_dict) > 0:
+ self.manager_gui.official_kernels = self.queue_kernels.get()
+
+ self.queue_kernels.task_done()
+ self.refreshed = True
+ if self.manager_gui.official_kernels is not None:
+ switch.set_sensitive(False)
+
+ self.update_official_list()
+ self.update_timestamp()
+ self.label_cache_update_status.set_markup(
+ "Cache refresh completed"
+ )
+ else:
+ self.label_cache_update_status.set_markup(
+ "Cache refresh failed"
+ )
+ self.refreshed = False
+ self.update_timestamp()
+ break
+ else:
+ self.label_cache_update_status.set_markup(
+ "Cache refresh in progress"
+ )
+ # fn.time.sleep(0.3)
+
+ def refresh_toggle(self, switch, data):
+ if switch.get_active() is True:
+ # refresh cache
+ fn.logger.info("Refreshing cache file %s" % fn.cache_file)
+ switch.set_sensitive(False)
+
+ try:
+ th_refresh_cache = fn.Thread(
+ name=fn.thread_refresh_cache,
+ target=fn.refresh_cache,
+ args=(self,),
+ daemon=True,
+ )
+
+ th_refresh_cache.start()
+
+ # monitor queue
+ fn.Thread(
+ target=self.monitor_kernels_queue, daemon=True, args=(switch,)
+ ).start()
+
+ except Exception as e:
+ fn.logger.error("Exception in refresh_toggle(): %s" % e)
+ self.label_cache_update_status.set_markup("Cache refresh failed")
+
+ def update_timestamp(self):
+ if self.refreshed is True:
+ self.label_cache_lastmodified.set_markup(
+ "Last modified date: %s"
+ % fn.get_cache_last_modified()
+ )
+ else:
+ self.label_cache_lastmodified.set_markup(
+ "Last modified date: %s"
+ % "Refresh failed"
+ )
+
+ def update_official_list(self):
+ self.manager_gui.installed_kernels = fn.get_installed_kernels()
+ GLib.idle_add(
+ self.manager_gui.kernel_stack.add_official_kernels_to_stack,
+ True,
+ )
+
+ def on_close_clicked(self, widget):
+ self.destroy()
+
+ def on_button_logfile_clicked(self, widget):
+ try:
+ cmd = ["sudo", "-u", fn.sudo_username, "xdg-open", fn.event_log_file]
+ fn.subprocess.Popen(
+ cmd,
+ shell=False,
+ stdout=fn.subprocess.PIPE,
+ stderr=fn.subprocess.STDOUT,
+ )
+
+ except Exception as e:
+ fn.logger.error("Exception in on_button_logfile_clicked(): %s" % e)
+
+
+class Bootloader(GObject.Object):
+ __gtype_name__ = "Bootloader"
+
+ def __init__(self, id, name):
+ super().__init__()
+
+ self.id = id
+ self.name = name
+
+ @GObject.Property
+ def bootloader_id(self):
+ return self.id
+
+ @GObject.Property
+ def bootloader_name(self):
+ return self.name
diff --git a/ui/SplashScreen.py b/ui/SplashScreen.py
new file mode 100644
index 0000000..f5799dd
--- /dev/null
+++ b/ui/SplashScreen.py
@@ -0,0 +1,30 @@
+import gi
+import libs.functions as fn
+
+gi.require_version("Gtk", "4.0")
+from gi.repository import Gtk, Gio
+
+base_dir = fn.os.path.abspath(fn.os.path.join(fn.os.path.dirname(__file__), ".."))
+
+
+class SplashScreen(Gtk.Window):
+ def __init__(self, app_name, **kwargs):
+ super().__init__(**kwargs)
+ self.set_decorated(False)
+ self.set_resizable(False)
+ self.set_default_size(600, 400)
+
+ self.set_modal(True)
+ self.set_title(app_name)
+ self.set_icon_name("archlinux-kernel-manager-tux")
+
+ tux_icon = Gtk.Picture.new_for_file(
+ file=Gio.File.new_for_path(
+ fn.os.path.join(base_dir, "images/600x400/akm-tux-splash.png")
+ )
+ )
+
+ tux_icon.set_content_fit(content_fit=Gtk.ContentFit.FILL)
+
+ self.set_child(child=tux_icon)
+ self.present()
diff --git a/ui/Stack.py b/ui/Stack.py
new file mode 100644
index 0000000..a9454ef
--- /dev/null
+++ b/ui/Stack.py
@@ -0,0 +1,30 @@
+import gi
+
+gi.require_version("Gtk", "4.0")
+from gi.repository import Gtk
+
+
+class Stack(Gtk.Stack):
+ def __init__(self, transition_type):
+ super(Stack, self).__init__()
+
+ # self.set_transition_type(Gtk.StackTransitionType.ROTATE_LEFT)
+ if transition_type == "ROTATE_LEFT":
+ transition_type = Gtk.StackTransitionType.ROTATE_LEFT
+ if transition_type == "ROTATE_RIGHT":
+ transition_type = Gtk.StackTransitionType.ROTATE_RIGHT
+ if transition_type == "CROSSFADE":
+ transition_type = Gtk.StackTransitionType.CROSSFADE
+ if transition_type == "SLIDE_UP":
+ transition_type = Gtk.StackTransitionType.SLIDE_UP
+ if transition_type == "SLIDE_DOWN":
+ transition_type = Gtk.StackTransitionType.SLIDE_DOWN
+ if transition_type == "OVER_DOWN":
+ transition_type = Gtk.StackTransitionType.OVER_DOWN
+
+ self.set_transition_type(transition_type)
+ self.set_hexpand(True)
+ self.set_vexpand(True)
+ self.set_transition_duration(250)
+ self.set_hhomogeneous(False)
+ self.set_vhomogeneous(False)