The Battle for Wesnoth  1.19.1+dev
mp_connect.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2024
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  [](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>(&win, "host_name", false);
84  listbox& server_list = find_widget<listbox>(&win, "server_list", false);
85  button& button_add = find_widget<button>(&win, "server_add", false);
86  button& button_del = find_widget<button>(&win, "server_delete", false);
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 {
104  const widget_data& entry{
105  { "name", widget_item{{"label", srv.name}} },
106  { "address", widget_item{{"label", srv.address}} },
107  };
108 
109  listbox.add_row(entry, pos);
110 }
111 
113 {
114  window* window = get_window();
115 
116  text_box& hostname_box = find_widget<text_box>(window, "host_name", false);
117  listbox& server_list = find_widget<listbox>(window, "server_list", false);
118  button& button_add = find_widget<button>(window, "server_add", false);
119  button& button_del = find_widget<button>(window, "server_delete", false);
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  window* window = get_window();
163 
164  text_box& hostname_box = find_widget<text_box>(window, "host_name", false);
165  listbox& server_list = find_widget<listbox>(window, "server_list", false);
166 
167  const auto& address = boost::trim_copy(hostname_box.get_value());
168  const auto& selection = current_selection();
169 
170  if(address.empty() || !selection.valid()) {
171  // We're not supposed to be here
172  return;
173  }
174 
175  // We insert under the selection. If a built-in server is selected or the
176  // user-defined list is empty, we insert at the start of the user-defined
177  // list instead.
178 
179  const std::size_t mem_pos = selection.user_defined() && !user_servers_.empty()
180  ? 1 + selection.relative_index() : 0;
181  const unsigned int ui_pos = selection.user_defined() ? 1 + selection.row() : builtin_servers_.size();
182 
183  std::string name;
184 
185  if(!gui2::dialogs::edit_text::execute(_("Add Server"), _("Name:"), name, true) || name.empty()) {
186  return;
187  }
188 
190  info.name = name;
191  info.address = address;
192 
193  user_servers_.insert(user_servers_.begin() + mem_pos, info);
195 
198 }
199 
201 {
202  window* window = get_window();
203 
204  listbox& server_list = find_widget<listbox>(window, "server_list", false);
205 
206  auto selection = current_selection();
207 
208  if(!selection.valid() || !selection.user_defined()) {
209  // We're not supposed to be here
210  return;
211  }
212 
215 
216  server_list.remove_row(selection.row());
218 }
219 
221 {
222  window* window = get_window();
223 
224  text_box& hostname_box = find_widget<text_box>(window, "host_name", false);
225  button& button_add = find_widget<button>(window, "server_add", false);
226  button& button_del = find_widget<button>(window, "server_delete", false);
227 
228  auto selection = current_selection();
229 
230  if(!selection.valid()) {
231  // The user cleared the selection. We can't delete what isn't selected
232  // and the Add button's status was already set to a value that makes
233  // sense by another signal handler, so just disable Delete.
234  button_del.set_active(false);
235  return;
236  }
237 
238  hostname_box.set_value(selection.get().address);
239 
240  // Can't Add what's already there
241  button_add.set_active(false);
242  // Can only Delete user-defined servers
243  button_del.set_active(selection.user_defined());
244 }
245 
247 {
248  listbox& server_list = find_widget<listbox>(get_window(), "server_list", false);
249  return { this, server_list.get_selected_row() };
250 }
251 
253 {
254  must_be_valid();
255  return parent_list().at(relative_index());
256 }
257 
259 {
260  must_be_valid();
261  return unsigned(row_);
262 }
263 
265 {
266  must_be_valid();
267  return user_defined() ? row() - owner_->builtin_servers_.size() : row();
268 }
269 
271 {
272  must_be_valid();
273  return user_defined() ? owner_->user_servers_ : owner_->builtin_servers_;
274 }
275 
276 } // namespace dialogs
277 } // 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.
window * get_window()
Returns a pointer to the dialog's window.
server_list & parent_list() const
Definition: mp_connect.cpp:270
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(window &window) override
Actions to be taken before showing the window.
Definition: mp_connect.cpp:81
The listbox class.
Definition: listbox.hpp:43
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:59
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
base class of top level items, the only item which needs to store the final canvases to draw on.
Definition: window.hpp:61
dialogs::modal_dialog * owner_
The dialog that owns the window.
Definition: window.hpp:487
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:93
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:203
void connect_signal_mouse_left_click(dispatcher &dispatcher, const signal &signal)
Connects a signal handler for a left mouse button click.
Definition: dispatcher.cpp:177
Generic file dialog.
std::map< std::string, widget_item > widget_data
Definition: widget.hpp:34
std::map< std::string, t_string > widget_item
Definition: widget.hpp:31
logger & info()
Definition: log.cpp:316
std::string address
may include ':' followed by port number
Definition: game_config.hpp:71