Format code with Black

This commit is contained in:
heliguy
2023-12-22 23:33:28 -05:00
parent 51c3fab1ad
commit e91a06be2e
11 changed files with 1173 additions and 367 deletions

View File

@@ -10,8 +10,8 @@ from .snapshots_window import SnapshotsWindow
from .filter_window import FilterWindow
from .common import myUtils
class AppRow(Adw.ActionRow):
class AppRow(Adw.ActionRow):
def set_selectable(self, is_selectable):
self.tickbox.set_active(False)
self.tickbox.set_visible(is_selectable)
@@ -61,13 +61,38 @@ class AppRow(Adw.ActionRow):
if len(current_flatpak[13]) == 0:
self.is_runtime = True
info_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, valign=Gtk.Align.CENTER, halign=Gtk.Align.CENTER, hexpand=True, vexpand=True, spacing=6)
info_box = Gtk.Box(
orientation=Gtk.Orientation.VERTICAL,
valign=Gtk.Align.CENTER,
halign=Gtk.Align.CENTER,
hexpand=True,
vexpand=True,
spacing=6,
)
# justify=Gtk.Justification.RIGHT
self.mask_label = Gtk.Label(label=_("Updates Disabled"), visible=False, hexpand=True, wrap=True, valign=Gtk.Align.CENTER, tooltip_text=_("{} is masked and will not be updated").format(self.app_name))
self.mask_label = Gtk.Label(
label=_("Updates Disabled"),
visible=False,
hexpand=True,
wrap=True,
valign=Gtk.Align.CENTER,
tooltip_text=_("{} is masked and will not be updated").format(
self.app_name
),
)
self.mask_label.add_css_class("warning")
self.eol_app_label = Gtk.Label(label=_("App EOL"), visible=False, hexpand=True, wrap=True, valign=Gtk.Align.CENTER, tooltip_text=_("{} has reached its End of Life and will not receive any security updates").format(self.app_name))
self.eol_app_label = Gtk.Label(
label=_("App EOL"),
visible=False,
hexpand=True,
wrap=True,
valign=Gtk.Align.CENTER,
tooltip_text=_(
"{} has reached its End of Life and will not receive any security updates"
).format(self.app_name),
)
self.eol_app_label.add_css_class("error")
info_box.append(self.eol_app_label)
if "eol" in parent_window.host_flatpaks[index][12]:
@@ -75,7 +100,16 @@ class AppRow(Adw.ActionRow):
# justify=Gtk.Justification.RIGHT
self.eol_app_label.set_visible(True)
self.eol_runtime_label = Gtk.Label(label=_("Runtime EOL"), visible=False, hexpand=True, wrap=True, valign=Gtk.Align.CENTER, tooltip_text=_("{}'s runtime has reached its End of Life and will not receive any security updates").format(self.app_name))
self.eol_runtime_label = Gtk.Label(
label=_("Runtime EOL"),
visible=False,
hexpand=True,
wrap=True,
valign=Gtk.Align.CENTER,
tooltip_text=_(
"{}'s runtime has reached its End of Life and will not receive any security updates"
).format(self.app_name),
)
self.eol_runtime_label.add_css_class("error")
info_box.append(self.eol_runtime_label)
if current_flatpak[13] in parent_window.eol_list:
@@ -85,92 +119,188 @@ class AppRow(Adw.ActionRow):
info_pop = Gtk.Popover()
info_pop.set_child(info_box)
self.info_button = Gtk.MenuButton(visible=False, valign=Gtk.Align.CENTER, popover=info_pop, icon_name="software-update-urgent-symbolic")
self.info_button = Gtk.MenuButton(
visible=False,
valign=Gtk.Align.CENTER,
popover=info_pop,
icon_name="software-update-urgent-symbolic",
)
self.info_button.add_css_class("flat")
info_box.append(self.mask_label)
self.add_suffix(self.info_button)
properties_button = Gtk.Button(icon_name="info-symbolic", valign=Gtk.Align.CENTER, tooltip_text=_("View Properties"))
properties_button = Gtk.Button(
icon_name="info-symbolic",
valign=Gtk.Align.CENTER,
tooltip_text=_("View Properties"),
)
properties_button.add_css_class("flat")
properties_button.connect("clicked", lambda *_: PropertiesWindow(index, host_flatpaks, parent_window))
properties_button.connect(
"clicked", lambda *_: PropertiesWindow(index, host_flatpaks, parent_window)
)
self.add_suffix(properties_button)
self.tickbox = Gtk.CheckButton(visible=False) # visible=self.in_batch_mode
self.tickbox = Gtk.CheckButton(visible=False) # visible=self.in_batch_mode
self.tickbox.add_css_class("selection-mode")
self.tickbox.connect("toggled", parent_window.rowSelectHandler)
self.add_suffix(self.tickbox)
self.set_activatable_widget(self.tickbox)
self.set_activatable(False)
self.row_menu = Gtk.MenuButton(icon_name="view-more-symbolic", valign=Gtk.Align.CENTER) # visible=not self.in_batch_mode
self.row_menu = Gtk.MenuButton(
icon_name="view-more-symbolic", valign=Gtk.Align.CENTER
) # visible=not self.in_batch_mode
self.row_menu.add_css_class("flat")
row_menu_model = Gio.Menu()
copy_menu_model = Gio.Menu()
advanced_menu_model = Gio.Menu()
self.add_suffix(self.row_menu)
parent_window.create_action(("copy-name" + str(index)), lambda *_, name=self.app_name, toast=_("Copied name"): parent_window.copyItem(name, toast))
copy_menu_model.append_item(Gio.MenuItem.new(_("Copy Name"), f"win.copy-name{index}"))
parent_window.create_action(
("copy-name" + str(index)),
lambda *_, name=self.app_name, toast=_(
"Copied name"
): parent_window.copyItem(name, toast),
)
copy_menu_model.append_item(
Gio.MenuItem.new(_("Copy Name"), f"win.copy-name{index}")
)
parent_window.create_action(("copy-id" + str(index)), lambda *_, id=self.app_id, toast=_("Copied ID"): parent_window.copyItem(id, toast))
copy_menu_model.append_item(Gio.MenuItem.new(_("Copy ID"), f"win.copy-id{index}"))
parent_window.create_action(
("copy-id" + str(index)),
lambda *_, id=self.app_id, toast=_("Copied ID"): parent_window.copyItem(
id, toast
),
)
copy_menu_model.append_item(
Gio.MenuItem.new(_("Copy ID"), f"win.copy-id{index}")
)
parent_window.create_action(("copy-ref" + str(index)), lambda *_, ref=self.app_ref, toast=_("Copied ref"): parent_window.copyItem(ref, toast))
copy_menu_model.append_item(Gio.MenuItem.new(_("Copy Ref"), f"win.copy-ref{index}"))
parent_window.create_action(
("copy-ref" + str(index)),
lambda *_, ref=self.app_ref, toast=_("Copied ref"): parent_window.copyItem(
ref, toast
),
)
copy_menu_model.append_item(
Gio.MenuItem.new(_("Copy Ref"), f"win.copy-ref{index}")
)
parent_window.create_action(("copy-command" + str(index)), lambda *_, ref=self.app_ref, toast=_("Copied launch command"): parent_window.copyItem(f"flatpak run {ref}", toast))
copy_menu_model.append_item(Gio.MenuItem.new(_("Copy Launch Command"), f"win.copy-command{index}"))
parent_window.create_action(
("copy-command" + str(index)),
lambda *_, ref=self.app_ref, toast=_(
"Copied launch command"
): parent_window.copyItem(f"flatpak run {ref}", toast),
)
copy_menu_model.append_item(
Gio.MenuItem.new(_("Copy Launch Command"), f"win.copy-command{index}")
)
row_menu_model.append_submenu(_("Copy"), copy_menu_model)
if "runtime" not in parent_window.host_flatpaks[index][12]:
parent_window.create_action(("run" + str(index)), lambda *_a, ref=self.app_ref, name=self.app_name: parent_window.runAppThread(ref, _("Opened {}").format(name)))
parent_window.create_action(
("run" + str(index)),
lambda *_a, ref=self.app_ref, name=self.app_name: parent_window.runAppThread(
ref, _("Opened {}").format(name)
),
)
run_item = Gio.MenuItem.new(_("Open"), f"win.run{index}")
row_menu_model.append_item(run_item)
parent_window.create_action(("uninstall" + str(index)), lambda *_: parent_window.uninstallButtonHandler(self, self.app_name, self.app_ref, self.app_id))
parent_window.create_action(
("uninstall" + str(index)),
lambda *_: parent_window.uninstallButtonHandler(
self, self.app_name, self.app_ref, self.app_id
),
)
uninstall_item = Gio.MenuItem.new(_("Uninstall"), f"win.uninstall{index}")
row_menu_model.append_item(uninstall_item)
data_menu_model = Gio.Menu()
parent_window.create_action(("open-data" + str(index)), lambda *_, path=(parent_window.user_data_path + self.app_id): parent_window.openDataFolder(path))
open_data_item = Gio.MenuItem.new(_("Open User Data Folder"), f"win.open-data{index}")
open_data_item.set_attribute_value("hidden-when", GLib.Variant.new_string("action-disabled"))
parent_window.create_action(
("open-data" + str(index)),
lambda *_, path=(
parent_window.user_data_path + self.app_id
): parent_window.openDataFolder(path),
)
open_data_item = Gio.MenuItem.new(
_("Open User Data Folder"), f"win.open-data{index}"
)
open_data_item.set_attribute_value(
"hidden-when", GLib.Variant.new_string("action-disabled")
)
data_menu_model.append_item(open_data_item)
parent_window.create_action(("trash" + str(index)), lambda *_, name=self.app_name, id=self.app_id, index=index: parent_window.trashData(name, id, index))
parent_window.create_action(
("trash" + str(index)),
lambda *_, name=self.app_name, id=self.app_id, index=index: parent_window.trashData(
name, id, index
),
)
trash_item = Gio.MenuItem.new(_("Trash User Data"), f"win.trash{index}")
trash_item.set_attribute_value("hidden-when", GLib.Variant.new_string("action-disabled"))
trash_item.set_attribute_value(
"hidden-when", GLib.Variant.new_string("action-disabled")
)
data_menu_model.append_item(trash_item)
row_menu_model.append_section(None, data_menu_model)
if not os.path.exists(parent_window.user_data_path + self.app_id):
parent_window.lookup_action(f"open-data{self.index}").set_enabled(False)
parent_window.lookup_action(f"trash{self.index}").set_enabled(False)
parent_window.create_action(("mask" + str(index)), lambda *_, id=self.app_id, type=self.install_type, index=index: parent_window.maskFlatpak(self))
parent_window.create_action(
("mask" + str(index)),
lambda *_, id=self.app_id, type=self.install_type, index=index: parent_window.maskFlatpak(
self
),
)
mask_item = Gio.MenuItem.new(_("Disable Updates"), f"win.mask{index}")
mask_item.set_attribute_value("hidden-when", GLib.Variant.new_string("action-disabled"))
mask_item.set_attribute_value(
"hidden-when", GLib.Variant.new_string("action-disabled")
)
advanced_menu_model.append_item(mask_item)
parent_window.create_action(("unmask" + str(index)), lambda *_, id=self.app_id, type=self.install_type, index=index: parent_window.maskFlatpak(self))
parent_window.create_action(
("unmask" + str(index)),
lambda *_, id=self.app_id, type=self.install_type, index=index: parent_window.maskFlatpak(
self
),
)
unmask_item = Gio.MenuItem.new(_("Enable Updates"), f"win.unmask{index}")
unmask_item.set_attribute_value("hidden-when", GLib.Variant.new_string("action-disabled"))
unmask_item.set_attribute_value(
"hidden-when", GLib.Variant.new_string("action-disabled")
)
advanced_menu_model.append_item(unmask_item)
if "runtime" not in parent_window.host_flatpaks[index][12]:
parent_window.create_action(("snapshot" + str(index)), lambda *_, row=current_flatpak: SnapshotsWindow(parent_window, row).present())
snapshot_item = Gio.MenuItem.new(_("Manage Snapshots"), f"win.snapshot{index}")
parent_window.create_action(
("snapshot" + str(index)),
lambda *_, row=current_flatpak: SnapshotsWindow(
parent_window, row
).present(),
)
snapshot_item = Gio.MenuItem.new(
_("Manage Snapshots"), f"win.snapshot{index}"
)
advanced_menu_model.append_item(snapshot_item)
parent_window.create_action(("downgrade" + str(index)), lambda *_, row=current_flatpak, index=index: DowngradeWindow(parent_window, row, index))
parent_window.create_action(
("downgrade" + str(index)),
lambda *_, row=current_flatpak, index=index: DowngradeWindow(
parent_window, row, index
),
)
downgrade_item = Gio.MenuItem.new(_("Downgrade"), f"win.downgrade{index}")
advanced_menu_model.append_item(downgrade_item)
if self.app_id in parent_window.system_mask_list or self.app_id in parent_window.user_mask_list:
if (
self.app_id in parent_window.system_mask_list
or self.app_id in parent_window.user_mask_list
):
self.mask_label.set_visible(True)
parent_window.lookup_action(f"mask{index}").set_enabled(False)
else:

View File

