Translations update
[openttd/fttd.git] / src / order_gui.cpp
bloba2a76915734ff5c2771df26984e710c32e7c9746
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 "roadveh.h"
16 #include "timetable.h"
17 #include "strings_func.h"
18 #include "company_func.h"
19 #include "widgets/dropdown_type.h"
20 #include "widgets/dropdown_func.h"
21 #include "textbuf_gui.h"
22 #include "string.h"
23 #include "tilehighlight_func.h"
24 #include "network/network.h"
25 #include "station_base.h"
26 #include "waypoint_base.h"
27 #include "core/geometry_func.hpp"
28 #include "hotkeys.h"
29 #include "aircraft.h"
30 #include "engine_func.h"
32 #include "widgets/order_widget.h"
35 /** Order load types that could be given to station orders. */
36 static const StringID _station_load_types[][5][5] = {
38 /* No refitting. */
40 STR_EMPTY,
41 INVALID_STRING_ID,
42 STR_ORDER_FULL_LOAD,
43 STR_ORDER_FULL_LOAD_ANY,
44 STR_ORDER_NO_LOAD,
45 }, {
46 STR_ORDER_UNLOAD,
47 INVALID_STRING_ID,
48 STR_ORDER_UNLOAD_FULL_LOAD,
49 STR_ORDER_UNLOAD_FULL_LOAD_ANY,
50 STR_ORDER_UNLOAD_NO_LOAD,
51 }, {
52 STR_ORDER_TRANSFER,
53 INVALID_STRING_ID,
54 STR_ORDER_TRANSFER_FULL_LOAD,
55 STR_ORDER_TRANSFER_FULL_LOAD_ANY,
56 STR_ORDER_TRANSFER_NO_LOAD,
57 }, {
58 /* Unload and transfer do not work together. */
59 INVALID_STRING_ID,
60 INVALID_STRING_ID,
61 INVALID_STRING_ID,
62 INVALID_STRING_ID,
63 }, {
64 STR_ORDER_NO_UNLOAD,
65 INVALID_STRING_ID,
66 STR_ORDER_NO_UNLOAD_FULL_LOAD,
67 STR_ORDER_NO_UNLOAD_FULL_LOAD_ANY,
68 STR_ORDER_NO_UNLOAD_NO_LOAD,
70 }, {
71 /* With auto-refitting. No loading and auto-refitting do not work together. */
73 STR_ORDER_AUTO_REFIT,
74 INVALID_STRING_ID,
75 STR_ORDER_FULL_LOAD_REFIT,
76 STR_ORDER_FULL_LOAD_ANY_REFIT,
77 INVALID_STRING_ID,
78 }, {
79 STR_ORDER_UNLOAD_REFIT,
80 INVALID_STRING_ID,
81 STR_ORDER_UNLOAD_FULL_LOAD_REFIT,
82 STR_ORDER_UNLOAD_FULL_LOAD_ANY_REFIT,
83 INVALID_STRING_ID,
84 }, {
85 STR_ORDER_TRANSFER_REFIT,
86 INVALID_STRING_ID,
87 STR_ORDER_TRANSFER_FULL_LOAD_REFIT,
88 STR_ORDER_TRANSFER_FULL_LOAD_ANY_REFIT,
89 INVALID_STRING_ID,
90 }, {
91 /* Unload and transfer do not work together. */
92 INVALID_STRING_ID,
93 INVALID_STRING_ID,
94 INVALID_STRING_ID,
95 INVALID_STRING_ID,
96 }, {
97 STR_ORDER_NO_UNLOAD_REFIT,
98 INVALID_STRING_ID,
99 STR_ORDER_NO_UNLOAD_FULL_LOAD_REFIT,
100 STR_ORDER_NO_UNLOAD_FULL_LOAD_ANY_REFIT,
101 INVALID_STRING_ID,
106 static const StringID _order_non_stop_drowdown[] = {
107 STR_ORDER_GO_TO,
108 STR_ORDER_GO_NON_STOP_TO,
109 STR_ORDER_GO_VIA,
110 STR_ORDER_GO_NON_STOP_VIA,
111 INVALID_STRING_ID
114 static const StringID _order_full_load_drowdown[] = {
115 STR_ORDER_DROP_LOAD_IF_POSSIBLE,
116 STR_EMPTY,
117 STR_ORDER_DROP_FULL_LOAD_ALL,
118 STR_ORDER_DROP_FULL_LOAD_ANY,
119 STR_ORDER_DROP_NO_LOADING,
120 INVALID_STRING_ID
123 static const StringID _order_unload_drowdown[] = {
124 STR_ORDER_DROP_UNLOAD_IF_ACCEPTED,
125 STR_ORDER_DROP_UNLOAD,
126 STR_ORDER_DROP_TRANSFER,
127 STR_EMPTY,
128 STR_ORDER_DROP_NO_UNLOADING,
129 INVALID_STRING_ID
132 static const StringID _order_goto_dropdown[] = {
133 STR_ORDER_GO_TO,
134 STR_ORDER_GO_TO_NEAREST_DEPOT,
135 STR_ORDER_CONDITIONAL,
136 STR_ORDER_SHARE,
137 INVALID_STRING_ID
140 static const StringID _order_goto_dropdown_aircraft[] = {
141 STR_ORDER_GO_TO,
142 STR_ORDER_GO_TO_NEAREST_HANGAR,
143 STR_ORDER_CONDITIONAL,
144 STR_ORDER_SHARE,
145 INVALID_STRING_ID
148 /** Variables for conditional orders; this defines the order of appearance in the dropdown box */
149 static const OrderConditionVariable _order_conditional_variable[] = {
150 OCV_LOAD_PERCENTAGE,
151 OCV_RELIABILITY,
152 OCV_MAX_SPEED,
153 OCV_AGE,
154 OCV_REMAINING_LIFETIME,
155 OCV_REQUIRES_SERVICE,
156 OCV_UNCONDITIONALLY,
159 static const StringID _order_conditional_condition[] = {
160 STR_ORDER_CONDITIONAL_COMPARATOR_EQUALS,
161 STR_ORDER_CONDITIONAL_COMPARATOR_NOT_EQUALS,
162 STR_ORDER_CONDITIONAL_COMPARATOR_LESS_THAN,
163 STR_ORDER_CONDITIONAL_COMPARATOR_LESS_EQUALS,
164 STR_ORDER_CONDITIONAL_COMPARATOR_MORE_THAN,
165 STR_ORDER_CONDITIONAL_COMPARATOR_MORE_EQUALS,
166 STR_ORDER_CONDITIONAL_COMPARATOR_IS_TRUE,
167 STR_ORDER_CONDITIONAL_COMPARATOR_IS_FALSE,
168 INVALID_STRING_ID,
171 extern uint ConvertSpeedToDisplaySpeed(uint speed);
172 extern uint ConvertDisplaySpeedToSpeed(uint speed);
174 static const StringID _order_depot_action_dropdown[] = {
175 STR_ORDER_DROP_GO_ALWAYS_DEPOT,
176 STR_ORDER_DROP_SERVICE_DEPOT,
177 STR_ORDER_DROP_HALT_DEPOT,
178 INVALID_STRING_ID
181 static int DepotActionStringIndex(const Order *order)
183 if (order->GetDepotActionType() & ODATFB_HALT) {
184 return DA_STOP;
185 } else if (order->GetDepotOrderType() & ODTFB_SERVICE) {
186 return DA_SERVICE;
187 } else {
188 return DA_ALWAYS_GO;
193 * Draws an order in order or timetable GUI
194 * @param v Vehicle the order belongs to
195 * @param order The order to draw
196 * @param order_index Index of the order in the orders of the vehicle
197 * @param dpi Area to draw on
198 * @param y Y position for drawing
199 * @param selected True, if the order is selected
200 * @param timetable True, when drawing in the timetable GUI
201 * @param left Left border for text drawing
202 * @param middle X position between order index and order text
203 * @param right Right border for text drawing
205 void DrawOrderString (const Vehicle *v, const Order *order, int order_index,
206 BlitArea *dpi, int y, bool selected, bool timetable,
207 int left, int middle, int right)
209 bool rtl = _current_text_dir == TD_RTL;
211 SpriteID sprite = rtl ? SPR_ARROW_LEFT : SPR_ARROW_RIGHT;
212 Dimension sprite_size = GetSpriteSize(sprite);
213 if (v->cur_real_order_index == order_index) {
214 DrawSprite (dpi, sprite, PAL_NONE, rtl ? right - sprite_size.width : left, y + ((int)FONT_HEIGHT_NORMAL - (int)sprite_size.height) / 2);
215 DrawSprite (dpi, sprite, PAL_NONE, rtl ? right - 2 * sprite_size.width : left + sprite_size.width, y + ((int)FONT_HEIGHT_NORMAL - (int)sprite_size.height) / 2);
216 } else if (v->cur_implicit_order_index == order_index) {
217 DrawSprite (dpi, sprite, PAL_NONE, rtl ? right - sprite_size.width : left, y + ((int)FONT_HEIGHT_NORMAL - (int)sprite_size.height) / 2);
220 TextColour colour = TC_BLACK;
221 if (order->IsType(OT_IMPLICIT)) {
222 colour = (selected ? TC_SILVER : TC_GREY) | TC_NO_SHADE;
223 } else if (selected) {
224 colour = TC_WHITE;
227 SetDParam(0, order_index + 1);
228 DrawString (dpi, left, rtl ? right - 2 * sprite_size.width - 3 : middle, y, STR_ORDER_INDEX, colour, SA_RIGHT | SA_FORCE);
230 SetDParam(5, STR_EMPTY);
231 SetDParam(8, STR_EMPTY);
233 /* Check range for aircraft. */
234 if (v->type == VEH_AIRCRAFT && Aircraft::From(v)->GetRange() > 0 && order->IsGotoOrder()) {
235 const Order *next = order->next != NULL ? order->next : v->GetFirstOrder();
236 if (GetOrderDistance(order, next, v) > Aircraft::From(v)->acache.cached_max_range_sqr) SetDParam(8, STR_ORDER_OUT_OF_RANGE);
239 switch (order->GetType()) {
240 case OT_DUMMY:
241 SetDParam(0, STR_INVALID_ORDER);
242 SetDParam(1, order->GetDestination());
243 break;
245 case OT_IMPLICIT:
246 SetDParam(0, STR_ORDER_GO_TO_STATION);
247 SetDParam(1, STR_ORDER_GO_TO);
248 SetDParam(2, order->GetDestination());
249 SetDParam(3, timetable ? STR_EMPTY : STR_ORDER_IMPLICIT);
250 break;
252 case OT_GOTO_STATION: {
253 OrderLoadFlags load = order->GetLoadType();
254 OrderUnloadFlags unload = order->GetUnloadType();
256 SetDParam(0, STR_ORDER_GO_TO_STATION);
257 SetDParam(1, STR_ORDER_GO_TO + (v->IsGroundVehicle() ? order->GetNonStopType() : 0));
258 SetDParam(2, order->GetDestination());
260 if (timetable) {
261 SetDParam(3, STR_EMPTY);
263 if (order->GetWaitTime() > 0) {
264 SetDParam(5, order->IsWaitTimetabled() ? STR_TIMETABLE_STAY_FOR : STR_TIMETABLE_STAY_FOR_ESTIMATED);
265 SetTimetableParams(6, 7, order->GetWaitTime());
267 } else {
268 SetDParam(3, (order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION) ? STR_EMPTY : _station_load_types[order->IsRefit()][unload][load]);
269 if (order->IsRefit()) {
270 SetDParam(4, order->IsAutoRefit() ? STR_ORDER_AUTO_REFIT_ANY : CargoSpec::Get(order->GetRefitCargo())->name);
272 if (v->type == VEH_TRAIN && (order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION) == 0) {
273 SetDParam(5, order->GetStopLocation() + STR_ORDER_STOP_LOCATION_NEAR_END);
276 break;
279 case OT_GOTO_DEPOT:
280 if (order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) {
281 SetDParam(0, STR_ORDER_GO_TO_NEAREST_DEPOT_FORMAT);
282 if (v->type == VEH_AIRCRAFT) {
283 SetDParam(2, STR_ORDER_NEAREST_HANGAR);
284 SetDParam(3, STR_EMPTY);
285 } else {
286 SetDParam(2, STR_ORDER_NEAREST_DEPOT);
287 SetDParam(3, STR_ORDER_TRAIN_DEPOT + v->type);
289 } else {
290 SetDParam(0, STR_ORDER_GO_TO_DEPOT_FORMAT);
291 SetDParam(2, v->type);
292 SetDParam(3, order->GetDestination());
295 if (order->GetDepotOrderType() & ODTFB_SERVICE) {
296 SetDParam(1, (order->GetNonStopType() & ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS) ? STR_ORDER_SERVICE_NON_STOP_AT : STR_ORDER_SERVICE_AT);
297 } else {
298 SetDParam(1, (order->GetNonStopType() & ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS) ? STR_ORDER_GO_NON_STOP_TO : STR_ORDER_GO_TO);
301 if (!timetable && (order->GetDepotActionType() & ODATFB_HALT)) {
302 SetDParam(5, STR_ORDER_STOP_ORDER);
305 if (!timetable && order->IsRefit()) {
306 SetDParam(5, (order->GetDepotActionType() & ODATFB_HALT) ? STR_ORDER_REFIT_STOP_ORDER : STR_ORDER_REFIT_ORDER);
307 SetDParam(6, CargoSpec::Get(order->GetRefitCargo())->name);
309 break;
311 case OT_GOTO_WAYPOINT:
312 SetDParam(0, (order->GetNonStopType() & ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS) ? STR_ORDER_GO_NON_STOP_TO_WAYPOINT : STR_ORDER_GO_TO_WAYPOINT);
313 SetDParam(1, order->GetDestination());
314 break;
316 case OT_CONDITIONAL:
317 SetDParam(1, order->GetConditionSkipToOrder() + 1);
318 if (order->GetConditionVariable() == OCV_UNCONDITIONALLY) {
319 SetDParam(0, STR_ORDER_CONDITIONAL_UNCONDITIONAL);
320 } else {
321 OrderConditionComparator occ = order->GetConditionComparator();
322 SetDParam(0, (occ == OCC_IS_TRUE || occ == OCC_IS_FALSE) ? STR_ORDER_CONDITIONAL_TRUE_FALSE : STR_ORDER_CONDITIONAL_NUM);
323 SetDParam(2, STR_ORDER_CONDITIONAL_LOAD_PERCENTAGE + order->GetConditionVariable());
324 SetDParam(3, STR_ORDER_CONDITIONAL_COMPARATOR_EQUALS + occ);
326 uint value = order->GetConditionValue();
327 if (order->GetConditionVariable() == OCV_MAX_SPEED) value = ConvertSpeedToDisplaySpeed(value);
328 SetDParam(4, value);
331 if (timetable && order->GetWaitTime() > 0) {
332 SetDParam(5, order->IsWaitTimetabled() ? STR_TIMETABLE_AND_TRAVEL_FOR : STR_TIMETABLE_AND_TRAVEL_FOR_ESTIMATED);
333 SetTimetableParams(6, 7, order->GetWaitTime());
334 } else {
335 SetDParam(5, STR_EMPTY);
337 break;
339 default: NOT_REACHED();
342 DrawString (dpi, rtl ? left : middle, rtl ? middle : right, y, STR_ORDER_TEXT, colour);
346 StringID GetErrCloneOrder (TileIndex tile, uint32 p1, uint32 p2, const char *text)
348 switch (GB(p1, 30, 2)) {
349 case CO_SHARE: return STR_ERROR_CAN_T_SHARE_ORDER_LIST;
350 case CO_COPY: return STR_ERROR_CAN_T_COPY_ORDER_LIST;
351 default: return STR_ERROR_CAN_T_STOP_SHARING_ORDER_LIST;
355 StringID GetErrSkipToOrder (TileIndex tile, uint32 p1, uint32 p2, const char *text)
357 return HasBit (p1, 31) ? STR_ERROR_CAN_T_SKIP_TO_ORDER : STR_ERROR_CAN_T_SKIP_ORDER;
362 * Get the order command a vehicle can do in a given tile.
363 * @param v Vehicle involved.
364 * @param tile Tile being queried.
365 * @return The order associated to vehicle v in given tile (or empty order if vehicle can do nothing in the tile).
367 static BaseOrder GetOrderCmdFromTile (const Vehicle *v, TileIndex tile)
369 BaseOrder order;
371 switch (GetTileType(tile)) {
372 case TT_MISC:
373 if (!IsTileSubtype(tile, TT_MISC_DEPOT)) break;
374 if (v->type != (IsRailDepot(tile) ? VEH_TRAIN : VEH_ROAD)) break;
375 if (!IsTileOwner (tile, _local_company)) break;
376 order.MakeGoToDepot (GetDepotIndex (tile), ODTFB_PART_OF_ORDERS,
377 _settings_client.gui.new_nonstop ? ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS : ONSF_STOP_EVERYWHERE);
378 if (_ctrl_pressed) order.SetDepotOrderType((OrderDepotTypeFlags)(order.GetDepotOrderType() ^ ODTFB_SERVICE));
379 return order;
381 case TT_WATER:
382 if (v->type != VEH_SHIP) break;
383 if (!IsShipDepot (tile)) break;
384 if (!IsTileOwner (tile, _local_company)) break;
385 order.MakeGoToDepot (GetDepotIndex (tile), ODTFB_PART_OF_ORDERS, ONSF_STOP_EVERYWHERE);
386 if (_ctrl_pressed) order.SetDepotOrderType ((OrderDepotTypeFlags)(order.GetDepotOrderType() ^ ODTFB_SERVICE));
387 return order;
389 case TT_STATION: {
390 StationID st_index = GetStationIndex (tile);
391 switch (GetStationType (tile)) {
392 case STATION_WAYPOINT:
393 /* check waypoint */
394 if (v->type != VEH_TRAIN) break;
395 if (!IsTileOwner (tile, _local_company)) break;
396 order.MakeGoToWaypoint (st_index);
397 if (_settings_client.gui.new_nonstop != _ctrl_pressed) order.SetNonStopType (ONSF_NO_STOP_AT_ANY_STATION);
398 return order;
400 case STATION_BUOY:
401 /* check buoy (no ownership) */
402 if (v->type != VEH_SHIP) break;
403 order.MakeGoToWaypoint (st_index);
404 return order;
406 case STATION_AIRPORT:
407 /* special case for hangars */
408 if (v->type == VEH_AIRCRAFT && IsTileOwner (tile, _local_company) && IsHangar(tile)) {
409 order.MakeGoToDepot (st_index, ODTFB_PART_OF_ORDERS, ONSF_STOP_EVERYWHERE);
410 if (_ctrl_pressed) order.SetDepotOrderType ((OrderDepotTypeFlags)(order.GetDepotOrderType() ^ ODTFB_SERVICE));
411 return order;
413 /* fall through */
414 default:
415 const Station *st = Station::Get (st_index);
416 if (st->owner != _local_company && st->owner != OWNER_NONE) break;
418 byte facil = (v->type == VEH_SHIP) ? FACIL_DOCK :
419 (v->type == VEH_TRAIN) ? FACIL_TRAIN :
420 (v->type == VEH_AIRCRAFT) ? FACIL_AIRPORT :
421 RoadVehicle::From(v)->IsBus() ? FACIL_BUS_STOP : FACIL_TRUCK_STOP;
423 if (st->facilities & facil) {
424 order.MakeGoToStation (st_index);
425 if (_ctrl_pressed) order.SetLoadType(OLF_FULL_LOAD_ANY);
426 if (_settings_client.gui.new_nonstop && v->IsGroundVehicle()) order.SetNonStopType(ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS);
427 order.SetStopLocation(v->type == VEH_TRAIN ? (OrderStopLocation)(_settings_client.gui.stop_location) : OSL_PLATFORM_FAR_END);
428 return order;
432 break;
435 default:
436 break;
439 /* not found */
440 return order;
443 /** Hotkeys for order window. */
444 enum {
445 OHK_SKIP,
446 OHK_DELETE,
447 OHK_GOTO,
448 OHK_NONSTOP,
449 OHK_FULLLOAD,
450 OHK_UNLOAD,
451 OHK_NEAREST_DEPOT,
452 OHK_ALWAYS_SERVICE,
453 OHK_TRANSFER,
454 OHK_NO_UNLOAD,
455 OHK_NO_LOAD,
459 * %Order window code for all vehicles.
461 * At the bottom of the window two button rows are located for changing the orders of the vehicle.
463 * \section top-row Top row
464 * 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.
466 * The top-row buttons of one of your trains or road vehicles is one of the following three cases:
467 * \verbatim
468 * +-----------------+-----------------+-----------------+-----------------+
469 * | NON-STOP | FULL_LOAD | UNLOAD | REFIT | (normal)
470 * +-----------------+-----+-----------+-----------+-----+-----------------+
471 * | COND_VAR | COND_COMPARATOR | COND_VALUE | (for conditional orders)
472 * +-----------------+-----+-----------+-----------+-----+-----------------+
473 * | NON-STOP | SERVICE | (empty) | REFIT | (for depot orders)
474 * +-----------------+-----------------+-----------------+-----------------+
475 * \endverbatim
477 * Airplanes and ships have one of the following three top-row button rows:
478 * \verbatim
479 * +-----------------+-----------------+-----------------+
480 * | FULL_LOAD | UNLOAD | REFIT | (normal)
481 * +-----------------+-----------------+-----------------+
482 * | COND_VAR | COND_COMPARATOR | COND_VALUE | (for conditional orders)
483 * +-----------------+--------+--------+-----------------+
484 * | REFIT | SERVICE | (for depot order)
485 * +--------------------------+--------------------------+
486 * \endverbatim
488 * \section bottom-row Bottom row
489 * The second row (the bottom row) is for manipulating the list of orders:
490 * \verbatim
491 * +-----------------+-----------------+-----------------+
492 * | SKIP | DELETE | GOTO |
493 * +-----------------+-----------------+-----------------+
494 * \endverbatim
496 * For vehicles of other companies, both button rows are not displayed.
498 struct OrdersWindow : public Window {
499 private:
500 /** Under what reason are we using the PlaceObject functionality? */
501 enum OrderPlaceObjectState {
502 OPOS_NONE,
503 OPOS_GOTO,
504 OPOS_CONDITIONAL,
505 OPOS_SHARE,
506 OPOS_END,
509 /** Displayed planes of the #NWID_SELECTION widgets. */
510 enum DisplayPane {
511 /* WID_O_SEL_TOP_ROW_GROUNDVEHICLE */
512 DP_GROUNDVEHICLE_ROW_NORMAL = 0, ///< Display the row for normal/depot orders in the top row of the train/rv order window.
513 DP_GROUNDVEHICLE_ROW_CONDITIONAL = 1, ///< Display the row for conditional orders in the top row of the train/rv order window.
515 /* WID_O_SEL_TOP_LEFT */
516 DP_LEFT_LOAD = 0, ///< Display 'load' in the left button of the top row of the train/rv order window.
517 DP_LEFT_SERVICE = 1, ///< Display 'service' in the left button of the top row of the train/rv order window.
519 /* WID_O_SEL_TOP_MIDDLE */
520 DP_MIDDLE_UNLOAD = 0, ///< Display 'unload' in the middle button of the top row of the train/rv order window.
521 DP_MIDDLE_EMPTY = 1, ///< Display an empty panel in the middle button of the top row of the train/rv order window.
523 /* WID_O_SEL_TOP_RIGHT */
524 DP_RIGHT_REFIT = 0, ///< Display 'refit' (button) in the right button of the top row of the train/rv order window.
525 DP_RIGHT_REFIT_AUTO = 1, ///< Display 'refit' (dropdown) in the right button of the top row of the train/rv order window.
527 /* WID_O_SEL_TOP_ROW */
528 DP_ROW_LOAD = 0, ///< Display 'load' / 'unload' / 'refit' buttons in the top row of the ship/airplane order window.
529 DP_ROW_DEPOT = 1, ///< Display 'refit' / 'service' buttons in the top row of the ship/airplane order window.
530 DP_ROW_CONDITIONAL = 2, ///< Display the conditional order buttons in the top row of the ship/airplane order window.
532 /* WID_O_SEL_BOTTOM_MIDDLE */
533 DP_BOTTOM_MIDDLE_DELETE = 0, ///< Display 'delete' in the middle button of the bottom row of the vehicle order window.
534 DP_BOTTOM_MIDDLE_STOP_SHARING = 1, ///< Display 'stop sharing' in the middle button of the bottom row of the vehicle order window.
537 int selected_order;
538 VehicleOrderID order_over; ///< Order over which another order is dragged, \c INVALID_VEH_ORDER_ID if none.
539 OrderPlaceObjectState goto_type;
540 const Vehicle *vehicle; ///< Vehicle owning the orders being displayed and manipulated.
541 Scrollbar *vscroll;
542 bool can_do_refit; ///< Vehicle chain can be refitted in depot.
543 bool can_do_autorefit; ///< Vehicle chain can be auto-refitted.
546 * Return the memorised selected order.
547 * @return the memorised order if it is a valid one
548 * else return the number of orders
550 VehicleOrderID OrderGetSel() const
552 int num = this->selected_order;
553 return (num >= 0 && num < vehicle->GetNumOrders()) ? num : vehicle->GetNumOrders();
557 * Calculate the selected order.
558 * The calculation is based on the relative (to the window) y click position and
559 * the position of the scrollbar.
561 * @param y Y-value of the click relative to the window origin
562 * @return The selected order if the order is valid, else return \c INVALID_VEH_ORDER_ID.
564 VehicleOrderID GetOrderFromPt(int y)
566 NWidgetBase *nwid = this->GetWidget<NWidgetBase>(WID_O_ORDER_LIST);
567 int sel = (y - nwid->pos_y - WD_FRAMERECT_TOP) / nwid->resize_y; // Selected line in the WID_O_ORDER_LIST panel.
569 if ((uint)sel >= this->vscroll->GetCapacity()) return INVALID_VEH_ORDER_ID;
571 sel += this->vscroll->GetPosition();
573 return (sel <= vehicle->GetNumOrders() && sel >= 0) ? sel : INVALID_VEH_ORDER_ID;
577 * Handle the click on the goto button.
579 void OrderClick_Goto(OrderPlaceObjectState type)
581 assert(type > OPOS_NONE && type < OPOS_END);
583 static const PointerMode goto_mode[OPOS_END - 1] = {
584 POINTER_TILE_VEHICLE, // OPOS_GOTO
585 POINTER_NONE, // OPOS_CONDITIONAL
586 POINTER_VEHICLE, // OPOS_SHARE
588 SetPointerMode (goto_mode[type - 1], this, ANIMCURSOR_PICKSTATION);
589 this->goto_type = type;
590 this->SetWidgetDirty(WID_O_GOTO);
594 * Handle the click on the full load button.
595 * @param load_type the way to load.
597 void OrderClick_FullLoad(int load_type)
599 VehicleOrderID sel_ord = this->OrderGetSel();
600 const Order *order = this->vehicle->GetOrder(sel_ord);
602 if (order == NULL || order->GetLoadType() == load_type) return;
604 if (load_type < 0) {
605 load_type = order->GetLoadType() == OLF_LOAD_IF_POSSIBLE ? OLF_FULL_LOAD_ANY : OLF_LOAD_IF_POSSIBLE;
607 DoCommandP(this->vehicle->tile, this->vehicle->index + (sel_ord << 20), MOF_LOAD | (load_type << 4), CMD_MODIFY_ORDER);
611 * Handle the 'no loading' hotkey
613 void OrderHotkey_NoLoad()
615 this->OrderClick_FullLoad(OLFB_NO_LOAD);
619 * Handle the click on the service.
621 void OrderClick_Service(int i)
623 VehicleOrderID sel_ord = this->OrderGetSel();
625 if (i < 0) {
626 const Order *order = this->vehicle->GetOrder(sel_ord);
627 if (order == NULL) return;
628 i = (order->GetDepotOrderType() & ODTFB_SERVICE) ? DA_ALWAYS_GO : DA_SERVICE;
630 DoCommandP(this->vehicle->tile, this->vehicle->index + (sel_ord << 20), MOF_DEPOT_ACTION | (i << 4), CMD_MODIFY_ORDER);
634 * Handle the click on the service in nearest depot button.
636 void OrderClick_NearestDepot()
638 Order order;
639 order.next = NULL;
640 order.index = 0;
641 order.MakeGoToDepot(0, ODTFB_PART_OF_ORDERS,
642 _settings_client.gui.new_nonstop && this->vehicle->IsGroundVehicle() ? ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS : ONSF_STOP_EVERYWHERE);
643 order.SetDepotActionType(ODATFB_NEAREST_DEPOT);
645 DoCommandP(this->vehicle->tile, this->vehicle->index + (this->OrderGetSel() << 20), order.Pack(), CMD_INSERT_ORDER);
649 * Handle the click on the unload button.
651 void OrderClick_Unload(int unload_type)
653 VehicleOrderID sel_ord = this->OrderGetSel();
654 const Order *order = this->vehicle->GetOrder(sel_ord);
656 if (order == NULL || order->GetUnloadType() == unload_type) return;
658 if (unload_type < 0) {
659 unload_type = order->GetUnloadType() == OUF_UNLOAD_IF_POSSIBLE ? OUFB_UNLOAD : OUF_UNLOAD_IF_POSSIBLE;
662 DoCommandP(this->vehicle->tile, this->vehicle->index + (sel_ord << 20), MOF_UNLOAD | (unload_type << 4), CMD_MODIFY_ORDER);
664 /* Transfer orders with leave empty as default */
665 if (unload_type == OUFB_TRANSFER && order->GetLoadType() != OLFB_NO_LOAD && !order->IsRefit()) {
666 DoCommandP(this->vehicle->tile, this->vehicle->index + (sel_ord << 20), MOF_LOAD | (OLFB_NO_LOAD << 4), CMD_MODIFY_ORDER);
667 this->SetWidgetDirty(WID_O_FULL_LOAD);
672 * Handle the transfer hotkey
674 void OrderHotkey_Transfer()
676 this->OrderClick_Unload(OUFB_TRANSFER);
680 * Handle the 'no unload' hotkey
682 void OrderHotkey_NoUnload()
684 this->OrderClick_Unload(OUFB_NO_UNLOAD);
688 * Handle the click on the nonstop button.
689 * @param non_stop what non-stop type to use; -1 to use the 'next' one.
691 void OrderClick_Nonstop(int non_stop)
693 if (!this->vehicle->IsGroundVehicle()) return;
695 VehicleOrderID sel_ord = this->OrderGetSel();
696 const Order *order = this->vehicle->GetOrder(sel_ord);
698 if (order == NULL || order->GetNonStopType() == non_stop) return;
700 /* Keypress if negative, so 'toggle' to the next */
701 if (non_stop < 0) {
702 non_stop = order->GetNonStopType() ^ ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS;
705 this->SetWidgetDirty(WID_O_NON_STOP);
706 DoCommandP(this->vehicle->tile, this->vehicle->index + (sel_ord << 20), MOF_NON_STOP | non_stop << 4, CMD_MODIFY_ORDER);
710 * Handle the click on the skip button.
711 * If ctrl is pressed, skip to selected order, else skip to current order + 1
713 void OrderClick_Skip()
715 /* Don't skip when there's nothing to skip */
716 if (_ctrl_pressed && this->vehicle->cur_implicit_order_index == this->OrderGetSel()) return;
717 if (this->vehicle->GetNumOrders() <= 1) return;
719 DoCommandP(this->vehicle->tile, this->vehicle->index | (_ctrl_pressed ? (1 << 31) : 0),
720 _ctrl_pressed ? this->OrderGetSel() : ((this->vehicle->cur_implicit_order_index + 1) % this->vehicle->GetNumOrders()),
721 CMD_SKIP_TO_ORDER);
725 * Handle the click on the delete button.
727 void OrderClick_Delete()
729 /* When networking, move one order lower */
730 int selected = this->selected_order + (int)_networking;
732 if (DoCommandP(this->vehicle->tile, this->vehicle->index, this->OrderGetSel(), CMD_DELETE_ORDER)) {
733 this->selected_order = selected >= this->vehicle->GetNumOrders() ? -1 : selected;
734 this->UpdateButtonState();
739 * Handle the click on the 'stop sharing' button.
740 * If 'End of Shared Orders' isn't selected, do nothing. If Ctrl is pressed, call OrderClick_Delete and exit.
741 * To stop sharing this vehicle order list, we copy the orders of a vehicle that share this order list. That way we
742 * exit the group of shared vehicles while keeping the same order list.
744 void OrderClick_StopSharing()
746 /* Don't try to stop sharing orders if 'End of Shared Orders' isn't selected. */
747 if (!this->vehicle->IsOrderListShared() || this->selected_order != this->vehicle->GetNumOrders()) return;
748 /* If Ctrl is pressed, delete the order list as if we clicked the 'Delete' button. */
749 if (_ctrl_pressed) {
750 this->OrderClick_Delete();
751 return;
754 /* Get another vehicle that share orders with this vehicle. */
755 Vehicle *other_shared = (this->vehicle->FirstShared() == this->vehicle) ? this->vehicle->NextShared() : this->vehicle->PreviousShared();
756 /* Copy the order list of the other vehicle. */
757 if (DoCommandP(this->vehicle->tile, this->vehicle->index | CO_UNSHARE_COPY << 30, other_shared->index, CMD_CLONE_ORDER)) {
758 this->UpdateButtonState();
762 /** Cache auto-refittability of the vehicle chain. */
763 void UpdateAutoRefitState()
765 this->can_do_refit = false;
766 this->can_do_autorefit = false;
767 for (const Vehicle *w = this->vehicle; w != NULL; w = w->IsGroundVehicle() ? w->Next() : NULL) {
768 if (IsEngineRefittable(w->engine_type)) this->can_do_refit = true;
769 if (HasBit(Engine::Get(w->engine_type)->info.misc_flags, EF_AUTO_REFIT)) this->can_do_autorefit = true;
773 public:
774 OrdersWindow (const WindowDesc *desc, const Vehicle *v) :
775 Window (desc), selected_order (-1),
776 order_over (INVALID_VEH_ORDER_ID),
777 goto_type (OPOS_NONE), vehicle (v), vscroll (NULL),
778 can_do_refit (false), can_do_autorefit (false)
780 this->CreateNestedTree();
781 this->vscroll = this->GetScrollbar(WID_O_SCROLLBAR);
782 this->InitNested(v->index);
784 this->owner = v->owner;
786 this->UpdateAutoRefitState();
788 if (_settings_client.gui.quick_goto && v->owner == _local_company) {
789 /* If there are less than 2 station, make Go To active. */
790 int station_orders = 0;
791 const Order *order;
792 FOR_VEHICLE_ORDERS(v, order) {
793 if (order->IsType(OT_GOTO_STATION)) station_orders++;
796 if (station_orders < 2) this->OrderClick_Goto(OPOS_GOTO);
798 this->OnInvalidateData(VIWD_MODIFY_ORDERS);
801 virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
803 switch (widget) {
804 case WID_O_ORDER_LIST:
805 resize->height = FONT_HEIGHT_NORMAL;
806 size->height = 6 * resize->height + WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
807 break;
809 case WID_O_COND_VARIABLE: {
810 Dimension d = {0, 0};
811 for (uint i = 0; i < lengthof(_order_conditional_variable); i++) {
812 d = maxdim(d, GetStringBoundingBox(STR_ORDER_CONDITIONAL_LOAD_PERCENTAGE + _order_conditional_variable[i]));
814 d.width += padding.width;
815 d.height += padding.height;
816 *size = maxdim(*size, d);
817 break;
820 case WID_O_COND_COMPARATOR: {
821 Dimension d = {0, 0};
822 for (int i = 0; _order_conditional_condition[i] != INVALID_STRING_ID; i++) {
823 d = maxdim(d, GetStringBoundingBox(_order_conditional_condition[i]));
825 d.width += padding.width;
826 d.height += padding.height;
827 *size = maxdim(*size, d);
828 break;
834 * Some data on this window has become invalid.
835 * @param data Information about the changed data.
836 * @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.
838 virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
840 VehicleOrderID from = INVALID_VEH_ORDER_ID;
841 VehicleOrderID to = INVALID_VEH_ORDER_ID;
843 switch (data) {
844 case VIWD_AUTOREPLACE:
845 /* Autoreplace replaced the vehicle */
846 this->vehicle = Vehicle::Get(this->window_number);
847 /* FALL THROUGH */
849 case VIWD_CONSIST_CHANGED:
850 /* Vehicle composition was changed. */
851 this->UpdateAutoRefitState();
852 break;
854 case VIWD_REMOVE_ALL_ORDERS:
855 /* Removed / replaced all orders (after deleting / sharing) */
856 if (this->selected_order == -1) break;
858 this->DeleteChildWindows();
859 HideDropDownMenu(this);
860 this->selected_order = -1;
861 break;
863 case VIWD_MODIFY_ORDERS:
864 /* Some other order changes */
865 break;
867 default:
868 if (data < 0) break;
870 if (gui_scope) break; // only do this once; from command scope
871 from = GB(data, 0, 8);
872 to = GB(data, 8, 8);
873 /* Moving an order. If one of these is INVALID_VEH_ORDER_ID, then
874 * the order is being created / removed */
875 if (this->selected_order == -1) break;
877 if (from == to) break; // no need to change anything
879 if (from != this->selected_order) {
880 /* Moving from preceding order? */
881 this->selected_order -= (int)(from <= this->selected_order);
882 /* Moving to preceding order? */
883 this->selected_order += (int)(to <= this->selected_order);
884 break;
887 /* Now we are modifying the selected order */
888 if (to == INVALID_VEH_ORDER_ID) {
889 /* Deleting selected order */
890 this->DeleteChildWindows();
891 HideDropDownMenu(this);
892 this->selected_order = -1;
893 break;
896 /* Moving selected order */
897 this->selected_order = to;
898 break;
901 this->vscroll->SetCount(this->vehicle->GetNumOrders() + 1);
902 if (gui_scope) this->UpdateButtonState();
904 /* Scroll to the new order. */
905 if (from == INVALID_VEH_ORDER_ID && to != INVALID_VEH_ORDER_ID && !this->vscroll->IsVisible(to)) {
906 this->vscroll->ScrollTowards(to);
910 void UpdateButtonState()
912 if (this->vehicle->owner != _local_company) return; // No buttons are displayed with competitor order windows.
914 bool shared_orders = this->vehicle->IsOrderListShared();
915 VehicleOrderID sel = this->OrderGetSel();
916 const Order *order = this->vehicle->GetOrder(sel);
918 /* Second row. */
919 /* skip */
920 this->SetWidgetDisabledState(WID_O_SKIP, this->vehicle->GetNumOrders() <= 1);
922 /* delete / stop sharing */
923 NWidgetStacked *delete_sel = this->GetWidget<NWidgetStacked>(WID_O_SEL_BOTTOM_MIDDLE);
924 if (shared_orders && this->selected_order == this->vehicle->GetNumOrders()) {
925 /* The 'End of Shared Orders' order is selected, show the 'stop sharing' button. */
926 delete_sel->SetDisplayedPlane(DP_BOTTOM_MIDDLE_STOP_SHARING);
927 } else {
928 /* The 'End of Shared Orders' order isn't selected, show the 'delete' button. */
929 delete_sel->SetDisplayedPlane(DP_BOTTOM_MIDDLE_DELETE);
930 this->SetWidgetDisabledState(WID_O_DELETE,
931 (uint)this->vehicle->GetNumOrders() + ((shared_orders || this->vehicle->GetNumOrders() != 0) ? 1 : 0) <= (uint)this->selected_order);
933 /* Set the tooltip of the 'delete' button depending on whether the
934 * 'End of Orders' order or a regular order is selected. */
935 NWidgetCore *nwi = this->GetWidget<NWidgetCore>(WID_O_DELETE);
936 if (this->selected_order == this->vehicle->GetNumOrders()) {
937 nwi->SetDataTip(STR_ORDERS_DELETE_BUTTON, STR_ORDERS_DELETE_ALL_TOOLTIP);
938 } else {
939 nwi->SetDataTip(STR_ORDERS_DELETE_BUTTON, STR_ORDERS_DELETE_TOOLTIP);
943 /* First row. */
944 this->RaiseWidget(WID_O_FULL_LOAD);
945 this->RaiseWidget(WID_O_UNLOAD);
946 this->RaiseWidget(WID_O_SERVICE);
948 /* Selection widgets. */
949 /* Train or road vehicle. */
950 NWidgetStacked *train_row_sel = this->GetWidget<NWidgetStacked>(WID_O_SEL_TOP_ROW_GROUNDVEHICLE);
951 NWidgetStacked *left_sel = this->GetWidget<NWidgetStacked>(WID_O_SEL_TOP_LEFT);
952 NWidgetStacked *middle_sel = this->GetWidget<NWidgetStacked>(WID_O_SEL_TOP_MIDDLE);
953 NWidgetStacked *right_sel = this->GetWidget<NWidgetStacked>(WID_O_SEL_TOP_RIGHT);
954 /* Ship or airplane. */
955 NWidgetStacked *row_sel = this->GetWidget<NWidgetStacked>(WID_O_SEL_TOP_ROW);
956 assert(row_sel != NULL || (train_row_sel != NULL && left_sel != NULL && middle_sel != NULL && right_sel != NULL));
958 OrderType order_type;
959 if (order == NULL) {
960 order_type = OT_NOTHING;
961 } else {
962 this->SetWidgetDisabledState(WID_O_FULL_LOAD, (order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION) != 0); // full load
963 this->SetWidgetDisabledState(WID_O_UNLOAD, (order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION) != 0); // unload
964 order_type = order->GetType();
967 switch (order_type) {
968 case OT_GOTO_STATION:
969 if (row_sel != NULL) {
970 row_sel->SetDisplayedPlane(DP_ROW_LOAD);
971 } else {
972 train_row_sel->SetDisplayedPlane(DP_GROUNDVEHICLE_ROW_NORMAL);
973 left_sel->SetDisplayedPlane(DP_LEFT_LOAD);
974 middle_sel->SetDisplayedPlane(DP_MIDDLE_UNLOAD);
975 right_sel->SetDisplayedPlane(DP_RIGHT_REFIT_AUTO);
976 this->EnableWidget(WID_O_NON_STOP);
977 this->SetWidgetLoweredState(WID_O_NON_STOP, order->GetNonStopType() & ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS);
979 this->SetWidgetLoweredState(WID_O_FULL_LOAD, order->GetLoadType() == OLF_FULL_LOAD_ANY);
980 this->SetWidgetLoweredState(WID_O_UNLOAD, order->GetUnloadType() == OUFB_UNLOAD);
982 /* Can only do refitting when stopping at the destination and loading cargo.
983 * Also enable the button if a refit is already set to allow clearing it. */
984 this->SetWidgetDisabledState(WID_O_REFIT_DROPDOWN,
985 order->GetLoadType() == OLFB_NO_LOAD || (order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION) ||
986 ((!this->can_do_refit || !this->can_do_autorefit) && !order->IsRefit()));
988 break;
990 case OT_GOTO_WAYPOINT:
991 if (row_sel != NULL) {
992 row_sel->SetDisplayedPlane(DP_ROW_LOAD);
993 } else {
994 train_row_sel->SetDisplayedPlane(DP_GROUNDVEHICLE_ROW_NORMAL);
995 left_sel->SetDisplayedPlane(DP_LEFT_LOAD);
996 middle_sel->SetDisplayedPlane(DP_MIDDLE_UNLOAD);
997 right_sel->SetDisplayedPlane(DP_RIGHT_REFIT);
998 this->EnableWidget(WID_O_NON_STOP);
999 this->SetWidgetLoweredState(WID_O_NON_STOP, order->GetNonStopType() & ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS);
1001 this->DisableWidget(WID_O_FULL_LOAD);
1002 this->DisableWidget(WID_O_UNLOAD);
1003 /* Ground vehicles use one and aircraft and
1004 * ships use the other, so disable both. */
1005 this->DisableWidget(WID_O_REFIT);
1006 this->DisableWidget(WID_O_REFIT_DROPDOWN);
1007 break;
1009 case OT_GOTO_DEPOT:
1010 if (row_sel != NULL) {
1011 row_sel->SetDisplayedPlane(DP_ROW_DEPOT);
1012 } else {
1013 train_row_sel->SetDisplayedPlane(DP_GROUNDVEHICLE_ROW_NORMAL);
1014 left_sel->SetDisplayedPlane(DP_LEFT_SERVICE);
1015 middle_sel->SetDisplayedPlane(DP_MIDDLE_EMPTY);
1016 right_sel->SetDisplayedPlane(DP_RIGHT_REFIT);
1017 this->EnableWidget(WID_O_NON_STOP);
1018 this->SetWidgetLoweredState(WID_O_NON_STOP, order->GetNonStopType() & ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS);
1020 /* Disable refit button if the order is no 'always go' order.
1021 * However, keep the service button enabled for refit-orders to allow clearing refits (without knowing about ctrl). */
1022 this->SetWidgetDisabledState(WID_O_REFIT,
1023 (order->GetDepotOrderType() & ODTFB_SERVICE) || (order->GetDepotActionType() & ODATFB_HALT) ||
1024 (!this->can_do_refit && !order->IsRefit()));
1025 this->SetWidgetLoweredState(WID_O_SERVICE, order->GetDepotOrderType() & ODTFB_SERVICE);
1026 break;
1028 case OT_CONDITIONAL: {
1029 if (row_sel != NULL) {
1030 row_sel->SetDisplayedPlane(DP_ROW_CONDITIONAL);
1031 } else {
1032 train_row_sel->SetDisplayedPlane(DP_GROUNDVEHICLE_ROW_CONDITIONAL);
1034 OrderConditionVariable ocv = order->GetConditionVariable();
1035 /* Set the strings for the dropdown boxes. */
1036 this->GetWidget<NWidgetCore>(WID_O_COND_VARIABLE)->widget_data = STR_ORDER_CONDITIONAL_LOAD_PERCENTAGE + ocv;
1037 this->GetWidget<NWidgetCore>(WID_O_COND_COMPARATOR)->widget_data = _order_conditional_condition[order->GetConditionComparator()];
1038 this->SetWidgetDisabledState(WID_O_COND_COMPARATOR, ocv == OCV_UNCONDITIONALLY);
1039 this->SetWidgetDisabledState(WID_O_COND_VALUE, ocv == OCV_REQUIRES_SERVICE || ocv == OCV_UNCONDITIONALLY);
1040 break;
1043 default: // every other order
1044 if (row_sel != NULL) {
1045 row_sel->SetDisplayedPlane(DP_ROW_LOAD);
1046 } else {
1047 train_row_sel->SetDisplayedPlane(DP_GROUNDVEHICLE_ROW_NORMAL);
1048 left_sel->SetDisplayedPlane(DP_LEFT_LOAD);
1049 middle_sel->SetDisplayedPlane(DP_MIDDLE_UNLOAD);
1050 right_sel->SetDisplayedPlane(DP_RIGHT_REFIT);
1051 this->DisableWidget(WID_O_NON_STOP);
1052 this->RaiseWidget(WID_O_NON_STOP);
1054 this->DisableWidget(WID_O_FULL_LOAD);
1055 this->DisableWidget(WID_O_UNLOAD);
1056 /* Ground vehicles use one and aircraft and
1057 * ships use the other, so disable both. */
1058 this->DisableWidget(WID_O_REFIT);
1059 this->DisableWidget(WID_O_REFIT_DROPDOWN);
1060 break;
1063 /* Disable list of vehicles with the same shared orders if there is no list */
1064 this->SetWidgetDisabledState(WID_O_SHARED_ORDER_LIST, !shared_orders);
1066 this->SetDirty();
1069 void OnPaint (BlitArea *dpi) OVERRIDE
1071 if (this->vehicle->owner != _local_company) {
1072 this->selected_order = -1; // Disable selection any selected row at a competitor order window.
1073 } else {
1074 this->SetWidgetLoweredState(WID_O_GOTO, this->goto_type != OPOS_NONE);
1076 this->DrawWidgets (dpi);
1079 void DrawWidget (BlitArea *dpi, const Rect &r, int widget) const OVERRIDE
1081 if (widget != WID_O_ORDER_LIST) return;
1083 bool rtl = _current_text_dir == TD_RTL;
1084 SetDParamMaxValue(0, this->vehicle->GetNumOrders(), 2);
1085 int index_column_width = GetStringBoundingBox(STR_ORDER_INDEX).width + 2 * GetSpriteSize(rtl ? SPR_ARROW_RIGHT : SPR_ARROW_LEFT).width + 3;
1086 int middle = rtl ? r.right - WD_FRAMETEXT_RIGHT - index_column_width : r.left + WD_FRAMETEXT_LEFT + index_column_width;
1088 int y = r.top + WD_FRAMERECT_TOP;
1089 int line_height = this->GetWidget<NWidgetBase>(WID_O_ORDER_LIST)->resize_y;
1091 int i = this->vscroll->GetPosition();
1092 const Order *order = this->vehicle->GetOrder(i);
1093 /* First draw the highlighting underground if it exists. */
1094 if (this->order_over != INVALID_VEH_ORDER_ID) {
1095 while (order != NULL) {
1096 /* Don't draw anything if it extends past the end of the window. */
1097 if (!this->vscroll->IsVisible(i)) break;
1099 if (i != this->selected_order && i == this->order_over) {
1100 /* Highlight dragged order destination. */
1101 int top = (this->order_over < this->selected_order ? y : y + line_height) - WD_FRAMERECT_TOP;
1102 int bottom = min(top + 2, r.bottom - WD_FRAMERECT_BOTTOM);
1103 top = max(top - 3, r.top + WD_FRAMERECT_TOP);
1104 GfxFillRect (dpi, r.left + WD_FRAMETEXT_LEFT, top, r.right - WD_FRAMETEXT_RIGHT, bottom, _colour_gradient[COLOUR_GREY][7]);
1105 break;
1107 y += line_height;
1109 i++;
1110 order = order->next;
1113 /* Reset counters for drawing the orders. */
1114 y = r.top + WD_FRAMERECT_TOP;
1115 i = this->vscroll->GetPosition();
1116 order = this->vehicle->GetOrder(i);
1119 /* Draw the orders. */
1120 while (order != NULL) {
1121 /* Don't draw anything if it extends past the end of the window. */
1122 if (!this->vscroll->IsVisible(i)) break;
1124 DrawOrderString (this->vehicle, order, i, dpi, y, i == this->selected_order, false, r.left + WD_FRAMETEXT_LEFT, middle, r.right - WD_FRAMETEXT_RIGHT);
1125 y += line_height;
1127 i++;
1128 order = order->next;
1131 if (this->vscroll->IsVisible(i)) {
1132 StringID str = this->vehicle->IsOrderListShared() ? STR_ORDERS_END_OF_SHARED_ORDERS : STR_ORDERS_END_OF_ORDERS;
1133 DrawString (dpi, rtl ? r.left + WD_FRAMETEXT_LEFT : middle, rtl ? middle : r.right - WD_FRAMETEXT_RIGHT, y, str, (i == this->selected_order) ? TC_WHITE : TC_BLACK);
1137 virtual void SetStringParameters(int widget) const
1139 switch (widget) {
1140 case WID_O_COND_VALUE: {
1141 VehicleOrderID sel = this->OrderGetSel();
1142 const Order *order = this->vehicle->GetOrder(sel);
1144 if (order != NULL && order->IsType(OT_CONDITIONAL)) {
1145 uint value = order->GetConditionValue();
1146 if (order->GetConditionVariable() == OCV_MAX_SPEED) value = ConvertSpeedToDisplaySpeed(value);
1147 SetDParam(0, value);
1149 break;
1152 case WID_O_CAPTION:
1153 SetDParam(0, this->vehicle->index);
1154 break;
1158 virtual void OnClick(Point pt, int widget, int click_count)
1160 switch (widget) {
1161 case WID_O_ORDER_LIST: {
1162 if (this->goto_type == OPOS_CONDITIONAL) {
1163 VehicleOrderID order_id = this->GetOrderFromPt(_cursor.pos.y - this->top);
1164 if (order_id != INVALID_VEH_ORDER_ID) {
1165 Order order;
1166 order.next = NULL;
1167 order.index = 0;
1168 order.MakeConditional(order_id);
1170 DoCommandP(this->vehicle->tile, this->vehicle->index + (this->OrderGetSel() << 20), order.Pack(), CMD_INSERT_ORDER);
1172 ResetPointerMode();
1173 break;
1176 VehicleOrderID sel = this->GetOrderFromPt(pt.y);
1178 if (_ctrl_pressed && sel < this->vehicle->GetNumOrders()) {
1179 TileIndex xy = this->vehicle->GetOrder(sel)->GetLocation(this->vehicle);
1180 if (xy != INVALID_TILE) ScrollMainWindowToTile(xy);
1181 return;
1184 /* This order won't be selected any more, close all child windows and dropdowns */
1185 this->DeleteChildWindows();
1186 HideDropDownMenu(this);
1188 if (sel == INVALID_VEH_ORDER_ID || this->vehicle->owner != _local_company) {
1189 /* Deselect clicked order */
1190 this->selected_order = -1;
1191 } else if (sel == this->selected_order) {
1192 if (this->vehicle->type == VEH_TRAIN && sel < this->vehicle->GetNumOrders()) {
1193 DoCommandP(this->vehicle->tile, this->vehicle->index + (sel << 20),
1194 MOF_STOP_LOCATION | ((this->vehicle->GetOrder(sel)->GetStopLocation() + 1) % OSL_END) << 4,
1195 CMD_MODIFY_ORDER);
1197 } else {
1198 /* Select clicked order */
1199 this->selected_order = sel;
1201 if (this->vehicle->owner == _local_company) {
1202 /* Activate drag and drop */
1203 SetPointerMode (POINTER_DRAG, this, SPR_CURSOR_MOUSE);
1207 this->UpdateButtonState();
1208 break;
1211 case WID_O_SKIP:
1212 this->OrderClick_Skip();
1213 break;
1215 case WID_O_DELETE:
1216 this->OrderClick_Delete();
1217 break;
1219 case WID_O_STOP_SHARING:
1220 this->OrderClick_StopSharing();
1221 break;
1223 case WID_O_NON_STOP:
1224 if (this->GetWidget<NWidgetLeaf>(widget)->ButtonHit(pt)) {
1225 this->OrderClick_Nonstop(-1);
1226 } else {
1227 const Order *o = this->vehicle->GetOrder(this->OrderGetSel());
1228 ShowDropDownMenu(this, _order_non_stop_drowdown, o->GetNonStopType(), WID_O_NON_STOP, 0,
1229 o->IsType(OT_GOTO_STATION) ? 0 : (o->IsType(OT_GOTO_WAYPOINT) ? 3 : 12));
1231 break;
1233 case WID_O_GOTO:
1234 if (this->GetWidget<NWidgetLeaf>(widget)->ButtonHit(pt)) {
1235 if (this->goto_type != OPOS_NONE) {
1236 ResetPointerMode();
1237 } else {
1238 this->OrderClick_Goto(OPOS_GOTO);
1240 } else {
1241 int sel;
1242 switch (this->goto_type) {
1243 case OPOS_NONE: sel = -1; break;
1244 case OPOS_GOTO: sel = 0; break;
1245 case OPOS_CONDITIONAL: sel = 2; break;
1246 case OPOS_SHARE: sel = 3; break;
1247 default: NOT_REACHED();
1249 ShowDropDownMenu(this, this->vehicle->type == VEH_AIRCRAFT ? _order_goto_dropdown_aircraft : _order_goto_dropdown, sel, WID_O_GOTO, 0, 0);
1251 break;
1253 case WID_O_FULL_LOAD:
1254 if (this->GetWidget<NWidgetLeaf>(widget)->ButtonHit(pt)) {
1255 this->OrderClick_FullLoad(-1);
1256 } else {
1257 ShowDropDownMenu(this, _order_full_load_drowdown, this->vehicle->GetOrder(this->OrderGetSel())->GetLoadType(), WID_O_FULL_LOAD, 0, 2);
1259 break;
1261 case WID_O_UNLOAD:
1262 if (this->GetWidget<NWidgetLeaf>(widget)->ButtonHit(pt)) {
1263 this->OrderClick_Unload(-1);
1264 } else {
1265 ShowDropDownMenu(this, _order_unload_drowdown, this->vehicle->GetOrder(this->OrderGetSel())->GetUnloadType(), WID_O_UNLOAD, 0, 8);
1267 break;
1269 case WID_O_REFIT:
1270 case WID_O_REFIT_DROPDOWN:
1271 if (_ctrl_pressed) {
1272 /* Cancel refitting */
1273 DoCommandP (this->vehicle->tile, this->vehicle->index | (this->OrderGetSel() << 24), 0, CMD_ORDER_REFIT);
1274 } else {
1275 /* Auto-refit to available cargo type. */
1276 ShowVehicleRefitWindow (this, this->vehicle, this->OrderGetSel(), widget == WID_O_REFIT_DROPDOWN, this->vehicle->GetOrder(this->OrderGetSel())->GetRefitCargoMask());
1278 break;
1280 case WID_O_SERVICE:
1281 if (this->GetWidget<NWidgetLeaf>(widget)->ButtonHit(pt)) {
1282 this->OrderClick_Service(-1);
1283 } else {
1284 ShowDropDownMenu(this, _order_depot_action_dropdown, DepotActionStringIndex(this->vehicle->GetOrder(this->OrderGetSel())), WID_O_SERVICE, 0, 0);
1286 break;
1288 case WID_O_TIMETABLE_VIEW:
1289 ShowTimetableWindow(this->vehicle);
1290 break;
1292 case WID_O_COND_VARIABLE: {
1293 DropDownList *list = new DropDownList();
1294 for (uint i = 0; i < lengthof(_order_conditional_variable); i++) {
1295 *list->Append() = new DropDownListStringItem(STR_ORDER_CONDITIONAL_LOAD_PERCENTAGE + _order_conditional_variable[i], _order_conditional_variable[i], false);
1297 ShowDropDownList(this, list, this->vehicle->GetOrder(this->OrderGetSel())->GetConditionVariable(), WID_O_COND_VARIABLE);
1298 break;
1301 case WID_O_COND_COMPARATOR: {
1302 const Order *o = this->vehicle->GetOrder(this->OrderGetSel());
1303 ShowDropDownMenu(this, _order_conditional_condition, o->GetConditionComparator(), WID_O_COND_COMPARATOR, 0, (o->GetConditionVariable() == OCV_REQUIRES_SERVICE) ? 0x3F : 0xC0);
1304 break;
1307 case WID_O_COND_VALUE: {
1308 const Order *order = this->vehicle->GetOrder(this->OrderGetSel());
1309 uint value = order->GetConditionValue();
1310 if (order->GetConditionVariable() == OCV_MAX_SPEED) value = ConvertSpeedToDisplaySpeed(value);
1311 SetDParam(0, value);
1312 ShowQueryString(STR_JUST_INT, STR_ORDER_CONDITIONAL_VALUE_CAPT, 5, this, CS_NUMERAL, QSF_NONE);
1313 break;
1316 case WID_O_SHARED_ORDER_LIST:
1317 ShowVehicleListWindow(this->vehicle);
1318 break;
1322 virtual void OnQueryTextFinished(char *str)
1324 if (!StrEmpty(str)) {
1325 VehicleOrderID sel = this->OrderGetSel();
1326 uint value = atoi(str);
1328 switch (this->vehicle->GetOrder(sel)->GetConditionVariable()) {
1329 case OCV_MAX_SPEED:
1330 value = ConvertDisplaySpeedToSpeed(value);
1331 break;
1333 case OCV_RELIABILITY:
1334 case OCV_LOAD_PERCENTAGE:
1335 value = Clamp(value, 0, 100);
1336 break;
1338 default:
1339 break;
1341 DoCommandP(this->vehicle->tile, this->vehicle->index + (sel << 20), MOF_COND_VALUE | Clamp(value, 0, 2047) << 4, CMD_MODIFY_ORDER);
1345 virtual void OnDropdownSelect(int widget, int index)
1347 switch (widget) {
1348 case WID_O_NON_STOP:
1349 this->OrderClick_Nonstop(index);
1350 break;
1352 case WID_O_FULL_LOAD:
1353 this->OrderClick_FullLoad(index);
1354 break;
1356 case WID_O_UNLOAD:
1357 this->OrderClick_Unload(index);
1358 break;
1360 case WID_O_GOTO:
1361 switch (index) {
1362 case 0: this->OrderClick_Goto(OPOS_GOTO); break;
1363 case 1: this->OrderClick_NearestDepot(); break;
1364 case 2: this->OrderClick_Goto(OPOS_CONDITIONAL); break;
1365 case 3: this->OrderClick_Goto(OPOS_SHARE); break;
1366 default: NOT_REACHED();
1368 break;
1370 case WID_O_SERVICE:
1371 this->OrderClick_Service(index);
1372 break;
1374 case WID_O_COND_VARIABLE:
1375 DoCommandP(this->vehicle->tile, this->vehicle->index + (this->OrderGetSel() << 20), MOF_COND_VARIABLE | index << 4, CMD_MODIFY_ORDER);
1376 break;
1378 case WID_O_COND_COMPARATOR:
1379 DoCommandP(this->vehicle->tile, this->vehicle->index + (this->OrderGetSel() << 20), MOF_COND_COMPARATOR | index << 4, CMD_MODIFY_ORDER);
1380 break;
1384 virtual void OnDragDrop(Point pt, int widget)
1386 switch (widget) {
1387 case WID_O_ORDER_LIST: {
1388 VehicleOrderID from_order = this->OrderGetSel();
1389 VehicleOrderID to_order = this->GetOrderFromPt(pt.y);
1391 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()) &&
1392 DoCommandP(this->vehicle->tile, this->vehicle->index, from_order | (to_order << 16), CMD_MOVE_ORDER)) {
1393 this->selected_order = -1;
1394 this->UpdateButtonState();
1396 break;
1399 case WID_O_DELETE:
1400 this->OrderClick_Delete();
1401 break;
1403 case WID_O_STOP_SHARING:
1404 this->OrderClick_StopSharing();
1405 break;
1408 ResetPointerMode();
1410 if (this->order_over != INVALID_VEH_ORDER_ID) {
1411 /* End of drag-and-drop, hide dragged order destination highlight. */
1412 this->order_over = INVALID_VEH_ORDER_ID;
1413 this->SetWidgetDirty(WID_O_ORDER_LIST);
1417 virtual EventState OnHotkey(int hotkey)
1419 if (this->vehicle->owner != _local_company) return ES_NOT_HANDLED;
1421 switch (hotkey) {
1422 case OHK_SKIP: this->OrderClick_Skip(); break;
1423 case OHK_DELETE: this->OrderClick_Delete(); break;
1424 case OHK_GOTO: this->OrderClick_Goto(OPOS_GOTO); break;
1425 case OHK_NONSTOP: this->OrderClick_Nonstop(-1); break;
1426 case OHK_FULLLOAD: this->OrderClick_FullLoad(-1); break;
1427 case OHK_UNLOAD: this->OrderClick_Unload(-1); break;
1428 case OHK_NEAREST_DEPOT: this->OrderClick_NearestDepot(); break;
1429 case OHK_ALWAYS_SERVICE: this->OrderClick_Service(-1); break;
1430 case OHK_TRANSFER: this->OrderHotkey_Transfer(); break;
1431 case OHK_NO_UNLOAD: this->OrderHotkey_NoUnload(); break;
1432 case OHK_NO_LOAD: this->OrderHotkey_NoLoad(); break;
1433 default: return ES_NOT_HANDLED;
1435 return ES_HANDLED;
1438 virtual void OnPlaceObject(Point pt, TileIndex tile)
1440 if (this->goto_type == OPOS_GOTO) {
1441 const BaseOrder cmd = GetOrderCmdFromTile (this->vehicle, tile);
1442 if (cmd.IsType(OT_NOTHING)) return;
1444 if (DoCommandP(this->vehicle->tile, this->vehicle->index + (this->OrderGetSel() << 20), cmd.Pack(), CMD_INSERT_ORDER)) {
1445 /* With quick goto the Go To button stays active */
1446 if (!_settings_client.gui.quick_goto) ResetPointerMode();
1451 virtual bool OnVehicleSelect(const Vehicle *v)
1453 /* v is vehicle getting orders. Only copy/clone orders if vehicle doesn't have any orders yet.
1454 * We disallow copying orders of other vehicles if we already have at least one order entry
1455 * ourself as it easily copies orders of vehicles within a station when we mean the station.
1456 * Obviously if you press CTRL on a non-empty orders vehicle you know what you are doing
1457 * TODO: give a warning message */
1458 bool share_order = _ctrl_pressed || this->goto_type == OPOS_SHARE;
1459 if (this->vehicle->GetNumOrders() != 0 && !share_order) return false;
1461 if (DoCommandP(this->vehicle->tile, this->vehicle->index | (share_order ? CO_SHARE : CO_COPY) << 30, v->index, CMD_CLONE_ORDER)) {
1462 this->selected_order = -1;
1463 ResetPointerMode();
1465 return true;
1468 virtual void OnPlaceObjectAbort()
1470 this->goto_type = OPOS_NONE;
1471 this->SetWidgetDirty(WID_O_GOTO);
1473 /* Remove drag highlighting if it exists. */
1474 if (this->order_over != INVALID_VEH_ORDER_ID) {
1475 this->order_over = INVALID_VEH_ORDER_ID;
1476 this->SetWidgetDirty(WID_O_ORDER_LIST);
1480 virtual void OnMouseDrag(Point pt, int widget)
1482 if (this->selected_order != -1 && widget == WID_O_ORDER_LIST) {
1483 /* An order is dragged.. */
1484 VehicleOrderID from_order = this->OrderGetSel();
1485 VehicleOrderID to_order = this->GetOrderFromPt(pt.y);
1486 uint num_orders = this->vehicle->GetNumOrders();
1488 if (from_order != INVALID_VEH_ORDER_ID && from_order <= num_orders) {
1489 if (to_order != INVALID_VEH_ORDER_ID && to_order <= num_orders) { // ..over an existing order.
1490 this->order_over = to_order;
1491 this->SetWidgetDirty(widget);
1492 } else if (from_order != to_order && this->order_over != INVALID_VEH_ORDER_ID) { // ..outside of the order list.
1493 this->order_over = INVALID_VEH_ORDER_ID;
1494 this->SetWidgetDirty(widget);
1500 virtual void OnResize()
1502 /* Update the scroll bar */
1503 this->vscroll->SetCapacityFromWidget(this, WID_O_ORDER_LIST);
1506 static HotkeyList hotkeys;
1509 static const Hotkey order_hotkeys[] = {
1510 Hotkey ("skip", OHK_SKIP, 'D'),
1511 Hotkey ("delete", OHK_DELETE, 'F'),
1512 Hotkey ("goto", OHK_GOTO, 'G'),
1513 Hotkey ("nonstop", OHK_NONSTOP, 'H'),
1514 Hotkey ("fullload", OHK_FULLLOAD, 'J'),
1515 Hotkey ("unload", OHK_UNLOAD, 'K'),
1516 Hotkey ("nearest_depot", OHK_NEAREST_DEPOT),
1517 Hotkey ("always_service", OHK_ALWAYS_SERVICE),
1518 Hotkey ("transfer", OHK_TRANSFER),
1519 Hotkey ("no_unload", OHK_NO_UNLOAD),
1520 Hotkey ("no_load", OHK_NO_LOAD),
1522 HotkeyList OrdersWindow::hotkeys("order", order_hotkeys);
1524 /** Nested widget definition for "your" train orders. */
1525 static const NWidgetPart _nested_orders_train_widgets[] = {
1526 NWidget(NWID_HORIZONTAL),
1527 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
1528 NWidget(WWT_CAPTION, COLOUR_GREY, WID_O_CAPTION), SetDataTip(STR_ORDERS_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
1529 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_O_TIMETABLE_VIEW), SetMinimalSize(61, 14), SetDataTip(STR_ORDERS_TIMETABLE_VIEW, STR_ORDERS_TIMETABLE_VIEW_TOOLTIP),
1530 NWidget(WWT_SHADEBOX, COLOUR_GREY),
1531 NWidget(WWT_DEFSIZEBOX, COLOUR_GREY),
1532 NWidget(WWT_STICKYBOX, COLOUR_GREY),
1533 EndContainer(),
1534 NWidget(NWID_HORIZONTAL),
1535 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(),
1536 NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_O_SCROLLBAR),
1537 EndContainer(),
1539 /* First button row. */
1540 NWidget(NWID_HORIZONTAL),
1541 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_O_SEL_TOP_ROW_GROUNDVEHICLE),
1542 NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
1543 NWidget(NWID_BUTTON_DROPDOWN, COLOUR_GREY, WID_O_NON_STOP), SetMinimalSize(93, 12), SetFill(1, 0),
1544 SetDataTip(STR_ORDER_NON_STOP, STR_ORDER_TOOLTIP_NON_STOP), SetResize(1, 0),
1545 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_O_SEL_TOP_LEFT),
1546 NWidget(NWID_BUTTON_DROPDOWN, COLOUR_GREY, WID_O_FULL_LOAD), SetMinimalSize(93, 12), SetFill(1, 0),
1547 SetDataTip(STR_ORDER_TOGGLE_FULL_LOAD, STR_ORDER_TOOLTIP_FULL_LOAD), SetResize(1, 0),
1548 NWidget(NWID_BUTTON_DROPDOWN, COLOUR_GREY, WID_O_SERVICE), SetMinimalSize(93, 12), SetFill(1, 0),
1549 SetDataTip(STR_ORDER_SERVICE, STR_ORDER_SERVICE_TOOLTIP), SetResize(1, 0),
1550 EndContainer(),
1551 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_O_SEL_TOP_MIDDLE),
1552 NWidget(NWID_BUTTON_DROPDOWN, COLOUR_GREY, WID_O_UNLOAD), SetMinimalSize(93, 12), SetFill(1, 0),
1553 SetDataTip(STR_ORDER_TOGGLE_UNLOAD, STR_ORDER_TOOLTIP_UNLOAD), SetResize(1, 0),
1554 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_O_EMPTY), SetMinimalSize(93, 12), SetFill(1, 0), SetResize(1, 0),
1555 EndContainer(),
1556 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_O_SEL_TOP_RIGHT),
1557 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_O_REFIT), SetMinimalSize(93, 12), SetFill(1, 0),
1558 SetDataTip(STR_ORDER_REFIT, STR_ORDER_REFIT_TOOLTIP), SetResize(1, 0),
1559 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_O_REFIT_DROPDOWN), SetMinimalSize(93, 12), SetFill(1, 0),
1560 SetDataTip(STR_ORDER_REFIT_AUTO, STR_ORDER_REFIT_AUTO_TOOLTIP), SetResize(1, 0),
1561 EndContainer(),
1562 EndContainer(),
1563 NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
1564 NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_O_COND_VARIABLE), SetMinimalSize(124, 12), SetFill(1, 0),
1565 SetDataTip(STR_NULL, STR_ORDER_CONDITIONAL_VARIABLE_TOOLTIP), SetResize(1, 0),
1566 NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_O_COND_COMPARATOR), SetMinimalSize(124, 12), SetFill(1, 0),
1567 SetDataTip(STR_NULL, STR_ORDER_CONDITIONAL_COMPARATOR_TOOLTIP), SetResize(1, 0),
1568 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_O_COND_VALUE), SetMinimalSize(124, 12), SetFill(1, 0),
1569 SetDataTip(STR_BLACK_COMMA, STR_ORDER_CONDITIONAL_VALUE_TOOLTIP), SetResize(1, 0),
1570 EndContainer(),
1571 EndContainer(),
1572 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),
1573 EndContainer(),
1575 /* Second button row. */
1576 NWidget(NWID_HORIZONTAL),
1577 NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
1578 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_O_SKIP), SetMinimalSize(124, 12), SetFill(1, 0),
1579 SetDataTip(STR_ORDERS_SKIP_BUTTON, STR_ORDERS_SKIP_TOOLTIP), SetResize(1, 0),
1580 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_O_SEL_BOTTOM_MIDDLE),
1581 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_O_DELETE), SetMinimalSize(124, 12), SetFill(1, 0),
1582 SetDataTip(STR_ORDERS_DELETE_BUTTON, STR_ORDERS_DELETE_TOOLTIP), SetResize(1, 0),
1583 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_O_STOP_SHARING), SetMinimalSize(124, 12), SetFill(1, 0),
1584 SetDataTip(STR_ORDERS_STOP_SHARING_BUTTON, STR_ORDERS_STOP_SHARING_TOOLTIP), SetResize(1, 0),
1585 EndContainer(),
1586 NWidget(NWID_BUTTON_DROPDOWN, COLOUR_GREY, WID_O_GOTO), SetMinimalSize(124, 12), SetFill(1, 0),
1587 SetDataTip(STR_ORDERS_GO_TO_BUTTON, STR_ORDERS_GO_TO_TOOLTIP), SetResize(1, 0),
1588 EndContainer(),
1589 NWidget(WWT_RESIZEBOX, COLOUR_GREY),
1590 EndContainer(),
1593 static WindowDesc::Prefs _orders_train_prefs ("view_vehicle_orders_train");
1595 static const WindowDesc _orders_train_desc(
1596 WDP_AUTO, 384, 100,
1597 WC_VEHICLE_ORDERS, WC_VEHICLE_VIEW,
1598 WDF_CONSTRUCTION,
1599 _nested_orders_train_widgets, lengthof(_nested_orders_train_widgets),
1600 &_orders_train_prefs, &OrdersWindow::hotkeys
1603 /** Nested widget definition for "your" orders (non-train). */
1604 static const NWidgetPart _nested_orders_widgets[] = {
1605 NWidget(NWID_HORIZONTAL),
1606 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
1607 NWidget(WWT_CAPTION, COLOUR_GREY, WID_O_CAPTION), SetDataTip(STR_ORDERS_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
1608 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_O_TIMETABLE_VIEW), SetMinimalSize(61, 14), SetDataTip(STR_ORDERS_TIMETABLE_VIEW, STR_ORDERS_TIMETABLE_VIEW_TOOLTIP),
1609 NWidget(WWT_SHADEBOX, COLOUR_GREY),
1610 NWidget(WWT_DEFSIZEBOX, COLOUR_GREY),
1611 NWidget(WWT_STICKYBOX, COLOUR_GREY),
1612 EndContainer(),
1613 NWidget(NWID_HORIZONTAL),
1614 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(),
1615 NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_O_SCROLLBAR),
1616 EndContainer(),
1618 /* First button row. */
1619 NWidget(NWID_HORIZONTAL),
1620 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_O_SEL_TOP_ROW),
1621 /* Load + unload + refit buttons. */
1622 NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
1623 NWidget(NWID_BUTTON_DROPDOWN, COLOUR_GREY, WID_O_FULL_LOAD), SetMinimalSize(124, 12), SetFill(1, 0),
1624 SetDataTip(STR_ORDER_TOGGLE_FULL_LOAD, STR_ORDER_TOOLTIP_FULL_LOAD), SetResize(1, 0),
1625 NWidget(NWID_BUTTON_DROPDOWN, COLOUR_GREY, WID_O_UNLOAD), SetMinimalSize(124, 12), SetFill(1, 0),
1626 SetDataTip(STR_ORDER_TOGGLE_UNLOAD, STR_ORDER_TOOLTIP_UNLOAD), SetResize(1, 0),
1627 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_O_REFIT_DROPDOWN), SetMinimalSize(124, 12), SetFill(1, 0),
1628 SetDataTip(STR_ORDER_REFIT_AUTO, STR_ORDER_REFIT_AUTO_TOOLTIP), SetResize(1, 0),
1629 EndContainer(),
1630 /* Refit + service buttons. */
1631 NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
1632 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_O_REFIT), SetMinimalSize(186, 12), SetFill(1, 0),
1633 SetDataTip(STR_ORDER_REFIT, STR_ORDER_REFIT_TOOLTIP), SetResize(1, 0),
1634 NWidget(NWID_BUTTON_DROPDOWN, COLOUR_GREY, WID_O_SERVICE), SetMinimalSize(124, 12), SetFill(1, 0),
1635 SetDataTip(STR_ORDER_SERVICE, STR_ORDER_SERVICE_TOOLTIP), SetResize(1, 0),
1636 EndContainer(),
1638 /* Buttons for setting a condition. */
1639 NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
1640 NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_O_COND_VARIABLE), SetMinimalSize(124, 12), SetFill(1, 0),
1641 SetDataTip(STR_NULL, STR_ORDER_CONDITIONAL_VARIABLE_TOOLTIP), SetResize(1, 0),
1642 NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_O_COND_COMPARATOR), SetMinimalSize(124, 12), SetFill(1, 0),
1643 SetDataTip(STR_NULL, STR_ORDER_CONDITIONAL_COMPARATOR_TOOLTIP), SetResize(1, 0),
1644 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_O_COND_VALUE), SetMinimalSize(124, 12), SetFill(1, 0),
1645 SetDataTip(STR_BLACK_COMMA, STR_ORDER_CONDITIONAL_VALUE_TOOLTIP), SetResize(1, 0),
1646 EndContainer(),
1647 EndContainer(),
1649 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),
1650 EndContainer(),
1652 /* Second button row. */
1653 NWidget(NWID_HORIZONTAL),
1654 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_O_SKIP), SetMinimalSize(124, 12), SetFill(1, 0),
1655 SetDataTip(STR_ORDERS_SKIP_BUTTON, STR_ORDERS_SKIP_TOOLTIP), SetResize(1, 0),
1656 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_O_SEL_BOTTOM_MIDDLE),
1657 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_O_DELETE), SetMinimalSize(124, 12), SetFill(1, 0),
1658 SetDataTip(STR_ORDERS_DELETE_BUTTON, STR_ORDERS_DELETE_TOOLTIP), SetResize(1, 0),
1659 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_O_STOP_SHARING), SetMinimalSize(124, 12), SetFill(1, 0),
1660 SetDataTip(STR_ORDERS_STOP_SHARING_BUTTON, STR_ORDERS_STOP_SHARING_TOOLTIP), SetResize(1, 0),
1661 EndContainer(),
1662 NWidget(NWID_BUTTON_DROPDOWN, COLOUR_GREY, WID_O_GOTO), SetMinimalSize(124, 12), SetFill(1, 0),
1663 SetDataTip(STR_ORDERS_GO_TO_BUTTON, STR_ORDERS_GO_TO_TOOLTIP), SetResize(1, 0),
1664 NWidget(WWT_RESIZEBOX, COLOUR_GREY),
1665 EndContainer(),
1668 static WindowDesc::Prefs _orders_prefs ("view_vehicle_orders");
1670 static const WindowDesc _orders_desc(
1671 WDP_AUTO, 384, 100,
1672 WC_VEHICLE_ORDERS, WC_VEHICLE_VIEW,
1673 WDF_CONSTRUCTION,
1674 _nested_orders_widgets, lengthof(_nested_orders_widgets),
1675 &_orders_prefs, &OrdersWindow::hotkeys
1678 /** Nested widget definition for competitor orders. */
1679 static const NWidgetPart _nested_other_orders_widgets[] = {
1680 NWidget(NWID_HORIZONTAL),
1681 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
1682 NWidget(WWT_CAPTION, COLOUR_GREY, WID_O_CAPTION), SetDataTip(STR_ORDERS_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
1683 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_O_TIMETABLE_VIEW), SetMinimalSize(61, 14), SetDataTip(STR_ORDERS_TIMETABLE_VIEW, STR_ORDERS_TIMETABLE_VIEW_TOOLTIP),
1684 NWidget(WWT_SHADEBOX, COLOUR_GREY),
1685 NWidget(WWT_DEFSIZEBOX, COLOUR_GREY),
1686 NWidget(WWT_STICKYBOX, COLOUR_GREY),
1687 EndContainer(),
1688 NWidget(NWID_HORIZONTAL),
1689 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(),
1690 NWidget(NWID_VERTICAL),
1691 NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_O_SCROLLBAR),
1692 NWidget(WWT_RESIZEBOX, COLOUR_GREY),
1693 EndContainer(),
1694 EndContainer(),
1697 static WindowDesc::Prefs _other_orders_prefs ("view_vehicle_orders_competitor");
1699 static const WindowDesc _other_orders_desc(
1700 WDP_AUTO, 384, 86,
1701 WC_VEHICLE_ORDERS, WC_VEHICLE_VIEW,
1702 WDF_CONSTRUCTION,
1703 _nested_other_orders_widgets, lengthof(_nested_other_orders_widgets),
1704 &_other_orders_prefs, &OrdersWindow::hotkeys
1707 void ShowOrdersWindow(const Vehicle *v)
1709 DeleteWindowById(WC_VEHICLE_DETAILS, v->index, false);
1710 DeleteWindowById(WC_VEHICLE_TIMETABLE, v->index, false);
1711 if (BringWindowToFrontById(WC_VEHICLE_ORDERS, v->index) != NULL) return;
1713 /* Using a different WindowDescs for _local_company causes problems.
1714 * Due to this we have to close order windows in ChangeWindowOwner/DeleteCompanyWindows,
1715 * because we cannot change switch the WindowDescs and keeping the old WindowDesc results
1716 * in crashed due to missing widges.
1717 * TODO Rewrite the order GUI to not use different WindowDescs.
1719 if (v->owner != _local_company) {
1720 new OrdersWindow(&_other_orders_desc, v);
1721 } else {
1722 new OrdersWindow(v->IsGroundVehicle() ? &_orders_train_desc : &_orders_desc, v);