From 43a7208cf36b358b617a82f70adaf0b71968f34a Mon Sep 17 00:00:00 2001 From: Christian Moser Date: Mon, 13 Jan 2025 20:13:03 +0100 Subject: [PATCH] added ManageSteamLibraries Dialog --- sgbackup/game.py | 2 +- sgbackup/gui/_app.py | 11 +- sgbackup/gui/_steam.py | 198 +++++++++++++++++++++++++++++++++ sgbackup/gui/appmenu.ui | 6 + sgbackup/gui/settingsdialog.py | 17 --- sgbackup/steam.py | 42 ++++--- 6 files changed, 243 insertions(+), 33 deletions(-) create mode 100644 sgbackup/gui/_steam.py delete mode 100644 sgbackup/gui/settingsdialog.py diff --git a/sgbackup/game.py b/sgbackup/game.py index 29e1b54..401ca21 100644 --- a/sgbackup/game.py +++ b/sgbackup/game.py @@ -1344,7 +1344,7 @@ class GameManager(GObject): return self.__games @Property(type=object) - def stam_games(self)->dict[int:Game]: + def steam_games(self)->dict[int:Game]: return self.__steam_games @Property(type=object) diff --git a/sgbackup/gui/_app.py b/sgbackup/gui/_app.py index f8a60b5..670ce1d 100644 --- a/sgbackup/gui/_app.py +++ b/sgbackup/gui/_app.py @@ -29,6 +29,7 @@ from ..settings import settings from ._settingsdialog import SettingsDialog from ._gamedialog import GameDialog from ..game import Game,GameManager +from ._steam import SteamLibrariesDialog __gtype_name__ = __name__ @@ -554,6 +555,10 @@ class Application(Gtk.Application): action_settings.connect('activate',self._on_action_settings) self.add_action(action_settings) + action_steam_manage_libraries = Gio.SimpleAction.new('steam-manage-libraries') + action_steam_manage_libraries.connect('activate',self._on_action_steam_manage_libraries) + self.add_action(action_steam_manage_libraries) + # add accels self.set_accels_for_action('app.quit',["q"]) @@ -596,7 +601,11 @@ class Application(Gtk.Application): dialog.connect('response', self._on_dialog_response_refresh, Gtk.ResponseType.APPLY) - dialog.present() + dialog.present() + + def _on_action_steam_manage_libraries(self,action,param): + dialog = SteamLibrariesDialog(self.appwindow) + dialog.present() def new_settings_dialog(self)->SettingsDialog: """ diff --git a/sgbackup/gui/_steam.py b/sgbackup/gui/_steam.py new file mode 100644 index 0000000..b04b0a5 --- /dev/null +++ b/sgbackup/gui/_steam.py @@ -0,0 +1,198 @@ +############################################################################### +# sgbackup - The SaveGame Backup tool # +# Copyright (C) 2024 Christian Moser # +# # +# 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 gi.repository import Gtk,Gio,GLib +from gi.repository.GObject import GObject,Property,Signal,BindingFlags + +from ..steam import Steam,SteamLibrary +import os + + +class SteamLibrariesDialog(Gtk.Dialog): + def __init__(self,parent:Gtk.Window|None=None): + Gtk.Dialog.__init__(self) + self.set_title("sgbackup: Steam Libraries") + self.set_default_size(620,480) + if parent is not None: + self.set_transient_for(parent) + + self.__steam = Steam() + + hbox = Gtk.Box.new(Gtk.Orientation.HORIZONTAL,2) + icon = Gtk.Image.new_from_icon_name('list-add-symbolic') + self.__add_lib_button=Gtk.Button() + self.__add_lib_button.set_child(icon) + self.__add_lib_button.connect('clicked',self._on_add_library_button_clicked) + hbox.append(self.__add_lib_button) + + self.__lib_editable = Gtk.EditableLabel() + self.__lib_editable.set_hexpand(True) + self.__lib_editable.connect('changed',self._on_add_library_label_changed) + self._on_add_library_label_changed(self.__lib_editable) + hbox.append(self.__lib_editable) + + icon = Gtk.Image.new_from_icon_name('document-open-symbolic') + self.__lib_chooser_button = Gtk.Button() + self.__lib_chooser_button.set_child(icon) + self.__lib_chooser_button.connect('clicked',self._on_choose_library_button_clicked) + hbox.append(self.__lib_chooser_button) + + self.get_content_area().append(hbox) + + frame = Gtk.Frame.new("Steam libraries") + scrolled = Gtk.ScrolledWindow() + + self.__listmodel = Gio.ListStore.new(SteamLibrary) + for lib in self.__steam.libraries: + self.__listmodel.append(lib) + + factory = Gtk.SignalListItemFactory() + factory.connect('setup',self._on_library_setup) + factory.connect('bind',self._on_library_bind) + + selection = Gtk.SingleSelection.new(self.__listmodel) + self.__listview = Gtk.ListView.new(selection,factory) + + scrolled.set_child(self.__listview) + scrolled.set_hexpand(True) + scrolled.set_vexpand(True) + frame.set_child(scrolled) + frame.set_hexpand(True) + frame.set_vexpand(True) + self.get_content_area().append(frame) + + self.add_button("Apply",Gtk.ResponseType.APPLY) + self.add_button("Cancel",Gtk.ResponseType.CANCEL) + + def _on_add_library_button_clicked(self,button): + try: + steamlib = SteamLibrary(self.__lib_editable.get_text()) + for i in range(self.__listmodel.get_n_items()): + item = self.__listmodel.get_item(i) + if steamlib.directory == item.directory: + self.__lib_editable.set_text("") + return + + self.__listmodel.append(steamlib) + self.__lib_editable.set_text("") + except: + pass + + def _on_choose_library_button_clicked(self,button): + dialog = Gtk.FileDialog.new() + dialog.set_title("sgbackup: Select Steam library path") + dialog.set_modal(True) + if (self.__lib_editable.get_text() and os.path.isdir(self.__lib_editable.get_text())): + dialog.set_initial_folder(Gio.File.new_for_path(self.__lib_editable.get_text())) + + dialog.select_folder(self,None,self._on_choose_library_select_folder) + + def _on_choose_library_select_folder(self,dialog,result,*args): + try: + file = dialog.select_folder_finish(result) + except GLib.Error as ex: + return + + if file is None: + return + + self.__lib_editable.set_text(file.get_path()) + + + def _on_add_library_label_changed(self,label): + if label.get_text() and os.path.isdir(label.get_text()): + self.__add_lib_button.set_sensitive(True) + else: + self.__add_lib_button.set_sensitive(False) + + def _on_list_chooser_button_clicked(self,button,label): + dialog = Gtk.FileDialog.new() + dialog.set_initial_folder(Gio.File.new_for_path(label.get_text())) + dialog.set_modal(True) + dialog.set_title("sgbackup: Change Steam library path") + dialog.select_folder(self,None,self._on_list_chooser_dialog_select_folder,label) + + def _on_list_chooser_dialog_select_folder(self,dialog,result,label,*data): + try: + file = dialog.select_folder_finish(result) + except GLib.Error as ex: + return + + if file is None or not file.get_path(): + return + + label.set_text(file.get_path()) + + + def _on_list_remove_button_clicked(self,button,label): + for i in range(self.__listmodel.get_n_items()): + item = self.__listmodel.get_item(i) + if label.get_text() == item.directory: + self.__listmodel.remove(i) + return + + def _on_library_setup(self,factory,item): + child = Gtk.Box.new(Gtk.Orientation.HORIZONTAL,2) + child.label = Gtk.EditableLabel() + child.label.set_hexpand(True) + child.append(child.label) + + icon = Gtk.Image.new_from_icon_name('document-open-symbolic') + child.chooser_button = Gtk.Button() + child.chooser_button.set_child(icon) + child.append(child.chooser_button) + + icon = Gtk.Image.new_from_icon_name('list-remove-symbolic') + child.remove_button = Gtk.Button() + child.remove_button.set_child(icon) + child.append(child.remove_button) + + item.set_child(child) + + + def _on_library_bind(self,factory,item): + child = item.get_child() + lib = item.get_item() + child.label.set_text(lib.directory) + child.label.bind_property('text',lib,'directory',BindingFlags.DEFAULT) + child.chooser_button.connect('clicked',self._on_list_chooser_button_clicked,child.label) + child.remove_button.connect('clicked',self._on_list_remove_button_clicked,child.label) + + def do_response(self,response): + if response == Gtk.ResponseType.APPLY: + steamlibs = [] + for i in range(self.__listmodel.get_n_items()): + item = self.__listmodel.get_item(i) + if os.path.isdir(item.directory): + steamlibs.append(item) + self.__steam.libraries = steamlibs + + self.hide() + self.destroy() + + +class NewSteamAppsData(GObject): + def __init__(self,data): + GObject.__init__(self) + + +class NewSteamAppsDialog(Gtk.Dialog): + def __init__(self,parent:Gtk.Window|None=None): + Gtk.Dialog.__init__(self) + self.__steam = Steam() + \ No newline at end of file diff --git a/sgbackup/gui/appmenu.ui b/sgbackup/gui/appmenu.ui index a87c277..be19648 100644 --- a/sgbackup/gui/appmenu.ui +++ b/sgbackup/gui/appmenu.ui @@ -12,6 +12,12 @@ _Steam +
+ + Manage Libraries + app.steam-manage-libraries + +
_Epic diff --git a/sgbackup/gui/settingsdialog.py b/sgbackup/gui/settingsdialog.py deleted file mode 100644 index 35212c4..0000000 --- a/sgbackup/gui/settingsdialog.py +++ /dev/null @@ -1,17 +0,0 @@ -############################################################################### -# sgbackup - The SaveGame Backup tool # -# Copyright (C) 2024 Christian Moser # -# # -# 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 . # -############################################################################### diff --git a/sgbackup/steam.py b/sgbackup/steam.py index 3dff60f..ec72a29 100644 --- a/sgbackup/steam.py +++ b/sgbackup/steam.py @@ -229,14 +229,14 @@ class Steam(GObject): self.__libraries.append(SteamLibrary(i)) break else: - with open(str(self.steamlib_list_file),'r',encoding="utf-8") as ifile: + with open(str(self.steamlib_list_file),'rt',encoding="utf-8") as ifile: for line in (i.strip() for i in ifile.readlines()): if not line or line.startswith('#'): continue libdir = Path(line).resolve() if libdir.is_dir(): try: - self.add_library(str(libdir)) + self.__libraries.append(SteamLibrary(str(libdir))) except: pass @@ -259,19 +259,33 @@ class Steam(GObject): @Property def ignore_apps_file(self)->Path: - return Path(settings.config_dir).resolve / 'ignore_steamapps.json' + return Path(settings.config_dir).resolve() / 'ignore_steamapps.json' @Property def libraries(self)->list[SteamLibrary]: return self.__libraries + @libraries.setter + def libraries(self,steamlibs:list[SteamLibrary|str]): + libs=[] + for sl in steamlibs: + if isinstance(sl,SteamLibrary): + libs.append(sl) + elif isinstance(sl,str): + try: + libs.append(SteamLibrary(sl)) + except: + continue + self.__libraries = libs + self.__write_steamlib_list_file() @Property def ignore_apps(self)->dict[int:IgnoreSteamApp]: return self.__ignore_apps def __write_steamlib_list_file(self): - with open(self.steamlib_list_file,'w',encoding='utf-8') as ofile: - ofile.write('\n'.join(str(sl.directory) for sl in self.libraries)) + with open(self.steamlib_list_file,'wt',encoding='utf-8') as ofile: + output = '\n'.join([sl.directory for sl in self.__libraries]) + ofile.write(output) def __write_ignore_steamapps_file(self): with open(self.ignore_apps_file,'w',encoding='utf-8') as ofile: @@ -299,15 +313,15 @@ class Steam(GObject): else: libdir = str(steamlib) - delete_libs=[] - for i in range(len(self.__libraries)): - if self.__libraries[i].directory == libdir: - delete_libs.append(i) - if delete_libs: - for i in sorted(delete_libs,reverse=True): - del self.__libraries[i] - self.__write_steamlib_list_file() + for i in sorted(range(len(self.__libraries)),reverse=True): + if libdir == self.__libraries[i].directory: + del self.__libraries[i] + self.__write_steamlib_list_file() + + def save_libararies(self): + self.__write_steamlib_list_file() + def add_ignore_app(self,app:IgnoreSteamApp): self.__ignore_apps[app.appid] = app @@ -326,7 +340,7 @@ class Steam(GObject): new_apps = [] for lib in self.libraries: for app in lib.steam_apps: - if not app.appid in STEAM_GAMES and not app.appid in self.ignore_apps: + if not app.appid in GameManager.steam_games and not app.appid in self.ignore_apps: new_apps.append(app) return sorted(new_apps)