Package horizons :: Package gui :: Package modules :: Module select_savegame
[hide private]
[frames] | no frames]

Source Code for Module horizons.gui.modules.select_savegame

  1  # ################################################### 
  2  # Copyright (C) 2008-2017 The Unknown Horizons Team 
  3  # team@unknown-horizons.org 
  4  # This file is part of Unknown Horizons. 
  5  # 
  6  # Unknown Horizons is free software; you can redistribute it and/or modify 
  7  # it under the terms of the GNU General Public License as published by 
  8  # the Free Software Foundation; either version 2 of the License, or 
  9  # (at your option) any later version. 
 10  # 
 11  # This program is distributed in the hope that it will be useful, 
 12  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 13  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 14  # GNU General Public License for more details. 
 15  # 
 16  # You should have received a copy of the GNU General Public License 
 17  # along with this program; if not, write to the 
 18  # Free Software Foundation, Inc., 
 19  # 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA 
 20  # ################################################### 
 21   
 22  import os 
 23  import os.path 
 24  import tempfile 
 25  import time 
 26   
 27  from fife import fife 
 28   
 29  from horizons.engine import Fife 
 30  from horizons.extscheduler import ExtScheduler 
 31  from horizons.gui.util import load_uh_widget 
 32  from horizons.gui.widgets.imagebutton import CancelButton, DeleteButton, OkButton 
 33  from horizons.gui.windows import Dialog 
 34  from horizons.i18n import gettext as T, ngettext as NT 
 35  from horizons.savegamemanager import SavegameManager 
 36  from horizons.util.python.callback import Callback 
 37  from horizons.util.savegameupgrader import SavegameUpgrader 
 38   
 39   
