Merge lp:~widelands-dev/widelands/handling-various-fs-errors into lp:widelands

Proposed by Arty
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
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_correct_loader
- file deletion/renaming in GameClient::handle_packet

To post a comment you must log in.
Revision history for this message
Arty (artydent) wrote :

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.

Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 4226. State: passed. Details: https://travis-ci.org/widelands/widelands/builds/454985800.
Appveyor build 4022. State: failed. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_handling_various_fs_errors-4022.

Revision history for this message
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://code.launchpad.net/widelands/+activereviews

Your C++ is already a lot better than mine was when I started doing them :)

review: Needs Fixing
Revision history for this message
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?

Revision history for this message
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.

Revision history for this message
GunChleoc (gunchleoc) wrote :

Code LGTM, still needs testing.

I have also added a reply to one of your comments.

review: Approve
Revision history for this message
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.

review: Needs Fixing
Revision history for this message
GunChleoc (gunchleoc) wrote :

Tested and all good now :)

@bunnybot merge

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/editor/ui_menus/main_menu_save_map.cc'
--- src/editor/ui_menus/main_menu_save_map.cc 2018-11-12 06:52:50 +0000
+++ src/editor/ui_menus/main_menu_save_map.cc 2018-11-19 20:04:35 +0000
@@ -155,15 +155,42 @@
155void MainMenuSaveMap::clicked_make_directory() {155void MainMenuSaveMap::clicked_make_directory() {
156 /** TRANSLATORS: A folder that hasn't been given a name yet */156 /** TRANSLATORS: A folder that hasn't been given a name yet */
157 MainMenuSaveMapMakeDirectory md(this, _("unnamed"));157 MainMenuSaveMapMakeDirectory md(this, _("unnamed"));
158 if (md.run<UI::Panel::Returncodes>() == UI::Panel::Returncodes::kOk) {158 bool open_dialogue = true;
159 g_fs->ensure_directory_exists(curdir_);159 while (open_dialogue) {
160 // Create directory.160 open_dialogue = false;
161 std::string fullname = curdir_ + g_fs->file_separator() + md.get_dirname();161 if (md.run<UI::Panel::Returncodes>() == UI::Panel::Returncodes::kOk) {
162 // Trim it for preceding/trailing whitespaces in user input162 std::string fullname = curdir_ + g_fs->file_separator() + md.get_dirname();
163 boost::trim(fullname);163 // Trim it for preceding/trailing whitespaces in user input
164 g_fs->make_directory(fullname);164 boost::trim(fullname);
165 fill_table();165 if (g_fs->file_exists(fullname)) {
166 const std::string s =
167 _("A file or directory with that name already exists.");
168 UI::WLMessageBox mbox(this, _("Error Creating Directory!"), s,
169 UI::WLMessageBox::MBoxType::kOk);
170 mbox.run<UI::Panel::Returncodes>();
171 open_dialogue = true;
172 } else {
173 try {
174 g_fs->ensure_directory_exists(curdir_);
175 // Create directory.
176 g_fs->make_directory(fullname);
177 } catch (const FileError& e) {
178 log("directory creation failed in MainMenuSaveMap::"
179 "clicked_make_directory: %s\n", e.what());
180 const std::string s =
181 (boost::format(_("Error while creating directory ā€˜%sā€™."))
182 % fullname).str();
183 UI::WLMessageBox mbox(this, _("Error Creating Directory!"), s,
184 UI::WLMessageBox::MBoxType::kOk);
185 mbox.run<UI::Panel::Returncodes>();
186 }
187 fill_table();
188 }
189 }
166 }190 }
191 table_.focus();
192 // TODO(Arty): In case of successful dir creation we should select the
193 // new dir in the table.
167}194}
168195
169void MainMenuSaveMap::clicked_edit_options() {196void MainMenuSaveMap::clicked_edit_options() {
@@ -256,8 +283,9 @@
256 boost::trim(filename);283 boost::trim(filename);
257284
258 // OK, first check if the extension matches (ignoring case).285 // OK, first check if the extension matches (ignoring case).
259 if (!boost::iends_with(filename, kWidelandsMapExtension))286 if (!boost::iends_with(filename, kWidelandsMapExtension)) {
260 filename += kWidelandsMapExtension;287 filename += kWidelandsMapExtension;
288 }
261289
262 // Append directory name.290 // Append directory name.
263 const std::string complete_filename = curdir_ + g_fs->file_separator() + filename;291 const std::string complete_filename = curdir_ + g_fs->file_separator() + filename;
@@ -269,8 +297,9 @@
269 FileSystem::fs_filename(filename.c_str()))297 FileSystem::fs_filename(filename.c_str()))
270 .str();298 .str();
271 UI::WLMessageBox mbox(this, _("Error Saving Map!"), s, UI::WLMessageBox::MBoxType::kOkCancel);299 UI::WLMessageBox mbox(this, _("Error Saving Map!"), s, UI::WLMessageBox::MBoxType::kOkCancel);
272 if (mbox.run<UI::Panel::Returncodes>() == UI::Panel::Returncodes::kBack)300 if (mbox.run<UI::Panel::Returncodes>() == UI::Panel::Returncodes::kBack) {
273 return false;301 return false;
302 }
274 }303 }
275304
276 // Try deleting file (if it exists). If it fails, give a message and let the player choose a new305 // Try deleting file (if it exists). If it fails, give a message and let the player choose a new
277306
=== modified file 'src/editor/ui_menus/main_menu_save_map_make_directory.cc'
--- src/editor/ui_menus/main_menu_save_map_make_directory.cc 2018-07-08 13:53:45 +0000
+++ src/editor/ui_menus/main_menu_save_map_make_directory.cc 2018-11-19 20:04:35 +0000
@@ -19,9 +19,12 @@
1919
20#include "editor/ui_menus/main_menu_save_map_make_directory.h"20#include "editor/ui_menus/main_menu_save_map_make_directory.h"
2121
22#include <boost/algorithm/string.hpp>
23
22#include "base/i18n.h"24#include "base/i18n.h"
23#include "graphic/font_handler.h"25#include "graphic/font_handler.h"
24#include "io/filesystem/layered_filesystem.h"26#include "io/filesystem/layered_filesystem.h"
27#include "logic/filesystem_constants.h"
2528
26MainMenuSaveMapMakeDirectory::MainMenuSaveMapMakeDirectory(UI::Panel* const parent,29MainMenuSaveMapMakeDirectory::MainMenuSaveMapMakeDirectory(UI::Panel* const parent,
27 char const* dirname)30 char const* dirname)
@@ -63,15 +66,23 @@
63 vbox_.set_size(get_inner_w() - 2 * padding_, get_inner_h() - 3 * padding_ - buth_);66 vbox_.set_size(get_inner_w() - 2 * padding_, get_inner_h() - 3 * padding_ - buth_);
6467
65 edit_.set_text(dirname_);68 edit_.set_text(dirname_);
66 edit_.changed.connect(boost::bind(&MainMenuSaveMapMakeDirectory::edit_changed, this));69 edit_.changed.connect(
70 boost::bind(&MainMenuSaveMapMakeDirectory::edit_changed, this));
71 edit_.ok.connect(
72 boost::bind(&MainMenuSaveMapMakeDirectory::clicked_ok, this));
73 edit_.cancel.connect(
74 boost::bind(&MainMenuSaveMapMakeDirectory::end_modal<UI::Panel::Returncodes>,
75 boost::ref(*this), UI::Panel::Returncodes::kBack));
67 ok_button_.sigclicked.connect(76 ok_button_.sigclicked.connect(
68 boost::bind(&MainMenuSaveMapMakeDirectory::end_modal<UI::Panel::Returncodes>,77 boost::bind(&MainMenuSaveMapMakeDirectory::clicked_ok, this));
69 boost::ref(*this), UI::Panel::Returncodes::kOk));
70 ok_button_.set_enabled(!dirname_.empty());78 ok_button_.set_enabled(!dirname_.empty());
71 cancel_button_.sigclicked.connect(79 cancel_button_.sigclicked.connect(
72 boost::bind(&MainMenuSaveMapMakeDirectory::end_modal<UI::Panel::Returncodes>,80 boost::bind(&MainMenuSaveMapMakeDirectory::end_modal<UI::Panel::Returncodes>,
73 boost::ref(*this), UI::Panel::Returncodes::kBack));81 boost::ref(*this), UI::Panel::Returncodes::kBack));
74 center_to_parent();82 center_to_parent();
83}
84
85void MainMenuSaveMapMakeDirectory::start() {
75 edit_.focus();86 edit_.focus();
76}87}
7788
@@ -82,7 +93,25 @@
82 const std::string& text = edit_.text();93 const std::string& text = edit_.text();
83 // Prevent the user from creating nonsense directory names, like e.g. ".." or "...".94 // Prevent the user from creating nonsense directory names, like e.g. ".." or "...".
84 const bool is_legal_filename = FileSystem::is_legal_filename(text);95 const bool is_legal_filename = FileSystem::is_legal_filename(text);
85 ok_button_.set_enabled(is_legal_filename);96 // Prevent the user from creating directory names that the game would
86 edit_.set_tooltip(is_legal_filename ? "" : illegal_filename_tooltip_);97 // try to interpret as maps
98 const bool has_map_extension =
99 boost::iends_with(text, kWidelandsMapExtension) ||
100 boost::iends_with(text, kS2MapExtension1) ||
101 boost::iends_with(text, kS2MapExtension2);
102 ok_button_.set_enabled(is_legal_filename && !has_map_extension);
103 edit_.set_tooltip(is_legal_filename ?
104 (has_map_extension ? _("This extension is reserved!") : "" ) :
105 illegal_filename_tooltip_);
87 dirname_ = text;106 dirname_ = text;
88}107}
108
109/**
110 * Clicked OK button oder pressed Enter in edit box
111 */
112void MainMenuSaveMapMakeDirectory::clicked_ok() {
113 if (!ok_button_.enabled()) {
114 return;
115 }
116 end_modal<UI::Panel::Returncodes>(UI::Panel::Returncodes::kOk);
117}
89118
=== modified file 'src/editor/ui_menus/main_menu_save_map_make_directory.h'
--- src/editor/ui_menus/main_menu_save_map_make_directory.h 2018-05-18 05:45:50 +0000
+++ src/editor/ui_menus/main_menu_save_map_make_directory.h 2018-11-19 20:04:35 +0000
@@ -37,6 +37,8 @@
37struct MainMenuSaveMapMakeDirectory : public UI::Window {37struct MainMenuSaveMapMakeDirectory : public UI::Window {
38 MainMenuSaveMapMakeDirectory(UI::Panel*, char const*);38 MainMenuSaveMapMakeDirectory(UI::Panel*, char const*);
3939
40 void start() override;
41
40 char const* get_dirname() {42 char const* get_dirname() {
41 return dirname_.c_str();43 return dirname_.c_str();
42 }44 }
@@ -52,6 +54,7 @@
52 UI::Button cancel_button_;54 UI::Button cancel_button_;
53 const std::string illegal_filename_tooltip_;55 const std::string illegal_filename_tooltip_;
54 void edit_changed();56 void edit_changed();
57 void clicked_ok();
55};58};
5659
57#endif // end of include guard: WL_EDITOR_UI_MENUS_MAIN_MENU_SAVE_MAP_MAKE_DIRECTORY_H60#endif // end of include guard: WL_EDITOR_UI_MENUS_MAIN_MENU_SAVE_MAP_MAKE_DIRECTORY_H
5861
=== modified file 'src/logic/game.cc'
--- src/logic/game.cc 2018-10-21 09:42:03 +0000
+++ src/logic/game.cc 2018-11-19 20:04:35 +0000
@@ -75,7 +75,13 @@
75Game::SyncWrapper::~SyncWrapper() {75Game::SyncWrapper::~SyncWrapper() {
76 if (dump_ != nullptr) {76 if (dump_ != nullptr) {
77 if (!syncstreamsave_)77 if (!syncstreamsave_)
78 g_fs->fs_unlink(dumpfname_);78 try {
79 g_fs->fs_unlink(dumpfname_);
80 } catch (const FileError& e) {
81 // not really a problem if deletion fails, but we'll log it
82 log("Deleting synchstream file %s failed: %s\n",
83 dumpfname_.c_str(), e.what());
84 }
79 }85 }
80}86}
8187
8288
=== modified file 'src/logic/map.cc'
--- src/logic/map.cc 2018-05-22 11:29:41 +0000
+++ src/logic/map.cc 2018-11-19 20:04:35 +0000
@@ -32,6 +32,7 @@
32#include "build_info.h"32#include "build_info.h"
33#include "economy/flag.h"33#include "economy/flag.h"
34#include "economy/road.h"34#include "economy/road.h"
35#include "io/filesystem/filesystem_exceptions.h"
35#include "io/filesystem/layered_filesystem.h"36#include "io/filesystem/layered_filesystem.h"
36#include "logic/filesystem_constants.h"37#include "logic/filesystem_constants.h"
37#include "logic/findimmovable.h"38#include "logic/findimmovable.h"
@@ -1566,16 +1567,19 @@
1566 std::string lower_filename = filename;1567 std::string lower_filename = filename;
1567 boost::algorithm::to_lower(lower_filename);1568 boost::algorithm::to_lower(lower_filename);
15681569
1569 if (boost::algorithm::ends_with(lower_filename, kWidelandsMapExtension)) {1570 try {
1570 try {1571 if (boost::algorithm::ends_with(lower_filename, kWidelandsMapExtension)) {
1571 result.reset(new WidelandsMapLoader(g_fs->make_sub_file_system(filename), this));1572 result.reset(
1572 } catch (...) {1573 new WidelandsMapLoader(g_fs->make_sub_file_system(filename), this));
1573 // If this fails, it is an illegal file.1574 } else if (boost::algorithm::ends_with(lower_filename, kS2MapExtension1) ||
1574 // TODO(unknown): catchall hides real errors! Replace with more specific code1575 boost::algorithm::ends_with(lower_filename, kS2MapExtension2)) {
1576 result.reset(new S2MapLoader(filename, *this));
1575 }1577 }
1576 } else if (boost::algorithm::ends_with(lower_filename, kS2MapExtension1) ||1578 } catch (const FileError& e) {
1577 boost::algorithm::ends_with(lower_filename, kS2MapExtension2)) {1579 // file might not have existed
1578 result.reset(new S2MapLoader(filename, *this));1580 log("Map::get_correct_loader: File error: %s\n", e.what());
1581 } catch (std::exception& e) {
1582 log("Map::get_correct_loader: Unknown error: %s\n", e.what());
1579 }1583 }
1580 return result;1584 return result;
1581}1585}
15821586
=== modified file 'src/network/gameclient.cc'
--- src/network/gameclient.cc 2018-11-06 17:05:10 +0000
+++ src/network/gameclient.cc 2018-11-19 20:04:35 +0000
@@ -25,6 +25,7 @@
25#include <boost/format.hpp>25#include <boost/format.hpp>
2626
27#include "base/i18n.h"27#include "base/i18n.h"
28#include "base/log.h"
28#include "base/warning.h"29#include "base/warning.h"
29#include "base/wexception.h"30#include "base/wexception.h"
30#include "build_info.h"31#include "build_info.h"
@@ -32,6 +33,7 @@
32#include "game_io/game_loader.h"33#include "game_io/game_loader.h"
33#include "helper.h"34#include "helper.h"
34#include "io/fileread.h"35#include "io/fileread.h"
36#include "io/filesystem/filesystem_exceptions.h"
35#include "io/filewrite.h"37#include "io/filewrite.h"
36#include "logic/filesystem_constants.h"38#include "logic/filesystem_constants.h"
37#include "logic/game.h"39#include "logic/game.h"
@@ -618,7 +620,14 @@
618 }620 }
619 }621 }
620 // Don't overwrite the file, better rename the original one622 // Don't overwrite the file, better rename the original one
621 g_fs->fs_rename(path, backup_file_name(path));623 try {
624 g_fs->fs_rename(path, backup_file_name(path));
625 } catch (const FileError& e) {
626 log("file error in GameClient::handle_packet: case NETCMD_FILE_PART: "
627 "%s\n", e.what());
628 // TODO(Arty): What now? It just means the next step will fail
629 // or possibly result in some corrupt file
630 }
622 }631 }
623632
624 // Yes we need the file!633 // Yes we need the file!
@@ -707,7 +716,12 @@
707 s.unsigned_8(NETCMD_CHAT);716 s.unsigned_8(NETCMD_CHAT);
708 s.string(_("/me 's file failed md5 checksumming."));717 s.string(_("/me 's file failed md5 checksumming."));
709 d->net->send(s);718 d->net->send(s);
710 g_fs->fs_unlink(file_->filename);719 try {
720 g_fs->fs_unlink(file_->filename);
721 } catch (const FileError& e) {
722 log("file error in GameClient::handle_packet: case NETCMD_FILE_PART: "
723 "%s\n", e.what());
724 }
711 }725 }
712 // Check file for validity726 // Check file for validity
713 bool invalid = false;727 bool invalid = false;
@@ -727,10 +741,15 @@
727 invalid = true;741 invalid = true;
728 }742 }
729 if (invalid) {743 if (invalid) {
730 g_fs->fs_unlink(file_->filename);744 try {
731 // Restore original file, if there was one before745 g_fs->fs_unlink(file_->filename);
732 if (g_fs->file_exists(backup_file_name(file_->filename)))746 // Restore original file, if there was one before
733 g_fs->fs_rename(backup_file_name(file_->filename), file_->filename);747 if (g_fs->file_exists(backup_file_name(file_->filename)))
748 g_fs->fs_rename(backup_file_name(file_->filename), file_->filename);
749 } catch (const FileError& e) {
750 log("file error in GameClient::handle_packet: case NETCMD_FILE_PART: "
751 "%s\n", e.what());
752 }
734 s.reset();753 s.reset();
735 s.unsigned_8(NETCMD_CHAT);754 s.unsigned_8(NETCMD_CHAT);
736 s.string(_("/me checked the received file. Although md5 check summing succeeded, "755 s.string(_("/me checked the received file. Although md5 check summing succeeded, "
737756
=== modified file 'src/ui_fsmenu/loadgame.cc'
--- src/ui_fsmenu/loadgame.cc 2018-06-08 16:36:29 +0000
+++ src/ui_fsmenu/loadgame.cc 2018-11-19 20:04:35 +0000
@@ -158,7 +158,8 @@
158}158}
159159
160void FullscreenMenuLoadGame::fill_table() {160void FullscreenMenuLoadGame::fill_table() {
161 load_or_save_.fill_table(showing_filenames_);161 load_or_save_.set_show_filenames(showing_filenames_);
162 load_or_save_.fill_table();
162}163}
163164
164const std::string& FullscreenMenuLoadGame::filename() const {165const std::string& FullscreenMenuLoadGame::filename() const {
165166
=== modified file 'src/wui/load_or_save_game.cc'
--- src/wui/load_or_save_game.cc 2018-10-26 07:09:29 +0000
+++ src/wui/load_or_save_game.cc 2018-11-19 20:04:35 +0000
@@ -32,6 +32,7 @@
32#include "game_io/game_preload_packet.h"32#include "game_io/game_preload_packet.h"
33#include "graphic/font_handler.h"33#include "graphic/font_handler.h"
34#include "helper.h"34#include "helper.h"
35#include "io/filesystem/filesystem_exceptions.h"
35#include "io/filesystem/layered_filesystem.h"36#include "io/filesystem/layered_filesystem.h"
36#include "logic/filesystem_constants.h"37#include "logic/filesystem_constants.h"
37#include "logic/game_controller.h"38#include "logic/game_controller.h"
@@ -74,6 +75,7 @@
74 table_box_(new UI::Box(parent, 0, 0, UI::Box::Vertical)),75 table_box_(new UI::Box(parent, 0, 0, UI::Box::Vertical)),
75 table_(table_box_, 0, 0, 0, 0, style, UI::TableRows::kMultiDescending),76 table_(table_box_, 0, 0, 0, 0, style, UI::TableRows::kMultiDescending),
76 filetype_(filetype),77 filetype_(filetype),
78 show_filenames_(false),
77 localize_autosave_(localize_autosave),79 localize_autosave_(localize_autosave),
78 // Savegame description80 // Savegame description
79 game_details_(81 game_details_(
@@ -136,8 +138,13 @@
136}138}
137139
138const std::string LoadOrSaveGame::filename_list_string() const {140const std::string LoadOrSaveGame::filename_list_string() const {
141 return filename_list_string(table_.selections());
142}
143
144const std::string
145LoadOrSaveGame::filename_list_string(const std::set<uint32_t>& selections) const {
139 boost::format message;146 boost::format message;
140 for (const uint32_t index : table_.selections()) {147 for (const uint32_t index : selections) {
141 const SavegameData& gamedata = games_data_[table_.get(table_.get_record(index))];148 const SavegameData& gamedata = games_data_[table_.get(table_.get_record(index))];
142149
143 if (gamedata.errormessage.empty()) {150 if (gamedata.errormessage.empty()) {
@@ -263,18 +270,75 @@
263270
264 UI::WLMessageBox confirmationBox(271 UI::WLMessageBox confirmationBox(
265 parent_->get_parent()->get_parent(),272 parent_->get_parent()->get_parent(),
266 ngettext("Confirm Deleting File", "Confirm Deleting Files", no_selections), message,273 no_selections == 1 ? _("Confirm Deleting File") : _("Confirm Deleting Files"),
274 message,
267 UI::WLMessageBox::MBoxType::kOkCancel);275 UI::WLMessageBox::MBoxType::kOkCancel);
268 do_delete = confirmationBox.run<UI::Panel::Returncodes>() == UI::Panel::Returncodes::kOk;276 do_delete = confirmationBox.run<UI::Panel::Returncodes>() == UI::Panel::Returncodes::kOk;
269 table_.focus();277 table_.focus();
270 }278 }
271 if (do_delete) {279 if (do_delete) {
280 // Failed deletions aren't a serious problem, we just catch the errors
281 // and keep track to notify the player.
282 std::set<uint32_t> failed_selections;
283 bool failed;
272 for (const uint32_t index : selections) {284 for (const uint32_t index : selections) {
285 failed = false;
273 const std::string& deleteme = get_filename(index);286 const std::string& deleteme = get_filename(index);
274 g_fs->fs_unlink(deleteme);287 try {
275 if (filetype_ == FileType::kReplay) {288 g_fs->fs_unlink(deleteme);
276 g_fs->fs_unlink(deleteme + kSavegameExtension);289 } catch (const FileError& e) {
277 }290 log("player-requested file deletion failed: %s", e.what());
291 failed = true;
292 }
293 if (filetype_ == FileType::kReplay) {
294 try {
295 g_fs->fs_unlink(deleteme + kSavegameExtension);
296 // If at least one of the two relevant files of a replay are
297 // successfully deleted then count it as success.
298 // (From the player perspective the replay is gone.)
299 failed = false;
300 // If it was a multiplayer replay, also delete the synchstream. Do
301 // it here, so it's only attempted if replay deletion was successful.
302 if (g_fs->file_exists(deleteme + kSyncstreamExtension)) {
303 g_fs->fs_unlink(deleteme + kSyncstreamExtension);
304 }
305 } catch (const FileError& e) {
306 log("player-requested file deletion failed: %s", e.what());
307 }
308 }
309 if (failed) {
310 failed_selections.insert(index);
311 }
312 }
313 if (!failed_selections.empty()) {
314 const uint32_t no_failed = failed_selections.size();
315 // Notify the player.
316 const std::string caption = (no_failed == 1) ?
317 _("Error Deleting File!") : _("Error Deleting Files!");
318 if (filetype_ == FileType::kReplay) {
319 if (selections.size() == 1) {
320 header = _("The replay could not be deleted.");
321 } else {
322 header =
323 (boost::format(ngettext("%d replay could not be deleted.",
324 "%d replays could not be deleted.", no_failed)) % no_failed).str();
325 }
326 } else {
327 if (selections.size() == 1) {
328 header = _("The game could not be deleted.");
329 } else {
330 header =
331 (boost::format(ngettext("%d game could not be deleted.",
332 "%d games could not be deleted.", no_failed)) % no_failed).str();
333 }
334 }
335 std::string message = (boost::format("%s\n%s") % header
336 % filename_list_string(failed_selections)).str();
337 UI::WLMessageBox msgBox(
338 parent_->get_parent()->get_parent(),
339 caption, message,
340 UI::WLMessageBox::MBoxType::kOk);
341 msgBox.run<UI::Panel::Returncodes>();
278 }342 }
279 fill_table();343 fill_table();
280344
@@ -297,7 +361,7 @@
297 return delete_;361 return delete_;
298}362}
299363
300void LoadOrSaveGame::fill_table(bool show_filenames) {364void LoadOrSaveGame::fill_table() {
301365
302 clear_selections();366 clear_selections();
303 table_.clear();367 table_.clear();
@@ -309,8 +373,9 @@
309 return boost::ends_with(fn, kReplayExtension);373 return boost::ends_with(fn, kReplayExtension);
310 });374 });
311 // Update description column title for replays375 // Update description column title for replays
312 table_.set_column_tooltip(2, show_filenames ? _("Filename: Map name (start of replay)") :376 table_.set_column_tooltip(2, show_filenames_ ?
313 _("Map name (start of replay)"));377 _("Filename: Map name (start of replay)") :
378 _("Map name (start of replay)"));
314 } else {379 } else {
315 gamefiles = g_fs->list_directory(kSaveDir);380 gamefiles = g_fs->list_directory(kSaveDir);
316 }381 }
@@ -459,7 +524,7 @@
459 te.set_string(1, gametypestring);524 te.set_string(1, gametypestring);
460 if (filetype_ == FileType::kReplay) {525 if (filetype_ == FileType::kReplay) {
461 const std::string map_basename =526 const std::string map_basename =
462 show_filenames ?527 show_filenames_ ?
463 map_filename(gamedata.filename, gamedata.mapname, localize_autosave_) :528 map_filename(gamedata.filename, gamedata.mapname, localize_autosave_) :
464 gamedata.mapname;529 gamedata.mapname;
465 te.set_string(2, (boost::format(pgettext("mapname_gametime", "%1% (%2%)")) %530 te.set_string(2, (boost::format(pgettext("mapname_gametime", "%1% (%2%)")) %
@@ -472,7 +537,7 @@
472 } else {537 } else {
473 te.set_string(1, map_filename(gamedata.filename, gamedata.mapname, localize_autosave_));538 te.set_string(1, map_filename(gamedata.filename, gamedata.mapname, localize_autosave_));
474 }539 }
475 } catch (const WException& e) {540 } catch (const std::exception& e) {
476 std::string errormessage = e.what();541 std::string errormessage = e.what();
477 boost::replace_all(errormessage, "\n", "<br>");542 boost::replace_all(errormessage, "\n", "<br>");
478 gamedata.errormessage =543 gamedata.errormessage =
@@ -501,3 +566,10 @@
501 table_.sort();566 table_.sort();
502 table_.focus();567 table_.focus();
503}568}
569
570void LoadOrSaveGame::set_show_filenames(bool show_filenames) {
571 if (filetype_ != FileType::kReplay) {
572 return;
573 }
574 show_filenames_ = show_filenames;
575}
504576
=== modified file 'src/wui/load_or_save_game.h'
--- src/wui/load_or_save_game.h 2018-10-25 04:00:51 +0000
+++ src/wui/load_or_save_game.h 2018-11-19 20:04:35 +0000
@@ -57,7 +57,10 @@
57 void select_by_name(const std::string& name);57 void select_by_name(const std::string& name);
5858
59 /// Read savegame/replay files and fill the table and games data.59 /// Read savegame/replay files and fill the table and games data.
60 void fill_table(bool show_filenames = false);60 void fill_table();
61
62 /// Set whether to show filenames. Has only an effect for Replays.
63 void set_show_filenames(bool);
6164
62 /// The table panel65 /// The table panel
63 UI::Table<uintptr_t const>& table();66 UI::Table<uintptr_t const>& table();
@@ -80,6 +83,8 @@
8083
81 /// Formats the current table selection as a list of filenames with savedate information.84 /// Formats the current table selection as a list of filenames with savedate information.
82 const std::string filename_list_string() const;85 const std::string filename_list_string() const;
86 /// Formats a given table selection as a list of filenames with savedate information.
87 const std::string filename_list_string(const std::set<uint32_t>& selections) const;
8388
84 /// Reverse default sort order for save date column89 /// Reverse default sort order for save date column
85 bool compare_date_descending(uint32_t, uint32_t) const;90 bool compare_date_descending(uint32_t, uint32_t) const;
@@ -88,6 +93,7 @@
88 UI::Box* table_box_;93 UI::Box* table_box_;
89 UI::Table<uintptr_t const> table_;94 UI::Table<uintptr_t const> table_;
90 FileType filetype_;95 FileType filetype_;
96 bool show_filenames_;
91 bool localize_autosave_;97 bool localize_autosave_;
92 std::vector<SavegameData> games_data_;98 std::vector<SavegameData> games_data_;
93 GameDetails game_details_;99 GameDetails game_details_;

Subscribers

People subscribed via source and target branches

to status/vote changes: