174 if (EngInfo(engine_a)->refit_mask & EngInfo(engine_b)->refit_mask) return true; // both can refit to the same |
131 if (EngInfo(engine_a)->refit_mask & EngInfo(engine_b)->refit_mask) return true; // both can refit to the same |
175 if (CanRefitTo(engine_a, b) || CanRefitTo(engine_b, a)) return true; // one can refit to what the other one carries |
132 if (CanRefitTo(engine_a, b) || CanRefitTo(engine_b, a)) return true; // one can refit to what the other one carries |
176 return false; |
133 return false; |
177 } |
134 } |
178 |
135 |
179 /** Generate a list |
136 /** |
180 * @param w Window, that contains the list |
137 * Window for the autoreplacing of vehicles. |
181 * @param draw_left true if generating the left list, otherwise false |
|
182 */ |
138 */ |
183 static void GenerateReplaceVehList(Window *w, bool draw_left) |
139 class ReplaceVehicleWindow : public Window { |
184 { |
140 byte sel_index[2]; |
185 EngineID selected_engine = INVALID_ENGINE; |
141 EngineID sel_engine[2]; |
186 VehicleType type = (VehicleType)w->window_number; |
142 uint16 count[2]; |
187 byte i = draw_left ? 0 : 1; |
143 bool wagon_btnstate; ///< true means engine is selected |
188 |
144 EngineList list[2]; |
189 EngineList *list = &WP(w, replaceveh_d).list[i]; |
145 bool update_left; |
190 list->clear(); |
146 bool update_right; |
191 |
147 bool init_lists; |
192 const Engine *e; |
148 GroupID sel_group; |
193 FOR_ALL_ENGINES_OF_TYPE(e, type) { |
149 static RailType sel_railtype; |
194 EngineID eid = e->index; |
150 |
195 if (type == VEH_TRAIN && !GenerateReplaceRailList(eid, draw_left, WP(w, replaceveh_d).wagon_btnstate)) continue; // special rules for trains |
151 /** Figure out if an engine should be added to a list |
196 |
152 * @param e The EngineID |
197 if (draw_left) { |
153 * @param draw_left If true, then the left list is drawn (the engines specific to the railtype you selected) |
198 const GroupID selected_group = WP(w, replaceveh_d).sel_group; |
154 * @param show_engines if truem then locomotives are drawn, else wagons (never both) |
199 const uint num_engines = GetGroupNumEngines(_local_player, selected_group, eid); |
155 * @return true if the engine should be in the list (based on this check) |
200 |
156 */ |
201 /* Skip drawing the engines we don't have any of and haven't set for replacement */ |
157 bool GenerateReplaceRailList(EngineID e, bool draw_left, bool show_engines) |
202 if (num_engines == 0 && EngineReplacementForPlayer(GetPlayer(_local_player), eid, selected_group) == INVALID_ENGINE) continue; |
158 { |
|
159 const RailVehicleInfo *rvi = RailVehInfo(e); |
|
160 |
|
161 /* Ensure that the wagon/engine selection fits the engine. */ |
|
162 if ((rvi->railveh_type == RAILVEH_WAGON) == show_engines) return false; |
|
163 |
|
164 if (draw_left && show_engines) { |
|
165 /* Ensure that the railtype is specific to the selected one */ |
|
166 if (rvi->railtype != this->sel_railtype) return false; |
203 } else { |
167 } else { |
204 /* This is for engines we can replace to and they should depend on what we selected to replace from */ |
168 /* Ensure that it's a compatible railtype to the selected one (like electric <-> diesel) |
205 if (!IsEngineBuildable(eid, type, _local_player)) continue; // we need to be able to build the engine |
169 * The vehicle do not have to have power on the railtype in question, only able to drive (pulled if needed) */ |
206 if (!EnginesGotCargoInCommon(eid, WP(w, replaceveh_d).sel_engine[0])) continue; // the engines needs to be able to carry the same cargo |
170 if (!IsCompatibleRail(rvi->railtype, this->sel_railtype)) return false; |
207 |
171 } |
208 /* Road vehicles can't be replaced by trams and vice-versa */ |
172 return true; |
209 if (type == VEH_ROAD && HasBit(EngInfo(WP(w, replaceveh_d).sel_engine[0])->misc_flags, EF_ROAD_TRAM) != HasBit(e->info.misc_flags, EF_ROAD_TRAM)) continue; |
173 } |
210 if (eid == WP(w, replaceveh_d).sel_engine[0]) continue; // we can't replace an engine into itself (that would be autorenew) |
174 |
211 } |
175 |
212 |
176 /** Generate a list |
213 list->push_back(eid); |
177 * @param w Window, that contains the list |
214 if (eid == WP(w, replaceveh_d).sel_engine[i]) selected_engine = eid; // The selected engine is still in the list |
178 * @param draw_left true if generating the left list, otherwise false |
215 } |
179 */ |
216 WP(w, replaceveh_d).sel_engine[i] = selected_engine; // update which engine we selected (the same or none, if it's not in the list anymore) |
180 void GenerateReplaceVehList(Window *w, bool draw_left) |
217 if (type == VEH_TRAIN) EngList_Sort(list, &TrainEngineNumberSorter); |
181 { |
218 } |
182 EngineID selected_engine = INVALID_ENGINE; |
219 |
183 VehicleType type = (VehicleType)this->window_number; |
220 /** Generate the lists |
184 byte i = draw_left ? 0 : 1; |
221 * @param w Window containing the lists |
185 |
222 */ |
186 EngineList *list = &this->list[i]; |
223 static void GenerateLists(Window *w) |
187 list->clear(); |
224 { |
188 |
225 EngineID e = WP(w, replaceveh_d).sel_engine[0]; |
189 const Engine *e; |
226 |
190 FOR_ALL_ENGINES_OF_TYPE(e, type) { |
227 if (WP(w, replaceveh_d).update_left == true) { |
191 EngineID eid = e->index; |
228 /* We need to rebuild the left list */ |
192 if (type == VEH_TRAIN && !GenerateReplaceRailList(eid, draw_left, this->wagon_btnstate)) continue; // special rules for trains |
229 GenerateReplaceVehList(w, true); |
193 |
230 SetVScrollCount(w, WP(w, replaceveh_d).list[0].size()); |
194 if (draw_left) { |
231 if (WP(w, replaceveh_d).init_lists && WP(w, replaceveh_d).sel_engine[0] == INVALID_ENGINE && WP(w, replaceveh_d).list[0].size() != 0) { |
195 const GroupID selected_group = this->sel_group; |
232 WP(w, replaceveh_d).sel_engine[0] = WP(w, replaceveh_d).list[0][0]; |
196 const uint num_engines = GetGroupNumEngines(_local_player, selected_group, eid); |
233 } |
197 |
234 } |
198 /* Skip drawing the engines we don't have any of and haven't set for replacement */ |
235 |
199 if (num_engines == 0 && EngineReplacementForPlayer(GetPlayer(_local_player), eid, selected_group) == INVALID_ENGINE) continue; |
236 if (WP(w, replaceveh_d).update_right || e != WP(w, replaceveh_d).sel_engine[0]) { |
200 } else { |
237 /* Either we got a request to rebuild the right list or the left list selected a different engine */ |
201 /* This is for engines we can replace to and they should depend on what we selected to replace from */ |
238 if (WP(w, replaceveh_d).sel_engine[0] == INVALID_ENGINE) { |
202 if (!IsEngineBuildable(eid, type, _local_player)) continue; // we need to be able to build the engine |
239 /* Always empty the right list when nothing is selected in the left list */ |
203 if (!EnginesGotCargoInCommon(eid, this->sel_engine[0])) continue; // the engines needs to be able to carry the same cargo |
240 WP(w, replaceveh_d).list[1].clear(); |
204 |
241 WP(w, replaceveh_d).sel_engine[1] = INVALID_ENGINE; |
205 /* Road vehicles can't be replaced by trams and vice-versa */ |
242 } else { |
206 if (type == VEH_ROAD && HasBit(EngInfo(this->sel_engine[0])->misc_flags, EF_ROAD_TRAM) != HasBit(e->info.misc_flags, EF_ROAD_TRAM)) continue; |
243 GenerateReplaceVehList(w, false); |
207 if (eid == this->sel_engine[0]) continue; // we can't replace an engine into itself (that would be autorenew) |
244 SetVScroll2Count(w, WP(w, replaceveh_d).list[1].size()); |
|
245 if (WP(w, replaceveh_d).init_lists && WP(w, replaceveh_d).sel_engine[1] == INVALID_ENGINE && WP(w, replaceveh_d).list[1].size() != 0) { |
|
246 WP(w, replaceveh_d).sel_engine[1] = WP(w, replaceveh_d).list[1][0]; |
|
247 } |
208 } |
248 } |
209 |
249 } |
210 list->push_back(eid); |
250 /* Reset the flags about needed updates */ |
211 if (eid == this->sel_engine[i]) selected_engine = eid; // The selected engine is still in the list |
251 WP(w, replaceveh_d).update_left = false; |
212 } |
252 WP(w, replaceveh_d).update_right = false; |
213 this->sel_engine[i] = selected_engine; // update which engine we selected (the same or none, if it's not in the list anymore) |
253 WP(w, replaceveh_d).init_lists = false; |
214 if (type == VEH_TRAIN) EngList_Sort(list, &TrainEngineNumberSorter); |
254 } |
215 } |
255 |
216 |
256 |
217 /** Generate the lists */ |
257 void DrawEngineList(VehicleType type, int x, int y, const EngineList eng_list, uint16 min, uint16 max, EngineID selected_id, int count_location, GroupID selected_group); |
218 void GenerateLists() |
258 |
219 { |
259 static void ReplaceVehicleWndProc(Window *w, WindowEvent *e) |
220 EngineID e = this->sel_engine[0]; |
260 { |
221 |
261 /* Strings for the pulldown menu */ |
222 if (this->update_left == true) { |
262 static const StringID _vehicle_type_names[] = { |
223 /* We need to rebuild the left list */ |
263 STR_019F_TRAIN, |
224 GenerateReplaceVehList(this, true); |
264 STR_019C_ROAD_VEHICLE, |
225 SetVScrollCount(this, this->list[0].size()); |
265 STR_019E_SHIP, |
226 if (this->init_lists && this->sel_engine[0] == INVALID_ENGINE && this->list[0].size() != 0) { |
266 STR_019D_AIRCRAFT |
227 this->sel_engine[0] = this->list[0][0]; |
267 }; |
|
268 |
|
269 switch (e->event) { |
|
270 case WE_CREATE: |
|
271 WP(w, replaceveh_d).wagon_btnstate = true; // start with locomotives (all other vehicles will not read this bool) |
|
272 new (&WP(w, replaceveh_d).list[0]) EngineList(); |
|
273 new (&WP(w, replaceveh_d).list[1]) EngineList(); |
|
274 WP(w, replaceveh_d).update_left = true; |
|
275 WP(w, replaceveh_d).update_right = true; |
|
276 WP(w, replaceveh_d).init_lists = true; |
|
277 WP(w, replaceveh_d).sel_engine[0] = INVALID_ENGINE; |
|
278 WP(w, replaceveh_d).sel_engine[1] = INVALID_ENGINE; |
|
279 break; |
|
280 |
|
281 case WE_PAINT: { |
|
282 if (WP(w, replaceveh_d).update_left || WP(w, replaceveh_d).update_right) GenerateLists(w); |
|
283 |
|
284 Player *p = GetPlayer(_local_player); |
|
285 EngineID selected_id[2]; |
|
286 const GroupID selected_group = WP(w, replaceveh_d).sel_group; |
|
287 |
|
288 selected_id[0] = WP(w, replaceveh_d).sel_engine[0]; |
|
289 selected_id[1] = WP(w, replaceveh_d).sel_engine[1]; |
|
290 |
|
291 /* Disable the "Start Replacing" button if: |
|
292 * Either list is empty |
|
293 * or The selected replacement engine has a replacement (to prevent loops) |
|
294 * or The right list (new replacement) has the existing replacement vehicle selected */ |
|
295 w->SetWidgetDisabledState(RVW_WIDGET_START_REPLACE, |
|
296 selected_id[0] == INVALID_ENGINE || |
|
297 selected_id[1] == INVALID_ENGINE || |
|
298 EngineReplacementForPlayer(p, selected_id[1], selected_group) != INVALID_ENGINE || |
|
299 EngineReplacementForPlayer(p, selected_id[0], selected_group) == selected_id[1]); |
|
300 |
|
301 /* Disable the "Stop Replacing" button if: |
|
302 * The left list (existing vehicle) is empty |
|
303 * or The selected vehicle has no replacement set up */ |
|
304 w->SetWidgetDisabledState(RVW_WIDGET_STOP_REPLACE, |
|
305 selected_id[0] == INVALID_ENGINE || |
|
306 !EngineHasReplacementForPlayer(p, selected_id[0], selected_group)); |
|
307 |
|
308 /* now the actual drawing of the window itself takes place */ |
|
309 SetDParam(0, _vehicle_type_names[w->window_number]); |
|
310 |
|
311 if (w->window_number == VEH_TRAIN) { |
|
312 /* set on/off for renew_keep_length */ |
|
313 SetDParam(1, p->renew_keep_length ? STR_CONFIG_PATCHES_ON : STR_CONFIG_PATCHES_OFF); |
|
314 |
|
315 /* set wagon/engine button */ |
|
316 SetDParam(2, WP(w, replaceveh_d).wagon_btnstate ? STR_ENGINES : STR_WAGONS); |
|
317 |
|
318 /* sets the colour of that art thing */ |
|
319 w->widget[RVW_WIDGET_TRAIN_FLUFF_LEFT].color = _player_colors[_local_player]; |
|
320 w->widget[RVW_WIDGET_TRAIN_FLUFF_RIGHT].color = _player_colors[_local_player]; |
|
321 } |
228 } |
322 |
229 } |
323 if (w->window_number == VEH_TRAIN) { |
230 |
324 /* Show the selected railtype in the pulldown menu */ |
231 if (this->update_right || e != this->sel_engine[0]) { |
325 RailType railtype = _railtype_selected_in_replace_gui; |
232 /* Either we got a request to rebuild the right list or the left list selected a different engine */ |
326 w->widget[RVW_WIDGET_TRAIN_RAILTYPE_DROPDOWN].data = _rail_types_list[railtype]; |
233 if (this->sel_engine[0] == INVALID_ENGINE) { |
327 } |
234 /* Always empty the right list when nothing is selected in the left list */ |
328 |
235 this->list[1].clear(); |
329 DrawWindowWidgets(w); |
236 this->sel_engine[1] = INVALID_ENGINE; |
330 |
|
331 /* sets up the string for the vehicle that is being replaced to */ |
|
332 if (selected_id[0] != INVALID_ENGINE) { |
|
333 if (!EngineHasReplacementForPlayer(p, selected_id[0], selected_group)) { |
|
334 SetDParam(0, STR_NOT_REPLACING); |
|
335 } else { |
|
336 SetDParam(0, STR_ENGINE_NAME); |
|
337 SetDParam(1, EngineReplacementForPlayer(p, selected_id[0], selected_group)); |
|
338 } |
|
339 } else { |
237 } else { |
340 SetDParam(0, STR_NOT_REPLACING_VEHICLE_SELECTED); |
238 GenerateReplaceVehList(this, false); |
341 } |
239 SetVScroll2Count(this, this->list[1].size()); |
342 |
240 if (this->init_lists && this->sel_engine[1] == INVALID_ENGINE && this->list[1].size() != 0) { |
343 DrawString(145, w->widget[RVW_WIDGET_INFO_TAB].top + 1, STR_02BD, TC_BLACK); |
241 this->sel_engine[1] = this->list[1][0]; |
344 |
|
345 /* Draw the lists */ |
|
346 for(byte i = 0; i < 2; i++) { |
|
347 uint widget = (i == 0) ? RVW_WIDGET_LEFT_MATRIX : RVW_WIDGET_RIGHT_MATRIX; |
|
348 EngineList list = WP(w, replaceveh_d).list[i]; // which list to draw |
|
349 EngineID start = i == 0 ? w->vscroll.pos : w->vscroll2.pos; // what is the offset for the start (scrolling) |
|
350 EngineID end = min((i == 0 ? w->vscroll.cap : w->vscroll2.cap) + start, list.size()); |
|
351 |
|
352 /* Do the actual drawing */ |
|
353 DrawEngineList((VehicleType)w->window_number, w->widget[widget].left + 2, w->widget[widget].top + 1, list, start, end, WP(w, replaceveh_d).sel_engine[i], i == 0 ? w->widget[RVW_WIDGET_LEFT_MATRIX].right - 2 : 0, selected_group); |
|
354 |
|
355 /* Also draw the details if an engine is selected */ |
|
356 if (WP(w, replaceveh_d).sel_engine[i] != INVALID_ENGINE) { |
|
357 const Widget *wi = &w->widget[i == 0 ? RVW_WIDGET_LEFT_DETAILS : RVW_WIDGET_RIGHT_DETAILS]; |
|
358 int text_end = DrawVehiclePurchaseInfo(wi->left + 2, wi->top + 1, wi->right - wi->left - 2, WP(w, replaceveh_d).sel_engine[i]); |
|
359 |
|
360 if (text_end > wi->bottom) { |
|
361 w->SetDirty(); |
|
362 ResizeWindowForWidget(w, i == 0 ? RVW_WIDGET_LEFT_DETAILS : RVW_WIDGET_RIGHT_DETAILS, 0, text_end - wi->bottom); |
|
363 w->SetDirty(); |
|
364 } |
|
365 } |
242 } |
366 } |
243 } |
367 |
244 } |
368 } break; // end of paint |
245 /* Reset the flags about needed updates */ |
369 |
246 this->update_left = false; |
370 case WE_CLICK: { |
247 this->update_right = false; |
371 switch (e->we.click.widget) { |
248 this->init_lists = false; |
372 case RVW_WIDGET_TRAIN_ENGINEWAGON_TOGGLE: |
249 } |
373 WP(w, replaceveh_d).wagon_btnstate = !(WP(w, replaceveh_d).wagon_btnstate); |
250 |
374 WP(w, replaceveh_d).update_left = true; |
251 public: |
375 WP(w, replaceveh_d).init_lists = true; |
252 ReplaceVehicleWindow(const WindowDesc *desc, VehicleType vehicletype, GroupID id_g) : Window(desc, NULL, window_number) |
376 w->SetDirty(); |
253 { |
377 break; |
254 this->wagon_btnstate = true; // start with locomotives (all other vehicles will not read this bool) |
378 |
255 new (&this->list[0]) EngineList(); |
379 case RVW_WIDGET_TRAIN_RAILTYPE_DROPDOWN: /* Railtype selection dropdown menu */ |
256 new (&this->list[1]) EngineList(); |
380 ShowDropDownMenu(w, _rail_types_list, _railtype_selected_in_replace_gui, RVW_WIDGET_TRAIN_RAILTYPE_DROPDOWN, 0, ~GetPlayer(_local_player)->avail_railtypes); |
257 this->update_left = true; |
381 break; |
258 this->update_right = true; |
382 |
259 this->init_lists = true; |
383 case RVW_WIDGET_TRAIN_WAGONREMOVE_TOGGLE: /* toggle renew_keep_length */ |
260 this->sel_engine[0] = INVALID_ENGINE; |
384 DoCommandP(0, 5, GetPlayer(_local_player)->renew_keep_length ? 0 : 1, NULL, CMD_SET_AUTOREPLACE); |
261 this->sel_engine[1] = INVALID_ENGINE; |
385 break; |
262 |
386 |
263 this->resize.step_height = GetVehicleListHeight(vehicletype); |
387 case RVW_WIDGET_START_REPLACE: { /* Start replacing */ |
264 this->vscroll.cap = this->resize.step_height == 14 ? 8 : 4; |
388 EngineID veh_from = WP(w, replaceveh_d).sel_engine[0]; |
265 |
389 EngineID veh_to = WP(w, replaceveh_d).sel_engine[1]; |
266 Widget *widget = this->widget; |
390 DoCommandP(0, 3 + (WP(w, replaceveh_d).sel_group << 16) , veh_from + (veh_to << 16), NULL, CMD_SET_AUTOREPLACE); |
267 widget[RVW_WIDGET_LEFT_MATRIX].data = widget[RVW_WIDGET_RIGHT_MATRIX].data = (this->vscroll.cap << 8) + 1; |
391 } break; |
268 |
392 |
269 if (vehicletype == VEH_TRAIN) { |
393 case RVW_WIDGET_STOP_REPLACE: { /* Stop replacing */ |
270 this->wagon_btnstate = true; |
394 EngineID veh_from = WP(w, replaceveh_d).sel_engine[0]; |
271 /* The train window is bigger so we will move some of the widgets to fit the new size. |
395 DoCommandP(0, 3 + (WP(w, replaceveh_d).sel_group << 16), veh_from + (INVALID_ENGINE << 16), NULL, CMD_SET_AUTOREPLACE); |
272 * We will start by moving the resize button to the lower right corner. */ |
396 } break; |
273 widget[RVW_WIDGET_RESIZE].top = widget[RVW_WIDGET_TRAIN_ENGINEWAGON_TOGGLE].top; |
397 |
274 widget[RVW_WIDGET_RESIZE].bottom = widget[RVW_WIDGET_TRAIN_ENGINEWAGON_TOGGLE].bottom; |
398 case RVW_WIDGET_LEFT_MATRIX: |
275 widget[RVW_WIDGET_STOP_REPLACE].right = widget[RVW_WIDGET_RESIZE].right; |
399 case RVW_WIDGET_RIGHT_MATRIX: { |
276 |
400 uint i = (e->we.click.pt.y - 14) / w->resize.step_height; |
277 /* The detail panel is one line taller for trains so we will move some of the widgets one line (10 pixels) down. */ |
401 uint16 click_scroll_pos = e->we.click.widget == RVW_WIDGET_LEFT_MATRIX ? w->vscroll.pos : w->vscroll2.pos; |
278 widget[RVW_WIDGET_LEFT_DETAILS].bottom += 10; |
402 uint16 click_scroll_cap = e->we.click.widget == RVW_WIDGET_LEFT_MATRIX ? w->vscroll.cap : w->vscroll2.cap; |
279 widget[RVW_WIDGET_RIGHT_DETAILS].bottom += 10; |
403 byte click_side = e->we.click.widget == RVW_WIDGET_LEFT_MATRIX ? 0 : 1; |
280 for (int i = RVW_WIDGET_START_REPLACE; i < RVW_WIDGET_RESIZE; i++) { |
404 size_t engine_count = WP(w, replaceveh_d).list[click_side].size(); |
281 widget[i].top += 10; |
405 |
282 widget[i].bottom += 10; |
406 if (i < click_scroll_cap) { |
283 } |
407 i += click_scroll_pos; |
284 } else { |
408 EngineID e = engine_count > i ? WP(w, replaceveh_d).list[click_side][i] : INVALID_ENGINE; |
285 /* Since it's not a train we will hide the train only widgets. */ |
409 if (e == WP(w, replaceveh_d).sel_engine[click_side]) break; // we clicked the one we already selected |
286 this->SetWidgetsHiddenState(true, |
410 WP(w, replaceveh_d).sel_engine[click_side] = e; |
287 RVW_WIDGET_TRAIN_ENGINEWAGON_TOGGLE, |
411 if (click_side == 0) { |
288 RVW_WIDGET_TRAIN_FLUFF_LEFT, |
412 WP(w, replaceveh_d).update_right = true; |
289 RVW_WIDGET_TRAIN_RAILTYPE_DROPDOWN, |
413 WP(w, replaceveh_d).init_lists = true; |
290 RVW_WIDGET_TRAIN_FLUFF_RIGHT, |
414 } |
291 RVW_WIDGET_TRAIN_WAGONREMOVE_TOGGLE, |
415 w->SetDirty(); |
292 WIDGET_LIST_END); |
416 } |
293 } |
417 break; |
294 |
|
295 ResizeWindow(this, 0, this->resize.step_height * this->vscroll.cap); |
|
296 |
|
297 /* Set the minimum window size to the current window size */ |
|
298 this->resize.width = this->width; |
|
299 this->resize.height = this->height; |
|
300 |
|
301 this->caption_color = _local_player; |
|
302 this->sel_group = id_g; |
|
303 this->vscroll2.cap = this->vscroll.cap; // these two are always the same |
|
304 } |
|
305 |
|
306 virtual void OnPaint() |
|
307 { |
|
308 static const StringID _vehicle_type_names[] = { |
|
309 STR_019F_TRAIN, |
|
310 STR_019C_ROAD_VEHICLE, |
|
311 STR_019E_SHIP, |
|
312 STR_019D_AIRCRAFT |
|
313 }; |
|
314 |
|
315 if (this->update_left || this->update_right) this->GenerateLists(); |
|
316 |
|
317 Player *p = GetPlayer(_local_player); |
|
318 EngineID selected_id[2]; |
|
319 const GroupID selected_group = this->sel_group; |
|
320 |
|
321 selected_id[0] = this->sel_engine[0]; |
|
322 selected_id[1] = this->sel_engine[1]; |
|
323 |
|
324 /* Disable the "Start Replacing" button if: |
|
325 * Either list is empty |
|
326 * or The selected replacement engine has a replacement (to prevent loops) |
|
327 * or The right list (new replacement) has the existing replacement vehicle selected */ |
|
328 this->SetWidgetDisabledState(RVW_WIDGET_START_REPLACE, |
|
329 selected_id[0] == INVALID_ENGINE || |
|
330 selected_id[1] == INVALID_ENGINE || |
|
331 EngineReplacementForPlayer(p, selected_id[1], selected_group) != INVALID_ENGINE || |
|
332 EngineReplacementForPlayer(p, selected_id[0], selected_group) == selected_id[1]); |
|
333 |
|
334 /* Disable the "Stop Replacing" button if: |
|
335 * The left list (existing vehicle) is empty |
|
336 * or The selected vehicle has no replacement set up */ |
|
337 this->SetWidgetDisabledState(RVW_WIDGET_STOP_REPLACE, |
|
338 selected_id[0] == INVALID_ENGINE || |
|
339 !EngineHasReplacementForPlayer(p, selected_id[0], selected_group)); |
|
340 |
|
341 /* now the actual drawing of the window itself takes place */ |
|
342 SetDParam(0, _vehicle_type_names[this->window_number]); |
|
343 |
|
344 if (this->window_number == VEH_TRAIN) { |
|
345 /* set on/off for renew_keep_length */ |
|
346 SetDParam(1, p->renew_keep_length ? STR_CONFIG_PATCHES_ON : STR_CONFIG_PATCHES_OFF); |
|
347 |
|
348 /* set wagon/engine button */ |
|
349 SetDParam(2, this->wagon_btnstate ? STR_ENGINES : STR_WAGONS); |
|
350 |
|
351 /* sets the colour of that art thing */ |
|
352 this->widget[RVW_WIDGET_TRAIN_FLUFF_LEFT].color = _player_colors[_local_player]; |
|
353 this->widget[RVW_WIDGET_TRAIN_FLUFF_RIGHT].color = _player_colors[_local_player]; |
|
354 } |
|
355 |
|
356 if (this->window_number == VEH_TRAIN) { |
|
357 /* Show the selected railtype in the pulldown menu */ |
|
358 this->widget[RVW_WIDGET_TRAIN_RAILTYPE_DROPDOWN].data = _rail_types_list[sel_railtype]; |
|
359 } |
|
360 |
|
361 DrawWindowWidgets(this); |
|
362 |
|
363 /* sets up the string for the vehicle that is being replaced to */ |
|
364 if (selected_id[0] != INVALID_ENGINE) { |
|
365 if (!EngineHasReplacementForPlayer(p, selected_id[0], selected_group)) { |
|
366 SetDParam(0, STR_NOT_REPLACING); |
|
367 } else { |
|
368 SetDParam(0, STR_ENGINE_NAME); |
|
369 SetDParam(1, EngineReplacementForPlayer(p, selected_id[0], selected_group)); |
|
370 } |
|
371 } else { |
|
372 SetDParam(0, STR_NOT_REPLACING_VEHICLE_SELECTED); |
|
373 } |
|
374 |
|
375 DrawString(145, this->widget[RVW_WIDGET_INFO_TAB].top + 1, STR_02BD, TC_BLACK); |
|
376 |
|
377 /* Draw the lists */ |
|
378 for (byte i = 0; i < 2; i++) { |
|
379 uint widget = (i == 0) ? RVW_WIDGET_LEFT_MATRIX : RVW_WIDGET_RIGHT_MATRIX; |
|
380 EngineList list = this->list[i]; // which list to draw |
|
381 EngineID start = i == 0 ? this->vscroll.pos : this->vscroll2.pos; // what is the offset for the start (scrolling) |
|
382 EngineID end = min((i == 0 ? this->vscroll.cap : this->vscroll2.cap) + start, list.size()); |
|
383 |
|
384 /* Do the actual drawing */ |
|
385 DrawEngineList((VehicleType)this->window_number, this->widget[widget].left + 2, 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); |
|
386 |
|
387 /* Also draw the details if an engine is selected */ |
|
388 if (this->sel_engine[i] != INVALID_ENGINE) { |
|
389 const Widget *wi = &this->widget[i == 0 ? RVW_WIDGET_LEFT_DETAILS : RVW_WIDGET_RIGHT_DETAILS]; |
|
390 int text_end = DrawVehiclePurchaseInfo(wi->left + 2, wi->top + 1, wi->right - wi->left - 2, this->sel_engine[i]); |
|
391 |
|
392 if (text_end > wi->bottom) { |
|
393 this->SetDirty(); |
|
394 ResizeWindowForWidget(this, i == 0 ? RVW_WIDGET_LEFT_DETAILS : RVW_WIDGET_RIGHT_DETAILS, 0, text_end - wi->bottom); |
|
395 this->SetDirty(); |
|
396 } |
|
397 } |
|
398 } |
|
399 } |
|
400 |
|
401 virtual void OnClick(Point pt, int widget) |
|
402 { |
|
403 switch (widget) { |
|
404 case RVW_WIDGET_TRAIN_ENGINEWAGON_TOGGLE: |
|
405 this->wagon_btnstate = !(this->wagon_btnstate); |
|
406 this->update_left = true; |
|
407 this->init_lists = true; |
|
408 this->SetDirty(); |
|
409 break; |
|
410 |
|
411 case RVW_WIDGET_TRAIN_RAILTYPE_DROPDOWN: /* Railtype selection dropdown menu */ |
|
412 ShowDropDownMenu(this, _rail_types_list, sel_railtype, RVW_WIDGET_TRAIN_RAILTYPE_DROPDOWN, 0, ~GetPlayer(_local_player)->avail_railtypes); |
|
413 break; |
|
414 |
|
415 case RVW_WIDGET_TRAIN_WAGONREMOVE_TOGGLE: /* toggle renew_keep_length */ |
|
416 DoCommandP(0, 5, GetPlayer(_local_player)->renew_keep_length ? 0 : 1, NULL, CMD_SET_AUTOREPLACE); |
|
417 break; |
|
418 |
|
419 case RVW_WIDGET_START_REPLACE: { /* Start replacing */ |
|
420 EngineID veh_from = this->sel_engine[0]; |
|
421 EngineID veh_to = this->sel_engine[1]; |
|
422 DoCommandP(0, 3 + (this->sel_group << 16) , veh_from + (veh_to << 16), NULL, CMD_SET_AUTOREPLACE); |
|
423 } break; |
|
424 |
|
425 case RVW_WIDGET_STOP_REPLACE: { /* Stop replacing */ |
|
426 EngineID veh_from = this->sel_engine[0]; |
|
427 DoCommandP(0, 3 + (this->sel_group << 16), veh_from + (INVALID_ENGINE << 16), NULL, CMD_SET_AUTOREPLACE); |
|
428 } break; |
|
429 |
|
430 case RVW_WIDGET_LEFT_MATRIX: |
|
431 case RVW_WIDGET_RIGHT_MATRIX: { |
|
432 uint i = (pt.y - 14) / this->resize.step_height; |
|
433 uint16 click_scroll_pos = widget == RVW_WIDGET_LEFT_MATRIX ? this->vscroll.pos : this->vscroll2.pos; |
|
434 uint16 click_scroll_cap = widget == RVW_WIDGET_LEFT_MATRIX ? this->vscroll.cap : this->vscroll2.cap; |
|
435 byte click_side = widget == RVW_WIDGET_LEFT_MATRIX ? 0 : 1; |
|
436 size_t engine_count = this->list[click_side].size(); |
|
437 |
|
438 if (i < click_scroll_cap) { |
|
439 i += click_scroll_pos; |
|
440 EngineID e = engine_count > i ? this->list[click_side][i] : INVALID_ENGINE; |
|
441 if (e == this->sel_engine[click_side]) break; // we clicked the one we already selected |
|
442 this->sel_engine[click_side] = e; |
|
443 if (click_side == 0) { |
|
444 this->update_right = true; |
|
445 this->init_lists = true; |
418 } |
446 } |
419 } |
447 this->SetDirty(); |
420 break; |
448 } |
421 } |
449 break; |
422 |
450 } |
423 case WE_DROPDOWN_SELECT: { /* we have selected a dropdown item in the list */ |
451 } |
424 RailType temp = (RailType)e->we.dropdown.index; |
452 } |
425 if (temp == _railtype_selected_in_replace_gui) break; // we didn't select a new one. No need to change anything |
453 |
426 _railtype_selected_in_replace_gui = temp; |
454 virtual void OnDropdownSelect(int widget, int index) |
427 /* Reset scrollbar positions */ |
455 { |
428 w->vscroll.pos = 0; |
456 RailType temp = (RailType)index; |
429 w->vscroll2.pos = 0; |
457 if (temp == sel_railtype) return; // we didn't select a new one. No need to change anything |
430 /* Rebuild the lists */ |
458 sel_railtype = temp; |
431 WP(w, replaceveh_d).update_left = true; |
459 /* Reset scrollbar positions */ |
432 WP(w, replaceveh_d).update_right = true; |
460 this->vscroll.pos = 0; |
433 WP(w, replaceveh_d).init_lists = true; |
461 this->vscroll2.pos = 0; |
434 w->SetDirty(); |
462 /* Rebuild the lists */ |
435 } break; |
463 this->update_left = true; |
436 |
464 this->update_right = true; |
437 case WE_RESIZE: |
465 this->init_lists = true; |
438 { |
466 this->SetDirty(); |
439 w->vscroll.cap += e->we.sizing.diff.y / (int)w->resize.step_height; |
467 } |
440 w->vscroll2.cap += e->we.sizing.diff.y / (int)w->resize.step_height; |
468 |
441 |
469 virtual void OnResize(Point new_size, Point delta) { |
442 Widget *widget = w->widget; |
470 this->vscroll.cap += delta.y / (int)this->resize.step_height; |
443 |
471 this->vscroll2.cap += delta.y / (int)this->resize.step_height; |
444 widget[RVW_WIDGET_LEFT_MATRIX].data = widget[RVW_WIDGET_RIGHT_MATRIX].data = (w->vscroll2.cap << 8) + 1; |
472 |
445 |
473 Widget *widget = this->widget; |
446 if (e->we.sizing.diff.x != 0) { |
474 |
447 /* We changed the width of the window so we have to resize the lists. |
475 widget[RVW_WIDGET_LEFT_MATRIX].data = widget[RVW_WIDGET_RIGHT_MATRIX].data = (this->vscroll2.cap << 8) + 1; |
448 * Because ResizeButtons() makes each widget the same size it can't be used on the lists |
476 |
449 * because then the lists would have the same size as the scrollbars. |
477 if (delta.x != 0) { |
450 * Instead we use it on the detail panels. |
478 /* We changed the width of the window so we have to resize the lists. |
451 * Afterwards we use the new location of the detail panels (the middle of the window) |
479 * Because ResizeButtons() makes each widget the same size it can't be used on the lists |
452 * to place the lists. |
480 * because then the lists would have the same size as the scrollbars. |
453 * This way the lists will have equal size while keeping the width of the scrollbars unchanged. */ |
481 * Instead we use it on the detail panels. |
454 ResizeButtons(w, RVW_WIDGET_LEFT_DETAILS, RVW_WIDGET_RIGHT_DETAILS); |
482 * Afterwards we use the new location of the detail panels (the middle of the window) |
455 widget[RVW_WIDGET_RIGHT_MATRIX].left = widget[RVW_WIDGET_RIGHT_DETAILS].left; |
483 * to place the lists. |
456 widget[RVW_WIDGET_LEFT_SCROLLBAR].right = widget[RVW_WIDGET_LEFT_DETAILS].right; |
484 * This way the lists will have equal size while keeping the width of the scrollbars unchanged. */ |
457 widget[RVW_WIDGET_LEFT_SCROLLBAR].left = widget[RVW_WIDGET_LEFT_SCROLLBAR].right - 11; |
485 ResizeButtons(this, RVW_WIDGET_LEFT_DETAILS, RVW_WIDGET_RIGHT_DETAILS); |
458 widget[RVW_WIDGET_LEFT_MATRIX].right = widget[RVW_WIDGET_LEFT_SCROLLBAR].left - 1; |
486 widget[RVW_WIDGET_RIGHT_MATRIX].left = widget[RVW_WIDGET_RIGHT_DETAILS].left; |
459 } |
487 widget[RVW_WIDGET_LEFT_SCROLLBAR].right = widget[RVW_WIDGET_LEFT_DETAILS].right; |
460 } break; |
488 widget[RVW_WIDGET_LEFT_SCROLLBAR].left = widget[RVW_WIDGET_LEFT_SCROLLBAR].right - 11; |
461 |
489 widget[RVW_WIDGET_LEFT_MATRIX].right = widget[RVW_WIDGET_LEFT_SCROLLBAR].left - 1; |
462 case WE_INVALIDATE_DATA: |
490 } |
463 if (e->we.invalidate.data == true) { |
491 } |
464 WP(w, replaceveh_d).update_left = true; |
492 |
465 } else { |
493 virtual void OnInvalidateData(int data) |
466 WP(w, replaceveh_d).update_right = true; |
494 { |
467 } |
495 if (data != 0) { |
468 break; |
496 this->update_left = true; |
469 |
497 } else { |
470 case WE_DESTROY: |
498 this->update_right = true; |
471 WP(w, replaceveh_d).list[0].~EngineList(); // call destructor explicitly |
499 } |
472 WP(w, replaceveh_d).list[1].~EngineList(); |
500 } |
473 break; |
501 }; |
474 } |
|
475 } |
|
476 |
502 |
477 static const Widget _replace_vehicle_widgets[] = { |
503 static const Widget _replace_vehicle_widgets[] = { |
478 { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, |
504 { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, |
479 { WWT_CAPTION, RESIZE_RIGHT, 14, 11, 443, 0, 13, STR_REPLACE_VEHICLES_WHITE, STR_018C_WINDOW_TITLE_DRAG_THIS}, |
505 { WWT_CAPTION, RESIZE_RIGHT, 14, 11, 443, 0, 13, STR_REPLACE_VEHICLES_WHITE, STR_018C_WINDOW_TITLE_DRAG_THIS}, |
480 { WWT_STICKYBOX, RESIZE_LR, 14, 444, 455, 0, 13, STR_NULL, STR_STICKY_BUTTON}, |
506 { WWT_STICKYBOX, RESIZE_LR, 14, 444, 455, 0, 13, STR_NULL, STR_STICKY_BUTTON}, |