Make it possible to stop road vehicles from slowing down in curves so "diagonal"...
[openttd-joker.git] / src / order_gui.cpp
blob7834bb619aa9ca75d68ce30637a78a1470702b1b
1 /* $Id$ */
3 /*
4 * This file is part of OpenTTD.
5 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
6 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
7 * See the GNU General Public License for more details. You should have/// received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
8 */
10 /** @file order_gui.cpp GUI related to orders. */
12 #include "stdafx.h"
13 #include "command_func.h"
14 #include "viewport_func.h"
15 #include "depot_map.h"
16 #include "roadveh.h"
17 #include "timetable.h"
18 #include "strings_func.h"
19 #include "company_func.h"
20 #include "widgets/dropdown_type.h"
21 #include "widgets/dropdown_func.h"
22 #include "textbuf_gui.h"
23 #include "string_func.h"
24 #include "tilehighlight_func.h"
25 #include "network/network.h"
26 #include "station_base.h"
27 #include "waypoint_base.h"
28 #include "core/geometry_func.hpp"
29 #include "hotkeys.h"
30 #include "aircraft.h"
31 #include "engine_func.h"
32 #include "tracerestrict.h"
34 #include "widgets/order_widget.h"
36 #include "safeguards.h"
38 enum CargoTypeOrdersWindowVariant {
39 CTOWV_LOAD = 0,
40 CTOWV_UNLOAD = 1,
43 /** Cargo type orders strings for load dropdowns. */
44 static const StringID _cargo_type_load_order_drowdown[] = {
45 STR_ORDER_DROP_LOAD_IF_POSSIBLE, // OLF_LOAD_IF_POSSIBLE
46 STR_EMPTY,
47 STR_CARGO_TYPE_ORDERS_DROP_FULL_LOAD, // OLFB_FULL_LOAD
48 STR_EMPTY,
49 STR_ORDER_DROP_NO_LOADING, // OLFB_NO_LOAD
50 INVALID_STRING_ID
52 static const uint32 _cargo_type_load_order_drowdown_hidden_mask = 0xA; // 01010
54 /** Cargo type orders strings for unload dropdowns. */
55 static const StringID _cargo_type_unload_order_drowdown[] = {
56 STR_ORDER_DROP_UNLOAD_IF_ACCEPTED, // OUF_UNLOAD_IF_POSSIBLE
57 STR_ORDER_DROP_UNLOAD, // OUFB_UNLOAD
58 STR_ORDER_DROP_TRANSFER, // OUFB_TRANSFER
59 STR_EMPTY,
60 STR_ORDER_DROP_NO_UNLOADING, // OUFB_NO_UNLOAD
61 INVALID_STRING_ID
63 static const uint32 _cargo_type_unload_order_drowdown_hidden_mask = 0x8; // 01000
65 DropDownList* GetSlotDropDownList(Owner owner, TraceRestrictSlotID slot_id, int &selected);
67 struct CargoTypeOrdersWindow : public Window {
68 private:
69 CargoTypeOrdersWindowVariant variant;
71 const Vehicle *vehicle; ///< Vehicle owning the orders being displayed and manipulated.
72 VehicleOrderID order_id; ///< Index of the order concerned by this window.
74 VehicleOrderID order_count; ///< Count of the orders of the vehicle owning this window
75 const Order *order; ///< Order pointer at construction time;
77 static const uint8 CARGO_ICON_WIDTH = 12;
78 static const uint8 CARGO_ICON_HEIGHT = 8;
80 const StringID *cargo_type_order_dropdown; ///< Strings used to populate order dropdowns.
81 uint32 cargo_type_order_dropdown_hmask; ///< Hidden mask for order dropdowns.
83 uint max_cargo_name_width; ///< Greatest width of cargo names.
84 uint max_cargo_dropdown_width; ///< Greatest width of order names.
86 uint set_to_all_dropdown_sel; ///< Selected entry for the 'set to all' dropdown
88 /**
89 * Initialize \c max_cargo_name_width and \c max_cargo_dropdown_width.
90 * @post \c max_cargo_name_width
91 * @post \c max_cargo_dropdown_width
93 void InitMaxWidgetWidth()
95 this->max_cargo_name_width = 0;
96 for (int i = 0; i < _sorted_standard_cargo_specs_size; i++) {
97 SetDParam(0, _sorted_cargo_specs[i]->name);
98 this->max_cargo_name_width = max(this->max_cargo_name_width, GetStringBoundingBox(STR_JUST_STRING).width);
100 this->max_cargo_dropdown_width = 0;
101 for (int i = 0; this->cargo_type_order_dropdown[i] != INVALID_STRING_ID; i++) {
102 SetDParam(0, this->cargo_type_order_dropdown[i]);
103 this->max_cargo_dropdown_width = max(this->max_cargo_dropdown_width, GetStringBoundingBox(STR_JUST_STRING).width);
107 /** Populate the selected entry of order dropdowns. */
108 void InitDropdownSelectedTypes()
110 StringID tooltip = STR_CARGO_TYPE_LOAD_ORDERS_DROP_TOOLTIP + this->variant;
111 const Order *order = this->vehicle->GetOrder(this->order_id);
112 for (int i = 0; i < _sorted_standard_cargo_specs_size; i++) {
113 const CargoSpec *cs = _sorted_cargo_specs[i];
114 CargoID cargo_id = GetCargoIDByBitnum(cs->bitnum);
115 uint8 order_type = (this->variant == CTOWV_LOAD) ? (uint8)order->GetCargoLoadTypeRaw(cargo_id) : (uint8)order->GetCargoUnloadTypeRaw(cargo_id);
116 this->GetWidget<NWidgetCore>(WID_CTO_CARGO_DROPDOWN_FIRST + i)->SetDataTip(this->cargo_type_order_dropdown[order_type], tooltip);
118 this->set_to_all_dropdown_sel = 0;
119 this->GetWidget<NWidgetCore>(WID_CTO_SET_TO_ALL_DROPDOWN)->widget_data = this->cargo_type_order_dropdown[this->set_to_all_dropdown_sel];
123 * Returns the load/unload type of this order for the specified cargo.
124 * @param cargo_id The cargo index for wich we want the load/unload type.
125 * @return an OrderLoadFlags if \c load_variant = true, an OrderUnloadFlags otherwise.
127 uint8 GetOrderActionTypeForCargo(CargoID cargo_id)
129 const Order *order = this->vehicle->GetOrder(this->order_id);
130 return (this->variant == CTOWV_LOAD) ? (uint8)order->GetCargoLoadTypeRaw(cargo_id) : (uint8)order->GetCargoUnloadTypeRaw(cargo_id);
133 bool CheckOrderStillValid() const
135 if (this->vehicle->GetNumOrders() != this->order_count) return false;
136 if (this->vehicle->GetOrder(this->order_id) != this->order) return false;
137 return true;
140 public:
142 * Instantiate a new CargoTypeOrdersWindow.
143 * @param desc The window description.
144 * @param v The vehicle the order belongs to.
145 * @param order_id Which order to display/edit.
146 * @param variant Which aspect of the order to display/edit: load or unload.
147 * @pre \c v != nullptr
149 CargoTypeOrdersWindow(WindowDesc *desc, const Vehicle *v, VehicleOrderID order_id, CargoTypeOrdersWindowVariant variant) : Window(desc)
151 this->variant = variant;
152 this->cargo_type_order_dropdown = (this->variant == CTOWV_LOAD) ? _cargo_type_load_order_drowdown : _cargo_type_unload_order_drowdown;
153 this->cargo_type_order_dropdown_hmask = (this->variant == CTOWV_LOAD) ? _cargo_type_load_order_drowdown_hidden_mask : _cargo_type_unload_order_drowdown_hidden_mask;
154 this->InitMaxWidgetWidth();
156 this->vehicle = v;
157 this->order_id = order_id;
158 this->order_count = v->GetNumOrders();
159 this->order = v->GetOrder(order_id);
161 this->CreateNestedTree(desc);
162 this->GetWidget<NWidgetCore>(WID_CTO_CAPTION)->SetDataTip(STR_CARGO_TYPE_ORDERS_LOAD_CAPTION + this->variant, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS);
163 this->GetWidget<NWidgetCore>(WID_CTO_HEADER)->SetDataTip(STR_CARGO_TYPE_ORDERS_LOAD_TITLE + this->variant, STR_NULL);
164 this->InitDropdownSelectedTypes();
165 this->FinishInitNested(v->index);
167 this->owner = v->owner;
170 ~CargoTypeOrdersWindow()
172 if (!FocusWindowById(WC_VEHICLE_ORDERS, this->window_number)) {
173 MarkAllRouteStepsDirty(this->vehicle);
177 virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override
179 if (widget == WID_CTO_HEADER) {
180 (*size).height = max((*size).height, (uint) WD_FRAMERECT_TOP + FONT_HEIGHT_NORMAL + WD_FRAMERECT_BOTTOM);
181 } else if (WID_CTO_CARGO_LABEL_FIRST <= widget && widget <= WID_CTO_CARGO_LABEL_LAST) {
182 (*size).width = max((*size).width, WD_FRAMERECT_LEFT + this->CARGO_ICON_WIDTH + WD_FRAMETEXT_LEFT + this->max_cargo_name_width + WD_FRAMETEXT_RIGHT + padding.width);
183 (*size).height = max((*size).height, (uint) WD_FRAMERECT_TOP + FONT_HEIGHT_NORMAL + WD_FRAMERECT_BOTTOM);
184 } else if ((WID_CTO_CARGO_DROPDOWN_FIRST <= widget && widget <= WID_CTO_CARGO_DROPDOWN_LAST) || widget == WID_CTO_SET_TO_ALL_DROPDOWN) {
185 (*size).width = max((*size).width, WD_DROPDOWNTEXT_LEFT + this->max_cargo_dropdown_width + WD_DROPDOWNTEXT_RIGHT + NWidgetLeaf::dropdown_dimension.width);
186 (*size).height = max((*size).height, (uint) WD_DROPDOWNTEXT_TOP + FONT_HEIGHT_NORMAL + WD_DROPDOWNTEXT_BOTTOM);
187 } else if (widget == WID_CTO_SET_TO_ALL_LABEL) {
188 (*size).width = max((*size).width, this->max_cargo_name_width + WD_FRAMETEXT_RIGHT + padding.width);
189 (*size).height = max((*size).height, (uint) WD_FRAMERECT_TOP + FONT_HEIGHT_NORMAL + WD_FRAMERECT_BOTTOM);
193 virtual void DrawWidget(const Rect &r, int widget) const override
195 if (WID_CTO_CARGO_LABEL_FIRST <= widget && widget <= WID_CTO_CARGO_LABEL_LAST) {
196 const CargoSpec *cs = _sorted_cargo_specs[widget - WID_CTO_CARGO_LABEL_FIRST];
197 bool rtl = (_current_text_dir == TD_RTL);
199 /* Draw cargo icon. */
200 int rect_left = rtl ? r.right - WD_FRAMETEXT_RIGHT - this->CARGO_ICON_WIDTH : r.left + WD_FRAMERECT_LEFT;
201 int rect_right = rect_left + this->CARGO_ICON_WIDTH;
202 int rect_top = r.top + WD_FRAMERECT_TOP + ((r.bottom - WD_FRAMERECT_BOTTOM - r.top - WD_FRAMERECT_TOP) - this->CARGO_ICON_HEIGHT) / 2;
203 int rect_bottom = rect_top + this->CARGO_ICON_HEIGHT;
204 GfxFillRect(rect_left, rect_top, rect_right, rect_bottom, PC_BLACK);
205 GfxFillRect(rect_left + 1, rect_top + 1, rect_right - 1, rect_bottom - 1, cs->legend_colour);
207 /* Draw cargo name */
208 int text_left = rtl ? r.left + WD_FRAMETEXT_LEFT : rect_right + WD_FRAMETEXT_LEFT;
209 int text_right = rtl ? rect_left - WD_FRAMETEXT_LEFT : r.right - WD_FRAMETEXT_RIGHT;
210 int text_top = r.top + WD_FRAMERECT_TOP;
211 SetDParam(0, cs->name);
212 DrawString(text_left, text_right, text_top, STR_BLACK_STRING);
216 virtual void OnClick(Point pt, int widget, int click_count) override
218 if (!this->CheckOrderStillValid()) {
219 delete this;
220 return;
222 if (widget == WID_CTO_CLOSEBTN) {
223 delete this;
224 } else if (WID_CTO_CARGO_DROPDOWN_FIRST <= widget && widget <= WID_CTO_CARGO_DROPDOWN_LAST) {
225 const CargoSpec *cs = _sorted_cargo_specs[widget - WID_CTO_CARGO_DROPDOWN_FIRST];
226 CargoID cargo_id = GetCargoIDByBitnum(cs->bitnum);
228 ShowDropDownMenu(this, this->cargo_type_order_dropdown, this->GetOrderActionTypeForCargo(cargo_id), widget, 0, this->cargo_type_order_dropdown_hmask);
229 } else if (widget == WID_CTO_SET_TO_ALL_DROPDOWN) {
230 ShowDropDownMenu(this, this->cargo_type_order_dropdown, this->set_to_all_dropdown_sel, widget, 0, this->cargo_type_order_dropdown_hmask);
234 virtual void OnDropdownSelect(int widget, int action_type) override
236 if (!this->CheckOrderStillValid()) {
237 delete this;
238 return;
240 if (WID_CTO_CARGO_DROPDOWN_FIRST <= widget && widget <= WID_CTO_CARGO_DROPDOWN_LAST) {
241 const CargoSpec *cs = _sorted_cargo_specs[widget - WID_CTO_CARGO_DROPDOWN_FIRST];
242 CargoID cargo_id = GetCargoIDByBitnum(cs->bitnum);
243 uint8 order_action_type = this->GetOrderActionTypeForCargo(cargo_id);
245 if (action_type == order_action_type) return;
247 ModifyOrderFlags mof = (this->variant == CTOWV_LOAD) ? MOF_CARGO_TYPE_LOAD : MOF_CARGO_TYPE_UNLOAD;
248 DoCommandP(this->vehicle->tile, this->vehicle->index + (this->order_id << 20), mof | (action_type << 4) | (cargo_id << 20), CMD_MODIFY_ORDER | CMD_MSG(STR_ERROR_CAN_T_MODIFY_THIS_ORDER));
250 this->GetWidget<NWidgetCore>(widget)->SetDataTip(this->cargo_type_order_dropdown[this->GetOrderActionTypeForCargo(cargo_id)], STR_CARGO_TYPE_LOAD_ORDERS_DROP_TOOLTIP + this->variant);
251 this->SetWidgetDirty(widget);
252 } else if (widget == WID_CTO_SET_TO_ALL_DROPDOWN) {
253 for (int i = 0; i < _sorted_standard_cargo_specs_size; i++) {
254 this->OnDropdownSelect(i + WID_CTO_CARGO_DROPDOWN_FIRST, action_type);
257 if (action_type != (int) this->set_to_all_dropdown_sel) {
258 this->set_to_all_dropdown_sel = action_type;
259 this->GetWidget<NWidgetCore>(widget)->widget_data = this->cargo_type_order_dropdown[this->set_to_all_dropdown_sel];
260 this->SetWidgetDirty(widget);
265 virtual void SetStringParameters(int widget) const override
267 if (!this->CheckOrderStillValid()) {
268 return;
270 if (widget == WID_CTO_CAPTION) {
271 SetDParam(0, this->vehicle->index);
272 SetDParam(1, this->order_id + 1);
273 SetDParam(2, this->vehicle->GetOrder(this->order_id)->GetDestination());
277 virtual void OnFocus(Window *previously_focused_window) override
279 if (HasFocusedVehicleChanged(this->window_number, previously_focused_window)) {
280 MarkAllRoutePathsDirty(this->vehicle);
281 MarkAllRouteStepsDirty(this->vehicle);
285 virtual void OnFocusLost(Window *newly_focused_window) override
287 if (HasFocusedVehicleChanged(this->window_number, newly_focused_window)) {
288 MarkAllRoutePathsDirty(this->vehicle);
289 MarkAllRouteStepsDirty(this->vehicle);
294 * Some data on this window has become invalid.
295 * @param data Information about the changed data.
296 * @param gui_scope Whether the call is done from GUI scope. You may not do everything when not in GUI scope. See #InvalidateWindowData() for details.
298 virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
300 if (!this->CheckOrderStillValid()) {
301 delete this;
302 return;
304 if (gui_scope) {
305 this->InitDropdownSelectedTypes();
306 this->SetDirty();
312 * Make a list of panel for each available cargo type.
313 * Each panel contains a label to display the cargo name.
314 * @param biggest_index Storage for collecting the biggest index used in the returned tree
315 * @return A vertical container of cargo type orders rows.
316 * @post \c *biggest_index contains the largest used index in the tree.
318 static NWidgetBase *MakeCargoTypeOrdersRows(int *biggest_index)
320 NWidgetVertical *ver = new NWidgetVertical;
322 for (int i = 0; i < _sorted_standard_cargo_specs_size; i++) {
323 /* Cargo row */
324 NWidgetBackground *panel = new NWidgetBackground(WWT_PANEL, COLOUR_GREY, WID_CTO_CARGO_ROW_FIRST + i);
325 ver->Add(panel);
326 NWidgetHorizontal *horiz = new NWidgetHorizontal;
327 panel->Add(horiz);
328 /* Cargo label */
329 NWidgetBackground *label = new NWidgetBackground(WWT_PANEL, COLOUR_GREY, WID_CTO_CARGO_LABEL_FIRST + i);
330 label->SetFill(1, 0);
331 label->SetResize(1, 0);
332 horiz->Add(label);
333 /* Orders dropdown */
334 NWidgetLeaf *dropdown = new NWidgetLeaf(WWT_DROPDOWN, COLOUR_GREY, WID_CTO_CARGO_DROPDOWN_FIRST + i, STR_NULL, STR_EMPTY);
335 dropdown->SetFill(1, 0);
336 dropdown->SetResize(1, 0);
337 horiz->Add(dropdown);
340 *biggest_index = WID_CTO_CARGO_DROPDOWN_LAST;
341 return ver;
344 /** Widgets definition of CargoTypeOrdersWindow. */
345 static const NWidgetPart _nested_cargo_type_orders_widgets[] = {
346 NWidget(NWID_HORIZONTAL),
347 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
348 NWidget(WWT_CAPTION, COLOUR_GREY, WID_CTO_CAPTION), SetDataTip(STR_NULL, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
349 EndContainer(),
350 NWidget(WWT_PANEL, COLOUR_GREY),
351 NWidget(WWT_LABEL, COLOUR_GREY, WID_CTO_HEADER), SetFill(1, 0), SetResize(1, 0), SetDataTip(STR_NULL, STR_NULL),
352 EndContainer(),
353 NWidgetFunction(MakeCargoTypeOrdersRows),
354 NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(1, 4), SetFill(1, 0), SetResize(1, 0), EndContainer(), // SPACER
355 NWidget(NWID_HORIZONTAL),
356 NWidget(WWT_PANEL, COLOUR_GREY),
357 NWidget(WWT_TEXT, COLOUR_GREY, WID_CTO_SET_TO_ALL_LABEL), SetPadding(0, 0, 0, WD_FRAMERECT_LEFT + 12 + WD_FRAMETEXT_LEFT), SetFill(1, 0), SetResize(1, 0), SetDataTip(STR_CARGO_TYPE_ORDERS_SET_TO_ALL_LABEL, STR_CARGO_TYPE_ORDERS_SET_TO_ALL_TOOLTIP),
358 EndContainer(),
359 NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_CTO_SET_TO_ALL_DROPDOWN), SetFill(1, 0), SetResize(1, 0), SetDataTip(STR_NULL, STR_CARGO_TYPE_ORDERS_SET_TO_ALL_TOOLTIP),
360 EndContainer(),
361 NWidget(NWID_HORIZONTAL),
362 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_CTO_CLOSEBTN), SetFill(1, 0), SetResize(1, 0), SetDataTip(STR_CARGO_TYPE_ORDERS_CLOSE_BUTTON, STR_TOOLTIP_CLOSE_WINDOW),
363 NWidget(WWT_RESIZEBOX, COLOUR_GREY),
364 EndContainer(),
367 /** Window description for the 'load' variant of CargoTypeOrdersWindow. */
368 static WindowDesc _cargo_type_load_orders_widgets (
369 WDP_AUTO, "view_cargo_type_load_order", 195, 186,
370 WC_VEHICLE_CARGO_TYPE_LOAD_ORDERS, WC_VEHICLE_ORDERS,
371 WDF_CONSTRUCTION,
372 _nested_cargo_type_orders_widgets, lengthof(_nested_cargo_type_orders_widgets)
375 /** Window description for the 'unload' variant of CargoTypeOrdersWindow. */
376 static WindowDesc _cargo_type_unload_orders_widgets (
377 WDP_AUTO, "view_cargo_type_unload_order", 195, 186,
378 WC_VEHICLE_CARGO_TYPE_UNLOAD_ORDERS, WC_VEHICLE_ORDERS,
379 WDF_CONSTRUCTION,
380 _nested_cargo_type_orders_widgets, lengthof(_nested_cargo_type_orders_widgets)
384 * Show the CargoTypeOrdersWindow for an order.
385 * @param v The vehicle the order belongs to.
386 * @param parent The parent window.
387 * @param order_id Which order to display/edit.
388 * @param variant Which aspect of the order to display/edit: load or unload.
389 * @pre \c v != nullptr
391 void ShowCargoTypeOrdersWindow(const Vehicle *v, Window *parent, VehicleOrderID order_id, CargoTypeOrdersWindowVariant variant)
393 WindowDesc &desc = (variant == CTOWV_LOAD) ? _cargo_type_load_orders_widgets : _cargo_type_unload_orders_widgets;
394 DeleteWindowById(desc.cls, v->index);
395 CargoTypeOrdersWindow *w = new CargoTypeOrdersWindow(&desc, v, order_id, variant);
396 w->parent = parent;
400 /** Order load types that could be given to station orders. */
401 static const StringID _station_load_types[][9][9] = {
403 /* No refitting. */
405 STR_EMPTY,
406 INVALID_STRING_ID,
407 STR_ORDER_FULL_LOAD,
408 STR_ORDER_FULL_LOAD_ANY,
409 STR_ORDER_NO_LOAD,
410 INVALID_STRING_ID,
411 INVALID_STRING_ID,
412 INVALID_STRING_ID,
413 STR_ORDER_CARGO_TYPE_LOAD,
414 }, {
415 STR_ORDER_UNLOAD,
416 INVALID_STRING_ID,
417 STR_ORDER_UNLOAD_FULL_LOAD,
418 STR_ORDER_UNLOAD_FULL_LOAD_ANY,
419 STR_ORDER_UNLOAD_NO_LOAD,
420 INVALID_STRING_ID,
421 INVALID_STRING_ID,
422 INVALID_STRING_ID,
423 STR_ORDER_UNLOAD_CARGO_TYPE_LOAD,
424 }, {
425 STR_ORDER_TRANSFER,
426 INVALID_STRING_ID,
427 STR_ORDER_TRANSFER_FULL_LOAD,
428 STR_ORDER_TRANSFER_FULL_LOAD_ANY,
429 STR_ORDER_TRANSFER_NO_LOAD,
430 INVALID_STRING_ID,
431 INVALID_STRING_ID,
432 INVALID_STRING_ID,
433 STR_ORDER_TRANSFER_CARGO_TYPE_LOAD,
434 }, {
435 /* Unload and transfer do not work together. */
436 INVALID_STRING_ID, INVALID_STRING_ID, INVALID_STRING_ID,
437 INVALID_STRING_ID, INVALID_STRING_ID, INVALID_STRING_ID,
438 INVALID_STRING_ID, INVALID_STRING_ID, INVALID_STRING_ID,
439 }, {
440 STR_ORDER_NO_UNLOAD,
441 INVALID_STRING_ID,
442 STR_ORDER_NO_UNLOAD_FULL_LOAD,
443 STR_ORDER_NO_UNLOAD_FULL_LOAD_ANY,
444 STR_ORDER_NO_UNLOAD_NO_LOAD,
445 INVALID_STRING_ID,
446 INVALID_STRING_ID,
447 INVALID_STRING_ID,
448 STR_ORDER_NO_UNLOAD_CARGO_TYPE_LOAD,
449 }, {
450 INVALID_STRING_ID, INVALID_STRING_ID, INVALID_STRING_ID,
451 INVALID_STRING_ID, INVALID_STRING_ID, INVALID_STRING_ID,
452 INVALID_STRING_ID, INVALID_STRING_ID, INVALID_STRING_ID,
453 }, {
454 INVALID_STRING_ID, INVALID_STRING_ID, INVALID_STRING_ID,
455 INVALID_STRING_ID, INVALID_STRING_ID, INVALID_STRING_ID,
456 INVALID_STRING_ID, INVALID_STRING_ID, INVALID_STRING_ID,
457 }, {
458 INVALID_STRING_ID, INVALID_STRING_ID, INVALID_STRING_ID,
459 INVALID_STRING_ID, INVALID_STRING_ID, INVALID_STRING_ID,
460 INVALID_STRING_ID, INVALID_STRING_ID, INVALID_STRING_ID,
461 }, {
462 STR_ORDER_CARGO_TYPE_UNLOAD,
463 INVALID_STRING_ID,
464 STR_ORDER_CARGO_TYPE_UNLOAD_FULL_LOAD,
465 STR_ORDER_CARGO_TYPE_UNLOAD_FULL_LOAD_ANY,
466 STR_ORDER_CARGO_TYPE_UNLOAD_NO_LOAD,
467 INVALID_STRING_ID,
468 INVALID_STRING_ID,
469 INVALID_STRING_ID,
470 STR_ORDER_CARGO_TYPE_UNLOAD_CARGO_TYPE_LOAD,
472 }, {
473 /* With auto-refitting. No loading and auto-refitting do not work together. */
475 STR_ORDER_AUTO_REFIT,
476 INVALID_STRING_ID,
477 STR_ORDER_FULL_LOAD_REFIT,
478 STR_ORDER_FULL_LOAD_ANY_REFIT,
479 INVALID_STRING_ID,
480 INVALID_STRING_ID,
481 INVALID_STRING_ID,
482 INVALID_STRING_ID,
483 STR_ORDER_CARGO_TYPE_LOAD_REFIT,
484 }, {
485 STR_ORDER_UNLOAD_REFIT,
486 INVALID_STRING_ID,
487 STR_ORDER_UNLOAD_FULL_LOAD_REFIT,
488 STR_ORDER_UNLOAD_FULL_LOAD_ANY_REFIT,
489 INVALID_STRING_ID,
490 INVALID_STRING_ID,
491 INVALID_STRING_ID,
492 INVALID_STRING_ID,
493 STR_ORDER_UNLOAD_CARGO_TYPE_LOAD_REFIT,
494 }, {
495 STR_ORDER_TRANSFER_REFIT,
496 INVALID_STRING_ID,
497 STR_ORDER_TRANSFER_FULL_LOAD_REFIT,
498 STR_ORDER_TRANSFER_FULL_LOAD_ANY_REFIT,
499 INVALID_STRING_ID,
500 INVALID_STRING_ID,
501 INVALID_STRING_ID,
502 INVALID_STRING_ID,
503 STR_ORDER_TRANSFER_CARGO_TYPE_LOAD_REFIT,
504 }, {
505 /* Unload and transfer do not work together. */
506 INVALID_STRING_ID, INVALID_STRING_ID, INVALID_STRING_ID,
507 INVALID_STRING_ID, INVALID_STRING_ID, INVALID_STRING_ID,
508 INVALID_STRING_ID, INVALID_STRING_ID, INVALID_STRING_ID,
509 }, {
510 STR_ORDER_NO_UNLOAD_REFIT,
511 INVALID_STRING_ID,
512 STR_ORDER_NO_UNLOAD_FULL_LOAD_REFIT,
513 STR_ORDER_NO_UNLOAD_FULL_LOAD_ANY_REFIT,
514 INVALID_STRING_ID,
515 INVALID_STRING_ID,
516 INVALID_STRING_ID,
517 INVALID_STRING_ID,
518 STR_ORDER_NO_UNLOAD_CARGO_TYPE_LOAD_REFIT,
519 }, {
520 INVALID_STRING_ID, INVALID_STRING_ID, INVALID_STRING_ID,
521 INVALID_STRING_ID, INVALID_STRING_ID, INVALID_STRING_ID,
522 INVALID_STRING_ID, INVALID_STRING_ID, INVALID_STRING_ID,
523 }, {
524 INVALID_STRING_ID, INVALID_STRING_ID, INVALID_STRING_ID,
525 INVALID_STRING_ID, INVALID_STRING_ID, INVALID_STRING_ID,
526 INVALID_STRING_ID, INVALID_STRING_ID, INVALID_STRING_ID,
527 }, {
528 INVALID_STRING_ID, INVALID_STRING_ID, INVALID_STRING_ID,
529 INVALID_STRING_ID, INVALID_STRING_ID, INVALID_STRING_ID,
530 INVALID_STRING_ID, INVALID_STRING_ID, INVALID_STRING_ID,
531 }, {
532 STR_ORDER_CARGO_TYPE_UNLOAD_REFIT,
533 INVALID_STRING_ID,
534 STR_ORDER_CARGO_TYPE_UNLOAD_FULL_LOAD_REFIT,
535 STR_ORDER_CARGO_TYPE_UNLOAD_FULL_LOAD_ANY_REFIT,
536 INVALID_STRING_ID,
537 INVALID_STRING_ID,
538 INVALID_STRING_ID,
539 INVALID_STRING_ID,
540 STR_ORDER_CARGO_TYPE_UNLOAD_CARGO_TYPE_LOAD_REFIT,
545 static const StringID _order_non_stop_drowdown[] = {
546 STR_ORDER_GO_TO,
547 STR_ORDER_GO_NON_STOP_TO,
548 STR_ORDER_GO_VIA,
549 STR_ORDER_GO_NON_STOP_VIA,
550 INVALID_STRING_ID
553 static const StringID _order_full_load_drowdown[] = {
554 STR_ORDER_DROP_LOAD_IF_POSSIBLE,
555 STR_EMPTY,
556 STR_ORDER_DROP_FULL_LOAD_ALL,
557 STR_ORDER_DROP_FULL_LOAD_ANY,
558 STR_ORDER_DROP_NO_LOADING,
559 STR_EMPTY,
560 STR_EMPTY,
561 STR_EMPTY,
562 STR_ORDER_DROP_CARGO_TYPE_LOAD,
563 INVALID_STRING_ID
566 static const StringID _order_unload_drowdown[] = {
567 STR_ORDER_DROP_UNLOAD_IF_ACCEPTED,
568 STR_ORDER_DROP_UNLOAD,
569 STR_ORDER_DROP_TRANSFER,
570 STR_EMPTY,
571 STR_ORDER_DROP_NO_UNLOADING,
572 STR_EMPTY,
573 STR_EMPTY,
574 STR_EMPTY,
575 STR_ORDER_DROP_CARGO_TYPE_UNLOAD,
576 INVALID_STRING_ID
579 static const StringID _order_goto_dropdown[] = {
580 STR_ORDER_GO_TO,
581 STR_ORDER_GO_TO_NEAREST_DEPOT,
582 STR_ORDER_CONDITIONAL,
583 STR_ORDER_SHARE,
584 INVALID_STRING_ID
587 static const StringID _order_goto_dropdown_aircraft[] = {
588 STR_ORDER_GO_TO,
589 STR_ORDER_GO_TO_NEAREST_HANGAR,
590 STR_ORDER_CONDITIONAL,
591 STR_ORDER_SHARE,
592 INVALID_STRING_ID
595 /** Variables for conditional orders; this defines the order of appearance in the dropdown box */
596 static const OrderConditionVariable _order_conditional_variable[] = {
597 OCV_LOAD_PERCENTAGE,
598 OCV_RELIABILITY,
599 OCV_MAX_SPEED,
600 OCV_AGE,
601 OCV_REMAINING_LIFETIME,
602 OCV_REQUIRES_SERVICE,
603 OCV_CARGO_WAITING,
604 OCV_CARGO_ACCEPTANCE,
605 OCV_FREE_PLATFORMS,
606 OCV_SLOT_OCCUPANCY,
607 OCV_PERCENT,
608 OCV_UNCONDITIONALLY,
611 static const StringID _order_conditional_condition[] = {
612 STR_ORDER_CONDITIONAL_COMPARATOR_EQUALS,
613 STR_ORDER_CONDITIONAL_COMPARATOR_NOT_EQUALS,
614 STR_ORDER_CONDITIONAL_COMPARATOR_LESS_THAN,
615 STR_ORDER_CONDITIONAL_COMPARATOR_LESS_EQUALS,
616 STR_ORDER_CONDITIONAL_COMPARATOR_MORE_THAN,
617 STR_ORDER_CONDITIONAL_COMPARATOR_MORE_EQUALS,
618 STR_ORDER_CONDITIONAL_COMPARATOR_IS_TRUE,
619 STR_ORDER_CONDITIONAL_COMPARATOR_IS_FALSE,
620 INVALID_STRING_ID,
623 static const StringID _order_conditional_condition_has[] = {
624 STR_ORDER_CONDITIONAL_COMPARATOR_HAS,
625 STR_ORDER_CONDITIONAL_COMPARATOR_HAS_NO,
626 STR_ORDER_CONDITIONAL_COMPARATOR_HAS_LESS_THAN,
627 STR_ORDER_CONDITIONAL_COMPARATOR_HAS_LESS_EQUALS,
628 STR_ORDER_CONDITIONAL_COMPARATOR_HAS_MORE_THAN,
629 STR_ORDER_CONDITIONAL_COMPARATOR_HAS_MORE_EQUALS,
630 STR_ORDER_CONDITIONAL_COMPARATOR_HAS,
631 STR_ORDER_CONDITIONAL_COMPARATOR_HAS_NO,
632 INVALID_STRING_ID,
635 static const StringID _order_conditional_condition_accepts[] = {
636 STR_NULL,
637 STR_NULL,
638 STR_NULL,
639 STR_NULL,
640 STR_NULL,
641 STR_NULL,
642 STR_ORDER_CONDITIONAL_COMPARATOR_ACCEPTS,
643 STR_ORDER_CONDITIONAL_COMPARATOR_DOES_NOT_ACCEPT,
644 INVALID_STRING_ID,
647 static const StringID _order_conditional_condition_is_fully_occupied[] = {
648 STR_NULL,
649 STR_NULL,
650 STR_NULL,
651 STR_NULL,
652 STR_NULL,
653 STR_NULL,
654 STR_ORDER_CONDITIONAL_COMPARATOR_FULLY_OCCUPIED,
655 STR_ORDER_CONDITIONAL_COMPARATOR_NOT_YET_FULLY_OCCUPIED,
656 INVALID_STRING_ID,
659 extern uint ConvertSpeedToDisplaySpeed(uint speed);
660 extern uint ConvertDisplaySpeedToSpeed(uint speed);
662 static const StringID _order_depot_action_dropdown[] = {
663 STR_ORDER_DROP_GO_ALWAYS_DEPOT,
664 STR_ORDER_DROP_SERVICE_DEPOT,
665 STR_ORDER_DROP_HALT_DEPOT,
666 INVALID_STRING_ID
669 static int DepotActionStringIndex(const Order *order)
671 if (order->GetDepotActionType() & ODATFB_HALT) {
672 return DA_STOP;
673 } else if (order->GetDepotOrderType() & ODTFB_SERVICE) {
674 return DA_SERVICE;
675 } else {
676 return DA_ALWAYS_GO;
680 static const StringID _order_refit_action_dropdown[] = {
681 STR_ORDER_DROP_REFIT_AUTO,
682 STR_ORDER_DROP_REFIT_AUTO_ANY,
683 INVALID_STRING_ID
687 * Draws an order in order or timetable GUI
688 * @param v Vehicle the order belongs to
689 * @param order The order to draw
690 * @param order_index Index of the order in the orders of the vehicle
691 * @param y Y position for drawing
692 * @param selected True, if the order is selected
693 * @param timetable True, when drawing in the timetable GUI
694 * @param left Left border for text drawing
695 * @param middle X position between order index and order text
696 * @param right Right border for text drawing
698 void DrawOrderString(const Vehicle *v, const Order *order, int order_index, int y, bool selected, bool timetable, int left, int middle, int right)
700 bool rtl = _current_text_dir == TD_RTL;
702 SpriteID sprite = rtl ? SPR_ARROW_LEFT : SPR_ARROW_RIGHT;
703 Dimension sprite_size = GetSpriteSize(sprite);
704 if (v->cur_real_order_index == order_index) {
705 DrawSprite(sprite, PAL_NONE, rtl ? right - sprite_size.width : left, y + ((int)FONT_HEIGHT_NORMAL - (int)sprite_size.height) / 2);
706 DrawSprite(sprite, PAL_NONE, rtl ? right - 2 * sprite_size.width : left + sprite_size.width, y + ((int)FONT_HEIGHT_NORMAL - (int)sprite_size.height) / 2);
707 } else if (v->cur_implicit_order_index == order_index) {
708 DrawSprite(sprite, PAL_NONE, rtl ? right - sprite_size.width : left, y + ((int)FONT_HEIGHT_NORMAL - (int)sprite_size.height) / 2);
711 TextColour colour = TC_BLACK;
712 if (order->IsType(OT_IMPLICIT)) {
713 colour = (selected ? TC_SILVER : TC_GREY) | TC_NO_SHADE;
714 } else if (selected) {
715 colour = TC_WHITE;
718 SetDParam(0, order_index + 1);
719 DrawString(left, rtl ? right - 2 * sprite_size.width - 3 : middle, y, STR_ORDER_INDEX, colour, SA_RIGHT | SA_FORCE);
721 SetDParam(5, STR_EMPTY);
723 /* Check range for aircraft. */
724 if (v->type == VEH_AIRCRAFT && Aircraft::From(v)->GetRange() > 0 && order->IsGotoOrder()) {
725 const Order *next = order->next != nullptr ? order->next : v->GetFirstOrder();
726 if (GetOrderDistance(order, next, v) > Aircraft::From(v)->acache.cached_max_range_sqr) SetDParam(8, STR_ORDER_OUT_OF_RANGE);
729 switch (order->GetType()) {
730 case OT_DUMMY:
731 SetDParam(0, STR_INVALID_ORDER);
732 SetDParam(1, order->GetDestination());
733 break;
735 case OT_IMPLICIT:
736 SetDParam(0, STR_ORDER_GO_TO_STATION);
737 SetDParam(1, STR_ORDER_GO_TO);
738 SetDParam(2, order->GetDestination());
739 SetDParam(3, timetable ? STR_EMPTY : STR_ORDER_IMPLICIT);
740 break;
742 case OT_GOTO_STATION: {
743 OrderLoadFlags load = order->GetLoadType();
744 OrderUnloadFlags unload = order->GetUnloadType();
746 SetDParam(0, STR_ORDER_GO_TO_STATION);
747 SetDParam(1, STR_ORDER_GO_TO + (v->IsGroundVehicle() ? order->GetNonStopType() : 0));
748 SetDParam(2, order->GetDestination());
750 if (timetable) {
751 SetDParam(3, STR_EMPTY);
753 if (order->GetWaitTime() > 0) {
754 SetDParam(5, order->IsWaitTimetabled() ? STR_TIMETABLE_STAY_FOR_MINUTES : STR_TIMETABLE_STAY_FOR_MINUTES_ESTIMATED);
755 SetTimetableParams(order->GetWaitTime(), 6);
757 } else {
758 SetDParam(3, (order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION) ? STR_EMPTY : _station_load_types[order->IsRefit()][unload][load]);
759 if (order->IsRefit()) {
760 SetDParam(4, order->IsAutoRefit() ? STR_ORDER_AUTO_REFIT_ANY : CargoSpec::Get(order->GetRefitCargo())->name);
762 if (v->type == VEH_TRAIN && (order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION) == 0) {
763 SetDParam(5, order->GetStopLocation() + STR_ORDER_STOP_LOCATION_NEAR_END);
766 break;
769 case OT_GOTO_DEPOT:
770 if (order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) {
771 SetDParam(0, STR_ORDER_GO_TO_NEAREST_DEPOT_FORMAT);
772 if (v->type == VEH_AIRCRAFT) {
773 SetDParam(2, STR_ORDER_NEAREST_HANGAR);
774 SetDParam(3, STR_EMPTY);
775 } else {
776 SetDParam(2, STR_ORDER_NEAREST_DEPOT);
777 SetDParam(3, STR_ORDER_TRAIN_DEPOT + v->type);
779 } else {
780 SetDParam(0, STR_ORDER_GO_TO_DEPOT_FORMAT);
781 SetDParam(2, v->type);
782 SetDParam(3, order->GetDestination());
785 if (order->GetDepotOrderType() & ODTFB_SERVICE) {
786 SetDParam(1, (order->GetNonStopType() & ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS) ? STR_ORDER_SERVICE_NON_STOP_AT : STR_ORDER_SERVICE_AT);
787 } else {
788 SetDParam(1, (order->GetNonStopType() & ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS) ? STR_ORDER_GO_NON_STOP_TO : STR_ORDER_GO_TO);
791 if (!timetable && (order->GetDepotActionType() & ODATFB_HALT)) {
792 SetDParam(5, STR_ORDER_STOP_ORDER);
795 if (!timetable && order->IsRefit()) {
796 SetDParam(5, (order->GetDepotActionType() & ODATFB_HALT) ? STR_ORDER_REFIT_STOP_ORDER : STR_ORDER_REFIT_ORDER);
797 SetDParam(6, CargoSpec::Get(order->GetRefitCargo())->name);
800 if (timetable) {
801 if (order->GetWaitTime() > 0) {
802 SetDParam(5, order->IsWaitTimetabled() ? STR_TIMETABLE_STAY_FOR_MINUTES : STR_TIMETABLE_STAY_FOR_MINUTES_ESTIMATED);
803 SetTimetableParams(order->GetWaitTime(), 6);
806 break;
808 case OT_GOTO_WAYPOINT: {
809 StringID str = (order->GetNonStopType() & ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS) ? STR_ORDER_GO_NON_STOP_TO_WAYPOINT : STR_ORDER_GO_TO_WAYPOINT;
810 if (order->GetWaypointFlags() & OWF_REVERSE) str += STR_ORDER_GO_TO_WAYPOINT_REVERSE - STR_ORDER_GO_TO_WAYPOINT;
811 SetDParam(0, str);
812 SetDParam(1, order->GetDestination());
813 break;
816 case OT_CONDITIONAL: {
817 SetDParam(1, order->GetConditionSkipToOrder() + 1);
818 const OrderConditionVariable ocv = order->GetConditionVariable();
819 /* handle some non-ordinary cases seperately */
820 if (ocv == OCV_UNCONDITIONALLY) {
821 SetDParam(0, STR_ORDER_CONDITIONAL_UNCONDITIONAL);
823 else if (ocv == OCV_PERCENT) {
824 SetDParam(0, STR_CONDITIONAL_PERCENT);
825 SetDParam(2, order->GetConditionValue());
827 else if (ocv == OCV_FREE_PLATFORMS) {
828 SetDParam(0, STR_CONDITIONAL_FREE_PLATFORMS);
829 SetDParam(2, STR_ORDER_CONDITIONAL_COMPARATOR_HAS + order->GetConditionComparator());
830 SetDParam(3, order->GetConditionValue());
832 else if (ocv == OCV_SLOT_OCCUPANCY) {
833 if (TraceRestrictSlot::IsValidID(order->GetXData())) {
834 SetDParam(0, STR_ORDER_CONDITIONAL_SLOT);
835 SetDParam(2, order->GetXData());
836 } else {
837 SetDParam(0, STR_ORDER_CONDITIONAL_INVALID_SLOT);
838 SetDParam(2, STR_TRACE_RESTRICT_VARIABLE_UNDEFINED);
840 SetDParam(3, order->GetConditionComparator() == OCC_IS_TRUE ? STR_ORDER_CONDITIONAL_COMPARATOR_FULLY_OCCUPIED : STR_ORDER_CONDITIONAL_COMPARATOR_NOT_YET_FULLY_OCCUPIED);
842 else {
843 OrderConditionComparator occ = order->GetConditionComparator();
844 bool is_cargo = ocv == OCV_CARGO_ACCEPTANCE || ocv == OCV_CARGO_WAITING;
845 SetDParam(0, is_cargo ? STR_ORDER_CONDITIONAL_CARGO : (occ == OCC_IS_TRUE || occ == OCC_IS_FALSE) ? STR_ORDER_CONDITIONAL_TRUE_FALSE : STR_ORDER_CONDITIONAL_NUM);
846 SetDParam(2, (ocv == OCV_CARGO_ACCEPTANCE || ocv == OCV_CARGO_WAITING || ocv == OCV_FREE_PLATFORMS) ? STR_ORDER_CONDITIONAL_NEXT_STATION : STR_ORDER_CONDITIONAL_LOAD_PERCENTAGE + ocv);
848 uint value = order->GetConditionValue();
849 switch (ocv) {
850 case OCV_CARGO_ACCEPTANCE:
851 SetDParam(3, STR_ORDER_CONDITIONAL_COMPARATOR_ACCEPTS + occ - OCC_IS_TRUE);
852 SetDParam(4, CargoSpec::Get(value)->name);
853 break;
854 case OCV_CARGO_WAITING:
855 SetDParam(3, STR_ORDER_CONDITIONAL_COMPARATOR_HAS + occ - OCC_IS_TRUE);
856 SetDParam(4, CargoSpec::Get(value)->name);
857 break;
858 case OCV_REQUIRES_SERVICE:
859 SetDParam(3, STR_ORDER_CONDITIONAL_COMPARATOR_EQUALS + occ);
860 break;
861 case OCV_MAX_SPEED:
862 value = ConvertSpeedToDisplaySpeed(value);
863 /* FALL THROUGH */
864 default:
865 SetDParam(3, STR_ORDER_CONDITIONAL_COMPARATOR_EQUALS + occ);
866 SetDParam(4, value);
870 if (timetable && order->GetWaitTime() > 0) {
871 SetDParam(5, order->IsWaitTimetabled() ? STR_TIMETABLE_AND_TRAVEL_FOR_MINUTES : STR_TIMETABLE_AND_TRAVEL_FOR_MINUTES_ESTIMATED);
872 SetTimetableParams(order->GetWaitTime(), 6);
874 else {
875 SetDParam(5, STR_EMPTY);
877 break;
880 default: NOT_REACHED();
883 DrawString(rtl ? left : middle, rtl ? middle : right, y, STR_ORDER_TEXT_MINUTES, colour);
887 * Get the order command a vehicle can do in a given tile.
888 * @param v Vehicle involved.
889 * @param tile Tile being queried.
890 * @return The order associated to vehicle v in given tile (or empty order if vehicle can do nothing in the tile).
892 static Order GetOrderCmdFromTile(const Vehicle *v, TileIndex tile)
894 /* Hack-ish; unpack order 0, so everything gets initialised with either zero
895 * or a suitable default value for the variable. Then also override the index
896 * as it is not coming from a pool, so would be initialised. */
897 Order order(0);
898 order.index = 0;
900 /* check depot first */
901 if (IsDepotTypeTile(tile, (TransportType)(uint)v->type) && IsTileOwner(tile, _local_company)) {
902 order.MakeGoToDepot(v->type == VEH_AIRCRAFT ? GetStationIndex(tile) : GetDepotIndex(tile),
903 ODTFB_PART_OF_ORDERS,
904 (_settings_client.gui.new_nonstop && v->IsGroundVehicle()) ? ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS : ONSF_STOP_EVERYWHERE);
906 if (_ctrl_pressed) order.SetDepotOrderType((OrderDepotTypeFlags)(order.GetDepotOrderType() ^ ODTFB_SERVICE));
908 return order;
911 /* check rail waypoint */
912 if (IsRailWaypointTile(tile) &&
913 v->type == VEH_TRAIN &&
914 IsTileOwner(tile, _local_company)) {
915 order.MakeGoToWaypoint(GetStationIndex(tile));
916 if (_settings_client.gui.new_nonstop != _ctrl_pressed) order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
917 return order;
920 /* check buoy (no ownership) */
921 if (IsBuoyTile(tile) && v->type == VEH_SHIP) {
922 order.MakeGoToWaypoint(GetStationIndex(tile));
923 return order;
926 if (IsTileType(tile, MP_STATION)) {
927 StationID st_index = GetStationIndex(tile);
928 const Station *st = Station::Get(st_index);
930 if (st->owner == _local_company || st->owner == OWNER_NONE) {
931 byte facil;
932 (facil = FACIL_DOCK, v->type == VEH_SHIP) ||
933 (facil = FACIL_TRAIN, v->type == VEH_TRAIN) ||
934 (facil = FACIL_AIRPORT, v->type == VEH_AIRCRAFT) ||
935 (facil = FACIL_BUS_STOP, v->type == VEH_ROAD && RoadVehicle::From(v)->IsBus()) ||
936 (facil = FACIL_TRUCK_STOP, 1);
937 if (st->facilities & facil) {
938 order.MakeGoToStation(st_index);
940 if (_ctrl_pressed && !_shift_pressed) {
941 order.SetLoadType(OLF_FULL_LOAD_ANY);
942 order.SetUnloadType(OUFB_NO_UNLOAD);
943 } else if (!_ctrl_pressed && _shift_pressed) {
944 order.SetLoadType(OLFB_NO_LOAD);
945 order.SetUnloadType(OUFB_UNLOAD);
946 } else if (_ctrl_pressed && _shift_pressed) {
947 order.SetLoadType(OLFB_NO_LOAD);
948 order.SetUnloadType(OUFB_TRANSFER);
951 if (_settings_client.gui.new_nonstop && v->IsGroundVehicle()) {
952 if (order.GetNonStopType() == ONSF_NO_STOP_AT_DESTINATION_STATION) {
953 order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
954 } else {
955 order.SetNonStopType(ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS);
959 order.SetStopLocation(v->type == VEH_TRAIN ? (OrderStopLocation)(_settings_client.gui.stop_location) : OSL_PLATFORM_FAR_END);
960 return order;
965 /* not found */
966 order.Free();
967 return order;
970 /** Hotkeys for order window. */
971 enum {
972 OHK_SKIP,
973 OHK_DELETE,
974 OHK_GOTO,
975 OHK_NONSTOP,
976 OHK_FULLLOAD,
977 OHK_UNLOAD,
978 OHK_NEAREST_DEPOT,
979 OHK_ALWAYS_SERVICE,
980 OHK_TRANSFER,
981 OHK_NO_UNLOAD,
982 OHK_NO_LOAD,
987 /** Confirm replacement of the existing orders with shared orders
988 * @param w parent Window
989 * @param confirmed true if confirmed, false otherwise
991 void AskShareOrdersCallback(Window *w, bool confirmed);
996 * %Order window code for all vehicles.
998 * At the bottom of the window two button rows are located for changing the orders of the vehicle.
1000 * \section top-row Top row
1001 * The top-row is for manipulating an individual order. What row is displayed depends on the type of vehicle, and whether or not you are the owner of the vehicle.
1003 * The top-row buttons of one of your trains or road vehicles is one of the following three cases:
1004 * \verbatim
1005 * +-----------------+-----------------+-----------------+-----------------+
1006 * | NON-STOP | FULL_LOAD | UNLOAD | REFIT | (normal)
1007 * +-----------------+-----+-----------+-----------+-----+-----------------+
1008 * | COND_VAR | COND_COMPARATOR | COND_VALUE | (for conditional orders)
1009 * +-----------------+-----+-----------+-----------+-----+-----------------+
1010 * | NON-STOP | REFIT | SERVICE | (empty) | (for depot orders)
1011 * +-----------------+-----------------+-----------------+-----------------+
1012 * \endverbatim
1014 * Airplanes and ships have one of the following three top-row button rows:
1015 * \verbatim
1016 * +-----------------+-----------------+-----------------+
1017 * | FULL_LOAD | UNLOAD | REFIT | (normal)
1018 * +-----------------+-----------------+-----------------+
1019 * | COND_VAR | COND_COMPARATOR | COND_VALUE | (for conditional orders)
1020 * +-----------------+--------+--------+-----------------+
1021 * | REFIT | SERVICE | (for depot order)
1022 * +--------------------------+--------------------------+
1023 * \endverbatim
1025 * \section bottom-row Bottom row
1026 * The second row (the bottom row) is for manipulating the list of orders:
1027 * \verbatim
1028 * +-----------------+-----------------+-----------------+
1029 * | SKIP | DELETE | GOTO |
1030 * +-----------------+-----------------+-----------------+
1031 * \endverbatim
1033 * For vehicles of other companies, both button rows are not displayed.
1035 struct OrdersWindow : public Window {
1036 private:
1037 /** Under what reason are we using the PlaceObject functionality? */
1038 enum OrderPlaceObjectState {
1039 OPOS_NONE,
1040 OPOS_GOTO,
1041 OPOS_CONDITIONAL,
1042 OPOS_SHARE,
1043 OPOS_END,
1046 /** Displayed planes of the #NWID_SELECTION widgets. */
1047 enum DisplayPane {
1048 /* WID_O_SEL_TOP_ROW_GROUNDVEHICLE */
1049 DP_GROUNDVEHICLE_ROW_NORMAL = 0, ///< Display the row for normal/depot orders in the top row of the train/rv order window.
1050 DP_GROUNDVEHICLE_ROW_CONDITIONAL = 1, ///< Display the row for conditional orders in the top row of the train/rv order window.
1052 /* WID_O_SEL_TOP_LEFT */
1053 DP_LEFT_LOAD = 0, ///< Display 'load' in the left button of the top row of the train/rv order window.
1054 DP_LEFT_REFIT = 1, ///< Display 'refit' in the left button of the top row of the train/rv order window.
1055 DP_LEFT_REVERSE = 2, ///< Display 'reverse' in the left button of the top row of the train/rv order window.
1057 /* WID_O_SEL_TOP_MIDDLE */
1058 DP_MIDDLE_UNLOAD = 0, ///< Display 'unload' in the middle button of the top row of the train/rv order window.
1059 DP_MIDDLE_SERVICE = 1, ///< Display 'service' in the middle button of the top row of the train/rv order window.
1061 /* WID_O_SEL_TOP_RIGHT */
1062 DP_RIGHT_EMPTY = 0, ///< Display an empty panel in the right button of the top row of the train/rv order window.
1063 DP_RIGHT_REFIT = 1, ///< Display 'refit' in the right button of the top row of the train/rv order window.
1065 /* WID_O_SEL_TOP_ROW */
1066 DP_ROW_LOAD = 0, ///< Display 'load' / 'unload' / 'refit' buttons in the top row of the ship/airplane order window.
1067 DP_ROW_DEPOT = 1, ///< Display 'refit' / 'service' buttons in the top row of the ship/airplane order window.
1068 DP_ROW_CONDITIONAL = 2, ///< Display the conditional order buttons in the top row of the ship/airplane order window.
1070 /* WID_O_SEL_COND_VALUE */
1071 DP_COND_VALUE_NUMBER = 0, ///< Display number widget
1072 DP_COND_VALUE_CARGO = 1, ///< Display dropdown widget cargo types
1073 DP_COND_VALUE_SLOT = 2, ///< Display dropdown widget tracerestrict slots
1075 /* WID_O_SEL_BOTTOM_MIDDLE */
1076 DP_BOTTOM_MIDDLE_DELETE = 0, ///< Display 'delete' in the middle button of the bottom row of the vehicle order window.
1077 DP_BOTTOM_MIDDLE_STOP_SHARING = 1, ///< Display 'stop sharing' in the middle button of the bottom row of the vehicle order window.
1080 int selected_order;
1081 VehicleOrderID order_over; ///< Order over which another order is dragged, \c INVALID_VEH_ORDER_ID if none.
1082 OrderPlaceObjectState goto_type;
1083 const Vehicle *vehicle; ///< Vehicle owning the orders being displayed and manipulated.
1084 Scrollbar *vscroll;
1085 bool can_do_refit; ///< Vehicle chain can be refitted in depot.
1086 bool can_do_autorefit; ///< Vehicle chain can be auto-refitted.
1087 uint32 index_vehicle_share; ///< index of vehicle to share
1090 * Return the memorised selected order.///
1091 * @return the memorised order if it is a valid one
1092 * else return the number of orders
1094 VehicleOrderID OrderGetSel() const
1096 int num = this->selected_order;
1097 return (num >= 0 && num < vehicle->GetNumOrders()) ? num : vehicle->GetNumOrders();
1101 * Calculate the selected order.
1102 * The calculation is based on the relative (to the window) y click position and
1103 * the position of the scrollbar.
1105 * @param y Y-value of the click relative to the window origin
1106 * @return The selected order if the order is valid, else return \c INVALID_VEH_ORDER_ID.
1108 VehicleOrderID GetOrderFromPt(int y)
1110 NWidgetBase *nwid = this->GetWidget<NWidgetBase>(WID_O_ORDER_LIST);
1111 int sel = (y - nwid->pos_y - WD_FRAMERECT_TOP) / nwid->resize_y; // Selected line in the WID_O_ORDER_LIST panel.
1113 if ((uint)sel >= this->vscroll->GetCapacity()) return INVALID_VEH_ORDER_ID;
1115 sel += this->vscroll->GetPosition();
1117 return (sel <= vehicle->GetNumOrders() && sel >= 0) ? sel : INVALID_VEH_ORDER_ID;
1121 * Determine which strings should be displayed in the conditional comparator dropdown
1123 * @param order the order to evaluate
1124 * @return the StringIDs to display
1126 static const StringID *GetComparatorStrings(const Order *order)
1128 if (order == nullptr) return _order_conditional_condition;
1129 switch (order->GetConditionVariable()) {
1130 case OCV_FREE_PLATFORMS: //fall through
1131 case OCV_CARGO_WAITING: return _order_conditional_condition_has; break;
1132 case OCV_CARGO_ACCEPTANCE: return _order_conditional_condition_accepts; break;
1133 case OCV_SLOT_OCCUPANCY: return _order_conditional_condition_is_fully_occupied; break;
1134 default: return _order_conditional_condition; break;
1139 * Handle the click on the goto button.
1141 void OrderClick_Goto(OrderPlaceObjectState type)
1143 assert(type > OPOS_NONE && type < OPOS_END);
1145 static const HighLightStyle goto_place_style[OPOS_END - 1] = {
1146 HT_RECT | HT_VEHICLE, // OPOS_GOTO
1147 HT_NONE, // OPOS_CONDITIONAL
1148 HT_VEHICLE, // OPOS_SHARE
1150 SetObjectToPlaceWnd(ANIMCURSOR_PICKSTATION, PAL_NONE, goto_place_style[type - 1], this);
1151 this->goto_type = type;
1152 this->SetWidgetDirty(WID_O_GOTO);
1156 * Handle the click on the full load button.
1157 * @param load_type the way to load.
1159 void OrderClick_FullLoad(int load_type)
1161 VehicleOrderID sel_ord = this->OrderGetSel();
1162 const Order *order = this->vehicle->GetOrder(sel_ord);
1164 if (order == nullptr || (order->GetLoadType() == load_type && load_type != OLFB_CARGO_TYPE_LOAD)) return;
1166 if (load_type < 0) {
1167 load_type = order->GetLoadType() == OLF_LOAD_IF_POSSIBLE ? OLF_FULL_LOAD_ANY : OLF_LOAD_IF_POSSIBLE;
1169 if (order->GetLoadType() != load_type) {
1170 DoCommandP(this->vehicle->tile, this->vehicle->index + (sel_ord << 20), MOF_LOAD | (load_type << 4), CMD_MODIFY_ORDER | CMD_MSG(STR_ERROR_CAN_T_MODIFY_THIS_ORDER));
1173 if (load_type == OLFB_CARGO_TYPE_LOAD) ShowCargoTypeOrdersWindow(this->vehicle, this, sel_ord, CTOWV_LOAD);
1177 * Handle the 'no loading' hotkey
1179 void OrderHotkey_NoLoad()
1181 this->OrderClick_FullLoad(OLFB_NO_LOAD);
1185 * Handle the click on the service.
1187 void OrderClick_Service(int i)
1189 VehicleOrderID sel_ord = this->OrderGetSel();
1191 if (i < 0) {
1192 const Order *order = this->vehicle->GetOrder(sel_ord);
1193 if (order == nullptr) return;
1194 i = (order->GetDepotOrderType() & ODTFB_SERVICE) ? DA_ALWAYS_GO : DA_SERVICE;
1196 DoCommandP(this->vehicle->tile, this->vehicle->index + (sel_ord << 20), MOF_DEPOT_ACTION | (i << 4), CMD_MODIFY_ORDER | CMD_MSG(STR_ERROR_CAN_T_MODIFY_THIS_ORDER));
1200 * Handle the click on the service in nearest depot button.
1202 void OrderClick_NearestDepot()
1204 Order order;
1205 order.next = nullptr;
1206 order.index = 0;
1207 order.MakeGoToDepot(0, ODTFB_PART_OF_ORDERS,
1208 _settings_client.gui.new_nonstop && this->vehicle->IsGroundVehicle() ? ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS : ONSF_STOP_EVERYWHERE);
1209 order.SetDepotActionType(ODATFB_NEAREST_DEPOT);
1211 DoCommandP(this->vehicle->tile, this->vehicle->index + (this->OrderGetSel() << 20), order.Pack(), CMD_INSERT_ORDER | CMD_MSG(STR_ERROR_CAN_T_INSERT_NEW_ORDER));
1215 * Handle the click on the unload button.
1217 void OrderClick_Unload(int unload_type)
1219 VehicleOrderID sel_ord = this->OrderGetSel();
1220 const Order *order = this->vehicle->GetOrder(sel_ord);
1222 if (order == nullptr || (order->GetUnloadType() == unload_type && unload_type != OUFB_CARGO_TYPE_UNLOAD)) return;
1224 if (unload_type < 0) {
1225 unload_type = order->GetUnloadType() == OUF_UNLOAD_IF_POSSIBLE ? OUFB_UNLOAD : OUF_UNLOAD_IF_POSSIBLE;
1228 if (order->GetUnloadType() != unload_type) {
1229 DoCommandP(this->vehicle->tile, this->vehicle->index + (sel_ord << 20), MOF_UNLOAD | (unload_type << 4), CMD_MODIFY_ORDER | CMD_MSG(STR_ERROR_CAN_T_MODIFY_THIS_ORDER));
1232 if (unload_type == OUFB_TRANSFER) {
1233 /* Transfer orders with leave empty as default */
1234 DoCommandP(this->vehicle->tile, this->vehicle->index + (sel_ord << 20), MOF_LOAD | (OLFB_NO_LOAD << 4), CMD_MODIFY_ORDER);
1235 this->SetWidgetDirty(WID_O_FULL_LOAD);
1236 } else if(unload_type == OUFB_CARGO_TYPE_UNLOAD) {
1237 ShowCargoTypeOrdersWindow(this->vehicle, this, sel_ord, CTOWV_UNLOAD);
1242 * Handle the transfer hotkey
1244 void OrderHotkey_Transfer()
1246 this->OrderClick_Unload(OUFB_TRANSFER);
1250 * Handle the 'no unload' hotkey
1252 void OrderHotkey_NoUnload()
1254 this->OrderClick_Unload(OUFB_NO_UNLOAD);
1258 * Handle the click on the nonstop button.
1259 * @param non_stop what non-stop type to use; -1 to use the 'next' one.
1261 void OrderClick_Nonstop(int non_stop)
1263 if (!this->vehicle->IsGroundVehicle()) return;
1265 VehicleOrderID sel_ord = this->OrderGetSel();
1266 const Order *order = this->vehicle->GetOrder(sel_ord);
1268 if (order == nullptr || order->GetNonStopType() == non_stop) return;
1270 /* Keypress if negative, so 'toggle' to the next */
1271 if (non_stop < 0) {
1272 non_stop = order->GetNonStopType() ^ ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS;
1275 this->SetWidgetDirty(WID_O_NON_STOP);
1276 DoCommandP(this->vehicle->tile, this->vehicle->index + (sel_ord << 20), MOF_NON_STOP | non_stop << 4, CMD_MODIFY_ORDER | CMD_MSG(STR_ERROR_CAN_T_MODIFY_THIS_ORDER));
1280 * Handle the click on the skip button.
1281 * If ctrl is pressed, skip to selected order, else skip to current order + 1
1283 void OrderClick_Skip()
1285 /* Don't skip when there's nothing to skip */
1286 if (_ctrl_pressed && this->vehicle->cur_implicit_order_index == this->OrderGetSel()) return;
1287 if (this->vehicle->GetNumOrders() <= 1) return;
1289 DoCommandP(this->vehicle->tile, this->vehicle->index, _ctrl_pressed ? this->OrderGetSel() : ((this->vehicle->cur_implicit_order_index + 1) % this->vehicle->GetNumOrders()),
1290 CMD_SKIP_TO_ORDER | CMD_MSG(_ctrl_pressed ? STR_ERROR_CAN_T_SKIP_TO_ORDER : STR_ERROR_CAN_T_SKIP_ORDER));
1294 * Handle the click on the delete button.
1296 void OrderClick_Delete()
1298 /* When networking, move one order lower */
1299 int selected = this->selected_order + (int)_networking;
1301 if (DoCommandP(this->vehicle->tile, this->vehicle->index, this->OrderGetSel(), CMD_DELETE_ORDER | CMD_MSG(STR_ERROR_CAN_T_DELETE_THIS_ORDER))) {
1302 this->selected_order = selected >= this->vehicle->GetNumOrders() ? -1 : selected;
1303 this->UpdateButtonState();
1308 * Handle the click on the 'stop sharing' button.
1309 * If 'End of Shared Orders' isn't selected, do nothing. If Ctrl is pressed, call OrderClick_Delete and exit.
1310 * To stop sharing this vehicle order list, we copy the orders of a vehicle that share this order list. That way we
1311 * exit the group of shared vehicles while keeping the same order list.
1313 void OrderClick_StopSharing()
1315 /* Don't try to stop sharing orders if 'End of Shared Orders' isn't selected. */
1316 if (!this->vehicle->HasSharedOrdersList() || this->selected_order != this->vehicle->GetNumOrders()) return;
1317 /* If Ctrl is pressed, delete the order list as if we clicked the 'Delete' button. */
1318 if (_ctrl_pressed) {
1319 this->OrderClick_Delete();
1320 return;
1323 /* Get another vehicle that share orders with this vehicle. */
1324 Vehicle *other_shared = (this->vehicle->FirstShared() == this->vehicle) ? this->vehicle->NextShared() : this->vehicle->PreviousShared();
1325 /* Copy the order list of the other vehicle. */
1326 if (DoCommandP(this->vehicle->tile, this->vehicle->index | CO_COPY << 30, other_shared->index, CMD_CLONE_ORDER | CMD_MSG(STR_ERROR_CAN_T_STOP_SHARING_ORDER_LIST))) {
1327 this->UpdateButtonState();
1332 * Handle the click on the refit button.
1333 * If ctrl is pressed, cancel refitting, else show the refit window.
1334 * @param i Selected refit command.
1335 * @param auto_refit Select refit for auto-refitting.
1337 void OrderClick_Refit(int i, bool auto_refit)
1339 if (_ctrl_pressed) {
1340 /* Cancel refitting */
1341 DoCommandP(this->vehicle->tile, this->vehicle->index, (this->OrderGetSel() << 16) | (CT_NO_REFIT << 8) | CT_NO_REFIT, CMD_ORDER_REFIT);
1342 } else {
1343 if (i == 1) { // Auto-refit to available cargo type.
1344 DoCommandP(this->vehicle->tile, this->vehicle->index, (this->OrderGetSel() << 16) | CT_AUTO_REFIT, CMD_ORDER_REFIT);
1345 } else {
1346 ShowVehicleRefitWindow(this->vehicle, this->OrderGetSel(), this, auto_refit);
1351 /** Cache auto-refittability of the vehicle chain. */
1352 void UpdateAutoRefitState()
1354 this->can_do_refit = false;
1355 this->can_do_autorefit = false;
1356 for (const Vehicle *w = this->vehicle; w != nullptr; w = w->IsGroundVehicle() ? w->Next() : nullptr) {
1357 if (IsEngineRefittable(w->engine_type)) this->can_do_refit = true;
1358 if (HasBit(Engine::Get(w->engine_type)->info.misc_flags, EF_AUTO_REFIT)) this->can_do_autorefit = true;
1362 public:
1363 OrdersWindow(WindowDesc *desc, const Vehicle *v) : Window(desc)
1365 this->vehicle = v;
1367 this->CreateNestedTree();
1368 this->vscroll = this->GetScrollbar(WID_O_SCROLLBAR);
1369 this->FinishInitNested(v->index);
1370 if (v->owner == _local_company) {
1371 this->DisableWidget(WID_O_EMPTY);
1374 this->selected_order = -1;
1375 this->order_over = INVALID_VEH_ORDER_ID;
1376 this->goto_type = OPOS_NONE;
1377 this->owner = v->owner;
1379 this->UpdateAutoRefitState();
1381 if (_settings_client.gui.quick_goto && v->owner == _local_company) {
1382 /* If there are less than 2 station, make Go To active. */
1383 int station_orders = 0;
1384 const Order *order;
1385 FOR_VEHICLE_ORDERS(v, order) {
1386 if (order->IsType(OT_GOTO_STATION)) station_orders++;
1389 if (station_orders < 2) this->OrderClick_Goto(OPOS_GOTO);
1391 this->OnInvalidateData(VIWD_MODIFY_ORDERS);
1394 ~OrdersWindow()
1396 DeleteWindowById(WC_VEHICLE_CARGO_TYPE_LOAD_ORDERS, this->window_number, false);
1397 DeleteWindowById(WC_VEHICLE_CARGO_TYPE_UNLOAD_ORDERS, this->window_number, false);
1398 if (!FocusWindowById(WC_VEHICLE_VIEW, this->window_number)) {
1399 MarkAllRouteStepsDirty(this->vehicle);
1403 virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override
1405 switch (widget) {
1406 case WID_O_ORDER_LIST:
1407 resize->height = FONT_HEIGHT_NORMAL;
1408 size->height = 6 * resize->height + WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
1409 break;
1411 case WID_O_COND_VARIABLE: {
1412 Dimension d = {0, 0};
1413 for (uint i = 0; i < lengthof(_order_conditional_variable); i++) {
1414 d = maxdim(d, GetStringBoundingBox(STR_ORDER_CONDITIONAL_LOAD_PERCENTAGE + _order_conditional_variable[i]));
1416 d.width += padding.width;
1417 d.height += padding.height;
1418 *size = maxdim(*size, d);
1419 break;
1422 case WID_O_COND_COMPARATOR: {
1423 Dimension d = {0, 0};
1424 for (int i = 0; _order_conditional_condition[i] != INVALID_STRING_ID; i++) {
1425 d = maxdim(d, GetStringBoundingBox(_order_conditional_condition[i]));
1427 d.width += padding.width;
1428 d.height += padding.height;
1429 *size = maxdim(*size, d);
1430 break;
1436 * Some data on this window has become invalid.
1437 * @param data Information about the changed data.
1438 * @param gui_scope Whether the call is done from GUI scope. You may not do everything when not in GUI scope. See #InvalidateWindowData() for details.
1440 virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
1442 VehicleOrderID from = INVALID_VEH_ORDER_ID;
1443 VehicleOrderID to = INVALID_VEH_ORDER_ID;
1445 switch (data) {
1446 case VIWD_AUTOREPLACE:
1447 /* Autoreplace replaced the vehicle */
1448 this->vehicle = Vehicle::Get(this->window_number);
1449 FALLTHROUGH;
1451 case VIWD_CONSIST_CHANGED:
1452 /* Vehicle composition was changed. */
1453 this->UpdateAutoRefitState();
1454 break;
1456 case VIWD_REMOVE_ALL_ORDERS:
1457 /* Removed / replaced all orders (after deleting / sharing) */
1458 if (this->selected_order == -1) break;
1460 this->DeleteChildWindows();
1461 HideDropDownMenu(this);
1462 this->selected_order = -1;
1463 break;
1465 case VIWD_MODIFY_ORDERS:
1466 /* Some other order changes */
1467 break;
1469 default:
1470 if (data < 0) break;
1472 if (gui_scope) break; // only do this once; from command scope
1473 from = GB(data, 0, 8);
1474 to = GB(data, 8, 8);
1475 /* Moving an order. If one of these is INVALID_VEH_ORDER_ID, then
1476 * the order is being created / removed */
1477 if (this->selected_order == -1) break;
1479 if (from == to) break; // no need to change anything
1481 if (from != this->selected_order) {
1482 /* Moving from preceding order? */
1483 this->selected_order -= (int)(from <= this->selected_order);
1484 /* Moving to preceding order? */
1485 this->selected_order += (int)(to <= this->selected_order);
1486 break;
1489 /* Now we are modifying the selected order */
1490 if (to == INVALID_VEH_ORDER_ID) {
1491 /* Deleting selected order */
1492 this->DeleteChildWindows();
1493 HideDropDownMenu(this);
1494 this->selected_order = -1;
1495 break;
1498 /* Moving selected order */
1499 this->selected_order = to;
1500 break;
1503 this->vscroll->SetCount(this->vehicle->GetNumOrders() + 1);
1504 if (gui_scope) {
1505 this->UpdateButtonState();
1506 InvalidateWindowClassesData(WC_VEHICLE_CARGO_TYPE_LOAD_ORDERS, 0);
1507 InvalidateWindowClassesData(WC_VEHICLE_CARGO_TYPE_UNLOAD_ORDERS, 0);
1510 /* Scroll to the new order. */
1511 if (from == INVALID_VEH_ORDER_ID && to != INVALID_VEH_ORDER_ID && !this->vscroll->IsVisible(to)) {
1512 this->vscroll->ScrollTowards(to);
1516 void UpdateButtonState()
1518 if (this->vehicle->owner != _local_company) return; // No buttons are displayed with competitor order windows.
1520 bool shared_orders = this->vehicle->HasSharedOrdersList();
1521 VehicleOrderID sel = this->OrderGetSel();
1522 const Order *order = this->vehicle->GetOrder(sel);
1524 /* Second row. */
1525 /* skip */
1526 this->SetWidgetDisabledState(WID_O_SKIP, this->vehicle->GetNumOrders() <= 1);
1528 /* delete / stop sharing */
1529 NWidgetStacked *delete_sel = this->GetWidget<NWidgetStacked>(WID_O_SEL_BOTTOM_MIDDLE);
1530 if (shared_orders && this->selected_order == this->vehicle->GetNumOrders()) {
1531 /* The 'End of Shared Orders' order is selected, show the 'stop sharing' button. */
1532 delete_sel->SetDisplayedPlane(DP_BOTTOM_MIDDLE_STOP_SHARING);
1533 } else {
1534 /* The 'End of Shared Orders' order isn't selected, show the 'delete' button. */
1535 delete_sel->SetDisplayedPlane(DP_BOTTOM_MIDDLE_DELETE);
1536 this->SetWidgetDisabledState(WID_O_DELETE,
1537 (uint)this->vehicle->GetNumOrders() + ((shared_orders || this->vehicle->GetNumOrders() != 0) ? 1 : 0) <= (uint)this->selected_order);
1539 /* Set the tooltip of the 'delete' button depending on whether the
1540 * 'End of Orders' order or a regular order is selected. */
1541 NWidgetCore *nwi = this->GetWidget<NWidgetCore>(WID_O_DELETE);
1542 if (this->selected_order == this->vehicle->GetNumOrders()) {
1543 nwi->SetDataTip(STR_ORDERS_DELETE_BUTTON, STR_ORDERS_DELETE_ALL_TOOLTIP);
1544 } else {
1545 nwi->SetDataTip(STR_ORDERS_DELETE_BUTTON, STR_ORDERS_DELETE_TOOLTIP);
1549 /* First row. */
1550 this->RaiseWidget(WID_O_FULL_LOAD);
1551 this->RaiseWidget(WID_O_UNLOAD);
1552 this->RaiseWidget(WID_O_SERVICE);
1554 /* Selection widgets. */
1555 /* Train or road vehicle. */
1556 NWidgetStacked *train_row_sel = this->GetWidget<NWidgetStacked>(WID_O_SEL_TOP_ROW_GROUNDVEHICLE);
1557 NWidgetStacked *left_sel = this->GetWidget<NWidgetStacked>(WID_O_SEL_TOP_LEFT);
1558 NWidgetStacked *middle_sel = this->GetWidget<NWidgetStacked>(WID_O_SEL_TOP_MIDDLE);
1559 NWidgetStacked *right_sel = this->GetWidget<NWidgetStacked>(WID_O_SEL_TOP_RIGHT);
1560 /* Ship or airplane. */
1561 NWidgetStacked *row_sel = this->GetWidget<NWidgetStacked>(WID_O_SEL_TOP_ROW);
1562 assert(row_sel != nullptr || (train_row_sel != nullptr && left_sel != nullptr && middle_sel != nullptr && right_sel != nullptr));
1565 if (order == nullptr) {
1566 if (row_sel != nullptr) {
1567 row_sel->SetDisplayedPlane(DP_ROW_LOAD);
1568 } else {
1569 train_row_sel->SetDisplayedPlane(DP_GROUNDVEHICLE_ROW_NORMAL);
1570 left_sel->SetDisplayedPlane(DP_LEFT_LOAD);
1571 middle_sel->SetDisplayedPlane(DP_MIDDLE_UNLOAD);
1572 right_sel->SetDisplayedPlane(DP_RIGHT_EMPTY);
1573 this->DisableWidget(WID_O_NON_STOP);
1574 this->RaiseWidget(WID_O_NON_STOP);
1576 this->DisableWidget(WID_O_FULL_LOAD);
1577 this->DisableWidget(WID_O_UNLOAD);
1578 this->DisableWidget(WID_O_REFIT_DROPDOWN);
1579 } else {
1580 this->SetWidgetDisabledState(WID_O_FULL_LOAD, (order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION) != 0); // full load
1581 this->SetWidgetDisabledState(WID_O_UNLOAD, (order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION) != 0); // unload
1583 switch (order->GetType()) {
1584 case OT_GOTO_STATION:
1585 if (row_sel != nullptr) {
1586 row_sel->SetDisplayedPlane(DP_ROW_LOAD);
1587 } else {
1588 train_row_sel->SetDisplayedPlane(DP_GROUNDVEHICLE_ROW_NORMAL);
1589 left_sel->SetDisplayedPlane(DP_LEFT_LOAD);
1590 middle_sel->SetDisplayedPlane(DP_MIDDLE_UNLOAD);
1591 right_sel->SetDisplayedPlane(DP_RIGHT_REFIT);
1592 this->EnableWidget(WID_O_NON_STOP);
1593 this->SetWidgetLoweredState(WID_O_NON_STOP, order->GetNonStopType() & ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS);
1595 this->SetWidgetLoweredState(WID_O_FULL_LOAD, order->GetLoadType() == OLF_FULL_LOAD_ANY);
1596 this->SetWidgetLoweredState(WID_O_UNLOAD, order->GetUnloadType() == OUFB_UNLOAD);
1598 /* Can only do refitting when stopping at the destination and loading cargo.
1599 * Also enable the button if a refit is already set to allow clearing it. */
1600 this->SetWidgetDisabledState(WID_O_REFIT_DROPDOWN,
1601 order->GetLoadType() == OLFB_NO_LOAD || (order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION) ||
1602 ((!this->can_do_refit || !this->can_do_autorefit) && !order->IsRefit()));
1604 break;
1606 case OT_GOTO_WAYPOINT:
1607 if (row_sel != nullptr) {
1608 row_sel->SetDisplayedPlane(DP_ROW_LOAD);
1609 } else {
1610 train_row_sel->SetDisplayedPlane(DP_GROUNDVEHICLE_ROW_NORMAL);
1611 left_sel->SetDisplayedPlane(DP_LEFT_REVERSE);
1612 middle_sel->SetDisplayedPlane(DP_MIDDLE_UNLOAD);
1613 right_sel->SetDisplayedPlane(DP_RIGHT_EMPTY);
1614 this->EnableWidget(WID_O_NON_STOP);
1615 this->SetWidgetLoweredState(WID_O_NON_STOP, order->GetNonStopType() & ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS);
1616 this->EnableWidget(WID_O_REVERSE);
1617 this->SetWidgetLoweredState(WID_O_REVERSE, (order->GetWaypointFlags() & OWF_REVERSE) != 0);
1619 this->DisableWidget(WID_O_UNLOAD);
1620 this->DisableWidget(WID_O_REFIT_DROPDOWN);
1621 break;
1623 case OT_GOTO_DEPOT:
1624 if (row_sel != nullptr) {
1625 row_sel->SetDisplayedPlane(DP_ROW_DEPOT);
1626 } else {
1627 train_row_sel->SetDisplayedPlane(DP_GROUNDVEHICLE_ROW_NORMAL);
1628 left_sel->SetDisplayedPlane(DP_LEFT_REFIT);
1629 middle_sel->SetDisplayedPlane(DP_MIDDLE_SERVICE);
1630 right_sel->SetDisplayedPlane(DP_RIGHT_EMPTY);
1631 this->EnableWidget(WID_O_NON_STOP);
1632 this->SetWidgetLoweredState(WID_O_NON_STOP, order->GetNonStopType() & ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS);
1634 /* Disable refit button if the order is no 'always go' order.
1635 * However, keep the service button enabled for refit-orders to allow clearing refits (without knowing about ctrl). */
1636 this->SetWidgetDisabledState(WID_O_REFIT,
1637 (order->GetDepotOrderType() & ODTFB_SERVICE) || (order->GetDepotActionType() & ODATFB_HALT) ||
1638 (!this->can_do_refit && !order->IsRefit()));
1639 this->SetWidgetLoweredState(WID_O_SERVICE, order->GetDepotOrderType() & ODTFB_SERVICE);
1640 break;
1642 case OT_CONDITIONAL: {
1643 if (row_sel != nullptr) {
1644 row_sel->SetDisplayedPlane(DP_ROW_CONDITIONAL);
1645 } else {
1646 train_row_sel->SetDisplayedPlane(DP_GROUNDVEHICLE_ROW_CONDITIONAL);
1649 OrderConditionVariable ocv = (order == nullptr) ? OCV_LOAD_PERCENTAGE : order->GetConditionVariable();
1650 bool is_cargo = ocv == OCV_CARGO_ACCEPTANCE || ocv == OCV_CARGO_WAITING;
1651 bool is_slot_occupancy = ocv == OCV_SLOT_OCCUPANCY;
1653 if (is_cargo) {
1654 CargoSpec* cargo_spec = CargoSpec::Get(order != nullptr ? order->GetConditionValue() : 0);
1655 this->GetWidget<NWidgetCore>(WID_O_COND_CARGO)->widget_data = cargo_spec->name;
1656 this->GetWidget<NWidgetStacked>(WID_O_SEL_COND_VALUE)->SetDisplayedPlane(DP_COND_VALUE_CARGO);
1658 else if (is_slot_occupancy) {
1659 TraceRestrictSlotID slot_id = (order != nullptr && TraceRestrictSlot::IsValidID(order->GetXData()) ? order->GetXData() : INVALID_TRACE_RESTRICT_SLOT_ID);
1661 this->GetWidget<NWidgetCore>(WID_O_COND_SLOT)->widget_data = (slot_id != INVALID_TRACE_RESTRICT_SLOT_ID) ? STR_TRACE_RESTRICT_SLOT_NAME : STR_TRACE_RESTRICT_VARIABLE_UNDEFINED;
1662 this->GetWidget<NWidgetStacked>(WID_O_SEL_COND_VALUE)->SetDisplayedPlane(DP_COND_VALUE_SLOT);
1664 else {
1665 this->GetWidget<NWidgetStacked>(WID_O_SEL_COND_VALUE)->SetDisplayedPlane(DP_COND_VALUE_NUMBER);
1668 /* Set the strings for the dropdown boxes. */
1669 this->GetWidget<NWidgetCore>(WID_O_COND_VARIABLE)->widget_data = STR_ORDER_CONDITIONAL_LOAD_PERCENTAGE + ocv;
1670 this->GetWidget<NWidgetCore>(WID_O_COND_COMPARATOR)->widget_data = GetComparatorStrings(order)[order == nullptr ? 0 : order->GetConditionComparator()];
1671 this->SetWidgetDisabledState(WID_O_COND_COMPARATOR, ocv == OCV_UNCONDITIONALLY || ocv == OCV_PERCENT);
1672 this->SetWidgetDisabledState(WID_O_COND_VALUE, ocv == OCV_REQUIRES_SERVICE || ocv == OCV_UNCONDITIONALLY);
1673 break;
1676 default: // every other order
1677 if (row_sel != nullptr) {
1678 row_sel->SetDisplayedPlane(DP_ROW_LOAD);
1679 } else {
1680 train_row_sel->SetDisplayedPlane(DP_GROUNDVEHICLE_ROW_NORMAL);
1681 left_sel->SetDisplayedPlane(DP_LEFT_LOAD);
1682 middle_sel->SetDisplayedPlane(DP_MIDDLE_UNLOAD);
1683 right_sel->SetDisplayedPlane(DP_RIGHT_EMPTY);
1684 this->DisableWidget(WID_O_NON_STOP);
1686 this->DisableWidget(WID_O_FULL_LOAD);
1687 this->DisableWidget(WID_O_UNLOAD);
1688 this->DisableWidget(WID_O_REFIT_DROPDOWN);
1689 break;
1693 /* Disable list of vehicles with the same shared orders if there is no list */
1694 this->SetWidgetDisabledState(WID_O_SHARED_ORDER_LIST, !shared_orders);
1696 this->SetDirty();
1699 virtual void OnPaint() override
1701 if (this->vehicle->owner != _local_company) {
1702 this->selected_order = -1; // Disable selection any selected row at a competitor order window.
1703 } else {
1704 this->SetWidgetLoweredState(WID_O_GOTO, this->goto_type != OPOS_NONE);
1706 this->DrawWidgets();
1709 virtual void DrawWidget(const Rect &r, int widget) const override
1711 if (widget != WID_O_ORDER_LIST) return;
1713 bool rtl = _current_text_dir == TD_RTL;
1714 SetDParamMaxValue(0, this->vehicle->GetNumOrders(), 2);
1715 int index_column_width = GetStringBoundingBox(STR_ORDER_INDEX).width + 2 * GetSpriteSize(rtl ? SPR_ARROW_RIGHT : SPR_ARROW_LEFT).width + 3;
1716 int middle = rtl ? r.right - WD_FRAMETEXT_RIGHT - index_column_width : r.left + WD_FRAMETEXT_LEFT + index_column_width;
1718 int y = r.top + WD_FRAMERECT_TOP;
1719 int line_height = this->GetWidget<NWidgetBase>(WID_O_ORDER_LIST)->resize_y;
1721 int i = this->vscroll->GetPosition();
1722 const Order *order = this->vehicle->GetOrder(i);
1723 /* First draw the highlighting underground if it exists. */
1724 if (this->order_over != INVALID_VEH_ORDER_ID) {
1725 while (order != nullptr) {
1726 /* Don't draw anything if it extends past the end of the window. */
1727 if (!this->vscroll->IsVisible(i)) break;
1729 if (i != this->selected_order && i == this->order_over) {
1730 /* Highlight dragged order destination. */
1731 int top = (this->order_over < this->selected_order ? y : y + line_height) - WD_FRAMERECT_TOP;
1732 int bottom = min(top + 2, r.bottom - WD_FRAMERECT_BOTTOM);
1733 top = max(top - 3, r.top + WD_FRAMERECT_TOP);
1734 GfxFillRect(r.left + WD_FRAMETEXT_LEFT, top, r.right - WD_FRAMETEXT_RIGHT, bottom, _colour_gradient[COLOUR_GREY][7]);
1735 break;
1737 y += line_height;
1739 i++;
1740 order = order->next;
1743 /* Reset counters for drawing the orders. */
1744 y = r.top + WD_FRAMERECT_TOP;
1745 i = this->vscroll->GetPosition();
1746 order = this->vehicle->GetOrder(i);
1749 /* Draw the orders. */
1750 while (order != nullptr) {
1751 /* Don't draw anything if it extends past the end of the window. */
1752 if (!this->vscroll->IsVisible(i)) break;
1754 DrawOrderString(this->vehicle, order, i, y, i == this->selected_order, false, r.left + WD_FRAMETEXT_LEFT, middle, r.right - WD_FRAMETEXT_RIGHT);
1755 y += line_height;
1757 i++;
1758 order = order->next;
1761 if (this->vscroll->IsVisible(i)) {
1762 StringID str = this->vehicle->HasSharedOrdersList() ? STR_ORDERS_END_OF_SHARED_ORDERS : STR_ORDERS_END_OF_ORDERS;
1763 DrawString(rtl ? r.left + WD_FRAMETEXT_LEFT : middle, rtl ? middle : r.right - WD_FRAMETEXT_RIGHT, y, str, (i == this->selected_order) ? TC_WHITE : TC_BLACK);
1767 virtual void SetStringParameters(int widget) const override
1769 switch (widget) {
1770 case WID_O_COND_VALUE: {
1771 VehicleOrderID sel = this->OrderGetSel();
1772 const Order *order = this->vehicle->GetOrder(sel);
1774 if (order != nullptr && order->IsType(OT_CONDITIONAL)) {
1775 uint value = order->GetConditionValue();
1776 if (order->GetConditionVariable() == OCV_MAX_SPEED) value = ConvertSpeedToDisplaySpeed(value);
1777 SetDParam(0, value);
1779 break;
1782 case WID_O_COND_SLOT: {
1783 VehicleOrderID sel = this->OrderGetSel();
1784 const Order *order = this->vehicle->GetOrder(sel);
1786 if (order != nullptr && order->IsType(OT_CONDITIONAL)) {
1787 TraceRestrictSlotID value = order->GetXData();
1788 SetDParam(0, value);
1790 break;
1793 case WID_O_CAPTION:
1794 SetDParam(0, this->vehicle->index);
1795 break;
1799 virtual void OnClick(Point pt, int widget, int click_count) override
1801 switch (widget) {
1802 case WID_O_ORDER_LIST: {
1803 if (this->goto_type == OPOS_CONDITIONAL) {
1804 VehicleOrderID order_id = this->GetOrderFromPt(_cursor.pos.y - this->top);
1805 if (order_id != INVALID_VEH_ORDER_ID) {
1806 Order order;
1807 order.next = nullptr;
1808 order.index = 0;
1809 order.MakeConditional(order_id);
1811 DoCommandP(this->vehicle->tile, this->vehicle->index + (this->OrderGetSel() << 20), order.Pack(), CMD_INSERT_ORDER | CMD_MSG(STR_ERROR_CAN_T_INSERT_NEW_ORDER));
1813 ResetObjectToPlace();
1814 break;
1817 if (_ctrl_pressed && _shift_pressed) {
1818 const Order* selected_order = this->vehicle->GetOrder(this->selected_order);
1820 if (selected_order != nullptr && selected_order->IsType(OrderType::OT_CONDITIONAL)) {
1821 VehicleOrderID order_id = this->GetOrderFromPt(_cursor.pos.y - this->top);
1822 if (order_id != INVALID_VEH_ORDER_ID) {
1823 DoCommandP(this->vehicle->tile, this->vehicle->index | (this->selected_order << 20),
1824 MOF_COND_DESTINATION | (order_id << 4),
1825 CMD_MODIFY_ORDER | CMD_MSG(STR_ERROR_CAN_T_MODIFY_THIS_ORDER));
1826 MarkAllRoutePathsDirty(this->vehicle);
1827 MarkAllRouteStepsDirty(this->vehicle);
1830 break;
1833 VehicleOrderID sel = this->GetOrderFromPt(pt.y);
1835 if (_ctrl_pressed && sel < this->vehicle->GetNumOrders()) {
1836 TileIndex xy = this->vehicle->GetOrder(sel)->GetLocation(this->vehicle);
1837 if (xy != INVALID_TILE) ScrollMainWindowToTile(xy);
1838 return;
1841 /* This order won't be selected any more, close all child windows and dropdowns */
1842 this->DeleteChildWindows();
1843 HideDropDownMenu(this);
1845 if (sel == INVALID_VEH_ORDER_ID || this->vehicle->owner != _local_company) {
1846 /* Deselect clicked order */
1847 this->selected_order = -1;
1848 } else if (sel == this->selected_order) {
1849 if (this->vehicle->type == VEH_TRAIN && sel < this->vehicle->GetNumOrders()) {
1850 DoCommandP(this->vehicle->tile, this->vehicle->index + (sel << 20),
1851 MOF_STOP_LOCATION | ((this->vehicle->GetOrder(sel)->GetStopLocation() + 1) % OSL_END) << 4,
1852 CMD_MODIFY_ORDER | CMD_MSG(STR_ERROR_CAN_T_MODIFY_THIS_ORDER));
1854 } else {
1855 /* Select clicked order */
1856 this->selected_order = sel;
1858 if (this->vehicle->owner == _local_company) {
1859 /* Activate drag and drop */
1860 SetObjectToPlaceWnd(SPR_CURSOR_MOUSE, PAL_NONE, HT_DRAG, this);
1864 this->UpdateButtonState();
1865 break;
1868 case WID_O_SKIP:
1869 this->OrderClick_Skip();
1870 break;
1872 case WID_O_DELETE:
1873 this->OrderClick_Delete();
1874 break;
1876 case WID_O_STOP_SHARING:
1877 this->OrderClick_StopSharing();
1878 break;
1880 case WID_O_NON_STOP:
1881 if (this->GetWidget<NWidgetLeaf>(widget)->ButtonHit(pt)) {
1882 this->OrderClick_Nonstop(-1);
1883 } else {
1884 const Order *o = this->vehicle->GetOrder(this->OrderGetSel());
1885 ShowDropDownMenu(this, _order_non_stop_drowdown, o->GetNonStopType(), WID_O_NON_STOP, 0,
1886 o->IsType(OT_GOTO_STATION) ? 0 : (o->IsType(OT_GOTO_WAYPOINT) ? 3 : 12), 0, DDSF_LOST_FOCUS);
1888 break;
1890 case WID_O_GOTO:
1891 if (this->GetWidget<NWidgetLeaf>(widget)->ButtonHit(pt)) {
1892 if (this->goto_type != OPOS_NONE) {
1893 ResetObjectToPlace();
1894 } else {
1895 this->OrderClick_Goto(OPOS_GOTO);
1897 } else {
1898 int sel;
1899 switch (this->goto_type) {
1900 case OPOS_NONE: sel = -1; break;
1901 case OPOS_GOTO: sel = 0; break;
1902 case OPOS_CONDITIONAL: sel = 2; break;
1903 case OPOS_SHARE: sel = 3; break;
1904 default: NOT_REACHED();
1906 ShowDropDownMenu(this, this->vehicle->type == VEH_AIRCRAFT ? _order_goto_dropdown_aircraft : _order_goto_dropdown, sel, WID_O_GOTO, 0, 0, 0, DDSF_LOST_FOCUS);
1908 break;
1910 case WID_O_FULL_LOAD:
1911 if (this->GetWidget<NWidgetLeaf>(widget)->ButtonHit(pt)) {
1912 this->OrderClick_FullLoad(-1);
1913 } else {
1914 ShowDropDownMenu(this, _order_full_load_drowdown, this->vehicle->GetOrder(this->OrderGetSel())->GetLoadType(), WID_O_FULL_LOAD, 0, 0xE2 /* 1110 0010 */, 0, DDSF_LOST_FOCUS);
1916 break;
1918 case WID_O_UNLOAD:
1919 if (this->GetWidget<NWidgetLeaf>(widget)->ButtonHit(pt)) {
1920 this->OrderClick_Unload(-1);
1921 } else {
1922 ShowDropDownMenu(this, _order_unload_drowdown, this->vehicle->GetOrder(this->OrderGetSel())->GetUnloadType(), WID_O_UNLOAD, 0, 0xE8 /* 1110 1000 */, 0, DDSF_LOST_FOCUS);
1924 break;
1926 case WID_O_REFIT:
1927 this->OrderClick_Refit(0, false);
1928 break;
1930 case WID_O_SERVICE:
1931 if (this->GetWidget<NWidgetLeaf>(widget)->ButtonHit(pt)) {
1932 this->OrderClick_Service(-1);
1933 } else {
1934 ShowDropDownMenu(this, _order_depot_action_dropdown, DepotActionStringIndex(this->vehicle->GetOrder(this->OrderGetSel())), WID_O_SERVICE, 0, 0, 0, DDSF_LOST_FOCUS);
1936 break;
1938 case WID_O_REFIT_DROPDOWN:
1939 if (this->GetWidget<NWidgetLeaf>(widget)->ButtonHit(pt)) {
1940 this->OrderClick_Refit(0, true);
1941 } else {
1942 ShowDropDownMenu(this, _order_refit_action_dropdown, 0, WID_O_REFIT_DROPDOWN, 0, 0, 0, DDSF_LOST_FOCUS);
1944 break;
1946 case WID_O_COND_CARGO: {
1947 DropDownList *lst = new DropDownList();
1948 const CargoSpec *cs;
1949 FOR_ALL_SORTED_STANDARD_CARGOSPECS(cs) {
1950 *lst->Append() = new DropDownListStringItem(cs->name, cs->Index(), false);
1952 if (lst->Length() == 0) {
1953 delete lst;
1954 break;
1957 uint value = this->vehicle->GetOrder(this->OrderGetSel())->GetConditionValue();
1958 ShowDropDownList(this, lst, value, WID_O_COND_CARGO, 0, true);
1959 break;
1962 case WID_O_COND_SLOT: {
1963 int selected;
1964 TraceRestrictSlotID value = this->vehicle->GetOrder(this->OrderGetSel())->GetXData();
1965 DropDownList *list = GetSlotDropDownList(this->vehicle->owner, value, selected);
1966 if (list != nullptr) ShowDropDownList(this, list, selected, WID_O_COND_SLOT, 0, true);
1967 break;
1970 case WID_O_REVERSE: {
1971 VehicleOrderID sel_ord = this->OrderGetSel();
1972 const Order *order = this->vehicle->GetOrder(sel_ord);
1974 if (order == nullptr) break;
1976 DoCommandP(this->vehicle->tile, this->vehicle->index + (sel_ord << 20), MOF_WAYPOINT_FLAGS | (order->GetWaypointFlags() ^ OWF_REVERSE) << 4,
1977 CMD_MODIFY_ORDER | CMD_MSG(STR_ERROR_CAN_T_MODIFY_THIS_ORDER));
1978 break;
1981 case WID_O_TIMETABLE_VIEW:
1982 ShowTimetableWindow(this->vehicle);
1983 break;
1985 case WID_O_COND_VARIABLE: {
1986 DropDownList *list = new DropDownList();
1987 for (uint i = 0; i < lengthof(_order_conditional_variable); i++) {
1988 *list->Append() = new DropDownListStringItem(STR_ORDER_CONDITIONAL_LOAD_PERCENTAGE + _order_conditional_variable[i], _order_conditional_variable[i], false);
1990 ShowDropDownList(this, list, this->vehicle->GetOrder(this->OrderGetSel())->GetConditionVariable(), WID_O_COND_VARIABLE);
1991 break;
1994 case WID_O_COND_COMPARATOR: {
1995 const Order *o = this->vehicle->GetOrder(this->OrderGetSel());
1996 OrderConditionVariable cond_var = o->GetConditionVariable();
1997 ShowDropDownMenu(this, GetComparatorStrings(o), o->GetConditionComparator(), WID_O_COND_COMPARATOR, 0,
1998 (cond_var == OCV_REQUIRES_SERVICE ||
1999 cond_var == OCV_CARGO_ACCEPTANCE ||
2000 cond_var == OCV_CARGO_WAITING ||
2001 cond_var == OCV_SLOT_OCCUPANCY) ? 0x3F : 0xC0);
2002 break;
2005 case WID_O_COND_VALUE: {
2006 const Order *order = this->vehicle->GetOrder(this->OrderGetSel());
2007 uint value = order->GetConditionValue();
2008 if (order->GetConditionVariable() == OCV_MAX_SPEED) value = ConvertSpeedToDisplaySpeed(value);
2009 SetDParam(0, value);
2010 ShowQueryString(STR_JUST_INT, STR_ORDER_CONDITIONAL_VALUE_CAPT, 5, this, CS_NUMERAL, QSF_NONE);
2011 break;
2014 case WID_O_SHARED_ORDER_LIST:
2015 ShowVehicleListWindow(this->vehicle);
2016 break;
2020 virtual void OnQueryTextFinished(char *str) override
2022 if (!StrEmpty(str)) {
2023 VehicleOrderID sel = this->OrderGetSel();
2024 uint value = atoi(str);
2026 switch (this->vehicle->GetOrder(sel)->GetConditionVariable()) {
2027 case OCV_MAX_SPEED:
2028 value = ConvertDisplaySpeedToSpeed(value);
2029 break;
2031 case OCV_PERCENT:
2032 case OCV_RELIABILITY:
2033 case OCV_LOAD_PERCENTAGE:
2034 value = Clamp(value, 0, 100);
2035 break;
2037 default:
2038 break;
2040 DoCommandP(this->vehicle->tile, this->vehicle->index + (sel << 20), MOF_COND_VALUE | Clamp(value, 0, 2047) << 4, CMD_MODIFY_ORDER | CMD_MSG(STR_ERROR_CAN_T_MODIFY_THIS_ORDER));
2044 virtual void OnDropdownSelect(int widget, int index) override
2046 switch (widget) {
2047 case WID_O_NON_STOP:
2048 this->OrderClick_Nonstop(index);
2049 break;
2051 case WID_O_FULL_LOAD:
2052 this->OrderClick_FullLoad(index);
2053 break;
2055 case WID_O_UNLOAD:
2056 this->OrderClick_Unload(index);
2057 break;
2059 case WID_O_GOTO:
2060 switch (index) {
2061 case 0: this->OrderClick_Goto(OPOS_GOTO); break;
2062 case 1: this->OrderClick_NearestDepot(); break;
2063 case 2: this->OrderClick_Goto(OPOS_CONDITIONAL); break;
2064 case 3: this->OrderClick_Goto(OPOS_SHARE); break;
2065 default: NOT_REACHED();
2067 break;
2069 case WID_O_SERVICE:
2070 this->OrderClick_Service(index);
2071 break;
2073 case WID_O_REFIT_DROPDOWN:
2074 this->OrderClick_Refit(index, true);
2075 break;
2077 case WID_O_COND_VARIABLE:
2078 DoCommandP(this->vehicle->tile, this->vehicle->index + (this->OrderGetSel() << 20), MOF_COND_VARIABLE | index << 4, CMD_MODIFY_ORDER | CMD_MSG(STR_ERROR_CAN_T_MODIFY_THIS_ORDER));
2079 break;
2081 case WID_O_COND_COMPARATOR:
2082 DoCommandP(this->vehicle->tile, this->vehicle->index + (this->OrderGetSel() << 20), MOF_COND_COMPARATOR | index << 4, CMD_MODIFY_ORDER | CMD_MSG(STR_ERROR_CAN_T_MODIFY_THIS_ORDER));
2083 break;
2085 case WID_O_COND_CARGO:
2086 DoCommandP(this->vehicle->tile, this->vehicle->index + (this->OrderGetSel() << 20), MOF_COND_VALUE | index << 4, CMD_MODIFY_ORDER | CMD_MSG(STR_ERROR_CAN_T_MODIFY_THIS_ORDER));
2087 break;
2089 case WID_O_COND_SLOT:
2090 DoCommandP(this->vehicle->tile, this->vehicle->index + (this->OrderGetSel() << 20), MOF_COND_VALUE | index << 4, CMD_MODIFY_ORDER | CMD_MSG(STR_ERROR_CAN_T_MODIFY_THIS_ORDER));
2091 break;
2095 virtual void OnDragDrop(Point pt, int widget) override
2097 switch (widget) {
2098 case WID_O_ORDER_LIST: {
2099 VehicleOrderID from_order = this->OrderGetSel();
2100 VehicleOrderID to_order = this->GetOrderFromPt(pt.y);
2102 if (!(from_order == to_order || from_order == INVALID_VEH_ORDER_ID || from_order > this->vehicle->GetNumOrders() || to_order == INVALID_VEH_ORDER_ID || to_order > this->vehicle->GetNumOrders()) &&
2103 DoCommandP(this->vehicle->tile, this->vehicle->index, from_order | (to_order << 16), CMD_MOVE_ORDER | CMD_MSG(STR_ERROR_CAN_T_MOVE_THIS_ORDER))) {
2104 this->selected_order = -1;
2105 this->UpdateButtonState();
2107 break;
2110 case WID_O_DELETE:
2111 this->OrderClick_Delete();
2112 break;
2114 case WID_O_STOP_SHARING:
2115 this->OrderClick_StopSharing();
2116 break;
2119 ResetObjectToPlace();
2121 if (this->order_over != INVALID_VEH_ORDER_ID) {
2122 /* End of drag-and-drop, hide dragged order destination highlight. */
2123 this->order_over = INVALID_VEH_ORDER_ID;
2124 this->SetWidgetDirty(WID_O_ORDER_LIST);
2128 virtual EventState OnHotkey(int hotkey) override
2130 if (this->vehicle->owner != _local_company) return ES_NOT_HANDLED;
2132 switch (hotkey) {
2133 case OHK_SKIP: this->OrderClick_Skip(); break;
2134 case OHK_DELETE: this->OrderClick_Delete(); break;
2135 case OHK_GOTO: this->OrderClick_Goto(OPOS_GOTO); break;
2136 case OHK_NONSTOP: this->OrderClick_Nonstop(-1); break;
2137 case OHK_FULLLOAD: this->OrderClick_FullLoad(-1); break;
2138 case OHK_UNLOAD: this->OrderClick_Unload(-1); break;
2139 case OHK_NEAREST_DEPOT: this->OrderClick_NearestDepot(); break;
2140 case OHK_ALWAYS_SERVICE: this->OrderClick_Service(-1); break;
2141 case OHK_TRANSFER: this->OrderHotkey_Transfer(); break;
2142 case OHK_NO_UNLOAD: this->OrderHotkey_NoUnload(); break;
2143 case OHK_NO_LOAD: this->OrderHotkey_NoLoad(); break;
2144 default: return ES_NOT_HANDLED;
2146 return ES_HANDLED;
2149 virtual void OnPlaceObject(Point pt, TileIndex tile) override
2151 if (this->goto_type == OPOS_GOTO) {
2152 const Order cmd = GetOrderCmdFromTile(this->vehicle, tile);
2153 if (cmd.IsType(OT_NOTHING)) return;
2155 if (DoCommandP(this->vehicle->tile, this->vehicle->index + (this->OrderGetSel() << 20), cmd.Pack(), CMD_INSERT_ORDER | CMD_MSG(STR_ERROR_CAN_T_INSERT_NEW_ORDER))) {
2156 /* With quick goto the Go To button stays active */
2157 if (!_settings_client.gui.quick_goto) ResetObjectToPlace();
2162 virtual bool OnVehicleSelect(const Vehicle *v) override
2164 /* v is vehicle getting orders. Only copy/clone orders if vehicle doesn't have any orders yet.
2165 * We disallow copying orders of other vehicles if we already have at least one order entry
2166 * ourself as it easily copies orders of vehicles within a station when we mean the station.
2167 * If you press CTRL on a non-empty orders vehicle a warning message will appear to confirm */
2168 bool share_order = _ctrl_pressed || this->goto_type == OPOS_SHARE;
2169 index_vehicle_share = v->index;
2171 if (this->vehicle->GetNumOrders() != 0) {
2172 if(!share_order) {
2173 return false;
2174 } else {
2175 ShowQuery(
2176 STR_SHARE_ORDERS_CAPTION,
2177 STR_SHARE_ORDERS_QUERY,
2178 this,
2179 AskShareOrdersCallback
2181 return true;
2185 CopyShareOrders(share_order);
2186 return true;
2189 /** Copy or Share the orders of a vehicle
2190 * @param share_order true for sharing, false for copying
2192 void CopyShareOrders(bool share_order)
2194 if (DoCommandP(this->vehicle->tile, this->vehicle->index | (share_order ? CO_SHARE : CO_COPY) << 30, index_vehicle_share,
2195 share_order ? CMD_CLONE_ORDER | CMD_MSG(STR_ERROR_CAN_T_SHARE_ORDER_LIST) : CMD_CLONE_ORDER | CMD_MSG(STR_ERROR_CAN_T_COPY_ORDER_LIST))) {
2196 this->selected_order = -1;
2197 ResetObjectToPlace();
2201 virtual void OnPlaceObjectAbort() override
2203 this->goto_type = OPOS_NONE;
2204 this->SetWidgetDirty(WID_O_GOTO);
2206 /* Remove drag highlighting if it exists. */
2207 if (this->order_over != INVALID_VEH_ORDER_ID) {
2208 this->order_over = INVALID_VEH_ORDER_ID;
2209 this->SetWidgetDirty(WID_O_ORDER_LIST);
2213 virtual void OnMouseDrag(Point pt, int widget) override
2215 if (this->selected_order != -1 && widget == WID_O_ORDER_LIST) {
2216 /* An order is dragged.. */
2217 VehicleOrderID from_order = this->OrderGetSel();
2218 VehicleOrderID to_order = this->GetOrderFromPt(pt.y);
2219 uint num_orders = this->vehicle->GetNumOrders();
2221 if (from_order != INVALID_VEH_ORDER_ID && from_order <= num_orders) {
2222 if (to_order != INVALID_VEH_ORDER_ID && to_order <= num_orders) { // ..over an existing order.
2223 this->order_over = to_order;
2224 this->SetWidgetDirty(widget);
2225 } else if (from_order != to_order && this->order_over != INVALID_VEH_ORDER_ID) { // ..outside of the order list.
2226 this->order_over = INVALID_VEH_ORDER_ID;
2227 this->SetWidgetDirty(widget);
2233 virtual void OnResize() override
2235 /* Update the scroll bar */
2236 this->vscroll->SetCapacityFromWidget(this, WID_O_ORDER_LIST);
2239 virtual void OnFocus(Window *previously_focused_window) override
2241 if (HasFocusedVehicleChanged(this->window_number, previously_focused_window)) {
2242 MarkAllRoutePathsDirty(this->vehicle);
2243 MarkAllRouteStepsDirty(this->vehicle);
2247 virtual void OnFocusLost(Window *newly_focused_window) override
2249 if (HasFocusedVehicleChanged(this->window_number, newly_focused_window)) {
2250 MarkAllRoutePathsDirty(this->vehicle);
2251 MarkAllRouteStepsDirty(this->vehicle);
2255 const Vehicle *GetVehicle()
2257 return this->vehicle;
2260 static HotkeyList hotkeys;
2264 void AskShareOrdersCallback(Window *w, bool confirmed)
2266 OrdersWindow *ow = static_cast<OrdersWindow *>(w);
2268 if (confirmed) {
2269 ow->CopyShareOrders(true);
2274 static Hotkey order_hotkeys[] = {
2275 Hotkey('D', "skip", OHK_SKIP),
2276 Hotkey('F', "delete", OHK_DELETE),
2277 Hotkey('G', "goto", OHK_GOTO),
2278 Hotkey('H', "nonstop", OHK_NONSTOP),
2279 Hotkey('J', "fullload", OHK_FULLLOAD),
2280 Hotkey('K', "unload", OHK_UNLOAD),
2281 Hotkey((uint16)0, "nearest_depot", OHK_NEAREST_DEPOT),
2282 Hotkey((uint16)0, "always_service", OHK_ALWAYS_SERVICE),
2283 Hotkey((uint16)0, "transfer", OHK_TRANSFER),
2284 Hotkey((uint16)0, "no_unload", OHK_NO_UNLOAD),
2285 Hotkey((uint16)0, "no_load", OHK_NO_LOAD),
2286 HOTKEY_LIST_END
2288 HotkeyList OrdersWindow::hotkeys("order", order_hotkeys);
2290 /** Nested widget definition for "your" train orders. */
2291 static const NWidgetPart _nested_orders_train_widgets[] = {
2292 NWidget(NWID_HORIZONTAL),
2293 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
2294 NWidget(WWT_CAPTION, COLOUR_GREY, WID_O_CAPTION), SetDataTip(STR_ORDERS_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
2295 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_O_TIMETABLE_VIEW), SetMinimalSize(61, 14), SetDataTip(STR_ORDERS_TIMETABLE_VIEW, STR_ORDERS_TIMETABLE_VIEW_TOOLTIP),
2296 NWidget(WWT_SHADEBOX, COLOUR_GREY),
2297 NWidget(WWT_DEFSIZEBOX, COLOUR_GREY),
2298 NWidget(WWT_STICKYBOX, COLOUR_GREY),
2299 EndContainer(),
2300 NWidget(NWID_HORIZONTAL),
2301 NWidget(WWT_PANEL, COLOUR_GREY, WID_O_ORDER_LIST), SetMinimalSize(372, 62), SetDataTip(0x0, STR_ORDERS_LIST_TOOLTIP), SetResize(1, 1), SetScrollbar(WID_O_SCROLLBAR), EndContainer(),
2302 NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_O_SCROLLBAR),
2303 EndContainer(),
2305 /* First button row. */
2306 NWidget(NWID_HORIZONTAL),
2307 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_O_SEL_TOP_ROW_GROUNDVEHICLE),
2308 NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
2309 NWidget(NWID_BUTTON_DROPDOWN, COLOUR_GREY, WID_O_NON_STOP), SetMinimalSize(93, 12), SetFill(1, 0),
2310 SetDataTip(STR_ORDER_NON_STOP, STR_ORDER_TOOLTIP_NON_STOP), SetResize(1, 0),
2311 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_O_SEL_TOP_LEFT),
2312 NWidget(NWID_BUTTON_DROPDOWN, COLOUR_GREY, WID_O_FULL_LOAD), SetMinimalSize(93, 12), SetFill(1, 0),
2313 SetDataTip(STR_ORDER_TOGGLE_FULL_LOAD, STR_ORDER_TOOLTIP_FULL_LOAD), SetResize(1, 0),
2314 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_O_REFIT), SetMinimalSize(93, 12), SetFill(1, 0),
2315 SetDataTip(STR_ORDER_REFIT, STR_ORDER_REFIT_TOOLTIP), SetResize(1, 0),
2316 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_O_REVERSE), SetMinimalSize(93, 12), SetFill(1, 0),
2317 SetDataTip(STR_ORDER_REVERSE, STR_ORDER_REVERSE_TOOLTIP), SetResize(1, 0),
2318 EndContainer(),
2319 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_O_SEL_TOP_MIDDLE),
2320 NWidget(NWID_BUTTON_DROPDOWN, COLOUR_GREY, WID_O_UNLOAD), SetMinimalSize(93, 12), SetFill(1, 0),
2321 SetDataTip(STR_ORDER_TOGGLE_UNLOAD, STR_ORDER_TOOLTIP_UNLOAD), SetResize(1, 0),
2322 NWidget(NWID_BUTTON_DROPDOWN, COLOUR_GREY, WID_O_SERVICE), SetMinimalSize(93, 12), SetFill(1, 0),
2323 SetDataTip(STR_ORDER_SERVICE, STR_ORDER_SERVICE_TOOLTIP), SetResize(1, 0),
2324 EndContainer(),
2325 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_O_SEL_TOP_RIGHT),
2326 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_O_EMPTY), SetMinimalSize(93, 12), SetFill(1, 0),
2327 SetDataTip(STR_ORDER_REFIT, STR_ORDER_REFIT_TOOLTIP), SetResize(1, 0),
2328 NWidget(NWID_BUTTON_DROPDOWN, COLOUR_GREY, WID_O_REFIT_DROPDOWN), SetMinimalSize(93, 12), SetFill(1, 0),
2329 SetDataTip(STR_ORDER_REFIT_AUTO, STR_ORDER_REFIT_AUTO_TOOLTIP), SetResize(1, 0),
2330 EndContainer(),
2331 EndContainer(),
2332 NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
2333 NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_O_COND_VARIABLE), SetMinimalSize(124, 12), SetFill(1, 0),
2334 SetDataTip(STR_NULL, STR_ORDER_CONDITIONAL_VARIABLE_TOOLTIP), SetResize(1, 0),
2335 NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_O_COND_COMPARATOR), SetMinimalSize(124, 12), SetFill(1, 0),
2336 SetDataTip(STR_NULL, STR_ORDER_CONDITIONAL_COMPARATOR_TOOLTIP), SetResize(1, 0),
2337 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_O_SEL_COND_VALUE),
2338 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_O_COND_VALUE), SetMinimalSize(124, 12), SetFill(1, 0),
2339 SetDataTip(STR_BLACK_COMMA, STR_ORDER_CONDITIONAL_VALUE_TOOLTIP), SetResize(1, 0),
2340 NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_O_COND_CARGO), SetMinimalSize(124, 12), SetFill(1, 0),
2341 SetDataTip(STR_NULL, STR_ORDER_CONDITIONAL_CARGO_TOOLTIP), SetResize(1, 0),
2342 NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_O_COND_SLOT), SetMinimalSize(124, 12), SetFill(1, 0),
2343 SetDataTip(STR_NULL, STR_ORDER_CONDITIONAL_SLOT_TOOLTIP), SetResize(1, 0),
2344 EndContainer(),
2345 EndContainer(),
2346 EndContainer(),
2347 NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_O_SHARED_ORDER_LIST), SetMinimalSize(12, 12), SetDataTip(SPR_SHARED_ORDERS_ICON, STR_ORDERS_VEH_WITH_SHARED_ORDERS_LIST_TOOLTIP),
2348 EndContainer(),
2350 /* Second button row. */
2351 NWidget(NWID_HORIZONTAL),
2352 NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
2353 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_O_SKIP), SetMinimalSize(124, 12), SetFill(1, 0),
2354 SetDataTip(STR_ORDERS_SKIP_BUTTON, STR_ORDERS_SKIP_TOOLTIP), SetResize(1, 0),
2355 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_O_SEL_BOTTOM_MIDDLE),
2356 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_O_DELETE), SetMinimalSize(124, 12), SetFill(1, 0),
2357 SetDataTip(STR_ORDERS_DELETE_BUTTON, STR_ORDERS_DELETE_TOOLTIP), SetResize(1, 0),
2358 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_O_STOP_SHARING), SetMinimalSize(124, 12), SetFill(1, 0),
2359 SetDataTip(STR_ORDERS_STOP_SHARING_BUTTON, STR_ORDERS_STOP_SHARING_TOOLTIP), SetResize(1, 0),
2360 EndContainer(),
2361 NWidget(NWID_BUTTON_DROPDOWN, COLOUR_GREY, WID_O_GOTO), SetMinimalSize(124, 12), SetFill(1, 0),
2362 SetDataTip(STR_ORDERS_GO_TO_BUTTON, STR_ORDERS_GO_TO_TOOLTIP), SetResize(1, 0),
2363 EndContainer(),
2364 NWidget(WWT_RESIZEBOX, COLOUR_GREY),
2365 EndContainer(),
2368 static WindowDesc _orders_train_desc(
2369 WDP_AUTO, "view_vehicle_orders_train", 384, 100,
2370 WC_VEHICLE_ORDERS, WC_VEHICLE_VIEW,
2371 WDF_CONSTRUCTION,
2372 _nested_orders_train_widgets, lengthof(_nested_orders_train_widgets),
2373 &OrdersWindow::hotkeys
2376 /** Nested widget definition for "your" orders (non-train). */
2377 static const NWidgetPart _nested_orders_widgets[] = {
2378 NWidget(NWID_HORIZONTAL),
2379 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
2380 NWidget(WWT_CAPTION, COLOUR_GREY, WID_O_CAPTION), SetDataTip(STR_ORDERS_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
2381 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_O_TIMETABLE_VIEW), SetMinimalSize(61, 14), SetDataTip(STR_ORDERS_TIMETABLE_VIEW, STR_ORDERS_TIMETABLE_VIEW_TOOLTIP),
2382 NWidget(WWT_SHADEBOX, COLOUR_GREY),
2383 NWidget(WWT_DEFSIZEBOX, COLOUR_GREY),
2384 NWidget(WWT_STICKYBOX, COLOUR_GREY),
2385 EndContainer(),
2386 NWidget(NWID_HORIZONTAL),
2387 NWidget(WWT_PANEL, COLOUR_GREY, WID_O_ORDER_LIST), SetMinimalSize(372, 62), SetDataTip(0x0, STR_ORDERS_LIST_TOOLTIP), SetResize(1, 1), SetScrollbar(WID_O_SCROLLBAR), EndContainer(),
2388 NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_O_SCROLLBAR),
2389 EndContainer(),
2391 /* First button row. */
2392 NWidget(NWID_HORIZONTAL),
2393 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_O_SEL_TOP_ROW),
2394 /* Load + unload + refit buttons. */
2395 NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
2396 NWidget(NWID_BUTTON_DROPDOWN, COLOUR_GREY, WID_O_FULL_LOAD), SetMinimalSize(124, 12), SetFill(1, 0),
2397 SetDataTip(STR_ORDER_TOGGLE_FULL_LOAD, STR_ORDER_TOOLTIP_FULL_LOAD), SetResize(1, 0),
2398 NWidget(NWID_BUTTON_DROPDOWN, COLOUR_GREY, WID_O_UNLOAD), SetMinimalSize(124, 12), SetFill(1, 0),
2399 SetDataTip(STR_ORDER_TOGGLE_UNLOAD, STR_ORDER_TOOLTIP_UNLOAD), SetResize(1, 0),
2400 NWidget(NWID_BUTTON_DROPDOWN, COLOUR_GREY, WID_O_REFIT_DROPDOWN), SetMinimalSize(124, 12), SetFill(1, 0),
2401 SetDataTip(STR_ORDER_REFIT_AUTO, STR_ORDER_REFIT_AUTO_TOOLTIP), SetResize(1, 0),
2402 EndContainer(),
2403 /* Refit + service buttons. */
2404 NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
2405 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_O_REFIT), SetMinimalSize(186, 12), SetFill(1, 0),
2406 SetDataTip(STR_ORDER_REFIT, STR_ORDER_REFIT_TOOLTIP), SetResize(1, 0),
2407 NWidget(NWID_BUTTON_DROPDOWN, COLOUR_GREY, WID_O_SERVICE), SetMinimalSize(124, 12), SetFill(1, 0),
2408 SetDataTip(STR_ORDER_SERVICE, STR_ORDER_SERVICE_TOOLTIP), SetResize(1, 0),
2409 EndContainer(),
2411 /* Buttons for setting a condition. */
2412 NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
2413 NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_O_COND_VARIABLE), SetMinimalSize(124, 12), SetFill(1, 0),
2414 SetDataTip(STR_NULL, STR_ORDER_CONDITIONAL_VARIABLE_TOOLTIP), SetResize(1, 0),
2415 NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_O_COND_COMPARATOR), SetMinimalSize(124, 12), SetFill(1, 0),
2416 SetDataTip(STR_NULL, STR_ORDER_CONDITIONAL_COMPARATOR_TOOLTIP), SetResize(1, 0),
2417 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_O_SEL_COND_VALUE),
2418 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_O_COND_VALUE), SetMinimalSize(124, 12), SetFill(1, 0),
2419 SetDataTip(STR_BLACK_COMMA, STR_ORDER_CONDITIONAL_VALUE_TOOLTIP), SetResize(1, 0),
2420 NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_O_COND_CARGO), SetMinimalSize(124, 12), SetFill(1, 0),
2421 SetDataTip(STR_NULL, STR_ORDER_CONDITIONAL_CARGO_TOOLTIP), SetResize(1, 0),
2422 NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_O_COND_SLOT), SetMinimalSize(124, 12), SetFill(1, 0),
2423 SetDataTip(STR_NULL, STR_ORDER_CONDITIONAL_SLOT_TOOLTIP), SetResize(1, 0),
2424 EndContainer(),
2425 EndContainer(),
2426 EndContainer(),
2428 NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_O_SHARED_ORDER_LIST), SetMinimalSize(12, 12), SetDataTip(SPR_SHARED_ORDERS_ICON, STR_ORDERS_VEH_WITH_SHARED_ORDERS_LIST_TOOLTIP),
2429 EndContainer(),
2431 /* Second button row. */
2432 NWidget(NWID_HORIZONTAL),
2433 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_O_SKIP), SetMinimalSize(124, 12), SetFill(1, 0),
2434 SetDataTip(STR_ORDERS_SKIP_BUTTON, STR_ORDERS_SKIP_TOOLTIP), SetResize(1, 0),
2435 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_O_SEL_BOTTOM_MIDDLE),
2436 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_O_DELETE), SetMinimalSize(124, 12), SetFill(1, 0),
2437 SetDataTip(STR_ORDERS_DELETE_BUTTON, STR_ORDERS_DELETE_TOOLTIP), SetResize(1, 0),
2438 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_O_STOP_SHARING), SetMinimalSize(124, 12), SetFill(1, 0),
2439 SetDataTip(STR_ORDERS_STOP_SHARING_BUTTON, STR_ORDERS_STOP_SHARING_TOOLTIP), SetResize(1, 0),
2440 EndContainer(),
2441 NWidget(NWID_BUTTON_DROPDOWN, COLOUR_GREY, WID_O_GOTO), SetMinimalSize(124, 12), SetFill(1, 0),
2442 SetDataTip(STR_ORDERS_GO_TO_BUTTON, STR_ORDERS_GO_TO_TOOLTIP), SetResize(1, 0),
2443 NWidget(WWT_RESIZEBOX, COLOUR_GREY),
2444 EndContainer(),
2447 static WindowDesc _orders_desc(
2448 WDP_AUTO, "view_vehicle_orders", 384, 100,
2449 WC_VEHICLE_ORDERS, WC_VEHICLE_VIEW,
2450 WDF_CONSTRUCTION,
2451 _nested_orders_widgets, lengthof(_nested_orders_widgets),
2452 &OrdersWindow::hotkeys
2455 /** Nested widget definition for competitor orders. */
2456 static const NWidgetPart _nested_other_orders_widgets[] = {
2457 NWidget(NWID_HORIZONTAL),
2458 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
2459 NWidget(WWT_CAPTION, COLOUR_GREY, WID_O_CAPTION), SetDataTip(STR_ORDERS_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
2460 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_O_TIMETABLE_VIEW), SetMinimalSize(61, 14), SetDataTip(STR_ORDERS_TIMETABLE_VIEW, STR_ORDERS_TIMETABLE_VIEW_TOOLTIP),
2461 NWidget(WWT_SHADEBOX, COLOUR_GREY),
2462 NWidget(WWT_DEFSIZEBOX, COLOUR_GREY),
2463 NWidget(WWT_STICKYBOX, COLOUR_GREY),
2464 EndContainer(),
2465 NWidget(NWID_HORIZONTAL),
2466 NWidget(WWT_PANEL, COLOUR_GREY, WID_O_ORDER_LIST), SetMinimalSize(372, 72), SetDataTip(0x0, STR_ORDERS_LIST_TOOLTIP), SetResize(1, 1), SetScrollbar(WID_O_SCROLLBAR), EndContainer(),
2467 NWidget(NWID_VERTICAL),
2468 NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_O_SCROLLBAR),
2469 NWidget(WWT_RESIZEBOX, COLOUR_GREY),
2470 EndContainer(),
2471 EndContainer(),
2474 static WindowDesc _other_orders_desc(
2475 WDP_AUTO, "view_vehicle_orders_competitor", 384, 86,
2476 WC_VEHICLE_ORDERS, WC_VEHICLE_VIEW,
2477 WDF_CONSTRUCTION,
2478 _nested_other_orders_widgets, lengthof(_nested_other_orders_widgets),
2479 &OrdersWindow::hotkeys
2482 void ShowOrdersWindow(const Vehicle *v)
2484 DeleteWindowById(WC_VEHICLE_DETAILS, v->index, false);
2485 DeleteWindowById(WC_VEHICLE_TIMETABLE, v->index, false);
2486 if (BringWindowToFrontById(WC_VEHICLE_ORDERS, v->index) != nullptr) return;
2488 /* Using a different WindowDescs for _local_company causes problems.
2489 * Due to this we have to close order windows in ChangeWindowOwner/DeleteCompanyWindows,
2490 * because we cannot change switch the WindowDescs and keeping the old WindowDesc results
2491 * in crashed due to missing widges.
2492 * TODO Rewrite the order GUI to not use different WindowDescs.
2494 if (v->owner != _local_company) {
2495 new OrdersWindow(&_other_orders_desc, v);
2496 } else {
2497 new OrdersWindow(v->IsGroundVehicle() ? &_orders_train_desc : &_orders_desc, v);