# graph_gui.cpp

Go to the documentation of this file.

    00001 /* $Id: graph_gui.cpp 20576 2010-08-20 10:31:20Z rubidium $ */
    00002
    00003 /*
    00004  * This file is part of OpenTTD.
    00005  * 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.
    00006  * 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.
    00007  * 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/>.
    00008  */
    00009
    00012 #include "stdafx.h"
    00013 #include "openttd.h"
    00014 #include "graph_gui.h"
    00015 #include "window_gui.h"
    00016 #include "company_base.h"
    00017 #include "company_gui.h"
    00018 #include "economy_func.h"
    00019 #include "cargotype.h"
    00020 #include "strings_func.h"
    00021 #include "window_func.h"
    00022 #include "date_func.h"
    00023 #include "gfx_func.h"
    00024 #include "sortlist_type.h"
    00025 #include "core/geometry_func.hpp"
    00026
    00027 #include "table/strings.h"
    00028 #include "table/sprites.h"
    00029
    00030 /* Bitmasks of company and cargo indices that shouldn't be drawn. */
    00031 static uint _legend_excluded_companies;
    00032 static uint _legend_excluded_cargo;
    00033
    00034 /* Apparently these don't play well with enums. */
    00035 static const OverflowSafeInt64 INVALID_DATAPOINT(INT64_MAX); // Value used for a datapoint that shouldn't be drawn.
    00036 static const uint INVALID_DATAPOINT_POS = UINT_MAX;  // Used to determine if the previous point was drawn.
    00037
    00038 /****************/
    00039 /* GRAPH LEGEND */
    00040 /****************/
    00041
    00043 enum GraphLegendWidgetNumbers {
    00044   GLW_BACKGROUND,
    00045
    00046   GLW_FIRST_COMPANY,
    00047   GLW_LAST_COMPANY = GLW_FIRST_COMPANY + MAX_COMPANIES - 1,
    00048 };
    00049
    00050 struct GraphLegendWindow : Window {
    00051   GraphLegendWindow(const WindowDesc *desc, WindowNumber window_number) : Window()
    00052   {
    00053     this->InitNested(desc, window_number);
    00054
    00055     for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
    00056       if (!HasBit(_legend_excluded_companies, c)) this->LowerWidget(c + GLW_FIRST_COMPANY);
    00057
    00058       this->OnInvalidateData(c);
    00059     }
    00060   }
    00061
    00062   virtual void OnPaint()
    00063   {
    00064     this->DrawWidgets();
    00065   }
    00066
    00067   virtual void DrawWidget(const Rect &r, int widget) const
    00068   {
    00069     if (!IsInsideMM(widget, GLW_FIRST_COMPANY, MAX_COMPANIES + GLW_FIRST_COMPANY)) return;
    00070
    00071     CompanyID cid = (CompanyID)(widget - GLW_FIRST_COMPANY);
    00072
    00073     if (!Company::IsValidID(cid)) return;
    00074
    00075     bool rtl = _dynlang.text_dir == TD_RTL;
    00076
    00077     DrawCompanyIcon(cid, rtl ? r.right - 16 : r.left + 2, r.top + 2 + (FONT_HEIGHT_NORMAL - 10) / 2);
    00078
    00079     SetDParam(0, cid);
    00080     SetDParam(1, cid);
    00081     DrawString(r.left + (rtl ? WD_FRAMERECT_LEFT : 19), r.right - (rtl ? 19 : WD_FRAMERECT_RIGHT), r.top + WD_FRAMERECT_TOP, STR_COMPANY_NAME_COMPANY_NUM, HasBit(_legend_excluded_companies, cid) ? TC_BLACK : TC_WHITE);
    00082   }
    00083
    00084   virtual void OnClick(Point pt, int widget, int click_count)
    00085   {
    00086     if (!IsInsideMM(widget, GLW_FIRST_COMPANY, MAX_COMPANIES + GLW_FIRST_COMPANY)) return;
    00087
    00088     ToggleBit(_legend_excluded_companies, widget - GLW_FIRST_COMPANY);
    00089     this->ToggleWidgetLoweredState(widget);
    00090     this->SetDirty();
    00091     InvalidateWindowData(WC_INCOME_GRAPH, 0);
    00092     InvalidateWindowData(WC_OPERATING_PROFIT, 0);
    00093     InvalidateWindowData(WC_DELIVERED_CARGO, 0);
    00094     InvalidateWindowData(WC_PERFORMANCE_HISTORY, 0);
    00095     InvalidateWindowData(WC_COMPANY_VALUE, 0);
    00096   }
    00097
    00098   virtual void OnInvalidateData(int data)
    00099   {
    00100     if (Company::IsValidID(data)) return;
    00101
    00102     SetBit(_legend_excluded_companies, data);
    00103     this->RaiseWidget(data + GLW_FIRST_COMPANY);
    00104   }
    00105 };
    00106
    00113 static NWidgetBase *MakeNWidgetCompanyLines(int *biggest_index)
    00114 {
    00115   NWidgetVertical *vert = new NWidgetVertical();
    00116
    00117   for (int widnum = GLW_FIRST_COMPANY; widnum <= GLW_LAST_COMPANY; widnum++) {
    00118     NWidgetBackground *panel = new NWidgetBackground(WWT_PANEL, COLOUR_GREY, widnum);
    00119     panel->SetMinimalSize(246, FONT_HEIGHT_NORMAL + WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM);
    00120     panel->SetFill(1, 0);
    00121     panel->SetDataTip(0x0, STR_GRAPH_KEY_COMPANY_SELECTION_TOOLTIP);
    00122     vert->Add(panel);
    00123   }
    00124   *biggest_index = GLW_LAST_COMPANY;
    00125   return vert;
    00126 }
    00127
    00128 static const NWidgetPart _nested_graph_legend_widgets[] = {
    00129   NWidget(NWID_HORIZONTAL),
    00130     NWidget(WWT_CLOSEBOX, COLOUR_GREY),
    00131     NWidget(WWT_CAPTION, COLOUR_GREY), SetDataTip(STR_GRAPH_KEY_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
    00132     NWidget(WWT_SHADEBOX, COLOUR_GREY),
    00133     NWidget(WWT_STICKYBOX, COLOUR_GREY),
    00134   EndContainer(),
    00135   NWidget(WWT_PANEL, COLOUR_GREY, GLW_BACKGROUND),
    00136     NWidget(NWID_SPACER), SetMinimalSize(0, 2),
    00137     NWidget(NWID_HORIZONTAL),
    00138       NWidget(NWID_SPACER), SetMinimalSize(2, 0),
    00139       NWidgetFunction(MakeNWidgetCompanyLines),
    00140       NWidget(NWID_SPACER), SetMinimalSize(2, 0),
    00141     EndContainer(),
    00142   EndContainer(),
    00143 };
    00144
    00145 static const WindowDesc _graph_legend_desc(
    00146   WDP_AUTO, 0, 0,
    00147   WC_GRAPH_LEGEND, WC_NONE,
    00148   0,
    00149   _nested_graph_legend_widgets, lengthof(_nested_graph_legend_widgets)
    00150 );
    00151
    00152 static void ShowGraphLegend()
    00153 {
    00154   AllocateWindowDescFront<GraphLegendWindow>(&_graph_legend_desc, 0);
    00155 }
    00156
    00157 /******************/
    00158 /* BASE OF GRAPHS */
    00159 /*****************/
    00160
    00162 enum CompanyValueWidgets {
    00163   BGW_KEY_BUTTON,
    00164   BGW_BACKGROUND,
    00165 };
    00166
    00167 struct BaseGraphWindow : Window {
    00168 protected:
    00169   enum {
    00170     GRAPH_MAX_DATASETS = 32,
    00171     GRAPH_AXIS_LINE_COLOUR  = 215,
    00172     GRAPH_NUM_MONTHS = 24,
    00173
    00174     GRAPH_NUM_LINES_Y = 9,
    00175     /* 9 is convenient as that means the distance between them is the gd_height of the graph / 8,
    00176      * which is the same
    00177      * as height >> 3. */
    00178   };
    00179
    00180   uint excluded_data;
    00181   byte num_dataset;
    00182   byte num_on_x_axis;
    00183   bool has_negative_values;
    00184   byte num_vert_lines;
    00185   static const TextColour graph_axis_label_colour = TC_BLACK;
    00186
    00187   /* The starting month and year that values are plotted against. If month is
    00188    * 0xFF, use x_values_start and x_values_increment below instead. */
    00189   byte month;
    00190   Year year;
    00191
    00192   /* These values are used if the graph is being plotted against values
    00193    * rather than the dates specified by month and year. */
    00194   uint16 x_values_start;
    00195   uint16 x_values_increment;
    00196
    00197   int graph_widget;
    00198   StringID format_str_y_axis;
    00199   byte colours[GRAPH_MAX_DATASETS];
    00200   OverflowSafeInt64 cost[GRAPH_MAX_DATASETS][GRAPH_NUM_MONTHS];
    00201
    00207   int64 GetHighestValue() const
    00208   {
    00209     OverflowSafeInt64 highest_value = 0;
    00210
    00211     for (int i = 0; i < this->num_dataset; i++) {
    00212       if (HasBit(this->excluded_data, i)) continue;
    00213       for (int j = 0; j < this->num_on_x_axis; j++) {
    00214         OverflowSafeInt64 datapoint = this->cost[i][j];
    00215
    00216         if (datapoint != INVALID_DATAPOINT) {
    00217           /* For now, if the graph has negative values the scaling is
    00218            * symmetrical about the x axis, so take the absolute value
    00219            * of each data point. */
    00220           highest_value = max(highest_value, abs(datapoint));
    00221         }
    00222       }
    00223     }
    00224
    00225     /* Prevent showing the highest value too close to the graph upper limit. */
    00226     highest_value = (11 * highest_value) / 10;
    00227     /* Avoid using zero as the highest value. */
    00228     if (highest_value == 0) highest_value = GRAPH_NUM_LINES_Y - 1;
    00229     /* Round up highest_value so that it will divide cleanly into the number of
    00230      * axis labels used. */
    00231     int round_val = highest_value % (GRAPH_NUM_LINES_Y - 1);
    00232     if (round_val != 0) highest_value += (GRAPH_NUM_LINES_Y - 1 - round_val);
    00233
    00234     return highest_value;
    00235   }
    00236
    00237   uint GetYLabelWidth(int64 highest_value) const
    00238   {
    00239     /* draw text strings on the y axis */
    00240     int64 y_label = highest_value;
    00241     int64 y_label_separation = highest_value / (GRAPH_NUM_LINES_Y - 1);
    00242
    00243     /* If there are negative values, the graph goes from highest_value to
    00244      * -highest_value, not highest_value to 0. */
    00245     if (this->has_negative_values) y_label_separation *= 2;
    00246
    00247     uint max_width = 0;
    00248
    00249     for (int i = 0; i < GRAPH_NUM_LINES_Y; i++) {
    00250       SetDParam(0, this->format_str_y_axis);
    00251       SetDParam(1, y_label);
    00252       Dimension d = GetStringBoundingBox(STR_GRAPH_Y_LABEL);
    00253       if (d.width > max_width) max_width = d.width;
    00254
    00255       y_label -= y_label_separation;
    00256     }
    00257
    00258     return max_width;
    00259   }
    00260
    00265   void DrawGraph(Rect r) const
    00266   {
    00267     uint x, y;
    00268     OverflowSafeInt64 highest_value;
    00269     int x_axis_offset;
    00270
    00271     /* the colours and cost array of GraphDrawer must accomodate
    00272      * both values for cargo and companies. So if any are higher, quit */
    00273     assert_compile(GRAPH_MAX_DATASETS >= (int)NUM_CARGO && GRAPH_MAX_DATASETS >= (int)MAX_COMPANIES);
    00274     assert(this->num_vert_lines > 0);
    00275
    00276     byte grid_colour = _colour_gradient[COLOUR_GREY][4];
    00277
    00278     /* Rect r will be adjusted to contain just the graph, with labels being
    00279      * placed outside the area. */
    00280     r.top    += 5 + GetCharacterHeight(FS_SMALL) / 2;
    00281     r.bottom -= (this->month == 0xFF ? 1 : 3) * GetCharacterHeight(FS_SMALL) + 4;
    00282     r.left   += 9;
    00283     r.right  -= 5;
    00284
    00285     highest_value = GetHighestValue();
    00286
    00287     /* Get width for Y labels */
    00288     int label_width = GetYLabelWidth(highest_value);
    00289
    00290     r.left += label_width;
    00291
    00292     int x_sep = (r.right - r.left) / this->num_vert_lines;
    00293     int y_sep = (r.bottom - r.top) / (GRAPH_NUM_LINES_Y - 1);
    00294
    00295     /* Redetermine right and bottom edge of graph to fit with the integer
    00296      * separation values. */
    00297     r.right = r.left + x_sep * this->num_vert_lines;
    00298     r.bottom = r.top + y_sep * (GRAPH_NUM_LINES_Y - 1);
    00299
    00300     /* Where to draw the X axis */
    00301     x_axis_offset = r.bottom - r.top;
    00302     if (this->has_negative_values) x_axis_offset /= 2;
    00303
    00304     /* Draw the vertical grid lines. */
    00305
    00306     /* Don't draw the first line, as that's where the axis will be. */
    00307     x = r.left + x_sep;
    00308
    00309     for (int i = 0; i < this->num_vert_lines; i++) {
    00310       GfxFillRect(x, r.top, x, r.bottom, grid_colour);
    00311       x += x_sep;
    00312     }
    00313
    00314     /* Draw the horizontal grid lines. */
    00315     y = r.bottom;
    00316
    00317     for (int i = 0; i < GRAPH_NUM_LINES_Y; i++) {
    00318       GfxFillRect(r.left - 3, y, r.left - 1, y, GRAPH_AXIS_LINE_COLOUR);
    00319       GfxFillRect(r.left, y, r.right, y, grid_colour);
    00320       y -= y_sep;
    00321     }
    00322
    00323     /* Draw the y axis. */
    00324     GfxFillRect(r.left, r.top, r.left, r.bottom, GRAPH_AXIS_LINE_COLOUR);
    00325
    00326     /* Draw the x axis. */
    00327     y = x_axis_offset + r.top;
    00328     GfxFillRect(r.left, y, r.right, y, GRAPH_AXIS_LINE_COLOUR);
    00329
    00330     /* Find the largest value that will be drawn. */
    00331     if (this->num_on_x_axis == 0)
    00332       return;
    00333
    00334     assert(this->num_on_x_axis > 0);
    00335     assert(this->num_dataset > 0);
    00336
    00337     /* draw text strings on the y axis */
    00338     int64 y_label = highest_value;
    00339     int64 y_label_separation = highest_value / (GRAPH_NUM_LINES_Y - 1);
    00340
    00341     /* If there are negative values, the graph goes from highest_value to
    00342      * -highest_value, not highest_value to 0. */
    00343     if (this->has_negative_values) y_label_separation *= 2;
    00344
    00345     y = r.top - GetCharacterHeight(FS_SMALL) / 2;
    00346
    00347     for (int i = 0; i < GRAPH_NUM_LINES_Y; i++) {
    00348       SetDParam(0, this->format_str_y_axis);
    00349       SetDParam(1, y_label);
    00350       DrawString(r.left - label_width - 4, r.left - 4, y, STR_GRAPH_Y_LABEL, graph_axis_label_colour, SA_RIGHT);
    00351
    00352       y_label -= y_label_separation;
    00353       y += y_sep;
    00354     }
    00355
    00356     /* draw strings on the x axis */
    00357     if (this->month != 0xFF) {
    00358       x = r.left;
    00359       y = r.bottom + 2;
    00360       byte month = this->month;
    00361       Year year  = this->year;
    00362       for (int i = 0; i < this->num_on_x_axis; i++) {
    00363         SetDParam(0, month + STR_MONTH_ABBREV_JAN);
    00364         SetDParam(1, month + STR_MONTH_ABBREV_JAN + 2);
    00365         SetDParam(2, year);
    00366         DrawStringMultiLine(x, x + x_sep, y, this->height, month == 0 ? STR_GRAPH_X_LABEL_MONTH_YEAR : STR_GRAPH_X_LABEL_MONTH, graph_axis_label_colour);
    00367
    00368         month += 3;
    00369         if (month >= 12) {
    00370           month = 0;
    00371           year++;
    00372         }
    00373         x += x_sep;
    00374       }
    00375     } else {
    00376       /* Draw the label under the data point rather than on the grid line. */
    00377       x = r.left;
    00378       y = r.bottom + 2;
    00379       uint16 label = this->x_values_start;
    00380
    00381       for (int i = 0; i < this->num_on_x_axis; i++) {
    00382         SetDParam(0, label);
    00383         DrawString(x + 1, x + x_sep - 1, y, STR_GRAPH_Y_LABEL_NUMBER, graph_axis_label_colour, SA_CENTER);
    00384
    00385         label += this->x_values_increment;
    00386         x += x_sep;
    00387       }
    00388     }
    00389
    00390     /* draw lines and dots */
    00391     for (int i = 0; i < this->num_dataset; i++) {
    00392       if (!HasBit(this->excluded_data, i)) {
    00393         /* Centre the dot between the grid lines. */
    00394         x = r.left + (x_sep / 2);
    00395
    00396         byte colour  = this->colours[i];
    00397         uint prev_x = INVALID_DATAPOINT_POS;
    00398         uint prev_y = INVALID_DATAPOINT_POS;
    00399
    00400         for (int j = 0; j < this->num_on_x_axis; j++) {
    00401           OverflowSafeInt64 datapoint = this->cost[i][j];
    00402
    00403           if (datapoint != INVALID_DATAPOINT) {
    00404             /*
    00405              * Check whether we need to reduce the 'accuracy' of the
    00406              * datapoint value and the highest value to splut overflows.
    00407              * And when 'drawing' 'one million' or 'one million and one'
    00408              * there is no significant difference, so the least
    00409              * significant bits can just be removed.
    00410              *
    00411              * If there are more bits needed than would fit in a 32 bits
    00412              * integer, so at about 31 bits because of the sign bit, the
    00413              * least significant bits are removed.
    00414              */
    00415             int mult_range = FindLastBit(x_axis_offset) + FindLastBit(abs(datapoint));
    00416             int reduce_range = max(mult_range - 31, 0);
    00417
    00418             /* Handle negative values differently (don't shift sign) */
    00419             if (datapoint < 0) {
    00420               datapoint = -(abs(datapoint) >> reduce_range);
    00421             } else {
    00422               datapoint >>= reduce_range;
    00423             }
    00424
    00425             y = r.top + x_axis_offset - (x_axis_offset * datapoint) / (highest_value >> reduce_range);
    00426
    00427             /* Draw the point. */
    00428             GfxFillRect(x - 1, y - 1, x + 1, y + 1, colour);
    00429
    00430             /* Draw the line connected to the previous point. */
    00431             if (prev_x != INVALID_DATAPOINT_POS) GfxDrawLine(prev_x, prev_y, x, y, colour);
    00432
    00433             prev_x = x;
    00434             prev_y = y;
    00435           } else {
    00436             prev_x = INVALID_DATAPOINT_POS;
    00437             prev_y = INVALID_DATAPOINT_POS;
    00438           }
    00439
    00440           x += x_sep;
    00441         }
    00442       }
    00443     }
    00444   }
    00445
    00446
    00447   BaseGraphWindow(int widget, bool has_negative_values, StringID format_str_y_axis) :
    00448       Window(), has_negative_values(has_negative_values),
    00449       format_str_y_axis(format_str_y_axis)
    00450   {
    00451     SetWindowDirty(WC_GRAPH_LEGEND, 0);
    00452     this->num_vert_lines = 24;
    00453     this->graph_widget = widget;
    00454   }
    00455
    00456   void InitializeWindow(const WindowDesc *desc, WindowNumber number)
    00457   {
    00458     /* Initialise the dataset */
    00459     this->UpdateStatistics(true);
    00460
    00461     this->InitNested(desc, number);
    00462   }
    00463
    00464 public:
    00465   virtual void OnPaint()
    00466   {
    00467     this->DrawWidgets();
    00468   }
    00469
    00470   virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
    00471   {
    00472     if (widget != this->graph_widget) return;
    00473
    00474     uint x_label_width = 0;
    00475
    00476     if (this->month != 0xFF) {
    00477       byte month = this->month;
    00478       Year year  = this->year;
    00479       for (int i = 0; i < this->num_on_x_axis; i++) {
    00480         SetDParam(0, month + STR_MONTH_ABBREV_JAN);
    00481         SetDParam(1, month + STR_MONTH_ABBREV_JAN + 2);
    00482         SetDParam(2, year);
    00483         x_label_width = max(x_label_width, GetStringBoundingBox(month == 0 ? STR_GRAPH_X_LABEL_MONTH_YEAR : STR_GRAPH_X_LABEL_MONTH).width);
    00484
    00485         month += 3;
    00486         if (month >= 12) {
    00487           month = 0;
    00488           year++;
    00489         }
    00490       }
    00491     } else {
    00492       /* Draw the label under the data point rather than on the grid line. */
    00493       SetDParam(0, this->x_values_start + this->num_on_x_axis * this->x_values_increment);
    00494       x_label_width = GetStringBoundingBox(STR_GRAPH_Y_LABEL_NUMBER).width;
    00495     }
    00496
    00497     SetDParam(0, this->format_str_y_axis);
    00498     SetDParam(1, INT64_MAX);
    00499     uint y_label_width = GetStringBoundingBox(STR_GRAPH_Y_LABEL).width;
    00500
    00501     size->width  = max<uint>(size->width,  5 + y_label_width + this->num_on_x_axis * (x_label_width + 5) + 9);
    00502     size->height = max<uint>(size->height, 5 + (1 + GRAPH_NUM_LINES_Y * 2 + (this->month != 0xFF ? 3 : 1)) * FONT_HEIGHT_SMALL + 4);
    00503     size->height = max<uint>(size->height, size->width / 3);
    00504   }
    00505
    00506   virtual void DrawWidget(const Rect &r, int widget) const
    00507   {
    00508     if (widget != this->graph_widget) return;
    00509
    00510     DrawGraph(r);
    00511   }
    00512
    00513   virtual OverflowSafeInt64 GetGraphData(const Company *c, int j)
    00514   {
    00515     return INVALID_DATAPOINT;
    00516   }
    00517
    00518   virtual void OnClick(Point pt, int widget, int click_count)
    00519   {
    00520     /* Clicked on legend? */
    00521     if (widget == BGW_KEY_BUTTON) ShowGraphLegend();
    00522   }
    00523
    00524   virtual void OnTick()
    00525   {
    00526     this->UpdateStatistics(false);
    00527   }
    00528
    00529   virtual void OnInvalidateData(int data)
    00530   {
    00531     this->UpdateStatistics(true);
    00532   }
    00533
    00538   void UpdateStatistics(bool initialize)
    00539   {
    00540     uint excluded_companies = _legend_excluded_companies;
    00541
    00542     /* Exclude the companies which aren't valid */
    00543     for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
    00544       if (!Company::IsValidID(c)) SetBit(excluded_companies, c);
    00545     }
    00546
    00547     byte nums = 0;
    00548     const Company *c;
    00549     FOR_ALL_COMPANIES(c) {
    00550       nums = min(this->num_vert_lines, max(nums, c->num_valid_stat_ent));
    00551     }
    00552
    00553     int mo = (_cur_month / 3 - nums) * 3;
    00554     int yr = _cur_year;
    00555     while (mo < 0) {
    00556       yr--;
    00557       mo += 12;
    00558     }
    00559
    00560     if (!initialize && this->excluded_data == excluded_companies && this->num_on_x_axis == nums &&
    00561         this->year == yr && this->month == mo) {
    00562       /* There's no reason to get new stats */
    00563       return;
    00564     }
    00565
    00566     this->excluded_data = excluded_companies;
    00567     this->num_on_x_axis = nums;
    00568     this->year = yr;
    00569     this->month = mo;
    00570
    00571     int numd = 0;
    00572     for (CompanyID k = COMPANY_FIRST; k < MAX_COMPANIES; k++) {
    00573       c = Company::GetIfValid(k);
    00574       if (c != NULL) {
    00575         this->colours[numd] = _colour_gradient[c->colour][6];
    00576         for (int j = this->num_on_x_axis, i = 0; --j >= 0;) {
    00577           this->cost[numd][i] = (j >= c->num_valid_stat_ent) ? INVALID_DATAPOINT : GetGraphData(c, j);
    00578           i++;
    00579         }
    00580       }
    00581       numd++;
    00582     }
    00583
    00584     this->num_dataset = numd;
    00585   }
    00586 };
    00587
    00588
    00589 /********************/
    00590 /* OPERATING PROFIT */
    00591 /********************/
    00592
    00593 struct OperatingProfitGraphWindow : BaseGraphWindow {
    00594   OperatingProfitGraphWindow(const WindowDesc *desc, WindowNumber window_number) :
    00595       BaseGraphWindow(BGW_BACKGROUND, true, STR_JUST_CURRCOMPACT)
    00596   {
    00597     this->InitializeWindow(desc, window_number);
    00598   }
    00599
    00600   virtual OverflowSafeInt64 GetGraphData(const Company *c, int j)
    00601   {
    00602     return c->old_economy[j].income + c->old_economy[j].expenses;
    00603   }
    00604 };
    00605
    00606 static const NWidgetPart _nested_operating_profit_widgets[] = {
    00607   NWidget(NWID_HORIZONTAL),
    00608     NWidget(WWT_CLOSEBOX, COLOUR_GREY),
    00609     NWidget(WWT_CAPTION, COLOUR_GREY), SetDataTip(STR_GRAPH_OPERATING_PROFIT_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
    00610     NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, BGW_KEY_BUTTON), SetMinimalSize(50, 0), SetMinimalTextLines(1, WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM + 2), SetDataTip(STR_GRAPH_KEY_BUTTON, STR_GRAPH_KEY_TOOLTIP),
    00611     NWidget(WWT_SHADEBOX, COLOUR_GREY),
    00612     NWidget(WWT_STICKYBOX, COLOUR_GREY),
    00613   EndContainer(),
    00614   NWidget(WWT_PANEL, COLOUR_GREY, BGW_BACKGROUND), SetMinimalSize(576, 160), EndContainer(),
    00615 };
    00616
    00617 static const WindowDesc _operating_profit_desc(
    00618   WDP_AUTO, 0, 0,
    00619   WC_OPERATING_PROFIT, WC_NONE,
    00620   WDF_UNCLICK_BUTTONS,
    00621   _nested_operating_profit_widgets, lengthof(_nested_operating_profit_widgets)
    00622 );
    00623
    00624
    00625 void ShowOperatingProfitGraph()
    00626 {
    00627   AllocateWindowDescFront<OperatingProfitGraphWindow>(&_operating_profit_desc, 0);
    00628 }
    00629
    00630
    00631 /****************/
    00632 /* INCOME GRAPH */
    00633 /****************/
    00634
    00635 struct IncomeGraphWindow : BaseGraphWindow {
    00636   IncomeGraphWindow(const WindowDesc *desc, WindowNumber window_number) :
    00637       BaseGraphWindow(BGW_BACKGROUND, false, STR_JUST_CURRCOMPACT)
    00638   {
    00639     this->InitializeWindow(desc, window_number);
    00640   }
    00641
    00642   virtual OverflowSafeInt64 GetGraphData(const Company *c, int j)
    00643   {
    00644     return c->old_economy[j].income;
    00645   }
    00646 };
    00647
    00648 static const NWidgetPart _nested_income_graph_widgets[] = {
    00649   NWidget(NWID_HORIZONTAL),
    00650     NWidget(WWT_CLOSEBOX, COLOUR_GREY),
    00651     NWidget(WWT_CAPTION, COLOUR_GREY), SetDataTip(STR_GRAPH_INCOME_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
    00652     NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, BGW_KEY_BUTTON), SetMinimalSize(50, 0), SetMinimalTextLines(1, WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM + 2), SetDataTip(STR_GRAPH_KEY_BUTTON, STR_GRAPH_KEY_TOOLTIP),
    00653     NWidget(WWT_SHADEBOX, COLOUR_GREY),
    00654     NWidget(WWT_STICKYBOX, COLOUR_GREY),
    00655   EndContainer(),
    00656   NWidget(WWT_PANEL, COLOUR_GREY, BGW_BACKGROUND), SetMinimalSize(576, 128), EndContainer(),
    00657 };
    00658
    00659
    00660 static const WindowDesc _income_graph_desc(
    00661   WDP_AUTO, 0, 0,
    00662   WC_INCOME_GRAPH, WC_NONE,
    00663   WDF_UNCLICK_BUTTONS,
    00664   _nested_income_graph_widgets, lengthof(_nested_income_graph_widgets)
    00665 );
    00666
    00667 void ShowIncomeGraph()
    00668 {
    00669   AllocateWindowDescFront<IncomeGraphWindow>(&_income_graph_desc, 0);
    00670 }
    00671
    00672 /*******************/
    00673 /* DELIVERED CARGO */
    00674 /*******************/
    00675
    00676 struct DeliveredCargoGraphWindow : BaseGraphWindow {
    00677   DeliveredCargoGraphWindow(const WindowDesc *desc, WindowNumber window_number) :
    00678       BaseGraphWindow(BGW_BACKGROUND, false, STR_JUST_COMMA)
    00679   {
    00680     this->InitializeWindow(desc, window_number);
    00681   }
    00682
    00683   virtual OverflowSafeInt64 GetGraphData(const Company *c, int j)
    00684   {
    00685     return c->old_economy[j].delivered_cargo;
    00686   }
    00687 };
    00688
    00689 static const NWidgetPart _nested_delivered_cargo_graph_widgets[] = {
    00690   NWidget(NWID_HORIZONTAL),
    00691     NWidget(WWT_CLOSEBOX, COLOUR_GREY),
    00692     NWidget(WWT_CAPTION, COLOUR_GREY), SetDataTip(STR_GRAPH_CARGO_DELIVERED_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
    00693     NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, BGW_KEY_BUTTON), SetMinimalSize(50, 0), SetMinimalTextLines(1, WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM + 2), SetDataTip(STR_GRAPH_KEY_BUTTON, STR_GRAPH_KEY_TOOLTIP),
    00694     NWidget(WWT_SHADEBOX, COLOUR_GREY),
    00695     NWidget(WWT_STICKYBOX, COLOUR_GREY),
    00696   EndContainer(),
    00697   NWidget(WWT_PANEL, COLOUR_GREY, BGW_BACKGROUND), SetMinimalSize(576, 128), EndContainer(),
    00698 };
    00699
    00700 static const WindowDesc _delivered_cargo_graph_desc(
    00701   WDP_AUTO, 0, 0,
    00702   WC_DELIVERED_CARGO, WC_NONE,
    00703   WDF_UNCLICK_BUTTONS,
    00704   _nested_delivered_cargo_graph_widgets, lengthof(_nested_delivered_cargo_graph_widgets)
    00705 );
    00706
    00707 void ShowDeliveredCargoGraph()
    00708 {
    00709   AllocateWindowDescFront<DeliveredCargoGraphWindow>(&_delivered_cargo_graph_desc, 0);
    00710 }
    00711
    00712 /***********************/
    00713 /* PERFORMANCE HISTORY */
    00714 /***********************/
    00715
    00717 enum PerformanceHistoryGraphWidgets {
    00718   PHW_KEY,
    00719   PHW_DETAILED_PERFORMANCE,
    00720   PHW_BACKGROUND,
    00721 };
    00722
    00723 struct PerformanceHistoryGraphWindow : BaseGraphWindow {
    00724   PerformanceHistoryGraphWindow(const WindowDesc *desc, WindowNumber window_number) :
    00725       BaseGraphWindow(PHW_BACKGROUND, false, STR_JUST_COMMA)
    00726   {
    00727     this->InitializeWindow(desc, window_number);
    00728   }
    00729
    00730   virtual OverflowSafeInt64 GetGraphData(const Company *c, int j)
    00731   {
    00732     return c->old_economy[j].performance_history;
    00733   }
    00734
    00735   virtual void OnClick(Point pt, int widget, int click_count)
    00736   {
    00737     if (widget == PHW_DETAILED_PERFORMANCE) ShowPerformanceRatingDetail();
    00738     this->BaseGraphWindow::OnClick(pt, widget, click_count);
    00739   }
    00740 };
    00741
    00742 static const NWidgetPart _nested_performance_history_widgets[] = {
    00743   NWidget(NWID_HORIZONTAL),
    00744     NWidget(WWT_CLOSEBOX, COLOUR_GREY),
    00745     NWidget(WWT_CAPTION, COLOUR_GREY), SetDataTip(STR_GRAPH_COMPANY_PERFORMANCE_RATINGS_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
    00746     NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, PHW_DETAILED_PERFORMANCE), SetMinimalSize(50, 0), SetMinimalTextLines(1, WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM + 2), SetDataTip(STR_PERFORMANCE_DETAIL_KEY, STR_GRAPH_PERFORMANCE_DETAIL_TOOLTIP),
    00747     NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, PHW_KEY), SetMinimalSize(50, 0), SetMinimalTextLines(1, WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM + 2), SetDataTip(STR_GRAPH_KEY_BUTTON, STR_GRAPH_KEY_TOOLTIP),
    00748     NWidget(WWT_SHADEBOX, COLOUR_GREY),
    00749     NWidget(WWT_STICKYBOX, COLOUR_GREY),
    00750   EndContainer(),
    00751   NWidget(WWT_PANEL, COLOUR_GREY, PHW_BACKGROUND), SetMinimalSize(576, 224), EndContainer(),
    00752 };
    00753
    00754 static const WindowDesc _performance_history_desc(
    00755   WDP_AUTO, 0, 0,
    00756   WC_PERFORMANCE_HISTORY, WC_NONE,
    00757   WDF_UNCLICK_BUTTONS,
    00758   _nested_performance_history_widgets, lengthof(_nested_performance_history_widgets)
    00759 );
    00760
    00761 void ShowPerformanceHistoryGraph()
    00762 {
    00763   AllocateWindowDescFront<PerformanceHistoryGraphWindow>(&_performance_history_desc, 0);
    00764 }
    00765
    00766 /*****************/
    00767 /* COMPANY VALUE */
    00768 /*****************/
    00769
    00770 struct CompanyValueGraphWindow : BaseGraphWindow {
    00771   CompanyValueGraphWindow(const WindowDesc *desc, WindowNumber window_number) :
    00772       BaseGraphWindow(BGW_BACKGROUND, false, STR_JUST_CURRCOMPACT)
    00773   {
    00774     this->InitializeWindow(desc, window_number);
    00775   }
    00776
    00777   virtual OverflowSafeInt64 GetGraphData(const Company *c, int j)
    00778   {
    00779     return c->old_economy[j].company_value;
    00780   }
    00781 };
    00782
    00783 static const NWidgetPart _nested_company_value_graph_widgets[] = {
    00784   NWidget(NWID_HORIZONTAL),
    00785     NWidget(WWT_CLOSEBOX, COLOUR_GREY),
    00786     NWidget(WWT_CAPTION, COLOUR_GREY), SetDataTip(STR_GRAPH_COMPANY_VALUES_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
    00787     NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, BGW_KEY_BUTTON), SetMinimalSize(50, 0), SetMinimalTextLines(1, WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM + 2), SetDataTip(STR_GRAPH_KEY_BUTTON, STR_GRAPH_KEY_TOOLTIP),
    00788     NWidget(WWT_SHADEBOX, COLOUR_GREY),
    00789     NWidget(WWT_STICKYBOX, COLOUR_GREY),
    00790   EndContainer(),
    00791   NWidget(WWT_PANEL, COLOUR_GREY, BGW_BACKGROUND), SetMinimalSize(576, 224), EndContainer(),
    00792 };
    00793
    00794 static const WindowDesc _company_value_graph_desc(
    00795   WDP_AUTO, 0, 0,
    00796   WC_COMPANY_VALUE, WC_NONE,
    00797   WDF_UNCLICK_BUTTONS,
    00798   _nested_company_value_graph_widgets, lengthof(_nested_company_value_graph_widgets)
    00799 );
    00800
    00801 void ShowCompanyValueGraph()
    00802 {
    00803   AllocateWindowDescFront<CompanyValueGraphWindow>(&_company_value_graph_desc, 0);
    00804 }
    00805
    00806 /*****************/
    00807 /* PAYMENT RATES */
    00808 /*****************/
    00809
    00811 enum CargoPaymentRatesWidgets {
    00812   CPW_BACKGROUND,
    00813   CPW_HEADER,
    00814   CPW_GRAPH,
    00815   CPW_FOOTER,
    00816   CPW_CARGO_FIRST,
    00817 };
    00818
    00819 struct PaymentRatesGraphWindow : BaseGraphWindow {
    00820   PaymentRatesGraphWindow(const WindowDesc *desc, WindowNumber window_number) :
    00821       BaseGraphWindow(CPW_GRAPH, false, STR_JUST_CURRCOMPACT)
    00822   {
    00823     this->num_on_x_axis = 20;
    00824     this->num_vert_lines = 20;
    00825     this->month = 0xFF;
    00826     this->x_values_start     = 10;
    00827     this->x_values_increment = 10;
    00828
    00829     /* Initialise the dataset */
    00830     this->OnHundredthTick();
    00831
    00832     this->InitNested(desc, window_number);
    00833
    00834     int i = 0;
    00835     const CargoSpec *cs;
    00836     FOR_ALL_CARGOSPECS(cs) {
    00837       this->SetWidgetLoweredState(CPW_CARGO_FIRST + cs->Index(), !HasBit(_legend_excluded_cargo, i));
    00838       i++;
    00839     }
    00840   }
    00841
    00842   virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
    00843   {
    00844     if (widget < CPW_CARGO_FIRST) {
    00845       BaseGraphWindow::UpdateWidgetSize(widget, size, padding, fill, resize);
    00846       return;
    00847     }
    00848
    00849     const CargoSpec *cs = CargoSpec::Get(widget - CPW_CARGO_FIRST);
    00850     SetDParam(0, cs->name);
    00851     Dimension d = GetStringBoundingBox(STR_GRAPH_CARGO_PAYMENT_CARGO);
    00852     d.width += 14; // colour field
    00853     d.width += WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT;
    00854     d.height += WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
    00855     *size = maxdim(d, *size);
    00856   }
    00857
    00858   virtual void DrawWidget(const Rect &r, int widget) const
    00859   {
    00860     if (widget < CPW_CARGO_FIRST) {
    00861       BaseGraphWindow::DrawWidget(r, widget);
    00862       return;
    00863     }
    00864
    00865     const CargoSpec *cs = CargoSpec::Get(widget - CPW_CARGO_FIRST);
    00866     bool rtl = _dynlang.text_dir == TD_RTL;
    00867
    00868     /* Since the buttons have no text, no images,
    00869      * both the text and the coloured box have to be manually painted.
    00870      * clk_dif will move one pixel down and one pixel to the right
    00871      * when the button is clicked */
    00872     byte clk_dif = this->IsWidgetLowered(widget) ? 1 : 0;
    00873     int x = r.left + WD_FRAMERECT_LEFT;
    00874     int y = r.top;
    00875
    00876     int rect_x = clk_dif + (rtl ? r.right - 12 : r.left + WD_FRAMERECT_LEFT);
    00877
    00878     GfxFillRect(rect_x, y + clk_dif, rect_x + 8, y + 5 + clk_dif, 0);
    00879     GfxFillRect(rect_x + 1, y + 1 + clk_dif, rect_x + 7, y + 4 + clk_dif, cs->legend_colour);
    00880     SetDParam(0, cs->name);
    00881     DrawString(rtl ? r.left : x + 14 + clk_dif, (rtl ? r.right - 14 + clk_dif : r.right), y + clk_dif, STR_GRAPH_CARGO_PAYMENT_CARGO);
    00882   }
    00883
    00884   virtual void OnClick(Point pt, int widget, int click_count)
    00885   {
    00886     if (widget >= CPW_CARGO_FIRST) {
    00887       int i = 0;
    00888       const CargoSpec *cs;
    00889       FOR_ALL_CARGOSPECS(cs) {
    00890         if (cs->Index() + CPW_CARGO_FIRST == widget) break;
    00891         i++;
    00892       }
    00893
    00894       ToggleBit(_legend_excluded_cargo, i);
    00895       this->ToggleWidgetLoweredState(widget);
    00896       this->excluded_data = _legend_excluded_cargo;
    00897       this->SetDirty();
    00898     }
    00899   }
    00900
    00901   virtual void OnTick()
    00902   {
    00903     /* Override default OnTick */
    00904   }
    00905
    00906   virtual void OnInvalidateData(int data)
    00907   {
    00908     this->OnHundredthTick();
    00909   }
    00910
    00911   virtual void OnHundredthTick()
    00912   {
    00913     this->excluded_data = _legend_excluded_cargo;
    00914
    00915     int i = 0;
    00916     const CargoSpec *cs;
    00917     FOR_ALL_CARGOSPECS(cs) {
    00918       this->colours[i] = cs->legend_colour;
    00919       for (uint j = 0; j != 20; j++) {
    00920         this->cost[i][j] = GetTransportedGoodsIncome(10, 20, j * 4 + 4, cs->Index());
    00921       }
    00922
    00923       i++;
    00924     }
    00925     this->num_dataset = i;
    00926   }
    00927 };
    00928
    00930 static NWidgetBase *MakeCargoButtons(int *biggest_index)
    00931 {
    00932   NWidgetVertical *ver = new NWidgetVertical;
    00933
    00934   const CargoSpec *cs;
    00935   FOR_ALL_CARGOSPECS(cs) {
    00936     *biggest_index = CPW_CARGO_FIRST + cs->Index();
    00937     NWidgetBackground *leaf = new NWidgetBackground(WWT_PANEL, COLOUR_ORANGE, *biggest_index, NULL);
    00938     leaf->tool_tip = STR_GRAPH_CARGO_PAYMENT_TOGGLE_CARGO;
    00939     leaf->SetFill(1, 0);
    00940     leaf->SetLowered(true);
    00941     ver->Add(leaf);
    00942   }
    00943   return ver;
    00944 }
    00945
    00946
    00947 static const NWidgetPart _nested_cargo_payment_rates_widgets[] = {
    00948   NWidget(NWID_HORIZONTAL),
    00949     NWidget(WWT_CLOSEBOX, COLOUR_GREY),
    00950     NWidget(WWT_CAPTION, COLOUR_GREY), SetDataTip(STR_GRAPH_CARGO_PAYMENT_RATES_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
    00951     NWidget(WWT_SHADEBOX, COLOUR_GREY),
    00952     NWidget(WWT_STICKYBOX, COLOUR_GREY),
    00953   EndContainer(),
    00954   NWidget(WWT_PANEL, COLOUR_GREY, CPW_BACKGROUND), SetMinimalSize(568, 128), SetResize(0, 1),
    00955     NWidget(NWID_VERTICAL),
    00956       NWidget(NWID_HORIZONTAL),
    00957         NWidget(NWID_SPACER), SetFill(1, 0),
    00958         NWidget(WWT_TEXT, COLOUR_GREY, CPW_HEADER), SetMinimalSize(0, 6), SetPadding(2, 0, 2, 0), SetDataTip(STR_GRAPH_CARGO_PAYMENT_RATES_TITLE, STR_NULL),
    00959         NWidget(NWID_SPACER), SetFill(1, 0),
    00960       EndContainer(),
    00961       NWidget(NWID_HORIZONTAL),
    00962         NWidget(WWT_EMPTY, COLOUR_GREY, CPW_GRAPH), SetMinimalSize(495, 0), SetFill(1, 1),
    00963         NWidget(NWID_VERTICAL),
    00964           NWidget(NWID_SPACER), SetMinimalSize(0, 24), SetFill(0, 0),
    00965             NWidgetFunction(MakeCargoButtons),
    00966           NWidget(NWID_SPACER), SetMinimalSize(0, 24), SetFill(0, 1),
    00967         EndContainer(),
    00968         NWidget(NWID_SPACER), SetMinimalSize(5, 0), SetFill(0, 1),
    00969       EndContainer(),
    00970       NWidget(NWID_HORIZONTAL),
    00971         NWidget(NWID_SPACER), SetFill(1, 0),
    00972         NWidget(WWT_TEXT, COLOUR_GREY, CPW_FOOTER), SetMinimalSize(0, 6), SetPadding(2, 0, 2, 0), SetDataTip(STR_GRAPH_CARGO_PAYMENT_RATES_X_LABEL, STR_NULL),
    00973         NWidget(NWID_SPACER), SetFill(1, 0),
    00974       EndContainer(),
    00975     EndContainer(),
    00976   EndContainer(),
    00977 };
    00978
    00979 static const WindowDesc _cargo_payment_rates_desc(
    00980   WDP_AUTO, 0, 0,
    00981   WC_PAYMENT_RATES, WC_NONE,
    00982   0,
    00983   _nested_cargo_payment_rates_widgets, lengthof(_nested_cargo_payment_rates_widgets)
    00984 );
    00985
    00986
    00987 void ShowCargoPaymentRates()
    00988 {
    00989   AllocateWindowDescFront<PaymentRatesGraphWindow>(&_cargo_payment_rates_desc, 0);
    00990 }
    00991
    00992 /************************/
    00993 /* COMPANY LEAGUE TABLE */
    00994 /************************/
    00995
    00997 enum CompanyLeagueWidgets {
    00998   CLW_BACKGROUND,
    00999 };
    01000
    01001 static const StringID _performance_titles[] = {
    01002   STR_COMPANY_LEAGUE_PERFORMANCE_TITLE_ENGINEER,
    01003   STR_COMPANY_LEAGUE_PERFORMANCE_TITLE_ENGINEER,
    01004   STR_COMPANY_LEAGUE_PERFORMANCE_TITLE_TRAFFIC_MANAGER,
    01005   STR_COMPANY_LEAGUE_PERFORMANCE_TITLE_TRAFFIC_MANAGER,
    01006   STR_COMPANY_LEAGUE_PERFORMANCE_TITLE_TRANSPORT_COORDINATOR,
    01007   STR_COMPANY_LEAGUE_PERFORMANCE_TITLE_TRANSPORT_COORDINATOR,
    01008   STR_COMPANY_LEAGUE_PERFORMANCE_TITLE_ROUTE_SUPERVISOR,
    01009   STR_COMPANY_LEAGUE_PERFORMANCE_TITLE_ROUTE_SUPERVISOR,
    01010   STR_COMPANY_LEAGUE_PERFORMANCE_TITLE_DIRECTOR,
    01011   STR_COMPANY_LEAGUE_PERFORMANCE_TITLE_DIRECTOR,
    01012   STR_COMPANY_LEAGUE_PERFORMANCE_TITLE_CHIEF_EXECUTIVE,
    01013   STR_COMPANY_LEAGUE_PERFORMANCE_TITLE_CHIEF_EXECUTIVE,
    01014   STR_COMPANY_LEAGUE_PERFORMANCE_TITLE_CHAIRMAN,
    01015   STR_COMPANY_LEAGUE_PERFORMANCE_TITLE_CHAIRMAN,
    01016   STR_COMPANY_LEAGUE_PERFORMANCE_TITLE_PRESIDENT,
    01017   STR_COMPANY_LEAGUE_PERFORMANCE_TITLE_TYCOON,
    01018 };
    01019
    01020 static inline StringID GetPerformanceTitleFromValue(uint value)
    01021 {
    01022   return _performance_titles[minu(value, 1000) >> 6];
    01023 }
    01024
    01025 class CompanyLeagueWindow : public Window {
    01026 private:
    01027   GUIList<const Company*> companies;
    01028   uint ordinal_width;
    01029   uint text_width;
    01030
    01034   void BuildCompanyList()
    01035   {
    01036     if (!this->companies.NeedRebuild()) return;
    01037
    01038     this->companies.Clear();
    01039
    01040     const Company *c;
    01041     FOR_ALL_COMPANIES(c) {
    01042       *this->companies.Append() = c;
    01043     }
    01044
    01045     this->companies.Compact();
    01046     this->companies.RebuildDone();
    01047   }
    01048
    01050   static int CDECL PerformanceSorter(const Company * const *c1, const Company * const *c2)
    01051   {
    01052     return (*c2)->old_economy[1].performance_history - (*c1)->old_economy[1].performance_history;
    01053   }
    01054
    01055 public:
    01056   CompanyLeagueWindow(const WindowDesc *desc, WindowNumber window_number) : Window()
    01057   {
    01058     this->InitNested(desc, window_number);
    01059     this->companies.ForceRebuild();
    01060     this->companies.NeedResort();
    01061   }
    01062
    01063   virtual void OnPaint()
    01064   {
    01065     this->BuildCompanyList();
    01066     this->companies.Sort(&PerformanceSorter);
    01067
    01068     this->DrawWidgets();
    01069   }
    01070
    01071   virtual void DrawWidget(const Rect &r, int widget) const
    01072   {
    01073     if (widget != CLW_BACKGROUND) return;
    01074
    01075     uint y = r.top + WD_FRAMERECT_TOP;
    01076     int icon_y_offset = 1 + (FONT_HEIGHT_NORMAL - 10) / 2;
    01077
    01078     bool rtl = _dynlang.text_dir == TD_RTL;
    01079     uint ordinal_left  = rtl ? r.right - WD_FRAMERECT_LEFT - this->ordinal_width : r.left + WD_FRAMERECT_LEFT;
    01080     uint ordinal_right = rtl ? r.right - WD_FRAMERECT_LEFT : r.left + WD_FRAMERECT_LEFT + this->ordinal_width;
    01081     uint icon_left     = r.left + WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT + (rtl ? this->text_width : this->ordinal_width);
    01082     uint text_left     = rtl ? r.left + WD_FRAMERECT_LEFT : r.right - WD_FRAMERECT_LEFT - this->text_width;
    01083     uint text_right    = rtl ? r.left + WD_FRAMERECT_LEFT + this->text_width : r.right - WD_FRAMERECT_LEFT;
    01084
    01085     for (uint i = 0; i != this->companies.Length(); i++) {
    01086       const Company *c = this->companies[i];
    01087       DrawString(ordinal_left, ordinal_right, y, i + STR_ORDINAL_NUMBER_1ST, i == 0 ? TC_WHITE : TC_YELLOW);
    01088
    01089       DrawCompanyIcon(c->index, icon_left, y + icon_y_offset);
    01090
    01091       SetDParam(0, c->index);
    01092       SetDParam(1, c->index);
    01093       SetDParam(2, GetPerformanceTitleFromValue(c->old_economy[1].performance_history));
    01094       DrawString(text_left, text_right, y, STR_COMPANY_LEAGUE_COMPANY_NAME);
    01095       y += FONT_HEIGHT_NORMAL;
    01096     }
    01097   }
    01098
    01099   virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
    01100   {
    01101     if (widget != CLW_BACKGROUND) return;
    01102
    01103     this->ordinal_width = 0;
    01104     for (uint i = 0; i < MAX_COMPANIES; i++) {
    01105       this->ordinal_width = max(this->ordinal_width, GetStringBoundingBox(STR_ORDINAL_NUMBER_1ST + i).width);
    01106     }
    01107     this->ordinal_width += 5; // Keep some extra spacing
    01108
    01109     uint widest_width = 0;
    01110     uint widest_title = 0;
    01111     for (uint i = 0; i < lengthof(_performance_titles); i++) {
    01112       uint width = GetStringBoundingBox(_performance_titles[i]).width;
    01113       if (width > widest_width) {
    01114         widest_title = i;
    01115         widest_width = width;
    01116       }
    01117     }
    01118
    01119     const Company *c;
    01120     FOR_ALL_COMPANIES(c) {
    01121       SetDParam(0, c->index);
    01122       SetDParam(1, c->index);
    01123       SetDParam(2, _performance_titles[widest_title]);
    01124       widest_width = max(widest_width, GetStringBoundingBox(STR_COMPANY_LEAGUE_COMPANY_NAME).width);
    01125     }
    01126
    01127     this->text_width = widest_width + 30; // Keep some extra spacing
    01128
    01129     size->width = WD_FRAMERECT_LEFT + this->ordinal_width + WD_FRAMERECT_RIGHT + 16 + WD_FRAMERECT_LEFT + this->text_width + WD_FRAMERECT_RIGHT;
    01130   }
    01131
    01132
    01133   virtual void OnTick()
    01134   {
    01135     if (this->companies.NeedResort()) {
    01136       this->SetDirty();
    01137     }
    01138   }
    01139
    01140   virtual void OnInvalidateData(int data)
    01141   {
    01142     if (data == 0) {
    01143       this->companies.ForceRebuild();
    01144     } else {
    01145       this->companies.ForceResort();
    01146     }
    01147   }
    01148 };
    01149
    01150 static const NWidgetPart _nested_company_league_widgets[] = {
    01151   NWidget(NWID_HORIZONTAL),
    01152     NWidget(WWT_CLOSEBOX, COLOUR_GREY),
    01153     NWidget(WWT_CAPTION, COLOUR_GREY), SetDataTip(STR_COMPANY_LEAGUE_TABLE_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
    01154     NWidget(WWT_SHADEBOX, COLOUR_GREY),
    01155     NWidget(WWT_STICKYBOX, COLOUR_GREY),
    01156   EndContainer(),
    01157   NWidget(WWT_PANEL, COLOUR_GREY, CLW_BACKGROUND), SetMinimalSize(400, 0), SetMinimalTextLines(15, WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM),
    01158 };
    01159
    01160 static const WindowDesc _company_league_desc(
    01161   WDP_AUTO, 0, 0,
    01162   WC_COMPANY_LEAGUE, WC_NONE,
    01163   0,
    01164   _nested_company_league_widgets, lengthof(_nested_company_league_widgets)
    01165 );
    01166
    01167 void ShowCompanyLeagueTable()
    01168 {
    01169   AllocateWindowDescFront<CompanyLeagueWindow>(&_company_league_desc, 0);
    01170 }
    01171
    01172 /*****************************/
    01173 /* PERFORMANCE RATING DETAIL */
    01174 /*****************************/
    01175
    01177 enum PerformanceRatingDetailsWidgets {
    01178   PRW_SCORE_FIRST,
    01179   PRW_SCORE_LAST = PRW_SCORE_FIRST + (SCORE_END - SCORE_BEGIN) - 1,
    01180
    01181   PRW_COMPANY_FIRST,
    01182   PRW_COMPANY_LAST  = PRW_COMPANY_FIRST + MAX_COMPANIES - 1,
    01183 };
    01184
    01185 struct PerformanceRatingDetailWindow : Window {
    01186   static CompanyID company;
    01187   int timeout;
    01188
    01189   PerformanceRatingDetailWindow(const WindowDesc *desc, WindowNumber window_number) : Window()
    01190   {
    01191     this->UpdateCompanyStats();
    01192
    01193     this->InitNested(desc, window_number);
    01194     this->OnInvalidateData(INVALID_COMPANY);
    01195   }
    01196
    01197   void UpdateCompanyStats()
    01198   {
    01199     /* Update all company stats with the current data
    01200      * (this is because _score_info is not saved to a savegame) */
    01201     Company *c;
    01202     FOR_ALL_COMPANIES(c) {
    01203       UpdateCompanyRatingAndValue(c, false);
    01204     }
    01205
    01206     this->timeout = DAY_TICKS * 5;
    01207   }
    01208
    01209   uint score_info_left;
    01210   uint score_info_right;
    01211   uint bar_left;
    01212   uint bar_right;
    01213   uint bar_width;
    01214   uint bar_height;
    01215   uint score_detail_left;
    01216   uint score_detail_right;
    01217
    01218   virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
    01219   {
    01220     switch (widget) {
    01221       case PRW_SCORE_FIRST:
    01222         this->bar_height = FONT_HEIGHT_NORMAL + 4;
    01223         size->height = this->bar_height + 2 * WD_MATRIX_TOP;
    01224
    01225         uint score_info_width = 0;
    01226         for (uint i = SCORE_BEGIN; i < SCORE_END; i++) {
    01227           score_info_width = max(score_info_width, GetStringBoundingBox(STR_PERFORMANCE_DETAIL_VEHICLES + i).width);
    01228         }
    01229         SetDParam(0, 1000);
    01230         score_info_width += GetStringBoundingBox(STR_BLACK_COMMA).width + WD_FRAMERECT_LEFT;
    01231
    01232         SetDParam(0, 100);
    01233         this->bar_width = GetStringBoundingBox(STR_PERFORMANCE_DETAIL_PERCENT).width + 20; // Wide bars!
    01234
    01235         /* At this number we are roughly at the max; it can become wider,
    01236          * but then you need at 1000 times more money. At that time you're
    01237          * not that interested anymore in the last few digits anyway. */
    01238         uint max = 999999999; // nine 9s
    01239         SetDParam(0, max);
    01240         SetDParam(1, max);
    01241         uint score_detail_width = GetStringBoundingBox(STR_PERFORMANCE_DETAIL_AMOUNT_CURRENCY).width;
    01242
    01243         size->width = 7 + score_info_width + 5 + this->bar_width + 5 + score_detail_width + 7;
    01244         uint left  = 7;
    01245         uint right = size->width - 7;
    01246
    01247         bool rtl = _dynlang.text_dir == TD_RTL;
    01248         this->score_info_left  = rtl ? right - score_info_width : left;
    01249         this->score_info_right = rtl ? right : left + score_info_width;
    01250
    01251         this->score_detail_left  = rtl ? left : right - score_detail_width;
    01252         this->score_detail_right = rtl ? left + score_detail_width : right;
    01253
    01254         this->bar_left  = left + (rtl ? score_detail_width : score_info_width) + 5;
    01255         this->bar_right = this->bar_left + this->bar_width;
    01256         break;
    01257     }
    01258   }
    01259
    01260   virtual void OnPaint()
    01261   {
    01262     /* Draw standard stuff */
    01263     this->DrawWidgets();
    01264   }
    01265
    01266   virtual void DrawWidget(const Rect &r, int widget) const
    01267   {
    01268     /* No need to draw when there's nothing to draw */
    01269     if (this->company == INVALID_COMPANY) return;
    01270
    01271     if (IsInsideMM(widget, PRW_COMPANY_FIRST, PRW_COMPANY_LAST + 1)) {
    01272       if (this->IsWidgetDisabled(widget)) return;
    01273       CompanyID cid = (CompanyID)(widget - PRW_COMPANY_FIRST);
    01274       int offset = (cid == this->company) ? 1 : 0;
    01275       Dimension sprite_size = GetSpriteSize(SPR_COMPANY_ICON);
    01276       DrawCompanyIcon(cid, (r.left + r.right - sprite_size.width) / 2 + offset, (r.top + r.bottom - sprite_size.height) / 2 + offset);
    01277       return;
    01278     }
    01279
    01280     if (!IsInsideMM(widget, PRW_SCORE_FIRST, PRW_SCORE_LAST + 1)) return;
    01281
    01282     ScoreID score_type = (ScoreID)(widget - PRW_SCORE_FIRST);
    01283
    01284       /* The colours used to show how the progress is going */
    01285     int colour_done = _colour_gradient[COLOUR_GREEN][4];
    01286     int colour_notdone = _colour_gradient[COLOUR_RED][4];
    01287
    01288     /* Draw all the score parts */
    01289     int val    = _score_part[company][score_type];
    01290     int needed = _score_info[score_type].needed;
    01291     int score  = _score_info[score_type].score;
    01292
    01293     /* SCORE_TOTAL has his own rules ;) */
    01294     if (score_type == SCORE_TOTAL) {
    01295       for (ScoreID i = SCORE_BEGIN; i < SCORE_END; i++) score += _score_info[i].score;
    01296       needed = SCORE_MAX;
    01297     }
    01298
    01299     uint bar_top  = r.top + WD_MATRIX_TOP;
    01300     uint text_top = bar_top + 2;
    01301
    01302     DrawString(this->score_info_left, this->score_info_right, text_top, STR_PERFORMANCE_DETAIL_VEHICLES + score_type);
    01303
    01304     /* Draw the score */
    01305     SetDParam(0, score);
    01306     DrawString(this->score_info_left, this->score_info_right, text_top, STR_BLACK_COMMA, TC_FROMSTRING, SA_RIGHT);
    01307
    01308     /* Calculate the %-bar */
    01309     uint x = Clamp(val, 0, needed) * this->bar_width / needed;
    01310     bool rtl = _dynlang.text_dir == TD_RTL;
    01311     if (rtl) {
    01312       x = this->bar_right - x;
    01313     } else {
    01314       x = this->bar_left + x;
    01315     }
    01316
    01317     /* Draw the bar */
    01318     if (x != this->bar_left)  GfxFillRect(this->bar_left, bar_top, x, bar_top + this->bar_height, rtl ? colour_notdone : colour_done);
    01319     if (x != this->bar_right) GfxFillRect(x, bar_top, this->bar_right, bar_top + this->bar_height, rtl ? colour_done : colour_notdone);
    01320
    01321     /* Draw it */
    01322     SetDParam(0, Clamp(val, 0, needed) * 100 / needed);
    01323     DrawString(this->bar_left, this->bar_right, text_top, STR_PERFORMANCE_DETAIL_PERCENT, TC_FROMSTRING, SA_CENTER);
    01324
    01325     /* SCORE_LOAN is inversed */
    01326     if (score_type == SCORE_LOAN) val = needed - val;
    01327
    01328     /* Draw the amount we have against what is needed
    01329      * For some of them it is in currency format */
    01330     SetDParam(0, val);
    01331     SetDParam(1, needed);
    01332     switch (score_type) {
    01333       case SCORE_MIN_PROFIT:
    01334       case SCORE_MIN_INCOME:
    01335       case SCORE_MAX_INCOME:
    01336       case SCORE_MONEY:
    01337       case SCORE_LOAN:
    01338         DrawString(this->score_detail_left, this->score_detail_right, text_top, STR_PERFORMANCE_DETAIL_AMOUNT_CURRENCY);
    01339         break;
    01340       default:
    01341         DrawString(this->score_detail_left, this->score_detail_right, text_top, STR_PERFORMANCE_DETAIL_AMOUNT_INT);
    01342     }
    01343   }
    01344
    01345   virtual void OnClick(Point pt, int widget, int click_count)
    01346   {
    01347     /* Check which button is clicked */
    01348     if (IsInsideMM(widget, PRW_COMPANY_FIRST, PRW_COMPANY_LAST + 1)) {
    01349       /* Is it no on disable? */
    01350       if (!this->IsWidgetDisabled(widget)) {
    01351         this->RaiseWidget(this->company + PRW_COMPANY_FIRST);
    01352         this->company = (CompanyID)(widget - PRW_COMPANY_FIRST);
    01353         this->LowerWidget(this->company + PRW_COMPANY_FIRST);
    01354         this->SetDirty();
    01355       }
    01356     }
    01357   }
    01358
    01359   virtual void OnTick()
    01360   {
    01361     if (_pause_mode != PM_UNPAUSED) return;
    01362
    01363     /* Update the company score every 5 days */
    01364     if (--this->timeout == 0) {
    01365       this->UpdateCompanyStats();
    01366       this->SetDirty();
    01367     }
    01368   }
    01369
    01374   virtual void OnInvalidateData(int data)
    01375   {
    01376     /* Disable the companies who are not active */
    01377     for (CompanyID i = COMPANY_FIRST; i < MAX_COMPANIES; i++) {
    01378       this->SetWidgetDisabledState(i + PRW_COMPANY_FIRST, !Company::IsValidID(i));
    01379     }
    01380
    01381     /* Check if the currently selected company is still active. */
    01382     if (this->company != INVALID_COMPANY && !Company::IsValidID(this->company)) {
    01383       /* Raise the widget for the previous selection. */
    01384       this->RaiseWidget(this->company + PRW_COMPANY_FIRST);
    01385       this->company = INVALID_COMPANY;
    01386     }
    01387
    01388     if (this->company == INVALID_COMPANY) {
    01389       const Company *c;
    01390       FOR_ALL_COMPANIES(c) {
    01391         this->company = c->index;
    01392         break;
    01393       }
    01394     }
    01395
    01396     /* Make sure the widget is lowered */
    01397     this->LowerWidget(this->company + PRW_COMPANY_FIRST);
    01398   }
    01399 };
    01400
    01401 CompanyID PerformanceRatingDetailWindow::company = INVALID_COMPANY;
    01402
    01408 static NWidgetBase *MakePerformanceDetailPanels(int *biggest_index)
    01409 {
    01410   const StringID performance_tips[] = {
    01411     STR_PERFORMANCE_DETAIL_VEHICLES_TOOLTIP,
    01412     STR_PERFORMANCE_DETAIL_STATIONS_TOOLTIP,
    01413     STR_PERFORMANCE_DETAIL_MIN_PROFIT_TOOLTIP,
    01414     STR_PERFORMANCE_DETAIL_MIN_INCOME_TOOLTIP,
    01415     STR_PERFORMANCE_DETAIL_MAX_INCOME_TOOLTIP,
    01416     STR_PERFORMANCE_DETAIL_DELIVERED_TOOLTIP,
    01417     STR_PERFORMANCE_DETAIL_CARGO_TOOLTIP,
    01418     STR_PERFORMANCE_DETAIL_MONEY_TOOLTIP,
    01419     STR_PERFORMANCE_DETAIL_LOAN_TOOLTIP,
    01420     STR_PERFORMANCE_DETAIL_TOTAL_TOOLTIP,
    01421   };
    01422
    01423   assert_compile(lengthof(performance_tips) == SCORE_END - SCORE_BEGIN);
    01424
    01425   NWidgetVertical *vert = new NWidgetVertical(NC_EQUALSIZE);
    01426   for (int widnum = PRW_SCORE_FIRST; widnum <= PRW_SCORE_LAST; widnum++) {
    01427     NWidgetBackground *panel = new NWidgetBackground(WWT_PANEL, COLOUR_GREY, widnum);
    01428     panel->SetFill(1, 1);
    01429     panel->SetDataTip(0x0, performance_tips[widnum - PRW_SCORE_FIRST]);
    01430     vert->Add(panel);
    01431   }
    01432   *biggest_index = PRW_SCORE_LAST;
    01433   return vert;
    01434 }
    01435
    01442 static NWidgetBase *MakeCompanyButtonRows(int *biggest_index)
    01443 {
    01444   static const int MAX_LENGTH = 8; // Maximal number of company buttons in one row.
    01445   NWidgetVertical *vert = NULL; // Storage for all rows.
    01446   NWidgetHorizontal *hor = NULL; // Storage for buttons in one row.
    01447   int hor_length = 0;
    01448
    01449   Dimension sprite_size = GetSpriteSize(SPR_COMPANY_ICON);
    01450   sprite_size.width  += WD_MATRIX_LEFT + WD_MATRIX_RIGHT;
    01451   sprite_size.height += WD_MATRIX_TOP + WD_MATRIX_BOTTOM + 1; // 1 for the 'offset' of being pressed
    01452
    01453   for (int widnum = PRW_COMPANY_FIRST; widnum <= PRW_COMPANY_LAST; widnum++) {
    01454     /* Ensure there is room in 'hor' for another button. */
    01455     if (hor_length == MAX_LENGTH) {
    01456       if (vert == NULL) vert = new NWidgetVertical();
    01457       vert->Add(hor);
    01458       hor = NULL;
    01459       hor_length = 0;
    01460     }
    01461     if (hor == NULL) {
    01462       hor = new NWidgetHorizontal();
    01463       hor_length = 0;
    01464     }
    01465
    01466     NWidgetBackground *panel = new NWidgetBackground(WWT_PANEL, COLOUR_GREY, widnum);
    01467     panel->SetMinimalSize(sprite_size.width, sprite_size.height);
    01468     panel->SetFill(1, 0);
    01469     panel->SetDataTip(0x0, STR_PERFORMANCE_DETAIL_SELECT_COMPANY_TOOLTIP);
    01470     hor->Add(panel);
    01471     hor_length++;
    01472   }
    01473   *biggest_index = PRW_COMPANY_LAST;
    01474   if (vert == NULL) return hor; // All buttons fit in a single row.
    01475
    01476   if (hor_length > 0 && hor_length < MAX_LENGTH) {
    01477     /* Last row is partial, add a spacer at the end to force all buttons to the left. */
    01478     NWidgetSpacer *spc = new NWidgetSpacer(0, 0);
    01479     spc->SetMinimalSize(sprite_size.width, sprite_size.height);
    01480     spc->SetFill(1, 0);
    01481     hor->Add(spc);
    01482   }
    01483   if (hor != NULL) vert->Add(hor);
    01484   return vert;
    01485 }
    01486
    01487 static const NWidgetPart _nested_performance_rating_detail_widgets[] = {
    01488   NWidget(NWID_HORIZONTAL),
    01489     NWidget(WWT_CLOSEBOX, COLOUR_GREY),
    01490     NWidget(WWT_CAPTION, COLOUR_GREY), SetDataTip(STR_PERFORMANCE_DETAIL, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
    01491     NWidget(WWT_SHADEBOX, COLOUR_GREY),
    01492     NWidget(WWT_STICKYBOX, COLOUR_GREY),
    01493   EndContainer(),
    01494   NWidget(WWT_PANEL, COLOUR_GREY),
    01495     NWidgetFunction(MakeCompanyButtonRows), SetPadding(0, 1, 1, 2),
    01496   EndContainer(),
    01497   NWidgetFunction(MakePerformanceDetailPanels),
    01498 };
    01499
    01500 static const WindowDesc _performance_rating_detail_desc(
    01501   WDP_AUTO, 0, 0,
    01502   WC_PERFORMANCE_DETAIL, WC_NONE,
    01503   0,
    01504   _nested_performance_rating_detail_widgets, lengthof(_nested_performance_rating_detail_widgets)
    01505 );
    01506
    01507 void ShowPerformanceRatingDetail()
    01508 {
    01509   AllocateWindowDescFront<PerformanceRatingDetailWindow>(&_performance_rating_detail_desc, 0);
    01510 }