#!/usr/bin/env python3
#
# Pardus Flatpak GUI main window module
# Copyright (C) 2020 Erdem Ersoy
#
# 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, either version 3 of the License, or
# (at your option) any later version.
#
# 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 .
from pardusflatpakgui.infowindow import InfoWindow
from pardusflatpakgui.installwindow import InstallWindow
from pardusflatpakgui.uninstallwindow import UninstallWindow
from pardusflatpakgui.updateallwindow import UpdateAllWindow
from pardusflatpakgui.version import Version
import gettext
import locale
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('GLib', '2.0')
gi.require_version('Flatpak', '1.0')
gi.require_version('Gio', '2.0')
from gi.repository import Gtk, GLib, Flatpak, Gio
locale.setlocale(locale.LC_ALL, "")
gettext.bindtextdomain("pardus-flatpak-gui", "/usr/share/locale/")
gettext.textdomain("pardus-flatpak-gui")
_ = gettext.gettext
gettext.install("pardus-flatpak-gui", "/usr/share/locale/")
class MainWindow(object):
def __init__(self, application):
self.Application = application
try:
main_gui_file = "/usr/share/pardus/pardus-flatpak-gui/ui/mainwindow.glade"
main_builder = Gtk.Builder.new_from_file(main_gui_file)
main_builder.connect_signals(self)
except GLib.GError:
print(_("Error reading GUI file: ") + main_gui_file)
raise
try:
about_gui_file = "/usr/share/pardus/pardus-flatpak-gui/ui/aboutdialog.glade"
about_builder = Gtk.Builder.new_from_file(about_gui_file)
about_builder.connect_signals(self)
except GLib.GError:
print(_("Error reading About dialog GUI file: ") + about_gui_file)
raise
try:
messages_gui_file = "/usr/share/pardus/pardus-flatpak-gui/ui/messagedialogs.glade"
messages_builder = Gtk.Builder.new_from_file(messages_gui_file)
messages_builder.connect_signals(self)
except GLib.GError:
print(_("Error reading message dialogs GUI file: ") +
messages_gui_file)
raise
self.FlatpakInstallation = Flatpak.Installation.new_system()
self.InstalledRefsList = self.FlatpakInstallation.list_installed_refs()
self.FlatHubRefsList = self.FlatpakInstallation.list_remote_refs_sync(
"flathub", Gio.Cancellable.new())
self.NonInstalledRefsList = []
for item in self.FlatHubRefsList:
self.NonInstalledRefsList.append(item)
for item_2 in self.InstalledRefsList:
if item.get_name() == item_2.get_name() and \
item.get_arch() == item_2.get_arch() and \
item.get_branch() == item_2.get_branch():
if len(self.NonInstalledRefsList) != 0:
self.NonInstalledRefsList.pop(len(self.NonInstalledRefsList) - 1)
else:
self.NonInstalledRefsList = []
self.AllRefsList = self.InstalledRefsList + self.NonInstalledRefsList
self.ListStoreMain = main_builder.get_object("ListStoreMain")
self.MessageDialogError = messages_builder.get_object("MessageDialogError")
# Debug print()'s:
# print("self.FlatHubRefsList:", len(self.FlatHubRefsList))
# print("self.InstalledRefsList:", len(self.InstalledRefsList))
# print("self.NonInstalledRefsList:", len(self.NonInstalledRefsList))
# print("self.AllRefsList:", len(self.AllRefsList))
for item in self.AllRefsList:
if item.get_kind() == Flatpak.RefKind.APP and \
item.get_arch() == Flatpak.get_default_arch():
if isinstance(item, Flatpak.RemoteRef):
item_is_installed = False
elif isinstance(item, Flatpak.InstalledRef):
item_is_installed = True
else:
item_is_installed = None
real_name = item.get_name()
arch = item.get_arch()
branch = item.get_branch()
installed_size = item.get_installed_size()
installed_size_mib = installed_size / 1048576
installed_size_mib_str = f"{installed_size_mib:.2f}" + " MiB"
if not item_is_installed:
if item in self.FlatHubRefsList:
remote_name = "flathub"
else:
remote_name = ""
download_size = item.get_download_size()
download_size_mib = download_size / 1048576
download_size_mib_str = f"{download_size_mib:.2f}" + " MiB"
name = ""
elif item_is_installed:
remote_name = item.get_origin()
download_size_mib_str = ""
name = item.get_appdata_name()
else:
remote_name = ""
download_size_mib_str = ""
name = ""
self.MessageDialogError.set_markup(
_("Invalid Flatpak Reference Error"))
self.MessageDialogError.format_secondary_text(
_("Invalid Flatpak reference is: ") + "app/" + real_name + "/" + arch + "/" + branch)
self.MessageDialogError.run()
self.MessageDialogError.hide()
if item_is_installed is not None:
self.ListStoreMain.append([real_name,
arch,
branch,
remote_name,
installed_size_mib_str,
download_size_mib_str,
name])
else:
continue
self.HeaderBarMain = main_builder.get_object("HeaderBarMain")
self.HeaderBarMain.set_title(_("Pardus Flatpak GUI"))
self.HeaderBarMain.set_subtitle(_("Manage Flatpak softwares via GUI on Pardus"))
self.RunMenuItem = main_builder.get_object("RunMenuItem")
self.RunMenuItem.set_label(_("_Run"))
self.InfoMenuItem = main_builder.get_object("InfoMenuItem")
self.InfoMenuItem.set_label(_("_Info"))
self.UninstallMenuItem = main_builder.get_object("UninstallMenuItem")
self.UninstallMenuItem.set_label(_("_Uninstall"))
self.InstallMenuItem = main_builder.get_object("InstallMenuItem")
self.InstallMenuItem.set_label(_("I_nstall"))
self.ActionsMenu = main_builder.get_object("ActionsMenu")
self.UpdateAllMenuItem = main_builder.get_object("UpdateAllMenuItem")
self.UpdateAllMenuItem.set_label(_("_Update All"))
self.AboutMenuItem = main_builder.get_object("AboutMenuItem")
self.AboutMenuItem.set_label(_("_About"))
self.TreeViewColumnRealName = main_builder.get_object(
"TreeViewColumnRealName")
self.TreeViewColumnRealName.set_title(_("Real Name"))
self.TreeViewColumnArch = main_builder.get_object("TreeViewColumnArch")
self.TreeViewColumnArch.set_title(_("Arch"))
self.TreeViewColumnBranch = main_builder.get_object("TreeViewColumnBranch")
self.TreeViewColumnBranch.set_title(_("Branch"))
self.TreeViewColumnRemoteName = main_builder.get_object(
"TreeViewColumnRemoteName")
self.TreeViewColumnRemoteName.set_title(_("Remote Name"))
self.TreeViewColumnInstalledSize = main_builder.get_object(
"TreeViewColumnInstalledSize")
self.TreeViewColumnInstalledSize.set_title(_("Installed Size"))
self.TreeViewColumnDownloadSize = main_builder.get_object(
"TreeViewColumnDownloadSize")
self.TreeViewColumnDownloadSize.set_title(_("Download Size"))
self.TreeViewColumnName = main_builder.get_object("TreeViewColumnName")
self.TreeViewColumnName.set_title(_("Name"))
self.TreeViewMain = main_builder.get_object("TreeViewMain")
self.SearchEntryMain = main_builder.get_object("SearchEntryMain")
self.SearchEntryMain.set_placeholder_text(_("Click here for search"))
self.SearchFilter = main_builder.get_object("SearchFilter")
self.SearchFilter.set_visible_func(self.search_filter_function)
self.SortModel = main_builder.get_object("SortModel")
self.SortModel.set_sort_func(0, self.sorting_compare_function, (self.TreeViewColumnRealName, 0))
self.SortModel.set_sort_func(1, self.sorting_compare_function, (self.TreeViewColumnArch, 1))
self.SortModel.set_sort_func(2, self.sorting_compare_function, (self.TreeViewColumnBranch, 2))
self.SortModel.set_sort_func(3, self.sorting_compare_function, (self.TreeViewColumnRemoteName, 3))
self.SortModel.set_sort_func(4, self.sorting_float_compare_function, (self.TreeViewColumnInstalledSize, 4))
self.SortModel.set_sort_func(5, self.sorting_float_compare_function, (self.TreeViewColumnDownloadSize, 5))
self.SortModel.set_sort_func(6, self.sorting_compare_function, (self.TreeViewColumnName, 6))
self.HeaderBarShowButton = main_builder.get_object("HeaderBarShowButton")
self.HeaderBarShowButton.set_label(_("Show Installed Apps"))
self.AboutDialog = about_builder.get_object("AboutDialog")
self.AboutDialog.set_comments(_("Flatpak GUI for Pardus"))
self.AboutDialog.set_copyright(_("Copyright (C) 2020 Erdem Ersoy"))
self.AboutDialog.set_program_name(_("Pardus Flatpak GUI"))
self.AboutDialog.set_version(Version.getVersion())
self.AboutDialog.set_website_label(_("Pardus Flatpak GUI Web Site"))
self.MainWindow = main_builder.get_object("MainWindow")
self.MainWindow.set_application(application)
self.MainWindow.show()
def search_filter_function(self, model, iteration, data):
search_entry_text = self.SearchEntryMain.get_text()
real_name = model[iteration][0]
name = model[iteration][6]
# If a reference is installed
if model[iteration][5] == "":
is_installed = True
else:
is_installed = False
if len(search_entry_text) == 0 and not self.HeaderBarShowButton.get_active():
return True
if len(search_entry_text) == 0 and self.HeaderBarShowButton.get_active():
if UpdateAllWindow.at_updating:
return True
else:
return is_installed
elif (real_name.lower().count(search_entry_text.lower()) > 0 or name.lower().count(
search_entry_text.lower()) > 0) and not self.HeaderBarShowButton.get_active():
return True
elif (real_name.lower().count(search_entry_text.lower()) > 0 or name.lower().count(
search_entry_text.lower()) > 0) and self.HeaderBarShowButton.get_active():
if UpdateAllWindow.at_updating:
return True
else:
return is_installed
else:
return False
def sorting_compare_function(self, tree_model_filter, row1, row2, data):
sorting_column, id_number = data
value1 = tree_model_filter.get_value(row1, id_number)
value2 = tree_model_filter.get_value(row2, id_number)
if value1 == "" and value2 == "":
return 0
elif value1 == "" and value2 != "":
return -1
elif value1 != "" and value2 == "":
return 1
if value1 < value2:
return -1
elif value1 == value2:
return 0
else:
return 1
def sorting_float_compare_function(self, tree_model_filter, row1, row2, data):
sorting_column, id_number = data
value1 = tree_model_filter.get_value(row1, id_number)[:-4]
value2 = tree_model_filter.get_value(row2, id_number)[:-4]
if value1 == "" and value2 == "":
return 0
elif value1 == "" and value2 != "":
return -1
elif value1 != "" and value2 == "":
return 1
value1_float = float(value1)
value2_float = float(value2)
if value1_float < value2_float:
return -1
elif value1_float == value2_float:
return 0
else:
return 1
def on_delete_main_window(self, widget, event):
widget.hide_on_delete()
def on_columns_changed(self, tree_view): # FIXME: Remove
selection = tree_view.get_selection()
tree_model, tree_iter = selection.get_selected()
if tree_iter is None:
return None
# If the selected app is installed
if tree_model.get_value(tree_iter, 5) == "":
self.RunMenuItem.set_sensitive(True)
self.UninstallMenuItem.set_sensitive(True)
self.InstallMenuItem.set_sensitive(False)
# If the selected app is not installed
else:
self.RunMenuItem.set_sensitive(False)
self.UninstallMenuItem.set_sensitive(False)
self.InstallMenuItem.set_sensitive(True)
def on_selection_changed(self, tree_selection):
tree_model, tree_iter = tree_selection.get_selected()
if tree_iter is None:
return None
# If the selected app is installed
if tree_model.get_value(tree_iter, 5) == "":
self.RunMenuItem.set_sensitive(True)
self.UninstallMenuItem.set_sensitive(True)
self.InstallMenuItem.set_sensitive(False)
# If the selected app is not installed
else:
self.RunMenuItem.set_sensitive(False)
self.UninstallMenuItem.set_sensitive(False)
self.InstallMenuItem.set_sensitive(True)
def on_search_changed(self, search_entry):
self.SearchFilter.refilter()
def on_resorted(self, tree_sortable):
self.SearchFilter.refilter()
def on_press_show_button(self, toggle_button):
self.SearchFilter.refilter()
def on_show_actions_menu(self, tree_view, path, column):
self.ActionsMenu.popup_at_pointer(None)
def on_run(self, menu_item):
selection = self.TreeViewMain.get_selection()
tree_model, tree_iter = selection.get_selected()
if tree_iter is None:
self.MessageDialogError.set_markup(
_("Selection Error"))
self.MessageDialogError.format_secondary_text(
_("None of the applications are selected."))
self.MessageDialogError.run()
self.MessageDialogError.hide()
return None
real_name = tree_model.get_value(tree_iter, 0)
arch = tree_model.get_value(tree_iter, 1)
branch = tree_model.get_value(tree_iter, 2)
ref = Flatpak.Ref.parse("app/" + real_name + "/" +
arch + "/" + branch)
commit = ref.get_commit()
try:
success = self.FlatpakInstallation.launch(
real_name,
arch,
branch,
commit,
Gio.Cancellable.new())
except GLib.Error:
self.MessageDialogError.set_markup(
_("Running Error"))
self.MessageDialogError.format_secondary_text(
_("The selected application couldn't run."))
self.MessageDialogError.run()
self.MessageDialogError.hide()
else:
if success:
pass
else:
self.MessageDialogError.set_markup(
_("Running Error"))
self.MessageDialogError.format_secondary_text(
_("The selected application couldn't run."))
self.MessageDialogError.run()
self.MessageDialogError.hide()
def on_info(self, menu_item):
selection = self.TreeViewMain.get_selection()
tree_model, tree_iter = selection.get_selected()
if tree_iter is None:
self.MessageDialogError.set_markup(
_("Selection Error"))
self.MessageDialogError.format_secondary_text(
_("None of the applications are selected."))
self.MessageDialogError.run()
self.MessageDialogError.hide()
return None
self.InstalledRefsList = self.FlatpakInstallation.list_installed_refs()
self.FlatHubRefsList = self.FlatpakInstallation.list_remote_refs_sync(
"flathub", Gio.Cancellable.new())
self.NonInstalledRefsList = []
for item in self.FlatHubRefsList:
self.NonInstalledRefsList.append(item)
for item_2 in self.InstalledRefsList:
if item.get_name() == item_2.get_name() and \
item.get_arch() == item_2.get_arch() and \
item.get_branch() == item_2.get_branch():
if len(self.NonInstalledRefsList) != 0:
self.NonInstalledRefsList.pop(len(self.NonInstalledRefsList) - 1)
else:
self.NonInstalledRefsList = []
self.AllRefsList = self.InstalledRefsList + self.NonInstalledRefsList
real_name = tree_model.get_value(tree_iter, 0)
arch = tree_model.get_value(tree_iter, 1)
branch = tree_model.get_value(tree_iter, 2)
for item in self.AllRefsList:
if item.get_name() == real_name:
ref = item
break
if ref not in self.AllRefsList:
self.MessageDialogError.set_markup(
_("Invalid Flatpak Reference Error"))
self.MessageDialogError.format_secondary_text(
_("Invalid Flatpak reference is: ") + "app/" + real_name + "/" + arch + "/" + branch)
self.MessageDialogError.run()
self.MessageDialogError.hide()
return None
collection_id = ref.get_collection_id()
if collection_id is None:
collection_id = _("None")
commit = ref.get_commit()
if isinstance(ref, Flatpak.RemoteRef):
is_installed = False
elif isinstance(ref, Flatpak.InstalledRef):
is_installed = True
else:
is_installed = None
if is_installed:
app_license = ref.get_appdata_license()
if app_license is None:
app_license = _("None")
name = ref.get_appdata_name()
if name is None:
name = _("None")
summary = ref.get_appdata_summary()
if summary is None:
summary = _("None")
version = ref.get_appdata_version()
if version is None:
version = _("None")
deploy_dir = ref.get_deploy_dir()
if deploy_dir is None:
deploy_dir = _("None")
eol_reason = ref.get_eol()
if eol_reason is None:
eol_reason = _("None")
eol_rebased = ref.get_eol_rebase()
if eol_rebased is None:
eol_rebased = _("None")
installed_size = ref.get_installed_size()
installed_size_mib = installed_size / 1048576
installed_size_mib_as_string = f"{installed_size_mib:.2f}" + " MiB"
is_current = ref.get_is_current()
if is_current:
is_current_str = _("Yes")
else:
is_current_str = _("No")
latest_commit = ref.get_latest_commit()
if latest_commit is None:
latest_commit = _("None")
origin = ref.get_origin()
if origin is None:
origin = _("None")
sub_paths = ref.get_subpaths()
if sub_paths is None or not sub_paths:
sub_paths_str = _("None")
else:
sub_paths_str = ""
for item in sub_paths:
sub_paths_str = sub_paths_str + item + ", "
sub_paths_str = sub_paths_str[:-2]
info_str = _("Real Name: ") + real_name + "\n" + \
_("Arch: ") + arch + "\n" + \
_("Branch: ") + branch + "\n" + \
_("Collection ID: ") + collection_id + "\n" + \
_("Commit: ") + commit + "\n" + \
_("Is Installed: ") + _("Yes") + "\n" + \
_("License: ") + app_license + "\n" + \
_("Name: ") + name + "\n" + \
_("Summary: ") + summary + "\n" + \
_("Version: ") + version + "\n" + \
_("Deploy Dir: ") + deploy_dir + "\n" + \
_("EOL Reason: ") + eol_reason + "\n" + \
_("EOL Rebased: ") + eol_rebased + "\n" + \
_("Installed Size: ") + installed_size_mib_as_string + "\n" + \
_("Is Current: ") + is_current_str + "\n" + \
_("Latest Commit: ") + latest_commit + "\n" + \
_("Origin: ") + origin + "\n" + \
_("Subpaths: ") + sub_paths_str + "\n"
elif not is_installed:
download_size = ref.get_download_size()
download_size_mib = download_size / 1048576
download_size_mib_str = f"{download_size_mib:.2f}" + " MiB"
eol_reason = ref.get_eol()
if eol_reason is None:
eol_reason = _("None")
eol_rebased = ref.get_eol_rebase()
if eol_rebased is None:
eol_rebased = _("None")
installed_size = ref.get_installed_size()
installed_size_mib = installed_size / 1048576
installed_size_mib_as_string = f"{installed_size_mib:.2f}" + " MiB"
remote = ref.get_remote_name()
if remote is None:
remote = _("None")
info_str = _("Real Name: ") + real_name + "\n" + \
_("Arch: ") + arch + "\n" + \
_("Branch: ") + branch + "\n" + \
_("Collection ID: ") + collection_id + "\n" + \
_("Commit: ") + commit + "\n" + \
_("Is Installed: ") + _("Yes") + "\n" + \
_("Download Size: ") + download_size_mib_str + "\n" + \
_("EOL Reason: ") + eol_reason + "\n" + \
_("EOL Rebased: ") + eol_rebased + "\n" + \
_("Installed Size: ") + installed_size_mib_as_string + "\n" + \
_("Remote Name: ") + remote + "\n"
else:
self.MessageDialogError.set_markup(
_("Invalid Flatpak Reference Error"))
self.MessageDialogError.format_secondary_text(
_("Invalid Flatpak reference is: ") + "app/" + real_name + "/" + arch + "/" + branch)
self.MessageDialogError.run()
self.MessageDialogError.hide()
return None
InfoWindow(self.Application, info_str, ref)
def on_uninstall(self, menu_item):
if not self.HeaderBarShowButton.get_active():
button_not_pressed_already = True
elif self.HeaderBarShowButton.get_active():
button_not_pressed_already = False
self.HeaderBarShowButton.set_active(False)
self.SearchFilter.refilter()
selection = self.TreeViewMain.get_selection()
tree_model, tree_iter = selection.get_selected()
if tree_iter is None:
self.MessageDialogError.set_markup(
_("Selection Error"))
self.MessageDialogError.format_secondary_text(
_("None of the applications are selected."))
self.MessageDialogError.run()
self.MessageDialogError.hide()
return None
real_name = tree_model.get_value(tree_iter, 0)
arch = tree_model.get_value(tree_iter, 1)
branch = tree_model.get_value(tree_iter, 2)
UninstallWindow(self.Application, self.FlatpakInstallation, real_name,
arch, branch, tree_model, tree_iter, selection, self.SearchFilter, self.HeaderBarShowButton,
button_not_pressed_already)
def on_install(self, menu_item):
selection = self.TreeViewMain.get_selection()
tree_model, tree_iter = selection.get_selected()
if tree_iter is None:
self.MessageDialogError.set_markup(
_("Selection Error"))
self.MessageDialogError.format_secondary_text(
_("None of the applications are selected."))
self.MessageDialogError.run()
self.MessageDialogError.hide()
return None
real_name = tree_model.get_value(tree_iter, 0)
arch = tree_model.get_value(tree_iter, 1)
branch = tree_model.get_value(tree_iter, 2)
remote = tree_model.get_value(tree_iter, 3)
InstallWindow(self.Application, self.FlatpakInstallation, real_name, arch, branch,
remote, tree_model, tree_iter, selection, self.SearchFilter)
def on_update_all(self, menu_item):
UpdateAllWindow.at_updating = True
tree_model = self.TreeViewMain.get_model()
UpdateAllWindow(self.Application, self.FlatpakInstallation,
tree_model, self.HeaderBarShowButton)
def on_about(self, menu_item):
self.AboutDialog.run()
self.AboutDialog.hide()