40 -class SelectSavegameDialog(Dialog):
41
42 - def __init__(self, mode, windows):
43 super().__init__(windows) 44 45 assert mode in ('load', 'save', 'editor-save') 46 self._mode = mode 47 48 self._gui = load_uh_widget('select_savegame.xml') 49 50 if self._mode == 'save': 51 helptext = T('Save game') 52 elif self._mode == 'load': 53 helptext = T('Load game') 54 elif self._mode == 'editor-save': 55 helptext = T('Save map') 56 self._gui.findChild(name='headline').text = helptext 57 self._gui.findChild(name=OkButton.DEFAULT_NAME).helptext = helptext 58 59 w = self._gui.findChild(name="gamename_box") 60 w.parent.hideChild(w) 61 62 w = self._gui.findChild(name="gamepassword_box") 63 w.parent.hideChild(w) 64 65 w = self._gui.findChild(name='enter_filename') 66 if self._mode in ('save', 'editor-save'): # only show enter_filename on save 67 w.parent.showChild(w) 68 else: 69 w.parent.hideChild(w) 70 71 self.last_click_event = None
72
73 - def prepare(self):
74 if self._mode == 'load': 75 self._map_files, self._map_file_display = SavegameManager.get_saves() 76 if not self._map_files: 77 self._windows.open_popup(T("No saved games"), T("There are no saved games to load.")) 78 return False 79 elif self._mode == 'save': 80 self._map_files, self._map_file_display = SavegameManager.get_regular_saves() 81 elif self._mode == 'editor-save': 82 self._map_files, self._map_file_display = SavegameManager.get_maps() 83 84 self._gui.distributeInitialData({'savegamelist': self._map_file_display}) 85 if self._mode == 'load': 86 self._gui.distributeData({'savegamelist': 0}) 87 88 self._cb = self._create_show_savegame_details(self._gui, self._map_files, 'savegamelist') 89 if self._mode in ('save', 'editor-save'): 90 def selected_changed(): 91 """Fills in the name of the savegame in the textbox when selected in the list""" 92 if self._gui.collectData('savegamelist') == -1: # set blank if nothing is selected 93 self._gui.findChild(name="savegamefile").text = "" 94 else: 95 savegamefile = self._map_file_display[self._gui.collectData('savegamelist')] 96 self._gui.distributeData({'savegamefile': savegamefile})
97 98 self._cb = Callback.ChainedCallbacks(self._cb, selected_changed) 99 100 self._cb() # Refresh data on start 101 self._gui.mapEvents({'savegamelist/action': self._cb}) 102 self._gui.findChild(name="savegamelist").capture(self._cb, event_name="keyPressed") 103 self._gui.findChild(name="savegamelist").capture(self.check_double_click, event_name="mousePressed") 104 105 self.return_events = { 106 OkButton.DEFAULT_NAME: True, 107 CancelButton.DEFAULT_NAME: False, 108 DeleteButton.DEFAULT_NAME: 'delete' 109 } 110 if self._mode in ('save', 'editor-save'): 111 self.return_events['savegamefile'] = True
112
113 - def check_double_click(self, event):
114 """Apply OK button if there was a left double click""" 115 if event.getButton() != fife.MouseEvent.LEFT: 116 return 117 if self.last_click_event == (event.getX(), event.getY()) and self.clicked: 118 self.clicked = False 119 ExtScheduler().rem_call(self, self.reset_click_status) 120 self.trigger_close(OkButton.DEFAULT_NAME) 121 else: 122 self.clicked = True 123 ExtScheduler().add_new_object(self.reset_click_status, self, run_in=0.3, loops=0) 124 self.last_click_event = (event.getX(), event.getY())
125
126 - def reset_click_status(self):
127 """Callback function to reset the click status by Scheduler""" 128 self.clicked = False
129
130 - def act(self, retval):
131 if not retval: # cancelled 132 return 133 134 if retval == 'delete': 135 # delete button was pressed. Apply delete and reshow dialog, delegating the return value 136 delete_retval = self._delete_savegame(self._map_files) 137 if delete_retval: 138 self._gui.distributeData({'savegamelist': -1}) 139 self._cb() 140 return self._windows.open(self) 141 142 selected_savegame = None 143 if self._mode in ('save', 'editor-save'): # return from textfield 144 selected_savegame = self._gui.collectData('savegamefile') 145 if selected_savegame == "": 146 self._windows.open_error_popup(windowtitle=T("No filename given"), 147 description=T("Please enter a valid filename.")) 148 return self._windows.open(self) 149 elif selected_savegame in self._map_file_display: # savegamename already exists 150 if self._mode == 'save': 151 message = T("A savegame with the name {name} already exists.") 152 elif self._mode == 'editor-save': 153 message = T("A map with the name {name} already exists.") 154 message = message.format(name=selected_savegame) 155 message += "\n" + T('Overwrite it?') 156 # keep the pop-up non-modal because otherwise it is double-modal (#1876) 157 if not self._windows.open_popup(T("Confirmation for overwriting"), message, show_cancel_button=True): 158 return self._windows.open(self) 159 160 elif self._mode == 'load': # return selected item from list 161 selected_savegame = self._gui.collectData('savegamelist') 162 assert selected_savegame != -1, "No savegame selected in savegamelist" 163 selected_savegame = self._map_files[selected_savegame] 164 165 return selected_savegame
166
167 - def _create_show_savegame_details(self, gui, map_files, savegamelist):
168 """Creates a function that displays details of a savegame in gui""" 169 170 def tmp_show_details(): 171 """Fetches details of selected savegame and displays it""" 172 gui.findChild(name="screenshot").image = None 173 map_file = None 174 map_file_index = gui.collectData(savegamelist) 175 176 savegame_details_box = gui.findChild(name="savegame_details") 177 savegame_details_parent = savegame_details_box.parent 178 if map_file_index == -1: 179 savegame_details_parent.hideChild(savegame_details_box) 180 return 181 else: 182 savegame_details_parent.showChild(savegame_details_box) 183 try: 184 map_file = map_files[map_file_index] 185 except IndexError: 186 # this was a click in the savegame list, but not on an element 187 # it happens when the savegame list is empty 188 return 189 savegame_info = SavegameManager.get_metadata(map_file) 190 191 if savegame_info.get('screenshot'): 192 # try to find a writable location, that is accessible via relative paths 193 # (required by fife) 194 fd, filename = tempfile.mkstemp() 195 try: 196 path_rel = os.path.relpath(filename) 197 except ValueError: # the relative path sometimes doesn't exist on win 198 os.close(fd) 199 os.unlink(filename) 200 # try again in the current dir, it's often writable 201 fd, filename = tempfile.mkstemp(dir=os.curdir) 202 try: 203 path_rel = os.path.relpath(filename) 204 except ValueError: 205 fd, filename = None, None 206 207 if fd: 208 with os.fdopen(fd, "wb") as f: 209 f.write(savegame_info['screenshot']) 210 # fife only supports relative paths 211 gui.findChild(name="screenshot").image = path_rel 212 os.unlink(filename) 213 214 # savegamedetails 215 details_label = gui.findChild(name="savegamedetails_lbl") 216 details_label.text = "" 217 if savegame_info['timestamp'] == -1: 218 details_label.text += T("Unknown savedate") 219 else: 220 savetime = time.strftime("%c", time.localtime(savegame_info['timestamp'])) 221 details_label.text += T("Saved at {time}").format(time=savetime) 222 details_label.text += '\n' 223 counter = savegame_info['savecounter'] 224 # NT takes care of plural forms for different languages 225 details_label.text += NT("Saved {amount} time", 226 "Saved {amount} times", 227 counter).format(amount=counter) 228 details_label.text += '\n' 229 230 from horizons.constants import VERSION 231 try: 232 details_label.text += T("Savegame version {version}").format( 233 version=savegame_info['savegamerev']) 234 if savegame_info['savegamerev'] != VERSION.SAVEGAMEREVISION: 235 if not SavegameUpgrader.can_upgrade(savegame_info['savegamerev']): 236 details_label.text += " " + T("(probably incompatible)") 237 except KeyError: 238 # this should only happen for very old savegames, so having this unfriendly 239 # error is ok (savegame is quite certainly fully unusable). 240 details_label.text += " " + T("Incompatible version") 241 242 gui.adaptLayout()
243 return tmp_show_details 244
245 - def _delete_savegame(self, map_files):
246 """Deletes the selected savegame if the user confirms 247 self._gui has to contain the widget "savegamelist" 248 @param map_files: list of files that corresponds to the entries of 'savegamelist' 249 @return: True if something was deleted, else False 250 """ 251 selected_item = self._gui.collectData("savegamelist") 252 if selected_item == -1 or selected_item >= len(map_files): 253 self._windows.open_popup(T("No file selected"), T("You need to select a savegame to delete.")) 254 return False 255 selected_file = map_files[selected_item] 256 message = T("Do you really want to delete the savegame '{name}'?").format( 257 name=SavegameManager.get_savegamename_from_filename(selected_file)) 258 if self._windows.open_popup(T("Confirm deletion"), message, show_cancel_button=True): 259 try: 260 os.unlink(selected_file) 261 return True 262 except: 263 self._windows.open_popup(T("Error!"), T("Failed to delete savefile!")) 264 return False 265 else: # player cancelled deletion 266 return False
267