@@ -1,9 +1,10 @@
from gi.repository import GLib, Gtk, Adw, Gio #, Gdk
from gi.repository import GLib, Gtk, Adw, Gio # , Gdk
import os
import subprocess
import pathlib
import time
class myUtils:
def __init__(self, window, **kwargs):
self.parent_window = window
@@ -11,15 +12,20 @@ class myUtils:
self.user_data_path = self.host_home + "/.var/app/"
self.install_success = True
self.uninstall_success = True
self.new_env = dict( os.environ )
self.new_env['LC_ALL'] = 'C'
self.new_env = dict(os.environ)
self.new_env["LC_ALL"] = "C"
def trashFolder(self, path):
if not os.path.exists(path):
print("error in common.trashFolder: path does not exists. path =", path)
return 1
try:
subprocess.run(["gio", "trash", path], capture_output=False, check=True, env=self.new_env)
subprocess.run(
["gio", "trash", path],
capture_output=False,
check=True,
env=self.new_env,
)
return 0
except subprocess.CalledProcessError as e:
print("error in common.trashFolder: CalledProcessError:", e)
@@ -68,10 +74,18 @@ class myUtils:
def findAppIcon(self, app_id):
icon_theme = Gtk.IconTheme.new()
icon_theme.add_search_path("/var/lib/flatpak/exports/share/icons/")
icon_theme.add_search_path(self.host_home + "/.local/share/flatpak/exports/share/icons")
icon_theme.add_search_path(
self.host_home + "/.local/share/flatpak/exports/share/icons"
)
try:
icon_path = (icon_theme.lookup_icon(app_id, None, 512, 1, self.parent_window.get_direction(), 0).get_file().get_path())
icon_path = (
icon_theme.lookup_icon(
app_id, None, 512, 1, self.parent_window.get_direction(), 0
)
.get_file()
.get_path()
)
except GLib.GError:
icon_path = None
if icon_path:
@@ -85,7 +99,12 @@ class myUtils:
def getHostUpdates(self):
list = []
output = subprocess.run(["flatpak-spawn", "--host", "flatpak", "update"], capture_output=True, text=True, env=self.new_env).stdout
output = subprocess.run(
["flatpak-spawn", "--host", "flatpak", "update"],
capture_output=True,
text=True,
env=self.new_env,
).stdout
lines = output.strip().split("\n")
columns = lines[0].split("\t")
data = [columns]
@@ -94,20 +113,37 @@ class myUtils:
data.append(row)
for i in range(len(data)):
if data[i][0].find('.') == 2:
if data[i][0].find(".") == 2:
list.append(data[i][2])
return(list)
return list
def getHostPins(self):
output = subprocess.run(["flatpak-spawn", "--host", "flatpak", "pin"], capture_output=True, text=True, env=self.new_env).stdout
output = subprocess.run(
["flatpak-spawn", "--host", "flatpak", "pin"],
capture_output=True,
text=True,
env=self.new_env,
).stdout
data = output.strip().split("\n")
for i in range(len(data)):
data[i] = data[i].strip()
return data
def getHostRemotes(self):
output = subprocess.run(["flatpak-spawn", "--host", "flatpak", "remotes", "--columns=all", "--show-disabled"], capture_output=True, text=True, env=self.new_env).stdout
output = subprocess.run(
[
"flatpak-spawn",
"--host",
"flatpak",
"remotes",
"--columns=all",
"--show-disabled",
],
capture_output=True,
text=True,
env=self.new_env,
).stdout
lines = output.strip().split("\n")
columns = lines[0].split("\t")
data = [columns]
@@ -117,7 +153,12 @@ class myUtils:
return data
def getHostFlatpaks(self):
output = subprocess.run(["flatpak-spawn", "--host", "flatpak", "list", "--columns=all"], capture_output=True, text=True, env=self.new_env).stdout
output = subprocess.run(
["flatpak-spawn", "--host", "flatpak", "list", "--columns=all"],
capture_output=True,
text=True,
env=self.new_env,
).stdout
lines = output.strip().split("\n")
columns = lines[0].split("\t")
data = [columns]
@@ -125,7 +166,12 @@ class myUtils:
row = line.split("\t")
data.append(row)
output = subprocess.run(["flatpak-spawn", "--host", "flatpak", "list", "--columns=runtime"], capture_output=True, text=True, env=self.new_env).stdout
output = subprocess.run(
["flatpak-spawn", "--host", "flatpak", "list", "--columns=runtime"],
capture_output=True,
text=True,
env=self.new_env,
).stdout
lines = output.split("\n")
for i in range(len(data)):
data[i].append(lines[i])
@@ -142,39 +188,65 @@ class myUtils:
dependent_runtimes.append(current[13])
except:
print("Could not get dependent runtime")
return(dependent_runtimes)
return dependent_runtimes
def getHostMasks(self, user_or_system):
output = subprocess.run(["flatpak-spawn", "--host", "flatpak", "mask", f"--{user_or_system}"], capture_output=True, text=True, env=self.new_env).stdout
output = subprocess.run(
["flatpak-spawn", "--host", "flatpak", "mask", f"--{user_or_system}"],
capture_output=True,
text=True,
env=self.new_env,
).stdout
lines = output.strip().split("\n")
for i in range(len(lines)):
lines[i] = lines[i].strip()
return(lines)
return lines
def maskFlatpak(self, app_id, user_or_system, remove=False):
command = ["flatpak-spawn", "--host", "flatpak", "mask", f"--{user_or_system}", app_id]
command = [
"flatpak-spawn",
"--host",
"flatpak",
"mask",
f"--{user_or_system}",
app_id,
]
if remove:
command.append("--remove")
response = ""
try:
response = subprocess.run(command, capture_output=True, text=True, env=self.new_env)
response = subprocess.run(
command, capture_output=True, text=True, env=self.new_env
)
except subprocess.CalledProcessError as e:
print(f"Error setting mask for {app_id}:\n", e)
return(1)
return 1
if len(response.stderr) > 0:
return(1)
return(0)
return 1
return 0
def downgradeFlatpak(self, ref, commit, install_type="system"):
command = ['flatpak-spawn', '--host', 'pkexec', 'flatpak', 'update', ref, f"--commit={commit}", f"--{install_type}", '-y']
command = [
"flatpak-spawn",
"--host",
"pkexec",
"flatpak",
"update",
ref,
f"--commit={commit}",
f"--{install_type}",
"-y",
]
try:
response = subprocess.run(command, capture_output=True, text=True, env=self.new_env).stderr
response = subprocess.run(
command, capture_output=True, text=True, env=self.new_env
).stderr
except subprocess.CalledProcessError as e:
if "note that" in response.lower():
return(0)
return 0
print(f"Error setting mask for {app_id}:\n", e)
return(1)
return(0)
return 1
return 0
def uninstallFlatpak(self, ref_arr, type_arr, should_trash, progress_bar=None):
self.uninstall_success = True
@@ -193,23 +265,40 @@ class myUtils:
# apps array guide: [app_ref, app_id, user_or_system_install]
for i in range(len(apps)):
command = ['flatpak-spawn', '--host', 'flatpak', 'remove', '-y', f"--{apps[i][2]}", apps[i][0]]
command = [
"flatpak-spawn",
"--host",
"flatpak",
"remove",
"-y",
f"--{apps[i][2]}",
apps[i][0],
]
try:
subprocess.run(command, capture_output=False, check=True, env=self.new_env)
subprocess.run(
command, capture_output=False, check=True, env=self.new_env
)
if progress_bar:
GLib.idle_add(progress_bar.set_visible, True)
GLib.idle_add(progress_bar.set_fraction, (i + 1.0) / len(ref_arr))
except subprocess.CalledProcessError:
fails.append(apps[i])
if len(fails) > 0: # Run this only if there is 1 or more non uninstalled apps
pk_command = ['flatpak-spawn', '--host', 'pkexec', 'flatpak', 'remove', '-y', '--system']
if len(fails) > 0: # Run this only if there is 1 or more non uninstalled apps
pk_command = [
"flatpak-spawn",
"--host",
"pkexec",
"flatpak",
"remove",
"-y",
"--system",
]
print("second uninstall process")
for i in range(len(fails)):
if fails[i][2] == "user":
self.uninstall_success = False
continue # Skip if app is a user install app
continue # Skip if app is a user install app
pk_command.append(fails[i][0])
try:
@@ -217,7 +306,9 @@ class myUtils:
if progress_bar:
GLib.idle_add(progress_bar.set_visible, True)
GLib.idle_add(progress_bar.set_fraction, 0.9)
subprocess.run(pk_command, capture_output=False, check=True, env=self.new_env)
subprocess.run(
pk_command, capture_output=False, check=True, env=self.new_env
)
except subprocess.CalledProcessError:
self.uninstall_success = False
@@ -242,29 +333,42 @@ class myUtils:
fails = []
for i in range(len(app_arr)):
command = ['flatpak-spawn', '--host', 'flatpak', 'install']
command = ["flatpak-spawn", "--host", "flatpak", "install"]
if remote != None:
command.append(remote)
command.append(f"--{user_or_system}")
command.append('-y')
command.append("-y")
command.append(app_arr[i])
try:
subprocess.run(command, capture_output=False, check=True, env=self.new_env)
subprocess.run(
command, capture_output=False, check=True, env=self.new_env
)
if progress_bar:
GLib.idle_add(progress_bar.set_visible, True)
GLib.idle_add(progress_bar.set_fraction, (i + 1.0) / len(app_arr))
except subprocess.CalledProcessError:
fails.append(app_arr[i])
if (len(fails) > 0) and (user_or_system == "system"):
pk_command = ['flatpak-spawn', '--host', 'pkexec', 'flatpak', 'install', remote, f"--{user_or_system}", '-y']
pk_command = [
"flatpak-spawn",
"--host",
"pkexec",
"flatpak",
"install",
remote,
f"--{user_or_system}",
"-y",
]
for i in range(len(fails)):
pk_command.append(fails[i])
try:
if progress_bar:
GLib.idle_add(progress_bar.set_visible, True)
GLib.idle_add(progress_bar.set_fraction, 0.9)
subprocess.run(pk_command, capture_output=False, check=True, env=self.new_env)
subprocess.run(
pk_command, capture_output=False, check=True, env=self.new_env
)
except subprocess.CalledProcessError:
self.install_success = False
@@ -279,7 +383,12 @@ class myUtils:
self.run_app_error = False
self.run_app_error_message = ""
try:
subprocess.run(['flatpak-spawn', '--host', 'flatpak', 'run', ref], check=True, env=self.new_env, start_new_session=True)
subprocess.run(
["flatpak-spawn", "--host", "flatpak", "run", ref],
check=True,
env=self.new_env,
start_new_session=True,
)
except subprocess.CalledProcessError as e:
self.run_app_error_message = str(e)
self.run_app_error = True
@@ -292,9 +401,20 @@ class myUtils:
if "system" in type_arr:
return "system"
def snapshotApps(self, epoch, app_snapshot_path_arr, app_version_arr, app_user_data_arr, progress_bar=None):
if not (len(app_snapshot_path_arr) == len(app_version_arr) == len(app_user_data_arr)):
print("error in common.snapshotApp: the lengths of app_snapshot_path_arr, app_version_arr, and app_user_data_arr do not match.")
def snapshotApps(
self,
epoch,
app_snapshot_path_arr,
app_version_arr,
app_user_data_arr,
progress_bar=None,
):
if not (
len(app_snapshot_path_arr) == len(app_version_arr) == len(app_user_data_arr)
):
print(
"error in common.snapshotApp: the lengths of app_snapshot_path_arr, app_version_arr, and app_user_data_arr do not match."
)
return 1
fails = []
@@ -303,7 +423,14 @@ class myUtils:
snapshot_path = app_snapshot_path_arr[i]
version = app_version_arr[i]
user_data = app_user_data_arr[i]
command = ['tar', 'cafv', f"{snapshot_path}{epoch}_{version}.tar.zst", "-C", f"{user_data}", "."]
command = [
"tar",
"cafv",
f"{snapshot_path}{epoch}_{version}.tar.zst",
"-C",
f"{user_data}",
".",
]
try:
if not os.path.exists(snapshot_path):
@@ -312,13 +439,18 @@ class myUtils:
subprocess.run(command, check=True, env=self.new_env)
if progress_bar:
GLib.idle_add(progress_bar.set_visible, True)
GLib.idle_add(progress_bar.set_fraction, (i + 1.0) / len(app_snapshot_path_arr))
GLib.idle_add(
progress_bar.set_fraction,
(i + 1.0) / len(app_snapshot_path_arr),
)
except subprocess.CalledProcessError as e:
print("error in common.snapshotApp:", e)
fails.append(user_data)
if(int(time.time()) == epoch): # Wait 1s if the snapshot is made too quickly, to prevent overriding a snapshot file
subprocess.run(['sleep', '1s'])
if (
int(time.time()) == epoch
): # Wait 1s if the snapshot is made too quickly, to prevent overriding a snapshot file
subprocess.run(["sleep", "1s"])
if progress_bar:
GLib.idle_add(progress_bar.set_visible, False)

View File

