Rearrange storage of reserved tracks for railway tiles
[openttd/fttd.git] / src / company_gui.cpp
blob1cf38fc521dc243ea7065f082fb70e1a90cb6b00
1 /* $Id$ */
3 /*
4 * This file is part of OpenTTD.
5 * 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.
6 * 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.
7 * 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/>.
8 */
10 /** @file company_gui.cpp %Company related GUIs. */
12 #include "stdafx.h"
13 #include "error.h"
14 #include "gui.h"
15 #include "window_gui.h"
16 #include "textbuf_gui.h"
17 #include "viewport_func.h"
18 #include "company_func.h"
19 #include "command_func.h"
20 #include "network/network.h"
21 #include "network/network_gui.h"
22 #include "network/network_func.h"
23 #include "newgrf.h"
24 #include "company_manager_face.h"
25 #include "strings_func.h"
26 #include "date_func.h"
27 #include "widgets/dropdown_type.h"
28 #include "tilehighlight_func.h"
29 #include "company_base.h"
30 #include "core/geometry_func.hpp"
31 #include "object_type.h"
32 #include "rail.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"
39 #include "widgets/company_widget.h"
42 /** Company GUI constants. */
43 static const uint EXP_LINESPACE = 2; ///< Amount of vertical space for a horizontal (sub-)total line.
44 static const uint EXP_BLOCKSPACE = 10; ///< Amount of vertical space between two blocks of numbers.
46 static void DoSelectCompanyManagerFace(Window *parent);
47 static void ShowCompanyInfrastructure(CompanyID company);
49 /** Standard unsorted list of expenses. */
50 static ExpensesType _expenses_list_1[] = {
51 EXPENSES_CONSTRUCTION,
52 EXPENSES_NEW_VEHICLES,
53 EXPENSES_TRAIN_RUN,
54 EXPENSES_ROADVEH_RUN,
55 EXPENSES_AIRCRAFT_RUN,
56 EXPENSES_SHIP_RUN,
57 EXPENSES_PROPERTY,
58 EXPENSES_TRAIN_INC,
59 EXPENSES_ROADVEH_INC,
60 EXPENSES_AIRCRAFT_INC,
61 EXPENSES_SHIP_INC,
62 EXPENSES_LOAN_INT,
63 EXPENSES_OTHER,
66 /** Grouped list of expenses. */
67 static ExpensesType _expenses_list_2[] = {
68 EXPENSES_TRAIN_INC,
69 EXPENSES_ROADVEH_INC,
70 EXPENSES_AIRCRAFT_INC,
71 EXPENSES_SHIP_INC,
72 INVALID_EXPENSES,
73 EXPENSES_TRAIN_RUN,
74 EXPENSES_ROADVEH_RUN,
75 EXPENSES_AIRCRAFT_RUN,
76 EXPENSES_SHIP_RUN,
77 EXPENSES_PROPERTY,
78 EXPENSES_LOAN_INT,
79 INVALID_EXPENSES,
80 EXPENSES_CONSTRUCTION,
81 EXPENSES_NEW_VEHICLES,
82 EXPENSES_OTHER,
83 INVALID_EXPENSES,
86 /** Expense list container. */
87 struct ExpensesList {
88 const ExpensesType *et; ///< Expenses items.
89 const uint length; ///< Number of items in list.
90 const uint num_subtotals; ///< Number of sub-totals in the list.
92 ExpensesList(ExpensesType *et, int length, int num_subtotals) : et(et), length(length), num_subtotals(num_subtotals)
96 uint GetHeight() const
98 /* heading + line + texts of expenses + sub-totals + total line + total text */
99 return FONT_HEIGHT_NORMAL + EXP_LINESPACE + this->length * FONT_HEIGHT_NORMAL + num_subtotals * (EXP_BLOCKSPACE + EXP_LINESPACE) + EXP_LINESPACE + FONT_HEIGHT_NORMAL;
102 /** Compute width of the expenses categories in pixels. */
103 uint GetCategoriesWidth() const
105 uint width = 0;
106 bool invalid_expenses_measured = false; // Measure 'Total' width only once.
107 for (uint i = 0; i < this->length; i++) {
108 ExpensesType et = this->et[i];
109 if (et == INVALID_EXPENSES) {
110 if (!invalid_expenses_measured) {
111 width = max(width, GetStringBoundingBox(STR_FINANCES_TOTAL_CAPTION).width);
112 invalid_expenses_measured = true;
114 } else {
115 width = max(width, GetStringBoundingBox(STR_FINANCES_SECTION_CONSTRUCTION + et).width);
118 return width;
122 static const ExpensesList _expenses_list_types[] = {
123 ExpensesList(_expenses_list_1, lengthof(_expenses_list_1), 0),
124 ExpensesList(_expenses_list_2, lengthof(_expenses_list_2), 3),
128 * Draw the expenses categories.
129 * @param r Available space for drawing.
130 * @note The environment must provide padding at the left and right of \a r.
132 static void DrawCategories(const Rect &r)
134 int y = r.top;
136 DrawString(r.left, r.right, y, STR_FINANCES_EXPENDITURE_INCOME_TITLE, TC_FROMSTRING, SA_HOR_CENTER, true);
137 y += FONT_HEIGHT_NORMAL + EXP_LINESPACE;
139 int type = _settings_client.gui.expenses_layout;
140 for (uint i = 0; i < _expenses_list_types[type].length; i++) {
141 const ExpensesType et = _expenses_list_types[type].et[i];
142 if (et == INVALID_EXPENSES) {
143 y += EXP_LINESPACE;
144 DrawString(r.left, r.right, y, STR_FINANCES_TOTAL_CAPTION, TC_FROMSTRING, SA_RIGHT);
145 y += FONT_HEIGHT_NORMAL + EXP_BLOCKSPACE;
146 } else {
147 DrawString(r.left, r.right, y, STR_FINANCES_SECTION_CONSTRUCTION + et);
148 y += FONT_HEIGHT_NORMAL;
152 DrawString(r.left, r.right, y + EXP_LINESPACE, STR_FINANCES_TOTAL_CAPTION, TC_FROMSTRING, SA_RIGHT);
156 * Draw an amount of money.
157 * @param amount Amount of money to draw,
158 * @param left Left coordinate of the space to draw in.
159 * @param right Right coordinate of the space to draw in.
160 * @param top Top coordinate of the space to draw in.
162 static void DrawPrice(Money amount, int left, int right, int top)
164 StringID str = STR_FINANCES_NEGATIVE_INCOME;
165 if (amount < 0) {
166 amount = -amount;
167 str++;
169 SetDParam(0, amount);
170 DrawString(left, right, top, str, TC_FROMSTRING, SA_RIGHT);
174 * Draw a column with prices.
175 * @param r Available space for drawing.
176 * @param year Year being drawn.
177 * @param tbl Pointer to table of amounts for \a year.
178 * @note The environment must provide padding at the left and right of \a r.
180 static void DrawYearColumn(const Rect &r, int year, const Money (*tbl)[EXPENSES_END])
182 int y = r.top;
184 SetDParam(0, year);
185 DrawString(r.left, r.right, y, STR_FINANCES_YEAR, TC_FROMSTRING, SA_RIGHT, true);
186 y += FONT_HEIGHT_NORMAL + EXP_LINESPACE;
188 Money sum = 0;
189 Money subtotal = 0;
190 int type = _settings_client.gui.expenses_layout;
191 for (uint i = 0; i < _expenses_list_types[type].length; i++) {
192 const ExpensesType et = _expenses_list_types[type].et[i];
193 if (et == INVALID_EXPENSES) {
194 Money cost = subtotal;
195 subtotal = 0;
196 GfxFillRect(r.left, y, r.right, y, PC_BLACK);
197 y += EXP_LINESPACE;
198 DrawPrice(cost, r.left, r.right, y);
199 y += FONT_HEIGHT_NORMAL + EXP_BLOCKSPACE;
200 } else {
201 Money cost = (*tbl)[et];
202 subtotal += cost;
203 sum += cost;
204 if (cost != 0) DrawPrice(cost, r.left, r.right, y);
205 y += FONT_HEIGHT_NORMAL;
209 GfxFillRect(r.left, y, r.right, y, PC_BLACK);
210 y += EXP_LINESPACE;
211 DrawPrice(sum, r.left, r.right, y);
214 static const NWidgetPart _nested_company_finances_widgets[] = {
215 NWidget(NWID_HORIZONTAL),
216 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
217 NWidget(WWT_CAPTION, COLOUR_GREY, WID_CF_CAPTION), SetDataTip(STR_FINANCES_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
218 NWidget(WWT_IMGBTN, COLOUR_GREY, WID_CF_TOGGLE_SIZE), SetDataTip(SPR_LARGE_SMALL_WINDOW, STR_TOOLTIP_TOGGLE_LARGE_SMALL_WINDOW),
219 NWidget(WWT_SHADEBOX, COLOUR_GREY),
220 NWidget(WWT_STICKYBOX, COLOUR_GREY),
221 EndContainer(),
222 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_CF_SEL_PANEL),
223 NWidget(WWT_PANEL, COLOUR_GREY),
224 NWidget(NWID_HORIZONTAL), SetPadding(WD_FRAMERECT_TOP, WD_FRAMERECT_RIGHT, WD_FRAMERECT_BOTTOM, WD_FRAMERECT_LEFT), SetPIP(0, 9, 0),
225 NWidget(WWT_EMPTY, COLOUR_GREY, WID_CF_EXPS_CATEGORY), SetMinimalSize(120, 0), SetFill(0, 0),
226 NWidget(WWT_EMPTY, COLOUR_GREY, WID_CF_EXPS_PRICE1), SetMinimalSize(86, 0), SetFill(0, 0),
227 NWidget(WWT_EMPTY, COLOUR_GREY, WID_CF_EXPS_PRICE2), SetMinimalSize(86, 0), SetFill(0, 0),
228 NWidget(WWT_EMPTY, COLOUR_GREY, WID_CF_EXPS_PRICE3), SetMinimalSize(86, 0), SetFill(0, 0),
229 EndContainer(),
230 EndContainer(),
231 EndContainer(),
232 NWidget(WWT_PANEL, COLOUR_GREY),
233 NWidget(NWID_HORIZONTAL), SetPadding(WD_FRAMERECT_TOP, WD_FRAMERECT_RIGHT, WD_FRAMERECT_BOTTOM, WD_FRAMERECT_LEFT),
234 NWidget(NWID_VERTICAL), // Vertical column with 'bank balance', 'loan'
235 NWidget(WWT_TEXT, COLOUR_GREY), SetDataTip(STR_FINANCES_BANK_BALANCE_TITLE, STR_NULL), SetFill(1, 0),
236 NWidget(WWT_TEXT, COLOUR_GREY), SetDataTip(STR_FINANCES_LOAN_TITLE, STR_NULL), SetFill(1, 0),
237 NWidget(NWID_SPACER), SetFill(0, 1),
238 EndContainer(),
239 NWidget(NWID_SPACER), SetFill(0, 0), SetMinimalSize(30, 0),
240 NWidget(NWID_VERTICAL), // Vertical column with bank balance amount, loan amount, and total.
241 NWidget(WWT_TEXT, COLOUR_GREY, WID_CF_BALANCE_VALUE), SetDataTip(STR_NULL, STR_NULL),
242 NWidget(WWT_TEXT, COLOUR_GREY, WID_CF_LOAN_VALUE), SetDataTip(STR_NULL, STR_NULL),
243 NWidget(WWT_EMPTY, COLOUR_GREY, WID_CF_LOAN_LINE), SetMinimalSize(0, 2), SetFill(1, 0),
244 NWidget(WWT_TEXT, COLOUR_GREY, WID_CF_TOTAL_VALUE), SetDataTip(STR_NULL, STR_NULL),
245 EndContainer(),
246 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_CF_SEL_MAXLOAN),
247 NWidget(NWID_HORIZONTAL),
248 NWidget(NWID_SPACER), SetFill(0, 1), SetMinimalSize(25, 0),
249 NWidget(NWID_VERTICAL), // Max loan information
250 NWidget(WWT_EMPTY, COLOUR_GREY, WID_CF_MAXLOAN_GAP), SetFill(0, 0),
251 NWidget(WWT_TEXT, COLOUR_GREY, WID_CF_MAXLOAN_VALUE), SetDataTip(STR_FINANCES_MAX_LOAN, STR_NULL),
252 NWidget(NWID_SPACER), SetFill(0, 1),
253 EndContainer(),
254 EndContainer(),
255 EndContainer(),
256 NWidget(NWID_SPACER), SetFill(1, 1),
257 EndContainer(),
258 EndContainer(),
259 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_CF_SEL_BUTTONS),
260 NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
261 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_CF_INCREASE_LOAN), SetFill(1, 0), SetDataTip(STR_FINANCES_BORROW_BUTTON, STR_FINANCES_BORROW_TOOLTIP),
262 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_CF_REPAY_LOAN), SetFill(1, 0), SetDataTip(STR_FINANCES_REPAY_BUTTON, STR_FINANCES_REPAY_TOOLTIP),
263 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_CF_INFRASTRUCTURE), SetFill(1, 0), SetDataTip(STR_FINANCES_INFRASTRUCTURE_BUTTON, STR_COMPANY_VIEW_INFRASTRUCTURE_TOOLTIP),
264 EndContainer(),
265 EndContainer(),
269 * Window class displaying the company finances.
270 * @todo #money_width should be calculated dynamically.
272 struct CompanyFinancesWindow : Window {
273 static Money max_money; ///< The maximum amount of money a company has had this 'run'
274 bool small; ///< Window is toggled to 'small'.
276 CompanyFinancesWindow(WindowDesc *desc, CompanyID company) : Window(desc)
278 this->small = false;
279 this->CreateNestedTree();
280 this->SetupWidgets();
281 this->FinishInitNested(company);
283 this->owner = (Owner)this->window_number;
286 virtual void SetStringParameters(int widget) const
288 switch (widget) {
289 case WID_CF_CAPTION:
290 SetDParam(0, (CompanyID)this->window_number);
291 SetDParam(1, (CompanyID)this->window_number);
292 break;
294 case WID_CF_MAXLOAN_VALUE:
295 SetDParam(0, _economy.max_loan);
296 break;
298 case WID_CF_INCREASE_LOAN:
299 case WID_CF_REPAY_LOAN:
300 SetDParam(0, LOAN_INTERVAL);
301 break;
305 virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
307 int type = _settings_client.gui.expenses_layout;
308 switch (widget) {
309 case WID_CF_EXPS_CATEGORY:
310 size->width = _expenses_list_types[type].GetCategoriesWidth();
311 size->height = _expenses_list_types[type].GetHeight();
312 break;
314 case WID_CF_EXPS_PRICE1:
315 case WID_CF_EXPS_PRICE2:
316 case WID_CF_EXPS_PRICE3:
317 size->height = _expenses_list_types[type].GetHeight();
318 /* FALL THROUGH */
319 case WID_CF_BALANCE_VALUE:
320 case WID_CF_LOAN_VALUE:
321 case WID_CF_TOTAL_VALUE:
322 SetDParamMaxValue(0, CompanyFinancesWindow::max_money);
323 size->width = max(GetStringBoundingBox(STR_FINANCES_NEGATIVE_INCOME).width, GetStringBoundingBox(STR_FINANCES_POSITIVE_INCOME).width) + padding.width;
324 break;
326 case WID_CF_MAXLOAN_GAP:
327 size->height = FONT_HEIGHT_NORMAL;
328 break;
332 virtual void DrawWidget(const Rect &r, int widget) const
334 switch (widget) {
335 case WID_CF_EXPS_CATEGORY:
336 DrawCategories(r);
337 break;
339 case WID_CF_EXPS_PRICE1:
340 case WID_CF_EXPS_PRICE2:
341 case WID_CF_EXPS_PRICE3: {
342 const Company *c = Company::Get((CompanyID)this->window_number);
343 int age = min(_cur_year - c->inaugurated_year, 2);
344 int wid_offset = widget - WID_CF_EXPS_PRICE1;
345 if (wid_offset <= age) {
346 DrawYearColumn(r, _cur_year - (age - wid_offset), c->yearly_expenses + (age - wid_offset));
348 break;
351 case WID_CF_BALANCE_VALUE: {
352 const Company *c = Company::Get((CompanyID)this->window_number);
353 SetDParam(0, c->money);
354 DrawString(r.left, r.right, r.top, STR_FINANCES_TOTAL_CURRENCY, TC_FROMSTRING, SA_RIGHT);
355 break;
358 case WID_CF_LOAN_VALUE: {
359 const Company *c = Company::Get((CompanyID)this->window_number);
360 SetDParam(0, c->current_loan);
361 DrawString(r.left, r.right, r.top, STR_FINANCES_TOTAL_CURRENCY, TC_FROMSTRING, SA_RIGHT);
362 break;
365 case WID_CF_TOTAL_VALUE: {
366 const Company *c = Company::Get((CompanyID)this->window_number);
367 SetDParam(0, c->money - c->current_loan);
368 DrawString(r.left, r.right, r.top, STR_FINANCES_TOTAL_CURRENCY, TC_FROMSTRING, SA_RIGHT);
369 break;
372 case WID_CF_LOAN_LINE:
373 GfxFillRect(r.left, r.top, r.right, r.top, PC_BLACK);
374 break;
379 * Setup the widgets in the nested tree, such that the finances window is displayed properly.
380 * @note After setup, the window must be (re-)initialized.
382 void SetupWidgets()
384 int plane = this->small ? SZSP_NONE : 0;
385 this->GetWidget<NWidgetStacked>(WID_CF_SEL_PANEL)->SetDisplayedPlane(plane);
386 this->GetWidget<NWidgetStacked>(WID_CF_SEL_MAXLOAN)->SetDisplayedPlane(plane);
388 CompanyID company = (CompanyID)this->window_number;
389 plane = (company != _local_company) ? SZSP_NONE : 0;
390 this->GetWidget<NWidgetStacked>(WID_CF_SEL_BUTTONS)->SetDisplayedPlane(plane);
393 virtual void OnPaint()
395 if (!this->IsShaded()) {
396 if (!this->small) {
397 /* Check that the expenses panel height matches the height needed for the layout. */
398 int type = _settings_client.gui.expenses_layout;
399 if (_expenses_list_types[type].GetHeight() != this->GetWidget<NWidgetBase>(WID_CF_EXPS_CATEGORY)->current_y) {
400 this->SetupWidgets();
401 this->ReInit();
402 return;
406 /* Check that the loan buttons are shown only when the user owns the company. */
407 CompanyID company = (CompanyID)this->window_number;
408 int req_plane = (company != _local_company) ? SZSP_NONE : 0;
409 if (req_plane != this->GetWidget<NWidgetStacked>(WID_CF_SEL_BUTTONS)->shown_plane) {
410 this->SetupWidgets();
411 this->ReInit();
412 return;
415 const Company *c = Company::Get(company);
416 this->SetWidgetDisabledState(WID_CF_INCREASE_LOAN, c->current_loan == _economy.max_loan); // Borrow button only shows when there is any more money to loan.
417 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.
420 this->DrawWidgets();
423 virtual void OnClick(Point pt, int widget, int click_count)
425 switch (widget) {
426 case WID_CF_TOGGLE_SIZE: // toggle size
427 this->small = !this->small;
428 this->SetupWidgets();
429 if (this->IsShaded()) {
430 /* Finances window is not resizable, so size hints given during unshading have no effect
431 * on the changed appearance of the window. */
432 this->SetShaded(false);
433 } else {
434 this->ReInit();
436 break;
438 case WID_CF_INCREASE_LOAN: // increase loan
439 DoCommandP(0, 0, _ctrl_pressed, CMD_INCREASE_LOAN | CMD_MSG(STR_ERROR_CAN_T_BORROW_ANY_MORE_MONEY));
440 break;
442 case WID_CF_REPAY_LOAN: // repay loan
443 DoCommandP(0, 0, _ctrl_pressed, CMD_DECREASE_LOAN | CMD_MSG(STR_ERROR_CAN_T_REPAY_LOAN));
444 break;
446 case WID_CF_INFRASTRUCTURE: // show infrastructure details
447 ShowCompanyInfrastructure((CompanyID)this->window_number);
448 break;
452 virtual void OnHundredthTick()
454 const Company *c = Company::Get((CompanyID)this->window_number);
455 if (c->money > CompanyFinancesWindow::max_money) {
456 CompanyFinancesWindow::max_money = max(c->money * 2, CompanyFinancesWindow::max_money * 4);
457 this->SetupWidgets();
458 this->ReInit();
463 /** First conservative estimate of the maximum amount of money */
464 Money CompanyFinancesWindow::max_money = INT32_MAX;
466 static WindowDesc _company_finances_desc(
467 WDP_AUTO, "company_finances", 0, 0,
468 WC_FINANCES, WC_NONE,
470 _nested_company_finances_widgets, lengthof(_nested_company_finances_widgets)
474 * Open the finances window of a company.
475 * @param company Company to show finances of.
476 * @pre is company a valid company.
478 void ShowCompanyFinances(CompanyID company)
480 if (!Company::IsValidID(company)) return;
481 if (BringWindowToFrontById(WC_FINANCES, company)) return;
483 new CompanyFinancesWindow(&_company_finances_desc, company);
486 /* List of colours for the livery window */
487 static const StringID _colour_dropdown[] = {
488 STR_COLOUR_DARK_BLUE,
489 STR_COLOUR_PALE_GREEN,
490 STR_COLOUR_PINK,
491 STR_COLOUR_YELLOW,
492 STR_COLOUR_RED,
493 STR_COLOUR_LIGHT_BLUE,
494 STR_COLOUR_GREEN,
495 STR_COLOUR_DARK_GREEN,
496 STR_COLOUR_BLUE,
497 STR_COLOUR_CREAM,
498 STR_COLOUR_MAUVE,
499 STR_COLOUR_PURPLE,
500 STR_COLOUR_ORANGE,
501 STR_COLOUR_BROWN,
502 STR_COLOUR_GREY,
503 STR_COLOUR_WHITE,
506 /* Association of liveries to livery classes */
507 static const LiveryClass _livery_class[LS_END] = {
508 LC_OTHER,
509 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,
510 LC_ROAD, LC_ROAD,
511 LC_SHIP, LC_SHIP,
512 LC_AIRCRAFT, LC_AIRCRAFT, LC_AIRCRAFT,
513 LC_ROAD, LC_ROAD,
516 class DropDownListColourItem : public DropDownListItem {
517 public:
518 DropDownListColourItem(int result, bool masked) : DropDownListItem(result, masked) {}
520 virtual ~DropDownListColourItem() {}
522 StringID String() const
524 return _colour_dropdown[this->result];
527 uint Height(uint width) const
529 return max(FONT_HEIGHT_NORMAL, 14);
532 bool Selectable() const
534 return true;
537 void Draw(int left, int right, int top, int bottom, bool sel, int bg_colour) const
539 bool rtl = _current_text_dir == TD_RTL;
540 DrawSprite(SPR_VEH_BUS_SIDE_VIEW, PALETTE_RECOLOUR_START + this->result, rtl ? right - 16 : left + 16, top + 7);
541 DrawString(rtl ? left + 2 : left + 32, rtl ? right - 32 : right - 2, top + max(0, 13 - FONT_HEIGHT_NORMAL), this->String(), sel ? TC_WHITE : TC_BLACK);
545 /** Company livery colour scheme window. */
546 struct SelectCompanyLiveryWindow : public Window {
547 private:
548 uint32 sel;
549 LiveryClass livery_class;
550 Dimension square;
551 Dimension box;
552 uint line_height;
554 void ShowColourDropDownMenu(uint32 widget)
556 uint32 used_colours = 0;
557 const Livery *livery;
558 LiveryScheme scheme;
560 /* Disallow other company colours for the primary colour */
561 if (HasBit(this->sel, LS_DEFAULT) && widget == WID_SCL_PRI_COL_DROPDOWN) {
562 const Company *c;
563 FOR_ALL_COMPANIES(c) {
564 if (c->index != _local_company) SetBit(used_colours, c->colour);
568 /* Get the first selected livery to use as the default dropdown item */
569 for (scheme = LS_BEGIN; scheme < LS_END; scheme++) {
570 if (HasBit(this->sel, scheme)) break;
572 if (scheme == LS_END) scheme = LS_DEFAULT;
573 livery = &Company::Get((CompanyID)this->window_number)->livery[scheme];
575 DropDownList *list = new DropDownList();
576 for (uint i = 0; i < lengthof(_colour_dropdown); i++) {
577 *list->Append() = new DropDownListColourItem(i, HasBit(used_colours, i));
580 ShowDropDownList(this, list, widget == WID_SCL_PRI_COL_DROPDOWN ? livery->colour1 : livery->colour2, widget);
583 public:
584 SelectCompanyLiveryWindow(WindowDesc *desc, CompanyID company) : Window(desc)
586 this->livery_class = LC_OTHER;
587 this->sel = 1;
589 this->square = GetSpriteSize(SPR_SQUARE);
590 this->box = maxdim(GetSpriteSize(SPR_BOX_CHECKED), GetSpriteSize(SPR_BOX_EMPTY));
591 this->line_height = max(max(this->square.height, this->box.height), (uint)FONT_HEIGHT_NORMAL) + 4;
593 this->InitNested(company);
594 this->owner = company;
595 this->LowerWidget(WID_SCL_CLASS_GENERAL);
596 this->InvalidateData(1);
599 virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
601 switch (widget) {
602 case WID_SCL_SPACER_DROPDOWN: {
603 /* The matrix widget below needs enough room to print all the schemes. */
604 Dimension d = {0, 0};
605 for (LiveryScheme scheme = LS_DEFAULT; scheme < LS_END; scheme++) {
606 d = maxdim(d, GetStringBoundingBox(STR_LIVERY_DEFAULT + scheme));
608 size->width = max(size->width, 5 + this->box.width + d.width + WD_FRAMERECT_RIGHT);
609 break;
612 case WID_SCL_MATRIX: {
613 uint livery_height = 0;
614 for (LiveryScheme scheme = LS_DEFAULT; scheme < LS_END; scheme++) {
615 if (_livery_class[scheme] == this->livery_class && HasBit(_loaded_newgrf_features.used_liveries, scheme)) {
616 livery_height++;
619 size->height = livery_height * this->line_height;
620 this->GetWidget<NWidgetCore>(WID_SCL_MATRIX)->widget_data = (livery_height << MAT_ROW_START) | (1 << MAT_COL_START);
621 break;
624 case WID_SCL_SEC_COL_DROPDOWN:
625 if (!_loaded_newgrf_features.has_2CC) {
626 size->width = 0;
627 break;
629 /* FALL THROUGH */
630 case WID_SCL_PRI_COL_DROPDOWN: {
631 int padding = this->square.width + NWidgetScrollbar::GetVerticalDimension().width + 10;
632 for (const StringID *id = _colour_dropdown; id != endof(_colour_dropdown); id++) {
633 size->width = max(size->width, GetStringBoundingBox(*id).width + padding);
635 break;
640 virtual void OnPaint()
642 /* Disable dropdown controls if no scheme is selected */
643 this->SetWidgetDisabledState(WID_SCL_PRI_COL_DROPDOWN, this->sel == 0);
644 this->SetWidgetDisabledState(WID_SCL_SEC_COL_DROPDOWN, this->sel == 0);
646 this->DrawWidgets();
649 virtual void SetStringParameters(int widget) const
651 switch (widget) {
652 case WID_SCL_PRI_COL_DROPDOWN:
653 case WID_SCL_SEC_COL_DROPDOWN: {
654 const Company *c = Company::Get((CompanyID)this->window_number);
655 LiveryScheme scheme = LS_DEFAULT;
657 if (this->sel != 0) {
658 for (scheme = LS_BEGIN; scheme < LS_END; scheme++) {
659 if (HasBit(this->sel, scheme)) break;
661 if (scheme == LS_END) scheme = LS_DEFAULT;
663 SetDParam(0, STR_COLOUR_DARK_BLUE + ((widget == WID_SCL_PRI_COL_DROPDOWN) ? c->livery[scheme].colour1 : c->livery[scheme].colour2));
664 break;
669 virtual void DrawWidget(const Rect &r, int widget) const
671 if (widget != WID_SCL_MATRIX) return;
673 bool rtl = _current_text_dir == TD_RTL;
675 /* Horizontal coordinates of scheme name column. */
676 const NWidgetBase *nwi = this->GetWidget<NWidgetBase>(WID_SCL_SPACER_DROPDOWN);
677 int sch_left = nwi->pos_x;
678 int sch_right = sch_left + nwi->current_x - 1;
679 /* Horizontal coordinates of first dropdown. */
680 nwi = this->GetWidget<NWidgetBase>(WID_SCL_PRI_COL_DROPDOWN);
681 int pri_left = nwi->pos_x;
682 int pri_right = pri_left + nwi->current_x - 1;
683 /* Horizontal coordinates of second dropdown. */
684 nwi = this->GetWidget<NWidgetBase>(WID_SCL_SEC_COL_DROPDOWN);
685 int sec_left = nwi->pos_x;
686 int sec_right = sec_left + nwi->current_x - 1;
688 int text_left = (rtl ? (uint)WD_FRAMERECT_LEFT : (this->box.width + 5));
689 int text_right = (rtl ? (this->box.width + 5) : (uint)WD_FRAMERECT_RIGHT);
691 int box_offs = (this->line_height - this->box.height) / 2;
692 int square_offs = (this->line_height - this->square.height) / 2 + 1;
693 int text_offs = (this->line_height - FONT_HEIGHT_NORMAL) / 2 + 1;
695 int y = r.top;
696 const Company *c = Company::Get((CompanyID)this->window_number);
697 for (LiveryScheme scheme = LS_DEFAULT; scheme < LS_END; scheme++) {
698 if (_livery_class[scheme] == this->livery_class && HasBit(_loaded_newgrf_features.used_liveries, scheme)) {
699 bool sel = HasBit(this->sel, scheme) != 0;
701 /* Optional check box + scheme name. */
702 if (scheme != LS_DEFAULT) {
703 DrawSprite(c->livery[scheme].in_use ? SPR_BOX_CHECKED : SPR_BOX_EMPTY, PAL_NONE, (rtl ? sch_right - (this->box.width + 5) + WD_FRAMERECT_RIGHT : sch_left) + WD_FRAMERECT_LEFT, y + box_offs);
705 DrawString(sch_left + text_left, sch_right - text_right, y + text_offs, STR_LIVERY_DEFAULT + scheme, sel ? TC_WHITE : TC_BLACK);
707 /* Text below the first dropdown. */
708 DrawSprite(SPR_SQUARE, GENERAL_SPRITE_COLOUR(c->livery[scheme].colour1), (rtl ? pri_right - (this->box.width + 5) + WD_FRAMERECT_RIGHT : pri_left) + WD_FRAMERECT_LEFT, y + square_offs);
709 DrawString(pri_left + text_left, pri_right - text_right, y + text_offs, STR_COLOUR_DARK_BLUE + c->livery[scheme].colour1, sel ? TC_WHITE : TC_GOLD);
711 /* Text below the second dropdown. */
712 if (sec_right > sec_left) { // Second dropdown has non-zero size.
713 DrawSprite(SPR_SQUARE, GENERAL_SPRITE_COLOUR(c->livery[scheme].colour2), (rtl ? sec_right - (this->box.width + 5) + WD_FRAMERECT_RIGHT : sec_left) + WD_FRAMERECT_LEFT, y + square_offs);
714 DrawString(sec_left + text_left, sec_right - text_right, y + text_offs, STR_COLOUR_DARK_BLUE + c->livery[scheme].colour2, sel ? TC_WHITE : TC_GOLD);
717 y += this->line_height;
722 virtual void OnClick(Point pt, int widget, int click_count)
724 switch (widget) {
725 /* Livery Class buttons */
726 case WID_SCL_CLASS_GENERAL:
727 case WID_SCL_CLASS_RAIL:
728 case WID_SCL_CLASS_ROAD:
729 case WID_SCL_CLASS_SHIP:
730 case WID_SCL_CLASS_AIRCRAFT:
731 this->RaiseWidget(this->livery_class + WID_SCL_CLASS_GENERAL);
732 this->livery_class = (LiveryClass)(widget - WID_SCL_CLASS_GENERAL);
733 this->LowerWidget(this->livery_class + WID_SCL_CLASS_GENERAL);
735 /* Select the first item in the list */
736 this->sel = 0;
737 for (LiveryScheme scheme = LS_DEFAULT; scheme < LS_END; scheme++) {
738 if (_livery_class[scheme] == this->livery_class && HasBit(_loaded_newgrf_features.used_liveries, scheme)) {
739 this->sel = 1 << scheme;
740 break;
744 this->ReInit();
745 break;
747 case WID_SCL_PRI_COL_DROPDOWN: // First colour dropdown
748 ShowColourDropDownMenu(WID_SCL_PRI_COL_DROPDOWN);
749 break;
751 case WID_SCL_SEC_COL_DROPDOWN: // Second colour dropdown
752 ShowColourDropDownMenu(WID_SCL_SEC_COL_DROPDOWN);
753 break;
755 case WID_SCL_MATRIX: {
756 const NWidgetBase *wid = this->GetWidget<NWidgetBase>(WID_SCL_MATRIX);
757 LiveryScheme j = (LiveryScheme)((pt.y - wid->pos_y) / this->line_height);
759 for (LiveryScheme scheme = LS_BEGIN; scheme <= j; scheme++) {
760 if (_livery_class[scheme] != this->livery_class || !HasBit(_loaded_newgrf_features.used_liveries, scheme)) j++;
761 if (scheme >= LS_END) return;
763 if (j >= LS_END) return;
765 /* If clicking on the left edge, toggle using the livery */
766 if (_current_text_dir == TD_RTL ? pt.x - wid->pos_x > wid->current_x - (this->box.width + 5) : pt.x - wid->pos_x < (this->box.width + 5)) {
767 DoCommandP(0, j | (2 << 8), !Company::Get((CompanyID)this->window_number)->livery[j].in_use, CMD_SET_COMPANY_COLOUR);
770 if (_ctrl_pressed) {
771 ToggleBit(this->sel, j);
772 } else {
773 this->sel = 1 << j;
775 this->SetDirty();
776 break;
781 virtual void OnDropdownSelect(int widget, int index)
783 for (LiveryScheme scheme = LS_DEFAULT; scheme < LS_END; scheme++) {
784 /* Changed colour for the selected scheme, or all visible schemes if CTRL is pressed. */
785 if (HasBit(this->sel, scheme) || (_ctrl_pressed && _livery_class[scheme] == this->livery_class && HasBit(_loaded_newgrf_features.used_liveries, scheme))) {
786 DoCommandP(0, scheme | (widget == WID_SCL_PRI_COL_DROPDOWN ? 0 : 256), index, CMD_SET_COMPANY_COLOUR);
792 * Some data on this window has become invalid.
793 * @param data Information about the changed data.
794 * @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.
796 virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
798 if (!gui_scope) return;
799 this->SetWidgetsDisabledState(true, WID_SCL_CLASS_RAIL, WID_SCL_CLASS_ROAD, WID_SCL_CLASS_SHIP, WID_SCL_CLASS_AIRCRAFT, WIDGET_LIST_END);
801 bool current_class_valid = this->livery_class == LC_OTHER;
802 if (_settings_client.gui.liveries == LIT_ALL || (_settings_client.gui.liveries == LIT_COMPANY && this->window_number == _local_company)) {
803 for (LiveryScheme scheme = LS_DEFAULT; scheme < LS_END; scheme++) {
804 if (HasBit(_loaded_newgrf_features.used_liveries, scheme)) {
805 if (_livery_class[scheme] == this->livery_class) current_class_valid = true;
806 this->EnableWidget(WID_SCL_CLASS_GENERAL + _livery_class[scheme]);
807 } else {
808 ClrBit(this->sel, scheme);
813 if (!current_class_valid) {
814 Point pt = {0, 0};
815 this->OnClick(pt, WID_SCL_CLASS_GENERAL, 1);
816 } else if (data == 0) {
817 this->ReInit();
822 static const NWidgetPart _nested_select_company_livery_widgets [] = {
823 NWidget(NWID_HORIZONTAL),
824 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
825 NWidget(WWT_CAPTION, COLOUR_GREY, WID_SCL_CAPTION), SetDataTip(STR_LIVERY_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
826 EndContainer(),
827 NWidget(NWID_HORIZONTAL),
828 NWidget(WWT_IMGBTN, COLOUR_GREY, WID_SCL_CLASS_GENERAL), SetMinimalSize(22, 22), SetFill(0, 1), SetDataTip(SPR_IMG_COMPANY_GENERAL, STR_LIVERY_GENERAL_TOOLTIP),
829 NWidget(WWT_IMGBTN, COLOUR_GREY, WID_SCL_CLASS_RAIL), SetMinimalSize(22, 22), SetFill(0, 1), SetDataTip(SPR_IMG_TRAINLIST, STR_LIVERY_TRAIN_TOOLTIP),
830 NWidget(WWT_IMGBTN, COLOUR_GREY, WID_SCL_CLASS_ROAD), SetMinimalSize(22, 22), SetFill(0, 1), SetDataTip(SPR_IMG_TRUCKLIST, STR_LIVERY_ROAD_VEHICLE_TOOLTIP),
831 NWidget(WWT_IMGBTN, COLOUR_GREY, WID_SCL_CLASS_SHIP), SetMinimalSize(22, 22), SetFill(0, 1), SetDataTip(SPR_IMG_SHIPLIST, STR_LIVERY_SHIP_TOOLTIP),
832 NWidget(WWT_IMGBTN, COLOUR_GREY, WID_SCL_CLASS_AIRCRAFT), SetMinimalSize(22, 22), SetFill(0, 1), SetDataTip(SPR_IMG_AIRPLANESLIST, STR_LIVERY_AIRCRAFT_TOOLTIP),
833 NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(90, 22), SetFill(1, 1), EndContainer(),
834 EndContainer(),
835 NWidget(NWID_HORIZONTAL),
836 NWidget(WWT_PANEL, COLOUR_GREY, WID_SCL_SPACER_DROPDOWN), SetMinimalSize(150, 12), SetFill(1, 1), EndContainer(),
837 NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_SCL_PRI_COL_DROPDOWN), SetMinimalSize(125, 12), SetFill(0, 1), SetDataTip(STR_BLACK_STRING, STR_LIVERY_PRIMARY_TOOLTIP),
838 NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_SCL_SEC_COL_DROPDOWN), SetMinimalSize(125, 12), SetFill(0, 1),
839 SetDataTip(STR_BLACK_STRING, STR_LIVERY_SECONDARY_TOOLTIP),
840 EndContainer(),
841 NWidget(WWT_MATRIX, COLOUR_GREY, WID_SCL_MATRIX), SetMinimalSize(275, 15), SetFill(1, 0), SetMatrixDataTip(1, 1, STR_LIVERY_PANEL_TOOLTIP),
844 static WindowDesc _select_company_livery_desc(
845 WDP_AUTO, "company_livery", 0, 0,
846 WC_COMPANY_COLOUR, WC_NONE,
848 _nested_select_company_livery_widgets, lengthof(_nested_select_company_livery_widgets)
852 * Draws the face of a company manager's face.
853 * @param cmf the company manager's face
854 * @param colour the (background) colour of the gradient
855 * @param x x-position to draw the face
856 * @param y y-position to draw the face
858 void DrawCompanyManagerFace(CompanyManagerFace cmf, int colour, int x, int y)
860 GenderEthnicity ge = (GenderEthnicity)GetCompanyManagerFaceBits(cmf, CMFV_GEN_ETHN, GE_WM);
862 bool has_moustache = !HasBit(ge, GENDER_FEMALE) && GetCompanyManagerFaceBits(cmf, CMFV_HAS_MOUSTACHE, ge) != 0;
863 bool has_tie_earring = !HasBit(ge, GENDER_FEMALE) || GetCompanyManagerFaceBits(cmf, CMFV_HAS_TIE_EARRING, ge) != 0;
864 bool has_glasses = GetCompanyManagerFaceBits(cmf, CMFV_HAS_GLASSES, ge) != 0;
865 PaletteID pal;
867 /* Modify eye colour palette only if 2 or more valid values exist */
868 if (_cmf_info[CMFV_EYE_COLOUR].valid_values[ge] < 2) {
869 pal = PAL_NONE;
870 } else {
871 switch (GetCompanyManagerFaceBits(cmf, CMFV_EYE_COLOUR, ge)) {
872 default: NOT_REACHED();
873 case 0: pal = PALETTE_TO_BROWN; break;
874 case 1: pal = PALETTE_TO_BLUE; break;
875 case 2: pal = PALETTE_TO_GREEN; break;
879 /* Draw the gradient (background) */
880 DrawSprite(SPR_GRADIENT, GENERAL_SPRITE_COLOUR(colour), x, y);
882 for (CompanyManagerFaceVariable cmfv = CMFV_CHEEKS; cmfv < CMFV_END; cmfv++) {
883 switch (cmfv) {
884 case CMFV_MOUSTACHE: if (!has_moustache) continue; break;
885 case CMFV_LIPS: // FALL THROUGH
886 case CMFV_NOSE: if (has_moustache) continue; break;
887 case CMFV_TIE_EARRING: if (!has_tie_earring) continue; break;
888 case CMFV_GLASSES: if (!has_glasses) continue; break;
889 default: break;
891 DrawSprite(GetCompanyManagerFaceSprite(cmf, cmfv, ge), (cmfv == CMFV_EYEBROWS) ? pal : PAL_NONE, x, y);
895 /** Nested widget description for the company manager face selection dialog */
896 static const NWidgetPart _nested_select_company_manager_face_widgets[] = {
897 NWidget(NWID_HORIZONTAL),
898 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
899 NWidget(WWT_CAPTION, COLOUR_GREY, WID_SCMF_CAPTION), SetDataTip(STR_FACE_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
900 NWidget(WWT_IMGBTN, COLOUR_GREY, WID_SCMF_TOGGLE_LARGE_SMALL), SetDataTip(SPR_LARGE_SMALL_WINDOW, STR_FACE_ADVANCED_TOOLTIP),
901 EndContainer(),
902 NWidget(WWT_PANEL, COLOUR_GREY, WID_SCMF_SELECT_FACE),
903 NWidget(NWID_SPACER), SetMinimalSize(0, 2),
904 NWidget(NWID_HORIZONTAL), SetPIP(2, 2, 2),
905 NWidget(NWID_VERTICAL),
906 NWidget(NWID_HORIZONTAL),
907 NWidget(NWID_SPACER), SetFill(1, 0),
908 NWidget(WWT_EMPTY, COLOUR_GREY, WID_SCMF_FACE), SetMinimalSize(92, 119),
909 NWidget(NWID_SPACER), SetFill(1, 0),
910 EndContainer(),
911 NWidget(NWID_SPACER), SetMinimalSize(0, 2),
912 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_RANDOM_NEW_FACE), SetFill(1, 0), SetDataTip(STR_FACE_NEW_FACE_BUTTON, STR_FACE_NEW_FACE_TOOLTIP),
913 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_SCMF_SEL_LOADSAVE), // Load/number/save buttons under the portrait in the advanced view.
914 NWidget(NWID_VERTICAL),
915 NWidget(NWID_SPACER), SetMinimalSize(0, 5), SetFill(0, 1),
916 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_LOAD), SetFill(1, 0), SetDataTip(STR_FACE_LOAD, STR_FACE_LOAD_TOOLTIP),
917 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_FACECODE), SetFill(1, 0), SetDataTip(STR_FACE_FACECODE, STR_FACE_FACECODE_TOOLTIP),
918 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_SAVE), SetFill(1, 0), SetDataTip(STR_FACE_SAVE, STR_FACE_SAVE_TOOLTIP),
919 NWidget(NWID_SPACER), SetMinimalSize(0, 5), SetFill(0, 1),
920 EndContainer(),
921 EndContainer(),
922 EndContainer(),
923 NWidget(NWID_VERTICAL),
924 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_TOGGLE_LARGE_SMALL_BUTTON), SetFill(1, 0), SetDataTip(STR_FACE_ADVANCED, STR_FACE_ADVANCED_TOOLTIP),
925 NWidget(NWID_SPACER), SetMinimalSize(0, 2),
926 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_SCMF_SEL_MALEFEMALE), // Simple male/female face setting.
927 NWidget(NWID_VERTICAL),
928 NWidget(NWID_SPACER), SetFill(0, 1),
929 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_SCMF_MALE), SetFill(1, 0), SetDataTip(STR_FACE_MALE_BUTTON, STR_FACE_MALE_TOOLTIP),
930 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_SCMF_FEMALE), SetFill(1, 0), SetDataTip(STR_FACE_FEMALE_BUTTON, STR_FACE_FEMALE_TOOLTIP),
931 NWidget(NWID_SPACER), SetFill(0, 1),
932 EndContainer(),
933 EndContainer(),
934 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_SCMF_SEL_PARTS), // Advanced face parts setting.
935 NWidget(NWID_VERTICAL),
936 NWidget(NWID_SPACER), SetMinimalSize(0, 2),
937 NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
938 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_SCMF_MALE2), SetFill(1, 0), SetDataTip(STR_FACE_MALE_BUTTON, STR_FACE_MALE_TOOLTIP),
939 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_SCMF_FEMALE2), SetFill(1, 0), SetDataTip(STR_FACE_FEMALE_BUTTON, STR_FACE_FEMALE_TOOLTIP),
940 EndContainer(),
941 NWidget(NWID_SPACER), SetMinimalSize(0, 2),
942 NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
943 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_SCMF_ETHNICITY_EUR), SetFill(1, 0), SetDataTip(STR_FACE_EUROPEAN, STR_FACE_SELECT_EUROPEAN),
944 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_SCMF_ETHNICITY_AFR), SetFill(1, 0), SetDataTip(STR_FACE_AFRICAN, STR_FACE_SELECT_AFRICAN),
945 EndContainer(),
946 NWidget(NWID_SPACER), SetMinimalSize(0, 4),
947 NWidget(NWID_HORIZONTAL),
948 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SCMF_HAS_MOUSTACHE_EARRING_TEXT), SetFill(1, 0),
949 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_HAS_MOUSTACHE_EARRING), SetDataTip(STR_EMPTY, STR_FACE_MOUSTACHE_EARRING_TOOLTIP),
950 EndContainer(),
951 NWidget(NWID_HORIZONTAL),
952 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SCMF_HAS_GLASSES_TEXT), SetFill(1, 0),
953 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_HAS_GLASSES), SetDataTip(STR_EMPTY, STR_FACE_GLASSES_TOOLTIP),
954 EndContainer(),
955 NWidget(NWID_SPACER), SetMinimalSize(0, 2), SetFill(1, 0),
956 NWidget(NWID_HORIZONTAL),
957 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SCMF_HAIR_TEXT), SetFill(1, 0),
958 NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_HAIR_L), SetDataTip(AWV_DECREASE, STR_FACE_HAIR_TOOLTIP),
959 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_HAIR), SetDataTip(STR_EMPTY, STR_FACE_HAIR_TOOLTIP),
960 NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_HAIR_R), SetDataTip(AWV_INCREASE, STR_FACE_HAIR_TOOLTIP),
961 EndContainer(),
962 NWidget(NWID_HORIZONTAL),
963 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SCMF_EYEBROWS_TEXT), SetFill(1, 0),
964 NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_EYEBROWS_L), SetDataTip(AWV_DECREASE, STR_FACE_EYEBROWS_TOOLTIP),
965 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_EYEBROWS), SetDataTip(STR_EMPTY, STR_FACE_EYEBROWS_TOOLTIP),
966 NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_EYEBROWS_R), SetDataTip(AWV_INCREASE, STR_FACE_EYEBROWS_TOOLTIP),
967 EndContainer(),
968 NWidget(NWID_HORIZONTAL),
969 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SCMF_EYECOLOUR_TEXT), SetFill(1, 0),
970 NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_EYECOLOUR_L), SetDataTip(AWV_DECREASE, STR_FACE_EYECOLOUR_TOOLTIP),
971 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_EYECOLOUR), SetDataTip(STR_EMPTY, STR_FACE_EYECOLOUR_TOOLTIP),
972 NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_EYECOLOUR_R), SetDataTip(AWV_INCREASE, STR_FACE_EYECOLOUR_TOOLTIP),
973 EndContainer(),
974 NWidget(NWID_HORIZONTAL),
975 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SCMF_GLASSES_TEXT), SetFill(1, 0),
976 NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_GLASSES_L), SetDataTip(AWV_DECREASE, STR_FACE_GLASSES_TOOLTIP_2),
977 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_GLASSES), SetDataTip(STR_EMPTY, STR_FACE_GLASSES_TOOLTIP_2),
978 NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_GLASSES_R), SetDataTip(AWV_INCREASE, STR_FACE_GLASSES_TOOLTIP_2),
979 EndContainer(),
980 NWidget(NWID_HORIZONTAL),
981 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SCMF_NOSE_TEXT), SetFill(1, 0),
982 NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_NOSE_L), SetDataTip(AWV_DECREASE, STR_FACE_NOSE_TOOLTIP),
983 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_NOSE), SetDataTip(STR_EMPTY, STR_FACE_NOSE_TOOLTIP),
984 NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_NOSE_R), SetDataTip(AWV_INCREASE, STR_FACE_NOSE_TOOLTIP),
985 EndContainer(),
986 NWidget(NWID_HORIZONTAL),
987 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SCMF_LIPS_MOUSTACHE_TEXT), SetFill(1, 0),
988 NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_LIPS_MOUSTACHE_L), SetDataTip(AWV_DECREASE, STR_FACE_LIPS_MOUSTACHE_TOOLTIP),
989 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_LIPS_MOUSTACHE), SetDataTip(STR_EMPTY, STR_FACE_LIPS_MOUSTACHE_TOOLTIP),
990 NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_LIPS_MOUSTACHE_R), SetDataTip(AWV_INCREASE, STR_FACE_LIPS_MOUSTACHE_TOOLTIP),
991 EndContainer(),
992 NWidget(NWID_HORIZONTAL),
993 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SCMF_CHIN_TEXT), SetFill(1, 0),
994 NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_CHIN_L), SetDataTip(AWV_DECREASE, STR_FACE_CHIN_TOOLTIP),
995 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_CHIN), SetDataTip(STR_EMPTY, STR_FACE_CHIN_TOOLTIP),
996 NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_CHIN_R), SetDataTip(AWV_INCREASE, STR_FACE_CHIN_TOOLTIP),
997 EndContainer(),
998 NWidget(NWID_HORIZONTAL),
999 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SCMF_JACKET_TEXT), SetFill(1, 0),
1000 NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_JACKET_L), SetDataTip(AWV_DECREASE, STR_FACE_JACKET_TOOLTIP),
1001 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_JACKET), SetDataTip(STR_EMPTY, STR_FACE_JACKET_TOOLTIP),
1002 NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_JACKET_R), SetDataTip(AWV_INCREASE, STR_FACE_JACKET_TOOLTIP),
1003 EndContainer(),
1004 NWidget(NWID_HORIZONTAL),
1005 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SCMF_COLLAR_TEXT), SetFill(1, 0),
1006 NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_COLLAR_L), SetDataTip(AWV_DECREASE, STR_FACE_COLLAR_TOOLTIP),
1007 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_COLLAR), SetDataTip(STR_EMPTY, STR_FACE_COLLAR_TOOLTIP),
1008 NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_COLLAR_R), SetDataTip(AWV_INCREASE, STR_FACE_COLLAR_TOOLTIP),
1009 EndContainer(),
1010 NWidget(NWID_HORIZONTAL),
1011 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SCMF_TIE_EARRING_TEXT), SetFill(1, 0),
1012 NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_TIE_EARRING_L), SetDataTip(AWV_DECREASE, STR_FACE_TIE_EARRING_TOOLTIP),
1013 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_TIE_EARRING), SetDataTip(STR_EMPTY, STR_FACE_TIE_EARRING_TOOLTIP),
1014 NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_TIE_EARRING_R), SetDataTip(AWV_INCREASE, STR_FACE_TIE_EARRING_TOOLTIP),
1015 EndContainer(),
1016 NWidget(NWID_SPACER), SetFill(0, 1),
1017 EndContainer(),
1018 EndContainer(),
1019 EndContainer(),
1020 EndContainer(),
1021 NWidget(NWID_SPACER), SetMinimalSize(0, 2),
1022 EndContainer(),
1023 NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
1024 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_CANCEL), SetFill(1, 0), SetDataTip(STR_BUTTON_CANCEL, STR_FACE_CANCEL_TOOLTIP),
1025 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_ACCEPT), SetFill(1, 0), SetDataTip(STR_BUTTON_OK, STR_FACE_OK_TOOLTIP),
1026 EndContainer(),
1029 /** Management class for customizing the face of the company manager. */
1030 class SelectCompanyManagerFaceWindow : public Window
1032 CompanyManagerFace face; ///< company manager face bits
1033 bool advanced; ///< advanced company manager face selection window
1035 GenderEthnicity ge; ///< Gender and ethnicity.
1036 bool is_female; ///< Female face.
1037 bool is_moust_male; ///< Male face with a moustache.
1039 Dimension yesno_dim; ///< Dimension of a yes/no button of a part in the advanced face window.
1040 Dimension number_dim; ///< Dimension of a number widget of a part in the advanced face window.
1042 static const StringID PART_TEXTS_IS_FEMALE[]; ///< Strings depending on #is_female, used to describe parts (2 entries for a part).
1043 static const StringID PART_TEXTS[]; ///< Fixed strings to describe parts of the face.
1046 * Draw dynamic a label to the left of the button and a value in the button
1048 * @param widget_index index of this widget in the window
1049 * @param val the value which will be draw
1050 * @param is_bool_widget is it a bool button
1052 void DrawFaceStringLabel(byte widget_index, uint8 val, bool is_bool_widget) const
1054 StringID str;
1055 const NWidgetCore *nwi_widget = this->GetWidget<NWidgetCore>(widget_index);
1056 if (!nwi_widget->IsDisabled()) {
1057 if (is_bool_widget) {
1058 /* if it a bool button write yes or no */
1059 str = (val != 0) ? STR_FACE_YES : STR_FACE_NO;
1060 } else {
1061 /* else write the value + 1 */
1062 SetDParam(0, val + 1);
1063 str = STR_JUST_INT;
1066 /* Draw the value/bool in white (0xC). If the button clicked adds 1px to x and y text coordinates (IsWindowWidgetLowered()). */
1067 DrawString(nwi_widget->pos_x + nwi_widget->IsLowered(), nwi_widget->pos_x + nwi_widget->current_x - 1 - nwi_widget->IsLowered(),
1068 nwi_widget->pos_y + 1 + nwi_widget->IsLowered(), str, TC_WHITE, SA_HOR_CENTER);
1072 void UpdateData()
1074 this->ge = (GenderEthnicity)GB(this->face, _cmf_info[CMFV_GEN_ETHN].offset, _cmf_info[CMFV_GEN_ETHN].length); // get the gender and ethnicity
1075 this->is_female = HasBit(this->ge, GENDER_FEMALE); // get the gender: 0 == male and 1 == female
1076 this->is_moust_male = !is_female && GetCompanyManagerFaceBits(this->face, CMFV_HAS_MOUSTACHE, this->ge) != 0; // is a male face with moustache
1079 public:
1080 SelectCompanyManagerFaceWindow(WindowDesc *desc, Window *parent) : Window(desc)
1082 this->advanced = false;
1083 this->CreateNestedTree();
1084 this->SelectDisplayPlanes(this->advanced);
1085 this->FinishInitNested(parent->window_number);
1086 this->parent = parent;
1087 this->owner = (Owner)this->window_number;
1088 this->face = Company::Get((CompanyID)this->window_number)->face;
1090 this->UpdateData();
1094 * 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.
1095 * @param advanced Display advanced face management window.
1097 void SelectDisplayPlanes(bool advanced)
1099 this->GetWidget<NWidgetStacked>(WID_SCMF_SEL_LOADSAVE)->SetDisplayedPlane(advanced ? 0 : SZSP_NONE);
1100 this->GetWidget<NWidgetStacked>(WID_SCMF_SEL_PARTS)->SetDisplayedPlane(advanced ? 0 : SZSP_NONE);
1101 this->GetWidget<NWidgetStacked>(WID_SCMF_SEL_MALEFEMALE)->SetDisplayedPlane(advanced ? SZSP_NONE : 0);
1102 this->GetWidget<NWidgetCore>(WID_SCMF_RANDOM_NEW_FACE)->widget_data = advanced ? STR_MAPGEN_RANDOM : STR_FACE_NEW_FACE_BUTTON;
1104 NWidgetCore *wi = this->GetWidget<NWidgetCore>(WID_SCMF_TOGGLE_LARGE_SMALL_BUTTON);
1105 if (advanced) {
1106 wi->SetDataTip(STR_FACE_SIMPLE, STR_FACE_SIMPLE_TOOLTIP);
1107 } else {
1108 wi->SetDataTip(STR_FACE_ADVANCED, STR_FACE_ADVANCED_TOOLTIP);
1112 virtual void OnInit()
1114 /* Size of the boolean yes/no button. */
1115 Dimension yesno_dim = maxdim(GetStringBoundingBox(STR_FACE_YES), GetStringBoundingBox(STR_FACE_NO));
1116 yesno_dim.width += WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT;
1117 yesno_dim.height += WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
1118 /* Size of the number button + arrows. */
1119 Dimension number_dim = {0, 0};
1120 for (int val = 1; val <= 12; val++) {
1121 SetDParam(0, val);
1122 number_dim = maxdim(number_dim, GetStringBoundingBox(STR_JUST_INT));
1124 uint arrows_width = GetSpriteSize(SPR_ARROW_LEFT).width + GetSpriteSize(SPR_ARROW_RIGHT).width + 2 * (WD_IMGBTN_LEFT + WD_IMGBTN_RIGHT);
1125 number_dim.width += WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT + arrows_width;
1126 number_dim.height += WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
1127 /* Compute width of both buttons. */
1128 yesno_dim.width = max(yesno_dim.width, number_dim.width);
1129 number_dim.width = yesno_dim.width - arrows_width;
1131 this->yesno_dim = yesno_dim;
1132 this->number_dim = number_dim;
1135 virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
1137 switch (widget) {
1138 case WID_SCMF_HAS_MOUSTACHE_EARRING_TEXT:
1139 case WID_SCMF_TIE_EARRING_TEXT: {
1140 int offset = (widget - WID_SCMF_HAS_MOUSTACHE_EARRING_TEXT) * 2;
1141 *size = maxdim(GetStringBoundingBox(PART_TEXTS_IS_FEMALE[offset]), GetStringBoundingBox(PART_TEXTS_IS_FEMALE[offset + 1]));
1142 size->width += WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT;
1143 size->height += WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
1144 break;
1147 case WID_SCMF_LIPS_MOUSTACHE_TEXT:
1148 *size = maxdim(GetStringBoundingBox(STR_FACE_LIPS), GetStringBoundingBox(STR_FACE_MOUSTACHE));
1149 size->width += WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT;
1150 size->height += WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
1151 break;
1153 case WID_SCMF_HAS_GLASSES_TEXT:
1154 case WID_SCMF_HAIR_TEXT:
1155 case WID_SCMF_EYEBROWS_TEXT:
1156 case WID_SCMF_EYECOLOUR_TEXT:
1157 case WID_SCMF_GLASSES_TEXT:
1158 case WID_SCMF_NOSE_TEXT:
1159 case WID_SCMF_CHIN_TEXT:
1160 case WID_SCMF_JACKET_TEXT:
1161 case WID_SCMF_COLLAR_TEXT:
1162 *size = GetStringBoundingBox(PART_TEXTS[widget - WID_SCMF_HAS_GLASSES_TEXT]);
1163 size->width += WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT;
1164 size->height += WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
1165 break;
1167 case WID_SCMF_HAS_MOUSTACHE_EARRING:
1168 case WID_SCMF_HAS_GLASSES:
1169 *size = this->yesno_dim;
1170 break;
1172 case WID_SCMF_EYECOLOUR:
1173 case WID_SCMF_CHIN:
1174 case WID_SCMF_EYEBROWS:
1175 case WID_SCMF_LIPS_MOUSTACHE:
1176 case WID_SCMF_NOSE:
1177 case WID_SCMF_HAIR:
1178 case WID_SCMF_JACKET:
1179 case WID_SCMF_COLLAR:
1180 case WID_SCMF_TIE_EARRING:
1181 case WID_SCMF_GLASSES:
1182 *size = this->number_dim;
1183 break;
1187 virtual void OnPaint()
1189 /* lower the non-selected gender button */
1190 this->SetWidgetsLoweredState(!this->is_female, WID_SCMF_MALE, WID_SCMF_MALE2, WIDGET_LIST_END);
1191 this->SetWidgetsLoweredState( this->is_female, WID_SCMF_FEMALE, WID_SCMF_FEMALE2, WIDGET_LIST_END);
1193 /* advanced company manager face selection window */
1195 /* lower the non-selected ethnicity button */
1196 this->SetWidgetLoweredState(WID_SCMF_ETHNICITY_EUR, !HasBit(this->ge, ETHNICITY_BLACK));
1197 this->SetWidgetLoweredState(WID_SCMF_ETHNICITY_AFR, HasBit(this->ge, ETHNICITY_BLACK));
1200 /* Disable dynamically the widgets which CompanyManagerFaceVariable has less than 2 options
1201 * (or in other words you haven't any choice).
1202 * If the widgets depend on a HAS-variable and this is false the widgets will be disabled, too. */
1204 /* Eye colour buttons */
1205 this->SetWidgetsDisabledState(_cmf_info[CMFV_EYE_COLOUR].valid_values[this->ge] < 2,
1206 WID_SCMF_EYECOLOUR, WID_SCMF_EYECOLOUR_L, WID_SCMF_EYECOLOUR_R, WIDGET_LIST_END);
1208 /* Chin buttons */
1209 this->SetWidgetsDisabledState(_cmf_info[CMFV_CHIN].valid_values[this->ge] < 2,
1210 WID_SCMF_CHIN, WID_SCMF_CHIN_L, WID_SCMF_CHIN_R, WIDGET_LIST_END);
1212 /* Eyebrows buttons */
1213 this->SetWidgetsDisabledState(_cmf_info[CMFV_EYEBROWS].valid_values[this->ge] < 2,
1214 WID_SCMF_EYEBROWS, WID_SCMF_EYEBROWS_L, WID_SCMF_EYEBROWS_R, WIDGET_LIST_END);
1216 /* Lips or (if it a male face with a moustache) moustache buttons */
1217 this->SetWidgetsDisabledState(_cmf_info[this->is_moust_male ? CMFV_MOUSTACHE : CMFV_LIPS].valid_values[this->ge] < 2,
1218 WID_SCMF_LIPS_MOUSTACHE, WID_SCMF_LIPS_MOUSTACHE_L, WID_SCMF_LIPS_MOUSTACHE_R, WIDGET_LIST_END);
1220 /* Nose buttons | male faces with moustache haven't any nose options */
1221 this->SetWidgetsDisabledState(_cmf_info[CMFV_NOSE].valid_values[this->ge] < 2 || this->is_moust_male,
1222 WID_SCMF_NOSE, WID_SCMF_NOSE_L, WID_SCMF_NOSE_R, WIDGET_LIST_END);
1224 /* Hair buttons */
1225 this->SetWidgetsDisabledState(_cmf_info[CMFV_HAIR].valid_values[this->ge] < 2,
1226 WID_SCMF_HAIR, WID_SCMF_HAIR_L, WID_SCMF_HAIR_R, WIDGET_LIST_END);
1228 /* Jacket buttons */
1229 this->SetWidgetsDisabledState(_cmf_info[CMFV_JACKET].valid_values[this->ge] < 2,
1230 WID_SCMF_JACKET, WID_SCMF_JACKET_L, WID_SCMF_JACKET_R, WIDGET_LIST_END);
1232 /* Collar buttons */
1233 this->SetWidgetsDisabledState(_cmf_info[CMFV_COLLAR].valid_values[this->ge] < 2,
1234 WID_SCMF_COLLAR, WID_SCMF_COLLAR_L, WID_SCMF_COLLAR_R, WIDGET_LIST_END);
1236 /* Tie/earring buttons | female faces without earring haven't any earring options */
1237 this->SetWidgetsDisabledState(_cmf_info[CMFV_TIE_EARRING].valid_values[this->ge] < 2 ||
1238 (this->is_female && GetCompanyManagerFaceBits(this->face, CMFV_HAS_TIE_EARRING, this->ge) == 0),
1239 WID_SCMF_TIE_EARRING, WID_SCMF_TIE_EARRING_L, WID_SCMF_TIE_EARRING_R, WIDGET_LIST_END);
1241 /* Glasses buttons | faces without glasses haven't any glasses options */
1242 this->SetWidgetsDisabledState(_cmf_info[CMFV_GLASSES].valid_values[this->ge] < 2 || GetCompanyManagerFaceBits(this->face, CMFV_HAS_GLASSES, this->ge) == 0,
1243 WID_SCMF_GLASSES, WID_SCMF_GLASSES_L, WID_SCMF_GLASSES_R, WIDGET_LIST_END);
1245 this->DrawWidgets();
1248 virtual void DrawWidget(const Rect &r, int widget) const
1250 switch (widget) {
1251 case WID_SCMF_HAS_MOUSTACHE_EARRING_TEXT:
1252 case WID_SCMF_TIE_EARRING_TEXT: {
1253 StringID str = PART_TEXTS_IS_FEMALE[(widget - WID_SCMF_HAS_MOUSTACHE_EARRING_TEXT) * 2 + this->is_female];
1254 DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, r.top + WD_FRAMERECT_TOP, str, TC_GOLD, SA_RIGHT);
1255 break;
1258 case WID_SCMF_LIPS_MOUSTACHE_TEXT:
1259 DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, r.top + WD_FRAMERECT_TOP, (this->is_moust_male) ? STR_FACE_MOUSTACHE : STR_FACE_LIPS, TC_GOLD, SA_RIGHT);
1260 break;
1262 case WID_SCMF_HAS_GLASSES_TEXT:
1263 case WID_SCMF_HAIR_TEXT:
1264 case WID_SCMF_EYEBROWS_TEXT:
1265 case WID_SCMF_EYECOLOUR_TEXT:
1266 case WID_SCMF_GLASSES_TEXT:
1267 case WID_SCMF_NOSE_TEXT:
1268 case WID_SCMF_CHIN_TEXT:
1269 case WID_SCMF_JACKET_TEXT:
1270 case WID_SCMF_COLLAR_TEXT:
1271 DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, r.top + WD_FRAMERECT_TOP, PART_TEXTS[widget - WID_SCMF_HAS_GLASSES_TEXT], TC_GOLD, SA_RIGHT);
1272 break;
1275 case WID_SCMF_HAS_MOUSTACHE_EARRING:
1276 if (this->is_female) { // Only for female faces
1277 this->DrawFaceStringLabel(WID_SCMF_HAS_MOUSTACHE_EARRING, GetCompanyManagerFaceBits(this->face, CMFV_HAS_TIE_EARRING, this->ge), true);
1278 } else { // Only for male faces
1279 this->DrawFaceStringLabel(WID_SCMF_HAS_MOUSTACHE_EARRING, GetCompanyManagerFaceBits(this->face, CMFV_HAS_MOUSTACHE, this->ge), true);
1281 break;
1283 case WID_SCMF_TIE_EARRING:
1284 this->DrawFaceStringLabel(WID_SCMF_TIE_EARRING, GetCompanyManagerFaceBits(this->face, CMFV_TIE_EARRING, this->ge), false);
1285 break;
1287 case WID_SCMF_LIPS_MOUSTACHE:
1288 if (this->is_moust_male) { // Only for male faces with moustache
1289 this->DrawFaceStringLabel(WID_SCMF_LIPS_MOUSTACHE, GetCompanyManagerFaceBits(this->face, CMFV_MOUSTACHE, this->ge), false);
1290 } else { // Only for female faces or male faces without moustache
1291 this->DrawFaceStringLabel(WID_SCMF_LIPS_MOUSTACHE, GetCompanyManagerFaceBits(this->face, CMFV_LIPS, this->ge), false);
1293 break;
1295 case WID_SCMF_HAS_GLASSES:
1296 this->DrawFaceStringLabel(WID_SCMF_HAS_GLASSES, GetCompanyManagerFaceBits(this->face, CMFV_HAS_GLASSES, this->ge), true );
1297 break;
1299 case WID_SCMF_HAIR:
1300 this->DrawFaceStringLabel(WID_SCMF_HAIR, GetCompanyManagerFaceBits(this->face, CMFV_HAIR, this->ge), false);
1301 break;
1303 case WID_SCMF_EYEBROWS:
1304 this->DrawFaceStringLabel(WID_SCMF_EYEBROWS, GetCompanyManagerFaceBits(this->face, CMFV_EYEBROWS, this->ge), false);
1305 break;
1307 case WID_SCMF_EYECOLOUR:
1308 this->DrawFaceStringLabel(WID_SCMF_EYECOLOUR, GetCompanyManagerFaceBits(this->face, CMFV_EYE_COLOUR, this->ge), false);
1309 break;
1311 case WID_SCMF_GLASSES:
1312 this->DrawFaceStringLabel(WID_SCMF_GLASSES, GetCompanyManagerFaceBits(this->face, CMFV_GLASSES, this->ge), false);
1313 break;
1315 case WID_SCMF_NOSE:
1316 this->DrawFaceStringLabel(WID_SCMF_NOSE, GetCompanyManagerFaceBits(this->face, CMFV_NOSE, this->ge), false);
1317 break;
1319 case WID_SCMF_CHIN:
1320 this->DrawFaceStringLabel(WID_SCMF_CHIN, GetCompanyManagerFaceBits(this->face, CMFV_CHIN, this->ge), false);
1321 break;
1323 case WID_SCMF_JACKET:
1324 this->DrawFaceStringLabel(WID_SCMF_JACKET, GetCompanyManagerFaceBits(this->face, CMFV_JACKET, this->ge), false);
1325 break;
1327 case WID_SCMF_COLLAR:
1328 this->DrawFaceStringLabel(WID_SCMF_COLLAR, GetCompanyManagerFaceBits(this->face, CMFV_COLLAR, this->ge), false);
1329 break;
1331 case WID_SCMF_FACE:
1332 DrawCompanyManagerFace(this->face, Company::Get((CompanyID)this->window_number)->colour, r.left, r.top);
1333 break;
1337 virtual void OnClick(Point pt, int widget, int click_count)
1339 switch (widget) {
1340 /* Toggle size, advanced/simple face selection */
1341 case WID_SCMF_TOGGLE_LARGE_SMALL:
1342 case WID_SCMF_TOGGLE_LARGE_SMALL_BUTTON:
1343 this->advanced = !this->advanced;
1344 this->SelectDisplayPlanes(this->advanced);
1345 this->ReInit();
1346 break;
1348 /* OK button */
1349 case WID_SCMF_ACCEPT:
1350 DoCommandP(0, 0, this->face, CMD_SET_COMPANY_MANAGER_FACE);
1351 /* FALL THROUGH */
1353 /* Cancel button */
1354 case WID_SCMF_CANCEL:
1355 delete this;
1356 break;
1358 /* Load button */
1359 case WID_SCMF_LOAD:
1360 this->face = _company_manager_face;
1361 ScaleAllCompanyManagerFaceBits(this->face);
1362 ShowErrorMessage(STR_FACE_LOAD_DONE, INVALID_STRING_ID, WL_INFO);
1363 this->UpdateData();
1364 this->SetDirty();
1365 break;
1367 /* 'Company manager face number' button, view and/or set company manager face number */
1368 case WID_SCMF_FACECODE:
1369 SetDParam(0, this->face);
1370 ShowQueryString(STR_JUST_INT, STR_FACE_FACECODE_CAPTION, 10 + 1, this, CS_NUMERAL, QSF_NONE);
1371 break;
1373 /* Save button */
1374 case WID_SCMF_SAVE:
1375 _company_manager_face = this->face;
1376 ShowErrorMessage(STR_FACE_SAVE_DONE, INVALID_STRING_ID, WL_INFO);
1377 break;
1379 /* Toggle gender (male/female) button */
1380 case WID_SCMF_MALE:
1381 case WID_SCMF_FEMALE:
1382 case WID_SCMF_MALE2:
1383 case WID_SCMF_FEMALE2:
1384 SetCompanyManagerFaceBits(this->face, CMFV_GENDER, this->ge, (widget == WID_SCMF_FEMALE || widget == WID_SCMF_FEMALE2));
1385 ScaleAllCompanyManagerFaceBits(this->face);
1386 this->UpdateData();
1387 this->SetDirty();
1388 break;
1390 /* Randomize face button */
1391 case WID_SCMF_RANDOM_NEW_FACE:
1392 RandomCompanyManagerFaceBits(this->face, this->ge, this->advanced);
1393 this->UpdateData();
1394 this->SetDirty();
1395 break;
1397 /* Toggle ethnicity (european/african) button */
1398 case WID_SCMF_ETHNICITY_EUR:
1399 case WID_SCMF_ETHNICITY_AFR:
1400 SetCompanyManagerFaceBits(this->face, CMFV_ETHNICITY, this->ge, widget - WID_SCMF_ETHNICITY_EUR);
1401 ScaleAllCompanyManagerFaceBits(this->face);
1402 this->UpdateData();
1403 this->SetDirty();
1404 break;
1406 default:
1407 /* Here all buttons from WID_SCMF_HAS_MOUSTACHE_EARRING to WID_SCMF_GLASSES_R are handled.
1408 * First it checks which CompanyManagerFaceVariable is being changed, and then either
1409 * a: invert the value for boolean variables, or
1410 * b: it checks inside of IncreaseCompanyManagerFaceBits() if a left (_L) butten is pressed and then decrease else increase the variable */
1411 if (widget >= WID_SCMF_HAS_MOUSTACHE_EARRING && widget <= WID_SCMF_GLASSES_R) {
1412 CompanyManagerFaceVariable cmfv; // which CompanyManagerFaceVariable shall be edited
1414 if (widget < WID_SCMF_EYECOLOUR_L) { // Bool buttons
1415 switch (widget - WID_SCMF_HAS_MOUSTACHE_EARRING) {
1416 default: NOT_REACHED();
1417 case 0: cmfv = this->is_female ? CMFV_HAS_TIE_EARRING : CMFV_HAS_MOUSTACHE; break; // Has earring/moustache button
1418 case 1: cmfv = CMFV_HAS_GLASSES; break; // Has glasses button
1420 SetCompanyManagerFaceBits(this->face, cmfv, this->ge, !GetCompanyManagerFaceBits(this->face, cmfv, this->ge));
1421 ScaleAllCompanyManagerFaceBits(this->face);
1422 } else { // Value buttons
1423 switch ((widget - WID_SCMF_EYECOLOUR_L) / 3) {
1424 default: NOT_REACHED();
1425 case 0: cmfv = CMFV_EYE_COLOUR; break; // Eye colour buttons
1426 case 1: cmfv = CMFV_CHIN; break; // Chin buttons
1427 case 2: cmfv = CMFV_EYEBROWS; break; // Eyebrows buttons
1428 case 3: cmfv = this->is_moust_male ? CMFV_MOUSTACHE : CMFV_LIPS; break; // Moustache or lips buttons
1429 case 4: cmfv = CMFV_NOSE; break; // Nose buttons
1430 case 5: cmfv = CMFV_HAIR; break; // Hair buttons
1431 case 6: cmfv = CMFV_JACKET; break; // Jacket buttons
1432 case 7: cmfv = CMFV_COLLAR; break; // Collar buttons
1433 case 8: cmfv = CMFV_TIE_EARRING; break; // Tie/earring buttons
1434 case 9: cmfv = CMFV_GLASSES; break; // Glasses buttons
1436 /* 0 == left (_L), 1 == middle or 2 == right (_R) - button click */
1437 IncreaseCompanyManagerFaceBits(this->face, cmfv, this->ge, (((widget - WID_SCMF_EYECOLOUR_L) % 3) != 0) ? 1 : -1);
1439 this->UpdateData();
1440 this->SetDirty();
1442 break;
1446 virtual void OnQueryTextFinished(char *str)
1448 if (str == NULL) return;
1449 /* Set a new company manager face number */
1450 if (!StrEmpty(str)) {
1451 this->face = strtoul(str, NULL, 10);
1452 ScaleAllCompanyManagerFaceBits(this->face);
1453 ShowErrorMessage(STR_FACE_FACECODE_SET, INVALID_STRING_ID, WL_INFO);
1454 this->UpdateData();
1455 this->SetDirty();
1456 } else {
1457 ShowErrorMessage(STR_FACE_FACECODE_ERR, INVALID_STRING_ID, WL_INFO);
1462 /** Both text values of parts of the face that depend on the #is_female boolean value. */
1463 const StringID SelectCompanyManagerFaceWindow::PART_TEXTS_IS_FEMALE[] = {
1464 STR_FACE_MOUSTACHE, STR_FACE_EARRING, // WID_SCMF_HAS_MOUSTACHE_EARRING_TEXT
1465 STR_FACE_TIE, STR_FACE_EARRING, // WID_SCMF_TIE_EARRING_TEXT
1468 /** Textual names for parts of the face. */
1469 const StringID SelectCompanyManagerFaceWindow::PART_TEXTS[] = {
1470 STR_FACE_GLASSES, // WID_SCMF_HAS_GLASSES_TEXT
1471 STR_FACE_HAIR, // WID_SCMF_HAIR_TEXT
1472 STR_FACE_EYEBROWS, // WID_SCMF_EYEBROWS_TEXT
1473 STR_FACE_EYECOLOUR, // WID_SCMF_EYECOLOUR_TEXT
1474 STR_FACE_GLASSES, // WID_SCMF_GLASSES_TEXT
1475 STR_FACE_NOSE, // WID_SCMF_NOSE_TEXT
1476 STR_FACE_CHIN, // WID_SCMF_CHIN_TEXT
1477 STR_FACE_JACKET, // WID_SCMF_JACKET_TEXT
1478 STR_FACE_COLLAR, // WID_SCMF_COLLAR_TEXT
1481 /** Company manager face selection window description */
1482 static WindowDesc _select_company_manager_face_desc(
1483 WDP_AUTO, "company_face", 0, 0,
1484 WC_COMPANY_MANAGER_FACE, WC_NONE,
1485 WDF_CONSTRUCTION,
1486 _nested_select_company_manager_face_widgets, lengthof(_nested_select_company_manager_face_widgets)
1490 * Open the simple/advanced company manager face selection window
1492 * @param parent the parent company window
1493 * @param adv simple or advanced face selection window
1494 * @param top previous top position of the window
1495 * @param left previous left position of the window
1497 static void DoSelectCompanyManagerFace(Window *parent)
1499 if (!Company::IsValidID((CompanyID)parent->window_number)) return;
1501 if (BringWindowToFrontById(WC_COMPANY_MANAGER_FACE, parent->window_number)) return;
1502 new SelectCompanyManagerFaceWindow(&_select_company_manager_face_desc, parent);
1505 static const NWidgetPart _nested_company_infrastructure_widgets[] = {
1506 NWidget(NWID_HORIZONTAL),
1507 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
1508 NWidget(WWT_CAPTION, COLOUR_GREY, WID_CI_CAPTION), SetDataTip(STR_COMPANY_INFRASTRUCTURE_VIEW_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
1509 NWidget(WWT_SHADEBOX, COLOUR_GREY),
1510 NWidget(WWT_STICKYBOX, COLOUR_GREY),
1511 EndContainer(),
1512 NWidget(WWT_PANEL, COLOUR_GREY),
1513 NWidget(NWID_VERTICAL), SetPIP(WD_FRAMERECT_TOP, 4, WD_FRAMETEXT_BOTTOM),
1514 NWidget(NWID_HORIZONTAL), SetPIP(2, 4, 2),
1515 NWidget(WWT_EMPTY, COLOUR_GREY, WID_CI_RAIL_DESC), SetMinimalTextLines(2, 0), SetFill(1, 0),
1516 NWidget(WWT_EMPTY, COLOUR_GREY, WID_CI_RAIL_COUNT), SetMinimalTextLines(2, 0), SetFill(0, 1),
1517 EndContainer(),
1518 NWidget(NWID_HORIZONTAL), SetPIP(2, 4, 2),
1519 NWidget(WWT_EMPTY, COLOUR_GREY, WID_CI_ROAD_DESC), SetMinimalTextLines(2, 0), SetFill(1, 0),
1520 NWidget(WWT_EMPTY, COLOUR_GREY, WID_CI_ROAD_COUNT), SetMinimalTextLines(2, 0), SetFill(0, 1),
1521 EndContainer(),
1522 NWidget(NWID_HORIZONTAL), SetPIP(2, 4, 2),
1523 NWidget(WWT_EMPTY, COLOUR_GREY, WID_CI_WATER_DESC), SetMinimalTextLines(2, 0), SetFill(1, 0),
1524 NWidget(WWT_EMPTY, COLOUR_GREY, WID_CI_WATER_COUNT), SetMinimalTextLines(2, 0), SetFill(0, 1),
1525 EndContainer(),
1526 NWidget(NWID_HORIZONTAL), SetPIP(2, 4, 2),
1527 NWidget(WWT_EMPTY, COLOUR_GREY, WID_CI_STATION_DESC), SetMinimalTextLines(3, 0), SetFill(1, 0),
1528 NWidget(WWT_EMPTY, COLOUR_GREY, WID_CI_STATION_COUNT), SetMinimalTextLines(3, 0), SetFill(0, 1),
1529 EndContainer(),
1530 NWidget(NWID_HORIZONTAL), SetPIP(2, 4, 2),
1531 NWidget(WWT_EMPTY, COLOUR_GREY, WID_CI_TOTAL_DESC), SetFill(1, 0),
1532 NWidget(WWT_EMPTY, COLOUR_GREY, WID_CI_TOTAL), SetFill(0, 1),
1533 EndContainer(),
1534 EndContainer(),
1535 EndContainer(),
1539 * Window with detailed information about the company's infrastructure.
1541 struct CompanyInfrastructureWindow : Window
1543 RailTypes railtypes; ///< Valid railtypes.
1544 RoadTypes roadtypes; ///< Valid roadtypes.
1546 uint total_width; ///< String width of the total cost line.
1548 CompanyInfrastructureWindow(WindowDesc *desc, WindowNumber window_number) : Window(desc)
1550 this->UpdateRailRoadTypes();
1552 this->InitNested(window_number);
1553 this->owner = (Owner)this->window_number;
1556 void UpdateRailRoadTypes()
1558 this->railtypes = RAILTYPES_NONE;
1559 this->roadtypes = ROADTYPES_ROAD; // Road is always available.
1561 /* Find the used railtypes. */
1562 Engine *e;
1563 FOR_ALL_ENGINES_OF_TYPE(e, VEH_TRAIN) {
1564 if (!HasBit(e->info.climates, _settings_game.game_creation.landscape)) continue;
1566 this->railtypes |= GetRailTypeInfo(e->u.rail.railtype)->introduces_railtypes;
1569 /* Get the date introduced railtypes as well. */
1570 this->railtypes = AddDateIntroducedRailTypes(this->railtypes, MAX_DAY);
1572 /* Tram is only visible when there will be a tram. */
1573 FOR_ALL_ENGINES_OF_TYPE(e, VEH_ROAD) {
1574 if (!HasBit(e->info.climates, _settings_game.game_creation.landscape)) continue;
1575 if (!HasBit(e->info.misc_flags, EF_ROAD_TRAM)) continue;
1577 this->roadtypes |= ROADTYPES_TRAM;
1578 break;
1582 /** Get total infrastructure maintenance cost. */
1583 Money GetTotalMaintenanceCost() const
1585 const Company *c = Company::Get((CompanyID)this->window_number);
1586 Money total;
1588 uint32 rail_total = c->infrastructure.GetRailTotal();
1589 for (RailType rt = RAILTYPE_BEGIN; rt != RAILTYPE_END; rt++) {
1590 if (HasBit(this->railtypes, rt)) total += RailMaintenanceCost(rt, c->infrastructure.rail[rt], rail_total);
1592 total += SignalMaintenanceCost(c->infrastructure.signal);
1594 if (HasBit(this->roadtypes, ROADTYPE_ROAD)) total += RoadMaintenanceCost(ROADTYPE_ROAD, c->infrastructure.road[ROADTYPE_ROAD]);
1595 if (HasBit(this->roadtypes, ROADTYPE_TRAM)) total += RoadMaintenanceCost(ROADTYPE_TRAM, c->infrastructure.road[ROADTYPE_TRAM]);
1597 total += CanalMaintenanceCost(c->infrastructure.water);
1598 total += StationMaintenanceCost(c->infrastructure.station);
1599 total += AirportMaintenanceCost(c->index);
1601 return total;
1604 virtual void SetStringParameters(int widget) const
1606 switch (widget) {
1607 case WID_CI_CAPTION:
1608 SetDParam(0, (CompanyID)this->window_number);
1609 break;
1613 virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
1615 const Company *c = Company::Get((CompanyID)this->window_number);
1617 switch (widget) {
1618 case WID_CI_RAIL_DESC: {
1619 uint lines = 1;
1621 size->width = max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_RAIL_SECT).width);
1623 for (RailType rt = RAILTYPE_BEGIN; rt < RAILTYPE_END; rt++) {
1624 if (HasBit(this->railtypes, rt)) {
1625 lines++;
1626 SetDParam(0, GetRailTypeInfo(rt)->strings.name);
1627 size->width = max(size->width, GetStringBoundingBox(STR_WHITE_STRING).width + WD_FRAMERECT_LEFT);
1630 if (this->railtypes != RAILTYPES_NONE) {
1631 lines++;
1632 size->width = max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_SIGNALS).width + WD_FRAMERECT_LEFT);
1635 size->height = max(size->height, lines * FONT_HEIGHT_NORMAL);
1636 break;
1639 case WID_CI_ROAD_DESC: {
1640 uint lines = 1;
1642 size->width = max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_ROAD_SECT).width);
1644 if (HasBit(this->roadtypes, ROADTYPE_ROAD)) {
1645 lines++;
1646 size->width = max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_ROAD).width + WD_FRAMERECT_LEFT);
1648 if (HasBit(this->roadtypes, ROADTYPE_TRAM)) {
1649 lines++;
1650 size->width = max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_TRAMWAY).width + WD_FRAMERECT_LEFT);
1653 size->height = max(size->height, lines * FONT_HEIGHT_NORMAL);
1654 break;
1657 case WID_CI_WATER_DESC:
1658 size->width = max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_WATER_SECT).width);
1659 size->width = max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_CANALS).width + WD_FRAMERECT_LEFT);
1660 break;
1662 case WID_CI_STATION_DESC:
1663 size->width = max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_STATION_SECT).width);
1664 size->width = max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_STATIONS).width + WD_FRAMERECT_LEFT);
1665 size->width = max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_AIRPORTS).width + WD_FRAMERECT_LEFT);
1666 break;
1668 case WID_CI_RAIL_COUNT:
1669 case WID_CI_ROAD_COUNT:
1670 case WID_CI_WATER_COUNT:
1671 case WID_CI_STATION_COUNT:
1672 case WID_CI_TOTAL: {
1673 /* Find the maximum count that is displayed. */
1674 uint32 max_val = 1000; // Some random number to reserve enough space.
1675 Money max_cost = 10000; // Some random number to reserve enough space.
1676 uint32 rail_total = c->infrastructure.GetRailTotal();
1677 for (RailType rt = RAILTYPE_BEGIN; rt < RAILTYPE_END; rt++) {
1678 max_val = max(max_val, c->infrastructure.rail[rt]);
1679 max_cost = max(max_cost, RailMaintenanceCost(rt, c->infrastructure.rail[rt], rail_total));
1681 max_val = max(max_val, c->infrastructure.signal);
1682 max_cost = max(max_cost, SignalMaintenanceCost(c->infrastructure.signal));
1683 for (RoadType rt = ROADTYPE_BEGIN; rt < ROADTYPE_END; rt++) {
1684 max_val = max(max_val, c->infrastructure.road[rt]);
1685 max_cost = max(max_cost, RoadMaintenanceCost(rt, c->infrastructure.road[rt]));
1687 max_val = max(max_val, c->infrastructure.water);
1688 max_cost = max(max_cost, CanalMaintenanceCost(c->infrastructure.water));
1689 max_val = max(max_val, c->infrastructure.station);
1690 max_cost = max(max_cost, StationMaintenanceCost(c->infrastructure.station));
1691 max_val = max(max_val, c->infrastructure.airport);
1692 max_cost = max(max_cost, AirportMaintenanceCost(c->index));
1694 SetDParamMaxValue(0, max_val);
1695 uint count_width = GetStringBoundingBox(STR_WHITE_COMMA).width + 20; // Reserve some wiggle room
1697 if (_settings_game.economy.infrastructure_maintenance) {
1698 SetDParamMaxValue(0, this->GetTotalMaintenanceCost() * 12); // Convert to per year
1699 this->total_width = GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_TOTAL).width + 20;
1700 size->width = max(size->width, this->total_width);
1702 SetDParamMaxValue(0, max_cost * 12); // Convert to per year
1703 count_width += max(this->total_width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_TOTAL).width);
1706 size->width = max(size->width, count_width);
1708 /* Set height of the total line. */
1709 if (widget == WID_CI_TOTAL) {
1710 size->height = _settings_game.economy.infrastructure_maintenance ? max(size->height, EXP_LINESPACE + FONT_HEIGHT_NORMAL) : 0;
1712 break;
1718 * Helper for drawing the counts line.
1719 * @param r The bounds to draw in.
1720 * @param y The y position to draw at.
1721 * @param count The count to show on this line.
1722 * @param monthly_cost The monthly costs.
1724 void DrawCountLine(const Rect &r, int &y, int count, Money monthly_cost) const
1726 SetDParam(0, count);
1727 DrawString(r.left, r.right, y += FONT_HEIGHT_NORMAL, STR_WHITE_COMMA, TC_FROMSTRING, SA_RIGHT);
1729 if (_settings_game.economy.infrastructure_maintenance) {
1730 SetDParam(0, monthly_cost * 12); // Convert to per year
1731 int left = _current_text_dir == TD_RTL ? r.right - this->total_width : r.left;
1732 DrawString(left, left + this->total_width, y, STR_COMPANY_INFRASTRUCTURE_VIEW_TOTAL, TC_FROMSTRING, SA_RIGHT);
1736 virtual void DrawWidget(const Rect &r, int widget) const
1738 const Company *c = Company::Get((CompanyID)this->window_number);
1739 int y = r.top;
1741 int offs_left = _current_text_dir == TD_LTR ? WD_FRAMERECT_LEFT : 0;
1742 int offs_right = _current_text_dir == TD_LTR ? 0 : WD_FRAMERECT_LEFT;
1744 switch (widget) {
1745 case WID_CI_RAIL_DESC:
1746 DrawString(r.left, r.right, y, STR_COMPANY_INFRASTRUCTURE_VIEW_RAIL_SECT);
1748 if (this->railtypes != RAILTYPES_NONE) {
1749 /* Draw name of each valid railtype. */
1750 for (RailType rt = RAILTYPE_BEGIN; rt != RAILTYPE_END; rt++) {
1751 if (HasBit(this->railtypes, rt)) {
1752 SetDParam(0, GetRailTypeInfo(rt)->strings.name);
1753 DrawString(r.left + offs_left, r.right - offs_right, y += FONT_HEIGHT_NORMAL, STR_WHITE_STRING);
1756 DrawString(r.left + offs_left, r.right - offs_right, y += FONT_HEIGHT_NORMAL, STR_COMPANY_INFRASTRUCTURE_VIEW_SIGNALS);
1757 } else {
1758 /* No valid railtype. */
1759 DrawString(r.left + offs_left, r.right - offs_right, y += FONT_HEIGHT_NORMAL, STR_COMPANY_VIEW_INFRASTRUCTURE_NONE);
1762 break;
1764 case WID_CI_RAIL_COUNT: {
1765 /* Draw infrastructure count for each valid railtype. */
1766 uint32 rail_total = c->infrastructure.GetRailTotal();
1767 for (RailType rt = RAILTYPE_BEGIN; rt != RAILTYPE_END; rt++) {
1768 if (HasBit(this->railtypes, rt)) {
1769 this->DrawCountLine(r, y, c->infrastructure.rail[rt], RailMaintenanceCost(rt, c->infrastructure.rail[rt], rail_total));
1772 if (this->railtypes != RAILTYPES_NONE) {
1773 this->DrawCountLine(r, y, c->infrastructure.signal, SignalMaintenanceCost(c->infrastructure.signal));
1775 break;
1778 case WID_CI_ROAD_DESC:
1779 DrawString(r.left, r.right, y, STR_COMPANY_INFRASTRUCTURE_VIEW_ROAD_SECT);
1781 if (this->roadtypes != ROADTYPES_NONE) {
1782 if (HasBit(this->roadtypes, ROADTYPE_ROAD)) DrawString(r.left + offs_left, r.right - offs_right, y += FONT_HEIGHT_NORMAL, STR_COMPANY_INFRASTRUCTURE_VIEW_ROAD);
1783 if (HasBit(this->roadtypes, ROADTYPE_TRAM)) DrawString(r.left + offs_left, r.right - offs_right, y += FONT_HEIGHT_NORMAL, STR_COMPANY_INFRASTRUCTURE_VIEW_TRAMWAY);
1784 } else {
1785 /* No valid roadtypes. */
1786 DrawString(r.left + offs_left, r.right - offs_right, y += FONT_HEIGHT_NORMAL, STR_COMPANY_VIEW_INFRASTRUCTURE_NONE);
1789 break;
1791 case WID_CI_ROAD_COUNT:
1792 if (HasBit(this->roadtypes, ROADTYPE_ROAD)) {
1793 this->DrawCountLine(r, y, c->infrastructure.road[ROADTYPE_ROAD], RoadMaintenanceCost(ROADTYPE_ROAD, c->infrastructure.road[ROADTYPE_ROAD]));
1795 if (HasBit(this->roadtypes, ROADTYPE_TRAM)) {
1796 this->DrawCountLine(r, y, c->infrastructure.road[ROADTYPE_TRAM], RoadMaintenanceCost(ROADTYPE_TRAM, c->infrastructure.road[ROADTYPE_TRAM]));
1798 break;
1800 case WID_CI_WATER_DESC:
1801 DrawString(r.left, r.right, y, STR_COMPANY_INFRASTRUCTURE_VIEW_WATER_SECT);
1802 DrawString(r.left + offs_left, r.right - offs_right, y += FONT_HEIGHT_NORMAL, STR_COMPANY_INFRASTRUCTURE_VIEW_CANALS);
1803 break;
1805 case WID_CI_WATER_COUNT:
1806 this->DrawCountLine(r, y, c->infrastructure.water, CanalMaintenanceCost(c->infrastructure.water));
1807 break;
1809 case WID_CI_TOTAL:
1810 if (_settings_game.economy.infrastructure_maintenance) {
1811 int left = _current_text_dir == TD_RTL ? r.right - this->total_width : r.left;
1812 GfxFillRect(left, y, left + this->total_width, y, PC_WHITE);
1813 y += EXP_LINESPACE;
1814 SetDParam(0, this->GetTotalMaintenanceCost() * 12); // Convert to per year
1815 DrawString(left, left + this->total_width, y, STR_COMPANY_INFRASTRUCTURE_VIEW_TOTAL, TC_FROMSTRING, SA_RIGHT);
1817 break;
1819 case WID_CI_STATION_DESC:
1820 DrawString(r.left, r.right, y, STR_COMPANY_INFRASTRUCTURE_VIEW_STATION_SECT);
1821 DrawString(r.left + offs_left, r.right - offs_right, y += FONT_HEIGHT_NORMAL, STR_COMPANY_INFRASTRUCTURE_VIEW_STATIONS);
1822 DrawString(r.left + offs_left, r.right - offs_right, y += FONT_HEIGHT_NORMAL, STR_COMPANY_INFRASTRUCTURE_VIEW_AIRPORTS);
1823 break;
1825 case WID_CI_STATION_COUNT:
1826 this->DrawCountLine(r, y, c->infrastructure.station, StationMaintenanceCost(c->infrastructure.station));
1827 this->DrawCountLine(r, y, c->infrastructure.airport, AirportMaintenanceCost(c->index));
1828 break;
1833 * Some data on this window has become invalid.
1834 * @param data Information about the changed data.
1835 * @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.
1837 virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
1839 if (!gui_scope) return;
1841 this->UpdateRailRoadTypes();
1842 this->ReInit();
1846 static WindowDesc _company_infrastructure_desc(
1847 WDP_AUTO, "company_infrastructure", 0, 0,
1848 WC_COMPANY_INFRASTRUCTURE, WC_NONE,
1850 _nested_company_infrastructure_widgets, lengthof(_nested_company_infrastructure_widgets)
1854 * Open the infrastructure window of a company.
1855 * @param company Company to show infrastructure of.
1857 static void ShowCompanyInfrastructure(CompanyID company)
1859 if (!Company::IsValidID(company)) return;
1860 AllocateWindowDescFront<CompanyInfrastructureWindow>(&_company_infrastructure_desc, company);
1863 static const NWidgetPart _nested_company_widgets[] = {
1864 NWidget(NWID_HORIZONTAL),
1865 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
1866 NWidget(WWT_CAPTION, COLOUR_GREY, WID_C_CAPTION), SetDataTip(STR_COMPANY_VIEW_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
1867 NWidget(WWT_SHADEBOX, COLOUR_GREY),
1868 NWidget(WWT_STICKYBOX, COLOUR_GREY),
1869 EndContainer(),
1870 NWidget(WWT_PANEL, COLOUR_GREY),
1871 NWidget(NWID_HORIZONTAL), SetPIP(4, 6, 4),
1872 NWidget(NWID_VERTICAL), SetPIP(4, 2, 4),
1873 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_C_FACE), SetMinimalSize(92, 119), SetFill(1, 0),
1874 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_C_FACE_TITLE), SetFill(1, 1), SetMinimalTextLines(2, 0),
1875 EndContainer(),
1876 NWidget(NWID_VERTICAL),
1877 NWidget(NWID_HORIZONTAL),
1878 NWidget(NWID_VERTICAL), SetPIP(4, 5, 5),
1879 NWidget(WWT_TEXT, COLOUR_GREY, WID_C_DESC_INAUGURATION), SetDataTip(STR_COMPANY_VIEW_INAUGURATED_TITLE, STR_NULL), SetFill(1, 0),
1880 NWidget(NWID_HORIZONTAL), SetPIP(0, 5, 0),
1881 NWidget(WWT_LABEL, COLOUR_GREY, WID_C_DESC_COLOUR_SCHEME), SetDataTip(STR_COMPANY_VIEW_COLOUR_SCHEME_TITLE, STR_NULL),
1882 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_C_DESC_COLOUR_SCHEME_EXAMPLE), SetMinimalSize(30, 0), SetFill(0, 1),
1883 NWidget(NWID_SPACER), SetFill(1, 0),
1884 EndContainer(),
1885 NWidget(NWID_HORIZONTAL), SetPIP(0, 4, 0),
1886 NWidget(NWID_VERTICAL),
1887 NWidget(WWT_TEXT, COLOUR_GREY, WID_C_DESC_VEHICLE), SetDataTip(STR_COMPANY_VIEW_VEHICLES_TITLE, STR_NULL),
1888 NWidget(NWID_SPACER), SetFill(0, 1),
1889 EndContainer(),
1890 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_C_DESC_VEHICLE_COUNTS), SetMinimalTextLines(4, 0),
1891 NWidget(NWID_SPACER), SetFill(1, 0),
1892 EndContainer(),
1893 EndContainer(),
1894 NWidget(NWID_VERTICAL), SetPIP(4, 2, 4),
1895 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_C_SELECT_VIEW_BUILD_HQ),
1896 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_C_VIEW_HQ), SetFill(1, 0), SetDataTip(STR_COMPANY_VIEW_VIEW_HQ_BUTTON, STR_COMPANY_VIEW_VIEW_HQ_TOOLTIP),
1897 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_C_BUILD_HQ), SetFill(1, 0), SetDataTip(STR_COMPANY_VIEW_BUILD_HQ_BUTTON, STR_COMPANY_VIEW_BUILD_HQ_TOOLTIP),
1898 EndContainer(),
1899 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_C_SELECT_RELOCATE),
1900 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_C_RELOCATE_HQ), SetFill(1, 0), SetDataTip(STR_COMPANY_VIEW_RELOCATE_HQ, STR_COMPANY_VIEW_RELOCATE_COMPANY_HEADQUARTERS),
1901 NWidget(NWID_SPACER), SetMinimalSize(90, 0),
1902 EndContainer(),
1903 NWidget(NWID_SPACER), SetFill(0, 1),
1904 EndContainer(),
1905 EndContainer(),
1906 NWidget(WWT_TEXT, COLOUR_GREY, WID_C_DESC_COMPANY_VALUE), SetDataTip(STR_COMPANY_VIEW_COMPANY_VALUE, STR_NULL), SetFill(1, 0),
1907 NWidget(NWID_VERTICAL), SetPIP(4, 2, 4),
1908 NWidget(NWID_HORIZONTAL), SetPIP(0, 4, 0),
1909 NWidget(NWID_VERTICAL),
1910 NWidget(WWT_TEXT, COLOUR_GREY, WID_C_DESC_INFRASTRUCTURE), SetDataTip(STR_COMPANY_VIEW_INFRASTRUCTURE, STR_NULL),
1911 NWidget(NWID_SPACER), SetFill(0, 1),
1912 EndContainer(),
1913 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_C_DESC_INFRASTRUCTURE_COUNTS), SetMinimalTextLines(5, 0), SetFill(1, 0),
1914 NWidget(NWID_VERTICAL),
1915 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_C_VIEW_INFRASTRUCTURE), SetDataTip(STR_COMPANY_VIEW_INFRASTRUCTURE_BUTTON, STR_COMPANY_VIEW_INFRASTRUCTURE_TOOLTIP),
1916 NWidget(NWID_SPACER), SetFill(0, 1), SetMinimalSize(90, 0),
1917 EndContainer(),
1918 EndContainer(),
1919 EndContainer(),
1920 NWidget(NWID_HORIZONTAL),
1921 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_C_SELECT_DESC_OWNERS),
1922 NWidget(NWID_VERTICAL), SetPIP(5, 5, 4),
1923 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_C_DESC_OWNERS), SetMinimalTextLines(3, 0),
1924 NWidget(NWID_SPACER), SetFill(0, 1),
1925 EndContainer(),
1926 EndContainer(),
1927 NWidget(NWID_VERTICAL), SetPIP(4, 2, 4),
1928 NWidget(NWID_SPACER), SetMinimalSize(90, 0), SetFill(0, 1),
1929 /* Multi player buttons. */
1930 NWidget(NWID_HORIZONTAL),
1931 NWidget(WWT_EMPTY, COLOUR_GREY, WID_C_HAS_PASSWORD),
1932 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_C_SELECT_MULTIPLAYER),
1933 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_C_COMPANY_PASSWORD), SetFill(1, 0), SetDataTip(STR_COMPANY_VIEW_PASSWORD, STR_COMPANY_VIEW_PASSWORD_TOOLTIP),
1934 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_C_COMPANY_JOIN), SetFill(1, 0), SetDataTip(STR_COMPANY_VIEW_JOIN, STR_COMPANY_VIEW_JOIN_TOOLTIP),
1935 EndContainer(),
1936 EndContainer(),
1937 EndContainer(),
1938 EndContainer(),
1939 EndContainer(),
1940 EndContainer(),
1941 EndContainer(),
1942 /* Button bars at the bottom. */
1943 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_C_SELECT_BUTTONS),
1944 NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
1945 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),
1946 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),
1947 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),
1948 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),
1949 EndContainer(),
1950 NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
1951 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),
1952 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),
1953 EndContainer(),
1954 EndContainer(),
1957 int GetAmountOwnedBy(const Company *c, Owner owner)
1959 return (c->share_owners[0] == owner) +
1960 (c->share_owners[1] == owner) +
1961 (c->share_owners[2] == owner) +
1962 (c->share_owners[3] == owner);
1965 /** Strings for the company vehicle counts */
1966 static const StringID _company_view_vehicle_count_strings[] = {
1967 STR_COMPANY_VIEW_TRAINS, STR_COMPANY_VIEW_ROAD_VEHICLES, STR_COMPANY_VIEW_SHIPS, STR_COMPANY_VIEW_AIRCRAFT
1971 * Window with general information about a company
1973 struct CompanyWindow : Window
1975 CompanyWidgets query_widget;
1977 /** Display planes in the company window. */
1978 enum CompanyWindowPlanes {
1979 /* Display planes of the #WID_C_SELECT_MULTIPLAYER selection widget. */
1980 CWP_MP_C_PWD = 0, ///< Display the company password button.
1981 CWP_MP_C_JOIN, ///< Display the join company button.
1983 /* Display planes of the #WID_C_SELECT_VIEW_BUILD_HQ selection widget. */
1984 CWP_VB_VIEW = 0, ///< Display the view button
1985 CWP_VB_BUILD, ///< Display the build button
1987 /* Display planes of the #WID_C_SELECT_RELOCATE selection widget. */
1988 CWP_RELOCATE_SHOW = 0, ///< Show the relocate HQ button.
1989 CWP_RELOCATE_HIDE, ///< Hide the relocate HQ button.
1991 /* Display planes of the #WID_C_SELECT_BUTTONS selection widget. */
1992 CWP_BUTTONS_LOCAL = 0, ///< Buttons of the local company.
1993 CWP_BUTTONS_OTHER, ///< Buttons of the other companies.
1996 CompanyWindow(WindowDesc *desc, WindowNumber window_number) : Window(desc)
1998 this->InitNested(window_number);
1999 this->owner = (Owner)this->window_number;
2000 this->OnInvalidateData();
2003 virtual void OnPaint()
2005 const Company *c = Company::Get((CompanyID)this->window_number);
2006 bool local = this->window_number == _local_company;
2008 if (!this->IsShaded()) {
2009 bool reinit = false;
2011 /* Button bar selection. */
2012 int plane = local ? CWP_BUTTONS_LOCAL : CWP_BUTTONS_OTHER;
2013 NWidgetStacked *wi = this->GetWidget<NWidgetStacked>(WID_C_SELECT_BUTTONS);
2014 if (plane != wi->shown_plane) {
2015 wi->SetDisplayedPlane(plane);
2016 this->SetDirty();
2017 return;
2020 /* Build HQ button handling. */
2021 plane = (local && c->location_of_HQ == INVALID_TILE) ? CWP_VB_BUILD : CWP_VB_VIEW;
2022 wi = this->GetWidget<NWidgetStacked>(WID_C_SELECT_VIEW_BUILD_HQ);
2023 if (plane != wi->shown_plane) {
2024 wi->SetDisplayedPlane(plane);
2025 this->SetDirty();
2026 return;
2029 this->SetWidgetDisabledState(WID_C_VIEW_HQ, c->location_of_HQ == INVALID_TILE);
2031 /* Enable/disable 'Relocate HQ' button. */
2032 plane = (!local || c->location_of_HQ == INVALID_TILE) ? CWP_RELOCATE_HIDE : CWP_RELOCATE_SHOW;
2033 wi = this->GetWidget<NWidgetStacked>(WID_C_SELECT_RELOCATE);
2034 if (plane != wi->shown_plane) {
2035 wi->SetDisplayedPlane(plane);
2036 this->SetDirty();
2037 return;
2040 /* Owners of company */
2041 plane = SZSP_HORIZONTAL;
2042 for (uint i = 0; i < lengthof(c->share_owners); i++) {
2043 if (c->share_owners[i] != INVALID_COMPANY) {
2044 plane = 0;
2045 break;
2048 wi = this->GetWidget<NWidgetStacked>(WID_C_SELECT_DESC_OWNERS);
2049 if (plane != wi->shown_plane) {
2050 wi->SetDisplayedPlane(plane);
2051 reinit = true;
2054 /* Multiplayer buttons. */
2055 plane = ((!_networking) ? (int)SZSP_NONE : (int)(local ? CWP_MP_C_PWD : CWP_MP_C_JOIN));
2056 wi = this->GetWidget<NWidgetStacked>(WID_C_SELECT_MULTIPLAYER);
2057 if (plane != wi->shown_plane) {
2058 wi->SetDisplayedPlane(plane);
2059 reinit = true;
2061 this->SetWidgetDisabledState(WID_C_COMPANY_JOIN, c->is_ai);
2063 if (reinit) {
2064 this->ReInit();
2065 return;
2069 this->DrawWidgets();
2072 virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
2074 switch (widget) {
2075 case WID_C_DESC_COLOUR_SCHEME_EXAMPLE: {
2076 Point offset;
2077 Dimension d = GetSpriteSize(SPR_VEH_BUS_SW_VIEW, &offset);
2078 d.width -= offset.x;
2079 d.height -= offset.y;
2080 *size = maxdim(*size, d);
2081 break;
2084 case WID_C_DESC_COMPANY_VALUE:
2085 SetDParam(0, INT64_MAX); // Arguably the maximum company value
2086 size->width = GetStringBoundingBox(STR_COMPANY_VIEW_COMPANY_VALUE).width;
2087 break;
2089 case WID_C_DESC_VEHICLE_COUNTS:
2090 SetDParamMaxValue(0, 5000); // Maximum number of vehicles
2091 for (uint i = 0; i < lengthof(_company_view_vehicle_count_strings); i++) {
2092 size->width = max(size->width, GetStringBoundingBox(_company_view_vehicle_count_strings[i]).width);
2094 break;
2096 case WID_C_DESC_INFRASTRUCTURE_COUNTS:
2097 SetDParamMaxValue(0, UINT_MAX);
2098 size->width = max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_INFRASTRUCTURE_RAIL).width);
2099 size->width = max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_INFRASTRUCTURE_ROAD).width);
2100 size->width = max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_INFRASTRUCTURE_WATER).width);
2101 size->width = max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_INFRASTRUCTURE_STATION).width);
2102 size->width = max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_INFRASTRUCTURE_AIRPORT).width);
2103 size->width = max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_INFRASTRUCTURE_NONE).width);
2104 break;
2106 case WID_C_DESC_OWNERS: {
2107 const Company *c2;
2109 FOR_ALL_COMPANIES(c2) {
2110 SetDParamMaxValue(0, 75);
2111 SetDParam(1, c2->index);
2113 size->width = max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_SHARES_OWNED_BY).width);
2115 break;
2118 #ifdef ENABLE_NETWORK
2119 case WID_C_HAS_PASSWORD:
2120 *size = maxdim(*size, GetSpriteSize(SPR_LOCK));
2121 break;
2122 #endif /* ENABLE_NETWORK */
2126 virtual void DrawWidget(const Rect &r, int widget) const
2128 const Company *c = Company::Get((CompanyID)this->window_number);
2129 switch (widget) {
2130 case WID_C_FACE:
2131 DrawCompanyManagerFace(c->face, c->colour, r.left, r.top);
2132 break;
2134 case WID_C_FACE_TITLE:
2135 SetDParam(0, c->index);
2136 DrawStringMultiLine(r.left, r.right, r.top, r.bottom, STR_COMPANY_VIEW_PRESIDENT_MANAGER_TITLE, TC_FROMSTRING, SA_HOR_CENTER);
2137 break;
2139 case WID_C_DESC_COLOUR_SCHEME_EXAMPLE: {
2140 Point offset;
2141 Dimension d = GetSpriteSize(SPR_VEH_BUS_SW_VIEW, &offset);
2142 d.height -= offset.y;
2143 DrawSprite(SPR_VEH_BUS_SW_VIEW, COMPANY_SPRITE_COLOUR(c->index), r.left - offset.x, (r.top + r.bottom - d.height) / 2 - offset.y);
2144 break;
2147 case WID_C_DESC_VEHICLE_COUNTS: {
2148 uint amounts[4];
2149 amounts[0] = c->group_all[VEH_TRAIN].num_vehicle;
2150 amounts[1] = c->group_all[VEH_ROAD].num_vehicle;
2151 amounts[2] = c->group_all[VEH_SHIP].num_vehicle;
2152 amounts[3] = c->group_all[VEH_AIRCRAFT].num_vehicle;
2154 int y = r.top;
2155 if (amounts[0] + amounts[1] + amounts[2] + amounts[3] == 0) {
2156 DrawString(r.left, r.right, y, STR_COMPANY_VIEW_VEHICLES_NONE);
2157 } else {
2158 assert_compile(lengthof(amounts) == lengthof(_company_view_vehicle_count_strings));
2160 for (uint i = 0; i < lengthof(amounts); i++) {
2161 if (amounts[i] != 0) {
2162 SetDParam(0, amounts[i]);
2163 DrawString(r.left, r.right, y, _company_view_vehicle_count_strings[i]);
2164 y += FONT_HEIGHT_NORMAL;
2168 break;
2171 case WID_C_DESC_INFRASTRUCTURE_COUNTS: {
2172 uint y = r.top;
2174 /* Collect rail and road counts. */
2175 uint rail_pices = c->infrastructure.signal;
2176 uint road_pieces = 0;
2177 for (uint i = 0; i < lengthof(c->infrastructure.rail); i++) rail_pices += c->infrastructure.rail[i];
2178 for (uint i = 0; i < lengthof(c->infrastructure.road); i++) road_pieces += c->infrastructure.road[i];
2180 if (rail_pices == 0 && road_pieces == 0 && c->infrastructure.water == 0 && c->infrastructure.station == 0 && c->infrastructure.airport == 0) {
2181 DrawString(r.left, r.right, y, STR_COMPANY_VIEW_INFRASTRUCTURE_NONE);
2182 } else {
2183 if (rail_pices != 0) {
2184 SetDParam(0, rail_pices);
2185 DrawString(r.left, r.right, y, STR_COMPANY_VIEW_INFRASTRUCTURE_RAIL);
2186 y += FONT_HEIGHT_NORMAL;
2188 if (road_pieces != 0) {
2189 SetDParam(0, road_pieces);
2190 DrawString(r.left, r.right, y, STR_COMPANY_VIEW_INFRASTRUCTURE_ROAD);
2191 y += FONT_HEIGHT_NORMAL;
2193 if (c->infrastructure.water != 0) {
2194 SetDParam(0, c->infrastructure.water);
2195 DrawString(r.left, r.right, y, STR_COMPANY_VIEW_INFRASTRUCTURE_WATER);
2196 y += FONT_HEIGHT_NORMAL;
2198 if (c->infrastructure.station != 0) {
2199 SetDParam(0, c->infrastructure.station);
2200 DrawString(r.left, r.right, y, STR_COMPANY_VIEW_INFRASTRUCTURE_STATION);
2201 y += FONT_HEIGHT_NORMAL;
2203 if (c->infrastructure.airport != 0) {
2204 SetDParam(0, c->infrastructure.airport);
2205 DrawString(r.left, r.right, y, STR_COMPANY_VIEW_INFRASTRUCTURE_AIRPORT);
2209 break;
2212 case WID_C_DESC_OWNERS: {
2213 const Company *c2;
2214 uint y = r.top;
2216 FOR_ALL_COMPANIES(c2) {
2217 uint amt = GetAmountOwnedBy(c, c2->index);
2218 if (amt != 0) {
2219 SetDParam(0, amt * 25);
2220 SetDParam(1, c2->index);
2222 DrawString(r.left, r.right, y, STR_COMPANY_VIEW_SHARES_OWNED_BY);
2223 y += FONT_HEIGHT_NORMAL;
2226 break;
2229 #ifdef ENABLE_NETWORK
2230 case WID_C_HAS_PASSWORD:
2231 if (_networking && NetworkCompanyIsPassworded(c->index)) {
2232 DrawSprite(SPR_LOCK, PAL_NONE, r.left, r.top);
2234 break;
2235 #endif /* ENABLE_NETWORK */
2239 virtual void SetStringParameters(int widget) const
2241 switch (widget) {
2242 case WID_C_CAPTION:
2243 SetDParam(0, (CompanyID)this->window_number);
2244 SetDParam(1, (CompanyID)this->window_number);
2245 break;
2247 case WID_C_DESC_INAUGURATION:
2248 SetDParam(0, Company::Get((CompanyID)this->window_number)->inaugurated_year);
2249 break;
2251 case WID_C_DESC_COMPANY_VALUE:
2252 SetDParam(0, CalculateCompanyValue(Company::Get((CompanyID)this->window_number)));
2253 break;
2257 virtual void OnClick(Point pt, int widget, int click_count)
2259 switch (widget) {
2260 case WID_C_NEW_FACE: DoSelectCompanyManagerFace(this); break;
2262 case WID_C_COLOUR_SCHEME:
2263 if (BringWindowToFrontById(WC_COMPANY_COLOUR, this->window_number)) break;
2264 new SelectCompanyLiveryWindow(&_select_company_livery_desc, (CompanyID)this->window_number);
2265 break;
2267 case WID_C_PRESIDENT_NAME:
2268 this->query_widget = WID_C_PRESIDENT_NAME;
2269 SetDParam(0, this->window_number);
2270 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);
2271 break;
2273 case WID_C_COMPANY_NAME:
2274 this->query_widget = WID_C_COMPANY_NAME;
2275 SetDParam(0, this->window_number);
2276 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);
2277 break;
2279 case WID_C_VIEW_HQ: {
2280 TileIndex tile = Company::Get((CompanyID)this->window_number)->location_of_HQ;
2281 if (_ctrl_pressed) {
2282 ShowExtraViewPortWindow(tile);
2283 } else {
2284 ScrollMainWindowToTile(tile);
2286 break;
2289 case WID_C_BUILD_HQ:
2290 if ((byte)this->window_number != _local_company) return;
2291 if (this->IsWidgetLowered(WID_C_BUILD_HQ)) {
2292 ResetObjectToPlace();
2293 this->RaiseButtons();
2294 break;
2296 SetObjectToPlaceWnd(SPR_CURSOR_HQ, PAL_NONE, HT_RECT, this);
2297 SetTileSelectSize(2, 2);
2298 this->LowerWidget(WID_C_BUILD_HQ);
2299 this->SetWidgetDirty(WID_C_BUILD_HQ);
2300 break;
2302 case WID_C_RELOCATE_HQ:
2303 if (this->IsWidgetLowered(WID_C_RELOCATE_HQ)) {
2304 ResetObjectToPlace();
2305 this->RaiseButtons();
2306 break;
2308 SetObjectToPlaceWnd(SPR_CURSOR_HQ, PAL_NONE, HT_RECT, this);
2309 SetTileSelectSize(2, 2);
2310 this->LowerWidget(WID_C_RELOCATE_HQ);
2311 this->SetWidgetDirty(WID_C_RELOCATE_HQ);
2312 break;
2314 case WID_C_VIEW_INFRASTRUCTURE:
2315 ShowCompanyInfrastructure((CompanyID)this->window_number);
2316 break;
2318 case WID_C_BUY_SHARE:
2319 DoCommandP(0, this->window_number, 0, CMD_BUY_SHARE_IN_COMPANY | CMD_MSG(STR_ERROR_CAN_T_BUY_25_SHARE_IN_THIS));
2320 break;
2322 case WID_C_SELL_SHARE:
2323 DoCommandP(0, this->window_number, 0, CMD_SELL_SHARE_IN_COMPANY | CMD_MSG(STR_ERROR_CAN_T_SELL_25_SHARE_IN));
2324 break;
2326 #ifdef ENABLE_NETWORK
2327 case WID_C_COMPANY_PASSWORD:
2328 if (this->window_number == _local_company) ShowNetworkCompanyPasswordWindow(this);
2329 break;
2331 case WID_C_COMPANY_JOIN: {
2332 this->query_widget = WID_C_COMPANY_JOIN;
2333 CompanyID company = (CompanyID)this->window_number;
2334 if (_network_server) {
2335 NetworkServerDoMove(CLIENT_ID_SERVER, company);
2336 MarkWholeScreenDirty();
2337 } else if (NetworkCompanyIsPassworded(company)) {
2338 /* ask for the password */
2339 ShowQueryString(STR_EMPTY, STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION, NETWORK_PASSWORD_LENGTH, this, CS_ALPHANUMERAL, QSF_NONE);
2340 } else {
2341 /* just send the join command */
2342 NetworkClientRequestMove(company);
2344 break;
2346 #endif /* ENABLE_NETWORK */
2350 virtual void OnHundredthTick()
2352 /* redraw the window every now and then */
2353 this->SetDirty();
2356 virtual void OnPlaceObject(Point pt, TileIndex tile)
2358 if (DoCommandP(tile, OBJECT_HQ, 0, CMD_BUILD_OBJECT | CMD_MSG(STR_ERROR_CAN_T_BUILD_COMPANY_HEADQUARTERS))) {
2359 ResetObjectToPlace();
2360 this->RaiseButtons();
2364 virtual void OnPlaceObjectAbort()
2366 this->RaiseButtons();
2369 virtual void OnQueryTextFinished(char *str)
2371 if (str == NULL) return;
2373 switch (this->query_widget) {
2374 default: NOT_REACHED();
2376 case WID_C_PRESIDENT_NAME:
2377 DoCommandP(0, 0, 0, CMD_RENAME_PRESIDENT | CMD_MSG(STR_ERROR_CAN_T_CHANGE_PRESIDENT), NULL, str);
2378 break;
2380 case WID_C_COMPANY_NAME:
2381 DoCommandP(0, 0, 0, CMD_RENAME_COMPANY | CMD_MSG(STR_ERROR_CAN_T_CHANGE_COMPANY_NAME), NULL, str);
2382 break;
2384 #ifdef ENABLE_NETWORK
2385 case WID_C_COMPANY_JOIN:
2386 NetworkClientRequestMove((CompanyID)this->window_number, str);
2387 break;
2388 #endif /* ENABLE_NETWORK */
2394 * Some data on this window has become invalid.
2395 * @param data Information about the changed data.
2396 * @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.
2398 virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
2400 if (this->window_number == _local_company) return;
2402 if (_settings_game.economy.allow_shares) { // Shares are allowed
2403 const Company *c = Company::Get(this->window_number);
2405 /* If all shares are owned by someone (none by nobody), disable buy button */
2406 this->SetWidgetDisabledState(WID_C_BUY_SHARE, GetAmountOwnedBy(c, INVALID_OWNER) == 0 ||
2407 /* Only 25% left to buy. If the company is human, disable buying it up.. TODO issues! */
2408 (GetAmountOwnedBy(c, INVALID_OWNER) == 1 && !c->is_ai) ||
2409 /* Spectators cannot do anything of course */
2410 _local_company == COMPANY_SPECTATOR);
2412 /* If the company doesn't own any shares, disable sell button */
2413 this->SetWidgetDisabledState(WID_C_SELL_SHARE, (GetAmountOwnedBy(c, _local_company) == 0) ||
2414 /* Spectators cannot do anything of course */
2415 _local_company == COMPANY_SPECTATOR);
2416 } else { // Shares are not allowed, disable buy/sell buttons
2417 this->DisableWidget(WID_C_BUY_SHARE);
2418 this->DisableWidget(WID_C_SELL_SHARE);
2423 static WindowDesc _company_desc(
2424 WDP_AUTO, "company", 0, 0,
2425 WC_COMPANY, WC_NONE,
2427 _nested_company_widgets, lengthof(_nested_company_widgets)
2431 * Show the window with the overview of the company.
2432 * @param company The company to show the window for.
2434 void ShowCompany(CompanyID company)
2436 if (!Company::IsValidID(company)) return;
2438 AllocateWindowDescFront<CompanyWindow>(&_company_desc, company);
2442 * Redraw all windows with company infrastructure counts.
2443 * @param company The company to redraw the windows of.
2445 void DirtyCompanyInfrastructureWindows(CompanyID company)
2447 SetWindowDirty(WC_COMPANY, company);
2448 SetWindowDirty(WC_COMPANY_INFRASTRUCTURE, company);
2451 struct BuyCompanyWindow : Window {
2452 BuyCompanyWindow(WindowDesc *desc, WindowNumber window_number) : Window(desc)
2454 this->InitNested(window_number);
2457 virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
2459 switch (widget) {
2460 case WID_BC_FACE:
2461 *size = GetSpriteSize(SPR_GRADIENT);
2462 break;
2464 case WID_BC_QUESTION:
2465 const Company *c = Company::Get((CompanyID)this->window_number);
2466 SetDParam(0, c->index);
2467 SetDParam(1, c->bankrupt_value);
2468 size->height = GetStringHeight(STR_BUY_COMPANY_MESSAGE, size->width);
2469 break;
2473 virtual void SetStringParameters(int widget) const
2475 switch (widget) {
2476 case WID_BC_CAPTION:
2477 SetDParam(0, STR_COMPANY_NAME);
2478 SetDParam(1, Company::Get((CompanyID)this->window_number)->index);
2479 break;
2483 virtual void DrawWidget(const Rect &r, int widget) const
2485 switch (widget) {
2486 case WID_BC_FACE: {
2487 const Company *c = Company::Get((CompanyID)this->window_number);
2488 DrawCompanyManagerFace(c->face, c->colour, r.left, r.top);
2489 break;
2492 case WID_BC_QUESTION: {
2493 const Company *c = Company::Get((CompanyID)this->window_number);
2494 SetDParam(0, c->index);
2495 SetDParam(1, c->bankrupt_value);
2496 DrawStringMultiLine(r.left, r.right, r.top, r.bottom, STR_BUY_COMPANY_MESSAGE, TC_FROMSTRING, SA_CENTER);
2497 break;
2502 virtual void OnClick(Point pt, int widget, int click_count)
2504 switch (widget) {
2505 case WID_BC_NO:
2506 delete this;
2507 break;
2509 case WID_BC_YES:
2510 DoCommandP(0, this->window_number, 0, CMD_BUY_COMPANY | CMD_MSG(STR_ERROR_CAN_T_BUY_COMPANY));
2511 break;
2516 static const NWidgetPart _nested_buy_company_widgets[] = {
2517 NWidget(NWID_HORIZONTAL),
2518 NWidget(WWT_CLOSEBOX, COLOUR_LIGHT_BLUE),
2519 NWidget(WWT_CAPTION, COLOUR_LIGHT_BLUE, WID_BC_CAPTION), SetDataTip(STR_ERROR_MESSAGE_CAPTION_OTHER_COMPANY, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
2520 EndContainer(),
2521 NWidget(WWT_PANEL, COLOUR_LIGHT_BLUE),
2522 NWidget(NWID_VERTICAL), SetPIP(8, 8, 8),
2523 NWidget(NWID_HORIZONTAL), SetPIP(8, 10, 8),
2524 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_BC_FACE), SetFill(0, 1),
2525 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_BC_QUESTION), SetMinimalSize(240, 0), SetFill(1, 1),
2526 EndContainer(),
2527 NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), SetPIP(100, 10, 100),
2528 NWidget(WWT_TEXTBTN, COLOUR_LIGHT_BLUE, WID_BC_NO), SetMinimalSize(60, 12), SetDataTip(STR_QUIT_NO, STR_NULL), SetFill(1, 0),
2529 NWidget(WWT_TEXTBTN, COLOUR_LIGHT_BLUE, WID_BC_YES), SetMinimalSize(60, 12), SetDataTip(STR_QUIT_YES, STR_NULL), SetFill(1, 0),
2530 EndContainer(),
2531 EndContainer(),
2532 EndContainer(),
2535 static WindowDesc _buy_company_desc(
2536 WDP_AUTO, NULL, 0, 0,
2537 WC_BUY_COMPANY, WC_NONE,
2538 WDF_CONSTRUCTION,
2539 _nested_buy_company_widgets, lengthof(_nested_buy_company_widgets)
2543 * Show the query to buy another company.
2544 * @param company The company to buy.
2546 void ShowBuyCompanyDialog(CompanyID company)
2548 AllocateWindowDescFront<BuyCompanyWindow>(&_buy_company_desc, company);