The Battle for Wesnoth  1.19.2+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  window.add_to_tab_order(find_widget<text_box>(&window, "tod_name", false, true));
85  window.add_to_tab_order(find_widget<text_box>(&window, "tod_desc", false, true));
86  window.add_to_tab_order(find_widget<text_box>(&window, "tod_id", false, true));
87 
88  for(const auto& data : metadata_stuff) {
89  button& copy_w = find_widget<button>(&window, "copy_" + data.first, false);
90 
92  std::bind(&custom_tod::copy_to_clipboard_callback, this, data));
93 
95  copy_w.set_active(false);
96  copy_w.set_tooltip(_("Clipboard support not found, contact your packager"));
97  }
98  }
99 
101  find_widget<button>(&window, "browse_image", false),
102  std::bind(&custom_tod::select_file<tod_getter_image>, this, "data/core/images/misc"));
103 
105  find_widget<button>(&window, "browse_mask", false),
106  std::bind(&custom_tod::select_file<tod_getter_mask>, this, "data/core/images"));
107 
109  find_widget<button>(&window, "browse_sound", false),
110  std::bind(&custom_tod::select_file<tod_getter_sound>, this, "data/core/sounds/ambient"));
111 
113  find_widget<button>(&window, "preview_image", false),
114  std::bind(&custom_tod::update_image, this, "image"));
115 
117  find_widget<button>(&window, "preview_mask", false),
118  std::bind(&custom_tod::update_image, this, "mask"));
119 
121  find_widget<button>(&window, "preview_sound", false),
122  std::bind(&custom_tod::play_sound, this));
123 
125  find_widget<button>(&window, "next_tod", false),
126  std::bind(&custom_tod::do_next_tod, this));
127 
129  find_widget<button>(&window, "previous_tod", false),
130  std::bind(&custom_tod::do_prev_tod, this));
131 
133  find_widget<button>(&window, "new", false),
134  std::bind(&custom_tod::do_new_tod, this));
135 
137  find_widget<button>(&window, "delete", false),
138  std::bind(&custom_tod::do_delete_tod, this));
139 
141  find_widget<button>(&window, "preview_color", false),
142  std::bind(&custom_tod::preview_schedule, this));
143 
145  find_widget<slider>(&window, "lawful_bonus", false),
146  std::bind(&custom_tod::update_lawful_bonus, this));
147 
150  std::bind(&custom_tod::color_slider_callback, this, COLOR_R));
151 
154  std::bind(&custom_tod::color_slider_callback, this, COLOR_G));
155 
158  std::bind(&custom_tod::color_slider_callback, this, COLOR_B));
159 
161 }
162 
163 template<custom_tod::string_pair(*fptr)(const time_of_day&)>
164 void custom_tod::select_file(const std::string& default_dir)
165 {
166  const string_pair& data = (*fptr)(get_selected_tod());
167 
168  std::string fn = filesystem::base_name(data.second);
169  std::string dn = filesystem::directory_name(fn);
170  if(dn.empty()) {
171  dn = default_dir;
172  }
173 
175 
176  dlg.set_title(_("Choose File"))
177  .set_ok_label(_("Select"))
178  .set_path(dn)
179  .set_read_only(true);
180 
181  if(dlg.show()) {
182  dn = dlg.path();
183  const std::string& message
184  = _("This file is outside Wesnoth's data dirs. Do you wish to copy it into your add-on?");
185 
186  if(data.first == "image") {
187  if (!filesystem::to_asset_path(dn, addon_id_, "images")) {
189  filesystem::copy_file(dlg.path(), dn);
190  }
191  }
192  times_[current_tod_].image = dn;
193  } else if(data.first == "mask") {
194  if (!filesystem::to_asset_path(dn, addon_id_, "images")) {
196  filesystem::copy_file(dlg.path(), dn);
197  }
198  }
199  times_[current_tod_].image_mask = dn;
200  } else if(data.first == "sound") {
201  if (!filesystem::to_asset_path(dn, addon_id_, "sounds")) {
203  filesystem::copy_file(dlg.path(), dn);
204  }
205  }
206  times_[current_tod_].sounds = dn;
207  }
208  }
209 
211 }
212 
214 {
215  current_tod_ = (current_tod_ + 1) % times_.size();
217 }
218 
220 {
221  current_tod_ = (current_tod_ ? current_tod_ : times_.size()) - 1;
223 }
224 
226 {
227  times_.insert(times_.begin() + current_tod_, time_of_day());
229 }
230 
232 {
233  assert(times_.begin() + current_tod_ < times_.end());
234 
235  if(times_.size() == 1) {
236  times_.emplace_back();
237  } else {
238  times_.erase(times_.begin() + current_tod_);
239 
240  if(times_.begin() + current_tod_ >= times_.end()) {
241  current_tod_ = times_.size() - 1;
242  }
243  }
244 
246 }
247 
249 {
250  try {
251  return times_.at(current_tod_);
252  } catch(const std::out_of_range&) {
253  throw std::string("Attempted to fetch a non-existant ToD!");
254  }
255 }
256 
258 {
259  time_of_day& current_tod = times_[current_tod_];
260 
261  switch(type)
262  {
263  case COLOR_R:
264  current_tod.color.r = color_field_r_->get_widget_value();
265  break;
266  case COLOR_G :
267  current_tod.color.g = color_field_g_->get_widget_value();
268  break;
269  case COLOR_B :
270  current_tod.color.b = color_field_b_->get_widget_value();
271  break;
272  }
273 
275 }
276 
278  std::string sound_path = find_widget<text_box>(get_window(), "path_sound", false).get_value();
280 }
281 
282 void custom_tod::update_image(const std::string& id_stem) {
283  std::string img_path = find_widget<text_box>(get_window(), "path_"+id_stem, false).get_value();
284  find_widget<image>(get_window(), "current_tod_" + id_stem, false).set_label(img_path);
285 
287 }
288 
290 {
292  assert(disp && "Display pointer is null!");
293 
294  // The display handles invaliding whatever tiles need invalidating.
295  disp->update_tod(&get_selected_tod());
296 
298 }
299 
301 {
302  times_[current_tod_].lawful_bonus = find_widget<slider>(get_window(), "lawful_bonus", false).get_value();
303 }
304 
306 {
307  const time_of_day& current_tod = get_selected_tod();
308 
309  find_widget<text_box>(get_window(), "tod_name", false).set_value(current_tod.name);
310  find_widget<text_box>(get_window(), "tod_desc", false).set_value(current_tod.description);
311  find_widget<text_box>(get_window(), "tod_id", false).set_value(current_tod.id);
312 
313  find_widget<text_box>(get_window(), "path_image", false).set_value(current_tod.image);
314  find_widget<text_box>(get_window(), "path_mask", false).set_value(current_tod.image_mask);
315  find_widget<text_box>(get_window(), "path_sound", false).set_value(current_tod.sounds);
316 
317  find_widget<image>(get_window(), "current_tod_image", false).set_image(current_tod.image);
318  find_widget<image>(get_window(), "current_tod_mask", false).set_image(current_tod.image_mask);
319 
320  find_widget<slider>(get_window(), "lawful_bonus", false).set_value(current_tod.lawful_bonus);
321 
322  color_field_r_->set_widget_value(current_tod.color.r);
323  color_field_g_->set_widget_value(current_tod.color.g);
324  color_field_b_->set_widget_value(current_tod.color.b);
325 
326  const std::string new_index_str = formatter() << (current_tod_ + 1) << "/" << times_.size();
327  find_widget<label>(get_window(), "tod_number", false).set_label(new_index_str);
328 
330 }
331 
332 void custom_tod::copy_to_clipboard_callback(std::pair<std::string, tod_attribute_getter> data)
333 {
334  auto& [type, getter] = data;
335  button& copy_w = find_widget<button>(get_window(), "copy_" + type, false);
337  copy_w.set_success(true);
338 }
339 
340 /** Quickly preview the schedule changes and color */
342 {
344 }
345 
347 {
348  /* Update times_ with values from the dialog */
349  times_[current_tod_].name = find_widget<text_box>(get_window(), "tod_name", false).get_value();
350  times_[current_tod_].description = find_widget<text_box>(get_window(), "tod_desc", false).get_value();
351  times_[current_tod_].id = find_widget<text_box>(get_window(), "tod_id", false).get_value();
352 
353  times_[current_tod_].image = find_widget<text_box>(get_window(), "path_image", false).get_value();
354  times_[current_tod_].image_mask = find_widget<text_box>(get_window(), "path_mask", false).get_value();
355  times_[current_tod_].sounds = find_widget<text_box>(get_window(), "path_sound", false).get_value();
356 
357  times_[current_tod_].lawful_bonus = find_widget<slider>(get_window(), "lawful_bonus", false).get_value();
358 
362 }
363 
364 const std::vector<time_of_day> custom_tod::get_schedule()
365 {
366  update_schedule();
367  return times_;
368 }
369 
370 void custom_tod::register_callback(std::function<void(std::vector<time_of_day>)> update_func)
371 {
372  update_map_and_schedule_ = update_func;
373 }
374 
375 void custom_tod::post_show(window& /*window*/)
376 {
378 }
379 
380 } // 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:405
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
virtual void set_active(const bool active) override
See styled_widget::set_active.
Definition: button.cpp:64
void set_success(bool success)
Definition: button.cpp:89
void update_schedule()
Update current TOD with values from the GUI.
Definition: custom_tod.cpp:346
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:282
field_integer * color_field_g_
Definition: custom_tod.hpp:106
void color_slider_callback(COLOR_TYPE type)
Definition: custom_tod.cpp:257
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:164
virtual void pre_show(window &window) override
Actions to be taken before showing the window.
Definition: custom_tod.cpp:76
void preview_schedule()
Callback for preview button.
Definition: custom_tod.cpp:341
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:332
const time_of_day & get_selected_tod() const
Definition: custom_tod.cpp:248
void do_next_tod()
Callback for the next tod button.
Definition: custom_tod.cpp:213
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:364
void play_sound()
Play sound when play is pressed.
Definition: custom_tod.cpp:277
int current_tod_
Current time of day (ToD) index.
Definition: custom_tod.hpp:103
void register_callback(std::function< void(std::vector< time_of_day >)>)
Register callback for update.
Definition: custom_tod.cpp:370
std::function< void(std::vector< time_of_day >)> update_map_and_schedule_
Definition: custom_tod.hpp:74
virtual void post_show(window &window) override
Actions to be taken after the window has been shown.
Definition: custom_tod.cpp:375
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.
window * get_window()
Returns a pointer to the dialog's window.
styled_widget * get_widget()
Definition: field.hpp:193
void set_widget_value(CT value)
Sets the value of the field.
Definition: field.hpp:344
T get_widget_value()
Gets the value of the field.
Definition: field.hpp:379
void set_tooltip(const t_string &tooltip)
base class of top level items, the only item which needs to store the final canvases to draw on.
Definition: window.hpp:61
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:1239
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, const bool)
Copies text to the clipboard.
Definition: clipboard.cpp:32
bool available()
Whether wesnoth was compiled with support for a clipboard.
Definition: clipboard.cpp:53
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:150
@ 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:194
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