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