diff --git a/sgbackup/game.py b/sgbackup/game.py
index bc1bcb3..2e23114 100644
--- a/sgbackup/game.py
+++ b/sgbackup/game.py
@@ -224,7 +224,7 @@ class GameFileMatcher(GObject):
raise TypeError("match_type is not a GameFileType instance!")
self.__match_type = match_type
- @Property
+ @Property(type=str)
def match_file(self)->str:
"""
match_file The matcher value.
diff --git a/sgbackup/gui/_app.py b/sgbackup/gui/_app.py
index 166ce81..3c26736 100644
--- a/sgbackup/gui/_app.py
+++ b/sgbackup/gui/_app.py
@@ -36,7 +36,67 @@ from ..archiver import ArchiverManager
__gtype_name__ = __name__
-class GameView(Gtk.ScrolledWindow):
+class GameViewKeySorter(Gtk.Sorter):
+ def __init__(self,sort_ascending:bool=True,*args,**kwargs):
+ Gtk.Sorter.__init__(self)
+
+ self.sort_ascending = sort_ascending
+
+ @Property(type=bool,default=True)
+ def sort_ascending(self)->bool:
+ return self.__sort_ascending
+ @sort_ascending.setter
+ def sort_ascending(self,asc:bool):
+ self.__sort_ascending = asc
+
+ def do_compare(self,game1,game2):
+ if self.sort_ascending:
+ if game1.key > game2.key:
+ return Gtk.Ordering.LARGER
+ elif game1.key < game2.key:
+ return Gtk.Ordering.SMALLER
+ else:
+ return Gtk.Ordering.EQUAL
+ else:
+ if game1.key < game2.key:
+ return Gtk.Ordering.LARGER
+ elif game1.key > game2.key:
+ return Gtk.Ordering.SMALLER
+ else:
+ return Gtk.Ordering.EQUAL
+
+class GameViewNameSorter(Gtk.Sorter):
+ def __init__(self,sort_ascending:bool=True,*args,**kwargs):
+ Gtk.Sorter.__init__(self)
+
+ self.sort_ascending = sort_ascending
+
+ @Property(type=bool,default=True)
+ def sort_ascending(self)->bool:
+ return self.__sort_ascending
+ @sort_ascending.setter
+ def sort_ascending(self,asc:bool):
+ self.__sort_ascending = asc
+
+ def do_compare(self,game1,game2):
+ name1 = game1.name.lower()
+ name2 = game2.name.lower()
+
+ if self.sort_ascending:
+ if name1 > name2:
+ return Gtk.Ordering.LARGER
+ elif name1 < name2:
+ return Gtk.Ordering.SMALLER
+ else:
+ return Gtk.Ordering.EQUAL
+ else:
+ if name1 < name2:
+ return Gtk.Ordering.LARGER
+ elif name1 > name2:
+ return Gtk.Ordering.SMALLER
+ else:
+ return Gtk.Ordering.EQUAL
+class GameView(Gtk.Box):
"""
GameView The View for games.
@@ -48,19 +108,70 @@ class GameView(Gtk.ScrolledWindow):
"""
GameView
"""
- Gtk.ScrolledWindow.__init__(self)
+ Gtk.Box.__init__(self,orientation=Gtk.Orientation.VERTICAL)
+ self.__key_sorter = GameViewKeySorter(True)
+ self.__name_sorter = GameViewNameSorter(True)
+
+ scrolled = Gtk.ScrolledWindow()
+ scrolled.set_vexpand(True)
+ scrolled.set_hexpand(True)
+
+ # set up the ActionBar for this widget
+ self.__actionbar = Gtk.ActionBar()
+ self.actionbar.set_hexpand(True)
+
+ icon = Gtk.Image.new_from_icon_name('list-add-symbolic')
+ icon.set_pixel_size(16)
+ add_game_button=Gtk.Button()
+ add_game_button.set_child(icon)
+ add_game_button.set_tooltip_text("Add a new game.")
+ add_game_button.connect('clicked',self._on_add_game_button_clicked)
+ self.actionbar.pack_start(add_game_button)
+
+
+ icon = Gtk.Image.new_from_icon_name('steam-svgrepo-com-symbolic')
+ icon.set_pixel_size(16)
+ new_steam_games_button=Gtk.Button()
+ new_steam_games_button.set_child(icon)
+ new_steam_games_button.set_tooltip_text("Manage new Steam-Apps")
+ new_steam_games_button.connect('clicked',self._on_new_steam_games_button_clicked)
+ self.actionbar.pack_start(new_steam_games_button)
+
+ self.actionbar.pack_start(Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL))
+
+ icon = Gtk.Image.new_from_icon_name('document-save-symbolic')
+ icon.set_pixel_size(16)
+ backup_active_live_button = Gtk.Button()
+ backup_active_live_button.set_child(icon)
+ backup_active_live_button.set_tooltip_markup("Backup all active and live Games.")
+ backup_active_live_button.connect('clicked',self._on_backup_active_live_button_clicked)
+ self.actionbar.pack_start(backup_active_live_button)
+
+
+
+ # Add a the search entry
+
+ self.__search_entry = Gtk.Entry()
+ self.__search_entry.set_hexpand(True)
+ self.actionbar.pack_end(self.__search_entry)
+ self.actionbar.pack_end(Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL))
+
+ self.append(self.actionbar)
+
self.__liststore = Gio.ListStore.new(Game)
for g in GameManager.get_global().games.values():
pass
self.__liststore.append(g)
-
+ self.__sort_model = Gtk.SortListModel.new(self._liststore,self.__name_sorter)
+ self.__sort_model
self.__action_dialog = None
self.__backup_dialog = None
factory_icon = Gtk.SignalListItemFactory.new()
factory_icon.connect('setup',self._on_icon_column_setup)
factory_icon.connect('bind',self._on_icon_column_bind)
+ factory_icon.connect('unbind',self._on_icon_column_unbind)
column_icon = Gtk.ColumnViewColumn.new("",factory_icon)
factory_key = Gtk.SignalListItemFactory.new()
@@ -89,10 +200,13 @@ class GameView(Gtk.ScrolledWindow):
factory_actions = Gtk.SignalListItemFactory.new()
factory_actions.connect('setup',self._on_actions_column_setup)
factory_actions.connect('bind',self._on_actions_column_bind)
+ #factory_actions.connect('ubind',self._on_actions_column_unbind)
column_actions = Gtk.ColumnViewColumn.new("",factory_actions)
- selection = Gtk.SingleSelection.new(self._liststore)
+ selection = Gtk.SingleSelection.new(self.__sort_model)
self.__columnview = Gtk.ColumnView.new(selection)
+ self.columnview.set_vexpand(True)
+ self.columnview.set_hexpand(True)
self.columnview.append_column(column_icon)
self.columnview.append_column(column_key)
self.columnview.append_column(column_name)
@@ -101,8 +215,9 @@ class GameView(Gtk.ScrolledWindow):
self.columnview.append_column(column_actions)
self.columnview.set_single_click_activate(True)
+ scrolled.set_child(self.columnview)
- self.set_child(self.columnview)
+ self.append(scrolled)
self.refresh()
@property
@@ -124,18 +239,65 @@ class GameView(Gtk.ScrolledWindow):
"""
return self.__columnview
+ @property
+ def actionbar(self)->Gtk.ActionBar:
+ return self.__actionbar
+
def refresh(self):
"""
refresh Refresh the view.
This method reloads the installed Games.
"""
- self.__liststore.remove_all()
+ self.emit('refresh')
+
+ @Signal(name="refresh",return_type=None,arg_types=(),flags=SignalFlags.RUN_FIRST)
+ def do_refresh(self):
+ self._liststore.remove_all()
for game in GameManager.get_global().games.values():
- self.__liststore.append(game)
+ self._liststore.append(game)
+ def _on_game_dialog_response(self,dialog,response):
+ if response == Gtk.ResponseType.APPLY:
+ self.refresh()
+
+ @Signal(name='game-active-changed',return_type=None,arg_types=(Game,),flags=SignalFlags.RUN_FIRST)
+ def do_game_active_changed(self,game:Game):
+ pass
+
+ @Signal(name='game-live-changed',return_type=None,arg_types=(Game,),flags=SignalFlags.RUN_FIRST)
+ def do_game_live_changed(self,game:Game):
+ pass
+
+ def _on_new_steamapps_dialog_response(self,dialog,response):
+ self.refresh()
+
+ def _on_add_game_button_clicked(self,button):
+ dialog = GameDialog(parent=self.get_root())
+ dialog.connect('response',self._on_game_dialog_response)
+ dialog.present()
+
+ def _on_new_steam_games_button_clicked(self,button):
+ dialog = NewSteamAppsDialog(parent=self.get_root())
+ dialog.connect('response',self._on_new_steamapps_dialog_response)
+ dialog.present()
+
+ def _on_backup_active_live_button_clicked(self,button):
+ backup_games = []
+ for i in range(self._liststore.get_n_items()):
+ game = self._liststore.get_item(i)
+ if game.is_live and game.is_active and os.path.exists(os.path.join(game.savegame_root,game.savegame_dir)):
+ backup_games.append(game)
+
+ # TODO:
+ #dialog = BackupManyDialog(parent=self.get_root(),games=backup_games)
+ #dialog.set_modal(False)
+ #dialog.present()
+
def _on_icon_column_setup(self,factory,item):
- item.set_child(Gtk.Image())
+ image = Gtk.Image()
+ image.set_pixel_size(24)
+ item.set_child(image)
def _on_icon_column_bind(self,factory,item):
def transform_to_icon_name(_bidning,sgtype):
@@ -145,11 +307,19 @@ class GameView(Gtk.ScrolledWindow):
return ""
icon = item.get_child()
game = item.get_item()
- game.bind_property('savegame_type',icon,'icon_name',BindingFlags.SYNC_CREATE,transform_to_icon_name)
+ if not hasattr(game,'_savegame_type_to_icon_name_binding'):
+ game._savegame_type_to_icon_name_binding = game.bind_property('savegame_type',icon,'icon_name',BindingFlags.SYNC_CREATE,transform_to_icon_name)
+ def _on_icon_column_unbind(self,factory,item):
+ game = item.get_item()
+ if hasattr(game,'_savegame_type_to_icon_name_binding'):
+ game._savegame_type_to_icon_name_binding.unbind()
+ del game._savegame_type_to_icon_name_binding
def _on_key_column_setup(self,factory,item):
- item.set_child(Gtk.Label())
+ label = Gtk.Label()
+ label.set_xalign(0.0)
+ item.set_child(label)
def _on_key_column_bind(self,factory,item):
label = item.get_child()
@@ -157,12 +327,21 @@ class GameView(Gtk.ScrolledWindow):
game.bind_property('key',label,'label',BindingFlags.SYNC_CREATE)
def _on_name_column_setup(self,factory,item):
- item.set_child(Gtk.Label())
+ label = Gtk.Label()
+ label.set_xalign(0.0)
+ label.set_use_markup(True)
+ item.set_child(label)
def _on_name_column_bind(self,factory,item):
label = item.get_child()
game = item.get_item()
- game.bind_property('name',label,'label',BindingFlags.SYNC_CREATE)
+ if not hasattr(label,'_property_label_from_name_binding'):
+ label._property_label_from_name_binding = game.bind_property('name',
+ label,
+ 'label',
+ BindingFlags.SYNC_CREATE,
+ lambda _binding,s: "{}".format(
+ GLib.markup_escape_text(s)))
def _on_active_column_setup(self,factory,item):
item.set_child(Gtk.Switch())
@@ -179,8 +358,9 @@ class GameView(Gtk.ScrolledWindow):
del item._signal_active_state_set
def _on_active_state_set(self,switch,state,game):
- game.is_active = state
+ game.is_active = switch.get_active()
game.save()
+ self.emit('game-active-changed',game)
def _on_live_column_setup(self,factory,item):
item.set_child(Gtk.Switch())
@@ -189,7 +369,8 @@ class GameView(Gtk.ScrolledWindow):
switch = item.get_child()
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)
+ if not hasattr(item,'_signal_live_state_set'):
+ item._signal_live_state_set = switch.connect('state-set',self._on_live_state_set,game)
def _on_live_column_unbind(self,factory,item):
if hasattr(item,'_signal_live_state_set'):
@@ -198,24 +379,25 @@ class GameView(Gtk.ScrolledWindow):
def _on_live_state_set(self,switch,state,game):
def on_dialog_response(dialog,response):
- if response == Gtk.ResponseType.YES:
- pass
- #archiver.backup(game)
dialog.hide()
dialog.destroy()
+ if response == Gtk.ResponseType.YES:
+ dialog = BackupSingleDialog(parent=self.get_root(),game=game)
+ dialog.run()
+
game.is_live = switch.get_active()
game.save()
if not game.is_live:
- dialog = Gtk.MessageDialog()
+ dialog = Gtk.MessageDialog(buttons=Gtk.ButtonsType.YES_NO)
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
dialog.props.secondary_text = "The new savegame is added to the finsihed savegames for the game."
dialog.props.secondary_use_markup = False
dialog.connect('response',on_dialog_response)
dialog.present()
+ self.emit('game-live-changed',game)
def _on_actions_column_setup(self,action,item):
child = Gtk.Box.new(Gtk.Orientation.HORIZONTAL,2)
@@ -241,16 +423,40 @@ class GameView(Gtk.ScrolledWindow):
child = item.get_child()
game = item.get_item()
archiver_manager = ArchiverManager.get_global()
- child.backup_button.connect('clicked',self._on_columnview_backup_button_clicked,item)
- child.edit_button.connect('clicked',self._on_columnview_edit_button_clicked,item)
- child.remove_button.connect('clicked',self._on_columnview_remove_button_clicked,item)
- archiver_manager.bind_property('backup-in-progress',child.backup_button,'sensitive',
- BindingFlags.SYNC_CREATE,lambda binding,x: False if x else True)
- archiver_manager.bind_property('backup-in-progress',child.edit_button,'sensitive',
- BindingFlags.SYNC_CREATE,lambda binding,x: False if x else True)
- archiver_manager.bind_property('backup-in-progress',child.remove_button,'sensitive',
- BindingFlags.SYNC_CREATE,lambda binding,x: False if x else True)
-
+
+ if not hasattr(child.backup_button,'_signal_clicked_connection'):
+ child.backup_button._signal_clicked_connection = child.backup_button.connect('clicked',self._on_columnview_backup_button_clicked,item)
+ if not hasattr(child.backup_button,'_property_backup_in_progress_binding'):
+ child.backup_button._property_backup_in_progress_binding = archiver_manager.bind_property('backup-in-progress',
+ child.backup_button,
+ 'sensitive',
+ BindingFlags.SYNC_CREATE,
+ lambda binding,x: False if x else True)
+
+ if not hasattr(child.edit_button,'_signal_clicked_connection'):
+ child.edit_button._signal_clicked_connection = child.edit_button.connect('clicked',self._on_columnview_edit_button_clicked,item)
+
+ if not hasattr(child.edit_button,'_property_backup_in_progress_binding'):
+ child.edit_button._property_backup_in_progress_binding = archiver_manager.bind_property('backup-in-progress',
+ child.edit_button,
+ 'sensitive',
+ BindingFlags.SYNC_CREATE,
+ lambda binding,x: False if x else True)
+
+ if not hasattr(child.remove_button,'_signal_clicked_connection'):
+ child.remove_button._signal_clicked_connection = child.remove_button.connect('clicked',self._on_columnview_remove_button_clicked,item)
+ if not hasattr(child.remove_button,'_property_backup_in_progress_binding'):
+ child.remove_button._property_backup_in_progress_binding = archiver_manager.bind_property('backup-in-progress',
+ child.remove_button,'sensitive',
+ BindingFlags.SYNC_CREATE,
+ lambda binding,x: False if x else True)
+
+
+ if os.path.exists(os.path.join(game.savegame_root,game.savegame_dir)):
+ child.backup_button.set_sensitive(True)
+ else:
+ child.backup_button.set_sensitive(False)
+
def _on_columnview_backup_button_clicked(self,button,item):
def on_dialog_response(dialog,response):
self.__backup_dialog = None
@@ -361,7 +567,7 @@ class BackupViewData(GObject):
:type: bool
"""
- pass
+ return self.__is_live
@Property(type=str)
def extension(self)->str:
@@ -571,11 +777,34 @@ class AppWindow(Gtk.ApplicationWindow):
vbox.append(self.__vpaned)
- statusbar = Gtk.Statusbar()
- statusbar.set_hexpand(True)
- statusbar.set_vexpand(False)
- statusbar.push(0,'Running ...')
- vbox.append(statusbar)
+ self.__statusbar = Gtk.Statusbar()
+ self.statusbar.set_hexpand(True)
+ self.statusbar.set_vexpand(False)
+ self.gameview.connect('refresh',self._on_gameview_refresh)
+ self.gameview.connect('game-active-changed',lambda gv,*data: self._on_gameview_refresh(gv))
+ self.gameview.connect('game-live-changed',lambda gv,*data: self._on_gameview_refresh(gv))
+
+ n_games = self.gameview._liststore.get_n_items()
+ n_live = 0
+ n_active = 0
+ n_finished = 0
+ for i in range(n_games):
+ game = self.gameview._liststore.get_item(i)
+ if game.is_live:
+ n_live += 1
+ else:
+ n_finished += 1
+
+ if game.is_active:
+ n_active += 1
+
+ self.statusbar.push(0,'{games} Games -- {active} Games active -- {live} Games live -- {finished} Games finished'.format(
+ games=n_games,
+ active=n_active,
+ live=n_live,
+ finished=n_finished))
+
+ vbox.append(self.statusbar)
self.set_child(vbox)
@@ -609,6 +838,10 @@ class AppWindow(Gtk.ApplicationWindow):
"""
return self.__gameview
+ @property
+ def statusbar(self):
+ return self.__statusbar
+
def refresh(self):
"""
refresh Refresh the views of this window.
@@ -617,6 +850,29 @@ class AppWindow(Gtk.ApplicationWindow):
self.gameview.refresh()
#self.backupview.refresh()
+ def _on_gameview_refresh(self,gameview):
+ self.statusbar.pop(0)
+ n_games = gameview._liststore.get_n_items()
+ n_active = 0
+ n_live = 0
+ n_finished = 0
+ for i in range(n_games):
+ game = gameview._liststore.get_item(i)
+ if game.is_live:
+ n_live += 1
+ else:
+ n_finished += 1
+
+ if game.is_active:
+ n_active += 1
+
+ self.statusbar.push(0,'{games} Games -- {active} Games active -- {live} Games live -- {finished} Games finished'.format(
+ games=n_games,
+ active=n_active,
+ live=n_live,
+ finished=n_finished))
+
+
class Application(Gtk.Application):
"""
Application The `Gtk.Application` for this app.
diff --git a/sgbackup/gui/_gamedialog.py b/sgbackup/gui/_gamedialog.py
index 8624815..e0889be 100644
--- a/sgbackup/gui/_gamedialog.py
+++ b/sgbackup/gui/_gamedialog.py
@@ -1025,9 +1025,9 @@ class GameDialog(Gtk.Dialog):
self.__game_linux = None
if self.get_is_valid_savegame_type(SavegameType.MACOS):
- data = get_game_data(self.__linux)
- binary = self.__linux.binary_entry.get_text()
- if self.__game.linux:
+ data = get_game_data(self.__macos)
+ binary = self.__macos.binary_entry.get_text()
+ if self.__game.macos:
mg = self.__game.macos
mg.savegame_root = data['sgroot']
mg.savegame_dir = data['sgdir']
@@ -1187,7 +1187,8 @@ 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',BindingFlags.SYNC_CREATE)
+ if not hasattr(label,'_property_data_to_label_binding'):
+ label._property_data_to_label_binding = data.bind_property('name',label,'label',BindingFlags.SYNC_CREATE)
def _on_variable_value_setup(self,factory,item):
label = Gtk.Label()
@@ -1196,7 +1197,8 @@ 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',BindingFlags.SYNC_CREATE)
+ if not hasattr(label,'_property_value_to_label_binding'):
+ label._property_value_to_label_binding = data.bind_property('value',label,'label',BindingFlags.SYNC_CREATE)
def _on_filematch_dropdown_selection_changed(self,dropdown,data,item):
data = item.get_item()
@@ -1221,7 +1223,8 @@ class GameDialog(Gtk.Dialog):
if (data.match_type == self.__filematch_dropdown_model.get_item(i).match_type):
dropdown.set_selected(i)
break
- dropdown.connect('notify::selected-item',self._on_filematch_dropdown_selection_changed,item)
+ if not hasattr(dropdown,'_signal_notify_selected_item_connector'):
+ dropdown._signal_notify_selected_item_connector = dropdown.connect('notify::selected-item',self._on_filematch_dropdown_selection_changed,item)
def _on_filematch_value_setup(self,factory,item,widget):
label = Gtk.EditableLabel()
@@ -1232,11 +1235,13 @@ class GameDialog(Gtk.Dialog):
data = item.get_item()
if (data.match_file):
label.set_text(data.match_file)
- label.bind_property('text',data,'match_file',BindingFlags.DEFAULT)
- label.connect('notify::editing',self._on_filematch_value_notify_editing,widget)
- else:
- label.bind_property('text',data,'match_file',BindingFlags.DEFAULT)
- label.connect('notify::editing',self._on_filematch_value_notify_editing,widget)
+
+ if not hasattr(label,'_property_text_to_matchfile_binding'):
+ label._property_text_to_matchfile_binding = label.bind_property('text',data,'match_file',BindingFlags.DEFAULT)
+ if not hasattr(label,'_signal_notify_editing_connector'):
+ label._signal_notify_editing_connector = label.connect('notify::editing',self._on_filematch_value_notify_editing,widget)
+
+ if not label.get_text():
label.grab_focus()
label.start_editing()
@@ -1293,23 +1298,20 @@ class GameDialog(Gtk.Dialog):
if not label.get_text().strip():
model = widget.columnview.get_model().get_model()
i = 0
- while i < model.get_n_items():
+ for i in reversed(range(model.get_n_items())):
item = model.get_item(i)
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():
+ for i in reversed(range(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())
diff --git a/sgbackup/gui/_settingsdialog.py b/sgbackup/gui/_settingsdialog.py
index 437d01f..41fb9e9 100644
--- a/sgbackup/gui/_settingsdialog.py
+++ b/sgbackup/gui/_settingsdialog.py
@@ -17,7 +17,7 @@
###############################################################################
from gi.repository import Gtk,GLib,Gio
-from gi.repository.GObject import GObject,Signal,Property,SignalFlags
+from gi.repository.GObject import GObject,Signal,Property,SignalFlags,BindingFlags
from ..settings import settings
from ..archiver import ArchiverManager,Archiver
@@ -65,6 +65,38 @@ class ZipfileCompressorDataSorter(Gtk.Sorter):
return Gtk.Ordering.EQUAL
+class VariableData(GObject):
+ def __init__(self,name:str,value:str):
+ GObject.__init__(self)
+ self.__name = name
+ self.__value = value
+
+ @Property(type=str)
+ def name(self)->str:
+ return self.__name
+
+ @name.setter
+ def name(self,name:str):
+ self.__name = name
+
+ @Property(type=str)
+ def value(self)->str:
+ return self.__value
+
+ @value.setter
+ def value(self,value:str):
+ self.__value = value
+
+class VariableDataSorter(Gtk.Sorter):
+ def do_compare(self,item1,item2):
+ if (item1.name < item2.name):
+ return Gtk.Ordering.SMALLER
+ elif (item1.name > item2.name):
+ return Gtk.Ordering.LARGER
+ else:
+ return Gtk.Ordering.EQUAL
+
+
class SettingsDialog(Gtk.Dialog):
def __init__(self,parent=None):
Gtk.Dialog.__init__(self)
@@ -80,6 +112,7 @@ class SettingsDialog(Gtk.Dialog):
self.__stack_sidebar = Gtk.StackSidebar.new()
self.__general_page = self.__add_general_settings_page()
self.__archiver_page = self.__add_archiver_settings_page()
+ self.__variables_page = self.__add_variables_settings_page()
sidebar_scrolled=Gtk.ScrolledWindow()
sidebar_scrolled.set_child(self.__stack_sidebar)
@@ -90,7 +123,6 @@ class SettingsDialog(Gtk.Dialog):
paned.set_vexpand(True)
self.__stack_sidebar.set_stack(self.__stack)
-
vbox.append(paned)
self.add_button("Apply",Gtk.ResponseType.APPLY)
@@ -157,7 +189,6 @@ class SettingsDialog(Gtk.Dialog):
grid.attach(page.archiver_dropdown,1,3,2,1)
-
vbox.append(grid)
page.set_child(vbox)
@@ -212,6 +243,99 @@ class SettingsDialog(Gtk.Dialog):
self.add_page(page,"zipfile","Archiver Settings")
return page
+ def _on_variable_add_button_clicked(self,button):
+ variable_model = self.__variables_page.variable_columnview.get_model()
+ while hasattr(variable_model,'get_model'):
+ variable_model = variable_model.get_model()
+ variable_model.append(VariableData("",""))
+
+ def __add_variables_settings_page(self):
+ page = Gtk.Box.new(Gtk.Orientation.VERTICAL,0)
+
+ actions = Gtk.ActionBar()
+ icon=Gtk.Image.new_from_icon_name('list-add-symbolic')
+ button = Gtk.Button()
+ button.set_child(icon)
+ button.connect('clicked',self._on_variable_add_button_clicked)
+ actions.pack_end(button)
+ page.append(actions)
+
+ scrolled = Gtk.ScrolledWindow()
+
+ model = Gio.ListStore.new(VariableData)
+ for vname,vvalue in settings.variables.items():
+ model.append(VariableData(vname,vvalue))
+
+ sorted_model = Gtk.SortListModel.new(model,VariableDataSorter())
+
+ vname_factory = Gtk.SignalListItemFactory()
+ vname_factory.connect('setup',self._on_variable_name_factory_setup)
+ vname_factory.connect('bind',self._on_variable_name_factory_bind)
+
+ vvalue_factory = Gtk.SignalListItemFactory()
+ vvalue_factory.connect('setup',self._on_variable_value_factory_setup)
+ vvalue_factory.connect('bind',self._on_variable_value_factory_bind)
+
+ selection = Gtk.SingleSelection(model=sorted_model)
+ page.variable_columnview = Gtk.ColumnView.new(selection)
+
+ vname_column = Gtk.ColumnViewColumn.new("Name",vname_factory)
+ vname_column.set_expand(True)
+ page.variable_columnview.append_column(vname_column)
+
+ vvalue_column = Gtk.ColumnViewColumn.new("Value",vvalue_factory)
+ page.variable_columnview.append_column(vvalue_column)
+
+ page.variable_columnview.set_vexpand(True)
+ scrolled.set_child(page.variable_columnview)
+ scrolled.set_hexpand(True)
+ page.append(scrolled)
+
+ self.add_page(page,"variables","Variables")
+
+ return page
+
+
+ def _on_variable_name_notify_editing(self,label,param,*data):
+ if label.props.editing == False:
+ if not label.get_text():
+ model = self.__variables_page.variable_columnview.get_model()
+ while hasattr(model,'get_model'):
+ model = model.get_model()
+
+ for i in reversed(range(model.get_n_items())):
+ if not model.get_item(i).name:
+ model.remove(i)
+
+
+ def _on_variable_name_factory_setup(self,factory,item):
+ label = Gtk.EditableLabel()
+ item.set_child(label)
+
+ def _on_variable_name_factory_bind(self,factory,item):
+ data = item.get_item()
+ label = item.get_child()
+ label.set_text(data.name)
+ if not hasattr(label,'_property_text_to_name_binding'):
+ label._property_text_to_name_binding = label.bind_property('text',data,'name',BindingFlags.DEFAULT)
+ if not hasattr(label,'_signal_notify_editing_connection'):
+ label._signal_notify_editing_connection = label.connect('notify::editing',self._on_variable_name_notify_editing)
+
+ if not data.name:
+ label.grab_focus()
+ label.start_editing()
+
+ def _on_variable_value_factory_setup(self,factory,item):
+ label = Gtk.EditableLabel()
+ item.set_child(label)
+
+ def _on_variable_value_factory_bind(self,factory,item):
+ label = item.get_child()
+ data = item.get_item()
+ label.set_text(data.value)
+ if not hasattr(label,'_property_text_to_value_binding'):
+ label._property_text_to_value_binding = label.bind_property('text',data,'name',BindingFlags.DEFAULT)
+
def _on_archiver_factory_setup(self,factory,item):
label = Gtk.Label()
item.set_child(label)
@@ -277,4 +401,13 @@ class SettingsDialog(Gtk.Dialog):
settings.zipfile_compression = self.archiver_page.zf_compressor_dropdown.get_selected_item().compressor
settings.zipfile_compresslevel = self.archiver_page.zf_compresslevel_spinbutton.get_value_as_int()
+ variables = {}
+ variable_model = self.__variables_page.variable_columnview.get_model()
+ while hasattr(variable_model,'get_model'):
+ variable_model = variable_model.get_model()
+ for i in range(variable_model.get_n_items()):
+ vdata = variable_model.get_item(i)
+ if vdata.name:
+ variables[vdata.name] = vdata.value
+ settings.variables = variables
\ No newline at end of file
diff --git a/sgbackup/gui/_steam.py b/sgbackup/gui/_steam.py
index 56aa3bf..8493284 100644
--- a/sgbackup/gui/_steam.py
+++ b/sgbackup/gui/_steam.py
@@ -223,6 +223,7 @@ class NewSteamAppsDialog(Gtk.Dialog):
self.__listview = Gtk.ListView.new(selection,factory)
self.__listview.set_vexpand(True)
self.__listview.set_show_separators(True)
+ self.__listview.set_single_click_activate(True)
scrolled = Gtk.ScrolledWindow()
scrolled.set_child(self.__listview)
@@ -286,8 +287,10 @@ class NewSteamAppsDialog(Gtk.Dialog):
child.name_label.set_markup("{}".format(GLib.markup_escape_text(data.name)))
child.appid_label.set_text(str(data.appid))
child.installdir_label.set_text(data.installdir)
- child.add_app_button.connect('clicked',self._on_add_steamapp_button_clicked,data)
- child.ignore_app_button.connect('clicked',self._on_ignore_steamapp_button_clicked,data)
+ if not hasattr(child.add_app_button,'_signal_clicked_connector'):
+ child.add_app_button._signal_clicked_connector = child.add_app_button.connect('clicked',self._on_add_steamapp_button_clicked,data)
+ if not hasattr(child.ignore_app_button,'_signal_clicked_connector'):
+ child.ignore_app_button._signal_clicked_connector = child.ignore_app_button.connect('clicked',self._on_ignore_steamapp_button_clicked,data)
def _on_add_steamapp_button_clicked(self,button,data:SteamApp,*args):
def on_dialog_response(dialog,response):
diff --git a/sgbackup/gui/appmenu.ui b/sgbackup/gui/appmenu.ui
index 1488f0a..d630803 100644
--- a/sgbackup/gui/appmenu.ui
+++ b/sgbackup/gui/appmenu.ui
@@ -14,12 +14,12 @@
_Steam
-
- Manage libraries
- app.steam-manage-libraries
+ New Steam Apps
+ app.steam-new-apps
-
- Manage new apps
- app.steam-new-apps
+ Manage Steam Libraries
+ app.steam-manage-libraries
diff --git a/sgbackup/settings.py b/sgbackup/settings.py
index a756e3e..91cd524 100644
--- a/sgbackup/settings.py
+++ b/sgbackup/settings.py
@@ -396,7 +396,10 @@ class Settings(GObject.GObject):
return self.get_string('variables',name,"")
def get_variables(self)->dict[str:str]:
- ret = dict(os.environ)
+ if PLATFORM_WINDOWS:
+ ret = dict(((name.upper(),value) for name,value in os.environ.items()))
+ else:
+ ret = dict(os.environ)
ret.update({
"DOCUMENTS": GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_DOCUMENTS),
"DOCUMENTS_DIR": GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_DOCUMENTS),