@@ -214,12 +214,13 @@ STR_UNITS_VOLUME_LONG_SI :{COMMA} m³
STR_UNITS_FORCE_SI :{COMMA} kN
STR_UNITS_HEIGHT_IMPERIAL :{COMMA} ft
STR_UNITS_HEIGHT_SI :{COMMA} m
# Common window strings
STR_LIST_FILTER_TITLE :{BLACK}Filter string:
STR_LIST_FILTER_OSKTITLE :{BLACK}Enter filter string
STR_LIST_FILTER_TOOLTIP :{BLACK}Enter a keyword to filter the list for
STR_TOOLTIP_SORT_ORDER :{BLACK}Select sorting order (descending/ascending)
STR_TOOLTIP_SORT_CRITERIA :{BLACK}Select sorting criteria
STR_TOOLTIP_FILTER_CRITERIA :{BLACK}Select filtering criteria
@@ -34,12 +34,14 @@
#include "../widgets/network_widget.h"
#include "table/strings.h"
#include "../table/sprites.h"
#include "../stringfilter_type.h"
static void ShowNetworkStartServerWindow();
static void ShowNetworkLobbyWindow(NetworkGameList *ngl);
static const StringID _connection_types_dropdown[] = {
STR_NETWORK_START_SERVER_LAN_INTERNET,
@@ -74,13 +76,13 @@ void SortNetworkLanguages()
*/
void UpdateNetworkGameWindow()
{
InvalidateWindowData(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_GAME, 0);
}
typedef GUIList<NetworkGameList*> GUIGameServerList;
typedef GUIList<NetworkGameList*, StringFilter&> GUIGameServerList;
typedef uint16 ServerListPosition;
static const ServerListPosition SLP_INVALID = 0xFFFF;
/** Full blown container to make it behave exactly as we want :) */
class NWidgetServerListHeader : public NWidgetContainer {
static const uint MINIMUM_NAME_WIDTH_BEFORE_NEW_HEADER = 150; ///< Minimum width before adding a new header
@@ -213,38 +215,56 @@ class NetworkGameWindow : public Window
protected:
/* Runtime saved values */
static Listing last_sorting;
/* Constants for sorting servers */
static GUIGameServerList::SortFunction * const sorter_funcs[];
static GUIGameServerList::FilterFunction * const filter_funcs[];
NetworkGameList *server; ///< selected server
NetworkGameList *last_joined; ///< the last joined server
GUIGameServerList servers; ///< list with game servers.
ServerListPosition list_pos; ///< position of the selected server
Scrollbar *vscroll;
Scrollbar *vscroll; ///< vertical scrollbar of the list of servers
QueryString name_editbox; ///< Client name editbox.
QueryString filter_editbox; ///< Editbox for filter on servers
/**
* (Re)build the network game list as its amount has changed because
* an item has been added or deleted for example
* (Re)build the GUI network game list (a.k.a. this->servers) as some
* major change has occurred. It ensures appropriate filtering and
* sorting, if both or either one is enabled.
void BuildNetworkGameList()
void BuildGUINetworkGameList()
if (!this->servers.NeedRebuild()) return;
/* Create temporary array of games to use for listing */
this->servers.Clear();
for (NetworkGameList *ngl = _network_game_list; ngl != NULL; ngl = ngl->next) {
*this->servers.Append() = ngl;
/* Apply the filter condition immediately, if a search string has been provided. */
StringFilter sf;
sf.SetFilterTerm(this->filter_editbox.text.buf);
if (!sf.IsEmpty()) {
this->servers.SetFilterState(true);
this->servers.Filter(sf);
} else {
this->servers.SetFilterState(false);
this->servers.Compact();
this->servers.RebuildDone();
this->vscroll->SetCount(this->servers.Length());
/* Sort the list of network games as requested. */
this->servers.Sort();
this->UpdateListPos();
* Skip some of the 'garbage' in the string that we don't want to use
* to sort on. This way the alphabetical sorting will work better as
* we would be actually using those characters instead of some other
@@ -341,12 +361,22 @@ protected:
this->list_pos = i;
break;
static bool CDECL NGameSearchFilter(NetworkGameList * const *item, StringFilter &sf)
assert(item != NULL);
assert((*item) != NULL);
sf.ResetState();
sf.AddLine((*item)->info.server_name);
return sf.GetState();
* Draw a single server line.
* @param cur_item the server to draw.
* @param y from where to draw?
* @param highlight does the line need to be highlighted?
@@ -425,34 +455,37 @@ protected:
if (this->list_pos == SLP_INVALID) return; // no server selected
this->vscroll->ScrollTowards(this->list_pos);
public:
NetworkGameWindow(const WindowDesc *desc) : name_editbox(NETWORK_CLIENT_NAME_LENGTH)
NetworkGameWindow(const WindowDesc *desc) : name_editbox(NETWORK_CLIENT_NAME_LENGTH), filter_editbox(120)
this->list_pos = SLP_INVALID;
this->server = NULL;
this->CreateNestedTree(desc);
this->vscroll = this->GetScrollbar(WID_NG_SCROLLBAR);
this->FinishInitNested(desc, WN_NETWORK_WINDOW_GAME);
this->querystrings[WID_NG_CLIENT] = &this->name_editbox;
this->name_editbox.text.Assign(_settings_client.network.client_name);
this->name_editbox.afilter = CS_ALPHANUMERAL;
this->SetFocusedWidget(WID_NG_CLIENT);
this->querystrings[WID_NG_FILTER] = &this->filter_editbox;
this->filter_editbox.cancel_button = QueryString::ACTION_CLEAR;
this->SetFocusedWidget(WID_NG_FILTER);
this->last_joined = NetworkGameListAddItem(NetworkAddress(_settings_client.network.last_host, _settings_client.network.last_port));
this->server = this->last_joined;
if (this->last_joined != NULL) NetworkUDPQueryServer(this->last_joined->address);
this->servers.SetListing(this->last_sorting);
this->servers.SetSortFuncs(this->sorter_funcs);
this->servers.SetFilterFuncs(this->filter_funcs);
this->servers.ForceRebuild();
this->SortNetworkGameList();
~NetworkGameWindow()
this->last_sorting = this->servers.GetListing();
@@ -558,13 +591,13 @@ public:
virtual void OnPaint()
if (this->servers.NeedRebuild()) {
this->BuildNetworkGameList();
this->BuildGUINetworkGameList();
NetworkGameList *sel = this->server;
/* 'Refresh' button invisible if no server selected */
this->SetWidgetDisabledState(WID_NG_REFRESH, sel == NULL);
@@ -678,13 +711,13 @@ public:
this->ScrollToSelectedServer();
this->SetDirty();
case WID_NG_MATRIX: { // Matrix to show networkgames
case WID_NG_MATRIX: { // Show available network games
uint id_v = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_NG_MATRIX);
this->server = (id_v < this->servers.Length()) ? this->servers[id_v] : NULL;
this->list_pos = (server == NULL) ? SLP_INVALID : id_v;
/* FIXME the disabling should go into some InvalidateData, which is called instead of the SetDirty */
@@ -835,12 +868,20 @@ public:
return state;
virtual void OnEditboxChanged(int wid)
switch (wid) {
case WID_NG_FILTER: {
case WID_NG_CLIENT:
/* Make sure the name does not start with a space, so TAB completion works */
if (!StrEmpty(this->name_editbox.text.buf) && this->name_editbox.text.buf[0] != ' ') {
strecpy(_settings_client.network.client_name, this->name_editbox.text.buf, lastof(_settings_client.network.client_name));
strecpy(_settings_client.network.client_name, "Player", lastof(_settings_client.network.client_name));
@@ -873,12 +914,16 @@ GUIGameServerList::SortFunction * const
&NGameMapSizeSorter,
&NGameDateSorter,
&NGameYearsSorter,
&NGameAllowedSorter
};
GUIGameServerList::FilterFunction * const NetworkGameWindow::filter_funcs[] = {
&NGameSearchFilter
static NWidgetBase *MakeResizableHeader(int *biggest_index)
*biggest_index = max<int>(*biggest_index, WID_NG_INFO);
return new NWidgetServerListHeader();
@@ -888,42 +933,51 @@ static const NWidgetPart _nested_network
NWidget(WWT_CLOSEBOX, COLOUR_LIGHT_BLUE),
NWidget(WWT_CAPTION, COLOUR_LIGHT_BLUE), SetDataTip(STR_NETWORK_SERVER_LIST_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
EndContainer(),
NWidget(WWT_PANEL, COLOUR_LIGHT_BLUE, WID_NG_MAIN),
NWidget(NWID_VERTICAL), SetPIP(10, 7, 0),
NWidget(NWID_HORIZONTAL), SetPIP(10, 7, 10),
/* LEFT SIDE */
NWidget(NWID_VERTICAL), SetPIP(0, 7, 0),
NWidget(NWID_HORIZONTAL), SetPIP(0, 7, 0),
NWidget(WWT_TEXT, COLOUR_LIGHT_BLUE, WID_NG_CONNECTION), SetDataTip(STR_NETWORK_SERVER_LIST_CONNECTION, STR_NULL),
NWidget(WWT_DROPDOWN, COLOUR_LIGHT_BLUE, WID_NG_CONN_BTN),
SetDataTip(STR_BLACK_STRING, STR_NETWORK_SERVER_LIST_CONNECTION_TOOLTIP),
NWidget(NWID_SPACER), SetFill(1, 0), SetResize(1, 0),
NWidget(WWT_TEXT, COLOUR_LIGHT_BLUE, WID_NG_CLIENT_LABEL), SetDataTip(STR_NETWORK_SERVER_LIST_PLAYER_NAME, STR_NULL),
NWidget(WWT_EDITBOX, COLOUR_LIGHT_BLUE, WID_NG_CLIENT), SetMinimalSize(151, 12),
SetDataTip(STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE, STR_NETWORK_SERVER_LIST_ENTER_NAME_TOOLTIP),
NWidget(NWID_VERTICAL),
NWidget(WWT_TEXT, COLOUR_LIGHT_BLUE, WID_NG_FILTER_LABEL), SetDataTip(STR_LIST_FILTER_TITLE, STR_NULL),
NWidget(WWT_EDITBOX, COLOUR_LIGHT_BLUE, WID_NG_FILTER), SetMinimalSize(251, 12), SetFill(1, 0), SetResize(1, 0),
SetDataTip(STR_LIST_FILTER_OSKTITLE, STR_LIST_FILTER_TOOLTIP),
NWidget(NWID_HORIZONTAL),
NWidgetFunction(MakeResizableHeader),
NWidget(WWT_MATRIX, COLOUR_LIGHT_BLUE, WID_NG_MATRIX), SetResize(1, 1), SetFill(1, 0),
SetDataTip(0, STR_NETWORK_SERVER_LIST_CLICK_GAME_TO_SELECT), SetScrollbar(WID_NG_SCROLLBAR),
NWidget(NWID_VSCROLLBAR, COLOUR_LIGHT_BLUE, WID_NG_SCROLLBAR),
NWidget(NWID_SPACER), SetMinimalSize(0, 7), SetResize(1, 0), SetFill(1, 1),
NWidget(WWT_TEXT, COLOUR_LIGHT_BLUE, WID_NG_LASTJOINED_LABEL), SetFill(1, 0),
SetDataTip(STR_NETWORK_SERVER_LIST_LAST_JOINED_SERVER, STR_NULL), SetResize(1, 0),
NWidget(WWT_PANEL, COLOUR_LIGHT_BLUE, WID_NG_LASTJOINED), SetFill(1, 0), SetResize(1, 0),
SetDataTip(0x0, STR_NETWORK_SERVER_LIST_CLICK_TO_SELECT_LAST),
NWidget(WWT_EMPTY, INVALID_COLOUR, WID_NG_LASTJOINED_SPACER), SetFill(0, 0),
/* RIGHT SIDE */
NWidget(WWT_EDITBOX, COLOUR_LIGHT_BLUE, WID_NG_CLIENT), SetMinimalSize(151, 12), SetFill(1, 0), SetResize(1, 0),
NWidget(WWT_PANEL, COLOUR_LIGHT_BLUE, WID_NG_DETAILS),
NWidget(NWID_VERTICAL, NC_EQUALSIZE), SetPIP(5, 5, 5),
NWidget(WWT_EMPTY, INVALID_COLOUR, WID_NG_DETAILS_SPACER), SetMinimalSize(140, 155), SetResize(0, 1), SetFill(1, 1), // Make sure it's at least this wide
NWidget(NWID_HORIZONTAL, NC_NONE), SetPIP(5, 5, 5),
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_NG_NEWGRF_MISSING_SEL),
NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NG_NEWGRF_MISSING), SetFill(1, 0), SetDataTip(STR_NEWGRF_SETTINGS_FIND_MISSING_CONTENT_BUTTON, STR_NEWGRF_SETTINGS_FIND_MISSING_CONTENT_TOOLTIP),
@@ -941,12 +995,13 @@ static const NWidgetPart _nested_network
NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NG_JOIN), SetFill(1, 0), SetDataTip(STR_NETWORK_SERVER_LIST_JOIN_GAME, STR_NULL),
NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NG_REFRESH), SetFill(1, 0), SetDataTip(STR_NETWORK_SERVER_LIST_REFRESH, STR_NETWORK_SERVER_LIST_REFRESH_TOOLTIP),
/* BOTTOM */
NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), SetPIP(10, 7, 4),
NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NG_FIND), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_NETWORK_SERVER_LIST_FIND_SERVER, STR_NETWORK_SERVER_LIST_FIND_SERVER_TOOLTIP),
NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NG_ADD), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_NETWORK_SERVER_LIST_ADD_SERVER, STR_NETWORK_SERVER_LIST_ADD_SERVER_TOOLTIP),
@@ -659,12 +659,14 @@ void SQGSWindow_Register(Squirrel *engin
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_NCL_SEL_ALL_UPDATE, "WID_NCL_SEL_ALL_UPDATE");
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_NG_MAIN, "WID_NG_MAIN");
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_NG_CONNECTION, "WID_NG_CONNECTION");
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_NG_CONN_BTN, "WID_NG_CONN_BTN");
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_NG_CLIENT_LABEL, "WID_NG_CLIENT_LABEL");
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_NG_CLIENT, "WID_NG_CLIENT");
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_NG_FILTER_LABEL, "WID_NG_FILTER_LABEL");
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_NG_FILTER, "WID_NG_FILTER");
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_NG_HEADER, "WID_NG_HEADER");
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_NG_NAME, "WID_NG_NAME");
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_NG_CLIENTS, "WID_NG_CLIENTS");
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_NG_MAPSIZE, "WID_NG_MAPSIZE");
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_NG_DATE, "WID_NG_DATE");
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_NG_YEARS, "WID_NG_YEARS");
@@ -1623,12 +1623,14 @@ public:
WID_NG_MAIN = ::WID_NG_MAIN, ///< Main panel.
WID_NG_CONNECTION = ::WID_NG_CONNECTION, ///< Label in front of connection droplist.
WID_NG_CONN_BTN = ::WID_NG_CONN_BTN, ///< 'Connection' droplist button.
WID_NG_CLIENT_LABEL = ::WID_NG_CLIENT_LABEL, ///< Label in front of client name edit box.
WID_NG_CLIENT = ::WID_NG_CLIENT, ///< Panel with editbox to set client name.
WID_NG_FILTER_LABEL = ::WID_NG_FILTER_LABEL, ///< Label in front of the filter/search edit box.
WID_NG_FILTER = ::WID_NG_FILTER, ///< Panel with the edit box to enter the search text.
WID_NG_HEADER = ::WID_NG_HEADER, ///< Header container of the matrix.
WID_NG_NAME = ::WID_NG_NAME, ///< 'Name' button.
WID_NG_CLIENTS = ::WID_NG_CLIENTS, ///< 'Clients' button.
WID_NG_MAPSIZE = ::WID_NG_MAPSIZE, ///< 'Map size' button.
WID_NG_DATE = ::WID_NG_DATE, ///< 'Date' button.
@@ -17,12 +17,14 @@ enum NetworkGameWidgets {
WID_NG_MAIN, ///< Main panel.
WID_NG_CONNECTION, ///< Label in front of connection droplist.
WID_NG_CONN_BTN, ///< 'Connection' droplist button.
WID_NG_CLIENT_LABEL, ///< Label in front of client name edit box.
WID_NG_CLIENT, ///< Panel with editbox to set client name.
WID_NG_FILTER_LABEL, ///< Label in front of the filter/search edit box.
WID_NG_FILTER, ///< Panel with the edit box to enter the search text.
WID_NG_HEADER, ///< Header container of the matrix.
WID_NG_NAME, ///< 'Name' button.
WID_NG_CLIENTS, ///< 'Clients' button.
WID_NG_MAPSIZE, ///< 'Map size' button.
WID_NG_DATE, ///< 'Date' button.
Status change: