00001
00002
00005 #include "stdafx.h"
00006 #include "openttd.h"
00007 #include "debug.h"
00008 #include "gui.h"
00009 #include "command_func.h"
00010 #include "variables.h"
00011 #include "vehicle_gui.h"
00012 #include "newgrf_engine.h"
00013 #include "group.h"
00014 #include "rail.h"
00015 #include "strings_func.h"
00016 #include "window_func.h"
00017 #include "vehicle_func.h"
00018 #include "autoreplace_func.h"
00019 #include "gfx_func.h"
00020 #include "company_func.h"
00021 #include "widgets/dropdown_type.h"
00022 #include "widgets/dropdown_func.h"
00023 #include "engine_func.h"
00024 #include "engine_base.h"
00025 #include "window_gui.h"
00026 #include "engine_gui.h"
00027
00028 #include "table/sprites.h"
00029 #include "table/strings.h"
00030
00031 void DrawEngineList(VehicleType type, int x, int r, int y, const GUIEngineList *eng_list, uint16 min, uint16 max, EngineID selected_id, int count_location, GroupID selected_group);
00032
00033 enum ReplaceVehicleWindowWidgets {
00034 RVW_WIDGET_LEFT_MATRIX = 3,
00035 RVW_WIDGET_LEFT_SCROLLBAR,
00036 RVW_WIDGET_RIGHT_MATRIX,
00037 RVW_WIDGET_RIGHT_SCROLLBAR,
00038 RVW_WIDGET_LEFT_DETAILS,
00039 RVW_WIDGET_RIGHT_DETAILS,
00040
00041
00042 RVW_WIDGET_START_REPLACE,
00043 RVW_WIDGET_INFO_TAB,
00044 RVW_WIDGET_STOP_REPLACE,
00045 RVW_WIDGET_RESIZE,
00046
00047
00048 RVW_WIDGET_TRAIN_ENGINEWAGON_TOGGLE,
00049 RVW_WIDGET_TRAIN_FLUFF_LEFT,
00050 RVW_WIDGET_TRAIN_RAILTYPE_DROPDOWN,
00051 RVW_WIDGET_TRAIN_FLUFF_RIGHT,
00052 RVW_WIDGET_TRAIN_WAGONREMOVE_TOGGLE,
00053 };
00054
00055 static int CDECL EngineNumberSorter(const void *a, const void *b)
00056 {
00057 const EngineID va = *(const EngineID*)a;
00058 const EngineID vb = *(const EngineID*)b;
00059 int r = ListPositionOfEngine(va) - ListPositionOfEngine(vb);
00060
00061 return r;
00062 }
00063
00072 void InvalidateAutoreplaceWindow(EngineID e, GroupID id_g)
00073 {
00074 Company *c = GetCompany(_local_company);
00075 uint num_engines = GetGroupNumEngines(_local_company, id_g, e);
00076
00077 if (num_engines == 0 || c->num_engines[e] == 0) {
00078
00079
00080
00081 InvalidateWindowData(WC_REPLACE_VEHICLE, GetEngine(e)->type, true);
00082 }
00083 }
00084
00088 void AddRemoveEngineFromAutoreplaceAndBuildWindows(VehicleType type)
00089 {
00090 InvalidateWindowData(WC_REPLACE_VEHICLE, type, false);
00091 InvalidateWindowClassesData(WC_BUILD_VEHICLE);
00092 }
00093
00097 class ReplaceVehicleWindow : public Window {
00098 byte sel_index[2];
00099 EngineID sel_engine[2];
00100 uint16 count[2];
00101 bool wagon_btnstate;
00102 GUIEngineList list[2];
00103 bool update_left;
00104 bool update_right;
00105 bool init_lists;
00106 GroupID sel_group;
00107 static RailType sel_railtype;
00108
00115 bool GenerateReplaceRailList(EngineID e, bool draw_left, bool show_engines)
00116 {
00117 const RailVehicleInfo *rvi = RailVehInfo(e);
00118
00119
00120 if ((rvi->railveh_type == RAILVEH_WAGON) == show_engines) return false;
00121
00122 if (draw_left && show_engines) {
00123
00124 if (rvi->railtype != this->sel_railtype) return false;
00125 }
00126 return true;
00127 }
00128
00129
00134 void GenerateReplaceVehList(Window *w, bool draw_left)
00135 {
00136 EngineID selected_engine = INVALID_ENGINE;
00137 VehicleType type = (VehicleType)this->window_number;
00138 byte i = draw_left ? 0 : 1;
00139
00140 GUIEngineList *list = &this->list[i];
00141 list->Clear();
00142
00143 const Engine *e;
00144 FOR_ALL_ENGINES_OF_TYPE(e, type) {
00145 EngineID eid = e->index;
00146 if (type == VEH_TRAIN && !GenerateReplaceRailList(eid, draw_left, this->wagon_btnstate)) continue;
00147
00148 if (draw_left) {
00149 const GroupID selected_group = this->sel_group;
00150 const uint num_engines = GetGroupNumEngines(_local_company, selected_group, eid);
00151
00152
00153 if (num_engines == 0 && EngineReplacementForCompany(GetCompany(_local_company), eid, selected_group) == INVALID_ENGINE) continue;
00154 } else {
00155 if (!CheckAutoreplaceValidity(this->sel_engine[0], eid, _local_company)) continue;
00156 }
00157
00158 *list->Append() = eid;
00159 if (eid == this->sel_engine[i]) selected_engine = eid;
00160 }
00161 this->sel_engine[i] = selected_engine;
00162 EngList_Sort(list, &EngineNumberSorter);
00163 }
00164
00166 void GenerateLists()
00167 {
00168 EngineID e = this->sel_engine[0];
00169
00170 if (this->update_left == true) {
00171
00172 GenerateReplaceVehList(this, true);
00173 SetVScrollCount(this, this->list[0].Length());
00174 if (this->init_lists && this->sel_engine[0] == INVALID_ENGINE && this->list[0].Length() != 0) {
00175 this->sel_engine[0] = this->list[0][0];
00176 }
00177 }
00178
00179 if (this->update_right || e != this->sel_engine[0]) {
00180
00181 if (this->sel_engine[0] == INVALID_ENGINE) {
00182
00183 this->list[1].Clear();
00184 this->sel_engine[1] = INVALID_ENGINE;
00185 } else {
00186 GenerateReplaceVehList(this, false);
00187 SetVScroll2Count(this, this->list[1].Length());
00188 if (this->init_lists && this->sel_engine[1] == INVALID_ENGINE && this->list[1].Length() != 0) {
00189 this->sel_engine[1] = this->list[1][0];
00190 }
00191 }
00192 }
00193
00194 this->update_left = false;
00195 this->update_right = false;
00196 this->init_lists = false;
00197 }
00198
00199 public:
00200 ReplaceVehicleWindow(const WindowDesc *desc, VehicleType vehicletype, GroupID id_g) : Window(desc, vehicletype)
00201 {
00202 this->wagon_btnstate = true;
00203 this->update_left = true;
00204 this->update_right = true;
00205 this->init_lists = true;
00206 this->sel_engine[0] = INVALID_ENGINE;
00207 this->sel_engine[1] = INVALID_ENGINE;
00208
00209 this->resize.step_height = GetVehicleListHeight(vehicletype);
00210 this->vscroll.cap = this->resize.step_height == 14 ? 8 : 4;
00211
00212 Widget *widget = this->widget;
00213 widget[RVW_WIDGET_LEFT_MATRIX].data = widget[RVW_WIDGET_RIGHT_MATRIX].data = (this->vscroll.cap << 8) + 1;
00214
00215 if (vehicletype == VEH_TRAIN) {
00216 this->wagon_btnstate = true;
00217
00218
00219 widget[RVW_WIDGET_RESIZE].top = widget[RVW_WIDGET_TRAIN_ENGINEWAGON_TOGGLE].top;
00220 widget[RVW_WIDGET_RESIZE].bottom = widget[RVW_WIDGET_TRAIN_ENGINEWAGON_TOGGLE].bottom;
00221 widget[RVW_WIDGET_STOP_REPLACE].right = widget[RVW_WIDGET_RESIZE].right;
00222
00223
00224 widget[RVW_WIDGET_LEFT_DETAILS].bottom += 10;
00225 widget[RVW_WIDGET_RIGHT_DETAILS].bottom += 10;
00226 for (int i = RVW_WIDGET_START_REPLACE; i < RVW_WIDGET_RESIZE; i++) {
00227 widget[i].top += 10;
00228 widget[i].bottom += 10;
00229 }
00230 } else {
00231
00232 this->SetWidgetsHiddenState(true,
00233 RVW_WIDGET_TRAIN_ENGINEWAGON_TOGGLE,
00234 RVW_WIDGET_TRAIN_FLUFF_LEFT,
00235 RVW_WIDGET_TRAIN_RAILTYPE_DROPDOWN,
00236 RVW_WIDGET_TRAIN_FLUFF_RIGHT,
00237 RVW_WIDGET_TRAIN_WAGONREMOVE_TOGGLE,
00238 WIDGET_LIST_END);
00239 }
00240
00241 ResizeWindow(this, 0, this->resize.step_height * this->vscroll.cap);
00242
00243
00244 this->resize.width = this->width;
00245 this->resize.height = this->height;
00246
00247 this->caption_color = _local_company;
00248 this->sel_group = id_g;
00249 this->vscroll2.cap = this->vscroll.cap;
00250
00251 this->FindWindowPlacementAndResize(desc);
00252 }
00253
00254 virtual void OnPaint()
00255 {
00256 static const StringID _vehicle_type_names[] = {
00257 STR_019F_TRAIN,
00258 STR_019C_ROAD_VEHICLE,
00259 STR_019E_SHIP,
00260 STR_019D_AIRCRAFT
00261 };
00262
00263 if (this->update_left || this->update_right) this->GenerateLists();
00264
00265 Company *c = GetCompany(_local_company);
00266 EngineID selected_id[2];
00267 const GroupID selected_group = this->sel_group;
00268
00269 selected_id[0] = this->sel_engine[0];
00270 selected_id[1] = this->sel_engine[1];
00271
00272
00273
00274
00275
00276 this->SetWidgetDisabledState(RVW_WIDGET_START_REPLACE,
00277 selected_id[0] == INVALID_ENGINE ||
00278 selected_id[1] == INVALID_ENGINE ||
00279 EngineReplacementForCompany(c, selected_id[1], selected_group) != INVALID_ENGINE ||
00280 EngineReplacementForCompany(c, selected_id[0], selected_group) == selected_id[1]);
00281
00282
00283
00284
00285 this->SetWidgetDisabledState(RVW_WIDGET_STOP_REPLACE,
00286 selected_id[0] == INVALID_ENGINE ||
00287 !EngineHasReplacementForCompany(c, selected_id[0], selected_group));
00288
00289
00290 SetDParam(0, _vehicle_type_names[this->window_number]);
00291
00292 if (this->window_number == VEH_TRAIN) {
00293
00294 SetDParam(1, c->renew_keep_length ? STR_CONFIG_PATCHES_ON : STR_CONFIG_PATCHES_OFF);
00295
00296
00297 SetDParam(2, this->wagon_btnstate ? STR_ENGINES : STR_WAGONS);
00298
00299
00300 this->widget[RVW_WIDGET_TRAIN_FLUFF_LEFT].color = _company_colours[_local_company];
00301 this->widget[RVW_WIDGET_TRAIN_FLUFF_RIGHT].color = _company_colours[_local_company];
00302 }
00303
00304 if (this->window_number == VEH_TRAIN) {
00305
00306 const RailtypeInfo *rti = GetRailTypeInfo(sel_railtype);
00307 this->widget[RVW_WIDGET_TRAIN_RAILTYPE_DROPDOWN].data = rti->strings.replace_text;
00308 }
00309
00310 this->DrawWidgets();
00311
00312
00313 if (selected_id[0] != INVALID_ENGINE) {
00314 if (!EngineHasReplacementForCompany(c, selected_id[0], selected_group)) {
00315 SetDParam(0, STR_NOT_REPLACING);
00316 } else {
00317 SetDParam(0, STR_ENGINE_NAME);
00318 SetDParam(1, EngineReplacementForCompany(c, selected_id[0], selected_group));
00319 }
00320 } else {
00321 SetDParam(0, STR_NOT_REPLACING_VEHICLE_SELECTED);
00322 }
00323
00324 DrawStringTruncated(this->widget[RVW_WIDGET_INFO_TAB].left + 6, this->widget[RVW_WIDGET_INFO_TAB].top + 1, STR_02BD, TC_BLACK, this->GetWidgetWidth(RVW_WIDGET_INFO_TAB) - 12);
00325
00326
00327 for (byte i = 0; i < 2; i++) {
00328 uint widget = (i == 0) ? RVW_WIDGET_LEFT_MATRIX : RVW_WIDGET_RIGHT_MATRIX;
00329 GUIEngineList *list = &this->list[i];
00330 EngineID start = i == 0 ? this->vscroll.pos : this->vscroll2.pos;
00331 EngineID end = min((i == 0 ? this->vscroll.cap : this->vscroll2.cap) + start, list->Length());
00332
00333
00334 DrawEngineList((VehicleType)this->window_number, this->widget[widget].left + 2, this->widget[widget].right, this->widget[widget].top + 1, list, start, end, this->sel_engine[i], i == 0 ? this->widget[RVW_WIDGET_LEFT_MATRIX].right - 2 : 0, selected_group);
00335
00336
00337 if (this->sel_engine[i] != INVALID_ENGINE) {
00338 const Widget *wi = &this->widget[i == 0 ? RVW_WIDGET_LEFT_DETAILS : RVW_WIDGET_RIGHT_DETAILS];
00339 int text_end = DrawVehiclePurchaseInfo(wi->left + 2, wi->top + 1, wi->right - wi->left - 2, this->sel_engine[i]);
00340
00341 if (text_end > wi->bottom) {
00342 this->SetDirty();
00343 ResizeWindowForWidget(this, i == 0 ? RVW_WIDGET_LEFT_DETAILS : RVW_WIDGET_RIGHT_DETAILS, 0, text_end - wi->bottom);
00344 this->SetDirty();
00345 }
00346 }
00347 }
00348 }
00349
00350 virtual void OnClick(Point pt, int widget)
00351 {
00352 switch (widget) {
00353 case RVW_WIDGET_TRAIN_ENGINEWAGON_TOGGLE:
00354 this->wagon_btnstate = !(this->wagon_btnstate);
00355 this->update_left = true;
00356 this->init_lists = true;
00357 this->SetDirty();
00358 break;
00359
00360 case RVW_WIDGET_TRAIN_RAILTYPE_DROPDOWN: {
00361 const Company *c = GetCompany(_local_company);
00362 DropDownList *list = new DropDownList();
00363 for (RailType rt = RAILTYPE_BEGIN; rt != RAILTYPE_END; rt++) {
00364 const RailtypeInfo *rti = GetRailTypeInfo(rt);
00365
00366 list->push_back(new DropDownListStringItem(rti->strings.replace_text, rt, !HasBit(c->avail_railtypes, rt)));
00367 }
00368 ShowDropDownList(this, list, sel_railtype, RVW_WIDGET_TRAIN_RAILTYPE_DROPDOWN);
00369 break;
00370 }
00371
00372 case RVW_WIDGET_TRAIN_WAGONREMOVE_TOGGLE:
00373 DoCommandP(0, 5, GetCompany(_local_company)->renew_keep_length ? 0 : 1, NULL, CMD_SET_AUTOREPLACE);
00374 break;
00375
00376 case RVW_WIDGET_START_REPLACE: {
00377 EngineID veh_from = this->sel_engine[0];
00378 EngineID veh_to = this->sel_engine[1];
00379 DoCommandP(0, 3 + (this->sel_group << 16) , veh_from + (veh_to << 16), NULL, CMD_SET_AUTOREPLACE);
00380 this->SetDirty();
00381 } break;
00382
00383 case RVW_WIDGET_STOP_REPLACE: {
00384 EngineID veh_from = this->sel_engine[0];
00385 DoCommandP(0, 3 + (this->sel_group << 16), veh_from + (INVALID_ENGINE << 16), NULL, CMD_SET_AUTOREPLACE);
00386 this->SetDirty();
00387 } break;
00388
00389 case RVW_WIDGET_LEFT_MATRIX:
00390 case RVW_WIDGET_RIGHT_MATRIX: {
00391 uint i = (pt.y - 14) / this->resize.step_height;
00392 uint16 click_scroll_pos = widget == RVW_WIDGET_LEFT_MATRIX ? this->vscroll.pos : this->vscroll2.pos;
00393 uint16 click_scroll_cap = widget == RVW_WIDGET_LEFT_MATRIX ? this->vscroll.cap : this->vscroll2.cap;
00394 byte click_side = widget == RVW_WIDGET_LEFT_MATRIX ? 0 : 1;
00395 size_t engine_count = this->list[click_side].Length();
00396
00397 if (i < click_scroll_cap) {
00398 i += click_scroll_pos;
00399 EngineID e = engine_count > i ? this->list[click_side][i] : INVALID_ENGINE;
00400 if (e == this->sel_engine[click_side]) break;
00401 this->sel_engine[click_side] = e;
00402 if (click_side == 0) {
00403 this->update_right = true;
00404 this->init_lists = true;
00405 }
00406 this->SetDirty();
00407 }
00408 break;
00409 }
00410 }
00411 }
00412
00413 virtual void OnDropdownSelect(int widget, int index)
00414 {
00415 RailType temp = (RailType)index;
00416 if (temp == sel_railtype) return;
00417 sel_railtype = temp;
00418
00419 this->vscroll.pos = 0;
00420 this->vscroll2.pos = 0;
00421
00422 this->update_left = true;
00423 this->update_right = true;
00424 this->init_lists = true;
00425 this->SetDirty();
00426 }
00427
00428 virtual void OnResize(Point new_size, Point delta) {
00429 this->vscroll.cap += delta.y / (int)this->resize.step_height;
00430 this->vscroll2.cap += delta.y / (int)this->resize.step_height;
00431
00432 Widget *widget = this->widget;
00433
00434 widget[RVW_WIDGET_LEFT_MATRIX].data = widget[RVW_WIDGET_RIGHT_MATRIX].data = (this->vscroll2.cap << 8) + 1;
00435
00436 if (delta.x != 0) {
00437
00438
00439
00440
00441
00442
00443
00444 ResizeButtons(this, RVW_WIDGET_LEFT_DETAILS, RVW_WIDGET_RIGHT_DETAILS);
00445 widget[RVW_WIDGET_RIGHT_MATRIX].left = widget[RVW_WIDGET_RIGHT_DETAILS].left;
00446 widget[RVW_WIDGET_LEFT_SCROLLBAR].right = widget[RVW_WIDGET_LEFT_DETAILS].right;
00447 widget[RVW_WIDGET_LEFT_SCROLLBAR].left = widget[RVW_WIDGET_LEFT_SCROLLBAR].right - 11;
00448 widget[RVW_WIDGET_LEFT_MATRIX].right = widget[RVW_WIDGET_LEFT_SCROLLBAR].left - 1;
00449 }
00450 }
00451
00452 virtual void OnInvalidateData(int data)
00453 {
00454 if (data != 0) {
00455 this->update_left = true;
00456 } else {
00457 this->update_right = true;
00458 }
00459 }
00460 };
00461
00462 static const Widget _replace_vehicle_widgets[] = {
00463 { WWT_CLOSEBOX, RESIZE_NONE, COLOUR_GREY, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
00464 { WWT_CAPTION, RESIZE_RIGHT, COLOUR_GREY, 11, 443, 0, 13, STR_REPLACE_VEHICLES_WHITE, STR_018C_WINDOW_TITLE_DRAG_THIS},
00465 { WWT_STICKYBOX, RESIZE_LR, COLOUR_GREY, 444, 455, 0, 13, STR_NULL, STR_STICKY_BUTTON},
00466
00467 { WWT_MATRIX, RESIZE_BOTTOM, COLOUR_GREY, 0, 215, 14, 13, 0x1, STR_REPLACE_HELP_LEFT_ARRAY},
00468 { WWT_SCROLLBAR, RESIZE_BOTTOM, COLOUR_GREY, 216, 227, 14, 13, STR_NULL, STR_0190_SCROLL_BAR_SCROLLS_LIST},
00469 { WWT_MATRIX, RESIZE_LRB, COLOUR_GREY, 228, 443, 14, 13, 0x1, STR_REPLACE_HELP_RIGHT_ARRAY},
00470 { WWT_SCROLL2BAR, RESIZE_LRB, COLOUR_GREY, 444, 455, 14, 13, STR_NULL, STR_0190_SCROLL_BAR_SCROLLS_LIST},
00471 { WWT_PANEL, RESIZE_TB, COLOUR_GREY, 0, 227, 14, 105, 0x0, STR_NULL},
00472 { WWT_PANEL, RESIZE_RTB, COLOUR_GREY, 228, 455, 14, 105, 0x0, STR_NULL},
00473
00474 { WWT_PUSHTXTBTN, RESIZE_TB, COLOUR_GREY, 0, 138, 106, 117, STR_REPLACE_VEHICLES_START, STR_REPLACE_HELP_START_BUTTON},
00475 { WWT_PANEL, RESIZE_RTB, COLOUR_GREY, 139, 305, 106, 117, 0x0, STR_REPLACE_HELP_REPLACE_INFO_TAB},
00476 { WWT_PUSHTXTBTN, RESIZE_LRTB, COLOUR_GREY, 306, 443, 106, 117, STR_REPLACE_VEHICLES_STOP, STR_REPLACE_HELP_STOP_BUTTON},
00477 { WWT_RESIZEBOX, RESIZE_LRTB, COLOUR_GREY, 444, 455, 106, 117, STR_NULL, STR_RESIZE_BUTTON},
00478
00479 { WWT_PUSHTXTBTN, RESIZE_TB, COLOUR_GREY, 0, 138, 128, 139, STR_REPLACE_ENGINE_WAGON_SELECT, STR_REPLACE_ENGINE_WAGON_SELECT_HELP},
00480 { WWT_PANEL, RESIZE_TB, COLOUR_GREY, 139, 153, 128, 139, 0x0, STR_NULL},
00481 { WWT_DROPDOWN, RESIZE_RTB, COLOUR_GREY, 154, 289, 128, 139, 0x0, STR_REPLACE_HELP_RAILTYPE},
00482 { WWT_PANEL, RESIZE_LRTB, COLOUR_GREY, 290, 305, 128, 139, 0x0, STR_NULL},
00483 { WWT_PUSHTXTBTN, RESIZE_LRTB, COLOUR_GREY, 306, 443, 128, 139, STR_REPLACE_REMOVE_WAGON, STR_REPLACE_REMOVE_WAGON_HELP},
00484 { WIDGETS_END},
00485 };
00486
00487 static const WindowDesc _replace_rail_vehicle_desc = {
00488 WDP_AUTO, WDP_AUTO, 456, 140, 456, 140,
00489 WC_REPLACE_VEHICLE, WC_NONE,
00490 WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
00491 _replace_vehicle_widgets,
00492 };
00493
00494 static const WindowDesc _replace_vehicle_desc = {
00495 WDP_AUTO, WDP_AUTO, 456, 118, 456, 118,
00496 WC_REPLACE_VEHICLE, WC_NONE,
00497 WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
00498 _replace_vehicle_widgets,
00499 };
00500
00501 RailType ReplaceVehicleWindow::sel_railtype = RAILTYPE_RAIL;
00502
00503 void ShowReplaceGroupVehicleWindow(GroupID id_g, VehicleType vehicletype)
00504 {
00505 DeleteWindowById(WC_REPLACE_VEHICLE, vehicletype);
00506 new ReplaceVehicleWindow(vehicletype == VEH_TRAIN ? &_replace_rail_vehicle_desc : &_replace_vehicle_desc, vehicletype, id_g);
00507 }