diff --git a/src/commands_gui.cpp b/src/commands_gui.cpp new file mode 100644 index 0000000000..aa4203c871 --- /dev/null +++ b/src/commands_gui.cpp @@ -0,0 +1,826 @@ +/* $Id: commands_gui.cpp 21909 2011-01-26 08:14:36Z xi $ */ + +#ifdef ENABLE_NETWORK +#include "stdafx.h" +#include "widgets/dropdown_type.h" //fillrect +#include "core/geometry_func.hpp" //maxdim +#include "settings_type.h" +#include "settings_func.h" //saveconfig +#include "3rdparty/md5/md5.h" //pass crypt +#include "network/network_func.h" //network chat +#include "textbuf_gui.h" //show query +#include "network/network.h" //networking +#include "network/core/tcp_http.h" //http connector +#include "ini_type.h" //ini file +#include "fileio_func.h" //personal dir +#include "error.h" //error message +#include "debug.h" + +bool _novahost = false; +IniFile *_inilogin = NULL; + +static const int HTTPBUFLEN = 128; + +static const char * const NOVAPOLIS_IPV4_PRIMARY = "37.157.196.78"; +static const char * const NOVAPOLIS_IPV6_PRIMARY = "2a02:2b88:2:1::1d73:1"; +static const char * const NOVAPOLIS_IPV4_SECONDARY = "89.111.65.225"; +static const char * const NOVAPOLIS_IPV6_SECONDARY = "fe80::20e:7fff:fe23:bee0"; +static const char * const NOVAPOLIS_STRING = "novapolis"; +static const char * const NICE_HTTP_LOGIN = "http://n-ice.org/openttd/gettoken.php?user=%s&password=%s"; +static const char * const BTPRO_HTTP_LOGIN = "http://openttd.btpro.nl/gettoken.php?user=%s&password=%s"; + +static const char * const NOVA_IP_ADDRESSES[] = { + NOVAPOLIS_IPV4_PRIMARY, + NOVAPOLIS_IPV6_PRIMARY, + NOVAPOLIS_IPV4_SECONDARY, + NOVAPOLIS_IPV6_SECONDARY, +}; + +static const char * const CFG_LOGIN_FILE = "novapolis.cfg"; +static const char * const CFG_LOGIN_KEY = "login"; +static const char * const NOVAPOLIS_LOGIN = "novapolis_login"; +static const char * const NOVAPOLIS_PW = "novapolis_pw"; +static const char * const NICE_LOGIN = "nice_login"; +static const char * const NICE_PW = "nice_pw"; +static const char * const BTPRO_LOGIN = "btpro_login"; +static const char * const BTPRO_PW = "btpro_pw"; + +static const char * const INI_LOGIN_KEYS[] = { + NOVAPOLIS_LOGIN, + NOVAPOLIS_PW, + NICE_LOGIN, + NICE_PW, + BTPRO_LOGIN, + BTPRO_PW, +}; + +/** Widget number of the commands window. */ +enum CommandsToolbarWidgets { + CTW_BACKGROUND, + CTW_GOAL, + CTW_SCORE, + CTW_TOWN, + CTW_QUEST, + CTW_TOWN_ID, + CTW_HINT, + CTW_LOGIN, + CTW_NAME, + CTW_TIMELEFT, + CTW_INFO, + CTW_RESETME, + CTW_SAVEME, + CTW_HELP, + CTW_RULES, + CTW_CBHINT, + CTW_CLIENTS, + CTW_BEST, + CTW_ME, + CTW_RANK, + CTW_TOPICS, + CTW_TOPIC1, + CTW_TOPIC2, + CTW_TOPIC3, + CTW_TOPIC4, + CTW_TOPIC5, + CTW_TOPIC6, + CTW_CHOOSE_CARGO, + CTW_CHOOSE_CARGO_AMOUNT, + CTW_CHOOSE_CARGO_INCOME, + CTW_NS0, + CTW_NS1, + CTW_NS2, + CTW_NS3, + CTW_NS4, + CTW_NS5, + CTW_NS6, + CTW_NS7, + CTW_NS8, + CTW_NS9, + CTW_NS10, + CTW_NSX1, + CTW_NSX2, + CTW_NSX3, + CTW_NSX4, + CTW_NSX5, + CTW_NSX6, + CTW_NSX7, + CTW_NSEND, + CTW_CARGO_FIRST, +}; + +enum CommandsToolbarQuery { + CTQ_TOWN_ID = 0, + CTQ_LOGIN_NAME, + CTQ_LOGIN_PASSWORD, + CTQ_NAME_NEWNAME, + CTQ_LOGIN_CREDENTIALS_PW, +}; + +enum CommandsToolbarCargoOption { + CTW_OPTION_CARGO = 0, + CTW_OPTION_CARGO_AMOUNT, + CTW_OPTION_CARGO_INCOME, +}; + +enum LoginWindowWidgets { + LWW_NOVAPOLIS, + LWW_NICE, + LWW_BTPRO, + LWW_NOVAPOLIS_LOGIN, + LWW_NOVAPOLIS_PW, + LWW_NICE_LOGIN, + LWW_NICE_PW, + LWW_BTPRO_LOGIN, + LWW_BTPRO_PW, + LWW_USERNAME, + LWW_PASSWORD, +}; + +enum LoginWindowQueryWidgets { + LQW_NOVAPOLIS, + LQW_NICE, + LQW_BTPRO, + LQW_NOVAPOLIS_LOGIN, + LQW_NOVAPOLIS_PW, + LQW_NICE_LOGIN, + LQW_NICE_PW, + LQW_BTPRO_LOGIN, + LQW_BTPRO_PW, +}; + +enum CommunityName { + NOVAPOLIS, + NICE, + BTPRO, +}; + +enum IniLoginKeys { + NOVAPOLISUSER, + NOVAPOLISPW, + NICEUSER, + NICEPW, + BTPROUSER, + BTPROPW, +}; + +char _inilogindata[6][64]; + +void ShowLoginWindow(); +void AccountLogin(CommunityName community); +void IniReloadLogin(); +char * GetLoginItem(const char * item); + +bool novahost(){ + _novahost = false; + for(int i = 0, len = lengthof(NOVA_IP_ADDRESSES); i < len; i++){ + if(strcmp(_settings_client.network.last_host, NOVA_IP_ADDRESSES[i]) == 0){ + _novahost = true; + break; + } + } + if(_novahost == false && strstr(_settings_client.network.last_host, NOVAPOLIS_STRING) != NULL){ + _novahost = true; + } + return _novahost; +} + +void strtomd5(char * buf, char * bufend, int length){ + uint8 digest[16]; + Md5 checksum; + checksum.Append(buf, length); + checksum.Finish(digest); + md5sumToString(buf, bufend, digest); + strtolower(buf); +} + +void UrlEncode(char * buf, const char * buflast, const char * url){ + while(*url != '\0' && buf < buflast){ + if((*url >= '0' && *url <= '9') || (*url >= 'A' && *url <= 'Z') || (*url >= 'a' && *url <= 'z') + || *url == '-' || *url == '_' || *url == '.' || *url == '~' + ){ + *buf++ = *url++; + } + else{ + buf += seprintf(buf, buflast, "%%%02X", *url++); + } + } + *buf = '\0'; +} + +/** Commands toolbar window handler. */ +struct CommandsToolbarWindow : Window { + + CommandsToolbarQuery query_widget; + CommandsToolbarCargoOption cargo_option; + + CommandsToolbarWindow(WindowDesc *desc, WindowNumber window_number) : Window(desc) + { + this->InitNested(window_number); + this->cargo_option = CTW_OPTION_CARGO_AMOUNT; + this->LowerWidget(CTW_CHOOSE_CARGO_AMOUNT); + } + + virtual void SetStringParameters(int widget) const + { + if(widget >= CTW_NS0 && widget < CTW_NSEND){ + SetDParam(0, widget - CTW_NS0); + } + } + + virtual void OnClick(Point pt, int widget, int click_count) + { + if (!_networking) return; + char msg[64]; + switch (widget) { + case CTW_GOAL: + NetworkClientSendChatToServer("!goal"); + break; + case CTW_SCORE: + NetworkClientSendChatToServer("!score"); + break; + case CTW_TOWN: + NetworkClientSendChatToServer("!town"); + break; + case CTW_QUEST: + NetworkClientSendChatToServer("!quest"); + break; + case CTW_TIMELEFT: + NetworkClientSendChatToServer("!timeleft"); + break; + case CTW_INFO: + NetworkClientSendChatToServer("!info"); + break; + case CTW_HINT: + NetworkClientSendChatToServer("!hint"); + break; + case CTW_RESETME: + NetworkClientSendChatToServer("!resetme"); + break; + case CTW_SAVEME: + NetworkClientSendChatToServer("!saveme"); + break; + case CTW_HELP: + NetworkClientSendChatToServer("!help"); + break; + case CTW_RULES: + NetworkClientSendChatToServer("!rules"); + break; + case CTW_CBHINT: + NetworkClientSendChatToServer("!hint"); + break; + case CTW_CLIENTS: + NetworkClientSendChatToServer("!clients"); + break; + case CTW_BEST: + NetworkClientSendChatToServer("!best"); + break; + case CTW_ME: + NetworkClientSendChatToServer("!me"); + break; + case CTW_RANK: + NetworkClientSendChatToServer("!rank"); + break; + case CTW_TOPICS: + NetworkClientSendChatToServer("!topic"); + break; + case CTW_TOWN_ID: + this->query_widget = CTQ_TOWN_ID; + ShowQueryString(STR_EMPTY, STR_TOOLBAR_COMMANDS_TOWN_QUERY, 8, this, CS_NUMERAL, QSF_NONE); + break; + case CTW_LOGIN: + this->query_widget = CTQ_LOGIN_NAME; + ShowQueryString(STR_EMPTY, STR_TOOLBAR_COMMANDS_LOGIN_NAME_QUERY, 24, this, CS_ALPHANUMERAL, QSF_NONE); + break; + case CTW_NAME: + this->query_widget = CTQ_NAME_NEWNAME; + ShowQueryString(STR_EMPTY, STR_TOOLBAR_COMMANDS_NAME_NEWNAME_QUERY, 25, this, CS_ALPHANUMERAL, QSF_NONE); + break; + case CTW_TOPIC1: + case CTW_TOPIC2: + case CTW_TOPIC3: + case CTW_TOPIC4: + case CTW_TOPIC5: + case CTW_TOPIC6: + seprintf(msg, lastof(msg), "!topic %i", widget - CTW_TOPIC1 + 1); + NetworkClientSendChatToServer(msg); + break; + case CTW_CHOOSE_CARGO: + case CTW_CHOOSE_CARGO_AMOUNT: + case CTW_CHOOSE_CARGO_INCOME: + this->cargo_option = (CommandsToolbarCargoOption)(widget - CTW_CHOOSE_CARGO); + this->RaiseWidget(CTW_CHOOSE_CARGO); + this->RaiseWidget(CTW_CHOOSE_CARGO_AMOUNT); + this->RaiseWidget(CTW_CHOOSE_CARGO_INCOME); + this->LowerWidget(widget); + this->SetWidgetDirty(CTW_CHOOSE_CARGO); + this->SetWidgetDirty(CTW_CHOOSE_CARGO_AMOUNT); + this->SetWidgetDirty(CTW_CHOOSE_CARGO_INCOME); + break; + default: + if(widget >= CTW_NS0 && widget < CTW_NSEND){ + char ip[16]; + if(widget < CTW_NSX4) strecpy(ip, NOVAPOLIS_IPV4_PRIMARY, lastof(ip)); + else strecpy(ip, NOVAPOLIS_IPV4_SECONDARY, lastof(ip)); + + NetworkClientConnectGame(NetworkAddress(ip, (3980 + widget - CTW_NS0)), COMPANY_SPECTATOR); + } + else if (widget >= CTW_CARGO_FIRST) { + int i = widget - CTW_CARGO_FIRST; + char name[128]; + GetString(name, _sorted_cargo_specs[i]->name, lastof(name)); + switch(this->cargo_option){ + case CTW_OPTION_CARGO: seprintf(msg, lastof(msg), "!%s", name); break; + case CTW_OPTION_CARGO_AMOUNT: seprintf(msg, lastof(msg), "!A%s", name); break; + case CTW_OPTION_CARGO_INCOME: seprintf(msg, lastof(msg), "!I%s", name); break; + } + NetworkClientSendChatToServer(msg); + this->ToggleWidgetLoweredState(widget); + this->SetWidgetDirty(widget); + } + break; + } + } + + void OnQueryTextFinished(char * str) + { + if (!_networking || str == NULL) return; + char msg[128]; + switch (this->query_widget) { + case CTQ_TOWN_ID: + seprintf(msg, lastof(msg), "!town %s", str); + NetworkClientSendChatToServer(msg); + break; + case CTQ_LOGIN_NAME: + seprintf(msg, lastof(msg), "!login %s", str); + NetworkClientSendChatToServer(msg); + break; + case CTQ_NAME_NEWNAME: + seprintf(msg, lastof(msg), "!name %s", str); + NetworkClientSendChatToServer(msg); + break; + } + } + + void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) + { + if (widget < CTW_CARGO_FIRST) return; + + const CargoSpec *cs = _sorted_cargo_specs[widget - CTW_CARGO_FIRST]; + SetDParam(0, cs->name); + Dimension d = GetStringBoundingBox(STR_GRAPH_CARGO_PAYMENT_CARGO); + d.width += 14; // colour field + d.width += WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT; + d.height += WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM; + *size = maxdim(d, *size); + } + + void DrawWidget(const Rect &r, int widget) const + { + if (widget < CTW_CARGO_FIRST) return; + + const CargoSpec *cs = _sorted_cargo_specs[widget - CTW_CARGO_FIRST]; + bool rtl = _current_text_dir == TD_RTL; + + /* Since the buttons have no text, no images, + * both the text and the coloured box have to be manually painted. + * clk_dif will move one pixel down and one pixel to the right + * when the button is clicked */ + byte clk_dif = this->IsWidgetLowered(widget) ? 1 : 0; + int x = r.left + WD_FRAMERECT_LEFT; + int y = r.top; + + int rect_x = clk_dif + (rtl ? r.right - 12 : r.left + WD_FRAMERECT_LEFT); + + GfxFillRect(rect_x, y + clk_dif, rect_x + 8, y + 5 + clk_dif, 0); + GfxFillRect(rect_x + 1, y + 1 + clk_dif, rect_x + 7, y + 4 + clk_dif, cs->legend_colour); + SetDParam(0, cs->name); + DrawString(rtl ? r.left : x + 14 + clk_dif, (rtl ? r.right - 14 + clk_dif : r.right), y + clk_dif, STR_GRAPH_CARGO_PAYMENT_CARGO); + } + + void OnHundredthTick() + { + for (int i = 0; i < _sorted_standard_cargo_specs_size; i++) { + if (this->IsWidgetLowered(i + CTW_CARGO_FIRST)) { + static int x = 0; + x++; + if (x >= 2) { + this->ToggleWidgetLoweredState(i + CTW_CARGO_FIRST); + this->SetWidgetDirty(i + CTW_CARGO_FIRST); + x = 0; + } + } + } + } + +}; + +/** Construct the row containing the digit keys. */ +static NWidgetBase *MakeCargoButtons(int *biggest_index) +{ + NWidgetVertical *ver = new NWidgetVertical; + + for (int i = 0; i < _sorted_standard_cargo_specs_size; i++) { + NWidgetBackground *leaf = new NWidgetBackground(WWT_PANEL, COLOUR_ORANGE, CTW_CARGO_FIRST + i, NULL); + leaf->tool_tip = STR_GRAPH_CARGO_PAYMENT_TOGGLE_CARGO; + leaf->SetFill(1, 0); + ver->Add(leaf); + } + *biggest_index = CTW_CARGO_FIRST + _sorted_standard_cargo_specs_size - 1; + return ver; +} + +static const NWidgetPart _nested_commands_toolbar_widgets[] = { + NWidget(NWID_HORIZONTAL), + NWidget(WWT_CLOSEBOX, COLOUR_GREY), + NWidget(WWT_CAPTION, COLOUR_GREY), SetDataTip(STR_TOOLBAR_COMMANDS_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), + NWidget(WWT_STICKYBOX, COLOUR_GREY), + EndContainer(), + NWidget(NWID_HORIZONTAL), + NWidget(NWID_VERTICAL), + NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, CTW_GOAL), SetMinimalSize(40, 20), SetFill(1, 0), SetDataTip(STR_TOOLBAR_COMMANDS_GOAL_CAPTION, STR_TOOLBAR_COMMANDS_GOAL_TOOLTIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, CTW_QUEST), SetMinimalSize(40, 20), SetFill(1, 0), SetDataTip(STR_TOOLBAR_COMMANDS_QUEST_CAPTION, STR_TOOLBAR_COMMANDS_QUEST_TOOLTIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, CTW_SCORE), SetMinimalSize(40, 20), SetFill(1, 0), SetDataTip(STR_TOOLBAR_COMMANDS_SCORE_CAPTION, STR_TOOLBAR_COMMANDS_SCORE_TOOLTIP), + EndContainer(), + NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, CTW_TOWN), SetMinimalSize(40, 20), SetFill(1, 0), SetDataTip(STR_TOOLBAR_COMMANDS_TOWN_CAPTION, STR_TOOLBAR_COMMANDS_TOWN_TOOLTIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, CTW_TOWN_ID), SetMinimalSize(40, 20), SetFill(1, 0), SetDataTip(STR_TOOLBAR_COMMANDS_TOWN_ID_CAPTION, STR_TOOLBAR_COMMANDS_TOWN_ID_TOOLTIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, CTW_HINT), SetMinimalSize(40, 20), SetFill(1, 0), SetDataTip(STR_TOOLBAR_COMMANDS_HINT_CAPTION, STR_TOOLBAR_COMMANDS_HINT_TOOLTIP), + EndContainer(), + NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, CTW_LOGIN), SetMinimalSize(40, 20), SetFill(1, 0), SetDataTip(STR_TOOLBAR_COMMANDS_LOGIN_CAPTION, STR_TOOLBAR_COMMANDS_LOGIN_TOOLTIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, CTW_NAME), SetMinimalSize(40, 20), SetFill(1, 0), SetDataTip(STR_TOOLBAR_COMMANDS_NAME_CAPTION, STR_TOOLBAR_COMMANDS_NAME_TOOLTIP), + EndContainer(), + NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, CTW_TIMELEFT), SetMinimalSize(40, 20), SetFill(1, 0), SetDataTip(STR_TOOLBAR_COMMANDS_TIMELEFT_CAPTION, STR_TOOLBAR_COMMANDS_TIMELEFT_TOOLTIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, CTW_INFO), SetMinimalSize(40, 20), SetFill(1, 0), SetDataTip(STR_TOOLBAR_COMMANDS_INFO_CAPTION, STR_TOOLBAR_COMMANDS_INFO_TOOLTIP), + EndContainer(), + NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), + NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, CTW_NS1), SetMinimalSize(40, 20), SetFill(1, 0), SetDataTip(STR_TOOLBAR_COMMANDS_NS_CAPTION, STR_TOOLBAR_COMMANDS_NS_TOOLTIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, CTW_NS2), SetMinimalSize(40, 20), SetFill(1, 0), SetDataTip(STR_TOOLBAR_COMMANDS_NS_CAPTION, STR_TOOLBAR_COMMANDS_NS_TOOLTIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, CTW_NS3), SetMinimalSize(40, 20), SetFill(1, 0), SetDataTip(STR_TOOLBAR_COMMANDS_NS_CAPTION, STR_TOOLBAR_COMMANDS_NS_TOOLTIP), + EndContainer(), + NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), + NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, CTW_NS4), SetMinimalSize(40, 20), SetFill(1, 0), SetDataTip(STR_TOOLBAR_COMMANDS_NS_CAPTION, STR_TOOLBAR_COMMANDS_NS_TOOLTIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, CTW_NS5), SetMinimalSize(40, 20), SetFill(1, 0), SetDataTip(STR_TOOLBAR_COMMANDS_NS_CAPTION, STR_TOOLBAR_COMMANDS_NS_TOOLTIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, CTW_NS6), SetMinimalSize(40, 20), SetFill(1, 0), SetDataTip(STR_TOOLBAR_COMMANDS_NS_CAPTION, STR_TOOLBAR_COMMANDS_NS_TOOLTIP), + EndContainer(), + NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), + NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, CTW_NS7), SetMinimalSize(40, 20), SetFill(1, 0), SetDataTip(STR_TOOLBAR_COMMANDS_NS_CAPTION, STR_TOOLBAR_COMMANDS_NS_TOOLTIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, CTW_NS9), SetMinimalSize(40, 20), SetFill(1, 0), SetDataTip(STR_TOOLBAR_COMMANDS_NS_CAPTION, STR_TOOLBAR_COMMANDS_NS_TOOLTIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, CTW_NSX1), SetMinimalSize(40, 20), SetFill(1, 0), SetDataTip(STR_TOOLBAR_COMMANDS_NS_CAPTION, STR_TOOLBAR_COMMANDS_NS_TOOLTIP), + EndContainer(), + NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), + NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, CTW_NSX2), SetMinimalSize(40, 20), SetFill(1, 0), SetDataTip(STR_TOOLBAR_COMMANDS_NS_CAPTION, STR_TOOLBAR_COMMANDS_NS_TOOLTIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, CTW_NSX3), SetMinimalSize(40, 20), SetFill(1, 0), SetDataTip(STR_TOOLBAR_COMMANDS_NS_CAPTION, STR_TOOLBAR_COMMANDS_NS_TOOLTIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, CTW_NSX4), SetMinimalSize(40, 20), SetFill(1, 0), SetDataTip(STR_TOOLBAR_COMMANDS_NS_CAPTION, STR_TOOLBAR_COMMANDS_NS_TOOLTIP), + EndContainer(), + NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), + NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, CTW_NSX5), SetMinimalSize(40, 20), SetFill(1, 0), SetDataTip(STR_TOOLBAR_COMMANDS_NS_CAPTION, STR_TOOLBAR_COMMANDS_NS_TOOLTIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, CTW_NSX6), SetMinimalSize(40, 20), SetFill(1, 0), SetDataTip(STR_TOOLBAR_COMMANDS_NS_CAPTION, STR_TOOLBAR_COMMANDS_NS_TOOLTIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, CTW_NSX7), SetMinimalSize(40, 20), SetFill(1, 0), SetDataTip(STR_TOOLBAR_COMMANDS_NS_CAPTION, STR_TOOLBAR_COMMANDS_NS_TOOLTIP), + EndContainer(), + NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, CTW_RESETME), SetMinimalSize(40, 20), SetFill(1, 0), SetDataTip(STR_TOOLBAR_COMMANDS_RESETME_CAPTION, STR_TOOLBAR_COMMANDS_RESETME_TOOLTIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, CTW_SAVEME), SetMinimalSize(40, 20), SetFill(1, 0), SetDataTip(STR_TOOLBAR_COMMANDS_SAVEME_CAPTION, STR_TOOLBAR_COMMANDS_SAVEME_TOOLTIP), + EndContainer(), + //more + NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, CTW_TOPICS), SetMinimalSize(40, 20), SetFill(1, 0), SetDataTip(STR_TOOLBAR_COMMANDS_TOPICS_CAPTION, STR_TOOLBAR_COMMANDS_TOPICS_TOOLTIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, CTW_HELP), SetMinimalSize(40, 20), SetFill(1, 0), SetDataTip(STR_TOOLBAR_COMMANDS_HELP_CAPTION, STR_TOOLBAR_COMMANDS_HELP_TOOLTIP), + EndContainer(), + NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, CTW_TOPIC1), SetMinimalSize(40, 20), SetFill(1, 0), SetDataTip(STR_TOOLBAR_COMMANDS_TOPIC1_CAPTION, STR_TOOLBAR_COMMANDS_TOPIC1_TOOLTIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, CTW_RULES), SetMinimalSize(40, 20), SetFill(1, 0), SetDataTip(STR_TOOLBAR_COMMANDS_RULES_CAPTION, STR_TOOLBAR_COMMANDS_RULES_TOOLTIP), + EndContainer(), + NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, CTW_TOPIC2), SetMinimalSize(40, 20), SetFill(1, 0), SetDataTip(STR_TOOLBAR_COMMANDS_TOPIC2_CAPTION, STR_TOOLBAR_COMMANDS_TOPIC2_TOOLTIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, CTW_CBHINT), SetMinimalSize(40, 20), SetFill(1, 0), SetDataTip(STR_TOOLBAR_COMMANDS_CBHINT_CAPTION, STR_TOOLBAR_COMMANDS_CBHINT_TOOLTIP), + EndContainer(), + NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, CTW_TOPIC3), SetMinimalSize(40, 20), SetFill(1, 0), SetDataTip(STR_TOOLBAR_COMMANDS_TOPIC3_CAPTION, STR_TOOLBAR_COMMANDS_TOPIC3_TOOLTIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, CTW_BEST), SetMinimalSize(40, 20), SetFill(1, 0), SetDataTip(STR_TOOLBAR_COMMANDS_BEST_CAPTION, STR_TOOLBAR_COMMANDS_BEST_TOOLTIP), + EndContainer(), + NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, CTW_TOPIC4), SetMinimalSize(40, 20), SetFill(1, 0), SetDataTip(STR_TOOLBAR_COMMANDS_TOPIC4_CAPTION, STR_TOOLBAR_COMMANDS_TOPIC4_TOOLTIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, CTW_RANK), SetMinimalSize(40, 20), SetFill(1, 0), SetDataTip(STR_TOOLBAR_COMMANDS_RANK_CAPTION, STR_TOOLBAR_COMMANDS_RANK_TOOLTIP), + EndContainer(), + NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, CTW_TOPIC5), SetMinimalSize(40, 20), SetFill(1, 0), SetDataTip(STR_TOOLBAR_COMMANDS_TOPIC5_CAPTION, STR_TOOLBAR_COMMANDS_TOPIC5_TOOLTIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, CTW_ME), SetMinimalSize(40, 20), SetFill(1, 0), SetDataTip(STR_TOOLBAR_COMMANDS_ME_CAPTION, STR_TOOLBAR_COMMANDS_ME_TOOLTIP), + EndContainer(), + NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, CTW_TOPIC6), SetMinimalSize(40, 20), SetFill(1, 0), SetDataTip(STR_TOOLBAR_COMMANDS_TOPIC6_CAPTION, STR_TOOLBAR_COMMANDS_TOPIC6_TOOLTIP), + NWidget(WWT_PANEL, COLOUR_GREY), SetResize(0, 1), EndContainer(), + EndContainer(), + NWidget(WWT_PANEL, COLOUR_GREY), SetResize(0, 1), EndContainer(), + EndContainer(), + NWidget(NWID_VERTICAL), + NWidget(WWT_TEXTBTN, COLOUR_GREY, CTW_CHOOSE_CARGO_AMOUNT), SetMinimalSize(40, 20), SetFill(1, 0), SetDataTip(STR_TOOLBAR_COMMANDS_OPTION_CARGO_A_CAPTION, STR_TOOLBAR_COMMANDS_OPTION_CARGO_A_TOOLTIP), + NWidget(WWT_TEXTBTN, COLOUR_GREY, CTW_CHOOSE_CARGO_INCOME), SetMinimalSize(40, 20), SetFill(1, 0), SetDataTip(STR_TOOLBAR_COMMANDS_OPTION_CARGO_I_CAPTION, STR_TOOLBAR_COMMANDS_OPTION_CARGO_I_TOOLTIP), + NWidget(WWT_TEXTBTN, COLOUR_GREY, CTW_CHOOSE_CARGO), SetMinimalSize(40, 20), SetFill(1, 0), SetDataTip(STR_TOOLBAR_COMMANDS_OPTION_CARGO_CAPTION, STR_TOOLBAR_COMMANDS_OPTION_CARGO_TOOLTIP), + NWidgetFunction(MakeCargoButtons), + NWidget(WWT_PANEL, COLOUR_GREY), SetResize(0, 1), EndContainer(), + EndContainer(), + EndContainer(), +}; + +static WindowDesc _commands_toolbar_desc( + WDP_ALIGN_TOOLBAR, NULL, 0, 0, + WC_COMMAND_TOOLBAR, WC_NONE, + WDF_CONSTRUCTION, + _nested_commands_toolbar_widgets, lengthof(_nested_commands_toolbar_widgets) +); + +void ShowCommandsToolbar() +{ + DeleteWindowByClass(WC_COMMAND_TOOLBAR); + AllocateWindowDescFront(&_commands_toolbar_desc, 0); +} + +// login window +class GetHTTPContent: public HTTPCallback { +public: + GetHTTPContent(char * uri): uri(uri) { + this->proccessing = false; + } + bool proccessing; + + void InitiateLoginSequence() { + if(this->proccessing) return; + this->proccessing = true; + this->cursor = this->buf; + NetworkHTTPSocketHandler::Connect(this->uri, this); + } + + virtual void OnReceiveData(const char * data, size_t length) { + size_t i = length; + if (data == NULL) { + this->cursor = NULL; + this->LoginAlready(); + } + else { + while(i) { + *this->cursor = *data; + data++; + this->cursor++; + i--; + } + if (this->cursor - this->buf >= HTTPBUFLEN) this->buf[HTTPBUFLEN] = NULL; + else *this->cursor = NULL; + } + } + + virtual void OnFailure() { + ShowErrorMessage(STR_LOGINERROR_NOCONNECT, INVALID_STRING_ID, WL_ERROR); + } + + void LoginAlready(){ + if(strlen(this->buf) == 4 && _networking){ + char msg[32]; + seprintf(msg, lastof(msg), "!login %s", this->buf); + NetworkClientSendChatToServer(msg); + } + else{ + ShowErrorMessage(STR_LOGINERROR_BADINPUT, INVALID_STRING_ID, WL_ERROR); + } + this->proccessing = false; + } + + virtual ~GetHTTPContent() { + free(this->cursor); + free(this->buf); + } +private: + NetworkHTTPContentConnecter *conn; + char buf[HTTPBUFLEN]; + char * cursor; + char * uri; +}; +//send login +void AccountLogin(CommunityName community){ + char uri[128]; + switch(community){ + case NOVAPOLIS: + seprintf(uri, lastof(uri), "!login %s %s", _inilogindata[NOVAPOLISUSER], _inilogindata[NOVAPOLISPW]); + NetworkClientSendChatToServer(uri); + return; + case NICE: + seprintf(uri, lastof(uri), NICE_HTTP_LOGIN, GetLoginItem(NICE_LOGIN), GetLoginItem(NICE_PW)); + break; + case BTPRO: + seprintf(uri, lastof(uri), BTPRO_HTTP_LOGIN, GetLoginItem(BTPRO_LOGIN), GetLoginItem(BTPRO_PW)); + break; + default: + return; + } + static GetHTTPContent login(uri); + login.InitiateLoginSequence(); +} +#endif /* ENABLE_NETWORK */ + +//ini login hadling +void IniLoginInitiate(){ + static const char * const list_login[] = { + CFG_LOGIN_KEY, + NULL + }; + + if(_inilogin != NULL) return; //it was already set + _inilogin = new IniFile(list_login); + _inilogin->LoadFromDisk(CFG_LOGIN_FILE, BASE_DIR); + IniReloadLogin(); +} + +void IniReloadLogin(){ + char str[64]; + const char * itemvalue; + for(int i = 0, len = lengthof(INI_LOGIN_KEYS); i < len; i++){ + itemvalue = GetLoginItem(INI_LOGIN_KEYS[i]); + if(itemvalue == NULL){ + GetString(str, STR_LOGIN_NOTSET, lastof(str)); + } + else{ + strecpy(str, itemvalue, lastof(str)); + } + strecpy(_inilogindata[i], str, lastof(_inilogindata[i])); + } +} + +char * GetLoginItem(const char * itemname){ + IniGroup *group = _inilogin->GetGroup(CFG_LOGIN_KEY); + if(group == NULL) return NULL; + IniItem *item = group->GetItem(itemname, true); + if(item == NULL || item->value == NULL) return NULL; + return item->value; +} + +void SetLoginItem(const char * itemname, const char * value){ + IniGroup *group = _inilogin->GetGroup(CFG_LOGIN_KEY); + if(group == NULL) return; + IniItem *item = group->GetItem(itemname, true); + if(item == NULL) return; + item->SetValue(value); + _inilogin->SaveToDisk(str_fmt("%s%s", _personal_dir, CFG_LOGIN_FILE)); + IniReloadLogin(); +} +//login window +struct LoginWindow : Window { + LoginWindowQueryWidgets query_widget; + + LoginWindow(WindowDesc *desc, WindowNumber window_number) : Window(desc) + { + this->InitNested(window_number); + if(_novahost || !_networking){ + this->DisableWidget(LWW_NICE); + this->DisableWidget(LWW_BTPRO); + } + if(!_novahost || !_networking) this->DisableWidget(LWW_NOVAPOLIS); + } + + virtual void SetStringParameters(int widget) const + { + switch(widget){ + case LWW_NOVAPOLIS_LOGIN: + SetDParamStr(0, _inilogindata[NOVAPOLISUSER]); + break; + case LWW_NOVAPOLIS_PW: + SetDParam(0, (GetLoginItem(NOVAPOLIS_PW) == NULL ? STR_LOGIN_NOTSET : STR_LOGIN_SET)); + break; + case LWW_NICE_LOGIN: + SetDParamStr(0, _inilogindata[NICEUSER]); + break; + case LWW_NICE_PW: + SetDParam(0, (GetLoginItem(NICE_PW) == NULL ? STR_LOGIN_NOTSET : STR_LOGIN_SET)); + break; + case LWW_BTPRO_LOGIN: + SetDParamStr(0, _inilogindata[BTPROUSER]); + break; + case LWW_BTPRO_PW: + SetDParam(0, (GetLoginItem(BTPRO_PW) == NULL ? STR_LOGIN_NOTSET : STR_LOGIN_SET)); + break; + } + } + + virtual void OnClick(Point pt, int widget, int click_count) + { + switch (widget) { + case LWW_NOVAPOLIS: + if(_novahost && _networking) AccountLogin(NOVAPOLIS); + break; + case LWW_NICE: + if(_networking) AccountLogin(NICE); + break; + case LWW_BTPRO: + if(_networking) AccountLogin(BTPRO); + break; + case LWW_NOVAPOLIS_LOGIN: + case LWW_NOVAPOLIS_PW: + case LWW_NICE_LOGIN: + case LWW_NICE_PW: + case LWW_BTPRO_LOGIN: + case LWW_BTPRO_PW: + this->query_widget = (LoginWindowQueryWidgets)widget; + ShowQueryString(STR_EMPTY, STR_LOGIN_CHANGE_USERNAME, 32, this, CS_ALPHANUMERAL, QSF_NONE); + break; + } + } + + void OnQueryTextFinished(char * str) + { + if (str == NULL) return; + char item[128]; + switch(this->query_widget){ + case LQW_NOVAPOLIS_LOGIN: { + SetLoginItem(NOVAPOLIS_LOGIN, str); + break; + } + case LQW_NOVAPOLIS_PW: { + char msg[128]; + strecpy(msg, str, lastof(msg)); + strtomd5(msg, lastof(msg), (int)strlen(msg)); + SetLoginItem(NOVAPOLIS_PW, msg); + break; + } + case LQW_NICE_LOGIN: + case LQW_NICE_PW: + case LQW_BTPRO_LOGIN: + case LQW_BTPRO_PW: + UrlEncode(item, lastof(item), str); + SetLoginItem(INI_LOGIN_KEYS[this->query_widget - 3], item); // - LWW_NICE_LOGIN + NICE_LOGIN + break; + default: return; + } + this->SetDirty(); + } +}; + +static const NWidgetPart _nested_login_window_widgets[] = { + NWidget(NWID_HORIZONTAL), + NWidget(WWT_CLOSEBOX, COLOUR_GREY), + NWidget(WWT_CAPTION, COLOUR_GREY), SetDataTip(STR_LOGINWINDOW_CAPTION, 0), + NWidget(WWT_STICKYBOX, COLOUR_GREY), + EndContainer(), + NWidget(WWT_PANEL, COLOUR_GREY), SetResize(1, 0), + NWidget(NWID_VERTICAL, NC_EQUALSIZE), SetPadding(10), + //novapolis + NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), + NWidget(WWT_TEXT, COLOUR_BROWN, LWW_USERNAME), SetDataTip(STR_LOGIN_USERNAME, 0), + NWidget(NWID_SPACER), SetMinimalSize(20, 0), + NWidget(WWT_TEXT, COLOUR_BROWN, LWW_PASSWORD), SetDataTip(STR_LOGIN_PASSWORD, 0), + EndContainer(), + NWidget(NWID_SPACER), SetMinimalSize(0, 5), + NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), + NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, LWW_NOVAPOLIS_LOGIN), SetMinimalSize(60, 20), SetFill(1, 0), SetDataTip(STR_LOGIN_USERNAME_DISPLAY, STR_LOGIN_CHANGE_USERNAME), + NWidget(NWID_SPACER), SetMinimalSize(20, 0), + NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, LWW_NOVAPOLIS_PW), SetMinimalSize(30, 20), SetFill(1, 0), SetDataTip(STR_LOGIN_PASSWORD_DISPLAY, STR_LOGIN_CHANGE_PASSWORD), + EndContainer(), + NWidget(NWID_SPACER), SetMinimalSize(0, 10), + NWidget(WWT_PUSHTXTBTN, COLOUR_PURPLE, LWW_NOVAPOLIS), SetMinimalSize(100, 30), SetFill(1, 0), SetDataTip(STR_LOGINWINDOW_NOVAPOLIS, STR_LOGIN_SEND_LOGIN_TT), + NWidget(NWID_SPACER), SetMinimalSize(0, 10), + //n-ice + NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), + NWidget(WWT_TEXT, COLOUR_BROWN, LWW_USERNAME), SetDataTip(STR_LOGIN_USERNAME, 0), + NWidget(NWID_SPACER), SetMinimalSize(20, 0), + NWidget(WWT_TEXT, COLOUR_BROWN, LWW_PASSWORD), SetDataTip(STR_LOGIN_PASSWORD, 0), + EndContainer(), + NWidget(NWID_SPACER), SetMinimalSize(0, 5), + NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), + NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, LWW_NICE_LOGIN), SetMinimalSize(60, 20), SetFill(1, 0), SetDataTip(STR_LOGIN_USERNAME_DISPLAY, STR_LOGIN_CHANGE_USERNAME), + NWidget(NWID_SPACER), SetMinimalSize(20, 0), + NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, LWW_NICE_PW), SetMinimalSize(30, 20), SetFill(1, 0), SetDataTip(STR_LOGIN_PASSWORD_DISPLAY, STR_LOGIN_CHANGE_PASSWORD), + EndContainer(), + NWidget(NWID_SPACER), SetMinimalSize(0, 10), + NWidget(WWT_PUSHTXTBTN, COLOUR_LIGHT_BLUE, LWW_NICE), SetMinimalSize(100, 30), SetFill(1, 0), SetDataTip(STR_LOGINWINDOW_NICE, STR_LOGIN_SEND_LOGIN_TT), + NWidget(NWID_SPACER), SetMinimalSize(0, 10), + //btpro + NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), + NWidget(WWT_TEXT, COLOUR_BROWN, LWW_USERNAME), SetDataTip(STR_LOGIN_USERNAME, 0), + NWidget(NWID_SPACER), SetMinimalSize(20, 0), + NWidget(WWT_TEXT, COLOUR_BROWN, LWW_PASSWORD), SetDataTip(STR_LOGIN_PASSWORD, 0), + EndContainer(), + NWidget(NWID_SPACER), SetMinimalSize(0, 5), + NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), + NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, LWW_BTPRO_LOGIN), SetMinimalSize(60, 20), SetFill(1, 0), SetDataTip(STR_LOGIN_USERNAME_DISPLAY, STR_LOGIN_CHANGE_USERNAME), + NWidget(NWID_SPACER), SetMinimalSize(20, 0), + NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, LWW_BTPRO_PW), SetMinimalSize(30, 20), SetFill(1, 0), SetDataTip(STR_LOGIN_PASSWORD_DISPLAY, STR_LOGIN_CHANGE_PASSWORD), + EndContainer(), + NWidget(NWID_SPACER), SetMinimalSize(0, 10), + NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, LWW_BTPRO), SetMinimalSize(100, 30), SetFill(1, 0), SetDataTip(STR_LOGINWINDOW_BTPRO, STR_LOGIN_SEND_LOGIN_TT), + EndContainer(), + EndContainer(), +}; + +static WindowDesc _login_window_desc( + WDP_CENTER, NULL, 0, 0, + WC_LOGIN_WINDOW, WC_NONE, + WDF_CONSTRUCTION, + _nested_login_window_widgets, lengthof(_nested_login_window_widgets) +); + +void ShowLoginWindow() +{ + IniLoginInitiate(); + DeleteWindowByClass(WC_LOGIN_WINDOW); + AllocateWindowDescFront(&_login_window_desc, 0); +} diff --git a/src/watch_gui.cpp b/src/watch_gui.cpp new file mode 100644 index 0000000000..26979ce27d --- /dev/null +++ b/src/watch_gui.cpp @@ -0,0 +1,610 @@ +/* $Id: watch_gui.cpp 17678 2009-10-07 20:54:05 muxy $ */ + +/** @file watch_gui.cpp GUI that follow other company building. */ + +#include "stdafx.h" +#include "watch_gui.h" +#include "widget_type.h" +#include "gfx_type.h" +#include "gfx_func.h" +#include "company_base.h" +#include "company_gui.h" +#include "viewport_func.h" +#include "window_func.h" +#include "strings_func.h" +#include "zoom_func.h" +#include "map_func.h" + +#include "network/network.h" +#include "network/network_func.h" +#include "network/network_base.h" +#include "network/network_gui.h" +#include "table/sprites.h" +#include "table/strings.h" + +#include "textbuf_gui.h" +#include "company_gui.h" //company window +#include "network/network_gui.h" //private message +#include "console_func.h" //IConsolePrintF +#include "debug.h" +/** Make the widgets columns for company button, has_client and activity Blot. + * @param biggest_index Storage for collecting the biggest index used in the returned tree. + * @return Horizontal container with butons columns. + * @post \c *biggest_index contains the largest used index in the tree. + */ +static NWidgetBase *MakeCompanyButtons(int *biggest_index) +{ + NWidgetHorizontal *widget_container_horiz = NULL; // Storage for all cols. + NWidgetVertical *widget_container_company = NULL; // Storage for company Col. + NWidgetVertical *widget_container_hasclient = NULL; // Storage for Has Client Blot. + // NWidgetVertical *widget_container_activity = NULL; // Storage for Activity Blot. + + widget_container_horiz = new NWidgetHorizontal( ); + widget_container_company = new NWidgetVertical( ); + widget_container_hasclient = new NWidgetVertical( ); + // widget_container_activity = new NWidgetVertical( ); + + Dimension company_sprite_size = GetSpriteSize( SPR_COMPANY_ICON ); + company_sprite_size.width += WD_MATRIX_LEFT + WD_MATRIX_RIGHT; + company_sprite_size.height += WD_MATRIX_TOP + WD_MATRIX_BOTTOM + 1; // 1 for the 'offset' of being pressed + + Dimension blot_sprite_size = GetSpriteSize( SPR_BLOT ); + blot_sprite_size.width += WD_MATRIX_LEFT + WD_MATRIX_RIGHT; + blot_sprite_size.height += WD_MATRIX_TOP + WD_MATRIX_BOTTOM + 1; // 1 for the 'offset' of being pressed + + + for (int company_num = COMPANY_FIRST; company_num < MAX_COMPANIES; company_num++ ) { + /* Manage Company Buttons */ + NWidgetBackground *company_panel = new NWidgetBackground( WWT_PANEL, COLOUR_GREY, EWW_PB_COMPANY_FIRST + company_num ); + company_panel->SetMinimalSize( company_sprite_size.width, company_sprite_size.height ); + company_panel->SetResize( 0, 0 ); + company_panel->SetFill( 1, 0 ); + company_panel->SetDataTip( 0x0, STR_WATCH_CLICK_TO_WATCH_COMPANY ); + widget_container_company->Add( company_panel ); + + /* Manage Has Client Blot */ + NWidgetBackground *hasclient_panel = new NWidgetBackground( WWT_PANEL, COLOUR_GREY, EWW_HAS_CLIENT_FIRST + company_num ); + company_panel->SetMinimalSize( blot_sprite_size.width, blot_sprite_size.height ); + company_panel->SetResize( 0, 0 ); + company_panel->SetFill( 1, 0 ); + widget_container_hasclient->Add( hasclient_panel ); + } + + /* Add the verticals widgets to the horizontal container */ + widget_container_horiz->Add( widget_container_company ); + widget_container_horiz->Add( widget_container_hasclient ); + + /* return the horizontal widget container */ + return widget_container_horiz; +} + +/** + * Watch Company Window Widgets Array + * The Company Button, Has Client Blot and Activity Blot Columns + * Are made through a function regarding MAX_COMPANIES value + */ +static const NWidgetPart _nested_watch_company_widgets[] = { + /* Title Bar with close box, title, shade and stick boxes */ + NWidget(NWID_HORIZONTAL), + NWidget(WWT_CLOSEBOX, COLOUR_GREY), + NWidget(WWT_CAPTION, COLOUR_GREY, EWW_CAPTION ), SetDataTip(STR_WATCH_WINDOW_TITLE, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), + NWidget(WWT_SHADEBOX, COLOUR_GREY), + NWidget(WWT_DEFSIZEBOX, COLOUR_GREY), + NWidget(WWT_STICKYBOX, COLOUR_GREY), + EndContainer( ), + NWidget( NWID_HORIZONTAL ), + NWidget( NWID_VERTICAL ), + NWidgetFunction( MakeCompanyButtons ), + /* Buton Zoom Out, In, Scrollto */ + NWidget(NWID_HORIZONTAL), + NWidget( WWT_PUSHIMGBTN, COLOUR_GREY, EWW_ZOOMOUT ), SetDataTip( SPR_IMG_ZOOMOUT, STR_TOOLBAR_TOOLTIP_ZOOM_THE_VIEW_OUT), + NWidget( WWT_PUSHIMGBTN, COLOUR_GREY, EWW_ZOOMIN ), SetDataTip( SPR_IMG_ZOOMIN, STR_TOOLBAR_TOOLTIP_ZOOM_THE_VIEW_IN), + NWidget( WWT_PUSHIMGBTN, COLOUR_GREY, EWW_CENTER ), SetDataTip( SPR_CENTRE_VIEW_VEHICLE, STR_EXTRA_VIEW_MOVE_MAIN_TO_VIEW_TT), + NWidget( WWT_PANEL, COLOUR_GREY, EWW_NEW_WINDOW ), SetDataTip( 0, STR_WATCH_CLICK_NEW_WINDOW ), EndContainer( ), + EndContainer( ), + /* Background panel for resize purpose */ + NWidget( WWT_PANEL, COLOUR_GREY ), SetResize( 0, 1 ), EndContainer( ), + EndContainer( ), + /* Watch Pannel */ + NWidget(WWT_PANEL, COLOUR_GREY), + NWidget(NWID_VIEWPORT, INVALID_COLOUR, EWW_WATCH), SetPadding(2, 2, 2, 2), SetResize(1, 1), SetFill(1, 1), + EndContainer( ), + EndContainer( ), + /* Status Bar with resize buton */ + NWidget(NWID_HORIZONTAL), + NWidget(WWT_PANEL, COLOUR_GREY), SetFill(1, 1), SetResize(1, 0), EndContainer(), + NWidget(WWT_RESIZEBOX, COLOUR_GREY), + EndContainer( ), +}; + +/** + * Watch Company Window Descriptor + */ +static WindowDesc _watch_company_desc( + WDP_AUTO, "watch_gui", 300, 257, + WC_WATCH_COMPANY, WC_NONE, + WDF_NO_FOCUS, + _nested_watch_company_widgets, lengthof( _nested_watch_company_widgets ) +); + +// admin version +static const NWidgetPart _nested_watch_company_widgetsA[] = { + /* Title Bar with close box, title, shade and stick boxes */ + NWidget(NWID_HORIZONTAL), + NWidget(WWT_CLOSEBOX, COLOUR_GREY), + NWidget(WWT_CAPTION, COLOUR_GREY, EWW_CAPTION ), SetDataTip(STR_WATCH_WINDOW_TITLE, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), + NWidget(WWT_SHADEBOX, COLOUR_GREY), + NWidget(WWT_DEFSIZEBOX, COLOUR_GREY), + NWidget(WWT_STICKYBOX, COLOUR_GREY), + EndContainer(), + NWidget( NWID_HORIZONTAL ), + NWidget( NWID_VERTICAL ), + /* Buton Zoom Out, In, Scrollto */ + NWidget(NWID_SELECTION, INVALID_COLOUR, EWW_ENABLE_SELECT), + NWidget(NWID_VERTICAL ), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, EWW_KICK), SetMinimalSize(40, 20), SetFill(1, 0), SetDataTip(STR_XI_KICK, 0), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, EWW_BAN), SetMinimalSize(40, 20), SetFill(1, 0), SetDataTip(STR_XI_BAN, 0 ), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, EWW_LOCK), SetMinimalSize(40, 20), SetFill(1, 0), SetDataTip(STR_XI_LOCK, 0), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, EWW_UNLOCK), SetMinimalSize(40, 20), SetFill(1, 0), SetDataTip(STR_XI_UNLOCK, 0), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, EWW_JOIN), SetMinimalSize(40, 20), SetFill(1, 0), SetDataTip(STR_XI_JOIN, 0), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, EWW_KICKC), SetMinimalSize(40, 20), SetFill(1, 0), SetDataTip(STR_XI_KICKC, 0), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, EWW_RESET), SetMinimalSize(40, 20), SetFill(1, 0), SetDataTip(STR_XI_RESET, 0), + EndContainer(), + EndContainer(), + NWidget(NWID_HORIZONTAL), + NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, EWW_ZOOMOUT), SetDataTip(SPR_IMG_ZOOMOUT, STR_TOOLBAR_TOOLTIP_ZOOM_THE_VIEW_OUT), + NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, EWW_ZOOMIN), SetDataTip(SPR_IMG_ZOOMIN, STR_TOOLBAR_TOOLTIP_ZOOM_THE_VIEW_IN), + NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, EWW_CENTER), SetDataTip(SPR_CENTRE_VIEW_VEHICLE, STR_EXTRA_VIEW_MOVE_MAIN_TO_VIEW_TT), + NWidget(WWT_PANEL, COLOUR_GREY, EWW_NEW_WINDOW), SetDataTip(0, 0), EndContainer(), + EndContainer(), + NWidget(NWID_HORIZONTAL), + NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, EWW_CLIENTS), SetMinimalSize(23, 10), SetDataTip(SPR_IMG_COMPANY_GENERAL, STR_XI_PLAYERS_TOOLTIP), + NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, EWW_COMPANYW), SetMinimalSize(23, 10), SetDataTip(SPR_IMG_COMPANY_LIST, STR_XI_COMPANYW_TOOLTIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, EWW_COMPANYHQ), SetMinimalSize(23, 10), SetDataTip(STR_XI_COMPANYHQ, STR_XI_COMPANYHQ_TOOLTIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, EWW_PRIVATEP_MESSAGE), SetMinimalSize(23, 10), SetDataTip(STR_XI_PRIVATE_PLAYER_MESSAGE, STR_XI_PRIVATE_PLAYER_MESSAGE_TOOLTIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, EWW_PRIVATEC_MESSAGE), SetMinimalSize(23, 10), SetDataTip(STR_XI_PRIVATE_COMPANY_MESSAGE, STR_XI_PRIVATE_COMPANY_MESSAGE_TOOLTIP), + NWidget(WWT_PANEL, COLOUR_GREY, EWW_NEW_WINDOW), SetDataTip(0, 0), EndContainer(), + EndContainer(), + /* Background panel for resize purpose */ + NWidget(WWT_PANEL, COLOUR_GREY), SetResize(0, 1), EndContainer(), + EndContainer(), + /* Watch Pannel */ + NWidget(WWT_PANEL, COLOUR_GREY), + NWidget(NWID_VIEWPORT, INVALID_COLOUR, EWW_WATCH), SetPadding(2, 2, 2, 2), SetResize(1, 1), SetFill(1, 1), + EndContainer(), + EndContainer(), + /* Status Bar with resize buton */ + NWidget(NWID_HORIZONTAL), + NWidget(WWT_PANEL, COLOUR_GREY), SetFill(1, 1), SetResize(1, 0), EndContainer(), + NWidget(WWT_RESIZEBOX, COLOUR_GREY), + EndContainer(), +}; + +/** + * Watch Company Window Descriptor + */ +static WindowDesc _watch_company_descA( + WDP_AUTO, "watch_gui_client", 448, 256, + WC_WATCH_COMPANYA, WC_NONE, + WDF_CONSTRUCTION, + _nested_watch_company_widgetsA, lengthof(_nested_watch_company_widgetsA) +); + +static void ResetCallback(Window *w, bool confirmed) +{ + if (confirmed) { + NetworkClientInfo *ci = NetworkClientInfo::GetByClientID((ClientID)w->window_number); + if (ci && ci->client_playas != INVALID_COMPANY) { + char msg[16]; + seprintf(msg, lastof(msg), "!reset %i", ci->client_playas + 1); + NetworkClientSendChatToServer(msg); + } + } +} + +/** Watch Company Class Constructor + * @param desc Window Descriptor The Window Descriptor + * @param window_number The window number for the class + * @param company_to_watch Company ID for watching a particular company + */ +WatchCompany::WatchCompany(WindowDesc *desc, int window_number, CompanyID company_to_watch = INVALID_COMPANY, int Wtype = EWT_COMPANY) : Window(desc) +{ + this->Wtype = Wtype; + if(this->Wtype == EWT_CLIENT){ + this->watched_client = window_number; + NetworkClientInfo *ci = NetworkClientInfo::GetByClientID((ClientID)this->watched_client); + + this->watched_company = (ci != NULL) ? ci->client_playas : INVALID_COMPANY; + this->InitNested(window_number); + this->owner = (Owner)this->watched_company; + } + else if(this->Wtype == EWT_COMPANY){ + + this->watched_company = company_to_watch; + + this->InitNested(window_number); + this->owner = this->watched_company; + + /* Reset activity and client count for all companies */ + for (CompanyID i = COMPANY_FIRST; i < MAX_COMPANIES; i++) { + this->company_activity[i] = 0; + this->company_count_client[i] = 0; + } + + GetString( this->company_name, STR_JUST_NOTHING, lastof(this->company_name) ); + } + /* Init the viewport area */ + NWidgetViewport *nvp = this->GetWidget(EWW_WATCH); + nvp->InitializeViewport(this, 0, ZOOM_LVL_NORMAL); + + Point pt; + /* the main window with the main view */ + const Window *w = FindWindowById(WC_MAIN_WINDOW, 0); + + /* center on same place as main window (zoom is maximum, no adjustment needed) */ + pt.x = w->viewport->scrollpos_x + w->viewport->virtual_width / 2; + pt.y = w->viewport->scrollpos_y + w->viewport->virtual_height / 2; + + this->viewport->scrollpos_x = pt.x - this->viewport->virtual_width / 2; + this->viewport->scrollpos_y = pt.y - this->viewport->virtual_height / 2; + this->viewport->dest_scrollpos_x = this->viewport->scrollpos_x; + this->viewport->dest_scrollpos_y = this->viewport->scrollpos_y; + + if ( this->watched_company != INVALID_COMPANY ) { + Company *c = Company::Get( this->watched_company ); + this->ScrollToTile( c->last_build_coordinate ); + } + this->InvalidateData( ); +} + +void WatchCompany::SetStringParameters(int widget) +{ + if(widget != EWW_CAPTION) return; + if(this->Wtype == EWT_COMPANY){ + SetDParamStr(0, this->company_name); + return; + } + //EWT_CLIENT + if (!Company::IsValidHumanID(this->watched_company)){ + GetString(this->company_name, STR_JUST_NOTHING, lastof(this->company_name)); + } + else { + const Company *c = Company::Get(this->watched_company); + SetDParam(0, c->index); + GetString(this->company_name, STR_COMPANY_NAME, lastof(this->company_name)); + } + NetworkClientInfo *ci = NetworkClientInfo::GetByClientID((ClientID)this->watched_client); + if(ci){ + strecpy(this->client_name, ci->client_name, lastof(this->client_name)); + } + else{ + GetString(this->client_name, STR_JUST_NOTHING, lastof(this->client_name)); + } + SetDParamStr(0, this->client_name); + SetDParamStr(1, this->company_name); + if (Company::IsValidHumanID(this->watched_company)){ + SetDParam(2, this->watched_company + 1); + } + else { + SetDParam(2, COMPANY_SPECTATOR); + } +} + +void WatchCompany::OnPaint() +{ + if(this->Wtype == EWT_CLIENT){ + bool wstate = this->watched_company == INVALID_COMPANY ? true : false; + for(int i = EWW_LOCK; i <= EWW_COMPANYW; i++){ + this->SetWidgetDisabledState(i, wstate); + } + } + + this->DrawWidgets(); +} + +void WatchCompany::DrawWidget(const Rect &r, int widget) const +{ + if(this->Wtype != EWT_COMPANY) return; + /* draw the widget */ + /* Company Button */ + if (IsInsideMM(widget, EWW_PB_COMPANY_FIRST, EWW_PB_COMPANY_LAST + 1)) { + if (this->IsWidgetDisabled(widget)) return; + if ( Company::IsValidID( widget - EWW_PB_COMPANY_FIRST ) ) { + CompanyID cid = (CompanyID)(widget - ( EWW_PB_COMPANY_FIRST ) ); + int offset = (cid == this->watched_company) ? 1 : 0; + Dimension sprite_size = GetSpriteSize(SPR_COMPANY_ICON); + DrawCompanyIcon(cid, (r.left + r.right - sprite_size.width) / 2 + offset, (r.top + r.bottom - sprite_size.height) / 2 + offset); + } + return; + } + /* Has Client Blot */ + if (IsInsideMM( widget, EWW_HAS_CLIENT_FIRST, EWW_HAS_CLIENT_LAST + 1 )) { + if ( Company::IsValidID( widget-EWW_HAS_CLIENT_FIRST ) ) { + /* Draw the Blot only if Company Exists */ + Dimension sprite_size = GetSpriteSize(SPR_BLOT); +#ifdef ENABLE_NETWORK + if (!_networking) { // Local game, draw the Blot + DrawSprite(SPR_BLOT, Company::IsValidAiID(widget - EWW_HAS_CLIENT_FIRST) ? PALETTE_TO_ORANGE : PALETTE_TO_GREEN, (r.left + r.right - sprite_size.width) / 2, (r.top + r.bottom - sprite_size.height) / 2 ); + } else { // Network game, draw the blot according to company client count + DrawSprite(SPR_BLOT, this->company_count_client[widget-EWW_HAS_CLIENT_FIRST] > 0 ? (company_activity[widget-EWW_HAS_CLIENT_FIRST] > 0 ? PALETTE_TO_RED : PALETTE_TO_GREEN) : PALETTE_TO_GREY, (r.left + r.right - sprite_size.width) / 2, (r.top + r.bottom - sprite_size.height) / 2 ); + } +#else + DrawSprite(SPR_BLOT, Company::IsValidAiID(widget-EWW_HAS_CLIENT_FIRST) ? PALETTE_TO_ORANGE : PALETTE_TO_GREEN, (r.left + r.right - sprite_size.width) / 2, (r.top + r.bottom - sprite_size.height) / 2 ); +#endif + } + } +} + +void WatchCompany::OnResize() +{ + if (this->viewport != NULL) { + NWidgetViewport *nvp = this->GetWidget(EWW_WATCH); + nvp->UpdateViewportCoordinates(this); + } +} + +void WatchCompany::OnScroll(Point delta) +{ + const ViewPort *vp = IsPtInWindowViewport(this, _cursor.pos.x, _cursor.pos.y); + if (vp == NULL) return; + + this->viewport->scrollpos_x += ScaleByZoom(delta.x, vp->zoom); + this->viewport->scrollpos_y += ScaleByZoom(delta.y, vp->zoom); + this->viewport->dest_scrollpos_x = this->viewport->scrollpos_x; + this->viewport->dest_scrollpos_y = this->viewport->scrollpos_y; +} + +void WatchCompany::OnMouseWheel( int wheel ) +{ + ZoomInOrOutToCursorWindow(wheel < 0, this); +} + +void WatchCompany::OnClick(Point pt, int widget, int click_count) +{ + if (IsInsideMM(widget, EWW_PB_COMPANY_FIRST, EWW_PB_COMPANY_LAST + 1)) { + /* Click on Company Button */ + if (!this->IsWidgetDisabled(widget)) { + if (this->watched_company != INVALID_COMPANY) { + /* Raise the watched company button */ + this->RaiseWidget(this->watched_company + EWW_PB_COMPANY_FIRST); + } + if (this->watched_company == (CompanyID)(widget - EWW_PB_COMPANY_FIRST)) { + /* Stop watching watched_company */ + this->watched_company = INVALID_COMPANY; + GetString( this->company_name, STR_JUST_NOTHING, lastof(this->company_name) ); + } else { + /* Lower the new watched company button */ + this->watched_company = (CompanyID)(widget - EWW_PB_COMPANY_FIRST); + this->LowerWidget(this->watched_company + EWW_PB_COMPANY_FIRST); + Company *c = Company::Get( this->watched_company ); + SetDParam( 0, c->index ); + GetString( this->company_name, STR_COMPANY_NAME, lastof(this->company_name) ); + + this->ScrollToTile( c->last_build_coordinate ); + } + this->owner = this->watched_company; + this->SetDirty(); + } + } + else if ( IsInsideMM(widget, EWW_PB_ACTION1_FIRST, EWW_PB_ACTION1_LAST + 1)) { + if ( !this->IsWidgetDisabled(widget) ) { + this->ToggleWidgetLoweredState( widget ); + this->SetDirty(); + } + } +#ifdef ENABLE_NETWORK + else if ( IsInsideMM(widget, EWW_HAS_CLIENT_FIRST, EWW_HAS_CLIENT_LAST + 1)) { + if(_networking && Company::IsValidID(widget - EWW_HAS_CLIENT_FIRST)){ + ShowNetworkChatQueryWindow(DESTTYPE_TEAM, widget - EWW_HAS_CLIENT_FIRST); + } + } +#endif + else { + char msg[128]; + switch (widget) { + case EWW_ZOOMOUT: DoZoomInOutWindow(ZOOM_OUT, this); break; + case EWW_ZOOMIN: DoZoomInOutWindow(ZOOM_IN, this); break; + case EWW_CENTER: { // location button (move main view to same spot as this view) 'Center Main View' + Window *w = FindWindowById(WC_MAIN_WINDOW, 0); + int x = this->viewport->scrollpos_x; // Where is the watch looking at + int y = this->viewport->scrollpos_y; + + /* set the main view to same location. Based on the center, adjusting for zoom */ + w->viewport->dest_scrollpos_x = x - (w->viewport->virtual_width - this->viewport->virtual_width) / 2; + w->viewport->dest_scrollpos_y = y - (w->viewport->virtual_height - this->viewport->virtual_height) / 2; + } break; + case EWW_NEW_WINDOW: + ShowWatchWindow(this->watched_company, 0); + break; + case EWW_LOCK: + case EWW_UNLOCK: + seprintf(msg, lastof(msg), "!lockp %i", this->watched_company + 1); + NetworkClientSendChatToServer(msg); + break; + case EWW_KICK: + seprintf(msg, lastof(msg), "!kick %i", this->watched_client); + NetworkClientSendChatToServer(msg); + break; + case EWW_KICKC: + seprintf(msg, lastof(msg), "!move %i %i", this->watched_client, COMPANY_SPECTATOR); + NetworkClientSendChatToServer(msg); + break; + case EWW_JOIN: + seprintf(msg, lastof(msg), "!move %i", this->watched_company + 1); + NetworkClientSendChatToServer(msg); + break; + case EWW_BAN: + this->query_widget = EWQ_BAN; + ShowQueryString(STR_XI_BAN_DAYSDEFAULT, STR_XI_BAN_QUERY, 128, this, CS_ALPHANUMERAL, QSF_NONE); + break; + case EWW_RESET: + ShowQuery(STR_XI_RESET_CAPTION, STR_XI_REALY_RESET, this, ResetCallback); + break; + case EWW_PRIVATEP_MESSAGE:{ + const NetworkClientInfo *ci = NetworkClientInfo::GetByClientID((ClientID)this->watched_client); + if(ci) ShowNetworkChatQueryWindow(DESTTYPE_CLIENT, ci->client_id); + break; + } + case EWW_PRIVATEC_MESSAGE: + ShowNetworkChatQueryWindow(DESTTYPE_TEAM, this->watched_company); + break; + case EWW_CLIENTS: + seprintf(msg, lastof(msg), "!clients"); + NetworkClientSendChatToServer(msg); + break; + case EWW_COMPANYW: + if(this->watched_company != INVALID_COMPANY) ShowCompany(this->watched_company); + break; + case EWW_COMPANYHQ: + if(this->watched_company != INVALID_COMPANY){ + TileIndex tile = Company::Get((CompanyID)this->watched_company)->location_of_HQ; + ScrollMainWindowToTile(tile); + } + break; + } + } +} + +void WatchCompany::OnQueryTextFinished(char *str) +{ + if (str == NULL) return; + char msg[128]; + switch (this->query_widget) { + case EWQ_BAN: + seprintf(msg, lastof(msg), "!ban %i %s", this->watched_client, str); + NetworkClientSendChatToServer(msg); + break; + default: + break; + } +} + +void WatchCompany::OnInvalidateData(int data, bool gui_scope) +{ + if(this->Wtype == EWT_COMPANY){ + /* Disable the companies who are not active */ + for (CompanyID i = COMPANY_FIRST; i < MAX_COMPANIES; i++) { + this->SetWidgetDisabledState(EWW_PB_COMPANY_FIRST + i , !Company::IsValidID(i) ); + this->SetWidgetDisabledState(EWW_PB_ACTION1_FIRST + i , !Company::IsValidID(i) ); + } + /* Check if the currently selected company is still active. */ + if (this->watched_company != INVALID_COMPANY) { + /* Make sure the widget is lowered */ + this->LowerWidget(EWW_PB_COMPANY_FIRST + this->watched_company); + /* Check if the watched Company is still a valid one */ + if (!Company::IsValidID(this->watched_company)) { + /* Invalid Company Raise the associated widget. */ + this->RaiseWidget(this->watched_company + EWW_PB_COMPANY_FIRST ); + this->watched_company = INVALID_COMPANY; + GetString( this->company_name, STR_JUST_NOTHING, lastof(this->company_name) ); + } else { + Company *c = Company::Get( this->watched_company ); + SetDParam( 0, c->index ); + GetString( this->company_name, STR_COMPANY_NAME, lastof(this->company_name) ); + } + } else { + GetString( this->company_name, STR_JUST_NOTHING, lastof(this->company_name) ); + + } + #ifdef ENABLE_NETWORK + if (_networking) { // Local game, draw the Blot + /* Reset company count - network only */ + for (CompanyID i = COMPANY_FIRST; i < MAX_COMPANIES; i++) { + this->company_count_client[i] = 0; + } + /* Calculate client count into company - network only */ + NetworkClientInfo *ci; + FOR_ALL_CLIENT_INFOS( ci ) { + if (Company::IsValidID(ci->client_playas)) { + company_count_client[ci->client_playas] += 1; + } + } + } + #endif + } + else if(this->Wtype == EWT_CLIENT){ + if (data == 2) { + delete this; + return; + } + NetworkClientInfo *ci = NetworkClientInfo::GetByClientID((ClientID)this->watched_client); + if (!ci) { + delete this; + return; + } + else { + this->watched_company = ci->client_playas; + this->owner = (Owner)this->watched_company; + bool wstate = this->watched_company == INVALID_COMPANY ? true : false; + for(int i = EWW_LOCK; i <= EWW_COMPANYW; i++){ + this->SetWidgetDisabledState(i, wstate); + } + } + } + HandleZoomMessage(this, this->viewport, EWW_ZOOMIN, EWW_ZOOMOUT); +} + +void WatchCompany::ScrollToTile( TileIndex tile ) +{ + /* Scroll window to the tile, only if not zero */ + if (tile != 0) { + ScrollWindowTo( TileX(tile) * TILE_SIZE + TILE_SIZE / 2, TileY(tile) * TILE_SIZE + TILE_SIZE / 2, -1, this ); + } +} + +/** OnDoCommand function - Called by the DoCommand + * @param company The company ID who's client is building + * @param tile The tile number where action took place + */ +void WatchCompany::OnDoCommand( CompanyByte company, TileIndex tile ) +{ + /* Check if its my company */ + if (this->watched_company == company) + { + this->ScrollToTile(tile); + } + /* set the company_activity to its max in order to paint the BLOT in red + * This will result by having the activity blot set to red for all companies + * even the one watched. To avoid this behaviour and not to light the blot of + * the watched company, the code can be moved just after the ScrollToTile call. + */ + if (tile != 0) { + this->company_activity[company] = MAX_ACTIVITY; + this->SetDirty( ); + } +} + +/** Used to decrement the activity counter + * + */ +void WatchCompany::OnTick() +{ + bool set_dirty = false; + for (CompanyID i = COMPANY_FIRST; i < MAX_COMPANIES; i++) { + if ( this->company_activity[i]>0 ) { + this->company_activity[i]--; + if ( this->company_activity[i]==0 ) { + set_dirty = true; + } + } + } + /* If one company_activity reaches 0, then redraw */ + if (set_dirty) { + this->SetDirty(); + } +} + +void ShowWatchWindow(CompanyID company_to_watch = INVALID_COMPANY, int type = EWT_COMPANY) +{ + if(type == EWT_COMPANY || !_novarole){ + int i = 0; + /* find next free window number for watch viewport */ + while (FindWindowById(WC_WATCH_COMPANY, i) != NULL) i++; + new WatchCompany(&_watch_company_desc, i, company_to_watch, type); + } + else if(type == EWT_CLIENT){ + if (BringWindowToFrontById(WC_WATCH_COMPANYA, company_to_watch)) return; + new WatchCompany(&_watch_company_descA, company_to_watch, company_to_watch, type); + } +} + diff --git a/src/watch_gui.h b/src/watch_gui.h new file mode 100644 index 0000000000..63da2dcfb4 --- /dev/null +++ b/src/watch_gui.h @@ -0,0 +1,91 @@ +/* $Id: watch_gui.h 17678 2009-10-07 20:54:05 muxy $ */ + +/** @file watch_gui.h GUI Functions related to watching. */ + +#ifndef WATCH_GUI_H +#define WATCH_GUI_H + +#include "window_gui.h" +#include "company_base.h" + +#define MAX_ACTIVITY 30 + +enum WatchCompanyWidgets { + EWW_CAPTION, + EWW_PB_COMPANY_FIRST, + EWW_PB_COMPANY_LAST = EWW_PB_COMPANY_FIRST + MAX_COMPANIES - 1, + EWW_HAS_CLIENT_FIRST, + EWW_HAS_CLIENT_LAST = EWW_HAS_CLIENT_FIRST + MAX_COMPANIES - 1, + EWW_ACTIVITY_FIRST, + EWW_ACTIVITY_LAST = EWW_ACTIVITY_FIRST + MAX_COMPANIES - 1, + EWW_PB_ACTION1_FIRST, + EWW_PB_ACTION1_LAST = EWW_PB_ACTION1_FIRST + MAX_COMPANIES - 1, + EWW_WATCH, + EWW_ZOOMIN, + EWW_ZOOMOUT, + EWW_CENTER, + EWW_NEW_WINDOW, + + EWW_KICK, + EWW_BAN, + EWW_BAN1, + EWW_LOCK, + EWW_UNLOCK, + EWW_JOIN, + EWW_KICKC, + EWW_RESET, + EWW_COMPANYW, + EWW_COMPANYHQ, + EWW_PRIVATEP_MESSAGE, + EWW_PRIVATEC_MESSAGE, + EWW_CLIENTS, + EWW_ENABLE_SELECT, +}; + +enum WatchCompanyQuery { + EWQ_BAN = 0, + EWQ_BAN1, +}; + +enum WatchCompanyType { + EWT_COMPANY = 0, + EWT_CLIENT, +}; + +class WatchCompany : public Window +{ +protected: + CompanyID watched_company; // Company ID beeing watched. + int company_activity[MAX_COMPANIES]; // int array for activity blot. + int company_count_client[MAX_COMPANIES]; // company client count. + char company_name[MAX_LENGTH_COMPANY_NAME_CHARS]; // company name for title display + char client_name[NETWORK_CLIENT_NAME_LENGTH]; + + int watched_client; + WatchCompanyQuery query_widget; + int Wtype; + + void SetWatchWindowTitle( ); + void ScrollToTile( TileIndex tile ); + +public: + WatchCompany(WindowDesc *desc, int window_number, CompanyID company_to_watch, int Wtype); + + virtual void DrawWidget(const Rect &r, int widget) const; + virtual void OnClick(Point pt, int widget, int click_count); + virtual void OnResize(); + virtual void OnScroll(Point delta); + virtual void OnMouseWheel(int wheel); + virtual void OnInvalidateData(int data, bool gui_scope); + virtual void SetStringParameters(int widget); + virtual void OnTick(); + virtual void OnPaint(); + virtual void OnQueryTextFinished(char *str); + + void OnDoCommand(CompanyByte company, TileIndex tile); +}; + +void ShowWatchWindow(CompanyID company_to_watch, int type); + +#endif // COMPANY_GUI_H + diff --git a/src/zoning_cmd.cpp b/src/zoning_cmd.cpp new file mode 100644 index 0000000000..f37d5c5ddb --- /dev/null +++ b/src/zoning_cmd.cpp @@ -0,0 +1,295 @@ +/** @file zoning_cmd.cpp */ +#include "stdafx.h" +#include "station_base.h" +#include "industry.h" +#include "viewport_func.h" +#include "town.h" +#include "zoning.h" + +Zoning _zoning = {CHECKNOTHING, CHECKNOTHING}; +static const SpriteID INVALID_SPRITE_ID = UINT_MAX; +//RED GREEN BLACK LIGHT_BLUE ORANGE WHITE YELLOW PURPLE + +/** + * Draw the zoning sprites. + * @param SpriteID image + * the image + * @param SpriteID colour + * the colour of the zoning + * @param TileInfo ti + * the tile + */ +const byte _tileh_to_sprite[32] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 0, + 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 17, 0, 15, 18, 0, +}; + +void DrawZoningSprites(SpriteID image, SpriteID colour, const TileInfo *ti) { + if (colour != INVALID_SPRITE_ID){ + AddSortableSpriteToDraw(image + _tileh_to_sprite[ti->tileh], colour, ti->x, ti->y, 0x10, 0x10, 1, ti->z + 7); + } +} + +/** + * Detect whether this area is within the acceptance of any station. + * @param TileArea area + * the area to search by + * @return true if a station is found + */ +bool IsAreaWithinAcceptanceZoneOfStation(TileArea area) { + int catchment = _settings_game.station.station_spread + (_settings_game.station.modified_catchment ? MAX_CATCHMENT : CA_UNMODIFIED); + + StationFinder morestations(TileArea(TileXY(TileX(area.tile) - catchment / 2, TileY(area.tile) - catchment / 2), + TileX(area.tile) + area.w + catchment, TileY(area.tile) + area.h + catchment)); + + for (Station * const *st_iter = morestations.GetStations()->Begin(); st_iter != morestations.GetStations()->End(); ++st_iter) { + Station *st = *st_iter; + Rect rect = st->GetCatchmentRect(); + return TileArea(TileXY(rect.left, rect.top), TileXY(rect.right, rect.bottom)).Intersects(area); + } + return false; +} + +/** + * Detect whether this tile is within the acceptance of any station. + * @param TileIndex tile + * the tile to search by + * @return true if a station is found + */ +bool IsTileWithinAcceptanceZoneOfStation(TileIndex tile) { + int catchment = _settings_game.station.station_spread + (_settings_game.station.modified_catchment ? MAX_CATCHMENT : CA_UNMODIFIED); + + StationFinder morestations(TileArea(TileXY(TileX(tile) - catchment / 2, TileY(tile) - catchment / 2), + catchment, catchment)); + + for (Station * const *st_iter = morestations.GetStations()->Begin(); st_iter != morestations.GetStations()->End(); ++st_iter) { + Station *st = *st_iter; + Rect rect = st->GetCatchmentRect(); + if ((uint)rect.left <= TileX(tile) && TileX(tile) <= (uint)rect.right + && (uint)rect.top <= TileY(tile) && TileY(tile) <= (uint)rect.bottom ) + { + return true; + } + } + return false; +} + +//Check the opinion of the local authority in the tile. +SpriteID TileZoneCheckOpinionEvaluation(TileIndex tile, Owner owner) { + Town *town = ClosestTownFromTile(tile, _settings_game.economy.dist_local_authority); + + if (town == NULL) return INVALID_SPRITE_ID; // no town + else if (HasBit(town->have_ratings, owner)) { // good : bad + int16 rating = town->ratings[owner]; + if(rating <= RATING_APPALLING) return SPR_PALETTE_ZONING_RED; + if(rating <= RATING_POOR) return SPR_PALETTE_ZONING_ORANGE; + if(rating <= RATING_MEDIOCRE) return SPR_PALETTE_ZONING_YELLOW; + if(rating <= RATING_GOOD) return SPR_PALETTE_ZONING_WHITE; + if(rating <= RATING_VERYGOOD) return SPR_PALETTE_ZONING_PURPLE; + if(rating <= RATING_EXCELLENT) return SPR_PALETTE_ZONING_LIGHT_BLUE; + return SPR_PALETTE_ZONING_GREEN; + } + else { + return SPR_PALETTE_ZONING_BLACK; // no opinion + } +} + +//Check whether the player can build in tile. +SpriteID TileZoneCheckBuildEvaluation(TileIndex tile, Owner owner) { + /* Let's first check for the obvious things you cannot build on */ + switch ( GetTileType(tile) ) { + case MP_INDUSTRY: + case MP_OBJECT: + case MP_HOUSE: + return SPR_PALETTE_ZONING_RED; //can't build + case MP_STATION: + case MP_TUNNELBRIDGE: + case MP_ROAD: + case MP_RAILWAY: { + if (GetTileOwner(tile) != owner) return SPR_PALETTE_ZONING_RED; //can't build + else return INVALID_SPRITE_ID; + } + default: return INVALID_SPRITE_ID; + } +} + +//Detect whether the tile is within the catchment zone of a station. +//black if within, light blue if only in acceptance zone and nothing if no nearby station. + SpriteID TileZoneCheckStationCatchmentEvaluation(TileIndex tile) { + // Never on a station. + if (IsTileType(tile, MP_STATION)){ + return INVALID_SPRITE_ID; + } + // For provided goods + StationFinder stations(TileArea(tile, 1, 1)); + if (stations.GetStations()->Length() > 0) { + return SPR_PALETTE_ZONING_GREEN; + } + // For accepted goods + if (IsTileWithinAcceptanceZoneOfStation(tile)){ + return SPR_PALETTE_ZONING_LIGHT_BLUE; + } + return INVALID_SPRITE_ID; +} + +//Detect whether a building is unserved by a station of owner. +//return red if unserved, orange if only accepting, nothing if served or not a building +SpriteID TileZoneCheckUnservedBuildingsEvaluation(TileIndex tile) { + CargoArray dat; + + if (IsTileType (tile, MP_HOUSE)) + //&& ( ( memset(&dat, 0, sizeof(dat)), AddAcceptedCargo(tile, dat, NULL), (dat[CT_MAIL] + dat[CT_PASSENGERS] > 0) ) + // || ( memset(&dat, 0, sizeof(dat)), AddProducedCargo(tile, dat), (dat[CT_MAIL] + dat[CT_PASSENGERS] > 0) ) ) ) + { + StationFinder stations(TileArea(tile, 1, 1)); + + if (stations.GetStations()->Length() > 0) { + return INVALID_SPRITE_ID; + } + // For accepted goods + if (IsTileWithinAcceptanceZoneOfStation(tile)){ + return SPR_PALETTE_ZONING_ORANGE; + } + return SPR_PALETTE_ZONING_RED; + } + return INVALID_SPRITE_ID; +} + +//Detect whether an industry is unserved by a station of owner. +//return red if unserved, orange if only accepting, nothing if served or not a building +SpriteID TileZoneCheckUnservedIndustriesEvaluation(TileIndex tile) { + if (IsTileType(tile, MP_INDUSTRY)) { + Industry *ind = Industry::GetByTile(tile); + StationFinder stations(ind->location); + + if (stations.GetStations()->Length() > 0){ + return INVALID_SPRITE_ID; + } + + // For accepted goods + if (IsAreaWithinAcceptanceZoneOfStation(ind->location)){ + return SPR_PALETTE_ZONING_ORANGE; + } + return SPR_PALETTE_ZONING_RED; + } + return INVALID_SPRITE_ID; +} + +//Check which town zone tile belongs to. +SpriteID TileZoneCheckTownZones(TileIndex tile) { + HouseZonesBits next_zone = HZB_BEGIN, tz = HZB_END; + + Town *town; + FOR_ALL_TOWNS(town) { + while (next_zone < HZB_END + && (town->cache.squared_town_zone_radius[next_zone] == 0 + || DistanceSquare(tile, town->xy) <= town->cache.squared_town_zone_radius[next_zone]) + ){ + if(town->cache.squared_town_zone_radius[next_zone] != 0) tz = next_zone; + next_zone++; + } + } + + switch (tz) { + case HZB_TOWN_EDGE: return SPR_PALETTE_ZONING_LIGHT_BLUE; // Tz0 + case HZB_TOWN_OUTSKIRT: return SPR_PALETTE_ZONING_RED; // Tz1 + case HZB_TOWN_OUTER_SUBURB: return SPR_PALETTE_ZONING_YELLOW; // Tz2 + case HZB_TOWN_INNER_SUBURB: return SPR_PALETTE_ZONING_GREEN; // Tz3 + case HZB_TOWN_CENTRE: return SPR_PALETTE_ZONING_WHITE; // Tz4 - center + default: return INVALID_SPRITE_ID; // no town + } + return INVALID_SPRITE_ID; +} + +//Check CB town acceptance area +SpriteID TileZoneCheckCBBorders(TileIndex tile) { + Town *town = CalcClosestTownFromTile(tile); + + if (town != NULL) { + if (DistanceManhattan(town->xy, tile) <= _settings_client.gui.cb_distance_check) { + return SPR_PALETTE_ZONING_LIGHT_BLUE; //cb catchment + } + } + return INVALID_SPRITE_ID; // no town +} + +//Check whether the tile is within citybuilder server town border (where houses could be built) +SpriteID TileZoneCheckCBTownBorders(TileIndex tile) { + Town *town; + FOR_ALL_TOWNS(town) { + uint32 distMax = DistanceMax(town->xy, tile); + if (distMax * distMax < town->cache.squared_town_zone_radius[0]){ + return SPR_PALETTE_ZONING_GREEN; + } + } + return INVALID_SPRITE_ID; +} + +//Check which advertisement zone(small, medium, large) tile belongs to +SpriteID TileZoneCheckTownAdvertisementZones(TileIndex tile) { + Town *town = CalcClosestTownFromTile(tile, max((uint)_settings_game.economy.dist_local_authority, 20U)); + if (town == NULL) return INVALID_SPRITE_ID; //nothing + + uint dist = DistanceManhattan(town->xy, tile); + + if (dist <= 10) return SPR_PALETTE_ZONING_GREEN; + if (dist <= 15) return SPR_PALETTE_ZONING_YELLOW; + if (dist <= 20) return SPR_PALETTE_ZONING_LIGHT_BLUE; + return INVALID_SPRITE_ID; +} + +//Checks for tile in growth tiles info +SpriteID TileZoneCheckTownsGrowthTiles(TileIndex tile) { + switch (max(_towns_growth_tiles[tile], _towns_growth_tiles_last_month[tile])) { + case TGTS_CB_HOUSE_REMOVED_NOGROW: return SPR_PALETTE_ZONING_LIGHT_BLUE; + case TGTS_RH_REMOVED: return SPR_PALETTE_ZONING_LIGHT_BLUE; + case TGTS_RH_REBUILT: return SPR_PALETTE_ZONING_WHITE; + case TGTS_NEW_HOUSE: return SPR_PALETTE_ZONING_GREEN; + case TGTS_CYCLE_SKIPPED: return SPR_PALETTE_ZONING_ORANGE; + case TGTS_HOUSE_SKIPPED: return SPR_PALETTE_ZONING_YELLOW; + case TGTS_CB_HOUSE_REMOVED: return SPR_PALETTE_ZONING_RED; + default: return INVALID_SPRITE_ID; + } +} + +/** + * General evaluation function; calls all the other functions depending on + * evaluation mode. + * @param TileIndex tile + * Tile to be evaluated. + * @param EvaluationMode ev_mode + * The current evaluation mode. + * @return The colour returned by the evaluation functions (none if no ev_mode). + */ +SpriteID TileZoningSpriteEvaluation(TileIndex tile, Owner owner, EvaluationMode ev_mode) { + switch (ev_mode) { + case CHECKOPINION: return TileZoneCheckOpinionEvaluation(tile, owner); + case CHECKBUILD: return TileZoneCheckBuildEvaluation(tile, owner); + case CHECKSTACATCH: return TileZoneCheckStationCatchmentEvaluation(tile); + case CHECKBULUNSER: return TileZoneCheckUnservedBuildingsEvaluation(tile); + case CHECKINDUNSER: return TileZoneCheckUnservedIndustriesEvaluation(tile); + case CHECKTOWNZONES: return TileZoneCheckTownZones(tile); + case CHECKCBBORDERS: return TileZoneCheckCBBorders(tile); + case CHECKCBTOWNBORDERS: return TileZoneCheckCBTownBorders(tile); + case CHECKTOWNADZONES: return TileZoneCheckTownAdvertisementZones(tile); + case CHECKTOWNGROWTHTILES: return TileZoneCheckTownsGrowthTiles(tile); + default: return INVALID_SPRITE_ID; + } +} + +/** + * Draw the the zoning on the tile. + * @param TileInfo ti + * the tile to draw on. + */ +void DrawTileZoning(const TileInfo *ti) { + if(_zoning.outer == CHECKNOTHING && _zoning.inner == CHECKNOTHING) return; //nothing to do + if (_game_mode != GM_NORMAL || ti->tile >= MapSize() || IsTileType(ti->tile, MP_VOID)) return; //check invalid + if (_zoning.outer != CHECKNOTHING){ + DrawZoningSprites(SPR_SELECT_TILE, TileZoningSpriteEvaluation(ti->tile, _local_company, _zoning.outer), ti); + } + if (_zoning.inner != CHECKNOTHING){ + DrawZoningSprites(SPR_INNER_HIGHLIGHT_BASE, TileZoningSpriteEvaluation(ti->tile, _local_company, _zoning.inner), ti); + } +} + diff --git a/src/zoning_gui.cpp b/src/zoning_gui.cpp new file mode 100644 index 0000000000..d979fcd682 --- /dev/null +++ b/src/zoning_gui.cpp @@ -0,0 +1,189 @@ +/** @file zoning_gui.cpp */ +#include "stdafx.h" +#include "widgets/dropdown_func.h" +#include "table/strings.h" +#include "strings_func.h" +#include "gfx_func.h" +#include "core/geometry_func.hpp" +#include "zoning.h" +#include "hotkeys.h" + +const StringID _zone_types[] = { + //STR_ZONING_NO_ZONING, + STR_ZONING_AUTHORITY, + STR_ZONING_CAN_BUILD, + STR_ZONING_STA_CATCH, + STR_ZONING_BUL_UNSER, + STR_ZONING_IND_UNSER, + STR_ZONING_TOWN_ZONES, + STR_ZONING_CB_BORDERS, + STR_ZONING_CB_TOWN_BORDERS, + STR_ZONING_ADVERTISEMENT_ZONES, + STR_ZONING_TOWN_GROWTH_TILES, +}; + +enum ZoningToolbarWidgets { + ZTW_CAPTION, + ZTW_OUTER_FIRST, + ZTW_INNER_FIRST = ZTW_OUTER_FIRST + 10, + ZTW_INNER_END = ZTW_INNER_FIRST + 10, +}; + +struct ZoningWindow : public Window { + uint maxwidth; + uint maxheight; + + ZoningWindow(WindowDesc *desc, int window_number) : Window(desc) { + int zone_types_size = lengthof(_zone_types); + Dimension dim; + this->maxwidth = 0; + this->maxheight = 0; + for (int i = 0; i < zone_types_size; i++) { + dim = GetStringBoundingBox(_zone_types[i]); + this->maxwidth = max(this->maxwidth, dim.width); + this->maxheight = max(this->maxheight, dim.height); + } + + this->InitNested(window_number); + this->InvalidateData(); + if(_zoning.outer != CHECKNOTHING) this->LowerWidget(ZTW_OUTER_FIRST + _zoning.outer - 1); //-1:skip CHECKNOTHING + if(_zoning.inner != CHECKNOTHING) this->LowerWidget(ZTW_INNER_FIRST + _zoning.inner - 1); + } + + virtual void OnPaint() { + this->DrawWidgets(); + } + + virtual void OnClick(Point pt, int widget, int click_count) { + bool outer = true; + bool deselect = false; + EvaluationMode clicked; + if (widget >= ZTW_OUTER_FIRST && widget < ZTW_INNER_FIRST){ + clicked = (EvaluationMode)(widget - ZTW_OUTER_FIRST + 1); //+1:skip CHECKNOTHING + deselect = _zoning.outer == clicked; + _zoning.outer = deselect ? CHECKNOTHING : clicked; + } + else if (widget >= ZTW_INNER_FIRST && widget < ZTW_INNER_END){ + clicked = (EvaluationMode)(widget - ZTW_INNER_FIRST + 1); + deselect = _zoning.inner == clicked; + _zoning.inner = deselect ? CHECKNOTHING : clicked; + outer = false; + } + else return; + + this->RaiseAllWidgets(outer); + if(!deselect) this->ToggleWidgetLoweredState(widget); + this->InvalidateData(); + MarkWholeScreenDirty(); + } + + void DrawWidget(const Rect &r, int widget) const + { + StringID strid = STR_EMPTY; + if (widget >= ZTW_OUTER_FIRST && widget < ZTW_INNER_FIRST){ + strid = _zone_types[widget - ZTW_OUTER_FIRST]; + } + else if (widget >= ZTW_INNER_FIRST && widget < ZTW_INNER_END){ + strid = _zone_types[widget - ZTW_INNER_FIRST]; + } + else return; + + bool rtl = _current_text_dir == TD_RTL; + byte clk_dif = this->IsWidgetLowered(widget) ? 1 : 0; + int x = r.left + WD_FRAMERECT_LEFT; + int y = r.top; + + DrawString(rtl ? r.left : x + clk_dif + 1, (rtl ? r.right + clk_dif : r.right), y + 1 + clk_dif, strid, TC_FROMSTRING, SA_LEFT); + } + + virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) { + if (widget >= ZTW_OUTER_FIRST && widget < ZTW_INNER_END){ + size->width = this->maxwidth + padding.width + 8; + size->height = this->maxheight + 2; + } + } + + void RaiseAllWidgets(bool outer){ + byte start = outer ? ZTW_OUTER_FIRST : ZTW_INNER_FIRST; + byte end = outer ? ZTW_INNER_FIRST : ZTW_INNER_END; + for(byte i = start; i < end; i++){ + if(this->IsWidgetLowered(i)){ + this->ToggleWidgetLoweredState(i); + break; + } + } + } + + virtual EventState OnHotkey(int hotkey) + { + return Window::OnHotkey(hotkey); + } + + static HotkeyList hotkeys; +}; + +static Hotkey zoning_hotkeys[] = { + Hotkey(WKC_SHIFT | '1', "authority", ZTW_OUTER_FIRST), + Hotkey(WKC_SHIFT | '2', "build_status", ZTW_OUTER_FIRST + 1), + Hotkey(WKC_SHIFT | '3', "station_catchment", ZTW_OUTER_FIRST + 2), + Hotkey(WKC_SHIFT | '4', "unserved_buildings", ZTW_OUTER_FIRST + 3), + Hotkey(WKC_SHIFT | '5', "unserved_industries", ZTW_OUTER_FIRST + 4), + Hotkey(WKC_SHIFT | '6', "town_zone", ZTW_OUTER_FIRST + 5), + Hotkey(WKC_SHIFT | '7', "CB_acceptance", ZTW_OUTER_FIRST + 6), + Hotkey(WKC_SHIFT | '8', "CB_build_borders", ZTW_OUTER_FIRST + 7), + Hotkey(WKC_SHIFT | '9', "advertisement", ZTW_OUTER_FIRST + 8), + Hotkey(WKC_SHIFT | '0', "growth_tiles", ZTW_OUTER_FIRST + 9), + HOTKEY_LIST_END +}; + +HotkeyList ZoningWindow::hotkeys("zoning_gui", zoning_hotkeys); + + +/** Construct the row containing the digit keys. */ +static NWidgetBase *MakeZoningButtons(int *biggest_index) +{ + NWidgetHorizontal *hor = new NWidgetHorizontal(NC_EQUALSIZE); + int zone_types_size = lengthof(_zone_types); + hor->SetPadding(1, 1, 1, 1); + + for(int i = 0; i < 2; i++){ + NWidgetVertical *ver = new NWidgetVertical; + + int offset = (i == 0) ? ZTW_OUTER_FIRST : ZTW_INNER_FIRST; + + for (int j = 0; j < zone_types_size; j++) { + NWidgetBackground *leaf = new NWidgetBackground(WWT_PANEL, i==0 ? COLOUR_ORANGE : COLOUR_YELLOW, offset + j, NULL); + leaf->SetFill(1, 0); + leaf->SetPadding(0, 0, 0, 0); + ver->Add(leaf); + } + hor->Add(ver); + } + *biggest_index = ZTW_INNER_END - 1; + return hor; +} + +static const NWidgetPart _nested_zoning_widgets[] = { + NWidget(NWID_HORIZONTAL), + NWidget(WWT_CLOSEBOX, COLOUR_GREY), + NWidget(WWT_CAPTION, COLOUR_GREY, ZTW_CAPTION), SetDataTip(STR_ZONING_TOOLBAR, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), + NWidget(WWT_SHADEBOX, COLOUR_GREY), + NWidget(WWT_STICKYBOX, COLOUR_GREY), + EndContainer(), + NWidget(WWT_PANEL, COLOUR_GREY), + NWidgetFunction(MakeZoningButtons), + EndContainer() +}; + +static WindowDesc _zoning_desc ( + WDP_AUTO, NULL, 0, 0, + WC_ZONING_TOOLBAR, WC_NONE, + 0, + _nested_zoning_widgets, lengthof(_nested_zoning_widgets), + &ZoningWindow::hotkeys +); + +void ShowZoningToolbar() { + AllocateWindowDescFront(&_zoning_desc, 0); +} +