# graph_gui.cpp

Go to the documentation of this file.

    00001 /* $Id: graph_gui.cpp 14269 2008-09-07 22:14:48Z rubidium $ */
    00002
    00005 #include "stdafx.h"
    00006 #include "openttd.h"
    00007 #include "gui.h"
    00008 #include "window_gui.h"
    00009 #include "player_base.h"
    00010 #include "player_gui.h"
    00011 #include "economy_func.h"
    00012 #include "variables.h"
    00013 #include "cargotype.h"
    00014 #include "strings_func.h"
    00015 #include "core/alloc_func.hpp"
    00016 #include "window_func.h"
    00017 #include "date_func.h"
    00018 #include "gfx_func.h"
    00019
    00020 #include "table/strings.h"
    00021 #include "table/sprites.h"
    00022
    00023 /* Bitmasks of player and cargo indices that shouldn't be drawn. */
    00024 static uint _legend_excluded_players;
    00025 static uint _legend_excluded_cargo;
    00026
    00027 /************************/
    00028 /* GENERIC GRAPH DRAWER */
    00029 /************************/
    00030
    00031 enum {
    00032   GRAPH_MAX_DATASETS = 32,
    00033   GRAPH_AXIS_LABEL_COLOUR = TC_BLACK,
    00034   GRAPH_AXIS_LINE_COLOUR  = 215,
    00035
    00036   GRAPH_X_POSITION_BEGINNING  = 44,
    00037   GRAPH_X_POSITION_SEPARATION = 22,
    00038
    00039   GRAPH_NUM_LINES_Y = 9,
    00040   /* 9 is convenient as that means the distance between them is the height of the graph / 8,
    00041    * which is the same
    00042    * as height >> 3. */
    00043 };
    00044
    00045 /* Apparently these don't play well with enums. */
    00046 static const OverflowSafeInt64 INVALID_DATAPOINT(INT64_MAX); // Value used for a datapoint that shouldn't be drawn.
    00047 static const uint INVALID_DATAPOINT_POS = UINT_MAX;  // Used to determine if the previous point was drawn.
    00048
    00049 struct GraphDrawer {
    00050   uint excluded_data;
    00051   byte num_dataset;
    00052   byte num_on_x_axis;
    00053   bool has_negative_values;
    00054   byte num_vert_lines;
    00055
    00056   /* The starting month and year that values are plotted against. If month is
    00057    * 0xFF, use x_values_start and x_values_increment below instead. */
    00058   byte month;
    00059   Year year;
    00060
    00061   /* These values are used if the graph is being plotted against values
    00062    * rather than the dates specified by month and year. */
    00063   uint16 x_values_start;
    00064   uint16 x_values_increment;
    00065
    00066   int left, top;
    00067   uint height;
    00068   StringID format_str_y_axis;
    00069   byte colors[GRAPH_MAX_DATASETS];
    00070   OverflowSafeInt64 cost[GRAPH_MAX_DATASETS][24];
    00071 };
    00072
    00073 static void DrawGraph(const GraphDrawer *gw)
    00074 {
    00075   uint x, y;
    00076   OverflowSafeInt64 highest_value;
    00077   int x_axis_offset;
    00078
    00079   /* the colors and cost array of GraphDrawer must accomodate
    00080    * both values for cargo and players. So if any are higher, quit */
    00081   assert(GRAPH_MAX_DATASETS >= (int)NUM_CARGO && GRAPH_MAX_DATASETS >= (int)MAX_PLAYERS);
    00082   assert(gw->num_vert_lines > 0);
    00083
    00084   byte grid_colour = _colour_gradient[14][4];
    00085
    00086   /* The coordinates of the opposite edges of the graph. */
    00087   int bottom = gw->top + gw->height - 1;
    00088   int right  = gw->left + GRAPH_X_POSITION_BEGINNING + gw->num_vert_lines * GRAPH_X_POSITION_SEPARATION - 1;
    00089
    00090   /* Draw the vertical grid lines. */
    00091
    00092   /* Don't draw the first line, as that's where the axis will be. */
    00093   x = gw->left + GRAPH_X_POSITION_BEGINNING + GRAPH_X_POSITION_SEPARATION;
    00094
    00095   for (int i = 0; i < gw->num_vert_lines; i++) {
    00096     GfxFillRect(x, gw->top, x, bottom, grid_colour);
    00097     x += GRAPH_X_POSITION_SEPARATION;
    00098   }
    00099
    00100   /* Draw the horizontal grid lines. */
    00101   x = gw->left + GRAPH_X_POSITION_BEGINNING;
    00102   y = gw->height + gw->top;
    00103
    00104   for (int i = 0; i < GRAPH_NUM_LINES_Y; i++) {
    00105     GfxFillRect(x, y, right, y, grid_colour);
    00106     y -= (gw->height / (GRAPH_NUM_LINES_Y - 1));
    00107   }
    00108
    00109   /* Draw the y axis. */
    00110   GfxFillRect(x, gw->top, x, bottom, GRAPH_AXIS_LINE_COLOUR);
    00111
    00112   /* Find the distance from the top of the graph to the x axis. */
    00113   x_axis_offset = gw->height;
    00114
    00115   /* The graph is currently symmetrical about the x axis. */
    00116   if (gw->has_negative_values) x_axis_offset /= 2;
    00117
    00118   /* Draw the x axis. */
    00119   y = x_axis_offset + gw->top;
    00120   GfxFillRect(x, y, right, y, GRAPH_AXIS_LINE_COLOUR);
    00121
    00122   /* Find the largest value that will be drawn. */
    00123   if (gw->num_on_x_axis == 0)
    00124     return;
    00125
    00126   assert(gw->num_on_x_axis > 0);
    00127   assert(gw->num_dataset > 0);
    00128
    00129   /* Start of with a value of twice the height of the graph in pixels. It's a
    00130    * bit arbitrary, but it makes the cargo payment graph look a little nicer,
    00131    * and prevents division by zero when calculating where the datapoint
    00132    * should be drawn. */
    00133   highest_value = x_axis_offset * 2;
    00134
    00135   for (int i = 0; i < gw->num_dataset; i++) {
    00136     if (!HasBit(gw->excluded_data, i)) {
    00137       for (int j = 0; j < gw->num_on_x_axis; j++) {
    00138         OverflowSafeInt64 datapoint = gw->cost[i][j];
    00139
    00140         if (datapoint != INVALID_DATAPOINT) {
    00141           /* For now, if the graph has negative values the scaling is
    00142            * symmetrical about the x axis, so take the absolute value
    00143            * of each data point. */
    00144           highest_value = max(highest_value, abs(datapoint));
    00145         }
    00146       }
    00147     }
    00148   }
    00149
    00150   /* Round up highest_value so that it will divide cleanly into the number of
    00151    * axis labels used. */
    00152   int round_val = highest_value % (GRAPH_NUM_LINES_Y - 1);
    00153   if (round_val != 0) highest_value += (GRAPH_NUM_LINES_Y - 1 - round_val);
    00154
    00155   /* draw text strings on the y axis */
    00156   int64 y_label = highest_value;
    00157   int64 y_label_separation = highest_value / (GRAPH_NUM_LINES_Y - 1);
    00158
    00159   /* If there are negative values, the graph goes from highest_value to
    00160    * -highest_value, not highest_value to 0. */
    00161   if (gw->has_negative_values) y_label_separation *= 2;
    00162
    00163   x = gw->left + GRAPH_X_POSITION_BEGINNING + 1;
    00164   y = gw->top - 3;
    00165
    00166   for (int i = 0; i < GRAPH_NUM_LINES_Y; i++) {
    00167     SetDParam(0, gw->format_str_y_axis);
    00168     SetDParam(1, y_label);
    00169     DrawStringRightAligned(x, y, STR_0170, GRAPH_AXIS_LABEL_COLOUR);
    00170
    00171     y_label -= y_label_separation;
    00172     y += (gw->height / (GRAPH_NUM_LINES_Y - 1));
    00173   }
    00174
    00175   /* draw strings on the x axis */
    00176   if (gw->month != 0xFF) {
    00177     x = gw->left + GRAPH_X_POSITION_BEGINNING;
    00178     y = gw->top + gw->height + 1;
    00179     byte month = gw->month;
    00180     Year year  = gw->year;
    00181     for (int i = 0; i < gw->num_on_x_axis; i++) {
    00182       SetDParam(0, month + STR_0162_JAN);
    00183       SetDParam(1, month + STR_0162_JAN + 2);
    00184       SetDParam(2, year);
    00185       DrawString(x, y, month == 0 ? STR_016F : STR_016E, GRAPH_AXIS_LABEL_COLOUR);
    00186
    00187       month += 3;
    00188       if (month >= 12) {
    00189         month = 0;
    00190         year++;
    00191       }
    00192       x += GRAPH_X_POSITION_SEPARATION;
    00193     }
    00194   } else {
    00195     /* Draw the label under the data point rather than on the grid line. */
    00196     x = gw->left + GRAPH_X_POSITION_BEGINNING + (GRAPH_X_POSITION_SEPARATION / 2) + 1;
    00197     y = gw->top + gw->height + 1;
    00198     uint16 label = gw->x_values_start;
    00199
    00200     for (int i = 0; i < gw->num_on_x_axis; i++) {
    00201       SetDParam(0, label);
    00202       DrawStringCentered(x, y, STR_01CB, GRAPH_AXIS_LABEL_COLOUR);
    00203
    00204       label += gw->x_values_increment;
    00205       x += GRAPH_X_POSITION_SEPARATION;
    00206     }
    00207   }
    00208
    00209   /* draw lines and dots */
    00210   for (int i = 0; i < gw->num_dataset; i++) {
    00211     if (!HasBit(gw->excluded_data, i)) {
    00212       /* Centre the dot between the grid lines. */
    00213       x = gw->left + GRAPH_X_POSITION_BEGINNING + (GRAPH_X_POSITION_SEPARATION / 2);
    00214
    00215       byte color  = gw->colors[i];
    00216       uint prev_x = INVALID_DATAPOINT_POS;
    00217       uint prev_y = INVALID_DATAPOINT_POS;
    00218
    00219       for (int j = 0; j < gw->num_on_x_axis; j++) {
    00220         OverflowSafeInt64 datapoint = gw->cost[i][j];
    00221
    00222         if (datapoint != INVALID_DATAPOINT) {
    00223           /*
    00224            * Check whether we need to reduce the 'accuracy' of the
    00225            * datapoint value and the highest value to splut overflows.
    00226            * And when 'drawing' 'one million' or 'one million and one'
    00227            * there is no significant difference, so the least
    00228            * significant bits can just be removed.
    00229            *
    00230            * If there are more bits needed than would fit in a 32 bits
    00231            * integer, so at about 31 bits because of the sign bit, the
    00232            * least significant bits are removed.
    00233            */
    00234           int mult_range = FindLastBit(x_axis_offset) + FindLastBit(abs(datapoint));
    00235           int reduce_range = max(mult_range - 31, 0);
    00236
    00237           /* Handle negative values differently (don't shift sign) */
    00238           if (datapoint < 0) {
    00239             datapoint = -(abs(datapoint) >> reduce_range);
    00240           } else {
    00241             datapoint >>= reduce_range;
    00242           }
    00243
    00244           y = gw->top + x_axis_offset - (x_axis_offset * datapoint) / (highest_value >> reduce_range);
    00245
    00246           /* Draw the point. */
    00247           GfxFillRect(x - 1, y - 1, x + 1, y + 1, color);
    00248
    00249           /* Draw the line connected to the previous point. */
    00250           if (prev_x != INVALID_DATAPOINT_POS) GfxDrawLine(prev_x, prev_y, x, y, color);
    00251
    00252           prev_x = x;
    00253           prev_y = y;
    00254         } else {
    00255           prev_x = INVALID_DATAPOINT_POS;
    00256           prev_y = INVALID_DATAPOINT_POS;
    00257         }
    00258
    00259         x += GRAPH_X_POSITION_SEPARATION;
    00260       }
    00261     }
    00262   }
    00263 }
    00264
    00265 /****************/
    00266 /* GRAPH LEGEND */
    00267 /****************/
    00268
    00269 static void GraphLegendWndProc(Window *w, WindowEvent *e)
    00270 {
    00271   switch (e->event) {
    00272     case WE_CREATE:
    00273       for (uint i = 3; i < w->widget_count; i++) {
    00274         if (!HasBit(_legend_excluded_players, i - 3)) w->LowerWidget(i);
    00275       }
    00276       break;
    00277
    00278     case WE_PAINT: {
    00279       const Player *p;
    00280
    00281       FOR_ALL_PLAYERS(p) {
    00282         if (p->is_active) continue;
    00283
    00284         SetBit(_legend_excluded_players, p->index);
    00285         w->RaiseWidget(p->index + 3);
    00286       }
    00287
    00288       DrawWindowWidgets(w);
    00289
    00290       FOR_ALL_PLAYERS(p) {
    00291         if (!p->is_active) continue;
    00292
    00293         DrawPlayerIcon(p->index, 4, 18 + p->index * 12);
    00294
    00295         SetDParam(0, p->index);
    00296         SetDParam(1, p->index);
    00297         DrawString(21, 17 + p->index * 12, STR_7021, HasBit(_legend_excluded_players, p->index) ? TC_BLACK : TC_WHITE);
    00298       }
    00299       break;
    00300     }
    00301
    00302     case WE_CLICK:
    00303       if (!IsInsideMM(e->we.click.widget, 3, 11)) return;
    00304
    00305       ToggleBit(_legend_excluded_players, e->we.click.widget - 3);
    00306       w->ToggleWidgetLoweredState(e->we.click.widget);
    00307       SetWindowDirty(w);
    00308       InvalidateWindow(WC_INCOME_GRAPH, 0);
    00309       InvalidateWindow(WC_OPERATING_PROFIT, 0);
    00310       InvalidateWindow(WC_DELIVERED_CARGO, 0);
    00311       InvalidateWindow(WC_PERFORMANCE_HISTORY, 0);
    00312       InvalidateWindow(WC_COMPANY_VALUE, 0);
    00313       break;
    00314   }
    00315 }
    00316
    00317 static const Widget _graph_legend_widgets[] = {
    00318 {   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,                       STR_018B_CLOSE_WINDOW},
    00319 {    WWT_CAPTION,   RESIZE_NONE,    14,    11,   249,     0,    13, STR_704E_KEY_TO_COMPANY_GRAPHS, STR_018C_WINDOW_TITLE_DRAG_THIS},
    00320 {      WWT_PANEL,   RESIZE_NONE,    14,     0,   249,    14,   113, 0x0,                            STR_NULL},
    00321 {      WWT_PANEL,   RESIZE_NONE,    14,     2,   247,    16,    27, 0x0,                            STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY},
    00322 {      WWT_PANEL,   RESIZE_NONE,    14,     2,   247,    28,    39, 0x0,                            STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY},
    00323 {      WWT_PANEL,   RESIZE_NONE,    14,     2,   247,    40,    51, 0x0,                            STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY},
    00324 {      WWT_PANEL,   RESIZE_NONE,    14,     2,   247,    52,    63, 0x0,                            STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY},
    00325 {      WWT_PANEL,   RESIZE_NONE,    14,     2,   247,    64,    75, 0x0,                            STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY},
    00326 {      WWT_PANEL,   RESIZE_NONE,    14,     2,   247,    76,    87, 0x0,                            STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY},
    00327 {      WWT_PANEL,   RESIZE_NONE,    14,     2,   247,    88,    99, 0x0,                            STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY},
    00328 {      WWT_PANEL,   RESIZE_NONE,    14,     2,   247,   100,   111, 0x0,                            STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY},
    00329 {   WIDGETS_END},
    00330 };
    00331
    00332 static const WindowDesc _graph_legend_desc = {
    00333   WDP_AUTO, WDP_AUTO, 250, 114, 250, 114,
    00334   WC_GRAPH_LEGEND, WC_NONE,
    00335   WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
    00336   _graph_legend_widgets,
    00337   GraphLegendWndProc
    00338 };
    00339
    00340 static void ShowGraphLegend()
    00341 {
    00342   AllocateWindowDescFront(&_graph_legend_desc, 0);
    00343 }
    00344
    00345 /********************/
    00346 /* OPERATING PROFIT */
    00347 /********************/
    00348
    00349 static void SetupGraphDrawerForPlayers(GraphDrawer *gd)
    00350 {
    00351   const Player* p;
    00352   uint excluded_players = _legend_excluded_players;
    00353   byte nums;
    00354   int mo, yr;
    00355
    00356   /* Exclude the players which aren't valid */
    00357   FOR_ALL_PLAYERS(p) {
    00358     if (!p->is_active) SetBit(excluded_players, p->index);
    00359   }
    00360   gd->excluded_data = excluded_players;
    00361   gd->num_vert_lines = 24;
    00362
    00363   nums = 0;
    00364   FOR_ALL_PLAYERS(p) {
    00365     if (p->is_active) nums = max(nums, p->num_valid_stat_ent);
    00366   }
    00367   gd->num_on_x_axis = min(nums, 24);
    00368
    00369   mo = (_cur_month / 3 - nums) * 3;
    00370   yr = _cur_year;
    00371   while (mo < 0) {
    00372     yr--;
    00373     mo += 12;
    00374   }
    00375
    00376   gd->year = yr;
    00377   gd->month = mo;
    00378 }
    00379
    00380 static void OperatingProfitWndProc(Window *w, WindowEvent *e)
    00381 {
    00382   switch (e->event) {
    00383     case WE_PAINT: {
    00384       GraphDrawer gd;
    00385       const Player* p;
    00386
    00387       DrawWindowWidgets(w);
    00388
    00389       gd.left = 2;
    00390       gd.top = 18;
    00391       gd.height = 136;
    00392       gd.has_negative_values = true;
    00393       gd.format_str_y_axis = STR_CURRCOMPACT;
    00394
    00395       SetupGraphDrawerForPlayers(&gd);
    00396
    00397       int numd = 0;
    00398       FOR_ALL_PLAYERS(p) {
    00399         if (p->is_active) {
    00400           gd.colors[numd] = _colour_gradient[p->player_color][6];
    00401           for (int j = gd.num_on_x_axis, i = 0; --j >= 0;) {
    00402             gd.cost[numd][i] = (j >= p->num_valid_stat_ent) ? INVALID_DATAPOINT : (p->old_economy[j].income + p->old_economy[j].expenses);
    00403             i++;
    00404           }
    00405         }
    00406         numd++;
    00407       }
    00408
    00409       gd.num_dataset = numd;
    00410
    00411       DrawGraph(&gd);
    00412       break;
    00413     }
    00414
    00415     case WE_CLICK:
    00416       /* Clicked on legend? */
    00417       if (e->we.click.widget == 2) ShowGraphLegend();
    00418       break;
    00419   }
    00420 }
    00421
    00422 static const Widget _operating_profit_widgets[] = {
    00423 {   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,                        STR_018B_CLOSE_WINDOW},
    00424 {    WWT_CAPTION,   RESIZE_NONE,    14,    11,   525,     0,    13, STR_7025_OPERATING_PROFIT_GRAPH, STR_018C_WINDOW_TITLE_DRAG_THIS},
    00425 { WWT_PUSHTXTBTN,   RESIZE_NONE,    14,   526,   575,     0,    13, STR_704C_KEY,                    STR_704D_SHOW_KEY_TO_GRAPHS},
    00426 {      WWT_PANEL,   RESIZE_NONE,    14,     0,   575,    14,   173, 0x0,                             STR_NULL},
    00427 {   WIDGETS_END},
    00428 };
    00429
    00430 static const WindowDesc _operating_profit_desc = {
    00431   WDP_AUTO, WDP_AUTO, 576, 174, 576, 174,
    00432   WC_OPERATING_PROFIT, WC_NONE,
    00433   WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
    00434   _operating_profit_widgets,
    00435   OperatingProfitWndProc
    00436 };
    00437
    00438
    00439 void ShowOperatingProfitGraph()
    00440 {
    00441   if (AllocateWindowDescFront(&_operating_profit_desc, 0)) {
    00442     InvalidateWindow(WC_GRAPH_LEGEND, 0);
    00443   }
    00444 }
    00445
    00446
    00447 /****************/
    00448 /* INCOME GRAPH */
    00449 /****************/
    00450
    00451 static void IncomeGraphWndProc(Window *w, WindowEvent *e)
    00452 {
    00453   switch (e->event) {
    00454     case WE_PAINT: {
    00455       GraphDrawer gd;
    00456       const Player* p;
    00457
    00458       DrawWindowWidgets(w);
    00459
    00460       gd.left = 2;
    00461       gd.top = 18;
    00462       gd.height = 104;
    00463       gd.has_negative_values = false;
    00464       gd.format_str_y_axis = STR_CURRCOMPACT;
    00465       SetupGraphDrawerForPlayers(&gd);
    00466
    00467       int numd = 0;
    00468       FOR_ALL_PLAYERS(p) {
    00469         if (p->is_active) {
    00470           gd.colors[numd] = _colour_gradient[p->player_color][6];
    00471           for (int j = gd.num_on_x_axis, i = 0; --j >= 0;) {
    00472             gd.cost[numd][i] = (j >= p->num_valid_stat_ent) ? INVALID_DATAPOINT : p->old_economy[j].income;
    00473             i++;
    00474           }
    00475         }
    00476         numd++;
    00477       }
    00478
    00479       gd.num_dataset = numd;
    00480
    00481       DrawGraph(&gd);
    00482       break;
    00483     }
    00484
    00485     case WE_CLICK:
    00486       if (e->we.click.widget == 2) ShowGraphLegend();
    00487       break;
    00488   }
    00489 }
    00490
    00491 static const Widget _income_graph_widgets[] = {
    00492 {   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,              STR_018B_CLOSE_WINDOW},
    00493 {    WWT_CAPTION,   RESIZE_NONE,    14,    11,   525,     0,    13, STR_7022_INCOME_GRAPH, STR_018C_WINDOW_TITLE_DRAG_THIS},
    00494 { WWT_PUSHTXTBTN,   RESIZE_NONE,    14,   526,   575,     0,    13, STR_704C_KEY,          STR_704D_SHOW_KEY_TO_GRAPHS},
    00495 {      WWT_PANEL,   RESIZE_NONE,    14,     0,   575,    14,   141, 0x0,                   STR_NULL},
    00496 {   WIDGETS_END},
    00497 };
    00498
    00499 static const WindowDesc _income_graph_desc = {
    00500   WDP_AUTO, WDP_AUTO, 576, 142, 576, 142,
    00501   WC_INCOME_GRAPH, WC_NONE,
    00502   WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
    00503   _income_graph_widgets,
    00504   IncomeGraphWndProc
    00505 };
    00506
    00507 void ShowIncomeGraph()
    00508 {
    00509   if (AllocateWindowDescFront(&_income_graph_desc, 0)) {
    00510     InvalidateWindow(WC_GRAPH_LEGEND, 0);
    00511   }
    00512 }
    00513
    00514 /*******************/
    00515 /* DELIVERED CARGO */
    00516 /*******************/
    00517
    00518 static void DeliveredCargoGraphWndProc(Window *w, WindowEvent *e)
    00519 {
    00520   switch (e->event) {
    00521     case WE_PAINT: {
    00522       GraphDrawer gd;
    00523       const Player* p;
    00524
    00525       DrawWindowWidgets(w);
    00526
    00527       gd.left = 2;
    00528       gd.top = 18;
    00529       gd.height = 104;
    00530       gd.has_negative_values = false;
    00531       gd.format_str_y_axis = STR_7024;
    00532       SetupGraphDrawerForPlayers(&gd);
    00533
    00534       int numd = 0;
    00535       FOR_ALL_PLAYERS(p) {
    00536         if (p->is_active) {
    00537           gd.colors[numd] = _colour_gradient[p->player_color][6];
    00538           for (int j = gd.num_on_x_axis, i = 0; --j >= 0;) {
    00539             gd.cost[numd][i] = (j >= p->num_valid_stat_ent) ? INVALID_DATAPOINT : (OverflowSafeInt64)p->old_economy[j].delivered_cargo;
    00540             i++;
    00541           }
    00542         }
    00543         numd++;
    00544       }
    00545
    00546       gd.num_dataset = numd;
    00547
    00548       DrawGraph(&gd);
    00549       break;
    00550     }
    00551
    00552     case WE_CLICK:
    00553       if (e->we.click.widget == 2) ShowGraphLegend();
    00554       break;
    00555   }
    00556 }
    00557
    00558 static const Widget _delivered_cargo_graph_widgets[] = {
    00559 {   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,                          STR_018B_CLOSE_WINDOW},
    00560 {    WWT_CAPTION,   RESIZE_NONE,    14,    11,   525,     0,    13, STR_7050_UNITS_OF_CARGO_DELIVERED, STR_018C_WINDOW_TITLE_DRAG_THIS},
    00561 { WWT_PUSHTXTBTN,   RESIZE_NONE,    14,   526,   575,     0,    13, STR_704C_KEY,                      STR_704D_SHOW_KEY_TO_GRAPHS},
    00562 {      WWT_PANEL,   RESIZE_NONE,    14,     0,   575,    14,   141, 0x0,                               STR_NULL},
    00563 {   WIDGETS_END},
    00564 };
    00565
    00566 static const WindowDesc _delivered_cargo_graph_desc = {
    00567   WDP_AUTO, WDP_AUTO, 576, 142, 576, 142,
    00568   WC_DELIVERED_CARGO, WC_NONE,
    00569   WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
    00570   _delivered_cargo_graph_widgets,
    00571   DeliveredCargoGraphWndProc
    00572 };
    00573
    00574 void ShowDeliveredCargoGraph()
    00575 {
    00576   if (AllocateWindowDescFront(&_delivered_cargo_graph_desc, 0)) {
    00577     InvalidateWindow(WC_GRAPH_LEGEND, 0);
    00578   }
    00579 }
    00580
    00581 /***********************/
    00582 /* PERFORMANCE HISTORY */
    00583 /***********************/
    00584
    00585 static void PerformanceHistoryWndProc(Window *w, WindowEvent *e)
    00586 {
    00587   switch (e->event) {
    00588     case WE_PAINT: {
    00589       GraphDrawer gd;
    00590       const Player* p;
    00591
    00592       DrawWindowWidgets(w);
    00593
    00594       gd.left = 2;
    00595       gd.top = 18;
    00596       gd.height = 200;
    00597       gd.has_negative_values = false;
    00598       gd.format_str_y_axis = STR_7024;
    00599       SetupGraphDrawerForPlayers(&gd);
    00600
    00601       int numd = 0;
    00602       FOR_ALL_PLAYERS(p) {
    00603         if (p->is_active) {
    00604           gd.colors[numd] = _colour_gradient[p->player_color][6];
    00605           for (int j = gd.num_on_x_axis, i = 0; --j >= 0;) {
    00606             gd.cost[numd][i] = (j >= p->num_valid_stat_ent) ? INVALID_DATAPOINT : (OverflowSafeInt64)p->old_economy[j].performance_history;
    00607             i++;
    00608           }
    00609         }
    00610         numd++;
    00611       }
    00612
    00613       gd.num_dataset = numd;
    00614
    00615       DrawGraph(&gd);
    00616       break;
    00617     }
    00618
    00619     case WE_CLICK:
    00620       if (e->we.click.widget == 2) ShowGraphLegend();
    00621       if (e->we.click.widget == 3) ShowPerformanceRatingDetail();
    00622       break;
    00623   }
    00624 }
    00625
    00626 static const Widget _performance_history_widgets[] = {
    00627 {   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,                             STR_018B_CLOSE_WINDOW},
    00628 {    WWT_CAPTION,   RESIZE_NONE,    14,    11,   475,     0,    13, STR_7051_COMPANY_PERFORMANCE_RATINGS, STR_018C_WINDOW_TITLE_DRAG_THIS},
    00629 { WWT_PUSHTXTBTN,   RESIZE_NONE,    14,   526,   575,     0,    13, STR_704C_KEY,                         STR_704D_SHOW_KEY_TO_GRAPHS},
    00630 { WWT_PUSHTXTBTN,   RESIZE_NONE,    14,   476,   525,     0,    13, STR_PERFORMANCE_DETAIL_KEY,           STR_704D_SHOW_KEY_TO_GRAPHS},
    00631 {      WWT_PANEL,   RESIZE_NONE,    14,     0,   575,    14,   237, 0x0,                                  STR_NULL},
    00632 {   WIDGETS_END},
    00633 };
    00634
    00635 static const WindowDesc _performance_history_desc = {
    00636   WDP_AUTO, WDP_AUTO, 576, 238, 576, 238,
    00637   WC_PERFORMANCE_HISTORY, WC_NONE,
    00638   WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
    00639   _performance_history_widgets,
    00640   PerformanceHistoryWndProc
    00641 };
    00642
    00643 void ShowPerformanceHistoryGraph()
    00644 {
    00645   if (AllocateWindowDescFront(&_performance_history_desc, 0)) {
    00646     InvalidateWindow(WC_GRAPH_LEGEND, 0);
    00647   }
    00648 }
    00649
    00650 /*****************/
    00651 /* COMPANY VALUE */
    00652 /*****************/
    00653
    00654 static void CompanyValueGraphWndProc(Window *w, WindowEvent *e)
    00655 {
    00656   switch (e->event) {
    00657     case WE_PAINT: {
    00658       GraphDrawer gd;
    00659       const Player* p;
    00660
    00661       DrawWindowWidgets(w);
    00662
    00663       gd.left = 2;
    00664       gd.top = 18;
    00665       gd.height = 200;
    00666       gd.has_negative_values = false;
    00667       gd.format_str_y_axis = STR_CURRCOMPACT;
    00668       SetupGraphDrawerForPlayers(&gd);
    00669
    00670       int numd = 0;
    00671       FOR_ALL_PLAYERS(p) {
    00672         if (p->is_active) {
    00673           gd.colors[numd] = _colour_gradient[p->player_color][6];
    00674           for (int j = gd.num_on_x_axis, i = 0; --j >= 0;) {
    00675             gd.cost[numd][i] = (j >= p->num_valid_stat_ent) ? INVALID_DATAPOINT : p->old_economy[j].company_value;
    00676             i++;
    00677           }
    00678         }
    00679         numd++;
    00680       }
    00681
    00682       gd.num_dataset = numd;
    00683
    00684       DrawGraph(&gd);
    00685       break;
    00686     }
    00687
    00688     case WE_CLICK:
    00689       if (e->we.click.widget == 2) ShowGraphLegend();
    00690       break;
    00691   }
    00692 }
    00693
    00694 static const Widget _company_value_graph_widgets[] = {
    00695 {   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,                STR_018B_CLOSE_WINDOW},
    00696 {    WWT_CAPTION,   RESIZE_NONE,    14,    11,   525,     0,    13, STR_7052_COMPANY_VALUES, STR_018C_WINDOW_TITLE_DRAG_THIS},
    00697 { WWT_PUSHTXTBTN,   RESIZE_NONE,    14,   526,   575,     0,    13, STR_704C_KEY,            STR_704D_SHOW_KEY_TO_GRAPHS},
    00698 {      WWT_PANEL,   RESIZE_NONE,    14,     0,   575,    14,   237, 0x0,                     STR_NULL},
    00699 {   WIDGETS_END},
    00700 };
    00701
    00702 static const WindowDesc _company_value_graph_desc = {
    00703   WDP_AUTO, WDP_AUTO, 576, 238, 576, 238,
    00704   WC_COMPANY_VALUE, WC_NONE,
    00705   WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
    00706   _company_value_graph_widgets,
    00707   CompanyValueGraphWndProc
    00708 };
    00709
    00710 void ShowCompanyValueGraph()
    00711 {
    00712   if (AllocateWindowDescFront(&_company_value_graph_desc, 0)) {
    00713     InvalidateWindow(WC_GRAPH_LEGEND, 0);
    00714   }
    00715 }
    00716
    00717 /*****************/
    00718 /* PAYMENT RATES */
    00719 /*****************/
    00720
    00721 static void CargoPaymentRatesWndProc(Window *w, WindowEvent *e)
    00722 {
    00723   switch (e->event) {
    00724     case WE_PAINT: {
    00725       GraphDrawer gd;
    00726
    00727       DrawWindowWidgets(w);
    00728
    00729       int x = 495;
    00730       int y = 24;
    00731
    00732       gd.excluded_data = _legend_excluded_cargo;
    00733       gd.left = 2;
    00734       gd.top = 24;
    00735       gd.height = w->height - 38;
    00736       gd.has_negative_values = false;
    00737       gd.format_str_y_axis = STR_CURRCOMPACT;
    00738       gd.num_on_x_axis = 20;
    00739       gd.num_vert_lines = 20;
    00740       gd.month = 0xFF;
    00741       gd.x_values_start     = 10;
    00742       gd.x_values_increment = 10;
    00743
    00744       uint i = 0;
    00745       for (CargoID c = 0; c < NUM_CARGO; c++) {
    00746         const CargoSpec *cs = GetCargo(c);
    00747         if (!cs->IsValid()) continue;
    00748
    00749         /* Only draw labels for widgets that exist. If the widget doesn't
    00750          * exist then the local player has used the climate cheat or
    00751          * changed the NewGRF configuration with this window open. */
    00752         if (i + 3 < w->widget_count) {
    00753           /* Since the buttons have no text, no images,
    00754            * both the text and the colored box have to be manually painted.
    00755            * clk_dif will move one pixel down and one pixel to the right
    00756            * when the button is clicked */
    00757           byte clk_dif = w->IsWidgetLowered(i + 3) ? 1 : 0;
    00758
    00759           GfxFillRect(x + clk_dif, y + clk_dif, x + 8 + clk_dif, y + 5 + clk_dif, 0);
    00760           GfxFillRect(x + 1 + clk_dif, y + 1 + clk_dif, x + 7 + clk_dif, y + 4 + clk_dif, cs->legend_colour);
    00761           SetDParam(0, cs->name);
    00762           DrawString(x + 14 + clk_dif, y + clk_dif, STR_7065, TC_FROMSTRING);
    00763           y += 8;
    00764         }
    00765
    00766         gd.colors[i] = cs->legend_colour;
    00767         for (uint j = 0; j != 20; j++) {
    00768           gd.cost[i][j] = GetTransportedGoodsIncome(10, 20, j * 4 + 4, c);
    00769         }
    00770
    00771         i++;
    00772       }
    00773       gd.num_dataset = i;
    00774
    00775       DrawGraph(&gd);
    00776
    00777       DrawString(2 + 46, 24 + gd.height + 7, STR_7062_DAYS_IN_TRANSIT, TC_FROMSTRING);
    00778       DrawString(2 + 84, 24 - 9, STR_7063_PAYMENT_FOR_DELIVERING, TC_FROMSTRING);
    00779       break;
    00780     }
    00781
    00782     case WE_CLICK:
    00783       if (e->we.click.widget >= 3) {
    00784         ToggleBit(_legend_excluded_cargo, e->we.click.widget - 3);
    00785         w->ToggleWidgetLoweredState(e->we.click.widget);
    00786         SetWindowDirty(w);
    00787       }
    00788       break;
    00789   }
    00790 }
    00791
    00792 static const Widget _cargo_payment_rates_widgets[] = {
    00793 {   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,                     STR_018B_CLOSE_WINDOW},
    00794 {    WWT_CAPTION,   RESIZE_NONE,    14,    11,   567,     0,    13, STR_7061_CARGO_PAYMENT_RATES, STR_018C_WINDOW_TITLE_DRAG_THIS},
    00795 {      WWT_PANEL, RESIZE_BOTTOM,    14,     0,   567,    14,    45, 0x0,                          STR_NULL},
    00796 {   WIDGETS_END},
    00797 };
    00798
    00799 static const WindowDesc _cargo_payment_rates_desc = {
    00800   WDP_AUTO, WDP_AUTO, 568, 46, 568, 46,
    00801   WC_PAYMENT_RATES, WC_NONE,
    00802   WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
    00803   _cargo_payment_rates_widgets,
    00804   CargoPaymentRatesWndProc
    00805 };
    00806
    00807
    00808 void ShowCargoPaymentRates()
    00809 {
    00810   Window *w = AllocateWindowDescFront(&_cargo_payment_rates_desc, 0);
    00811   if (w == NULL) return;
    00812
    00813   /* Count the number of active cargo types */
    00814   uint num_active = 0;
    00815   for (CargoID c = 0; c < NUM_CARGO; c++) {
    00816     if (GetCargo(c)->IsValid()) num_active++;
    00817   }
    00818
    00819   /* Resize the window to fit the cargo types */
    00820   ResizeWindow(w, 0, max(num_active, 12U) * 8);
    00821
    00822   /* Add widgets for each cargo type */
    00823   w->widget_count += num_active;
    00824   w->widget = ReallocT(w->widget, w->widget_count + 1);
    00825   w->widget[w->widget_count].type = WWT_LAST;
    00826
    00827   /* Set the properties of each widget */
    00828   for (uint i = 0; i != num_active; i++) {
    00829     Widget *wi = &w->widget[3 + i];
    00830     wi->type     = WWT_PANEL;
    00831     wi->display_flags = RESIZE_NONE;
    00832     wi->color    = 12;
    00833     wi->left     = 493;
    00834     wi->right    = 562;
    00835     wi->top      = 24 + i * 8;
    00836     wi->bottom   = wi->top + 7;
    00837     wi->data     = 0;
    00838     wi->tooltips = STR_7064_TOGGLE_GRAPH_FOR_CARGO;
    00839
    00840     if (!HasBit(_legend_excluded_cargo, i)) w->LowerWidget(i + 3);
    00841   }
    00842
    00843   SetWindowDirty(w);
    00844 }
    00845
    00846 /************************/
    00847 /* COMPANY LEAGUE TABLE */
    00848 /************************/
    00849
    00850 static const StringID _performance_titles[] = {
    00851   STR_7066_ENGINEER,
    00852   STR_7066_ENGINEER,
    00853   STR_7067_TRAFFIC_MANAGER,
    00854   STR_7067_TRAFFIC_MANAGER,
    00855   STR_7068_TRANSPORT_COORDINATOR,
    00856   STR_7068_TRANSPORT_COORDINATOR,
    00857   STR_7069_ROUTE_SUPERVISOR,
    00858   STR_7069_ROUTE_SUPERVISOR,
    00859   STR_706A_DIRECTOR,
    00860   STR_706A_DIRECTOR,
    00861   STR_706B_CHIEF_EXECUTIVE,
    00862   STR_706B_CHIEF_EXECUTIVE,
    00863   STR_706C_CHAIRMAN,
    00864   STR_706C_CHAIRMAN,
    00865   STR_706D_PRESIDENT,
    00866   STR_706E_TYCOON,
    00867 };
    00868
    00869 static inline StringID GetPerformanceTitleFromValue(uint value)
    00870 {
    00871   return _performance_titles[minu(value, 1000) >> 6];
    00872 }
    00873
    00874 static int CDECL PerfHistComp(const void* elem1, const void* elem2)
    00875 {
    00876   const Player* p1 = *(const Player* const*)elem1;
    00877   const Player* p2 = *(const Player* const*)elem2;
    00878
    00879   return p2->old_economy[1].performance_history - p1->old_economy[1].performance_history;
    00880 }
    00881
    00882 static void CompanyLeagueWndProc(Window *w, WindowEvent *e)
    00883 {
    00884   switch (e->event) {
    00885     case WE_PAINT: {
    00886       const Player* plist[MAX_PLAYERS];
    00887       const Player* p;
    00888
    00889       DrawWindowWidgets(w);
    00890
    00891       uint pl_num = 0;
    00892       FOR_ALL_PLAYERS(p) if (p->is_active) plist[pl_num++] = p;
    00893
    00894       qsort((void*)plist, pl_num, sizeof(*plist), PerfHistComp);
    00895
    00896       for (uint i = 0; i != pl_num; i++) {
    00897         p = plist[i];
    00898         SetDParam(0, i + STR_01AC_1ST);
    00899         SetDParam(1, p->index);
    00900         SetDParam(2, p->index);
    00901         SetDParam(3, GetPerformanceTitleFromValue(p->old_economy[1].performance_history));
    00902
    00903         DrawString(2, 15 + i * 10, i == 0 ? STR_7054 : STR_7055, TC_FROMSTRING);
    00904         DrawPlayerIcon(p->index, 27, 16 + i * 10);
    00905       }
    00906
    00907       break;
    00908     }
    00909   }
    00910 }
    00911
    00912
    00913 static const Widget _company_league_widgets[] = {
    00914 {   WWT_CLOSEBOX, RESIZE_NONE, 14,   0,  10,  0, 13, STR_00C5,                      STR_018B_CLOSE_WINDOW},
    00915 {    WWT_CAPTION, RESIZE_NONE, 14,  11, 387,  0, 13, STR_7053_COMPANY_LEAGUE_TABLE, STR_018C_WINDOW_TITLE_DRAG_THIS},
    00916 {  WWT_STICKYBOX, RESIZE_NONE, 14, 388, 399,  0, 13, STR_NULL,                      STR_STICKY_BUTTON},
    00917 {      WWT_PANEL, RESIZE_NONE, 14,   0, 399, 14, 96, 0x0,                           STR_NULL},
    00918 {   WIDGETS_END},
    00919 };
    00920
    00921 static const WindowDesc _company_league_desc = {
    00922   WDP_AUTO, WDP_AUTO, 400, 97, 400, 97,
    00923   WC_COMPANY_LEAGUE, WC_NONE,
    00924   WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_STICKY_BUTTON,
    00925   _company_league_widgets,
    00926   CompanyLeagueWndProc
    00927 };
    00928
    00929 void ShowCompanyLeagueTable()
    00930 {
    00931   AllocateWindowDescFront(&_company_league_desc, 0);
    00932 }
    00933
    00934 /*****************************/
    00935 /* PERFORMANCE RATING DETAIL */
    00936 /*****************************/
    00937
    00938 static void PerformanceRatingDetailWndProc(Window *w, WindowEvent *e)
    00939 {
    00940   static PlayerID _performance_rating_detail_player = INVALID_PLAYER;
    00941
    00942   switch (e->event) {
    00943     case WE_PAINT: {
    00944       byte x;
    00945       uint16 y = 14;
    00946       int total_score = 0;
    00947       int color_done, color_notdone;
    00948
    00949       /* Draw standard stuff */
    00950       DrawWindowWidgets(w);
    00951
    00952       /* Check if the currently selected player is still active. */
    00953       if (_performance_rating_detail_player == INVALID_PLAYER || !GetPlayer(_performance_rating_detail_player)->is_active) {
    00954         if (_performance_rating_detail_player != INVALID_PLAYER) {
    00955           /* Raise and disable the widget for the previous selection. */
    00956           w->RaiseWidget(_performance_rating_detail_player + 13);
    00957           w->DisableWidget(_performance_rating_detail_player + 13);
    00958           SetWindowDirty(w);
    00959
    00960           _performance_rating_detail_player = INVALID_PLAYER;
    00961         }
    00962
    00963         for (PlayerID i = PLAYER_FIRST; i < MAX_PLAYERS; i++) {
    00964           if (GetPlayer(i)->is_active) {
    00965             /* Lower the widget corresponding to this player. */
    00966             w->LowerWidget(i + 13);
    00967             SetWindowDirty(w);
    00968
    00969             _performance_rating_detail_player = i;
    00970             break;
    00971           }
    00972         }
    00973       }
    00974
    00975       /* If there are no active players, don't display anything else. */
    00976       if (_performance_rating_detail_player == INVALID_PLAYER) break;
    00977
    00978       /* Paint the player icons */
    00979       for (PlayerID i = PLAYER_FIRST; i < MAX_PLAYERS; i++) {
    00980         if (!GetPlayer(i)->is_active) {
    00981           /* Check if we have the player as an active player */
    00982           if (!w->IsWidgetDisabled(i + 13)) {
    00983             /* Bah, player gone :( */
    00984             w->DisableWidget(i + 13);
    00985
    00986             /* We need a repaint */
    00987             SetWindowDirty(w);
    00988           }
    00989           continue;
    00990         }
    00991
    00992         /* Check if we have the player marked as inactive */
    00993         if (w->IsWidgetDisabled(i + 13)) {
    00994           /* New player! Yippie :p */
    00995           w->EnableWidget(i + 13);
    00996           /* We need a repaint */
    00997           SetWindowDirty(w);
    00998         }
    00999
    01000         x = (i == _performance_rating_detail_player) ? 1 : 0;
    01001         DrawPlayerIcon(i, i * 37 + 13 + x, 16 + x);
    01002       }
    01003
    01004       /* The colors used to show how the progress is going */
    01005       color_done = _colour_gradient[COLOUR_GREEN][4];
    01006       color_notdone = _colour_gradient[COLOUR_RED][4];
    01007
    01008       /* Draw all the score parts */
    01009       for (ScoreID i = SCORE_BEGIN; i < SCORE_END; i++) {
    01010         int val    = _score_part[_performance_rating_detail_player][i];
    01011         int needed = _score_info[i].needed;
    01012         int score  = _score_info[i].score;
    01013
    01014         y += 20;
    01015         /* SCORE_TOTAL has his own rulez ;) */
    01016         if (i == SCORE_TOTAL) {
    01017           needed = total_score;
    01018           score = SCORE_MAX;
    01019         } else {
    01020           total_score += score;
    01021         }
    01022
    01023         DrawString(7, y, STR_PERFORMANCE_DETAIL_VEHICLES + i, TC_FROMSTRING);
    01024
    01025         /* Draw the score */
    01026         SetDParam(0, score);
    01027         DrawStringRightAligned(107, y, SET_PERFORMANCE_DETAIL_INT, TC_FROMSTRING);
    01028
    01029         /* Calculate the %-bar */
    01030         x = Clamp(val, 0, needed) * 50 / needed;
    01031
    01032         /* SCORE_LOAN is inversed */
    01033         if (val < 0 && i == SCORE_LOAN) x = 0;
    01034
    01035         /* Draw the bar */
    01036         if (x !=  0) GfxFillRect(112,     y - 2, 112 + x,  y + 10, color_done);
    01037         if (x != 50) GfxFillRect(112 + x, y - 2, 112 + 50, y + 10, color_notdone);
    01038
    01039         /* Calculate the % */
    01040         x = Clamp(val, 0, needed) * 100 / needed;
    01041
    01042         /* SCORE_LOAN is inversed */
    01043         if (val < 0 && i == SCORE_LOAN) x = 0;
    01044
    01045         /* Draw it */
    01046         SetDParam(0, x);
    01047         DrawStringCentered(137, y, STR_PERFORMANCE_DETAIL_PERCENT, TC_FROMSTRING);
    01048
    01049         /* SCORE_LOAN is inversed */
    01050         if (i == SCORE_LOAN) val = needed - val;
    01051
    01052         /* Draw the amount we have against what is needed
    01053          * For some of them it is in currency format */
    01054         SetDParam(0, val);
    01055         SetDParam(1, needed);
    01056         switch (i) {
    01057           case SCORE_MIN_PROFIT:
    01058           case SCORE_MIN_INCOME:
    01059           case SCORE_MAX_INCOME:
    01060           case SCORE_MONEY:
    01061           case SCORE_LOAN:
    01062             DrawString(167, y, STR_PERFORMANCE_DETAIL_AMOUNT_CURRENCY, TC_FROMSTRING);
    01063             break;
    01064           default:
    01065             DrawString(167, y, STR_PERFORMANCE_DETAIL_AMOUNT_INT, TC_FROMSTRING);
    01066         }
    01067       }
    01068
    01069       break;
    01070     }
    01071
    01072     case WE_CLICK:
    01073       /* Check which button is clicked */
    01074       if (IsInsideMM(e->we.click.widget, 13, 21)) {
    01075         /* Is it no on disable? */
    01076         if (!w->IsWidgetDisabled(e->we.click.widget)) {
    01077           w->RaiseWidget(_performance_rating_detail_player + 13);
    01078           _performance_rating_detail_player = (PlayerID)(e->we.click.widget - 13);
    01079           w->LowerWidget(_performance_rating_detail_player + 13);
    01080           SetWindowDirty(w);
    01081         }
    01082       }
    01083       break;
    01084
    01085     case WE_CREATE: {
    01086       Player *p2;
    01087
    01088       /* Disable the players who are not active */
    01089       for (PlayerID i = PLAYER_FIRST; i < MAX_PLAYERS; i++) {
    01090         w->SetWidgetDisabledState(i + 13, !GetPlayer(i)->is_active);
    01091       }
    01092       /* Update all player stats with the current data
    01093        * (this is because _score_info is not saved to a savegame) */
    01094       FOR_ALL_PLAYERS(p2) {
    01095         if (p2->is_active) UpdateCompanyRatingAndValue(p2, false);
    01096       }
    01097
    01098       w->custom[0] = DAY_TICKS;
    01099       w->custom[1] = 5;
    01100
    01101       if (_performance_rating_detail_player != INVALID_PLAYER) w->LowerWidget(_performance_rating_detail_player + 13);
    01102       SetWindowDirty(w);
    01103
    01104       break;
    01105     }
    01106
    01107     case WE_TICK:
    01108       if (_pause_game != 0) break;
    01109
    01110       /* Update the player score every 5 days */
    01111       if (--w->custom[0] == 0) {
    01112         w->custom[0] = DAY_TICKS;
    01113         if (--w->custom[1] == 0) {
    01114           Player *p2;
    01115
    01116           w->custom[1] = 5;
    01117           FOR_ALL_PLAYERS(p2) {
    01118             /* Skip if player is not active */
    01119             if (p2->is_active) UpdateCompanyRatingAndValue(p2, false);
    01120           }
    01121           SetWindowDirty(w);
    01122         }
    01123       }
    01124
    01125       break;
    01126   }
    01127 }
    01128
    01129 static const Widget _performance_rating_detail_widgets[] = {
    01130 {   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,               STR_018B_CLOSE_WINDOW},
    01131 {    WWT_CAPTION,   RESIZE_NONE,    14,    11,   298,     0,    13, STR_PERFORMANCE_DETAIL, STR_018C_WINDOW_TITLE_DRAG_THIS},
    01132 {      WWT_PANEL,   RESIZE_NONE,    14,     0,   298,    14,    27, 0x0,                    STR_NULL},
    01133
    01134 {      WWT_PANEL,   RESIZE_NONE,    14,     0,   298,    28,    47, 0x0,                    STR_PERFORMANCE_DETAIL_VEHICLES_TIP},
    01135 {      WWT_PANEL,   RESIZE_NONE,    14,     0,   298,    48,    67, 0x0,                    STR_PERFORMANCE_DETAIL_STATIONS_TIP},
    01136 {      WWT_PANEL,   RESIZE_NONE,    14,     0,   298,    68,    87, 0x0,                    STR_PERFORMANCE_DETAIL_MIN_PROFIT_TIP},
    01137 {      WWT_PANEL,   RESIZE_NONE,    14,     0,   298,    88,   107, 0x0,                    STR_PERFORMANCE_DETAIL_MIN_INCOME_TIP},
    01138 {      WWT_PANEL,   RESIZE_NONE,    14,     0,   298,   108,   127, 0x0,                    STR_PERFORMANCE_DETAIL_MAX_INCOME_TIP},
    01139 {      WWT_PANEL,   RESIZE_NONE,    14,     0,   298,   128,   147, 0x0,                    STR_PERFORMANCE_DETAIL_DELIVERED_TIP},
    01140 {      WWT_PANEL,   RESIZE_NONE,    14,     0,   298,   148,   167, 0x0,                    STR_PERFORMANCE_DETAIL_CARGO_TIP},
    01141 {      WWT_PANEL,   RESIZE_NONE,    14,     0,   298,   168,   187, 0x0,                    STR_PERFORMANCE_DETAIL_MONEY_TIP},
    01142 {      WWT_PANEL,   RESIZE_NONE,    14,     0,   298,   188,   207, 0x0,                    STR_PERFORMANCE_DETAIL_LOAN_TIP},
    01143 {      WWT_PANEL,   RESIZE_NONE,    14,     0,   298,   208,   227, 0x0,                    STR_PERFORMANCE_DETAIL_TOTAL_TIP},
    01144
    01145 {      WWT_PANEL,   RESIZE_NONE,    14,     2,    38,    14,    26, 0x0,                    STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY},
    01146 {      WWT_PANEL,   RESIZE_NONE,    14,    39,    75,    14,    26, 0x0,                    STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY},
    01147 {      WWT_PANEL,   RESIZE_NONE,    14,    76,   112,    14,    26, 0x0,                    STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY},
    01148 {      WWT_PANEL,   RESIZE_NONE,    14,   113,   149,    14,    26, 0x0,                    STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY},
    01149 {      WWT_PANEL,   RESIZE_NONE,    14,   150,   186,    14,    26, 0x0,                    STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY},
    01150 {      WWT_PANEL,   RESIZE_NONE,    14,   187,   223,    14,    26, 0x0,                    STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY},
    01151 {      WWT_PANEL,   RESIZE_NONE,    14,   224,   260,    14,    26, 0x0,                    STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY},
    01152 {      WWT_PANEL,   RESIZE_NONE,    14,   261,   297,    14,    26, 0x0,                    STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY},
    01153 {   WIDGETS_END},
    01154 };
    01155
    01156 static const WindowDesc _performance_rating_detail_desc = {
    01157   WDP_AUTO, WDP_AUTO, 299, 228, 299, 228,
    01158   WC_PERFORMANCE_DETAIL, WC_NONE,
    01159   WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
    01160   _performance_rating_detail_widgets,
    01161   PerformanceRatingDetailWndProc
    01162 };
    01163
    01164 void ShowPerformanceRatingDetail()
    01165 {
    01166   AllocateWindowDescFront(&_performance_rating_detail_desc, 0);
    01167 }