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 /**
16  * @file
17  * Implements some helper classes to ease adding fields to a dialog and hide
18  * the synchronization needed. Since some templates are used all is stored in
19  * the header.
20  *
21  */
22 
23 #pragma once
24 
25 #include "gui/auxiliary/find_widget.hpp"
26 #include "gui/auxiliary/field-fwd.hpp"
27 #include "gui/widgets/styled_widget.hpp"
28 #include "gui/widgets/selectable_item.hpp"
29 #include "gui/widgets/text_box.hpp"
30 #include "gui/widgets/window.hpp"
31 
32 namespace gui2
33 {
34 
35 /**
36  * Abstract base class for the fields.
37  *
38  * @note In this context a widget is a @ref gui2::styled_widget and not a @ref
39  * gui2::widget. This name widget is a generic name and fits, however some
40  * functions used are first declared in a styled_widget.
41  */
42 class field_base
43 {
44 public:
45 	/**
46 	 * Constructor.
47 	 *
48 	 * @param id                  The id of the widget to connect to the window.
49 	 *                            A widget can only be connected once.
50 	 * @param mandatory           Is the widget mandatory
51 	 */
field_base(const std::string & id,const bool mandatory)52 	field_base(const std::string& id, const bool mandatory)
53 		: id_(id), mandatory_(mandatory), widget_(nullptr)
54 	{
55 	}
56 
~field_base()57 	virtual ~field_base()
58 	{
59 	}
60 
61 	/**
62 	 * Attaches the field to a window.
63 	 *
64 	 * When attached the widget which we're a wrapper around is stored linked
65 	 * in here.
66 	 *
67 	 * @warning After attaching the window must remain a valid. Before the
68 	 * window is destroyed the @ref detach_from_window function must be called.
69 	 *
70 	 * @todo Most functions that have a window parameter only use it to get the
71 	 * widget. Evaluate and remove the window parameter where applicable.
72 	 *
73 	 * @pre widget_ == nullptr
74 	 *
75 	 * @param window               The window to be attached to.
76 	 */
attach_to_window(window & window)77 	void attach_to_window(window& window)
78 	{
79 		assert(!widget_);
80 		widget_ = find_widget<styled_widget>(&window, id(), false, mandatory_);
81 	}
82 
83 	/**
84 	 * Initializes the widget.
85 	 *
86 	 * This routine is called before the dialog is shown and the pre_show() is
87 	 * called. So the user can override the values set. This routine does the
88 	 * following:
89 	 * - If no widget available exit gives feedback it the widget must exist.
90 	 * - If a getter is defined we use to set value_ and the widget.
91 	 * - If no setter is defined we use the widget value to set value_.
92 	 *
93 	 * The function calls two functions
94 	 *  - init_generic which is to be used in the template subclass.
95 	 *  - init_specialized which is to be used in subclasses of the template
96 	 *     class. This way they can override this function without to use their
97 	 *     signature to inherit.
98 	 *
99 	 * @param window              The window containing the widget.
100 	 */
widget_init(window & window)101 	void widget_init(window& window)
102 	{
103 		init_generic(window);
104 		init_specialized(window);
105 	}
106 
107 	/**
108 	 * Finalizes the widget.
109 	 *
110 	 * This routine is called after the dialog is closed with OK. It's called
111 	 * before post_show(). This routine does the following:
112 	 * - if no active widget available exit.
113 	 * - if a setter is defined the widget value is saved in the setter.
114 	 * - The widget value is saved in value_.
115 	 *
116 	 * Like widget_init it calls two functions with the same purpose.
117 	 *
118 	 * @param window              The window containing the widget.
119 	 */
widget_finalize(window & window)120 	void widget_finalize(window& window)
121 	{
122 		finalize_generic(window);
123 		finalize_specialized(window);
124 	}
125 
126 	/**
127 	 * Detaches the field from a window.
128 	 *
129 	 * @pre widget_ != nullptr || !mandatory_
130 	 */
detach_from_window()131 	void detach_from_window()
132 	{
133 		assert(!mandatory_ || widget_);
134 		widget_ = nullptr;
135 	}
136 
137 	/**
138 	 * Saves a widget.
139 	 *
140 	 * It can be a window must be recreated, in that case the state needs to be
141 	 * saved and restored. This routine does the following:
142 	 * - if no widget available exit (doesn't look at the active state).
143 	 * - The widget value is saved in value_.
144 	 *
145 	 * @param window              The window containing the widget.
146 	 */
147 	virtual void widget_save(window& window) = 0;
148 
149 	/**
150 	 * Restores a widget.
151 	 *
152 	 * See widget_save for more info.
153 	 *
154 	 * @param window              The window containing the widget.
155 	 */
156 	virtual void widget_restore(window& window) = 0;
157 
158 	/**
159 	 * Enables a widget.
160 	 *
161 	 * @param window              The window containing the widget.
162 	 * @param enable              If true enables the widget, disables
163 	 *                            otherwise.
164 	 * @param sync                If the state is changed do we need to
165 	 *                            synchronize. Upon disabling, write the value
166 	 *                            of the widget in the variable value_. Upon
167 	 *                            enabling write the value of value_ in the
168 	 *                            widget.
169 	 */
widget_set_enabled(window & window,const bool enable,const bool sync)170 	void widget_set_enabled(window& window, const bool enable, const bool sync)
171 	{
172 		styled_widget* widget = dynamic_cast<styled_widget*>(window.find(id(), false));
173 
174 		if(!widget) {
175 			return;
176 		}
177 
178 		const bool widget_state = widget->get_active();
179 		if(widget_state == enable) {
180 			return;
181 		}
182 
183 		if(sync) {
184 			if(enable) {
185 				widget_restore(window);
186 			} else {
187 				widget_save(window);
188 			}
189 		}
190 
191 		widget->set_active(enable);
192 	}
193 
194 	/***** ***** ***** setters / getters for members ***** ****** *****/
195 
id() const196 	const std::string& id() const
197 	{
198 		return id_;
199 	}
200 
is_mandatory() const201 	bool is_mandatory() const
202 	{
203 		return mandatory_;
204 	}
205 
get_widget()206 	styled_widget* get_widget()
207 	{
208 		return widget_;
209 	}
210 
get_widget() const211 	const styled_widget* get_widget() const
212 	{
213 		return widget_;
214 	}
215 
216 private:
217 	/** The id field of the widget, should be unique in a window. */
218 	const std::string id_;
219 
220 	/** Is the widget optional or mandatory in this window. */
221 	const bool mandatory_;
222 
223 	/** The widget attached to the field. */
224 	styled_widget* widget_;
225 
226 	/** See widget_init. */
227 	virtual void init_generic(window& window) = 0;
228 
229 	/** See widget_init. */
init_specialized(window &)230 	virtual void init_specialized(window& /*window*/)
231 	{
232 	}
233 
234 	/** See widget_finalize. */
235 	virtual void finalize_generic(window& window) = 0;
236 
237 	/** See widget_finalize. */
finalize_specialized(window &)238 	virtual void finalize_specialized(window& /*window*/)
239 	{
240 	}
241 };
242 
243 /**
244  * Template class to implement the generic field implementation.
245  *
246  * @tparam T                      The type of the item to show in the widget.
247  * @tparam W                      The type of widget to show, this is not a
248  *                                widget class but a behavior class.
249  * @tparam CT                     The type tp be used in the
250  *                                callback_save_value callback. Normally this
251  *                                is const T but for example with strings it
252  *                                can be const T&. Note the const needs to be
253  *                                in the template otherwise compilation on
254  *                                GCC-4.3 fails (not sure whether compiler bug
255  *                                or not).
256  */
257 template <class T, class W, class CT>
258 class field : public field_base
259 {
260 public:
261 	/**
262 	 * Constructor.
263 	 *
264 	 * @param id                  The id of the widget to connect to the window.
265 	 *                            A widget can only be connected once.
266 	 * @param mandatory           Is the widget mandatory?
267 	 * @param callback_load_value A callback function which is called when the
268 	 *                            window is shown. This callback returns the
269 	 *                            initial value of the field.
270 	 * @param callback_save_value A callback function which is called when the
271 	 *                            window closed with the OK button. The
272 	 *                            callback is executed with the new value of
273 	 *                            the field. It's meant to set the value of
274 	 *                            some variable in the engine after the window
275 	 *                            is closed with OK.
276 	 */
field(const std::string & id,const bool mandatory,const std::function<T ()> & callback_load_value,const std::function<void (CT)> & callback_save_value)277 	field(const std::string& id,
278 		   const bool mandatory,
279 		   const std::function<T()>& callback_load_value,
280 		   const std::function<void(CT)>& callback_save_value)
281 		: field_base(id, mandatory)
282 		, value_(T())
283 		, link_(value_)
284 		, callback_load_value_(callback_load_value)
285 		, callback_save_value_(callback_save_value)
286 	{
287 		static_assert(!std::is_same<styled_widget, W>::value, "Second template argument cannot be styled_widget");
288 	}
289 
290 	/**
291 	 * Constructor.
292 	 *
293 	 * @param id                  The id of the widget to connect to the window.
294 	 *                            A widget can only be connected once.
295 	 * @param mandatory           Is the widget mandatory?
296 	 * @param linked_variable     The variable which is linked to the field.
297 	 *                            * Upon loading its value is used as initial
298 	 *                              value of the widget.
299 	 *                            * Upon closing:
300 	 *                              * with OK its value is set to the value of
301 	 *                                the widget.
302 	 *                              * else, its value is undefined.
303 	 */
field(const std::string & id,const bool mandatory,T & linked_variable)304 	field(const std::string& id, const bool mandatory, T& linked_variable)
305 		: field_base(id, mandatory)
306 		, value_(T())
307 		, link_(linked_variable)
308 		, callback_load_value_(nullptr)
309 		, callback_save_value_(nullptr)
310 	{
311 		static_assert(!std::is_same<styled_widget, W>::value, "Second template argument cannot be styled_widget");
312 	}
313 
314 	/**
315 	 * Constructor.
316 	 *
317 	 * This version is used for read only variables.
318 	 *
319 	 * @note The difference between this constructor and the one above is the
320 	 * sending of the third parameter as const ref instead of a non-const ref.
321 	 * So it feels a bit tricky. Since this constructor is only used for a
322 	 * the @ref styled_widget class and the other constructors not the issue is
323 	 * solved by using static asserts to test whether the proper constructor
324 	 * is used.
325 	 *
326 	 * @param mandatory            Is the widget mandatory?
327 	 * @param id                  The id of the widget to connect to the window.
328 	 *                            A widget can only be connected once.
329 	 * @param value               The value of the widget.
330 	 */
field(const std::string & id,const bool mandatory,const T & value)331 	field(const std::string& id, const bool mandatory, const T& value)
332 		: field_base(id, mandatory)
333 		, value_(value)
334 		, link_(value_)
335 		, callback_load_value_(nullptr)
336 		, callback_save_value_(nullptr)
337 	{
338 		static_assert(std::is_same<styled_widget, W>::value, "Second template argument must be styled_widget");
339 	}
340 
341 	/** Inherited from field_base. */
widget_restore(window & window)342 	void widget_restore(window& window)
343 	{
344 		validate_widget(window);
345 
346 		restore(window);
347 	}
348 
349 	/**
350 	 * Sets the value of the field.
351 	 *
352 	 * This sets the value in both the internal cache value and in the widget
353 	 * itself.
354 	 *
355 	 * @param window              The window containing the widget.
356 	 * @param value               The new value.
357 	 */
set_widget_value(window & window,CT value)358 	void set_widget_value(window& window, CT value)
359 	{
360 		value_ = value;
361 		restore(window);
362 	}
363 
364 	/**
365 	 * Sets the value of the field.
366 	 *
367 	 * This sets the internal cache value but not the widget value, this can
368 	 * be used to initialize the field.
369 	 *
370 	 * @param value               The new value.
371 	 */
set_cache_value(CT value)372 	void set_cache_value(CT value)
373 	{
374 		value_ = value;
375 	}
376 
377 	/** Inherited from field_base. */
widget_save(window & window)378 	void widget_save(window& window)
379 	{
380 		save(window, false);
381 	}
382 
383 	/**
384 	 * Gets the value of the field.
385 	 *
386 	 * This function gets the value of the widget and stores that in the
387 	 * internal cache, then that value is returned.
388 	 *
389 	 * @deprecated Use references to a variable instead.
390 	 *
391 	 * @param window              The window containing the widget.
392 	 *
393 	 * @returns                   The current value of the widget.
394 	 */
get_widget_value(window & window)395 	T get_widget_value(window& window)
396 	{
397 		save(window, false);
398 		return value_;
399 	}
400 
401 private:
402 	/**
403 	 * The value_ of the widget, this value is also available once the widget
404 	 * is destroyed.
405 	 */
406 	T value_;
407 
408 	/**
409 	 * The variable linked to the field.
410 	 *
411 	 * When set determines the initial value and the final value is stored here
412 	 * in the finalizer.
413 	 */
414 	T& link_;
415 
416 	/**
417 	 * The callback function to load the value.
418 	 *
419 	 * This is used to load the initial value of the widget, if defined.
420 	 */
421 	std::function<T()> callback_load_value_;
422 
423 	/** Inherited from field_base. */
init_generic(window & window)424 	void init_generic(window& window)
425 	{
426 		validate_widget(window);
427 
428 		if(callback_load_value_) {
429 			value_ = callback_load_value_();
430 		} else {
431 			value_ = link_;
432 		}
433 
434 		restore(window);
435 	}
436 
437 	/** Inherited from field_base. */
finalize_generic(window & window)438 	void finalize_generic(window& window)
439 	{
440 		save(window, true);
441 
442 		if(callback_save_value_) {
443 			callback_save_value_(value_);
444 		} else {
445 			link_ = value_;
446 		}
447 	}
448 
449 	/**
450 	 * The callback function to save the value.
451 	 *
452 	 * Once the dialog has been successful this function is used to store the
453 	 * result of this widget.
454 	 */
455 	std::function<void(CT)> callback_save_value_;
456 
457 	/**
458 	 * Test whether the widget exists if the widget is mandatory.
459 	 *
460 	 * @param window              The window containing the widget.
461 	 */
validate_widget(window & window)462 	void validate_widget(window& window)
463 	{
464 		if(!is_mandatory()) {
465 			return;
466 		}
467 		find_widget<const W>(&window, id(), false);
468 	}
469 
470 	/**
471 	 * Stores the value in the widget in the interval value_.
472 	 *
473 	 * @param window              The window containing the widget.
474 	 * @param must_be_active      If true only active widgets will store their
475 	 *value.
476 	 */
477 	void save(window& window, const bool must_be_active);
478 
479 	/**
480 	 * Stores the internal value_ in the widget.
481 	 *
482 	 * @param window              The window containing the widget.
483 	 */
484 	void restore(window& window);
485 };
486 
487 template <class T, class W, class CT>
save(window & window,const bool must_be_active)488 void field<T, W, CT>::save(window& window, const bool must_be_active)
489 {
490 	const W* widget
491 			= find_widget<const W>(&window, id(), must_be_active, false);
492 
493 	if(widget) {
494 		value_ = widget->get_value();
495 	}
496 }
497 
498 template <>
save(window & window,const bool must_be_active)499 inline void field<bool, selectable_item>::save(
500 		window& window, const bool must_be_active)
501 {
502 	const selectable_item* selectable
503 			= find_widget<const selectable_item>(&window, id(), must_be_active, false);
504 
505 	if(selectable) {
506 		value_ = selectable->get_value_bool();
507 	}
508 }
509 
510 template <>
save(window & window,const bool must_be_active)511 inline void field<std::string, styled_widget, const std::string&>::save(
512 		window& window, const bool must_be_active)
513 {
514 	const styled_widget* control
515 			= find_widget<const styled_widget>(&window, id(), must_be_active, false);
516 
517 	if(control) {
518 		value_ = control->get_label();
519 	}
520 }
521 
522 template <class T, class W, class CT>
restore(window & window)523 void field<T, W, CT>::restore(window& window)
524 {
525 	W* widget = find_widget<W>(&window, id(), false, false);
526 
527 	if(widget) {
528 		widget->set_value(value_);
529 	}
530 }
531 
532 template <>
533 inline void
restore(window & window)534 field<std::string, styled_widget, const std::string&>::restore(window& window)
535 {
536 	styled_widget* control = find_widget<styled_widget>(&window, id(), false, false);
537 
538 	if(control) {
539 		control->set_label(value_);
540 	}
541 }
542 
543 /** Specialized field class for boolean. */
544 class field_bool : public field<bool, selectable_item>
545 {
546 public:
field_bool(const std::string & id,const bool mandatory,const std::function<bool ()> & callback_load_value,const std::function<void (const bool)> & callback_save_value,const std::function<void (widget &)> & callback_change,const bool initial_fire)547 	field_bool(const std::string& id,
548 				const bool mandatory,
549 				const std::function<bool()>& callback_load_value,
550 				const std::function<void(const bool)>& callback_save_value,
551 				const std::function<void(widget&)>& callback_change,
552 				const bool initial_fire)
553 		: field<bool, gui2::selectable_item>(
554 				  id, mandatory, callback_load_value, callback_save_value)
555 		, callback_change_(callback_change)
556 		, initial_fire_(initial_fire)
557 	{
558 	}
559 
field_bool(const std::string & id,const bool mandatory,bool & linked_variable,const std::function<void (widget &)> & callback_change,const bool initial_fire)560 	field_bool(const std::string& id,
561 				const bool mandatory,
562 				bool& linked_variable,
563 				const std::function<void(widget&)>& callback_change,
564 				const bool initial_fire)
565 		: field<bool, gui2::selectable_item>(id, mandatory, linked_variable)
566 		, callback_change_(callback_change)
567 		, initial_fire_(initial_fire)
568 	{
569 	}
570 
571 private:
572 	/** Overridden from field_base. */
init_specialized(window & window)573 	void init_specialized(window& window)
574 	{
575 		if(callback_change_) {
576 			if(widget* widget = window.find(id(), false)) {
577 				if(initial_fire_) {
578 					callback_change_(*widget);
579 				}
580 
581 				connect_signal_notify_modified(*widget, std::bind(callback_change_, _1));
582 			}
583 		}
584 	}
585 
586 	std::function<void(widget&)> callback_change_;
587 
588 	const bool initial_fire_;
589 };
590 
591 /** Specialized field class for text. */
592 class field_text : public field<std::string, text_box_base, const std::string&>
593 {
594 public:
field_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)595 	field_text(const std::string& id,
596 				const bool mandatory,
597 				const std::function<std::string()>& callback_load_value,
598 				const std::function<void(const std::string&)>&
599 						callback_save_value)
600 		: field<std::string, text_box_base, const std::string&>(
601 				  id, mandatory, callback_load_value, callback_save_value)
602 	{
603 	}
604 
field_text(const std::string & id,const bool mandatory,std::string & linked_variable)605 	field_text(const std::string& id,
606 				const bool mandatory,
607 				std::string& linked_variable)
608 		: field<std::string, text_box_base, const std::string&>(
609 				  id, mandatory, linked_variable)
610 	{
611 	}
612 
613 private:
614 	/** Overridden from field_base. */
finalize_specialized(window & window)615 	void finalize_specialized(window& window)
616 	{
617 		text_box* widget = dynamic_cast<text_box*>(window.find(id(), false));
618 
619 		if(widget) {
620 			widget->save_to_history();
621 		}
622 	}
623 };
624 
625 /** Specialized field class for a styled_widget, used for labels and images. */
626 class field_label : public field<std::string, styled_widget, const std::string&>
627 {
628 public:
field_label(const std::string & id,const bool mandatory,const std::string & text,const bool use_markup)629 	field_label(const std::string& id,
630 				 const bool mandatory,
631 				 const std::string& text,
632 				 const bool use_markup)
633 		: field<std::string, styled_widget, const std::string&>(id, mandatory, text)
634 		, use_markup_(use_markup)
635 	{
636 	}
637 
638 private:
639 	/** Whether or not the label uses markup. */
640 	bool use_markup_;
641 
642 	/** Overridden from field_base. */
init_specialized(window & window)643 	void init_specialized(window& window)
644 	{
645 		find_widget<styled_widget>(&window, id(), false).set_use_markup(use_markup_);
646 	}
647 };
648 
649 } // namespace gui2
650