The Battle for Wesnoth  1.19.10+dev
mp_connect.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2025
3  by Mark de Wever <koraq@xs4all.nl>
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 
19 
20 #include "gettext.hpp"
22 #include "gui/auxiliary/field.hpp"
25 #include "gui/widgets/button.hpp"
26 #include "gui/widgets/listbox.hpp"
27 
28 #include "log.hpp"
29 
30 #include <functional>
31 
32 #include <boost/algorithm/string/trim.hpp>
33 
34 static lg::log_domain log_mpconnect{"gui/dialogs/mp_connect"};
35 #define ERR_DLG LOG_STREAM(err, log_mpconnect)
36 #define WRN_DLG LOG_STREAM(warn, log_mpconnect)
37 #define LOG_DLG LOG_STREAM(info, log_mpconnect)
38 #define DBG_DLG LOG_STREAM(debug, log_mpconnect)
39 
40 namespace gui2
41 {
42 
43 namespace
44 {
45 
46 // NOTE: See mp_connect::select_first_match() below
47 #if 0
48 void clear_listbox_selection(listbox& listbox)
49 {
50  const auto selection = listbox.get_selected_row();
51  if(selection >= 0) {
52  listbox.select_row(selection, false);
53  }
54 }
55 #endif
56 
57 }
58 
59 namespace dialogs
60 {
61 
62 REGISTER_DIALOG(mp_connect)
63 
65  : modal_dialog(window_id())
66  , host_name_(register_text("host_name",
67  true,
68  []() {return prefs::get().network_host();},
69  [](const std::string& v) {prefs::get().set_network_host(v);},
70  true))
71  , builtin_servers_(prefs::get().builtin_servers_list())
72  , user_servers_(prefs::get().user_servers_list())
73 {
74 }
75 
76 std::array<mp_connect::server_list*, 2> mp_connect::server_lists()
77 {
78  return {{ &builtin_servers_, &user_servers_ }};
79 }
80 
82 {
83  text_box& hostname_box = find_widget<text_box>("host_name");
84  listbox& server_list = find_widget<listbox>("server_list");
85  button& button_add = find_widget<button>("server_add");
86  button& button_del = find_widget<button>("server_delete");
87 
88  for(const auto* servers : server_lists()) {
89  for(const auto& server : *servers) {
91  }
92  }
93 
95 
96  connect_signal_notify_modified(hostname_box, std::bind(&mp_connect::on_address_change, this));
98  connect_signal_mouse_left_click(button_add, std::bind(&mp_connect::on_server_add, this));
100 }
101 
103 {
105  { "name", {
106  { "label", srv.name }
107  }},
108  { "address", {
109  { "label", srv.address }
110  }},
111  }, pos);
112 }
113 
115 {
116  text_box& hostname_box = find_widget<text_box>("host_name");
117  listbox& server_list = find_widget<listbox>("server_list");
118  button& button_add = find_widget<button>("server_add");
119  button& button_del = find_widget<button>("server_delete");
120 
121  const auto& address = boost::trim_copy(hostname_box.get_value());
122 
123  std::size_t row = 0;
124 
125  for(const auto* servers : server_lists()) {
126  for(const auto& server : *servers) {
127  if(server.address == address) {
128  server_list.select_row(row);
129  // Can't Add what's already there or Delete built-in servers
130  button_add.set_active(false);
131  button_del.set_active(servers == &user_servers_);
132  return;
133  }
134 
135  ++row;
136  }
137  }
138 
139  // NOTE: Do not use this in production. It requires the listbox to be
140  // defined with has_minimum=false in WML, and makes some UI interactions
141  // awkward. In particular it means we would need to keep track of where
142  // the selection was last at every time we clear the selection so that
143  // the Add button can add under it instead of appending to the very end
144  // of the list (currently it just bails out if there's no selection).
145 #if 0
146  // The user wrote a brand new hostname in so there's no matches, clear the
147  // selection accordingly
148  clear_listbox_selection(server_list);
149  button_del.set_active(false);
150 #endif
151  button_add.set_active(!address.empty());
152 }
153 
155 {
156  // Select the first matching list entry or clear the current selection
158 }
159 
161 {
162  text_box& hostname_box = find_widget<text_box>("host_name");
163  listbox& server_list = find_widget<listbox>("server_list");
164 
165  const auto& address = boost::trim_copy(hostname_box.get_value());
166  const auto& selection = current_selection();
167 
168  if(address.empty() || !selection.valid()) {
169  // We're not supposed to be here
170  return;
171  }
172 
173  // We insert under the selection. If a built-in server is selected or the
174  // user-defined list is empty, we insert at the start of the user-defined
175  // list instead.
176 
177  const std::size_t mem_pos = selection.user_defined() && !user_servers_.empty()
178  ? 1 + selection.relative_index() : 0;
179  const unsigned int ui_pos = selection.user_defined() ? 1 + selection.row() : builtin_servers_.size();
180 
181  std::string name;
182 
183  if(!gui2::dialogs::edit_text::execute(_("Add Server"), _("Name:"), name, true) || name.empty()) {
184  return;
185  }
186 
188  info.name = name;
189  info.address = address;
190 
191  user_servers_.insert(user_servers_.begin() + mem_pos, info);
193 
196 }
197 
199 {
200  listbox& server_list = find_widget<listbox>("server_list");
201 
202  auto selection = current_selection();
203 
204  if(!selection.valid() || !selection.user_defined()) {
205  // We're not supposed to be here
206  return;
207  }
208 
211 
212  server_list.remove_row(selection.row());
214 }
215 
217 {
218  text_box& hostname_box = find_widget<text_box>("host_name");
219  button& button_add = find_widget<button>("server_add");
220  button& button_del = find_widget<button>("server_delete");
221 
222  auto selection = current_selection();
223 
224  if(!selection.valid()) {
225  // The user cleared the selection. We can't delete what isn't selected
226  // and the Add button's status was already set to a value that makes
227  // sense by another signal handler, so just disable Delete.
228  button_del.set_active(false);
229  return;
230  }
231 
232  hostname_box.set_value(selection.get().address);
233 
234  // Can't Add what's already there
235  button_add.set_active(false);
236  // Can only Delete user-defined servers
237  button_del.set_active(selection.user_defined());
238 }
239 
241 {
242  listbox& server_list = find_widget<listbox>("server_list");
243  return { this, server_list.get_selected_row() };
244 }
245 
247 {
248  must_be_valid();
249  return parent_list().at(relative_index());
250 }
251 
253 {
254  // An invalid selection is the same as one from the read-only list of
255  // built-in servers for interaction purposes since it can't be written to.
256  return valid() && std::size_t(row_) >= owner_->builtin_servers_.size();
257 }
258 
260 {
261  must_be_valid();
262  return unsigned(row_);
263 }
264 
266 {
267  must_be_valid();
268  return user_defined() ? row() - owner_->builtin_servers_.size() : row();
269 }
270 
272 {
273  must_be_valid();
274  return user_defined() ? owner_->user_servers_ : owner_->builtin_servers_;
275 }
276 
277 } // namespace dialogs
278 } // namespace gui2
Simple push button.
Definition: button.hpp:36
virtual void set_active(const bool active) override
See styled_widget::set_active.
Definition: button.cpp:64
Abstract base class for all modal dialogs.
server_list & parent_list() const
Definition: mp_connect.cpp:271
void insert_into_server_listbox(listbox &listbox, const server_info &srv, int pos=-1)
Definition: mp_connect.cpp:102
std::vector< server_info > server_list
Definition: mp_connect.hpp:59
std::array< server_list *, 2 > server_lists()
Definition: mp_connect.cpp:76
virtual void pre_show() override
Actions to be taken before showing the window.
Definition: mp_connect.cpp:81
The listbox class.
Definition: listbox.hpp:41
grid & add_row(const widget_item &item, const int index=-1)
When an item in the list is selected by the user we need to update the state.
Definition: listbox.cpp:92
std::string get_value() const
virtual void set_value(const std::string &text)
The set_value is virtual for the password_box class.
A widget that allows the user to input text in single line.
Definition: text_box.hpp:125
dialogs::modal_dialog * owner_
The dialog that owns the window.
Definition: window.hpp:486
void set_network_host(const std::string &host)
std::string network_host()
static prefs & get()
void set_user_servers_list(const std::vector< game_config::server_info > &value)
Implements some helper classes to ease adding fields to a dialog and hide the synchronization needed.
static std::string _(const char *str)
Definition: gettext.hpp:97
Standard logging facilities (interface).
static lg::log_domain log_mpconnect
Definition: mp_connect.cpp:34
Various uncategorised dialogs.
REGISTER_DIALOG(editor_edit_unit)
void connect_signal_notify_modified(dispatcher &dispatcher, const signal_notification &signal)
Connects a signal handler for getting a notification upon modification.
Definition: dispatcher.cpp:189
void connect_signal_mouse_left_click(dispatcher &dispatcher, const signal &signal)
Connects a signal handler for a left mouse button click.
Definition: dispatcher.cpp:163
Generic file dialog.
std::map< std::string, widget_item > widget_data
Definition: widget.hpp:36
logger & info()
Definition: log.cpp:318
std::string address
may include ':' followed by port number
Definition: game_config.hpp:73