The Battle for Wesnoth  1.19.5+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"
25 #include "gui/widgets/button.hpp"
27 #include "gui/widgets/window.hpp"
29 
30 #include "gettext.hpp"
31 
32 #include <functional>
33 
34 namespace
35 {
36 
37 void strip_trailing_dir_separators(std::string& str)
38 {
39  while(filesystem::is_path_sep(str.back())) {
40  str.erase(str.size() - 1);
41  }
42 }
43 
44 std::string format_file_list(const std::vector<std::string>& files_original)
45 {
46  if(files_original.empty()) {
47  return "";
48  }
49 
50  const std::string& addons_path = filesystem::get_addons_dir();
51  std::vector<std::string> files(files_original);
52 
53  for(std::string & file : files)
54  {
55  std::string base;
56  std::string filename = filesystem::base_name(file);
57  std::string parent_path;
58 
59  const bool is_main_cfg = filename == "_main.cfg";
60 
61  if(is_main_cfg) {
62  parent_path = filesystem::directory_name(file) + "/..";
63  } else {
64  parent_path = filesystem::directory_name(file);
65  }
66 
67  // Only proceed to pretty-format the filename if it's from the add-ons
68  // directory.
69  if(filesystem::normalize_path(parent_path) != filesystem::normalize_path(addons_path)) {
70  continue;
71  }
72 
73  if(is_main_cfg) {
74  base = filesystem::directory_name(file);
75  // HACK: fool filesystem::base_name() into giving us the parent directory name
76  // alone by making base seem not like a directory path,
77  // otherwise it returns an empty string.
78  strip_trailing_dir_separators(base);
79  base = filesystem::base_name(base);
80  } else {
81  base = filename;
82  }
83 
84  if(base.empty()) {
85  // We did something wrong. In the interest of not messing up the
86  // report, leave the original filename intact.
87  continue;
88  }
89 
90  //
91  // Display the name as an add-on name instead of a filename.
92  //
93 
94  if(!is_main_cfg) {
95 
96  if(base.size() > filesystem::wml_extension.size()) {
97  const std::size_t suffix_pos = base.size() - filesystem::wml_extension.size();
98  if(base.substr(suffix_pos) == filesystem::wml_extension) {
99  base.erase(suffix_pos);
100  }
101  }
102  }
103 
104  if(have_addon_install_info(base)) {
105  // _info.cfg may have the add-on's title starting with 1.11.7,
106  // if the add-on was downloaded using the revised _info.cfg writer.
107  config info_cfg;
108  get_addon_install_info(base, info_cfg);
109 
110  if(!info_cfg.empty() && !info_cfg["title"].empty()) {
111  file = info_cfg["title"].str();
112  continue;
113  }
114  }
115 
116  // Fallback to using a synthetic title with underscores replaced with
117  // whitespace.
118  file = make_addon_title(base);
119  }
120 
121  if(files.size() == 1) {
122  return utils::indent(files.front());
123  }
124 
125  return utils::bullet_list(files);
126 }
127 }
128 
129 namespace gui2::dialogs
130 {
131 
132 REGISTER_DIALOG(wml_error)
133 
134 wml_error::wml_error(const std::string& summary,
135  const std::string& post_summary,
136  const std::vector<std::string>& files,
137  const std::string& details)
138  : modal_dialog(window_id())
139  , have_files_(!files.empty())
140  , have_post_summary_(!post_summary.empty())
141  , report_()
142 {
143  const std::string& file_list_text = format_file_list(files);
144 
145  report_ = summary;
146 
147  if(!file_list_text.empty()) {
148  report_ += "\n" + file_list_text;
149  }
150 
151  if(!post_summary.empty()) {
152  report_ += "\n\n" + post_summary;
153  }
154 
155  report_ += "\n\n";
156  report_ += _("Details:");
157  report_ += "\n\n" + utils::indent(details);
158  // Since this is likely to be pasted into a text file, add a final line
159  // break for convenience, since otherwise the report ends mid-line.
160  report_ += "\n";
161 
162  register_label("summary", true, summary);
163  register_label("post_summary", true, post_summary);
164  register_label("files", true, file_list_text);
165  register_label("details", true, details);
166 }
167 
169 {
170  if(!have_files_) {
171  styled_widget& filelist = find_widget<styled_widget>("files");
173  }
174 
175  if(!have_post_summary_) {
176  styled_widget& post_summary
177  = find_widget<styled_widget>("post_summary");
179  }
180 
181  button& copy_button = find_widget<button>("copy");
182 
184  copy_button, std::bind(&wml_error::copy_report_callback, this));
185 }
186 
188 {
190 }
191 
192 } // 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:107
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:102
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:172
bool empty() const
Definition: config.cpp:849
Simple push button.
Definition: button.hpp:36
Abstract base class for all modal dialogs.
virtual void pre_show() override
Actions to be taken before showing the window.
Definition: wml_error.cpp:168
void set_visible(const visibility visible)
Definition: widget.cpp:479
@ invisible
The user set the widget invisible, that means:
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:320
void copy_to_clipboard(const std::string &text)
Copies text to the clipboard.
Definition: clipboard.cpp:27
const std::string wml_extension
Definition: filesystem.hpp:81
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.
std::string filename
Filename.