The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
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"
29#include "gui/widgets/image.hpp"
30#include "gui/widgets/label.hpp"
33#include "sound.hpp"
34
35#include <boost/filesystem.hpp>
36#include <functional>
37#include <utility>
38
39namespace 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
62custom_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
92 connect_signal_mouse_left_click(copy_w, std::bind(&custom_tod::copy_to_clipboard_callback, this, data));
93 }
94
95 connect_signal_mouse_left_click(
96 find_widget<button>("browse_image"),
97 std::bind(&custom_tod::select_file<tod_getter_image>, this, "data/core/images/misc"));
98
99 connect_signal_mouse_left_click(
100 find_widget<button>("browse_mask"),
101 std::bind(&custom_tod::select_file<tod_getter_mask>, this, "data/core/images"));
102
103 connect_signal_mouse_left_click(
104 find_widget<button>("browse_sound"),
105 std::bind(&custom_tod::select_file<tod_getter_sound>, this, "data/core/sounds/ambient"));
106
107 connect_signal_mouse_left_click(
108 find_widget<button>("preview_image"),
109 std::bind(&custom_tod::update_image, this, "image"));
110
111 connect_signal_mouse_left_click(
112 find_widget<button>("preview_mask"),
113 std::bind(&custom_tod::update_image, this, "mask"));
114
115 connect_signal_mouse_left_click(
116 find_widget<button>("preview_sound"),
117 std::bind(&custom_tod::play_sound, this));
118
119 connect_signal_mouse_left_click(
120 find_widget<button>("next_tod"),
121 std::bind(&custom_tod::do_next_tod, this));
122
123 connect_signal_mouse_left_click(
124 find_widget<button>("previous_tod"),
125 std::bind(&custom_tod::do_prev_tod, this));
126
127 connect_signal_mouse_left_click(
128 find_widget<button>("new"),
129 std::bind(&custom_tod::do_new_tod, this));
130
131 connect_signal_mouse_left_click(
132 find_widget<button>("delete"),
133 std::bind(&custom_tod::do_delete_tod, this));
134
135 connect_signal_mouse_left_click(
136 find_widget<button>("preview_color"),
137 std::bind(&custom_tod::preview_schedule, this));
138
139 connect_signal_notify_modified(
140 find_widget<slider>("lawful_bonus"),
141 std::bind(&custom_tod::update_lawful_bonus, this));
142
143 connect_signal_notify_modified(
145 std::bind(&custom_tod::color_slider_callback, this, COLOR_R));
146
147 connect_signal_notify_modified(
149 std::bind(&custom_tod::color_slider_callback, this, COLOR_G));
150
151 connect_signal_notify_modified(
153 std::bind(&custom_tod::color_slider_callback, this, COLOR_B));
154
156}
157
158template<custom_tod::string_pair(*fptr)(const time_of_day&)>
159void 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();
188 boost::filesystem::path output_path = filesystem::get_current_editor_dir(addon_id);
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
216
222
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();
277 sound::play_sound(sound_path, sound_tracks::type::sound_source);
278}
279
280void 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.
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
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
330void 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 */
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
362const std::vector<time_of_day> custom_tod::get_schedule()
363{
365 return times_;
366}
367
368void 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
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:387
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.
std::pair< std::string, std::string > string_pair
The execute function.
void update_image(const std::string &id_stem)
Update image when preview is pressed.
field_integer * color_field_g_
void color_slider_callback(COLOR_TYPE type)
const std::string addon_id_
ID of the current addon.
void select_file(const std::string &default_dir)
void preview_schedule()
Callback for preview button.
std::vector< time_of_day > times_
Available time of days.
COLOR_TYPE
enum used in identifying sliders
virtual void post_show() override
Actions to be taken after the window has been shown.
const time_of_day & get_selected_tod() const
void do_next_tod()
Callback for the next tod button.
field_integer * color_field_b_
field_integer * color_field_r_
const std::vector< time_of_day > get_schedule()
Return current schedule.
void copy_to_clipboard_callback(const std::pair< std::string, tod_attribute_getter > &data)
void play_sound()
Play sound when play is pressed.
int current_tod_
Current time of day (ToD) index.
virtual void pre_show() override
Actions to be taken before showing the window.
void register_callback(std::function< void(std::vector< time_of_day >)>)
Register callback for update.
std::function< void(std::vector< time_of_day >)> update_map_and_schedule_
file_dialog & set_path(const std::string &value)
Sets the initial file selection.
file_dialog & set_ok_label(const std::string &value)
Sets the OK button label.
std::string path() const
Gets the current file selection.
file_dialog & set_read_only(bool value)
Whether to provide user interface elements for manipulating existing objects.
file_dialog & set_title(const std::string &value)
Sets the current dialog title text.
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:759
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:1223
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:100
#define REGISTER_DIALOG(window_id)
Wrapper for REGISTER_DIALOG2.
void copy_to_clipboard(const std::string &text)
Copies text to the clipboard.
Definition clipboard.cpp:24
std::string base_name(const std::string &file, const bool remove_extension=false)
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 &abs_path, const std::string &addon_id, const 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)
static custom_tod::string_pair tod_getter_image(const time_of_day &tod)
static custom_tod::string_pair tod_getter_sound(const time_of_day &tod)
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, sound_tracks::type group, unsigned int repeats)
Definition sound.cpp:978
STL namespace.
std::string_view data
Definition picture.cpp:188
Object which defines a time of day with associated bonuses, image, sounds etc.
std::string id
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.
t_string description
t_string name
std::string image
The image to be displayed in the game status.
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.