# vehicle_gui.cpp

Go to the documentation of this file.

    00001 /* $Id: vehicle_gui.cpp 14268 2008-09-07 22:04:39Z rubidium $ */
    00002
    00005 #include "stdafx.h"
    00006 #include "openttd.h"
    00007 #include "debug.h"
    00008 #include "player_func.h"
    00009 #include "station.h"
    00010 #include "engine.h"
    00011 #include "gui.h"
    00012 #include "window_gui.h"
    00013 #include "textbuf_gui.h"
    00014 #include "command_func.h"
    00015 #include "variables.h"
    00016 #include "vehicle_gui.h"
    00017 #include "viewport_func.h"
    00018 #include "gfx_func.h"
    00019 #include "train.h"
    00020 #include "newgrf_callbacks.h"
    00021 #include "newgrf_engine.h"
    00022 #include "newgrf_text.h"
    00023 #include "ship.h"
    00024 #include "aircraft.h"
    00025 #include "roadveh.h"
    00026 #include "depot.h"
    00027 #include "cargotype.h"
    00028 #include "group.h"
    00029 #include "group_gui.h"
    00030 #include "strings_func.h"
    00031 #include "functions.h"
    00032 #include "window_func.h"
    00033 #include "vehicle_func.h"
    00034 #include "autoreplace_gui.h"
    00035 #include "core/alloc_func.hpp"
    00036 #include "string_func.h"
    00037 #include "settings_type.h"
    00038 #include "widgets/dropdown_func.h"
    00039
    00040 #include "table/sprites.h"
    00041 #include "table/strings.h"
    00042
    00043 struct Sorting {
    00044   Listing aircraft;
    00045   Listing roadveh;
    00046   Listing ship;
    00047   Listing train;
    00048 };
    00049
    00050 static Sorting _sorting;
    00051
    00052 static bool   _internal_sort_order;     // descending/ascending
    00053
    00054 typedef int CDECL VehicleSortListingTypeFunction(const void*, const void*);
    00055
    00056 static VehicleSortListingTypeFunction VehicleNumberSorter;
    00057 static VehicleSortListingTypeFunction VehicleNameSorter;
    00058 static VehicleSortListingTypeFunction VehicleAgeSorter;
    00059 static VehicleSortListingTypeFunction VehicleProfitThisYearSorter;
    00060 static VehicleSortListingTypeFunction VehicleProfitLastYearSorter;
    00061 static VehicleSortListingTypeFunction VehicleCargoSorter;
    00062 static VehicleSortListingTypeFunction VehicleReliabilitySorter;
    00063 static VehicleSortListingTypeFunction VehicleMaxSpeedSorter;
    00064 static VehicleSortListingTypeFunction VehicleModelSorter;
    00065 static VehicleSortListingTypeFunction VehicleValueSorter;
    00066
    00067 static VehicleSortListingTypeFunction* const _vehicle_sorter[] = {
    00068   &VehicleNumberSorter,
    00069   &VehicleNameSorter,
    00070   &VehicleAgeSorter,
    00071   &VehicleProfitThisYearSorter,
    00072   &VehicleProfitLastYearSorter,
    00073   &VehicleCargoSorter,
    00074   &VehicleReliabilitySorter,
    00075   &VehicleMaxSpeedSorter,
    00076   &VehicleModelSorter,
    00077   &VehicleValueSorter,
    00078 };
    00079
    00080 const StringID _vehicle_sort_listing[] = {
    00081   STR_SORT_BY_NUMBER,
    00082   STR_SORT_BY_DROPDOWN_NAME,
    00083   STR_SORT_BY_AGE,
    00084   STR_SORT_BY_PROFIT_THIS_YEAR,
    00085   STR_SORT_BY_PROFIT_LAST_YEAR,
    00086   STR_SORT_BY_TOTAL_CAPACITY_PER_CARGOTYPE,
    00087   STR_SORT_BY_RELIABILITY,
    00088   STR_SORT_BY_MAX_SPEED,
    00089   STR_SORT_BY_MODEL,
    00090   STR_SORT_BY_VALUE,
    00091   INVALID_STRING_ID
    00092 };
    00093
    00094 void RebuildVehicleLists()
    00095 {
    00096   Window* const *wz;
    00097
    00098   FOR_ALL_WINDOWS(wz) {
    00099     Window *w = *wz;
    00100
    00101     switch (w->window_class) {
    00102       case WC_TRAINS_LIST:
    00103       case WC_ROADVEH_LIST:
    00104       case WC_SHIPS_LIST:
    00105       case WC_AIRCRAFT_LIST:
    00106         WP(w, vehiclelist_d).l.flags |= VL_REBUILD;
    00107         SetWindowDirty(w);
    00108         break;
    00109
    00110       default: break;
    00111     }
    00112   }
    00113 }
    00114
    00115 void ResortVehicleLists()
    00116 {
    00117   Window* const *wz;
    00118
    00119   FOR_ALL_WINDOWS(wz) {
    00120     Window *w = *wz;
    00121
    00122     switch (w->window_class) {
    00123       case WC_TRAINS_LIST:
    00124       case WC_ROADVEH_LIST:
    00125       case WC_SHIPS_LIST:
    00126       case WC_AIRCRAFT_LIST:
    00127         WP(w, vehiclelist_d).l.flags |= VL_RESORT;
    00128         SetWindowDirty(w);
    00129         break;
    00130
    00131       default: break;
    00132     }
    00133   }
    00134 }
    00135
    00136 void BuildVehicleList(vehiclelist_d *vl, PlayerID owner, uint16 index, uint16 window_type)
    00137 {
    00138   if (!(vl->l.flags & VL_REBUILD)) return;
    00139
    00140   DEBUG(misc, 3, "Building vehicle list for player %d at station %d", owner, index);
    00141
    00142   vl->l.list_length = GenerateVehicleSortList(&vl->sort_list, &vl->length_of_sort_list, vl->vehicle_type, owner, index, window_type);
    00143
    00144   vl->l.flags &= ~VL_REBUILD;
    00145   vl->l.flags |= VL_RESORT;
    00146 }
    00147
    00148 /* cached values for VehicleNameSorter to spare many GetString() calls */
    00149 static const Vehicle *_last_vehicle[2] = { NULL, NULL };
    00150 static char           _last_name[2][64] = { "", "" };
    00151
    00152 void SortVehicleList(vehiclelist_d *vl)
    00153 {
    00154   if (!(vl->l.flags & VL_RESORT)) return;
    00155
    00156   /* invalidate cached values for name sorter - vehicle names could change */
    00157   _last_vehicle[0] = _last_vehicle[1] = NULL;
    00158
    00159   _internal_sort_order = (vl->l.flags & VL_DESC) != 0;
    00160   qsort((void*)vl->sort_list, vl->l.list_length, sizeof(vl->sort_list[0]),
    00161     _vehicle_sorter[vl->l.sort_type]);
    00162
    00163   vl->l.resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS;
    00164   vl->l.flags &= ~VL_RESORT;
    00165 }
    00166
    00167 void DepotSortList(Vehicle **v, uint16 length)
    00168 {
    00169   _internal_sort_order = 0;
    00170   qsort((void*)v, length, sizeof(v[0]), _vehicle_sorter[0]);
    00171 }
    00172
    00174 void DrawVehicleProfitButton(const Vehicle *v, int x, int y)
    00175 {
    00176   SpriteID pal;
    00177
    00178   /* draw profit-based colored icons */
    00179   if (v->age <= 365 * 2) {
    00180     pal = PALETTE_TO_GREY;
    00181   } else if (v->GetDisplayProfitLastYear() < 0) {
    00182     pal = PALETTE_TO_RED;
    00183   } else if (v->GetDisplayProfitLastYear() < 10000) {
    00184     pal = PALETTE_TO_YELLOW;
    00185   } else {
    00186     pal = PALETTE_TO_GREEN;
    00187   }
    00188   DrawSprite(SPR_BLOT, pal, x, y);
    00189 }
    00190
    00191 struct RefitOption {
    00192   CargoID cargo;
    00193   byte subtype;
    00194   uint16 value;
    00195   EngineID engine;
    00196 };
    00197
    00198 struct RefitList {
    00199   uint num_lines;
    00200   RefitOption *items;
    00201 };
    00202
    00203 static RefitList *BuildRefitList(const Vehicle *v)
    00204 {
    00205   uint max_lines = 256;
    00206   RefitOption *refit = CallocT<RefitOption>(max_lines);
    00207   RefitList *list = CallocT<RefitList>(1);
    00208   Vehicle *u = (Vehicle*)v;
    00209   uint num_lines = 0;
    00210   uint i;
    00211
    00212   do {
    00213     uint32 cmask = EngInfo(u->engine_type)->refit_mask;
    00214     byte callbackmask = EngInfo(u->engine_type)->callbackmask;
    00215
    00216     /* Skip this engine if it has no capacity */
    00217     if (u->cargo_cap == 0) continue;
    00218
    00219     /* Loop through all cargos in the refit mask */
    00220     for (CargoID cid = 0; cid < NUM_CARGO && num_lines < max_lines; cid++) {
    00221       /* Skip cargo type if it's not listed */
    00222       if (!HasBit(cmask, cid)) continue;
    00223
    00224       /* Check the vehicle's callback mask for cargo suffixes */
    00225       if (HasBit(callbackmask, CBM_VEHICLE_CARGO_SUFFIX)) {
    00226         /* Make a note of the original cargo type. It has to be
    00227          * changed to test the cargo & subtype... */
    00228         CargoID temp_cargo = u->cargo_type;
    00229         byte temp_subtype  = u->cargo_subtype;
    00230         byte refit_cyc;
    00231
    00232         u->cargo_type = cid;
    00233
    00234         for (refit_cyc = 0; refit_cyc < 16 && num_lines < max_lines; refit_cyc++) {
    00235           bool duplicate = false;
    00236           uint16 callback;
    00237
    00238           u->cargo_subtype = refit_cyc;
    00239           callback = GetVehicleCallback(CBID_VEHICLE_CARGO_SUFFIX, 0, 0, u->engine_type, u);
    00240
    00241           if (callback == 0xFF) callback = CALLBACK_FAILED;
    00242           if (refit_cyc != 0 && callback == CALLBACK_FAILED) break;
    00243
    00244           /* Check if this cargo and subtype combination are listed */
    00245           for (i = 0; i < num_lines && !duplicate; i++) {
    00246             if (refit[i].cargo == cid && refit[i].value == callback) duplicate = true;
    00247           }
    00248
    00249           if (duplicate) continue;
    00250
    00251           refit[num_lines].cargo   = cid;
    00252           refit[num_lines].subtype = refit_cyc;
    00253           refit[num_lines].value   = callback;
    00254           refit[num_lines].engine  = u->engine_type;
    00255           num_lines++;
    00256         }
    00257
    00258         /* Reset the vehicle's cargo type */
    00259         u->cargo_type    = temp_cargo;
    00260         u->cargo_subtype = temp_subtype;
    00261       } else {
    00262         /* No cargo suffix callback -- use no subtype */
    00263         bool duplicate = false;
    00264
    00265         for (i = 0; i < num_lines && !duplicate; i++) {
    00266           if (refit[i].cargo == cid && refit[i].value == CALLBACK_FAILED) duplicate = true;
    00267         }
    00268
    00269         if (!duplicate) {
    00270           refit[num_lines].cargo   = cid;
    00271           refit[num_lines].subtype = 0;
    00272           refit[num_lines].value   = CALLBACK_FAILED;
    00273           refit[num_lines].engine  = INVALID_ENGINE;
    00274           num_lines++;
    00275         }
    00276       }
    00277     }
    00278   } while ((v->type == VEH_TRAIN || v->type == VEH_ROAD) && (u = u->Next()) != NULL && num_lines < max_lines);
    00279
    00280   list->num_lines = num_lines;
    00281   list->items = refit;
    00282
    00283   return list;
    00284 }
    00285
    00295 static RefitOption *DrawVehicleRefitWindow(const RefitList *list, int sel, uint pos, uint rows, uint delta)
    00296 {
    00297   RefitOption *refit = list->items;
    00298   RefitOption *selected = NULL;
    00299   uint num_lines = list->num_lines;
    00300   uint y = 31;
    00301   uint i;
    00302
    00303   /* Draw the list, and find the selected cargo (by its position in list) */
    00304   for (i = 0; i < num_lines; i++) {
    00305     TextColour colour = TC_BLACK;
    00306     if (sel == 0) {
    00307       selected = &refit[i];
    00308       colour = TC_WHITE;
    00309     }
    00310
    00311     if (i >= pos && i < pos + rows) {
    00312       /* Draw the cargo name */
    00313       int last_x = DrawString(2, y, GetCargo(refit[i].cargo)->name, colour);
    00314
    00315       /* If the callback succeeded, draw the cargo suffix */
    00316       if (refit[i].value != CALLBACK_FAILED) {
    00317         DrawString(last_x + 1, y, GetGRFStringID(GetEngineGRFID(refit[i].engine), 0xD000 + refit[i].value), colour);
    00318       }
    00319       y += delta;
    00320     }
    00321
    00322     sel--;
    00323   }
    00324
    00325   return selected;
    00326 }
    00327
    00328 static void VehicleRefitWndProc(Window *w, WindowEvent *e)
    00329 {
    00330   switch (e->event) {
    00331     case WE_PAINT: {
    00332       Vehicle *v = GetVehicle(w->window_number);
    00333
    00334       if (v->type == VEH_TRAIN) {
    00335         uint length = CountVehiclesInChain(v);
    00336
    00337         if (length != WP(w, refit_d).length) {
    00338           /* Consist length has changed, so rebuild the refit list */
    00339           free(WP(w, refit_d).list->items);
    00340           free(WP(w, refit_d).list);
    00341           WP(w, refit_d).list = BuildRefitList(v);
    00342           WP(w, refit_d).length = length;
    00343         }
    00344       }
    00345
    00346       SetVScrollCount(w, WP(w, refit_d).list->num_lines);
    00347
    00348       SetDParam(0, v->index);
    00349       DrawWindowWidgets(w);
    00350
    00351       WP(w, refit_d).cargo = DrawVehicleRefitWindow(WP(w, refit_d).list, WP(w, refit_d).sel, w->vscroll.pos, w->vscroll.cap, w->resize.step_height);
    00352
    00353       if (WP(w, refit_d).cargo != NULL) {
    00354         CommandCost cost;
    00355
    00356         cost = DoCommand(v->tile, v->index, WP(w, refit_d).cargo->cargo | WP(w, refit_d).cargo->subtype << 8,
    00357                  DC_QUERY_COST, GetCmdRefitVeh(GetVehicle(w->window_number)->type));
    00358
    00359         if (CmdSucceeded(cost)) {
    00360           SetDParam(0, WP(w, refit_d).cargo->cargo);
    00361           SetDParam(1, _returned_refit_capacity);
    00362           SetDParam(2, cost.GetCost());
    00363           DrawString(2, w->widget[5].top + 1, STR_9840_NEW_CAPACITY_COST_OF_REFIT, TC_FROMSTRING);
    00364         }
    00365       }
    00366     } break;
    00367
    00368     case WE_CLICK:
    00369       switch (e->we.click.widget) {
    00370         case 3: { // listbox
    00371           int y = e->we.click.pt.y - w->widget[3].top;
    00372           if (y >= 0) {
    00373             WP(w, refit_d).sel = (y / (int)w->resize.step_height) + w->vscroll.pos;
    00374             SetWindowDirty(w);
    00375           }
    00376         } break;
    00377         case 6: // refit button
    00378           if (WP(w, refit_d).cargo != NULL) {
    00379             const Vehicle *v = GetVehicle(w->window_number);
    00380
    00381             if (WP(w, refit_d).order == INVALID_VEH_ORDER_ID) {
    00382               int command = 0;
    00383
    00384               switch (v->type) {
    00385                 default: NOT_REACHED();
    00386                 case VEH_TRAIN:    command = CMD_REFIT_RAIL_VEHICLE | CMD_MSG(STR_RAIL_CAN_T_REFIT_VEHICLE);  break;
    00387                 case VEH_ROAD:     command = CMD_REFIT_ROAD_VEH     | CMD_MSG(STR_REFIT_ROAD_VEHICLE_CAN_T);  break;
    00388                 case VEH_SHIP:     command = CMD_REFIT_SHIP         | CMD_MSG(STR_9841_CAN_T_REFIT_SHIP);     break;
    00389                 case VEH_AIRCRAFT: command = CMD_REFIT_AIRCRAFT     | CMD_MSG(STR_A042_CAN_T_REFIT_AIRCRAFT); break;
    00390               }
    00391               if (DoCommandP(v->tile, v->index, WP(w, refit_d).cargo->cargo | WP(w, refit_d).cargo->subtype << 8, NULL, command)) DeleteWindow(w);
    00392             } else {
    00393               if (DoCommandP(v->tile, v->index, WP(w, refit_d).cargo->cargo | WP(w, refit_d).cargo->subtype << 8 | WP(w, refit_d).order << 16, NULL, CMD_ORDER_REFIT)) DeleteWindow(w);
    00394             }
    00395           }
    00396           break;
    00397       }
    00398       break;
    00399
    00400     case WE_RESIZE:
    00401       w->vscroll.cap += e->we.sizing.diff.y / (int)w->resize.step_height;
    00402       w->widget[3].data = (w->vscroll.cap << 8) + 1;
    00403       break;
    00404
    00405     case WE_DESTROY:
    00406       free(WP(w, refit_d).list->items);
    00407       free(WP(w, refit_d).list);
    00408       break;
    00409   }
    00410 }
    00411
    00412
    00413 static const Widget _vehicle_refit_widgets[] = {
    00414   {   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,                            STR_018B_CLOSE_WINDOW},
    00415   {    WWT_CAPTION,   RESIZE_NONE,    14,    11,   239,     0,    13, STR_983B_REFIT,                      STR_018C_WINDOW_TITLE_DRAG_THIS},
    00416   {    WWT_TEXTBTN,   RESIZE_NONE,    14,     0,   239,    14,    27, STR_983F_SELECT_CARGO_TYPE_TO_CARRY, STR_983D_SELECT_TYPE_OF_CARGO_FOR},
    00417   {     WWT_MATRIX, RESIZE_BOTTOM,    14,     0,   227,    28,   139, 0x801,                               STR_EMPTY},
    00418   {  WWT_SCROLLBAR, RESIZE_BOTTOM,    14,   228,   239,    28,   139, 0x0,                                 STR_0190_SCROLL_BAR_SCROLLS_LIST},
    00419   {      WWT_PANEL,     RESIZE_TB,    14,     0,   239,   140,   161, 0x0,                                 STR_NULL},
    00420   { WWT_PUSHTXTBTN,     RESIZE_TB,    14,     0,   227,   162,   173, 0x0,                                 STR_NULL},
    00421   {  WWT_RESIZEBOX,     RESIZE_TB,    14,   228,   239,   162,   173, 0x0,                                 STR_RESIZE_BUTTON},
    00422   {   WIDGETS_END},
    00423 };
    00424
    00425 static const WindowDesc _vehicle_refit_desc = {
    00426   WDP_AUTO, WDP_AUTO, 240, 174, 240, 174,
    00427   WC_VEHICLE_REFIT, WC_VEHICLE_VIEW,
    00428   WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_RESIZABLE,
    00429   _vehicle_refit_widgets,
    00430   VehicleRefitWndProc,
    00431 };
    00432
    00437 void ShowVehicleRefitWindow(const Vehicle *v, VehicleOrderID order)
    00438 {
    00439   Window *w;
    00440
    00441   DeleteWindowById(WC_VEHICLE_REFIT, v->index);
    00442
    00443   w = AllocateWindowDescFront(&_vehicle_refit_desc, v->index);
    00444   WP(w, refit_d).order = order;
    00445
    00446   if (w != NULL) {
    00447     w->caption_color = v->owner;
    00448     w->vscroll.cap = 8;
    00449     w->resize.step_height = 14;
    00450     WP(w, refit_d).sel  = -1;
    00451     WP(w, refit_d).list = BuildRefitList(v);
    00452     if (v->type == VEH_TRAIN) WP(w, refit_d).length = CountVehiclesInChain(v);
    00453     SetVScrollCount(w, WP(w, refit_d).list->num_lines);
    00454
    00455     switch (v->type) {
    00456       case VEH_TRAIN:
    00457         w->widget[3].tooltips = STR_RAIL_SELECT_TYPE_OF_CARGO_FOR;
    00458         w->widget[6].data     = STR_RAIL_REFIT_VEHICLE;
    00459         w->widget[6].tooltips = STR_RAIL_REFIT_TO_CARRY_HIGHLIGHTED;
    00460         break;
    00461       case VEH_ROAD:
    00462         w->widget[3].tooltips = STR_ROAD_SELECT_TYPE_OF_CARGO_FOR;
    00463         w->widget[6].data     = STR_REFIT_ROAD_VEHICLE;
    00464         w->widget[6].tooltips = STR_REFIT_ROAD_VEHICLE_TO_CARRY_HIGHLIGHTED;
    00465         break;
    00466       case VEH_SHIP:
    00467         w->widget[3].tooltips = STR_983D_SELECT_TYPE_OF_CARGO_FOR;
    00468         w->widget[6].data     = STR_983C_REFIT_SHIP;
    00469         w->widget[6].tooltips = STR_983E_REFIT_SHIP_TO_CARRY_HIGHLIGHTED;
    00470         break;
    00471       case VEH_AIRCRAFT:
    00472         w->widget[3].tooltips = STR_A03E_SELECT_TYPE_OF_CARGO_FOR;
    00473         w->widget[6].data     = STR_A03D_REFIT_AIRCRAFT;
    00474         w->widget[6].tooltips = STR_A03F_REFIT_AIRCRAFT_TO_CARRY;
    00475         break;
    00476       default: NOT_REACHED();
    00477     }
    00478   }
    00479 }
    00480
    00482 uint ShowAdditionalText(int x, int y, uint w, EngineID engine)
    00483 {
    00484   uint16 callback = GetVehicleCallback(CBID_VEHICLE_ADDITIONAL_TEXT, 0, 0, engine, NULL);
    00485   if (callback == CALLBACK_FAILED) return 0;
    00486
    00487   /* STR_02BD is used to start the string with {BLACK} */
    00488   SetDParam(0, GetGRFStringID(GetEngineGRFID(engine), 0xD000 + callback));
    00489   PrepareTextRefStackUsage(0);
    00490   uint result = DrawStringMultiLine(x, y, STR_02BD, w);
    00491   StopTextRefStackUsage();
    00492   return result;
    00493 }
    00494
    00496 uint ShowRefitOptionsList(int x, int y, uint w, EngineID engine)
    00497 {
    00498   /* List of cargo types of this engine */
    00499   uint32 cmask = EngInfo(engine)->refit_mask;
    00500   /* List of cargo types available in this climate */
    00501   uint32 lmask = _cargo_mask;
    00502   char *b = _userstring;
    00503
    00504   /* Draw nothing if the engine is not refittable */
    00505   if (CountBits(cmask) <= 1) return 0;
    00506
    00507   b = InlineString(b, STR_PURCHASE_INFO_REFITTABLE_TO);
    00508
    00509   if (cmask == lmask) {
    00510     /* Engine can be refitted to all types in this climate */
    00511     b = InlineString(b, STR_PURCHASE_INFO_ALL_TYPES);
    00512   } else {
    00513     /* Check if we are able to refit to more cargo types and unable to. If
    00514      * so, invert the cargo types to list those that we can't refit to. */
    00515     if (CountBits(cmask ^ lmask) < CountBits(cmask)) {
    00516       cmask ^= lmask;
    00517       b = InlineString(b, STR_PURCHASE_INFO_ALL_BUT);
    00518     }
    00519
    00520     bool first = true;
    00521
    00522     /* Add each cargo type to the list */
    00523     for (CargoID cid = 0; cid < NUM_CARGO; cid++) {
    00524       if (!HasBit(cmask, cid)) continue;
    00525
    00526       if (b >= lastof(_userstring) - (2 + 2 * 4)) break; // ", " and two calls to Utf8Encode()
    00527
    00528       if (!first) b = strecpy(b, ", ", lastof(_userstring));
    00529       first = false;
    00530
    00531       b = InlineString(b, GetCargo(cid)->name);
    00532     }
    00533   }
    00534
    00535   /* Terminate and display the completed string */
    00536   *b = '\0';
    00537
    00538   /* Make sure we detect any buffer overflow */
    00539   assert(b < endof(_userstring));
    00540
    00541   return DrawStringMultiLine(x, y, STR_SPEC_USERSTRING, w);
    00542 }
    00543
    00544
    00545 /* if the sorting criteria had the same value, sort vehicle by unitnumber */
    00546 #define VEHICLEUNITNUMBERSORTER(r, a, b) {if (r == 0) {r = a->unitnumber - b->unitnumber;}}
    00547
    00548 static int CDECL VehicleNumberSorter(const void *a, const void *b)
    00549 {
    00550   const Vehicle* va = *(const Vehicle**)a;
    00551   const Vehicle* vb = *(const Vehicle**)b;
    00552   int r = va->unitnumber - vb->unitnumber;
    00553
    00554   return (_internal_sort_order & 1) ? -r : r;
    00555 }
    00556
    00557 static int CDECL VehicleNameSorter(const void *a, const void *b)
    00558 {
    00559   const Vehicle* va = *(const Vehicle**)a;
    00560   const Vehicle* vb = *(const Vehicle**)b;
    00561   int r;
    00562
    00563   if (va != _last_vehicle[0]) {
    00564     _last_vehicle[0] = va;
    00565     SetDParam(0, va->index);
    00566     GetString(_last_name[0], STR_VEHICLE_NAME, lastof(_last_name[0]));
    00567   }
    00568
    00569   if (vb != _last_vehicle[1]) {
    00570     _last_vehicle[1] = vb;
    00571     SetDParam(0, vb->index);
    00572     GetString(_last_name[1], STR_VEHICLE_NAME, lastof(_last_name[1]));
    00573   }
    00574
    00575   r = strcmp(_last_name[0], _last_name[1]); // sort by name
    00576
    00577   VEHICLEUNITNUMBERSORTER(r, va, vb);
    00578
    00579   return (_internal_sort_order & 1) ? -r : r;
    00580 }
    00581
    00582 static int CDECL VehicleAgeSorter(const void *a, const void *b)
    00583 {
    00584   const Vehicle* va = *(const Vehicle**)a;
    00585   const Vehicle* vb = *(const Vehicle**)b;
    00586   int r = va->age - vb->age;
    00587
    00588   VEHICLEUNITNUMBERSORTER(r, va, vb);
    00589
    00590   return (_internal_sort_order & 1) ? -r : r;
    00591 }
    00592
    00593 static int CDECL VehicleProfitThisYearSorter(const void *a, const void *b)
    00594 {
    00595   const Vehicle* va = *(const Vehicle**)a;
    00596   const Vehicle* vb = *(const Vehicle**)b;
    00597   int r = ClampToI32(va->GetDisplayProfitThisYear() - vb->GetDisplayProfitThisYear());
    00598
    00599   VEHICLEUNITNUMBERSORTER(r, va, vb);
    00600
    00601   return (_internal_sort_order & 1) ? -r : r;
    00602 }
    00603
    00604 static int CDECL VehicleProfitLastYearSorter(const void *a, const void *b)
    00605 {
    00606   const Vehicle* va = *(const Vehicle**)a;
    00607   const Vehicle* vb = *(const Vehicle**)b;
    00608   int r = ClampToI32(va->GetDisplayProfitLastYear() - vb->GetDisplayProfitLastYear());
    00609
    00610   VEHICLEUNITNUMBERSORTER(r, va, vb);
    00611
    00612   return (_internal_sort_order & 1) ? -r : r;
    00613 }
    00614
    00615 static int CDECL VehicleCargoSorter(const void *a, const void *b)
    00616 {
    00617   const Vehicle* va = *(const Vehicle**)a;
    00618   const Vehicle* vb = *(const Vehicle**)b;
    00619   const Vehicle* v;
    00620   AcceptedCargo cargoa;
    00621   AcceptedCargo cargob;
    00622   int r = 0;
    00623
    00624   memset(cargoa, 0, sizeof(cargoa));
    00625   memset(cargob, 0, sizeof(cargob));
    00626   for (v = va; v != NULL; v = v->Next()) cargoa[v->cargo_type] += v->cargo_cap;
    00627   for (v = vb; v != NULL; v = v->Next()) cargob[v->cargo_type] += v->cargo_cap;
    00628
    00629   for (CargoID i = 0; i < NUM_CARGO; i++) {
    00630     r = cargoa[i] - cargob[i];
    00631     if (r != 0) break;
    00632   }
    00633
    00634   VEHICLEUNITNUMBERSORTER(r, va, vb);
    00635
    00636   return (_internal_sort_order & 1) ? -r : r;
    00637 }
    00638
    00639 static int CDECL VehicleReliabilitySorter(const void *a, const void *b)
    00640 {
    00641   const Vehicle* va = *(const Vehicle**)a;
    00642   const Vehicle* vb = *(const Vehicle**)b;
    00643   int r = va->reliability - vb->reliability;
    00644
    00645   VEHICLEUNITNUMBERSORTER(r, va, vb);
    00646
    00647   return (_internal_sort_order & 1) ? -r : r;
    00648 }
    00649
    00650 static int CDECL VehicleMaxSpeedSorter(const void *a, const void *b)
    00651 {
    00652   const Vehicle* va = *(const Vehicle**)a;
    00653   const Vehicle* vb = *(const Vehicle**)b;
    00654   int r;
    00655
    00656   if (va->type == VEH_TRAIN && vb->type == VEH_TRAIN) {
    00657     r = va->u.rail.cached_max_speed - vb->u.rail.cached_max_speed;
    00658   } else {
    00659     r = va->max_speed - vb->max_speed;
    00660   }
    00661
    00662   VEHICLEUNITNUMBERSORTER(r, va, vb);
    00663
    00664   return (_internal_sort_order & 1) ? -r : r;
    00665 }
    00666
    00667 static int CDECL VehicleModelSorter(const void *a, const void *b)
    00668 {
    00669   const Vehicle* va = *(const Vehicle**)a;
    00670   const Vehicle* vb = *(const Vehicle**)b;
    00671   int r = va->engine_type - vb->engine_type;
    00672
    00673   VEHICLEUNITNUMBERSORTER(r, va, vb);
    00674
    00675   return (_internal_sort_order & 1) ? -r : r;
    00676 }
    00677
    00678 static int CDECL VehicleValueSorter(const void *a, const void *b)
    00679 {
    00680   const Vehicle* va = *(const Vehicle**)a;
    00681   const Vehicle* vb = *(const Vehicle**)b;
    00682   const Vehicle *u;
    00683   Money valuea = 0, valueb = 0;
    00684
    00685   for (u = va; u != NULL; u = u->Next()) valuea += u->value;
    00686   for (u = vb; u != NULL; u = u->Next()) valueb += u->value;
    00687
    00688   int r = ClampToI32(valuea - valueb);
    00689
    00690   VEHICLEUNITNUMBERSORTER(r, va, vb);
    00691
    00692   return (_internal_sort_order & 1) ? -r : r;
    00693 }
    00694
    00695 void InitializeGUI()
    00696 {
    00697   memset(&_sorting, 0, sizeof(_sorting));
    00698 }
    00699
    00706 void ChangeVehicleViewWindow(const Vehicle *from_v, const Vehicle *to_v)
    00707 {
    00708   Window *w;
    00709
    00710   w = FindWindowById(WC_VEHICLE_VIEW, from_v->index);
    00711   if (w != NULL) {
    00712     w->window_number = to_v->index;
    00713     WP(w, vp_d).follow_vehicle = to_v->index;
    00714     SetWindowDirty(w);
    00715
    00716     w = FindWindowById(WC_VEHICLE_ORDERS, from_v->index);
    00717     if (w != NULL) {
    00718       w->window_number = to_v->index;
    00719       SetWindowDirty(w);
    00720     }
    00721
    00722     w = FindWindowById(WC_VEHICLE_REFIT, from_v->index);
    00723     if (w != NULL) {
    00724       w->window_number = to_v->index;
    00725       SetWindowDirty(w);
    00726     }
    00727
    00728     w = FindWindowById(WC_VEHICLE_DETAILS, from_v->index);
    00729     if (w != NULL) {
    00730       w->window_number = to_v->index;
    00731       SetWindowDirty(w);
    00732     }
    00733
    00734     w = FindWindowById(WC_VEHICLE_TIMETABLE, from_v->index);
    00735     if (w != NULL) {
    00736       w->window_number = to_v->index;
    00737       SetWindowDirty(w);
    00738     }
    00739   }
    00740 }
    00741
    00742 enum VehicleListWindowWidgets {
    00743   VLW_WIDGET_CLOSEBOX = 0,
    00744   VLW_WIDGET_CAPTION,
    00745   VLW_WIDGET_STICKY,
    00746   VLW_WIDGET_SORT_ORDER,
    00747   VLW_WIDGET_SORT_BY_PULLDOWN,
    00748   VLW_WIDGET_EMPTY_TOP_RIGHT,
    00749   VLW_WIDGET_LIST,
    00750   VLW_WIDGET_SCROLLBAR,
    00751   VLW_WIDGET_OTHER_PLAYER_FILLER,
    00752   VLW_WIDGET_AVAILABLE_VEHICLES,
    00753   VLW_WIDGET_MANAGE_VEHICLES_DROPDOWN,
    00754   VLW_WIDGET_STOP_ALL,
    00755   VLW_WIDGET_START_ALL,
    00756   VLW_WIDGET_EMPTY_BOTTOM_RIGHT,
    00757   VLW_WIDGET_RESIZE,
    00758 };
    00759
    00760 static const Widget _vehicle_list_widgets[] = {
    00761   {   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,             STR_018B_CLOSE_WINDOW},
    00762   {    WWT_CAPTION,  RESIZE_RIGHT,    14,    11,   247,     0,    13, 0x0,                  STR_018C_WINDOW_TITLE_DRAG_THIS},
    00763   {  WWT_STICKYBOX,     RESIZE_LR,    14,   248,   259,     0,    13, 0x0,                  STR_STICKY_BUTTON},
    00764   { WWT_PUSHTXTBTN,   RESIZE_NONE,    14,     0,    80,    14,    25, STR_SORT_BY,          STR_SORT_ORDER_TIP},
    00765   {   WWT_DROPDOWN,   RESIZE_NONE,    14,    81,   247,    14,    25, 0x0,                  STR_SORT_CRITERIA_TIP},
    00766   {      WWT_PANEL,  RESIZE_RIGHT,    14,   248,   259,    14,    25, 0x0,                  STR_NULL},
    00767   {     WWT_MATRIX,     RESIZE_RB,    14,     0,   247,    26,   169, 0x0,                  STR_NULL},
    00768   {  WWT_SCROLLBAR,    RESIZE_LRB,    14,   248,   259,    26,   169, 0x0,                  STR_0190_SCROLL_BAR_SCROLLS_LIST},
    00769   /* Widget to be shown for other players hiding the following 6 widgets */
    00770   {      WWT_PANEL,    RESIZE_RTB,    14,     0,   247,   170,   181, 0x0,                  STR_NULL},
    00771
    00772   { WWT_PUSHTXTBTN,     RESIZE_TB,    14,     0,   105,   170,   181, 0x0,                  STR_AVAILABLE_ENGINES_TIP},
    00773   {   WWT_DROPDOWN,     RESIZE_TB,    14,   106,   223,   170,   181, STR_MANAGE_LIST,      STR_MANAGE_LIST_TIP},
    00774
    00775   { WWT_PUSHIMGBTN,     RESIZE_TB,    14,   224,   235,   170,   181, SPR_FLAG_VEH_STOPPED, STR_MASS_STOP_LIST_TIP},
    00776   { WWT_PUSHIMGBTN,     RESIZE_TB,    14,   236,   247,   170,   181, SPR_FLAG_VEH_RUNNING, STR_MASS_START_LIST_TIP},
    00777   {      WWT_PANEL,    RESIZE_RTB,    14,   248,   247,   170,   181, 0x0,                  STR_NULL},
    00778   {  WWT_RESIZEBOX,   RESIZE_LRTB,    14,   248,   259,   170,   181, 0x0,                  STR_RESIZE_BUTTON},
    00779   {   WIDGETS_END},
    00780 };
    00781
    00782 static void CreateVehicleListWindow(Window *w)
    00783 {
    00784   vehiclelist_d *vl = &WP(w, vehiclelist_d);
    00785   uint16 window_type = w->window_number & VLW_MASK;
    00786   PlayerID player = (PlayerID)GB(w->window_number, 0, 8);
    00787
    00788   vl->vehicle_type = (VehicleType)GB(w->window_number, 11, 5);
    00789   vl->length_of_sort_list = 0;
    00790   vl->sort_list = NULL;
    00791   w->caption_color = player;
    00792
    00793   /* Hide the widgets that we will not use in this window
    00794    * Some windows contains actions only fit for the owner */
    00795   if (player == _local_player) {
    00796     w->HideWidget(VLW_WIDGET_OTHER_PLAYER_FILLER);
    00797     w->SetWidgetDisabledState(VLW_WIDGET_AVAILABLE_VEHICLES, window_type != VLW_STANDARD);
    00798   } else {
    00799     w->SetWidgetsHiddenState(true,
    00800       VLW_WIDGET_AVAILABLE_VEHICLES,
    00801       VLW_WIDGET_MANAGE_VEHICLES_DROPDOWN,
    00802       VLW_WIDGET_STOP_ALL,
    00803       VLW_WIDGET_START_ALL,
    00804       VLW_WIDGET_EMPTY_BOTTOM_RIGHT,
    00805       WIDGET_LIST_END);
    00806   }
    00807
    00808   /* Set up the window widgets */
    00809   switch (vl->vehicle_type) {
    00810     case VEH_TRAIN:
    00811       w->widget[VLW_WIDGET_LIST].tooltips          = STR_883D_TRAINS_CLICK_ON_TRAIN_FOR;
    00812       w->widget[VLW_WIDGET_AVAILABLE_VEHICLES].data = STR_AVAILABLE_TRAINS;
    00813       break;
    00814
    00815     case VEH_ROAD:
    00816       w->widget[VLW_WIDGET_LIST].tooltips          = STR_901A_ROAD_VEHICLES_CLICK_ON;
    00817       w->widget[VLW_WIDGET_AVAILABLE_VEHICLES].data = STR_AVAILABLE_ROAD_VEHICLES;
    00818       break;
    00819
    00820     case VEH_SHIP:
    00821       w->widget[VLW_WIDGET_LIST].tooltips          = STR_9823_SHIPS_CLICK_ON_SHIP_FOR;
    00822       w->widget[VLW_WIDGET_AVAILABLE_VEHICLES].data = STR_AVAILABLE_SHIPS;
    00823       break;
    00824
    00825     case VEH_AIRCRAFT:
    00826       w->widget[VLW_WIDGET_LIST].tooltips          = STR_A01F_AIRCRAFT_CLICK_ON_AIRCRAFT;
    00827       w->widget[VLW_WIDGET_AVAILABLE_VEHICLES].data = STR_AVAILABLE_AIRCRAFT;
    00828       break;
    00829
    00830     default: NOT_REACHED();
    00831   }
    00832
    00833   switch (window_type) {
    00834     case VLW_SHARED_ORDERS:
    00835       w->widget[VLW_WIDGET_CAPTION].data  = STR_VEH_WITH_SHARED_ORDERS_LIST;
    00836       break;
    00837     case VLW_STANDARD: /* Company Name - standard widget setup */
    00838       switch (vl->vehicle_type) {
    00839         case VEH_TRAIN:    w->widget[VLW_WIDGET_CAPTION].data = STR_881B_TRAINS;        break;
    00840         case VEH_ROAD:     w->widget[VLW_WIDGET_CAPTION].data = STR_9001_ROAD_VEHICLES; break;
    00841         case VEH_SHIP:     w->widget[VLW_WIDGET_CAPTION].data = STR_9805_SHIPS;         break;
    00842         case VEH_AIRCRAFT: w->widget[VLW_WIDGET_CAPTION].data = STR_A009_AIRCRAFT;      break;
    00843         default: NOT_REACHED(); break;
    00844       }
    00845       break;
    00846     case VLW_STATION_LIST: /* Station Name */
    00847       switch (vl->vehicle_type) {
    00848         case VEH_TRAIN:    w->widget[VLW_WIDGET_CAPTION].data = STR_SCHEDULED_TRAINS;        break;
    00849         case VEH_ROAD:     w->widget[VLW_WIDGET_CAPTION].data = STR_SCHEDULED_ROAD_VEHICLES; break;
    00850         case VEH_SHIP:     w->widget[VLW_WIDGET_CAPTION].data = STR_SCHEDULED_SHIPS;         break;
    00851         case VEH_AIRCRAFT: w->widget[VLW_WIDGET_CAPTION].data = STR_SCHEDULED_AIRCRAFT;      break;
    00852         default: NOT_REACHED(); break;
    00853       }
    00854       break;
    00855
    00856     case VLW_DEPOT_LIST:
    00857       switch (vl->vehicle_type) {
    00858         case VEH_TRAIN:    w->widget[VLW_WIDGET_CAPTION].data = STR_VEHICLE_LIST_TRAIN_DEPOT;    break;
    00859         case VEH_ROAD:     w->widget[VLW_WIDGET_CAPTION].data = STR_VEHICLE_LIST_ROADVEH_DEPOT;  break;
    00860         case VEH_SHIP:     w->widget[VLW_WIDGET_CAPTION].data = STR_VEHICLE_LIST_SHIP_DEPOT;     break;
    00861         case VEH_AIRCRAFT: w->widget[VLW_WIDGET_CAPTION].data = STR_VEHICLE_LIST_AIRCRAFT_DEPOT; break;
    00862         default: NOT_REACHED(); break;
    00863       }
    00864       break;
    00865     default: NOT_REACHED(); break;
    00866   }
    00867
    00868   switch (vl->vehicle_type) {
    00869     case VEH_TRAIN:
    00870       w->resize.step_width = 1;
    00871       /* Fallthrough */
    00872     case VEH_ROAD:
    00873       w->vscroll.cap = 7;
    00874       w->resize.step_height = PLY_WND_PRC__SIZE_OF_ROW_SMALL;
    00875       w->resize.height = 220 - (PLY_WND_PRC__SIZE_OF_ROW_SMALL * 3); // Minimum of 4 vehicles
    00876       break;
    00877     case VEH_SHIP:
    00878     case VEH_AIRCRAFT:
    00879       w->vscroll.cap = 4;
    00880       w->resize.step_height = PLY_WND_PRC__SIZE_OF_ROW_BIG;
    00881       break;
    00882     default: NOT_REACHED();
    00883   }
    00884
    00885   w->widget[VLW_WIDGET_LIST].data = (w->vscroll.cap << 8) + 1;
    00886
    00887   /* Set up sorting. Make the window-specific _sorting variable
    00888     * point to the correct global _sorting struct so we are freed
    00889     * from having conditionals during window operation */
    00890   switch (vl->vehicle_type) {
    00891     case VEH_TRAIN:    vl->_sorting = &_sorting.train; break;
    00892     case VEH_ROAD:     vl->_sorting = &_sorting.roadveh; break;
    00893     case VEH_SHIP:     vl->_sorting = &_sorting.ship; break;
    00894     case VEH_AIRCRAFT: vl->_sorting = &_sorting.aircraft; break;
    00895     default: NOT_REACHED(); break;
    00896   }
    00897
    00898   vl->l.flags = VL_REBUILD | (vl->_sorting->order ? VL_DESC : VL_NONE);
    00899   vl->l.sort_type = vl->_sorting->criteria;
    00900   vl->sort_list = NULL;
    00901   vl->l.resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS; // Set up resort timer
    00902 }
    00903
    00904 void DrawSmallOrderList(const Vehicle *v, int x, int y)
    00905 {
    00906   const Order *order;
    00907   int sel, i = 0;
    00908
    00909   sel = v->cur_order_index;
    00910
    00911   FOR_VEHICLE_ORDERS(v, order) {
    00912     if (sel == 0) DrawString(x - 6, y, STR_SMALL_RIGHT_ARROW, TC_BLACK);
    00913     sel--;
    00914
    00915     if (order->type == OT_GOTO_STATION) {
    00916       if (v->type == VEH_SHIP && GetStation(order->dest)->IsBuoy()) continue;
    00917
    00918       SetDParam(0, order->dest);
    00919       DrawString(x, y, STR_A036, TC_FROMSTRING);
    00920
    00921       y += 6;
    00922       if (++i == 4) break;
    00923     }
    00924   }
    00925 }
    00926
    00927 static void DrawVehicleListWindow(Window *w)
    00928 {
    00929   vehiclelist_d *vl = &WP(w, vehiclelist_d);
    00930   int x = 2;
    00931   int y = PLY_WND_PRC__OFFSET_TOP_WIDGET;
    00932   int max;
    00933   int i;
    00934   const PlayerID owner = (PlayerID)w->caption_color;
    00935   const uint16 window_type = w->window_number & VLW_MASK;
    00936   const uint16 index = GB(w->window_number, 16, 16);
    00937
    00938   BuildVehicleList(vl, owner, index, window_type);
    00939   SortVehicleList(vl);
    00940   SetVScrollCount(w, vl->l.list_length);
    00941
    00942   if (vl->l.list_length == 0) HideDropDownMenu(w);
    00943
    00944   /* draw the widgets */
    00945   switch (window_type) {
    00946     case VLW_SHARED_ORDERS: /* Shared Orders */
    00947       if (vl->l.list_length == 0) {
    00948         /* We can't open this window without vehicles using this order
    00949          * and we should close the window when deleting the order      */
    00950         NOT_REACHED();
    00951       }
    00952       SetDParam(0, w->vscroll.count);
    00953       break;
    00954
    00955     case VLW_STANDARD: /* Company Name */
    00956       SetDParam(0, owner);
    00957       SetDParam(1, w->vscroll.count);
    00958       break;
    00959
    00960     case VLW_STATION_LIST: /* Station Name */
    00961       SetDParam(0, index);
    00962       SetDParam(1, w->vscroll.count);
    00963       break;
    00964
    00965     case VLW_DEPOT_LIST:
    00966       switch (vl->vehicle_type) {
    00967         case VEH_TRAIN:    SetDParam(0, STR_8800_TRAIN_DEPOT);        break;
    00968         case VEH_ROAD:     SetDParam(0, STR_9003_ROAD_VEHICLE_DEPOT); break;
    00969         case VEH_SHIP:     SetDParam(0, STR_9803_SHIP_DEPOT);         break;
    00970         case VEH_AIRCRAFT: SetDParam(0, STR_A002_AIRCRAFT_HANGAR);    break;
    00971         default: NOT_REACHED(); break;
    00972       }
    00973       if (vl->vehicle_type == VEH_AIRCRAFT) {
    00974         SetDParam(1, index); // Airport name
    00975       } else {
    00976         SetDParam(1, GetDepot(index)->town_index);
    00977       }
    00978       SetDParam(2, w->vscroll.count);
    00979       break;
    00980     default: NOT_REACHED(); break;
    00981   }
    00982
    00983   w->SetWidgetsDisabledState(vl->l.list_length == 0,
    00984     VLW_WIDGET_MANAGE_VEHICLES_DROPDOWN,
    00985     VLW_WIDGET_STOP_ALL,
    00986     VLW_WIDGET_START_ALL,
    00987     WIDGET_LIST_END);
    00988
    00989   DrawWindowWidgets(w);
    00990
    00991   /* draw sorting criteria string */
    00992   DrawString(85, 15, _vehicle_sort_listing[vl->l.sort_type], TC_BLACK);
    00993   /* draw arrow pointing up/down for ascending/descending sorting */
    00994   DrawSortButtonState(w, VLW_WIDGET_SORT_ORDER, vl->l.flags & VL_DESC ? SBS_DOWN : SBS_UP);
    00995
    00996   max = min(w->vscroll.pos + w->vscroll.cap, vl->l.list_length);
    00997   for (i = w->vscroll.pos; i < max; ++i) {
    00998     const Vehicle *v = vl->sort_list[i];
    00999     StringID str;
    01000
    01001     SetDParam(0, v->GetDisplayProfitThisYear());
    01002     SetDParam(1, v->GetDisplayProfitLastYear());
    01003
    01004     DrawVehicleImage(v, x + 19, y + 6, INVALID_VEHICLE, w->widget[VLW_WIDGET_LIST].right - w->widget[VLW_WIDGET_LIST].left - 20, 0);
    01005     DrawString(x + 19, y + w->resize.step_height - 8, STR_0198_PROFIT_THIS_YEAR_LAST_YEAR, TC_FROMSTRING);
    01006
    01007     if (v->name != NULL) {
    01008       /* The vehicle got a name so we will print it */
    01009       SetDParam(0, v->index);
    01010       DrawString(x + 19, y, STR_01AB, TC_FROMSTRING);
    01011     }
    01012
    01013     if (w->resize.step_height == PLY_WND_PRC__SIZE_OF_ROW_BIG) DrawSmallOrderList(v, x + 138, y);
    01014
    01015     if (v->IsInDepot()) {
    01016       str = STR_021F;
    01017     } else {
    01018       str = (v->age > v->max_age - 366) ? STR_00E3 : STR_00E2;
    01019     }
    01020
    01021     SetDParam(0, v->unitnumber);
    01022     DrawString(x, y + 2, str, TC_FROMSTRING);
    01023
    01024     DrawVehicleProfitButton(v, x, y + 13);
    01025
    01026     y += w->resize.step_height;
    01027   }
    01028 }
    01029
    01030 /*
    01031  * bitmask for w->window_number
    01032  * 0-7 PlayerID (owner)
    01033  * 8-10 window type (use flags in vehicle_gui.h)
    01034  * 11-15 vehicle type (using VEH_, but can be compressed to fewer bytes if needed)
    01035  * 16-31 StationID or OrderID depending on window type (bit 8-10)
    01036  **/
    01037 void PlayerVehWndProc(Window *w, WindowEvent *e)
    01038 {
    01039   vehiclelist_d *vl = &WP(w, vehiclelist_d);
    01040
    01041   switch (e->event) {
    01042     case WE_CREATE:
    01043       CreateVehicleListWindow(w);
    01044       break;
    01045
    01046     case WE_PAINT:
    01047       DrawVehicleListWindow(w);
    01048       break;
    01049
    01050     case WE_CLICK: {
    01051       switch (e->we.click.widget) {
    01052         case VLW_WIDGET_SORT_ORDER: /* Flip sorting method ascending/descending */
    01053           vl->l.flags ^= VL_DESC;
    01054           vl->l.flags |= VL_RESORT;
    01055
    01056           vl->_sorting->order = !!(vl->l.flags & VL_DESC);
    01057           SetWindowDirty(w);
    01058           break;
    01059         case VLW_WIDGET_SORT_BY_PULLDOWN:/* Select sorting criteria dropdown menu */
    01060           ShowDropDownMenu(w, _vehicle_sort_listing, vl->l.sort_type, VLW_WIDGET_SORT_BY_PULLDOWN, 0, 0);
    01061           return;
    01062         case VLW_WIDGET_LIST: { /* Matrix to show vehicles */
    01063           uint32 id_v = (e->we.click.pt.y - PLY_WND_PRC__OFFSET_TOP_WIDGET) / w->resize.step_height;
    01064           const Vehicle *v;
    01065
    01066           if (id_v >= w->vscroll.cap) return; // click out of bounds
    01067
    01068           id_v += w->vscroll.pos;
    01069
    01070           if (id_v >= vl->l.list_length) return; // click out of list bound
    01071
    01072           v = vl->sort_list[id_v];
    01073
    01074           ShowVehicleViewWindow(v);
    01075         } break;
    01076
    01077         case VLW_WIDGET_AVAILABLE_VEHICLES:
    01078           ShowBuildVehicleWindow(0, vl->vehicle_type);
    01079           break;
    01080
    01081         case VLW_WIDGET_MANAGE_VEHICLES_DROPDOWN: {
    01082           static StringID action_str[] = {
    01083             STR_REPLACE_VEHICLES,
    01084             STR_SEND_FOR_SERVICING,
    01085             STR_NULL,
    01086             INVALID_STRING_ID
    01087           };
    01088
    01089           static const StringID depot_name[] = {
    01090             STR_SEND_TRAIN_TO_DEPOT,
    01091             STR_SEND_ROAD_VEHICLE_TO_DEPOT,
    01092             STR_SEND_SHIP_TO_DEPOT,
    01093             STR_SEND_AIRCRAFT_TO_HANGAR
    01094           };
    01095
    01096           /* XXX - Substite string since the dropdown cannot handle dynamic strings */
    01097           action_str[2] = depot_name[vl->vehicle_type];
    01098           ShowDropDownMenu(w, action_str, 0, VLW_WIDGET_MANAGE_VEHICLES_DROPDOWN, 0, (w->window_number & VLW_MASK) == VLW_STANDARD ? 0 : 1);
    01099           break;
    01100         }
    01101
    01102         case VLW_WIDGET_STOP_ALL:
    01103         case VLW_WIDGET_START_ALL:
    01104           DoCommandP(0, GB(w->window_number, 16, 16), (w->window_number & VLW_MASK) | (1 << 6) | (e->we.click.widget == VLW_WIDGET_START_ALL ? (1 << 5) : 0) | vl->vehicle_type, NULL, CMD_MASS_START_STOP);
    01105           break;
    01106       }
    01107     } break;
    01108
    01109     case WE_DROPDOWN_SELECT: /* we have selected a dropdown item in the list */
    01110       switch (e->we.dropdown.button) {
    01111         case VLW_WIDGET_SORT_BY_PULLDOWN:
    01112           if (vl->l.sort_type != e->we.dropdown.index) {
    01113             /* value has changed -> resort */
    01114             vl->l.flags |= VL_RESORT;
    01115             vl->l.sort_type = e->we.dropdown.index;
    01116             vl->_sorting->criteria = vl->l.sort_type;
    01117           }
    01118           break;
    01119         case VLW_WIDGET_MANAGE_VEHICLES_DROPDOWN:
    01120           assert(vl->l.list_length != 0);
    01121
    01122           switch (e->we.dropdown.index) {
    01123             case 0: /* Replace window */
    01124               ShowReplaceGroupVehicleWindow(DEFAULT_GROUP, vl->vehicle_type);
    01125               break;
    01126             case 1: /* Send for servicing */
    01127               DoCommandP(0, GB(w->window_number, 16, 16) /* StationID or OrderID (depending on VLW) */,
    01128                 (w->window_number & VLW_MASK) | DEPOT_MASS_SEND | DEPOT_SERVICE,
    01129                 NULL,
    01130                 GetCmdSendToDepot(vl->vehicle_type));
    01131               break;
    01132             case 2: /* Send to Depots */
    01133               DoCommandP(0, GB(w->window_number, 16, 16) /* StationID or OrderID (depending on VLW) */,
    01134                 (w->window_number & VLW_MASK) | DEPOT_MASS_SEND,
    01135                 NULL,
    01136                 GetCmdSendToDepot(vl->vehicle_type));
    01137               break;
    01138
    01139             default: NOT_REACHED();
    01140           }
    01141           break;
    01142         default: NOT_REACHED();
    01143       }
    01144       SetWindowDirty(w);
    01145       break;
    01146
    01147     case WE_DESTROY:
    01148       free((void*)vl->sort_list);
    01149       break;
    01150
    01151     case WE_TICK: /* resort the list every 20 seconds orso (10 days) */
    01152       if (_pause_game != 0) break;
    01153       if (--vl->l.resort_timer == 0) {
    01154         StationID station = ((w->window_number & VLW_MASK) == VLW_STATION_LIST) ? GB(w->window_number, 16, 16) : INVALID_STATION;
    01155         PlayerID owner = (PlayerID)w->caption_color;
    01156
    01157         DEBUG(misc, 3, "Periodic resort %d list player %d at station %d", vl->vehicle_type, owner, station);
    01158         vl->l.resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS;
    01159         vl->l.flags |= VL_RESORT;
    01160         SetWindowDirty(w);
    01161       }
    01162       break;
    01163
    01164     case WE_RESIZE: /* Update the scroll + matrix */
    01165       w->vscroll.cap += e->we.sizing.diff.y / (int)w->resize.step_height;
    01166       w->widget[VLW_WIDGET_LIST].data = (w->vscroll.cap << 8) + 1;
    01167       break;
    01168   }
    01169 }
    01170
    01171 static const WindowDesc _player_vehicle_list_train_desc = {
    01172   WDP_AUTO, WDP_AUTO, 260, 182, 260, 182,
    01173   WC_TRAINS_LIST, WC_NONE,
    01174   WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
    01175   _vehicle_list_widgets,
    01176   PlayerVehWndProc
    01177 };
    01178
    01179 static const WindowDesc _player_vehicle_list_road_veh_desc = {
    01180   WDP_AUTO, WDP_AUTO, 260, 182, 260, 182,
    01181   WC_ROADVEH_LIST, WC_NONE,
    01182   WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
    01183   _vehicle_list_widgets,
    01184   PlayerVehWndProc
    01185 };
    01186
    01187 static const WindowDesc _player_vehicle_list_ship_desc = {
    01188   WDP_AUTO, WDP_AUTO, 260, 182, 260, 182,
    01189   WC_SHIPS_LIST, WC_NONE,
    01190   WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
    01191   _vehicle_list_widgets,
    01192   PlayerVehWndProc
    01193 };
    01194
    01195 static const WindowDesc _player_vehicle_list_aircraft_desc = {
    01196   WDP_AUTO, WDP_AUTO, 260, 182, 260, 182,
    01197   WC_AIRCRAFT_LIST, WC_NONE,
    01198   WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
    01199   _vehicle_list_widgets,
    01200   PlayerVehWndProc
    01201 };
    01202
    01203 static void ShowVehicleListWindowLocal(PlayerID player, uint16 VLW_flag, VehicleType vehicle_type, uint16 unique_number)
    01204 {
    01205   Window *w;
    01206   WindowNumber num;
    01207
    01208   if (!IsValidPlayer(player)) return;
    01209
    01210   num = (unique_number << 16) | (vehicle_type << 11) | VLW_flag | player;
    01211
    01212   /* The vehicle list windows have been unified. Just some strings need
    01213    * to be changed which happens in the WE_CREATE event and resizing
    01214    * some of the windows to the correct size */
    01215   switch (vehicle_type) {
    01216     default: NOT_REACHED();
    01217     case VEH_TRAIN:
    01218       w = AllocateWindowDescFront(&_player_vehicle_list_train_desc, num);
    01219       if (w != NULL) ResizeWindow(w, 65, 38);
    01220       break;
    01221     case VEH_ROAD:
    01222       w = AllocateWindowDescFront(&_player_vehicle_list_road_veh_desc, num);
    01223       if (w != NULL) ResizeWindow(w, 0, 38);
    01224       break;
    01225     case VEH_SHIP:
    01226       w = AllocateWindowDescFront(&_player_vehicle_list_ship_desc, num);
    01227       break;
    01228     case VEH_AIRCRAFT:
    01229       w = AllocateWindowDescFront(&_player_vehicle_list_aircraft_desc, num);
    01230       break;
    01231   }
    01232
    01233   if (w != NULL) {
    01234     /* Set the minimum window size to the current window size */
    01235     w->resize.width = w->width;
    01236     w->resize.height = w->height;
    01237   }
    01238 }
    01239
    01240 void ShowVehicleListWindow(PlayerID player, VehicleType vehicle_type)
    01241 {
    01242   /* If _patches.advanced_vehicle_list > 1, display the Advanced list
    01243    * if _patches.advanced_vehicle_list == 1, display Advanced list only for local player
    01244    * if _ctrl_pressed, do the opposite action (Advanced list x Normal list)
    01245    */
    01246
    01247   if ((_patches.advanced_vehicle_list > (uint)(player != _local_player)) != _ctrl_pressed) {
    01248     ShowPlayerGroup(player, vehicle_type);
    01249   } else {
    01250     ShowVehicleListWindowLocal(player, VLW_STANDARD, vehicle_type, 0);
    01251   }
    01252 }
    01253
    01254 void ShowVehicleListWindow(const Vehicle *v)
    01255 {
    01256   if (v->orders == NULL) return; // no shared list to show
    01257   ShowVehicleListWindowLocal(v->owner, VLW_SHARED_ORDERS, v->type, v->orders->index);
    01258 }
    01259
    01260 void ShowVehicleListWindow(PlayerID player, VehicleType vehicle_type, StationID station)
    01261 {
    01262   ShowVehicleListWindowLocal(player, VLW_STATION_LIST, vehicle_type, station);
    01263 }
    01264
    01265 void ShowVehicleListWindow(PlayerID player, VehicleType vehicle_type, TileIndex depot_tile)
    01266 {
    01267   uint16 depot_airport_index;
    01268
    01269   if (vehicle_type == VEH_AIRCRAFT) {
    01270     depot_airport_index = GetStationIndex(depot_tile);
    01271   } else {
    01272     Depot *depot = GetDepotByTile(depot_tile);
    01273     if (depot == NULL) return; // no depot to show
    01274     depot_airport_index = depot->index;
    01275   }
    01276   ShowVehicleListWindowLocal(player, VLW_DEPOT_LIST, vehicle_type, depot_airport_index);
    01277 }
    01278
    01279
    01280 /* Unified vehicle GUI - Vehicle Details Window */
    01281
    01283 enum VehicleDetailsWindowWidgets {
    01284   VLD_WIDGET_CLOSEBOX = 0,
    01285   VLD_WIDGET_CAPTION,
    01286   VLD_WIDGET_RENAME_VEHICLE,
    01287   VLD_WIDGET_TOP_DETAILS,
    01288   VLD_WIDGET_INCREASE_SERVICING_INTERVAL,
    01289   VLD_WIDGET_DECREASE_SERVICING_INTERVAL,
    01290   VLD_WIDGET_BOTTOM_RIGHT,
    01291   VLD_WIDGET_MIDDLE_DETAILS,
    01292   VLD_WIDGET_SCROLLBAR,
    01293   VLD_WIDGET_DETAILS_CARGO_CARRIED,
    01294   VLD_WIDGET_DETAILS_TRAIN_VEHICLES,
    01295   VLD_WIDGET_DETAILS_CAPACITY_OF_EACH,
    01296   VLD_WIDGET_DETAILS_TOTAL_CARGO,
    01297   VLD_WIDGET_RESIZE,
    01298 };
    01299
    01301 static const Widget _vehicle_details_widgets[] = {
    01302   {   WWT_CLOSEBOX,   RESIZE_NONE, 14,   0,  10,   0,  13, STR_00C5,             STR_018B_CLOSE_WINDOW},                  // VLD_WIDGET_CLOSEBOX
    01303   {    WWT_CAPTION,  RESIZE_RIGHT, 14,  11, 364,   0,  13, 0x0,                  STR_018C_WINDOW_TITLE_DRAG_THIS},        // VLD_WIDGET_CAPTION
    01304   { WWT_PUSHTXTBTN,     RESIZE_LR, 14, 365, 404,   0,  13, STR_01AA_NAME,        STR_NULL /* filled in later */},         // VLD_WIDGET_RENAME_VEHICLE
    01305   {      WWT_PANEL,  RESIZE_RIGHT, 14,   0, 404,  14,  55, 0x0,                  STR_NULL},                               // VLD_WIDGET_TOP_DETAILS
    01306   { WWT_PUSHTXTBTN,     RESIZE_TB, 14,   0,  10, 101, 106, STR_0188,             STR_884D_INCREASE_SERVICING_INTERVAL},   // VLD_WIDGET_INCREASE_SERVICING_INTERVAL
    01307   { WWT_PUSHTXTBTN,     RESIZE_TB, 14,   0,  10, 107, 112, STR_0189,             STR_884E_DECREASE_SERVICING_INTERVAL},   // VLD_WIDGET_DECREASE_SERVICING_INTERVAL
    01308   {      WWT_PANEL,    RESIZE_RTB, 14,  11, 404, 101, 112, 0x0,                  STR_NULL},                               // VLD_WIDGET_BOTTOM_RIGHT
    01309   {     WWT_MATRIX,     RESIZE_RB, 14,   0, 392,  56, 100, 0x701,                STR_NULL},                               // VLD_WIDGET_MIDDLE_DETAILS
    01310   {  WWT_SCROLLBAR,    RESIZE_LRB, 14, 393, 404,  56, 100, 0x0,                  STR_0190_SCROLL_BAR_SCROLLS_LIST},       // VLD_WIDGET_SCROLLBAR
    01311   { WWT_PUSHTXTBTN,     RESIZE_TB, 14,   0,  95, 113, 124, STR_013C_CARGO,       STR_884F_SHOW_DETAILS_OF_CARGO_CARRIED}, // VLD_WIDGET_DETAILS_CARGO_CARRIED
    01312   { WWT_PUSHTXTBTN,     RESIZE_TB, 14,  96, 194, 113, 124, STR_013D_INFORMATION, STR_8850_SHOW_DETAILS_OF_TRAIN_VEHICLES},// VLD_WIDGET_DETAILS_TRAIN_VEHICLES
    01313   { WWT_PUSHTXTBTN,     RESIZE_TB, 14, 195, 293, 113, 124, STR_013E_CAPACITIES,  STR_8851_SHOW_CAPACITIES_OF_EACH},       // VLD_WIDGET_DETAILS_CAPACITY_OF_EACH
    01314   { WWT_PUSHTXTBTN,    RESIZE_RTB, 14, 294, 392, 113, 124, STR_TOTAL_CARGO,      STR_SHOW_TOTAL_CARGO},                   // VLD_WIDGET_DETAILS_TOTAL_CARGO
    01315   {  WWT_RESIZEBOX,   RESIZE_LRTB, 14, 393, 404, 113, 124, 0x0,                  STR_RESIZE_BUTTON},                      // VLD_RESIZE
    01316   {   WIDGETS_END},
    01317 };
    01318
    01319
    01321 enum VehicleStringTranslation {
    01322   VST_VEHICLE_AGE_RUNNING_COST_YR,
    01323   VST_VEHICLE_MAX_SPEED,
    01324   VST_VEHICLE_PROFIT_THIS_YEAR_LAST_YEAR,
    01325   VST_VEHICLE_RELIABILITY_BREAKDOWNS,
    01326 };
    01327
    01329 static const StringID _vehicle_translation_table[][4] = {
    01330   { // VST_VEHICLE_AGE_RUNNING_COST_YR
    01331     STR_885D_AGE_RUNNING_COST_YR,
    01332     STR_900D_AGE_RUNNING_COST_YR,
    01333     STR_9812_AGE_RUNNING_COST_YR,
    01334     STR_A00D_AGE_RUNNING_COST_YR,
    01335   },
    01336   { // VST_VEHICLE_MAX_SPEED
    01337     STR_NULL,
    01338     STR_900E_MAX_SPEED,
    01339     STR_9813_MAX_SPEED,
    01340     STR_A00E_MAX_SPEED,
    01341   },
    01342   { // VST_VEHICLE_PROFIT_THIS_YEAR_LAST_YEAR
    01343     STR_885F_PROFIT_THIS_YEAR_LAST_YEAR,
    01344     STR_900F_PROFIT_THIS_YEAR_LAST_YEAR,
    01345     STR_9814_PROFIT_THIS_YEAR_LAST_YEAR,
    01346     STR_A00F_PROFIT_THIS_YEAR_LAST_YEAR,
    01347   },
    01348   { // VST_VEHICLE_RELIABILITY_BREAKDOWNS
    01349     STR_8860_RELIABILITY_BREAKDOWNS,
    01350     STR_9010_RELIABILITY_BREAKDOWNS,
    01351     STR_9815_RELIABILITY_BREAKDOWNS,
    01352     STR_A010_RELIABILITY_BREAKDOWNS,
    01353   },
    01354 };
    01355
    01357 void CreateVehicleDetailsWindow(Window *w)
    01358 {
    01359   const Vehicle *v = GetVehicle(w->window_number);
    01360
    01361   switch (v->type) {
    01362     case VEH_TRAIN:
    01363       ResizeWindow(w, 0, 39);
    01364
    01365       w->vscroll.cap = 6;
    01366       w->height += 12;
    01367       w->resize.step_height = 14;
    01368       w->resize.height = w->height - 14 * 2; // Minimum of 4 wagons in the display
    01369
    01370       w->widget[VLD_WIDGET_RENAME_VEHICLE].tooltips = STR_8867_NAME_TRAIN;
    01371       w->widget[VLD_WIDGET_CAPTION].data = STR_8802_DETAILS;
    01372       break;
    01373
    01374     case VEH_ROAD: {
    01375       w->widget[VLD_WIDGET_CAPTION].data = STR_900C_DETAILS;
    01376       w->widget[VLD_WIDGET_RENAME_VEHICLE].tooltips = STR_902E_NAME_ROAD_VEHICLE;
    01377
    01378       if (!RoadVehHasArticPart(v)) break;
    01379
    01380       /* Draw the text under the vehicle instead of next to it, minus the
    01381       * height already allocated for the cargo of the first vehicle. */
    01382       uint height_extension = 15 - 11;
    01383
    01384       /* Add space for the cargo amount for each part. */
    01385       for (const Vehicle *u = v; u != NULL; u = u->Next()) {
    01386         if (u->cargo_cap != 0) height_extension += 11;
    01387       }
    01388
    01389       ResizeWindow(w, 0, height_extension);
    01390     } break;
    01391
    01392     case VEH_SHIP:
    01393       w->widget[VLD_WIDGET_RENAME_VEHICLE].tooltips = STR_982F_NAME_SHIP;
    01394       w->widget[VLD_WIDGET_CAPTION].data = STR_9811_DETAILS;
    01395       break;
    01396
    01397     case VEH_AIRCRAFT:
    01398       ResizeWindow(w, 0, 11);
    01399       w->widget[VLD_WIDGET_RENAME_VEHICLE].tooltips = STR_A032_NAME_AIRCRAFT;
    01400       w->widget[VLD_WIDGET_CAPTION].data = STR_A00C_DETAILS;
    01401       break;
    01402     default: NOT_REACHED();
    01403   }
    01404
    01405   if (v->type != VEH_TRAIN) {
    01406     w->vscroll.cap = 1;
    01407     w->widget[VLD_WIDGET_MIDDLE_DETAILS].right += 12;
    01408   }
    01409
    01410   w->widget[VLD_WIDGET_MIDDLE_DETAILS].data = (w->vscroll.cap << 8) + 1;
    01411   w->caption_color = v->owner;
    01412
    01413   WP(w, vehicledetails_d).tab = 0;
    01414 }
    01415
    01417 static inline bool IsVehicleServiceIntervalEnabled(const VehicleType vehicle_type)
    01418 {
    01419   switch (vehicle_type) {
    01420     default: NOT_REACHED();
    01421     case VEH_TRAIN:    return _patches.servint_trains   != 0; break;
    01422     case VEH_ROAD:     return _patches.servint_roadveh  != 0; break;
    01423     case VEH_SHIP:     return _patches.servint_ships    != 0; break;
    01424     case VEH_AIRCRAFT: return _patches.servint_aircraft != 0; break;
    01425   }
    01426   return false; // kill a compiler warning
    01427 }
    01428
    01429 extern int GetTrainDetailsWndVScroll(VehicleID veh_id, byte det_tab);
    01430 extern void DrawTrainDetails(const Vehicle *v, int x, int y, int vscroll_pos, uint16 vscroll_cap, byte det_tab);
    01431 extern void DrawRoadVehDetails(const Vehicle *v, int x, int y);
    01432 extern void DrawShipDetails(const Vehicle *v, int x, int y);
    01433 extern void DrawAircraftDetails(const Vehicle *v, int x, int y);
    01434
    01445 static inline void DrawVehicleDetails(const Vehicle *v, int x, int y, int vscroll_pos, uint vscroll_cap, byte det_tab)
    01446 {
    01447   switch (v->type) {
    01448     case VEH_TRAIN:    DrawTrainDetails(v, x, y, vscroll_pos, vscroll_cap, det_tab);  break;
    01449     case VEH_ROAD:     DrawRoadVehDetails(v, x, y);  break;
    01450     case VEH_SHIP:     DrawShipDetails(v, x, y);     break;
    01451     case VEH_AIRCRAFT: DrawAircraftDetails(v, x, y); break;
    01452     default: NOT_REACHED();
    01453   }
    01454 }
    01455
    01457 static void DrawVehicleDetailsWindow(Window *w)
    01458 {
    01459   const Vehicle *v = GetVehicle(w->window_number);
    01460   byte det_tab = WP(w, vehicledetails_d).tab;
    01461
    01462   w->SetWidgetDisabledState(VLD_WIDGET_RENAME_VEHICLE, v->owner != _local_player);
    01463
    01464   if (v->type == VEH_TRAIN) {
    01465     w->DisableWidget(det_tab + VLD_WIDGET_DETAILS_CARGO_CARRIED);
    01466     SetVScrollCount(w, GetTrainDetailsWndVScroll(v->index, det_tab));
    01467   }
    01468
    01469   w->SetWidgetsHiddenState(v->type != VEH_TRAIN,
    01470     VLD_WIDGET_SCROLLBAR,
    01471     VLD_WIDGET_DETAILS_CARGO_CARRIED,
    01472     VLD_WIDGET_DETAILS_TRAIN_VEHICLES,
    01473     VLD_WIDGET_DETAILS_CAPACITY_OF_EACH,
    01474     VLD_WIDGET_DETAILS_TOTAL_CARGO,
    01475     VLD_WIDGET_RESIZE,
    01476     WIDGET_LIST_END);
    01477
    01478   /* Disable service-scroller when interval is set to disabled */
    01479   w->SetWidgetsDisabledState(!IsVehicleServiceIntervalEnabled(v->type),
    01480     VLD_WIDGET_INCREASE_SERVICING_INTERVAL,
    01481     VLD_WIDGET_DECREASE_SERVICING_INTERVAL,
    01482     WIDGET_LIST_END);
    01483
    01484
    01485   SetDParam(0, v->index);
    01486   DrawWindowWidgets(w);
    01487
    01488   /* Draw running cost */
    01489   SetDParam(1, v->age / 366);
    01490   SetDParam(0, (v->age + 365 < v->max_age) ? STR_AGE : STR_AGE_RED);
    01491   SetDParam(2, v->max_age / 366);
    01492   SetDParam(3, v->GetDisplayRunningCost());
    01493   DrawString(2, 15, _vehicle_translation_table[VST_VEHICLE_AGE_RUNNING_COST_YR][v->type], TC_FROMSTRING);
    01494
    01495   /* Draw max speed */
    01496   switch (v->type) {
    01497     case VEH_TRAIN:
    01498       SetDParam(2, v->GetDisplayMaxSpeed());
    01499       SetDParam(1, v->u.rail.cached_power);
    01500       SetDParam(0, v->u.rail.cached_weight);
    01501       SetDParam(3, v->u.rail.cached_max_te / 1000);
    01502       DrawString(2, 25, (_patches.realistic_acceleration && v->u.rail.railtype != RAILTYPE_MAGLEV) ?
    01503         STR_VEHICLE_INFO_WEIGHT_POWER_MAX_SPEED_MAX_TE :
    01504         STR_VEHICLE_INFO_WEIGHT_POWER_MAX_SPEED, TC_FROMSTRING);
    01505       break;
    01506
    01507     case VEH_ROAD:
    01508     case VEH_SHIP:
    01509     case VEH_AIRCRAFT:
    01510       SetDParam(0, v->GetDisplayMaxSpeed());
    01511       DrawString(2, 25, _vehicle_translation_table[VST_VEHICLE_MAX_SPEED][v->type], TC_FROMSTRING);
    01512       break;
    01513
    01514     default: NOT_REACHED();
    01515   }
    01516
    01517   /* Draw profit */
    01518   SetDParam(0, v->GetDisplayProfitThisYear());
    01519   SetDParam(1, v->GetDisplayProfitLastYear());
    01520   DrawString(2, 35, _vehicle_translation_table[VST_VEHICLE_PROFIT_THIS_YEAR_LAST_YEAR][v->type], TC_FROMSTRING);
    01521
    01522   /* Draw breakdown & reliability */
    01523   SetDParam(0, v->reliability * 100 >> 16);
    01524   SetDParam(1, v->breakdowns_since_last_service);
    01525   DrawString(2, 45, _vehicle_translation_table[VST_VEHICLE_RELIABILITY_BREAKDOWNS][v->type], TC_FROMSTRING);
    01526
    01527   /* Draw service interval text */
    01528   SetDParam(0, v->service_interval);
    01529   SetDParam(1, v->date_of_last_service);
    01530   DrawString(13, w->height - (v->type != VEH_TRAIN ? 11 : 23), _patches.servint_ispercent ? STR_SERVICING_INTERVAL_PERCENT : STR_883C_SERVICING_INTERVAL_DAYS, TC_FROMSTRING);
    01531
    01532   switch (v->type) {
    01533     case VEH_TRAIN:
    01534       DrawVehicleDetails(v, 2, 57, w->vscroll.pos, w->vscroll.cap, det_tab);
    01535       break;
    01536
    01537     case VEH_ROAD:
    01538     case VEH_SHIP:
    01539     case VEH_AIRCRAFT:
    01540       DrawVehicleImage(v, 3, 57, INVALID_VEHICLE, 0, 0);
    01541       DrawVehicleDetails(v, 75, 57, w->vscroll.pos, w->vscroll.cap, det_tab);
    01542       break;
    01543
    01544     default: NOT_REACHED();
    01545   }
    01546 }
    01547
    01549 static const StringID _name_vehicle_title[] = {
    01550   STR_8865_NAME_TRAIN,
    01551   STR_902C_NAME_ROAD_VEHICLE,
    01552   STR_9831_NAME_SHIP,
    01553   STR_A030_NAME_AIRCRAFT
    01554 };
    01555
    01557 static const StringID _name_vehicle_error[] = {
    01558   STR_8866_CAN_T_NAME_TRAIN,
    01559   STR_902D_CAN_T_NAME_ROAD_VEHICLE,
    01560   STR_9832_CAN_T_NAME_SHIP,
    01561   STR_A031_CAN_T_NAME_AIRCRAFT
    01562 };
    01563
    01565 static void VehicleDetailsWndProc(Window *w, WindowEvent *e)
    01566 {
    01567   switch (e->event) {
    01568     case WE_CREATE:
    01569       CreateVehicleDetailsWindow(w);
    01570       break;
    01571
    01572     case WE_PAINT:
    01573       DrawVehicleDetailsWindow(w);
    01574       break;
    01575
    01576     case WE_CLICK: {
    01577       switch (e->we.click.widget) {
    01578         case VLD_WIDGET_RENAME_VEHICLE: {// rename
    01579           const Vehicle *v = GetVehicle(w->window_number);
    01580           SetDParam(0, v->index);
    01581           ShowQueryString(STR_VEHICLE_NAME, _name_vehicle_title[v->type], 31, 150, w, CS_ALPHANUMERAL);
    01582         } break;
    01583
    01584         case VLD_WIDGET_INCREASE_SERVICING_INTERVAL:   // increase int
    01585         case VLD_WIDGET_DECREASE_SERVICING_INTERVAL: { // decrease int
    01586           int mod = _ctrl_pressed ? 5 : 10;
    01587           const Vehicle *v = GetVehicle(w->window_number);
    01588
    01589           mod = (e->we.click.widget == VLD_WIDGET_DECREASE_SERVICING_INTERVAL) ? -mod : mod;
    01590           mod = GetServiceIntervalClamped(mod + v->service_interval);
    01591           if (mod == v->service_interval) return;
    01592
    01593           DoCommandP(v->tile, v->index, mod, NULL, CMD_CHANGE_SERVICE_INT | CMD_MSG(STR_018A_CAN_T_CHANGE_SERVICING));
    01594         } break;
    01595
    01596         case VLD_WIDGET_DETAILS_CARGO_CARRIED:
    01597         case VLD_WIDGET_DETAILS_TRAIN_VEHICLES:
    01598         case VLD_WIDGET_DETAILS_CAPACITY_OF_EACH:
    01599         case VLD_WIDGET_DETAILS_TOTAL_CARGO:
    01600           w->SetWidgetsDisabledState(false,
    01601             VLD_WIDGET_DETAILS_CARGO_CARRIED,
    01602             VLD_WIDGET_DETAILS_TRAIN_VEHICLES,
    01603             VLD_WIDGET_DETAILS_CAPACITY_OF_EACH,
    01604             VLD_WIDGET_DETAILS_TOTAL_CARGO,
    01605             e->we.click.widget,
    01606             WIDGET_LIST_END);
    01607
    01608           WP(w, vehicledetails_d).tab = e->we.click.widget - VLD_WIDGET_DETAILS_CARGO_CARRIED;
    01609           SetWindowDirty(w);
    01610           break;
    01611       }
    01612     } break;
    01613
    01614     case WE_ON_EDIT_TEXT:
    01615       if (!StrEmpty(e->we.edittext.str)) {
    01616         _cmd_text = e->we.edittext.str;
    01617         DoCommandP(0, w->window_number, 0, NULL, CMD_NAME_VEHICLE | CMD_MSG(_name_vehicle_error[GetVehicle(w->window_number)->type]));
    01618       }
    01619       break;
    01620
    01621     case WE_RESIZE:
    01622       if (e->we.sizing.diff.x != 0) ResizeButtons(w, VLD_WIDGET_DETAILS_CARGO_CARRIED, VLD_WIDGET_DETAILS_TOTAL_CARGO);
    01623       if (e->we.sizing.diff.y == 0) break;
    01624
    01625       w->vscroll.cap += e->we.sizing.diff.y / 14;
    01626       w->widget[VLD_WIDGET_MIDDLE_DETAILS].data = (w->vscroll.cap << 8) + 1;
    01627       break;
    01628   }
    01629 }
    01630
    01632 static const WindowDesc _vehicle_details_desc = {
    01633   WDP_AUTO, WDP_AUTO, 405, 113, 405, 113,
    01634   WC_VEHICLE_DETAILS, WC_VEHICLE_VIEW,
    01635   WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_RESIZABLE,
    01636   _vehicle_details_widgets,
    01637   VehicleDetailsWndProc
    01638 };
    01639
    01641 static void ShowVehicleDetailsWindow(const Vehicle *v)
    01642 {
    01643   DeleteWindowById(WC_VEHICLE_ORDERS, v->index);
    01644   DeleteWindowById(WC_VEHICLE_DETAILS, v->index);
    01645   AllocateWindowDescFront(&_vehicle_details_desc, v->index);
    01646 }
    01647
    01648
    01649 /* Unified vehicle GUI - Vehicle View Window */
    01650
    01652 static const Widget _vehicle_view_widgets[] = {
    01653   {   WWT_CLOSEBOX,  RESIZE_NONE,  14,   0,  10,   0,  13, STR_00C5,                 STR_018B_CLOSE_WINDOW },           // VVW_WIDGET_CLOSEBOX
    01654   {    WWT_CAPTION, RESIZE_RIGHT,  14,  11, 237,   0,  13, 0x0 /* filled later */,   STR_018C_WINDOW_TITLE_DRAG_THIS }, // VVW_WIDGET_CAPTION
    01655   {  WWT_STICKYBOX,    RESIZE_LR,  14, 238, 249,   0,  13, 0x0,                      STR_STICKY_BUTTON },               // VVW_WIDGET_STICKY
    01656   {      WWT_PANEL,    RESIZE_RB,  14,   0, 231,  14, 103, 0x0,                      STR_NULL },                        // VVW_WIDGET_PANEL
    01657   {      WWT_INSET,    RESIZE_RB,  14,   2, 229,  16, 101, 0x0,                      STR_NULL },                        // VVW_WIDGET_VIEWPORT
    01658   {    WWT_PUSHBTN,   RESIZE_RTB,  14,   0, 237, 104, 115, 0x0,                      0x0 /* filled later */ },          // VVW_WIDGET_START_STOP_VEH
    01659   { WWT_PUSHIMGBTN,    RESIZE_LR,  14, 232, 249,  14,  31, SPR_CENTRE_VIEW_VEHICLE,  0x0 /* filled later */ },          // VVW_WIDGET_CENTER_MAIN_VIEH
    01660   { WWT_PUSHIMGBTN,    RESIZE_LR,  14, 232, 249,  32,  49, 0x0 /* filled later */,   0x0 /* filled later */ },          // VVW_WIDGET_GOTO_DEPOT
    01661   { WWT_PUSHIMGBTN,    RESIZE_LR,  14, 232, 249,  50,  67, SPR_REFIT_VEHICLE,        0x0 /* filled later */ },          // VVW_WIDGET_REFIT_VEH
    01662   { WWT_PUSHIMGBTN,    RESIZE_LR,  14, 232, 249,  68,  85, SPR_SHOW_ORDERS,          0x0 /* filled later */ },          // VVW_WIDGET_SHOW_ORDERS
    01663   { WWT_PUSHIMGBTN,    RESIZE_LR,  14, 232, 249,  86, 103, SPR_SHOW_VEHICLE_DETAILS, 0x0 /* filled later */ },          // VVW_WIDGET_SHOW_DETAILS
    01664   { WWT_PUSHIMGBTN,    RESIZE_LR,  14, 232, 249,  32,  49, 0x0 /* filled later */,   0x0 /* filled later */ },          // VVW_WIDGET_CLONE_VEH
    01665   {      WWT_PANEL,   RESIZE_LRB,  14, 232, 249, 104, 103, 0x0,                      STR_NULL },                        // VVW_WIDGET_EMPTY_BOTTOM_RIGHT
    01666   {  WWT_RESIZEBOX,  RESIZE_LRTB,  14, 238, 249, 104, 115, 0x0,                      STR_NULL },                        // VVW_WIDGET_RESIZE
    01667   { WWT_PUSHIMGBTN,    RESIZE_LR,  14, 232, 249,  50,  67, SPR_FORCE_VEHICLE_TURN,   STR_9020_FORCE_VEHICLE_TO_TURN_AROUND }, // VVW_WIDGET_TURN_AROUND
    01668   { WWT_PUSHIMGBTN,    RESIZE_LR,  14, 232, 249,  50,  67, SPR_IGNORE_SIGNALS,       STR_884A_FORCE_TRAIN_TO_PROCEED },       // VVW_WIDGET_FORCE_PROCEED
    01669 {   WIDGETS_END},
    01670 };
    01671
    01672
    01673 static void VehicleViewWndProc(Window *w, WindowEvent *e);
    01674
    01676 static const WindowDesc _vehicle_view_desc = {
    01677   WDP_AUTO, WDP_AUTO, 250, 116, 250, 116,
    01678   WC_VEHICLE_VIEW, WC_NONE,
    01679   WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
    01680   _vehicle_view_widgets,
    01681   VehicleViewWndProc
    01682 };
    01683
    01687 static const WindowDesc _train_view_desc = {
    01688   WDP_AUTO, WDP_AUTO, 250, 134, 250, 134,
    01689   WC_VEHICLE_VIEW, WC_NONE,
    01690   WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
    01691   _vehicle_view_widgets,
    01692   VehicleViewWndProc
    01693 };
    01694
    01695
    01696 /* Just to make sure, nobody has changed the vehicle type constants, as we are
    01697    using them for array indexing in a number of places here. */
    01698 assert_compile(VEH_TRAIN == 0);
    01699 assert_compile(VEH_ROAD == 1);
    01700 assert_compile(VEH_SHIP == 2);
    01701 assert_compile(VEH_AIRCRAFT == 3);
    01702
    01704 static const ZoomLevel _vehicle_view_zoom_levels[] = {
    01705   ZOOM_LVL_TRAIN,
    01706   ZOOM_LVL_ROADVEH,
    01707   ZOOM_LVL_SHIP,
    01708   ZOOM_LVL_AIRCRAFT,
    01709 };
    01710
    01711 /* Constants for geometry of vehicle view viewport */
    01712 static const int VV_VIEWPORT_X = 3;
    01713 static const int VV_VIEWPORT_Y = 17;
    01714 static const int VV_INITIAL_VIEWPORT_WIDTH = 226;
    01715 static const int VV_INITIAL_VIEWPORT_HEIGHT = 84;
    01716 static const int VV_INITIAL_VIEWPORT_HEIGHT_TRAIN = 102;
    01717
    01719 void ShowVehicleViewWindow(const Vehicle *v)
    01720 {
    01721   Window *w = AllocateWindowDescFront((v->type == VEH_TRAIN) ? &_train_view_desc : &_vehicle_view_desc, v->index);
    01722
    01723   if (w != NULL) {
    01724     w->caption_color = v->owner;
    01725     AssignWindowViewport(w, VV_VIEWPORT_X, VV_VIEWPORT_Y, VV_INITIAL_VIEWPORT_WIDTH,
    01726                          (v->type == VEH_TRAIN) ? VV_INITIAL_VIEWPORT_HEIGHT_TRAIN : VV_INITIAL_VIEWPORT_HEIGHT,
    01727                          w->window_number | (1 << 31), _vehicle_view_zoom_levels[v->type]);
    01728   }
    01729 }
    01730
    01732 static void CreateVehicleViewWindow(Window *w)
    01733 {
    01734   const Vehicle *v = GetVehicle(w->window_number);
    01735
    01736   /*
    01737    * fill in data and tooltip codes for the widgets and
    01738    * move some of the buttons for trains
    01739    */
    01740   switch (v->type) {
    01741     case VEH_TRAIN:
    01742       w->widget[VVW_WIDGET_CAPTION].data = STR_882E;
    01743
    01744       w->widget[VVW_WIDGET_START_STOP_VEH].tooltips = STR_8846_CURRENT_TRAIN_ACTION_CLICK;
    01745
    01746       w->widget[VVW_WIDGET_CENTER_MAIN_VIEH].tooltips = STR_8848_CENTER_MAIN_VIEW_ON_TRAIN;
    01747
    01748       w->widget[VVW_WIDGET_GOTO_DEPOT].data = SPR_SEND_TRAIN_TODEPOT;
    01749       w->widget[VVW_WIDGET_GOTO_DEPOT].tooltips = STR_8849_SEND_TRAIN_TO_DEPOT;
    01750
    01751       w->widget[VVW_WIDGET_REFIT_VEH].tooltips = STR_RAIL_REFIT_VEHICLE_TO_CARRY;
    01752
    01753       w->widget[VVW_WIDGET_SHOW_ORDERS].tooltips = STR_8847_SHOW_TRAIN_S_ORDERS;
    01754
    01755       w->widget[VVW_WIDGET_SHOW_DETAILS].tooltips = STR_884C_SHOW_TRAIN_DETAILS;
    01756
    01757       w->widget[VVW_WIDGET_CLONE_VEH].data = SPR_CLONE_TRAIN;
    01758       w->widget[VVW_WIDGET_CLONE_VEH].tooltips = STR_CLONE_TRAIN_INFO;
    01759
    01760       w->widget[VVW_WIDGET_TURN_AROUND].tooltips = STR_884B_REVERSE_DIRECTION_OF_TRAIN;
    01761
    01762
    01763       /* due to more buttons we must modify the layout a bit for trains */
    01764       w->widget[VVW_WIDGET_PANEL].bottom = 121;
    01765       w->widget[VVW_WIDGET_VIEWPORT].bottom = 119;
    01766
    01767       w->widget[VVW_WIDGET_START_STOP_VEH].top = 122;
    01768       w->widget[VVW_WIDGET_START_STOP_VEH].bottom = 133;
    01769
    01770       w->widget[VVW_WIDGET_REFIT_VEH].top = 68;
    01771       w->widget[VVW_WIDGET_REFIT_VEH].bottom = 85;
    01772
    01773       w->widget[VVW_WIDGET_SHOW_ORDERS].top = 86;
    01774       w->widget[VVW_WIDGET_SHOW_ORDERS].bottom = 103;
    01775
    01776       w->widget[VVW_WIDGET_SHOW_DETAILS].top = 104;
    01777       w->widget[VVW_WIDGET_SHOW_DETAILS].bottom = 121;
    01778
    01779       w->widget[VVW_WIDGET_EMPTY_BOTTOM_RIGHT].top = 122;
    01780       w->widget[VVW_WIDGET_EMPTY_BOTTOM_RIGHT].bottom = 121;
    01781
    01782       w->widget[VVW_WIDGET_RESIZE].top = 122;
    01783       w->widget[VVW_WIDGET_RESIZE].bottom = 133;
    01784
    01785       w->widget[VVW_WIDGET_TURN_AROUND].top = 68;
    01786       w->widget[VVW_WIDGET_TURN_AROUND].bottom = 85;
    01787       break;
    01788
    01789     case VEH_ROAD:
    01790       w->widget[VVW_WIDGET_CAPTION].data = STR_9002;
    01791
    01792       w->widget[VVW_WIDGET_START_STOP_VEH].tooltips = STR_901C_CURRENT_VEHICLE_ACTION;
    01793
    01794       w->widget[VVW_WIDGET_CENTER_MAIN_VIEH].tooltips = STR_901E_CENTER_MAIN_VIEW_ON_VEHICLE;
    01795
    01796       w->widget[VVW_WIDGET_GOTO_DEPOT].data = SPR_SEND_ROADVEH_TODEPOT;
    01797       w->widget[VVW_WIDGET_GOTO_DEPOT].tooltips = STR_901F_SEND_VEHICLE_TO_DEPOT;
    01798
    01799       w->widget[VVW_WIDGET_REFIT_VEH].tooltips = STR_REFIT_ROAD_VEHICLE_TO_CARRY;
    01800
    01801       w->widget[VVW_WIDGET_SHOW_ORDERS].tooltips = STR_901D_SHOW_VEHICLE_S_ORDERS;
    01802
    01803       w->widget[VVW_WIDGET_SHOW_DETAILS].tooltips = STR_9021_SHOW_ROAD_VEHICLE_DETAILS;
    01804
    01805       w->widget[VVW_WIDGET_CLONE_VEH].data = SPR_CLONE_ROADVEH;
    01806       w->widget[VVW_WIDGET_CLONE_VEH].tooltips = STR_CLONE_ROAD_VEHICLE_INFO;
    01807
    01808       w->SetWidgetHiddenState(VVW_WIDGET_FORCE_PROCEED, true);
    01809       break;
    01810
    01811     case VEH_SHIP:
    01812       w->widget[VVW_WIDGET_CAPTION].data = STR_980F;
    01813
    01814       w->widget[VVW_WIDGET_START_STOP_VEH].tooltips = STR_9827_CURRENT_SHIP_ACTION_CLICK;
    01815
    01816       w->widget[VVW_WIDGET_CENTER_MAIN_VIEH].tooltips = STR_9829_CENTER_MAIN_VIEW_ON_SHIP;
    01817
    01818       w->widget[VVW_WIDGET_GOTO_DEPOT].data = SPR_SEND_SHIP_TODEPOT;
    01819       w->widget[VVW_WIDGET_GOTO_DEPOT].tooltips = STR_982A_SEND_SHIP_TO_DEPOT;
    01820
    01821       w->widget[VVW_WIDGET_REFIT_VEH].tooltips = STR_983A_REFIT_CARGO_SHIP_TO_CARRY;
    01822
    01823       w->widget[VVW_WIDGET_SHOW_ORDERS].tooltips = STR_9828_SHOW_SHIP_S_ORDERS;
    01824
    01825       w->widget[VVW_WIDGET_SHOW_DETAILS].tooltips = STR_982B_SHOW_SHIP_DETAILS;
    01826
    01827       w->widget[VVW_WIDGET_CLONE_VEH].data = SPR_CLONE_SHIP;
    01828       w->widget[VVW_WIDGET_CLONE_VEH].tooltips = STR_CLONE_SHIP_INFO;
    01829
    01830       w->SetWidgetsHiddenState(true,
    01831                                   VVW_WIDGET_TURN_AROUND,
    01832                                   VVW_WIDGET_FORCE_PROCEED,
    01833                                   WIDGET_LIST_END);
    01834       break;
    01835
    01836     case VEH_AIRCRAFT:
    01837       w->widget[VVW_WIDGET_CAPTION].data = STR_A00A;
    01838
    01839       w->widget[VVW_WIDGET_START_STOP_VEH].tooltips = STR_A027_CURRENT_AIRCRAFT_ACTION;
    01840
    01841       w->widget[VVW_WIDGET_CENTER_MAIN_VIEH].tooltips = STR_A029_CENTER_MAIN_VIEW_ON_AIRCRAFT;
    01842
    01843       w->widget[VVW_WIDGET_GOTO_DEPOT].data = SPR_SEND_AIRCRAFT_TODEPOT;
    01844       w->widget[VVW_WIDGET_GOTO_DEPOT].tooltips = STR_A02A_SEND_AIRCRAFT_TO_HANGAR;
    01845
    01846       w->widget[VVW_WIDGET_REFIT_VEH].tooltips = STR_A03B_REFIT_AIRCRAFT_TO_CARRY;
    01847
    01848       w->widget[VVW_WIDGET_SHOW_ORDERS].tooltips = STR_A028_SHOW_AIRCRAFT_S_ORDERS;
    01849
    01850       w->widget[VVW_WIDGET_SHOW_DETAILS].tooltips = STR_A02B_SHOW_AIRCRAFT_DETAILS;
    01851
    01852       w->widget[VVW_WIDGET_CLONE_VEH].data = SPR_CLONE_AIRCRAFT;
    01853       w->widget[VVW_WIDGET_CLONE_VEH].tooltips = STR_CLONE_AIRCRAFT_INFO;
    01854
    01855       w->SetWidgetsHiddenState(true,
    01856                                   VVW_WIDGET_TURN_AROUND,
    01857                                   VVW_WIDGET_FORCE_PROCEED,
    01858                                   WIDGET_LIST_END);
    01859       break;
    01860
    01861       default: NOT_REACHED();
    01862   }
    01863 }
    01864
    01866 static bool IsVehicleRefitable(const Vehicle *v)
    01867 {
    01868   /* Why is this so different for different vehicles?
    01869    * Does maybe work one solution for all?
    01870    */
    01871   switch (v->type) {
    01872     case VEH_TRAIN:    return false;
    01873     case VEH_ROAD:     return EngInfo(v->engine_type)->refit_mask != 0 && v->IsStoppedInDepot();
    01874     case VEH_SHIP:     return ShipVehInfo(v->engine_type)->refittable && v->IsStoppedInDepot();
    01875     case VEH_AIRCRAFT: return v->IsStoppedInDepot();
    01876     default: NOT_REACHED();
    01877   }
    01878 }
    01879
    01881 static const StringID _heading_for_depot_strings[] = {
    01882   STR_HEADING_FOR_TRAIN_DEPOT,
    01883   STR_HEADING_FOR_ROAD_DEPOT,
    01884   STR_HEADING_FOR_SHIP_DEPOT,
    01885   STR_HEADING_FOR_HANGAR,
    01886 };
    01887
    01889 static const StringID _heading_for_depot_service_strings[] = {
    01890   STR_HEADING_FOR_TRAIN_DEPOT_SERVICE,
    01891   STR_HEADING_FOR_ROAD_DEPOT_SERVICE,
    01892   STR_HEADING_FOR_SHIP_DEPOT_SERVICE,
    01893   STR_HEADING_FOR_HANGAR_SERVICE,
    01894 };
    01895
    01897 static void DrawVehicleViewWindow(Window *w)
    01898 {
    01899   const Vehicle *v = GetVehicle(w->window_number);
    01900   StringID str;
    01901   bool is_localplayer = v->owner == _local_player;
    01902   bool refitable_and_stopped_in_depot = IsVehicleRefitable(v);
    01903
    01904   w->SetWidgetDisabledState(VVW_WIDGET_GOTO_DEPOT, !is_localplayer);
    01905   w->SetWidgetDisabledState(VVW_WIDGET_REFIT_VEH,
    01906                                !refitable_and_stopped_in_depot || !is_localplayer);
    01907   w->SetWidgetDisabledState(VVW_WIDGET_CLONE_VEH, !is_localplayer);
    01908
    01909   if (v->type == VEH_TRAIN) {
    01910     w->SetWidgetDisabledState(VVW_WIDGET_FORCE_PROCEED, !is_localplayer);
    01911     w->SetWidgetDisabledState(VVW_WIDGET_TURN_AROUND, !is_localplayer);
    01912
    01913     /* Cargo refit button is disabled, until we know we can enable it below. */
    01914
    01915     if (is_localplayer) {
    01916       /* See if any vehicle can be refitted */
    01917       for (const Vehicle *u = v; u != NULL; u = u->Next()) {
    01918         if (EngInfo(u->engine_type)->refit_mask != 0 ||
    01919             (RailVehInfo(v->engine_type)->railveh_type != RAILVEH_WAGON && v->cargo_cap != 0)) {
    01920           w->EnableWidget(VVW_WIDGET_REFIT_VEH);
    01921           /* We have a refittable carriage, bail out */
    01922           break;
    01923         }
    01924       }
    01925     }
    01926   }
    01927
    01928   /* draw widgets & caption */
    01929   SetDParam(0, v->index);
    01930   DrawWindowWidgets(w);
    01931
    01932   if (v->vehstatus & VS_CRASHED) {
    01933     str = STR_8863_CRASHED;
    01934   } else if (v->type != VEH_AIRCRAFT && v->breakdown_ctr == 1) { // check for aircraft necessary?
    01935     str = STR_885C_BROKEN_DOWN;
    01936   } else if (v->vehstatus & VS_STOPPED) {
    01937     if (v->type == VEH_TRAIN) {
    01938       if (v->cur_speed == 0) {
    01939         if (v->u.rail.cached_power == 0) {
    01940           str = STR_TRAIN_NO_POWER;
    01941         } else {
    01942           str = STR_8861_STOPPED;
    01943         }
    01944       } else {
    01945         SetDParam(0, v->GetDisplaySpeed());
    01946         str = STR_TRAIN_STOPPING + _patches.vehicle_speed;
    01947       }
    01948     } else { // no train
    01949       str = STR_8861_STOPPED;
    01950     }
    01951   } else { // vehicle is in a "normal" state, show current order
    01952     switch (v->current_order.type) {
    01953       case OT_GOTO_STATION: {
    01954         SetDParam(0, v->current_order.dest);
    01955         SetDParam(1, v->GetDisplaySpeed());
    01956         str = STR_HEADING_FOR_STATION + _patches.vehicle_speed;
    01957       } break;
    01958
    01959       case OT_GOTO_DEPOT: {
    01960         if (v->type == VEH_AIRCRAFT) {
    01961           /* Aircrafts always go to a station, even if you say depot */
    01962           SetDParam(0, v->current_order.dest);
    01963           SetDParam(1, v->GetDisplaySpeed());
    01964         } else {
    01965           Depot *depot = GetDepot(v->current_order.dest);
    01966           SetDParam(0, depot->town_index);
    01967           SetDParam(1, v->GetDisplaySpeed());
    01968         }
    01969         if (HasBit(v->current_order.flags, OF_HALT_IN_DEPOT) && !HasBit(v->current_order.flags, OF_PART_OF_ORDERS)) {
    01970           str = _heading_for_depot_strings[v->type] + _patches.vehicle_speed;
    01971         } else {
    01972           str = _heading_for_depot_service_strings[v->type] + _patches.vehicle_speed;
    01973         }
    01974       } break;
    01975
    01976       case OT_LOADING:
    01977         str = STR_882F_LOADING_UNLOADING;
    01978         break;
    01979
    01980       case OT_GOTO_WAYPOINT: {
    01981         assert(v->type == VEH_TRAIN);
    01982         SetDParam(0, v->current_order.dest);
    01983         str = STR_HEADING_FOR_WAYPOINT + _patches.vehicle_speed;
    01984         SetDParam(1, v->GetDisplaySpeed());
    01985         break;
    01986       }
    01987
    01988       case OT_LEAVESTATION:
    01989         if (v->type != VEH_AIRCRAFT) {
    01990           str = STR_882F_LOADING_UNLOADING;
    01991           break;
    01992         }
    01993         /* fall-through if aircraft. Does this even happen? */
    01994
    01995       default:
    01996         if (v->num_orders == 0) {
    01997           str = STR_NO_ORDERS + _patches.vehicle_speed;
    01998           SetDParam(0, v->GetDisplaySpeed());
    01999         } else {
    02000           str = STR_EMPTY;
    02001         }
    02002         break;
    02003     }
    02004   }
    02005
    02006   /* draw the flag plus orders */
    02007   DrawSprite(v->vehstatus & VS_STOPPED ? SPR_FLAG_VEH_STOPPED : SPR_FLAG_VEH_RUNNING, PAL_NONE, 2, w->widget[VVW_WIDGET_START_STOP_VEH].top + 1);
    02008   DrawStringCenteredTruncated(w->widget[VVW_WIDGET_START_STOP_VEH].left + 8, w->widget[VVW_WIDGET_START_STOP_VEH].right, w->widget[VVW_WIDGET_START_STOP_VEH].top + 1, str, TC_FROMSTRING);
    02009   DrawWindowViewport(w);
    02010 }
    02011
    02013 enum VehicleCommandTranslation {
    02014   VCT_CMD_START_STOP = 0,
    02015   VCT_CMD_GOTO_DEPOT,
    02016   VCT_CMD_CLONE_VEH,
    02017   VCT_CMD_TURN_AROUND,
    02018 };
    02019
    02021 static const uint32 _vehicle_command_translation_table[][4] = {
    02022   { // VCT_CMD_START_STOP
    02023     CMD_START_STOP_TRAIN | CMD_MSG(STR_883B_CAN_T_STOP_START_TRAIN),
    02024     CMD_START_STOP_ROADVEH | CMD_MSG(STR_9015_CAN_T_STOP_START_ROAD_VEHICLE),
    02025     CMD_START_STOP_SHIP | CMD_MSG(STR_9818_CAN_T_STOP_START_SHIP),
    02026     CMD_START_STOP_AIRCRAFT | CMD_MSG(STR_A016_CAN_T_STOP_START_AIRCRAFT)
    02027   },
    02028   { // VCT_CMD_GOTO_DEPOT
    02029     /* TrainGotoDepot has a nice randomizer in the pathfinder, which causes desyncs... */
    02030     CMD_SEND_TRAIN_TO_DEPOT | CMD_NO_TEST_IF_IN_NETWORK | CMD_MSG(STR_8830_CAN_T_SEND_TRAIN_TO_DEPOT),
    02031     CMD_SEND_ROADVEH_TO_DEPOT | CMD_MSG(STR_9018_CAN_T_SEND_VEHICLE_TO_DEPOT),
    02032     CMD_SEND_SHIP_TO_DEPOT | CMD_MSG(STR_9819_CAN_T_SEND_SHIP_TO_DEPOT),
    02033     CMD_SEND_AIRCRAFT_TO_HANGAR | CMD_MSG(STR_A012_CAN_T_SEND_AIRCRAFT_TO)
    02034   },
    02035   { // VCT_CMD_CLONE_VEH
    02036     CMD_CLONE_VEHICLE | CMD_MSG(STR_882B_CAN_T_BUILD_RAILROAD_VEHICLE),
    02037     CMD_CLONE_VEHICLE | CMD_MSG(STR_9009_CAN_T_BUILD_ROAD_VEHICLE),
    02038     CMD_CLONE_VEHICLE | CMD_MSG(STR_980D_CAN_T_BUILD_SHIP),
    02039     CMD_CLONE_VEHICLE | CMD_MSG(STR_A008_CAN_T_BUILD_AIRCRAFT)
    02040   },
    02041   { // VCT_CMD_TURN_AROUND
    02042     CMD_REVERSE_TRAIN_DIRECTION | CMD_MSG(STR_8869_CAN_T_REVERSE_DIRECTION),
    02043     CMD_TURN_ROADVEH | CMD_MSG(STR_9033_CAN_T_MAKE_VEHICLE_TURN),
    02044     0xffffffff, // invalid for ships
    02045     0xffffffff  // invalid for aircrafts
    02046   },
    02047 };
    02048
    02050 static void VehicleViewWndProc(Window *w, WindowEvent *e)
    02051 {
    02052   switch (e->event) {
    02053     case WE_CREATE:
    02054       CreateVehicleViewWindow(w);
    02055       break;
    02056
    02057     case WE_PAINT:
    02058       DrawVehicleViewWindow(w);
    02059       break;
    02060
    02061     case WE_CLICK: {
    02062       const Vehicle *v = GetVehicle(w->window_number);
    02063
    02064       switch (e->we.click.widget) {
    02065         case VVW_WIDGET_START_STOP_VEH: /* start stop */
    02066           DoCommandP(v->tile, v->index, 0, NULL,
    02067                      _vehicle_command_translation_table[VCT_CMD_START_STOP][v->type]);
    02068           break;
    02069         case VVW_WIDGET_CENTER_MAIN_VIEH: {/* center main view */
    02070           const Window *mainwindow = FindWindowById(WC_MAIN_WINDOW, 0);
    02071           /* code to allow the main window to 'follow' the vehicle if the ctrl key is pressed */
    02072           if (_ctrl_pressed && mainwindow->viewport->zoom == ZOOM_LVL_NORMAL) {
    02073             WP(mainwindow, vp_d).follow_vehicle = v->index;
    02074           } else {
    02075             ScrollMainWindowTo(v->x_pos, v->y_pos);
    02076           }
    02077         } break;
    02078
    02079         case VVW_WIDGET_GOTO_DEPOT: /* goto hangar */
    02080           DoCommandP(v->tile, v->index, _ctrl_pressed ? DEPOT_SERVICE : 0, NULL,
    02081             _vehicle_command_translation_table[VCT_CMD_GOTO_DEPOT][v->type]);
    02082           break;
    02083         case VVW_WIDGET_REFIT_VEH: /* refit */
    02084           ShowVehicleRefitWindow(v, INVALID_VEH_ORDER_ID);
    02085           break;
    02086         case VVW_WIDGET_SHOW_ORDERS: /* show orders */
    02087           ShowOrdersWindow(v);
    02088           break;
    02089         case VVW_WIDGET_SHOW_DETAILS: /* show details */
    02090           ShowVehicleDetailsWindow(v);
    02091           break;
    02092         case VVW_WIDGET_CLONE_VEH: /* clone vehicle */
    02093           DoCommandP(v->tile, v->index, _ctrl_pressed ? 1 : 0, CcCloneVehicle,
    02094                      _vehicle_command_translation_table[VCT_CMD_CLONE_VEH][v->type]);
    02095           break;
    02096         case VVW_WIDGET_TURN_AROUND: /* turn around */
    02097           assert(v->type == VEH_TRAIN || v->type == VEH_ROAD);
    02098           DoCommandP(v->tile, v->index, 0, NULL,
    02099                      _vehicle_command_translation_table[VCT_CMD_TURN_AROUND][v->type]);
    02100           break;
    02101         case VVW_WIDGET_FORCE_PROCEED: /* force proceed */
    02102           assert(v->type == VEH_TRAIN);
    02103           DoCommandP(v->tile, v->index, 0, NULL, CMD_FORCE_TRAIN_PROCEED | CMD_MSG(STR_8862_CAN_T_MAKE_TRAIN_PASS_SIGNAL));
    02104           break;
    02105       }
    02106     } break;
    02107
    02108     case WE_RESIZE:
    02109       w->viewport->width          += e->we.sizing.diff.x;
    02110       w->viewport->height         += e->we.sizing.diff.y;
    02111       w->viewport->virtual_width  += e->we.sizing.diff.x;
    02112       w->viewport->virtual_height += e->we.sizing.diff.y;
    02113       break;
    02114
    02115     case WE_DESTROY:
    02116       DeleteWindowById(WC_VEHICLE_ORDERS, w->window_number);
    02117       DeleteWindowById(WC_VEHICLE_REFIT, w->window_number);
    02118       DeleteWindowById(WC_VEHICLE_DETAILS, w->window_number);
    02119       DeleteWindowById(WC_VEHICLE_TIMETABLE, w->window_number);
    02120       break;
    02121
    02122     case WE_MOUSELOOP: {
    02123       const Vehicle *v = GetVehicle(w->window_number);
    02124       bool veh_stopped = v->IsStoppedInDepot();
    02125
    02126       /* Widget VVW_WIDGET_GOTO_DEPOT must be hidden if the vehicle is already
    02127        * stopped in depot.
    02128        * Widget VVW_WIDGET_CLONE_VEH should then be shown, since cloning is
    02129        * allowed only while in depot and stopped.
    02130        * This sytem allows to have two buttons, on top of each other.
    02131        * The same system applies to widget VVW_WIDGET_REFIT_VEH and VVW_WIDGET_TURN_AROUND.*/
    02132       if (veh_stopped != w->IsWidgetHidden(VVW_WIDGET_GOTO_DEPOT) || veh_stopped == w->IsWidgetHidden(VVW_WIDGET_CLONE_VEH)) {
    02133         w->SetWidgetHiddenState( VVW_WIDGET_GOTO_DEPOT, veh_stopped);  // send to depot
    02134         w->SetWidgetHiddenState(VVW_WIDGET_CLONE_VEH, !veh_stopped); // clone
    02135         if (v->type == VEH_ROAD || v->type == VEH_TRAIN) {
    02136           w->SetWidgetHiddenState( VVW_WIDGET_REFIT_VEH, !veh_stopped); // refit
    02137           w->SetWidgetHiddenState(VVW_WIDGET_TURN_AROUND, veh_stopped);  // force turn around
    02138         }
    02139         SetWindowDirty(w);
    02140       }
    02141     } break;
    02142   }
    02143 }
    02144
    02145 void DrawVehicleImage(const Vehicle *v, int x, int y, VehicleID selection, int count, int skip)
    02146 {
    02147   switch (v->type) {
    02148     case VEH_TRAIN:    DrawTrainImage(v, x, y, selection, count, skip); break;
    02149     case VEH_ROAD:     DrawRoadVehImage(v, x, y, selection, count);     break;
    02150     case VEH_SHIP:     DrawShipImage(v, x, y, selection);               break;
    02151     case VEH_AIRCRAFT: DrawAircraftImage(v, x, y, selection);           break;
    02152     default: NOT_REACHED();
    02153   }
    02154 }