1 /*
2 Copyright (C) 2008 - 2018 by Mark de Wever <koraq@xs4all.nl>
3 Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY.
11
12 See the COPYING file for more details.
13 */
14
15 #define GETTEXT_DOMAIN "wesnoth-lib"
16
17 #include "gui/dialogs/modal_dialog.hpp"
18
19 #include "cursor.hpp"
20 #include "gui/auxiliary/field.hpp"
21 #include "gui/widgets/integer_selector.hpp"
22 #include "scripting/plugins/context.hpp"
23 #include "scripting/plugins/manager.hpp"
24 #include "video.hpp"
25
26 namespace gui2
27 {
28 namespace dialogs
29 {
modal_dialog()30 modal_dialog::modal_dialog()
31 : window_(nullptr)
32 , retval_(retval::NONE)
33 , always_save_fields_(false)
34 , fields_()
35 , focus_()
36 , restore_(false)
37 , allow_plugin_skip_(true)
38 , show_even_without_video_(false)
39 {
40 }
41
~modal_dialog()42 modal_dialog::~modal_dialog()
43 {
44 }
45
46 namespace {
47 struct window_stack_handler {
window_stack_handlergui2::dialogs::__anonbcce9c850111::window_stack_handler48 window_stack_handler(std::unique_ptr<window>& win) : local_window(win) {
49 open_window_stack.push_back(local_window.get());
50 }
~window_stack_handlergui2::dialogs::__anonbcce9c850111::window_stack_handler51 ~window_stack_handler() {
52 remove_from_window_stack(local_window.get());
53 }
54 std::unique_ptr<window>& local_window;
55 };
56 }
57
show(const unsigned auto_close_time)58 bool modal_dialog::show(const unsigned auto_close_time)
59 {
60 if(CVideo::get_singleton().faked() && !show_even_without_video_) {
61 if(!allow_plugin_skip_) {
62 return false;
63 }
64
65 plugins_manager* pm = plugins_manager::get();
66 if (pm && pm->any_running())
67 {
68 plugins_context pc("Dialog");
69 pc.set_callback("skip_dialog", [this](config) { retval_ = retval::OK; }, false);
70 pc.set_callback("quit", [](config) {}, false);
71 pc.play_slice();
72 }
73
74 return false;
75 }
76
77 window_.reset(build_window());
78 assert(window_.get());
79
80 post_build(*window_);
81
82 window_->set_owner(this);
83
84 init_fields(*window_);
85
86 pre_show(*window_);
87
88 { // Scope the window stack
89 cursor::setter cur{cursor::NORMAL};
90 window_stack_handler push_window_stack(window_);
91 retval_ = window_->show(restore_, auto_close_time);
92 }
93
94 /*
95 * It can happen that when two clicks follow each other fast that the event
96 * handling code in events.cpp generates a DOUBLE_CLICK_EVENT. For some
97 * reason it can happen that this event gets pushed in the queue when the
98 * window is shown, but processed after the window is closed. This causes
99 * the next window to get this pending event.
100 *
101 * This caused a bug where double clicking in the campaign selection dialog
102 * directly selected a difficulty level and started the campaign. In order
103 * to avoid that problem, filter all pending DOUBLE_CLICK_EVENT events after
104 * the window is closed.
105 */
106 SDL_FlushEvent(DOUBLE_CLICK_EVENT);
107
108 finalize_fields(*window_, (retval_ == retval::OK || always_save_fields_));
109
110 post_show(*window_);
111
112 // post_show may have updated the window retval. Update it here.
113 retval_ = window_->get_retval();
114
115 // Reset window object.
116 window_.reset(nullptr);
117
118 return retval_ == retval::OK;
119 }
120
register_bool(const std::string & id,const bool mandatory,const std::function<bool ()> callback_load_value,const std::function<void (bool)> callback_save_value,const std::function<void (widget &)> callback_change,const bool initial_fire)121 field_bool* modal_dialog::register_bool(
122 const std::string& id,
123 const bool mandatory,
124 const std::function<bool()> callback_load_value,
125 const std::function<void(bool)> callback_save_value,
126 const std::function<void(widget&)> callback_change,
127 const bool initial_fire)
128 {
129 field_bool* field = new field_bool(id,
130 mandatory,
131 callback_load_value,
132 callback_save_value,
133 callback_change,
134 initial_fire);
135
136 fields_.emplace_back(field);
137 return field;
138 }
139
140 field_bool*
register_bool(const std::string & id,const bool mandatory,bool & linked_variable,const std::function<void (widget &)> callback_change,const bool initial_fire)141 modal_dialog::register_bool(const std::string& id,
142 const bool mandatory,
143 bool& linked_variable,
144 const std::function<void(widget&)> callback_change,
145 const bool initial_fire)
146 {
147 field_bool* field
148 = new field_bool(id, mandatory, linked_variable, callback_change, initial_fire);
149
150 fields_.emplace_back(field);
151 return field;
152 }
153
register_integer(const std::string & id,const bool mandatory,const std::function<int ()> callback_load_value,const std::function<void (const int)> callback_save_value)154 field_integer* modal_dialog::register_integer(
155 const std::string& id,
156 const bool mandatory,
157 const std::function<int()> callback_load_value,
158 const std::function<void(const int)> callback_save_value)
159 {
160 field_integer* field = new field_integer(
161 id, mandatory, callback_load_value, callback_save_value);
162
163 fields_.emplace_back(field);
164 return field;
165 }
166
register_integer(const std::string & id,const bool mandatory,int & linked_variable)167 field_integer* modal_dialog::register_integer(const std::string& id,
168 const bool mandatory,
169 int& linked_variable)
170 {
171 field_integer* field = new field_integer(id, mandatory, linked_variable);
172
173 fields_.emplace_back(field);
174 return field;
175 }
176
register_text(const std::string & id,const bool mandatory,const std::function<std::string ()> callback_load_value,const std::function<void (const std::string &)> callback_save_value,const bool capture_focus)177 field_text* modal_dialog::register_text(
178 const std::string& id,
179 const bool mandatory,
180 const std::function<std::string()> callback_load_value,
181 const std::function<void(const std::string&)> callback_save_value,
182 const bool capture_focus)
183 {
184 field_text* field = new field_text(
185 id, mandatory, callback_load_value, callback_save_value);
186
187 if(capture_focus) {
188 focus_ = id;
189 }
190
191 fields_.emplace_back(field);
192 return field;
193 }
194
register_text(const std::string & id,const bool mandatory,std::string & linked_variable,const bool capture_focus)195 field_text* modal_dialog::register_text(const std::string& id,
196 const bool mandatory,
197 std::string& linked_variable,
198 const bool capture_focus)
199 {
200 field_text* field = new field_text(id, mandatory, linked_variable);
201
202 if(capture_focus) {
203 focus_ = id;
204 }
205
206 fields_.emplace_back(field);
207 return field;
208 }
209
register_label(const std::string & id,const bool mandatory,const std::string & text,const bool use_markup)210 field_label* modal_dialog::register_label(const std::string& id,
211 const bool mandatory,
212 const std::string& text,
213 const bool use_markup)
214 {
215 field_label* field = new field_label(id, mandatory, text, use_markup);
216
217 fields_.emplace_back(field);
218 return field;
219 }
220
build_window() const221 window* modal_dialog::build_window() const
222 {
223 return build(window_id());
224 }
225
post_build(window &)226 void modal_dialog::post_build(window& /*window*/)
227 {
228 /* DO NOTHING */
229 }
230
pre_show(window &)231 void modal_dialog::pre_show(window& /*window*/)
232 {
233 /* DO NOTHING */
234 }
235
post_show(window &)236 void modal_dialog::post_show(window& /*window*/)
237 {
238 /* DO NOTHING */
239 }
240
init_fields(window & window)241 void modal_dialog::init_fields(window& window)
242 {
243 for(auto& field : fields_)
244 {
245 field->attach_to_window(window);
246 field->widget_init(window);
247 }
248
249 if(!focus_.empty()) {
250 if(widget* widget = window.find(focus_, false)) {
251 window.keyboard_capture(widget);
252 }
253 }
254 }
255
finalize_fields(window & window,const bool save_fields)256 void modal_dialog::finalize_fields(window& window, const bool save_fields)
257 {
258 for(auto& field : fields_)
259 {
260 if(save_fields) {
261 field->widget_finalize(window);
262 }
263 field->detach_from_window();
264 }
265 }
266
267 } // namespace dialogs
268 } // namespace gui2
269
270
271 /*WIKI
272 * @page = GUIWindowDefinitionWML
273 * @order = 1
274 *
275 * {{Autogenerated}}
276 *
277 * = Window definition =
278 *
279 * The window definition define how the windows shown in the dialog look.
280 */
281
282 /*WIKI
283 * @page = GUIWindowDefinitionWML
284 * @order = ZZZZZZ_footer
285 *
286 * [[Category: WML Reference]]
287 * [[Category: GUI WML Reference]]
288 */
289