Files
warehouse/src/window.py
2024-02-06 10:39:55 -05:00

876 lines
34 KiB
Python

# window.py
#
# Copyright 2023 Heliguy
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, version 3 of the License only.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# SPDX-License-Identifier: GPL-3.0-only
import os
import pathlib
import subprocess
import re
import time
from gi.repository import Adw, Gdk, Gio, GLib, Gtk
from .properties_window import PropertiesWindow
from .filter_window import FilterWindow
from .common import myUtils
from .remotes_window import RemotesWindow
from .downgrade_window import DowngradeWindow
from .snapshots_window import SnapshotsWindow
from .const import Config
from .app_row_widget import AppRow
@Gtk.Template(resource_path="/io/github/flattool/Warehouse/../data/ui/window.ui")
class WarehouseWindow(Adw.ApplicationWindow):
__gtype_name__ = "WarehouseWindow"
main_window_title = "Warehouse"
flatpaks_list_box = Gtk.Template.Child()
search_entry = Gtk.Template.Child()
search_button = Gtk.Template.Child()
search_bar = Gtk.Template.Child()
toast_overlay = Gtk.Template.Child()
refresh_button = Gtk.Template.Child()
no_flatpaks = Gtk.Template.Child()
no_results = Gtk.Template.Child()
main_stack = Gtk.Template.Child()
batch_mode_button = Gtk.Template.Child()
batch_mode_bar = Gtk.Template.Child()
batch_select_all_button = Gtk.Template.Child()
batch_uninstall_button = Gtk.Template.Child()
batch_clean_button = Gtk.Template.Child()
batch_copy_button = Gtk.Template.Child()
batch_snapshot_button = Gtk.Template.Child()
main_box = Gtk.Template.Child()
main_overlay = Gtk.Template.Child()
main_toolbar_view = Gtk.Template.Child()
filter_button = Gtk.Template.Child()
scrolled_window = Gtk.Template.Child()
main_menu = Gtk.Template.Child()
installing = Gtk.Template.Child()
uninstalling = Gtk.Template.Child()
snapshotting = Gtk.Template.Child()
loading_flatpaks = Gtk.Template.Child()
no_matches = Gtk.Template.Child()
main_progress_bar = Gtk.ProgressBar(visible=False, can_target=False)
main_progress_bar.add_css_class("osd")
clipboard = Gdk.Display.get_default().get_clipboard()
host_home = str(pathlib.Path.home())
user_data_path = host_home + "/.var/app/"
in_batch_mode = False
should_select_all = False
host_flatpaks = None
install_success = True
no_close = None
re_get_flatpaks = False
currently_uninstalling = False
is_result = False
is_empty = False
total_selected = 0
def filter_func(self, row):
if (self.search_entry.get_text().lower() in row.get_title().lower()) or (
self.search_entry.get_text().lower() in row.get_subtitle().lower()
):
self.is_result = True
return True
def uninstall_buttons_enable(self, should_enable):
if self.currently_uninstalling:
return
self.refresh_button.set_sensitive(should_enable)
if not should_enable:
self.batch_uninstall_button.set_sensitive(False)
def uninstall_flatpak_callback(self, _a, _b):
self.currently_uninstalling = False
self.refresh_list_of_flatpaks(_a, False)
self.main_toolbar_view.set_sensitive(True)
self.disconnect(self.no_close)
self.uninstall_buttons_enable(True)
self.main_stack.set_visible_child(self.main_box)
self.search_button.set_sensitive(True)
if self.my_utils.uninstall_success:
self.refresh_list_of_flatpaks(self, False)
self.toast_overlay.add_toast(Adw.Toast.new(_("Uninstalled successfully")))
else:
self.toast_overlay.add_toast(
Adw.Toast.new(_("Could not uninstall some apps"))
)
def uninstall_flatpak_thread(self, ref_arr, id_arr, type_arr, should_trash):
self.my_utils.uninstall_flatpak(
ref_arr, type_arr, should_trash, self.main_progress_bar
)
def uninstall_flatpak(self, should_trash):
ref_arr = []
id_arr = []
type_arr = []
self.currently_uninstalling = True
i = 0
while self.flatpaks_list_box.get_row_at_index(i) != None:
current = self.flatpaks_list_box.get_row_at_index(i)
if current.tickbox.get_active() == True:
ref_arr.append(current.app_ref)
id_arr.append(current.app_id)
type_arr.append(current.install_type)
i += 1
self.set_title(self.main_window_title)
task = Gio.Task.new(None, None, self.uninstall_flatpak_callback)
task.run_in_thread(
lambda _task, _obj, _data, _cancellable, ref_arr=ref_arr, id_arr=id_arr, type_arr=type_arr, should_trash=should_trash: self.uninstall_flatpak_thread(
ref_arr, id_arr, type_arr, should_trash
)
)
def batch_uninstall_button_handler(self, _widget):
has_user_data = False
def batch_uninstall_response(_idk, response_id, _widget):
if response_id == "cancel":
return 1
try:
should_trash = trash_check.get_active()
except:
should_trash = False
self.uninstall_buttons_enable(False)
self.no_close = self.connect(
"close-request", lambda event: True
) # Make window unable to close
self.main_stack.set_visible_child(self.uninstalling)
self.search_button.set_sensitive(False)
self.uninstall_flatpak(should_trash)
# Create Widgets
dialog = Adw.MessageDialog.new(
self,
_("Uninstall Selected Apps?"),
_("It will not be possible to use these apps after removal."),
)
# Check to see if at least one app in the list has user data
i = 0
while True:
current = self.flatpaks_list_box.get_row_at_index(i)
i += 1
if current == None:
break
if current.tickbox.get_active() and os.path.exists(
f"{self.user_data_path}{current.app_id}"
):
has_user_data = True
break
if has_user_data:
# Create Widgets
options_box = Gtk.Box(orientation="vertical")
header = Gtk.Label(
label=_("App Settings & Data"), halign="start", margin_top=10
)
options_list = Gtk.ListBox(selection_mode="none", margin_top=15)
keep_data = Adw.ActionRow(
title=_("Keep"),
subtitle=_("Allow restoring these apps' settings and content"),
)
trash_data = Adw.ActionRow(
title=_("Trash"),
subtitle=_("Send these apps' settings and content to the trash"),
)
keep_check = Gtk.CheckButton()
trash_check = Gtk.CheckButton()
# Apply Widgets
keep_data.add_prefix(keep_check)
keep_data.set_activatable_widget(keep_check)
trash_data.add_prefix(trash_check)
trash_data.set_activatable_widget(trash_check)
keep_check.set_group(trash_check)
options_list.append(keep_data)
options_list.append(trash_data)
options_box.append(header)
options_box.append(options_list)
dialog.set_extra_child(options_box)
# Calls
keep_check.set_active(True)
options_list.add_css_class("boxed-list")
header.add_css_class("heading")
header.add_css_class("h4")
# Connections
dialog.connect("response", batch_uninstall_response, dialog.choose_finish)
# Calls
dialog.set_close_response("cancel")
dialog.add_response("cancel", _("Cancel"))
dialog.add_response("continue", _("Uninstall"))
dialog.set_response_appearance("continue", Adw.ResponseAppearance.DESTRUCTIVE)
Gtk.Window.present(dialog)
def uninstall_button_handler(self, row, name, ref, id):
if self.currently_uninstalling:
self.toast_overlay.add_toast(
Adw.Toast.new(_("Cannot uninstall while already uninstalling"))
)
return
def uninstall_response(_idk, response_id, _widget):
if response_id == "cancel":
return 1
try:
should_trash = trash_check.get_active()
except:
should_trash = False
if response_id == "purge":
should_trash = True
self.uninstall_buttons_enable(False)
self.no_close = self.connect(
"close-request", lambda event: True
) # Make window unable to close
self.main_stack.set_visible_child(self.uninstalling)
self.search_button.set_sensitive(False)
self.uninstall_flatpak(should_trash)
row.tickbox.set_active(True)
# Create Widgets
dialog = Adw.MessageDialog.new(
self,
_("Uninstall {}?").format(name),
_("It will not be possible to use {} after removal.").format(name),
)
if os.path.exists(f"{self.user_data_path}{id}"):
# Create Widgets for Trash
options_box = Gtk.Box(orientation="vertical")
header = Gtk.Label(
label=_("App Settings & Data"), halign="start", margin_top=10
)
options_list = Gtk.ListBox(selection_mode="none", margin_top=15)
keep_data = Adw.ActionRow(
title=_("Keep"),
subtitle=_("Allow restoring this app's settings and content"),
)
trash_data = Adw.ActionRow(
title=_("Trash"),
subtitle=_("Send this app's settings and content to the trash"),
)
keep_check = Gtk.CheckButton(active=True)
trash_check = Gtk.CheckButton()
# Apply Widgets for Trash
keep_data.add_prefix(keep_check)
keep_data.set_activatable_widget(keep_check)
trash_data.add_prefix(trash_check)
trash_data.set_activatable_widget(trash_check)
keep_check.set_group(trash_check)
dialog.set_extra_child(options_box)
options_list.append(keep_data)
options_list.append(trash_data)
options_box.append(header)
options_box.append(options_list)
options_list.add_css_class("boxed-list")
dialog.set_close_response("cancel")
dialog.add_response("cancel", _("Cancel"))
dialog.add_response("continue", _("Uninstall"))
dialog.set_response_appearance("continue", Adw.ResponseAppearance.DESTRUCTIVE)
dialog.connect("response", uninstall_response, dialog.choose_finish)
Gtk.Window.present(dialog)
def window_set_empty(self, is_empty):
self.batch_mode_button.set_sensitive(not is_empty)
self.search_button.set_sensitive(not is_empty)
self.filter_button.set_sensitive(not is_empty)
self.is_empty = is_empty
if is_empty:
self.batch_mode_button.set_active(False)
self.main_stack.set_visible_child(self.no_flatpaks)
self.search_button.set_sensitive(False)
else:
self.main_stack.set_visible_child(self.main_box)
self.search_button.set_sensitive(True)
def create_row(self, index):
row = AppRow(self, self.host_flatpaks, index)
self.flatpaks_list_box.insert(row, index)
def generate_list_of_flatpaks(self):
self.host_flatpaks = self.my_utils.get_host_flatpaks()
self.dependent_runtimes = self.my_utils.get_dependent_runtimes()
self.set_title(self.main_window_title)
self.eol_list = []
self.system_mask_list = self.my_utils.get_host_masks("system")
self.user_mask_list = self.my_utils.get_host_masks("user")
for index in range(len(self.host_flatpaks)):
try:
if "eol" in self.host_flatpaks[index][12]:
self.eol_list.append(self.host_flatpaks[index][8])
except:
print("Could not find EOL")
for index in range(len(self.host_flatpaks)):
self.create_row(index)
# self.windowSetEmpty(not self.flatpaks_list_box.get_row_at_index(0))
self.apply_filter()
self.batch_actions_enable(False)
self.main_stack.set_visible_child(self.main_box)
def refresh_list_of_flatpaks(self, widget, should_toast):
if self.currently_uninstalling:
return
if should_toast:
self.toast_overlay.add_toast(Adw.Toast.new(_("List refreshed")))
self.flatpaks_list_box.remove_all()
self.generate_list_of_flatpaks()
self.batch_mode_button.set_active(False)
def apply_filter(self):
settings = Gio.Settings.new("io.github.flattool.Warehouse.filter")
show_apps = settings.get_boolean("show-apps")
show_runtimes = settings.get_boolean("show-runtimes")
remotes_list = settings.get_string("remotes-list").split(",")
runtimes_list = settings.get_string("runtimes-list").split(",")
i = 0
while self.flatpaks_list_box.get_row_at_index(i) != None:
current = self.flatpaks_list_box.get_row_at_index(i)
id = current.app_id
dependant = current.dependent_runtime
remote = current.origin_remote
is_runtime = current.is_runtime
visible = True
if (not show_apps) and (not is_runtime):
visible = False
if (not show_runtimes) and is_runtime:
visible = False
if (not "all" in remotes_list) and (not remote in remotes_list):
visible = False
if (not "all" in runtimes_list) and (not dependant in runtimes_list):
visible = False
current.set_is_visible(visible)
i += 1
def open_data_folder(self, path):
try:
Gio.AppInfo.launch_default_for_uri(f"file://{path}", None)
except GLib.GError:
self.toast_overlay.add_toast(Adw.Toast.new(_("Could not open folder")))
def trash_data(self, name, id, index):
def on_continue(dialog, response):
if response == "cancel":
return
result = self.my_utils.trash_folder(f"{self.user_data_path}{id}")
if result != 0:
self.toast_overlay.add_toast(
Adw.Toast.new(_("Could not trash user data"))
)
return
self.lookup_action(f"open-data{index}").set_enabled(False)
self.lookup_action(f"trash{index}").set_enabled(False)
self.toast_overlay.add_toast(Adw.Toast.new(_("Trashed user data")))
dialog = Adw.MessageDialog.new(
self, _("Send {}'s User Data to the Trash?").format(name)
)
dialog.set_body(
_("Your files and data for this app will be sent to the trash.")
)
dialog.add_response("cancel", _("Cancel"))
dialog.set_close_response("cancel")
dialog.add_response("continue", _("Trash Data"))
dialog.set_response_appearance("continue", Adw.ResponseAppearance.DESTRUCTIVE)
dialog.connect("response", on_continue)
dialog.present()
def mask_flatpak(self, row):
is_masked = (
row.mask_label.get_visible()
) # Check the visibility of the mask label to see if the flatpak is masked
result = []
def callback():
if result[0] == 1:
self.toast_overlay.add_toast(
Adw.Toast.new(
_("Could not disable updates for {}").format(row.app_name)
)
)
return
row.set_masked(not is_masked)
self.lookup_action(f"mask{row.index}").set_enabled(is_masked)
self.lookup_action(f"unmask{row.index}").set_enabled(not is_masked)
def on_continue(dialog, response):
if response == "cancel":
return
task = Gio.Task.new(None, None, lambda *_: callback())
task.run_in_thread(
lambda *_: result.append(
self.my_utils.mask_flatpak(row.app_id, row.install_type, is_masked)
)
)
if is_masked:
on_continue(self, None)
else:
dialog = Adw.MessageDialog.new(
self, _("Disable Updates for {}?").format(row.app_name)
)
dialog.set_body(
_(
"This will mask {} ensuring it will never recieve any feature or security updates."
).format(row.app_name)
)
dialog.add_response("cancel", _("Cancel"))
dialog.set_close_response("cancel")
dialog.add_response("continue", _("Disable Updates"))
dialog.connect("response", on_continue)
dialog.present()
def copy_item(self, to_copy, to_toast=None):
self.clipboard.set(to_copy)
if to_toast:
self.toast_overlay.add_toast(Adw.Toast.new(to_toast))
def run_callback(self, _a, _b):
if not self.my_utils.run_app_error:
return
error = self.my_utils.run_app_error_message
dialog = Adw.MessageDialog.new(self, _("Could not Run App"), error)
copy_button = Gtk.Button(
label=_("Copy"), halign=Gtk.Align.CENTER, margin_top=12
)
copy_button.add_css_class("pill")
copy_button.add_css_class("suggested-action")
copy_button.connect("clicked", lambda *_: self.clipboard.set(error))
dialog.set_extra_child(copy_button)
dialog.add_response("ok", _("OK"))
dialog.set_close_response("ok")
dialog.present()
def run_app_thread(self, ref, to_toast=None):
self.run_app_error = False
task = Gio.Task.new(None, None, self.run_callback)
task.run_in_thread(lambda *_: self.my_utils.run_app(ref))
if to_toast:
self.toast_overlay.add_toast(Adw.Toast.new(to_toast))
def batch_mode_handler(self, widget):
batch_mode = widget.get_active()
i = 0
while self.flatpaks_list_box.get_row_at_index(i) != None:
current = self.flatpaks_list_box.get_row_at_index(i)
current.set_selectable(batch_mode)
i += 1
self.in_batch_mode = batch_mode
self.batch_mode_bar.set_revealed(batch_mode)
if not widget.get_active():
self.batch_select_all_button.set_active(False)
def batch_key_handler(self, _b, event, _c, _d):
if event == Gdk.KEY_Escape:
self.batch_mode_button.set_active(False)
def batch_actions_enable(self, should_enable):
self.batch_copy_button.set_sensitive(should_enable)
self.batch_clean_button.set_sensitive(should_enable)
self.batch_snapshot_button.set_sensitive(should_enable)
if not self.currently_uninstalling:
self.batch_uninstall_button.set_sensitive(should_enable)
def on_batch_clean_response(self, dialog, response, _a):
if response == "cancel":
return
i = 0
trashReturnCodes = 0
while True:
current = self.flatpaks_list_box.get_row_at_index(i)
i += 1
if current == None:
break
if current.tickbox.get_active() == False:
continue
trash = self.my_utils.trash_folder(f"{self.user_data_path}{current.app_id}")
if trash == 1:
self.toast_overlay.add_toast(
Adw.Toast.new(_("{} has no data to trash").format(current.app_name))
)
continue
if trash == 2:
self.toast_overlay.add_toast(
Adw.Toast.new(
_("Could not trash {}'s data").format(current.app_name)
)
)
continue
self.lookup_action(f"open-data{current.index}").set_enabled(
False
) # Disable the Open User Data dropdown option when the data was deleted
self.lookup_action(f"trash{current.index}").set_enabled(
False
) # Disable the Trash User Data dropdown option when the data was deleted
def batch_clean_handler(self, widget):
dialog = Adw.MessageDialog.new(
self,
_("Trash Selected Apps' User Data?"),
_("Your files and data for these apps will be sent to the trash."),
)
dialog.set_close_response("cancel")
dialog.add_response("cancel", _("Cancel"))
dialog.add_response("continue", _("Trash Data"))
dialog.set_response_appearance("continue", Adw.ResponseAppearance.DESTRUCTIVE)
dialog.connect("response", self.on_batch_clean_response, dialog.choose_finish)
Gtk.Window.present(dialog)
def select_all_handler(self, widget):
self.set_select_all(widget.get_active())
def batch_snapshot_handler(self, widget):
def batch_snapshot_response(dialog, response, _a):
if response == "cancel":
return
i = 0
snapshots_path = (
self.host_home
+ "/.var/app/io.github.flattool.Warehouse/data/Snapshots/"
)
snapshot_arr = []
app_ver_arr = []
app_data_arr = []
epoch = int(time.time())
self.no_close = self.connect(
"close-request", lambda event: True
) # Make window unable to close
while self.flatpaks_list_box.get_row_at_index(i) != None:
current = self.flatpaks_list_box.get_row_at_index(i)
i += 1
if current.tickbox.get_active() == False:
continue
if not os.path.exists(f"{self.user_data_path}{current.app_id}"):
continue
snapshot_arr.append(snapshots_path + current.app_id + "/")
app_ver_arr.append(current.app_version)
app_data_arr.append(f"{self.user_data_path}{current.app_id}")
def thread():
capture = self.my_utils.snapshot_apps(
epoch,
snapshot_arr,
app_ver_arr,
app_data_arr,
self.main_progress_bar,
)
if capture != 0:
GLib.idle_add(
lambda *_: self.toast_overlay.add_toast(
Adw.Toast.new(_("Could not snapshot some apps"))
)
)
def callback(*args):
self.main_stack.set_visible_child(self.main_box)
self.disconnect(self.no_close)
self.batch_select_all_button.set_active(True)
self.set_select_all(True)
self.search_button.set_sensitive(True)
self.batch_mode_button.set_sensitive(True)
self.batch_mode_bar.set_revealed(True)
self.batch_select_all_button.set_active(False)
self.set_select_all(False)
self.search_button.set_sensitive(False)
self.batch_mode_button.set_sensitive(False)
self.batch_mode_bar.set_revealed(False)
self.main_stack.set_visible_child(self.snapshotting)
task = Gio.Task.new(None, None, callback)
task.run_in_thread(lambda *_: thread())
dialog = Adw.MessageDialog.new(
self,
_("Create Snapshots?"),
_(
"Snapshots are backups of the app's user data. They can be reapplied at any time. This could take a while."
),
)
dialog.set_close_response("cancel")
dialog.add_response("cancel", _("Cancel"))
dialog.add_response("continue", _("Create Snapshots"))
dialog.connect("response", batch_snapshot_response, dialog.choose_finish)
Gtk.Window.present(dialog)
def set_select_all(self, should_select_all):
i = 0
while self.flatpaks_list_box.get_row_at_index(i) != None:
current = self.flatpaks_list_box.get_row_at_index(i)
if current.get_visible() == True:
current.tickbox.set_active(should_select_all)
i += 1
def row_select_handler(self, tickbox):
if tickbox.get_active() == True:
self.total_selected += 1
else:
self.total_selected -= 1
if self.total_selected == 0:
buttons_enable = False
self.set_title(self.main_window_title)
self.batch_actions_enable(False)
else:
self.set_title(f"{self.total_selected} Selected")
self.batch_actions_enable(True)
def create_action(self, name, callback, shortcuts=None):
"""Add a window action.
Args:
name: the name of the action
callback: the function to be called when the action is
activated
shortcuts: an optional list of accelerators
"""
action = Gio.SimpleAction.new(name, None)
action.connect("activate", callback)
self.add_action(action)
if shortcuts:
self.set_accels_for_action(f"app.{name}", shortcuts)
def copy_names(self, widget, _a):
to_copy = ""
i = 0
while True:
current = self.flatpaks_list_box.get_row_at_index(i)
i += 1
if current == None:
break
if current.tickbox.get_active():
to_copy += f"{current.app_name}\n"
self.clipboard.set(to_copy)
self.toast_overlay.add_toast(Adw.Toast.new(_("Copied selected app names")))
def copy_IDs(self, widget, _a):
to_copy = ""
i = 0
while True:
current = self.flatpaks_list_box.get_row_at_index(i)
i += 1
if current == None:
break
if current.tickbox.get_active():
to_copy += f"{current.app_id}\n"
self.clipboard.set(to_copy)
self.toast_overlay.add_toast(Adw.Toast.new(_("Copied selected app IDs")))
def copy_refs(self, widget, _a):
to_copy = ""
i = 0
while True:
current = self.flatpaks_list_box.get_row_at_index(i)
i += 1
if current == None:
break
if current.tickbox.get_active():
to_copy += f"{current.app_ref}\n"
self.clipboard.set(to_copy)
self.toast_overlay.add_toast(Adw.Toast.new(_("Copied selected app refs")))
def install_callback(self, _a, _b):
self.main_stack.set_visible_child(self.main_box)
self.search_button.set_sensitive(True)
if self.my_utils.install_success:
self.refresh_list_of_flatpaks(self, False)
self.toast_overlay.add_toast(Adw.Toast.new(_("Installed successfully")))
else:
self.toast_overlay.add_toast(Adw.Toast.new(_("Could not install app")))
def install_thread(self, filepath, user_or_system):
self.my_utils.install_flatpak(
[filepath], None, user_or_system, self.main_progress_bar
)
def install_file(self, filepath):
def response(dialog, response, _a):
if response == "cancel":
return
self.main_stack.set_visible_child(self.installing)
self.search_button.set_sensitive(False)
user_or_system = "user"
if system_check.get_active():
user_or_system = "system"
task = Gio.Task.new(None, None, self.install_callback)
task.run_in_thread(lambda *_: self.install_thread(filepath, user_or_system))
name = filepath.split("/")
name = name[len(name) - 1]
dialog = Adw.MessageDialog.new(self, _("Install {}?").format(name))
dialog.set_close_response("cancel")
dialog.add_response("cancel", _("Cancel"))
dialog.add_response("continue", _("Install"))
dialog.set_response_appearance("continue", Adw.ResponseAppearance.SUGGESTED)
dialog.connect("response", response, dialog.choose_finish)
# Create Widgets
options_box = Gtk.Box(orientation="vertical")
options_list = Gtk.ListBox(selection_mode="none", margin_top=15)
user_row = Adw.ActionRow(
title=_("User"), subtitle=_("The app will be available to only you")
)
system_row = Adw.ActionRow(
title=_("System"),
subtitle=_("The app will be available to every user on the system"),
)
user_check = Gtk.CheckButton()
system_check = Gtk.CheckButton()
# Apply Widgets
user_row.add_prefix(user_check)
user_row.set_activatable_widget(user_check)
system_row.add_prefix(system_check)
system_row.set_activatable_widget(system_check)
user_check.set_group(system_check)
options_list.append(user_row)
options_list.append(system_row)
options_box.append(options_list)
dialog.set_extra_child(options_box)
# Calls
user_check.set_active(True)
options_list.add_css_class("boxed-list")
Gtk.Window.present(dialog)
def drop_callback(self, target, _x, _y, _data):
filepath = target.get_value().get_path()
if filepath.endswith(".flatpak") or filepath.endswith(".flatpakref"):
self.install_file(filepath)
elif filepath.endswith(".flatpakrepo"):
remotes_window = RemotesWindow(self)
remotes_window.present()
remotes_window.add_remote_file(filepath)
else:
self.toast_overlay.add_toast(Adw.Toast.new(_("File type not supported")))
def on_invalidate(self, row):
if self.is_empty:
self.batch_mode_button.set_active(False)
self.main_stack.set_visible_child(self.no_flatpaks)
self.search_button.set_sensitive(False)
else:
self.main_stack.set_visible_child(self.main_box)
self.search_button.set_sensitive(True)
self.is_result = False
self.flatpaks_list_box.invalidate_filter()
if self.is_result == False:
self.main_stack.set_visible_child(self.no_results)
self.search_button.set_sensitive(False)
def on_change(self, prop, prop2):
if self.search_bar.get_search_mode() == False:
if self.is_empty:
self.batch_mode_button.set_active(False)
self.main_stack.set_visible_child(self.no_flatpaks)
self.search_button.set_sensitive(False)
else:
self.main_stack.set_visible_child(self.main_box)
self.search_button.set_sensitive(True)
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.my_utils = myUtils(self)
self.set_size_request(0, 230)
self.settings = Gio.Settings.new("io.github.flattool.Warehouse")
self.settings.bind(
"window-width", self, "default-width", Gio.SettingsBindFlags.DEFAULT
)
self.settings.bind(
"window-height", self, "default-height", Gio.SettingsBindFlags.DEFAULT
)
self.settings.bind(
"is-maximized", self, "maximized", Gio.SettingsBindFlags.DEFAULT
)
self.settings.bind(
"is-fullscreen", self, "fullscreened", Gio.SettingsBindFlags.DEFAULT
)
other = Gio.Settings.new("io.github.flattool.Warehouse.filter")
print(other.get_boolean("show-apps"))
self.new_env = dict(os.environ)
self.new_env["LC_ALL"] = "C"
if self.host_flatpaks == [["", ""]]:
self.window_set_empty(True)
return
self.flatpaks_list_box.set_filter_func(self.filter_func)
task = Gio.Task()
task.run_in_thread(
lambda *_: GLib.idle_add(lambda *_: self.generate_list_of_flatpaks())
)
self.search_entry.connect("search-changed", self.on_invalidate)
self.search_bar.connect_entry(self.search_entry)
self.search_bar.connect("notify", self.on_change)
self.refresh_button.connect("clicked", self.refresh_list_of_flatpaks, True)
self.filter_button.connect("clicked", lambda *_: FilterWindow(self))
self.batch_mode_button.connect("toggled", self.batch_mode_handler)
self.batch_clean_button.connect("clicked", self.batch_clean_handler)
self.batch_uninstall_button.connect(
"clicked", self.batch_uninstall_button_handler
)
self.batch_select_all_button.connect("clicked", self.select_all_handler)
self.batch_snapshot_button.connect("clicked", self.batch_snapshot_handler)
self.batch_actions_enable(False)
event_controller = Gtk.EventControllerKey()
event_controller.connect("key-pressed", self.batch_key_handler)
self.add_controller(event_controller)
self.main_overlay.add_overlay(self.main_progress_bar)
self.create_action("copy-names", self.copy_names)
self.create_action("copy-ids", self.copy_IDs)
self.create_action("copy-refs", self.copy_refs)
file_drop = Gtk.DropTarget.new(Gio.File, Gdk.DragAction.COPY)
file_drop.connect("drop", self.drop_callback)
self.scrolled_window.add_controller(file_drop)
if Config.DEVEL:
self.add_css_class("devel")