1 /*
2    Copyright (C) 2011 - 2018 by Lukasz Dobrogowski
3    <lukasz.dobrogowski@gmail.com>
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 
18 #include "gui/dialogs/multiplayer/mp_change_control.hpp"
19 
20 #include "font/text_formatting.hpp"
21 #include "formatter.hpp"
22 #include "formula/string_utils.hpp"
23 #include "game_board.hpp"
24 #include "game_display.hpp"
25 #include "preferences/credentials.hpp"
26 #include "gui/auxiliary/find_widget.hpp"
27 #include "gui/widgets/label.hpp"
28 #include "gui/widgets/listbox.hpp"
29 #include "gui/widgets/settings.hpp"
30 #include "gui/widgets/window.hpp"
31 #include "log.hpp"
32 #include "menu_events.hpp"
33 #include "team.hpp"
34 
35 #include "utils/functional.hpp"
36 
37 static lg::log_domain log_gui("gui/dialogs/mp_change_control");
38 #define ERR_GUI LOG_STREAM(err,   log_gui)
39 #define WRN_GUI LOG_STREAM(warn,  log_gui)
40 #define LOG_GUI LOG_STREAM(info,  log_gui)
41 #define DBG_GUI LOG_STREAM(debug, log_gui)
42 
43 namespace gui2
44 {
45 namespace dialogs
46 {
47 
48 /*WIKI
49  * @page = GUIWindowDefinitionWML
50  * @order = 2_mp_change_control
51  *
52  * == Change control dialog ==
53  *
54  * This shows the multiplayer change control dialog.
55  *
56  * @begin{table}{dialog_widgets}
57  * sides_list & & listbox & m &
58  *         List of sides participating in the MP game. $
59  *
60  * nicks_list & & listbox & m &
61  *         List of nicks of all clients playing or observing the MP game. $
62  *
63  * @end{table}
64  *
65  */
66 
REGISTER_DIALOG(mp_change_control)67 REGISTER_DIALOG(mp_change_control)
68 
69 mp_change_control::mp_change_control(events::menu_handler& mh)
70 	: menu_handler_(mh)
71 	, selected_side_(0)
72 	, selected_nick_(0)
73 	, sides_()
74 	, nicks_()
75 {
76 }
77 
pre_show(window & window)78 void mp_change_control::pre_show(window& window)
79 {
80 	listbox& sides_list = find_widget<listbox>(&window, "sides_list", false);
81 	listbox& nicks_list = find_widget<listbox>(&window, "nicks_list", false);
82 
83 	connect_signal_notify_modified(sides_list,
84 		std::bind(&mp_change_control::handle_sides_list_item_clicked, this, std::ref(window)));
85 
86 	connect_signal_notify_modified(nicks_list,
87 		std::bind(&mp_change_control::handle_nicks_list_item_clicked, this, std::ref(window)));
88 
89 	//
90 	// Initialize sides list
91 	//
92 	const unsigned int num_sides = menu_handler_.board().teams().size();
93 
94 	for(unsigned int side = 1; side <= num_sides; ++side) {
95 		if(menu_handler_.board().get_team(side).hidden()) {
96 			continue;
97 		}
98 
99 		sides_.push_back(side);
100 
101 		std::map<std::string, string_map> data;
102 		string_map item;
103 
104 		std::string side_str = VGETTEXT("Side $side", {{"side", std::to_string(side)}});
105 		side_str = font::span_color(team::get_side_color(side)) + side_str + "</span>";
106 
107 		item["id"] = (formatter() << "side_" << side).str();
108 		item["label"] = side_str;
109 		item["use_markup"] = "true";
110 		data.emplace("side", item);
111 
112 		sides_list.add_row(data);
113 	}
114 
115 	//
116 	// Prepare set of available nicknames
117 	//
118 	std::set<std::string> temp_nicks;
119 	for(const auto& team : menu_handler_.board().teams()) {
120 		if(!team.is_local_ai() && !team.is_network_ai() && !team.is_idle()
121 			&& !team.is_empty() && !team.current_player().empty())
122 		{
123 			temp_nicks.insert(team.current_player());
124 		}
125 	}
126 
127 	const std::set<std::string>& observers = game_display::get_singleton()->observers();
128 	temp_nicks.insert(observers.begin(), observers.end());
129 
130 	// In case we are an observer, it isn't in the observers set and has to be added manually.
131 	temp_nicks.insert(preferences::login());
132 
133 	//
134 	// Initialize nick list
135 	//
136 	for(const std::string& nick : temp_nicks) {
137 		nicks_.push_back(nick);
138 
139 		std::map<std::string, string_map> data;
140 		string_map item;
141 
142 		item["id"] = nick;
143 		item["label"] = nick;
144 		item["use_markup"] = "true";
145 		data.emplace("nick", item);
146 
147 		nicks_list.add_row(data);
148 	}
149 
150 	handle_sides_list_item_clicked(window);
151 	handle_nicks_list_item_clicked(window);
152 }
153 
handle_sides_list_item_clicked(window & window)154 void mp_change_control::handle_sides_list_item_clicked(window& window)
155 {
156 	selected_side_ = find_widget<listbox>(&window, "sides_list", false).get_selected_row();
157 
158 	highlight_side_nick(window);
159 }
160 
handle_nicks_list_item_clicked(window & window)161 void mp_change_control::handle_nicks_list_item_clicked(window& window)
162 {
163 	selected_nick_ = find_widget<listbox>(&window, "nicks_list", false).get_selected_row();
164 }
165 
highlight_side_nick(window & window)166 void mp_change_control::highlight_side_nick(window& window)
167 {
168 	listbox& nicks_list = find_widget<listbox>(&window, "nicks_list", false);
169 	const auto& teams = menu_handler_.board().teams();
170 
171 	int i = 0;
172 	for(const std::string& nick : nicks_) {
173 		std::string label_str = "";
174 
175 		if(selected_side_ <= static_cast<unsigned int>(teams.size()) && teams.at(selected_side_).current_player() == nick) {
176 			label_str = formatter() << "<b>" << nick << "</b>";
177 		} else {
178 			label_str = nick;
179 		}
180 
181 		grid* row_grid = nicks_list.get_row_grid(i);
182 		find_widget<label>(row_grid, nick, false).set_label(label_str);
183 
184 		++i;
185 	}
186 }
187 
post_show(window & window)188 void mp_change_control::post_show(window& window)
189 {
190 	if(window.get_retval() == retval::OK) {
191 		DBG_GUI << "Main: changing control of side "
192 		        << sides_[selected_side_] << " to nick "
193 		        << nicks_[selected_nick_] << std::endl;
194 
195 		menu_handler_.request_control_change(
196 			sides_[selected_side_],
197 			nicks_[selected_nick_]
198 		);
199 	}
200 }
201 
202 } // namespace dialogs
203 } // namespace gui2
204