Merge lp:~widelands-dev/widelands/handling-various-fs-errors into lp:widelands
- handling-various-fs-errors
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | 8929 |
Proposed branch: | lp:~widelands-dev/widelands/handling-various-fs-errors |
Merge into: | lp:widelands |
Diff against target: |
512 lines (+213/-44) 9 files modified
src/editor/ui_menus/main_menu_save_map.cc (+39/-10) src/editor/ui_menus/main_menu_save_map_make_directory.cc (+34/-5) src/editor/ui_menus/main_menu_save_map_make_directory.h (+3/-0) src/logic/game.cc (+7/-1) src/logic/map.cc (+13/-9) src/network/gameclient.cc (+25/-6) src/ui_fsmenu/loadgame.cc (+2/-1) src/wui/load_or_save_game.cc (+83/-11) src/wui/load_or_save_game.h (+7/-1) |
To merge this branch: | bzr merge lp:~widelands-dev/widelands/handling-various-fs-errors |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
GunChleoc | Approve | ||
Review via email: mp+358767@code.launchpad.net |
Commit message
Improved error handling for file save dialogs
In Load/Save menu for games/replays:
- All deletion errors are caught, player gets a message.
- When deleting multiplayer replays, also the corresponding syncstream file is deleted.
- After deleting replays, the table now respects the Show Filenames setting.
In Save menu for maps:
- Errors when trying to create a directory are now caught, player gets a message.
- Directory creation now doesn't allow names with map extensions, because the game would just trying to interpret them as maps and not show as directories. A tooltip is shown.
- Directory creation can now also be triggered by pressing Enter in the name edit box.
Other file errors are caught and logged:
- syncstream deletion in SyncWrapper destructor
- in Map::get_
- file deletion/renaming in GameClient:
Description of the change
Arty (artydent) wrote : | # |
bunnybot (widelandsofficial) wrote : | # |
Continuous integration builds have changed state:
Travis build 4226. State: passed. Details: https:/
Appveyor build 4022. State: failed. Details: https:/
GunChleoc (gunchleoc) wrote : | # |
I have done a code review via diff comments. Not tested yet.
How would you feel about trying your hand at code reviews too?
https:/
Your C++ is already a lot better than mine was when I started doing them :)
Arty (artydent) wrote : | # |
Fixed those things.
Sure, I can do code reviews. I have looked at other active reviews before but mostly ignored them because they were either trivial one-line fixes or part of the code I wasn't familiar with at all yet.
Anything in particular I should look out for in code reviews?
GunChleoc (gunchleoc) wrote : | # |
Just look at it to see if anything catches your attention that doesn't look right and test the change.
You can also do the code review only and leave the testing to somebody else if you don't have time to test as well.
The purpose of reviews is to help avoid introducing new bugs and to prevent spaghetti code and to make the code easy to read by having a consistent code style.
GunChleoc (gunchleoc) wrote : | # |
Code LGTM, still needs testing.
I have also added a reply to one of your comments.
GunChleoc (gunchleoc) wrote : | # |
Testing done:
- When a directory with the name already exists, the "Create directory" dialog is closed. It would be good to reopen it with the name pre-filled.
GunChleoc (gunchleoc) wrote : | # |
Tested and all good now :)
@bunnybot merge
Preview Diff
1 | === modified file 'src/editor/ui_menus/main_menu_save_map.cc' |
2 | --- src/editor/ui_menus/main_menu_save_map.cc 2018-11-12 06:52:50 +0000 |
3 | +++ src/editor/ui_menus/main_menu_save_map.cc 2018-11-19 20:04:35 +0000 |
4 | @@ -155,15 +155,42 @@ |
5 | void MainMenuSaveMap::clicked_make_directory() { |
6 | /** TRANSLATORS: A folder that hasn't been given a name yet */ |
7 | MainMenuSaveMapMakeDirectory md(this, _("unnamed")); |
8 | - if (md.run<UI::Panel::Returncodes>() == UI::Panel::Returncodes::kOk) { |
9 | - g_fs->ensure_directory_exists(curdir_); |
10 | - // Create directory. |
11 | - std::string fullname = curdir_ + g_fs->file_separator() + md.get_dirname(); |
12 | - // Trim it for preceding/trailing whitespaces in user input |
13 | - boost::trim(fullname); |
14 | - g_fs->make_directory(fullname); |
15 | - fill_table(); |
16 | + bool open_dialogue = true; |
17 | + while (open_dialogue) { |
18 | + open_dialogue = false; |
19 | + if (md.run<UI::Panel::Returncodes>() == UI::Panel::Returncodes::kOk) { |
20 | + std::string fullname = curdir_ + g_fs->file_separator() + md.get_dirname(); |
21 | + // Trim it for preceding/trailing whitespaces in user input |
22 | + boost::trim(fullname); |
23 | + if (g_fs->file_exists(fullname)) { |
24 | + const std::string s = |
25 | + _("A file or directory with that name already exists."); |
26 | + UI::WLMessageBox mbox(this, _("Error Creating Directory!"), s, |
27 | + UI::WLMessageBox::MBoxType::kOk); |
28 | + mbox.run<UI::Panel::Returncodes>(); |
29 | + open_dialogue = true; |
30 | + } else { |
31 | + try { |
32 | + g_fs->ensure_directory_exists(curdir_); |
33 | + // Create directory. |
34 | + g_fs->make_directory(fullname); |
35 | + } catch (const FileError& e) { |
36 | + log("directory creation failed in MainMenuSaveMap::" |
37 | + "clicked_make_directory: %s\n", e.what()); |
38 | + const std::string s = |
39 | + (boost::format(_("Error while creating directory ā%sā.")) |
40 | + % fullname).str(); |
41 | + UI::WLMessageBox mbox(this, _("Error Creating Directory!"), s, |
42 | + UI::WLMessageBox::MBoxType::kOk); |
43 | + mbox.run<UI::Panel::Returncodes>(); |
44 | + } |
45 | + fill_table(); |
46 | + } |
47 | + } |
48 | } |
49 | + table_.focus(); |
50 | + // TODO(Arty): In case of successful dir creation we should select the |
51 | + // new dir in the table. |
52 | } |
53 | |
54 | void MainMenuSaveMap::clicked_edit_options() { |
55 | @@ -256,8 +283,9 @@ |
56 | boost::trim(filename); |
57 | |
58 | // OK, first check if the extension matches (ignoring case). |
59 | - if (!boost::iends_with(filename, kWidelandsMapExtension)) |
60 | + if (!boost::iends_with(filename, kWidelandsMapExtension)) { |
61 | filename += kWidelandsMapExtension; |
62 | + } |
63 | |
64 | // Append directory name. |
65 | const std::string complete_filename = curdir_ + g_fs->file_separator() + filename; |
66 | @@ -269,8 +297,9 @@ |
67 | FileSystem::fs_filename(filename.c_str())) |
68 | .str(); |
69 | UI::WLMessageBox mbox(this, _("Error Saving Map!"), s, UI::WLMessageBox::MBoxType::kOkCancel); |
70 | - if (mbox.run<UI::Panel::Returncodes>() == UI::Panel::Returncodes::kBack) |
71 | + if (mbox.run<UI::Panel::Returncodes>() == UI::Panel::Returncodes::kBack) { |
72 | return false; |
73 | + } |
74 | } |
75 | |
76 | // Try deleting file (if it exists). If it fails, give a message and let the player choose a new |
77 | |
78 | === modified file 'src/editor/ui_menus/main_menu_save_map_make_directory.cc' |
79 | --- src/editor/ui_menus/main_menu_save_map_make_directory.cc 2018-07-08 13:53:45 +0000 |
80 | +++ src/editor/ui_menus/main_menu_save_map_make_directory.cc 2018-11-19 20:04:35 +0000 |
81 | @@ -19,9 +19,12 @@ |
82 | |
83 | #include "editor/ui_menus/main_menu_save_map_make_directory.h" |
84 | |
85 | +#include <boost/algorithm/string.hpp> |
86 | + |
87 | #include "base/i18n.h" |
88 | #include "graphic/font_handler.h" |
89 | #include "io/filesystem/layered_filesystem.h" |
90 | +#include "logic/filesystem_constants.h" |
91 | |
92 | MainMenuSaveMapMakeDirectory::MainMenuSaveMapMakeDirectory(UI::Panel* const parent, |
93 | char const* dirname) |
94 | @@ -63,15 +66,23 @@ |
95 | vbox_.set_size(get_inner_w() - 2 * padding_, get_inner_h() - 3 * padding_ - buth_); |
96 | |
97 | edit_.set_text(dirname_); |
98 | - edit_.changed.connect(boost::bind(&MainMenuSaveMapMakeDirectory::edit_changed, this)); |
99 | + edit_.changed.connect( |
100 | + boost::bind(&MainMenuSaveMapMakeDirectory::edit_changed, this)); |
101 | + edit_.ok.connect( |
102 | + boost::bind(&MainMenuSaveMapMakeDirectory::clicked_ok, this)); |
103 | + edit_.cancel.connect( |
104 | + boost::bind(&MainMenuSaveMapMakeDirectory::end_modal<UI::Panel::Returncodes>, |
105 | + boost::ref(*this), UI::Panel::Returncodes::kBack)); |
106 | ok_button_.sigclicked.connect( |
107 | - boost::bind(&MainMenuSaveMapMakeDirectory::end_modal<UI::Panel::Returncodes>, |
108 | - boost::ref(*this), UI::Panel::Returncodes::kOk)); |
109 | + boost::bind(&MainMenuSaveMapMakeDirectory::clicked_ok, this)); |
110 | ok_button_.set_enabled(!dirname_.empty()); |
111 | cancel_button_.sigclicked.connect( |
112 | boost::bind(&MainMenuSaveMapMakeDirectory::end_modal<UI::Panel::Returncodes>, |
113 | boost::ref(*this), UI::Panel::Returncodes::kBack)); |
114 | center_to_parent(); |
115 | +} |
116 | + |
117 | +void MainMenuSaveMapMakeDirectory::start() { |
118 | edit_.focus(); |
119 | } |
120 | |
121 | @@ -82,7 +93,25 @@ |
122 | const std::string& text = edit_.text(); |
123 | // Prevent the user from creating nonsense directory names, like e.g. ".." or "...". |
124 | const bool is_legal_filename = FileSystem::is_legal_filename(text); |
125 | - ok_button_.set_enabled(is_legal_filename); |
126 | - edit_.set_tooltip(is_legal_filename ? "" : illegal_filename_tooltip_); |
127 | + // Prevent the user from creating directory names that the game would |
128 | + // try to interpret as maps |
129 | + const bool has_map_extension = |
130 | + boost::iends_with(text, kWidelandsMapExtension) || |
131 | + boost::iends_with(text, kS2MapExtension1) || |
132 | + boost::iends_with(text, kS2MapExtension2); |
133 | + ok_button_.set_enabled(is_legal_filename && !has_map_extension); |
134 | + edit_.set_tooltip(is_legal_filename ? |
135 | + (has_map_extension ? _("This extension is reserved!") : "" ) : |
136 | + illegal_filename_tooltip_); |
137 | dirname_ = text; |
138 | } |
139 | + |
140 | +/** |
141 | + * Clicked OK button oder pressed Enter in edit box |
142 | + */ |
143 | +void MainMenuSaveMapMakeDirectory::clicked_ok() { |
144 | + if (!ok_button_.enabled()) { |
145 | + return; |
146 | + } |
147 | + end_modal<UI::Panel::Returncodes>(UI::Panel::Returncodes::kOk); |
148 | +} |
149 | |
150 | === modified file 'src/editor/ui_menus/main_menu_save_map_make_directory.h' |
151 | --- src/editor/ui_menus/main_menu_save_map_make_directory.h 2018-05-18 05:45:50 +0000 |
152 | +++ src/editor/ui_menus/main_menu_save_map_make_directory.h 2018-11-19 20:04:35 +0000 |
153 | @@ -37,6 +37,8 @@ |
154 | struct MainMenuSaveMapMakeDirectory : public UI::Window { |
155 | MainMenuSaveMapMakeDirectory(UI::Panel*, char const*); |
156 | |
157 | + void start() override; |
158 | + |
159 | char const* get_dirname() { |
160 | return dirname_.c_str(); |
161 | } |
162 | @@ -52,6 +54,7 @@ |
163 | UI::Button cancel_button_; |
164 | const std::string illegal_filename_tooltip_; |
165 | void edit_changed(); |
166 | + void clicked_ok(); |
167 | }; |
168 | |
169 | #endif // end of include guard: WL_EDITOR_UI_MENUS_MAIN_MENU_SAVE_MAP_MAKE_DIRECTORY_H |
170 | |
171 | === modified file 'src/logic/game.cc' |
172 | --- src/logic/game.cc 2018-10-21 09:42:03 +0000 |
173 | +++ src/logic/game.cc 2018-11-19 20:04:35 +0000 |
174 | @@ -75,7 +75,13 @@ |
175 | Game::SyncWrapper::~SyncWrapper() { |
176 | if (dump_ != nullptr) { |
177 | if (!syncstreamsave_) |
178 | - g_fs->fs_unlink(dumpfname_); |
179 | + try { |
180 | + g_fs->fs_unlink(dumpfname_); |
181 | + } catch (const FileError& e) { |
182 | + // not really a problem if deletion fails, but we'll log it |
183 | + log("Deleting synchstream file %s failed: %s\n", |
184 | + dumpfname_.c_str(), e.what()); |
185 | + } |
186 | } |
187 | } |
188 | |
189 | |
190 | === modified file 'src/logic/map.cc' |
191 | --- src/logic/map.cc 2018-05-22 11:29:41 +0000 |
192 | +++ src/logic/map.cc 2018-11-19 20:04:35 +0000 |
193 | @@ -32,6 +32,7 @@ |
194 | #include "build_info.h" |
195 | #include "economy/flag.h" |
196 | #include "economy/road.h" |
197 | +#include "io/filesystem/filesystem_exceptions.h" |
198 | #include "io/filesystem/layered_filesystem.h" |
199 | #include "logic/filesystem_constants.h" |
200 | #include "logic/findimmovable.h" |
201 | @@ -1566,16 +1567,19 @@ |
202 | std::string lower_filename = filename; |
203 | boost::algorithm::to_lower(lower_filename); |
204 | |
205 | - if (boost::algorithm::ends_with(lower_filename, kWidelandsMapExtension)) { |
206 | - try { |
207 | - result.reset(new WidelandsMapLoader(g_fs->make_sub_file_system(filename), this)); |
208 | - } catch (...) { |
209 | - // If this fails, it is an illegal file. |
210 | - // TODO(unknown): catchall hides real errors! Replace with more specific code |
211 | + try { |
212 | + if (boost::algorithm::ends_with(lower_filename, kWidelandsMapExtension)) { |
213 | + result.reset( |
214 | + new WidelandsMapLoader(g_fs->make_sub_file_system(filename), this)); |
215 | + } else if (boost::algorithm::ends_with(lower_filename, kS2MapExtension1) || |
216 | + boost::algorithm::ends_with(lower_filename, kS2MapExtension2)) { |
217 | + result.reset(new S2MapLoader(filename, *this)); |
218 | } |
219 | - } else if (boost::algorithm::ends_with(lower_filename, kS2MapExtension1) || |
220 | - boost::algorithm::ends_with(lower_filename, kS2MapExtension2)) { |
221 | - result.reset(new S2MapLoader(filename, *this)); |
222 | + } catch (const FileError& e) { |
223 | + // file might not have existed |
224 | + log("Map::get_correct_loader: File error: %s\n", e.what()); |
225 | + } catch (std::exception& e) { |
226 | + log("Map::get_correct_loader: Unknown error: %s\n", e.what()); |
227 | } |
228 | return result; |
229 | } |
230 | |
231 | === modified file 'src/network/gameclient.cc' |
232 | --- src/network/gameclient.cc 2018-11-06 17:05:10 +0000 |
233 | +++ src/network/gameclient.cc 2018-11-19 20:04:35 +0000 |
234 | @@ -25,6 +25,7 @@ |
235 | #include <boost/format.hpp> |
236 | |
237 | #include "base/i18n.h" |
238 | +#include "base/log.h" |
239 | #include "base/warning.h" |
240 | #include "base/wexception.h" |
241 | #include "build_info.h" |
242 | @@ -32,6 +33,7 @@ |
243 | #include "game_io/game_loader.h" |
244 | #include "helper.h" |
245 | #include "io/fileread.h" |
246 | +#include "io/filesystem/filesystem_exceptions.h" |
247 | #include "io/filewrite.h" |
248 | #include "logic/filesystem_constants.h" |
249 | #include "logic/game.h" |
250 | @@ -618,7 +620,14 @@ |
251 | } |
252 | } |
253 | // Don't overwrite the file, better rename the original one |
254 | - g_fs->fs_rename(path, backup_file_name(path)); |
255 | + try { |
256 | + g_fs->fs_rename(path, backup_file_name(path)); |
257 | + } catch (const FileError& e) { |
258 | + log("file error in GameClient::handle_packet: case NETCMD_FILE_PART: " |
259 | + "%s\n", e.what()); |
260 | + // TODO(Arty): What now? It just means the next step will fail |
261 | + // or possibly result in some corrupt file |
262 | + } |
263 | } |
264 | |
265 | // Yes we need the file! |
266 | @@ -707,7 +716,12 @@ |
267 | s.unsigned_8(NETCMD_CHAT); |
268 | s.string(_("/me 's file failed md5 checksumming.")); |
269 | d->net->send(s); |
270 | - g_fs->fs_unlink(file_->filename); |
271 | + try { |
272 | + g_fs->fs_unlink(file_->filename); |
273 | + } catch (const FileError& e) { |
274 | + log("file error in GameClient::handle_packet: case NETCMD_FILE_PART: " |
275 | + "%s\n", e.what()); |
276 | + } |
277 | } |
278 | // Check file for validity |
279 | bool invalid = false; |
280 | @@ -727,10 +741,15 @@ |
281 | invalid = true; |
282 | } |
283 | if (invalid) { |
284 | - g_fs->fs_unlink(file_->filename); |
285 | - // Restore original file, if there was one before |
286 | - if (g_fs->file_exists(backup_file_name(file_->filename))) |
287 | - g_fs->fs_rename(backup_file_name(file_->filename), file_->filename); |
288 | + try { |
289 | + g_fs->fs_unlink(file_->filename); |
290 | + // Restore original file, if there was one before |
291 | + if (g_fs->file_exists(backup_file_name(file_->filename))) |
292 | + g_fs->fs_rename(backup_file_name(file_->filename), file_->filename); |
293 | + } catch (const FileError& e) { |
294 | + log("file error in GameClient::handle_packet: case NETCMD_FILE_PART: " |
295 | + "%s\n", e.what()); |
296 | + } |
297 | s.reset(); |
298 | s.unsigned_8(NETCMD_CHAT); |
299 | s.string(_("/me checked the received file. Although md5 check summing succeeded, " |
300 | |
301 | === modified file 'src/ui_fsmenu/loadgame.cc' |
302 | --- src/ui_fsmenu/loadgame.cc 2018-06-08 16:36:29 +0000 |
303 | +++ src/ui_fsmenu/loadgame.cc 2018-11-19 20:04:35 +0000 |
304 | @@ -158,7 +158,8 @@ |
305 | } |
306 | |
307 | void FullscreenMenuLoadGame::fill_table() { |
308 | - load_or_save_.fill_table(showing_filenames_); |
309 | + load_or_save_.set_show_filenames(showing_filenames_); |
310 | + load_or_save_.fill_table(); |
311 | } |
312 | |
313 | const std::string& FullscreenMenuLoadGame::filename() const { |
314 | |
315 | === modified file 'src/wui/load_or_save_game.cc' |
316 | --- src/wui/load_or_save_game.cc 2018-10-26 07:09:29 +0000 |
317 | +++ src/wui/load_or_save_game.cc 2018-11-19 20:04:35 +0000 |
318 | @@ -32,6 +32,7 @@ |
319 | #include "game_io/game_preload_packet.h" |
320 | #include "graphic/font_handler.h" |
321 | #include "helper.h" |
322 | +#include "io/filesystem/filesystem_exceptions.h" |
323 | #include "io/filesystem/layered_filesystem.h" |
324 | #include "logic/filesystem_constants.h" |
325 | #include "logic/game_controller.h" |
326 | @@ -74,6 +75,7 @@ |
327 | table_box_(new UI::Box(parent, 0, 0, UI::Box::Vertical)), |
328 | table_(table_box_, 0, 0, 0, 0, style, UI::TableRows::kMultiDescending), |
329 | filetype_(filetype), |
330 | + show_filenames_(false), |
331 | localize_autosave_(localize_autosave), |
332 | // Savegame description |
333 | game_details_( |
334 | @@ -136,8 +138,13 @@ |
335 | } |
336 | |
337 | const std::string LoadOrSaveGame::filename_list_string() const { |
338 | + return filename_list_string(table_.selections()); |
339 | +} |
340 | + |
341 | +const std::string |
342 | +LoadOrSaveGame::filename_list_string(const std::set<uint32_t>& selections) const { |
343 | boost::format message; |
344 | - for (const uint32_t index : table_.selections()) { |
345 | + for (const uint32_t index : selections) { |
346 | const SavegameData& gamedata = games_data_[table_.get(table_.get_record(index))]; |
347 | |
348 | if (gamedata.errormessage.empty()) { |
349 | @@ -263,18 +270,75 @@ |
350 | |
351 | UI::WLMessageBox confirmationBox( |
352 | parent_->get_parent()->get_parent(), |
353 | - ngettext("Confirm Deleting File", "Confirm Deleting Files", no_selections), message, |
354 | + no_selections == 1 ? _("Confirm Deleting File") : _("Confirm Deleting Files"), |
355 | + message, |
356 | UI::WLMessageBox::MBoxType::kOkCancel); |
357 | do_delete = confirmationBox.run<UI::Panel::Returncodes>() == UI::Panel::Returncodes::kOk; |
358 | table_.focus(); |
359 | } |
360 | if (do_delete) { |
361 | + // Failed deletions aren't a serious problem, we just catch the errors |
362 | + // and keep track to notify the player. |
363 | + std::set<uint32_t> failed_selections; |
364 | + bool failed; |
365 | for (const uint32_t index : selections) { |
366 | + failed = false; |
367 | const std::string& deleteme = get_filename(index); |
368 | - g_fs->fs_unlink(deleteme); |
369 | - if (filetype_ == FileType::kReplay) { |
370 | - g_fs->fs_unlink(deleteme + kSavegameExtension); |
371 | - } |
372 | + try { |
373 | + g_fs->fs_unlink(deleteme); |
374 | + } catch (const FileError& e) { |
375 | + log("player-requested file deletion failed: %s", e.what()); |
376 | + failed = true; |
377 | + } |
378 | + if (filetype_ == FileType::kReplay) { |
379 | + try { |
380 | + g_fs->fs_unlink(deleteme + kSavegameExtension); |
381 | + // If at least one of the two relevant files of a replay are |
382 | + // successfully deleted then count it as success. |
383 | + // (From the player perspective the replay is gone.) |
384 | + failed = false; |
385 | + // If it was a multiplayer replay, also delete the synchstream. Do |
386 | + // it here, so it's only attempted if replay deletion was successful. |
387 | + if (g_fs->file_exists(deleteme + kSyncstreamExtension)) { |
388 | + g_fs->fs_unlink(deleteme + kSyncstreamExtension); |
389 | + } |
390 | + } catch (const FileError& e) { |
391 | + log("player-requested file deletion failed: %s", e.what()); |
392 | + } |
393 | + } |
394 | + if (failed) { |
395 | + failed_selections.insert(index); |
396 | + } |
397 | + } |
398 | + if (!failed_selections.empty()) { |
399 | + const uint32_t no_failed = failed_selections.size(); |
400 | + // Notify the player. |
401 | + const std::string caption = (no_failed == 1) ? |
402 | + _("Error Deleting File!") : _("Error Deleting Files!"); |
403 | + if (filetype_ == FileType::kReplay) { |
404 | + if (selections.size() == 1) { |
405 | + header = _("The replay could not be deleted."); |
406 | + } else { |
407 | + header = |
408 | + (boost::format(ngettext("%d replay could not be deleted.", |
409 | + "%d replays could not be deleted.", no_failed)) % no_failed).str(); |
410 | + } |
411 | + } else { |
412 | + if (selections.size() == 1) { |
413 | + header = _("The game could not be deleted."); |
414 | + } else { |
415 | + header = |
416 | + (boost::format(ngettext("%d game could not be deleted.", |
417 | + "%d games could not be deleted.", no_failed)) % no_failed).str(); |
418 | + } |
419 | + } |
420 | + std::string message = (boost::format("%s\n%s") % header |
421 | + % filename_list_string(failed_selections)).str(); |
422 | + UI::WLMessageBox msgBox( |
423 | + parent_->get_parent()->get_parent(), |
424 | + caption, message, |
425 | + UI::WLMessageBox::MBoxType::kOk); |
426 | + msgBox.run<UI::Panel::Returncodes>(); |
427 | } |
428 | fill_table(); |
429 | |
430 | @@ -297,7 +361,7 @@ |
431 | return delete_; |
432 | } |
433 | |
434 | -void LoadOrSaveGame::fill_table(bool show_filenames) { |
435 | +void LoadOrSaveGame::fill_table() { |
436 | |
437 | clear_selections(); |
438 | table_.clear(); |
439 | @@ -309,8 +373,9 @@ |
440 | return boost::ends_with(fn, kReplayExtension); |
441 | }); |
442 | // Update description column title for replays |
443 | - table_.set_column_tooltip(2, show_filenames ? _("Filename: Map name (start of replay)") : |
444 | - _("Map name (start of replay)")); |
445 | + table_.set_column_tooltip(2, show_filenames_ ? |
446 | + _("Filename: Map name (start of replay)") : |
447 | + _("Map name (start of replay)")); |
448 | } else { |
449 | gamefiles = g_fs->list_directory(kSaveDir); |
450 | } |
451 | @@ -459,7 +524,7 @@ |
452 | te.set_string(1, gametypestring); |
453 | if (filetype_ == FileType::kReplay) { |
454 | const std::string map_basename = |
455 | - show_filenames ? |
456 | + show_filenames_ ? |
457 | map_filename(gamedata.filename, gamedata.mapname, localize_autosave_) : |
458 | gamedata.mapname; |
459 | te.set_string(2, (boost::format(pgettext("mapname_gametime", "%1% (%2%)")) % |
460 | @@ -472,7 +537,7 @@ |
461 | } else { |
462 | te.set_string(1, map_filename(gamedata.filename, gamedata.mapname, localize_autosave_)); |
463 | } |
464 | - } catch (const WException& e) { |
465 | + } catch (const std::exception& e) { |
466 | std::string errormessage = e.what(); |
467 | boost::replace_all(errormessage, "\n", "<br>"); |
468 | gamedata.errormessage = |
469 | @@ -501,3 +566,10 @@ |
470 | table_.sort(); |
471 | table_.focus(); |
472 | } |
473 | + |
474 | +void LoadOrSaveGame::set_show_filenames(bool show_filenames) { |
475 | + if (filetype_ != FileType::kReplay) { |
476 | + return; |
477 | + } |
478 | + show_filenames_ = show_filenames; |
479 | +} |
480 | |
481 | === modified file 'src/wui/load_or_save_game.h' |
482 | --- src/wui/load_or_save_game.h 2018-10-25 04:00:51 +0000 |
483 | +++ src/wui/load_or_save_game.h 2018-11-19 20:04:35 +0000 |
484 | @@ -57,7 +57,10 @@ |
485 | void select_by_name(const std::string& name); |
486 | |
487 | /// Read savegame/replay files and fill the table and games data. |
488 | - void fill_table(bool show_filenames = false); |
489 | + void fill_table(); |
490 | + |
491 | + /// Set whether to show filenames. Has only an effect for Replays. |
492 | + void set_show_filenames(bool); |
493 | |
494 | /// The table panel |
495 | UI::Table<uintptr_t const>& table(); |
496 | @@ -80,6 +83,8 @@ |
497 | |
498 | /// Formats the current table selection as a list of filenames with savedate information. |
499 | const std::string filename_list_string() const; |
500 | + /// Formats a given table selection as a list of filenames with savedate information. |
501 | + const std::string filename_list_string(const std::set<uint32_t>& selections) const; |
502 | |
503 | /// Reverse default sort order for save date column |
504 | bool compare_date_descending(uint32_t, uint32_t) const; |
505 | @@ -88,6 +93,7 @@ |
506 | UI::Box* table_box_; |
507 | UI::Table<uintptr_t const> table_; |
508 | FileType filetype_; |
509 | + bool show_filenames_; |
510 | bool localize_autosave_; |
511 | std::vector<SavegameData> games_data_; |
512 | GameDetails game_details_; |
Some of the "handling" is just catching and logging.
In GameClient: :handle_ packet, when renaming a file to a backup fails, we could possibly handle this by finding another filename to save to, but I think this would have to be agreed upon by the participating clients. I didn't attempt this, because I am not yet familiar with the network code.