The Battle for Wesnoth  1.19.12+dev
custom_tod.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2025
3  by Mark de Wever <koraq@xs4all.nl>
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 "desktop/clipboard.hpp"
21 #include "display.hpp"
22 #include "filesystem.hpp"
23 #include "formatter.hpp"
24 #include "gettext.hpp"
25 #include "gui/auxiliary/field.hpp"
27 #include "gui/dialogs/message.hpp"
28 #include "gui/widgets/button.hpp"
29 #include "gui/widgets/image.hpp"
30 #include "gui/widgets/label.hpp"
31 #include "gui/widgets/slider.hpp"
32 #include "gui/widgets/text_box.hpp"
33 #include "sound.hpp"
34 
35 #include <boost/filesystem.hpp>
36 #include <functional>
37 #include <utility>
38 
39 namespace gui2::dialogs
40 {
41 
43 {
44  static std::string type = "image";
45  return {type, tod.image};
46 }
47 
49 {
50  static std::string type = "mask";
51  return {type, tod.image_mask};
52 }
53 
55 {
56  static std::string type = "sound";
57  return {type, tod.sounds};
58 }
59 
61 
62 custom_tod::custom_tod(const std::vector<time_of_day>& times, int current_time, const std::string& addon_id)
63  : modal_dialog(window_id())
64  , addon_id_(addon_id)
65  , times_(times)
66  , current_tod_(current_time)
67  , color_field_r_(register_integer("tod_red", true))
68  , color_field_g_(register_integer("tod_green", true))
69  , color_field_b_(register_integer("tod_blue", true))
70 {
71  if(times_.empty())
72  {
73  times_.push_back(time_of_day());
74  }
75 }
76 
78 {
79  static std::map<std::string, tod_attribute_getter> metadata_stuff {
80  {"image", tod_getter_image},
81  {"mask", tod_getter_mask },
82  {"sound", tod_getter_sound}
83  };
84 
85  add_to_tab_order(find_widget<text_box>("tod_name", false, true));
86  add_to_tab_order(find_widget<text_box>("tod_desc", false, true));
87  add_to_tab_order(find_widget<text_box>("tod_id", false, true));
88 
89  for(const auto& data : metadata_stuff) {
90  button& copy_w = find_widget<button>("copy_" + data.first);
91 
93  }
94 
96  find_widget<button>("browse_image"),
97  std::bind(&custom_tod::select_file<tod_getter_image>, this, "data/core/images/misc"));
98 
100  find_widget<button>("browse_mask"),
101  std::bind(&custom_tod::select_file<tod_getter_mask>, this, "data/core/images"));
102 
104  find_widget<button>("browse_sound"),
105  std::bind(&custom_tod::select_file<tod_getter_sound>, this, "data/core/sounds/ambient"));
106 
108  find_widget<button>("preview_image"),
109  std::bind(&custom_tod::update_image, this, "image"));
110 
112  find_widget<button>("preview_mask"),
113  std::bind(&custom_tod::update_image, this, "mask"));
114 
116  find_widget<button>("preview_sound"),
117  std::bind(&custom_tod::play_sound, this));
118 
120  find_widget<button>("next_tod"),
121  std::bind(&custom_tod::do_next_tod, this));
122 
124  find_widget<button>("previous_tod"),
125  std::bind(&custom_tod::do_prev_tod, this));
126 
128  find_widget<button>("new"),
129  std::bind(&custom_tod::do_new_tod, this));
130 
132  find_widget<button>("delete"),
133  std::bind(&custom_tod::do_delete_tod, this));
134 
136  find_widget<button>("preview_color"),
137  std::bind(&custom_tod::preview_schedule, this));
138 
140  find_widget<slider>("lawful_bonus"),
141  std::bind(&custom_tod::update_lawful_bonus, this));
142 
145  std::bind(&custom_tod::color_slider_callback, this, COLOR_R));
146 
149  std::bind(&custom_tod::color_slider_callback, this, COLOR_G));
150 
153  std::bind(&custom_tod::color_slider_callback, this, COLOR_B));
154 
156 }
157 
158 template<custom_tod::string_pair(*fptr)(const time_of_day&)>
159 void custom_tod::select_file(const std::string& default_dir)
160 {
161  const string_pair& data = (*fptr)(get_selected_tod());
162 
163  std::string fn = filesystem::base_name(data.second);
164  std::string dn = filesystem::directory_name(fn);
165  if(dn.empty()) {
166  dn = default_dir;
167  }
168 
170 
171  dlg.set_title(_("Choose File"))
172  .set_ok_label(_("Select"))
173  .set_path(dn)
174  .set_read_only(true);
175 
176  // If the file is found inside Wesnoth's data, give its relative path
177  // if not, ask user if they want to copy it into their addon.
178  // If yes, copy and return correct relative path inside addon.
179  // return empty otherwise.
180  auto find_or_copy = [](const std::string& path, const std::string& addon_id, const std::string& type) {
181  const std::string message
182  = _("This file is outside Wesnoth’s data dirs. Do you wish to copy it into your add-on?");
183  const auto optional_path = filesystem::to_asset_path(path, addon_id, type);
184 
185  if(optional_path.has_value()) {
186  return optional_path.value();
189  output_path /= type;
190  output_path /= boost::filesystem::path(path).filename();
191  filesystem::copy_file(path, output_path.string());
192  return output_path.filename().string();
193  } else {
194  return std::string();
195  }
196  };
197 
198  if(dlg.show()) {
199  if(data.first == "image") {
200  times_[current_tod_].image = find_or_copy(dlg.path(), addon_id_, "images");
201  } else if(data.first == "mask") {
202  times_[current_tod_].image_mask = find_or_copy(dlg.path(), addon_id_, "images");
203  } else if(data.first == "sound") {
204  times_[current_tod_].sounds = find_or_copy(dlg.path(), addon_id_, "sounds");
205  }
206  }
207 
209 }
210 
212 {
213  current_tod_ = (current_tod_ + 1) % times_.size();
215 }
216 
218 {
219  current_tod_ = (current_tod_ ? current_tod_ : times_.size()) - 1;
221 }
222 
224 {
225  times_.insert(times_.begin() + current_tod_, time_of_day());
227 }
228 
230 {
231  assert(times_.begin() + current_tod_ < times_.end());
232 
233  if(times_.size() == 1) {
234  times_.emplace_back();
235  } else {
236  times_.erase(times_.begin() + current_tod_);
237 
238  if(times_.begin() + current_tod_ >= times_.end()) {
239  current_tod_ = times_.size() - 1;
240  }
241  }
242 
244 }
245 
247 {
248  try {
249  return times_.at(current_tod_);
250  } catch(const std::out_of_range&) {
251  throw std::string("Attempted to fetch a non-existent ToD!");
252  }
253 }
254 
256 {
257  time_of_day& current_tod = times_[current_tod_];
258 
259  switch(type)
260  {
261  case COLOR_R:
262  current_tod.color.r = color_field_r_->get_widget_value();
263  break;
264  case COLOR_G :
265  current_tod.color.g = color_field_g_->get_widget_value();
266  break;
267  case COLOR_B :
268  current_tod.color.b = color_field_b_->get_widget_value();
269  break;
270  }
271 
273 }
274 
276  std::string sound_path = find_widget<text_box>("path_sound").get_value();
278 }
279 
280 void custom_tod::update_image(const std::string& id_stem) {
281  std::string img_path = find_widget<text_box>("path_"+id_stem).get_value();
282  find_widget<image>("current_tod_" + id_stem).set_label(img_path);
283 
285 }
286 
288 {
290  assert(disp && "Display pointer is null!");
291 
292  // The display handles invaliding whatever tiles need invalidating.
293  disp->update_tod(&get_selected_tod());
294 
296 }
297 
299 {
300  times_[current_tod_].lawful_bonus = find_widget<slider>("lawful_bonus").get_value();
301 }
302 
304 {
305  const time_of_day& current_tod = get_selected_tod();
306 
307  find_widget<text_box>("tod_name").set_value(current_tod.name);
308  find_widget<text_box>("tod_desc").set_value(current_tod.description);
309  find_widget<text_box>("tod_id").set_value(current_tod.id);
310 
311  find_widget<text_box>("path_image").set_value(current_tod.image);
312  find_widget<text_box>("path_mask").set_value(current_tod.image_mask);
313  find_widget<text_box>("path_sound").set_value(current_tod.sounds);
314 
315  find_widget<image>("current_tod_image").set_image(current_tod.image);
316  find_widget<image>("current_tod_mask").set_image(current_tod.image_mask);
317 
318  find_widget<slider>("lawful_bonus").set_value(current_tod.lawful_bonus);
319 
320  color_field_r_->set_widget_value(current_tod.color.r);
321  color_field_g_->set_widget_value(current_tod.color.g);
322  color_field_b_->set_widget_value(current_tod.color.b);
323 
324  const std::string new_index_str = formatter() << (current_tod_ + 1) << "/" << times_.size();
325  find_widget<label>("tod_number").set_label(new_index_str);
326 
328 }
329 
330 void custom_tod::copy_to_clipboard_callback(const std::pair<std::string, tod_attribute_getter>& data)
331 {
332  auto& [type, getter] = data;
333  button& copy_w = find_widget<button>("copy_" + type);
335  copy_w.set_success(true);
336 }
337 
338 /** Quickly preview the schedule changes and color */
340 {
342 }
343 
345 {
346  /* Update times_ with values from the dialog */
347  times_[current_tod_].name = find_widget<text_box>("tod_name").get_value();
348  times_[current_tod_].description = find_widget<text_box>("tod_desc").get_value();
349  times_[current_tod_].id = find_widget<text_box>("tod_id").get_value();
350 
351  times_[current_tod_].image = find_widget<text_box>("path_image").get_value();
352  times_[current_tod_].image_mask = find_widget<text_box>("path_mask").get_value();
353  times_[current_tod_].sounds = find_widget<text_box>("path_sound").get_value();
354 
355  times_[current_tod_].lawful_bonus = find_widget<slider>("lawful_bonus").get_value();
356 
360 }
361 
362 const std::vector<time_of_day> custom_tod::get_schedule()
363 {
364  update_schedule();
365  return times_;
366 }
367 
368 void custom_tod::register_callback(std::function<void(std::vector<time_of_day>)> update_func)
369 {
370  update_map_and_schedule_ = std::move(update_func);
371 }
372 
374 {
376 }
377 
378 } // namespace dialogs
Sort-of-Singleton that many classes, both GUI and non-GUI, use to access the game data.
Definition: display.hpp:88
void update_tod(const time_of_day *tod_override=nullptr)
Applies r,g,b coloring to the map.
Definition: display.cpp:385
static display * get_singleton()
Returns the display object if a display object exists.
Definition: display.hpp:102
std::ostringstream wrapper.
Definition: formatter.hpp:40
Simple push button.
Definition: button.hpp:36
void set_success(bool success)
Definition: button.cpp:89
void update_schedule()
Update current TOD with values from the GUI.
Definition: custom_tod.cpp:344
std::pair< std::string, std::string > string_pair
The execute function.
Definition: custom_tod.hpp:34
void update_image(const std::string &id_stem)
Update image when preview is pressed.
Definition: custom_tod.cpp:280
field_integer * color_field_g_
Definition: custom_tod.hpp:102
void color_slider_callback(COLOR_TYPE type)
Definition: custom_tod.cpp:255
const std::string addon_id_
ID of the current addon.
Definition: custom_tod.hpp:93
void select_file(const std::string &default_dir)
Definition: custom_tod.cpp:159
void preview_schedule()
Callback for preview button.
Definition: custom_tod.cpp:339
std::vector< time_of_day > times_
Available time of days.
Definition: custom_tod.hpp:96
COLOR_TYPE
enum used in identifying sliders
Definition: custom_tod.hpp:44
virtual void post_show() override
Actions to be taken after the window has been shown.
Definition: custom_tod.cpp:373
const time_of_day & get_selected_tod() const
Definition: custom_tod.cpp:246
void do_next_tod()
Callback for the next tod button.
Definition: custom_tod.cpp:211
field_integer * color_field_b_
Definition: custom_tod.hpp:103
field_integer * color_field_r_
Definition: custom_tod.hpp:101
const std::vector< time_of_day > get_schedule()
Return current schedule.
Definition: custom_tod.cpp:362
void copy_to_clipboard_callback(const std::pair< std::string, tod_attribute_getter > &data)
Definition: custom_tod.cpp:330
void play_sound()
Play sound when play is pressed.
Definition: custom_tod.cpp:275
int current_tod_
Current time of day (ToD) index.
Definition: custom_tod.hpp:99
virtual void pre_show() override
Actions to be taken before showing the window.
Definition: custom_tod.cpp:77
void register_callback(std::function< void(std::vector< time_of_day >)>)
Register callback for update.
Definition: custom_tod.cpp:368
std::function< void(std::vector< time_of_day >)> update_map_and_schedule_
Definition: custom_tod.hpp:70
file_dialog & set_ok_label(const std::string &value)
Sets the OK button label.
file_dialog & set_path(const std::string &value)
Sets the initial file selection.
file_dialog & set_title(const std::string &value)
Sets the current dialog title text.
Definition: file_dialog.hpp:59
file_dialog & set_read_only(bool value)
Whether to provide user interface elements for manipulating existing objects.
std::string path() const
Gets the current file selection.
Main class to show messages to the user.
Definition: message.hpp:36
@ yes_no_buttons
Shows a yes and no button.
Definition: message.hpp:81
Abstract base class for all modal dialogs.
bool show(const unsigned auto_close_time=0)
Shows the window.
styled_widget * get_widget()
Definition: field.hpp:192
void set_widget_value(CT value)
Sets the value of the field.
Definition: field.hpp:343
T get_widget_value()
Gets the value of the field.
Definition: field.hpp:378
void invalidate_layout()
Updates the size of the window.
Definition: window.cpp:760
static const std::string & type()
Static type getter that does not rely on the widget being constructed.
void add_to_tab_order(widget *widget, int at=-1)
Add the widget to the tabbing order.
Definition: window.cpp:1219
map_display and display: classes which take care of displaying the map and game-data on the screen.
Implements some helper classes to ease adding fields to a dialog and hide the synchronization needed.
Declarations for File-IO.
static std::string _(const char *str)
Definition: gettext.hpp:97
void copy_to_clipboard(const std::string &text)
Copies text to the clipboard.
Definition: clipboard.cpp:27
std::string base_name(const std::string &file, const bool remove_extension)
Returns the base filename of a file, with directory name stripped.
void copy_file(const std::string &src, const std::string &dest)
Read a file and then writes it back out.
std::string directory_name(const std::string &file)
Returns the directory name of a file, with filename stripped.
std::string get_current_editor_dir(const std::string &addon_id)
utils::optional< std::string > to_asset_path(const std::string &path, const std::string &addon_id, const std::string &asset_type)
Helper function to convert absolute path to wesnoth relative path.
std::string path
Definition: filesystem.cpp:102
static custom_tod::string_pair tod_getter_mask(const time_of_day &tod)
Definition: custom_tod.cpp:48
REGISTER_DIALOG(editor_edit_unit)
static custom_tod::string_pair tod_getter_image(const time_of_day &tod)
Definition: custom_tod.cpp:42
static custom_tod::string_pair tod_getter_sound(const time_of_day &tod)
Definition: custom_tod.cpp:54
void connect_signal_notify_modified(dispatcher &dispatcher, const signal_notification &signal)
Connects a signal handler for getting a notification upon modification.
Definition: dispatcher.cpp:189
void connect_signal_mouse_left_click(dispatcher &dispatcher, const signal &signal)
Connects a signal handler for a left mouse button click.
Definition: dispatcher.cpp:163
void show_message(const std::string &title, const std::string &msg, const std::string &button_caption, const bool auto_close, const bool message_use_markup, const bool title_use_markup)
Shows a message to the user.
Definition: message.cpp:148
@ OK
Dialog was closed with the OK button.
Definition: retval.hpp:35
void play_sound(const std::string &files, channel_group group, unsigned int repeats)
Definition: sound.cpp:1043
@ SOUND_SOURCES
Definition: sound.hpp:30
std::string_view data
Definition: picture.cpp:188
Object which defines a time of day with associated bonuses, image, sounds etc.
Definition: time_of_day.hpp:57
std::string id
Definition: time_of_day.hpp:90
tod_color color
The color modifications that should be made to the game board to reflect the time of day.
int lawful_bonus
The % bonus lawful units receive.
Definition: time_of_day.hpp:83
t_string description
Definition: time_of_day.hpp:89
t_string name
Definition: time_of_day.hpp:88
std::string image
The image to be displayed in the game status.
Definition: time_of_day.hpp:87
std::string sounds
List of "ambient" sounds associated with this time_of_day, Played at the beginning of turn.
std::string image_mask
The image that is to be laid over all images while this time of day lasts.
Definition: time_of_day.hpp:96