The Battle for Wesnoth  1.19.4+dev
custom_tod.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2024
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-editor"
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 <functional>
36 #include <boost/filesystem.hpp>
37 
38 namespace gui2::dialogs
39 {
40 
42 {
43  static std::string type = "image";
44  return {type, tod.image};
45 }
46 
48 {
49  static std::string type = "mask";
50  return {type, tod.image_mask};
51 }
52 
54 {
55  static std::string type = "sound";
56  return {type, tod.sounds};
57 }
58 
60 
61 custom_tod::custom_tod(const std::vector<time_of_day>& times, int current_time, const std::string addon_id)
62  : modal_dialog(window_id())
63  , addon_id_(addon_id)
64  , times_(times)
65  , current_tod_(current_time)
66  , color_field_r_(register_integer("tod_red", true))
67  , color_field_g_(register_integer("tod_green", true))
68  , color_field_b_(register_integer("tod_blue", true))
69 {
70  if(times_.empty())
71  {
72  times_.push_back(time_of_day());
73  }
74 }
75 
77 {
78  static std::map<std::string, tod_attribute_getter> metadata_stuff {
79  {"image", tod_getter_image},
80  {"mask", tod_getter_mask },
81  {"sound", tod_getter_sound}
82  };
83 
84  add_to_tab_order(find_widget<text_box>("tod_name", false, true));
85  add_to_tab_order(find_widget<text_box>("tod_desc", false, true));
86  add_to_tab_order(find_widget<text_box>("tod_id", false, true));
87 
88  for(const auto& data : metadata_stuff) {
89  button& copy_w = find_widget<button>("copy_" + data.first);
90 
92  }
93 
95  find_widget<button>("browse_image"),
96  std::bind(&custom_tod::select_file<tod_getter_image>, this, "data/core/images/misc"));
97 
99  find_widget<button>("browse_mask"),
100  std::bind(&custom_tod::select_file<tod_getter_mask>, this, "data/core/images"));
101 
103  find_widget<button>("browse_sound"),
104  std::bind(&custom_tod::select_file<tod_getter_sound>, this, "data/core/sounds/ambient"));
105 
107  find_widget<button>("preview_image"),
108  std::bind(&custom_tod::update_image, this, "image"));
109 
111  find_widget<button>("preview_mask"),
112  std::bind(&custom_tod::update_image, this, "mask"));
113 
115  find_widget<button>("preview_sound"),
116  std::bind(&custom_tod::play_sound, this));
117 
119  find_widget<button>("next_tod"),
120  std::bind(&custom_tod::do_next_tod, this));
121 
123  find_widget<button>("previous_tod"),
124  std::bind(&custom_tod::do_prev_tod, this));
125 
127  find_widget<button>("new"),
128  std::bind(&custom_tod::do_new_tod, this));
129 
131  find_widget<button>("delete"),
132  std::bind(&custom_tod::do_delete_tod, this));
133 
135  find_widget<button>("preview_color"),
136  std::bind(&custom_tod::preview_schedule, this));
137 
139  find_widget<slider>("lawful_bonus"),
140  std::bind(&custom_tod::update_lawful_bonus, this));
141 
144  std::bind(&custom_tod::color_slider_callback, this, COLOR_R));
145 
148  std::bind(&custom_tod::color_slider_callback, this, COLOR_G));
149 
152  std::bind(&custom_tod::color_slider_callback, this, COLOR_B));
153 
155 }
156 
157 template<custom_tod::string_pair(*fptr)(const time_of_day&)>
158 void custom_tod::select_file(const std::string& default_dir)
159 {
160  const string_pair& data = (*fptr)(get_selected_tod());
161 
162  std::string fn = filesystem::base_name(data.second);
163  std::string dn = filesystem::directory_name(fn);
164  if(dn.empty()) {
165  dn = default_dir;
166  }
167 
169 
170  dlg.set_title(_("Choose File"))
171  .set_ok_label(_("Select"))
172  .set_path(dn)
173  .set_read_only(true);
174 
175  if(dlg.show()) {
176  dn = dlg.path();
177  const std::string& message
178  = _("This file is outside Wesnoth’s data dirs. Do you wish to copy it into your add-on?");
179 
180  if(data.first == "image") {
181  if (!filesystem::to_asset_path(dn, addon_id_, "images")) {
183  filesystem::copy_file(dlg.path(), dn);
184  }
185  }
186  times_[current_tod_].image = dn;
187  } else if(data.first == "mask") {
188  if (!filesystem::to_asset_path(dn, addon_id_, "images")) {
190  filesystem::copy_file(dlg.path(), dn);
191  }
192  }
193  times_[current_tod_].image_mask = dn;
194  } else if(data.first == "sound") {
195  if (!filesystem::to_asset_path(dn, addon_id_, "sounds")) {
197  filesystem::copy_file(dlg.path(), dn);
198  }
199  }
200  times_[current_tod_].sounds = dn;
201  }
202  }
203 
205 }
206 
208 {
209  current_tod_ = (current_tod_ + 1) % times_.size();
211 }
212 
214 {
215  current_tod_ = (current_tod_ ? current_tod_ : times_.size()) - 1;
217 }
218 
220 {
221  times_.insert(times_.begin() + current_tod_, time_of_day());
223 }
224 
226 {
227  assert(times_.begin() + current_tod_ < times_.end());
228 
229  if(times_.size() == 1) {
230  times_.emplace_back();
231  } else {
232  times_.erase(times_.begin() + current_tod_);
233 
234  if(times_.begin() + current_tod_ >= times_.end()) {
235  current_tod_ = times_.size() - 1;
236  }
237  }
238 
240 }
241 
243 {
244  try {
245  return times_.at(current_tod_);
246  } catch(const std::out_of_range&) {
247  throw std::string("Attempted to fetch a non-existant ToD!");
248  }
249 }
250 
252 {
253  time_of_day& current_tod = times_[current_tod_];
254 
255  switch(type)
256  {
257  case COLOR_R:
258  current_tod.color.r = color_field_r_->get_widget_value();
259  break;
260  case COLOR_G :
261  current_tod.color.g = color_field_g_->get_widget_value();
262  break;
263  case COLOR_B :
264  current_tod.color.b = color_field_b_->get_widget_value();
265  break;
266  }
267 
269 }
270 
272  std::string sound_path = find_widget<text_box>("path_sound").get_value();
274 }
275 
276 void custom_tod::update_image(const std::string& id_stem) {
277  std::string img_path = find_widget<text_box>("path_"+id_stem).get_value();
278  find_widget<image>("current_tod_" + id_stem).set_label(img_path);
279 
281 }
282 
284 {
286  assert(disp && "Display pointer is null!");
287 
288  // The display handles invaliding whatever tiles need invalidating.
289  disp->update_tod(&get_selected_tod());
290 
292 }
293 
295 {
296  times_[current_tod_].lawful_bonus = find_widget<slider>("lawful_bonus").get_value();
297 }
298 
300 {
301  const time_of_day& current_tod = get_selected_tod();
302 
303  find_widget<text_box>("tod_name").set_value(current_tod.name);
304  find_widget<text_box>("tod_desc").set_value(current_tod.description);
305  find_widget<text_box>("tod_id").set_value(current_tod.id);
306 
307  find_widget<text_box>("path_image").set_value(current_tod.image);
308  find_widget<text_box>("path_mask").set_value(current_tod.image_mask);
309  find_widget<text_box>("path_sound").set_value(current_tod.sounds);
310 
311  find_widget<image>("current_tod_image").set_image(current_tod.image);
312  find_widget<image>("current_tod_mask").set_image(current_tod.image_mask);
313 
314  find_widget<slider>("lawful_bonus").set_value(current_tod.lawful_bonus);
315 
316  color_field_r_->set_widget_value(current_tod.color.r);
317  color_field_g_->set_widget_value(current_tod.color.g);
318  color_field_b_->set_widget_value(current_tod.color.b);
319 
320  const std::string new_index_str = formatter() << (current_tod_ + 1) << "/" << times_.size();
321  find_widget<label>("tod_number").set_label(new_index_str);
322 
324 }
325 
326 void custom_tod::copy_to_clipboard_callback(std::pair<std::string, tod_attribute_getter> data)
327 {
328  auto& [type, getter] = data;
329  button& copy_w = find_widget<button>("copy_" + type);
331  copy_w.set_success(true);
332 }
333 
334 /** Quickly preview the schedule changes and color */
336 {
338 }
339 
341 {
342  /* Update times_ with values from the dialog */
343  times_[current_tod_].name = find_widget<text_box>("tod_name").get_value();
344  times_[current_tod_].description = find_widget<text_box>("tod_desc").get_value();
345  times_[current_tod_].id = find_widget<text_box>("tod_id").get_value();
346 
347  times_[current_tod_].image = find_widget<text_box>("path_image").get_value();
348  times_[current_tod_].image_mask = find_widget<text_box>("path_mask").get_value();
349  times_[current_tod_].sounds = find_widget<text_box>("path_sound").get_value();
350 
351  times_[current_tod_].lawful_bonus = find_widget<slider>("lawful_bonus").get_value();
352 
356 }
357 
358 const std::vector<time_of_day> custom_tod::get_schedule()
359 {
360  update_schedule();
361  return times_;
362 }
363 
364 void custom_tod::register_callback(std::function<void(std::vector<time_of_day>)> update_func)
365 {
366  update_map_and_schedule_ = update_func;
367 }
368 
370 {
372 }
373 
374 } // namespace dialogs
Sort-of-Singleton that many classes, both GUI and non-GUI, use to access the game data.
Definition: display.hpp:89
void update_tod(const time_of_day *tod_override=nullptr)
Applies r,g,b coloring to the map.
Definition: display.cpp:397
static display * get_singleton()
Returns the display object if a display object exists.
Definition: display.hpp:103
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:340
std::pair< std::string, std::string > string_pair
The execute function.
Definition: custom_tod.hpp:38
void update_image(const std::string &id_stem)
Update image when preview is pressed.
Definition: custom_tod.cpp:276
field_integer * color_field_g_
Definition: custom_tod.hpp:106
void color_slider_callback(COLOR_TYPE type)
Definition: custom_tod.cpp:251
const std::string addon_id_
ID of the current addon.
Definition: custom_tod.hpp:97
void select_file(const std::string &default_dir)
Definition: custom_tod.cpp:158
void preview_schedule()
Callback for preview button.
Definition: custom_tod.cpp:335
std::vector< time_of_day > times_
Available time of days.
Definition: custom_tod.hpp:100
COLOR_TYPE
enum used in identifying sliders
Definition: custom_tod.hpp:48
void copy_to_clipboard_callback(std::pair< std::string, tod_attribute_getter > data)
Definition: custom_tod.cpp:326
virtual void post_show() override
Actions to be taken after the window has been shown.
Definition: custom_tod.cpp:369
const time_of_day & get_selected_tod() const
Definition: custom_tod.cpp:242
void do_next_tod()
Callback for the next tod button.
Definition: custom_tod.cpp:207
field_integer * color_field_b_
Definition: custom_tod.hpp:107
field_integer * color_field_r_
Definition: custom_tod.hpp:105
const std::vector< time_of_day > get_schedule()
Return current schedule.
Definition: custom_tod.cpp:358
void play_sound()
Play sound when play is pressed.
Definition: custom_tod.cpp:271
int current_tod_
Current time of day (ToD) index.
Definition: custom_tod.hpp:103
virtual void pre_show() override
Actions to be taken before showing the window.
Definition: custom_tod.cpp:76
void register_callback(std::function< void(std::vector< time_of_day >)>)
Register callback for update.
Definition: custom_tod.cpp:364
std::function< void(std::vector< time_of_day >)> update_map_and_schedule_
Definition: custom_tod.hpp:74
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
window * get_window()
Get the parent window.
Definition: widget.cpp:117
void invalidate_layout()
Updates the size of the window.
Definition: window.cpp:773
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:1237
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:93
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.
bool to_asset_path(std::string &path, std::string addon_id, std::string asset_type)
Helper function to convert absolute path to wesnoth relative path.
static custom_tod::string_pair tod_getter_mask(const time_of_day &tod)
Definition: custom_tod.cpp:47
REGISTER_DIALOG(editor_edit_unit)
static custom_tod::string_pair tod_getter_image(const time_of_day &tod)
Definition: custom_tod.cpp:41
static custom_tod::string_pair tod_getter_sound(const time_of_day &tod)
Definition: custom_tod.cpp:53
void connect_signal_notify_modified(dispatcher &dispatcher, const signal_notification &signal)
Connects a signal handler for getting a notification upon modification.
Definition: dispatcher.cpp:203
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
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:1035
@ SOUND_SOURCES
Definition: sound.hpp:30
std::string_view data
Definition: picture.cpp:178
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