Fix: List a max of four share owners instead of three
[openttd-github.git] / src / company_gui.cpp
blobfdcc7309dfe671322f27412971c5bf384e63e5a3
1 /*
2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6 */
8 /** @file company_gui.cpp %Company related GUIs. */
10 #include "stdafx.h"
11 #include "currency.h"
12 #include "error.h"
13 #include "gui.h"
14 #include "window_gui.h"
15 #include "textbuf_gui.h"
16 #include "viewport_func.h"
17 #include "company_func.h"
18 #include "command_func.h"
19 #include "network/network.h"
20 #include "network/network_gui.h"
21 #include "network/network_func.h"
22 #include "newgrf.h"
23 #include "company_manager_face.h"
24 #include "strings_func.h"
25 #include "date_func.h"
26 #include "widgets/dropdown_type.h"
27 #include "tilehighlight_func.h"
28 #include "company_base.h"
29 #include "core/geometry_func.hpp"
30 #include "object_type.h"
31 #include "rail.h"
32 #include "road.h"
33 #include "engine_base.h"
34 #include "window_func.h"
35 #include "road_func.h"
36 #include "water.h"
37 #include "station_func.h"
38 #include "zoom_func.h"
39 #include "sortlist_type.h"
40 #include "company_cmd.h"
41 #include "economy_cmd.h"
42 #include "group_cmd.h"
43 #include "misc_cmd.h"
44 #include "object_cmd.h"
46 #include "widgets/company_widget.h"
48 #include "safeguards.h"
51 /** Company GUI constants. */
52 static const uint EXP_LINESPACE = 2; ///< Amount of vertical space for a horizontal (sub-)total line.
53 static const uint EXP_BLOCKSPACE = 10; ///< Amount of vertical space between two blocks of numbers.
54 static const int EXP_INDENT = 10; ///< Amount of horizontal space for an indented line.
56 static void DoSelectCompanyManagerFace(Window *parent);
57 static void ShowCompanyInfrastructure(CompanyID company);
59 /** List of revenues. */
60 static ExpensesType _expenses_list_revenue[] = {
61 EXPENSES_TRAIN_REVENUE,
62 EXPENSES_ROADVEH_REVENUE,
63 EXPENSES_AIRCRAFT_REVENUE,
64 EXPENSES_SHIP_REVENUE,
67 /** List of operating expenses. */
68 static ExpensesType _expenses_list_operating_costs[] = {
69 EXPENSES_TRAIN_RUN,
70 EXPENSES_ROADVEH_RUN,
71 EXPENSES_AIRCRAFT_RUN,
72 EXPENSES_SHIP_RUN,
73 EXPENSES_PROPERTY,
74 EXPENSES_LOAN_INTEREST,
77 /** List of capital expenses. */
78 static ExpensesType _expenses_list_capital_costs[] = {
79 EXPENSES_CONSTRUCTION,
80 EXPENSES_NEW_VEHICLES,
81 EXPENSES_OTHER,
84 /** Expense list container. */
85 struct ExpensesList {
86 const ExpensesType *et; ///< Expenses items.
87 const uint length; ///< Number of items in list.
89 ExpensesList(ExpensesType *et, int length) : et(et), length(length)
93 uint GetHeight() const
95 /* Add up the height of all the lines. */
96 return this->length * FONT_HEIGHT_NORMAL;
99 /** Compute width of the expenses categories in pixels. */
100 uint GetListWidth() const
102 uint width = 0;
103 for (uint i = 0; i < this->length; i++) {
104 ExpensesType et = this->et[i];
105 width = std::max(width, GetStringBoundingBox(STR_FINANCES_SECTION_CONSTRUCTION + et).width);
107 return width;
111 /** Types of expense lists */
112 static const ExpensesList _expenses_list_types[] = {
113 ExpensesList(_expenses_list_revenue, lengthof(_expenses_list_revenue)),
114 ExpensesList(_expenses_list_operating_costs, lengthof(_expenses_list_operating_costs)),
115 ExpensesList(_expenses_list_capital_costs, lengthof(_expenses_list_capital_costs)),
119 * Get the total height of the "categories" column.
120 * @return The total height in pixels.
122 static uint GetTotalCategoriesHeight()
124 /* There's an empty line and blockspace on the year row */
125 uint total_height = FONT_HEIGHT_NORMAL + EXP_BLOCKSPACE;
127 for (uint i = 0; i < lengthof(_expenses_list_types); i++) {
128 /* Title + expense list + total line + total + blockspace after category */
129 total_height += FONT_HEIGHT_NORMAL + _expenses_list_types[i].GetHeight() + EXP_LINESPACE + FONT_HEIGHT_NORMAL + EXP_BLOCKSPACE;
132 /* Total income */
133 total_height += EXP_LINESPACE + FONT_HEIGHT_NORMAL + EXP_BLOCKSPACE;
135 return total_height;
139 * Get the required width of the "categories" column, equal to the widest element.
140 * @return The required width in pixels.
142 static uint GetMaxCategoriesWidth()
144 uint max_width = 0;
146 /* Loop through categories to check max widths. */
147 for (uint i = 0; i < lengthof(_expenses_list_types); i++) {
148 /* Title of category */
149 max_width = std::max(max_width, GetStringBoundingBox(STR_FINANCES_REVENUE_TITLE + i).width);
150 /* Entries in category */
151 max_width = std::max(max_width, _expenses_list_types[i].GetListWidth());
154 return max_width;
158 * Draw a category of expenses (revenue, operating expenses, capital expenses).
160 static void DrawCategory(const Rect &r, int start_y, ExpensesList list)
162 int offs_left = _current_text_dir == TD_LTR ? EXP_INDENT : 0;
163 int offs_right = _current_text_dir == TD_LTR ? 0 : EXP_INDENT;
165 int y = start_y;
166 ExpensesType et;
168 for (uint i = 0; i < list.length; i++) {
169 et = list.et[i];
170 DrawString(r.left + offs_left, r.right - offs_right, y, STR_FINANCES_SECTION_CONSTRUCTION + et);
171 y += FONT_HEIGHT_NORMAL;
176 * Draw the expenses categories.
177 * @param r Available space for drawing.
178 * @note The environment must provide padding at the left and right of \a r.
180 static void DrawCategories(const Rect &r)
182 /* Start with an empty space in the year row, plus the blockspace under the year. */
183 int y = r.top + FONT_HEIGHT_NORMAL + EXP_BLOCKSPACE;
185 for (uint i = 0; i < lengthof(_expenses_list_types); i++) {
186 /* Draw category title and advance y */
187 DrawString(r.left, r.right, y, (STR_FINANCES_REVENUE_TITLE + i), TC_FROMSTRING, SA_LEFT);
188 y += FONT_HEIGHT_NORMAL;
190 /* Draw category items and advance y */
191 DrawCategory(r, y, _expenses_list_types[i]);
192 y += _expenses_list_types[i].GetHeight();
194 /* Advance y by the height of the total and associated total line */
195 y += EXP_LINESPACE + FONT_HEIGHT_NORMAL;
197 /* Advance y by a blockspace after this category block */
198 y += EXP_BLOCKSPACE;
201 /* Draw total profit/loss */
202 y += EXP_LINESPACE;
203 DrawString(r.left, r.right, y, STR_FINANCES_NET_PROFIT, TC_FROMSTRING, SA_LEFT);
207 * Draw an amount of money.
208 * @param amount Amount of money to draw,
209 * @param left Left coordinate of the space to draw in.
210 * @param right Right coordinate of the space to draw in.
211 * @param top Top coordinate of the space to draw in.
212 * @param colour The TextColour of the string.
214 static void DrawPrice(Money amount, int left, int right, int top, TextColour colour)
216 StringID str = STR_FINANCES_NEGATIVE_INCOME;
217 if (amount == 0) {
218 str = STR_FINANCES_ZERO_INCOME;
219 } else if (amount < 0) {
220 amount = -amount;
221 str = STR_FINANCES_POSITIVE_INCOME;
223 SetDParam(0, amount);
224 DrawString(left, right, top, str, colour, SA_RIGHT);
228 * Draw a category of expenses/revenues in the year column.
229 * @return The income sum of the category.
231 static Money DrawYearCategory (const Rect &r, int start_y, ExpensesList list, const Money(*tbl)[EXPENSES_END])
233 int y = start_y;
234 ExpensesType et;
235 Money sum = 0;
237 for (uint i = 0; i < list.length; i++) {
238 et = list.et[i];
239 Money cost = (*tbl)[et];
240 sum += cost;
241 if (cost != 0) DrawPrice(cost, r.left, r.right, y, TC_BLACK);
242 y += FONT_HEIGHT_NORMAL;
245 /* Draw the total at the bottom of the category. */
246 GfxFillRect(r.left, y, r.right, y, PC_BLACK);
247 y += EXP_LINESPACE;
248 if (sum != 0) DrawPrice(sum, r.left, r.right, y, TC_WHITE);
250 /* Return the sum for the yearly total. */
251 return sum;
256 * Draw a column with prices.
257 * @param r Available space for drawing.
258 * @param year Year being drawn.
259 * @param tbl Pointer to table of amounts for \a year.
260 * @note The environment must provide padding at the left and right of \a r.
262 static void DrawYearColumn(const Rect &r, int year, const Money (*tbl)[EXPENSES_END])
264 int y = r.top;
265 Money sum;
267 /* Year header */
268 SetDParam(0, year);
269 DrawString(r.left, r.right, y, STR_FINANCES_YEAR, TC_FROMSTRING, SA_RIGHT, true);
270 y += FONT_HEIGHT_NORMAL + EXP_BLOCKSPACE;
272 /* Categories */
273 for (uint i = 0; i < lengthof(_expenses_list_types); i++) {
274 y += FONT_HEIGHT_NORMAL;
275 sum += DrawYearCategory(r, y, _expenses_list_types[i], tbl);
276 /* Expense list + expense category title + expense category total + blockspace after category */
277 y += _expenses_list_types[i].GetHeight() + EXP_LINESPACE + FONT_HEIGHT_NORMAL + EXP_BLOCKSPACE;
280 /* Total income. */
281 GfxFillRect(r.left, y, r.right, y, PC_BLACK);
282 y += EXP_LINESPACE;
283 DrawPrice(sum, r.left, r.right, y, TC_WHITE);
286 static const NWidgetPart _nested_company_finances_widgets[] = {
287 NWidget(NWID_HORIZONTAL),
288 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
289 NWidget(WWT_CAPTION, COLOUR_GREY, WID_CF_CAPTION), SetDataTip(STR_FINANCES_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
290 NWidget(WWT_IMGBTN, COLOUR_GREY, WID_CF_TOGGLE_SIZE), SetDataTip(SPR_LARGE_SMALL_WINDOW, STR_TOOLTIP_TOGGLE_LARGE_SMALL_WINDOW),
291 NWidget(WWT_SHADEBOX, COLOUR_GREY),
292 NWidget(WWT_STICKYBOX, COLOUR_GREY),
293 EndContainer(),
294 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_CF_SEL_PANEL),
295 NWidget(WWT_PANEL, COLOUR_GREY),
296 NWidget(NWID_HORIZONTAL), SetPadding(WD_FRAMERECT_TOP, WD_FRAMERECT_RIGHT, WD_FRAMERECT_BOTTOM, WD_FRAMERECT_LEFT), SetPIP(0, 9, 0),
297 NWidget(WWT_EMPTY, COLOUR_GREY, WID_CF_EXPS_CATEGORY), SetMinimalSize(120, 0), SetFill(0, 0),
298 NWidget(WWT_EMPTY, COLOUR_GREY, WID_CF_EXPS_PRICE1), SetMinimalSize(86, 0), SetFill(0, 0),
299 NWidget(WWT_EMPTY, COLOUR_GREY, WID_CF_EXPS_PRICE2), SetMinimalSize(86, 0), SetFill(0, 0),
300 NWidget(WWT_EMPTY, COLOUR_GREY, WID_CF_EXPS_PRICE3), SetMinimalSize(86, 0), SetFill(0, 0),
301 EndContainer(),
302 EndContainer(),
303 EndContainer(),
304 NWidget(WWT_PANEL, COLOUR_GREY),
305 NWidget(NWID_HORIZONTAL), SetPadding(WD_FRAMERECT_TOP, WD_FRAMERECT_RIGHT, WD_FRAMERECT_BOTTOM, WD_FRAMERECT_LEFT),
306 NWidget(NWID_VERTICAL), // Vertical column with 'bank balance', 'loan'
307 NWidget(WWT_TEXT, COLOUR_GREY), SetDataTip(STR_FINANCES_OWN_FUNDS_TITLE, STR_NULL), SetFill(1, 0),
308 NWidget(WWT_TEXT, COLOUR_GREY), SetDataTip(STR_FINANCES_LOAN_TITLE, STR_NULL), SetFill(1, 0),
309 NWidget(NWID_SPACER), SetMinimalSize(0, 2), SetFill(1, 0),
310 NWidget(WWT_TEXT, COLOUR_GREY), SetDataTip(STR_FINANCES_BANK_BALANCE_TITLE, STR_NULL), SetFill(1, 0),
311 NWidget(NWID_SPACER), SetFill(0, 1),
312 EndContainer(),
313 NWidget(NWID_SPACER), SetFill(0, 0), SetMinimalSize(30, 0),
314 NWidget(NWID_VERTICAL), // Vertical column with bank balance amount, loan amount, and total.
315 NWidget(WWT_TEXT, COLOUR_GREY, WID_CF_OWN_VALUE), SetDataTip(STR_FINANCES_TOTAL_CURRENCY, STR_NULL), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
316 NWidget(WWT_TEXT, COLOUR_GREY, WID_CF_LOAN_VALUE), SetDataTip(STR_FINANCES_TOTAL_CURRENCY, STR_NULL), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
317 NWidget(WWT_EMPTY, COLOUR_GREY, WID_CF_BALANCE_LINE), SetMinimalSize(0, 2), SetFill(1, 0),
318 NWidget(WWT_TEXT, COLOUR_GREY, WID_CF_BALANCE_VALUE), SetDataTip(STR_FINANCES_TOTAL_CURRENCY, STR_NULL), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
319 EndContainer(),
320 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_CF_SEL_MAXLOAN),
321 NWidget(NWID_HORIZONTAL),
322 NWidget(NWID_SPACER), SetFill(0, 1), SetMinimalSize(25, 0),
323 NWidget(NWID_VERTICAL), // Max loan information
324 NWidget(WWT_TEXT, COLOUR_GREY, WID_CF_INTEREST_RATE), SetDataTip(STR_FINANCES_INTEREST_RATE, STR_NULL),
325 NWidget(WWT_TEXT, COLOUR_GREY, WID_CF_MAXLOAN_VALUE), SetDataTip(STR_FINANCES_MAX_LOAN, STR_NULL),
326 NWidget(NWID_SPACER), SetFill(0, 1),
327 EndContainer(),
328 EndContainer(),
329 EndContainer(),
330 NWidget(NWID_SPACER), SetFill(1, 1),
331 EndContainer(),
332 EndContainer(),
333 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_CF_SEL_BUTTONS),
334 NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
335 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_CF_INCREASE_LOAN), SetFill(1, 0), SetDataTip(STR_FINANCES_BORROW_BUTTON, STR_FINANCES_BORROW_TOOLTIP),
336 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_CF_REPAY_LOAN), SetFill(1, 0), SetDataTip(STR_FINANCES_REPAY_BUTTON, STR_FINANCES_REPAY_TOOLTIP),
337 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_CF_INFRASTRUCTURE), SetFill(1, 0), SetDataTip(STR_FINANCES_INFRASTRUCTURE_BUTTON, STR_COMPANY_VIEW_INFRASTRUCTURE_TOOLTIP),
338 EndContainer(),
339 EndContainer(),
342 /** Window class displaying the company finances. */
343 struct CompanyFinancesWindow : Window {
344 static Money max_money; ///< The maximum amount of money a company has had this 'run'
345 bool small; ///< Window is toggled to 'small'.
347 CompanyFinancesWindow(WindowDesc *desc, CompanyID company) : Window(desc)
349 this->small = false;
350 this->CreateNestedTree();
351 this->SetupWidgets();
352 this->FinishInitNested(company);
354 this->owner = (Owner)this->window_number;
357 void SetStringParameters(int widget) const override
359 switch (widget) {
360 case WID_CF_CAPTION:
361 SetDParam(0, (CompanyID)this->window_number);
362 SetDParam(1, (CompanyID)this->window_number);
363 break;
365 case WID_CF_BALANCE_VALUE: {
366 const Company *c = Company::Get((CompanyID)this->window_number);
367 SetDParam(0, c->money);
368 break;
371 case WID_CF_LOAN_VALUE: {
372 const Company *c = Company::Get((CompanyID)this->window_number);
373 SetDParam(0, c->current_loan);
374 break;
377 case WID_CF_OWN_VALUE: {
378 const Company *c = Company::Get((CompanyID)this->window_number);
379 SetDParam(0, c->money - c->current_loan);
380 break;
383 case WID_CF_INTEREST_RATE:
384 SetDParam(0, _settings_game.difficulty.initial_interest);
385 break;
387 case WID_CF_MAXLOAN_VALUE:
388 SetDParam(0, _economy.max_loan);
389 break;
391 case WID_CF_INCREASE_LOAN:
392 case WID_CF_REPAY_LOAN:
393 SetDParam(0, LOAN_INTERVAL);
394 break;
398 void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override
400 switch (widget) {
401 case WID_CF_EXPS_CATEGORY:
402 size->width = GetMaxCategoriesWidth();
403 size->height = GetTotalCategoriesHeight();
404 break;
406 case WID_CF_EXPS_PRICE1:
407 case WID_CF_EXPS_PRICE2:
408 case WID_CF_EXPS_PRICE3:
409 size->height = GetTotalCategoriesHeight();
410 FALLTHROUGH;
412 case WID_CF_BALANCE_VALUE:
413 case WID_CF_LOAN_VALUE:
414 case WID_CF_OWN_VALUE:
415 SetDParamMaxValue(0, CompanyFinancesWindow::max_money);
416 size->width = std::max(GetStringBoundingBox(STR_FINANCES_NEGATIVE_INCOME).width, GetStringBoundingBox(STR_FINANCES_POSITIVE_INCOME).width) + padding.width;
417 break;
419 case WID_CF_INTEREST_RATE:
420 size->height = FONT_HEIGHT_NORMAL;
421 break;
425 void DrawWidget(const Rect &r, int widget) const override
427 switch (widget) {
428 case WID_CF_EXPS_CATEGORY:
429 DrawCategories(r);
430 break;
432 case WID_CF_EXPS_PRICE1:
433 case WID_CF_EXPS_PRICE2:
434 case WID_CF_EXPS_PRICE3: {
435 const Company *c = Company::Get((CompanyID)this->window_number);
436 int age = std::min(_cur_year - c->inaugurated_year, 2);
437 int wid_offset = widget - WID_CF_EXPS_PRICE1;
438 if (wid_offset <= age) {
439 DrawYearColumn(r, _cur_year - (age - wid_offset), c->yearly_expenses + (age - wid_offset));
441 break;
444 case WID_CF_BALANCE_LINE:
445 GfxFillRect(r.left, r.top, r.right, r.top, PC_BLACK);
446 break;
451 * Setup the widgets in the nested tree, such that the finances window is displayed properly.
452 * @note After setup, the window must be (re-)initialized.
454 void SetupWidgets()
456 int plane = this->small ? SZSP_NONE : 0;
457 this->GetWidget<NWidgetStacked>(WID_CF_SEL_PANEL)->SetDisplayedPlane(plane);
458 this->GetWidget<NWidgetStacked>(WID_CF_SEL_MAXLOAN)->SetDisplayedPlane(plane);
460 CompanyID company = (CompanyID)this->window_number;
461 plane = (company != _local_company) ? SZSP_NONE : 0;
462 this->GetWidget<NWidgetStacked>(WID_CF_SEL_BUTTONS)->SetDisplayedPlane(plane);
465 void OnPaint() override
467 if (!this->IsShaded()) {
468 if (!this->small) {
469 /* Check that the expenses panel height matches the height needed for the layout. */
470 if (GetTotalCategoriesHeight() != this->GetWidget<NWidgetBase>(WID_CF_EXPS_CATEGORY)->current_y) {
471 this->SetupWidgets();
472 this->ReInit();
473 return;
477 /* Check that the loan buttons are shown only when the user owns the company. */
478 CompanyID company = (CompanyID)this->window_number;
479 int req_plane = (company != _local_company) ? SZSP_NONE : 0;
480 if (req_plane != this->GetWidget<NWidgetStacked>(WID_CF_SEL_BUTTONS)->shown_plane) {
481 this->SetupWidgets();
482 this->ReInit();
483 return;
486 const Company *c = Company::Get(company);
487 this->SetWidgetDisabledState(WID_CF_INCREASE_LOAN, c->current_loan == _economy.max_loan); // Borrow button only shows when there is any more money to loan.
488 this->SetWidgetDisabledState(WID_CF_REPAY_LOAN, company != _local_company || c->current_loan == 0); // Repay button only shows when there is any more money to repay.
491 this->DrawWidgets();
494 void OnClick(Point pt, int widget, int click_count) override
496 switch (widget) {
497 case WID_CF_TOGGLE_SIZE: // toggle size
498 this->small = !this->small;
499 this->SetupWidgets();
500 if (this->IsShaded()) {
501 /* Finances window is not resizable, so size hints given during unshading have no effect
502 * on the changed appearance of the window. */
503 this->SetShaded(false);
504 } else {
505 this->ReInit();
507 break;
509 case WID_CF_INCREASE_LOAN: // increase loan
510 Command<CMD_INCREASE_LOAN>::Post(STR_ERROR_CAN_T_BORROW_ANY_MORE_MONEY, _ctrl_pressed ? LoanCommand::Max : LoanCommand::Interval, 0);
511 break;
513 case WID_CF_REPAY_LOAN: // repay loan
514 Command<CMD_DECREASE_LOAN>::Post(STR_ERROR_CAN_T_REPAY_LOAN, _ctrl_pressed ? LoanCommand::Max : LoanCommand::Interval, 0);
515 break;
517 case WID_CF_INFRASTRUCTURE: // show infrastructure details
518 ShowCompanyInfrastructure((CompanyID)this->window_number);
519 break;
523 void OnHundredthTick() override
525 const Company *c = Company::Get((CompanyID)this->window_number);
526 if (c->money > CompanyFinancesWindow::max_money) {
527 CompanyFinancesWindow::max_money = std::max(c->money * 2, CompanyFinancesWindow::max_money * 4);
528 this->SetupWidgets();
529 this->ReInit();
534 /** First conservative estimate of the maximum amount of money */
535 Money CompanyFinancesWindow::max_money = INT32_MAX;
537 static WindowDesc _company_finances_desc(
538 WDP_AUTO, "company_finances", 0, 0,
539 WC_FINANCES, WC_NONE,
541 _nested_company_finances_widgets, lengthof(_nested_company_finances_widgets)
545 * Open the finances window of a company.
546 * @param company Company to show finances of.
547 * @pre is company a valid company.
549 void ShowCompanyFinances(CompanyID company)
551 if (!Company::IsValidID(company)) return;
552 if (BringWindowToFrontById(WC_FINANCES, company)) return;
554 new CompanyFinancesWindow(&_company_finances_desc, company);
557 /* List of colours for the livery window */
558 static const StringID _colour_dropdown[] = {
559 STR_COLOUR_DARK_BLUE,
560 STR_COLOUR_PALE_GREEN,
561 STR_COLOUR_PINK,
562 STR_COLOUR_YELLOW,
563 STR_COLOUR_RED,
564 STR_COLOUR_LIGHT_BLUE,
565 STR_COLOUR_GREEN,
566 STR_COLOUR_DARK_GREEN,
567 STR_COLOUR_BLUE,
568 STR_COLOUR_CREAM,
569 STR_COLOUR_MAUVE,
570 STR_COLOUR_PURPLE,
571 STR_COLOUR_ORANGE,
572 STR_COLOUR_BROWN,
573 STR_COLOUR_GREY,
574 STR_COLOUR_WHITE,
577 /* Association of liveries to livery classes */
578 static const LiveryClass _livery_class[LS_END] = {
579 LC_OTHER,
580 LC_RAIL, LC_RAIL, LC_RAIL, LC_RAIL, LC_RAIL, LC_RAIL, LC_RAIL, LC_RAIL, LC_RAIL, LC_RAIL, LC_RAIL, LC_RAIL, LC_RAIL,
581 LC_ROAD, LC_ROAD,
582 LC_SHIP, LC_SHIP,
583 LC_AIRCRAFT, LC_AIRCRAFT, LC_AIRCRAFT,
584 LC_ROAD, LC_ROAD,
587 class DropDownListColourItem : public DropDownListItem {
588 public:
589 DropDownListColourItem(int result, bool masked) : DropDownListItem(result, masked) {}
591 StringID String() const
593 return this->result >= COLOUR_END ? STR_COLOUR_DEFAULT : _colour_dropdown[this->result];
596 uint Height(uint width) const override
598 return std::max(FONT_HEIGHT_NORMAL, ScaleGUITrad(12) + 2);
601 bool Selectable() const override
603 return true;
606 void Draw(int left, int right, int top, int bottom, bool sel, Colours bg_colour) const override
608 bool rtl = _current_text_dir == TD_RTL;
609 int height = bottom - top;
610 int icon_y_offset = height / 2;
611 int text_y_offset = (height - FONT_HEIGHT_NORMAL) / 2 + 1;
612 DrawSprite(SPR_VEH_BUS_SIDE_VIEW, PALETTE_RECOLOUR_START + (this->result % COLOUR_END),
613 rtl ? right - 2 - ScaleGUITrad(14) : left + ScaleGUITrad(14) + 2,
614 top + icon_y_offset);
615 DrawString(rtl ? left + 2 : left + ScaleGUITrad(28) + 4,
616 rtl ? right - ScaleGUITrad(28) - 4 : right - 2,
617 top + text_y_offset, this->String(), sel ? TC_WHITE : TC_BLACK);
621 static const int LEVEL_WIDTH = 10; ///< Indenting width of a sub-group in pixels
623 typedef GUIList<const Group*> GUIGroupList;
625 /** Company livery colour scheme window. */
626 struct SelectCompanyLiveryWindow : public Window {
627 private:
628 uint32 sel;
629 LiveryClass livery_class;
630 Dimension square;
631 uint rows;
632 uint line_height;
633 GUIGroupList groups;
634 std::vector<int> indents;
635 Scrollbar *vscroll;
637 void ShowColourDropDownMenu(uint32 widget)
639 uint32 used_colours = 0;
640 const Company *c;
641 const Livery *livery, *default_livery = nullptr;
642 bool primary = widget == WID_SCL_PRI_COL_DROPDOWN;
643 byte default_col;
645 /* Disallow other company colours for the primary colour */
646 if (this->livery_class < LC_GROUP_RAIL && HasBit(this->sel, LS_DEFAULT) && primary) {
647 for (const Company *c : Company::Iterate()) {
648 if (c->index != _local_company) SetBit(used_colours, c->colour);
652 c = Company::Get((CompanyID)this->window_number);
654 if (this->livery_class < LC_GROUP_RAIL) {
655 /* Get the first selected livery to use as the default dropdown item */
656 LiveryScheme scheme;
657 for (scheme = LS_BEGIN; scheme < LS_END; scheme++) {
658 if (HasBit(this->sel, scheme)) break;
660 if (scheme == LS_END) scheme = LS_DEFAULT;
661 livery = &c->livery[scheme];
662 if (scheme != LS_DEFAULT) default_livery = &c->livery[LS_DEFAULT];
663 } else {
664 const Group *g = Group::Get(this->sel);
665 livery = &g->livery;
666 if (g->parent == INVALID_GROUP) {
667 default_livery = &c->livery[LS_DEFAULT];
668 } else {
669 const Group *pg = Group::Get(g->parent);
670 default_livery = &pg->livery;
674 DropDownList list;
675 if (default_livery != nullptr) {
676 /* Add COLOUR_END to put the colour out of range, but also allow us to show what the default is */
677 default_col = (primary ? default_livery->colour1 : default_livery->colour2) + COLOUR_END;
678 list.emplace_back(new DropDownListColourItem(default_col, false));
680 for (uint i = 0; i < lengthof(_colour_dropdown); i++) {
681 list.emplace_back(new DropDownListColourItem(i, HasBit(used_colours, i)));
684 byte sel = (default_livery == nullptr || HasBit(livery->in_use, primary ? 0 : 1)) ? (primary ? livery->colour1 : livery->colour2) : default_col;
685 ShowDropDownList(this, std::move(list), sel, widget);
688 void AddChildren(GUIGroupList *source, GroupID parent, int indent)
690 for (const Group *g : *source) {
691 if (g->parent != parent) continue;
692 this->groups.push_back(g);
693 this->indents.push_back(indent);
694 AddChildren(source, g->index, indent + 1);
698 void BuildGroupList(CompanyID owner)
700 if (!this->groups.NeedRebuild()) return;
702 this->groups.clear();
703 this->indents.clear();
705 if (this->livery_class >= LC_GROUP_RAIL) {
706 GUIGroupList list;
707 VehicleType vtype = (VehicleType)(this->livery_class - LC_GROUP_RAIL);
709 for (const Group *g : Group::Iterate()) {
710 if (g->owner == owner && g->vehicle_type == vtype) {
711 list.push_back(g);
715 list.ForceResort();
717 /* Sort the groups by their name */
718 const Group *last_group[2] = { nullptr, nullptr };
719 char last_name[2][64] = { "", "" };
720 list.Sort([&](const Group * const &a, const Group * const &b) -> bool {
721 if (a != last_group[0]) {
722 last_group[0] = a;
723 SetDParam(0, a->index);
724 GetString(last_name[0], STR_GROUP_NAME, lastof(last_name[0]));
727 if (b != last_group[1]) {
728 last_group[1] = b;
729 SetDParam(0, b->index);
730 GetString(last_name[1], STR_GROUP_NAME, lastof(last_name[1]));
733 int r = strnatcmp(last_name[0], last_name[1]); // Sort by name (natural sorting).
734 if (r == 0) return a->index < b->index;
735 return r < 0;
738 AddChildren(&list, INVALID_GROUP, 0);
741 this->groups.shrink_to_fit();
742 this->groups.RebuildDone();
745 void SetRows()
747 if (this->livery_class < LC_GROUP_RAIL) {
748 this->rows = 0;
749 for (LiveryScheme scheme = LS_DEFAULT; scheme < LS_END; scheme++) {
750 if (_livery_class[scheme] == this->livery_class && HasBit(_loaded_newgrf_features.used_liveries, scheme)) {
751 this->rows++;
754 } else {
755 this->rows = (uint)this->groups.size();
758 this->vscroll->SetCount(this->rows);
761 public:
762 SelectCompanyLiveryWindow(WindowDesc *desc, CompanyID company, GroupID group) : Window(desc)
764 this->CreateNestedTree();
765 this->vscroll = this->GetScrollbar(WID_SCL_MATRIX_SCROLLBAR);
767 if (group == INVALID_GROUP) {
768 this->livery_class = LC_OTHER;
769 this->sel = 1;
770 this->LowerWidget(WID_SCL_CLASS_GENERAL);
771 this->BuildGroupList(company);
772 this->SetRows();
773 } else {
774 this->SetSelectedGroup(company, group);
777 this->FinishInitNested(company);
778 this->owner = company;
779 this->InvalidateData(1);
782 void SetSelectedGroup(CompanyID company, GroupID group)
784 this->RaiseWidget(this->livery_class + WID_SCL_CLASS_GENERAL);
785 const Group *g = Group::Get(group);
786 switch (g->vehicle_type) {
787 case VEH_TRAIN: this->livery_class = LC_GROUP_RAIL; break;
788 case VEH_ROAD: this->livery_class = LC_GROUP_ROAD; break;
789 case VEH_SHIP: this->livery_class = LC_GROUP_SHIP; break;
790 case VEH_AIRCRAFT: this->livery_class = LC_GROUP_AIRCRAFT; break;
791 default: NOT_REACHED();
793 this->sel = group;
794 this->LowerWidget(this->livery_class + WID_SCL_CLASS_GENERAL);
796 this->groups.ForceRebuild();
797 this->BuildGroupList(company);
798 this->SetRows();
800 /* Position scrollbar to selected group */
801 for (uint i = 0; i < this->rows; i++) {
802 if (this->groups[i]->index == sel) {
803 this->vscroll->SetPosition(Clamp(i - this->vscroll->GetCapacity() / 2, 0, std::max(this->vscroll->GetCount() - this->vscroll->GetCapacity(), 0)));
804 break;
809 void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override
811 switch (widget) {
812 case WID_SCL_SPACER_DROPDOWN: {
813 /* The matrix widget below needs enough room to print all the schemes. */
814 Dimension d = {0, 0};
815 for (LiveryScheme scheme = LS_DEFAULT; scheme < LS_END; scheme++) {
816 d = maxdim(d, GetStringBoundingBox(STR_LIVERY_DEFAULT + scheme));
819 /* And group names */
820 for (const Group *g : Group::Iterate()) {
821 if (g->owner == (CompanyID)this->window_number) {
822 SetDParam(0, g->index);
823 d = maxdim(d, GetStringBoundingBox(STR_GROUP_NAME));
827 size->width = std::max(size->width, 5 + d.width + WD_FRAMERECT_RIGHT);
828 break;
831 case WID_SCL_MATRIX: {
832 /* 11 items in the default rail class */
833 this->square = GetSpriteSize(SPR_SQUARE);
834 this->line_height = std::max(this->square.height, (uint)FONT_HEIGHT_NORMAL) + 4;
836 size->height = 11 * this->line_height;
837 resize->width = 1;
838 resize->height = this->line_height;
839 break;
842 case WID_SCL_SEC_COL_DROPDOWN:
843 if (!_loaded_newgrf_features.has_2CC) {
844 size->width = 0;
845 break;
847 FALLTHROUGH;
849 case WID_SCL_PRI_COL_DROPDOWN: {
850 this->square = GetSpriteSize(SPR_SQUARE);
851 int string_padding = this->square.width + NWidgetScrollbar::GetVerticalDimension().width + 10;
852 for (const StringID *id = _colour_dropdown; id != endof(_colour_dropdown); id++) {
853 size->width = std::max(size->width, GetStringBoundingBox(*id).width + string_padding);
855 size->width = std::max(size->width, GetStringBoundingBox(STR_COLOUR_DEFAULT).width + string_padding);
856 break;
861 void OnPaint() override
863 bool local = (CompanyID)this->window_number == _local_company;
865 /* Disable dropdown controls if no scheme is selected */
866 bool disabled = this->livery_class < LC_GROUP_RAIL ? (this->sel == 0) : (this->sel == INVALID_GROUP);
867 this->SetWidgetDisabledState(WID_SCL_PRI_COL_DROPDOWN, !local || disabled);
868 this->SetWidgetDisabledState(WID_SCL_SEC_COL_DROPDOWN, !local || disabled);
870 this->BuildGroupList((CompanyID)this->window_number);
872 this->DrawWidgets();
875 void SetStringParameters(int widget) const override
877 switch (widget) {
878 case WID_SCL_CAPTION:
879 SetDParam(0, (CompanyID)this->window_number);
880 break;
882 case WID_SCL_PRI_COL_DROPDOWN:
883 case WID_SCL_SEC_COL_DROPDOWN: {
884 const Company *c = Company::Get((CompanyID)this->window_number);
885 bool primary = widget == WID_SCL_PRI_COL_DROPDOWN;
886 StringID colour = STR_COLOUR_DEFAULT;
888 if (this->livery_class < LC_GROUP_RAIL) {
889 if (this->sel != 0) {
890 LiveryScheme scheme = LS_DEFAULT;
891 for (scheme = LS_BEGIN; scheme < LS_END; scheme++) {
892 if (HasBit(this->sel, scheme)) break;
894 if (scheme == LS_END) scheme = LS_DEFAULT;
895 const Livery *livery = &c->livery[scheme];
896 if (scheme == LS_DEFAULT || HasBit(livery->in_use, primary ? 0 : 1)) {
897 colour = STR_COLOUR_DARK_BLUE + (primary ? livery->colour1 : livery->colour2);
900 } else {
901 if (this->sel != INVALID_GROUP) {
902 const Group *g = Group::Get(this->sel);
903 const Livery *livery = &g->livery;
904 if (HasBit(livery->in_use, primary ? 0 : 1)) {
905 colour = STR_COLOUR_DARK_BLUE + (primary ? livery->colour1 : livery->colour2);
909 SetDParam(0, colour);
910 break;
915 void DrawWidget(const Rect &r, int widget) const override
917 if (widget != WID_SCL_MATRIX) return;
919 bool rtl = _current_text_dir == TD_RTL;
921 /* Horizontal coordinates of scheme name column. */
922 const NWidgetBase *nwi = this->GetWidget<NWidgetBase>(WID_SCL_SPACER_DROPDOWN);
923 int sch_left = nwi->pos_x;
924 int sch_right = sch_left + nwi->current_x - 1;
925 /* Horizontal coordinates of first dropdown. */
926 nwi = this->GetWidget<NWidgetBase>(WID_SCL_PRI_COL_DROPDOWN);
927 int pri_left = nwi->pos_x;
928 int pri_right = pri_left + nwi->current_x - 1;
929 /* Horizontal coordinates of second dropdown. */
930 nwi = this->GetWidget<NWidgetBase>(WID_SCL_SEC_COL_DROPDOWN);
931 int sec_left = nwi->pos_x;
932 int sec_right = sec_left + nwi->current_x - 1;
934 int text_left = (rtl ? (uint)WD_FRAMERECT_LEFT : (this->square.width + 5));
935 int text_right = (rtl ? (this->square.width + 5) : (uint)WD_FRAMERECT_RIGHT);
937 int square_offs = (this->line_height - this->square.height) / 2 + 1;
938 int text_offs = (this->line_height - FONT_HEIGHT_NORMAL) / 2 + 1;
940 int y = r.top;
942 /* Helper function to draw livery info. */
943 auto draw_livery = [&](StringID str, const Livery &liv, bool sel, bool def, int indent) {
944 /* Livery Label. */
945 DrawString(sch_left + WD_FRAMERECT_LEFT + (rtl ? 0 : indent), sch_right - WD_FRAMERECT_RIGHT - (rtl ? indent : 0), y + text_offs, str, sel ? TC_WHITE : TC_BLACK);
947 /* Text below the first dropdown. */
948 DrawSprite(SPR_SQUARE, GENERAL_SPRITE_COLOUR(liv.colour1), (rtl ? pri_right - (this->square.width + 5) + WD_FRAMERECT_RIGHT : pri_left) + WD_FRAMERECT_LEFT, y + square_offs);
949 DrawString(pri_left + text_left, pri_right - text_right, y + text_offs, (def || HasBit(liv.in_use, 0)) ? STR_COLOUR_DARK_BLUE + liv.colour1 : STR_COLOUR_DEFAULT, sel ? TC_WHITE : TC_GOLD);
951 /* Text below the second dropdown. */
952 if (sec_right > sec_left) { // Second dropdown has non-zero size.
953 DrawSprite(SPR_SQUARE, GENERAL_SPRITE_COLOUR(liv.colour2), (rtl ? sec_right - (this->square.width + 5) + WD_FRAMERECT_RIGHT : sec_left) + WD_FRAMERECT_LEFT, y + square_offs);
954 DrawString(sec_left + text_left, sec_right - text_right, y + text_offs, (def || HasBit(liv.in_use, 1)) ? STR_COLOUR_DARK_BLUE + liv.colour2 : STR_COLOUR_DEFAULT, sel ? TC_WHITE : TC_GOLD);
957 y += this->line_height;
960 if (livery_class < LC_GROUP_RAIL) {
961 int pos = this->vscroll->GetPosition();
962 const Company *c = Company::Get((CompanyID)this->window_number);
963 for (LiveryScheme scheme = LS_DEFAULT; scheme < LS_END; scheme++) {
964 if (_livery_class[scheme] == this->livery_class && HasBit(_loaded_newgrf_features.used_liveries, scheme)) {
965 if (pos-- > 0) continue;
966 draw_livery(STR_LIVERY_DEFAULT + scheme, c->livery[scheme], HasBit(this->sel, scheme), scheme == LS_DEFAULT, 0);
969 } else {
970 uint max = static_cast<uint>(std::min<size_t>(this->vscroll->GetPosition() + this->vscroll->GetCapacity(), this->groups.size()));
971 for (uint i = this->vscroll->GetPosition(); i < max; ++i) {
972 const Group *g = this->groups[i];
973 SetDParam(0, g->index);
974 draw_livery(STR_GROUP_NAME, g->livery, this->sel == g->index, false, this->indents[i] * LEVEL_WIDTH);
979 void OnClick(Point pt, int widget, int click_count) override
981 switch (widget) {
982 /* Livery Class buttons */
983 case WID_SCL_CLASS_GENERAL:
984 case WID_SCL_CLASS_RAIL:
985 case WID_SCL_CLASS_ROAD:
986 case WID_SCL_CLASS_SHIP:
987 case WID_SCL_CLASS_AIRCRAFT:
988 case WID_SCL_GROUPS_RAIL:
989 case WID_SCL_GROUPS_ROAD:
990 case WID_SCL_GROUPS_SHIP:
991 case WID_SCL_GROUPS_AIRCRAFT:
992 this->RaiseWidget(this->livery_class + WID_SCL_CLASS_GENERAL);
993 this->livery_class = (LiveryClass)(widget - WID_SCL_CLASS_GENERAL);
994 this->LowerWidget(this->livery_class + WID_SCL_CLASS_GENERAL);
996 /* Select the first item in the list */
997 if (this->livery_class < LC_GROUP_RAIL) {
998 this->sel = 0;
999 for (LiveryScheme scheme = LS_DEFAULT; scheme < LS_END; scheme++) {
1000 if (_livery_class[scheme] == this->livery_class && HasBit(_loaded_newgrf_features.used_liveries, scheme)) {
1001 this->sel = 1 << scheme;
1002 break;
1005 } else {
1006 this->sel = INVALID_GROUP;
1007 this->groups.ForceRebuild();
1008 this->BuildGroupList((CompanyID)this->window_number);
1010 if (this->groups.size() > 0) {
1011 this->sel = this->groups[0]->index;
1015 this->SetRows();
1016 this->SetDirty();
1017 break;
1019 case WID_SCL_PRI_COL_DROPDOWN: // First colour dropdown
1020 ShowColourDropDownMenu(WID_SCL_PRI_COL_DROPDOWN);
1021 break;
1023 case WID_SCL_SEC_COL_DROPDOWN: // Second colour dropdown
1024 ShowColourDropDownMenu(WID_SCL_SEC_COL_DROPDOWN);
1025 break;
1027 case WID_SCL_MATRIX: {
1028 uint row = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_SCL_MATRIX);
1029 if (row >= this->rows) return;
1031 if (this->livery_class < LC_GROUP_RAIL) {
1032 LiveryScheme j = (LiveryScheme)row;
1034 for (LiveryScheme scheme = LS_BEGIN; scheme <= j && scheme < LS_END; scheme++) {
1035 if (_livery_class[scheme] != this->livery_class || !HasBit(_loaded_newgrf_features.used_liveries, scheme)) j++;
1037 assert(j < LS_END);
1039 if (_ctrl_pressed) {
1040 ToggleBit(this->sel, j);
1041 } else {
1042 this->sel = 1 << j;
1044 } else {
1045 this->sel = this->groups[row]->index;
1047 this->SetDirty();
1048 break;
1053 void OnResize() override
1055 this->vscroll->SetCapacityFromWidget(this, WID_SCL_MATRIX);
1058 void OnDropdownSelect(int widget, int index) override
1060 bool local = (CompanyID)this->window_number == _local_company;
1061 if (!local) return;
1063 if (index >= COLOUR_END) index = INVALID_COLOUR;
1065 if (this->livery_class < LC_GROUP_RAIL) {
1066 /* Set company colour livery */
1067 for (LiveryScheme scheme = LS_DEFAULT; scheme < LS_END; scheme++) {
1068 /* Changed colour for the selected scheme, or all visible schemes if CTRL is pressed. */
1069 if (HasBit(this->sel, scheme) || (_ctrl_pressed && _livery_class[scheme] == this->livery_class && HasBit(_loaded_newgrf_features.used_liveries, scheme))) {
1070 Command<CMD_SET_COMPANY_COLOUR>::Post(scheme, widget == WID_SCL_PRI_COL_DROPDOWN, (Colours)index);
1073 } else {
1074 /* Setting group livery */
1075 Command<CMD_SET_GROUP_LIVERY>::Post(this->sel, widget == WID_SCL_PRI_COL_DROPDOWN, (Colours)index);
1080 * Some data on this window has become invalid.
1081 * @param data Information about the changed data.
1082 * @param gui_scope Whether the call is done from GUI scope. You may not do everything when not in GUI scope. See #InvalidateWindowData() for details.
1084 void OnInvalidateData(int data = 0, bool gui_scope = true) override
1086 if (!gui_scope) return;
1088 if (data != -1) {
1089 /* data contains a VehicleType, rebuild list if it displayed */
1090 if (this->livery_class == data + LC_GROUP_RAIL) {
1091 this->groups.ForceRebuild();
1092 this->BuildGroupList((CompanyID)this->window_number);
1093 this->SetRows();
1095 if (!Group::IsValidID(this->sel)) {
1096 this->sel = INVALID_GROUP;
1097 if (this->groups.size() > 0) this->sel = this->groups[0]->index;
1100 this->SetDirty();
1102 return;
1105 this->SetWidgetsDisabledState(true, WID_SCL_CLASS_RAIL, WID_SCL_CLASS_ROAD, WID_SCL_CLASS_SHIP, WID_SCL_CLASS_AIRCRAFT, WIDGET_LIST_END);
1107 bool current_class_valid = this->livery_class == LC_OTHER || this->livery_class >= LC_GROUP_RAIL;
1108 if (_settings_client.gui.liveries == LIT_ALL || (_settings_client.gui.liveries == LIT_COMPANY && this->window_number == _local_company)) {
1109 for (LiveryScheme scheme = LS_DEFAULT; scheme < LS_END; scheme++) {
1110 if (HasBit(_loaded_newgrf_features.used_liveries, scheme)) {
1111 if (_livery_class[scheme] == this->livery_class) current_class_valid = true;
1112 this->EnableWidget(WID_SCL_CLASS_GENERAL + _livery_class[scheme]);
1113 } else if (this->livery_class < LC_GROUP_RAIL) {
1114 ClrBit(this->sel, scheme);
1119 if (!current_class_valid) {
1120 Point pt = {0, 0};
1121 this->OnClick(pt, WID_SCL_CLASS_GENERAL, 1);
1126 static const NWidgetPart _nested_select_company_livery_widgets [] = {
1127 NWidget(NWID_HORIZONTAL),
1128 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
1129 NWidget(WWT_CAPTION, COLOUR_GREY, WID_SCL_CAPTION), SetDataTip(STR_LIVERY_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
1130 EndContainer(),
1131 NWidget(NWID_HORIZONTAL),
1132 NWidget(WWT_IMGBTN, COLOUR_GREY, WID_SCL_CLASS_GENERAL), SetMinimalSize(22, 22), SetFill(0, 1), SetDataTip(SPR_IMG_COMPANY_GENERAL, STR_LIVERY_GENERAL_TOOLTIP),
1133 NWidget(WWT_IMGBTN, COLOUR_GREY, WID_SCL_CLASS_RAIL), SetMinimalSize(22, 22), SetFill(0, 1), SetDataTip(SPR_IMG_TRAINLIST, STR_LIVERY_TRAIN_TOOLTIP),
1134 NWidget(WWT_IMGBTN, COLOUR_GREY, WID_SCL_CLASS_ROAD), SetMinimalSize(22, 22), SetFill(0, 1), SetDataTip(SPR_IMG_TRUCKLIST, STR_LIVERY_ROAD_VEHICLE_TOOLTIP),
1135 NWidget(WWT_IMGBTN, COLOUR_GREY, WID_SCL_CLASS_SHIP), SetMinimalSize(22, 22), SetFill(0, 1), SetDataTip(SPR_IMG_SHIPLIST, STR_LIVERY_SHIP_TOOLTIP),
1136 NWidget(WWT_IMGBTN, COLOUR_GREY, WID_SCL_CLASS_AIRCRAFT), SetMinimalSize(22, 22), SetFill(0, 1), SetDataTip(SPR_IMG_AIRPLANESLIST, STR_LIVERY_AIRCRAFT_TOOLTIP),
1137 NWidget(WWT_IMGBTN, COLOUR_GREY, WID_SCL_GROUPS_RAIL), SetMinimalSize(22, 22), SetFill(0, 1), SetDataTip(SPR_GROUP_LIVERY_TRAIN, STR_LIVERY_TRAIN_TOOLTIP),
1138 NWidget(WWT_IMGBTN, COLOUR_GREY, WID_SCL_GROUPS_ROAD), SetMinimalSize(22, 22), SetFill(0, 1), SetDataTip(SPR_GROUP_LIVERY_ROADVEH, STR_LIVERY_ROAD_VEHICLE_TOOLTIP),
1139 NWidget(WWT_IMGBTN, COLOUR_GREY, WID_SCL_GROUPS_SHIP), SetMinimalSize(22, 22), SetFill(0, 1), SetDataTip(SPR_GROUP_LIVERY_SHIP, STR_LIVERY_SHIP_TOOLTIP),
1140 NWidget(WWT_IMGBTN, COLOUR_GREY, WID_SCL_GROUPS_AIRCRAFT), SetMinimalSize(22, 22), SetFill(0, 1), SetDataTip(SPR_GROUP_LIVERY_AIRCRAFT, STR_LIVERY_AIRCRAFT_TOOLTIP),
1141 NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(90, 22), SetFill(1, 1), EndContainer(),
1142 EndContainer(),
1143 NWidget(NWID_HORIZONTAL),
1144 NWidget(WWT_PANEL, COLOUR_GREY, WID_SCL_SPACER_DROPDOWN), SetMinimalSize(150, 12), SetFill(1, 1), EndContainer(),
1145 NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_SCL_PRI_COL_DROPDOWN), SetMinimalSize(125, 12), SetFill(0, 1), SetDataTip(STR_BLACK_STRING, STR_LIVERY_PRIMARY_TOOLTIP),
1146 NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_SCL_SEC_COL_DROPDOWN), SetMinimalSize(125, 12), SetFill(0, 1),
1147 SetDataTip(STR_BLACK_STRING, STR_LIVERY_SECONDARY_TOOLTIP),
1148 EndContainer(),
1149 NWidget(NWID_HORIZONTAL),
1150 NWidget(WWT_MATRIX, COLOUR_GREY, WID_SCL_MATRIX), SetMinimalSize(275, 0), SetResize(1, 0), SetFill(1, 1), SetMatrixDataTip(1, 0, STR_LIVERY_PANEL_TOOLTIP), SetScrollbar(WID_SCL_MATRIX_SCROLLBAR),
1151 NWidget(NWID_VERTICAL),
1152 NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_SCL_MATRIX_SCROLLBAR),
1153 NWidget(WWT_RESIZEBOX, COLOUR_GREY),
1154 EndContainer(),
1155 EndContainer(),
1158 static WindowDesc _select_company_livery_desc(
1159 WDP_AUTO, "company_livery", 0, 0,
1160 WC_COMPANY_COLOUR, WC_NONE,
1162 _nested_select_company_livery_widgets, lengthof(_nested_select_company_livery_widgets)
1165 void ShowCompanyLiveryWindow(CompanyID company, GroupID group)
1167 SelectCompanyLiveryWindow *w = (SelectCompanyLiveryWindow *)BringWindowToFrontById(WC_COMPANY_COLOUR, company);
1168 if (w == nullptr) {
1169 new SelectCompanyLiveryWindow(&_select_company_livery_desc, company, group);
1170 } else if (group != INVALID_GROUP) {
1171 w->SetSelectedGroup(company, group);
1176 * Draws the face of a company manager's face.
1177 * @param cmf the company manager's face
1178 * @param colour the (background) colour of the gradient
1179 * @param x x-position to draw the face
1180 * @param y y-position to draw the face
1182 void DrawCompanyManagerFace(CompanyManagerFace cmf, int colour, int x, int y)
1184 GenderEthnicity ge = (GenderEthnicity)GetCompanyManagerFaceBits(cmf, CMFV_GEN_ETHN, GE_WM);
1186 bool has_moustache = !HasBit(ge, GENDER_FEMALE) && GetCompanyManagerFaceBits(cmf, CMFV_HAS_MOUSTACHE, ge) != 0;
1187 bool has_tie_earring = !HasBit(ge, GENDER_FEMALE) || GetCompanyManagerFaceBits(cmf, CMFV_HAS_TIE_EARRING, ge) != 0;
1188 bool has_glasses = GetCompanyManagerFaceBits(cmf, CMFV_HAS_GLASSES, ge) != 0;
1189 PaletteID pal;
1191 /* Modify eye colour palette only if 2 or more valid values exist */
1192 if (_cmf_info[CMFV_EYE_COLOUR].valid_values[ge] < 2) {
1193 pal = PAL_NONE;
1194 } else {
1195 switch (GetCompanyManagerFaceBits(cmf, CMFV_EYE_COLOUR, ge)) {
1196 default: NOT_REACHED();
1197 case 0: pal = PALETTE_TO_BROWN; break;
1198 case 1: pal = PALETTE_TO_BLUE; break;
1199 case 2: pal = PALETTE_TO_GREEN; break;
1203 /* Draw the gradient (background) */
1204 DrawSprite(SPR_GRADIENT, GENERAL_SPRITE_COLOUR(colour), x, y);
1206 for (CompanyManagerFaceVariable cmfv = CMFV_CHEEKS; cmfv < CMFV_END; cmfv++) {
1207 switch (cmfv) {
1208 case CMFV_MOUSTACHE: if (!has_moustache) continue; break;
1209 case CMFV_LIPS:
1210 case CMFV_NOSE: if (has_moustache) continue; break;
1211 case CMFV_TIE_EARRING: if (!has_tie_earring) continue; break;
1212 case CMFV_GLASSES: if (!has_glasses) continue; break;
1213 default: break;
1215 DrawSprite(GetCompanyManagerFaceSprite(cmf, cmfv, ge), (cmfv == CMFV_EYEBROWS) ? pal : PAL_NONE, x, y);
1219 /** Nested widget description for the company manager face selection dialog */
1220 static const NWidgetPart _nested_select_company_manager_face_widgets[] = {
1221 NWidget(NWID_HORIZONTAL),
1222 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
1223 NWidget(WWT_CAPTION, COLOUR_GREY, WID_SCMF_CAPTION), SetDataTip(STR_FACE_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
1224 NWidget(WWT_IMGBTN, COLOUR_GREY, WID_SCMF_TOGGLE_LARGE_SMALL), SetDataTip(SPR_LARGE_SMALL_WINDOW, STR_FACE_ADVANCED_TOOLTIP),
1225 EndContainer(),
1226 NWidget(WWT_PANEL, COLOUR_GREY, WID_SCMF_SELECT_FACE),
1227 NWidget(NWID_SPACER), SetMinimalSize(0, 2),
1228 NWidget(NWID_HORIZONTAL), SetPIP(2, 2, 2),
1229 NWidget(NWID_VERTICAL),
1230 NWidget(NWID_HORIZONTAL),
1231 NWidget(NWID_SPACER), SetFill(1, 0),
1232 NWidget(WWT_EMPTY, COLOUR_GREY, WID_SCMF_FACE), SetMinimalSize(92, 119),
1233 NWidget(NWID_SPACER), SetFill(1, 0),
1234 EndContainer(),
1235 NWidget(NWID_SPACER), SetMinimalSize(0, 2),
1236 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_RANDOM_NEW_FACE), SetFill(1, 0), SetDataTip(STR_FACE_NEW_FACE_BUTTON, STR_FACE_NEW_FACE_TOOLTIP),
1237 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_SCMF_SEL_LOADSAVE), // Load/number/save buttons under the portrait in the advanced view.
1238 NWidget(NWID_VERTICAL),
1239 NWidget(NWID_SPACER), SetMinimalSize(0, 5), SetFill(0, 1),
1240 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_LOAD), SetFill(1, 0), SetDataTip(STR_FACE_LOAD, STR_FACE_LOAD_TOOLTIP),
1241 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_FACECODE), SetFill(1, 0), SetDataTip(STR_FACE_FACECODE, STR_FACE_FACECODE_TOOLTIP),
1242 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_SAVE), SetFill(1, 0), SetDataTip(STR_FACE_SAVE, STR_FACE_SAVE_TOOLTIP),
1243 NWidget(NWID_SPACER), SetMinimalSize(0, 5), SetFill(0, 1),
1244 EndContainer(),
1245 EndContainer(),
1246 EndContainer(),
1247 NWidget(NWID_VERTICAL),
1248 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_TOGGLE_LARGE_SMALL_BUTTON), SetFill(1, 0), SetDataTip(STR_FACE_ADVANCED, STR_FACE_ADVANCED_TOOLTIP),
1249 NWidget(NWID_SPACER), SetMinimalSize(0, 2),
1250 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_SCMF_SEL_MALEFEMALE), // Simple male/female face setting.
1251 NWidget(NWID_VERTICAL),
1252 NWidget(NWID_SPACER), SetFill(0, 1),
1253 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_SCMF_MALE), SetFill(1, 0), SetDataTip(STR_FACE_MALE_BUTTON, STR_FACE_MALE_TOOLTIP),
1254 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_SCMF_FEMALE), SetFill(1, 0), SetDataTip(STR_FACE_FEMALE_BUTTON, STR_FACE_FEMALE_TOOLTIP),
1255 NWidget(NWID_SPACER), SetFill(0, 1),
1256 EndContainer(),
1257 EndContainer(),
1258 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_SCMF_SEL_PARTS), // Advanced face parts setting.
1259 NWidget(NWID_VERTICAL),
1260 NWidget(NWID_SPACER), SetMinimalSize(0, 2),
1261 NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
1262 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_SCMF_MALE2), SetFill(1, 0), SetDataTip(STR_FACE_MALE_BUTTON, STR_FACE_MALE_TOOLTIP),
1263 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_SCMF_FEMALE2), SetFill(1, 0), SetDataTip(STR_FACE_FEMALE_BUTTON, STR_FACE_FEMALE_TOOLTIP),
1264 EndContainer(),
1265 NWidget(NWID_SPACER), SetMinimalSize(0, 2),
1266 NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
1267 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_SCMF_ETHNICITY_EUR), SetFill(1, 0), SetDataTip(STR_FACE_EUROPEAN, STR_FACE_SELECT_EUROPEAN),
1268 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_SCMF_ETHNICITY_AFR), SetFill(1, 0), SetDataTip(STR_FACE_AFRICAN, STR_FACE_SELECT_AFRICAN),
1269 EndContainer(),
1270 NWidget(NWID_SPACER), SetMinimalSize(0, 4),
1271 NWidget(NWID_HORIZONTAL),
1272 NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_HAS_MOUSTACHE_EARRING_TEXT), SetFill(1, 0), SetPadding(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT),
1273 SetDataTip(STR_FACE_EYECOLOUR, STR_NULL), SetTextColour(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
1274 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_HAS_MOUSTACHE_EARRING), SetDataTip(STR_WHITE_STRING, STR_FACE_MOUSTACHE_EARRING_TOOLTIP),
1275 EndContainer(),
1276 NWidget(NWID_HORIZONTAL),
1277 NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_HAS_GLASSES_TEXT), SetFill(1, 0), SetPadding(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT),
1278 SetDataTip(STR_FACE_GLASSES, STR_NULL), SetTextColour(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
1279 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_HAS_GLASSES), SetDataTip(STR_WHITE_STRING, STR_FACE_GLASSES_TOOLTIP),
1280 EndContainer(),
1281 NWidget(NWID_SPACER), SetMinimalSize(0, 2), SetFill(1, 0),
1282 NWidget(NWID_HORIZONTAL),
1283 NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_HAIR_TEXT), SetFill(1, 0), SetPadding(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT),
1284 SetDataTip(STR_FACE_HAIR, STR_NULL), SetTextColour(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
1285 NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_HAIR_L), SetDataTip(AWV_DECREASE, STR_FACE_HAIR_TOOLTIP),
1286 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_HAIR), SetDataTip(STR_WHITE_STRING, STR_FACE_HAIR_TOOLTIP),
1287 NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_HAIR_R), SetDataTip(AWV_INCREASE, STR_FACE_HAIR_TOOLTIP),
1288 EndContainer(),
1289 NWidget(NWID_HORIZONTAL),
1290 NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_EYEBROWS_TEXT), SetFill(1, 0), SetPadding(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT),
1291 SetDataTip(STR_FACE_EYEBROWS, STR_NULL), SetTextColour(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
1292 NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_EYEBROWS_L), SetDataTip(AWV_DECREASE, STR_FACE_EYEBROWS_TOOLTIP),
1293 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_EYEBROWS), SetDataTip(STR_WHITE_STRING, STR_FACE_EYEBROWS_TOOLTIP),
1294 NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_EYEBROWS_R), SetDataTip(AWV_INCREASE, STR_FACE_EYEBROWS_TOOLTIP),
1295 EndContainer(),
1296 NWidget(NWID_HORIZONTAL),
1297 NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_EYECOLOUR_TEXT), SetFill(1, 0), SetPadding(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT),
1298 SetDataTip(STR_FACE_EYECOLOUR, STR_NULL), SetTextColour(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
1299 NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_EYECOLOUR_L), SetDataTip(AWV_DECREASE, STR_FACE_EYECOLOUR_TOOLTIP),
1300 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_EYECOLOUR), SetDataTip(STR_WHITE_STRING, STR_FACE_EYECOLOUR_TOOLTIP),
1301 NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_EYECOLOUR_R), SetDataTip(AWV_INCREASE, STR_FACE_EYECOLOUR_TOOLTIP),
1302 EndContainer(),
1303 NWidget(NWID_HORIZONTAL),
1304 NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_GLASSES_TEXT), SetFill(1, 0), SetPadding(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT),
1305 SetDataTip(STR_FACE_GLASSES, STR_NULL), SetTextColour(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
1306 NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_GLASSES_L), SetDataTip(AWV_DECREASE, STR_FACE_GLASSES_TOOLTIP_2),
1307 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_GLASSES), SetDataTip(STR_WHITE_STRING, STR_FACE_GLASSES_TOOLTIP_2),
1308 NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_GLASSES_R), SetDataTip(AWV_INCREASE, STR_FACE_GLASSES_TOOLTIP_2),
1309 EndContainer(),
1310 NWidget(NWID_HORIZONTAL),
1311 NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_NOSE_TEXT), SetFill(1, 0), SetPadding(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT),
1312 SetDataTip(STR_FACE_NOSE, STR_NULL), SetTextColour(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
1313 NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_NOSE_L), SetDataTip(AWV_DECREASE, STR_FACE_NOSE_TOOLTIP),
1314 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_NOSE), SetDataTip(STR_WHITE_STRING, STR_FACE_NOSE_TOOLTIP),
1315 NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_NOSE_R), SetDataTip(AWV_INCREASE, STR_FACE_NOSE_TOOLTIP),
1316 EndContainer(),
1317 NWidget(NWID_HORIZONTAL),
1318 NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_LIPS_MOUSTACHE_TEXT), SetFill(1, 0), SetPadding(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT),
1319 SetDataTip(STR_FACE_MOUSTACHE, STR_NULL), SetTextColour(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
1320 NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_LIPS_MOUSTACHE_L), SetDataTip(AWV_DECREASE, STR_FACE_LIPS_MOUSTACHE_TOOLTIP),
1321 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_LIPS_MOUSTACHE), SetDataTip(STR_WHITE_STRING, STR_FACE_LIPS_MOUSTACHE_TOOLTIP),
1322 NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_LIPS_MOUSTACHE_R), SetDataTip(AWV_INCREASE, STR_FACE_LIPS_MOUSTACHE_TOOLTIP),
1323 EndContainer(),
1324 NWidget(NWID_HORIZONTAL),
1325 NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_CHIN_TEXT), SetFill(1, 0), SetPadding(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT),
1326 SetDataTip(STR_FACE_CHIN, STR_NULL), SetTextColour(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
1327 NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_CHIN_L), SetDataTip(AWV_DECREASE, STR_FACE_CHIN_TOOLTIP),
1328 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_CHIN), SetDataTip(STR_WHITE_STRING, STR_FACE_CHIN_TOOLTIP),
1329 NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_CHIN_R), SetDataTip(AWV_INCREASE, STR_FACE_CHIN_TOOLTIP),
1330 EndContainer(),
1331 NWidget(NWID_HORIZONTAL),
1332 NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_JACKET_TEXT), SetFill(1, 0), SetPadding(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT),
1333 SetDataTip(STR_FACE_JACKET, STR_NULL), SetTextColour(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
1334 NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_JACKET_L), SetDataTip(AWV_DECREASE, STR_FACE_JACKET_TOOLTIP),
1335 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_JACKET), SetDataTip(STR_WHITE_STRING, STR_FACE_JACKET_TOOLTIP),
1336 NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_JACKET_R), SetDataTip(AWV_INCREASE, STR_FACE_JACKET_TOOLTIP),
1337 EndContainer(),
1338 NWidget(NWID_HORIZONTAL),
1339 NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_COLLAR_TEXT), SetFill(1, 0), SetPadding(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT),
1340 SetDataTip(STR_FACE_COLLAR, STR_NULL), SetTextColour(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
1341 NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_COLLAR_L), SetDataTip(AWV_DECREASE, STR_FACE_COLLAR_TOOLTIP),
1342 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_COLLAR), SetDataTip(STR_WHITE_STRING, STR_FACE_COLLAR_TOOLTIP),
1343 NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_COLLAR_R), SetDataTip(AWV_INCREASE, STR_FACE_COLLAR_TOOLTIP),
1344 EndContainer(),
1345 NWidget(NWID_HORIZONTAL),
1346 NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_TIE_EARRING_TEXT), SetFill(1, 0), SetPadding(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT),
1347 SetDataTip(STR_FACE_EARRING, STR_NULL), SetTextColour(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
1348 NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_TIE_EARRING_L), SetDataTip(AWV_DECREASE, STR_FACE_TIE_EARRING_TOOLTIP),
1349 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_TIE_EARRING), SetDataTip(STR_WHITE_STRING, STR_FACE_TIE_EARRING_TOOLTIP),
1350 NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_TIE_EARRING_R), SetDataTip(AWV_INCREASE, STR_FACE_TIE_EARRING_TOOLTIP),
1351 EndContainer(),
1352 NWidget(NWID_SPACER), SetFill(0, 1),
1353 EndContainer(),
1354 EndContainer(),
1355 EndContainer(),
1356 EndContainer(),
1357 NWidget(NWID_SPACER), SetMinimalSize(0, 2),
1358 EndContainer(),
1359 NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
1360 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_CANCEL), SetFill(1, 0), SetDataTip(STR_BUTTON_CANCEL, STR_FACE_CANCEL_TOOLTIP),
1361 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_ACCEPT), SetFill(1, 0), SetDataTip(STR_BUTTON_OK, STR_FACE_OK_TOOLTIP),
1362 EndContainer(),
1365 /** Management class for customizing the face of the company manager. */
1366 class SelectCompanyManagerFaceWindow : public Window
1368 CompanyManagerFace face; ///< company manager face bits
1369 bool advanced; ///< advanced company manager face selection window
1371 GenderEthnicity ge; ///< Gender and ethnicity.
1372 bool is_female; ///< Female face.
1373 bool is_moust_male; ///< Male face with a moustache.
1375 Dimension yesno_dim; ///< Dimension of a yes/no button of a part in the advanced face window.
1376 Dimension number_dim; ///< Dimension of a number widget of a part in the advanced face window.
1379 * Set parameters for value of face control buttons.
1381 * @param widget_index index of this widget in the window
1382 * @param val the value which will be displayed
1383 * @param is_bool_widget is it a bool button
1385 void SetFaceStringParameters(byte widget_index, uint8 val, bool is_bool_widget) const
1387 const NWidgetCore *nwi_widget = this->GetWidget<NWidgetCore>(widget_index);
1388 if (nwi_widget->IsDisabled()) {
1389 SetDParam(0, STR_EMPTY);
1390 } else {
1391 if (is_bool_widget) {
1392 /* if it a bool button write yes or no */
1393 SetDParam(0, (val != 0) ? STR_FACE_YES : STR_FACE_NO);
1394 } else {
1395 /* else write the value + 1 */
1396 SetDParam(0, STR_JUST_INT);
1397 SetDParam(1, val + 1);
1402 void UpdateData()
1404 this->ge = (GenderEthnicity)GB(this->face, _cmf_info[CMFV_GEN_ETHN].offset, _cmf_info[CMFV_GEN_ETHN].length); // get the gender and ethnicity
1405 this->is_female = HasBit(this->ge, GENDER_FEMALE); // get the gender: 0 == male and 1 == female
1406 this->is_moust_male = !is_female && GetCompanyManagerFaceBits(this->face, CMFV_HAS_MOUSTACHE, this->ge) != 0; // is a male face with moustache
1408 this->GetWidget<NWidgetCore>(WID_SCMF_HAS_MOUSTACHE_EARRING_TEXT)->widget_data = this->is_female ? STR_FACE_EARRING : STR_FACE_MOUSTACHE;
1409 this->GetWidget<NWidgetCore>(WID_SCMF_TIE_EARRING_TEXT)->widget_data = this->is_female ? STR_FACE_EARRING : STR_FACE_TIE;
1410 this->GetWidget<NWidgetCore>(WID_SCMF_LIPS_MOUSTACHE_TEXT)->widget_data = this->is_moust_male ? STR_FACE_MOUSTACHE : STR_FACE_LIPS;
1413 public:
1414 SelectCompanyManagerFaceWindow(WindowDesc *desc, Window *parent) : Window(desc)
1416 this->advanced = false;
1417 this->CreateNestedTree();
1418 this->SelectDisplayPlanes(this->advanced);
1419 this->FinishInitNested(parent->window_number);
1420 this->parent = parent;
1421 this->owner = (Owner)this->window_number;
1422 this->face = Company::Get((CompanyID)this->window_number)->face;
1424 this->UpdateData();
1428 * Select planes to display to the user with the #NWID_SELECTION widgets #WID_SCMF_SEL_LOADSAVE, #WID_SCMF_SEL_MALEFEMALE, and #WID_SCMF_SEL_PARTS.
1429 * @param advanced Display advanced face management window.
1431 void SelectDisplayPlanes(bool advanced)
1433 this->GetWidget<NWidgetStacked>(WID_SCMF_SEL_LOADSAVE)->SetDisplayedPlane(advanced ? 0 : SZSP_NONE);
1434 this->GetWidget<NWidgetStacked>(WID_SCMF_SEL_PARTS)->SetDisplayedPlane(advanced ? 0 : SZSP_NONE);
1435 this->GetWidget<NWidgetStacked>(WID_SCMF_SEL_MALEFEMALE)->SetDisplayedPlane(advanced ? SZSP_NONE : 0);
1436 this->GetWidget<NWidgetCore>(WID_SCMF_RANDOM_NEW_FACE)->widget_data = advanced ? STR_FACE_RANDOM : STR_FACE_NEW_FACE_BUTTON;
1438 NWidgetCore *wi = this->GetWidget<NWidgetCore>(WID_SCMF_TOGGLE_LARGE_SMALL_BUTTON);
1439 if (advanced) {
1440 wi->SetDataTip(STR_FACE_SIMPLE, STR_FACE_SIMPLE_TOOLTIP);
1441 } else {
1442 wi->SetDataTip(STR_FACE_ADVANCED, STR_FACE_ADVANCED_TOOLTIP);
1446 void OnInit() override
1448 /* Size of the boolean yes/no button. */
1449 Dimension yesno_dim = maxdim(GetStringBoundingBox(STR_FACE_YES), GetStringBoundingBox(STR_FACE_NO));
1450 yesno_dim.width += WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT;
1451 yesno_dim.height += WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
1452 /* Size of the number button + arrows. */
1453 Dimension number_dim = {0, 0};
1454 for (int val = 1; val <= 12; val++) {
1455 SetDParam(0, val);
1456 number_dim = maxdim(number_dim, GetStringBoundingBox(STR_JUST_INT));
1458 uint arrows_width = GetSpriteSize(SPR_ARROW_LEFT).width + GetSpriteSize(SPR_ARROW_RIGHT).width + 2 * (WD_IMGBTN_LEFT + WD_IMGBTN_RIGHT);
1459 number_dim.width += WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT + arrows_width;
1460 number_dim.height += WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
1461 /* Compute width of both buttons. */
1462 yesno_dim.width = std::max(yesno_dim.width, number_dim.width);
1463 number_dim.width = yesno_dim.width - arrows_width;
1465 this->yesno_dim = yesno_dim;
1466 this->number_dim = number_dim;
1469 void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override
1471 switch (widget) {
1472 case WID_SCMF_HAS_MOUSTACHE_EARRING_TEXT:
1473 *size = maxdim(*size, GetStringBoundingBox(STR_FACE_EARRING));
1474 *size = maxdim(*size, GetStringBoundingBox(STR_FACE_MOUSTACHE));
1475 break;
1477 case WID_SCMF_TIE_EARRING_TEXT:
1478 *size = maxdim(*size, GetStringBoundingBox(STR_FACE_EARRING));
1479 *size = maxdim(*size, GetStringBoundingBox(STR_FACE_TIE));
1480 break;
1482 case WID_SCMF_LIPS_MOUSTACHE_TEXT:
1483 *size = maxdim(*size, GetStringBoundingBox(STR_FACE_LIPS));
1484 *size = maxdim(*size, GetStringBoundingBox(STR_FACE_MOUSTACHE));
1485 break;
1487 case WID_SCMF_FACE: {
1488 Dimension face_size = GetSpriteSize(SPR_GRADIENT);
1489 size->width = std::max(size->width, face_size.width);
1490 size->height = std::max(size->height, face_size.height);
1491 break;
1494 case WID_SCMF_HAS_MOUSTACHE_EARRING:
1495 case WID_SCMF_HAS_GLASSES:
1496 *size = this->yesno_dim;
1497 break;
1499 case WID_SCMF_EYECOLOUR:
1500 case WID_SCMF_CHIN:
1501 case WID_SCMF_EYEBROWS:
1502 case WID_SCMF_LIPS_MOUSTACHE:
1503 case WID_SCMF_NOSE:
1504 case WID_SCMF_HAIR:
1505 case WID_SCMF_JACKET:
1506 case WID_SCMF_COLLAR:
1507 case WID_SCMF_TIE_EARRING:
1508 case WID_SCMF_GLASSES:
1509 *size = this->number_dim;
1510 break;
1514 void OnPaint() override
1516 /* lower the non-selected gender button */
1517 this->SetWidgetsLoweredState(!this->is_female, WID_SCMF_MALE, WID_SCMF_MALE2, WIDGET_LIST_END);
1518 this->SetWidgetsLoweredState( this->is_female, WID_SCMF_FEMALE, WID_SCMF_FEMALE2, WIDGET_LIST_END);
1520 /* advanced company manager face selection window */
1522 /* lower the non-selected ethnicity button */
1523 this->SetWidgetLoweredState(WID_SCMF_ETHNICITY_EUR, !HasBit(this->ge, ETHNICITY_BLACK));
1524 this->SetWidgetLoweredState(WID_SCMF_ETHNICITY_AFR, HasBit(this->ge, ETHNICITY_BLACK));
1527 /* Disable dynamically the widgets which CompanyManagerFaceVariable has less than 2 options
1528 * (or in other words you haven't any choice).
1529 * If the widgets depend on a HAS-variable and this is false the widgets will be disabled, too. */
1531 /* Eye colour buttons */
1532 this->SetWidgetsDisabledState(_cmf_info[CMFV_EYE_COLOUR].valid_values[this->ge] < 2,
1533 WID_SCMF_EYECOLOUR, WID_SCMF_EYECOLOUR_L, WID_SCMF_EYECOLOUR_R, WIDGET_LIST_END);
1535 /* Chin buttons */
1536 this->SetWidgetsDisabledState(_cmf_info[CMFV_CHIN].valid_values[this->ge] < 2,
1537 WID_SCMF_CHIN, WID_SCMF_CHIN_L, WID_SCMF_CHIN_R, WIDGET_LIST_END);
1539 /* Eyebrows buttons */
1540 this->SetWidgetsDisabledState(_cmf_info[CMFV_EYEBROWS].valid_values[this->ge] < 2,
1541 WID_SCMF_EYEBROWS, WID_SCMF_EYEBROWS_L, WID_SCMF_EYEBROWS_R, WIDGET_LIST_END);
1543 /* Lips or (if it a male face with a moustache) moustache buttons */
1544 this->SetWidgetsDisabledState(_cmf_info[this->is_moust_male ? CMFV_MOUSTACHE : CMFV_LIPS].valid_values[this->ge] < 2,
1545 WID_SCMF_LIPS_MOUSTACHE, WID_SCMF_LIPS_MOUSTACHE_L, WID_SCMF_LIPS_MOUSTACHE_R, WIDGET_LIST_END);
1547 /* Nose buttons | male faces with moustache haven't any nose options */
1548 this->SetWidgetsDisabledState(_cmf_info[CMFV_NOSE].valid_values[this->ge] < 2 || this->is_moust_male,
1549 WID_SCMF_NOSE, WID_SCMF_NOSE_L, WID_SCMF_NOSE_R, WIDGET_LIST_END);
1551 /* Hair buttons */
1552 this->SetWidgetsDisabledState(_cmf_info[CMFV_HAIR].valid_values[this->ge] < 2,
1553 WID_SCMF_HAIR, WID_SCMF_HAIR_L, WID_SCMF_HAIR_R, WIDGET_LIST_END);
1555 /* Jacket buttons */
1556 this->SetWidgetsDisabledState(_cmf_info[CMFV_JACKET].valid_values[this->ge] < 2,
1557 WID_SCMF_JACKET, WID_SCMF_JACKET_L, WID_SCMF_JACKET_R, WIDGET_LIST_END);
1559 /* Collar buttons */
1560 this->SetWidgetsDisabledState(_cmf_info[CMFV_COLLAR].valid_values[this->ge] < 2,
1561 WID_SCMF_COLLAR, WID_SCMF_COLLAR_L, WID_SCMF_COLLAR_R, WIDGET_LIST_END);
1563 /* Tie/earring buttons | female faces without earring haven't any earring options */
1564 this->SetWidgetsDisabledState(_cmf_info[CMFV_TIE_EARRING].valid_values[this->ge] < 2 ||
1565 (this->is_female && GetCompanyManagerFaceBits(this->face, CMFV_HAS_TIE_EARRING, this->ge) == 0),
1566 WID_SCMF_TIE_EARRING, WID_SCMF_TIE_EARRING_L, WID_SCMF_TIE_EARRING_R, WIDGET_LIST_END);
1568 /* Glasses buttons | faces without glasses haven't any glasses options */
1569 this->SetWidgetsDisabledState(_cmf_info[CMFV_GLASSES].valid_values[this->ge] < 2 || GetCompanyManagerFaceBits(this->face, CMFV_HAS_GLASSES, this->ge) == 0,
1570 WID_SCMF_GLASSES, WID_SCMF_GLASSES_L, WID_SCMF_GLASSES_R, WIDGET_LIST_END);
1572 this->DrawWidgets();
1575 void SetStringParameters(int widget) const override
1577 switch (widget) {
1578 case WID_SCMF_HAS_MOUSTACHE_EARRING:
1579 if (this->is_female) { // Only for female faces
1580 this->SetFaceStringParameters(WID_SCMF_HAS_MOUSTACHE_EARRING, GetCompanyManagerFaceBits(this->face, CMFV_HAS_TIE_EARRING, this->ge), true);
1581 } else { // Only for male faces
1582 this->SetFaceStringParameters(WID_SCMF_HAS_MOUSTACHE_EARRING, GetCompanyManagerFaceBits(this->face, CMFV_HAS_MOUSTACHE, this->ge), true);
1584 break;
1586 case WID_SCMF_TIE_EARRING:
1587 this->SetFaceStringParameters(WID_SCMF_TIE_EARRING, GetCompanyManagerFaceBits(this->face, CMFV_TIE_EARRING, this->ge), false);
1588 break;
1590 case WID_SCMF_LIPS_MOUSTACHE:
1591 if (this->is_moust_male) { // Only for male faces with moustache
1592 this->SetFaceStringParameters(WID_SCMF_LIPS_MOUSTACHE, GetCompanyManagerFaceBits(this->face, CMFV_MOUSTACHE, this->ge), false);
1593 } else { // Only for female faces or male faces without moustache
1594 this->SetFaceStringParameters(WID_SCMF_LIPS_MOUSTACHE, GetCompanyManagerFaceBits(this->face, CMFV_LIPS, this->ge), false);
1596 break;
1598 case WID_SCMF_HAS_GLASSES:
1599 this->SetFaceStringParameters(WID_SCMF_HAS_GLASSES, GetCompanyManagerFaceBits(this->face, CMFV_HAS_GLASSES, this->ge), true );
1600 break;
1602 case WID_SCMF_HAIR:
1603 this->SetFaceStringParameters(WID_SCMF_HAIR, GetCompanyManagerFaceBits(this->face, CMFV_HAIR, this->ge), false);
1604 break;
1606 case WID_SCMF_EYEBROWS:
1607 this->SetFaceStringParameters(WID_SCMF_EYEBROWS, GetCompanyManagerFaceBits(this->face, CMFV_EYEBROWS, this->ge), false);
1608 break;
1610 case WID_SCMF_EYECOLOUR:
1611 this->SetFaceStringParameters(WID_SCMF_EYECOLOUR, GetCompanyManagerFaceBits(this->face, CMFV_EYE_COLOUR, this->ge), false);
1612 break;
1614 case WID_SCMF_GLASSES:
1615 this->SetFaceStringParameters(WID_SCMF_GLASSES, GetCompanyManagerFaceBits(this->face, CMFV_GLASSES, this->ge), false);
1616 break;
1618 case WID_SCMF_NOSE:
1619 this->SetFaceStringParameters(WID_SCMF_NOSE, GetCompanyManagerFaceBits(this->face, CMFV_NOSE, this->ge), false);
1620 break;
1622 case WID_SCMF_CHIN:
1623 this->SetFaceStringParameters(WID_SCMF_CHIN, GetCompanyManagerFaceBits(this->face, CMFV_CHIN, this->ge), false);
1624 break;
1626 case WID_SCMF_JACKET:
1627 this->SetFaceStringParameters(WID_SCMF_JACKET, GetCompanyManagerFaceBits(this->face, CMFV_JACKET, this->ge), false);
1628 break;
1630 case WID_SCMF_COLLAR:
1631 this->SetFaceStringParameters(WID_SCMF_COLLAR, GetCompanyManagerFaceBits(this->face, CMFV_COLLAR, this->ge), false);
1632 break;
1636 void DrawWidget(const Rect &r, int widget) const override
1638 switch (widget) {
1639 case WID_SCMF_FACE:
1640 DrawCompanyManagerFace(this->face, Company::Get((CompanyID)this->window_number)->colour, r.left, r.top);
1641 break;
1645 void OnClick(Point pt, int widget, int click_count) override
1647 switch (widget) {
1648 /* Toggle size, advanced/simple face selection */
1649 case WID_SCMF_TOGGLE_LARGE_SMALL:
1650 case WID_SCMF_TOGGLE_LARGE_SMALL_BUTTON:
1651 this->advanced = !this->advanced;
1652 this->SelectDisplayPlanes(this->advanced);
1653 this->ReInit();
1654 break;
1656 /* OK button */
1657 case WID_SCMF_ACCEPT:
1658 Command<CMD_SET_COMPANY_MANAGER_FACE>::Post(this->face);
1659 FALLTHROUGH;
1661 /* Cancel button */
1662 case WID_SCMF_CANCEL:
1663 this->Close();
1664 break;
1666 /* Load button */
1667 case WID_SCMF_LOAD:
1668 this->face = _company_manager_face;
1669 ScaleAllCompanyManagerFaceBits(this->face);
1670 ShowErrorMessage(STR_FACE_LOAD_DONE, INVALID_STRING_ID, WL_INFO);
1671 this->UpdateData();
1672 this->SetDirty();
1673 break;
1675 /* 'Company manager face number' button, view and/or set company manager face number */
1676 case WID_SCMF_FACECODE:
1677 SetDParam(0, this->face);
1678 ShowQueryString(STR_JUST_INT, STR_FACE_FACECODE_CAPTION, 10 + 1, this, CS_NUMERAL, QSF_NONE);
1679 break;
1681 /* Save button */
1682 case WID_SCMF_SAVE:
1683 _company_manager_face = this->face;
1684 ShowErrorMessage(STR_FACE_SAVE_DONE, INVALID_STRING_ID, WL_INFO);
1685 break;
1687 /* Toggle gender (male/female) button */
1688 case WID_SCMF_MALE:
1689 case WID_SCMF_FEMALE:
1690 case WID_SCMF_MALE2:
1691 case WID_SCMF_FEMALE2:
1692 SetCompanyManagerFaceBits(this->face, CMFV_GENDER, this->ge, (widget == WID_SCMF_FEMALE || widget == WID_SCMF_FEMALE2));
1693 ScaleAllCompanyManagerFaceBits(this->face);
1694 this->UpdateData();
1695 this->SetDirty();
1696 break;
1698 /* Randomize face button */
1699 case WID_SCMF_RANDOM_NEW_FACE:
1700 RandomCompanyManagerFaceBits(this->face, this->ge, this->advanced);
1701 this->UpdateData();
1702 this->SetDirty();
1703 break;
1705 /* Toggle ethnicity (european/african) button */
1706 case WID_SCMF_ETHNICITY_EUR:
1707 case WID_SCMF_ETHNICITY_AFR:
1708 SetCompanyManagerFaceBits(this->face, CMFV_ETHNICITY, this->ge, widget - WID_SCMF_ETHNICITY_EUR);
1709 ScaleAllCompanyManagerFaceBits(this->face);
1710 this->UpdateData();
1711 this->SetDirty();
1712 break;
1714 default:
1715 /* Here all buttons from WID_SCMF_HAS_MOUSTACHE_EARRING to WID_SCMF_GLASSES_R are handled.
1716 * First it checks which CompanyManagerFaceVariable is being changed, and then either
1717 * a: invert the value for boolean variables, or
1718 * b: it checks inside of IncreaseCompanyManagerFaceBits() if a left (_L) butten is pressed and then decrease else increase the variable */
1719 if (widget >= WID_SCMF_HAS_MOUSTACHE_EARRING && widget <= WID_SCMF_GLASSES_R) {
1720 CompanyManagerFaceVariable cmfv; // which CompanyManagerFaceVariable shall be edited
1722 if (widget < WID_SCMF_EYECOLOUR_L) { // Bool buttons
1723 switch (widget - WID_SCMF_HAS_MOUSTACHE_EARRING) {
1724 default: NOT_REACHED();
1725 case 0: cmfv = this->is_female ? CMFV_HAS_TIE_EARRING : CMFV_HAS_MOUSTACHE; break; // Has earring/moustache button
1726 case 1: cmfv = CMFV_HAS_GLASSES; break; // Has glasses button
1728 SetCompanyManagerFaceBits(this->face, cmfv, this->ge, !GetCompanyManagerFaceBits(this->face, cmfv, this->ge));
1729 ScaleAllCompanyManagerFaceBits(this->face);
1730 } else { // Value buttons
1731 switch ((widget - WID_SCMF_EYECOLOUR_L) / 3) {
1732 default: NOT_REACHED();
1733 case 0: cmfv = CMFV_EYE_COLOUR; break; // Eye colour buttons
1734 case 1: cmfv = CMFV_CHIN; break; // Chin buttons
1735 case 2: cmfv = CMFV_EYEBROWS; break; // Eyebrows buttons
1736 case 3: cmfv = this->is_moust_male ? CMFV_MOUSTACHE : CMFV_LIPS; break; // Moustache or lips buttons
1737 case 4: cmfv = CMFV_NOSE; break; // Nose buttons
1738 case 5: cmfv = CMFV_HAIR; break; // Hair buttons
1739 case 6: cmfv = CMFV_JACKET; break; // Jacket buttons
1740 case 7: cmfv = CMFV_COLLAR; break; // Collar buttons
1741 case 8: cmfv = CMFV_TIE_EARRING; break; // Tie/earring buttons
1742 case 9: cmfv = CMFV_GLASSES; break; // Glasses buttons
1744 /* 0 == left (_L), 1 == middle or 2 == right (_R) - button click */
1745 IncreaseCompanyManagerFaceBits(this->face, cmfv, this->ge, (((widget - WID_SCMF_EYECOLOUR_L) % 3) != 0) ? 1 : -1);
1747 this->UpdateData();
1748 this->SetDirty();
1750 break;
1754 void OnQueryTextFinished(char *str) override
1756 if (str == nullptr) return;
1757 /* Set a new company manager face number */
1758 if (!StrEmpty(str)) {
1759 this->face = strtoul(str, nullptr, 10);
1760 ScaleAllCompanyManagerFaceBits(this->face);
1761 ShowErrorMessage(STR_FACE_FACECODE_SET, INVALID_STRING_ID, WL_INFO);
1762 this->UpdateData();
1763 this->SetDirty();
1764 } else {
1765 ShowErrorMessage(STR_FACE_FACECODE_ERR, INVALID_STRING_ID, WL_INFO);
1770 /** Company manager face selection window description */
1771 static WindowDesc _select_company_manager_face_desc(
1772 WDP_AUTO, "company_face", 0, 0,
1773 WC_COMPANY_MANAGER_FACE, WC_NONE,
1774 WDF_CONSTRUCTION,
1775 _nested_select_company_manager_face_widgets, lengthof(_nested_select_company_manager_face_widgets)
1779 * Open the simple/advanced company manager face selection window
1781 * @param parent the parent company window
1783 static void DoSelectCompanyManagerFace(Window *parent)
1785 if (!Company::IsValidID((CompanyID)parent->window_number)) return;
1787 if (BringWindowToFrontById(WC_COMPANY_MANAGER_FACE, parent->window_number)) return;
1788 new SelectCompanyManagerFaceWindow(&_select_company_manager_face_desc, parent);
1791 static const NWidgetPart _nested_company_infrastructure_widgets[] = {
1792 NWidget(NWID_HORIZONTAL),
1793 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
1794 NWidget(WWT_CAPTION, COLOUR_GREY, WID_CI_CAPTION), SetDataTip(STR_COMPANY_INFRASTRUCTURE_VIEW_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
1795 NWidget(WWT_SHADEBOX, COLOUR_GREY),
1796 NWidget(WWT_STICKYBOX, COLOUR_GREY),
1797 EndContainer(),
1798 NWidget(WWT_PANEL, COLOUR_GREY),
1799 NWidget(NWID_VERTICAL), SetPIP(WD_FRAMERECT_TOP, 4, WD_FRAMETEXT_BOTTOM),
1800 NWidget(NWID_HORIZONTAL), SetPIP(2, 4, 2),
1801 NWidget(WWT_EMPTY, COLOUR_GREY, WID_CI_RAIL_DESC), SetMinimalTextLines(2, 0), SetFill(1, 0),
1802 NWidget(WWT_EMPTY, COLOUR_GREY, WID_CI_RAIL_COUNT), SetMinimalTextLines(2, 0), SetFill(0, 1),
1803 EndContainer(),
1804 NWidget(NWID_HORIZONTAL), SetPIP(2, 4, 2),
1805 NWidget(WWT_EMPTY, COLOUR_GREY, WID_CI_ROAD_DESC), SetMinimalTextLines(2, 0), SetFill(1, 0),
1806 NWidget(WWT_EMPTY, COLOUR_GREY, WID_CI_ROAD_COUNT), SetMinimalTextLines(2, 0), SetFill(0, 1),
1807 EndContainer(),
1808 NWidget(NWID_HORIZONTAL), SetPIP(2, 4, 2),
1809 NWidget(WWT_EMPTY, COLOUR_GREY, WID_CI_TRAM_DESC), SetMinimalTextLines(2, 0), SetFill(1, 0),
1810 NWidget(WWT_EMPTY, COLOUR_GREY, WID_CI_TRAM_COUNT), SetMinimalTextLines(2, 0), SetFill(0, 1),
1811 EndContainer(),
1812 NWidget(NWID_HORIZONTAL), SetPIP(2, 4, 2),
1813 NWidget(WWT_EMPTY, COLOUR_GREY, WID_CI_WATER_DESC), SetMinimalTextLines(2, 0), SetFill(1, 0),
1814 NWidget(WWT_EMPTY, COLOUR_GREY, WID_CI_WATER_COUNT), SetMinimalTextLines(2, 0), SetFill(0, 1),
1815 EndContainer(),
1816 NWidget(NWID_HORIZONTAL), SetPIP(2, 4, 2),
1817 NWidget(WWT_EMPTY, COLOUR_GREY, WID_CI_STATION_DESC), SetMinimalTextLines(3, 0), SetFill(1, 0),
1818 NWidget(WWT_EMPTY, COLOUR_GREY, WID_CI_STATION_COUNT), SetMinimalTextLines(3, 0), SetFill(0, 1),
1819 EndContainer(),
1820 NWidget(NWID_HORIZONTAL), SetPIP(2, 4, 2),
1821 NWidget(WWT_EMPTY, COLOUR_GREY, WID_CI_TOTAL_DESC), SetFill(1, 0),
1822 NWidget(WWT_EMPTY, COLOUR_GREY, WID_CI_TOTAL), SetFill(0, 1),
1823 EndContainer(),
1824 EndContainer(),
1825 EndContainer(),
1829 * Window with detailed information about the company's infrastructure.
1831 struct CompanyInfrastructureWindow : Window
1833 RailTypes railtypes; ///< Valid railtypes.
1834 RoadTypes roadtypes; ///< Valid roadtypes.
1836 uint total_width; ///< String width of the total cost line.
1838 CompanyInfrastructureWindow(WindowDesc *desc, WindowNumber window_number) : Window(desc)
1840 this->UpdateRailRoadTypes();
1842 this->InitNested(window_number);
1843 this->owner = (Owner)this->window_number;
1846 void UpdateRailRoadTypes()
1848 this->railtypes = RAILTYPES_NONE;
1849 this->roadtypes = ROADTYPES_NONE;
1851 /* Find the used railtypes. */
1852 for (const Engine *e : Engine::IterateType(VEH_TRAIN)) {
1853 if (!HasBit(e->info.climates, _settings_game.game_creation.landscape)) continue;
1855 this->railtypes |= GetRailTypeInfo(e->u.rail.railtype)->introduces_railtypes;
1858 /* Get the date introduced railtypes as well. */
1859 this->railtypes = AddDateIntroducedRailTypes(this->railtypes, MAX_DAY);
1861 /* Find the used roadtypes. */
1862 for (const Engine *e : Engine::IterateType(VEH_ROAD)) {
1863 if (!HasBit(e->info.climates, _settings_game.game_creation.landscape)) continue;
1865 this->roadtypes |= GetRoadTypeInfo(e->u.road.roadtype)->introduces_roadtypes;
1868 /* Get the date introduced roadtypes as well. */
1869 this->roadtypes = AddDateIntroducedRoadTypes(this->roadtypes, MAX_DAY);
1870 this->roadtypes &= ~_roadtypes_hidden_mask;
1873 /** Get total infrastructure maintenance cost. */
1874 Money GetTotalMaintenanceCost() const
1876 const Company *c = Company::Get((CompanyID)this->window_number);
1877 Money total;
1879 uint32 rail_total = c->infrastructure.GetRailTotal();
1880 for (RailType rt = RAILTYPE_BEGIN; rt != RAILTYPE_END; rt++) {
1881 if (HasBit(this->railtypes, rt)) total += RailMaintenanceCost(rt, c->infrastructure.rail[rt], rail_total);
1883 total += SignalMaintenanceCost(c->infrastructure.signal);
1885 uint32 road_total = c->infrastructure.GetRoadTotal();
1886 uint32 tram_total = c->infrastructure.GetTramTotal();
1887 for (RoadType rt = ROADTYPE_BEGIN; rt != ROADTYPE_END; rt++) {
1888 if (HasBit(this->roadtypes, rt)) total += RoadMaintenanceCost(rt, c->infrastructure.road[rt], RoadTypeIsRoad(rt) ? road_total : tram_total);
1891 total += CanalMaintenanceCost(c->infrastructure.water);
1892 total += StationMaintenanceCost(c->infrastructure.station);
1893 total += AirportMaintenanceCost(c->index);
1895 return total;
1898 void SetStringParameters(int widget) const override
1900 switch (widget) {
1901 case WID_CI_CAPTION:
1902 SetDParam(0, (CompanyID)this->window_number);
1903 break;
1907 void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override
1909 const Company *c = Company::Get((CompanyID)this->window_number);
1911 switch (widget) {
1912 case WID_CI_RAIL_DESC: {
1913 uint lines = 1; // Starts at 1 because a line is also required for the section title
1915 size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_RAIL_SECT).width);
1917 for (const auto &rt : _sorted_railtypes) {
1918 if (HasBit(this->railtypes, rt)) {
1919 lines++;
1920 SetDParam(0, GetRailTypeInfo(rt)->strings.name);
1921 size->width = std::max(size->width, GetStringBoundingBox(STR_WHITE_STRING).width + WD_FRAMERECT_LEFT);
1924 if (this->railtypes != RAILTYPES_NONE) {
1925 lines++;
1926 size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_SIGNALS).width + WD_FRAMERECT_LEFT);
1929 size->height = std::max(size->height, lines * FONT_HEIGHT_NORMAL);
1930 break;
1933 case WID_CI_ROAD_DESC:
1934 case WID_CI_TRAM_DESC: {
1935 uint lines = 1; // Starts at 1 because a line is also required for the section title
1937 size->width = std::max(size->width, GetStringBoundingBox(widget == WID_CI_ROAD_DESC ? STR_COMPANY_INFRASTRUCTURE_VIEW_ROAD_SECT : STR_COMPANY_INFRASTRUCTURE_VIEW_TRAM_SECT).width);
1939 for (const auto &rt : _sorted_roadtypes) {
1940 if (HasBit(this->roadtypes, rt) && RoadTypeIsRoad(rt) == (widget == WID_CI_ROAD_DESC)) {
1941 lines++;
1942 SetDParam(0, GetRoadTypeInfo(rt)->strings.name);
1943 size->width = std::max(size->width, GetStringBoundingBox(STR_WHITE_STRING).width + WD_FRAMERECT_LEFT);
1947 size->height = std::max(size->height, lines * FONT_HEIGHT_NORMAL);
1948 break;
1951 case WID_CI_WATER_DESC:
1952 size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_WATER_SECT).width);
1953 size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_CANALS).width + WD_FRAMERECT_LEFT);
1954 break;
1956 case WID_CI_STATION_DESC:
1957 size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_STATION_SECT).width);
1958 size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_STATIONS).width + WD_FRAMERECT_LEFT);
1959 size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_AIRPORTS).width + WD_FRAMERECT_LEFT);
1960 break;
1962 case WID_CI_RAIL_COUNT:
1963 case WID_CI_ROAD_COUNT:
1964 case WID_CI_TRAM_COUNT:
1965 case WID_CI_WATER_COUNT:
1966 case WID_CI_STATION_COUNT:
1967 case WID_CI_TOTAL: {
1968 /* Find the maximum count that is displayed. */
1969 uint32 max_val = 1000; // Some random number to reserve enough space.
1970 Money max_cost = 10000; // Some random number to reserve enough space.
1971 uint32 rail_total = c->infrastructure.GetRailTotal();
1972 for (RailType rt = RAILTYPE_BEGIN; rt < RAILTYPE_END; rt++) {
1973 max_val = std::max(max_val, c->infrastructure.rail[rt]);
1974 max_cost = std::max(max_cost, RailMaintenanceCost(rt, c->infrastructure.rail[rt], rail_total));
1976 max_val = std::max(max_val, c->infrastructure.signal);
1977 max_cost = std::max(max_cost, SignalMaintenanceCost(c->infrastructure.signal));
1978 uint32 road_total = c->infrastructure.GetRoadTotal();
1979 uint32 tram_total = c->infrastructure.GetTramTotal();
1980 for (RoadType rt = ROADTYPE_BEGIN; rt < ROADTYPE_END; rt++) {
1981 max_val = std::max(max_val, c->infrastructure.road[rt]);
1982 max_cost = std::max(max_cost, RoadMaintenanceCost(rt, c->infrastructure.road[rt], RoadTypeIsRoad(rt) ? road_total : tram_total));
1985 max_val = std::max(max_val, c->infrastructure.water);
1986 max_cost = std::max(max_cost, CanalMaintenanceCost(c->infrastructure.water));
1987 max_val = std::max(max_val, c->infrastructure.station);
1988 max_cost = std::max(max_cost, StationMaintenanceCost(c->infrastructure.station));
1989 max_val = std::max(max_val, c->infrastructure.airport);
1990 max_cost = std::max(max_cost, AirportMaintenanceCost(c->index));
1992 SetDParamMaxValue(0, max_val);
1993 uint count_width = GetStringBoundingBox(STR_WHITE_COMMA).width + 20; // Reserve some wiggle room
1995 if (_settings_game.economy.infrastructure_maintenance) {
1996 SetDParamMaxValue(0, this->GetTotalMaintenanceCost() * 12); // Convert to per year
1997 this->total_width = GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_TOTAL).width + 20;
1998 size->width = std::max(size->width, this->total_width);
2000 SetDParamMaxValue(0, max_cost * 12); // Convert to per year
2001 count_width += std::max(this->total_width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_TOTAL).width);
2004 size->width = std::max(size->width, count_width);
2006 /* Set height of the total line. */
2007 if (widget == WID_CI_TOTAL) {
2008 size->height = _settings_game.economy.infrastructure_maintenance ? std::max(size->height, EXP_LINESPACE + FONT_HEIGHT_NORMAL) : 0;
2010 break;
2016 * Helper for drawing the counts line.
2017 * @param r The bounds to draw in.
2018 * @param y The y position to draw at.
2019 * @param count The count to show on this line.
2020 * @param monthly_cost The monthly costs.
2022 void DrawCountLine(const Rect &r, int &y, int count, Money monthly_cost) const
2024 SetDParam(0, count);
2025 DrawString(r.left, r.right, y += FONT_HEIGHT_NORMAL, STR_WHITE_COMMA, TC_FROMSTRING, SA_RIGHT);
2027 if (_settings_game.economy.infrastructure_maintenance) {
2028 SetDParam(0, monthly_cost * 12); // Convert to per year
2029 int left = _current_text_dir == TD_RTL ? r.right - this->total_width : r.left;
2030 DrawString(left, left + this->total_width, y, STR_COMPANY_INFRASTRUCTURE_VIEW_TOTAL, TC_FROMSTRING, SA_RIGHT);
2034 void DrawWidget(const Rect &r, int widget) const override
2036 const Company *c = Company::Get((CompanyID)this->window_number);
2037 int y = r.top;
2039 int offs_left = _current_text_dir == TD_LTR ? WD_FRAMERECT_LEFT : 0;
2040 int offs_right = _current_text_dir == TD_LTR ? 0 : WD_FRAMERECT_LEFT;
2042 switch (widget) {
2043 case WID_CI_RAIL_DESC:
2044 DrawString(r.left, r.right, y, STR_COMPANY_INFRASTRUCTURE_VIEW_RAIL_SECT);
2046 if (this->railtypes != RAILTYPES_NONE) {
2047 /* Draw name of each valid railtype. */
2048 for (const auto &rt : _sorted_railtypes) {
2049 if (HasBit(this->railtypes, rt)) {
2050 SetDParam(0, GetRailTypeInfo(rt)->strings.name);
2051 DrawString(r.left + offs_left, r.right - offs_right, y += FONT_HEIGHT_NORMAL, STR_WHITE_STRING);
2054 DrawString(r.left + offs_left, r.right - offs_right, y += FONT_HEIGHT_NORMAL, STR_COMPANY_INFRASTRUCTURE_VIEW_SIGNALS);
2055 } else {
2056 /* No valid railtype. */
2057 DrawString(r.left + offs_left, r.right - offs_right, y += FONT_HEIGHT_NORMAL, STR_COMPANY_VIEW_INFRASTRUCTURE_NONE);
2060 break;
2062 case WID_CI_RAIL_COUNT: {
2063 /* Draw infrastructure count for each valid railtype. */
2064 uint32 rail_total = c->infrastructure.GetRailTotal();
2065 for (const auto &rt : _sorted_railtypes) {
2066 if (HasBit(this->railtypes, rt)) {
2067 this->DrawCountLine(r, y, c->infrastructure.rail[rt], RailMaintenanceCost(rt, c->infrastructure.rail[rt], rail_total));
2070 if (this->railtypes != RAILTYPES_NONE) {
2071 this->DrawCountLine(r, y, c->infrastructure.signal, SignalMaintenanceCost(c->infrastructure.signal));
2073 break;
2076 case WID_CI_ROAD_DESC:
2077 case WID_CI_TRAM_DESC: {
2078 DrawString(r.left, r.right, y, widget == WID_CI_ROAD_DESC ? STR_COMPANY_INFRASTRUCTURE_VIEW_ROAD_SECT : STR_COMPANY_INFRASTRUCTURE_VIEW_TRAM_SECT);
2080 /* Draw name of each valid roadtype. */
2081 for (const auto &rt : _sorted_roadtypes) {
2082 if (HasBit(this->roadtypes, rt) && RoadTypeIsRoad(rt) == (widget == WID_CI_ROAD_DESC)) {
2083 SetDParam(0, GetRoadTypeInfo(rt)->strings.name);
2084 DrawString(r.left + offs_left, r.right - offs_right, y += FONT_HEIGHT_NORMAL, STR_WHITE_STRING);
2088 break;
2091 case WID_CI_ROAD_COUNT:
2092 case WID_CI_TRAM_COUNT: {
2093 uint32 road_tram_total = widget == WID_CI_ROAD_COUNT ? c->infrastructure.GetRoadTotal() : c->infrastructure.GetTramTotal();
2094 for (const auto &rt : _sorted_roadtypes) {
2095 if (HasBit(this->roadtypes, rt) && RoadTypeIsRoad(rt) == (widget == WID_CI_ROAD_COUNT)) {
2096 this->DrawCountLine(r, y, c->infrastructure.road[rt], RoadMaintenanceCost(rt, c->infrastructure.road[rt], road_tram_total));
2099 break;
2102 case WID_CI_WATER_DESC:
2103 DrawString(r.left, r.right, y, STR_COMPANY_INFRASTRUCTURE_VIEW_WATER_SECT);
2104 DrawString(r.left + offs_left, r.right - offs_right, y += FONT_HEIGHT_NORMAL, STR_COMPANY_INFRASTRUCTURE_VIEW_CANALS);
2105 break;
2107 case WID_CI_WATER_COUNT:
2108 this->DrawCountLine(r, y, c->infrastructure.water, CanalMaintenanceCost(c->infrastructure.water));
2109 break;
2111 case WID_CI_TOTAL:
2112 if (_settings_game.economy.infrastructure_maintenance) {
2113 int left = _current_text_dir == TD_RTL ? r.right - this->total_width : r.left;
2114 GfxFillRect(left, y, left + this->total_width, y, PC_WHITE);
2115 y += EXP_LINESPACE;
2116 SetDParam(0, this->GetTotalMaintenanceCost() * 12); // Convert to per year
2117 DrawString(left, left + this->total_width, y, STR_COMPANY_INFRASTRUCTURE_VIEW_TOTAL, TC_FROMSTRING, SA_RIGHT);
2119 break;
2121 case WID_CI_STATION_DESC:
2122 DrawString(r.left, r.right, y, STR_COMPANY_INFRASTRUCTURE_VIEW_STATION_SECT);
2123 DrawString(r.left + offs_left, r.right - offs_right, y += FONT_HEIGHT_NORMAL, STR_COMPANY_INFRASTRUCTURE_VIEW_STATIONS);
2124 DrawString(r.left + offs_left, r.right - offs_right, y += FONT_HEIGHT_NORMAL, STR_COMPANY_INFRASTRUCTURE_VIEW_AIRPORTS);
2125 break;
2127 case WID_CI_STATION_COUNT:
2128 this->DrawCountLine(r, y, c->infrastructure.station, StationMaintenanceCost(c->infrastructure.station));
2129 this->DrawCountLine(r, y, c->infrastructure.airport, AirportMaintenanceCost(c->index));
2130 break;
2135 * Some data on this window has become invalid.
2136 * @param data Information about the changed data.
2137 * @param gui_scope Whether the call is done from GUI scope. You may not do everything when not in GUI scope. See #InvalidateWindowData() for details.
2139 void OnInvalidateData(int data = 0, bool gui_scope = true) override
2141 if (!gui_scope) return;
2143 this->UpdateRailRoadTypes();
2144 this->ReInit();
2148 static WindowDesc _company_infrastructure_desc(
2149 WDP_AUTO, "company_infrastructure", 0, 0,
2150 WC_COMPANY_INFRASTRUCTURE, WC_NONE,
2152 _nested_company_infrastructure_widgets, lengthof(_nested_company_infrastructure_widgets)
2156 * Open the infrastructure window of a company.
2157 * @param company Company to show infrastructure of.
2159 static void ShowCompanyInfrastructure(CompanyID company)
2161 if (!Company::IsValidID(company)) return;
2162 AllocateWindowDescFront<CompanyInfrastructureWindow>(&_company_infrastructure_desc, company);
2165 static const NWidgetPart _nested_company_widgets[] = {
2166 NWidget(NWID_HORIZONTAL),
2167 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
2168 NWidget(WWT_CAPTION, COLOUR_GREY, WID_C_CAPTION), SetDataTip(STR_COMPANY_VIEW_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
2169 NWidget(WWT_SHADEBOX, COLOUR_GREY),
2170 NWidget(WWT_STICKYBOX, COLOUR_GREY),
2171 EndContainer(),
2172 NWidget(WWT_PANEL, COLOUR_GREY),
2173 NWidget(NWID_HORIZONTAL), SetPIP(4, 6, 4),
2174 NWidget(NWID_VERTICAL), SetPIP(4, 2, 4),
2175 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_C_FACE), SetMinimalSize(92, 119), SetFill(1, 0),
2176 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_C_FACE_TITLE), SetFill(1, 1), SetMinimalTextLines(2, 0),
2177 EndContainer(),
2178 NWidget(NWID_VERTICAL),
2179 NWidget(NWID_HORIZONTAL),
2180 NWidget(NWID_VERTICAL), SetPIP(4, 5, 5),
2181 NWidget(WWT_TEXT, COLOUR_GREY, WID_C_DESC_INAUGURATION), SetDataTip(STR_COMPANY_VIEW_INAUGURATED_TITLE, STR_NULL), SetFill(1, 0),
2182 NWidget(NWID_HORIZONTAL), SetPIP(0, 5, 0),
2183 NWidget(WWT_LABEL, COLOUR_GREY, WID_C_DESC_COLOUR_SCHEME), SetDataTip(STR_COMPANY_VIEW_COLOUR_SCHEME_TITLE, STR_NULL),
2184 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_C_DESC_COLOUR_SCHEME_EXAMPLE), SetMinimalSize(30, 0), SetFill(0, 1),
2185 NWidget(NWID_SPACER), SetFill(1, 0),
2186 EndContainer(),
2187 NWidget(NWID_HORIZONTAL), SetPIP(0, 4, 0),
2188 NWidget(NWID_VERTICAL),
2189 NWidget(WWT_TEXT, COLOUR_GREY, WID_C_DESC_VEHICLE), SetDataTip(STR_COMPANY_VIEW_VEHICLES_TITLE, STR_NULL),
2190 NWidget(NWID_SPACER), SetFill(0, 1),
2191 EndContainer(),
2192 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_C_DESC_VEHICLE_COUNTS), SetMinimalTextLines(4, 0),
2193 NWidget(NWID_SPACER), SetFill(1, 0),
2194 EndContainer(),
2195 EndContainer(),
2196 NWidget(NWID_VERTICAL), SetPIP(4, 2, 4),
2197 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_C_SELECT_VIEW_BUILD_HQ),
2198 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_C_VIEW_HQ), SetDataTip(STR_COMPANY_VIEW_VIEW_HQ_BUTTON, STR_COMPANY_VIEW_VIEW_HQ_TOOLTIP),
2199 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_C_BUILD_HQ), SetDataTip(STR_COMPANY_VIEW_BUILD_HQ_BUTTON, STR_COMPANY_VIEW_BUILD_HQ_TOOLTIP),
2200 EndContainer(),
2201 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_C_SELECT_RELOCATE),
2202 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_C_RELOCATE_HQ), SetDataTip(STR_COMPANY_VIEW_RELOCATE_HQ, STR_COMPANY_VIEW_RELOCATE_COMPANY_HEADQUARTERS),
2203 NWidget(NWID_SPACER),
2204 EndContainer(),
2205 NWidget(NWID_SPACER), SetFill(0, 1),
2206 EndContainer(),
2207 EndContainer(),
2208 NWidget(WWT_TEXT, COLOUR_GREY, WID_C_DESC_COMPANY_VALUE), SetDataTip(STR_COMPANY_VIEW_COMPANY_VALUE, STR_NULL), SetFill(1, 0),
2209 NWidget(NWID_VERTICAL), SetPIP(4, 2, 4),
2210 NWidget(NWID_HORIZONTAL), SetPIP(0, 4, 0),
2211 NWidget(NWID_VERTICAL),
2212 NWidget(WWT_TEXT, COLOUR_GREY, WID_C_DESC_INFRASTRUCTURE), SetDataTip(STR_COMPANY_VIEW_INFRASTRUCTURE, STR_NULL),
2213 NWidget(NWID_SPACER), SetFill(0, 1),
2214 EndContainer(),
2215 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_C_DESC_INFRASTRUCTURE_COUNTS), SetMinimalTextLines(5, 0), SetFill(1, 0),
2216 NWidget(NWID_VERTICAL),
2217 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_C_VIEW_INFRASTRUCTURE), SetDataTip(STR_COMPANY_VIEW_INFRASTRUCTURE_BUTTON, STR_COMPANY_VIEW_INFRASTRUCTURE_TOOLTIP),
2218 NWidget(NWID_SPACER),
2219 EndContainer(),
2220 EndContainer(),
2221 EndContainer(),
2222 NWidget(NWID_HORIZONTAL),
2223 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_C_SELECT_DESC_OWNERS),
2224 NWidget(NWID_VERTICAL), SetPIP(5, 5, 4),
2225 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_C_DESC_OWNERS), SetMinimalTextLines(MAX_COMPANY_SHARE_OWNERS, 0),
2226 NWidget(NWID_SPACER), SetFill(0, 1),
2227 EndContainer(),
2228 EndContainer(),
2229 /* Multi player buttons. */
2230 NWidget(NWID_VERTICAL), SetPIP(4, 2, 4),
2231 NWidget(NWID_SPACER), SetFill(0, 1),
2232 NWidget(NWID_HORIZONTAL), SetPIP(0, 4, 0),
2233 NWidget(NWID_SPACER), SetFill(1, 0),
2234 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_C_SELECT_GIVE_MONEY),
2235 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_C_GIVE_MONEY), SetDataTip(STR_COMPANY_VIEW_GIVE_MONEY_BUTTON, STR_COMPANY_VIEW_GIVE_MONEY_TOOLTIP),
2236 EndContainer(),
2237 EndContainer(),
2238 NWidget(NWID_HORIZONTAL), SetPIP(0, 4, 0),
2239 NWidget(WWT_EMPTY, COLOUR_GREY, WID_C_HAS_PASSWORD),
2240 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_C_SELECT_MULTIPLAYER),
2241 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_C_COMPANY_PASSWORD), SetDataTip(STR_COMPANY_VIEW_PASSWORD, STR_COMPANY_VIEW_PASSWORD_TOOLTIP),
2242 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_C_COMPANY_JOIN), SetDataTip(STR_COMPANY_VIEW_JOIN, STR_COMPANY_VIEW_JOIN_TOOLTIP),
2243 EndContainer(),
2244 EndContainer(),
2245 EndContainer(),
2246 EndContainer(),
2247 EndContainer(),
2248 EndContainer(),
2249 EndContainer(),
2250 /* Button bars at the bottom. */
2251 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_C_SELECT_BUTTONS),
2252 NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
2253 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_C_NEW_FACE), SetFill(1, 0), SetDataTip(STR_COMPANY_VIEW_NEW_FACE_BUTTON, STR_COMPANY_VIEW_NEW_FACE_TOOLTIP),
2254 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_C_COLOUR_SCHEME), SetFill(1, 0), SetDataTip(STR_COMPANY_VIEW_COLOUR_SCHEME_BUTTON, STR_COMPANY_VIEW_COLOUR_SCHEME_TOOLTIP),
2255 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_C_PRESIDENT_NAME), SetFill(1, 0), SetDataTip(STR_COMPANY_VIEW_PRESIDENT_NAME_BUTTON, STR_COMPANY_VIEW_PRESIDENT_NAME_TOOLTIP),
2256 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_C_COMPANY_NAME), SetFill(1, 0), SetDataTip(STR_COMPANY_VIEW_COMPANY_NAME_BUTTON, STR_COMPANY_VIEW_COMPANY_NAME_TOOLTIP),
2257 EndContainer(),
2258 NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
2259 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_C_BUY_SHARE), SetFill(1, 0), SetDataTip(STR_COMPANY_VIEW_BUY_SHARE_BUTTON, STR_COMPANY_VIEW_BUY_SHARE_TOOLTIP),
2260 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_C_SELL_SHARE), SetFill(1, 0), SetDataTip(STR_COMPANY_VIEW_SELL_SHARE_BUTTON, STR_COMPANY_VIEW_SELL_SHARE_TOOLTIP),
2261 EndContainer(),
2262 EndContainer(),
2265 int GetAmountOwnedBy(const Company *c, Owner owner)
2267 auto share_owned_by = [owner](auto share_owner) { return share_owner == owner; };
2268 return std::count_if(c->share_owners.begin(), c->share_owners.end(), share_owned_by);
2271 /** Strings for the company vehicle counts */
2272 static const StringID _company_view_vehicle_count_strings[] = {
2273 STR_COMPANY_VIEW_TRAINS, STR_COMPANY_VIEW_ROAD_VEHICLES, STR_COMPANY_VIEW_SHIPS, STR_COMPANY_VIEW_AIRCRAFT
2277 * Window with general information about a company
2279 struct CompanyWindow : Window
2281 CompanyWidgets query_widget;
2283 /** Display planes in the company window. */
2284 enum CompanyWindowPlanes {
2285 /* Display planes of the #WID_C_SELECT_MULTIPLAYER selection widget. */
2286 CWP_MP_C_PWD = 0, ///< Display the company password button.
2287 CWP_MP_C_JOIN, ///< Display the join company button.
2289 /* Display planes of the #WID_C_SELECT_VIEW_BUILD_HQ selection widget. */
2290 CWP_VB_VIEW = 0, ///< Display the view button
2291 CWP_VB_BUILD, ///< Display the build button
2293 /* Display planes of the #WID_C_SELECT_RELOCATE selection widget. */
2294 CWP_RELOCATE_SHOW = 0, ///< Show the relocate HQ button.
2295 CWP_RELOCATE_HIDE, ///< Hide the relocate HQ button.
2297 /* Display planes of the #WID_C_SELECT_BUTTONS selection widget. */
2298 CWP_BUTTONS_LOCAL = 0, ///< Buttons of the local company.
2299 CWP_BUTTONS_OTHER, ///< Buttons of the other companies.
2302 CompanyWindow(WindowDesc *desc, WindowNumber window_number) : Window(desc)
2304 this->InitNested(window_number);
2305 this->owner = (Owner)this->window_number;
2306 this->OnInvalidateData();
2309 void OnPaint() override
2311 const Company *c = Company::Get((CompanyID)this->window_number);
2312 bool local = this->window_number == _local_company;
2314 if (!this->IsShaded()) {
2315 bool reinit = false;
2317 /* Button bar selection. */
2318 int plane = local ? CWP_BUTTONS_LOCAL : CWP_BUTTONS_OTHER;
2319 NWidgetStacked *wi = this->GetWidget<NWidgetStacked>(WID_C_SELECT_BUTTONS);
2320 if (plane != wi->shown_plane) {
2321 wi->SetDisplayedPlane(plane);
2322 this->InvalidateData();
2323 reinit = true;
2326 /* Build HQ button handling. */
2327 plane = (local && c->location_of_HQ == INVALID_TILE) ? CWP_VB_BUILD : CWP_VB_VIEW;
2328 wi = this->GetWidget<NWidgetStacked>(WID_C_SELECT_VIEW_BUILD_HQ);
2329 if (plane != wi->shown_plane) {
2330 wi->SetDisplayedPlane(plane);
2331 reinit = true;
2334 this->SetWidgetDisabledState(WID_C_VIEW_HQ, c->location_of_HQ == INVALID_TILE);
2336 /* Enable/disable 'Relocate HQ' button. */
2337 plane = (!local || c->location_of_HQ == INVALID_TILE) ? CWP_RELOCATE_HIDE : CWP_RELOCATE_SHOW;
2338 wi = this->GetWidget<NWidgetStacked>(WID_C_SELECT_RELOCATE);
2339 if (plane != wi->shown_plane) {
2340 wi->SetDisplayedPlane(plane);
2341 reinit = true;
2344 /* Owners of company */
2345 auto invalid_owner = [](auto owner) { return owner == INVALID_COMPANY; };
2346 plane = std::all_of(c->share_owners.begin(), c->share_owners.end(), invalid_owner) ? SZSP_HORIZONTAL : 0;
2347 wi = this->GetWidget<NWidgetStacked>(WID_C_SELECT_DESC_OWNERS);
2348 if (plane != wi->shown_plane) {
2349 wi->SetDisplayedPlane(plane);
2350 reinit = true;
2353 /* Enable/disable 'Give money' button. */
2354 plane = ((local || _local_company == COMPANY_SPECTATOR || !_settings_game.economy.give_money) ? SZSP_NONE : 0);
2355 wi = this->GetWidget<NWidgetStacked>(WID_C_SELECT_GIVE_MONEY);
2356 if (plane != wi->shown_plane) {
2357 wi->SetDisplayedPlane(plane);
2358 reinit = true;
2361 /* Multiplayer buttons. */
2362 plane = ((!_networking) ? (int)SZSP_NONE : (int)(local ? CWP_MP_C_PWD : CWP_MP_C_JOIN));
2363 wi = this->GetWidget<NWidgetStacked>(WID_C_SELECT_MULTIPLAYER);
2364 if (plane != wi->shown_plane) {
2365 wi->SetDisplayedPlane(plane);
2366 reinit = true;
2368 this->SetWidgetDisabledState(WID_C_COMPANY_JOIN, c->is_ai);
2370 if (reinit) {
2371 this->ReInit();
2372 return;
2376 this->DrawWidgets();
2379 void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override
2381 switch (widget) {
2382 case WID_C_FACE: {
2383 Dimension face_size = GetSpriteSize(SPR_GRADIENT);
2384 size->width = std::max(size->width, face_size.width);
2385 size->height = std::max(size->height, face_size.height);
2386 break;
2389 case WID_C_DESC_COLOUR_SCHEME_EXAMPLE: {
2390 Point offset;
2391 Dimension d = GetSpriteSize(SPR_VEH_BUS_SW_VIEW, &offset);
2392 d.width -= offset.x;
2393 d.height -= offset.y;
2394 *size = maxdim(*size, d);
2395 break;
2398 case WID_C_DESC_COMPANY_VALUE:
2399 SetDParam(0, INT64_MAX); // Arguably the maximum company value
2400 size->width = GetStringBoundingBox(STR_COMPANY_VIEW_COMPANY_VALUE).width;
2401 break;
2403 case WID_C_DESC_VEHICLE_COUNTS:
2404 SetDParamMaxValue(0, 5000); // Maximum number of vehicles
2405 for (uint i = 0; i < lengthof(_company_view_vehicle_count_strings); i++) {
2406 size->width = std::max(size->width, GetStringBoundingBox(_company_view_vehicle_count_strings[i]).width);
2408 break;
2410 case WID_C_DESC_INFRASTRUCTURE_COUNTS:
2411 SetDParamMaxValue(0, UINT_MAX);
2412 size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_INFRASTRUCTURE_RAIL).width);
2413 size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_INFRASTRUCTURE_ROAD).width);
2414 size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_INFRASTRUCTURE_WATER).width);
2415 size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_INFRASTRUCTURE_STATION).width);
2416 size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_INFRASTRUCTURE_AIRPORT).width);
2417 size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_INFRASTRUCTURE_NONE).width);
2418 break;
2420 case WID_C_DESC_OWNERS: {
2421 for (const Company *c2 : Company::Iterate()) {
2422 SetDParamMaxValue(0, 75);
2423 SetDParam(1, c2->index);
2425 size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_SHARES_OWNED_BY).width);
2427 break;
2430 case WID_C_VIEW_HQ:
2431 case WID_C_BUILD_HQ:
2432 case WID_C_RELOCATE_HQ:
2433 case WID_C_VIEW_INFRASTRUCTURE:
2434 case WID_C_GIVE_MONEY:
2435 case WID_C_COMPANY_PASSWORD:
2436 case WID_C_COMPANY_JOIN:
2437 size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_VIEW_HQ_BUTTON).width);
2438 size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_BUILD_HQ_BUTTON).width);
2439 size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_RELOCATE_HQ).width);
2440 size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_INFRASTRUCTURE_BUTTON).width);
2441 size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_GIVE_MONEY_BUTTON).width);
2442 size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_PASSWORD).width);
2443 size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_JOIN).width);
2444 break;
2446 case WID_C_HAS_PASSWORD:
2447 *size = maxdim(*size, GetSpriteSize(SPR_LOCK));
2448 break;
2452 void DrawWidget(const Rect &r, int widget) const override
2454 const Company *c = Company::Get((CompanyID)this->window_number);
2455 switch (widget) {
2456 case WID_C_FACE:
2457 DrawCompanyManagerFace(c->face, c->colour, r.left, r.top);
2458 break;
2460 case WID_C_FACE_TITLE:
2461 SetDParam(0, c->index);
2462 DrawStringMultiLine(r.left, r.right, r.top, r.bottom, STR_COMPANY_VIEW_PRESIDENT_MANAGER_TITLE, TC_FROMSTRING, SA_HOR_CENTER);
2463 break;
2465 case WID_C_DESC_COLOUR_SCHEME_EXAMPLE: {
2466 Point offset;
2467 Dimension d = GetSpriteSize(SPR_VEH_BUS_SW_VIEW, &offset);
2468 d.height -= offset.y;
2469 DrawSprite(SPR_VEH_BUS_SW_VIEW, COMPANY_SPRITE_COLOUR(c->index), r.left - offset.x, (r.top + r.bottom - d.height) / 2 - offset.y);
2470 break;
2473 case WID_C_DESC_VEHICLE_COUNTS: {
2474 uint amounts[4];
2475 amounts[0] = c->group_all[VEH_TRAIN].num_vehicle;
2476 amounts[1] = c->group_all[VEH_ROAD].num_vehicle;
2477 amounts[2] = c->group_all[VEH_SHIP].num_vehicle;
2478 amounts[3] = c->group_all[VEH_AIRCRAFT].num_vehicle;
2480 int y = r.top;
2481 if (amounts[0] + amounts[1] + amounts[2] + amounts[3] == 0) {
2482 DrawString(r.left, r.right, y, STR_COMPANY_VIEW_VEHICLES_NONE);
2483 } else {
2484 static_assert(lengthof(amounts) == lengthof(_company_view_vehicle_count_strings));
2486 for (uint i = 0; i < lengthof(amounts); i++) {
2487 if (amounts[i] != 0) {
2488 SetDParam(0, amounts[i]);
2489 DrawString(r.left, r.right, y, _company_view_vehicle_count_strings[i]);
2490 y += FONT_HEIGHT_NORMAL;
2494 break;
2497 case WID_C_DESC_INFRASTRUCTURE_COUNTS: {
2498 uint y = r.top;
2500 /* Collect rail and road counts. */
2501 uint rail_pieces = c->infrastructure.signal;
2502 uint road_pieces = 0;
2503 for (uint i = 0; i < lengthof(c->infrastructure.rail); i++) rail_pieces += c->infrastructure.rail[i];
2504 for (uint i = 0; i < lengthof(c->infrastructure.road); i++) road_pieces += c->infrastructure.road[i];
2506 if (rail_pieces == 0 && road_pieces == 0 && c->infrastructure.water == 0 && c->infrastructure.station == 0 && c->infrastructure.airport == 0) {
2507 DrawString(r.left, r.right, y, STR_COMPANY_VIEW_INFRASTRUCTURE_NONE);
2508 } else {
2509 if (rail_pieces != 0) {
2510 SetDParam(0, rail_pieces);
2511 DrawString(r.left, r.right, y, STR_COMPANY_VIEW_INFRASTRUCTURE_RAIL);
2512 y += FONT_HEIGHT_NORMAL;
2514 if (road_pieces != 0) {
2515 SetDParam(0, road_pieces);
2516 DrawString(r.left, r.right, y, STR_COMPANY_VIEW_INFRASTRUCTURE_ROAD);
2517 y += FONT_HEIGHT_NORMAL;
2519 if (c->infrastructure.water != 0) {
2520 SetDParam(0, c->infrastructure.water);
2521 DrawString(r.left, r.right, y, STR_COMPANY_VIEW_INFRASTRUCTURE_WATER);
2522 y += FONT_HEIGHT_NORMAL;
2524 if (c->infrastructure.station != 0) {
2525 SetDParam(0, c->infrastructure.station);
2526 DrawString(r.left, r.right, y, STR_COMPANY_VIEW_INFRASTRUCTURE_STATION);
2527 y += FONT_HEIGHT_NORMAL;
2529 if (c->infrastructure.airport != 0) {
2530 SetDParam(0, c->infrastructure.airport);
2531 DrawString(r.left, r.right, y, STR_COMPANY_VIEW_INFRASTRUCTURE_AIRPORT);
2535 break;
2538 case WID_C_DESC_OWNERS: {
2539 uint y = r.top;
2541 for (const Company *c2 : Company::Iterate()) {
2542 uint amt = GetAmountOwnedBy(c, c2->index);
2543 if (amt != 0) {
2544 SetDParam(0, amt * 25);
2545 SetDParam(1, c2->index);
2547 DrawString(r.left, r.right, y, STR_COMPANY_VIEW_SHARES_OWNED_BY);
2548 y += FONT_HEIGHT_NORMAL;
2551 break;
2554 case WID_C_HAS_PASSWORD:
2555 if (_networking && NetworkCompanyIsPassworded(c->index)) {
2556 DrawSprite(SPR_LOCK, PAL_NONE, r.left, r.top);
2558 break;
2562 void SetStringParameters(int widget) const override
2564 switch (widget) {
2565 case WID_C_CAPTION:
2566 SetDParam(0, (CompanyID)this->window_number);
2567 SetDParam(1, (CompanyID)this->window_number);
2568 break;
2570 case WID_C_DESC_INAUGURATION:
2571 SetDParam(0, Company::Get((CompanyID)this->window_number)->inaugurated_year);
2572 break;
2574 case WID_C_DESC_COMPANY_VALUE:
2575 SetDParam(0, CalculateCompanyValue(Company::Get((CompanyID)this->window_number)));
2576 break;
2580 void OnClick(Point pt, int widget, int click_count) override
2582 switch (widget) {
2583 case WID_C_NEW_FACE: DoSelectCompanyManagerFace(this); break;
2585 case WID_C_COLOUR_SCHEME:
2586 ShowCompanyLiveryWindow((CompanyID)this->window_number, INVALID_GROUP);
2587 break;
2589 case WID_C_PRESIDENT_NAME:
2590 this->query_widget = WID_C_PRESIDENT_NAME;
2591 SetDParam(0, this->window_number);
2592 ShowQueryString(STR_PRESIDENT_NAME, STR_COMPANY_VIEW_PRESIDENT_S_NAME_QUERY_CAPTION, MAX_LENGTH_PRESIDENT_NAME_CHARS, this, CS_ALPHANUMERAL, QSF_ENABLE_DEFAULT | QSF_LEN_IN_CHARS);
2593 break;
2595 case WID_C_COMPANY_NAME:
2596 this->query_widget = WID_C_COMPANY_NAME;
2597 SetDParam(0, this->window_number);
2598 ShowQueryString(STR_COMPANY_NAME, STR_COMPANY_VIEW_COMPANY_NAME_QUERY_CAPTION, MAX_LENGTH_COMPANY_NAME_CHARS, this, CS_ALPHANUMERAL, QSF_ENABLE_DEFAULT | QSF_LEN_IN_CHARS);
2599 break;
2601 case WID_C_VIEW_HQ: {
2602 TileIndex tile = Company::Get((CompanyID)this->window_number)->location_of_HQ;
2603 if (_ctrl_pressed) {
2604 ShowExtraViewportWindow(tile);
2605 } else {
2606 ScrollMainWindowToTile(tile);
2608 break;
2611 case WID_C_BUILD_HQ:
2612 if ((byte)this->window_number != _local_company) return;
2613 if (this->IsWidgetLowered(WID_C_BUILD_HQ)) {
2614 ResetObjectToPlace();
2615 this->RaiseButtons();
2616 break;
2618 SetObjectToPlaceWnd(SPR_CURSOR_HQ, PAL_NONE, HT_RECT, this);
2619 SetTileSelectSize(2, 2);
2620 this->LowerWidget(WID_C_BUILD_HQ);
2621 this->SetWidgetDirty(WID_C_BUILD_HQ);
2622 break;
2624 case WID_C_RELOCATE_HQ:
2625 if (this->IsWidgetLowered(WID_C_RELOCATE_HQ)) {
2626 ResetObjectToPlace();
2627 this->RaiseButtons();
2628 break;
2630 SetObjectToPlaceWnd(SPR_CURSOR_HQ, PAL_NONE, HT_RECT, this);
2631 SetTileSelectSize(2, 2);
2632 this->LowerWidget(WID_C_RELOCATE_HQ);
2633 this->SetWidgetDirty(WID_C_RELOCATE_HQ);
2634 break;
2636 case WID_C_VIEW_INFRASTRUCTURE:
2637 ShowCompanyInfrastructure((CompanyID)this->window_number);
2638 break;
2640 case WID_C_GIVE_MONEY:
2641 this->query_widget = WID_C_GIVE_MONEY;
2642 ShowQueryString(STR_EMPTY, STR_COMPANY_VIEW_GIVE_MONEY_QUERY_CAPTION, 30, this, CS_NUMERAL, QSF_NONE);
2643 break;
2645 case WID_C_BUY_SHARE:
2646 Command<CMD_BUY_SHARE_IN_COMPANY>::Post(STR_ERROR_CAN_T_BUY_25_SHARE_IN_THIS, (CompanyID)this->window_number);
2647 break;
2649 case WID_C_SELL_SHARE:
2650 Command<CMD_SELL_SHARE_IN_COMPANY>::Post(STR_ERROR_CAN_T_SELL_25_SHARE_IN, (CompanyID)this->window_number);
2651 break;
2653 case WID_C_COMPANY_PASSWORD:
2654 if (this->window_number == _local_company) ShowNetworkCompanyPasswordWindow(this);
2655 break;
2657 case WID_C_COMPANY_JOIN: {
2658 this->query_widget = WID_C_COMPANY_JOIN;
2659 CompanyID company = (CompanyID)this->window_number;
2660 if (_network_server) {
2661 NetworkServerDoMove(CLIENT_ID_SERVER, company);
2662 MarkWholeScreenDirty();
2663 } else if (NetworkCompanyIsPassworded(company)) {
2664 /* ask for the password */
2665 ShowQueryString(STR_EMPTY, STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION, NETWORK_PASSWORD_LENGTH, this, CS_ALPHANUMERAL, QSF_PASSWORD);
2666 } else {
2667 /* just send the join command */
2668 NetworkClientRequestMove(company);
2670 break;
2675 void OnHundredthTick() override
2677 /* redraw the window every now and then */
2678 this->SetDirty();
2681 void OnPlaceObject(Point pt, TileIndex tile) override
2683 if (Command<CMD_BUILD_OBJECT>::Post(STR_ERROR_CAN_T_BUILD_COMPANY_HEADQUARTERS, tile, OBJECT_HQ, 0) && !_shift_pressed) {
2684 ResetObjectToPlace();
2685 this->RaiseButtons();
2689 void OnPlaceObjectAbort() override
2691 this->RaiseButtons();
2694 void OnQueryTextFinished(char *str) override
2696 if (str == nullptr) return;
2698 switch (this->query_widget) {
2699 default: NOT_REACHED();
2701 case WID_C_GIVE_MONEY: {
2702 Money money = (Money)(strtoull(str, nullptr, 10) / _currency->rate);
2703 uint32 money_c = Clamp(ClampToI32(money), 0, 20000000); // Clamp between 20 million and 0
2705 Command<CMD_GIVE_MONEY>::Post(STR_ERROR_CAN_T_GIVE_MONEY, money_c, (CompanyID)this->window_number);
2706 break;
2709 case WID_C_PRESIDENT_NAME:
2710 Command<CMD_RENAME_PRESIDENT>::Post(STR_ERROR_CAN_T_CHANGE_PRESIDENT, str);
2711 break;
2713 case WID_C_COMPANY_NAME:
2714 Command<CMD_RENAME_COMPANY>::Post(STR_ERROR_CAN_T_CHANGE_COMPANY_NAME, str);
2715 break;
2717 case WID_C_COMPANY_JOIN:
2718 NetworkClientRequestMove((CompanyID)this->window_number, str);
2719 break;
2725 * Some data on this window has become invalid.
2726 * @param data Information about the changed data.
2727 * @param gui_scope Whether the call is done from GUI scope. You may not do everything when not in GUI scope. See #InvalidateWindowData() for details.
2729 void OnInvalidateData(int data = 0, bool gui_scope = true) override
2731 if (this->window_number == _local_company) return;
2733 if (_settings_game.economy.allow_shares) { // Shares are allowed
2734 const Company *c = Company::Get(this->window_number);
2736 /* If all shares are owned by someone (none by nobody), disable buy button */
2737 this->SetWidgetDisabledState(WID_C_BUY_SHARE, GetAmountOwnedBy(c, INVALID_OWNER) == 0 ||
2738 /* Only 25% left to buy. If the company is human, disable buying it up.. TODO issues! */
2739 (GetAmountOwnedBy(c, INVALID_OWNER) == 1 && !c->is_ai) ||
2740 /* Spectators cannot do anything of course */
2741 _local_company == COMPANY_SPECTATOR);
2743 /* If the company doesn't own any shares, disable sell button */
2744 this->SetWidgetDisabledState(WID_C_SELL_SHARE, (GetAmountOwnedBy(c, _local_company) == 0) ||
2745 /* Spectators cannot do anything of course */
2746 _local_company == COMPANY_SPECTATOR);
2747 } else { // Shares are not allowed, disable buy/sell buttons
2748 this->DisableWidget(WID_C_BUY_SHARE);
2749 this->DisableWidget(WID_C_SELL_SHARE);
2754 static WindowDesc _company_desc(
2755 WDP_AUTO, "company", 0, 0,
2756 WC_COMPANY, WC_NONE,
2758 _nested_company_widgets, lengthof(_nested_company_widgets)
2762 * Show the window with the overview of the company.
2763 * @param company The company to show the window for.
2765 void ShowCompany(CompanyID company)
2767 if (!Company::IsValidID(company)) return;
2769 AllocateWindowDescFront<CompanyWindow>(&_company_desc, company);
2773 * Redraw all windows with company infrastructure counts.
2774 * @param company The company to redraw the windows of.
2776 void DirtyCompanyInfrastructureWindows(CompanyID company)
2778 SetWindowDirty(WC_COMPANY, company);
2779 SetWindowDirty(WC_COMPANY_INFRASTRUCTURE, company);
2782 struct BuyCompanyWindow : Window {
2783 BuyCompanyWindow(WindowDesc *desc, WindowNumber window_number) : Window(desc)
2785 this->InitNested(window_number);
2788 void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override
2790 switch (widget) {
2791 case WID_BC_FACE:
2792 *size = GetSpriteSize(SPR_GRADIENT);
2793 break;
2795 case WID_BC_QUESTION:
2796 const Company *c = Company::Get((CompanyID)this->window_number);
2797 SetDParam(0, c->index);
2798 SetDParam(1, c->bankrupt_value);
2799 size->height = GetStringHeight(STR_BUY_COMPANY_MESSAGE, size->width);
2800 break;
2804 void SetStringParameters(int widget) const override
2806 switch (widget) {
2807 case WID_BC_CAPTION:
2808 SetDParam(0, STR_COMPANY_NAME);
2809 SetDParam(1, Company::Get((CompanyID)this->window_number)->index);
2810 break;
2814 void DrawWidget(const Rect &r, int widget) const override
2816 switch (widget) {
2817 case WID_BC_FACE: {
2818 const Company *c = Company::Get((CompanyID)this->window_number);
2819 DrawCompanyManagerFace(c->face, c->colour, r.left, r.top);
2820 break;
2823 case WID_BC_QUESTION: {
2824 const Company *c = Company::Get((CompanyID)this->window_number);
2825 SetDParam(0, c->index);
2826 SetDParam(1, c->bankrupt_value);
2827 DrawStringMultiLine(r.left, r.right, r.top, r.bottom, STR_BUY_COMPANY_MESSAGE, TC_FROMSTRING, SA_CENTER);
2828 break;
2833 void OnClick(Point pt, int widget, int click_count) override
2835 switch (widget) {
2836 case WID_BC_NO:
2837 this->Close();
2838 break;
2840 case WID_BC_YES:
2841 Command<CMD_BUY_COMPANY>::Post(STR_ERROR_CAN_T_BUY_COMPANY, (CompanyID)this->window_number);
2842 break;
2847 static const NWidgetPart _nested_buy_company_widgets[] = {
2848 NWidget(NWID_HORIZONTAL),
2849 NWidget(WWT_CLOSEBOX, COLOUR_LIGHT_BLUE),
2850 NWidget(WWT_CAPTION, COLOUR_LIGHT_BLUE, WID_BC_CAPTION), SetDataTip(STR_ERROR_MESSAGE_CAPTION_OTHER_COMPANY, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
2851 EndContainer(),
2852 NWidget(WWT_PANEL, COLOUR_LIGHT_BLUE),
2853 NWidget(NWID_VERTICAL), SetPIP(8, 8, 8),
2854 NWidget(NWID_HORIZONTAL), SetPIP(8, 10, 8),
2855 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_BC_FACE), SetFill(0, 1),
2856 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_BC_QUESTION), SetMinimalSize(240, 0), SetFill(1, 1),
2857 EndContainer(),
2858 NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), SetPIP(100, 10, 100),
2859 NWidget(WWT_TEXTBTN, COLOUR_LIGHT_BLUE, WID_BC_NO), SetMinimalSize(60, 12), SetDataTip(STR_QUIT_NO, STR_NULL), SetFill(1, 0),
2860 NWidget(WWT_TEXTBTN, COLOUR_LIGHT_BLUE, WID_BC_YES), SetMinimalSize(60, 12), SetDataTip(STR_QUIT_YES, STR_NULL), SetFill(1, 0),
2861 EndContainer(),
2862 EndContainer(),
2863 EndContainer(),
2866 static WindowDesc _buy_company_desc(
2867 WDP_AUTO, nullptr, 0, 0,
2868 WC_BUY_COMPANY, WC_NONE,
2869 WDF_CONSTRUCTION,
2870 _nested_buy_company_widgets, lengthof(_nested_buy_company_widgets)
2874 * Show the query to buy another company.
2875 * @param company The company to buy.
2877 void ShowBuyCompanyDialog(CompanyID company)
2879 AllocateWindowDescFront<BuyCompanyWindow>(&_buy_company_desc, company);