mirror of
https://github.com/morgan9e/warehouse
synced 2026-04-14 00:04:08 +09:00
Remove trailing whitespace
This commit is contained in:
2
.github/workflows/flatpak-builder.yml
vendored
2
.github/workflows/flatpak-builder.yml
vendored
@@ -18,4 +18,4 @@ jobs:
|
|||||||
uses: flatpak/flatpak-github-actions/flatpak-builder@v6.1
|
uses: flatpak/flatpak-github-actions/flatpak-builder@v6.1
|
||||||
with:
|
with:
|
||||||
bundle: io.github.flattool.Warehouse.flatpak
|
bundle: io.github.flattool.Warehouse.flatpak
|
||||||
manifest-path: io.github.flattool.Warehouse.json
|
manifest-path: io.github.flattool.Warehouse.json
|
||||||
|
|||||||
1
COPYING
1
COPYING
@@ -672,4 +672,3 @@ may consider it more useful to permit linking proprietary applications with
|
|||||||
the library. If this is what you want to do, use the GNU Lesser General
|
the library. If this is what you want to do, use the GNU Lesser General
|
||||||
Public License instead of this License. But first, please read
|
Public License instead of this License. But first, please read
|
||||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
||||||
|
|
||||||
|
|||||||
@@ -139,7 +139,7 @@
|
|||||||
<li>Update to GNOME 46 GTK Technologies</li>
|
<li>Update to GNOME 46 GTK Technologies</li>
|
||||||
<li>Updated translations</li>
|
<li>Updated translations</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>Previous Releases's Bug Fixes</p>
|
<p>Previous Releases's Bug Fixes</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>When an attempt to run an app fails, correct runtime error information is shown</li>
|
<li>When an attempt to run an app fails, correct runtime error information is shown</li>
|
||||||
<li>Install From The Web no longer behaves incorrectly on remote installations with options</li>
|
<li>Install From The Web no longer behaves incorrectly on remote installations with options</li>
|
||||||
@@ -158,7 +158,7 @@
|
|||||||
<li>Update to GNOME 46 GTK Technologies</li>
|
<li>Update to GNOME 46 GTK Technologies</li>
|
||||||
<li>Updated translations</li>
|
<li>Updated translations</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>Bug Fixes</p>
|
<p>Bug Fixes</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>When an attempt to run an app fails, correct runtime error information is shown</li>
|
<li>When an attempt to run an app fails, correct runtime error information is shown</li>
|
||||||
<li>Install From The Web no longer behaves incorrectly on remote installations with options</li>
|
<li>Install From The Web no longer behaves incorrectly on remote installations with options</li>
|
||||||
@@ -168,7 +168,7 @@
|
|||||||
</release>
|
</release>
|
||||||
<release version="1.5.1" date="2024-3-8" timestamp="1709921475">
|
<release version="1.5.1" date="2024-3-8" timestamp="1709921475">
|
||||||
<description translate="no">
|
<description translate="no">
|
||||||
<p>Bug Fixes</p>
|
<p>Bug Fixes</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Main list is no longer scrolled to the bottom on launch</li>
|
<li>Main list is no longer scrolled to the bottom on launch</li>
|
||||||
<li>Leftover Data window no longer tries to use a different window for toast messages</li>
|
<li>Leftover Data window no longer tries to use a different window for toast messages</li>
|
||||||
@@ -189,7 +189,7 @@
|
|||||||
<li>Period, 0 to 9, and underscores are now allowed in new Custom Remote names</li>
|
<li>Period, 0 to 9, and underscores are now allowed in new Custom Remote names</li>
|
||||||
<li>Updated translations</li>
|
<li>Updated translations</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>Bug Fixes</p>
|
<p>Bug Fixes</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Hide Show Disabled Remotes button when there aren't any</li>
|
<li>Hide Show Disabled Remotes button when there aren't any</li>
|
||||||
<li>Fix Batch Snapshots accidentally triggering Select All</li>
|
<li>Fix Batch Snapshots accidentally triggering Select All</li>
|
||||||
|
|||||||
@@ -18,21 +18,21 @@ class ChangeVersionPage(Adw.NavigationPage):
|
|||||||
versions_group = gtc()
|
versions_group = gtc()
|
||||||
action_bar = gtc()
|
action_bar = gtc()
|
||||||
apply_button = gtc()
|
apply_button = gtc()
|
||||||
|
|
||||||
selected_commit = None
|
selected_commit = None
|
||||||
failure = None
|
failure = None
|
||||||
|
|
||||||
def get_commits(self, *args):
|
def get_commits(self, *args):
|
||||||
cmd = ['flatpak-spawn', '--host', 'sh', '-c']
|
cmd = ['flatpak-spawn', '--host', 'sh', '-c']
|
||||||
script = f"LC_ALL=C flatpak remote-info --log {self.package.info['origin']} {self.package.info['ref']} "
|
script = f"LC_ALL=C flatpak remote-info --log {self.package.info['origin']} {self.package.info['ref']} "
|
||||||
installation = self.package.info["installation"]
|
installation = self.package.info["installation"]
|
||||||
if installation == "user" or installation == "system":
|
if installation == "user" or installation == "system":
|
||||||
script += f"--{installation}"
|
script += f"--{installation}"
|
||||||
else:
|
else:
|
||||||
script += f"--installation={installation}"
|
script += f"--installation={installation}"
|
||||||
|
|
||||||
cmd.append(script)
|
cmd.append(script)
|
||||||
|
|
||||||
commits = []
|
commits = []
|
||||||
changes = []
|
changes = []
|
||||||
dates = []
|
dates = []
|
||||||
@@ -55,11 +55,11 @@ class ChangeVersionPage(Adw.NavigationPage):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.failure = str(e)
|
self.failure = str(e)
|
||||||
return
|
return
|
||||||
|
|
||||||
if not (len(commits) == len(changes) == len(dates)):
|
if not (len(commits) == len(changes) == len(dates)):
|
||||||
self.failure = "Commits, Changes, and Dates are not of equivalent length"
|
self.failure = "Commits, Changes, and Dates are not of equivalent length"
|
||||||
return
|
return
|
||||||
|
|
||||||
def idle(*args):
|
def idle(*args):
|
||||||
for index, commit in enumerate(commits):
|
for index, commit in enumerate(commits):
|
||||||
row = Adw.ActionRow(title=GLib.markup_escape_text(changes[index]), subtitle=f"{GLib.markup_escape_text(commit)}\n{GLib.markup_escape_text(dates[index])}")
|
row = Adw.ActionRow(title=GLib.markup_escape_text(changes[index]), subtitle=f"{GLib.markup_escape_text(commit)}\n{GLib.markup_escape_text(dates[index])}")
|
||||||
@@ -73,28 +73,28 @@ class ChangeVersionPage(Adw.NavigationPage):
|
|||||||
check.set_group(self.root_group_check_button)
|
check.set_group(self.root_group_check_button)
|
||||||
row.set_activatable_widget(check)
|
row.set_activatable_widget(check)
|
||||||
row.add_prefix(check)
|
row.add_prefix(check)
|
||||||
|
|
||||||
self.versions_group.add(row)
|
self.versions_group.add(row)
|
||||||
|
|
||||||
GLib.idle_add(idle)
|
GLib.idle_add(idle)
|
||||||
|
|
||||||
def set_commit(self, commit):
|
def set_commit(self, commit):
|
||||||
self.selected_commit = commit
|
self.selected_commit = commit
|
||||||
|
|
||||||
def get_commits_callback(self, *args):
|
def get_commits_callback(self, *args):
|
||||||
if not self.failure is None:
|
if not self.failure is None:
|
||||||
self.toast_overlay.add_toast(ErrorToast(_("Could not get versions"), self.failure).toast)
|
self.toast_overlay.add_toast(ErrorToast(_("Could not get versions"), self.failure).toast)
|
||||||
else:
|
else:
|
||||||
self.scrolled_window.set_child(self.versions_clamp)
|
self.scrolled_window.set_child(self.versions_clamp)
|
||||||
|
|
||||||
def callback(self, did_error):
|
def callback(self, did_error):
|
||||||
HostInfo.main_window.refresh_handler()
|
HostInfo.main_window.refresh_handler()
|
||||||
if not did_error:
|
if not did_error:
|
||||||
HostInfo.main_window.toast_overlay.add_toast(Adw.Toast(title=_("Changed {}'s Version").format(self.package.info['name'])))
|
HostInfo.main_window.toast_overlay.add_toast(Adw.Toast(title=_("Changed {}'s Version").format(self.package.info['name'])))
|
||||||
|
|
||||||
def error_callback(self, user_facing_label, error_message):
|
def error_callback(self, user_facing_label, error_message):
|
||||||
HostInfo.main_window.toast_overlay.add_toast(ErrorToast(user_facing_label, error_message).toast)
|
HostInfo.main_window.toast_overlay.add_toast(ErrorToast(user_facing_label, error_message).toast)
|
||||||
|
|
||||||
def on_apply(self, *args):
|
def on_apply(self, *args):
|
||||||
if ChangeVersionWorker.change_version(
|
if ChangeVersionWorker.change_version(
|
||||||
self.mask_row.get_active(),
|
self.mask_row.get_active(),
|
||||||
@@ -104,21 +104,21 @@ class ChangeVersionPage(Adw.NavigationPage):
|
|||||||
self.error_callback,
|
self.error_callback,
|
||||||
):
|
):
|
||||||
self.packages_page.set_status(self.packages_page.changing_version)
|
self.packages_page.set_status(self.packages_page.changing_version)
|
||||||
|
|
||||||
def __init__(self, packages_page, package, **kwargs):
|
def __init__(self, packages_page, package, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
# Extra Object Creation
|
# Extra Object Creation
|
||||||
self.packages_page = packages_page
|
self.packages_page = packages_page
|
||||||
self.package = package
|
self.package = package
|
||||||
|
|
||||||
# Apply
|
# Apply
|
||||||
pkg_name = package.info["name"]
|
pkg_name = package.info["name"]
|
||||||
self.set_title(_("{} Versions").format(pkg_name))
|
self.set_title(_("{} Versions").format(pkg_name))
|
||||||
self.mask_row.set_subtitle(_("Ensure that {} will never be updated to a newer version").format(pkg_name))
|
self.mask_row.set_subtitle(_("Ensure that {} will never be updated to a newer version").format(pkg_name))
|
||||||
self.scrolled_window.set_child(LoadingStatus(_("Fetching Releases"), _("This could take a while")))
|
self.scrolled_window.set_child(LoadingStatus(_("Fetching Releases"), _("This could take a while")))
|
||||||
Gio.Task.new(None, None, self.get_commits_callback).run_in_thread(self.get_commits)
|
Gio.Task.new(None, None, self.get_commits_callback).run_in_thread(self.get_commits)
|
||||||
|
|
||||||
# Connections
|
# Connections
|
||||||
self.root_group_check_button.connect("toggled", lambda *_: self.action_bar.set_revealed(True))
|
self.root_group_check_button.connect("toggled", lambda *_: self.action_bar.set_revealed(True))
|
||||||
self.apply_button.connect("clicked", self.on_apply)
|
self.apply_button.connect("clicked", self.on_apply)
|
||||||
|
|||||||
@@ -8,41 +8,41 @@ class ChangeVersionWorker:
|
|||||||
error_callback = None
|
error_callback = None
|
||||||
loading_status = None
|
loading_status = None
|
||||||
did_error = False
|
did_error = False
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def update_status(this, package_ratio, complete, total):
|
def update_status(this, package_ratio, complete, total):
|
||||||
final_ratio = (package_ratio + complete) / (total or 1)
|
final_ratio = (package_ratio + complete) / (total or 1)
|
||||||
|
|
||||||
print(f"fr: {final_ratio:.2f}")
|
print(f"fr: {final_ratio:.2f}")
|
||||||
print("r:", package_ratio, ", c:", complete, ", t:", total)
|
print("r:", package_ratio, ", c:", complete, ", t:", total)
|
||||||
print("=======================================")
|
print("=======================================")
|
||||||
|
|
||||||
if not this.loading_status is None:
|
if not this.loading_status is None:
|
||||||
GLib.idle_add(lambda *_: this.loading_status.progress_bar.set_fraction(final_ratio))
|
GLib.idle_add(lambda *_: this.loading_status.progress_bar.set_fraction(final_ratio))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def change_version_thread(this, should_mask, package, commit):
|
def change_version_thread(this, should_mask, package, commit):
|
||||||
try:
|
try:
|
||||||
cmd = ['flatpak-spawn', '--host', 'pkexec', 'sh', '-c']
|
cmd = ['flatpak-spawn', '--host', 'pkexec', 'sh', '-c']
|
||||||
|
|
||||||
installation = package.info['installation']
|
installation = package.info['installation']
|
||||||
real_installation = ""
|
real_installation = ""
|
||||||
if installation == "user" or installation == "system":
|
if installation == "user" or installation == "system":
|
||||||
real_installation = f"--{installation}"
|
real_installation = f"--{installation}"
|
||||||
else:
|
else:
|
||||||
real_installation = f"--installation={installation}"
|
real_installation = f"--installation={installation}"
|
||||||
|
|
||||||
suffix = ""
|
suffix = ""
|
||||||
unmask_cmd = f"flatpak mask --remove {real_installation} {package.info['id']} && "
|
unmask_cmd = f"flatpak mask --remove {real_installation} {package.info['id']} && "
|
||||||
change_version_cmd = f"flatpak update {package.info['ref']} --commit={commit} {real_installation} -y"
|
change_version_cmd = f"flatpak update {package.info['ref']} --commit={commit} {real_installation} -y"
|
||||||
mask_cmd = f" && flatpak mask {real_installation} {package.info['id']}"
|
mask_cmd = f" && flatpak mask {real_installation} {package.info['id']}"
|
||||||
if package.is_masked:
|
if package.is_masked:
|
||||||
suffix += unmask_cmd
|
suffix += unmask_cmd
|
||||||
|
|
||||||
suffix += change_version_cmd
|
suffix += change_version_cmd
|
||||||
if should_mask:
|
if should_mask:
|
||||||
suffix += mask_cmd
|
suffix += mask_cmd
|
||||||
|
|
||||||
cmd.append(suffix)
|
cmd.append(suffix)
|
||||||
this.process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
this.process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
||||||
percent_pattern = r'\d{1,3}%'
|
percent_pattern = r'\d{1,3}%'
|
||||||
@@ -60,52 +60,52 @@ class ChangeVersionWorker:
|
|||||||
this.update_status(ratio, complete, total)
|
this.update_status(ratio, complete, total)
|
||||||
else:
|
else:
|
||||||
this.update_status(ratio, 0, 1)
|
this.update_status(ratio, 0, 1)
|
||||||
|
|
||||||
this.process.wait(timeout=10)
|
this.process.wait(timeout=10)
|
||||||
if error := this.process.communicate()[1].strip():
|
if error := this.process.communicate()[1].strip():
|
||||||
this.on_error(_("Error occurred while changing version"), error)
|
this.on_error(_("Error occurred while changing version"), error)
|
||||||
|
|
||||||
except subprocess.TimeoutExpired as te:
|
except subprocess.TimeoutExpired as te:
|
||||||
this.process.terminate()
|
this.process.terminate()
|
||||||
this.on_error(_("Error occurred while changing version"), _("Failed to exit cleanly"))
|
this.on_error(_("Error occurred while changing version"), _("Failed to exit cleanly"))
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
this.process.terminate()
|
this.process.terminate()
|
||||||
this.on_error(_("Error occurred while changing version"), str(e))
|
this.on_error(_("Error occurred while changing version"), str(e))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def cancel(this):
|
def cancel(this):
|
||||||
if this.process is None:
|
if this.process is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
this.process.terminate()
|
this.process.terminate()
|
||||||
this.process.wait(timeout=10)
|
this.process.wait(timeout=10)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
this.on_error(_("Could not cancel version change"), str(e))
|
this.on_error(_("Could not cancel version change"), str(e))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def on_done(this, *args):
|
def on_done(this, *args):
|
||||||
this.process = None
|
this.process = None
|
||||||
HostInfo.main_window.remove_refresh_lockout("changing version")
|
HostInfo.main_window.remove_refresh_lockout("changing version")
|
||||||
if not this.loading_status is None:
|
if not this.loading_status is None:
|
||||||
this.loading_status.progress_bar.set_fraction(0.0)
|
this.loading_status.progress_bar.set_fraction(0.0)
|
||||||
|
|
||||||
if not this.callback is None:
|
if not this.callback is None:
|
||||||
this.callback(this.did_error)
|
this.callback(this.did_error)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def on_error(this, user_facing_label, error_message):
|
def on_error(this, user_facing_label, error_message):
|
||||||
this.did_error = True
|
this.did_error = True
|
||||||
if not this.error_callback is None:
|
if not this.error_callback is None:
|
||||||
this.error_callback(user_facing_label, error_message)
|
this.error_callback(user_facing_label, error_message)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def change_version(this, should_mask, package, commit, loading_status=None, callback=None, error_callback=None):
|
def change_version(this, should_mask, package, commit, loading_status=None, callback=None, error_callback=None):
|
||||||
if not this.process is None:
|
if not this.process is None:
|
||||||
this.on_error(_("Could not change version"), _("Another package is changing version."))
|
this.on_error(_("Could not change version"), _("Another package is changing version."))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
this.loading_status = loading_status
|
this.loading_status = loading_status
|
||||||
this.callback = callback
|
this.callback = callback
|
||||||
this.error_callback = error_callback
|
this.error_callback = error_callback
|
||||||
|
|||||||
@@ -6,15 +6,15 @@ from .error_toast import ErrorToast
|
|||||||
class AttemptInstallDialog(Adw.AlertDialog):
|
class AttemptInstallDialog(Adw.AlertDialog):
|
||||||
__gtype_name__ = "AttemptInstallDialog"
|
__gtype_name__ = "AttemptInstallDialog"
|
||||||
gtc = Gtk.Template.Child
|
gtc = Gtk.Template.Child
|
||||||
|
|
||||||
preferences_group = gtc()
|
preferences_group = gtc()
|
||||||
|
|
||||||
def generate_list(self):
|
def generate_list(self):
|
||||||
for installation, remotes in HostInfo.remotes.items():
|
for installation, remotes in HostInfo.remotes.items():
|
||||||
for remote in remotes:
|
for remote in remotes:
|
||||||
if remote.disabled:
|
if remote.disabled:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
row = Adw.ActionRow(title=remote.title, subtitle=_("Installation: {}").format(installation))
|
row = Adw.ActionRow(title=remote.title, subtitle=_("Installation: {}").format(installation))
|
||||||
row.remote_name = remote.name
|
row.remote_name = remote.name
|
||||||
row.remote_installation = installation
|
row.remote_installation = installation
|
||||||
@@ -28,20 +28,20 @@ class AttemptInstallDialog(Adw.AlertDialog):
|
|||||||
button.set_group(self.rows[0].check_button)
|
button.set_group(self.rows[0].check_button)
|
||||||
else:
|
else:
|
||||||
button.activate()
|
button.activate()
|
||||||
|
|
||||||
def on_response(self, dialog, response):
|
def on_response(self, dialog, response):
|
||||||
if response != "continue":
|
if response != "continue":
|
||||||
if not self.callback is None:
|
if not self.callback is None:
|
||||||
self.callback(False)
|
self.callback(False)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
active_row = None
|
active_row = None
|
||||||
for row in self.rows:
|
for row in self.rows:
|
||||||
if row.check_button.get_active():
|
if row.check_button.get_active():
|
||||||
active_row = row
|
active_row = row
|
||||||
break
|
break
|
||||||
|
|
||||||
if not active_row is None:
|
if not active_row is None:
|
||||||
install_page = HostInfo.main_window.pages[HostInfo.main_window.install_row]
|
install_page = HostInfo.main_window.pages[HostInfo.main_window.install_row]
|
||||||
HostInfo.main_window.activate_row(HostInfo.main_window.install_row)
|
HostInfo.main_window.activate_row(HostInfo.main_window.install_row)
|
||||||
@@ -54,23 +54,23 @@ class AttemptInstallDialog(Adw.AlertDialog):
|
|||||||
}])
|
}])
|
||||||
elif not self.callback is None:
|
elif not self.callback is None:
|
||||||
self.callback(False)
|
self.callback(False)
|
||||||
|
|
||||||
def __init__(self, package_names, callback=None, **kwargs):
|
def __init__(self, package_names, callback=None, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
# Extra Object Creation
|
# Extra Object Creation
|
||||||
self.rows = []
|
self.rows = []
|
||||||
self.package_names = package_names
|
self.package_names = package_names
|
||||||
self.callback = callback
|
self.callback = callback
|
||||||
|
|
||||||
# Apply
|
# Apply
|
||||||
self.generate_list()
|
self.generate_list()
|
||||||
if len(self.rows) == 1:
|
if len(self.rows) == 1:
|
||||||
self.set_extra_child(None)
|
self.set_extra_child(None)
|
||||||
elif len(self.rows) < 1:
|
elif len(self.rows) < 1:
|
||||||
HostInfo.main_window.toast_overlay.add_toast(ErrorToast(_("Can't find matching packages"), _("Your system has no remotes added")).toast)
|
HostInfo.main_window.toast_overlay.add_toast(ErrorToast(_("Can't find matching packages"), _("Your system has no remotes added")).toast)
|
||||||
|
|
||||||
self.present(HostInfo.main_window)
|
self.present(HostInfo.main_window)
|
||||||
|
|
||||||
# Connections
|
# Connections
|
||||||
self.connect("response", self.on_response)
|
self.connect("response", self.on_response)
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ from gi.repository import Adw, Gtk, Gdk, GLib
|
|||||||
class ErrorToast:
|
class ErrorToast:
|
||||||
main_window = None
|
main_window = None
|
||||||
def __init__(self, display_msg, error_msg):
|
def __init__(self, display_msg, error_msg):
|
||||||
|
|
||||||
def on_response(dialog, response_id):
|
def on_response(dialog, response_id):
|
||||||
if response_id == "copy":
|
if response_id == "copy":
|
||||||
self.clipboard.set(error_msg)
|
self.clipboard.set(error_msg)
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from .host_info import HostInfo
|
|||||||
class InstallationChooser(Adw.PreferencesGroup):
|
class InstallationChooser(Adw.PreferencesGroup):
|
||||||
__gtype_name__ = 'InstallationChooser'
|
__gtype_name__ = 'InstallationChooser'
|
||||||
gtc = Gtk.Template.Child
|
gtc = Gtk.Template.Child
|
||||||
|
|
||||||
user_row = gtc()
|
user_row = gtc()
|
||||||
system_row = gtc()
|
system_row = gtc()
|
||||||
single_row = gtc()
|
single_row = gtc()
|
||||||
@@ -14,14 +14,14 @@ class InstallationChooser(Adw.PreferencesGroup):
|
|||||||
system_check = gtc()
|
system_check = gtc()
|
||||||
single_check = gtc()
|
single_check = gtc()
|
||||||
choice_check = gtc()
|
choice_check = gtc()
|
||||||
|
|
||||||
def get_installation(self):
|
def get_installation(self):
|
||||||
for button, func in self.check_buttons.items():
|
for button, func in self.check_buttons.items():
|
||||||
if button.get_active():
|
if button.get_active():
|
||||||
return func()
|
return func()
|
||||||
|
|
||||||
return "" # Case for when no button is active (which shouldn't happen)
|
return "" # Case for when no button is active (which shouldn't happen)
|
||||||
|
|
||||||
def set_content_strings(self, content_name, is_plural):
|
def set_content_strings(self, content_name, is_plural):
|
||||||
if is_plural:
|
if is_plural:
|
||||||
self.user_row.set_subtitle(_("These {} will only be available to you").format(content_name))
|
self.user_row.set_subtitle(_("These {} will only be available to you").format(content_name))
|
||||||
@@ -31,31 +31,31 @@ class InstallationChooser(Adw.PreferencesGroup):
|
|||||||
self.user_row.set_subtitle(_("This {} will only be available to you").format(content_name))
|
self.user_row.set_subtitle(_("This {} will only be available to you").format(content_name))
|
||||||
self.system_row.set_subtitle(_("This {} will be available to everyone").format(content_name))
|
self.system_row.set_subtitle(_("This {} will be available to everyone").format(content_name))
|
||||||
self.set_description(_("Choose how this {} will be installed").format(content_name))
|
self.set_description(_("Choose how this {} will be installed").format(content_name))
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
self.check_buttons = {
|
self.check_buttons = {
|
||||||
self.user_check : lambda: "user",
|
self.user_check : lambda: "user",
|
||||||
self.system_check: lambda: "system",
|
self.system_check: lambda: "system",
|
||||||
self.single_check: self.single_row.get_title,
|
self.single_check: self.single_row.get_title,
|
||||||
self.choice_check: lambda: self.choice_row.get_selected_item().get_string(),
|
self.choice_check: lambda: self.choice_row.get_selected_item().get_string(),
|
||||||
}
|
}
|
||||||
|
|
||||||
# Apply
|
# Apply
|
||||||
custom_installations = []
|
custom_installations = []
|
||||||
for installation in HostInfo.installations:
|
for installation in HostInfo.installations:
|
||||||
if installation.startswith("user") or installation.startswith("system"):
|
if installation.startswith("user") or installation.startswith("system"):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
custom_installations.append(installation)
|
custom_installations.append(installation)
|
||||||
|
|
||||||
if len(custom_installations) == 1:
|
if len(custom_installations) == 1:
|
||||||
self.single_row.set_visible(True)
|
self.single_row.set_visible(True)
|
||||||
self.single_row.set_title(custom_installations[0])
|
self.single_row.set_title(custom_installations[0])
|
||||||
elif len(custom_installations) > 1:
|
elif len(custom_installations) > 1:
|
||||||
self.choice_row.set_visible(True)
|
self.choice_row.set_visible(True)
|
||||||
self.choice_row.set_model(Gtk.StringList(strings=custom_installations))
|
self.choice_row.set_model(Gtk.StringList(strings=custom_installations))
|
||||||
|
|
||||||
# Connections
|
# Connections
|
||||||
self.choice_row.connect("notify::css-classes", lambda *_: self.choice_check.activate())
|
self.choice_row.connect("notify::css-classes", lambda *_: self.choice_check.activate())
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ class SidebarButton(Gtk.Button):
|
|||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
# Extra Object Creation
|
# Extra Object Creation
|
||||||
main_split = HostInfo.main_window.main_split
|
main_split = HostInfo.main_window.main_split
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ icon_theme.add_search_path(f"{home}/.local/share/flatpak/exports/share/icons")
|
|||||||
direction = Gtk.Image().get_direction()
|
direction = Gtk.Image().get_direction()
|
||||||
|
|
||||||
class Flatpak:
|
class Flatpak:
|
||||||
|
|
||||||
def open_app(self, callback=None):
|
def open_app(self, callback=None):
|
||||||
self.failed_app_run = None
|
self.failed_app_run = None
|
||||||
def thread(*args):
|
def thread(*args):
|
||||||
@@ -58,10 +57,10 @@ class Flatpak:
|
|||||||
cmd.append(f"--{installation}")
|
cmd.append(f"--{installation}")
|
||||||
else:
|
else:
|
||||||
cmd.append(f"--installation={installation}")
|
cmd.append(f"--installation={installation}")
|
||||||
|
|
||||||
if not should_mask:
|
if not should_mask:
|
||||||
cmd.append("--remove")
|
cmd.append("--remove")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
subprocess.run(cmd, check=True, capture_output=True, text=True)
|
subprocess.run(cmd, check=True, capture_output=True, text=True)
|
||||||
self.is_masked = should_mask
|
self.is_masked = should_mask
|
||||||
@@ -76,7 +75,7 @@ class Flatpak:
|
|||||||
self.failed_pin = None
|
self.failed_pin = None
|
||||||
if not self.is_runtime:
|
if not self.is_runtime:
|
||||||
self.failed_pin = "Cannot pin an application"
|
self.failed_pin = "Cannot pin an application"
|
||||||
|
|
||||||
def thread(*args):
|
def thread(*args):
|
||||||
cmd = ['flatpak-spawn', '--host', 'flatpak', 'pin', f"runtime/{self.info['ref']}"]
|
cmd = ['flatpak-spawn', '--host', 'flatpak', 'pin', f"runtime/{self.info['ref']}"]
|
||||||
installation = self.info["installation"]
|
installation = self.info["installation"]
|
||||||
@@ -99,7 +98,7 @@ class Flatpak:
|
|||||||
|
|
||||||
def uninstall(self, callee_callback=None):
|
def uninstall(self, callee_callback=None):
|
||||||
self.failed_uninstall = None
|
self.failed_uninstall = None
|
||||||
|
|
||||||
def callback(*args):
|
def callback(*args):
|
||||||
HostInfo.main_window.remove_refresh_lockout("uninstalling packages")
|
HostInfo.main_window.remove_refresh_lockout("uninstalling packages")
|
||||||
if not callee_callback is None:
|
if not callee_callback is None:
|
||||||
@@ -149,16 +148,16 @@ class Flatpak:
|
|||||||
first = lines.pop(0)
|
first = lines.pop(0)
|
||||||
if " - " in first:
|
if " - " in first:
|
||||||
cli_info["description"] = first.split(" - ")[1]
|
cli_info["description"] = first.split(" - ")[1]
|
||||||
|
|
||||||
# Handle descriptions that contain newlines
|
# Handle descriptions that contain newlines
|
||||||
while (line := lines.pop(0)) and not ":" in line:
|
while (line := lines.pop(0)) and not ":" in line:
|
||||||
if len(line) > 0:
|
if len(line) > 0:
|
||||||
cli_info["description"] += f" {line}"
|
cli_info["description"] += f" {line}"
|
||||||
|
|
||||||
for i, word in enumerate(lines):
|
for i, word in enumerate(lines):
|
||||||
if not ":" in word:
|
if not ":" in word:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
word = word.strip().split(": ", 1)
|
word = word.strip().split(": ", 1)
|
||||||
if len(word) < 2:
|
if len(word) < 2:
|
||||||
continue
|
continue
|
||||||
@@ -248,7 +247,7 @@ class HostInfo:
|
|||||||
lines = output.strip().split("\n")
|
lines = output.strip().split("\n")
|
||||||
for i in lines:
|
for i in lines:
|
||||||
icon_theme.add_search_path(f"{i}/exports/share/icons")
|
icon_theme.add_search_path(f"{i}/exports/share/icons")
|
||||||
|
|
||||||
flatpaks = []
|
flatpaks = []
|
||||||
id_to_flatpak = {}
|
id_to_flatpak = {}
|
||||||
ref_to_flatpak = {}
|
ref_to_flatpak = {}
|
||||||
@@ -270,7 +269,6 @@ class HostInfo:
|
|||||||
this.dependent_runtime_refs.clear()
|
this.dependent_runtime_refs.clear()
|
||||||
|
|
||||||
def thread(task, *args):
|
def thread(task, *args):
|
||||||
|
|
||||||
# Remotes
|
# Remotes
|
||||||
def remote_info(installation):
|
def remote_info(installation):
|
||||||
cmd = ['flatpak-spawn', '--host',
|
cmd = ['flatpak-spawn', '--host',
|
||||||
@@ -353,7 +351,7 @@ class HostInfo:
|
|||||||
this.flatpaks.append(package)
|
this.flatpaks.append(package)
|
||||||
this.id_to_flatpak[package.info["id"]] = package
|
this.id_to_flatpak[package.info["id"]] = package
|
||||||
this.ref_to_flatpak[package.info["ref"]] = package
|
this.ref_to_flatpak[package.info["ref"]] = package
|
||||||
|
|
||||||
# Dependent Runtimes
|
# Dependent Runtimes
|
||||||
output = subprocess.run(
|
output = subprocess.run(
|
||||||
['flatpak-spawn', '--host',
|
['flatpak-spawn', '--host',
|
||||||
@@ -366,19 +364,19 @@ class HostInfo:
|
|||||||
split_line = line.split("\t")
|
split_line = line.split("\t")
|
||||||
if len(split_line) < 2 or split_line[0] == '':
|
if len(split_line) < 2 or split_line[0] == '':
|
||||||
continue
|
continue
|
||||||
|
|
||||||
package = this.flatpaks[index]
|
package = this.flatpaks[index]
|
||||||
if package.is_runtime:
|
if package.is_runtime:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
runtime = split_line[0]
|
runtime = split_line[0]
|
||||||
package.dependent_runtime = this.ref_to_flatpak[runtime]
|
package.dependent_runtime = this.ref_to_flatpak[runtime]
|
||||||
if not runtime in this.dependent_runtime_refs:
|
if not runtime in this.dependent_runtime_refs:
|
||||||
this.dependent_runtime_refs.append(runtime)
|
this.dependent_runtime_refs.append(runtime)
|
||||||
|
|
||||||
except subprocess.CalledProcessError as cpe:
|
except subprocess.CalledProcessError as cpe:
|
||||||
this.main_window.toast_overlay.add_toast(ErrorToast(_("Could not load packages"), cpe.stderr).toast)
|
this.main_window.toast_overlay.add_toast(ErrorToast(_("Could not load packages"), cpe.stderr).toast)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
this.main_window.toast_overlay.add_toast(ErrorToast(_("Could not load packages"), str(e)).toast)
|
this.main_window.toast_overlay.add_toast(ErrorToast(_("Could not load packages"), str(e)).toast)
|
||||||
|
|
||||||
Gio.Task.new(None, None, callback).run_in_thread(thread)
|
Gio.Task.new(None, None, callback).run_in_thread(thread)
|
||||||
|
|||||||
@@ -4,30 +4,30 @@ from gi.repository import Adw, Gtk
|
|||||||
class FileInstallDialog(Adw.Dialog):
|
class FileInstallDialog(Adw.Dialog):
|
||||||
__gtype_name__ = "FileInstallDialog"
|
__gtype_name__ = "FileInstallDialog"
|
||||||
gtc = Gtk.Template.Child
|
gtc = Gtk.Template.Child
|
||||||
|
|
||||||
packages_group = gtc()
|
packages_group = gtc()
|
||||||
installation_chooser = gtc()
|
installation_chooser = gtc()
|
||||||
cancel_button = gtc()
|
cancel_button = gtc()
|
||||||
apply_button = gtc()
|
apply_button = gtc()
|
||||||
|
|
||||||
def generate_list(self):
|
def generate_list(self):
|
||||||
for file in self.files:
|
for file in self.files:
|
||||||
row = Adw.ActionRow(title=file.get_basename())
|
row = Adw.ActionRow(title=file.get_basename())
|
||||||
row.add_prefix(Gtk.Image(icon_name="flatpak-symbolic"))
|
row.add_prefix(Gtk.Image(icon_name="flatpak-symbolic"))
|
||||||
self.packages_group.add(row)
|
self.packages_group.add(row)
|
||||||
|
|
||||||
def on_response(self, *args):
|
def on_response(self, *args):
|
||||||
self.on_add(self.installation_chooser.get_installation(), self.files)
|
self.on_add(self.installation_chooser.get_installation(), self.files)
|
||||||
self.close()
|
self.close()
|
||||||
|
|
||||||
def __init__(self, parent_page, files, on_add, **kwargs):
|
def __init__(self, parent_page, files, on_add, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
# Extra Object Creation
|
# Extra Object Creation
|
||||||
self.parent_page = parent_page
|
self.parent_page = parent_page
|
||||||
self.files = files
|
self.files = files
|
||||||
self.on_add = on_add
|
self.on_add = on_add
|
||||||
|
|
||||||
# Apply
|
# Apply
|
||||||
self.generate_list()
|
self.generate_list()
|
||||||
if len(files) > 1:
|
if len(files) > 1:
|
||||||
@@ -40,7 +40,7 @@ class FileInstallDialog(Adw.Dialog):
|
|||||||
# self.packages_group.set_title(_("Review Package"))
|
# self.packages_group.set_title(_("Review Package"))
|
||||||
self.packages_group.set_description(_("The following package will be installed"))
|
self.packages_group.set_description(_("The following package will be installed"))
|
||||||
self.installation_chooser.set_content_strings(_("package"), False)
|
self.installation_chooser.set_content_strings(_("package"), False)
|
||||||
|
|
||||||
# Connections
|
# Connections
|
||||||
self.cancel_button.connect("clicked", lambda *_: self.close())
|
self.cancel_button.connect("clicked", lambda *_: self.close())
|
||||||
self.apply_button.connect("clicked", self.on_response)
|
self.apply_button.connect("clicked", self.on_response)
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ from .error_toast import ErrorToast
|
|||||||
class InstallPage(Adw.BreakpointBin):
|
class InstallPage(Adw.BreakpointBin):
|
||||||
__gtype_name__ = "InstallPage"
|
__gtype_name__ = "InstallPage"
|
||||||
gtc = Gtk.Template.Child
|
gtc = Gtk.Template.Child
|
||||||
|
|
||||||
break_point = gtc()
|
break_point = gtc()
|
||||||
split_view = gtc()
|
split_view = gtc()
|
||||||
multi_view = gtc()
|
multi_view = gtc()
|
||||||
@@ -23,42 +23,42 @@ class InstallPage(Adw.BreakpointBin):
|
|||||||
bottom_sheet = gtc()
|
bottom_sheet = gtc()
|
||||||
bottom_child = gtc()
|
bottom_child = gtc()
|
||||||
bottom_label = gtc()
|
bottom_label = gtc()
|
||||||
|
|
||||||
# Referred to in the main window
|
# Referred to in the main window
|
||||||
# It is used to determine if a new page should be made or not
|
# It is used to determine if a new page should be made or not
|
||||||
# This must be set to the created object from within the class's __init__ method
|
# This must be set to the created object from within the class's __init__ method
|
||||||
instance = None
|
instance = None
|
||||||
page_name = "install"
|
page_name = "install"
|
||||||
|
|
||||||
current_installation = ""
|
current_installation = ""
|
||||||
current_remote = None
|
current_remote = None
|
||||||
did_error = False
|
did_error = False
|
||||||
|
|
||||||
def start_loading(self):
|
def start_loading(self):
|
||||||
self.total_added_packages = 0
|
self.total_added_packages = 0
|
||||||
self.bottom_bar_visual_handler(False)
|
self.bottom_bar_visual_handler(False)
|
||||||
self.status_stack.set_visible_child(self.loading_view)
|
self.status_stack.set_visible_child(self.loading_view)
|
||||||
self.select_page.start_loading()
|
self.select_page.start_loading()
|
||||||
self.pending_page.reset()
|
self.pending_page.reset()
|
||||||
|
|
||||||
def end_loading(self):
|
def end_loading(self):
|
||||||
self.select_page.end_loading()
|
self.select_page.end_loading()
|
||||||
self.status_stack.set_visible_child(self.multi_view)
|
self.status_stack.set_visible_child(self.multi_view)
|
||||||
|
|
||||||
def install_callback(self):
|
def install_callback(self):
|
||||||
HostInfo.main_window.refresh_handler()
|
HostInfo.main_window.refresh_handler()
|
||||||
if not self.did_error:
|
if not self.did_error:
|
||||||
HostInfo.main_window.toast_overlay.add_toast(Adw.Toast(title=_("Installed Packages")))
|
HostInfo.main_window.toast_overlay.add_toast(Adw.Toast(title=_("Installed Packages")))
|
||||||
|
|
||||||
def install_error_callback(self, user_facing_label, error_message):
|
def install_error_callback(self, user_facing_label, error_message):
|
||||||
self.did_error = True
|
self.did_error = True
|
||||||
GLib.idle_add(lambda *_: HostInfo.main_window.toast_overlay.add_toast(ErrorToast(user_facing_label, error_message).toast))
|
GLib.idle_add(lambda *_: HostInfo.main_window.toast_overlay.add_toast(ErrorToast(user_facing_label, error_message).toast))
|
||||||
|
|
||||||
def install_packages(self, package_requests):
|
def install_packages(self, package_requests):
|
||||||
self.did_error = False
|
self.did_error = False
|
||||||
if PackageInstallWorker.install(package_requests, self.installing_status, self.install_callback, self.install_error_callback):
|
if PackageInstallWorker.install(package_requests, self.installing_status, self.install_callback, self.install_error_callback):
|
||||||
self.status_stack.set_visible_child(self.installing_view)
|
self.status_stack.set_visible_child(self.installing_view)
|
||||||
|
|
||||||
def bottom_bar_visual_handler(self, is_added):
|
def bottom_bar_visual_handler(self, is_added):
|
||||||
total = self.total_added_packages
|
total = self.total_added_packages
|
||||||
if total == 0:
|
if total == 0:
|
||||||
@@ -71,25 +71,25 @@ class InstallPage(Adw.BreakpointBin):
|
|||||||
self.bottom_child.set_reveal_child(True)
|
self.bottom_child.set_reveal_child(True)
|
||||||
else:
|
else:
|
||||||
self.bottom_label.set_label(_("{} Pending Packages").format(total))
|
self.bottom_label.set_label(_("{} Pending Packages").format(total))
|
||||||
|
|
||||||
def package_added(self):
|
def package_added(self):
|
||||||
self.total_added_packages += 1
|
self.total_added_packages += 1
|
||||||
self.bottom_bar_visual_handler(True)
|
self.bottom_bar_visual_handler(True)
|
||||||
|
|
||||||
def package_removed(self):
|
def package_removed(self):
|
||||||
self.total_added_packages -= 1
|
self.total_added_packages -= 1
|
||||||
self.bottom_bar_visual_handler(False)
|
self.bottom_bar_visual_handler(False)
|
||||||
|
|
||||||
def __init__(self, main_window, **kwargs):
|
def __init__(self, main_window, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
self.instance = self
|
self.instance = self
|
||||||
|
|
||||||
# Extra Object Creation
|
# Extra Object Creation
|
||||||
self.installing_status = LoadingStatus(_("Installing Packages"), _("This could take a while"), True, PackageInstallWorker.cancel)
|
self.installing_status = LoadingStatus(_("Installing Packages"), _("This could take a while"), True, PackageInstallWorker.cancel)
|
||||||
self.total_added_packages = 0
|
self.total_added_packages = 0
|
||||||
|
|
||||||
# Connections
|
# Connections
|
||||||
|
|
||||||
# Apply
|
# Apply
|
||||||
self.select_page.results_page.pending_page = self.pending_page
|
self.select_page.results_page.pending_page = self.pending_page
|
||||||
self.select_page.results_page.install_page = self
|
self.select_page.results_page.install_page = self
|
||||||
|
|||||||
@@ -4,30 +4,30 @@ from .result_row import ResultRow
|
|||||||
|
|
||||||
class AddedGroup(Adw.PreferencesGroup):
|
class AddedGroup(Adw.PreferencesGroup):
|
||||||
__gtype_name__ = "AddedGroup"
|
__gtype_name__ = "AddedGroup"
|
||||||
|
|
||||||
def add_row(self, row):
|
def add_row(self, row):
|
||||||
self.rows.append(row)
|
self.rows.append(row)
|
||||||
self.add(row)
|
self.add(row)
|
||||||
|
|
||||||
def rem_row(self, row):
|
def rem_row(self, row):
|
||||||
if row in self.rows:
|
if row in self.rows:
|
||||||
self.rows.remove(row)
|
self.rows.remove(row)
|
||||||
self.remove(row)
|
self.remove(row)
|
||||||
|
|
||||||
def remove_all(self, *args):
|
def remove_all(self, *args):
|
||||||
while len(self.rows) > 0 and (row := self.rows[0]):
|
while len(self.rows) > 0 and (row := self.rows[0]):
|
||||||
row.activate()
|
row.activate()
|
||||||
|
|
||||||
def __init__(self, remote, installation, **kwargs):
|
def __init__(self, remote, installation, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
self.remote = remote
|
self.remote = remote
|
||||||
self.installation = installation
|
self.installation = installation
|
||||||
self.rows = []
|
self.rows = []
|
||||||
|
|
||||||
self.set_title(f"{remote.title}")
|
self.set_title(f"{remote.title}")
|
||||||
self.set_description(_("Installation: {}").format(installation))
|
self.set_description(_("Installation: {}").format(installation))
|
||||||
|
|
||||||
remove_all = Gtk.Button(
|
remove_all = Gtk.Button(
|
||||||
child=Adw.ButtonContent(
|
child=Adw.ButtonContent(
|
||||||
icon_name="list-remove-all-symbolic",
|
icon_name="list-remove-all-symbolic",
|
||||||
@@ -38,18 +38,18 @@ class AddedGroup(Adw.PreferencesGroup):
|
|||||||
remove_all.add_css_class("flat")
|
remove_all.add_css_class("flat")
|
||||||
remove_all.connect("clicked", self.remove_all)
|
remove_all.connect("clicked", self.remove_all)
|
||||||
self.set_header_suffix(remove_all)
|
self.set_header_suffix(remove_all)
|
||||||
|
|
||||||
@Gtk.Template(resource_path="/io/github/flattool/Warehouse/install_page/pending_page.ui")
|
@Gtk.Template(resource_path="/io/github/flattool/Warehouse/install_page/pending_page.ui")
|
||||||
class PendingPage(Adw.NavigationPage):
|
class PendingPage(Adw.NavigationPage):
|
||||||
__gtype_name__ = "PendingPage"
|
__gtype_name__ = "PendingPage"
|
||||||
gtc = Gtk.Template.Child
|
gtc = Gtk.Template.Child
|
||||||
|
|
||||||
stack = gtc()
|
stack = gtc()
|
||||||
main_view = gtc()
|
main_view = gtc()
|
||||||
none_pending = gtc()
|
none_pending = gtc()
|
||||||
preferences_page = gtc()
|
preferences_page = gtc()
|
||||||
install_button = gtc()
|
install_button = gtc()
|
||||||
|
|
||||||
def add_package_row(self, row):
|
def add_package_row(self, row):
|
||||||
self.added_packages.append(row.package)
|
self.added_packages.append(row.package)
|
||||||
row.set_state(ResultRow.PackageState.SELECTED)
|
row.set_state(ResultRow.PackageState.SELECTED)
|
||||||
@@ -64,32 +64,32 @@ class PendingPage(Adw.NavigationPage):
|
|||||||
group.add_row(added_row)
|
group.add_row(added_row)
|
||||||
self.groups[key] = group
|
self.groups[key] = group
|
||||||
self.preferences_page.add(group)
|
self.preferences_page.add(group)
|
||||||
|
|
||||||
added_row.connect("activated", self.remove_package_row, group)
|
added_row.connect("activated", self.remove_package_row, group)
|
||||||
self.stack.set_visible_child(self.main_view)
|
self.stack.set_visible_child(self.main_view)
|
||||||
|
|
||||||
def remove_package_row(self, row, group):
|
def remove_package_row(self, row, group):
|
||||||
# row.origin_row.set_state(ResultRow.PackageState.NEW)
|
# row.origin_row.set_state(ResultRow.PackageState.NEW)
|
||||||
for item in row.origin_list_box:
|
for item in row.origin_list_box:
|
||||||
if item.state == ResultRow.PackageState.SELECTED and item.package.is_similar(row.package):
|
if item.state == ResultRow.PackageState.SELECTED and item.package.is_similar(row.package):
|
||||||
item.set_state(ResultRow.PackageState.NEW)
|
item.set_state(ResultRow.PackageState.NEW)
|
||||||
break
|
break
|
||||||
|
|
||||||
group.rem_row(row)
|
group.rem_row(row)
|
||||||
if row.package in self.added_packages:
|
if row.package in self.added_packages:
|
||||||
self.added_packages.remove(row.package)
|
self.added_packages.remove(row.package)
|
||||||
|
|
||||||
if len(group.rows) == 0:
|
if len(group.rows) == 0:
|
||||||
key = f"{row.package.remote}<>{row.package.installation}"
|
key = f"{row.package.remote}<>{row.package.installation}"
|
||||||
self.groups.pop(key, None)
|
self.groups.pop(key, None)
|
||||||
self.preferences_page.remove(group)
|
self.preferences_page.remove(group)
|
||||||
|
|
||||||
if len(self.added_packages) == 0:
|
if len(self.added_packages) == 0:
|
||||||
self.stack.set_visible_child(self.none_pending)
|
self.stack.set_visible_child(self.none_pending)
|
||||||
|
|
||||||
install_page = HostInfo.main_window.pages[HostInfo.main_window.install_row]
|
install_page = HostInfo.main_window.pages[HostInfo.main_window.install_row]
|
||||||
install_page.package_removed()
|
install_page.package_removed()
|
||||||
|
|
||||||
def on_install(self, *args):
|
def on_install(self, *args):
|
||||||
package_requests = []
|
package_requests = []
|
||||||
for key, group in self.groups.items():
|
for key, group in self.groups.items():
|
||||||
@@ -101,28 +101,28 @@ class PendingPage(Adw.NavigationPage):
|
|||||||
}
|
}
|
||||||
for row in group.rows:
|
for row in group.rows:
|
||||||
item['package_names'].append(row.package.app_id)
|
item['package_names'].append(row.package.app_id)
|
||||||
|
|
||||||
package_requests.append(item)
|
package_requests.append(item)
|
||||||
|
|
||||||
install_page = HostInfo.main_window.pages[HostInfo.main_window.install_row]
|
install_page = HostInfo.main_window.pages[HostInfo.main_window.install_row]
|
||||||
install_page.install_packages(package_requests)
|
install_page.install_packages(package_requests)
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
for key, group in self.groups.items():
|
for key, group in self.groups.items():
|
||||||
self.preferences_page.remove(group)
|
self.preferences_page.remove(group)
|
||||||
|
|
||||||
self.groups.clear()
|
self.groups.clear()
|
||||||
self.added_packages.clear()
|
self.added_packages.clear()
|
||||||
self.stack.set_visible_child(self.none_pending)
|
self.stack.set_visible_child(self.none_pending)
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
# Extra Object Creation
|
# Extra Object Creation
|
||||||
self.groups = {} # remote<>installation: adw.preference_group
|
self.groups = {} # remote<>installation: adw.preference_group
|
||||||
self.added_packages = []
|
self.added_packages = []
|
||||||
|
|
||||||
# Connections
|
# Connections
|
||||||
self.install_button.connect("clicked", self.on_install)
|
self.install_button.connect("clicked", self.on_install)
|
||||||
|
|
||||||
# Apply
|
# Apply
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ class AddedPackage:
|
|||||||
class ResultsPage(Adw.NavigationPage):
|
class ResultsPage(Adw.NavigationPage):
|
||||||
__gtype_name__ = "ResultsPage"
|
__gtype_name__ = "ResultsPage"
|
||||||
gtc = Gtk.Template.Child
|
gtc = Gtk.Template.Child
|
||||||
|
|
||||||
search_entry = gtc()
|
search_entry = gtc()
|
||||||
results_list = gtc()
|
results_list = gtc()
|
||||||
stack = gtc()
|
stack = gtc()
|
||||||
@@ -43,7 +43,7 @@ class ResultsPage(Adw.NavigationPage):
|
|||||||
too_many = gtc()
|
too_many = gtc()
|
||||||
results_view= gtc()
|
results_view= gtc()
|
||||||
no_results = gtc()
|
no_results = gtc()
|
||||||
|
|
||||||
def show_remote(self, row, remote, installation, nav_view=None):
|
def show_remote(self, row, remote, installation, nav_view=None):
|
||||||
self.remote = remote
|
self.remote = remote
|
||||||
self.installation = installation
|
self.installation = installation
|
||||||
@@ -52,12 +52,12 @@ class ResultsPage(Adw.NavigationPage):
|
|||||||
self.search_entry.grab_focus()
|
self.search_entry.grab_focus()
|
||||||
if nav_view:
|
if nav_view:
|
||||||
nav_view.push(self)
|
nav_view.push(self)
|
||||||
|
|
||||||
def add_package_row(self, row):
|
def add_package_row(self, row):
|
||||||
self.pending_page.add_package_row(row)
|
self.pending_page.add_package_row(row)
|
||||||
if not self.install_page is None:
|
if not self.install_page is None:
|
||||||
self.install_page.package_added()
|
self.install_page.package_added()
|
||||||
|
|
||||||
def on_search(self, *args):
|
def on_search(self, *args):
|
||||||
self.packages.clear()
|
self.packages.clear()
|
||||||
self.stack.set_visible_child(self.loading)
|
self.stack.set_visible_child(self.loading)
|
||||||
@@ -66,14 +66,14 @@ class ResultsPage(Adw.NavigationPage):
|
|||||||
if search_text == "":
|
if search_text == "":
|
||||||
self.stack.set_visible_child(self.new_search)
|
self.stack.set_visible_child(self.new_search)
|
||||||
return
|
return
|
||||||
|
|
||||||
def thread(*args):
|
def thread(*args):
|
||||||
installation = ""
|
installation = ""
|
||||||
if self.installation == "user" or self.installation == "system":
|
if self.installation == "user" or self.installation == "system":
|
||||||
installation = f"--{self.installation}"
|
installation = f"--{self.installation}"
|
||||||
else:
|
else:
|
||||||
installation = f"--installation={self.installation}"
|
installation = f"--installation={self.installation}"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
output = subprocess.run(
|
output = subprocess.run(
|
||||||
['flatpak-spawn', '--host', 'flatpak', 'search', '--columns=all', installation, self.search_entry.get_text()],
|
['flatpak-spawn', '--host', 'flatpak', 'search', '--columns=all', installation, self.search_entry.get_text()],
|
||||||
@@ -82,51 +82,51 @@ class ResultsPage(Adw.NavigationPage):
|
|||||||
if len(output) > 100:
|
if len(output) > 100:
|
||||||
GLib.idle_add(lambda *_: self.stack.set_visible_child(self.too_many))
|
GLib.idle_add(lambda *_: self.stack.set_visible_child(self.too_many))
|
||||||
return
|
return
|
||||||
|
|
||||||
for line in output:
|
for line in output:
|
||||||
line = line.strip()
|
line = line.strip()
|
||||||
|
|
||||||
info = line.split('\t')
|
info = line.split('\t')
|
||||||
if len(info) != 6:
|
if len(info) != 6:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
remotes = info[5].split(',')
|
remotes = info[5].split(',')
|
||||||
if not self.remote.name in remotes:
|
if not self.remote.name in remotes:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
package = AddedPackage(info[0], info[2], info[4], info[3], self.remote, self.installation)
|
package = AddedPackage(info[0], info[2], info[4], info[3], self.remote, self.installation)
|
||||||
row = ResultRow(package, ResultRow.PackageState.NEW, self.results_list)
|
row = ResultRow(package, ResultRow.PackageState.NEW, self.results_list)
|
||||||
for item in self.pending_page.added_packages:
|
for item in self.pending_page.added_packages:
|
||||||
if package.is_similar(item):
|
if package.is_similar(item):
|
||||||
row.set_state(ResultRow.PackageState.SELECTED)
|
row.set_state(ResultRow.PackageState.SELECTED)
|
||||||
|
|
||||||
if package.app_id in HostInfo.id_to_flatpak:
|
if package.app_id in HostInfo.id_to_flatpak:
|
||||||
installed_package = HostInfo.id_to_flatpak[package.app_id]
|
installed_package = HostInfo.id_to_flatpak[package.app_id]
|
||||||
if installed_package.info["id"] == package.app_id and installed_package.info["branch"] == package.branch:
|
if installed_package.info["id"] == package.app_id and installed_package.info["branch"] == package.branch:
|
||||||
row.set_state(ResultRow.PackageState.INSTALLED)
|
row.set_state(ResultRow.PackageState.INSTALLED)
|
||||||
|
|
||||||
row.connect("activated", self.add_package_row)
|
row.connect("activated", self.add_package_row)
|
||||||
self.packages.append(package)
|
self.packages.append(package)
|
||||||
GLib.idle_add(lambda *_, _row=row: self.results_list.append(_row))
|
GLib.idle_add(lambda *_, _row=row: self.results_list.append(_row))
|
||||||
|
|
||||||
if len(self.packages) > 0:
|
if len(self.packages) > 0:
|
||||||
GLib.idle_add(lambda *_: self.stack.set_visible_child(self.results_view))
|
GLib.idle_add(lambda *_: self.stack.set_visible_child(self.results_view))
|
||||||
else:
|
else:
|
||||||
GLib.idle_add(lambda *_: self.stack.set_visible_child(self.no_results))
|
GLib.idle_add(lambda *_: self.stack.set_visible_child(self.no_results))
|
||||||
|
|
||||||
except subprocess.CalledProcessError as cpe:
|
except subprocess.CalledProcessError as cpe:
|
||||||
GLib.idle_add(lambda *_, cpe=cpe: HostInfo.main_window.toast_overlay.add_toast(ErrorToast("Could not search for package", cpe.stderr).toast))
|
GLib.idle_add(lambda *_, cpe=cpe: HostInfo.main_window.toast_overlay.add_toast(ErrorToast("Could not search for package", cpe.stderr).toast))
|
||||||
GLib.idle_add(lambda *_: self.install_page.select_page.nav_view.pop())
|
GLib.idle_add(lambda *_: self.install_page.select_page.nav_view.pop())
|
||||||
|
|
||||||
Gio.Task().run_in_thread(thread)
|
Gio.Task().run_in_thread(thread)
|
||||||
|
|
||||||
def on_back(self, *args):
|
def on_back(self, *args):
|
||||||
self.results_list.remove_all()
|
self.results_list.remove_all()
|
||||||
self.stack.set_visible_child(self.new_search)
|
self.stack.set_visible_child(self.new_search)
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
# Extra Object Creation
|
# Extra Object Creation
|
||||||
self.remote = None
|
self.remote = None
|
||||||
self.installation = None
|
self.installation = None
|
||||||
@@ -134,9 +134,9 @@ class ResultsPage(Adw.NavigationPage):
|
|||||||
self.pending_page = None
|
self.pending_page = None
|
||||||
self.loading = LoadingStatus(_("Searching"), _("This should only take a moment"))
|
self.loading = LoadingStatus(_("Searching"), _("This should only take a moment"))
|
||||||
self.install_page = None
|
self.install_page = None
|
||||||
|
|
||||||
# Connections
|
# Connections
|
||||||
self.search_entry.connect("search-changed", self.on_search)
|
self.search_entry.connect("search-changed", self.on_search)
|
||||||
|
|
||||||
# Apply
|
# Apply
|
||||||
self.stack.add_child(self.loading)
|
self.stack.add_child(self.loading)
|
||||||
|
|||||||
@@ -9,33 +9,33 @@ from .file_install_dialog import FileInstallDialog
|
|||||||
class SelectPage(Adw.NavigationPage):
|
class SelectPage(Adw.NavigationPage):
|
||||||
__gtype_name__ = "SelectPage"
|
__gtype_name__ = "SelectPage"
|
||||||
gtc = Gtk.Template.Child
|
gtc = Gtk.Template.Child
|
||||||
|
|
||||||
nav_view = gtc()
|
nav_view = gtc()
|
||||||
results_page = gtc()
|
results_page = gtc()
|
||||||
remotes_group = gtc()
|
remotes_group = gtc()
|
||||||
add_remote_row = gtc()
|
add_remote_row = gtc()
|
||||||
open_row = gtc()
|
open_row = gtc()
|
||||||
|
|
||||||
def start_loading(self):
|
def start_loading(self):
|
||||||
self.nav_view.pop()
|
self.nav_view.pop()
|
||||||
for row in self.remote_rows:
|
for row in self.remote_rows:
|
||||||
self.remotes_group.remove(row)
|
self.remotes_group.remove(row)
|
||||||
self.remote_rows.clear()
|
self.remote_rows.clear()
|
||||||
|
|
||||||
def end_loading(self):
|
def end_loading(self):
|
||||||
for installation, remotes in HostInfo.remotes.items():
|
for installation, remotes in HostInfo.remotes.items():
|
||||||
for remote in remotes:
|
for remote in remotes:
|
||||||
if remote.disabled:
|
if remote.disabled:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
row = Adw.ActionRow(title=remote.title, subtitle=_("Installation: {}").format(installation), activatable=True)
|
row = Adw.ActionRow(title=remote.title, subtitle=_("Installation: {}").format(installation), activatable=True)
|
||||||
row.add_suffix(Gtk.Image(icon_name="right-large-symbolic"))
|
row.add_suffix(Gtk.Image(icon_name="right-large-symbolic"))
|
||||||
row.connect("activated", self.results_page.show_remote, remote, installation, self.nav_view)
|
row.connect("activated", self.results_page.show_remote, remote, installation, self.nav_view)
|
||||||
self.remotes_group.add(row)
|
self.remotes_group.add(row)
|
||||||
self.remote_rows.append(row)
|
self.remote_rows.append(row)
|
||||||
|
|
||||||
self.remotes_group.set_visible(len(self.remote_rows) != 0)
|
self.remotes_group.set_visible(len(self.remote_rows) != 0)
|
||||||
|
|
||||||
def local_install_apply_callback(self, installation, file_names):
|
def local_install_apply_callback(self, installation, file_names):
|
||||||
install_page = HostInfo.main_window.pages[HostInfo.main_window.install_row]
|
install_page = HostInfo.main_window.pages[HostInfo.main_window.install_row]
|
||||||
requests = []
|
requests = []
|
||||||
@@ -47,25 +47,25 @@ class SelectPage(Adw.NavigationPage):
|
|||||||
"package_names": [file.get_path()],
|
"package_names": [file.get_path()],
|
||||||
"extra_flags": [],
|
"extra_flags": [],
|
||||||
})
|
})
|
||||||
|
|
||||||
install_page.install_packages(requests)
|
install_page.install_packages(requests)
|
||||||
|
|
||||||
def file_dialog_handler(self, files):
|
def file_dialog_handler(self, files):
|
||||||
FileInstallDialog(self, files, self.local_install_apply_callback).present(HostInfo.main_window)
|
FileInstallDialog(self, files, self.local_install_apply_callback).present(HostInfo.main_window)
|
||||||
|
|
||||||
def file_choose_callback(self, object, result):
|
def file_choose_callback(self, object, result):
|
||||||
try:
|
try:
|
||||||
files = object.open_multiple_finish(result)
|
files = object.open_multiple_finish(result)
|
||||||
if not files:
|
if not files:
|
||||||
HostInfo.main_window.toast_overlay.add_toast(ErrorToast(_("Could not add files"), _("No files were found to install")))
|
HostInfo.main_window.toast_overlay.add_toast(ErrorToast(_("Could not add files"), _("No files were found to install")))
|
||||||
return
|
return
|
||||||
|
|
||||||
self.file_dialog_handler(files)
|
self.file_dialog_handler(files)
|
||||||
|
|
||||||
except GLib.GError as gle:
|
except GLib.GError as gle:
|
||||||
if not (gle.domain == "gtk-dialog-error-quark" and gle.code == 2):
|
if not (gle.domain == "gtk-dialog-error-quark" and gle.code == 2):
|
||||||
HostInfo.main_window.toast_overlay.add_toast(ErrorToast(_("Could not add files"), str(gle)).toast)
|
HostInfo.main_window.toast_overlay.add_toast(ErrorToast(_("Could not add files"), str(gle)).toast)
|
||||||
|
|
||||||
def on_open(self, *args):
|
def on_open(self, *args):
|
||||||
file_filter = Gtk.FileFilter(name=_("Flatpaks"))
|
file_filter = Gtk.FileFilter(name=_("Flatpaks"))
|
||||||
file_filter.add_suffix("flatpak")
|
file_filter.add_suffix("flatpak")
|
||||||
@@ -76,13 +76,13 @@ class SelectPage(Adw.NavigationPage):
|
|||||||
file_chooser.set_filters(filters)
|
file_chooser.set_filters(filters)
|
||||||
file_chooser.set_default_filter(file_filter)
|
file_chooser.set_default_filter(file_filter)
|
||||||
file_chooser.open_multiple(HostInfo.main_window, None, self.file_choose_callback)
|
file_chooser.open_multiple(HostInfo.main_window, None, self.file_choose_callback)
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
# Extra Object Creation
|
# Extra Object Creation
|
||||||
self.remote_rows = []
|
self.remote_rows = []
|
||||||
|
|
||||||
# Connections
|
# Connections
|
||||||
self.add_remote_row.connect("activated", lambda *_: HostInfo.main_window.activate_row(HostInfo.main_window.remotes_row))
|
self.add_remote_row.connect("activated", lambda *_: HostInfo.main_window.activate_row(HostInfo.main_window.remotes_row))
|
||||||
self.nav_view.connect("popped", self.results_page.on_back)
|
self.nav_view.connect("popped", self.results_page.on_back)
|
||||||
|
|||||||
56
src/main.py
56
src/main.py
@@ -32,10 +32,10 @@ from .error_toast import ErrorToast
|
|||||||
|
|
||||||
class WarehouseApplication(Adw.Application):
|
class WarehouseApplication(Adw.Application):
|
||||||
"""The main application singleton class."""
|
"""The main application singleton class."""
|
||||||
|
|
||||||
troubleshooting = "OS: {os}\nWarehouse version: {wv}\nGTK: {gtk}\nlibadwaita: {adw}\nApp ID: {app_id}\nProfile: {profile}\nLanguage: {lang}"
|
troubleshooting = "OS: {os}\nWarehouse version: {wv}\nGTK: {gtk}\nlibadwaita: {adw}\nApp ID: {app_id}\nProfile: {profile}\nLanguage: {lang}"
|
||||||
version = Config.VERSION
|
version = Config.VERSION
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__(
|
super().__init__(
|
||||||
application_id="io.github.flattool.Warehouse",
|
application_id="io.github.flattool.Warehouse",
|
||||||
@@ -47,13 +47,13 @@ class WarehouseApplication(Adw.Application):
|
|||||||
self.create_action("open-menu", lambda *_: self.props.active_window.main_menu.popup(), ["F10"])
|
self.create_action("open-menu", lambda *_: self.props.active_window.main_menu.popup(), ["F10"])
|
||||||
self.create_action("refresh", lambda *_: self.props.active_window.refresh_handler(), ["<primary>r", "F5"])
|
self.create_action("refresh", lambda *_: self.props.active_window.refresh_handler(), ["<primary>r", "F5"])
|
||||||
self.create_action("open-files", self.on_open_files_shortcut, ["<primary>o"])
|
self.create_action("open-files", self.on_open_files_shortcut, ["<primary>o"])
|
||||||
|
|
||||||
self.create_action("show-packages-page", lambda *_: self.props.active_window.switch_page_shortcut_handler("p"), ["<primary>p"])
|
self.create_action("show-packages-page", lambda *_: self.props.active_window.switch_page_shortcut_handler("p"), ["<primary>p"])
|
||||||
self.create_action("show-remotes-page", lambda *_: self.props.active_window.switch_page_shortcut_handler("m"), ["<primary>m"])
|
self.create_action("show-remotes-page", lambda *_: self.props.active_window.switch_page_shortcut_handler("m"), ["<primary>m"])
|
||||||
self.create_action("show-user-data-page", lambda *_: self.props.active_window.switch_page_shortcut_handler("d"), ["<primary>d"])
|
self.create_action("show-user-data-page", lambda *_: self.props.active_window.switch_page_shortcut_handler("d"), ["<primary>d"])
|
||||||
self.create_action("show-snapshots-page", lambda *_: self.props.active_window.switch_page_shortcut_handler("s"), ["<primary>s"])
|
self.create_action("show-snapshots-page", lambda *_: self.props.active_window.switch_page_shortcut_handler("s"), ["<primary>s"])
|
||||||
self.create_action("show-install-page", lambda *_: self.props.active_window.switch_page_shortcut_handler("i"), ["<primary>i"])
|
self.create_action("show-install-page", lambda *_: self.props.active_window.switch_page_shortcut_handler("i"), ["<primary>i"])
|
||||||
|
|
||||||
self.create_action("toggle-select-mode", self.on_toggle_select_mode_shortcut, ["<primary>b", "<primary>Return"])
|
self.create_action("toggle-select-mode", self.on_toggle_select_mode_shortcut, ["<primary>b", "<primary>Return"])
|
||||||
self.create_action("toggle-selection-kp-enter", self.on_toggle_select_mode_shortcut, ["<primary>KP_Enter"]) # Doesn't show in the shortcuts window
|
self.create_action("toggle-selection-kp-enter", self.on_toggle_select_mode_shortcut, ["<primary>KP_Enter"]) # Doesn't show in the shortcuts window
|
||||||
self.create_action("search-mode", self.on_search_mode_shortcut, ["<primary>f"])
|
self.create_action("search-mode", self.on_search_mode_shortcut, ["<primary>f"])
|
||||||
@@ -61,9 +61,9 @@ class WarehouseApplication(Adw.Application):
|
|||||||
self.create_action("new", self.on_new_shortcut, ["<primary>n"])
|
self.create_action("new", self.on_new_shortcut, ["<primary>n"])
|
||||||
self.create_action("active-data-view", lambda *_: self.on_data_view_shortcut(True), ["<Alt>1"])
|
self.create_action("active-data-view", lambda *_: self.on_data_view_shortcut(True), ["<Alt>1"])
|
||||||
self.create_action("leftover-data-view", lambda *_: self.on_data_view_shortcut(False), ["<Alt>2"])
|
self.create_action("leftover-data-view", lambda *_: self.on_data_view_shortcut(False), ["<Alt>2"])
|
||||||
|
|
||||||
self.is_dialog_open = False
|
self.is_dialog_open = False
|
||||||
|
|
||||||
gtk_version = (
|
gtk_version = (
|
||||||
str(Gtk.MAJOR_VERSION)
|
str(Gtk.MAJOR_VERSION)
|
||||||
+ "."
|
+ "."
|
||||||
@@ -80,7 +80,7 @@ class WarehouseApplication(Adw.Application):
|
|||||||
)
|
)
|
||||||
os_string = GLib.get_os_info("NAME") + " " + GLib.get_os_info("VERSION")
|
os_string = GLib.get_os_info("NAME") + " " + GLib.get_os_info("VERSION")
|
||||||
lang = GLib.environ_getenv(GLib.get_environ(), "LANG")
|
lang = GLib.environ_getenv(GLib.get_environ(), "LANG")
|
||||||
|
|
||||||
self.troubleshooting = self.troubleshooting.format(
|
self.troubleshooting = self.troubleshooting.format(
|
||||||
os=os_string,
|
os=os_string,
|
||||||
wv=self.version,
|
wv=self.version,
|
||||||
@@ -90,22 +90,22 @@ class WarehouseApplication(Adw.Application):
|
|||||||
app_id=self.get_application_id(),
|
app_id=self.get_application_id(),
|
||||||
lang=lang,
|
lang=lang,
|
||||||
)
|
)
|
||||||
|
|
||||||
def on_open_files_shortcut(self, *args):
|
def on_open_files_shortcut(self, *args):
|
||||||
window = self.props.active_window
|
window = self.props.active_window
|
||||||
|
|
||||||
def file_choose_callback(object, result):
|
def file_choose_callback(object, result):
|
||||||
try:
|
try:
|
||||||
files = object.open_multiple_finish(result)
|
files = object.open_multiple_finish(result)
|
||||||
if not files:
|
if not files:
|
||||||
window.toast_overlay.add_toast(ErrorToast(_("Could not add files"), _("No files were found")).toast)
|
window.toast_overlay.add_toast(ErrorToast(_("Could not add files"), _("No files were found")).toast)
|
||||||
return
|
return
|
||||||
|
|
||||||
window.on_file_drop(None, files, None, None)
|
window.on_file_drop(None, files, None, None)
|
||||||
except GLib.GError as gle:
|
except GLib.GError as gle:
|
||||||
if not (gle.domain == "gtk-dialog-error-quark" and gle.code == 2):
|
if not (gle.domain == "gtk-dialog-error-quark" and gle.code == 2):
|
||||||
window.toast_overlay.add_toast(ErrorToast(_("Could not add files"), str(gle)).toast)
|
window.toast_overlay.add_toast(ErrorToast(_("Could not add files"), str(gle)).toast)
|
||||||
|
|
||||||
file_filter = Gtk.FileFilter(name=_("Flatpaks & Remotes"))
|
file_filter = Gtk.FileFilter(name=_("Flatpaks & Remotes"))
|
||||||
file_filter.add_suffix("flatpak")
|
file_filter.add_suffix("flatpak")
|
||||||
file_filter.add_suffix("flatpakref")
|
file_filter.add_suffix("flatpakref")
|
||||||
@@ -116,63 +116,63 @@ class WarehouseApplication(Adw.Application):
|
|||||||
file_chooser.set_filters(filters)
|
file_chooser.set_filters(filters)
|
||||||
file_chooser.set_default_filter(file_filter)
|
file_chooser.set_default_filter(file_filter)
|
||||||
file_chooser.open_multiple(window, None, file_choose_callback)
|
file_chooser.open_multiple(window, None, file_choose_callback)
|
||||||
|
|
||||||
def on_toggle_select_mode_shortcut(self, *args):
|
def on_toggle_select_mode_shortcut(self, *args):
|
||||||
try:
|
try:
|
||||||
button = self.props.active_window.stack.get_visible_child().select_button
|
button = self.props.active_window.stack.get_visible_child().select_button
|
||||||
button.set_active(not button.get_active())
|
button.set_active(not button.get_active())
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def on_search_mode_shortcut(self, *args):
|
def on_search_mode_shortcut(self, *args):
|
||||||
try:
|
try:
|
||||||
button = self.props.active_window.stack.get_visible_child().search_button
|
button = self.props.active_window.stack.get_visible_child().search_button
|
||||||
button.set_active(True)
|
button.set_active(True)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def on_filter_shortcut(self, *args):
|
def on_filter_shortcut(self, *args):
|
||||||
try:
|
try:
|
||||||
button = self.props.active_window.stack.get_visible_child().filter_button
|
button = self.props.active_window.stack.get_visible_child().filter_button
|
||||||
button.set_active(not button.get_active())
|
button.set_active(not button.get_active())
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
try:
|
try:
|
||||||
button = self.props.active_window.stack.get_visible_child().sort_button
|
button = self.props.active_window.stack.get_visible_child().sort_button
|
||||||
button.set_active(True)
|
button.set_active(True)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
try:
|
try:
|
||||||
button = self.props.active_window.stack.get_visible_child().show_disabled_button
|
button = self.props.active_window.stack.get_visible_child().show_disabled_button
|
||||||
if button.get_visible():
|
if button.get_visible():
|
||||||
button.set_active(not button.get_active())
|
button.set_active(not button.get_active())
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def on_new_shortcut(self, *args):
|
def on_new_shortcut(self, *args):
|
||||||
page = self.props.active_window.stack.get_visible_child()
|
page = self.props.active_window.stack.get_visible_child()
|
||||||
try:
|
try:
|
||||||
page.new_custom_handler()
|
page.new_custom_handler()
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
try:
|
try:
|
||||||
page.on_new()
|
page.on_new()
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def on_delete_shortcut(self, *args):
|
def on_delete_shortcut(self, *args):
|
||||||
page = self.props.active_window.stack.get_visible_child()
|
page = self.props.active_window.stack.get_visible_child()
|
||||||
try:
|
try:
|
||||||
if not page.select_button.get_active():
|
if not page.select_button.get_active():
|
||||||
return
|
return
|
||||||
|
|
||||||
page.select_trash_handler()
|
page.select_trash_handler()
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def on_data_view_shortcut(self, is_active):
|
def on_data_view_shortcut(self, is_active):
|
||||||
page = self.props.active_window.stack.get_visible_child()
|
page = self.props.active_window.stack.get_visible_child()
|
||||||
try:
|
try:
|
||||||
@@ -181,10 +181,10 @@ class WarehouseApplication(Adw.Application):
|
|||||||
page.stack.set_visible_child(adp if is_active else ldp)
|
page.stack.set_visible_child(adp if is_active else ldp)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def do_activate(self):
|
def do_activate(self):
|
||||||
"""Called when the application is activated.
|
"""Called when the application is activated.
|
||||||
|
|
||||||
We raise the application's main window, creating it if
|
We raise the application's main window, creating it if
|
||||||
necessary.
|
necessary.
|
||||||
"""
|
"""
|
||||||
@@ -192,7 +192,7 @@ class WarehouseApplication(Adw.Application):
|
|||||||
if not win:
|
if not win:
|
||||||
win = WarehouseWindow(application=self)
|
win = WarehouseWindow(application=self)
|
||||||
win.present()
|
win.present()
|
||||||
|
|
||||||
def on_about_action(self, widget, _a):
|
def on_about_action(self, widget, _a):
|
||||||
"""Callback for the app.about action."""
|
"""Callback for the app.about action."""
|
||||||
about = Adw.AboutDialog(
|
about = Adw.AboutDialog(
|
||||||
@@ -233,14 +233,14 @@ class WarehouseApplication(Adw.Application):
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
about.present(self.props.active_window)
|
about.present(self.props.active_window)
|
||||||
|
|
||||||
def on_preferences_action(self, widget, _):
|
def on_preferences_action(self, widget, _):
|
||||||
"""Callback for the app.preferences action."""
|
"""Callback for the app.preferences action."""
|
||||||
print("app.preferences action activated")
|
print("app.preferences action activated")
|
||||||
|
|
||||||
def create_action(self, name, callback, shortcuts=None):
|
def create_action(self, name, callback, shortcuts=None):
|
||||||
"""Add an application action.
|
"""Add an application action.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
name: the name of the action
|
name: the name of the action
|
||||||
callback: the function to be called when the action is activated
|
callback: the function to be called when the action is activated
|
||||||
@@ -251,7 +251,7 @@ class WarehouseApplication(Adw.Application):
|
|||||||
self.add_action(action)
|
self.add_action(action)
|
||||||
if shortcuts:
|
if shortcuts:
|
||||||
self.set_accels_for_action(f"app.{name}", shortcuts)
|
self.set_accels_for_action(f"app.{name}", shortcuts)
|
||||||
|
|
||||||
def main(version):
|
def main(version):
|
||||||
"""The application's entry point."""
|
"""The application's entry point."""
|
||||||
app = WarehouseApplication()
|
app = WarehouseApplication()
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ template $WarehouseWindow: Adw.ApplicationWindow {
|
|||||||
label: _("Remotes");
|
label: _("Remotes");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Box user_data_row {
|
Box user_data_row {
|
||||||
margin-top: 12;
|
margin-top: 12;
|
||||||
margin-bottom: 12;
|
margin-bottom: 12;
|
||||||
|
|||||||
@@ -54,11 +54,11 @@ class WarehouseWindow(Adw.ApplicationWindow):
|
|||||||
for _, page in self.pages.items():
|
for _, page in self.pages.items():
|
||||||
if page.instance:
|
if page.instance:
|
||||||
page.instance.end_loading()
|
page.instance.end_loading()
|
||||||
|
|
||||||
self.refresh_button.set_sensitive(True)
|
self.refresh_button.set_sensitive(True)
|
||||||
self.refresh_requested = False
|
self.refresh_requested = False
|
||||||
self.remove_refresh_lockout("refresh handler direct")
|
self.remove_refresh_lockout("refresh handler direct")
|
||||||
|
|
||||||
def do_refresh(self):
|
def do_refresh(self):
|
||||||
self.start_loading()
|
self.start_loading()
|
||||||
self.refresh_button.set_sensitive(False)
|
self.refresh_button.set_sensitive(False)
|
||||||
@@ -72,15 +72,15 @@ class WarehouseWindow(Adw.ApplicationWindow):
|
|||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
self.refresh_requested = True
|
self.refresh_requested = True
|
||||||
|
|
||||||
def add_refresh_lockout(self, reason):
|
def add_refresh_lockout(self, reason):
|
||||||
self.refresh_lockouts.append(reason)
|
self.refresh_lockouts.append(reason)
|
||||||
self.refresh_button.set_sensitive(False)
|
self.refresh_button.set_sensitive(False)
|
||||||
|
|
||||||
def remove_refresh_lockout(self, reason):
|
def remove_refresh_lockout(self, reason):
|
||||||
if reason in self.refresh_lockouts:
|
if reason in self.refresh_lockouts:
|
||||||
self.refresh_lockouts.remove(reason)
|
self.refresh_lockouts.remove(reason)
|
||||||
|
|
||||||
if len(self.refresh_lockouts) == 0:
|
if len(self.refresh_lockouts) == 0:
|
||||||
if self.refresh_requested:
|
if self.refresh_requested:
|
||||||
self.do_refresh()
|
self.do_refresh()
|
||||||
@@ -116,7 +116,7 @@ class WarehouseWindow(Adw.ApplicationWindow):
|
|||||||
|
|
||||||
if not page_found:
|
if not page_found:
|
||||||
self.navigation_row_listbox.get_row_at_index(0).activate()
|
self.navigation_row_listbox.get_row_at_index(0).activate()
|
||||||
|
|
||||||
def on_file_drop(self, drop_target, value, x, y):
|
def on_file_drop(self, drop_target, value, x, y):
|
||||||
try:
|
try:
|
||||||
paks = []
|
paks = []
|
||||||
@@ -135,7 +135,7 @@ class WarehouseWindow(Adw.ApplicationWindow):
|
|||||||
dialog.add_response("continue", _("OK"))
|
dialog.add_response("continue", _("OK"))
|
||||||
dialog.present(self)
|
dialog.present(self)
|
||||||
return
|
return
|
||||||
|
|
||||||
if len(remotes) > 0 and len(paks) > 0:
|
if len(remotes) > 0 and len(paks) > 0:
|
||||||
dialog = Adw.AlertDialog(
|
dialog = Adw.AlertDialog(
|
||||||
heading=_("Mixed Filetypes"),
|
heading=_("Mixed Filetypes"),
|
||||||
@@ -145,7 +145,7 @@ class WarehouseWindow(Adw.ApplicationWindow):
|
|||||||
dialog.add_response("continue", _("OK"))
|
dialog.add_response("continue", _("OK"))
|
||||||
dialog.present(self)
|
dialog.present(self)
|
||||||
return
|
return
|
||||||
|
|
||||||
if len(remotes) > 1:
|
if len(remotes) > 1:
|
||||||
dialog = Adw.AlertDialog(
|
dialog = Adw.AlertDialog(
|
||||||
heading=_("Too Many Remotes"),
|
heading=_("Too Many Remotes"),
|
||||||
@@ -154,7 +154,7 @@ class WarehouseWindow(Adw.ApplicationWindow):
|
|||||||
dialog.add_response("continue", _("OK"))
|
dialog.add_response("continue", _("OK"))
|
||||||
dialog.present(self)
|
dialog.present(self)
|
||||||
return
|
return
|
||||||
|
|
||||||
if len(remotes) == 1:
|
if len(remotes) == 1:
|
||||||
# Adding a remote
|
# Adding a remote
|
||||||
self.activate_row(self.remotes_row)
|
self.activate_row(self.remotes_row)
|
||||||
@@ -165,22 +165,22 @@ class WarehouseWindow(Adw.ApplicationWindow):
|
|||||||
self.activate_row(self.install_row)
|
self.activate_row(self.install_row)
|
||||||
install_page = self.pages[self.install_row]
|
install_page = self.pages[self.install_row]
|
||||||
install_page.select_page.file_dialog_handler(paks)
|
install_page.select_page.file_dialog_handler(paks)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.toast_overlay.add_toast(ErrorToast(_("Could not open files"), str(e)).toast)
|
self.toast_overlay.add_toast(ErrorToast(_("Could not open files"), str(e)).toast)
|
||||||
|
|
||||||
def on_drop_enter(self, *args):
|
def on_drop_enter(self, *args):
|
||||||
self.main_split.add_css_class("blurred")
|
self.main_split.add_css_class("blurred")
|
||||||
self.file_drop_revealer.set_reveal_child(True)
|
self.file_drop_revealer.set_reveal_child(True)
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
def on_drop_leave(self, *args):
|
def on_drop_leave(self, *args):
|
||||||
self.main_split.remove_css_class("blurred")
|
self.main_split.remove_css_class("blurred")
|
||||||
self.file_drop_revealer.set_reveal_child(False)
|
self.file_drop_revealer.set_reveal_child(False)
|
||||||
|
|
||||||
def switch_page_shortcut_handler(self, letter):
|
def switch_page_shortcut_handler(self, letter):
|
||||||
self.activate_row(self.shortcut_to_pages[letter])
|
self.activate_row(self.shortcut_to_pages[letter])
|
||||||
|
|
||||||
def key_handler(self, controller, keyval, keycode, state):
|
def key_handler(self, controller, keyval, keycode, state):
|
||||||
page = self.stack.get_visible_child()
|
page = self.stack.get_visible_child()
|
||||||
if keyval == Gdk.KEY_BackSpace or keyval == Gdk.KEY_Delete:
|
if keyval == Gdk.KEY_BackSpace or keyval == Gdk.KEY_Delete:
|
||||||
@@ -194,7 +194,7 @@ class WarehouseWindow(Adw.ApplicationWindow):
|
|||||||
page.on_escape_handler()
|
page.on_escape_handler()
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
@@ -239,7 +239,7 @@ class WarehouseWindow(Adw.ApplicationWindow):
|
|||||||
file_drop.connect("leave", self.on_drop_leave)
|
file_drop.connect("leave", self.on_drop_leave)
|
||||||
event_controller.connect("key-pressed", self.key_handler)
|
event_controller.connect("key-pressed", self.key_handler)
|
||||||
self.refresh_button.connect("clicked", self.refresh_handler)
|
self.refresh_button.connect("clicked", self.refresh_handler)
|
||||||
|
|
||||||
# Apply again
|
# Apply again
|
||||||
self.start_loading()
|
self.start_loading()
|
||||||
HostInfo.get_flatpaks(callback=self.end_loading)
|
HostInfo.get_flatpaks(callback=self.end_loading)
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ class PackageInstallWorker:
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
"""
|
"""
|
||||||
|
|
||||||
groups = None
|
groups = None
|
||||||
process = None
|
process = None
|
||||||
callback = None
|
callback = None
|
||||||
@@ -24,19 +24,19 @@ class PackageInstallWorker:
|
|||||||
loading_status = None
|
loading_status = None
|
||||||
total_groups = 0
|
total_groups = 0
|
||||||
cancelled = False
|
cancelled = False
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def update_status(this, index, package_ratio, complete, total):
|
def update_status(this, index, package_ratio, complete, total):
|
||||||
group_ratio = (package_ratio + complete) / (total or 1)
|
group_ratio = (package_ratio + complete) / (total or 1)
|
||||||
final_ratio = (group_ratio + index) / (this.total_groups or 1)
|
final_ratio = (group_ratio + index) / (this.total_groups or 1)
|
||||||
|
|
||||||
print(f"gr: {(package_ratio + complete) / (total or 1):.2f}, fr: {((package_ratio + complete) / (total or 1) + index) / (this.total_groups or 1):.2f}")
|
print(f"gr: {(package_ratio + complete) / (total or 1):.2f}, fr: {((package_ratio + complete) / (total or 1) + index) / (this.total_groups or 1):.2f}")
|
||||||
print("i:", index, ", g:", this.total_groups, ", r:", package_ratio, ", c:", complete, ", t:", total)
|
print("i:", index, ", g:", this.total_groups, ", r:", package_ratio, ", c:", complete, ", t:", total)
|
||||||
print("=======================================")
|
print("=======================================")
|
||||||
|
|
||||||
if not this.loading_status is None:
|
if not this.loading_status is None:
|
||||||
GLib.idle_add(lambda *_: this.loading_status.progress_bar.set_fraction(final_ratio))
|
GLib.idle_add(lambda *_: this.loading_status.progress_bar.set_fraction(final_ratio))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def install_thread(this):
|
def install_thread(this):
|
||||||
try:
|
try:
|
||||||
@@ -44,20 +44,20 @@ class PackageInstallWorker:
|
|||||||
for index, group in enumerate(this.groups):
|
for index, group in enumerate(this.groups):
|
||||||
if this.cancelled:
|
if this.cancelled:
|
||||||
return
|
return
|
||||||
|
|
||||||
real_installation = ""
|
real_installation = ""
|
||||||
installation = group['installation']
|
installation = group['installation']
|
||||||
if installation == "user" or installation == "system":
|
if installation == "user" or installation == "system":
|
||||||
real_installation = f"--{installation}"
|
real_installation = f"--{installation}"
|
||||||
else:
|
else:
|
||||||
real_installation = f"--installation={installation}"
|
real_installation = f"--installation={installation}"
|
||||||
|
|
||||||
cmd = ['flatpak-spawn', '--host', 'flatpak', 'install', '-y']
|
cmd = ['flatpak-spawn', '--host', 'flatpak', 'install', '-y']
|
||||||
|
|
||||||
# Handle local file installs. They don't have a remote specified
|
# Handle local file installs. They don't have a remote specified
|
||||||
if group['remote'] != "local_file":
|
if group['remote'] != "local_file":
|
||||||
cmd.append(group['remote'])
|
cmd.append(group['remote'])
|
||||||
|
|
||||||
cmd += [real_installation] + group['package_names'] + group['extra_flags']
|
cmd += [real_installation] + group['package_names'] + group['extra_flags']
|
||||||
this.process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
this.process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
||||||
percent_pattern = r'\d{1,3}%'
|
percent_pattern = r'\d{1,3}%'
|
||||||
@@ -75,34 +75,34 @@ class PackageInstallWorker:
|
|||||||
this.update_status(index, ratio, complete, total)
|
this.update_status(index, ratio, complete, total)
|
||||||
else:
|
else:
|
||||||
this.update_status(index, ratio, 0, 1)
|
this.update_status(index, ratio, 0, 1)
|
||||||
|
|
||||||
this.process.wait(timeout=10)
|
this.process.wait(timeout=10)
|
||||||
if error := this.process.communicate()[1].strip():
|
if error := this.process.communicate()[1].strip():
|
||||||
errors.append(error)
|
errors.append(error)
|
||||||
|
|
||||||
if len(errors) > 0:
|
if len(errors) > 0:
|
||||||
this.on_error(_("Errors occurred during installation"), "\n".join(errors))
|
this.on_error(_("Errors occurred during installation"), "\n".join(errors))
|
||||||
|
|
||||||
except subprocess.TimeoutExpired as te:
|
except subprocess.TimeoutExpired as te:
|
||||||
this.process.terminate()
|
this.process.terminate()
|
||||||
this.on_error(_("Error occurred during installation"), _("Failed to exit cleanly"))
|
this.on_error(_("Error occurred during installation"), _("Failed to exit cleanly"))
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
this.process.terminate()
|
this.process.terminate()
|
||||||
this.on_error(_("Error occurred during installation"), str(e))
|
this.on_error(_("Error occurred during installation"), str(e))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def cancel(this):
|
def cancel(this):
|
||||||
if this.process is None:
|
if this.process is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
this.cancelled = True
|
this.cancelled = True
|
||||||
this.process.terminate()
|
this.process.terminate()
|
||||||
this.process.wait(timeout=10)
|
this.process.wait(timeout=10)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
this.on_error(_("Could not cancel installation"), str(e))
|
this.on_error(_("Could not cancel installation"), str(e))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def on_done(this, *args):
|
def on_done(this, *args):
|
||||||
this.process = None
|
this.process = None
|
||||||
@@ -110,31 +110,31 @@ class PackageInstallWorker:
|
|||||||
HostInfo.main_window.remove_refresh_lockout("installing packages")
|
HostInfo.main_window.remove_refresh_lockout("installing packages")
|
||||||
if not this.loading_status is None:
|
if not this.loading_status is None:
|
||||||
this.loading_status.progress_bar.set_fraction(0.0)
|
this.loading_status.progress_bar.set_fraction(0.0)
|
||||||
|
|
||||||
if not this.callback is None:
|
if not this.callback is None:
|
||||||
this.callback()
|
this.callback()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def on_error(this, user_facing_label, error_message):
|
def on_error(this, user_facing_label, error_message):
|
||||||
if not this.error_callback is None:
|
if not this.error_callback is None:
|
||||||
this.error_callback(user_facing_label, error_message)
|
this.error_callback(user_facing_label, error_message)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def install(this, groups, loading_status=None, callback=None, error_callback=None):
|
def install(this, groups, loading_status=None, callback=None, error_callback=None):
|
||||||
if not this.process is None:
|
if not this.process is None:
|
||||||
this.on_error(_("Could not install packages"), _("Packages are currently being installed."))
|
this.on_error(_("Could not install packages"), _("Packages are currently being installed."))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
this.callback = callback
|
this.callback = callback
|
||||||
this.groups = groups
|
this.groups = groups
|
||||||
this.total_groups = len(groups)
|
this.total_groups = len(groups)
|
||||||
this.loading_status = loading_status
|
this.loading_status = loading_status
|
||||||
this.error_callback = error_callback
|
this.error_callback = error_callback
|
||||||
|
|
||||||
if this.total_groups < 1:
|
if this.total_groups < 1:
|
||||||
this.on_error(_("Could not install packages"), _("No packages were requested to be installed."))
|
this.on_error(_("Could not install packages"), _("No packages were requested to be installed."))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
HostInfo.main_window.add_refresh_lockout("installing packages")
|
HostInfo.main_window.add_refresh_lockout("installing packages")
|
||||||
Gio.Task.new(None, None, this.on_done).run_in_thread(lambda *_: this.install_thread())
|
Gio.Task.new(None, None, this.on_done).run_in_thread(lambda *_: this.install_thread())
|
||||||
return True
|
return True
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ class FiltersPage(Adw.NavigationPage):
|
|||||||
def app_check_handler(self, *args):
|
def app_check_handler(self, *args):
|
||||||
self.show_apps = self.app_check.get_active()
|
self.show_apps = self.app_check.get_active()
|
||||||
self.update_gsettings()
|
self.update_gsettings()
|
||||||
|
|
||||||
def runtime_check_handler(self, *args):
|
def runtime_check_handler(self, *args):
|
||||||
self.show_runtimes = self.runtime_check.get_active()
|
self.show_runtimes = self.runtime_check.get_active()
|
||||||
self.update_gsettings()
|
self.update_gsettings()
|
||||||
@@ -76,21 +76,21 @@ class FiltersPage(Adw.NavigationPage):
|
|||||||
self.remotes_string += f"{row.item.name}<>{row.installation};"
|
self.remotes_string += f"{row.item.name}<>{row.installation};"
|
||||||
elif state:
|
elif state:
|
||||||
self.remotes_string.replace(f"{row.item.name}<>{row.installation};", "")
|
self.remotes_string.replace(f"{row.item.name}<>{row.installation};", "")
|
||||||
|
|
||||||
self.update_gsettings()
|
self.update_gsettings()
|
||||||
|
|
||||||
def all_runtimes_handler(self, switch, state):
|
def all_runtimes_handler(self, switch, state):
|
||||||
self.runtimes_string = ""
|
self.runtimes_string = ""
|
||||||
if not state:
|
if not state:
|
||||||
self.runtimes_string = "all"
|
self.runtimes_string = "all"
|
||||||
|
|
||||||
for row in self.runtime_rows:
|
for row in self.runtime_rows:
|
||||||
row.set_visible(state)
|
row.set_visible(state)
|
||||||
if state and row.check_button.get_active():
|
if state and row.check_button.get_active():
|
||||||
self.runtimes_string += f"{row.item};"
|
self.runtimes_string += f"{row.item};"
|
||||||
elif state:
|
elif state:
|
||||||
self.runtimes_string.replace(f"{row.item};", "")
|
self.runtimes_string.replace(f"{row.item};", "")
|
||||||
|
|
||||||
self.update_gsettings()
|
self.update_gsettings()
|
||||||
|
|
||||||
def remote_row_check_handler(self, row):
|
def remote_row_check_handler(self, row):
|
||||||
@@ -160,7 +160,7 @@ class FiltersPage(Adw.NavigationPage):
|
|||||||
self.show_runtimes = self.settings.get_boolean("show-runtimes")
|
self.show_runtimes = self.settings.get_boolean("show-runtimes")
|
||||||
self.remotes_string = self.settings.get_string("remotes-list")
|
self.remotes_string = self.settings.get_string("remotes-list")
|
||||||
self.runtimes_string = self.settings.get_string("runtimes-list")
|
self.runtimes_string = self.settings.get_string("runtimes-list")
|
||||||
|
|
||||||
self.app_check.set_active(self.show_apps)
|
self.app_check.set_active(self.show_apps)
|
||||||
self.runtime_check.set_active(self.show_runtimes)
|
self.runtime_check.set_active(self.show_runtimes)
|
||||||
|
|
||||||
|
|||||||
@@ -47,42 +47,41 @@ class PackagesPage(Adw.BreakpointBin):
|
|||||||
uninstall_button = gtc()
|
uninstall_button = gtc()
|
||||||
properties_page = gtc()
|
properties_page = gtc()
|
||||||
filters_page = gtc()
|
filters_page = gtc()
|
||||||
|
|
||||||
# Referred to in the main window
|
# Referred to in the main window
|
||||||
# It is used to determine if a new page should be made or not
|
# It is used to determine if a new page should be made or not
|
||||||
# This must be set to the created object from within the class's __init__ method
|
# This must be set to the created object from within the class's __init__ method
|
||||||
instance = None
|
instance = None
|
||||||
page_name = "packages"
|
page_name = "packages"
|
||||||
last_activated_row = None
|
last_activated_row = None
|
||||||
|
|
||||||
def set_status(self, to_set):
|
def set_status(self, to_set):
|
||||||
|
|
||||||
if to_set is self.scrolled_window:
|
if to_set is self.scrolled_window:
|
||||||
self.properties_page.stack.set_visible_child(self.properties_page.nav_view)
|
self.properties_page.stack.set_visible_child(self.properties_page.nav_view)
|
||||||
self.select_button.set_sensitive(True)
|
self.select_button.set_sensitive(True)
|
||||||
self.filter_button.set_sensitive(True)
|
self.filter_button.set_sensitive(True)
|
||||||
self.filters_page.set_sensitive(True)
|
self.filters_page.set_sensitive(True)
|
||||||
|
|
||||||
self.search_button.set_sensitive(True)
|
self.search_button.set_sensitive(True)
|
||||||
self.search_entry.set_editable(True)
|
self.search_entry.set_editable(True)
|
||||||
else:
|
else:
|
||||||
self.select_button.set_sensitive(False)
|
self.select_button.set_sensitive(False)
|
||||||
|
|
||||||
if to_set is self.no_packages:
|
if to_set is self.no_packages:
|
||||||
self.properties_page.stack.set_visible_child(self.properties_page.error_tbv)
|
self.properties_page.stack.set_visible_child(self.properties_page.error_tbv)
|
||||||
self.filter_button.set_sensitive(False)
|
self.filter_button.set_sensitive(False)
|
||||||
self.filter_button.set_active(False)
|
self.filter_button.set_active(False)
|
||||||
|
|
||||||
if to_set is self.no_filter_results:
|
if to_set is self.no_filter_results:
|
||||||
self.properties_page.stack.set_visible_child(self.properties_page.error_tbv)
|
self.properties_page.stack.set_visible_child(self.properties_page.error_tbv)
|
||||||
self.filter_button.set_sensitive(True)
|
self.filter_button.set_sensitive(True)
|
||||||
self.filters_page.set_sensitive(True)
|
self.filters_page.set_sensitive(True)
|
||||||
if not self.packages_split.get_collapsed():
|
if not self.packages_split.get_collapsed():
|
||||||
self.filter_button.set_active(True)
|
self.filter_button.set_active(True)
|
||||||
|
|
||||||
if to_set is self.no_results:
|
if to_set is self.no_results:
|
||||||
self.filters_page.set_sensitive(False)
|
self.filters_page.set_sensitive(False)
|
||||||
|
|
||||||
if to_set is self.loading_packages:
|
if to_set is self.loading_packages:
|
||||||
self.stack.set_visible_child(self.loading_view)
|
self.stack.set_visible_child(self.loading_view)
|
||||||
elif to_set is self.uninstalling:
|
elif to_set is self.uninstalling:
|
||||||
@@ -94,7 +93,7 @@ class PackagesPage(Adw.BreakpointBin):
|
|||||||
else:
|
else:
|
||||||
self.stack.set_visible_child(self.packages_split)
|
self.stack.set_visible_child(self.packages_split)
|
||||||
self.status_stack.set_visible_child(to_set)
|
self.status_stack.set_visible_child(to_set)
|
||||||
|
|
||||||
def apply_filters(self):
|
def apply_filters(self):
|
||||||
i = 0
|
i = 0
|
||||||
show_apps = self.filter_settings.get_boolean("show-apps")
|
show_apps = self.filter_settings.get_boolean("show-apps")
|
||||||
@@ -113,20 +112,20 @@ class PackagesPage(Adw.BreakpointBin):
|
|||||||
visible = False
|
visible = False
|
||||||
if runtimes_list != "all" and (row.package.is_runtime or row.package.dependent_runtime and not row.package.dependent_runtime.info["ref"] in runtimes_list):
|
if runtimes_list != "all" and (row.package.is_runtime or row.package.dependent_runtime and not row.package.dependent_runtime.info["ref"] in runtimes_list):
|
||||||
visible = False
|
visible = False
|
||||||
|
|
||||||
row.set_visible(visible)
|
row.set_visible(visible)
|
||||||
if visible:
|
if visible:
|
||||||
total_visible += 1
|
total_visible += 1
|
||||||
else:
|
else:
|
||||||
row.check_button.set_active(False)
|
row.check_button.set_active(False)
|
||||||
|
|
||||||
if total_visible == 0:
|
if total_visible == 0:
|
||||||
self.set_status(self.no_filter_results)
|
self.set_status(self.no_filter_results)
|
||||||
else:
|
else:
|
||||||
GLib.idle_add(lambda *_: self.set_status(self.scrolled_window))
|
GLib.idle_add(lambda *_: self.set_status(self.scrolled_window))
|
||||||
if self.current_row_for_properties and not self.current_row_for_properties.get_visible():
|
if self.current_row_for_properties and not self.current_row_for_properties.get_visible():
|
||||||
self.select_first_visible_row()
|
self.select_first_visible_row()
|
||||||
|
|
||||||
def select_first_visible_row(self):
|
def select_first_visible_row(self):
|
||||||
first_visible_row = None
|
first_visible_row = None
|
||||||
i = 0
|
i = 0
|
||||||
@@ -136,17 +135,17 @@ class PackagesPage(Adw.BreakpointBin):
|
|||||||
first_visible_row = row
|
first_visible_row = row
|
||||||
self.current_row_for_properties = row
|
self.current_row_for_properties = row
|
||||||
break
|
break
|
||||||
|
|
||||||
if not first_visible_row is None:
|
if not first_visible_row is None:
|
||||||
self.packages_list_box.select_row(first_visible_row)
|
self.packages_list_box.select_row(first_visible_row)
|
||||||
self.properties_page.set_properties(first_visible_row.package)
|
self.properties_page.set_properties(first_visible_row.package)
|
||||||
|
|
||||||
def row_select_handler(self, row):
|
def row_select_handler(self, row):
|
||||||
if row.check_button.get_active():
|
if row.check_button.get_active():
|
||||||
self.selected_rows.append(row)
|
self.selected_rows.append(row)
|
||||||
else:
|
else:
|
||||||
self.selected_rows.remove(row)
|
self.selected_rows.remove(row)
|
||||||
|
|
||||||
if (total := len(self.selected_rows)) > 0:
|
if (total := len(self.selected_rows)) > 0:
|
||||||
self.packages_navpage.set_title(_("{} Selected").format(total))
|
self.packages_navpage.set_title(_("{} Selected").format(total))
|
||||||
self.copy_button.set_sensitive(True)
|
self.copy_button.set_sensitive(True)
|
||||||
@@ -155,17 +154,17 @@ class PackagesPage(Adw.BreakpointBin):
|
|||||||
self.packages_navpage.set_title(_("Packages"))
|
self.packages_navpage.set_title(_("Packages"))
|
||||||
self.copy_button.set_sensitive(False)
|
self.copy_button.set_sensitive(False)
|
||||||
self.uninstall_button.set_sensitive(False)
|
self.uninstall_button.set_sensitive(False)
|
||||||
|
|
||||||
def select_all_handler(self, *args):
|
def select_all_handler(self, *args):
|
||||||
i = 0
|
i = 0
|
||||||
while row := self.packages_list_box.get_row_at_index(i):
|
while row := self.packages_list_box.get_row_at_index(i):
|
||||||
i += 1
|
i += 1
|
||||||
row.check_button.set_active(row.get_visible())
|
row.check_button.set_active(row.get_visible())
|
||||||
|
|
||||||
def row_rclick_handler(self, row):
|
def row_rclick_handler(self, row):
|
||||||
self.select_button.set_active(True)
|
self.select_button.set_active(True)
|
||||||
GLib.idle_add(lambda *_, button=row.check_button: button.set_active(not button.get_active()))
|
GLib.idle_add(lambda *_, button=row.check_button: button.set_active(not button.get_active()))
|
||||||
|
|
||||||
def generate_list(self, *args):
|
def generate_list(self, *args):
|
||||||
self.properties_page.nav_view.pop_to_page(self.properties_page.inner_nav_page)
|
self.properties_page.nav_view.pop_to_page(self.properties_page.inner_nav_page)
|
||||||
self.packages_list_box.remove_all()
|
self.packages_list_box.remove_all()
|
||||||
@@ -176,7 +175,7 @@ class PackagesPage(Adw.BreakpointBin):
|
|||||||
if len(HostInfo.flatpaks) == 0:
|
if len(HostInfo.flatpaks) == 0:
|
||||||
self.set_status(self.no_packages)
|
self.set_status(self.no_packages)
|
||||||
return
|
return
|
||||||
|
|
||||||
for package in HostInfo.flatpaks:
|
for package in HostInfo.flatpaks:
|
||||||
row = AppRow(package, self.row_rclick_handler)
|
row = AppRow(package, self.row_rclick_handler)
|
||||||
package.app_row = row
|
package.app_row = row
|
||||||
@@ -190,26 +189,26 @@ class PackagesPage(Adw.BreakpointBin):
|
|||||||
row.eol_runtime_status_icon.set_visible(package.dependent_runtime.is_eol)
|
row.eol_runtime_status_icon.set_visible(package.dependent_runtime.is_eol)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.packages_toast_overlay.add_toast(ErrorToast(_("Error getting Flatpak '{}'").format(package.info["name"]), str(e)).toast)
|
self.packages_toast_overlay.add_toast(ErrorToast(_("Error getting Flatpak '{}'").format(package.info["name"]), str(e)).toast)
|
||||||
|
|
||||||
self.packages_list_box.append(row)
|
self.packages_list_box.append(row)
|
||||||
|
|
||||||
self.apply_filters()
|
self.apply_filters()
|
||||||
self.select_first_visible_row()
|
self.select_first_visible_row()
|
||||||
|
|
||||||
self.scrolled_window.set_vadjustment(Gtk.Adjustment.new(0,0,0,0,0,0)) # Scroll list to top
|
self.scrolled_window.set_vadjustment(Gtk.Adjustment.new(0,0,0,0,0,0)) # Scroll list to top
|
||||||
|
|
||||||
def row_activate_handler(self, list_box, row):
|
def row_activate_handler(self, list_box, row):
|
||||||
if self.select_button.get_active():
|
if self.select_button.get_active():
|
||||||
row.check_button.set_active(not row.check_button.get_active())
|
row.check_button.set_active(not row.check_button.get_active())
|
||||||
return
|
return
|
||||||
|
|
||||||
self.last_activated_row = row
|
self.last_activated_row = row
|
||||||
self.properties_page.set_properties(row.package)
|
self.properties_page.set_properties(row.package)
|
||||||
self.properties_page.nav_view.pop()
|
self.properties_page.nav_view.pop()
|
||||||
self.packages_split.set_show_content(True)
|
self.packages_split.set_show_content(True)
|
||||||
self.filter_button.set_active(False)
|
self.filter_button.set_active(False)
|
||||||
self.current_row_for_properties = row
|
self.current_row_for_properties = row
|
||||||
|
|
||||||
def filter_func(self, row):
|
def filter_func(self, row):
|
||||||
search_text = self.search_entry.get_text().lower()
|
search_text = self.search_entry.get_text().lower()
|
||||||
title = row.get_title().lower()
|
title = row.get_title().lower()
|
||||||
@@ -217,20 +216,20 @@ class PackagesPage(Adw.BreakpointBin):
|
|||||||
if row.get_visible() and (search_text in title or search_text in subtitle):
|
if row.get_visible() and (search_text in title or search_text in subtitle):
|
||||||
self.is_result = True
|
self.is_result = True
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def set_selection_mode(self, is_enabled):
|
def set_selection_mode(self, is_enabled):
|
||||||
if is_enabled:
|
if is_enabled:
|
||||||
self.packages_list_box.set_selection_mode(Gtk.SelectionMode.NONE)
|
self.packages_list_box.set_selection_mode(Gtk.SelectionMode.NONE)
|
||||||
else:
|
else:
|
||||||
self.packages_list_box.set_selection_mode(Gtk.SelectionMode.SINGLE)
|
self.packages_list_box.set_selection_mode(Gtk.SelectionMode.SINGLE)
|
||||||
self.packages_list_box.select_row(self.last_activated_row)
|
self.packages_list_box.select_row(self.last_activated_row)
|
||||||
|
|
||||||
i = 0
|
i = 0
|
||||||
while row := self.packages_list_box.get_row_at_index(i):
|
while row := self.packages_list_box.get_row_at_index(i):
|
||||||
i += 1
|
i += 1
|
||||||
GLib.idle_add(row.check_button.set_active, False)
|
GLib.idle_add(row.check_button.set_active, False)
|
||||||
GLib.idle_add(row.check_button.set_visible, is_enabled)
|
GLib.idle_add(row.check_button.set_visible, is_enabled)
|
||||||
|
|
||||||
def selection_copy(self, box, row):
|
def selection_copy(self, box, row):
|
||||||
self.copy_pop.popdown()
|
self.copy_pop.popdown()
|
||||||
info = ""
|
info = ""
|
||||||
@@ -245,7 +244,7 @@ class PackagesPage(Adw.BreakpointBin):
|
|||||||
case self.copy_refs:
|
case self.copy_refs:
|
||||||
info = "ref"
|
info = "ref"
|
||||||
feedback = _("Refs")
|
feedback = _("Refs")
|
||||||
|
|
||||||
to_copy = []
|
to_copy = []
|
||||||
for row in self.selected_rows:
|
for row in self.selected_rows:
|
||||||
to_copy.append(row.package.info[info])
|
to_copy.append(row.package.info[info])
|
||||||
@@ -255,11 +254,11 @@ class PackagesPage(Adw.BreakpointBin):
|
|||||||
self.packages_toast_overlay.add_toast(Adw.Toast(title=_("Copied {}").format(feedback)))
|
self.packages_toast_overlay.add_toast(Adw.Toast(title=_("Copied {}").format(feedback)))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.packages_toast_overlay.add_toast(ErrorToast(_("Could not copy {}").format(feedback), str(e)).toast)
|
self.packages_toast_overlay.add_toast(ErrorToast(_("Could not copy {}").format(feedback), str(e)).toast)
|
||||||
|
|
||||||
def selection_uninstall(self, *args):
|
def selection_uninstall(self, *args):
|
||||||
if len(self.selected_rows) < 1 or not self.uninstall_button.get_sensitive():
|
if len(self.selected_rows) < 1 or not self.uninstall_button.get_sensitive():
|
||||||
return
|
return
|
||||||
|
|
||||||
def on_response(should_trash):
|
def on_response(should_trash):
|
||||||
GLib.idle_add(lambda *_: self.set_status(self.uninstalling))
|
GLib.idle_add(lambda *_: self.set_status(self.uninstalling))
|
||||||
error = []
|
error = []
|
||||||
@@ -268,37 +267,37 @@ class PackagesPage(Adw.BreakpointBin):
|
|||||||
cmd = ['flatpak-spawn', '--host', 'flatpak', 'uninstall', '-y']
|
cmd = ['flatpak-spawn', '--host', 'flatpak', 'uninstall', '-y']
|
||||||
to_uninstall = {} # { <remote><><installation>: [<ref1>, <ref2>, <ref3>, ...], ... }
|
to_uninstall = {} # { <remote><><installation>: [<ref1>, <ref2>, <ref3>, ...], ... }
|
||||||
to_trash = []
|
to_trash = []
|
||||||
|
|
||||||
for row in self.selected_rows:
|
for row in self.selected_rows:
|
||||||
key = row.package.info['installation']
|
key = row.package.info['installation']
|
||||||
if ls := to_uninstall.get(key, False):
|
if ls := to_uninstall.get(key, False):
|
||||||
ls.append(row.package.info['ref'])
|
ls.append(row.package.info['ref'])
|
||||||
else:
|
else:
|
||||||
to_uninstall[key] = [row.package.info['ref']]
|
to_uninstall[key] = [row.package.info['ref']]
|
||||||
|
|
||||||
if should_trash and os.path.exists(row.package.data_path):
|
if should_trash and os.path.exists(row.package.data_path):
|
||||||
to_trash.append(row.package.data_path)
|
to_trash.append(row.package.data_path)
|
||||||
|
|
||||||
for installation, packages in to_uninstall.items():
|
for installation, packages in to_uninstall.items():
|
||||||
suffix = []
|
suffix = []
|
||||||
if installation == "user" or installation == "system":
|
if installation == "user" or installation == "system":
|
||||||
suffix.append(f"--{installation}")
|
suffix.append(f"--{installation}")
|
||||||
else:
|
else:
|
||||||
suffix.append(f"--installation={installation}")
|
suffix.append(f"--installation={installation}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
subprocess.run(cmd + suffix + packages, check=True, text=True, capture_output=True)
|
subprocess.run(cmd + suffix + packages, check=True, text=True, capture_output=True)
|
||||||
except subprocess.CalledProcessError as cpe:
|
except subprocess.CalledProcessError as cpe:
|
||||||
error.append(str(cpe.stderr))
|
error.append(str(cpe.stderr))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
error.append(str(e))
|
error.append(str(e))
|
||||||
|
|
||||||
if should_trash and len(to_trash) > 0:
|
if should_trash and len(to_trash) > 0:
|
||||||
try:
|
try:
|
||||||
subprocess.run(['gio', 'trash'] + to_trash, check=True, text=True, capture_output=True)
|
subprocess.run(['gio', 'trash'] + to_trash, check=True, text=True, capture_output=True)
|
||||||
except subprocess.CalledProcessError as cpe:
|
except subprocess.CalledProcessError as cpe:
|
||||||
error.append(cpe)
|
error.append(cpe)
|
||||||
|
|
||||||
def callback(*args):
|
def callback(*args):
|
||||||
self.main_window.refresh_handler()
|
self.main_window.refresh_handler()
|
||||||
HostInfo.main_window.remove_refresh_lockout("batch uninstalling packages")
|
HostInfo.main_window.remove_refresh_lockout("batch uninstalling packages")
|
||||||
@@ -307,25 +306,25 @@ class PackagesPage(Adw.BreakpointBin):
|
|||||||
GLib.idle_add(lambda *args: self.packages_toast_overlay.add_toast(ErrorToast(_("Errors occurred while uninstalling"), details).toast))
|
GLib.idle_add(lambda *args: self.packages_toast_overlay.add_toast(ErrorToast(_("Errors occurred while uninstalling"), details).toast))
|
||||||
else:
|
else:
|
||||||
GLib.idle_add(lambda *args: self.packages_toast_overlay.add_toast(Adw.Toast(title=_("Uninstalled Packages"))))
|
GLib.idle_add(lambda *args: self.packages_toast_overlay.add_toast(Adw.Toast(title=_("Uninstalled Packages"))))
|
||||||
|
|
||||||
Gio.Task.new(None, None, callback).run_in_thread(thread)
|
Gio.Task.new(None, None, callback).run_in_thread(thread)
|
||||||
|
|
||||||
dialog = UninstallDialog(on_response, True)
|
dialog = UninstallDialog(on_response, True)
|
||||||
dialog.present(self.main_window)
|
dialog.present(self.main_window)
|
||||||
|
|
||||||
def start_loading(self):
|
def start_loading(self):
|
||||||
self.search_button.set_active(False)
|
self.search_button.set_active(False)
|
||||||
self.last_activated_row = None
|
self.last_activated_row = None
|
||||||
self.packages_navpage.set_title(_("Packages"))
|
self.packages_navpage.set_title(_("Packages"))
|
||||||
self.select_button.set_active(False)
|
self.select_button.set_active(False)
|
||||||
self.set_status(self.loading_packages)
|
self.set_status(self.loading_packages)
|
||||||
|
|
||||||
def end_loading(self):
|
def end_loading(self):
|
||||||
GLib.idle_add(lambda *_: self.generate_list())
|
GLib.idle_add(lambda *_: self.generate_list())
|
||||||
|
|
||||||
def select_button_handler(self, button):
|
def select_button_handler(self, button):
|
||||||
self.set_selection_mode(button.get_active())
|
self.set_selection_mode(button.get_active())
|
||||||
|
|
||||||
def filter_button_handler(self, button):
|
def filter_button_handler(self, button):
|
||||||
if button.get_active():
|
if button.get_active():
|
||||||
self.content_stack.set_visible_child(self.filters_page)
|
self.content_stack.set_visible_child(self.filters_page)
|
||||||
@@ -333,35 +332,35 @@ class PackagesPage(Adw.BreakpointBin):
|
|||||||
else:
|
else:
|
||||||
self.content_stack.set_visible_child(self.properties_page)
|
self.content_stack.set_visible_child(self.properties_page)
|
||||||
self.packages_split.set_show_content(False)
|
self.packages_split.set_show_content(False)
|
||||||
|
|
||||||
def filter_page_handler(self, *args):
|
def filter_page_handler(self, *args):
|
||||||
if self.packages_split.get_collapsed() and not self.packages_split.get_show_content():
|
if self.packages_split.get_collapsed() and not self.packages_split.get_show_content():
|
||||||
self.filter_button.set_active(False)
|
self.filter_button.set_active(False)
|
||||||
|
|
||||||
def on_invalidate(self, row):
|
def on_invalidate(self, row):
|
||||||
current_status = self.status_stack.get_visible_child()
|
current_status = self.status_stack.get_visible_child()
|
||||||
if not current_status is self.no_results:
|
if not current_status is self.no_results:
|
||||||
self.prev_status = current_status
|
self.prev_status = current_status
|
||||||
|
|
||||||
self.is_result = False
|
self.is_result = False
|
||||||
self.packages_list_box.invalidate_filter()
|
self.packages_list_box.invalidate_filter()
|
||||||
if self.is_result:
|
if self.is_result:
|
||||||
self.set_status(self.prev_status)
|
self.set_status(self.prev_status)
|
||||||
else:
|
else:
|
||||||
self.set_status(self.no_results)
|
self.set_status(self.no_results)
|
||||||
|
|
||||||
def sort_func(self, row1, row2):
|
def sort_func(self, row1, row2):
|
||||||
return row1.package.info["name"].lower() > row2.package.info["name"].lower()
|
return row1.package.info["name"].lower() > row2.package.info["name"].lower()
|
||||||
|
|
||||||
def on_escape_handler(self):
|
def on_escape_handler(self):
|
||||||
if self.select_button.get_active():
|
if self.select_button.get_active():
|
||||||
self.select_button.set_active(False)
|
self.select_button.set_active(False)
|
||||||
elif self.filter_button.get_active():
|
elif self.filter_button.get_active():
|
||||||
self.filter_button.set_active(False)
|
self.filter_button.set_active(False)
|
||||||
|
|
||||||
def __init__(self, main_window, **kwargs):
|
def __init__(self, main_window, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
# Extra Object Creation
|
# Extra Object Creation
|
||||||
self.main_window = main_window
|
self.main_window = main_window
|
||||||
self.loading_packages = LoadingStatus(_("Loading Packages"), _("This should only take a moment"))
|
self.loading_packages = LoadingStatus(_("Loading Packages"), _("This should only take a moment"))
|
||||||
@@ -377,7 +376,7 @@ class PackagesPage(Adw.BreakpointBin):
|
|||||||
self.selected_rows = []
|
self.selected_rows = []
|
||||||
self.current_row_for_properties = None
|
self.current_row_for_properties = None
|
||||||
self.on_backspace_handler = self.selection_uninstall
|
self.on_backspace_handler = self.selection_uninstall
|
||||||
|
|
||||||
# Apply
|
# Apply
|
||||||
self.loading_view.set_content(self.loading_packages)
|
self.loading_view.set_content(self.loading_packages)
|
||||||
self.packages_list_box.set_filter_func(self.filter_func)
|
self.packages_list_box.set_filter_func(self.filter_func)
|
||||||
@@ -385,7 +384,7 @@ class PackagesPage(Adw.BreakpointBin):
|
|||||||
self.properties_page.packages_page = self
|
self.properties_page.packages_page = self
|
||||||
self.filters_page.packages_page = self
|
self.filters_page.packages_page = self
|
||||||
self.__class__.instance = self
|
self.__class__.instance = self
|
||||||
|
|
||||||
# Connections
|
# Connections
|
||||||
self.search_entry.connect("search-changed", self.on_invalidate)
|
self.search_entry.connect("search-changed", self.on_invalidate)
|
||||||
self.search_bar.set_key_capture_widget(main_window)
|
self.search_bar.set_key_capture_widget(main_window)
|
||||||
|
|||||||
@@ -4,35 +4,35 @@ from gi.repository import Adw, Gtk, GLib
|
|||||||
class UninstallDialog(Adw.AlertDialog):
|
class UninstallDialog(Adw.AlertDialog):
|
||||||
__gtype_name__ = "UninstallDialog"
|
__gtype_name__ = "UninstallDialog"
|
||||||
gtc = Gtk.Template.Child
|
gtc = Gtk.Template.Child
|
||||||
|
|
||||||
group = gtc()
|
group = gtc()
|
||||||
trash = gtc()
|
trash = gtc()
|
||||||
is_open = False
|
is_open = False
|
||||||
|
|
||||||
def on_response(self, dialog, response):
|
def on_response(self, dialog, response):
|
||||||
self.__class__.is_open = False
|
self.__class__.is_open = False
|
||||||
if response != "continue":
|
if response != "continue":
|
||||||
return
|
return
|
||||||
|
|
||||||
self.continue_callback(self.trash.get_active())
|
self.continue_callback(self.trash.get_active())
|
||||||
|
|
||||||
def present(self, *args, **kwargs):
|
def present(self, *args, **kwargs):
|
||||||
if self.__class__.is_open:
|
if self.__class__.is_open:
|
||||||
return
|
return
|
||||||
|
|
||||||
self.__class__.is_open = True
|
self.__class__.is_open = True
|
||||||
super().present(*args, **kwargs)
|
super().present(*args, **kwargs)
|
||||||
|
|
||||||
def __init__(self, continue_callback, show_trash_option, package_name=None, **kwargs):
|
def __init__(self, continue_callback, show_trash_option, package_name=None, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
if package_name:
|
if package_name:
|
||||||
self.set_heading(GLib.markup_escape_text(_("Uninstall {}?").format(package_name)))
|
self.set_heading(GLib.markup_escape_text(_("Uninstall {}?").format(package_name)))
|
||||||
self.set_body(GLib.markup_escape_text(_("It will not be possible to use {} after removal").format(package_name)))
|
self.set_body(GLib.markup_escape_text(_("It will not be possible to use {} after removal").format(package_name)))
|
||||||
else:
|
else:
|
||||||
self.set_heading(GLib.markup_escape_text(_("Uninstall Packages?")))
|
self.set_heading(GLib.markup_escape_text(_("Uninstall Packages?")))
|
||||||
self.set_body(GLib.markup_escape_text(_("It will not be possible to use these packages after removal")))
|
self.set_body(GLib.markup_escape_text(_("It will not be possible to use these packages after removal")))
|
||||||
|
|
||||||
self.continue_callback = continue_callback
|
self.continue_callback = continue_callback
|
||||||
self.add_response("cancel", _("Cancel"))
|
self.add_response("cancel", _("Cancel"))
|
||||||
self.add_response("continue", _("Uninstall"))
|
self.add_response("continue", _("Uninstall"))
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ template $PropertiesPage : Adw.NavigationPage {
|
|||||||
margin-bottom: 12;
|
margin-bottom: 12;
|
||||||
orientation: vertical;
|
orientation: vertical;
|
||||||
halign: fill;
|
halign: fill;
|
||||||
|
|
||||||
Image app_icon {
|
Image app_icon {
|
||||||
pixel-size: 100;
|
pixel-size: 100;
|
||||||
margin-top: 6;
|
margin-top: 6;
|
||||||
|
|||||||
@@ -14,10 +14,10 @@ class PropertiesPage(Adw.NavigationPage):
|
|||||||
stack = gtc()
|
stack = gtc()
|
||||||
error_tbv = gtc()
|
error_tbv = gtc()
|
||||||
loading_tbv = gtc()
|
loading_tbv = gtc()
|
||||||
|
|
||||||
more_menu = gtc()
|
more_menu = gtc()
|
||||||
more_list = gtc()
|
more_list = gtc()
|
||||||
|
|
||||||
nav_view = gtc()
|
nav_view = gtc()
|
||||||
inner_nav_page = gtc()
|
inner_nav_page = gtc()
|
||||||
toast_overlay = gtc()
|
toast_overlay = gtc()
|
||||||
@@ -29,7 +29,7 @@ class PropertiesPage(Adw.NavigationPage):
|
|||||||
eol_box = gtc()
|
eol_box = gtc()
|
||||||
open_app_button = gtc()
|
open_app_button = gtc()
|
||||||
uninstall_button = gtc()
|
uninstall_button = gtc()
|
||||||
|
|
||||||
pin_row = gtc()
|
pin_row = gtc()
|
||||||
pin_switch = gtc()
|
pin_switch = gtc()
|
||||||
data_row = gtc()
|
data_row = gtc()
|
||||||
@@ -44,30 +44,30 @@ class PropertiesPage(Adw.NavigationPage):
|
|||||||
installed_size_row = gtc()
|
installed_size_row = gtc()
|
||||||
runtime_row = gtc()
|
runtime_row = gtc()
|
||||||
eol_package_package_status_icon = gtc()
|
eol_package_package_status_icon = gtc()
|
||||||
|
|
||||||
id_row = gtc()
|
id_row = gtc()
|
||||||
ref_row = gtc()
|
ref_row = gtc()
|
||||||
arch_row = gtc()
|
arch_row = gtc()
|
||||||
branch_row = gtc()
|
branch_row = gtc()
|
||||||
license_row = gtc()
|
license_row = gtc()
|
||||||
|
|
||||||
sdk_row = gtc()
|
sdk_row = gtc()
|
||||||
origin_row = gtc()
|
origin_row = gtc()
|
||||||
collection_row = gtc()
|
collection_row = gtc()
|
||||||
installation_row = gtc()
|
installation_row = gtc()
|
||||||
|
|
||||||
commit_row = gtc()
|
commit_row = gtc()
|
||||||
parent_row = gtc()
|
parent_row = gtc()
|
||||||
subject_row = gtc()
|
subject_row = gtc()
|
||||||
date_row = gtc()
|
date_row = gtc()
|
||||||
|
|
||||||
package = None
|
package = None
|
||||||
|
|
||||||
def set_properties(self, package, refresh=False):
|
def set_properties(self, package, refresh=False):
|
||||||
if package == self.package and not refresh:
|
if package == self.package and not refresh:
|
||||||
# Do not update the ui if the same app row is clicked
|
# Do not update the ui if the same app row is clicked
|
||||||
return
|
return
|
||||||
|
|
||||||
self.reinstall_did_error = False
|
self.reinstall_did_error = False
|
||||||
self.package = package
|
self.package = package
|
||||||
pkg_name = package.info["name"]
|
pkg_name = package.info["name"]
|
||||||
@@ -78,12 +78,12 @@ class PropertiesPage(Adw.NavigationPage):
|
|||||||
else:
|
else:
|
||||||
self.name.set_visible(False)
|
self.name.set_visible(False)
|
||||||
self.inner_nav_page.set_title(_("Properties"))
|
self.inner_nav_page.set_title(_("Properties"))
|
||||||
|
|
||||||
if package.icon_path:
|
if package.icon_path:
|
||||||
GLib.idle_add(lambda *_: self.app_icon.set_from_file(package.icon_path))
|
GLib.idle_add(lambda *_: self.app_icon.set_from_file(package.icon_path))
|
||||||
else:
|
else:
|
||||||
GLib.idle_add(lambda *_: self.app_icon.set_from_icon_name("application-x-executable-symbolic"))
|
GLib.idle_add(lambda *_: self.app_icon.set_from_icon_name("application-x-executable-symbolic"))
|
||||||
|
|
||||||
self.eol_box.set_visible(package.is_eol)
|
self.eol_box.set_visible(package.is_eol)
|
||||||
self.pin_row.set_visible(package.is_runtime)
|
self.pin_row.set_visible(package.is_runtime)
|
||||||
self.open_app_button.set_visible(package.is_runtime)
|
self.open_app_button.set_visible(package.is_runtime)
|
||||||
@@ -96,29 +96,29 @@ class PropertiesPage(Adw.NavigationPage):
|
|||||||
has_path = os.path.exists(package.data_path)
|
has_path = os.path.exists(package.data_path)
|
||||||
self.trash_data_button.set_sensitive(has_path and self.package.info['id'] != "io.github.flattool.Warehouse")
|
self.trash_data_button.set_sensitive(has_path and self.package.info['id'] != "io.github.flattool.Warehouse")
|
||||||
self.open_data_button.set_sensitive(has_path)
|
self.open_data_button.set_sensitive(has_path)
|
||||||
|
|
||||||
if not self.package.dependent_runtime is None:
|
if not self.package.dependent_runtime is None:
|
||||||
self.runtime_row.set_visible(True)
|
self.runtime_row.set_visible(True)
|
||||||
self.runtime_row.set_subtitle(self.package.dependent_runtime.info["name"])
|
self.runtime_row.set_subtitle(self.package.dependent_runtime.info["name"])
|
||||||
self.eol_package_package_status_icon.set_visible(self.package.dependent_runtime.is_eol)
|
self.eol_package_package_status_icon.set_visible(self.package.dependent_runtime.is_eol)
|
||||||
|
|
||||||
if has_path:
|
if has_path:
|
||||||
self.trash_data_button.set_visible(False)
|
self.trash_data_button.set_visible(False)
|
||||||
self.open_data_button.set_visible(False)
|
self.open_data_button.set_visible(False)
|
||||||
self.data_spinner.set_visible(True)
|
self.data_spinner.set_visible(True)
|
||||||
self.data_row.set_subtitle(_("Loading User Data"))
|
self.data_row.set_subtitle(_("Loading User Data"))
|
||||||
|
|
||||||
def callback(size):
|
def callback(size):
|
||||||
self.trash_data_button.set_visible(True)
|
self.trash_data_button.set_visible(True)
|
||||||
self.open_data_button.set_visible(True)
|
self.open_data_button.set_visible(True)
|
||||||
self.data_spinner.set_visible(False)
|
self.data_spinner.set_visible(False)
|
||||||
self.data_row.set_subtitle(size)
|
self.data_row.set_subtitle(size)
|
||||||
|
|
||||||
self.package.get_data_size(lambda size: callback(size))
|
self.package.get_data_size(lambda size: callback(size))
|
||||||
else:
|
else:
|
||||||
self.data_row.set_subtitle(_("No User Data"))
|
self.data_row.set_subtitle(_("No User Data"))
|
||||||
self.data_spinner.set_visible(False)
|
self.data_spinner.set_visible(False)
|
||||||
|
|
||||||
cli_info = None
|
cli_info = None
|
||||||
try:
|
try:
|
||||||
cli_info = package.get_cli_info()
|
cli_info = package.get_cli_info()
|
||||||
@@ -128,10 +128,10 @@ class PropertiesPage(Adw.NavigationPage):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.toast_overlay.add_toast(ErrorToast(_("Could not get properties"), str(e)).toast)
|
self.toast_overlay.add_toast(ErrorToast(_("Could not get properties"), str(e)).toast)
|
||||||
return
|
return
|
||||||
|
|
||||||
for key, row in self.info_rows.items():
|
for key, row in self.info_rows.items():
|
||||||
row.set_visible(False)
|
row.set_visible(False)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
subtitle = cli_info[key]
|
subtitle = cli_info[key]
|
||||||
row.set_subtitle(subtitle)
|
row.set_subtitle(subtitle)
|
||||||
@@ -144,7 +144,7 @@ class PropertiesPage(Adw.NavigationPage):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.toast_overlay.add_toast(ErrorToast(_("Could not get properties"), str(e)).toast)
|
self.toast_overlay.add_toast(ErrorToast(_("Could not get properties"), str(e)).toast)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
self.mask_label.set_visible(package.is_masked)
|
self.mask_label.set_visible(package.is_masked)
|
||||||
self.mask_switch.set_active(package.is_masked)
|
self.mask_switch.set_active(package.is_masked)
|
||||||
self.pin_switch.set_active(package.is_pinned)
|
self.pin_switch.set_active(package.is_pinned)
|
||||||
@@ -153,14 +153,14 @@ class PropertiesPage(Adw.NavigationPage):
|
|||||||
if self.open_app_button.get_visible():
|
if self.open_app_button.get_visible():
|
||||||
self.more_list.append(self.view_snapshots)
|
self.more_list.append(self.view_snapshots)
|
||||||
self.more_list.append(self.copy_launch_command)
|
self.more_list.append(self.copy_launch_command)
|
||||||
|
|
||||||
self.more_list.append(self.show_details)
|
self.more_list.append(self.show_details)
|
||||||
self.more_list.append(self.reinstall)
|
self.more_list.append(self.reinstall)
|
||||||
|
|
||||||
def open_data_handler(self, *args):
|
def open_data_handler(self, *args):
|
||||||
if error := self.package.open_data():
|
if error := self.package.open_data():
|
||||||
self.toast_overlay.add_toast(ErrorToast(_("Could not open data"), str(error)).toast)
|
self.toast_overlay.add_toast(ErrorToast(_("Could not open data"), str(error)).toast)
|
||||||
|
|
||||||
def trash_data_handler(self, *args):
|
def trash_data_handler(self, *args):
|
||||||
def on_choice(dialog, response):
|
def on_choice(dialog, response):
|
||||||
if response != 'continue':
|
if response != 'continue':
|
||||||
@@ -176,12 +176,12 @@ class PropertiesPage(Adw.NavigationPage):
|
|||||||
snapshot_list_package = snapshot_list_page.package_or_folder
|
snapshot_list_package = snapshot_list_page.package_or_folder
|
||||||
if not snapshot_list_package is None:
|
if not snapshot_list_package is None:
|
||||||
snapshot_list_page.set_snapshots(snapshot_list_package, True)
|
snapshot_list_page.set_snapshots(snapshot_list_package, True)
|
||||||
|
|
||||||
except subprocess.CalledProcessError as cpe:
|
except subprocess.CalledProcessError as cpe:
|
||||||
self.toast_overlay.add_toast(ErrorToast(_("Could not trash data"), cpe.stderr).toast)
|
self.toast_overlay.add_toast(ErrorToast(_("Could not trash data"), cpe.stderr).toast)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.toast_overlay.add_toast(ErrorToast(_("Could not trash data"), str(e)).toast)
|
self.toast_overlay.add_toast(ErrorToast(_("Could not trash data"), str(e)).toast)
|
||||||
|
|
||||||
dialog = Adw.AlertDialog(
|
dialog = Adw.AlertDialog(
|
||||||
heading=_("Send {}'s User Data to the Trash?").format(self.package.info["name"]),
|
heading=_("Send {}'s User Data to the Trash?").format(self.package.info["name"]),
|
||||||
body=_("Your settings and data for this app will be sent to the trash")
|
body=_("Your settings and data for this app will be sent to the trash")
|
||||||
@@ -191,7 +191,7 @@ class PropertiesPage(Adw.NavigationPage):
|
|||||||
dialog.connect("response", on_choice)
|
dialog.connect("response", on_choice)
|
||||||
dialog.set_response_appearance('continue', Adw.ResponseAppearance.DESTRUCTIVE)
|
dialog.set_response_appearance('continue', Adw.ResponseAppearance.DESTRUCTIVE)
|
||||||
dialog.present(self.main_window)
|
dialog.present(self.main_window)
|
||||||
|
|
||||||
def set_mask_handler(self, *args):
|
def set_mask_handler(self, *args):
|
||||||
state = not self.mask_switch.get_active()
|
state = not self.mask_switch.get_active()
|
||||||
def callback(*args):
|
def callback(*args):
|
||||||
@@ -207,9 +207,9 @@ class PropertiesPage(Adw.NavigationPage):
|
|||||||
GLib.idle_add(lambda *_: self.mask_switch.set_active(state))
|
GLib.idle_add(lambda *_: self.mask_switch.set_active(state))
|
||||||
GLib.idle_add(lambda *_: self.mask_label.set_visible(state))
|
GLib.idle_add(lambda *_: self.mask_label.set_visible(state))
|
||||||
self.package.app_row.masked_status_icon.set_visible(state)
|
self.package.app_row.masked_status_icon.set_visible(state)
|
||||||
|
|
||||||
self.package.set_mask(state, callback)
|
self.package.set_mask(state, callback)
|
||||||
|
|
||||||
def set_pin_handler(self, *args):
|
def set_pin_handler(self, *args):
|
||||||
state = not self.pin_switch.get_active()
|
state = not self.pin_switch.get_active()
|
||||||
def callback(*args):
|
def callback(*args):
|
||||||
@@ -223,9 +223,9 @@ class PropertiesPage(Adw.NavigationPage):
|
|||||||
self.toast_overlay.add_toast(Adw.Toast(title=response))
|
self.toast_overlay.add_toast(Adw.Toast(title=response))
|
||||||
GLib.idle_add(lambda *_: self.pin_switch.set_active(state))
|
GLib.idle_add(lambda *_: self.pin_switch.set_active(state))
|
||||||
self.package.app_row.pinned_status_icon.set_visible(state)
|
self.package.app_row.pinned_status_icon.set_visible(state)
|
||||||
|
|
||||||
self.package.set_pin(state, callback)
|
self.package.set_pin(state, callback)
|
||||||
|
|
||||||
def uninstall_handler(self, *args):
|
def uninstall_handler(self, *args):
|
||||||
def on_choice(should_trash):
|
def on_choice(should_trash):
|
||||||
self.packages_page.set_status(self.packages_page.uninstalling)
|
self.packages_page.set_status(self.packages_page.uninstalling)
|
||||||
@@ -238,7 +238,7 @@ class PropertiesPage(Adw.NavigationPage):
|
|||||||
self.toast_overlay.add_toast(ErrorToast(_("Could not trash data"), cpe.stderr).toast)
|
self.toast_overlay.add_toast(ErrorToast(_("Could not trash data"), cpe.stderr).toast)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.toast_overlay.add_toast(ErrorToast(_("Could not trash data"), str(e)).toast)
|
self.toast_overlay.add_toast(ErrorToast(_("Could not trash data"), str(e)).toast)
|
||||||
|
|
||||||
def callback(*args):
|
def callback(*args):
|
||||||
if fail := self.package.failed_uninstall:
|
if fail := self.package.failed_uninstall:
|
||||||
fail = fail.stderr if type(fail) is subprocess.CalledProcessError else fail
|
fail = fail.stderr if type(fail) is subprocess.CalledProcessError else fail
|
||||||
@@ -247,48 +247,48 @@ class PropertiesPage(Adw.NavigationPage):
|
|||||||
else:
|
else:
|
||||||
self.main_window.refresh_handler()
|
self.main_window.refresh_handler()
|
||||||
HostInfo.main_window.toast_overlay.add_toast(Adw.Toast(title=_("Uninstalled {}").format(self.package.info["name"])))
|
HostInfo.main_window.toast_overlay.add_toast(Adw.Toast(title=_("Uninstalled {}").format(self.package.info["name"])))
|
||||||
|
|
||||||
dialog = UninstallDialog(on_choice, os.path.exists(self.package.data_path), self.package.info["name"])
|
dialog = UninstallDialog(on_choice, os.path.exists(self.package.data_path), self.package.info["name"])
|
||||||
dialog.present(self.main_window)
|
dialog.present(self.main_window)
|
||||||
|
|
||||||
def runtime_row_handler(self, *args):
|
def runtime_row_handler(self, *args):
|
||||||
new_page = self.__class__()
|
new_page = self.__class__()
|
||||||
new_page.packages_page = self.packages_page
|
new_page.packages_page = self.packages_page
|
||||||
new_page.set_properties(self.package.dependent_runtime)
|
new_page.set_properties(self.package.dependent_runtime)
|
||||||
self.nav_view.push(new_page)
|
self.nav_view.push(new_page)
|
||||||
|
|
||||||
def open_app_handler(self, *args):
|
def open_app_handler(self, *args):
|
||||||
self.toast_overlay.add_toast(Adw.Toast(title=_("Opening {}…").format(self.package.info["name"])))
|
self.toast_overlay.add_toast(Adw.Toast(title=_("Opening {}…").format(self.package.info["name"])))
|
||||||
|
|
||||||
def callback(*args):
|
def callback(*args):
|
||||||
if fail := self.package.failed_app_run:
|
if fail := self.package.failed_app_run:
|
||||||
fail = fail.stderr if type(fail) is subprocess.CalledProcessError else fail
|
fail = fail.stderr if type(fail) is subprocess.CalledProcessError else fail
|
||||||
self.toast_overlay.add_toast(ErrorToast(_("Could not open {}").format(self.package.info["name"]), str(fail)).toast)
|
self.toast_overlay.add_toast(ErrorToast(_("Could not open {}").format(self.package.info["name"]), str(fail)).toast)
|
||||||
|
|
||||||
self.package.open_app(callback)
|
self.package.open_app(callback)
|
||||||
|
|
||||||
def copy_handler(self, row):
|
def copy_handler(self, row):
|
||||||
HostInfo.clipboard.set(row.get_subtitle())
|
HostInfo.clipboard.set(row.get_subtitle())
|
||||||
self.toast_overlay.add_toast(Adw.Toast(title=_("Copied {}").format(row.get_title())))
|
self.toast_overlay.add_toast(Adw.Toast(title=_("Copied {}").format(row.get_title())))
|
||||||
|
|
||||||
def change_version_handler(self, row):
|
def change_version_handler(self, row):
|
||||||
page = ChangeVersionPage(self.packages_page, self.package)
|
page = ChangeVersionPage(self.packages_page, self.package)
|
||||||
self.nav_view.push(page)
|
self.nav_view.push(page)
|
||||||
|
|
||||||
def reinstall_callback(self):
|
def reinstall_callback(self):
|
||||||
HostInfo.main_window.refresh_handler()
|
HostInfo.main_window.refresh_handler()
|
||||||
if not self.reinstall_did_error:
|
if not self.reinstall_did_error:
|
||||||
HostInfo.main_window.toast_overlay.add_toast(Adw.Toast(title=_("Reinstalled {}").format(self.package.info['name'])))
|
HostInfo.main_window.toast_overlay.add_toast(Adw.Toast(title=_("Reinstalled {}").format(self.package.info['name'])))
|
||||||
|
|
||||||
def reinstall_error_callback(self, user_facing_label, error_message):
|
def reinstall_error_callback(self, user_facing_label, error_message):
|
||||||
self.reinstall_did_error = True
|
self.reinstall_did_error = True
|
||||||
GLib.idle_add(lambda *_: HostInfo.main_window.toast_overlay.add_toast(ErrorToast(user_facing_label, error_message).toast))
|
GLib.idle_add(lambda *_: HostInfo.main_window.toast_overlay.add_toast(ErrorToast(user_facing_label, error_message).toast))
|
||||||
|
|
||||||
def reinstall_handler(self):
|
def reinstall_handler(self):
|
||||||
def on_response(dialog, response):
|
def on_response(dialog, response):
|
||||||
if response != "continue":
|
if response != "continue":
|
||||||
return
|
return
|
||||||
|
|
||||||
self.reinstall_did_error = False
|
self.reinstall_did_error = False
|
||||||
PackageInstallWorker.install(
|
PackageInstallWorker.install(
|
||||||
[{
|
[{
|
||||||
@@ -302,7 +302,7 @@ class PropertiesPage(Adw.NavigationPage):
|
|||||||
self.reinstall_error_callback,
|
self.reinstall_error_callback,
|
||||||
)
|
)
|
||||||
self.packages_page.set_status(self.packages_page.reinstalling)
|
self.packages_page.set_status(self.packages_page.reinstalling)
|
||||||
|
|
||||||
dialog = Adw.AlertDialog(
|
dialog = Adw.AlertDialog(
|
||||||
heading=_("Reinstall {}?").format(self.package.info['name']),
|
heading=_("Reinstall {}?").format(self.package.info['name']),
|
||||||
body=_("This package will be uninstalled, and then reinstalled from the same remote and installation.")
|
body=_("This package will be uninstalled, and then reinstalled from the same remote and installation.")
|
||||||
@@ -312,7 +312,7 @@ class PropertiesPage(Adw.NavigationPage):
|
|||||||
dialog.set_response_appearance("continue", Adw.ResponseAppearance.SUGGESTED)
|
dialog.set_response_appearance("continue", Adw.ResponseAppearance.SUGGESTED)
|
||||||
dialog.connect("response", on_response)
|
dialog.connect("response", on_response)
|
||||||
dialog.present(HostInfo.main_window)
|
dialog.present(HostInfo.main_window)
|
||||||
|
|
||||||
def more_menu_handler(self, listbox, row):
|
def more_menu_handler(self, listbox, row):
|
||||||
self.more_menu.popdown()
|
self.more_menu.popdown()
|
||||||
match row.get_child():
|
match row.get_child():
|
||||||
@@ -321,43 +321,43 @@ class PropertiesPage(Adw.NavigationPage):
|
|||||||
snapshots_page = HostInfo.main_window.pages[snapshots_row]
|
snapshots_page = HostInfo.main_window.pages[snapshots_row]
|
||||||
HostInfo.main_window.activate_row(snapshots_row)
|
HostInfo.main_window.activate_row(snapshots_row)
|
||||||
snapshots_page.show_snapshot(self.package)
|
snapshots_page.show_snapshot(self.package)
|
||||||
|
|
||||||
case self.copy_launch_command:
|
case self.copy_launch_command:
|
||||||
try:
|
try:
|
||||||
HostInfo.clipboard.set(f"flatpak run {self.package.info['ref']}")
|
HostInfo.clipboard.set(f"flatpak run {self.package.info['ref']}")
|
||||||
self.toast_overlay.add_toast(Adw.Toast.new(_("Copied launch command")))
|
self.toast_overlay.add_toast(Adw.Toast.new(_("Copied launch command")))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.toast_overlay.add_toast(ErrorToast(_("Could not copy launch command"), str(e)).toast)
|
self.toast_overlay.add_toast(ErrorToast(_("Could not copy launch command"), str(e)).toast)
|
||||||
|
|
||||||
case self.show_details:
|
case self.show_details:
|
||||||
try:
|
try:
|
||||||
Gio.AppInfo.launch_default_for_uri(f"appstream://{self.package.info['id']}", None)
|
Gio.AppInfo.launch_default_for_uri(f"appstream://{self.package.info['id']}", None)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.toast_overlay.add_toast(ErrorToast(_("Could not show details"), str(e)).toast)
|
self.toast_overlay.add_toast(ErrorToast(_("Could not show details"), str(e)).toast)
|
||||||
|
|
||||||
case self.reinstall:
|
case self.reinstall:
|
||||||
self.reinstall_handler()
|
self.reinstall_handler()
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
# Extra Object Creation
|
# Extra Object Creation
|
||||||
self.main_window = HostInfo.main_window
|
self.main_window = HostInfo.main_window
|
||||||
self.info_rows = {
|
self.info_rows = {
|
||||||
"version": self.version_row,
|
"version": self.version_row,
|
||||||
"installed": self.installed_size_row,
|
"installed": self.installed_size_row,
|
||||||
|
|
||||||
"id": self.id_row,
|
"id": self.id_row,
|
||||||
"ref": self.ref_row,
|
"ref": self.ref_row,
|
||||||
"arch": self.arch_row,
|
"arch": self.arch_row,
|
||||||
"branch": self.branch_row,
|
"branch": self.branch_row,
|
||||||
"license": self.license_row,
|
"license": self.license_row,
|
||||||
|
|
||||||
"sdk": self.sdk_row,
|
"sdk": self.sdk_row,
|
||||||
"origin": self.origin_row,
|
"origin": self.origin_row,
|
||||||
"collection": self.collection_row,
|
"collection": self.collection_row,
|
||||||
"installation": self.installation_row,
|
"installation": self.installation_row,
|
||||||
|
|
||||||
"commit": self.commit_row,
|
"commit": self.commit_row,
|
||||||
"parent": self.parent_row,
|
"parent": self.parent_row,
|
||||||
"subject": self.subject_row,
|
"subject": self.subject_row,
|
||||||
@@ -371,9 +371,9 @@ class PropertiesPage(Adw.NavigationPage):
|
|||||||
self.show_details = Gtk.Label(halign=Gtk.Align.START, label=_("Show Details"))
|
self.show_details = Gtk.Label(halign=Gtk.Align.START, label=_("Show Details"))
|
||||||
self.reinstall = Gtk.Label(halign=Gtk.Align.START, label=_("Reinstall"))
|
self.reinstall = Gtk.Label(halign=Gtk.Align.START, label=_("Reinstall"))
|
||||||
self.reinstall_did_error = False
|
self.reinstall_did_error = False
|
||||||
|
|
||||||
# Apply
|
# Apply
|
||||||
|
|
||||||
# Connections
|
# Connections
|
||||||
self.more_list.connect("row-activated", self.more_menu_handler)
|
self.more_list.connect("row-activated", self.more_menu_handler)
|
||||||
self.open_data_button.connect("clicked", self.open_data_handler)
|
self.open_data_button.connect("clicked", self.open_data_handler)
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import subprocess, re
|
|||||||
class AddRemoteDialog(Adw.Dialog):
|
class AddRemoteDialog(Adw.Dialog):
|
||||||
__gtype_name__ = "AddRemoteDialog"
|
__gtype_name__ = "AddRemoteDialog"
|
||||||
gtc = Gtk.Template.Child
|
gtc = Gtk.Template.Child
|
||||||
|
|
||||||
toast_overlay = gtc()
|
toast_overlay = gtc()
|
||||||
cancel_button = gtc()
|
cancel_button = gtc()
|
||||||
apply_button = gtc()
|
apply_button = gtc()
|
||||||
@@ -19,7 +19,7 @@ class AddRemoteDialog(Adw.Dialog):
|
|||||||
url_row = gtc()
|
url_row = gtc()
|
||||||
installation_chooser = gtc()
|
installation_chooser = gtc()
|
||||||
is_open = False
|
is_open = False
|
||||||
|
|
||||||
def on_apply(self, *args):
|
def on_apply(self, *args):
|
||||||
self.parent_page.status_stack.set_visible_child(self.parent_page.adding_view)
|
self.parent_page.status_stack.set_visible_child(self.parent_page.adding_view)
|
||||||
self.apply_button.set_sensitive(False)
|
self.apply_button.set_sensitive(False)
|
||||||
@@ -38,14 +38,14 @@ class AddRemoteDialog(Adw.Dialog):
|
|||||||
cmd.append(f"--{installation}")
|
cmd.append(f"--{installation}")
|
||||||
else:
|
else:
|
||||||
cmd.append(f"--installation={installation}")
|
cmd.append(f"--installation={installation}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
subprocess.run(cmd, check=True, capture_output=True, text=True)
|
subprocess.run(cmd, check=True, capture_output=True, text=True)
|
||||||
except subprocess.CalledProcessError as cpe:
|
except subprocess.CalledProcessError as cpe:
|
||||||
error[0] = cpe.stderr
|
error[0] = cpe.stderr
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
error[0] = e
|
error[0] = e
|
||||||
|
|
||||||
def callback(*args):
|
def callback(*args):
|
||||||
HostInfo.main_window.remove_refresh_lockout("adding remote")
|
HostInfo.main_window.remove_refresh_lockout("adding remote")
|
||||||
if error[0]:
|
if error[0]:
|
||||||
@@ -55,17 +55,17 @@ class AddRemoteDialog(Adw.Dialog):
|
|||||||
else:
|
else:
|
||||||
self.main_window.refresh_handler()
|
self.main_window.refresh_handler()
|
||||||
self.parent_page.toast_overlay.add_toast(Adw.Toast(title=_("Added {}").format(self.title_row.get_text())))
|
self.parent_page.toast_overlay.add_toast(Adw.Toast(title=_("Added {}").format(self.title_row.get_text())))
|
||||||
|
|
||||||
Gio.Task.new(None, None, callback).run_in_thread(thread)
|
Gio.Task.new(None, None, callback).run_in_thread(thread)
|
||||||
self.close()
|
self.close()
|
||||||
|
|
||||||
def check_entries(self, row):
|
def check_entries(self, row):
|
||||||
is_passing = re.match(self.rexes[row], row.get_text())
|
is_passing = re.match(self.rexes[row], row.get_text())
|
||||||
if is_passing:
|
if is_passing:
|
||||||
row.remove_css_class("error")
|
row.remove_css_class("error")
|
||||||
else:
|
else:
|
||||||
row.add_css_class("error")
|
row.add_css_class("error")
|
||||||
|
|
||||||
match row:
|
match row:
|
||||||
case self.title_row:
|
case self.title_row:
|
||||||
self.title_passes = bool(is_passing)
|
self.title_passes = bool(is_passing)
|
||||||
@@ -73,27 +73,27 @@ class AddRemoteDialog(Adw.Dialog):
|
|||||||
self.name_passes = bool(is_passing)
|
self.name_passes = bool(is_passing)
|
||||||
case self.url_row:
|
case self.url_row:
|
||||||
self.url_passes = bool(is_passing)
|
self.url_passes = bool(is_passing)
|
||||||
|
|
||||||
self.apply_button.set_sensitive(self.title_passes and self.name_passes and self.url_passes)
|
self.apply_button.set_sensitive(self.title_passes and self.name_passes and self.url_passes)
|
||||||
|
|
||||||
def present(self, *args, **kwargs):
|
def present(self, *args, **kwargs):
|
||||||
if self.__class__.is_open:
|
if self.__class__.is_open:
|
||||||
return
|
return
|
||||||
|
|
||||||
self.__class__.is_open = True
|
self.__class__.is_open = True
|
||||||
super().present(*args, **kwargs)
|
super().present(*args, **kwargs)
|
||||||
|
|
||||||
def on_close(self, *args):
|
def on_close(self, *args):
|
||||||
self.__class__.is_open = False
|
self.__class__.is_open = False
|
||||||
|
|
||||||
def __init__(self, main_window, parent_page, remote_info=None, **kwargs):
|
def __init__(self, main_window, parent_page, remote_info=None, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
# Extra Object Creation
|
# Extra Object Creation
|
||||||
self.string_list = Gtk.StringList(strings=HostInfo.installations)
|
self.string_list = Gtk.StringList(strings=HostInfo.installations)
|
||||||
self.main_window = main_window
|
self.main_window = main_window
|
||||||
self.parent_page = parent_page
|
self.parent_page = parent_page
|
||||||
|
|
||||||
self.rexes = {
|
self.rexes = {
|
||||||
self.title_row: r"^(?=.*[A-Za-z0-9])[A-Za-z0-9._-]+( +[A-Za-z0-9._-]+)*$",
|
self.title_row: r"^(?=.*[A-Za-z0-9])[A-Za-z0-9._-]+( +[A-Za-z0-9._-]+)*$",
|
||||||
self.name_row: r"^[a-zA-Z0-9\-._]+$",
|
self.name_row: r"^[a-zA-Z0-9\-._]+$",
|
||||||
@@ -102,7 +102,7 @@ class AddRemoteDialog(Adw.Dialog):
|
|||||||
self.title_passes = False
|
self.title_passes = False
|
||||||
self.name_passes = False
|
self.name_passes = False
|
||||||
self.url_passes = False
|
self.url_passes = False
|
||||||
|
|
||||||
# Apply
|
# Apply
|
||||||
self.installation_chooser.set_content_strings(_("remote"), False)
|
self.installation_chooser.set_content_strings(_("remote"), False)
|
||||||
if remote_info:
|
if remote_info:
|
||||||
@@ -121,7 +121,7 @@ class AddRemoteDialog(Adw.Dialog):
|
|||||||
self.apply_button.set_sensitive(True)
|
self.apply_button.set_sensitive(True)
|
||||||
else:
|
else:
|
||||||
self.apply_button.set_sensitive(False)
|
self.apply_button.set_sensitive(False)
|
||||||
|
|
||||||
# Connections
|
# Connections
|
||||||
self.connect("closed", self.on_close)
|
self.connect("closed", self.on_close)
|
||||||
self.cancel_button.connect("clicked", lambda *_: self.close())
|
self.cancel_button.connect("clicked", lambda *_: self.close())
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ class RemoteRow(Adw.ActionRow):
|
|||||||
if len(has_error) > 0:
|
if len(has_error) > 0:
|
||||||
GLib.idle_add(lambda *args, cpe=cpe: self.parent_page.toast_overlay.add_toast(ErrorToast(_("Could not enable remote"), has_error[0]).toast))
|
GLib.idle_add(lambda *args, cpe=cpe: self.parent_page.toast_overlay.add_toast(ErrorToast(_("Could not enable remote"), has_error[0]).toast))
|
||||||
return
|
return
|
||||||
|
|
||||||
self.remove_css_class("warning")
|
self.remove_css_class("warning")
|
||||||
self.set_icon_name("")
|
self.set_icon_name("")
|
||||||
self.set_tooltip_text("")
|
self.set_tooltip_text("")
|
||||||
@@ -60,7 +60,7 @@ class RemoteRow(Adw.ActionRow):
|
|||||||
if self.parent_page.total_disabled == 0:
|
if self.parent_page.total_disabled == 0:
|
||||||
self.parent_page.show_disabled_button.set_active(False)
|
self.parent_page.show_disabled_button.set_active(False)
|
||||||
self.parent_page.show_disabled_button.set_visible(False)
|
self.parent_page.show_disabled_button.set_visible(False)
|
||||||
|
|
||||||
Gio.Task.new(None, None, callback).run_in_thread(thread)
|
Gio.Task.new(None, None, callback).run_in_thread(thread)
|
||||||
|
|
||||||
def disable_remote_handler(self, *args):
|
def disable_remote_handler(self, *args):
|
||||||
@@ -115,7 +115,7 @@ class RemoteRow(Adw.ActionRow):
|
|||||||
def on_response(_, response):
|
def on_response(_, response):
|
||||||
if response != "continue":
|
if response != "continue":
|
||||||
return
|
return
|
||||||
|
|
||||||
Gio.Task.new(None, None, callback).run_in_thread(thread)
|
Gio.Task.new(None, None, callback).run_in_thread(thread)
|
||||||
|
|
||||||
dialog = Adw.AlertDialog(heading=_("Disable {}?").format(self.remote.title), body=_("Any installed apps from {} will stop receiving updates").format(self.remote.name))
|
dialog = Adw.AlertDialog(heading=_("Disable {}?").format(self.remote.title), body=_("Any installed apps from {} will stop receiving updates").format(self.remote.name))
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ class NewRemoteRow(Adw.ActionRow):
|
|||||||
|
|
||||||
@Gtk.Template(resource_path="/io/github/flattool/Warehouse/remotes_page/remotes_page.ui")
|
@Gtk.Template(resource_path="/io/github/flattool/Warehouse/remotes_page/remotes_page.ui")
|
||||||
class RemotesPage(Adw.NavigationPage):
|
class RemotesPage(Adw.NavigationPage):
|
||||||
|
|
||||||
# Preselected Remotes
|
# Preselected Remotes
|
||||||
new_remotes = [
|
new_remotes = [
|
||||||
{
|
{
|
||||||
@@ -92,7 +91,7 @@ class RemotesPage(Adw.NavigationPage):
|
|||||||
# This must be set to the created object from within the class's __init__ method
|
# This must be set to the created object from within the class's __init__ method
|
||||||
instance = None
|
instance = None
|
||||||
page_name = "remotes"
|
page_name = "remotes"
|
||||||
|
|
||||||
def start_loading(self):
|
def start_loading(self):
|
||||||
self.search_button.set_active(False)
|
self.search_button.set_active(False)
|
||||||
self.status_stack.set_visible_child(self.loading_view)
|
self.status_stack.set_visible_child(self.loading_view)
|
||||||
@@ -137,7 +136,7 @@ class RemotesPage(Adw.NavigationPage):
|
|||||||
if row.get_visible():
|
if row.get_visible():
|
||||||
any_visible = True
|
any_visible = True
|
||||||
break
|
break
|
||||||
|
|
||||||
self.none_visible.set_visible(not any_visible)
|
self.none_visible.set_visible(not any_visible)
|
||||||
|
|
||||||
def filter_remote(self, row):
|
def filter_remote(self, row):
|
||||||
@@ -150,7 +149,7 @@ class RemotesPage(Adw.NavigationPage):
|
|||||||
packages_page.apply_filters()
|
packages_page.apply_filters()
|
||||||
GLib.idle_add(lambda *_: self.main_window.activate_row(self.main_window.packages_row))
|
GLib.idle_add(lambda *_: self.main_window.activate_row(self.main_window.packages_row))
|
||||||
GLib.idle_add(lambda *args: packages_page.packages_toast_overlay.add_toast(Adw.Toast(title=_("Showing all packages from {}").format(row.remote.title))))
|
GLib.idle_add(lambda *args: packages_page.packages_toast_overlay.add_toast(Adw.Toast(title=_("Showing all packages from {}").format(row.remote.title))))
|
||||||
|
|
||||||
def remove_remote(self, row):
|
def remove_remote(self, row):
|
||||||
error = [None]
|
error = [None]
|
||||||
def thread(*args):
|
def thread(*args):
|
||||||
@@ -182,7 +181,7 @@ class RemotesPage(Adw.NavigationPage):
|
|||||||
def on_response(_, response):
|
def on_response(_, response):
|
||||||
if response != "continue":
|
if response != "continue":
|
||||||
return
|
return
|
||||||
|
|
||||||
Gio.Task.new(None, None, callback).run_in_thread(thread)
|
Gio.Task.new(None, None, callback).run_in_thread(thread)
|
||||||
|
|
||||||
dialog = Adw.AlertDialog(heading=_("Remove {}?").format(row.remote.title), body=_("Any installed apps from {} will stop receiving updates").format(row.remote.name))
|
dialog = Adw.AlertDialog(heading=_("Remove {}?").format(row.remote.title), body=_("Any installed apps from {} will stop receiving updates").format(row.remote.name))
|
||||||
@@ -196,7 +195,7 @@ class RemotesPage(Adw.NavigationPage):
|
|||||||
text = entry.get_text().lower()
|
text = entry.get_text().lower()
|
||||||
total = 0
|
total = 0
|
||||||
show_disabled = self.show_disabled_button.get_active()
|
show_disabled = self.show_disabled_button.get_active()
|
||||||
|
|
||||||
for row in self.current_remote_rows:
|
for row in self.current_remote_rows:
|
||||||
title_match = text in row.get_title().lower()
|
title_match = text in row.get_title().lower()
|
||||||
subtitle_match = text in row.get_subtitle().lower()
|
subtitle_match = text in row.get_subtitle().lower()
|
||||||
@@ -209,7 +208,7 @@ class RemotesPage(Adw.NavigationPage):
|
|||||||
return
|
return
|
||||||
|
|
||||||
self.stack.set_visible_child(self.content_page if total > 0 else self.no_results)
|
self.stack.set_visible_child(self.content_page if total > 0 else self.no_results)
|
||||||
|
|
||||||
def local_file_handler(self, path):
|
def local_file_handler(self, path):
|
||||||
try:
|
try:
|
||||||
name = path.split("/")[-1].split(".")[0]
|
name = path.split("/")[-1].split(".")[0]
|
||||||
@@ -260,7 +259,7 @@ class RemotesPage(Adw.NavigationPage):
|
|||||||
total_visible += 1
|
total_visible += 1
|
||||||
|
|
||||||
self.none_visible.set_visible(total_visible == 0)
|
self.none_visible.set_visible(total_visible == 0)
|
||||||
|
|
||||||
def new_custom_handler(self, *args):
|
def new_custom_handler(self, *args):
|
||||||
AddRemoteDialog(self.main_window, self).present(self.main_window)
|
AddRemoteDialog(self.main_window, self).present(self.main_window)
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import os, time
|
|||||||
class NewSnapshotDialog(Adw.Dialog):
|
class NewSnapshotDialog(Adw.Dialog):
|
||||||
__gtype_name__ = "NewSnapshotDialog"
|
__gtype_name__ = "NewSnapshotDialog"
|
||||||
gtc = Gtk.Template.Child
|
gtc = Gtk.Template.Child
|
||||||
|
|
||||||
toast_overlay = gtc()
|
toast_overlay = gtc()
|
||||||
nav_page = gtc()
|
nav_page = gtc()
|
||||||
list_cancel_button = gtc()
|
list_cancel_button = gtc()
|
||||||
@@ -24,39 +24,39 @@ class NewSnapshotDialog(Adw.Dialog):
|
|||||||
no_results = gtc()
|
no_results = gtc()
|
||||||
stack = gtc()
|
stack = gtc()
|
||||||
is_open = False
|
is_open = False
|
||||||
|
|
||||||
def row_gesture_handler(self, row):
|
def row_gesture_handler(self, row):
|
||||||
row.check_button.set_active(not row.check_button.get_active())
|
row.check_button.set_active(not row.check_button.get_active())
|
||||||
|
|
||||||
def row_select_handler(self, row):
|
def row_select_handler(self, row):
|
||||||
if row.check_button.get_active():
|
if row.check_button.get_active():
|
||||||
self.selected_rows.append(row)
|
self.selected_rows.append(row)
|
||||||
else:
|
else:
|
||||||
self.selected_rows.remove(row)
|
self.selected_rows.remove(row)
|
||||||
|
|
||||||
total = len(self.selected_rows)
|
total = len(self.selected_rows)
|
||||||
self.total_selected_label.set_label(_("{} Selected").format(total))
|
self.total_selected_label.set_label(_("{} Selected").format(total))
|
||||||
self.total_selected_label.set_visible(total > 0)
|
self.total_selected_label.set_visible(total > 0)
|
||||||
self.valid_checker()
|
self.valid_checker()
|
||||||
|
|
||||||
def generate_list(self, *args):
|
def generate_list(self, *args):
|
||||||
for package in HostInfo.flatpaks:
|
for package in HostInfo.flatpaks:
|
||||||
if "io.github.flattool.Warehouse" in package.info["id"]:
|
if "io.github.flattool.Warehouse" in package.info["id"]:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if package.is_runtime or not os.path.exists(package.data_path):
|
if package.is_runtime or not os.path.exists(package.data_path):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
row = AppRow(package, self.row_gesture_handler)
|
row = AppRow(package, self.row_gesture_handler)
|
||||||
row.check_button.set_visible(True)
|
row.check_button.set_visible(True)
|
||||||
row.check_button.connect("toggled", lambda *_, row=row: self.row_select_handler(row))
|
row.check_button.connect("toggled", lambda *_, row=row: self.row_select_handler(row))
|
||||||
row.set_activatable(True)
|
row.set_activatable(True)
|
||||||
row.set_activatable_widget(row.check_button)
|
row.set_activatable_widget(row.check_button)
|
||||||
self.listbox.append(row)
|
self.listbox.append(row)
|
||||||
|
|
||||||
def sort_func(self, row1, row2):
|
def sort_func(self, row1, row2):
|
||||||
return row1.package.info["name"].lower() > row2.package.info["name"].lower()
|
return row1.package.info["name"].lower() > row2.package.info["name"].lower()
|
||||||
|
|
||||||
def filter_func(self, row):
|
def filter_func(self, row):
|
||||||
title = row.get_title().lower()
|
title = row.get_title().lower()
|
||||||
subtitle = row.get_subtitle().lower()
|
subtitle = row.get_subtitle().lower()
|
||||||
@@ -66,13 +66,13 @@ class NewSnapshotDialog(Adw.Dialog):
|
|||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def on_close(self, *args):
|
def on_close(self, *args):
|
||||||
self.__class__.is_open = False
|
self.__class__.is_open = False
|
||||||
self.search_button.set_active(False)
|
self.search_button.set_active(False)
|
||||||
for row in self.selected_rows.copy():
|
for row in self.selected_rows.copy():
|
||||||
GLib.idle_add(lambda *_, row=row: row.check_button.set_active(False))
|
GLib.idle_add(lambda *_, row=row: row.check_button.set_active(False))
|
||||||
|
|
||||||
def valid_checker(self):
|
def valid_checker(self):
|
||||||
text = self.name_entry.get_text().strip()
|
text = self.name_entry.get_text().strip()
|
||||||
something_selected = len(self.selected_rows) > 0
|
something_selected = len(self.selected_rows) > 0
|
||||||
@@ -82,9 +82,9 @@ class NewSnapshotDialog(Adw.Dialog):
|
|||||||
self.name_entry.remove_css_class("error")
|
self.name_entry.remove_css_class("error")
|
||||||
else:
|
else:
|
||||||
self.name_entry.add_css_class("error")
|
self.name_entry.add_css_class("error")
|
||||||
|
|
||||||
return something_selected and text_good
|
return something_selected and text_good
|
||||||
|
|
||||||
def get_total_fraction(self):
|
def get_total_fraction(self):
|
||||||
total = 0
|
total = 0
|
||||||
stopped_workers_amount = 0
|
stopped_workers_amount = 0
|
||||||
@@ -92,20 +92,20 @@ class NewSnapshotDialog(Adw.Dialog):
|
|||||||
total += worker.fraction
|
total += worker.fraction
|
||||||
if worker.stop:
|
if worker.stop:
|
||||||
stopped_workers_amount += 1
|
stopped_workers_amount += 1
|
||||||
|
|
||||||
if stopped_workers_amount == len(self.workers):
|
if stopped_workers_amount == len(self.workers):
|
||||||
self.loading_status.progress_bar.set_fraction(1)
|
self.loading_status.progress_bar.set_fraction(1)
|
||||||
self.loading_status.progress_label.set_label(f"{len(self.workers)} / {len(self.workers)}")
|
self.loading_status.progress_label.set_label(f"{len(self.workers)} / {len(self.workers)}")
|
||||||
self.workers.clear()
|
self.workers.clear()
|
||||||
if self.on_done:
|
if self.on_done:
|
||||||
self.on_done()
|
self.on_done()
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
self.loading_status.progress_label.set_label(f"{stopped_workers_amount + 1} / {len(self.workers)}")
|
self.loading_status.progress_label.set_label(f"{stopped_workers_amount + 1} / {len(self.workers)}")
|
||||||
self.loading_status.progress_bar.set_fraction(total / len(self.workers))
|
self.loading_status.progress_bar.set_fraction(total / len(self.workers))
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def on_create(self, button):
|
def on_create(self, button):
|
||||||
self.loading_status.title_label.set_label(_("Creating Snapshot"))
|
self.loading_status.title_label.set_label(_("Creating Snapshot"))
|
||||||
self.loading_status.progress_bar.set_fraction(0.0)
|
self.loading_status.progress_bar.set_fraction(0.0)
|
||||||
@@ -114,7 +114,7 @@ class NewSnapshotDialog(Adw.Dialog):
|
|||||||
for row in self.selected_rows:
|
for row in self.selected_rows:
|
||||||
if "io.github.flattool.Warehouse" in row.package.info["id"]:
|
if "io.github.flattool.Warehouse" in row.package.info["id"]:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
package = row.package
|
package = row.package
|
||||||
worker = TarWorker(
|
worker = TarWorker(
|
||||||
existing_path=package.data_path,
|
existing_path=package.data_path,
|
||||||
@@ -125,11 +125,11 @@ class NewSnapshotDialog(Adw.Dialog):
|
|||||||
)
|
)
|
||||||
self.workers.append(worker)
|
self.workers.append(worker)
|
||||||
worker.compress()
|
worker.compress()
|
||||||
|
|
||||||
self.loading_status.progress_label.set_visible(len(self.workers) > 1)
|
self.loading_status.progress_label.set_visible(len(self.workers) > 1)
|
||||||
GLib.timeout_add(200, self.get_total_fraction)
|
GLib.timeout_add(200, self.get_total_fraction)
|
||||||
self.close()
|
self.close()
|
||||||
|
|
||||||
def on_invalidate(self, search_entry):
|
def on_invalidate(self, search_entry):
|
||||||
self.is_result = False
|
self.is_result = False
|
||||||
self.listbox.invalidate_filter()
|
self.listbox.invalidate_filter()
|
||||||
@@ -137,36 +137,36 @@ class NewSnapshotDialog(Adw.Dialog):
|
|||||||
self.stack.set_visible_child(self.scrolled_window)
|
self.stack.set_visible_child(self.scrolled_window)
|
||||||
else:
|
else:
|
||||||
self.stack.set_visible_child(self.no_results)
|
self.stack.set_visible_child(self.no_results)
|
||||||
|
|
||||||
def on_select_all(self, button):
|
def on_select_all(self, button):
|
||||||
i = 0
|
i = 0
|
||||||
while row := self.listbox.get_row_at_index(i):
|
while row := self.listbox.get_row_at_index(i):
|
||||||
i += 1
|
i += 1
|
||||||
row.check_button.set_active(True)
|
row.check_button.set_active(True)
|
||||||
|
|
||||||
def set_packages(self):
|
def set_packages(self):
|
||||||
for package in self.packages:
|
for package in self.packages:
|
||||||
row = AppRow(package)
|
row = AppRow(package)
|
||||||
row.set_activatable(False)
|
row.set_activatable(False)
|
||||||
self.selected_rows.append(row)
|
self.selected_rows.append(row)
|
||||||
self.listbox.append(row)
|
self.listbox.append(row)
|
||||||
|
|
||||||
def enter_handler(self, *args):
|
def enter_handler(self, *args):
|
||||||
if self.create_button.get_sensitive():
|
if self.create_button.get_sensitive():
|
||||||
self.create_button.activate()
|
self.create_button.activate()
|
||||||
|
|
||||||
def present(self, *args, **kwargs):
|
def present(self, *args, **kwargs):
|
||||||
if self.__class__.is_open:
|
if self.__class__.is_open:
|
||||||
return
|
return
|
||||||
|
|
||||||
super().present(*args, **kwargs)
|
super().present(*args, **kwargs)
|
||||||
self.__class__.is_open = True
|
self.__class__.is_open = True
|
||||||
if not self.search_button.get_visible():
|
if not self.search_button.get_visible():
|
||||||
self.name_entry.grab_focus()
|
self.name_entry.grab_focus()
|
||||||
|
|
||||||
def __init__(self, snapshot_page, loading_status, on_done=None, packages=None, **kwargs):
|
def __init__(self, snapshot_page, loading_status, on_done=None, packages=None, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
# Extra Object Creations
|
# Extra Object Creations
|
||||||
self.snapshot_page = snapshot_page
|
self.snapshot_page = snapshot_page
|
||||||
self.loading_status = loading_status
|
self.loading_status = loading_status
|
||||||
@@ -176,7 +176,7 @@ class NewSnapshotDialog(Adw.Dialog):
|
|||||||
self.selected_rows = []
|
self.selected_rows = []
|
||||||
self.workers = []
|
self.workers = []
|
||||||
self.packages = packages
|
self.packages = packages
|
||||||
|
|
||||||
# Connections
|
# Connections
|
||||||
self.connect("closed", self.on_close)
|
self.connect("closed", self.on_close)
|
||||||
self.create_button.connect("clicked", self.on_create)
|
self.create_button.connect("clicked", self.on_create)
|
||||||
@@ -185,7 +185,7 @@ class NewSnapshotDialog(Adw.Dialog):
|
|||||||
self.name_entry.connect("changed", lambda *_: self.valid_checker())
|
self.name_entry.connect("changed", lambda *_: self.valid_checker())
|
||||||
self.name_entry.connect("entry-activated", self.enter_handler)
|
self.name_entry.connect("entry-activated", self.enter_handler)
|
||||||
self.select_all_button.connect("clicked", self.on_select_all)
|
self.select_all_button.connect("clicked", self.on_select_all)
|
||||||
|
|
||||||
# Apply
|
# Apply
|
||||||
self.listbox.set_sort_func(self.sort_func)
|
self.listbox.set_sort_func(self.sort_func)
|
||||||
self.listbox.set_filter_func(self.filter_func)
|
self.listbox.set_filter_func(self.filter_func)
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import os, subprocess, json
|
|||||||
class SnapshotBox(Gtk.Box):
|
class SnapshotBox(Gtk.Box):
|
||||||
__gtype_name__ = "SnapshotBox"
|
__gtype_name__ = "SnapshotBox"
|
||||||
gtc = Gtk.Template.Child
|
gtc = Gtk.Template.Child
|
||||||
|
|
||||||
title = gtc()
|
title = gtc()
|
||||||
date = gtc()
|
date = gtc()
|
||||||
version = gtc()
|
version = gtc()
|
||||||
@@ -18,7 +18,7 @@ class SnapshotBox(Gtk.Box):
|
|||||||
rename_entry = gtc()
|
rename_entry = gtc()
|
||||||
apply_rename = gtc()
|
apply_rename = gtc()
|
||||||
trash_button = gtc()
|
trash_button = gtc()
|
||||||
|
|
||||||
def create_json(self):
|
def create_json(self):
|
||||||
try:
|
try:
|
||||||
data = {
|
data = {
|
||||||
@@ -28,10 +28,10 @@ class SnapshotBox(Gtk.Box):
|
|||||||
with open(self.json_path, 'w') as file:
|
with open(self.json_path, 'w') as file:
|
||||||
json.dump(data, file, indent=4)
|
json.dump(data, file, indent=4)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.toast_overlay.add_toast(ErrorToast(_("Could not write data"), str(e)).toast)
|
self.toast_overlay.add_toast(ErrorToast(_("Could not write data"), str(e)).toast)
|
||||||
|
|
||||||
def update_json(self, key, value):
|
def update_json(self, key, value):
|
||||||
try:
|
try:
|
||||||
with open(self.json_path, 'r+') as file:
|
with open(self.json_path, 'r+') as file:
|
||||||
@@ -40,14 +40,14 @@ class SnapshotBox(Gtk.Box):
|
|||||||
file.seek(0)
|
file.seek(0)
|
||||||
json.dump(data, file, indent=4)
|
json.dump(data, file, indent=4)
|
||||||
file.truncate()
|
file.truncate()
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.toast_overlay.add_toast(ErrorToast(_("Could not write data"), str(e)).toast)
|
self.toast_overlay.add_toast(ErrorToast(_("Could not write data"), str(e)).toast)
|
||||||
|
|
||||||
def load_from_json(self):
|
def load_from_json(self):
|
||||||
if not os.path.exists(self.json_path):
|
if not os.path.exists(self.json_path):
|
||||||
self.create_json()
|
self.create_json()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with open(self.json_path, 'r') as file:
|
with open(self.json_path, 'r') as file:
|
||||||
data = json.load(file)
|
data = json.load(file)
|
||||||
@@ -56,18 +56,18 @@ class SnapshotBox(Gtk.Box):
|
|||||||
self.title.set_label(GLib.markup_escape_text(name))
|
self.title.set_label(GLib.markup_escape_text(name))
|
||||||
else:
|
else:
|
||||||
self.title.set_label(_("No Name Set"))
|
self.title.set_label(_("No Name Set"))
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.toast_overlay.add_toast(ErrorToast(_("Could not write data"), str(e)).toast)
|
self.toast_overlay.add_toast(ErrorToast(_("Could not write data"), str(e)).toast)
|
||||||
|
|
||||||
def on_rename(self, widget):
|
def on_rename(self, widget):
|
||||||
if not self.valid_checker():
|
if not self.valid_checker():
|
||||||
return
|
return
|
||||||
|
|
||||||
self.update_json('name', self.rename_entry.get_text().strip())
|
self.update_json('name', self.rename_entry.get_text().strip())
|
||||||
self.load_from_json()
|
self.load_from_json()
|
||||||
self.rename_menu.popdown()
|
self.rename_menu.popdown()
|
||||||
|
|
||||||
def valid_checker(self, *args):
|
def valid_checker(self, *args):
|
||||||
text = self.rename_entry.get_text().strip()
|
text = self.rename_entry.get_text().strip()
|
||||||
valid = not ("/" in text or "\0" in text) and len(text) > 0
|
valid = not ("/" in text or "\0" in text) and len(text) > 0
|
||||||
@@ -76,15 +76,15 @@ class SnapshotBox(Gtk.Box):
|
|||||||
self.rename_entry.remove_css_class("error")
|
self.rename_entry.remove_css_class("error")
|
||||||
else:
|
else:
|
||||||
self.rename_entry.add_css_class("error")
|
self.rename_entry.add_css_class("error")
|
||||||
|
|
||||||
return valid
|
return valid
|
||||||
|
|
||||||
def on_trash(self, button):
|
def on_trash(self, button):
|
||||||
error = [None]
|
error = [None]
|
||||||
path = f"{self.snapshots_path}{self.folder}"
|
path = f"{self.snapshots_path}{self.folder}"
|
||||||
if self.snapshot_page.is_trash_dialog_open:
|
if self.snapshot_page.is_trash_dialog_open:
|
||||||
return
|
return
|
||||||
|
|
||||||
def thread(*args):
|
def thread(*args):
|
||||||
try:
|
try:
|
||||||
subprocess.run(['gio', 'trash', path], capture_output=True, text=True, check=True)
|
subprocess.run(['gio', 'trash', path], capture_output=True, text=True, check=True)
|
||||||
@@ -92,22 +92,22 @@ class SnapshotBox(Gtk.Box):
|
|||||||
error[0] = cpe.stderr
|
error[0] = cpe.stderr
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
error[0] = str(e)
|
error[0] = str(e)
|
||||||
|
|
||||||
def callback(*args):
|
def callback(*args):
|
||||||
if not error[0] is None:
|
if not error[0] is None:
|
||||||
self.toast_overlay.add_toast(ErrorToast(_("Could not trash snapshot"), error[0]).toast)
|
self.toast_overlay.add_toast(ErrorToast(_("Could not trash snapshot"), error[0]).toast)
|
||||||
return
|
return
|
||||||
|
|
||||||
self.parent_page.on_trash()
|
self.parent_page.on_trash()
|
||||||
self.toast_overlay.add_toast(Adw.Toast.new(_("Trashed snapshot")))
|
self.toast_overlay.add_toast(Adw.Toast.new(_("Trashed snapshot")))
|
||||||
|
|
||||||
def on_response(_, response):
|
def on_response(_, response):
|
||||||
self.snapshot_page.is_trash_dialog_open = False
|
self.snapshot_page.is_trash_dialog_open = False
|
||||||
if response != "continue":
|
if response != "continue":
|
||||||
return
|
return
|
||||||
|
|
||||||
Gio.Task.new(None, None, callback).run_in_thread(thread)
|
Gio.Task.new(None, None, callback).run_in_thread(thread)
|
||||||
|
|
||||||
self.snapshot_page.is_trash_dialog_open = True
|
self.snapshot_page.is_trash_dialog_open = True
|
||||||
dialog = Adw.AlertDialog(heading=_("Trash Snapshot?"), body=_("This snapshot will be sent to the trash"))
|
dialog = Adw.AlertDialog(heading=_("Trash Snapshot?"), body=_("This snapshot will be sent to the trash"))
|
||||||
dialog.add_response("cancel", _("Cancel"))
|
dialog.add_response("cancel", _("Cancel"))
|
||||||
@@ -115,7 +115,7 @@ class SnapshotBox(Gtk.Box):
|
|||||||
dialog.set_response_appearance("continue", Adw.ResponseAppearance.DESTRUCTIVE)
|
dialog.set_response_appearance("continue", Adw.ResponseAppearance.DESTRUCTIVE)
|
||||||
dialog.connect("response", on_response)
|
dialog.connect("response", on_response)
|
||||||
dialog.present(HostInfo.main_window)
|
dialog.present(HostInfo.main_window)
|
||||||
|
|
||||||
def get_fraction(self):
|
def get_fraction(self):
|
||||||
loading_status = self.snapshot_page.snapshotting_status
|
loading_status = self.snapshot_page.snapshotting_status
|
||||||
loading_status.progress_bar.set_fraction(self.worker.fraction)
|
loading_status.progress_bar.set_fraction(self.worker.fraction)
|
||||||
@@ -129,16 +129,16 @@ class SnapshotBox(Gtk.Box):
|
|||||||
data_page.end_loading()
|
data_page.end_loading()
|
||||||
if self.worker in self.snapshot_page.workers:
|
if self.worker in self.snapshot_page.workers:
|
||||||
self.snapshot_page.workers.remove(self.worker)
|
self.snapshot_page.workers.remove(self.worker)
|
||||||
|
|
||||||
return False # Stop the timeout
|
return False # Stop the timeout
|
||||||
else:
|
else:
|
||||||
return True # Continue the timeout
|
return True # Continue the timeout
|
||||||
|
|
||||||
def on_apply(self, button):
|
def on_apply(self, button):
|
||||||
def on_response(dialog, response):
|
def on_response(dialog, response):
|
||||||
if response != "continue":
|
if response != "continue":
|
||||||
return
|
return
|
||||||
|
|
||||||
self.snapshot_page.snapshotting_status.title_label.set_label(_("Applying Snapshot"))
|
self.snapshot_page.snapshotting_status.title_label.set_label(_("Applying Snapshot"))
|
||||||
self.snapshot_page.snapshotting_status.progress_label.set_visible(False)
|
self.snapshot_page.snapshotting_status.progress_label.set_visible(False)
|
||||||
self.snapshot_page.snapshotting_status.progress_bar.set_fraction(0.0)
|
self.snapshot_page.snapshotting_status.progress_bar.set_fraction(0.0)
|
||||||
@@ -146,7 +146,7 @@ class SnapshotBox(Gtk.Box):
|
|||||||
self.snapshot_page.workers.append(self.worker)
|
self.snapshot_page.workers.append(self.worker)
|
||||||
self.worker.extract()
|
self.worker.extract()
|
||||||
GLib.timeout_add(200, self.get_fraction)
|
GLib.timeout_add(200, self.get_fraction)
|
||||||
|
|
||||||
has_data = os.path.exists(self.worker.new_path)
|
has_data = os.path.exists(self.worker.new_path)
|
||||||
dialog = Adw.AlertDialog(
|
dialog = Adw.AlertDialog(
|
||||||
heading=_("Apply Snapshot?"),
|
heading=_("Apply Snapshot?"),
|
||||||
@@ -156,10 +156,10 @@ class SnapshotBox(Gtk.Box):
|
|||||||
dialog.add_response("continue", _("Apply"))
|
dialog.add_response("continue", _("Apply"))
|
||||||
dialog.connect("response", on_response)
|
dialog.connect("response", on_response)
|
||||||
dialog.present(HostInfo.main_window)
|
dialog.present(HostInfo.main_window)
|
||||||
|
|
||||||
def __init__(self, parent_page, folder, snapshots_path, toast_overlay, **kwargs):
|
def __init__(self, parent_page, folder, snapshots_path, toast_overlay, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
self.snapshot_page = parent_page.parent_page
|
self.snapshot_page = parent_page.parent_page
|
||||||
self.toast_overlay = toast_overlay
|
self.toast_overlay = toast_overlay
|
||||||
self.app_id = snapshots_path.split('/')[-2].strip()
|
self.app_id = snapshots_path.split('/')[-2].strip()
|
||||||
@@ -168,11 +168,11 @@ class SnapshotBox(Gtk.Box):
|
|||||||
new_path=f"{HostInfo.home}/.var/app/{self.app_id}/",
|
new_path=f"{HostInfo.home}/.var/app/{self.app_id}/",
|
||||||
toast_overlay=self.toast_overlay,
|
toast_overlay=self.toast_overlay,
|
||||||
)
|
)
|
||||||
|
|
||||||
split_folder = folder.split('_')
|
split_folder = folder.split('_')
|
||||||
if len(split_folder) < 2:
|
if len(split_folder) < 2:
|
||||||
return
|
return
|
||||||
|
|
||||||
self.parent_page = parent_page
|
self.parent_page = parent_page
|
||||||
self.folder = folder
|
self.folder = folder
|
||||||
self.snapshots_path = snapshots_path
|
self.snapshots_path = snapshots_path
|
||||||
|
|||||||
@@ -62,10 +62,10 @@ template $SnapshotPage : Adw.BreakpointBin {
|
|||||||
ScrolledWindow scrolled_window {
|
ScrolledWindow scrolled_window {
|
||||||
Box {
|
Box {
|
||||||
orientation: vertical;
|
orientation: vertical;
|
||||||
|
|
||||||
Box active_box {
|
Box active_box {
|
||||||
orientation: vertical;
|
orientation: vertical;
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
label: _("Active Snapshots");
|
label: _("Active Snapshots");
|
||||||
halign: start;
|
halign: start;
|
||||||
@@ -94,7 +94,7 @@ template $SnapshotPage : Adw.BreakpointBin {
|
|||||||
}
|
}
|
||||||
Box leftover_box {
|
Box leftover_box {
|
||||||
orientation: vertical;
|
orientation: vertical;
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
label: _("Leftover Snapshots");
|
label: _("Leftover Snapshots");
|
||||||
halign: start;
|
halign: start;
|
||||||
|
|||||||
@@ -12,27 +12,27 @@ import os, subprocess
|
|||||||
|
|
||||||
class LeftoverSnapshotRow(Adw.ActionRow):
|
class LeftoverSnapshotRow(Adw.ActionRow):
|
||||||
__gtype_name__ = "LeftoverSnapshotRow"
|
__gtype_name__ = "LeftoverSnapshotRow"
|
||||||
|
|
||||||
def idle_stuff(self):
|
def idle_stuff(self):
|
||||||
self.set_title(self.name)
|
self.set_title(self.name)
|
||||||
icon = Gtk.Image.new_from_icon_name("application-x-executable-symbolic")
|
icon = Gtk.Image.new_from_icon_name("application-x-executable-symbolic")
|
||||||
icon.set_icon_size(Gtk.IconSize.LARGE)
|
icon.set_icon_size(Gtk.IconSize.LARGE)
|
||||||
self.add_prefix(icon)
|
self.add_prefix(icon)
|
||||||
self.add_suffix(self.check_button)
|
self.add_suffix(self.check_button)
|
||||||
|
|
||||||
def gesture_handler(self, *args):
|
def gesture_handler(self, *args):
|
||||||
self.on_long_press(self)
|
self.on_long_press(self)
|
||||||
|
|
||||||
def __init__(self, folder, on_long_press, **kwargs):
|
def __init__(self, folder, on_long_press, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
# Extra Object Creation
|
# Extra Object Creation
|
||||||
self.folder = folder
|
self.folder = folder
|
||||||
self.check_button = Gtk.CheckButton(visible=False)
|
self.check_button = Gtk.CheckButton(visible=False)
|
||||||
self.on_long_press = on_long_press
|
self.on_long_press = on_long_press
|
||||||
self.rclick_gesture = Gtk.GestureClick(button=3)
|
self.rclick_gesture = Gtk.GestureClick(button=3)
|
||||||
self.long_press_gesture = Gtk.GestureLongPress()
|
self.long_press_gesture = Gtk.GestureLongPress()
|
||||||
|
|
||||||
# Apply
|
# Apply
|
||||||
self.add_controller(self.rclick_gesture)
|
self.add_controller(self.rclick_gesture)
|
||||||
self.add_controller(self.long_press_gesture)
|
self.add_controller(self.long_press_gesture)
|
||||||
@@ -40,16 +40,16 @@ class LeftoverSnapshotRow(Adw.ActionRow):
|
|||||||
self.name = self.folder.split('.')[-1]
|
self.name = self.folder.split('.')[-1]
|
||||||
self.set_activatable(True)
|
self.set_activatable(True)
|
||||||
GLib.idle_add(lambda *_: self.idle_stuff())
|
GLib.idle_add(lambda *_: self.idle_stuff())
|
||||||
|
|
||||||
# Connections
|
# Connections
|
||||||
self.rclick_gesture.connect("released", self.gesture_handler)
|
self.rclick_gesture.connect("released", self.gesture_handler)
|
||||||
self.long_press_gesture.connect("pressed", self.gesture_handler)
|
self.long_press_gesture.connect("pressed", self.gesture_handler)
|
||||||
|
|
||||||
@Gtk.Template(resource_path="/io/github/flattool/Warehouse/snapshot_page/snapshot_page.ui")
|
@Gtk.Template(resource_path="/io/github/flattool/Warehouse/snapshot_page/snapshot_page.ui")
|
||||||
class SnapshotPage(Adw.BreakpointBin):
|
class SnapshotPage(Adw.BreakpointBin):
|
||||||
__gtype_name__ = "SnapshotPage"
|
__gtype_name__ = "SnapshotPage"
|
||||||
gtc = Gtk.Template.Child
|
gtc = Gtk.Template.Child
|
||||||
|
|
||||||
toast_overlay = gtc()
|
toast_overlay = gtc()
|
||||||
sidebar_navpage = gtc()
|
sidebar_navpage = gtc()
|
||||||
search_button = gtc()
|
search_button = gtc()
|
||||||
@@ -80,7 +80,7 @@ class SnapshotPage(Adw.BreakpointBin):
|
|||||||
apply_snapshots = gtc()
|
apply_snapshots = gtc()
|
||||||
install_from_snapshots = gtc()
|
install_from_snapshots = gtc()
|
||||||
trash_snapshots = gtc()
|
trash_snapshots = gtc()
|
||||||
|
|
||||||
# Referred to in the main window
|
# Referred to in the main window
|
||||||
# It is used to determine if a new page should be made or not
|
# It is used to determine if a new page should be made or not
|
||||||
# This must be set to the created object from within the class's __init__ method
|
# This must be set to the created object from within the class's __init__ method
|
||||||
@@ -88,94 +88,94 @@ class SnapshotPage(Adw.BreakpointBin):
|
|||||||
page_name = "snapshots"
|
page_name = "snapshots"
|
||||||
is_trash_dialog_open = False
|
is_trash_dialog_open = False
|
||||||
last_activated_row = None
|
last_activated_row = None
|
||||||
|
|
||||||
def sort_snapshots(self, *args):
|
def sort_snapshots(self, *args):
|
||||||
self.active_snapshot_paks.clear()
|
self.active_snapshot_paks.clear()
|
||||||
self.leftover_snapshots.clear()
|
self.leftover_snapshots.clear()
|
||||||
bad_folders = []
|
bad_folders = []
|
||||||
|
|
||||||
if not os.path.exists(HostInfo.snapshots_path):
|
if not os.path.exists(HostInfo.snapshots_path):
|
||||||
try:
|
try:
|
||||||
os.makedirs(HostInfo.snapshots_path)
|
os.makedirs(HostInfo.snapshots_path)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.toast_overlay.add_toast(ErrorToast(_("Could not load Snapshots"), str(e)).toast)
|
self.toast_overlay.add_toast(ErrorToast(_("Could not load Snapshots"), str(e)).toast)
|
||||||
return
|
return
|
||||||
|
|
||||||
for folder in os.listdir(HostInfo.snapshots_path):
|
for folder in os.listdir(HostInfo.snapshots_path):
|
||||||
if folder.count('.') < 2 or ' ' in folder:
|
if folder.count('.') < 2 or ' ' in folder:
|
||||||
bad_folders.append(folder)
|
bad_folders.append(folder)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
has_tar = False
|
has_tar = False
|
||||||
for file in os.listdir(f"{HostInfo.snapshots_path}{folder}"):
|
for file in os.listdir(f"{HostInfo.snapshots_path}{folder}"):
|
||||||
if file.endswith(".tar.zst"):
|
if file.endswith(".tar.zst"):
|
||||||
has_tar = True
|
has_tar = True
|
||||||
break
|
break
|
||||||
|
|
||||||
if not has_tar:
|
if not has_tar:
|
||||||
bad_folders.append(folder)
|
bad_folders.append(folder)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
try:
|
try:
|
||||||
pak = HostInfo.id_to_flatpak[folder]
|
pak = HostInfo.id_to_flatpak[folder]
|
||||||
self.active_snapshot_paks.append(pak)
|
self.active_snapshot_paks.append(pak)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
self.leftover_snapshots.append(folder)
|
self.leftover_snapshots.append(folder)
|
||||||
|
|
||||||
for folder in bad_folders:
|
for folder in bad_folders:
|
||||||
try:
|
try:
|
||||||
subprocess.run(['gio', 'trash', f'{HostInfo.snapshots_path}{folder}'])
|
subprocess.run(['gio', 'trash', f'{HostInfo.snapshots_path}{folder}'])
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def long_press_handler(self, row):
|
def long_press_handler(self, row):
|
||||||
self.select_button.set_active(True)
|
self.select_button.set_active(True)
|
||||||
row.check_button.set_active(not row.check_button.get_active())
|
row.check_button.set_active(not row.check_button.get_active())
|
||||||
|
|
||||||
def generate_active_list(self):
|
def generate_active_list(self):
|
||||||
for pak in self.active_snapshot_paks:
|
for pak in self.active_snapshot_paks:
|
||||||
row = AppRow(pak, self.long_press_handler)
|
row = AppRow(pak, self.long_press_handler)
|
||||||
row.check_button.connect("toggled", lambda *_, _row=row: self.row_select_handler(_row))
|
row.check_button.connect("toggled", lambda *_, _row=row: self.row_select_handler(_row))
|
||||||
self.active_listbox.append(row)
|
self.active_listbox.append(row)
|
||||||
|
|
||||||
if len(self.active_snapshot_paks) > 0:
|
if len(self.active_snapshot_paks) > 0:
|
||||||
self.active_box.set_visible(True)
|
self.active_box.set_visible(True)
|
||||||
else:
|
else:
|
||||||
self.active_box.set_visible(False)
|
self.active_box.set_visible(False)
|
||||||
|
|
||||||
def generate_leftover_list(self):
|
def generate_leftover_list(self):
|
||||||
for folder in self.leftover_snapshots:
|
for folder in self.leftover_snapshots:
|
||||||
row = LeftoverSnapshotRow(folder, self.long_press_handler)
|
row = LeftoverSnapshotRow(folder, self.long_press_handler)
|
||||||
row.check_button.connect("toggled", lambda *_, _row=row: self.row_select_handler(_row))
|
row.check_button.connect("toggled", lambda *_, _row=row: self.row_select_handler(_row))
|
||||||
self.leftover_listbox.append(row)
|
self.leftover_listbox.append(row)
|
||||||
|
|
||||||
if len(self.leftover_snapshots) > 0:
|
if len(self.leftover_snapshots) > 0:
|
||||||
self.leftover_box.set_visible(True)
|
self.leftover_box.set_visible(True)
|
||||||
if len(self.active_snapshot_paks) == 0:
|
if len(self.active_snapshot_paks) == 0:
|
||||||
self.stack.set_visible_child(self.scrolled_window)
|
self.stack.set_visible_child(self.scrolled_window)
|
||||||
else:
|
else:
|
||||||
self.leftover_box.set_visible(False)
|
self.leftover_box.set_visible(False)
|
||||||
|
|
||||||
def active_select_handler(self, listbox, row, should_show_content=True, refresh=False):
|
def active_select_handler(self, listbox, row, should_show_content=True, refresh=False):
|
||||||
if row.check_button.get_visible():
|
if row.check_button.get_visible():
|
||||||
row.check_button.set_active(not row.check_button.get_active())
|
row.check_button.set_active(not row.check_button.get_active())
|
||||||
return
|
return
|
||||||
|
|
||||||
self.last_activated_row = row
|
self.last_activated_row = row
|
||||||
self.leftover_listbox.select_row(None)
|
self.leftover_listbox.select_row(None)
|
||||||
self.list_page.set_snapshots(row.package, refresh)
|
self.list_page.set_snapshots(row.package, refresh)
|
||||||
self.split_view.set_show_content(should_show_content)
|
self.split_view.set_show_content(should_show_content)
|
||||||
|
|
||||||
def leftover_select_handler(self, listbox, row, should_show_content=True, refresh=False):
|
def leftover_select_handler(self, listbox, row, should_show_content=True, refresh=False):
|
||||||
if row.check_button.get_visible():
|
if row.check_button.get_visible():
|
||||||
row.check_button.set_active(not row.check_button.get_active())
|
row.check_button.set_active(not row.check_button.get_active())
|
||||||
return
|
return
|
||||||
|
|
||||||
self.last_activated_row = row
|
self.last_activated_row = row
|
||||||
self.active_listbox.select_row(None)
|
self.active_listbox.select_row(None)
|
||||||
self.list_page.set_snapshots(row.folder, refresh)
|
self.list_page.set_snapshots(row.folder, refresh)
|
||||||
self.split_view.set_show_content(should_show_content)
|
self.split_view.set_show_content(should_show_content)
|
||||||
|
|
||||||
def select_first_row(self):
|
def select_first_row(self):
|
||||||
if row := self.active_listbox.get_row_at_index(0):
|
if row := self.active_listbox.get_row_at_index(0):
|
||||||
self.active_listbox.select_row(row)
|
self.active_listbox.select_row(row)
|
||||||
@@ -183,7 +183,7 @@ class SnapshotPage(Adw.BreakpointBin):
|
|||||||
elif row := self.leftover_listbox.get_row_at_index(0):
|
elif row := self.leftover_listbox.get_row_at_index(0):
|
||||||
self.leftover_listbox.select_row(row)
|
self.leftover_listbox.select_row(row)
|
||||||
self.leftover_select_handler(None, row, False, True)
|
self.leftover_select_handler(None, row, False, True)
|
||||||
|
|
||||||
def show_snapshot(self, package):
|
def show_snapshot(self, package):
|
||||||
i = 0
|
i = 0
|
||||||
while row := self.active_listbox.get_row_at_index(i):
|
while row := self.active_listbox.get_row_at_index(i):
|
||||||
@@ -198,7 +198,7 @@ class SnapshotPage(Adw.BreakpointBin):
|
|||||||
toast = Adw.Toast(title=_("No snapshots for {}").format(package.info['name']), button_label=_("New"))
|
toast = Adw.Toast(title=_("No snapshots for {}").format(package.info['name']), button_label=_("New"))
|
||||||
toast.connect("button-clicked", lambda *_: dialog.present(HostInfo.main_window))
|
toast.connect("button-clicked", lambda *_: dialog.present(HostInfo.main_window))
|
||||||
self.toast_overlay.add_toast(toast)
|
self.toast_overlay.add_toast(toast)
|
||||||
|
|
||||||
def start_loading(self):
|
def start_loading(self):
|
||||||
self.last_activated_row = None
|
self.last_activated_row = None
|
||||||
self.search_button.set_active(False)
|
self.search_button.set_active(False)
|
||||||
@@ -211,7 +211,7 @@ class SnapshotPage(Adw.BreakpointBin):
|
|||||||
self.leftover_listbox.remove_all()
|
self.leftover_listbox.remove_all()
|
||||||
self.selected_active_rows.clear()
|
self.selected_active_rows.clear()
|
||||||
self.selected_leftover_rows.clear()
|
self.selected_leftover_rows.clear()
|
||||||
|
|
||||||
def end_loading(self):
|
def end_loading(self):
|
||||||
def callback(*args):
|
def callback(*args):
|
||||||
self.generate_active_list()
|
self.generate_active_list()
|
||||||
@@ -222,16 +222,16 @@ class SnapshotPage(Adw.BreakpointBin):
|
|||||||
self.select_first_row()
|
self.select_first_row()
|
||||||
GLib.idle_add(lambda *_: self.stack.set_visible_child(self.scrolled_window))
|
GLib.idle_add(lambda *_: self.stack.set_visible_child(self.scrolled_window))
|
||||||
GLib.idle_add(lambda *_: self.status_stack.set_visible_child(self.split_view))
|
GLib.idle_add(lambda *_: self.status_stack.set_visible_child(self.split_view))
|
||||||
|
|
||||||
data_exists = False
|
data_exists = False
|
||||||
for package in HostInfo.flatpaks:
|
for package in HostInfo.flatpaks:
|
||||||
if package.info['id'] == "io.github.flattool.Warehouse":
|
if package.info['id'] == "io.github.flattool.Warehouse":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if os.path.exists(package.data_path):
|
if os.path.exists(package.data_path):
|
||||||
data_exists = True
|
data_exists = True
|
||||||
break
|
break
|
||||||
|
|
||||||
if data_exists:
|
if data_exists:
|
||||||
self.new_button.set_sensitive(True)
|
self.new_button.set_sensitive(True)
|
||||||
self.new_button.set_tooltip_text(None)
|
self.new_button.set_tooltip_text(None)
|
||||||
@@ -242,34 +242,34 @@ class SnapshotPage(Adw.BreakpointBin):
|
|||||||
self.new_button.set_tooltip_text(_("No Data Found to Snapshot"))
|
self.new_button.set_tooltip_text(_("No Data Found to Snapshot"))
|
||||||
self.status_new_button.set_sensitive(False)
|
self.status_new_button.set_sensitive(False)
|
||||||
self.status_new_button.set_tooltip_text(_("No Data Found to Snapshot"))
|
self.status_new_button.set_tooltip_text(_("No Data Found to Snapshot"))
|
||||||
|
|
||||||
Gio.Task.new(None, None, callback).run_in_thread(self.sort_snapshots)
|
Gio.Task.new(None, None, callback).run_in_thread(self.sort_snapshots)
|
||||||
|
|
||||||
def open_snapshots_folder(self, button):
|
def open_snapshots_folder(self, button):
|
||||||
try:
|
try:
|
||||||
Gio.AppInfo.launch_default_for_uri(f"file://{HostInfo.snapshots_path}", None)
|
Gio.AppInfo.launch_default_for_uri(f"file://{HostInfo.snapshots_path}", None)
|
||||||
self.toast_overlay.add_toast(Adw.Toast.new(_("Opened snapshots folder")))
|
self.toast_overlay.add_toast(Adw.Toast.new(_("Opened snapshots folder")))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.toast_overlay.add_toast(ErrorToast(_("Could not open folder"), str(e)).toast)
|
self.toast_overlay.add_toast(ErrorToast(_("Could not open folder"), str(e)).toast)
|
||||||
|
|
||||||
def on_cancel(self):
|
def on_cancel(self):
|
||||||
for worker in self.workers:
|
for worker in self.workers:
|
||||||
worker.do_cancel("manual_cancel")
|
worker.do_cancel("manual_cancel")
|
||||||
|
|
||||||
if self.new_snapshot_dialog is None:
|
if self.new_snapshot_dialog is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
for worker in self.new_snapshot_dialog.workers:
|
for worker in self.new_snapshot_dialog.workers:
|
||||||
worker.do_cancel("manual_cancel")
|
worker.do_cancel("manual_cancel")
|
||||||
|
|
||||||
def on_new(self, *args):
|
def on_new(self, *args):
|
||||||
self.new_snapshot_dialog = NewSnapshotDialog(self, self.snapshotting_status, self.refresh)
|
self.new_snapshot_dialog = NewSnapshotDialog(self, self.snapshotting_status, self.refresh)
|
||||||
self.new_snapshot_dialog.present(HostInfo.main_window)
|
self.new_snapshot_dialog.present(HostInfo.main_window)
|
||||||
|
|
||||||
def refresh(self):
|
def refresh(self):
|
||||||
self.start_loading()
|
self.start_loading()
|
||||||
self.end_loading()
|
self.end_loading()
|
||||||
|
|
||||||
def on_search(self, search_entry):
|
def on_search(self, search_entry):
|
||||||
text = search_entry.get_text().lower()
|
text = search_entry.get_text().lower()
|
||||||
i = 0
|
i = 0
|
||||||
@@ -281,7 +281,7 @@ class SnapshotPage(Adw.BreakpointBin):
|
|||||||
row.set_visible(True)
|
row.set_visible(True)
|
||||||
total_active_visible += 1
|
total_active_visible += 1
|
||||||
self.active_box.set_visible(total_active_visible > 0)
|
self.active_box.set_visible(total_active_visible > 0)
|
||||||
|
|
||||||
i = 0
|
i = 0
|
||||||
total_leftover_visible = 0
|
total_leftover_visible = 0
|
||||||
while row := self.leftover_listbox.get_row_at_index(i):
|
while row := self.leftover_listbox.get_row_at_index(i):
|
||||||
@@ -291,18 +291,18 @@ class SnapshotPage(Adw.BreakpointBin):
|
|||||||
row.set_visible(True)
|
row.set_visible(True)
|
||||||
total_leftover_visible += 1
|
total_leftover_visible += 1
|
||||||
self.leftover_box.set_visible(total_leftover_visible > 0)
|
self.leftover_box.set_visible(total_leftover_visible > 0)
|
||||||
|
|
||||||
if total_active_visible > 0 or total_leftover_visible > 0:
|
if total_active_visible > 0 or total_leftover_visible > 0:
|
||||||
self.stack.set_visible_child(self.scrolled_window)
|
self.stack.set_visible_child(self.scrolled_window)
|
||||||
else:
|
else:
|
||||||
self.stack.set_visible_child(self.no_results)
|
self.stack.set_visible_child(self.no_results)
|
||||||
|
|
||||||
def sort_func(self, row1, row2):
|
def sort_func(self, row1, row2):
|
||||||
if type(row1) is AppRow:
|
if type(row1) is AppRow:
|
||||||
return row1.package.info['name'].lower() > row2.package.info['name'].lower()
|
return row1.package.info['name'].lower() > row2.package.info['name'].lower()
|
||||||
else:
|
else:
|
||||||
return row1.name.lower() > row2.name.lower()
|
return row1.name.lower() > row2.name.lower()
|
||||||
|
|
||||||
def set_selection_mode(self, *args):
|
def set_selection_mode(self, *args):
|
||||||
enable = self.select_button.get_active()
|
enable = self.select_button.get_active()
|
||||||
self.active_listbox.set_selection_mode(Gtk.SelectionMode.NONE if enable else Gtk.SelectionMode.SINGLE)
|
self.active_listbox.set_selection_mode(Gtk.SelectionMode.NONE if enable else Gtk.SelectionMode.SINGLE)
|
||||||
@@ -311,32 +311,32 @@ class SnapshotPage(Adw.BreakpointBin):
|
|||||||
self.active_listbox.select_row(self.last_activated_row)
|
self.active_listbox.select_row(self.last_activated_row)
|
||||||
elif self.last_activated_row in self.leftover_listbox:
|
elif self.last_activated_row in self.leftover_listbox:
|
||||||
self.leftover_listbox.select_row(self.last_activated_row)
|
self.leftover_listbox.select_row(self.last_activated_row)
|
||||||
|
|
||||||
i = 0
|
i = 0
|
||||||
while row := self.active_listbox.get_row_at_index(i):
|
while row := self.active_listbox.get_row_at_index(i):
|
||||||
i += 1
|
i += 1
|
||||||
row.check_button.set_visible(enable)
|
row.check_button.set_visible(enable)
|
||||||
if not enable:
|
if not enable:
|
||||||
row.check_button.set_active(False)
|
row.check_button.set_active(False)
|
||||||
|
|
||||||
i = 0
|
i = 0
|
||||||
while row := self.leftover_listbox.get_row_at_index(i):
|
while row := self.leftover_listbox.get_row_at_index(i):
|
||||||
i += 1
|
i += 1
|
||||||
row.check_button.set_visible(enable)
|
row.check_button.set_visible(enable)
|
||||||
if not enable:
|
if not enable:
|
||||||
row.check_button.set_active(False)
|
row.check_button.set_active(False)
|
||||||
|
|
||||||
def select_all_handler(self, *args):
|
def select_all_handler(self, *args):
|
||||||
i = 0
|
i = 0
|
||||||
while row := self.active_listbox.get_row_at_index(i):
|
while row := self.active_listbox.get_row_at_index(i):
|
||||||
i += 1
|
i += 1
|
||||||
row.check_button.set_active(True)
|
row.check_button.set_active(True)
|
||||||
|
|
||||||
i = 0
|
i = 0
|
||||||
while row := self.leftover_listbox.get_row_at_index(i):
|
while row := self.leftover_listbox.get_row_at_index(i):
|
||||||
i += 1
|
i += 1
|
||||||
row.check_button.set_active(True)
|
row.check_button.set_active(True)
|
||||||
|
|
||||||
def row_select_handler(self, row):
|
def row_select_handler(self, row):
|
||||||
if type(row) is AppRow:
|
if type(row) is AppRow:
|
||||||
if row.check_button.get_active():
|
if row.check_button.get_active():
|
||||||
@@ -348,7 +348,7 @@ class SnapshotPage(Adw.BreakpointBin):
|
|||||||
self.selected_leftover_rows.append(row)
|
self.selected_leftover_rows.append(row)
|
||||||
elif row in self.selected_leftover_rows:
|
elif row in self.selected_leftover_rows:
|
||||||
self.selected_leftover_rows.remove(row)
|
self.selected_leftover_rows.remove(row)
|
||||||
|
|
||||||
total_active = len(self.selected_active_rows)
|
total_active = len(self.selected_active_rows)
|
||||||
total_leftover = len(self.selected_leftover_rows)
|
total_leftover = len(self.selected_leftover_rows)
|
||||||
total = total_active + total_leftover
|
total = total_active + total_leftover
|
||||||
@@ -366,7 +366,7 @@ class SnapshotPage(Adw.BreakpointBin):
|
|||||||
row.set_visible(total_active > 0 and total_leftover == 0)
|
row.set_visible(total_active > 0 and total_leftover == 0)
|
||||||
case self.install_from_snapshots:
|
case self.install_from_snapshots:
|
||||||
row.set_visible(total_active == 0 and total_leftover > 0)
|
row.set_visible(total_active == 0 and total_leftover > 0)
|
||||||
|
|
||||||
def select_copy_handler(self, *args):
|
def select_copy_handler(self, *args):
|
||||||
to_copy = ""
|
to_copy = ""
|
||||||
i = 0
|
i = 0
|
||||||
@@ -374,48 +374,48 @@ class SnapshotPage(Adw.BreakpointBin):
|
|||||||
i += 1
|
i += 1
|
||||||
if row.check_button.get_active():
|
if row.check_button.get_active():
|
||||||
to_copy += f"{HostInfo.snapshots_path}{row.package.info['id']}\n"
|
to_copy += f"{HostInfo.snapshots_path}{row.package.info['id']}\n"
|
||||||
|
|
||||||
i = 0
|
i = 0
|
||||||
while row := self.leftover_listbox.get_row_at_index(i):
|
while row := self.leftover_listbox.get_row_at_index(i):
|
||||||
i += 1
|
i += 1
|
||||||
if row.check_button.get_active():
|
if row.check_button.get_active():
|
||||||
to_copy += f"{HostInfo.snapshots_path}{row.folder}\n"
|
to_copy += f"{HostInfo.snapshots_path}{row.folder}\n"
|
||||||
|
|
||||||
to_copy = to_copy[0:-1]
|
to_copy = to_copy[0:-1]
|
||||||
HostInfo.clipboard.set(to_copy)
|
HostInfo.clipboard.set(to_copy)
|
||||||
self.toast_overlay.add_toast(Adw.Toast(title=_("Copied Snapshot Paths")))
|
self.toast_overlay.add_toast(Adw.Toast(title=_("Copied Snapshot Paths")))
|
||||||
|
|
||||||
def select_new_handler(self):
|
def select_new_handler(self):
|
||||||
packages = []
|
packages = []
|
||||||
for row in self.selected_active_rows:
|
for row in self.selected_active_rows:
|
||||||
if os.path.exists(row.package.data_path):
|
if os.path.exists(row.package.data_path):
|
||||||
packages.append(row.package)
|
packages.append(row.package)
|
||||||
|
|
||||||
if len(packages) == 0:
|
if len(packages) == 0:
|
||||||
self.toast_overlay.add_toast(Adw.Toast(title=_("No apps in your selection can be snapshotted")))
|
self.toast_overlay.add_toast(Adw.Toast(title=_("No apps in your selection can be snapshotted")))
|
||||||
return
|
return
|
||||||
|
|
||||||
self.new_snapshot_dialog = NewSnapshotDialog(self, self.snapshotting_status, self.refresh, packages)
|
self.new_snapshot_dialog = NewSnapshotDialog(self, self.snapshotting_status, self.refresh, packages)
|
||||||
self.new_snapshot_dialog.present(HostInfo.main_window)
|
self.new_snapshot_dialog.present(HostInfo.main_window)
|
||||||
|
|
||||||
def get_snapshots_from_entry(self, app_ids):
|
def get_snapshots_from_entry(self, app_ids):
|
||||||
id_to_tar = {}
|
id_to_tar = {}
|
||||||
for app_id in app_ids:
|
for app_id in app_ids:
|
||||||
path = f"{HostInfo.snapshots_path}{app_id}"
|
path = f"{HostInfo.snapshots_path}{app_id}"
|
||||||
if not os.path.exists(path):
|
if not os.path.exists(path):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
tarlist = []
|
tarlist = []
|
||||||
for file in os.listdir(path):
|
for file in os.listdir(path):
|
||||||
if file.endswith(".tar.zst"):
|
if file.endswith(".tar.zst"):
|
||||||
tarlist.append(file)
|
tarlist.append(file)
|
||||||
|
|
||||||
id_to_tar[app_id] = tarlist
|
id_to_tar[app_id] = tarlist
|
||||||
if len(tarlist) < 1:
|
if len(tarlist) < 1:
|
||||||
id_to_tar.pop(app_id, None)
|
id_to_tar.pop(app_id, None)
|
||||||
|
|
||||||
return id_to_tar
|
return id_to_tar
|
||||||
|
|
||||||
def get_total_fraction(self):
|
def get_total_fraction(self):
|
||||||
total = 0
|
total = 0
|
||||||
stopped_workers_amount = 0
|
stopped_workers_amount = 0
|
||||||
@@ -423,29 +423,29 @@ class SnapshotPage(Adw.BreakpointBin):
|
|||||||
total += worker.fraction
|
total += worker.fraction
|
||||||
if worker.stop:
|
if worker.stop:
|
||||||
stopped_workers_amount += 1
|
stopped_workers_amount += 1
|
||||||
|
|
||||||
if stopped_workers_amount == len(self.workers):
|
if stopped_workers_amount == len(self.workers):
|
||||||
self.snapshotting_status.progress_bar.set_fraction(1)
|
self.snapshotting_status.progress_bar.set_fraction(1)
|
||||||
self.snapshotting_status.progress_label.set_label(f"{len(self.workers)} / {len(self.workers)}")
|
self.snapshotting_status.progress_label.set_label(f"{len(self.workers)} / {len(self.workers)}")
|
||||||
HostInfo.main_window.refresh_handler()
|
HostInfo.main_window.refresh_handler()
|
||||||
self.workers.clear()
|
self.workers.clear()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
self.snapshotting_status.progress_label.set_label(f"{stopped_workers_amount + 1} / {len(self.workers)}")
|
self.snapshotting_status.progress_label.set_label(f"{stopped_workers_amount + 1} / {len(self.workers)}")
|
||||||
self.snapshotting_status.progress_bar.set_fraction(total / len(self.workers))
|
self.snapshotting_status.progress_bar.set_fraction(total / len(self.workers))
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def on_apply_response(self, dialog, response):
|
def on_apply_response(self, dialog, response):
|
||||||
if response != "continue":
|
if response != "continue":
|
||||||
return
|
return
|
||||||
|
|
||||||
app_ids = []
|
app_ids = []
|
||||||
for row in self.selected_active_rows:
|
for row in self.selected_active_rows:
|
||||||
app_ids.append(row.package.info['id'])
|
app_ids.append(row.package.info['id'])
|
||||||
|
|
||||||
for row in self.selected_leftover_rows:
|
for row in self.selected_leftover_rows:
|
||||||
app_ids.append(row.folder)
|
app_ids.append(row.folder)
|
||||||
|
|
||||||
id_to_tar = self.get_snapshots_from_entry(app_ids)
|
id_to_tar = self.get_snapshots_from_entry(app_ids)
|
||||||
for app_id in id_to_tar:
|
for app_id in id_to_tar:
|
||||||
biggest = 0
|
biggest = 0
|
||||||
@@ -453,9 +453,9 @@ class SnapshotPage(Adw.BreakpointBin):
|
|||||||
epoch = int(tar.split('_')[0])
|
epoch = int(tar.split('_')[0])
|
||||||
if epoch > biggest:
|
if epoch > biggest:
|
||||||
biggest = epoch
|
biggest = epoch
|
||||||
|
|
||||||
id_to_tar[app_id] = tar
|
id_to_tar[app_id] = tar
|
||||||
|
|
||||||
for app_id, tar in id_to_tar.items():
|
for app_id, tar in id_to_tar.items():
|
||||||
worker = TarWorker(
|
worker = TarWorker(
|
||||||
existing_path=f"{HostInfo.snapshots_path}{app_id}/{tar}",
|
existing_path=f"{HostInfo.snapshots_path}{app_id}/{tar}",
|
||||||
@@ -464,7 +464,7 @@ class SnapshotPage(Adw.BreakpointBin):
|
|||||||
)
|
)
|
||||||
self.workers.append(worker)
|
self.workers.append(worker)
|
||||||
worker.extract()
|
worker.extract()
|
||||||
|
|
||||||
if len(self.workers) > 0:
|
if len(self.workers) > 0:
|
||||||
self.snapshotting_status.title_label.set_label(_("Applying Snapshots"))
|
self.snapshotting_status.title_label.set_label(_("Applying Snapshots"))
|
||||||
self.snapshotting_status.progress_bar.set_fraction(0.0)
|
self.snapshotting_status.progress_bar.set_fraction(0.0)
|
||||||
@@ -473,40 +473,40 @@ class SnapshotPage(Adw.BreakpointBin):
|
|||||||
GLib.timeout_add(200, self.get_total_fraction)
|
GLib.timeout_add(200, self.get_total_fraction)
|
||||||
else:
|
else:
|
||||||
self.toast_overlay.add_toast(ErrorToast(_("No snapshots to extract"), _("No snapshots were found to extract")))
|
self.toast_overlay.add_toast(ErrorToast(_("No snapshots to extract"), _("No snapshots were found to extract")))
|
||||||
|
|
||||||
def select_apply_handler(self):
|
def select_apply_handler(self):
|
||||||
dialog = Adw.AlertDialog(heading=_("Apply These Snapshots?"), body=_("This will trash the current apps' user data, and apply their newest snapshot"))
|
dialog = Adw.AlertDialog(heading=_("Apply These Snapshots?"), body=_("This will trash the current apps' user data, and apply their newest snapshot"))
|
||||||
dialog.add_response("cancel", _("Cancel"))
|
dialog.add_response("cancel", _("Cancel"))
|
||||||
dialog.add_response("continue", _("Continue"))
|
dialog.add_response("continue", _("Continue"))
|
||||||
dialog.connect("response", self.on_apply_response)
|
dialog.connect("response", self.on_apply_response)
|
||||||
dialog.present(HostInfo.main_window)
|
dialog.present(HostInfo.main_window)
|
||||||
|
|
||||||
def install_handler(self):
|
def install_handler(self):
|
||||||
package_names = []
|
package_names = []
|
||||||
for row in self.selected_leftover_rows:
|
for row in self.selected_leftover_rows:
|
||||||
package_names.append(row.folder)
|
package_names.append(row.folder)
|
||||||
|
|
||||||
AttemptInstallDialog(package_names, lambda is_valid: self.select_button.set_active(not is_valid))
|
AttemptInstallDialog(package_names, lambda is_valid: self.select_button.set_active(not is_valid))
|
||||||
|
|
||||||
def selection_trash_handler(self):
|
def selection_trash_handler(self):
|
||||||
if (
|
if (
|
||||||
len(self.selected_active_rows) + len(self.selected_leftover_rows) < 1
|
len(self.selected_active_rows) + len(self.selected_leftover_rows) < 1
|
||||||
or self.is_trash_dialog_open
|
or self.is_trash_dialog_open
|
||||||
):
|
):
|
||||||
return
|
return
|
||||||
|
|
||||||
def on_response(dialog, response):
|
def on_response(dialog, response):
|
||||||
self.is_trash_dialog_open = False
|
self.is_trash_dialog_open = False
|
||||||
to_trash = []
|
to_trash = []
|
||||||
if response != "continue":
|
if response != "continue":
|
||||||
return
|
return
|
||||||
|
|
||||||
for row in self.selected_active_rows:
|
for row in self.selected_active_rows:
|
||||||
to_trash.append(f"{HostInfo.snapshots_path}{row.package.info['id']}")
|
to_trash.append(f"{HostInfo.snapshots_path}{row.package.info['id']}")
|
||||||
|
|
||||||
for row in self.selected_leftover_rows:
|
for row in self.selected_leftover_rows:
|
||||||
to_trash.append(f"{HostInfo.snapshots_path}{row.folder}")
|
to_trash.append(f"{HostInfo.snapshots_path}{row.folder}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
subprocess.run(['gio', 'trash'] + to_trash, check=True, text=True, capture_output=True)
|
subprocess.run(['gio', 'trash'] + to_trash, check=True, text=True, capture_output=True)
|
||||||
self.start_loading()
|
self.start_loading()
|
||||||
@@ -514,7 +514,7 @@ class SnapshotPage(Adw.BreakpointBin):
|
|||||||
self.toast_overlay.add_toast(Adw.Toast(title=_("Trashed snapshots")))
|
self.toast_overlay.add_toast(Adw.Toast(title=_("Trashed snapshots")))
|
||||||
except subprocess.CalledProcessError as cpe:
|
except subprocess.CalledProcessError as cpe:
|
||||||
self.toast_overlay.add_toast(ErrorToast(_("Could not trash snapshots"), cpe.stderr).toast)
|
self.toast_overlay.add_toast(ErrorToast(_("Could not trash snapshots"), cpe.stderr).toast)
|
||||||
|
|
||||||
self.is_trash_dialog_open = True
|
self.is_trash_dialog_open = True
|
||||||
dialog = Adw.AlertDialog(heading=_("Trash Snapshots?"), body=_("These apps' snapshots will be sent to the trash"))
|
dialog = Adw.AlertDialog(heading=_("Trash Snapshots?"), body=_("These apps' snapshots will be sent to the trash"))
|
||||||
dialog.add_response("cancel", _("Cancel"))
|
dialog.add_response("cancel", _("Cancel"))
|
||||||
@@ -522,7 +522,7 @@ class SnapshotPage(Adw.BreakpointBin):
|
|||||||
dialog.set_response_appearance("continue", Adw.ResponseAppearance.DESTRUCTIVE)
|
dialog.set_response_appearance("continue", Adw.ResponseAppearance.DESTRUCTIVE)
|
||||||
dialog.connect("response", on_response)
|
dialog.connect("response", on_response)
|
||||||
dialog.present(HostInfo.main_window)
|
dialog.present(HostInfo.main_window)
|
||||||
|
|
||||||
def more_menu_handler(self, listbox, row):
|
def more_menu_handler(self, listbox, row):
|
||||||
self.more_popover.popdown()
|
self.more_popover.popdown()
|
||||||
row = row.get_child()
|
row = row.get_child()
|
||||||
@@ -535,10 +535,10 @@ class SnapshotPage(Adw.BreakpointBin):
|
|||||||
self.install_handler()
|
self.install_handler()
|
||||||
case self.trash_snapshots:
|
case self.trash_snapshots:
|
||||||
self.selection_trash_handler()
|
self.selection_trash_handler()
|
||||||
|
|
||||||
def __init__(self, main_window, **kwargs):
|
def __init__(self, main_window, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
# Extra Object Creation
|
# Extra Object Creation
|
||||||
self.__class__.instance = self
|
self.__class__.instance = self
|
||||||
self.main_window = main_window
|
self.main_window = main_window
|
||||||
@@ -552,7 +552,7 @@ class SnapshotPage(Adw.BreakpointBin):
|
|||||||
self.new_snapshot_dialog = None
|
self.new_snapshot_dialog = None
|
||||||
self.on_backspace_handler = self.selection_trash_handler
|
self.on_backspace_handler = self.selection_trash_handler
|
||||||
self.on_escape_handler = lambda *_: self.select_button.set_active(False)
|
self.on_escape_handler = lambda *_: self.select_button.set_active(False)
|
||||||
|
|
||||||
# Apply
|
# Apply
|
||||||
self.search_bar.set_key_capture_widget(HostInfo.main_window)
|
self.search_bar.set_key_capture_widget(HostInfo.main_window)
|
||||||
self.loading_view.set_content(LoadingStatus(_("Loading Snapshots"), _("This should only take a moment")))
|
self.loading_view.set_content(LoadingStatus(_("Loading Snapshots"), _("This should only take a moment")))
|
||||||
@@ -560,7 +560,7 @@ class SnapshotPage(Adw.BreakpointBin):
|
|||||||
self.split_view.set_content(self.list_page)
|
self.split_view.set_content(self.list_page)
|
||||||
self.active_listbox.set_sort_func(self.sort_func)
|
self.active_listbox.set_sort_func(self.sort_func)
|
||||||
self.leftover_listbox.set_sort_func(self.sort_func)
|
self.leftover_listbox.set_sort_func(self.sort_func)
|
||||||
|
|
||||||
# Connections
|
# Connections
|
||||||
self.active_listbox.connect("row-activated", self.active_select_handler)
|
self.active_listbox.connect("row-activated", self.active_select_handler)
|
||||||
self.leftover_listbox.connect("row-activated", self.leftover_select_handler)
|
self.leftover_listbox.connect("row-activated", self.leftover_select_handler)
|
||||||
|
|||||||
@@ -10,38 +10,38 @@ import os
|
|||||||
class SnapshotsListPage(Adw.NavigationPage):
|
class SnapshotsListPage(Adw.NavigationPage):
|
||||||
__gtype_name__ = "SnapshotsListPage"
|
__gtype_name__ = "SnapshotsListPage"
|
||||||
gtc = Gtk.Template.Child
|
gtc = Gtk.Template.Child
|
||||||
|
|
||||||
toolbar_view = gtc()
|
toolbar_view = gtc()
|
||||||
listbox = gtc()
|
listbox = gtc()
|
||||||
toast_overlay = gtc()
|
toast_overlay = gtc()
|
||||||
open_button = gtc()
|
open_button = gtc()
|
||||||
new_button = gtc()
|
new_button = gtc()
|
||||||
|
|
||||||
def thread(self, *args):
|
def thread(self, *args):
|
||||||
is_leftover = type(self.package_or_folder) is str
|
is_leftover = type(self.package_or_folder) is str
|
||||||
for snapshot in os.listdir(folder := f"{self.snapshots_path}{self.current_folder}/"):
|
for snapshot in os.listdir(folder := f"{self.snapshots_path}{self.current_folder}/"):
|
||||||
if snapshot.endswith(".json"):
|
if snapshot.endswith(".json"):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
row = SnapshotBox(self, snapshot, folder, self.toast_overlay)
|
row = SnapshotBox(self, snapshot, folder, self.toast_overlay)
|
||||||
row.apply_button.set_sensitive(not is_leftover)
|
row.apply_button.set_sensitive(not is_leftover)
|
||||||
self.snapshots_rows.append(row)
|
self.snapshots_rows.append(row)
|
||||||
if is_leftover:
|
if is_leftover:
|
||||||
row.apply_button.set_tooltip_text(_("App not Installed"))
|
row.apply_button.set_tooltip_text(_("App not Installed"))
|
||||||
|
|
||||||
def callback(self, *args):
|
def callback(self, *args):
|
||||||
if len(self.snapshots_rows) == 0:
|
if len(self.snapshots_rows) == 0:
|
||||||
self.parent_page.refresh()
|
self.parent_page.refresh()
|
||||||
return
|
return
|
||||||
|
|
||||||
for i, row in enumerate(self.snapshots_rows):
|
for i, row in enumerate(self.snapshots_rows):
|
||||||
self.listbox.append(row)
|
self.listbox.append(row)
|
||||||
self.listbox.get_row_at_index(i).set_activatable(False)
|
self.listbox.get_row_at_index(i).set_activatable(False)
|
||||||
|
|
||||||
def set_snapshots(self, package_or_folder, refresh=False):
|
def set_snapshots(self, package_or_folder, refresh=False):
|
||||||
if package_or_folder == self.package_or_folder and not refresh:
|
if package_or_folder == self.package_or_folder and not refresh:
|
||||||
return
|
return
|
||||||
|
|
||||||
folder = None
|
folder = None
|
||||||
self.package_or_folder = package_or_folder
|
self.package_or_folder = package_or_folder
|
||||||
if type(package_or_folder) is str:
|
if type(package_or_folder) is str:
|
||||||
@@ -58,53 +58,53 @@ class SnapshotsListPage(Adw.NavigationPage):
|
|||||||
else:
|
else:
|
||||||
self.new_button.set_sensitive(False)
|
self.new_button.set_sensitive(False)
|
||||||
self.new_button.set_tooltip_text(_("No Data Found to Snapshot"))
|
self.new_button.set_tooltip_text(_("No Data Found to Snapshot"))
|
||||||
|
|
||||||
self.current_folder = folder
|
self.current_folder = folder
|
||||||
self.snapshots_rows.clear()
|
self.snapshots_rows.clear()
|
||||||
self.listbox.remove_all()
|
self.listbox.remove_all()
|
||||||
|
|
||||||
Gio.Task.new(None, None, self.callback).run_in_thread(self.thread)
|
Gio.Task.new(None, None, self.callback).run_in_thread(self.thread)
|
||||||
|
|
||||||
def open_snapshots_folder(self, button):
|
def open_snapshots_folder(self, button):
|
||||||
path = f"{self.snapshots_path}{self.current_folder}/"
|
path = f"{self.snapshots_path}{self.current_folder}/"
|
||||||
try:
|
try:
|
||||||
if not os.path.exists(path):
|
if not os.path.exists(path):
|
||||||
raise Exception(f"error: File '{path}' does not exist")
|
raise Exception(f"error: File '{path}' does not exist")
|
||||||
|
|
||||||
Gio.AppInfo.launch_default_for_uri(f"file://{path}", None)
|
Gio.AppInfo.launch_default_for_uri(f"file://{path}", None)
|
||||||
self.toast_overlay.add_toast(Adw.Toast.new(_("Opened snapshots folder")))
|
self.toast_overlay.add_toast(Adw.Toast.new(_("Opened snapshots folder")))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.toast_overlay.add_toast(ErrorToast(_("Could not open folder"), str(e)).toast)
|
self.toast_overlay.add_toast(ErrorToast(_("Could not open folder"), str(e)).toast)
|
||||||
|
|
||||||
def on_done(self):
|
def on_done(self):
|
||||||
self.parent_page.status_stack.set_visible_child(self.parent_page.split_view)
|
self.parent_page.status_stack.set_visible_child(self.parent_page.split_view)
|
||||||
self.set_snapshots(self.package_or_folder, refresh=True)
|
self.set_snapshots(self.package_or_folder, refresh=True)
|
||||||
|
|
||||||
def on_new(self, button):
|
def on_new(self, button):
|
||||||
self.parent_page.new_snapshot_dialog = NewSnapshotDialog(self.parent_page, self.parent_page.snapshotting_status, self.on_done, [self.package_or_folder])
|
self.parent_page.new_snapshot_dialog = NewSnapshotDialog(self.parent_page, self.parent_page.snapshotting_status, self.on_done, [self.package_or_folder])
|
||||||
self.parent_page.new_snapshot_dialog.present(HostInfo.main_window)
|
self.parent_page.new_snapshot_dialog.present(HostInfo.main_window)
|
||||||
|
|
||||||
def sort_func(self, row1, row2):
|
def sort_func(self, row1, row2):
|
||||||
row1 = row1.get_child()
|
row1 = row1.get_child()
|
||||||
row2 = row2.get_child()
|
row2 = row2.get_child()
|
||||||
return row1.epoch > row2.epoch
|
return row1.epoch > row2.epoch
|
||||||
|
|
||||||
def on_trash(self):
|
def on_trash(self):
|
||||||
self.set_snapshots(self.package_or_folder, refresh=True)
|
self.set_snapshots(self.package_or_folder, refresh=True)
|
||||||
|
|
||||||
def __init__(self, parent_page, **kwargs):
|
def __init__(self, parent_page, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
# Extra Object Creation
|
# Extra Object Creation
|
||||||
self.parent_page = parent_page
|
self.parent_page = parent_page
|
||||||
self.snapshots_path = HostInfo.snapshots_path
|
self.snapshots_path = HostInfo.snapshots_path
|
||||||
self.current_folder = None
|
self.current_folder = None
|
||||||
self.package_or_folder = None
|
self.package_or_folder = None
|
||||||
self.snapshots_rows = []
|
self.snapshots_rows = []
|
||||||
|
|
||||||
# Connections
|
# Connections
|
||||||
self.open_button.connect("clicked", self.open_snapshots_folder)
|
self.open_button.connect("clicked", self.open_snapshots_folder)
|
||||||
self.new_button.connect("clicked", self.on_new)
|
self.new_button.connect("clicked", self.on_new)
|
||||||
|
|
||||||
# Apply
|
# Apply
|
||||||
self.listbox.set_sort_func(self.sort_func)
|
self.listbox.set_sort_func(self.sort_func)
|
||||||
|
|||||||
@@ -8,102 +8,102 @@ class TarWorker:
|
|||||||
try:
|
try:
|
||||||
if not os.path.exists(self.new_path):
|
if not os.path.exists(self.new_path):
|
||||||
os.makedirs(self.new_path)
|
os.makedirs(self.new_path)
|
||||||
|
|
||||||
self.total = int(subprocess.run(['du', '-s', self.existing_path], check=True, text=True, capture_output=True).stdout.split('\t')[0])
|
self.total = int(subprocess.run(['du', '-s', self.existing_path], check=True, text=True, capture_output=True).stdout.split('\t')[0])
|
||||||
self.total /= 2.2 # estimate for space savings
|
self.total /= 2.2 # estimate for space savings
|
||||||
self.process = subprocess.Popen(['tar', 'cafv', f'{self.new_path}/{self.file_name}.tar.zst', '-C', self.existing_path, '.'],
|
self.process = subprocess.Popen(['tar', 'cafv', f'{self.new_path}/{self.file_name}.tar.zst', '-C', self.existing_path, '.'],
|
||||||
stdout=subprocess.PIPE,
|
stdout=subprocess.PIPE,
|
||||||
stderr=subprocess.PIPE
|
stderr=subprocess.PIPE
|
||||||
)
|
)
|
||||||
stdout, stderr = self.process.communicate()
|
stdout, stderr = self.process.communicate()
|
||||||
if self.process.returncode != 0:
|
if self.process.returncode != 0:
|
||||||
raise subprocess.CalledProcessError(self.process.returncode, self.process.args, output=stdout, stderr=stderr)
|
raise subprocess.CalledProcessError(self.process.returncode, self.process.args, output=stdout, stderr=stderr)
|
||||||
|
|
||||||
with open(f"{self.new_path}/{self.file_name}.json", 'w') as file:
|
with open(f"{self.new_path}/{self.file_name}.json", 'w') as file:
|
||||||
data = {
|
data = {
|
||||||
'snapshot_version': 1,
|
'snapshot_version': 1,
|
||||||
'name': self.name,
|
'name': self.name,
|
||||||
}
|
}
|
||||||
json.dump(data, file, indent=4)
|
json.dump(data, file, indent=4)
|
||||||
|
|
||||||
self.stop = True # tell the check timeout to stop, because we know the file is done being made
|
self.stop = True # tell the check timeout to stop, because we know the file is done being made
|
||||||
HostInfo.main_window.remove_refresh_lockout("managing snapshot")
|
HostInfo.main_window.remove_refresh_lockout("managing snapshot")
|
||||||
|
|
||||||
except subprocess.CalledProcessError as cpe:
|
except subprocess.CalledProcessError as cpe:
|
||||||
self.do_cancel(cpe.stderr.decode()) # stderr is in bytes, so decode it
|
self.do_cancel(cpe.stderr.decode()) # stderr is in bytes, so decode it
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.do_cancel(str(e))
|
self.do_cancel(str(e))
|
||||||
|
|
||||||
def extract_thread(self, *args):
|
def extract_thread(self, *args):
|
||||||
try:
|
try:
|
||||||
if os.path.exists(self.new_path):
|
if os.path.exists(self.new_path):
|
||||||
subprocess.run(['gio', 'trash', self.new_path], capture_output=True, check=True) # trash the current user data, because new data will go in its place
|
subprocess.run(['gio', 'trash', self.new_path], capture_output=True, check=True) # trash the current user data, because new data will go in its place
|
||||||
|
|
||||||
os.makedirs(self.new_path) # create the new user data path
|
os.makedirs(self.new_path) # create the new user data path
|
||||||
|
|
||||||
self.total = int(subprocess.run(['du', '-s', self.existing_path], check=True, text=True, capture_output=True).stdout.split('\t')[0])
|
self.total = int(subprocess.run(['du', '-s', self.existing_path], check=True, text=True, capture_output=True).stdout.split('\t')[0])
|
||||||
self.total *= 2.2 # estimate from space savings
|
self.total *= 2.2 # estimate from space savings
|
||||||
self.process = subprocess.Popen(['tar', '--zstd', '-xvf', self.existing_path, '-C', self.new_path],
|
self.process = subprocess.Popen(['tar', '--zstd', '-xvf', self.existing_path, '-C', self.new_path],
|
||||||
stdout=subprocess.PIPE,
|
stdout=subprocess.PIPE,
|
||||||
stderr=subprocess.PIPE
|
stderr=subprocess.PIPE
|
||||||
)
|
)
|
||||||
stdout, stderr = self.process.communicate()
|
stdout, stderr = self.process.communicate()
|
||||||
if self.process.returncode != 0:
|
if self.process.returncode != 0:
|
||||||
raise subprocess.CalledProcessError(self.process.returncode, self.process.args, output=stdout, stderr=stderr)
|
raise subprocess.CalledProcessError(self.process.returncode, self.process.args, output=stdout, stderr=stderr)
|
||||||
|
|
||||||
self.stop = True # tell the check timeout to stop, because we know the file is done being made
|
self.stop = True # tell the check timeout to stop, because we know the file is done being made
|
||||||
HostInfo.main_window.remove_refresh_lockout("managing snapshot")
|
HostInfo.main_window.remove_refresh_lockout("managing snapshot")
|
||||||
|
|
||||||
except subprocess.CalledProcessError as cpe:
|
except subprocess.CalledProcessError as cpe:
|
||||||
self.do_cancel(cpe.stderr.decode())
|
self.do_cancel(cpe.stderr.decode())
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.do_cancel(str(e))
|
self.do_cancel(str(e))
|
||||||
|
|
||||||
def do_cancel(self, error_str):
|
def do_cancel(self, error_str):
|
||||||
if self.has_cancelled or self.stop:
|
if self.has_cancelled or self.stop:
|
||||||
return
|
return
|
||||||
|
|
||||||
self.has_cancelled = True
|
self.has_cancelled = True
|
||||||
self.process.terminate()
|
self.process.terminate()
|
||||||
self.process.wait()
|
self.process.wait()
|
||||||
if len(self.files_to_trash_on_cancel) > 0:
|
if len(self.files_to_trash_on_cancel) > 0:
|
||||||
try:
|
try:
|
||||||
subprocess.run(['gio', 'trash'] + self.files_to_trash_on_cancel, capture_output=True, check=True)
|
subprocess.run(['gio', 'trash'] + self.files_to_trash_on_cancel, capture_output=True, check=True)
|
||||||
|
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
self.stop = True
|
self.stop = True
|
||||||
HostInfo.main_window.remove_refresh_lockout("managing snapshot")
|
HostInfo.main_window.remove_refresh_lockout("managing snapshot")
|
||||||
if self.toast_overlay and error_str != "manual_cancel":
|
if self.toast_overlay and error_str != "manual_cancel":
|
||||||
self.toast_overlay.add_toast(ErrorToast(_("Error in snapshot handling"), error_str).toast)
|
self.toast_overlay.add_toast(ErrorToast(_("Error in snapshot handling"), error_str).toast)
|
||||||
|
|
||||||
def check_size(self, check_path):
|
def check_size(self, check_path):
|
||||||
try:
|
try:
|
||||||
output = subprocess.run(['du', '-s', check_path], check=True, text=True, capture_output=True).stdout.split('\t')[0]
|
output = subprocess.run(['du', '-s', check_path], check=True, text=True, capture_output=True).stdout.split('\t')[0]
|
||||||
working_total = float(output)
|
working_total = float(output)
|
||||||
self.fraction = working_total / self.total
|
self.fraction = working_total / self.total
|
||||||
return not self.stop
|
return not self.stop
|
||||||
|
|
||||||
except subprocess.CalledProcessError as cpe:
|
except subprocess.CalledProcessError as cpe:
|
||||||
return not self.stop # continue the timeout or stop the timeout
|
return not self.stop # continue the timeout or stop the timeout
|
||||||
|
|
||||||
def compress(self):
|
def compress(self):
|
||||||
self.stop = False
|
self.stop = False
|
||||||
self.files_to_trash_on_cancel = [f'{self.new_path}/{self.file_name}.tar.zst', f'{self.new_path}/{self.file_name}.json']
|
self.files_to_trash_on_cancel = [f'{self.new_path}/{self.file_name}.tar.zst', f'{self.new_path}/{self.file_name}.json']
|
||||||
HostInfo.main_window.add_refresh_lockout("managing snapshot")
|
HostInfo.main_window.add_refresh_lockout("managing snapshot")
|
||||||
Gio.Task.new(None, None, None).run_in_thread(self.compress_thread)
|
Gio.Task.new(None, None, None).run_in_thread(self.compress_thread)
|
||||||
GLib.timeout_add(200, self.check_size, f"{self.new_path}/{self.file_name}.tar.zst")
|
GLib.timeout_add(200, self.check_size, f"{self.new_path}/{self.file_name}.tar.zst")
|
||||||
|
|
||||||
def extract(self):
|
def extract(self):
|
||||||
self.stop = False
|
self.stop = False
|
||||||
self.files_to_trash_on_cancel = [self.new_path]
|
self.files_to_trash_on_cancel = [self.new_path]
|
||||||
HostInfo.main_window.add_refresh_lockout("managing snapshot")
|
HostInfo.main_window.add_refresh_lockout("managing snapshot")
|
||||||
Gio.Task.new(None, None, None).run_in_thread(self.extract_thread)
|
Gio.Task.new(None, None, None).run_in_thread(self.extract_thread)
|
||||||
GLib.timeout_add(200, self.check_size, self.new_path)
|
GLib.timeout_add(200, self.check_size, self.new_path)
|
||||||
|
|
||||||
def __init__(self, existing_path, new_path, file_name="", name="", toast_overlay=None):
|
def __init__(self, existing_path, new_path, file_name="", name="", toast_overlay=None):
|
||||||
self.existing_path = existing_path
|
self.existing_path = existing_path
|
||||||
self.new_path = new_path
|
self.new_path = new_path
|
||||||
|
|||||||
@@ -8,20 +8,20 @@ import subprocess
|
|||||||
class DataBox(Gtk.ListBox):
|
class DataBox(Gtk.ListBox):
|
||||||
__gtype_name__ = 'DataBox'
|
__gtype_name__ = 'DataBox'
|
||||||
gtc = Gtk.Template.Child
|
gtc = Gtk.Template.Child
|
||||||
|
|
||||||
row = gtc()
|
row = gtc()
|
||||||
image = gtc()
|
image = gtc()
|
||||||
title_label = gtc()
|
title_label = gtc()
|
||||||
subtitle_label = gtc()
|
subtitle_label = gtc()
|
||||||
spinner = gtc()
|
spinner = gtc()
|
||||||
size_label = gtc()
|
size_label = gtc()
|
||||||
|
|
||||||
copy_button = gtc()
|
copy_button = gtc()
|
||||||
open_button = gtc()
|
open_button = gtc()
|
||||||
install_button = gtc()
|
install_button = gtc()
|
||||||
trash_button = gtc()
|
trash_button = gtc()
|
||||||
check_button = gtc()
|
check_button = gtc()
|
||||||
|
|
||||||
def human_readable_size(self):
|
def human_readable_size(self):
|
||||||
working_size = self.size
|
working_size = self.size
|
||||||
units = ['KB', 'MB', 'GB', 'TB']
|
units = ['KB', 'MB', 'GB', 'TB']
|
||||||
@@ -31,19 +31,19 @@ class DataBox(Gtk.ListBox):
|
|||||||
return f"~ {round(working_size)} {unit}"
|
return f"~ {round(working_size)} {unit}"
|
||||||
working_size /= 1024
|
working_size /= 1024
|
||||||
return f"~ {round(working_size)} PB"
|
return f"~ {round(working_size)} PB"
|
||||||
|
|
||||||
def get_size(self, *args):
|
def get_size(self, *args):
|
||||||
self.size = int(subprocess.run(['du', '-s', self.data_path], capture_output=True, text=True).stdout.split("\t")[0])
|
self.size = int(subprocess.run(['du', '-s', self.data_path], capture_output=True, text=True).stdout.split("\t")[0])
|
||||||
|
|
||||||
def show_size(self):
|
def show_size(self):
|
||||||
def callback(*args):
|
def callback(*args):
|
||||||
self.size_label.set_label(self.human_readable_size())
|
self.size_label.set_label(self.human_readable_size())
|
||||||
self.spinner.set_visible(False)
|
self.spinner.set_visible(False)
|
||||||
if self.callback:
|
if self.callback:
|
||||||
self.callback(self.size)
|
self.callback(self.size)
|
||||||
|
|
||||||
Gio.Task.new(None, None, callback).run_in_thread(self.get_size)
|
Gio.Task.new(None, None, callback).run_in_thread(self.get_size)
|
||||||
|
|
||||||
def idle_stuff(self):
|
def idle_stuff(self):
|
||||||
self.title_label.set_label(self.title)
|
self.title_label.set_label(self.title)
|
||||||
self.subtitle_label.set_label(self.subtitle)
|
self.subtitle_label.set_label(self.subtitle)
|
||||||
@@ -51,31 +51,31 @@ class DataBox(Gtk.ListBox):
|
|||||||
if self.icon_path:
|
if self.icon_path:
|
||||||
self.image.add_css_class("icon-dropshadow")
|
self.image.add_css_class("icon-dropshadow")
|
||||||
self.image.set_from_file(self.icon_path)
|
self.image.set_from_file(self.icon_path)
|
||||||
|
|
||||||
def copy_handler(self, *args):
|
def copy_handler(self, *args):
|
||||||
try:
|
try:
|
||||||
HostInfo.clipboard.set(self.data_path)
|
HostInfo.clipboard.set(self.data_path)
|
||||||
self.toast_overlay.add_toast(Adw.Toast.new(_("Copied data path")))
|
self.toast_overlay.add_toast(Adw.Toast.new(_("Copied data path")))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.toast_overlay.add_toast(ErrorToast(_("Could not copy data path"), str(e)).toast)
|
self.toast_overlay.add_toast(ErrorToast(_("Could not copy data path"), str(e)).toast)
|
||||||
|
|
||||||
def open_handler(self, *args):
|
def open_handler(self, *args):
|
||||||
try:
|
try:
|
||||||
Gio.AppInfo.launch_default_for_uri(f"file://{self.data_path}", None)
|
Gio.AppInfo.launch_default_for_uri(f"file://{self.data_path}", None)
|
||||||
self.toast_overlay.add_toast(Adw.Toast.new(_("Opened data folder")))
|
self.toast_overlay.add_toast(Adw.Toast.new(_("Opened data folder")))
|
||||||
except GLib.GError as e:
|
except GLib.GError as e:
|
||||||
self.toast_overlay.add_toast(ErrorToast(_("Could not open folder"), str(e)).toast)
|
self.toast_overlay.add_toast(ErrorToast(_("Could not open folder"), str(e)).toast)
|
||||||
|
|
||||||
def install_handler(self, *args):
|
def install_handler(self, *args):
|
||||||
self.parent_page.should_rclick = False
|
self.parent_page.should_rclick = False
|
||||||
def why_cant_this_just_be_a_lambda(*args):
|
def why_cant_this_just_be_a_lambda(*args):
|
||||||
self.parent_page.should_rclick = True
|
self.parent_page.should_rclick = True
|
||||||
|
|
||||||
AttemptInstallDialog([self.subtitle], why_cant_this_just_be_a_lambda)
|
AttemptInstallDialog([self.subtitle], why_cant_this_just_be_a_lambda)
|
||||||
|
|
||||||
def trash_handler(self, *args):
|
def trash_handler(self, *args):
|
||||||
self.failed_trash = False
|
self.failed_trash = False
|
||||||
|
|
||||||
def thread(*args):
|
def thread(*args):
|
||||||
try:
|
try:
|
||||||
subprocess.run(['gio', 'trash', self.data_path], check=True, text=True, capture_output=True)
|
subprocess.run(['gio', 'trash', self.data_path], check=True, text=True, capture_output=True)
|
||||||
@@ -83,17 +83,17 @@ class DataBox(Gtk.ListBox):
|
|||||||
properties_package = properties_page.package
|
properties_package = properties_page.package
|
||||||
if not properties_package is None:
|
if not properties_package is None:
|
||||||
properties_page.set_properties(properties_package, True)
|
properties_page.set_properties(properties_package, True)
|
||||||
|
|
||||||
snapshot_list_page = HostInfo.main_window.pages[HostInfo.main_window.snapshots_row].list_page
|
snapshot_list_page = HostInfo.main_window.pages[HostInfo.main_window.snapshots_row].list_page
|
||||||
snapshot_list_package = snapshot_list_page.package_or_folder
|
snapshot_list_package = snapshot_list_page.package_or_folder
|
||||||
if not snapshot_list_package is None:
|
if not snapshot_list_package is None:
|
||||||
snapshot_list_page.set_snapshots(snapshot_list_package, True)
|
snapshot_list_page.set_snapshots(snapshot_list_package, True)
|
||||||
|
|
||||||
except subprocess.CalledProcessError as cpe:
|
except subprocess.CalledProcessError as cpe:
|
||||||
self.failed_trash = cpe.stderr
|
self.failed_trash = cpe.stderr
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.failed_trash = e
|
self.failed_trash = e
|
||||||
|
|
||||||
def callback(*args):
|
def callback(*args):
|
||||||
if self.failed_trash:
|
if self.failed_trash:
|
||||||
self.toast_overlay.add_toast(ErrorToast(_("Could not trash data"), str(self.failed_trash)).toast)
|
self.toast_overlay.add_toast(ErrorToast(_("Could not trash data"), str(self.failed_trash)).toast)
|
||||||
@@ -101,14 +101,14 @@ class DataBox(Gtk.ListBox):
|
|||||||
self.toast_overlay.add_toast(Adw.Toast.new("Trashed data"))
|
self.toast_overlay.add_toast(Adw.Toast.new("Trashed data"))
|
||||||
if self.trash_callback:
|
if self.trash_callback:
|
||||||
self.trash_callback(self)
|
self.trash_callback(self)
|
||||||
|
|
||||||
def on_response(_, response):
|
def on_response(_, response):
|
||||||
self.parent_page.should_rclick = True
|
self.parent_page.should_rclick = True
|
||||||
if response != "continue":
|
if response != "continue":
|
||||||
return
|
return
|
||||||
|
|
||||||
Gio.Task.new(None, None, callback).run_in_thread(thread)
|
Gio.Task.new(None, None, callback).run_in_thread(thread)
|
||||||
|
|
||||||
self.parent_page.should_rclick = False
|
self.parent_page.should_rclick = False
|
||||||
dialog = Adw.AlertDialog(heading=_("Trash {}'s Data?").format(self.title), body=_("{}'s data will be sent to the trash").format(self.title))
|
dialog = Adw.AlertDialog(heading=_("Trash {}'s Data?").format(self.title), body=_("{}'s data will be sent to the trash").format(self.title))
|
||||||
dialog.add_response("cancel", _("Cancel"))
|
dialog.add_response("cancel", _("Cancel"))
|
||||||
@@ -116,10 +116,10 @@ class DataBox(Gtk.ListBox):
|
|||||||
dialog.set_response_appearance("continue", Adw.ResponseAppearance.DESTRUCTIVE)
|
dialog.set_response_appearance("continue", Adw.ResponseAppearance.DESTRUCTIVE)
|
||||||
dialog.connect("response", on_response)
|
dialog.connect("response", on_response)
|
||||||
dialog.present(HostInfo.main_window)
|
dialog.present(HostInfo.main_window)
|
||||||
|
|
||||||
def __init__(self, parent_page, toast_overlay, is_leftover, title, subtitle, data_path, icon_path=None, callback=None, trash_callback=None, **kwargs):
|
def __init__(self, parent_page, toast_overlay, is_leftover, title, subtitle, data_path, icon_path=None, callback=None, trash_callback=None, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
# Extra Object Creation
|
# Extra Object Creation
|
||||||
self.parent_page = parent_page
|
self.parent_page = parent_page
|
||||||
self.toast_overlay = toast_overlay
|
self.toast_overlay = toast_overlay
|
||||||
@@ -132,7 +132,7 @@ class DataBox(Gtk.ListBox):
|
|||||||
self.trash_callback = trash_callback
|
self.trash_callback = trash_callback
|
||||||
self.size = None
|
self.size = None
|
||||||
self.failed_trash = None
|
self.failed_trash = None
|
||||||
|
|
||||||
# Apply
|
# Apply
|
||||||
self.idle_stuff()
|
self.idle_stuff()
|
||||||
self.show_size()
|
self.show_size()
|
||||||
@@ -140,7 +140,7 @@ class DataBox(Gtk.ListBox):
|
|||||||
self.check_button.set_active = lambda *_: None
|
self.check_button.set_active = lambda *_: None
|
||||||
self.check_button.set_sensitive(False)
|
self.check_button.set_sensitive(False)
|
||||||
self.trash_button.set_sensitive(False)
|
self.trash_button.set_sensitive(False)
|
||||||
|
|
||||||
# Connections
|
# Connections
|
||||||
self.copy_button.connect("clicked", self.copy_handler)
|
self.copy_button.connect("clicked", self.copy_handler)
|
||||||
self.open_button.connect("clicked", self.open_handler)
|
self.open_button.connect("clicked", self.open_handler)
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ class DataSubpage(Gtk.Stack):
|
|||||||
gtc = Gtk.Template.Child
|
gtc = Gtk.Template.Child
|
||||||
|
|
||||||
scrolled_window = gtc()
|
scrolled_window = gtc()
|
||||||
|
|
||||||
label_box = gtc()
|
label_box = gtc()
|
||||||
subtitle_size_box = gtc()
|
subtitle_size_box = gtc()
|
||||||
title = gtc()
|
title = gtc()
|
||||||
@@ -100,7 +100,7 @@ class DataSubpage(Gtk.Stack):
|
|||||||
self.selected_boxes.remove(box)
|
self.selected_boxes.remove(box)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
total = len(self.selected_boxes)
|
total = len(self.selected_boxes)
|
||||||
self.subtitle.set_visible(not total == 0)
|
self.subtitle.set_visible(not total == 0)
|
||||||
self.size_label.set_visible(total == 0)
|
self.size_label.set_visible(total == 0)
|
||||||
@@ -148,7 +148,7 @@ class DataSubpage(Gtk.Stack):
|
|||||||
box = DataBox(self, self.parent_page.toast_overlay, True, folder.split('.')[-1], folder, f"{HostInfo.home}/.var/app/{folder}", None, self.box_size_callback, self.trash_handler)
|
box = DataBox(self, self.parent_page.toast_overlay, True, folder.split('.')[-1], folder, f"{HostInfo.home}/.var/app/{folder}", None, self.box_size_callback, self.trash_handler)
|
||||||
box.check_button.connect("toggled", lambda *_, box=box: self.box_select_handler(box))
|
box.check_button.connect("toggled", lambda *_, box=box: self.box_select_handler(box))
|
||||||
self.flow_box.append(box)
|
self.flow_box.append(box)
|
||||||
|
|
||||||
idx = 0
|
idx = 0
|
||||||
while box := self.flow_box.get_child_at_index(idx):
|
while box := self.flow_box.get_child_at_index(idx):
|
||||||
idx += 1
|
idx += 1
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import os, subprocess
|
|||||||
class UserDataPage(Adw.BreakpointBin):
|
class UserDataPage(Adw.BreakpointBin):
|
||||||
__gtype_name__ = 'UserDataPage'
|
__gtype_name__ = 'UserDataPage'
|
||||||
gtc = Gtk.Template.Child
|
gtc = Gtk.Template.Child
|
||||||
|
|
||||||
bpt = gtc()
|
bpt = gtc()
|
||||||
status_stack = gtc()
|
status_stack = gtc()
|
||||||
loading_view = gtc()
|
loading_view = gtc()
|
||||||
@@ -26,13 +26,13 @@ class UserDataPage(Adw.BreakpointBin):
|
|||||||
toast_overlay = gtc()
|
toast_overlay = gtc()
|
||||||
stack = gtc()
|
stack = gtc()
|
||||||
revealer = gtc()
|
revealer = gtc()
|
||||||
|
|
||||||
sort_ascend = gtc()
|
sort_ascend = gtc()
|
||||||
sort_descend = gtc()
|
sort_descend = gtc()
|
||||||
sort_name = gtc()
|
sort_name = gtc()
|
||||||
sort_id = gtc()
|
sort_id = gtc()
|
||||||
sort_size = gtc()
|
sort_size = gtc()
|
||||||
|
|
||||||
select_all_button = gtc()
|
select_all_button = gtc()
|
||||||
copy_button = gtc()
|
copy_button = gtc()
|
||||||
trash_button = gtc()
|
trash_button = gtc()
|
||||||
@@ -42,7 +42,7 @@ class UserDataPage(Adw.BreakpointBin):
|
|||||||
more_menu = gtc()
|
more_menu = gtc()
|
||||||
more_trash = gtc()
|
more_trash = gtc()
|
||||||
more_install = gtc()
|
more_install = gtc()
|
||||||
|
|
||||||
# Referred to in the main window
|
# Referred to in the main window
|
||||||
# It is used to determine if a new page should be made or not
|
# It is used to determine if a new page should be made or not
|
||||||
# This must be set to the created object from within the class's __init__ method
|
# This must be set to the created object from within the class's __init__ method
|
||||||
@@ -51,23 +51,23 @@ class UserDataPage(Adw.BreakpointBin):
|
|||||||
data_path = f"{HostInfo.home}/.var/app"
|
data_path = f"{HostInfo.home}/.var/app"
|
||||||
bpt_is_applied = False
|
bpt_is_applied = False
|
||||||
is_trash_dialog_open = False
|
is_trash_dialog_open = False
|
||||||
|
|
||||||
def sort_data(self, *args):
|
def sort_data(self, *args):
|
||||||
self.data_flatpaks.clear()
|
self.data_flatpaks.clear()
|
||||||
self.active_data.clear()
|
self.active_data.clear()
|
||||||
self.leftover_data.clear()
|
self.leftover_data.clear()
|
||||||
# paks = dict(HostInfo.id_to_flatpak)
|
# paks = dict(HostInfo.id_to_flatpak)
|
||||||
|
|
||||||
if not os.path.exists(self.data_path):
|
if not os.path.exists(self.data_path):
|
||||||
return
|
return
|
||||||
|
|
||||||
for folder in os.listdir(self.data_path):
|
for folder in os.listdir(self.data_path):
|
||||||
try:
|
try:
|
||||||
self.data_flatpaks.append(HostInfo.id_to_flatpak[folder])
|
self.data_flatpaks.append(HostInfo.id_to_flatpak[folder])
|
||||||
self.active_data.append(folder)
|
self.active_data.append(folder)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
self.leftover_data.append(folder)
|
self.leftover_data.append(folder)
|
||||||
|
|
||||||
def start_loading(self, *args):
|
def start_loading(self, *args):
|
||||||
self.status_stack.set_visible_child(self.loading_view)
|
self.status_stack.set_visible_child(self.loading_view)
|
||||||
self.search_button.set_active(False)
|
self.search_button.set_active(False)
|
||||||
@@ -76,23 +76,23 @@ class UserDataPage(Adw.BreakpointBin):
|
|||||||
self.adp.spinner.set_visible(True)
|
self.adp.spinner.set_visible(True)
|
||||||
self.ldp.size_label.set_label(_("Loading Size"))
|
self.ldp.size_label.set_label(_("Loading Size"))
|
||||||
self.ldp.spinner.set_visible(True)
|
self.ldp.spinner.set_visible(True)
|
||||||
|
|
||||||
def end_loading(self, *args):
|
def end_loading(self, *args):
|
||||||
def callback(*args):
|
def callback(*args):
|
||||||
self.adp.generate_list(self.data_flatpaks, self.active_data)
|
self.adp.generate_list(self.data_flatpaks, self.active_data)
|
||||||
self.ldp.generate_list([], self.leftover_data)
|
self.ldp.generate_list([], self.leftover_data)
|
||||||
|
|
||||||
Gio.Task.new(None, None, callback).run_in_thread(self.sort_data)
|
Gio.Task.new(None, None, callback).run_in_thread(self.sort_data)
|
||||||
|
|
||||||
def sort_button_handler(self, button):
|
def sort_button_handler(self, button):
|
||||||
if button in {self.sort_ascend, self.sort_descend}:
|
if button in {self.sort_ascend, self.sort_descend}:
|
||||||
self.settings.set_boolean("sort-ascend", self.sort_ascend.get_active())
|
self.settings.set_boolean("sort-ascend", self.sort_ascend.get_active())
|
||||||
else:
|
else:
|
||||||
self.settings.set_string("sort-mode", self.buttons_to_sort_modes[button])
|
self.settings.set_string("sort-mode", self.buttons_to_sort_modes[button])
|
||||||
|
|
||||||
self.adp.update_sort_mode()
|
self.adp.update_sort_mode()
|
||||||
self.ldp.update_sort_mode()
|
self.ldp.update_sort_mode()
|
||||||
|
|
||||||
def load_sort_settings(self):
|
def load_sort_settings(self):
|
||||||
mode = self.settings.get_string("sort-mode")
|
mode = self.settings.get_string("sort-mode")
|
||||||
ascend = self.settings.get_boolean("sort-ascend")
|
ascend = self.settings.get_boolean("sort-ascend")
|
||||||
@@ -100,7 +100,7 @@ class UserDataPage(Adw.BreakpointBin):
|
|||||||
(self.sort_ascend if ascend else self.sort_descend).set_active(True)
|
(self.sort_ascend if ascend else self.sort_descend).set_active(True)
|
||||||
self.adp.update_sort_mode()
|
self.adp.update_sort_mode()
|
||||||
self.ldp.update_sort_mode()
|
self.ldp.update_sort_mode()
|
||||||
|
|
||||||
def view_change_handler(self, *args):
|
def view_change_handler(self, *args):
|
||||||
child = self.stack.get_visible_child()
|
child = self.stack.get_visible_child()
|
||||||
if child.total_size == 0:
|
if child.total_size == 0:
|
||||||
@@ -116,17 +116,17 @@ class UserDataPage(Adw.BreakpointBin):
|
|||||||
self.select_button.set_sensitive(True)
|
self.select_button.set_sensitive(True)
|
||||||
self.sort_button.set_sensitive(True)
|
self.sort_button.set_sensitive(True)
|
||||||
self.search_entry.set_editable(True)
|
self.search_entry.set_editable(True)
|
||||||
|
|
||||||
self.more_button.set_visible(child is self.ldp and self.bpt_is_applied)
|
self.more_button.set_visible(child is self.ldp and self.bpt_is_applied)
|
||||||
self.install_button.set_visible(child is self.ldp and not self.bpt_is_applied)
|
self.install_button.set_visible(child is self.ldp and not self.bpt_is_applied)
|
||||||
self.trash_button.set_visible(child is self.adp or not self.bpt_is_applied)
|
self.trash_button.set_visible(child is self.adp or not self.bpt_is_applied)
|
||||||
|
|
||||||
has_selected = len(child.selected_boxes) > 0
|
has_selected = len(child.selected_boxes) > 0
|
||||||
self.copy_button.set_sensitive(has_selected)
|
self.copy_button.set_sensitive(has_selected)
|
||||||
self.trash_button.set_sensitive(has_selected)
|
self.trash_button.set_sensitive(has_selected)
|
||||||
self.install_button.set_sensitive(has_selected)
|
self.install_button.set_sensitive(has_selected)
|
||||||
self.more_button.set_sensitive(has_selected)
|
self.more_button.set_sensitive(has_selected)
|
||||||
|
|
||||||
def select_toggle_handler(self, *args):
|
def select_toggle_handler(self, *args):
|
||||||
active = self.select_button.get_active()
|
active = self.select_button.get_active()
|
||||||
self.adp.set_selection_mode(active)
|
self.adp.set_selection_mode(active)
|
||||||
@@ -136,27 +136,27 @@ class UserDataPage(Adw.BreakpointBin):
|
|||||||
self.trash_button.set_sensitive(False)
|
self.trash_button.set_sensitive(False)
|
||||||
self.install_button.set_sensitive(False)
|
self.install_button.set_sensitive(False)
|
||||||
self.more_button.set_sensitive(False)
|
self.more_button.set_sensitive(False)
|
||||||
|
|
||||||
def select_all_handler(self, *args):
|
def select_all_handler(self, *args):
|
||||||
child = self.stack.get_visible_child()
|
child = self.stack.get_visible_child()
|
||||||
child.select_all_handler()
|
child.select_all_handler()
|
||||||
|
|
||||||
def copy_handler(self, *args):
|
def copy_handler(self, *args):
|
||||||
child = self.stack.get_visible_child()
|
child = self.stack.get_visible_child()
|
||||||
to_copy = ""
|
to_copy = ""
|
||||||
for box in child.selected_boxes:
|
for box in child.selected_boxes:
|
||||||
to_copy += "\n" + box.data_path
|
to_copy += "\n" + box.data_path
|
||||||
|
|
||||||
if len(to_copy) == 0:
|
if len(to_copy) == 0:
|
||||||
self.toast_overlay.add_toast(ErrorToast(_("Could not copy paths"), _("No boxes were selected")).toast)
|
self.toast_overlay.add_toast(ErrorToast(_("Could not copy paths"), _("No boxes were selected")).toast)
|
||||||
else:
|
else:
|
||||||
HostInfo.clipboard.set(to_copy.replace("\n", "", 1))
|
HostInfo.clipboard.set(to_copy.replace("\n", "", 1))
|
||||||
self.toast_overlay.add_toast(Adw.Toast(title=_("Copied paths")))
|
self.toast_overlay.add_toast(Adw.Toast(title=_("Copied paths")))
|
||||||
|
|
||||||
def selection_trash_handler(self, *args):
|
def selection_trash_handler(self, *args):
|
||||||
error = [None]
|
error = [None]
|
||||||
child = self.stack.get_visible_child()
|
child = self.stack.get_visible_child()
|
||||||
|
|
||||||
def thread(path):
|
def thread(path):
|
||||||
cmd = ['gio', 'trash'] + path
|
cmd = ['gio', 'trash'] + path
|
||||||
try:
|
try:
|
||||||
@@ -165,17 +165,17 @@ class UserDataPage(Adw.BreakpointBin):
|
|||||||
properties_package = properties_page.package
|
properties_package = properties_page.package
|
||||||
if not properties_package is None:
|
if not properties_package is None:
|
||||||
properties_page.set_properties(properties_package, True)
|
properties_page.set_properties(properties_package, True)
|
||||||
|
|
||||||
snapshot_list_page = HostInfo.main_window.pages[HostInfo.main_window.snapshots_row].list_page
|
snapshot_list_page = HostInfo.main_window.pages[HostInfo.main_window.snapshots_row].list_page
|
||||||
snapshot_list_package = snapshot_list_page.package_or_folder
|
snapshot_list_package = snapshot_list_page.package_or_folder
|
||||||
if not snapshot_list_package is None:
|
if not snapshot_list_package is None:
|
||||||
snapshot_list_page.set_snapshots(snapshot_list_package, True)
|
snapshot_list_page.set_snapshots(snapshot_list_package, True)
|
||||||
|
|
||||||
except subprocess.CalledProcessError as cpe:
|
except subprocess.CalledProcessError as cpe:
|
||||||
error[0] = cpe.stderr
|
error[0] = cpe.stderr
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
error[0] = e
|
error[0] = e
|
||||||
|
|
||||||
def callback(*args):
|
def callback(*args):
|
||||||
self.start_loading()
|
self.start_loading()
|
||||||
self.end_loading()
|
self.end_loading()
|
||||||
@@ -183,27 +183,27 @@ class UserDataPage(Adw.BreakpointBin):
|
|||||||
self.toast_overlay.add_toast(ErrorToast(_("Could not trash data"), str(error[0])).toast)
|
self.toast_overlay.add_toast(ErrorToast(_("Could not trash data"), str(error[0])).toast)
|
||||||
else:
|
else:
|
||||||
self.toast_overlay.add_toast(Adw.Toast(title=_("Trashed data")))
|
self.toast_overlay.add_toast(Adw.Toast(title=_("Trashed data")))
|
||||||
|
|
||||||
def on_response(dialog, response):
|
def on_response(dialog, response):
|
||||||
self.is_trash_dialog_open = False
|
self.is_trash_dialog_open = False
|
||||||
if response != "continue":
|
if response != "continue":
|
||||||
return
|
return
|
||||||
|
|
||||||
to_trash = []
|
to_trash = []
|
||||||
for box in child.selected_boxes:
|
for box in child.selected_boxes:
|
||||||
to_trash.append(box.data_path)
|
to_trash.append(box.data_path)
|
||||||
|
|
||||||
if len(to_trash) == 0:
|
if len(to_trash) == 0:
|
||||||
self.toast_overlay.add_toast(ErrorToast(_("Could not trash data"), _("No boxes were selected")).toast)
|
self.toast_overlay.add_toast(ErrorToast(_("Could not trash data"), _("No boxes were selected")).toast)
|
||||||
return
|
return
|
||||||
|
|
||||||
self.select_button.set_active(False)
|
self.select_button.set_active(False)
|
||||||
self.status_stack.set_visible_child(self.loading_view)
|
self.status_stack.set_visible_child(self.loading_view)
|
||||||
Gio.Task.new(None, None, callback).run_in_thread(lambda *_: thread(to_trash))
|
Gio.Task.new(None, None, callback).run_in_thread(lambda *_: thread(to_trash))
|
||||||
|
|
||||||
if len(child.selected_boxes) < 1 or self.is_trash_dialog_open:
|
if len(child.selected_boxes) < 1 or self.is_trash_dialog_open:
|
||||||
return
|
return
|
||||||
|
|
||||||
self.is_trash_dialog_open = True
|
self.is_trash_dialog_open = True
|
||||||
dialog = Adw.AlertDialog(heading=_("Trash Data?"), body=_("Data will be sent to the trash"))
|
dialog = Adw.AlertDialog(heading=_("Trash Data?"), body=_("Data will be sent to the trash"))
|
||||||
dialog.add_response("cancel", _("Cancel"))
|
dialog.add_response("cancel", _("Cancel"))
|
||||||
@@ -211,7 +211,7 @@ class UserDataPage(Adw.BreakpointBin):
|
|||||||
dialog.set_response_appearance("continue", Adw.ResponseAppearance.DESTRUCTIVE)
|
dialog.set_response_appearance("continue", Adw.ResponseAppearance.DESTRUCTIVE)
|
||||||
dialog.connect("response", on_response)
|
dialog.connect("response", on_response)
|
||||||
dialog.present(ErrorToast.main_window)
|
dialog.present(ErrorToast.main_window)
|
||||||
|
|
||||||
def breakpoint_handler(self, bpt, is_applied):
|
def breakpoint_handler(self, bpt, is_applied):
|
||||||
self.bpt_is_applied = is_applied
|
self.bpt_is_applied = is_applied
|
||||||
self.adp.label_box.set_orientation(Gtk.Orientation.VERTICAL if is_applied else Gtk.Orientation.HORIZONTAL)
|
self.adp.label_box.set_orientation(Gtk.Orientation.VERTICAL if is_applied else Gtk.Orientation.HORIZONTAL)
|
||||||
@@ -220,15 +220,15 @@ class UserDataPage(Adw.BreakpointBin):
|
|||||||
self.install_button.set_visible(child is self.ldp and not is_applied)
|
self.install_button.set_visible(child is self.ldp and not is_applied)
|
||||||
self.more_button.set_visible(child is self.ldp and is_applied)
|
self.more_button.set_visible(child is self.ldp and is_applied)
|
||||||
self.trash_button.set_visible(child is self.adp or not is_applied)
|
self.trash_button.set_visible(child is self.adp or not is_applied)
|
||||||
|
|
||||||
def install_handler(self, *args):
|
def install_handler(self, *args):
|
||||||
child = self.stack.get_visible_child()
|
child = self.stack.get_visible_child()
|
||||||
package_names = []
|
package_names = []
|
||||||
for box in child.selected_boxes:
|
for box in child.selected_boxes:
|
||||||
package_names.append(box.subtitle)
|
package_names.append(box.subtitle)
|
||||||
|
|
||||||
AttemptInstallDialog(package_names, lambda is_valid: self.select_button.set_active(not is_valid))
|
AttemptInstallDialog(package_names, lambda is_valid: self.select_button.set_active(not is_valid))
|
||||||
|
|
||||||
def more_menu_handler(self, listbox, row):
|
def more_menu_handler(self, listbox, row):
|
||||||
self.more_popover.popdown()
|
self.more_popover.popdown()
|
||||||
row = row.get_child()
|
row = row.get_child()
|
||||||
@@ -237,10 +237,10 @@ class UserDataPage(Adw.BreakpointBin):
|
|||||||
self.install_handler()
|
self.install_handler()
|
||||||
case self.more_trash:
|
case self.more_trash:
|
||||||
self.selection_trash_handler()
|
self.selection_trash_handler()
|
||||||
|
|
||||||
def __init__(self, main_window, **kwargs):
|
def __init__(self, main_window, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
# Extra Object Creation
|
# Extra Object Creation
|
||||||
self.__class__.instance = self
|
self.__class__.instance = self
|
||||||
self.adp = DataSubpage(_("Active Data"), self, True, main_window)
|
self.adp = DataSubpage(_("Active Data"), self, True, main_window)
|
||||||
@@ -258,11 +258,11 @@ class UserDataPage(Adw.BreakpointBin):
|
|||||||
self.buttons_to_sort_modes = {}
|
self.buttons_to_sort_modes = {}
|
||||||
self.on_backspace_handler = self.selection_trash_handler
|
self.on_backspace_handler = self.selection_trash_handler
|
||||||
self.on_escape_handler = lambda *_: self.select_button.set_active(False)
|
self.on_escape_handler = lambda *_: self.select_button.set_active(False)
|
||||||
|
|
||||||
# Apply
|
# Apply
|
||||||
for key, button in self.sort_modes_to_buttons.items():
|
for key, button in self.sort_modes_to_buttons.items():
|
||||||
self.buttons_to_sort_modes[button] = key
|
self.buttons_to_sort_modes[button] = key
|
||||||
|
|
||||||
self.stack.add_titled_with_icon(
|
self.stack.add_titled_with_icon(
|
||||||
child=self.adp,
|
child=self.adp,
|
||||||
name="active",
|
name="active",
|
||||||
@@ -275,7 +275,7 @@ class UserDataPage(Adw.BreakpointBin):
|
|||||||
title=_("Leftover Data"),
|
title=_("Leftover Data"),
|
||||||
icon_name="folder-templates-symbolic",
|
icon_name="folder-templates-symbolic",
|
||||||
)
|
)
|
||||||
|
|
||||||
# Connections
|
# Connections
|
||||||
self.stack.connect("notify::visible-child", self.view_change_handler)
|
self.stack.connect("notify::visible-child", self.view_change_handler)
|
||||||
self.select_button.connect("toggled", self.select_toggle_handler)
|
self.select_button.connect("toggled", self.select_toggle_handler)
|
||||||
@@ -291,7 +291,7 @@ class UserDataPage(Adw.BreakpointBin):
|
|||||||
self.sort_size.connect("clicked", self.sort_button_handler)
|
self.sort_size.connect("clicked", self.sort_button_handler)
|
||||||
self.bpt.connect("apply", self.breakpoint_handler, True)
|
self.bpt.connect("apply", self.breakpoint_handler, True)
|
||||||
self.bpt.connect("unapply", self.breakpoint_handler, False)
|
self.bpt.connect("unapply", self.breakpoint_handler, False)
|
||||||
|
|
||||||
# Apply again
|
# Apply again
|
||||||
self.loading_view.set_content(LoadingStatus(_("Loading User Data"), _("This should only take a moment")))
|
self.loading_view.set_content(LoadingStatus(_("Loading User Data"), _("This should only take a moment")))
|
||||||
self.search_bar.set_key_capture_widget(main_window)
|
self.search_bar.set_key_capture_widget(main_window)
|
||||||
|
|||||||
@@ -5,4 +5,4 @@ revision = main
|
|||||||
depth = 1
|
depth = 1
|
||||||
|
|
||||||
[provide]
|
[provide]
|
||||||
program_names = blueprint-compiler
|
program_names = blueprint-compiler
|
||||||
|
|||||||
Reference in New Issue
Block a user