The Battle for Wesnoth  1.15.1+dev
wml_error.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2014 - 2018 by Iris Morelle <shadowm2006@gmail.com>
3  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 #define GETTEXT_DOMAIN "wesnoth-lib"
16 
18 
19 #include "addon/info.hpp"
20 // Needs the full path to avoid confusion with gui/dialogs/addon/manager.hpp.
21 #include "../../addon/manager.hpp"
22 #include "desktop/clipboard.hpp"
23 #include "filesystem.hpp"
25 #include "gui/widgets/button.hpp"
27 #include "gui/widgets/settings.hpp"
28 #include "gui/widgets/window.hpp"
30 
31 #include "gettext.hpp"
32 
33 #include "utils/functional.hpp"
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 cfg;
111  get_addon_install_info(base, cfg);
112 
113  const config& info_cfg = cfg.child("info");
114 
115  if(info_cfg && !info_cfg["title"].empty()) {
116  file = info_cfg["title"].str();
117  continue;
118  }
119  }
120 
121  // Fallback to using a synthetic title with underscores replaced with
122  // whitespace.
123  file = make_addon_title(base);
124  }
125 
126  if(files.size() == 1) {
127  return utils::indent(files.front());
128  }
129 
130  return utils::bullet_list(files);
131 }
132 }
133 
134 namespace gui2
135 {
136 namespace dialogs
137 {
138 
139 /*WIKI
140  * @page = GUIWindowDefinitionWML
141  * @order = 2_wml_error
142  *
143  * == WML error ==
144  *
145  * Dialog used to report WML parser or preprocessor errors.
146  *
147  * @begin{table}{dialog_widgets}
148  *
149  * summary & & styled_widget & m &
150  * Label used for displaying a brief summary of the error(s). $
151  *
152  * files & & styled_widget & m &
153  * Label used to display the list of affected add-ons or files, if
154  * applicable. It is hidden otherwise. It is recommended to place it
155  * after the summary label. $
156  *
157  * post_summary & & styled_widget & m &
158  * Label used for displaying instructions for reporting the error.
159  * It is recommended to place it after the file list label. It may be
160  * hidden if empty. $
161  *
162  * details & & styled_widget & m &
163  * Full report of the parser or preprocessor error(s) found. $
164  *
165  * copy & & button & m &
166  * Button that the user can click on to copy the error report to the
167  * system clipboard. $
168  *
169  * @end{table}
170  */
171 
173 
174 wml_error::wml_error(const std::string& summary,
175  const std::string& post_summary,
176  const std::vector<std::string>& files,
177  const std::string& details)
178  : have_files_(!files.empty())
179  , have_post_summary_(!post_summary.empty())
180  , report_()
181 {
182  set_restore(true);
183 
184  const std::string& file_list_text = format_file_list(files);
185 
186  report_ = summary;
187 
188  if(!file_list_text.empty()) {
189  report_ += "\n" + file_list_text;
190  }
191 
192  if(!post_summary.empty()) {
193  report_ += "\n\n" + post_summary;
194  }
195 
196  report_ += "\n\n";
197  report_ += _("Details:");
198  report_ += "\n\n" + utils::indent(details);
199  // Since this is likely to be pasted into a text file, add a final line
200  // break for convenience, since otherwise the report ends mid-line.
201  report_ += "\n";
202 
203  register_label("summary", true, summary);
204  register_label("post_summary", true, post_summary);
205  register_label("files", true, file_list_text);
206  register_label("details", true, details);
207 }
208 
210 {
211  if(!have_files_) {
212  styled_widget& filelist = find_widget<styled_widget>(&window, "files", false);
214  }
215 
216  if(!have_post_summary_) {
217  styled_widget& post_summary
218  = find_widget<styled_widget>(&window, "post_summary", false);
220  }
221 
222  button& copy_button = find_widget<button>(&window, "copy", false);
223 
225  copy_button, std::bind(&wml_error::copy_report_callback, this));
226 
228  copy_button.set_active(false);
229  copy_button.set_tooltip(_("Clipboard support not found, contact your packager"));
230  }
231 }
232 
234 {
236 }
237 
238 } // end namespace dialogs
239 } // end namespace gui2
config & child(config_key_type key, int n=0)
Returns the nth child with the given key, or a reference to an invalid config if there is none...
Definition: config.cpp:420
bool available()
Whether wesnoth was compiled with support for a clipboard.
Definition: clipboard.cpp:56
This file contains the window object, this object is a top level container which has the event manage...
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:104
STL namespace.
std::string normalize_path(const std::string &fpath, bool normalize_separators, bool resolve_dot_entries)
Returns the absolute path of a file.
Generic file dialog.
Definition: field-fwd.hpp:22
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:99
static UNUSEDNOWARN std::string _(const char *str)
Definition: gettext.hpp:91
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.
void set_tooltip(const t_string &tooltip)
This file contains the settings handling of the widget library.
WML preprocessor/parser error report dialog.
Definition: wml_error.hpp:25
void set_visible(const visibility visible)
Definition: widget.cpp:473
void connect_signal_mouse_left_click(dispatcher &dispatcher, const signal_function &signal)
Connects a signal handler for a left mouse button click.
Definition: dispatcher.cpp:233
Various uncategorised dialogs.
bool is_path_sep(char c)
Returns whether c is a path separator.
std::stringstream & wml_error()
Use this logger to send errors due to deprecated WML.
Definition: log.cpp:269
The user set the widget invisible, that means:
Declarations for File-IO.
Base class for all visible items.
std::string make_addon_title(const std::string &id)
Replaces underscores to dress up file or dirnames as add-on titles.
Definition: info.cpp:226
std::string base_name(const std::string &file, const bool remove_extension)
Returns the base filename of a file, with directory name stripped.
virtual void set_active(const bool active) override
See styled_widget::set_active.
Definition: button.cpp:62
void copy_to_clipboard(const std::string &text, const bool)
Copies text to the clipboard.
Definition: clipboard.cpp:35
std::string get_addons_dir()
virtual void pre_show(window &window) override
Inherited from modal_dialog.
Definition: wml_error.cpp:209
std::string indent(const std::string &string, std::size_t indent_size)
Indent a block of text.
Simple push button.
Definition: button.hpp:35
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:92
base class of top level items, the only item which needs to store the final canvases to draw on ...
Definition: window.hpp:63
std::string directory_name(const std::string &file)
Returns the directory name of a file, with filename stripped.