@@ -4,12 +4,13 @@ import subprocess
import os
import pathlib
@Gtk.Template(resource_path="/io/github/flattool/Warehouse/../data/ui/downgrade.ui")
class DowngradeWindow(Adw.Window):
__gtype_name__ = "DowngradeWindow"
new_env = dict( os.environ )
new_env['LC_ALL'] = 'C'
new_env = dict(os.environ)
new_env["LC_ALL"] = "C"
cancel_button = Gtk.Template.Child()
apply_button = Gtk.Template.Child()
@@ -34,14 +35,28 @@ class DowngradeWindow(Adw.Window):
self.commit_to_use = self.versions[index][0]
def getCommits(self):
output = subprocess.run(["flatpak-spawn", "--host", "flatpak", "remote-info", "--log", self.remote, self.app_ref, f"--{self.install_type}"], capture_output=True, text=True, env=self.new_env).stdout
output = subprocess.run(
[
"flatpak-spawn",
"--host",
"flatpak",
"remote-info",
"--log",
self.remote,
self.app_ref,
f"--{self.install_type}",
],
capture_output=True,
text=True,
env=self.new_env,
).stdout
lines = output.strip().split("\n")
columns = lines[0].split("\t")
data = [columns]
for line in lines[1:]:
row = line.split("\t")
data.append(row[0].strip())
commits = []
changes = []
dates = []
@@ -67,14 +82,24 @@ class DowngradeWindow(Adw.Window):
self.versions_group.add(group_button)
for i in range(len(self.versions)):
version = self.versions[i]
date_time = version[2].split(' ')
date = date_time[0].split('-')
date_time = version[2].split(" ")
date = date_time[0].split("-")
offset = date_time[2][:3] + ":" + date_time[2][3:]
time = date_time[1].split(':')
display_time = GLib.DateTime.new(GLib.TimeZone.new(offset), int(date[0]), int(date[1]), int(date[2]), int(time[0]), int(time[1]), int(time[2]))
time = date_time[1].split(":")
display_time = GLib.DateTime.new(
GLib.TimeZone.new(offset),
int(date[0]),
int(date[1]),
int(date[2]),
int(time[0]),
int(time[1]),
int(time[2]),
)
display_time = display_time.format("%x %X")
change = version[1].split('(')
row = Adw.ActionRow(title=GLib.markup_escape_text(change[0]), subtitle=str(display_time))
change = version[1].split("(")
row = Adw.ActionRow(
title=GLib.markup_escape_text(change[0]), subtitle=str(display_time)
)
row.set_tooltip_text(_("Commit Hash: {}").format(version[0]))
select = Gtk.CheckButton()
select.connect("toggled", self.selectionHandler, i)
@@ -98,18 +123,26 @@ class DowngradeWindow(Adw.Window):
self.progress_bar.set_visible = False
if self.response != 0:
self.toast_overlay.add_toast(Adw.Toast.new(_("Could not downgrade {}").format(self.app_name)))
self.toast_overlay.add_toast(
Adw.Toast.new(_("Could not downgrade {}").format(self.app_name))
)
return
if self.mask_row.get_active():
if self.my_utils.maskFlatpak(self.app_id, self.install_type) != 0:
self.parent_window.toast_overlay.add_toast(Adw.Toast.new(_("Could not disable updates for {}").format(self.app_name)))
self.parent_window.toast_overlay.add_toast(
Adw.Toast.new(
_("Could not disable updates for {}").format(self.app_name)
)
)
self.parent_window.refresh_list_of_flatpaks(self, False)
self.close()
def downgradeThread(self):
self.response = self.my_utils.downgradeFlatpak(self.app_ref, self.commit_to_use, self.install_type)
self.response = self.my_utils.downgradeFlatpak(
self.app_ref, self.commit_to_use, self.install_type
)
def onApply(self):
self.set_title(_("Downgrading…"))
@@ -118,7 +151,7 @@ class DowngradeWindow(Adw.Window):
self.should_pulse = True
self.progress_bar.set_visible(True)
self.pulser()
task = Gio.Task.new(None, None, lambda *_: self.downgradeCallack())
task.run_in_thread(lambda *_: self.downgradeThread())
@@ -152,8 +185,12 @@ class DowngradeWindow(Adw.Window):
self.add_controller(event_controller)
self.set_title(_("Fetching Releases…"))
self.set_transient_for(parent_window)
self.mask_row.set_subtitle(_("Ensure that {} will never be updated to a newer version").format(self.app_name))
self.mask_row.set_subtitle(
_("Ensure that {} will never be updated to a newer version").format(
self.app_name
)
)
self.generateList()
self.present()
self.present()

View File

@@ -4,6 +4,7 @@ import subprocess
import os
import pathlib
@Gtk.Template(resource_path="/io/github/flattool/Warehouse/../data/ui/filter.ui")
class FilterWindow(Adw.Window):
__gtype_name__ = "FilterWindow"
@@ -15,7 +16,6 @@ class FilterWindow(Adw.Window):
remotes_expander = Gtk.Template.Child()
runtimes_expander = Gtk.Template.Child()
def key_handler(self, _a, event, _c, _d):
if event == Gdk.KEY_Escape:
self.close()
@@ -35,10 +35,12 @@ class FilterWindow(Adw.Window):
self.apply_button.set_sensitive(False)
return
if self.apps_switch.get_active() and\
(not self.runtimes_switch.get_active()) and\
(not self.remotes_expander_switch.get_active()) and\
(not self.runtimes_expander_switch.get_active()):
if (
self.apps_switch.get_active()
and (not self.runtimes_switch.get_active())
and (not self.remotes_expander_switch.get_active())
and (not self.runtimes_expander_switch.get_active())
):
self.apply_button.set_sensitive(False)
return
@@ -69,7 +71,7 @@ class FilterWindow(Adw.Window):
def runtimesEnableHandler(self, switch, is_enabled):
self.runtimes_expander.set_enable_expansion(is_enabled)
for i in range(len(self.runtime_checkboxes)):
self.runtime_checkboxes[i].set_active(not is_enabled)
@@ -82,21 +84,25 @@ class FilterWindow(Adw.Window):
self.filter_list[4].append(runtime)
else:
self.filter_list[4].remove(runtime)
self.isListApplicable()
def generateList(self):
self.remotes_expander_switch = Gtk.Switch(valign=Gtk.Align.CENTER)
self.runtimes_expander_switch = Gtk.Switch(valign=Gtk.Align.CENTER)
dependent_runtimes = self.my_utils.getDependentRuntimes()
if len(self.host_remotes) < 2: # Don't give the ability to filter by remotes if there is only 1
if (
len(self.host_remotes) < 2
): # Don't give the ability to filter by remotes if there is only 1
self.remotes_expander.set_visible(False)
if len(dependent_runtimes) < 2: # Don't give the ability to filter by runtimes if there is only 1
if (
len(dependent_runtimes) < 2
): # Don't give the ability to filter by runtimes if there is only 1
self.runtimes_expander.set_visible(False)
self.remote_checkboxes = []
total = 0
for i in range(len(self.host_remotes)):
@@ -125,7 +131,9 @@ class FilterWindow(Adw.Window):
remote_row.add_suffix(remote_check)
remote_row.set_activatable_widget(remote_check)
remote_check.connect("toggled", self.remoteCheckHandler, install_type, name)
remote_check.connect(
"toggled", self.remoteCheckHandler, install_type, name
)
self.remote_checkboxes.append(remote_check)
remote_check.set_active(True)
except:
@@ -154,7 +162,7 @@ class FilterWindow(Adw.Window):
if not self.remotes_expander_switch.get_active():
self.filter_list[3] = "all"
if not self.runtimes_expander_switch.get_active():
self.filter_list[4] = "all"
self.filter_list[4] = "all"
def disableFilterToggle(self, _widget):
self.app_window.filter_button.set_active(self.has_apply_button_been_clicked)
@@ -176,8 +184,12 @@ class FilterWindow(Adw.Window):
self.add_controller(event_controller)
# Connections
self.apply_button.connect("clicked", lambda *_: self.setHas_apply_button_been_clicked(True))
self.apply_button.connect("clicked", lambda *_: main_window.applyFilter(self.filter_list))
self.apply_button.connect(
"clicked", lambda *_: self.setHas_apply_button_been_clicked(True)
)
self.apply_button.connect(
"clicked", lambda *_: main_window.applyFilter(self.filter_list)
)
self.apply_button.connect("clicked", lambda *_: self.close())
self.cancel_button.connect("clicked", lambda *_: self.close())
@@ -191,7 +203,7 @@ class FilterWindow(Adw.Window):
# Calls
self.apps_switch.set_active(True)
self.set_size_request(260, 230)
if not self.host_remotes[0][0] == '':
if not self.host_remotes[0][0] == "":
self.generateList()
else:
self.remotes_expander.set_visible(False)
@@ -199,5 +211,3 @@ class FilterWindow(Adw.Window):
self.filter_list[2] = "all"
self.filter_list[3] = "all"
self.filter_list[4] = "all"

View File

