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