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 #pragma once
16 
17 #include "gui/auxiliary/field-fwd.hpp"
18 #include "gui/core/static_registry.hpp"
19 #include "utils/functional.hpp"
20 
21 #include <string>
22 #include <vector>
23 
24 namespace gui2
25 {
26 namespace dialogs
27 {
28 /**
29  * Registers a window.
30  *
31  * This function registers a window. The registration is used to validate
32  * whether the config for the window exists when starting Wesnoth.
33  *
34  * @note Most of the time you want to call @ref REGISTER_DIALOG instead of this
35  * function. It also directly adds the code for the dialog's id function.
36  *
37  * @param id                      Id of the window, multiple dialogs can use
38  *                                the same window so the id doesn't need to be
39  *                                unique.
40  */
41 #define REGISTER_WINDOW(id)                                                                                            \
42 	namespace                                                                                                          \
43 	{                                                                                                                  \
44 	namespace ns_##id                                                                                                  \
45 	{                                                                                                                  \
46 		struct register_helper                                                                                         \
47 		{                                                                                                              \
48 			register_helper()                                                                                          \
49 			{                                                                                                          \
50 				register_window(#id);                                                                                  \
51 			}                                                                                                          \
52 		};                                                                                                             \
53                                                                                                                        \
54 		struct register_helper register_helper;                                                                        \
55 	}                                                                                                                  \
56 	}
57 
58 /**
59  * Registers a window for a dialog.
60  *
61  * Call this function to register a window. In the header of the class it adds
62  * the following code:
63  *@code
64  *  // Inherited from modal_dialog, implemented by REGISTER_DIALOG.
65  *	virtual const std::string& id() const;
66  *@endcode
67  * Then use this macro in the implementation, inside the gui2 namespace.
68  *
69  * @note When the @p id is "foo" and the type tfoo it's easier to use
70  * REGISTER_DIALOG(foo).
71  *
72  * @param type                    Class type of the window to register.
73  * @param id                      Id of the window, multiple dialogs can use
74  *                                the same window so the id doesn't need to be
75  *                                unique.
76  */
77 #define REGISTER_DIALOG2(type, id)                                                                                     \
78 	REGISTER_WINDOW(id) const std::string& type::window_id() const                                                     \
79 	{                                                                                                                  \
80 		static const std::string result(#id);                                                                          \
81 		return result;                                                                                                 \
82 	}
83 
84 /**
85  * Wrapper for REGISTER_DIALOG2.
86  *
87  * "Calls" REGISTER_DIALOG2(window_id, window_id)
88  */
89 #define REGISTER_DIALOG(window_id) REGISTER_DIALOG2(window_id, window_id)
90 
91 /**
92  * Adds a bare-bones static `display` function to a dialog class that immediately
93  * invokes the dialogs's @ref modal_dialog::show function. If more complex behavior
94  * is desired, the function should be defined manually.
95  *
96  * See the @ref modal_dialog documentation (below) for more info.
97  */
98 #define DEFINE_SIMPLE_DISPLAY_WRAPPER(dialog)                                                                          \
99 	template<typename... T>                                                                                            \
100 	static void display(T&&... args)                                                                                   \
101 	{                                                                                                                  \
102 		dialog(std::forward<T>(args)...).show();                                                                       \
103 	}
104 
105 /**
106  * Adds a bare-bonesstatic `execute` function to a dialog class that immediately
107  * invokes and return the result of the dialogs's @ref modal_dialog::show function.
108  * If more complex behavior is desired, the function should be defined manually.
109  *
110  * See the @ref modal_dialog documentation (below) for more info.
111  */
112 #define DEFINE_SIMPLE_EXECUTE_WRAPPER(dialog)                                                                          \
113 	template<typename... T>                                                                                            \
114 	static bool execute(T&&... args)                                                                                   \
115 	{                                                                                                                  \
116 		return dialog(std::forward<T>(args)...).show();                                                                \
117 	}
118 
119 /**
120  * Abstract base class for all modal dialogs.
121  *
122  * A dialog shows a certain window instance to the user. The subclasses of this
123  * class will hold the parameters used for a certain window, eg a server
124  * connection dialog will hold the name of the selected server as parameter that
125  * way the caller doesn't need to know about the 'contents' of the window.
126  *
127  * @par Usage
128  *
129  * Simple dialogs that are shown to query user information it is recommended to
130  * add a static member called @p execute. The parameters to the function are:
131  * - references to in + out parameters by reference
132  * - references to the in parameters
133  * - the parameters for @ref modal_dialog::show.
134  *
135  * The 'in + out parameters' are used as initial value and final value when the
136  * OK button is pressed. The 'in parameters' are just extra parameters for
137  * showing.
138  *
139  * When a function only has 'in parameters' it should return a void value and
140  * the function should be called @p display, if it has 'in + out parameters' it
141  * must return a bool value. This value indicates whether or not the OK button
142  * was pressed to close the dialog. See @ref editor_new_map::execute for an
143  * example.
144  */
145 class modal_dialog
146 {
147 	/**
148 	 * Special helper function to get the id of the window.
149 	 *
150 	 * This is used in the unit tests, but these implementation details
151 	 * shouldn't be used in the normal code.
152 	 */
153 	friend std::string unit_test_mark_as_tested(const modal_dialog& dialog);
154 
155 public:
156 	modal_dialog();
157 
158 	virtual ~modal_dialog();
159 
160 	/**
161 	 * Shows the window.
162 	 *
163 	 * @param auto_close_time     The time in ms after which the dialog will
164 	 *                            automatically close, if 0 it doesn't close.
165 	 *                            @note the timeout is a minimum time and
166 	 *                            there's no guarantee about how fast it closes
167 	 *                            after the minimum.
168 	 *
169 	 * @returns                   Whether the final retval_ == retval::OK
170 	 */
171 	bool show(const unsigned auto_close_time = 0);
172 
173 
174 	/***** ***** ***** setters / getters for members ***** ****** *****/
175 
176 	/** Returns a pointer to the dialog's window. Will be null if it hasn't been built yet. */
get_window() const177 	window* get_window() const
178 	{
179 		return window_.get();
180 	}
181 
get_retval() const182 	int get_retval() const
183 	{
184 		return retval_;
185 	}
186 
set_always_save_fields(const bool always_save_fields)187 	void set_always_save_fields(const bool always_save_fields)
188 	{
189 		always_save_fields_ = always_save_fields;
190 	}
191 
set_restore(const bool restore)192 	void set_restore(const bool restore)
193 	{
194 		restore_ = restore;
195 	}
196 
set_allow_plugin_skip(const bool allow_plugin_skip)197 	void set_allow_plugin_skip(const bool allow_plugin_skip)
198 	{
199 		allow_plugin_skip_ = allow_plugin_skip;
200 	}
201 
set_show_even_without_video(const bool show_even_without_video)202 	void set_show_even_without_video(const bool show_even_without_video)
203 	{
204 		show_even_without_video_ = show_even_without_video;
205 	}
206 
207 protected:
208 	/**
209 	 * Creates a new boolean field.
210 	 *
211 	 * The field created is owned by modal_dialog, the returned pointer can be used
212 	 * in the child classes as access to a field.
213 	 *
214 	 * @param id                  Id of the widget, same value as in WML.
215 	 * @param mandatory            Is the widget mandatory or mandatory.
216 	 * @param callback_load_value The callback function to set the initial value
217 	 *                            of the widget.
218 	 * @param callback_save_value The callback function to write the resulting
219 	 *                            value of the widget. Saving will only happen
220 	 *                            if the widget is enabled and the window closed
221 	 *                            with ok.
222 	 * @param callback_change     When the value of the widget changes this
223 	 *                            callback is called.
224 	 *
225 	 * @returns                   Pointer to the created widget.
226 	 */
227 	field_bool*
228 	register_bool(const std::string& id,
229 				  const bool mandatory,
230 				  const std::function<bool()> callback_load_value = nullptr,
231 				  const std::function<void(bool)> callback_save_value = nullptr,
232 				  const std::function<void(widget&)> callback_change = nullptr,
233 				  const bool initial_fire = false);
234 
235 	/**
236 	 * Creates a new boolean field.
237 	 *
238 	 * The field created is owned by modal_dialog, the returned pointer can be used
239 	 * in the child classes as access to a field.
240 	 *
241 	 * @param id                  Id of the widget, same value as in WML.
242 	 * @param mandatory            Is the widget mandatory or mandatory.
243 	 * @param linked_variable     The variable the widget is linked to. See
244 	 *                            @ref field::field for more information.
245 	 * @param callback_change     When the value of the widget changes this
246 	 *                            callback is called.
247 	 *
248 	 * @returns                   Pointer to the created widget.
249 	 */
250 	field_bool*
251 	register_bool(const std::string& id,
252 				  const bool mandatory,
253 				  bool& linked_variable,
254 				  const std::function<void(widget&)> callback_change = nullptr,
255 				  const bool initial_fire = false);
256 
257 	/**
258 	 * Creates a new integer field.
259 	 *
260 	 * See @ref register_bool for more info.
261 	 */
262 	field_integer*
263 	register_integer(const std::string& id,
264 					 const bool mandatory,
265 					 const std::function<int()> callback_load_value = nullptr,
266 					 const std::function<void(int)> callback_save_value = nullptr);
267 
268 	/**
269 	 * Creates a new integer field.
270 	 *
271 	 * See @ref register_bool for more info.
272 	 */
273 	field_integer* register_integer(const std::string& id,
274 									 const bool mandatory,
275 									 int& linked_variable);
276 	/**
277 	 * Creates a new text field.
278 	 *
279 	 * See @ref register_bool for more info.
280 	 */
281 	field_text* register_text(
282 			const std::string& id,
283 			const bool mandatory,
284 			const std::function<std::string()> callback_load_value = nullptr,
285 			const std::function<void(const std::string&)> callback_save_value = nullptr,
286 			const bool capture_focus = false);
287 
288 	/**
289 	 * Creates a new text field.
290 	 *
291 	 * See @ref register_bool for more info.
292 	 */
293 	field_text* register_text(const std::string& id,
294 							   const bool mandatory,
295 							   std::string& linked_variable,
296 							   const bool capture_focus = false);
297 
298 	/**
299 	 * Registers a new styled_widget as a label.
300 	 *
301 	 * The label is used for a styled_widget to set the 'label' since it calls the
302 	 * @ref styled_widget::set_label it can also be used for the @ref image since
303 	 * there this sets the filename. (The @p use_markup makes no sense in an
304 	 * image but that's a detail.)
305 	 *
306 	 * @note In general it's preferred a widget sets its markup flag in WML, but
307 	 * some generic windows (like messages) may need different versions
308 	 * depending on where used.
309 	 *
310 	 * @param id                  Id of the widget, same value as in WML.
311 	 * @param mandatory           Is the widget mandatory or optional.
312 	 * @param text                The text for the label.
313 	 * @param use_markup          Whether or not use markup for the label.
314 	 */
315 	field_label* register_label(const std::string& id,
316 								 const bool mandatory,
317 								 const std::string& text,
318 								 const bool use_markup = false);
319 
320 	/** Registers a new styled_widget as image. */
register_image(const std::string & id,const bool mandatory,const std::string & filename)321 	field_label* register_image(const std::string& id,
322 								 const bool mandatory,
323 								 const std::string& filename)
324 	{
325 		return register_label(id, mandatory, filename);
326 	}
327 
328 protected:
329 	/** The window object build for this dialog. */
330 	std::unique_ptr<window> window_;
331 
332 private:
333 	/** Returns the window exit status, 0 means not shown. */
334 	int retval_;
335 
336 	/**
337 	 * Always save the fields upon closing.
338 	 *
339 	 * Normally fields are only saved when the retval::OK button is pressed.
340 	 * With this flag set is always saves. Be careful with the flag since it
341 	 * also updates upon canceling, which can be a problem when the field sets
342 	 * a preference.
343 	 */
344 	bool always_save_fields_;
345 
346 	/**
347 	 * Contains the automatically managed fields.
348 	 *
349 	 * Since the fields are automatically managed and there are no search
350 	 * functions defined we don't offer access to the vector. If access is
351 	 * needed the creator should store a copy of the pointer.
352 	 */
353 	std::vector<std::unique_ptr<class field_base>> fields_;
354 
355 	/**
356 	 * Contains the widget that should get the focus when the window is shown.
357 	 */
358 	std::string focus_;
359 
360 	/**
361 	 * Restore the screen after showing?
362 	 *
363 	 * Most windows should restore the display after showing so this value
364 	 * defaults to true. Toplevel windows (like the titlescreen don't want this
365 	 * behavior so they can change it in pre_show().
366 	 */
367 	bool restore_;
368 
369 	/**
370 	 * Allow plugins to skip through the dialog?
371 	 * Most dialogs install a plugins context to allow plugins to accept whatever the dialog is offering
372 	 * and continue. Some dialogs, especially those that install their own plugins context, may want to
373 	 * disable this.
374 	 */
375 	bool allow_plugin_skip_;
376 
377 	/**
378 	 * Show the dialog even with --nogui?
379 	 * Some dialogs need to be shown even when --nogui is specified if the game is being driven by a plugin.
380 	 * Those dialogs allow the plugin to styled_widget them by creating a plugin context in pre_show().
381 	 */
382 	bool show_even_without_video_;
383 
384 	/** The id of the window to build. */
385 	virtual const std::string& window_id() const = 0;
386 
387 	/**
388 	 * Builds the window.
389 	 *
390 	 * Every dialog shows it's own kind of window, this function should return
391 	 * the window to show.
392 	 *
393 	 * @returns                   The window to show.
394 	 */
395 	window* build_window() const;
396 
397 	/**
398 	 * Actions to be taken directly after the window is build.
399 	 *
400 	 * At this point the registered fields are not yet registered.
401 	 *
402 	 * @param window              The window just created.
403 	 */
404 	virtual void post_build(window& window);
405 
406 	/**
407 	 * Actions to be taken before showing the window.
408 	 *
409 	 * At this point the registered fields are registered and initialized with
410 	 * their initial values.
411 	 *
412 	 * @param window              The window to be shown.
413 	 */
414 	virtual void pre_show(window& window);
415 
416 	/**
417 	 * Actions to be taken after the window has been shown.
418 	 *
419 	 * At this point the registered fields already stored their values (if the
420 	 * OK has been pressed).
421 	 *
422 	 * @param window              The window which has been shown.
423 	 */
424 	virtual void post_show(window& window);
425 
426 	/**
427 	 * Initializes all fields in the dialog and set the keyboard focus.
428 	 *
429 	 * @param window              The window which has been shown.
430 	 */
431 	virtual void init_fields(window& window);
432 
433 	/**
434 	 * When the dialog is closed with the OK status saves all fields.
435 	 *
436 	 * Saving only happens if a callback handler is installed.
437 	 *
438 	 * @param window              The window which has been shown.
439 	 * @param save_fields         Does the value in the fields need to be saved?
440 	 */
441 	virtual void finalize_fields(window& window, const bool save_fields);
442 };
443 
444 } // namespace dialogs
445 } // namespace gui2
446