@@ -32,6 +32,7 @@ from .orphans_window import OrphansWindow
from .search_install_window import SearchInstallWindow
from .const import Config
class WarehouseApplication(Adw.Application):
"""The main application singleton class."""
@@ -48,22 +49,54 @@ class WarehouseApplication(Adw.Application):
self.create_action("preferences", self.on_preferences_action)
self.create_action("search", self.on_search_action, ["<primary>f"])
self.create_action("manage-data-folders", self.manage_data_shortcut)
self.create_action("toggle-batch-mode", self.batch_mode_shortcut, ["<primary>b", "<primary>Return"])
self.create_action("toggle-batch-mode-keypad", self.batch_mode_shortcut, ["<primary>KP_Enter"]) # This action is not added to the shortcuts window
self.create_action("manage-data-folders", self.manage_data_shortcut, ["<primary>d"])
self.create_action("refresh-list", self.refresh_list_shortcut, ["<primary>r", "F5"])
self.create_action("show-remotes-window", self.show_remotes_shortcut, ["<primary>m"])
self.create_action(
"toggle-batch-mode",
self.batch_mode_shortcut,
["<primary>b", "<primary>Return"],
)
self.create_action(
"toggle-batch-mode-keypad", self.batch_mode_shortcut, ["<primary>KP_Enter"]
) # This action is not added to the shortcuts window
self.create_action(
"manage-data-folders", self.manage_data_shortcut, ["<primary>d"]
)
self.create_action(
"refresh-list", self.refresh_list_shortcut, ["<primary>r", "F5"]
)
self.create_action(
"show-remotes-window", self.show_remotes_shortcut, ["<primary>m"]
)
self.create_action("set-filter", self.filters_shortcut, ["<primary>t"])
self.create_action("install-from-file", self.install_from_file, ["<primary>o"])
self.create_action("open-menu", self.main_menu_shortcut, ["F10"])
# self.create_action("open-search-install", self.open_search_install, ["<primary>i"])
gtk_version = str(Gtk.MAJOR_VERSION) + "." + str(Gtk.MINOR_VERSION) + "." + str(Gtk.MICRO_VERSION)
adw_version = str(Adw.MAJOR_VERSION) + "." + str(Adw.MINOR_VERSION) + "." + str(Adw.MICRO_VERSION)
gtk_version = (
str(Gtk.MAJOR_VERSION)
+ "."
+ str(Gtk.MINOR_VERSION)
+ "."
+ str(Gtk.MICRO_VERSION)
)
adw_version = (
str(Adw.MAJOR_VERSION)
+ "."
+ str(Adw.MINOR_VERSION)
+ "."
+ str(Adw.MICRO_VERSION)
)
os_string = GLib.get_os_info("NAME") + " " + GLib.get_os_info("VERSION")
lang = GLib.environ_getenv(GLib.get_environ(), "LANG")
self.troubleshooting = self.troubleshooting.format( os = os_string, wv = self.version, gtk = gtk_version, adw = adw_version, profile = Config.PROFILE, app_id = self.get_application_id(), lang = lang )
self.troubleshooting = self.troubleshooting.format(
os=os_string,
wv=self.version,
gtk=gtk_version,
adw=adw_version,
profile=Config.PROFILE,
app_id=self.get_application_id(),
lang=lang,
)
def open_search_install(self, widget, _):
SearchInstallWindow(self.props.active_window).present()
@@ -129,25 +162,37 @@ class WarehouseApplication(Adw.Application):
application_icon="io.github.flattool.Warehouse",
developer_name="Heliguy",
version=self.version,
developers=["Heliguy https://github.com/heliguy4599", "kramo https://kramo.hu"],
artists=["Heliguy https://github.com/heliguy4599", "kramo https://kramo.hu", "Amy https://github.com/AtiusAmy", "eryn https://github.com/hericiumvevo"],
copyright='© 2023 Heliguy',
developers=[
"Heliguy https://github.com/heliguy4599",
"kramo https://kramo.hu",
],
artists=[
"Heliguy https://github.com/heliguy4599",
"kramo https://kramo.hu",
"Amy https://github.com/AtiusAmy",
"eryn https://github.com/hericiumvevo",
],
copyright="© 2023 Heliguy",
license_type=Gtk.License.GPL_3_0_ONLY,
debug_info=self.troubleshooting,
# Translators: do one of the following, one per line: Your Name, Your Name <email@email.org>, Your Name https://websi.te
translator_credits=_("translator-credits"),
debug_info_filename="{}.txt".format(self.get_application_id()),
website='https://github.com/flattool/warehouse',
support_url='https://matrix.to/#/#warehouse-development:matrix.org',
issue_url='https://github.com/flattool/warehouse/issues')
website="https://github.com/flattool/warehouse",
support_url="https://matrix.to/#/#warehouse-development:matrix.org",
issue_url="https://github.com/flattool/warehouse/issues",
)
about.add_link(_("Donate"), "https://github.com/flattool/warehouse/issues")
about.add_credit_section(_("Contributors"), [
# Contributors: do one of the following, one per line: Your Name, Your Name <email@email.org>, Your Name https://websi.te
"Win <winsdominoes2018@gmail.com>",
"Óscar Fernández Díaz",
"Runar https://github.com/runarcn",
"skøldis <warehouse@turtle.garden>"
])
about.add_credit_section(
_("Contributors"),
[
# Contributors: do one of the following, one per line: Your Name, Your Name <email@email.org>, Your Name https://websi.te
"Win <winsdominoes2018@gmail.com>",
"Óscar Fernández Díaz",
"Runar https://github.com/runarcn",
"skøldis <warehouse@turtle.garden>",
],
)
about.present()
def on_preferences_action(self, widget, _):
@@ -155,7 +200,9 @@ class WarehouseApplication(Adw.Application):
print("app.preferences action activated")
def on_search_action(self, widget, _):
self.props.active_window.search_bar.set_search_mode(not self.props.active_window.search_bar.get_search_mode())
self.props.active_window.search_bar.set_search_mode(
not self.props.active_window.search_bar.get_search_mode()
)
def create_action(self, name, callback, shortcuts=None):
"""Add an application action.

View File

@@ -4,6 +4,7 @@ import subprocess
import os
import pathlib
@Gtk.Template(resource_path="/io/github/flattool/Warehouse/../data/ui/orphans.ui")
class OrphansWindow(Adw.Window):
__gtype_name__ = "OrphansWindow"
@@ -37,7 +38,7 @@ class OrphansWindow(Adw.Window):
def key_handler(self, _a, event, _c, _d):
if event == Gdk.KEY_Escape:
self.close()
def selectionHandler(self, widget, dir_name):
if widget.get_active():
self.selected_dirs.append(dir_name)
@@ -45,9 +46,13 @@ class OrphansWindow(Adw.Window):
self.selected_dirs.remove(dir_name)
if len(self.selected_dirs) == 0:
self.set_title(self.window_title) # Set the window title back to what it was when there are no selected dirs
self.set_title(
self.window_title
) # Set the window title back to what it was when there are no selected dirs
else:
self.set_title(("{} selected").format(str(len(self.selected_dirs)))) # Set the window title to the amount of selected dirs
self.set_title(
("{} selected").format(str(len(self.selected_dirs)))
) # Set the window title to the amount of selected dirs
if len(self.selected_dirs) == 0:
self.install_button.set_sensitive(False)
@@ -67,12 +72,14 @@ class OrphansWindow(Adw.Window):
self.generateList()
self.progress_bar.set_visible(False)
self.app_window.refresh_list_of_flatpaks(self, False)
self.disconnect(self.no_close_id) # Make window able to close
self.disconnect(self.no_close_id) # Make window able to close
self.search_button.set_sensitive(True)
if self.my_utils.install_success:
self.toast_overlay.add_toast(Adw.Toast.new(_("Installed successfully")))
else:
self.toast_overlay.add_toast(Adw.Toast.new(_("Could not install some apps")))
self.toast_overlay.add_toast(
Adw.Toast.new(_("Could not install some apps"))
)
def installHandler(self):
self.main_stack.set_visible_child(self.installing)
@@ -80,15 +87,20 @@ class OrphansWindow(Adw.Window):
self.set_title(self.window_title)
self.keep_checking = True
task = Gio.Task.new(None, None, self.installCallback)
task.run_in_thread(lambda _task, _obj, _data, _cancellable, id_list=self.selected_dirs, remote=self.selected_remote, app_type=self.selected_remote_type, progress_bar=self.progress_bar: self.my_utils.installFlatpak(id_list, remote, app_type, progress_bar))
def installButtonHandler(self, button):
task.run_in_thread(
lambda _task, _obj, _data, _cancellable, id_list=self.selected_dirs, remote=self.selected_remote, app_type=self.selected_remote_type, progress_bar=self.progress_bar: self.my_utils.installFlatpak(
id_list, remote, app_type, progress_bar
)
)
def installButtonHandler(self, button):
def remote_select_handler(button, index):
if not button.get_active():
return
self.selected_remote = self.host_remotes[index][0]
self.selected_remote_type = self.my_utils.getInstallType(self.host_remotes[index][7])
self.selected_remote_type = self.my_utils.getInstallType(
self.host_remotes[index][7]
)
def onResponse(dialog, response_id, _function):
if response_id == "cancel":
@@ -96,9 +108,15 @@ class OrphansWindow(Adw.Window):
self.installHandler()
self.progress_bar.set_visible(True)
self.action_bar.set_visible(False)
self.no_close_id = self.connect("close-request", lambda event: True) # Make window unable to close
dialog = Adw.MessageDialog.new(self, _("Attempt to Install?"), _("Warehouse will attempt to install apps matching the selected data."))
self.no_close_id = self.connect(
"close-request", lambda event: True
) # Make window unable to close
dialog = Adw.MessageDialog.new(
self,
_("Attempt to Install?"),
_("Warehouse will attempt to install apps matching the selected data."),
)
dialog.set_close_response("cancel")
dialog.add_response("cancel", _("Cancel"))
dialog.add_response("continue", _("Install"))
@@ -135,7 +153,7 @@ class OrphansWindow(Adw.Window):
else:
remote_row.set_subtitle(_("Unknown install type"))
if remote_row.get_title() == '-':
if remote_row.get_title() == "-":
remote_row.set_title(self.host_remotes[i][0])
if total_added > 0:
@@ -146,7 +164,7 @@ class OrphansWindow(Adw.Window):
total_added += 1
remote_select_buttons[0].set_active(True)
if total_added > 1:
dialog.set_extra_child(remotes_scroll)
@@ -154,7 +172,6 @@ class OrphansWindow(Adw.Window):
dialog.present()
def trashHandler(self, button):
def onResponse(dialog, response_id, _function):
if response_id == "cancel":
return
@@ -163,8 +180,10 @@ class OrphansWindow(Adw.Window):
self.my_utils.trashFolder(path)
self.select_all_button.set_active(False)
self.generateList()
dialog = Adw.MessageDialog.new(self, _("Trash folders?"), _("These folders will be sent to the trash."))
dialog = Adw.MessageDialog.new(
self, _("Trash folders?"), _("These folders will be sent to the trash.")
)
dialog.connect("response", onResponse, dialog.choose_finish)
dialog.set_close_response("cancel")
dialog.add_response("cancel", _("Cancel"))
@@ -176,7 +195,9 @@ class OrphansWindow(Adw.Window):
try:
Gio.AppInfo.launch_default_for_uri(f"file://{path}", None)
except GLib.GError:
properties_toast_overlay.add_toast(Adw.Toast.new(_("Could not open folder")))
properties_toast_overlay.add_toast(
Adw.Toast.new(_("Could not open folder"))
)
def sizeCallBack(self, row_index):
row = self.list_of_data.get_row_at_index(row_index)
@@ -191,8 +212,10 @@ class OrphansWindow(Adw.Window):
self.data_rows = []
self.host_flatpaks = self.my_utils.getHostFlatpaks()
if self.host_flatpaks == [['', '']]:
self.app_window.toast_overlay.add_toast(Adw.Toast.new(_("Could not manage data")))
if self.host_flatpaks == [["", ""]]:
self.app_window.toast_overlay.add_toast(
Adw.Toast.new(_("Could not manage data"))
)
self.this_just_crashes_the_window_so_it_doesnt_open()
return
@@ -221,12 +244,24 @@ class OrphansWindow(Adw.Window):
self.data_rows.append([dir_row])
path = self.user_data_path + dir_name
index = len(self.data_rows) - 1
task = Gio.Task.new(None, None, lambda *_, index=index: self.sizeCallBack(index))
task.run_in_thread(lambda _task, _obj, _data, _cancellable, *_, index=index: self.sizeThread(index, path))
task = Gio.Task.new(
None, None, lambda *_, index=index: self.sizeCallBack(index)
)
task.run_in_thread(
lambda _task, _obj, _data, _cancellable, *_, index=index: self.sizeThread(
index, path
)
)
open_row_button = Gtk.Button(icon_name="document-open-symbolic", valign=Gtk.Align.CENTER, tooltip_text=_("Open User Data Folder"))
open_row_button = Gtk.Button(
icon_name="document-open-symbolic",
valign=Gtk.Align.CENTER,
tooltip_text=_("Open User Data Folder"),
)
open_row_button.add_css_class("flat")
open_row_button.connect("clicked", self.open_button_handler, (self.user_data_path + dir_name))
open_row_button.connect(
"clicked", self.open_button_handler, (self.user_data_path + dir_name)
)
dir_row.add_suffix(open_row_button)
select_button = Gtk.CheckButton()
@@ -247,7 +282,7 @@ class OrphansWindow(Adw.Window):
self.action_bar.set_visible(True)
def filter_func(self, row):
if (self.search_entry.get_text().lower() in row.get_title().lower()):
if self.search_entry.get_text().lower() in row.get_title().lower():
self.is_result = True
return True
@@ -276,7 +311,9 @@ class OrphansWindow(Adw.Window):
def __init__(self, main_window, **kwargs):
super().__init__(**kwargs)
self.my_utils = myUtils(self) # Access common utils and set the window to this window
self.my_utils = myUtils(
self
) # Access common utils and set the window to this window
self.host_remotes = self.my_utils.getHostRemotes()
self.host_flatpaks = self.my_utils.getHostFlatpaks()
@@ -294,7 +331,7 @@ class OrphansWindow(Adw.Window):
self.add_controller(event_controller)
self.install_button.connect("clicked", self.installButtonHandler)
if self.host_remotes[0][0] == '':
if self.host_remotes[0][0] == "":
self.install_button.set_visible(False)
self.trash_button.connect("clicked", self.trashHandler)
self.select_all_button.connect("toggled", self.selectAllHandler)
@@ -304,4 +341,4 @@ class OrphansWindow(Adw.Window):
self.search_entry.connect("search-changed", self.on_invalidate)
self.search_bar.connect("notify", self.on_change)
self.search_bar.connect_entry(self.search_entry)
self.oepn_folder_button.connect("clicked", self.open_button_handler)
self.oepn_folder_button.connect("clicked", self.open_button_handler)

View File

@@ -4,12 +4,13 @@ import subprocess
import os
import pathlib
@Gtk.Template(resource_path="/io/github/flattool/Warehouse/../data/ui/properties.ui")
class PropertiesWindow(Adw.Window):
__gtype_name__ = "PropertiesWindow"
new_env = dict( os.environ )
new_env['LC_ALL'] = 'C'
new_env = dict(os.environ)
new_env["LC_ALL"] = "C"
host_home = str(pathlib.Path.home())
user_data_path = host_home + "/.var/app/"
@@ -81,9 +82,19 @@ class PropertiesWindow(Adw.Window):
def generateLower(self):
column_headers = [
_('Name'), _('Description'), _('App ID'), _('Version'), _('Branch'),
_('Arch'), _('Origin'), _('Installation'), _('Ref'), _('Active Commit'),
_('Latest Commit'), _('Installed Size'), _('Options')
_("Name"),
_("Description"),
_("App ID"),
_("Version"),
_("Branch"),
_("Arch"),
_("Origin"),
_("Installation"),
_("Ref"),
_("Active Commit"),
_("Latest Commit"),
_("Installed Size"),
_("Options"),
]
for i in range(len(column_headers)):
@@ -93,7 +104,10 @@ class PropertiesWindow(Adw.Window):
row = Adw.ActionRow(title=column_headers[i], activatable=True)
row.add_suffix(Gtk.Image.new_from_icon_name("edit-copy-symbolic"))
row.set_subtitle(GLib.markup_escape_text(self.current_flatpak[i]))
row.connect("activated", lambda *_a, row=row: self.copyItem(row.get_subtitle(), row.get_title()))
row.connect(
"activated",
lambda *_a, row=row: self.copyItem(row.get_subtitle(), row.get_title()),
)
self.lower.add(row)
def viewAppsHandler(self, widget):
@@ -121,16 +135,20 @@ class PropertiesWindow(Adw.Window):
self.open_data.set_visible(False)
self.trash_data.set_visible(False)
else:
self.toast_overlay.add_toast(Adw.Toast.new(_("Could not trash user data")))
self.toast_overlay.add_toast(
Adw.Toast.new(_("Could not trash user data"))
)
dialog = Adw.MessageDialog.new(self, _("Send {}'s User Data to the Trash?").format(self.app_name))
dialog = Adw.MessageDialog.new(
self, _("Send {}'s User Data to the Trash?").format(self.app_name)
)
dialog.add_response("cancel", _("Cancel"))
dialog.set_close_response("cancel")
dialog.add_response("continue", _("Trash Data"))
dialog.set_response_appearance("continue", Adw.ResponseAppearance.DESTRUCTIVE)
dialog.connect("response", onResponse, dialog.choose_finish)
dialog.present()
def __init__(self, flatpak_index, host_flatpaks, parent_window, **kwargs):
super().__init__(**kwargs)
self.my_utils = myUtils(self)
@@ -147,27 +165,47 @@ class PropertiesWindow(Adw.Window):
self.user_data_path += self.app_id
self.details.connect("activated", self.show_details)
self.runtime_copy.connect("clicked", lambda *_: self.copyItem(self.runtime.get_subtitle(), self.runtime.get_title()))
self.runtime_copy.connect(
"clicked",
lambda *_: self.copyItem(
self.runtime.get_subtitle(), self.runtime.get_title()
),
)
self.runtime_properties.connect("clicked", lambda *_: self.close())
self.runtime_properties.connect("clicked", lambda *_: self.showPropertiesHandler())
self.runtime_properties.connect(
"clicked", lambda *_: self.showPropertiesHandler()
)
self.view_apps.connect("activated", self.viewAppsHandler)
self.trash_data.connect("clicked", lambda *_: self.trashDataHandler())
if "eol" in self.current_flatpak[12]:
self.eol_app_banner.set_revealed(True)
self.eol_app_banner.set_title(_("{} has reached its End of Life and will not receive any security updates").format(self.app_name))
self.eol_app_banner.set_title(
_(
"{} has reached its End of Life and will not receive any security updates"
).format(self.app_name)
)
if self.current_flatpak[13] in parent_window.eol_list:
self.eol_runtime_banner.set_revealed(True)
self.eol_runtime_banner.set_title(_("{}'s runtime has reached its End of Life and will not receive any security updates").format(self.app_name))
self.eol_runtime_banner.set_title(
_(
"{}'s runtime has reached its End of Life and will not receive any security updates"
).format(self.app_name)
)
if self.app_id in self.my_utils.getHostMasks("system") or self.app_id in self.my_utils.getHostMasks("user"):
if self.app_id in self.my_utils.getHostMasks(
"system"
) or self.app_id in self.my_utils.getHostMasks("user"):
self.mask_banner.set_revealed(True)
self.mask_banner.set_title(_("{} is masked and will not be updated").format(self.app_name))
self.mask_banner.set_title(
_("{} is masked and will not be updated").format(self.app_name)
)
def key_handler(_a, event, _c, _d):
if event == Gdk.KEY_Escape:
self.close()
event_controller = Gtk.EventControllerKey()
event_controller.connect("key-pressed", key_handler)
self.add_controller(event_controller)
@@ -178,4 +216,4 @@ class PropertiesWindow(Adw.Window):
self.set_title(_("{} Properties").format(self.app_name))
self.set_size_request(260, 230)
self.set_transient_for(parent_window)
self.present()
self.present()

View File

@@ -4,6 +4,7 @@ import subprocess
import os
import re
@Gtk.Template(resource_path="/io/github/flattool/Warehouse/../data/ui/remotes.ui")
class RemotesWindow(Adw.Window):
__gtype_name__ = "RemotesWindow"
@@ -34,7 +35,12 @@ class RemotesWindow(Adw.Window):
self.toast_overlay.add_toast(Adw.Toast.new(text))
def get_host_flatpaks(self):
output = subprocess.run(["flatpak-spawn", "--host", "flatpak", "list", "--columns=all"], capture_output=True, text=True, env=self.new_env).stdout
output = subprocess.run(
["flatpak-spawn", "--host", "flatpak", "list", "--columns=all"],
capture_output=True,
text=True,
env=self.new_env,
).stdout
lines = output.strip().split("\n")
columns = lines[0].split("\t")
data = [columns]
@@ -50,7 +56,15 @@ class RemotesWindow(Adw.Window):
name = self.host_remotes[index][0]
title = self.host_remotes[index][1]
install_type = self.host_remotes[index][7]
command = ['flatpak-spawn', '--host', 'flatpak', 'remote-delete', '--force', name, f'--{install_type}']
command = [
"flatpak-spawn",
"--host",
"flatpak",
"remote-delete",
"--force",
name,
f"--{install_type}",
]
try:
subprocess.run(command, capture_output=True, check=True, env=self.new_env)
except subprocess.CalledProcessError as e:
@@ -63,7 +77,9 @@ class RemotesWindow(Adw.Window):
title = self.host_remotes[index][1]
install_type = self.host_remotes[index][7]
body_text = _("Any installed apps from {} will stop receiving updates").format(name)
body_text = _("Any installed apps from {} will stop receiving updates").format(
name
)
dialog = Adw.MessageDialog.new(self, _("Remove {}?").format(title), body_text)
dialog.set_close_response("cancel")
dialog.add_response("cancel", _("Cancel"))
@@ -82,11 +98,24 @@ class RemotesWindow(Adw.Window):
type = "user"
try:
command = ['flatpak-spawn', '--host', 'flatpak', 'remote-modify', name, f"--{type}", "--enable"]
command = [
"flatpak-spawn",
"--host",
"flatpak",
"remote-modify",
name,
f"--{type}",
"--enable",
]
subprocess.run(command, capture_output=False, check=True, env=self.new_env)
except subprocess.CalledProcessError as e:
self.toast_overlay.add_toast(Adw.Toast.new(_("Could not enable {}").format(name)))
print(f"error in remotes_window.enable_handler: could not enable remote {name}:", e)
self.toast_overlay.add_toast(
Adw.Toast.new(_("Could not enable {}").format(name))
)
print(
f"error in remotes_window.enable_handler: could not enable remote {name}:",
e,
)
self.generate_list()
@@ -95,11 +124,26 @@ class RemotesWindow(Adw.Window):
if response == "cancel":
return
try:
command = ['flatpak-spawn', '--host', 'flatpak', 'remote-modify', name, f"--{type}", "--disable"]
subprocess.run(command, capture_output=False, check=True, env=self.new_env)
command = [
"flatpak-spawn",
"--host",
"flatpak",
"remote-modify",
name,
f"--{type}",
"--disable",
]
subprocess.run(
command, capture_output=False, check=True, env=self.new_env
)
except subprocess.CalledProcessError as e:
self.toast_overlay.add_toast(Adw.Toast.new(_("Could not disable {}").format(name)))
print(f"error in remotes_window.enable_handler: could not disable remote {name}:", e)
self.toast_overlay.add_toast(
Adw.Toast.new(_("Could not disable {}").format(name))
)
print(
f"error in remotes_window.enable_handler: could not disable remote {name}:",
e,
)
self.generate_list()
@@ -114,7 +158,9 @@ class RemotesWindow(Adw.Window):
popoever.popdown()
body_text = _("Any installed apps from {} will stop receiving updates").format(name)
body_text = _("Any installed apps from {} will stop receiving updates").format(
name
)
dialog = Adw.MessageDialog.new(self, _("Disable {}?").format(title), body_text)
dialog.set_close_response("cancel")
dialog.add_response("cancel", _("Cancel"))
@@ -130,7 +176,10 @@ class RemotesWindow(Adw.Window):
type = "system"
else:
self.make_toast(_("Could not view apps").format(to_copy))
print("error in remotes_window.view_apps(): remote installation type is not either system or user. type is:", type)
print(
"error in remotes_window.view_apps(): remote installation type is not either system or user. type is:",
type,
)
return
self.app_window.should_open_filter_window = False
self.app_window.filter_button.set_active(True)
@@ -140,9 +189,13 @@ class RemotesWindow(Adw.Window):
def generate_list(self):
if self.show_disabled_button.get_active():
self.show_disabled_button_button_content.set_icon_name("eye-open-negative-filled-symbolic")
self.show_disabled_button_button_content.set_icon_name(
"eye-open-negative-filled-symbolic"
)
else:
self.show_disabled_button_button_content.set_icon_name("eye-not-looking-symbolic")
self.show_disabled_button_button_content.set_icon_name(
"eye-not-looking-symbolic"
)
self.host_remotes = self.my_utils.getHostRemotes()
self.host_flatpaks = self.get_host_flatpaks()
@@ -164,28 +217,55 @@ class RemotesWindow(Adw.Window):
install_type = self.host_remotes[i][7]
remote_row = Adw.ActionRow(title=title)
more = Gtk.MenuButton(icon_name="view-more-symbolic", valign=Gtk.Align.CENTER)
more = Gtk.MenuButton(
icon_name="view-more-symbolic", valign=Gtk.Align.CENTER
)
more.add_css_class("flat")
options = Gtk.Popover()
options_box = Gtk.Box(halign=Gtk.Align.CENTER, valign=Gtk.Align.CENTER, orientation=Gtk.Orientation.VERTICAL)
options_box = Gtk.Box(
halign=Gtk.Align.CENTER,
valign=Gtk.Align.CENTER,
orientation=Gtk.Orientation.VERTICAL,
)
filter_button = Gtk.Button()
filter_button.set_child(Adw.ButtonContent(icon_name="funnel-symbolic", label=_("Set Filter")))
filter_button.set_child(
Adw.ButtonContent(
icon_name="funnel-symbolic", label=_("Set Filter")
)
)
filter_button.add_css_class("flat")
filter_button.connect("clicked", lambda *_, i=i: self.view_paks(self.host_remotes[i][7], self.host_remotes[i][0]))
filter_button.connect(
"clicked",
lambda *_, i=i: self.view_paks(
self.host_remotes[i][7], self.host_remotes[i][0]
),
)
enable_button = Gtk.Button(visible=False)
enable_button.set_child(Adw.ButtonContent(icon_name="eye-open-negative-filled-symbolic", label=_("Enable")))
enable_button.set_child(
Adw.ButtonContent(
icon_name="eye-open-negative-filled-symbolic", label=_("Enable")
)
)
enable_button.add_css_class("flat")
enable_button.connect("clicked", self.enable_handler, i)
disable_button = Gtk.Button()
disable_button.set_child(Adw.ButtonContent(icon_name="eye-not-looking-symbolic", label=_("Disable")))
disable_button.set_child(
Adw.ButtonContent(
icon_name="eye-not-looking-symbolic", label=_("Disable")
)
)
disable_button.add_css_class("flat")
disable_button.connect("clicked", self.disable_handler, i, options)
remove_button = Gtk.Button()
remove_button.set_child(Adw.ButtonContent(icon_name="user-trash-symbolic", label=_("Remove")))
remove_button.set_child(
Adw.ButtonContent(
icon_name="user-trash-symbolic", label=_("Remove")
)
)
remove_button.add_css_class("flat")
remove_button.connect("clicked", self.remove_handler, i, options)
@@ -196,7 +276,11 @@ class RemotesWindow(Adw.Window):
options.set_child(options_box)
more.set_popover(options)
copy_button = Gtk.Button(icon_name="edit-copy-symbolic", valign=Gtk.Align.CENTER, tooltip_text=_("Copy remote name"))
copy_button = Gtk.Button(
icon_name="edit-copy-symbolic",
valign=Gtk.Align.CENTER,
tooltip_text=_("Copy remote name"),
)
copy_button.add_css_class("flat")
copy_button.connect("clicked", rowCopyHandler, name)
@@ -207,7 +291,7 @@ class RemotesWindow(Adw.Window):
if install_type == "disabled":
if not self.show_disabled_button.get_active():
continue
remote_row.set_subtitle(_("Disabled"))
enable_button.set_visible(True)
disable_button.set_visible(False)
@@ -227,23 +311,61 @@ class RemotesWindow(Adw.Window):
self.rows_in_list.append(remote_row)
self.no_remotes.set_visible(False)
except Exception as e:
print("error in remotes_window.generate_list: could not add remote. error:", e)
print(
"error in remotes_window.generate_list: could not add remote. error:",
e,
)
# Popular remotes
for i in range(len(self.rows_in_popular_list)):
self.popular_remotes_list.remove(self.rows_in_popular_list[i])
self.rows_in_popular_list = []
remotes = [
# [Name to show in GUI, Name of remote for system, Link to repo to add, Description of remote]
["AppCenter", "appcenter", "https://flatpak.elementary.io/repo.flatpakrepo", _("The open source, pay-what-you-want app store from elementary")],
["Flathub", "flathub", "https://dl.flathub.org/repo/flathub.flatpakrepo", _("Central repository of Flatpak applications")],
["Flathub beta", "flathub-beta", "https://flathub.org/beta-repo/flathub-beta.flatpakrepo", _("Beta builds of Flatpak applications")],
["Fedora", "fedora", "oci+https://registry.fedoraproject.org", _("Flatpaks packaged by Fedora Linux")],
["GNOME Nightly", "gnome-nightly", "https://nightly.gnome.org/gnome-nightly.flatpakrepo", _("The latest beta GNOME Apps and Runtimes")],
["KDE Testing Applications", "kdeapps", "https://distribute.kde.org/kdeapps.flatpakrepo", _("Beta KDE Apps and Runtimes")],
["WebKit Developer SDK", "webkit-sdk", "https://software.igalia.com/flatpak-refs/webkit-sdk.flatpakrepo", _("Central repository of the WebKit Developer and Runtime SDK")],
# [Name to show in GUI, Name of remote for system, Link to repo to add, Description of remote]
[
"AppCenter",
"appcenter",
"https://flatpak.elementary.io/repo.flatpakrepo",
_("The open source, pay-what-you-want app store from elementary"),
],
[
"Flathub",
"flathub",
"https://dl.flathub.org/repo/flathub.flatpakrepo",
_("Central repository of Flatpak applications"),
],
[
"Flathub beta",
"flathub-beta",
"https://flathub.org/beta-repo/flathub-beta.flatpakrepo",
_("Beta builds of Flatpak applications"),
],
[
"Fedora",
"fedora",
"oci+https://registry.fedoraproject.org",
_("Flatpaks packaged by Fedora Linux"),
],
[
"GNOME Nightly",
"gnome-nightly",
"https://nightly.gnome.org/gnome-nightly.flatpakrepo",
_("The latest beta GNOME Apps and Runtimes"),
],
[
"KDE Testing Applications",
"kdeapps",
"https://distribute.kde.org/kdeapps.flatpakrepo",
_("Beta KDE Apps and Runtimes"),
],
[
"WebKit Developer SDK",
"webkit-sdk",
"https://software.igalia.com/flatpak-refs/webkit-sdk.flatpakrepo",
_("Central repository of the WebKit Developer and Runtime SDK"),
],
]
host_remotes = self.my_utils.getHostRemotes()
@@ -257,9 +379,11 @@ class RemotesWindow(Adw.Window):
for i in range(len(remotes)):
if remotes[i][1] in host_remotes_names:
continue
total_added += 1
row = Adw.ActionRow(title=remotes[i][0], subtitle=(remotes[i][2]), activatable=True)
row = Adw.ActionRow(
title=remotes[i][0], subtitle=(remotes[i][2]), activatable=True
)
row.connect("activated", self.add_handler, remotes[i][1], remotes[i][2])
row.add_suffix(Gtk.Image.new_from_icon_name("right-large-symbolic"))
self.rows_in_popular_list.append(row)
@@ -275,8 +399,13 @@ class RemotesWindow(Adw.Window):
try:
subprocess.run(command, capture_output=True, check=True, env=self.new_env)
except subprocess.CalledProcessError as e:
self.toast_overlay.add_toast(Adw.Toast.new(_("Could not add {}").format(self.name_to_add)))
print("error in remotes_window.addRemoteThread: could not add remote. error:", e)
self.toast_overlay.add_toast(
Adw.Toast.new(_("Could not add {}").format(self.name_to_add))
)
print(
"error in remotes_window.addRemoteThread: could not add remote. error:",
e,
)
def on_add_response(self, _dialog, response_id, _function, row):
if response_id == "cancel":
@@ -292,9 +421,20 @@ class RemotesWindow(Adw.Window):
self.name_to_add = self.name_to_add.strip()
self.url_to_add = self.url_to_add.strip()
command = ['flatpak-spawn', '--host', 'flatpak', 'remote-add', '--if-not-exists', self.name_to_add, self.url_to_add, install_type]
command = [
"flatpak-spawn",
"--host",
"flatpak",
"remote-add",
"--if-not-exists",
self.name_to_add,
self.url_to_add,
install_type,
]
task = Gio.Task.new(None, None, self.addRemoteCallback)
task.run_in_thread(lambda _task, _obj, _data, _cancellable: self.addRemoteThread(command))
task.run_in_thread(
lambda _task, _obj, _data, _cancellable: self.addRemoteThread(command)
)
def add_handler(self, row, name="", link=""):
dialog = Adw.MessageDialog.new(self, _("Add Flatpak Remote"))
@@ -308,7 +448,7 @@ class RemotesWindow(Adw.Window):
def name_update(widget):
is_enabled = True
self.name_to_add = widget.get_text()
name_pattern = re.compile(r'^[a-zA-Z\-]+$')
name_pattern = re.compile(r"^[a-zA-Z\-]+$")
if not name_pattern.match(self.name_to_add):
is_enabled = False
@@ -325,7 +465,7 @@ class RemotesWindow(Adw.Window):
def url_update(widget):
is_enabled = True
self.url_to_add = widget.get_text()
url_pattern = re.compile(r'^[a-zA-Z0-9\-._~:/?#[\]@!$&\'()*+,;=]+$')
url_pattern = re.compile(r"^[a-zA-Z0-9\-._~:/?#[\]@!$&\'()*+,;=]+$")
if not url_pattern.match(self.url_to_add):
is_enabled = False
@@ -354,7 +494,7 @@ class RemotesWindow(Adw.Window):
info_box = Gtk.Box(orientation="vertical")
entry_list = Gtk.ListBox(selection_mode="none", margin_bottom=12)
entry_list.add_css_class("boxed-list")
name_entry = Adw.EntryRow(title=_("Name"))
name_entry.set_text(name)
name_entry.connect("changed", name_update)
@@ -370,13 +510,18 @@ class RemotesWindow(Adw.Window):
install_type_list = Gtk.ListBox(selection_mode="none")
install_type_list.add_css_class("boxed-list")
user_row = Adw.ActionRow(title=_("User"), subtitle=_("Remote will be available to only you"))
user_row = Adw.ActionRow(
title=_("User"), subtitle=_("Remote will be available to only you")
)
user_check = Gtk.CheckButton(active=True)
user_check.connect("toggled", set_user)
user_row.add_prefix(user_check)
user_row.set_activatable_widget(user_check)
system_row = Adw.ActionRow(title=_("System"), subtitle=_("Remote will be available to every user on the system"))
system_row = Adw.ActionRow(
title=_("System"),
subtitle=_("Remote will be available to every user on the system"),
)
system_check = Gtk.CheckButton()
system_row.add_prefix(system_check)
system_check.set_group(user_check)
@@ -398,11 +543,32 @@ class RemotesWindow(Adw.Window):
def addRemoteFromFileThread(self, filepath, system_or_user, name):
try:
subprocess.run(['flatpak-spawn', '--host', 'flatpak', 'remote-add', '--if-not-exists', name, filepath, f"--{system_or_user}"], capture_output=True, check=True, env=self.new_env)
self.toast_overlay.add_toast(Adw.Toast.new(_("{} successfully added").format(name)))
subprocess.run(
[
"flatpak-spawn",
"--host",
"flatpak",
"remote-add",
"--if-not-exists",
name,
filepath,
f"--{system_or_user}",
],
capture_output=True,
check=True,
env=self.new_env,
)
self.toast_overlay.add_toast(
Adw.Toast.new(_("{} successfully added").format(name))
)
except subprocess.CalledProcessError as e:
self.toast_overlay.add_toast(Adw.Toast.new(_("Could not add {}").format(self.name_to_add)))
print("error in remotes_window.addRemoteFromFileThread: could not add remote. error:", e)
self.toast_overlay.add_toast(
Adw.Toast.new(_("Could not add {}").format(self.name_to_add))
)
print(
"error in remotes_window.addRemoteFromFileThread: could not add remote. error:",
e,
)
def addRemoteFromFile(self, filepath):
def response(dialog, response, _a):
@@ -415,12 +581,16 @@ class RemotesWindow(Adw.Window):
user_or_system = "system"
task = Gio.Task.new(None, None, self.addRemoteCallback)
task.run_in_thread(lambda *_: self.addRemoteFromFileThread(filepath, user_or_system, name_row.get_text()))
task.run_in_thread(
lambda *_: self.addRemoteFromFileThread(
filepath, user_or_system, name_row.get_text()
)
)
def name_update(widget):
is_enabled = True
self.name_to_add = widget.get_text()
name_pattern = re.compile(r'^[a-zA-Z\-]+$')
name_pattern = re.compile(r"^[a-zA-Z\-]+$")
if not name_pattern.match(self.name_to_add):
is_enabled = False
@@ -436,7 +606,7 @@ class RemotesWindow(Adw.Window):
self.should_pulse = True
name = filepath.split('/')
name = filepath.split("/")
name = name[len(name) - 1]
dialog = Adw.MessageDialog.new(self, _("Add {}?").format(name))
@@ -452,8 +622,13 @@ class RemotesWindow(Adw.Window):
options_list = Gtk.ListBox(selection_mode="none", margin_top=15)
name_row = Adw.EntryRow(title=_("Name"))
name_row.connect("changed", name_update)
user_row = Adw.ActionRow(title=_("User"), subtitle=_("Remote will be available to only you"))
system_row = Adw.ActionRow(title=_("System"), subtitle=_("Remote will be available to every user on the system"))
user_row = Adw.ActionRow(
title=_("User"), subtitle=_("Remote will be available to only you")
)
system_row = Adw.ActionRow(
title=_("System"),
subtitle=_("Remote will be available to every user on the system"),
)
user_check = Gtk.CheckButton()
system_check = Gtk.CheckButton()
@@ -473,7 +648,7 @@ class RemotesWindow(Adw.Window):
user_check.set_active(True)
options_list.add_css_class("boxed-list")
Gtk.Window.present(dialog)
def file_callback(self, object, result):
try:
file = object.open_finish(result)
@@ -499,8 +674,8 @@ class RemotesWindow(Adw.Window):
self.host_remotes = []
self.host_flatpaks = []
self.app_window = main_window
self.new_env = dict( os.environ )
self.new_env['LC_ALL'] = 'C'
self.new_env = dict(os.environ)
self.new_env["LC_ALL"] = "C"
self.should_pulse = False
# Window Stuffs
@@ -514,12 +689,16 @@ class RemotesWindow(Adw.Window):
self.refresh.connect("clicked", lambda *_: self.generate_list())
self.set_transient_for(main_window)
self.add_from_file.add_suffix(Gtk.Image.new_from_icon_name("right-large-symbolic"))
self.add_from_file.add_suffix(
Gtk.Image.new_from_icon_name("right-large-symbolic")
)
self.add_from_file.connect("activated", self.addFromFileHandler)
self.custom_remote.add_suffix(Gtk.Image.new_from_icon_name("right-large-symbolic"))
self.custom_remote.add_suffix(
Gtk.Image.new_from_icon_name("right-large-symbolic")
)
self.custom_remote.connect("activated", self.add_handler)
self.show_disabled_button.connect("clicked", lambda *_: self.generate_list())
# Calls
self.generate_list()
# self.show_new_remote_options()
# self.show_new_remote_options()

View File

@@ -4,8 +4,13 @@ import subprocess
import os
import pathlib
@Gtk.Template(resource_path="/io/github/flattool/Warehouse/../data/ui/search_install.ui")
class SearchInstallWindow (Adw.Window): # TODO: stop execution of thread when search is changed
@Gtk.Template(
resource_path="/io/github/flattool/Warehouse/../data/ui/search_install.ui"
)
class SearchInstallWindow(
Adw.Window
): # TODO: stop execution of thread when search is changed
__gtype_name__ = "SearchInstallWindow"
results_list_box = Gtk.Template.Child()
@@ -25,7 +30,9 @@ class SearchInstallWindow (Adw.Window): # TODO: stop execution of thread when se
def searchResponse(self, a, b):
self.results_list_box.remove_all()
print(self.search_results)
if (self.is_debug and len(self.search_results) == 5) or (len(self.search_results) == 1 and len(self.search_results[0]) == 1): #This is unreliable with G_DEBUG
if (self.is_debug and len(self.search_results) == 5) or (
len(self.search_results) == 1 and len(self.search_results[0]) == 1
): # This is unreliable with G_DEBUG
self.main_stack.set_visible_child(self.no_results)
return
if len(self.search_results) > 50:
@@ -35,12 +42,20 @@ class SearchInstallWindow (Adw.Window): # TODO: stop execution of thread when se
for i in range(len(self.search_results)):
try:
print("creating row {}".format(str(i)))
row = Adw.ActionRow(title=GLib.markup_escape_text(self.search_results[i][0]), subtitle=self.search_results[i][2])
row = Adw.ActionRow(
title=GLib.markup_escape_text(self.search_results[i][0]),
subtitle=self.search_results[i][2],
)
print("row {} is {}".format(str(i), self.search_results[i][0]))
check = Gtk.CheckButton()
check.add_css_class("selection-mode")
check.connect("toggled", self.on_check)
label = Gtk.Label(label=self.search_results[i][3], justify=Gtk.Justification.RIGHT, wrap=True, hexpand=True)
label = Gtk.Label(
label=self.search_results[i][3],
justify=Gtk.Justification.RIGHT,
wrap=True,
hexpand=True,
)
row.add_suffix(label)
row.add_suffix(check)
row.set_activatable_widget(check)
@@ -52,11 +67,20 @@ class SearchInstallWindow (Adw.Window): # TODO: stop execution of thread when se
print(button.get_active())
def searchThread(self):
command = ["flatpak-spawn", "--host", "flatpak", "search", "--columns=all", self.to_search]
command = [
"flatpak-spawn",
"--host",
"flatpak",
"search",
"--columns=all",
self.to_search,
]
if self.remote_to_search:
command += self.remote_to_search
output = subprocess.run(command, capture_output=True, text=True, env=self.new_env).stdout
output = subprocess.run(
command, capture_output=True, text=True, env=self.new_env
).stdout
lines = output.strip().split("\n")
columns = lines[0].split("\t")
data = [columns]
@@ -83,7 +107,7 @@ class SearchInstallWindow (Adw.Window): # TODO: stop execution of thread when se
remotes_pop = Gtk.Popover()
remotes_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
# remotes_pop.set_size_request(400, 1) # why?
remotes_pop.set_child(remotes_box) # don't use ScrolledWindows in popovers!
remotes_pop.set_child(remotes_box) # don't use ScrolledWindows in popovers!
# why not just comment this code out and leave it unimplemented— it does nothing of use
for i in range(1, 3):
@@ -107,8 +131,8 @@ class SearchInstallWindow (Adw.Window): # TODO: stop execution of thread when se
self.my_utils = myUtils(self)
self.search_results = []
self.to_search = ""
self.new_env = dict( os.environ )
self.new_env['LC_ALL'] = 'C'
self.new_env = dict(os.environ)
self.new_env["LC_ALL"] = "C"
event_controller = Gtk.EventControllerKey()
event_controller.connect("key-pressed", self.key_handler)
self.cancel_button.connect("clicked", lambda *_: self.close())
@@ -130,6 +154,3 @@ class SearchInstallWindow (Adw.Window): # TODO: stop execution of thread when se
self.remote_to_search = []
self.main_stack.set_visible_child(self.blank_page)

View File

@@ -5,15 +5,18 @@ import os
import pathlib
import time
@Gtk.Template(resource_path="/io/github/flattool/Warehouse/../data/ui/snapshots.ui")
class SnapshotsWindow(Adw.Window):
__gtype_name__ = "SnapshotsWindow"
new_env = dict( os.environ )
new_env['LC_ALL'] = 'C'
new_env = dict(os.environ)
new_env["LC_ALL"] = "C"
host_home = str(pathlib.Path.home())
user_data_path = host_home + "/.var/app/"
snapshots_path = host_home + "/.var/app/io.github.flattool.Warehouse/data/Snapshots/"
snapshots_path = (
host_home + "/.var/app/io.github.flattool.Warehouse/data/Snapshots/"
)
snapshots_group = Gtk.Template.Child()
main_stack = Gtk.Template.Child()
@@ -28,7 +31,7 @@ class SnapshotsWindow(Adw.Window):
def showListOrEmpty(self):
try:
self.disconnect(self.no_close_id) # Make window able to close
self.disconnect(self.no_close_id) # Make window able to close
except:
pass
@@ -51,7 +54,7 @@ class SnapshotsWindow(Adw.Window):
snapshot_files = os.listdir(self.snapshots_of_app_path)
to_trash = []
for i in range(len(snapshot_files)):
if not snapshot_files[i].endswith(".tar.zst"):
# Find all files that aren't snapshots
@@ -81,8 +84,13 @@ class SnapshotsWindow(Adw.Window):
task = Gio.Task()
task.run_in_thread(sizeThread)
label = Gtk.Label(label=_("Version {}").format(split_file[1]), hexpand=True, wrap=True, justify=Gtk.Justification.RIGHT)
label = Gtk.Label(
label=_("Version {}").format(split_file[1]),
hexpand=True,
wrap=True,
justify=Gtk.Justification.RIGHT,
)
row.add_suffix(label)
apply = Gtk.Button(icon_name="check-plain-symbolic", valign=Gtk.Align.CENTER)
@@ -97,7 +105,7 @@ class SnapshotsWindow(Adw.Window):
self.snapshots_group.insert(row, 0)
self.main_stack.set_visible_child(self.outerbox)
self.open_folder_button.set_sensitive(True)
def trash_snapshot(self, button, file, row):
def on_response(dialog, response, func):
if response == "cancel":
@@ -109,9 +117,15 @@ class SnapshotsWindow(Adw.Window):
self.my_utils.trashFolder(self.snapshots_of_app_path)
self.showListOrEmpty()
else:
self.toast_overlay.add_toast(Adw.Toast.new(_("Could not trash snapshot")))
self.toast_overlay.add_toast(
Adw.Toast.new(_("Could not trash snapshot"))
)
dialog = Adw.MessageDialog.new(self, _("Trash Snapshot?"), _("This snapshot and its contents will be sent to the trash."))
dialog = Adw.MessageDialog.new(
self,
_("Trash Snapshot?"),
_("This snapshot and its contents will be sent to the trash."),
)
dialog.add_response("cancel", _("Cancel"))
dialog.set_close_response("cancel")
dialog.add_response("continue", _("Trash Snapshot"))
@@ -123,14 +137,25 @@ class SnapshotsWindow(Adw.Window):
epoch = int(time.time())
def thread():
response = self.my_utils.snapshotApps(epoch, [self.snapshots_of_app_path], [self.app_version], [self.app_user_data])
response = self.my_utils.snapshotApps(
epoch,
[self.snapshots_of_app_path],
[self.app_version],
[self.app_user_data],
)
if response != 0:
GLib.idle_add(self.toast_overlay.add_toast(Adw.Toast.new(_("Could not create snapshot"))))
GLib.idle_add(
self.toast_overlay.add_toast(
Adw.Toast.new(_("Could not create snapshot"))
)
)
return
if self.showListOrEmpty() == "list":
self.create_row(f"{epoch}_{self.app_version}.tar.zst")
self.no_close_id = self.connect("close-request", lambda event: True) # Make window unable to close
self.no_close_id = self.connect(
"close-request", lambda event: True
) # Make window unable to close
self.loading_label.set_label(_("Creating Snapshot…"))
self.action_bar.set_revealed(False)
self.main_stack.set_visible_child(self.loading)
@@ -140,19 +165,33 @@ class SnapshotsWindow(Adw.Window):
def apply_snapshot(self, button, file, row):
self.applied = False
def thread():
try:
subprocess.run(
['tar', '--zstd', '-xvf', f"{self.snapshots_of_app_path}{file}", "-C", f"{self.app_user_data}"],
check=True, env=self.new_env
[
"tar",
"--zstd",
"-xvf",
f"{self.snapshots_of_app_path}{file}",
"-C",
f"{self.app_user_data}",
],
check=True,
env=self.new_env,
)
self.applied = True
except subprocess.CalledProcessError as e:
print("error in snapshots_window.apply_snapshot.thread: CalledProcessError:", e)
print(
"error in snapshots_window.apply_snapshot.thread: CalledProcessError:",
e,
)
def callback():
if not self.applied:
self.toast_overlay.add_toast(Adw.Toast.new(_("Could not apply snapshot")))
self.toast_overlay.add_toast(
Adw.Toast.new(_("Could not apply snapshot"))
)
else:
self.toast_overlay.add_toast(Adw.Toast.new(_("Snapshot applied")))
self.parent_window.refresh_list_of_flatpaks(self, False)
@@ -168,23 +207,35 @@ class SnapshotsWindow(Adw.Window):
if os.path.exists(to_trash):
a = self.my_utils.trashFolder(to_trash)
if a != 0:
self.toast_overlay.add_toast(Adw.Toast.new(_("Could not apply snapshot")))
self.toast_overlay.add_toast(
Adw.Toast.new(_("Could not apply snapshot"))
)
return
data = Gio.File.new_for_path(self.app_user_data)
data.make_directory()
if not os.path.exists(data.get_path()):
self.toast_overlay.add_toast(Adw.Toast.new(_("Could not apply snapshot")))
self.toast_overlay.add_toast(
Adw.Toast.new(_("Could not apply snapshot"))
)
return
self.no_close_id = self.connect("close-request", lambda event: True) # Make window unable to close
self.no_close_id = self.connect(
"close-request", lambda event: True
) # Make window unable to close
self.loading_label.set_label(_("Applying Snapshot…"))
self.action_bar.set_revealed(False)
self.main_stack.set_visible_child(self.loading)
task = Gio.Task.new(None, None, lambda *_: callback())
task.run_in_thread(lambda *_: thread())
dialog = Adw.MessageDialog.new(self, _("Apply Snapshot?"), _("Applying this snapshot will trash any current user data for {}.").format(self.app_name))
dialog = Adw.MessageDialog.new(
self,
_("Apply Snapshot?"),
_("Applying this snapshot will trash any current user data for {}.").format(
self.app_name
),
)
dialog.add_response("cancel", _("Cancel"))
dialog.set_close_response("cancel")
dialog.add_response("continue", _("Apply Snapshot"))
@@ -215,7 +266,11 @@ class SnapshotsWindow(Adw.Window):
self.app_user_data = self.user_data_path + self.app_id + "/"
self.parent_window = parent_window
if self.app_version == "" or self.app_version == "-" or self.app_version == None:
if (
self.app_version == ""
or self.app_version == "-"
or self.app_version == None
):
self.app_version = 0.0
if not os.path.exists(self.snapshots_path):
@@ -225,9 +280,11 @@ class SnapshotsWindow(Adw.Window):
# Calls
self.generateList()
self.open_folder_button.connect("clicked", self.open_button_handler, self.snapshots_of_app_path)
self.open_folder_button.connect(
"clicked", self.open_button_handler, self.snapshots_of_app_path
)
self.new_snapshot.connect("clicked", lambda *_: self.createSnapshot())
event_controller = Gtk.EventControllerKey()
event_controller.connect("key-pressed", self.key_handler)
self.add_controller(event_controller)
@@ -235,4 +292,4 @@ class SnapshotsWindow(Adw.Window):
# Window stuffs
self.set_title(_("{} Snapshots").format(self.app_name))
self.set_transient_for(parent_window)
self.set_size_request(0, 230)
self.set_size_request(0, 230)

View File

@@ -33,6 +33,7 @@ from .const import Config
from .app_row_widget import AppRow
@Gtk.Template(resource_path="/io/github/flattool/Warehouse/../data/ui/window.ui")
class WarehouseWindow(Adw.ApplicationWindow):
__gtype_name__ = "WarehouseWindow"
@@ -86,7 +87,9 @@ class WarehouseWindow(Adw.ApplicationWindow):
total_selected = 0
def filter_func(self, row):
if (self.search_entry.get_text().lower() in row.get_title().lower()) or (self.search_entry.get_text().lower() in row.get_subtitle().lower()):
if (self.search_entry.get_text().lower() in row.get_title().lower()) or (
self.search_entry.get_text().lower() in row.get_subtitle().lower()
):
self.is_result = True
return True
@@ -114,10 +117,14 @@ class WarehouseWindow(Adw.ApplicationWindow):
self.refresh_list_of_flatpaks(self, False)
self.toast_overlay.add_toast(Adw.Toast.new(_("Uninstalled successfully")))
else:
self.toast_overlay.add_toast(Adw.Toast.new(_("Could not uninstall some apps")))
self.toast_overlay.add_toast(
Adw.Toast.new(_("Could not uninstall some apps"))
)
def uninstallFlatpakThread(self, ref_arr, id_arr, type_arr, should_trash):
self.my_utils.uninstallFlatpak(ref_arr, type_arr, should_trash, self.main_progress_bar)
self.my_utils.uninstallFlatpak(
ref_arr, type_arr, should_trash, self.main_progress_bar
)
def uninstallFlatpak(self, should_trash):
ref_arr = []
@@ -125,7 +132,7 @@ class WarehouseWindow(Adw.ApplicationWindow):
type_arr = []
self.currently_uninstalling = True
i = 0
while(self.flatpaks_list_box.get_row_at_index(i) != None):
while self.flatpaks_list_box.get_row_at_index(i) != None:
current = self.flatpaks_list_box.get_row_at_index(i)
if current.tickbox.get_active() == True:
ref_arr.append(current.app_ref)
@@ -134,14 +141,18 @@ class WarehouseWindow(Adw.ApplicationWindow):
i += 1
self.set_title(self.main_window_title)
task = Gio.Task.new(None, None, self.uninstallFlatpakCallback)
task.run_in_thread(lambda _task, _obj, _data, _cancellable, ref_arr=ref_arr, id_arr=id_arr, type_arr=type_arr, should_trash=should_trash: self.uninstallFlatpakThread(ref_arr, id_arr, type_arr, should_trash))
task.run_in_thread(
lambda _task, _obj, _data, _cancellable, ref_arr=ref_arr, id_arr=id_arr, type_arr=type_arr, should_trash=should_trash: self.uninstallFlatpakThread(
ref_arr, id_arr, type_arr, should_trash
)
)
def batchUninstallButtonHandler(self, _widget):
has_user_data = False
def batchUninstallResponse(_idk, response_id, _widget):
if response_id == "cancel":
return 1
return 1
try:
should_trash = trash_check.get_active()
@@ -150,32 +161,48 @@ class WarehouseWindow(Adw.ApplicationWindow):
self.uninstallButtonsEnable(False)
self.no_close = self.connect("close-request", lambda event: True) # Make window unable to close
self.no_close = self.connect(
"close-request", lambda event: True
) # Make window unable to close
self.main_stack.set_visible_child(self.uninstalling)
self.search_button.set_sensitive(False)
self.uninstallFlatpak(should_trash)
# Create Widgets
dialog = Adw.MessageDialog.new(self, _("Uninstall Selected Apps?"), _("It will not be possible to use these apps after removal."))
dialog = Adw.MessageDialog.new(
self,
_("Uninstall Selected Apps?"),
_("It will not be possible to use these apps after removal."),
)
# Check to see if at least one app in the list has user data
i = 0
while(True):
while True:
current = self.flatpaks_list_box.get_row_at_index(i)
i += 1
if(current == None):
if current == None:
break
if current.tickbox.get_active() and os.path.exists(f"{self.user_data_path}{current.app_id}"):
if current.tickbox.get_active() and os.path.exists(
f"{self.user_data_path}{current.app_id}"
):
has_user_data = True
break
if has_user_data:
# Create Widgets
options_box = Gtk.Box(orientation="vertical")
header = Gtk.Label(label=_("App Settings & Data"), halign="start", margin_top=10)
header = Gtk.Label(
label=_("App Settings & Data"), halign="start", margin_top=10
)
options_list = Gtk.ListBox(selection_mode="none", margin_top=15)
keep_data = Adw.ActionRow(title=_("Keep"), subtitle=_("Allow restoring these apps' settings and content"))
trash_data = Adw.ActionRow(title=_("Trash"), subtitle=_("Send these apps' settings and content to the trash"))
keep_data = Adw.ActionRow(
title=_("Keep"),
subtitle=_("Allow restoring these apps' settings and content"),
)
trash_data = Adw.ActionRow(
title=_("Trash"),
subtitle=_("Send these apps' settings and content to the trash"),
)
keep_check = Gtk.CheckButton()
trash_check = Gtk.CheckButton()
@@ -209,7 +236,9 @@ class WarehouseWindow(Adw.ApplicationWindow):
def uninstallButtonHandler(self, row, name, ref, id):
if self.currently_uninstalling:
self.toast_overlay.add_toast(Adw.Toast.new(_("Cannot uninstall while already uninstalling")))
self.toast_overlay.add_toast(
Adw.Toast.new(_("Cannot uninstall while already uninstalling"))
)
return
def uninstallResponse(_idk, response_id, _widget):
@@ -226,7 +255,9 @@ class WarehouseWindow(Adw.ApplicationWindow):
self.uninstallButtonsEnable(False)
self.no_close = self.connect("close-request", lambda event: True) # Make window unable to close
self.no_close = self.connect(
"close-request", lambda event: True
) # Make window unable to close
self.main_stack.set_visible_child(self.uninstalling)
self.search_button.set_sensitive(False)
self.uninstallFlatpak(should_trash)
@@ -234,15 +265,27 @@ class WarehouseWindow(Adw.ApplicationWindow):
row.tickbox.set_active(True)
# Create Widgets
dialog = Adw.MessageDialog.new(self, _("Uninstall {}?").format(name), _("It will not be possible to use {} after removal.").format(name))
dialog = Adw.MessageDialog.new(
self,
_("Uninstall {}?").format(name),
_("It will not be possible to use {} after removal.").format(name),
)
if os.path.exists(f"{self.user_data_path}{id}"):
# Create Widgets for Trash
options_box = Gtk.Box(orientation="vertical")
header = Gtk.Label(label=_("App Settings & Data"), halign="start", margin_top=10)
header = Gtk.Label(
label=_("App Settings & Data"), halign="start", margin_top=10
)
options_list = Gtk.ListBox(selection_mode="none", margin_top=15)
keep_data = Adw.ActionRow(title=_("Keep"), subtitle=_("Allow restoring this app's settings and content"))
trash_data = Adw.ActionRow(title=_("Trash"), subtitle=_("Send this app's settings and content to the trash"))
keep_data = Adw.ActionRow(
title=_("Keep"),
subtitle=_("Allow restoring this app's settings and content"),
)
trash_data = Adw.ActionRow(
title=_("Trash"),
subtitle=_("Send this app's settings and content to the trash"),
)
keep_check = Gtk.CheckButton(active=True)
trash_check = Gtk.CheckButton()
@@ -283,7 +326,7 @@ class WarehouseWindow(Adw.ApplicationWindow):
def create_row(self, index):
row = AppRow(self, self.host_flatpaks, index)
self.flatpaks_list_box.insert(row, index)
def generate_list_of_flatpaks(self):
self.host_flatpaks = self.my_utils.getHostFlatpaks()
self.dependent_runtimes = self.my_utils.getDependentRuntimes()
@@ -328,14 +371,20 @@ class WarehouseWindow(Adw.ApplicationWindow):
return
result = self.my_utils.trashFolder(f"{self.user_data_path}{id}")
if result != 0:
self.toast_overlay.add_toast(Adw.Toast.new(_("Could not trash user data")))
self.toast_overlay.add_toast(
Adw.Toast.new(_("Could not trash user data"))
)
return
self.lookup_action(f"open-data{index}").set_enabled(False)
self.lookup_action(f"trash{index}").set_enabled(False)
self.toast_overlay.add_toast(Adw.Toast.new(_("Trashed user data")))
dialog = Adw.MessageDialog.new(self,_("Send {}'s User Data to the Trash?").format(name))
dialog.set_body(_("Your files and data for this app will be sent to the trash."))
dialog = Adw.MessageDialog.new(
self, _("Send {}'s User Data to the Trash?").format(name)
)
dialog.set_body(
_("Your files and data for this app will be sent to the trash.")
)
dialog.add_response("cancel", _("Cancel"))
dialog.set_close_response("cancel")
dialog.add_response("continue", _("Trash Data"))
@@ -344,12 +393,18 @@ class WarehouseWindow(Adw.ApplicationWindow):
dialog.present()
def maskFlatpak(self, row):
is_masked = row.mask_label.get_visible() # Check the visibility of the mask label to see if the flatpak is masked
is_masked = (
row.mask_label.get_visible()
) # Check the visibility of the mask label to see if the flatpak is masked
result = []
def callback():
if result[0] == 1:
self.toast_overlay.add_toast(Adw.Toast.new(_("Could not disable updates for {}").format(row.app_name)))
self.toast_overlay.add_toast(
Adw.Toast.new(
_("Could not disable updates for {}").format(row.app_name)
)
)
return
row.set_masked(not is_masked)
self.lookup_action(f"mask{row.index}").set_enabled(is_masked)
@@ -359,13 +414,23 @@ class WarehouseWindow(Adw.ApplicationWindow):
if response == "cancel":
return
task = Gio.Task.new(None, None, lambda *_: callback())
task.run_in_thread(lambda *_: result.append(self.my_utils.maskFlatpak(row.app_id, row.install_type, is_masked)))
task.run_in_thread(
lambda *_: result.append(
self.my_utils.maskFlatpak(row.app_id, row.install_type, is_masked)
)
)
if is_masked:
onContinue(self, None)
else:
dialog = Adw.MessageDialog.new(self, _("Disable Updates for {}?").format(row.app_name))
dialog.set_body(_("This will mask {} ensuring it will never recieve any feature or security updates.").format(row.app_name))
dialog = Adw.MessageDialog.new(
self, _("Disable Updates for {}?").format(row.app_name)
)
dialog.set_body(
_(
"This will mask {} ensuring it will never recieve any feature or security updates."
).format(row.app_name)
)
dialog.add_response("cancel", _("Cancel"))
dialog.set_close_response("cancel")
dialog.add_response("continue", _("Disable Updates"))
@@ -380,10 +445,12 @@ class WarehouseWindow(Adw.ApplicationWindow):
def runCallback(self, _a, _b):
if not self.my_utils.run_app_error:
return
error = self.my_utils.run_app_error_message
dialog = Adw.MessageDialog.new(self, _("Could not Run App"), error)
copy_button = Gtk.Button(label=_("Copy"), halign=Gtk.Align.CENTER, margin_top=12)
copy_button = Gtk.Button(
label=_("Copy"), halign=Gtk.Align.CENTER, margin_top=12
)
copy_button.add_css_class("pill")
copy_button.add_css_class("suggested-action")
copy_button.connect("clicked", lambda *_: self.clipboard.set(error))
@@ -402,7 +469,7 @@ class WarehouseWindow(Adw.ApplicationWindow):
def batch_mode_handler(self, widget):
batch_mode = widget.get_active()
i = 0
while(self.flatpaks_list_box.get_row_at_index(i) != None):
while self.flatpaks_list_box.get_row_at_index(i) != None:
current = self.flatpaks_list_box.get_row_at_index(i)
current.set_selectable(batch_mode)
i += 1
@@ -428,7 +495,7 @@ class WarehouseWindow(Adw.ApplicationWindow):
return
i = 0
trashReturnCodes = 0
while(True):
while True:
current = self.flatpaks_list_box.get_row_at_index(i)
i += 1
if current == None:
@@ -437,16 +504,30 @@ class WarehouseWindow(Adw.ApplicationWindow):
continue
trash = self.my_utils.trashFolder(f"{self.user_data_path}{current.app_id}")
if trash == 1:
self.toast_overlay.add_toast(Adw.Toast.new(_("{} has no data to trash").format(current.app_name)))
self.toast_overlay.add_toast(
Adw.Toast.new(_("{} has no data to trash").format(current.app_name))
)
continue
if trash == 2:
self.toast_overlay.add_toast(Adw.Toast.new(_("Could not trash {}'s data").format(current.app_name)))
self.toast_overlay.add_toast(
Adw.Toast.new(
_("Could not trash {}'s data").format(current.app_name)
)
)
continue
self.lookup_action(f"open-data{current.index}").set_enabled(False) # Disable the Open User Data dropdown option when the data was deleted
self.lookup_action(f"trash{current.index}").set_enabled(False) # Disable the Trash User Data dropdown option when the data was deleted
self.lookup_action(f"open-data{current.index}").set_enabled(
False
) # Disable the Open User Data dropdown option when the data was deleted
self.lookup_action(f"trash{current.index}").set_enabled(
False
) # Disable the Trash User Data dropdown option when the data was deleted
def batchCleanHandler(self, widget):
dialog = Adw.MessageDialog.new(self, _("Trash Selected Apps' User Data?"), _("Your files and data for these apps will be sent to the trash."))
dialog = Adw.MessageDialog.new(
self,
_("Trash Selected Apps' User Data?"),
_("Your files and data for these apps will be sent to the trash."),
)
dialog.set_close_response("cancel")
dialog.add_response("cancel", _("Cancel"))
dialog.add_response("continue", _("Trash Data"))
@@ -458,18 +539,22 @@ class WarehouseWindow(Adw.ApplicationWindow):
self.set_select_all(widget.get_active())
def batchSnapshotHandler(self, widget):
def batchSnapshotResponse(dialog, response, _a):
if response == "cancel":
return
i = 0
snapshots_path = self.host_home + "/.var/app/io.github.flattool.Warehouse/data/Snapshots/"
snapshots_path = (
self.host_home
+ "/.var/app/io.github.flattool.Warehouse/data/Snapshots/"
)
snapshot_arr = []
app_ver_arr = []
app_data_arr = []
epoch = int(time.time())
self.no_close = self.connect("close-request", lambda event: True) # Make window unable to close
while(self.flatpaks_list_box.get_row_at_index(i) != None):
self.no_close = self.connect(
"close-request", lambda event: True
) # Make window unable to close
while self.flatpaks_list_box.get_row_at_index(i) != None:
current = self.flatpaks_list_box.get_row_at_index(i)
i += 1
if current.tickbox.get_active() == False:
@@ -479,11 +564,21 @@ class WarehouseWindow(Adw.ApplicationWindow):
snapshot_arr.append(snapshots_path + current.app_id + "/")
app_ver_arr.append(current.app_version)
app_data_arr.append(f"{self.user_data_path}{current.app_id}")
def thread():
capture = self.my_utils.snapshotApps(epoch, snapshot_arr, app_ver_arr, app_data_arr, self.main_progress_bar)
capture = self.my_utils.snapshotApps(
epoch,
snapshot_arr,
app_ver_arr,
app_data_arr,
self.main_progress_bar,
)
if capture != 0:
GLib.idle_add(lambda *_: self.toast_overlay.add_toast(Adw.Toast.new(_("Could not snapshot some apps"))))
GLib.idle_add(
lambda *_: self.toast_overlay.add_toast(
Adw.Toast.new(_("Could not snapshot some apps"))
)
)
def callback(*args):
self.main_stack.set_visible_child(self.main_box)
@@ -503,17 +598,22 @@ class WarehouseWindow(Adw.ApplicationWindow):
task = Gio.Task.new(None, None, callback)
task.run_in_thread(lambda *_: thread())
dialog = Adw.MessageDialog.new(self, _("Create Snapshots?"), _("Snapshots are backups of the app's user data. They can be reapplied at any time. This could take a while."))
dialog = Adw.MessageDialog.new(
self,
_("Create Snapshots?"),
_(
"Snapshots are backups of the app's user data. They can be reapplied at any time. This could take a while."
),
)
dialog.set_close_response("cancel")
dialog.add_response("cancel", _("Cancel"))
dialog.add_response("continue", _("Create Snapshots"))
dialog.connect("response", batchSnapshotResponse, dialog.choose_finish)
Gtk.Window.present(dialog)
def set_select_all(self, should_select_all):
i = 0
while(self.flatpaks_list_box.get_row_at_index(i) != None):
while self.flatpaks_list_box.get_row_at_index(i) != None:
current = self.flatpaks_list_box.get_row_at_index(i)
if current.get_visible() == True:
current.tickbox.set_active(should_select_all)
@@ -551,10 +651,10 @@ class WarehouseWindow(Adw.ApplicationWindow):
def copyNames(self, widget, _a):
to_copy = ""
i = 0
while(True):
while True:
current = self.flatpaks_list_box.get_row_at_index(i)
i += 1
if(current == None):
if current == None:
break
if current.tickbox.get_active():
to_copy += f"{current.app_name}\n"
@@ -564,10 +664,10 @@ class WarehouseWindow(Adw.ApplicationWindow):
def copyIDs(self, widget, _a):
to_copy = ""
i = 0
while(True):
while True:
current = self.flatpaks_list_box.get_row_at_index(i)
i += 1
if(current == None):
if current == None:
break
if current.tickbox.get_active():
to_copy += f"{current.app_id}\n"
@@ -577,10 +677,10 @@ class WarehouseWindow(Adw.ApplicationWindow):
def copyRefs(self, widget, _a):
to_copy = ""
i = 0
while(True):
while True:
current = self.flatpaks_list_box.get_row_at_index(i)
i += 1
if(current == None):
if current == None:
break
if current.tickbox.get_active():
to_copy += f"{current.app_ref}\n"
@@ -614,34 +714,34 @@ class WarehouseWindow(Adw.ApplicationWindow):
self.set_select_all(False)
index = 0
while(self.flatpaks_list_box.get_row_at_index(index) != None):
while self.flatpaks_list_box.get_row_at_index(index) != None:
current = self.flatpaks_list_box.get_row_at_index(index)
visible = True
if show_apps == False and current.is_runtime == False:
visible = False
if show_runtimes == False and current.is_runtime == True:
visible = False
if (not 'all' in filter_install_type):
if not "all" in filter_install_type:
if not current.install_type in filter_install_type:
visible = False
if (not 'all' in filter_remotes_list):
if not "all" in filter_remotes_list:
if not current.origin_remote in filter_remotes_list:
visible = False
if (not 'all' in filter_runtimes_list):
if not "all" in filter_runtimes_list:
if not current.dependent_runtime in filter_runtimes_list:
visible = False
if visible == True:
total_visible += 1
current.set_visible(visible)
index += 1
if total_visible == 0:
self.main_stack.set_visible_child(self.no_matches)
self.search_button.set_sensitive(False)
@@ -659,7 +759,9 @@ class WarehouseWindow(Adw.ApplicationWindow):
self.toast_overlay.add_toast(Adw.Toast.new(_("Could not install app")))
def installThread(self, filepath, user_or_system):
self.my_utils.installFlatpak([filepath], None, user_or_system, self.main_progress_bar)
self.my_utils.installFlatpak(
[filepath], None, user_or_system, self.main_progress_bar
)
def install_file(self, filepath):
def response(dialog, response, _a):
@@ -675,7 +777,7 @@ class WarehouseWindow(Adw.ApplicationWindow):
task = Gio.Task.new(None, None, self.installCallback)
task.run_in_thread(lambda *_: self.installThread(filepath, user_or_system))
name = filepath.split('/')
name = filepath.split("/")
name = name[len(name) - 1]
dialog = Adw.MessageDialog.new(self, _("Install {}?").format(name))
@@ -688,8 +790,13 @@ class WarehouseWindow(Adw.ApplicationWindow):
# Create Widgets
options_box = Gtk.Box(orientation="vertical")
options_list = Gtk.ListBox(selection_mode="none", margin_top=15)
user_row = Adw.ActionRow(title=_("User"), subtitle=_("The app will be available to only you"))
system_row = Adw.ActionRow(title=_("System"), subtitle=_("The app will be available to every user on the system"))
user_row = Adw.ActionRow(
title=_("User"), subtitle=_("The app will be available to only you")
)
system_row = Adw.ActionRow(
title=_("System"),
subtitle=_("The app will be available to every user on the system"),
)
user_check = Gtk.CheckButton()
system_check = Gtk.CheckButton()
@@ -751,23 +858,33 @@ class WarehouseWindow(Adw.ApplicationWindow):
self.filter_list = [True, False, ["all"], ["all"], ["all"]]
self.set_size_request(0, 230)
self.settings = Gio.Settings.new("io.github.flattool.Warehouse")
self.settings.bind("window-width", self, "default-width", Gio.SettingsBindFlags.DEFAULT)
self.settings.bind("window-height", self, "default-height", Gio.SettingsBindFlags.DEFAULT)
self.settings.bind("is-maximized", self, "maximized", Gio.SettingsBindFlags.DEFAULT)
self.settings.bind("is-fullscreen", self, "fullscreened", Gio.SettingsBindFlags.DEFAULT)
self.settings.bind(
"window-width", self, "default-width", Gio.SettingsBindFlags.DEFAULT
)
self.settings.bind(
"window-height", self, "default-height", Gio.SettingsBindFlags.DEFAULT
)
self.settings.bind(
"is-maximized", self, "maximized", Gio.SettingsBindFlags.DEFAULT
)
self.settings.bind(
"is-fullscreen", self, "fullscreened", Gio.SettingsBindFlags.DEFAULT
)
self.new_env = dict( os.environ )
self.new_env['LC_ALL'] = 'C'
self.new_env = dict(os.environ)
self.new_env["LC_ALL"] = "C"
if self.host_flatpaks == [['', '']]:
if self.host_flatpaks == [["", ""]]:
self.windowSetEmpty(True)
return
self.flatpaks_list_box.set_filter_func(self.filter_func)
task = Gio.Task()
task.run_in_thread(lambda *_: GLib.idle_add(lambda *_: self.generate_list_of_flatpaks()))
task.run_in_thread(
lambda *_: GLib.idle_add(lambda *_: self.generate_list_of_flatpaks())
)
self.search_entry.connect("search-changed", self.on_invalidate)
self.search_bar.connect_entry(self.search_entry)
self.search_bar.connect("notify", self.on_change)
@@ -775,7 +892,9 @@ class WarehouseWindow(Adw.ApplicationWindow):
self.batch_mode_button.connect("toggled", self.batch_mode_handler)
self.batch_clean_button.connect("clicked", self.batchCleanHandler)
self.batch_uninstall_button.connect("clicked", self.batchUninstallButtonHandler)
self.batch_select_all_button.connect("clicked", self.batchSelectAllButtonHandler)
self.batch_select_all_button.connect(
"clicked", self.batchSelectAllButtonHandler
)
self.batch_snapshot_button.connect("clicked", self.batchSnapshotHandler)
self.batchActionsEnable(False)
event_controller = Gtk.EventControllerKey()
@@ -796,4 +915,3 @@ class WarehouseWindow(Adw.ApplicationWindow):
if Config.DEVEL:
self.add_css_class("devel")