From 391b42f3bb661e1f3fd8e97db91785d9564b07da Mon Sep 17 00:00:00 2001 From: Christian Moser Date: Thu, 16 Jan 2025 04:25:46 +0100 Subject: [PATCH] 2025.01.16 04:25:45 --- sgbackup/__init__.py | 3 +- sgbackup/_import_gtk.py | 23 +++ sgbackup/game.py | 91 ++++++----- sgbackup/gui/_app.py | 15 +- sgbackup/gui/_gamedialog.py | 314 ++++++++++++++---------------------- 5 files changed, 206 insertions(+), 240 deletions(-) create mode 100644 sgbackup/_import_gtk.py diff --git a/sgbackup/__init__.py b/sgbackup/__init__.py index a417072..df1c589 100644 --- a/sgbackup/__init__.py +++ b/sgbackup/__init__.py @@ -16,8 +16,7 @@ # along with this program. If not, see . # ############################################################################### -import gi -gi.require_version('Gtk','4.0') +from . import _import_gtk __version__ = "0.0.1" from .settings import settings diff --git a/sgbackup/_import_gtk.py b/sgbackup/_import_gtk.py new file mode 100644 index 0000000..099fb12 --- /dev/null +++ b/sgbackup/_import_gtk.py @@ -0,0 +1,23 @@ +############################################################################### +# 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 . # +############################################################################### + +_GTK_IMPORTED_ = False + +if not _GTK_IMPORTED_: + import gi; gi.require_version("Gtk","4.0") + _GTK_IMPORTED_ = True diff --git a/sgbackup/game.py b/sgbackup/game.py index 436a7cb..9aedabe 100644 --- a/sgbackup/game.py +++ b/sgbackup/game.py @@ -16,6 +16,8 @@ # along with this program. If not, see . # ############################################################################### +from . import _import_gtk + from gi.repository.GObject import Property,GObject,Signal,SignalFlags from gi.repository import GLib @@ -281,19 +283,12 @@ class GameData(GObject): self.__savegame_root = savegame_root self.__savegame_dir = savegame_dir self.__variables = {} - self.__filematchers = [] - self.__ignorematchers = [] + self.file_matchers = file_match + self.ignore_matchers = ignore_match if variables is not None: variables.update(variables) - if file_match is not None: - for fm in file_match: - self.add_file_match(fm) - - if ignore_match is not None: - for fm in ignore_match: - self.add_ignore_match(fm) @Property def savegame_type(self)->SavegameType: @@ -346,9 +341,8 @@ class GameData(GObject): @file_matchers.setter def file_matchers(self,fm:list[GameFileMatcher]|None): - if not fm: - self.__filematchers = [] - else: + self.__filematchers = [] + if fm: for matcher in fm: if not isinstance(matcher,GameFileMatcher): raise TypeError("\"file_match\" needs to be \"None\" or a list of \"GameFileMatcher\" instances!") @@ -363,9 +357,8 @@ class GameData(GObject): return self.__ignorematchers @ignore_matchers.setter def ignore_matchers(self,im:list[GameFileMatcher]|None): - if not im: - self.__ignorematchers = [] - else: + self.__ignorematchers = [] + if im: for matcher in im: if not isinstance(matcher,GameFileMatcher): raise TypeError("\"ignore_match\" needs to be \"None\" or a list of \"GameFileMatcher\" instances!") @@ -426,7 +419,7 @@ class GameData(GObject): :return: The variables as a dict. :rtype: dict[str:str] """ - return self.variables + return dict(self.variables) def match_file(self,rel_filename:str)->bool: """ @@ -791,6 +784,9 @@ class SteamGame(GameData): vars["INSTALLDIR"] = self.installdir if self.installdir else "" vars["STEAM_APPID"] = str(self.appid) vars["STEAM_LIBDIR"] = self.librarydir if self.librarydir else "" + vars["STEAM_LIBRARY_DIR"] = self.librarydir if self.librarydir else "" + vars["STEAM_COMPATDATA"] = self.compatdata if self.compatdata else "" + return vars @Property(type=int) @@ -810,8 +806,9 @@ class SteamGame(GameData): @Property def librarydir(self)->str|None: if not self.__librarydir and self.installdir: - return pathlib.Path(self.installdir).resolve().parent.parent.parent + return str(pathlib.Path(self.installdir).resolve().parent.parent.parent) return self.__librarydir + @librarydir.setter def librarydir(self,directory): if not directory: @@ -820,13 +817,20 @@ class SteamGame(GameData): raise ValueError("Steam librarydir is not a valid directory!") self.__librarydir = directory + @Property + def compatdata(self)->str|None: + libdir = self.librarydir + if libdir: + return str(pathlib.Path(libdir).resolve() / 'steamapps' / 'compatdata') + return None + def serialize(self): ret = super().serialize() ret['appid'] = self.appid if self.installdir: - ret['installdir'] = self.installdir if self.installdir else "" - ret['librarydir'] = self.librarydir if self.librarydir else "" + ret['installdir'] = str(self.installdir) if self.installdir else "" + ret['librarydir'] = str(self.librarydir) if self.librarydir else "" return ret @@ -893,7 +897,7 @@ class Game(GObject): @staticmethod def new_from_dict(config:str): - logger = logger.getChild("Game.new_from_dict()") + _logger = logger.getChild("Game.new_from_dict()") def get_file_match(conf:dict): conf_fm = conf['file_match'] if 'file_match' in conf else None @@ -904,11 +908,11 @@ class Game(GObject): for cfm in conf_fm: if ('type' in cfm and 'match' in cfm): try: - file_match.append(GameFileMatcher(GameFileType.from_string(cfm['type'],cfm['match']))) + file_match.append(GameFileMatcher(GameFileType.from_string(cfm['type']),cfm['match'])) except Exception as ex: - logger.error("Adding GameFileMatcher to file_match failed! ({})!".format(ex)) + _logger.error("Adding GameFileMatcher to file_match failed! ({})!".format(ex)) else: - logger.error("Illegal file_match settings! (\"type\" or \"match\" missing!)") + _logger.error("Illegal file_match settings! (\"type\" or \"match\" missing!)") else: file_match = None @@ -918,11 +922,11 @@ class Game(GObject): for cim in conf_im: if ('type' in cim and 'match' in cim): try: - file_match.append(GameFileMatcher(GameFileType.from_string(cim['type'],cim['match']))) + ignore_match.append(GameFileMatcher(GameFileType.from_string(cim['type']),cim['match'])) except Exception as ex: - logger.error("Adding GameFileMatcher to ignore_match failed! ({})!".format(ex)) + _logger.error("Adding GameFileMatcher to ignore_match failed! ({})!".format(ex)) else: - logger.error("Illegal ignore_match settings! (\"type\" or \"match\" missing!)") + _logger.error("Illegal ignore_match settings! (\"type\" or \"match\" missing!)") else: ignore_match = None @@ -938,18 +942,22 @@ class Game(GObject): if appid is not None and sgroot and sgdir: cls(appid,sgroot,sgdir,vars,installdir,file_match,ignore_match) - return None + return cls(appid,sgroot,sgdir,vars,installdir,file_match,ignore_match) # new_steam_game() - if not 'id' in config or not 'name' in config: + if not 'key' in config or not 'name' in config: return None - id = config['id'] + dbid = config['dbid'] if 'dbid' in config else None + key = config['key'] name = config['name'] - sgname = config['savegame_name'] if 'savegame_name' in config else id - sgtype = config['savegame_type'] if 'savegame_type' in config else SavegameType.UNSET + sgname = config['savegame_name'] if 'savegame_name' in config else key + sgtype = SavegameType.from_string(config['savegame_type']) if 'savegame_type' in config else SavegameType.UNSET - game = Game(id,name,sgname) + game = Game(key,name,sgname) + if dbid: + game.dbid = dbid + game.savegame_type = sgtype game.is_active = config['is_active'] if 'is_active' in config else False game.is_live = config['is_live'] if 'is_live' in config else True @@ -1007,7 +1015,12 @@ class Game(GObject): if not os.path.isfile(filename): raise FileNotFoundError("Filename \"{filename}\" not found!".format(filename=filename)) with open(filename,'rt',encoding="UTF-8") as ifile: - return Game.new_from_dict(json.loads(ifile.read())) + x=json.loads(ifile.read()) + game = Game.new_from_dict(x) + + if game is not None: + game.filename = filename + return game def __init__(self,key:str,name:str,savegame_name:str): GObject.__init__(self) @@ -1098,7 +1111,7 @@ class Game(GObject): self.__old_filename = self.__filename if not os.path.isabs(fn): - self.__filename = GLib.build_filename(settings.gameconf_dir,fn) + self.__filename = os.path.join(settings.gameconf_dir,fn) else: self.__filename = fn @@ -1246,13 +1259,15 @@ class Game(GObject): def serialize(self)->dict: ret = { - 'id': self.id, + 'key': self.key, 'name': self.name, 'savegame_name': self.savegame_name, 'savegame_type': self.savegame_type.value, 'is_active': self.is_active, 'is_live': self.is_live, } + if self.dbid: + ret['dbid'] = self.dbid if (self.windows): ret['windows'] = self.windows.serialize() @@ -1357,6 +1372,7 @@ class Game(GObject): class GameManager(GObject): __global_gamemanager = None + logger = logger.getChild('GameManager') @staticmethod def get_global(): @@ -1410,9 +1426,12 @@ class GameManager(GObject): try: game = Game.new_from_json_file(gcf) if not game: + self.logger.warn("Not loaded game \"{game}\"!".format( + game=(game.name if game is not None else "UNKNOWN GAME"))) + print(game.serialize()) continue except Exception as ex: - logger.error("Unable to load gameconf {gameconf}! ({what})".format( + self.logger.error("Unable to load gameconf {gameconf}! ({what})".format( gameconf = os.path.basename(gcf), what = str(ex))) continue diff --git a/sgbackup/gui/_app.py b/sgbackup/gui/_app.py index 1b13c90..65cf38a 100644 --- a/sgbackup/gui/_app.py +++ b/sgbackup/gui/_app.py @@ -17,7 +17,7 @@ ############################################################################### from gi.repository import Gtk,Gio,Gdk -from gi.repository.GObject import GObject,Signal,Property,SignalFlags +from gi.repository.GObject import GObject,Signal,Property,SignalFlags,BindingFlags import logging; logger=logging.getLogger(__name__) @@ -84,6 +84,7 @@ class GameView(Gtk.ScrolledWindow): self.columnview.set_single_click_activate(True) self.set_child(self.columnview) + self.refresh() @property def _liststore(self)->Gio.ListStore: @@ -120,7 +121,7 @@ class GameView(Gtk.ScrolledWindow): def _on_key_column_bind(self,factory,item): label = item.get_child() game = item.get_item() - label.bind_property(game,'key','label',GObject.BindingFlags.DEFAULT) + game.bind_property('key',label,'label',BindingFlags.SYNC_CREATE) def _on_name_column_setup(self,factory,item): item.set_child(Gtk.Label()) @@ -128,14 +129,14 @@ class GameView(Gtk.ScrolledWindow): def _on_name_column_bind(self,factory,item): label = item.get_child() game = item.get_item() - label.bind_proprety(game,'name','label',GObject.BindingFlags.DEFAULT) + game.bind_property('name',label,'label',BindingFlags.SYNC_CREATE) def _on_active_column_setup(self,factory,item): item.set_child(Gtk.Switch()) def _on_active_column_bind(self,factory,item): switch = item.get_child() - game = item.get_data() + game = item.get_item() switch.set_active(game.is_active) item._signal_active_state_set = switch.connect('state-set',self._on_active_state_set,game) @@ -153,8 +154,8 @@ class GameView(Gtk.ScrolledWindow): def _on_live_column_bind(self,factory,item): switch = item.get_child() - game = item.get_data() - switch.set_active(game.is_active) + game = item.get_item() + switch.set_active(game.is_live) item._signal_live_state_set = switch.connect('state-set',self._on_live_state_set,game) def _on_live_column_unbind(self,factory,item): @@ -174,7 +175,7 @@ class GameView(Gtk.ScrolledWindow): game.save() if not state: dialog = Gtk.MessageDialog() - dialog.set_transient_for(self.get_toplevel()) + dialog.set_transient_for(self.get_root()) dialog.props.buttons = Gtk.ButtonsType.YES_NO dialog.props.text = "Do you want to create a new savegame for {game}?".format(game=game.name) dialog.props.use_markup = True diff --git a/sgbackup/gui/_gamedialog.py b/sgbackup/gui/_gamedialog.py index 87991a5..66e0561 100644 --- a/sgbackup/gui/_gamedialog.py +++ b/sgbackup/gui/_gamedialog.py @@ -16,10 +16,14 @@ # along with this program. If not, see . # ############################################################################### +from .. import _import_gtk + from gi.repository import Gio,GLib,Gtk,Pango from gi.repository.GObject import Property,Signal,GObject,BindingFlags + from ..game import ( Game, + GameData, GameFileMatcher, GameFileType, SavegameType, @@ -793,201 +797,104 @@ class GameDialog(Gtk.Dialog): return widget + @Property(type=bool,default=False) + def has_game(self)->bool: + return (self.__game is not None) + def reset(self): """ reset Resets the dialog to the Game set on init or clears the dialog if no Game was set. """ - self.__active_switch.set_active(True) - self.__live_switch.set_active(True) - self.__name_entry.set_text("") - self.__sgname_entry.set_text("") - self.__game_variables.columnview.get_model().get_model().remove_all() + def set_variables(var_widget,vars:dict[str:str]|None): + model = var_widget.columnview.get_model().get_model() + model.remove_all() + if vars: + for k,v in vars.items(): + model.append(GameVariableData(str(k),str(v))) + + def set_game_widget_data(widget,data:GameData|None): + def set_filematch(fm_widget,filematchers:list[GameFileMatcher]): + model = fm_widget.columnview.get_model().get_model() + model.remove_all() + if filematchers: + for fm in filematchers: + model.append(GameFileMatcher(fm.match_type,fm.match_file)) + - #windows - self.__windows.sgroot_entry.set_text("") - self.__windows.sgdir_entry.set_text("") - self.__windows.variables.columnview.get_model().get_model().remove_all() - self.__windows.filematch.columnview.get_model().get_model().remove_all() - self.__windows.ignorematch.columnview.get_model().get_model().remove_all() - self.__windows.lookup_regkeys.listview.get_model().get_model().remove_all() - self.__windows.installdir_regkeys.listview.get_model().get_model().remove_all() - - #linux - self.__linux.sgroot_entry.set_text("") - self.__linux.sgdir_entry.set_text("") - self.__linux.binary_entry.set_text("") - self.__linux.filematch.columnview.get_model().get_model().remove_all() - self.__linux.ignorematch.columnview.get_model().get_model().remove_all() - self.__linux.variables.columnview.get_model().get_model().remove_all() - - #linux - self.__macos.sgroot_entry.set_text("") - self.__macos.sgdir_entry.set_text("") - self.__macos.binary_entry.set_text("") - self.__macos.filematch.columnview.get_model().get_model().remove_all() - self.__macos.ignorematch.columnview.get_model().get_model().remove_all() - self.__macos.variables.columnview.get_model().get_model().remove_all() - - #steam windows - self.__steam_windows.sgroot_entry.set_text("") - self.__steam_windows.sgdir_entry.set_text("") - self.__steam_windows.appid_entry.set_text("") - self.__steam_windows.installdir_entry.set_text("") - self.__steam_windows.filematch.columnview.get_model().get_model().remove_all() - self.__steam_windows.ignorematch.columnview.get_model().get_model().remove_all() - self.__steam_windows.variables.columnview.get_model().get_model().remove_all() + + widget.sgroot_entry.set_text(data.savegame_root if data else "") + widget.sgdir_entry.set_text(data.savegame_dir if data else "") + set_variables(widget.variables,data.variables if data else None) + set_filematch(widget.filematch,data.file_matchers if data else None) + set_filematch(widget.ignorematch,data.ignore_matchers if data else None) - #steam linux - self.__steam_linux.sgroot_entry.set_text("") - self.__steam_linux.sgdir_entry.set_text("") - self.__steam_linux.appid_entry.set_text("") - self.__steam_linux.installdir_entry.set_text("") - self.__steam_linux.filematch.columnview.get_model().get_model().remove_all() - self.__steam_linux.ignorematch.columnview.get_model().get_model().remove_all() - self.__steam_linux.variables.columnview.get_model().get_model().remove_all() + self.__active_switch.set_active(self.__game.is_active if self.has_game else True) + self.__live_switch.set_active(self.__game.is_live if self.has_game else True) + self.__name_entry.set_text(self.__game.name if self.has_game else "") + self.__sgname_entry.set_text(self.__game.savegame_name if self.has_game else "") + set_variables(self.__game_variables,self.__game.variables if self.has_game else None) - #steam macos - self.__steam_macos.sgroot_entry.set_text("") - self.__steam_macos.sgdir_entry.set_text("") - self.__steam_macos.appid_entry.set_text("") - self.__steam_macos.installdir_entry.set_text("") - self.__steam_macos.filematch.columnview.get_model().get_model().remove_all() - self.__steam_macos.ignorematch.columnview.get_model().get_model().remove_all() - self.__steam_macos.variables.columnview.get_model().get_model().remove_all() - - if self.__game is not None: - self.__active_switch.set_active(self.__game.is_active) - self.__live_switch.set_active(self.__game.is_live) - self.__name_entry.set_text(self.__game.name) - self.__sgname_entry.set_text(self.__game.savegame_name) + if self.has_game: model = self.__savegame_type_dropdown.get_model() + sgtype = self.__game.savegame_type for i in range(model.get_n_items()): - if model.get_item(i).savegame_type == self.__game.savegame_type: + item = model.get_item(i) + if sgtype == item.savegame_type: self.__savegame_type_dropdown.set_selected(i) break - - for name,value in self.__game.variables.items(): - self.__game_variables.get_model().get_model().append(GameVariableData(name,value)) - - if self.__game.windows: - self.__windows.sgroot_entry.set_text(self.__game.windows.savegame_root) - self.__windows.sgdir_entry.set_text(self.__game.windows.savegame_dir) - self.__windows.installdir_entry.set_text(self.__game.windows.installdir) - - #filematch - fm_model = self.__windows.filematch.columnview.get_model().get_model() - for fm in self.__game.windows.file_matchers: - fm_model.append(GameFileMatcher(im.match_type,im.match_file)) - - im_model = self.__windows.ignorematch.columnview.get_model().get_model() - for im in self.__game.windows.ignore_matchers: - im_model.append(GameFileMatcher(im.match_type,im.match_file)) - - # set lookup regkeys - var_model = self.__windows.variables.columnview.get_model().get_model() - grk_model = self.__windows.lookup_regkeys.listview.get_model().get_model() - irk_model = self.__windows.installdir_regkeys.listview.get_model().get_model() - for rk in self.__game.windows.game_registry_keys: - grk_model.append(RegistryKeyData(rk)) - - #set installdir regkeys - for rk in self.__game.windows.installdir_registry_keys: - irk_model.append(RegistryKeyData(rk)) - - #set variables - for name,value in self.__game.windows.variables.items(): - var_model.append(GameVariableData(name,value)) - if self.__game.linux: - self.__linux.sgroot_entry.set_text(self.__game.linux.savegame_root) - self.__linux.sgdir_entry.set_text(self.__game.linux.savegame_dir) - self.__linux.binary_entry.set_text(self.__game.linux.binary) + #windows + set_game_widget_data(self.__windows,self.__game.windows if self.has_game else None) + self.__windows.lookup_regkeys.listview.get_model().get_model().remove_all() + self.__windows.installdir_regkeys.listview.get_model().get_model().remove_all() + if self.has_game and self.__game.windows: + grk_model = self.__windows.lookup_regkeys.listview.get_model().get_model() + irk_model = self.__windows.installdir_regkeys.listview.get_model().get_model() + for rk in self.__game.windows.game_registry_keys: + grk_model.append(RegistryKeyData(rk)) - #filematch - fm_model = self.__linux.filematch.columnview.get_model().get_model() - for fm in self.__game.linux.file_matchers: - fm_model.append(GameFileMatcher(fm.match_type,fm.match_file)) - - im_model = self.__linux.ignorematch.columnview.get_model().get_model() - for im in self.__game.linux.ignore_matchers: - im_model.append(GameFileMatcher(im.match_type,im.match_file)) - - var_model = self.__linux.variables.columnview.get_model().get_model() - for name,value in self.__game.linux.variables.items(): - var_model.append(GameVariableData(name,value)) - - if self.__game.macos: - self.__macos.sgroot_entry.set_text(self.__game.macos.savegame_root) - self.__macos.sgdir_entry.set_text(self.__game.macos.savegame_dir) - self.__macos.binary_entry.set_text(self.__game.macos.binary) - - #filematch - fm_model = self.__macos.filematch.columnview.get_model().get_model() - for fm in self.__game.macos.file_matchers: - fm_model.append(GameFileMatcher(fm.match_type,fm.match_file)) - - im_model = self.__macos.ignorematch.columnview.get_model().get_model() - for im in self.__game.macos.ignore_matchers: - im_model.append(GameFileMatcher(im.match_type,im.match_file)) - - var_model = self.__macos.variables.columnview.get_model().get_model() - for name,value in self.__game.linux.variables.items(): - var_model.append(GameVariableData(name,value)) - - if self.__game.steam_windows: - self.__steam_windows.sgroot_entry.set_text(self.__game.steam_windows.savegame_root) - self.__steam_windows.sgdir_entry.set_text(self.__game.steam_windows.savegame_dir) - self.__steam_windows.appid_entry.set_text(str(self.__game.steam_windows.appid)) - self.__steam_windows.installdir_entry.set_text(self.__game.steam_windows.installdir if self.__game.steam_windows.installdir else "") - - #filematch - fm_model = self.__steam_windows.filematch.columnview.get_model().get_model() - for fm in self.__game.steam_windows.file_matchers: - fm_model.append(GameFileMatcher(fm.match_type,fm.match_file)) - - im_model = self.__steam_windows.ignorematch.columnview.get_model().get_model() - for im in self.__game.steam_windows.ignore_matchers: - im_model.append(GameFileMatcher(im.match_type,im.match_file)) - - var_model = self.__steam_windows.variables.columnview.get_model().get_model() - for name,value in self.__game.steam_windows.variables.items(): - var_model.append(GameVariableData(name,value)) - - if self.__game.steam_linux: - self.__steam_linux.sgroot_entry.set_text(self.__game.steam_linux.savegame_root) - self.__steam_linux.sgdir_entry.set_text(self.__game.steam_linux.savegame_dir) - self.__steam_linux.appid_entry.set_text(str(self.__game.steam_linux.appid)) - self.__steam_linux.installdir_entry.set_text(self.__game.steam_linux.installdir if self.__game.steam_linux.installdir else "") - - fm_model = self.__steam_linux.filematch.columnview.get_model().get_model() - for fm in self.__game.steam_linux.file_matchers: - fm_model.append(GameFileMatcher(fm.match_type,fm.match_file)) - - im_model = self.__steam_linux.ignorematch.columnview.get_model().get_model() - for im in self.__game.steam_linux.ignore_matchers: - im_model.append(GameFileMatcher(im.match_type,im.match_file)) - - var_model = self.__steam_linux.variables.columnview.get_model().get_model() - for name,value in self.__game.steam_linux.variables.items(): - var_model.append(GameVariableData(name,value)) + #set installdir regkeys + for rk in self.__game.windows.installdir_registry_keys: + irk_model.append(RegistryKeyData(rk)) + + #linux + set_game_widget_data(self.__linux,self.__game.linux if self.has_game else None) + self.__linux.binary_entry.set_text(self.__game.linux.binary if self.has_game and self.__game.linux else "") + + #macos + set_game_widget_data(self.__macos,self.__game.macos if self.__game else None) + self.__macos.binary_entry.set_text(self.__game.macos.binary if self.has_game and self.__game.macos else "") + + #steam windows + set_game_widget_data(self.__steam_windows,self.__game.steam_windows if self.has_game else None) + self.__steam_windows.appid_entry.set_text(str(self.__game.steam_windows.appid) + if self.has_game and self.__game.steam_windows else "") + self.__steam_windows.installdir_entry.set_text(self.__game.steam_windows.installdir + if self.has_game + and self.__game.steam_windows + and self.__game.steam_windows.installdir + else "") + + #steam linux + set_game_widget_data(self.__steam_linux,self.__game.steam_linux if self.has_game else None) + self.__steam_linux.appid_entry.set_text(str(self.__game.steam_linux.appid) + if self.has_game and self.__game.steam_linux else "") + self.__steam_linux.installdir_entry.set_text(self.__game.steam_linux.installdir + if self.has_game + and self.__game.steam_linux + and self.__game.steam_linux.installdir + else "") + + #steam macos + set_game_widget_data(self.__steam_macos,self.__game.steam_macos if self.has_game else None) + self.__steam_macos.appid_entry.set_text(str(self.__game.steam_macos.appid) + if self.has_game and self.__game.steam_macos else "") + self.__steam_macos.installdir_entry.set_text(self.__game.steam_macos.installdir + if self.has_game + and self.__game.steam_macos + and self.__game.steam_macos.installdir + else "") - if self.__game.steam_macos: - self.__steam_macos.sgroot_entry.set_text(self.__game.steam_macos.savegame_root) - self.__steam_macos.sgdir_entry.set_text(self.__game.steam_macos.savegame_dir) - self.__steam_macos.appid_entry.set_text(str(self.__game.steam_macos.appid)) - self.__steam_macos.installdir_entry.set_text(self.__game.steam_macos.installdir if self.__game.steam_macos.installdir else "") - - fm_model = self.__steam_macos.filematch.columnview.get_model().get_model() - for fm in self.__game.steam_macos.file_matchers: - fm_model.append(GameFileMatcher(fm.match_type,fm.match_file)) - - im_model = self.__steam_macos.ignorematch.columnview.get_model().get_model() - for im in self.__game.steam_macos.ignore_matchers: - im_model.append(GameFileMatcher(im.match_type,im.match_file)) - - var_model = self.__steam_macos.variables.columnview.get_model().get_model() - for name,value in self.__game.steam_macos.variables.items(): - var_model.append(GameVariableData(name,value)) # reset() def save(self): @@ -1005,11 +912,11 @@ class GameDialog(Gtk.Dialog): for i in range(fm_model.get_n_items()): fm_data = fm_model.get_item(i) - filematch.append(GameFileMatcher(fm_data.match_type,fm_data.match_value)) + filematch.append(fm_data) for i in range(im_model.get_n_items()): im_data = im_model.get_item(i) - ignorematch.append(GameFileMatcher(im_data.match_type,im_data.match_value)) + ignorematch.append(im_data) for i in range(var_model.get_n_items()): var = var_model.get_item(i) @@ -1043,13 +950,13 @@ class GameDialog(Gtk.Dialog): name = self.__name_entry.get_text() savegame_name = self.__sgname_entry.get_text() savegame_type = self.__savegame_type_dropdown.get_selected_item().savegame_type - if self.__game: + if self.has_game: self.__game.key = key self.__game.name = name self.__game.savegame_type = savegame_type self.__game.savegame_name = savegame_name self.__game.variables = variables - self.__game.filename = '.'.join((self.__game.key(),'gameconf')) + self.__game.filename = '.'.join((self.__game.key,'gameconf')) else: self.__game = Game(key,name,savegame_name) self.__game.savegame_type = savegame_type @@ -1279,7 +1186,7 @@ class GameDialog(Gtk.Dialog): def _on_variable_name_bind(self,factory,item): label = item.get_child() data = item.get_item() - data.bind_property('name',label,'label',GObject.BindingFlags.SYNC_CREATE) + data.bind_property('name',label,'label',BindingFlags.SYNC_CREATE) def _on_variable_value_setup(self,factory,item): label = Gtk.Label() @@ -1288,7 +1195,7 @@ class GameDialog(Gtk.Dialog): def _on_variable_value_bind(self,factory,item): label = item.get_child() data = item.get_item() - data.bind_property('value',label,'label',GObject.BindingFlags.SYNC_CREATE) + data.bind_property('value',label,'label',BindingFlags.SYNC_CREATE) def _on_filematch_dropdown_selection_changed(self,dropdown,data,item): data = item.get_item() @@ -1322,13 +1229,19 @@ class GameDialog(Gtk.Dialog): def _on_filematch_value_bind(self,factory,item,widget): label = item.get_child() data = item.get_item() - if (data.match_value): - label.set_text(data.match_value) + if (data.match_file): + label.set_text(data.match_file) + label.bind_property('text',data,'match_file',BindingFlags.DEFAULT) + #label.connect('changed',self._on_filematch_value_label_changed,widget) + label.connect('notify::editing',self._on_filematch_value_notify_editing,widget) else: - label.start_editing() + label.bind_property('text',data,'match_file',BindingFlags.DEFAULT) + #label.connect('changed',self._on_filematch_value_label_changed,widget) + label.connect('notify::editing',self._on_filematch_value_notify_editing,widget) label.grab_focus() - label.bind_property('text',data,'match_value',BindingFlags.DEFAULT) - label.connect('changed',self._on_filematch_value_label_changed,widget) + label.start_editing() + + def _on_windows_regkey_setup(self,factory,item): label = Gtk.EditableLabel() @@ -1378,16 +1291,27 @@ class GameDialog(Gtk.Dialog): widget.columnview.get_model().get_model().append(GameFileMatcher(GameFileType.GLOB,"")) def _on_filematch_value_label_changed(self,label,widget): - if not label.get_text(): + if not label.get_text().strip(): model = widget.columnview.get_model().get_model() i = 0 while i < model.get_n_items(): item = model.get_item(i) - if not item.match_value.strip(): + if not item.match_file.strip(): model.remove(i) continue i += 1 - + def _on_filematch_value_notify_editing(self,label,param,widget): + if label.props.editing == False: + if not label.get_text().strip(): + model = widget.columnview.get_model().get_model() + i = 0 + while i < model.get_n_items(): + item = model.get_item(i) + if not item.match_file.strip(): + model.remove(i) + continue + i += 1 + def _on_windows_regkey_add_button_clicked(self,button,widget): widget.listview.get_model().get_model().append(RegistryKeyData())