diff --git a/src/functions.py b/src/functions.py new file mode 100644 index 00000000..41206870 --- /dev/null +++ b/src/functions.py @@ -0,0 +1,72 @@ +from gi.repository import GLib, Gtk #Adw, Gdk, Gio +import os +import subprocess +import pathlib + +class functions(): + def __init__(self, window, **kwargs): + super().__init__(**kwargs) + self.main_window = window + self.host_home = str(pathlib.Path.home()) + self.user_data_path = self.host_home + "/.var/app/" + + def trash_folder(self, path): + if not os.path.exists(path): + return 1 + try: + subprocess.run(["flatpak-spawn", "--host", "gio", "trash", path], capture_output=True, check=True) + return 0 + except subprocess.CalledProcessError: + return 2 + + def get_size_format(self, b): + factor = 1024 + suffix = "B" + for unit in ["", "K", "M", "G", "T", "P", "E", "Z"]: + if b < factor: + return f"{b:.1f}{unit}{suffix}" + b /= factor + return f"{b:.1f}{suffix}" + + def get_directory_size(self, directory): + """Returns the `directory` size in bytes.""" + total = 0 + try: + # print("[+] Getting the size of", directory) + for entry in os.scandir(directory): + if entry.is_symlink(): + continue # Skip symlinks + if entry.is_file(): + # if it's a file, use stat() function + total += entry.stat().st_size + elif entry.is_dir(): + # if it's a directory, recursively call this function + try: + total += self.get_directory_size(entry.path) + except FileNotFoundError: + pass + except NotADirectoryError: + # if `directory` isn't a directory, get the file size then + return os.path.getsize(directory) + except PermissionError: + # if for whatever reason we can't open the folder, return 0 + return 0 + return total + + def find_app_icon(self, app_id): + icon_theme = Gtk.IconTheme.new() + icon_theme.add_search_path("/var/lib/flatpak/exports/share/icons/") + icon_theme.add_search_path(self.host_home + "/.local/share/flatpak/exports/share/icons") + + try: + icon_path = (icon_theme.lookup_icon(app_id, None, 512, 1, self.main_window.get_direction(), 0).get_file().get_path()) + except GLib.GError: + icon_path = None + if icon_path: + image = Gtk.Image.new_from_file(icon_path) + image.set_icon_size(Gtk.IconSize.LARGE) + image.add_css_class("icon-dropshadow") + else: + image = Gtk.Image.new_from_icon_name("application-x-executable-symbolic") + image.set_icon_size(Gtk.IconSize.LARGE) + return image \ No newline at end of file diff --git a/src/meson.build b/src/meson.build index ce370c33..57688dc3 100644 --- a/src/meson.build +++ b/src/meson.build @@ -43,6 +43,7 @@ warehouse_sources = [ 'properties_window.py', 'orphans_window.py', 'remotes.py', + 'functions.py', ] install_data(warehouse_sources, install_dir: moduledir) diff --git a/src/properties_window.py b/src/properties_window.py index 63ad369c..336bf2eb 100644 --- a/src/properties_window.py +++ b/src/properties_window.py @@ -1,4 +1,5 @@ from gi.repository import Gtk, Adw, GLib, Gdk, Gio +from .functions import functions import subprocess import os @@ -24,6 +25,8 @@ def show_properties_window(widget, index, window): user_data_list.append(user_data_row) user_data_list.add_css_class("boxed-list") + func = functions(window) + def key_handler(_a, event, _c, _d): if event == Gdk.KEY_Escape: properties_window.close() @@ -39,7 +42,7 @@ def key_handler(_a, event, _c, _d): def on_response(_a, response_id, _b): if response_id != "continue": return - if window.trash_folder(data_folder) == 0: + if func.trash_folder(data_folder) == 0: properties_toast_overlay.add_toast(Adw.Toast.new(_("Trashed user data"))) user_data_list.remove(user_data_row) user_data_list.append(Adw.ActionRow(title="No User Data")) @@ -67,7 +70,7 @@ def copy_button_handler(widget, title, to_copy): window.clipboard.set(to_copy) properties_toast_overlay.add_toast(Adw.Toast.new(_("Copied {}").format(title))) - image = window.find_app_icon(window.host_flatpaks[index][2]) + image = func.find_app_icon(window.host_flatpaks[index][2]) image.add_css_class("icon-dropshadow") image.set_margin_top(12) image.set_pixel_size(100) @@ -80,7 +83,7 @@ def copy_button_handler(widget, title, to_copy): if os.path.exists(path): user_data_row.set_title("User Data") - user_data_row.set_subtitle(f"{path}\n~{window.get_size_format(window.get_directory_size(path))}") + user_data_row.set_subtitle(f"{path}\n~{func.get_size_format(func.get_directory_size(path))}") open_button = Gtk.Button(icon_name="document-open-symbolic", valign=Gtk.Align.CENTER, tooltip_text=_("Open Data Folder")) open_button.add_css_class("flat") diff --git a/src/window.py b/src/window.py index c04bd5c9..3e0d6403 100644 --- a/src/window.py +++ b/src/window.py @@ -23,7 +23,7 @@ from gi.repository import Adw, Gdk, Gio, GLib, Gtk from .properties_window import show_properties_window from .orphans_window import show_orphans_window - +from .functions import functions @Gtk.Template(resource_path="/io/github/heliguy4599/Warehouse/window.ui") class WarehouseWindow(Adw.ApplicationWindow): @@ -61,10 +61,6 @@ class WarehouseWindow(Adw.ApplicationWindow): should_pulse = True no_close = None - icon_theme = Gtk.IconTheme.new() - icon_theme.add_search_path("/var/lib/flatpak/exports/share/icons/") - icon_theme.add_search_path(host_home + "/.local/share/flatpak/exports/share/icons") - def main_pulser(self): if self.should_pulse: self.main_progress_bar.pulse() @@ -74,49 +70,6 @@ def filter_func(self, row): if (self.search_entry.get_text().lower() in row.get_title().lower()) or (self.search_entry.get_text().lower() in row.get_subtitle().lower()): return True - def trash_folder(self, _a, path): - if not os.path.exists(path): - return 1 - try: - subprocess.run(["flatpak-spawn", "--host", "gio", "trash", path], capture_output=True, check=True) - return 0 - except subprocess.CalledProcessError: - return 2 - - def get_size_format(self, b): - factor = 1024 - suffix = "B" - for unit in ["", "K", "M", "G", "T", "P", "E", "Z"]: - if b < factor: - return f"{b:.1f}{unit}{suffix}" - b /= factor - return f"{b:.1f}{suffix}" - - def get_directory_size(self, directory): - """Returns the `directory` size in bytes.""" - total = 0 - try: - # print("[+] Getting the size of", directory) - for entry in os.scandir(directory): - if entry.is_symlink(): - continue # Skip symlinks - if entry.is_file(): - # if it's a file, use stat() function - total += entry.stat().st_size - elif entry.is_dir(): - # if it's a directory, recursively call this function - try: - total += self.get_directory_size(entry.path) - except FileNotFoundError: - pass - except NotADirectoryError: - # if `directory` isn't a directory, get the file size then - return os.path.getsize(directory) - except PermissionError: - # if for whatever reason we can't open the folder, return 0 - return 0 - return total - def uninstall_flatpak_callback(self, _a, _b): self.main_progress_bar.set_visible(False) self.should_pulse = False @@ -305,7 +258,7 @@ def generate_list(widget, is_select_all): if not file_list[i] in id_list: row_index += 1 select_orphans_tickbox = Gtk.CheckButton(halign=Gtk.Align.CENTER) - orphans_row = Adw.ActionRow(title=GLib.markup_escape_text(file_list[i]), subtitle=_("~") + self.get_size_format(self.get_directory_size(f"{self.user_data_path}{file_list[i]}"))) + orphans_row = Adw.ActionRow(title=GLib.markup_escape_text(file_list[i]), subtitle=_("~") + self.func.get_size_format(self.func.get_directory_size(f"{self.user_data_path}{file_list[i]}"))) orphans_row.add_suffix(select_orphans_tickbox) orphans_row.set_activatable_widget(select_orphans_tickbox) select_orphans_tickbox.connect("toggled", selection_handler, orphans_row.get_title()) @@ -476,20 +429,6 @@ def selection_handler(tickbox, file): selected_host_flatpak_indexes = [] - def find_app_icon(self, app_id): - try: - icon_path = (self.icon_theme.lookup_icon(app_id, None, 512, 1, self.get_direction(), 0).get_file().get_path()) - except GLib.GError: - icon_path = None - if icon_path: - image = Gtk.Image.new_from_file(icon_path) - image.set_icon_size(Gtk.IconSize.LARGE) - image.add_css_class("icon-dropshadow") - else: - image = Gtk.Image.new_from_icon_name("application-x-executable-symbolic") - image.set_icon_size(Gtk.IconSize.LARGE) - return image - def generate_list_of_flatpaks(self): self.set_title(self.main_window_title) self.batch_actions_enable(False) @@ -519,7 +458,7 @@ def get_host_flatpaks(): app_id = self.host_flatpaks[index][2] app_ref = self.host_flatpaks[index][8] flatpak_row = Adw.ActionRow(title=GLib.markup_escape_text(app_name)) - flatpak_row.add_prefix(self.find_app_icon(app_id)) + flatpak_row.add_prefix(self.func.find_app_icon(app_id)) if (not self.show_runtimes) and "runtime" in self.host_flatpaks[index][12]: continue @@ -604,7 +543,7 @@ def on_batch_clean_response(self, dialog, response, _a): app_id = self.host_flatpaks[self.selected_host_flatpak_indexes[i]][2] app_name = self.host_flatpaks[self.selected_host_flatpak_indexes[i]][0] path = f"{self.user_data_path}{app_id}" - trash = self.trash_folder(None, path) + trash = self.func.trash_folder(None, path) if trash == 1: show_success = False self.toast_overlay.add_toast(Adw.Toast.new(_("No user data for {}").format(app_name))) @@ -649,6 +588,7 @@ def flatpak_row_select_handler(self, tickbox, index): def __init__(self, **kwargs): super().__init__(**kwargs) + self.func = functions(self) self.list_of_flatpaks.set_filter_func(self.filter_func) self.set_size_request(0, 230) self.generate_list_of_flatpaks() @@ -665,3 +605,4 @@ def __init__(self, **kwargs): event_controller.connect("key-pressed", self.batch_key_handler) self.add_controller(event_controller) self.main_overlay.add_overlay(self.main_progress_bar) +