The Battle for Wesnoth  1.19.0-dev
wml_error.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2014 - 2024
3  by Iris Morelle <shadowm2006@gmail.com>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program 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  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 #define GETTEXT_DOMAIN "wesnoth-lib"
17 
19 
20 #include "addon/info.hpp"
21 // Needs the full path to avoid confusion with gui/dialogs/addon/manager.hpp.
22 #include "../../addon/manager.hpp"
23 #include "desktop/clipboard.hpp"
24 #include "filesystem.hpp"
26 #include "gui/widgets/button.hpp"
28 #include "gui/widgets/window.hpp"
30 
31 #include "gettext.hpp"
32 
33 #include <functional>
34 
35 namespace
36 {
37 
38 void strip_trailing_dir_separators(std::string& str)
39 {
40  while(filesystem::is_path_sep(str.back())) {
41  str.erase(str.size() - 1);
42  }
43 }
44 
45 std::string format_file_list(const std::vector<std::string>& files_original)
46 {
47  if(files_original.empty()) {
48  return "";
49  }
50 
51  const std::string& addons_path = filesystem::get_addons_dir();
52  std::vector<std::string> files(files_original);
53 
54  for(std::string & file : files)
55  {
56  std::string base;
57  std::string filename = filesystem::base_name(file);
58  std::string parent_path;
59 
60  const bool is_main_cfg = filename == "_main.cfg";
61 
62  if(is_main_cfg) {
63  parent_path = filesystem::directory_name(file) + "/..";
64  } else {
65  parent_path = filesystem::directory_name(file);
66  }
67 
68  // Only proceed to pretty-format the filename if it's from the add-ons
69  // directory.
70  if(filesystem::normalize_path(parent_path) != filesystem::normalize_path(addons_path)) {
71  continue;
72  }
73 
74  if(is_main_cfg) {
75  base = filesystem::directory_name(file);
76  // HACK: fool filesystem::base_name() into giving us the parent directory name
77  // alone by making base seem not like a directory path,
78  // otherwise it returns an empty string.
79  strip_trailing_dir_separators(base);
80  base = filesystem::base_name(base);
81  } else {
82  base = filename;
83  }
84 
85  if(base.empty()) {
86  // We did something wrong. In the interest of not messing up the
87  // report, leave the original filename intact.
88  continue;
89  }
90 
91  //
92  // Display the name as an add-on name instead of a filename.
93  //
94 
95  if(!is_main_cfg) {
96  // Remove the file extension first.
97  static const std::string wml_suffix = ".cfg";
98 
99  if(base.size() > wml_suffix.size()) {
100  const std::size_t suffix_pos = base.size() - wml_suffix.size();
101  if(base.substr(suffix_pos) == wml_suffix) {
102  base.erase(suffix_pos);
103  }
104  }
105  }
106 
107  if(have_addon_install_info(base)) {
108  // _info.cfg may have the add-on's title starting with 1.11.7,
109  // if the add-on was downloaded using the revised _info.cfg writer.
110  config info_cfg;
111  get_addon_install_info(base, info_cfg);
112 
113  if(!info_cfg.empty() && !info_cfg["title"].empty()) {
114  file = info_cfg["title"].str();
115  continue;
116  }
117  }
118 
119  // Fallback to using a synthetic title with underscores replaced with
120  // whitespace.
121  file = make_addon_title(base);
122  }
123 
124  if(files.size() == 1) {
125  return utils::indent(files.front());
126  }
127 
128  return utils::bullet_list(files);
129 }
130 }
131 
132 namespace gui2::dialogs
133 {
134 
135 REGISTER_DIALOG(wml_error)
136 
137 wml_error::wml_error(const std::string& summary,
138  const std::string& post_summary,
139  const std::vector<std::string>& files,
140  const std::string& details)
141  : modal_dialog(window_id())
142  , have_files_(!files.empty())
143  , have_post_summary_(!post_summary.empty())
144  , report_()
145 {
146  const std::string& file_list_text = format_file_list(files);
147 
148  report_ = summary;
149 
150  if(!file_list_text.empty()) {
151  report_ += "\n" + file_list_text;
152  }
153 
154  if(!post_summary.empty()) {
155  report_ += "\n\n" + post_summary;
156  }
157 
158  report_ += "\n\n";
159  report_ += _("Details:");
160  report_ += "\n\n" + utils::indent(details);
161  // Since this is likely to be pasted into a text file, add a final line
162  // break for convenience, since otherwise the report ends mid-line.
163  report_ += "\n";
164 
165  register_label("summary", true, summary);
166  register_label("post_summary", true, post_summary);
167  register_label("files", true, file_list_text);
168  register_label("details", true, details);
169 }
170 
172 {
173  if(!have_files_) {
174  styled_widget& filelist = find_widget<styled_widget>(&window, "files", false);
176  }
177 
178  if(!have_post_summary_) {
179  styled_widget& post_summary
180  = find_widget<styled_widget>(&window, "post_summary", false);
182  }
183 
184  button& copy_button = find_widget<button>(&window, "copy", false);
185 
187  copy_button, std::bind(&wml_error::copy_report_callback, this));
188 
190  copy_button.set_active(false);
191  copy_button.set_tooltip(_("Clipboard support not found, contact your packager"));
192  }
193 }
194 
196 {
198 }
199 
200 } // end namespace dialogs
void get_addon_install_info(const std::string &addon_name, config &cfg)
Gets the installation info (_info.cfg) for an add-on.
Definition: manager.cpp:106
bool have_addon_install_info(const std::string &addon_name)
Returns true if there is a local installation info (_info.cfg) file for the add-on.
Definition: manager.cpp:101
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:159
bool empty() const
Definition: config.cpp:852
Simple push button.
Definition: button.hpp:36
virtual void set_active(const bool active) override
See styled_widget::set_active.
Definition: button.cpp:63
Abstract base class for all modal dialogs.
WML preprocessor/parser error report dialog.
Definition: wml_error.hpp:36
virtual void pre_show(window &window) override
Actions to be taken before showing the window.
Definition: wml_error.cpp:171
Base class for all visible items.
void set_tooltip(const t_string &tooltip)
void set_visible(const visibility visible)
Definition: widget.cpp:470
@ invisible
The user set the widget invisible, that means:
base class of top level items, the only item which needs to store the final canvases to draw on.
Definition: window.hpp:63
Declarations for File-IO.
static std::string _(const char *str)
Definition: gettext.hpp:93
This file contains the window object, this object is a top level container which has the event manage...
std::string make_addon_title(const std::string &id)
Replaces underscores to dress up file or dirnames as add-on titles.
Definition: info.cpp:319
void copy_to_clipboard(const std::string &text, const bool)
Copies text to the clipboard.
Definition: clipboard.cpp:32
bool available()
Whether wesnoth was compiled with support for a clipboard.
Definition: clipboard.cpp:53
std::string base_name(const std::string &file, const bool remove_extension)
Returns the base filename of a file, with directory name stripped.
std::string directory_name(const std::string &file)
Returns the directory name of a file, with filename stripped.
std::string get_addons_dir()
bool is_path_sep(char c)
Returns whether c is a path separator.
std::string normalize_path(const std::string &fpath, bool normalize_separators, bool resolve_dot_entries)
Returns the absolute path of a file.
REGISTER_DIALOG(editor_edit_unit)
void connect_signal_mouse_left_click(dispatcher &dispatcher, const signal &signal)
Connects a signal handler for a left mouse button click.
Definition: dispatcher.cpp:177
std::string indent(const std::string &string, std::size_t indent_size)
Indent a block of text.
std::string bullet_list(const T &v, std::size_t indent=4, const std::string &bullet=font::unicode_bullet)
Generates a new string containing a bullet list.