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 }

Generated on Wed Oct 1 17:03:20 2008 for openttd by  doxygen 1.5.6