The Battle for Wesnoth  1.19.5+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>("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 {
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  text_box& hostname_box = find_widget<text_box>("host_name");
115  listbox& server_list = find_widget<listbox>("server_list");
116  button& button_add = find_widget<button>("server_add");
117  button& button_del = find_widget<button>("server_delete");
118 
119  const auto& address = boost::trim_copy(hostname_box.get_value());
120 
121  std::size_t row = 0;
122 
123  for(const auto* servers : server_lists()) {
124  for(const auto& server : *servers) {
125  if(server.address == address) {
126  server_list.select_row(row);
127  // Can't Add what's already there or Delete built-in servers
128  button_add.set_active(false);
129  button_del.set_active(servers == &user_servers_);
130  return;
131  }
132 
133  ++row;
134  }
135  }
136 
137  // NOTE: Do not use this in production. It requires the listbox to be
138  // defined with has_minimum=false in WML, and makes some UI interactions
139  // awkward. In particular it means we would need to keep track of where
140  // the selection was last at every time we clear the selection so that
141  // the Add button can add under it instead of appending to the very end
142  // of the list (currently it just bails out if there's no selection).
143 #if 0
144  // The user wrote a brand new hostname in so there's no matches, clear the
145  // selection accordingly
146  clear_listbox_selection(server_list);
147  button_del.set_active(false);
148 #endif
149  button_add.set_active(!address.empty());
150 }
151 
153 {
154  // Select the first matching list entry or clear the current selection
156 }
157 
159 {
160  text_box& hostname_box = find_widget<text_box>("host_name");
161  listbox& server_list = find_widget<listbox>("server_list");
162 
163  const auto& address = boost::trim_copy(hostname_box.get_value());
164  const auto& selection = current_selection();
165 
166  if(address.empty() || !selection.valid()) {
167  // We're not supposed to be here
168  return;
169  }
170 
171  // We insert under the selection. If a built-in server is selected or the
172  // user-defined list is empty, we insert at the start of the user-defined
173  // list instead.
174 
175  const std::size_t mem_pos = selection.user_defined() && !user_servers_.empty()
176  ? 1 + selection.relative_index() : 0;
177  const unsigned int ui_pos = selection.user_defined() ? 1 + selection.row() : builtin_servers_.size();
178 
179  std::string name;
180 
181  if(!gui2::dialogs::edit_text::execute(_("Add Server"), _("Name:"), name, true) || name.empty()) {
182  return;
183  }
184 
186  info.name = name;
187  info.address = address;
188 
189  user_servers_.insert(user_servers_.begin() + mem_pos, info);
191 
194 }
195 
197 {
198  listbox& server_list = find_widget<listbox>("server_list");
199 
200  auto selection = current_selection();
201 
202  if(!selection.valid() || !selection.user_defined()) {
203  // We're not supposed to be here
204  return;
205  }
206 
209 
210  server_list.remove_row(selection.row());
212 }
213 
215 {
216  text_box& hostname_box = find_widget<text_box>("host_name");
217  button& button_add = find_widget<button>("server_add");
218  button& button_del = find_widget<button>("server_delete");
219 
220  auto selection = current_selection();
221 
222  if(!selection.valid()) {
223  // The user cleared the selection. We can't delete what isn't selected
224  // and the Add button's status was already set to a value that makes
225  // sense by another signal handler, so just disable Delete.
226  button_del.set_active(false);
227  return;
228  }
229 
230  hostname_box.set_value(selection.get().address);
231 
232  // Can't Add what's already there
233  button_add.set_active(false);
234  // Can only Delete user-defined servers
235  button_del.set_active(selection.user_defined());
236 }
237 
239 {
240  listbox& server_list = find_widget<listbox>("server_list");
241  return { this, server_list.get_selected_row() };
242 }
243 
245 {
246  must_be_valid();
247  return parent_list().at(relative_index());
248 }
249 
251 {
252  // An invalid selection is the same as one from the read-only list of
253  // built-in servers for interaction purposes since it can't be written to.
254  return valid() && std::size_t(row_) >= owner_->builtin_servers_.size();
255 }
256 
258 {
259  must_be_valid();
260  return unsigned(row_);
261 }
262 
264 {
265  must_be_valid();
266  return user_defined() ? row() - owner_->builtin_servers_.size() : row();
267 }
268 
270 {
271  must_be_valid();
272  return user_defined() ? owner_->user_servers_ : owner_->builtin_servers_;
273 }
274 
275 } // namespace dialogs
276 } // 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:269
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: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:58
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:485
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:36
std::map< std::string, t_string > widget_item
Definition: widget.hpp:33
logger & info()
Definition: log.cpp:317
std::string address
may include ':' followed by port number
Definition: game_config.hpp:71