The Battle for Wesnoth  1.19.7+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-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(dlg.show()) {
177  dn = dlg.path();
178  const std::string& message
179  = _("This file is outside Wesnoth’s data dirs. Do you wish to copy it into your add-on?");
180 
181  if(data.first == "image") {
182  if (!filesystem::to_asset_path(dn, addon_id_, "images")) {
184  filesystem::copy_file(dlg.path(), dn);
185  }
186  }
187  times_[current_tod_].image = dn;
188  } else if(data.first == "mask") {
189  if (!filesystem::to_asset_path(dn, addon_id_, "images")) {
191  filesystem::copy_file(dlg.path(), dn);
192  }
193  }
194  times_[current_tod_].image_mask = dn;
195  } else if(data.first == "sound") {
196  if (!filesystem::to_asset_path(dn, addon_id_, "sounds")) {
198  filesystem::copy_file(dlg.path(), dn);
199  }
200  }
201  times_[current_tod_].sounds = dn;
202  }
203  }
204 
206 }
207 
209 {
210  current_tod_ = (current_tod_ + 1) % times_.size();
212 }
213 
215 {
216  current_tod_ = (current_tod_ ? current_tod_ : times_.size()) - 1;
218 }
219 
221 {
222  times_.insert(times_.begin() + current_tod_, time_of_day());
224 }
225 
227 {
228  assert(times_.begin() + current_tod_ < times_.end());
229 
230  if(times_.size() == 1) {
231  times_.emplace_back();
232  } else {
233  times_.erase(times_.begin() + current_tod_);
234 
235  if(times_.begin() + current_tod_ >= times_.end()) {
236  current_tod_ = times_.size() - 1;
237  }
238  }
239 
241 }
242 
244 {
245  try {
246  return times_.at(current_tod_);
247  } catch(const std::out_of_range&) {
248  throw std::string("Attempted to fetch a non-existent ToD!");
249  }
250 }
251 
253 {
254  time_of_day& current_tod = times_[current_tod_];
255 
256  switch(type)
257  {
258  case COLOR_R:
259  current_tod.color.r = color_field_r_->get_widget_value();
260  break;
261  case COLOR_G :
262  current_tod.color.g = color_field_g_->get_widget_value();
263  break;
264  case COLOR_B :
265  current_tod.color.b = color_field_b_->get_widget_value();
266  break;
267  }
268 
270 }
271 
273  std::string sound_path = find_widget<text_box>("path_sound").get_value();
275 }
276 
277 void custom_tod::update_image(const std::string& id_stem) {
278  std::string img_path = find_widget<text_box>("path_"+id_stem).get_value();
279  find_widget<image>("current_tod_" + id_stem).set_label(img_path);
280 
282 }
283 
285 {
287  assert(disp && "Display pointer is null!");
288 
289  // The display handles invaliding whatever tiles need invalidating.
290  disp->update_tod(&get_selected_tod());
291 
293 }
294 
296 {
297  times_[current_tod_].lawful_bonus = find_widget<slider>("lawful_bonus").get_value();
298 }
299 
301 {
302  const time_of_day& current_tod = get_selected_tod();
303 
304  find_widget<text_box>("tod_name").set_value(current_tod.name);
305  find_widget<text_box>("tod_desc").set_value(current_tod.description);
306  find_widget<text_box>("tod_id").set_value(current_tod.id);
307 
308  find_widget<text_box>("path_image").set_value(current_tod.image);
309  find_widget<text_box>("path_mask").set_value(current_tod.image_mask);
310  find_widget<text_box>("path_sound").set_value(current_tod.sounds);
311 
312  find_widget<image>("current_tod_image").set_image(current_tod.image);
313  find_widget<image>("current_tod_mask").set_image(current_tod.image_mask);
314 
315  find_widget<slider>("lawful_bonus").set_value(current_tod.lawful_bonus);
316 
317  color_field_r_->set_widget_value(current_tod.color.r);
318  color_field_g_->set_widget_value(current_tod.color.g);
319  color_field_b_->set_widget_value(current_tod.color.b);
320 
321  const std::string new_index_str = formatter() << (current_tod_ + 1) << "/" << times_.size();
322  find_widget<label>("tod_number").set_label(new_index_str);
323 
325 }
326 
327 void custom_tod::copy_to_clipboard_callback(const std::pair<std::string, tod_attribute_getter>& data)
328 {
329  auto& [type, getter] = data;
330  button& copy_w = find_widget<button>("copy_" + type);
332  copy_w.set_success(true);
333 }
334 
335 /** Quickly preview the schedule changes and color */
337 {
339 }
340 
342 {
343  /* Update times_ with values from the dialog */
344  times_[current_tod_].name = find_widget<text_box>("tod_name").get_value();
345  times_[current_tod_].description = find_widget<text_box>("tod_desc").get_value();
346  times_[current_tod_].id = find_widget<text_box>("tod_id").get_value();
347 
348  times_[current_tod_].image = find_widget<text_box>("path_image").get_value();
349  times_[current_tod_].image_mask = find_widget<text_box>("path_mask").get_value();
350  times_[current_tod_].sounds = find_widget<text_box>("path_sound").get_value();
351 
352  times_[current_tod_].lawful_bonus = find_widget<slider>("lawful_bonus").get_value();
353 
357 }
358 
359 const std::vector<time_of_day> custom_tod::get_schedule()
360 {
361  update_schedule();
362  return times_;
363 }
364 
365 void custom_tod::register_callback(std::function<void(std::vector<time_of_day>)> update_func)
366 {
367  update_map_and_schedule_ = std::move(update_func);
368 }
369 
371 {
373 }
374 
375 } // namespace dialogs
Sort-of-Singleton that many classes, both GUI and non-GUI, use to access the game data.
Definition: display.hpp:97
void update_tod(const time_of_day *tod_override=nullptr)
Applies r,g,b coloring to the map.
Definition: display.cpp:392
static display * get_singleton()
Returns the display object if a display object exists.
Definition: display.hpp:111
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:341
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:277
field_integer * color_field_g_
Definition: custom_tod.hpp:102
void color_slider_callback(COLOR_TYPE type)
Definition: custom_tod.cpp:252
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:336
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:370
const time_of_day & get_selected_tod() const
Definition: custom_tod.cpp:243
void do_next_tod()
Callback for the next tod button.
Definition: custom_tod.cpp:208
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:359
void copy_to_clipboard_callback(const std::pair< std::string, tod_attribute_getter > &data)
Definition: custom_tod.cpp:327
void play_sound()
Play sound when play is pressed.
Definition: custom_tod.cpp:272
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:365
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
window * get_window()
Get the parent window.
Definition: widget.cpp:117
void invalidate_layout()
Updates the size of the window.
Definition: window.cpp:761
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:1225
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
bool to_asset_path(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 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.
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: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:1047
@ 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