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/>.
10 /** @file timetable_gui.cpp GUI for time tabling. */
14 #include "command_func.h"
15 #include "company_func.h"
16 #include "date_func.h"
20 #include "settings_type.h"
21 #include "string_func.h"
22 #include "strings_func.h"
23 #include "textbuf_gui.h"
24 #include "timetable.h"
25 #include "vehicle_base.h"
26 #include "vehicle_gui.h"
27 #include "viewport_func.h"
28 #include "window_func.h"
29 #include "window_gui.h"
31 #include "table/sprites.h"
32 #include "table/strings.h"
33 #include "widgets/timetable_widget.h"
34 #include "widgets/dropdown_func.h"
36 //! Entries for mode selection dropdown list. Order must be identical to the one in #TTSepMode.
37 static const StringID TimetableSeparationDropdownOptions
[6] = {
38 STR_TTSEPARATION_AUTO
,
40 STR_TTSEPARATION_MAN_TIME
,
41 STR_TTSEPARATION_MAN_NUM
,
42 STR_TTSEPARATION_BUFFERED_AUTO
,
46 //! Container for the arrival/departure dates of a vehicle.
47 struct TimetableArrivalDeparture
49 Ticks arrival
; //!< The arrival time
50 Ticks departure
; //!< The departure time
53 //! Set the timetable parameters in the ticks (minutes) format.
54 //! @param param1 the first of three successive DParam to fill
55 //! @param ticks the number of ticks to 'draw'
56 void SetTimetableParams(Ticks ticks
, int param1
)
58 SetDParam(param1
, STR_TIMETABLE_TICKS_MINUTES
);
59 SetDParam(param1
+ 1, ticks
);
60 SetDParam(param1
+ 2, ticks
);
63 //! Check whether it is possible to determine how long the order takes.
64 //! @param order the order to check.
65 //! @param travelling whether we are interested in the travel or the wait part.
66 //! @return true if the travel/wait time can be used.
67 static bool CanDetermineTimeTaken(const Order
* order
, bool travelling
)
69 // Current order is conditional.
70 if (order
->IsType(OT_CONDITIONAL
) || order
->IsType(OT_IMPLICIT
)) return false;
72 // No travel time and we have not already finished travelling.
73 if (travelling
&& !order
->IsTravelTimetabled()) return false;
75 // No wait time but we are loading at this timetabled station.
76 if (!travelling
&& !order
->IsWaitTimetabled() && order
->IsType(OT_GOTO_STATION
) &&
77 !(order
->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION
)) {
84 //! Fill the table with arrivals and departures
85 //! @param vehicle Vehicle which must have at least 2 orders.
86 //! @param start order index to start at
87 //! @param travelling Are we still in the travelling part of the start order
88 //! @param table Fill in arrival and departures including intermediate orders
89 //! @param offset Add this value to result and all arrivals and departures
90 static void FillTimetableArrivalDepartureTable(const Vehicle
* vehicle
, VehicleOrderID start
, bool travelling
, TimetableArrivalDeparture
* table
, Ticks offset
)
92 assert(table
!= nullptr);
93 assert(vehicle
->GetNumOrders() >= 2);
94 assert(start
< vehicle
->GetNumOrders());
97 VehicleOrderID i
= start
;
98 const Order
* order
= vehicle
->GetOrder(i
);
100 // Pre-initialize with unknown time.
101 for (int j
= 0; j
< vehicle
->GetNumOrders(); ++j
) {
102 table
[j
].arrival
= table
[j
].departure
= INVALID_TICKS
;
105 // Cyclically loop over all orders until we reach the current one again.
106 // As we may start at the current order, do a post-checking loop.
108 // Automatic orders don't influence the overall timetable;
109 // they just add some untimetabled entries, but the time till
110 // the next non-implicit order can still be known.
111 if (!order
->IsType(OT_IMPLICIT
)) {
112 if (travelling
|| i
!= start
) {
113 if (!CanDetermineTimeTaken(order
, true)) return;
114 sum
+= order
->GetTimetabledTravel();
115 table
[i
].arrival
= sum
;
118 if (!CanDetermineTimeTaken(order
, false)) return;
119 sum
+= order
->GetTimetabledWait();
120 table
[i
].departure
= sum
;
125 if (i
>= vehicle
->GetNumOrders()) {
127 assert(order
== nullptr);
128 order
= vehicle
->GetFirstOrder();
130 } while (i
!= start
);
132 // When loading at a scheduled station we still have to treat the
133 // travelling part of the first order.
135 if (!CanDetermineTimeTaken(order
, true)) return;
136 sum
+= order
->GetTimetabledTravel();
137 table
[i
].arrival
= sum
;
141 struct TimetableWindow
: Window
144 const Vehicle
* vehicle
; //!< Vehicle monitored by the window.
145 bool show_expected
; //!< Whether we show expected arrival or scheduled
146 uint deparr_time_width
{}; //!< The width of the departure/arrival time
147 uint deparr_abbr_width
{}; //!< The width of the departure/arrival abbreviation
149 bool query_is_speed_query
{}; //!< The currently open query window is a speed query and not a time query
150 bool query_is_bulk_query
{}; //!< The currently open query window applies to all relevant orders.
151 TTSepSettings new_sep_settings
; //!< Contains new separation settings.
152 VehicleTimetableWidgets query_widget
{}; //!< Required to determinate source of input query
153 int summary_warnings
= 0; //!< Number of summary warnings shown
155 TimetableWindow(WindowDesc
* desc
, WindowNumber window_number
) :
158 vehicle(Vehicle::Get(window_number
)),
161 this->new_sep_settings
= vehicle
->GetTimetableSeparationSettings();
162 this->CreateNestedTree();
163 this->vscroll
= this->GetScrollbar(WID_VT_SCROLLBAR
);
164 this->UpdateSelectionStates();
165 this->FinishInitNested(window_number
);
166 this->owner
= this->vehicle
->owner
;
171 if (!FocusWindowById(WC_VEHICLE_VIEW
, this->window_number
)) {
172 MarkAllRouteStepsDirty(this->vehicle
);
176 TimetableWindow(const TimetableWindow
&) = delete;
177 TimetableWindow(TimetableWindow
&&) = delete;
178 TimetableWindow
& operator=(const TimetableWindow
&) = delete;
179 TimetableWindow
& operator=(TimetableWindow
&&) = delete;
181 //! Build the arrival-departure list for a given vehicle
182 //! @param vehicle the vehicle to make the list for
183 //! @param table the table to fill
184 //! @return if next arrival will be early
185 static bool BuildArrivalDepartureList(const Vehicle
* vehicle
, TimetableArrivalDeparture
* table
)
187 assert(HasBit(vehicle
->vehicle_flags
, VF_TIMETABLE_STARTED
));
189 const bool travelling
= (!(vehicle
->current_order
.IsType(OT_LOADING
) || vehicle
->current_order
.IsType(OT_WAITING
)) ||
190 vehicle
->current_order
.GetNonStopType() == ONSF_STOP_EVERYWHERE
);
191 Ticks start_time
= GetCurrentTickCount() - vehicle
->current_order_time
;
193 if (vehicle
->cur_timetable_order_index
!= INVALID_VEH_ORDER_ID
&& vehicle
->cur_timetable_order_index
!= vehicle
->cur_real_order_index
) {
194 // Vehicle is taking a conditional order branch, adjust start time to compensate.
195 const Order
* real_current_order
= vehicle
->GetOrder(vehicle
->cur_real_order_index
);
196 const Order
* real_timetable_order
= vehicle
->GetOrder(vehicle
->cur_timetable_order_index
);
197 assert(real_timetable_order
->IsType(OT_CONDITIONAL
));
198 start_time
+= (real_timetable_order
->GetWaitTime() - real_current_order
->GetTravelTime());
201 FillTimetableArrivalDepartureTable(vehicle
, vehicle
->cur_real_order_index
% vehicle
->GetNumOrders(), travelling
, table
, start_time
);
203 return (travelling
&& vehicle
->lateness_counter
< 0);
206 void UpdateWidgetSize(int widget
, Dimension
* size
, const Dimension
& padding
, Dimension
* fill
, Dimension
* resize
) override
209 case WID_VT_ARRIVAL_DEPARTURE_PANEL
:
210 SetDParamMaxValue(0, MAX_YEAR
* DAYS_IN_YEAR
, 0, FS_SMALL
);
211 this->deparr_time_width
= GetStringBoundingBox(STR_JUST_TIME
).width
;
212 this->deparr_abbr_width
= max(GetStringBoundingBox(STR_TIMETABLE_ARRIVAL_ABBREVIATION
).width
, GetStringBoundingBox(STR_TIMETABLE_DEPARTURE_ABBREVIATION
).width
);
213 size
->width
= WD_FRAMERECT_LEFT
+ this->deparr_abbr_width
+ 10 + this->deparr_time_width
+ 10 + WD_FRAMERECT_RIGHT
;
216 case WID_VT_ARRIVAL_DEPARTURE_SELECTION
:
217 case WID_VT_TIMETABLE_PANEL
:
218 resize
->height
= FONT_HEIGHT_NORMAL
;
219 size
->height
= WD_FRAMERECT_TOP
+ 8 * resize
->height
+ WD_FRAMERECT_BOTTOM
;
222 case WID_VT_SUMMARY_PANEL
: {
223 const Dimension warning_sign_size
= GetSpriteSize(SPR_WARNING_SIGN
);
224 size
->height
= WD_FRAMERECT_TOP
+ 2 * FONT_HEIGHT_NORMAL
+ this->summary_warnings
* max
<int>(warning_sign_size
.height
, FONT_HEIGHT_NORMAL
) + WD_FRAMERECT_BOTTOM
;
233 int GetOrderFromTimetableWndPt(int y
, const Vehicle
* v
)
235 int sel
= (y
- this->GetWidget
<NWidgetBase
>(WID_VT_TIMETABLE_PANEL
)->pos_y
- WD_FRAMERECT_TOP
) / FONT_HEIGHT_NORMAL
;
237 if (uint(sel
) >= this->vscroll
->GetCapacity()) return INVALID_ORDER
;
239 sel
+= this->vscroll
->GetPosition();
241 return (sel
< v
->GetNumOrders() * 2 && sel
>= 0) ? sel
: INVALID_ORDER
;
244 //! Some data on this window has become invalid.
245 //! @param data Information about the changed data.
246 //! @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.
247 void OnInvalidateData(int data
, bool gui_scope
) override
250 case VIWD_AUTOREPLACE
:
251 // Autoreplace replaced the vehicle.
252 this->vehicle
= Vehicle::Get(this->window_number
);
255 case VIWD_REMOVE_ALL_ORDERS
:
256 // Removed / replaced all orders (after deleting / sharing).
257 if (this->sel_index
== -1) break;
259 this->DeleteChildWindows();
260 this->sel_index
= -1;
263 case VIWD_MODIFY_ORDERS
:
264 if (!gui_scope
) break;
265 this->UpdateSelectionStates();
270 if (gui_scope
) break; // Only do this once; from command scope
272 this->new_sep_settings
= this->vehicle
->GetTimetableSeparationSettings();
274 // Moving an order. If one of these is INVALID_VEH_ORDER_ID, then
275 // the order is being created / removed.
276 if (this->sel_index
== -1) break;
278 const VehicleOrderID from
= GB(data
, 0, 8);
279 const VehicleOrderID to
= GB(data
, 8, 8);
281 if (from
== to
) break; // No need to change anything
283 // if from == INVALID_VEH_ORDER_ID, one order was added; if to == INVALID_VEH_ORDER_ID, one order was removed */
284 const uint old_num_orders
= this->vehicle
->GetNumOrders() - uint(from
== INVALID_VEH_ORDER_ID
) + uint(to
== INVALID_VEH_ORDER_ID
);
285 VehicleOrderID selected_order
= (this->sel_index
+ 1) / 2;
287 if (selected_order
== old_num_orders
) {
288 // When last travel time is selected, it belongs to order 0
292 const bool travel
= HasBit(this->sel_index
, 0);
294 if (from
!= selected_order
) {
295 // Moving from preceding order?
296 selected_order
-= int(from
<= selected_order
);
297 // Moving to preceding order?
298 selected_order
+= int(to
<= selected_order
);
300 // Now we are modifying the selected order.
301 if (to
== INVALID_VEH_ORDER_ID
) {
302 // Deleting selected order.
303 this->DeleteChildWindows();
304 this->sel_index
= -1;
308 // Moving selected order.
312 // Recompute new sel_index
313 this->sel_index
= 2 * selected_order
- int(travel
);
315 // Travel time of first order needs special handling.
316 if (this->sel_index
== -1) this->sel_index
= this->vehicle
->GetNumOrders() * 2 - 1;
322 void OnPaint() override
324 const Vehicle
* vehicle
= this->vehicle
;
325 const int selected
= this->sel_index
;
327 this->vscroll
->SetCount(vehicle
->GetNumOrders() * 2);
329 if (vehicle
->owner
== _local_company
) {
330 const bool disable
= IsActionDisabled(vehicle
, selected
);
331 const bool disable_speed
= disable
|| selected
% 2 != 1 || vehicle
->type
== VEH_AIRCRAFT
;
333 this->SetWidgetDisabledState(WID_VT_CHANGE_TIME
, disable
);
334 this->SetWidgetDisabledState(WID_VT_CLEAR_TIME
, disable
);
335 this->SetWidgetDisabledState(WID_VT_CHANGE_SPEED
, disable_speed
);
336 this->SetWidgetDisabledState(WID_VT_CLEAR_SPEED
, disable_speed
);
337 this->SetWidgetDisabledState(WID_VT_SHARED_ORDER_LIST
, !vehicle
->HasSharedOrdersList());
339 this->SetWidgetDisabledState(WID_VT_TTSEP_MODE_DROPDOWN
, !vehicle
->HasOrdersList());
340 this->SetWidgetDisabledState(WID_VT_CONFIRM_ALL
, !vehicle
->HasOrdersList());
341 this->SetWidgetDisabledState(WID_VT_RESET_LATENESS
, !vehicle
->HasOrdersList());
342 this->SetWidgetDisabledState(WID_VT_AUTOMATE
, !vehicle
->HasOrdersList());
344 this->DisableWidget(WID_VT_CONFIRM_ALL
);
345 this->DisableWidget(WID_VT_CHANGE_TIME
);
346 this->DisableWidget(WID_VT_CLEAR_TIME
);
347 this->DisableWidget(WID_VT_CHANGE_SPEED
);
348 this->DisableWidget(WID_VT_CLEAR_SPEED
);
349 this->DisableWidget(WID_VT_RESET_LATENESS
);
350 this->DisableWidget(WID_VT_AUTOMATE
);
351 this->DisableWidget(WID_VT_SHARED_ORDER_LIST
);
354 // We can only set parameters if we're in one of the manual modes.
355 const bool enabled_state
= (this->new_sep_settings
.mode
== TTS_MODE_MAN_N
) || (this->new_sep_settings
.mode
== TTS_MODE_MAN_T
);
357 this->SetWidgetDisabledState(WID_VT_TTSEP_SET_PARAMETER
, !enabled_state
);
358 this->SetWidgetLoweredState(WID_VT_AUTOMATE
, HasBit(vehicle
->vehicle_flags
, VF_AUTOMATE_TIMETABLE
));
363 void SetStringParameters(int widget
) const override
367 SetDParam(0, this->vehicle
->index
);
370 case WID_VT_EXPECTED
:
371 SetDParam(0, this->show_expected
? STR_TIMETABLE_EXPECTED
: STR_TIMETABLE_SCHEDULED
);
374 case WID_VT_TTSEP_MODE_DROPDOWN
:
375 SetDParam(0, TimetableSeparationDropdownOptions
[this->new_sep_settings
.mode
]);
378 case WID_VT_TTSEP_SET_PARAMETER
:
379 SetDParam(0, (this->new_sep_settings
.mode
== TTS_MODE_MAN_N
) ? STR_TTSEPARATION_SET_NUM
: STR_TTSEPARATION_SET_TIME
);
387 void DrawWarnings(const Rect
& rect
, int& y
, const Vehicle
* vehicle
) const
389 const bool have_missing_times
= !vehicle
->HasCompleteTimetable();
390 bool have_conditional
= false;
391 bool have_bad_full_load
= false;
393 const bool is_automated_timetable
= HasBit(vehicle
->vehicle_flags
, VF_AUTOMATE_TIMETABLE
);
395 for (int n
= 0; n
< vehicle
->GetNumOrders(); ++n
) {
396 const Order
* order
= vehicle
->GetOrder(n
);
398 if (order
->IsType(OT_CONDITIONAL
)) {
399 have_conditional
= true;
402 if (!have_bad_full_load
&& (is_automated_timetable
|| order
->IsWaitTimetabled())) {
403 if (order
->GetLoadType() & OLFB_FULL_LOAD
) {
404 have_bad_full_load
= true;
405 } else if (order
->GetLoadType() == OLFB_CARGO_TYPE_LOAD
) {
406 for (CargoID c
= 0; c
< NUM_CARGO
; c
++) {
407 if (order
->GetCargoLoadTypeRaw(c
) & OLFB_FULL_LOAD
) {
408 have_bad_full_load
= true;
416 const Dimension warning_dimensions
= GetSpriteSize(SPR_WARNING_SIGN
);
417 const int step_height
= max
<int>(warning_dimensions
.height
, FONT_HEIGHT_NORMAL
);
418 const int text_offset_y
= (step_height
- FONT_HEIGHT_NORMAL
) / 2;
419 const int warning_offset_y
= (step_height
- warning_dimensions
.height
) / 2;
420 const bool rtl
= _current_text_dir
== TD_RTL
;
422 int warning_count
= 0;
424 const auto draw_info
= [&](StringID text
, bool warning
) {
425 int left
= rect
.left
+ WD_FRAMERECT_LEFT
;
426 int right
= rect
.right
- WD_FRAMERECT_RIGHT
;
429 DrawSprite(SPR_WARNING_SIGN
, 0, rtl
? right
- warning_dimensions
.width
- 5 : left
+ 5, y
+ warning_offset_y
);
431 right
-= (warning_dimensions
.width
+ 10);
433 left
+= (warning_dimensions
.width
+ 10);
437 DrawString(left
, right
, y
+ text_offset_y
, text
);
443 if (this->new_sep_settings
.mode
!= TTS_MODE_OFF
) {
444 if (vehicle
->GetNumOrders() == 0) {
445 draw_info(STR_TIMETABLE_AUTOSEP_TIMETABLE_INCOMPLETE
, false);
446 } else if (have_missing_times
) {
447 if (is_automated_timetable
) {
448 draw_info(STR_TIMETABLE_AUTOSEP_TIMETABLE_INCOMPLETE
, false);
450 draw_info(STR_TIMETABLE_WARNING_AUTOSEP_MISSING_TIMINGS
, true);
453 draw_info(vehicle
->HasSharedOrdersList() ? STR_TIMETABLE_AUTOSEP_OK
: STR_TIMETABLE_AUTOSEP_SINGLE_VEH
, !vehicle
->HasSharedOrdersList());
456 if (have_conditional
) draw_info(STR_TIMETABLE_WARNING_AUTOSEP_CONDITIONAL
, true);
457 if (have_bad_full_load
) draw_info(STR_TIMETABLE_WARNING_FULL_LOAD
, true);
460 if (warning_count
!= this->summary_warnings
) {
461 auto mutable_this
= const_cast<TimetableWindow
*>(this);
462 mutable_this
->summary_warnings
= warning_count
;
463 mutable_this
->ReInit();
467 void DrawWidget(const Rect
& rect
, int widget
) const override
469 const Vehicle
* vehicle
= this->vehicle
;
470 const int selected
= this->sel_index
;
473 case WID_VT_TIMETABLE_PANEL
: {
474 int y
= rect
.top
+ WD_FRAMERECT_TOP
;
475 int scroll_position
= this->vscroll
->GetPosition();
476 VehicleOrderID order_id
= (scroll_position
+ 1) / 2;
477 bool final_order
= false;
479 const bool rtl
= _current_text_dir
== TD_RTL
;
480 SetDParamMaxValue(0, vehicle
->GetNumOrders(), 2);
481 const int index_column_width
= GetStringBoundingBox(STR_ORDER_INDEX
).width
+ 2 *
482 GetSpriteSize(rtl
? SPR_ARROW_RIGHT
: SPR_ARROW_LEFT
).width
+ 3;
484 const int middle
= rtl
?
485 rect
.right
- WD_FRAMERECT_RIGHT
- index_column_width
:
486 rect
.left
+ WD_FRAMERECT_LEFT
+ index_column_width
;
488 const Order
* order
= vehicle
->GetOrder(order_id
);
490 while (order
!= nullptr) {
491 // Don't draw anything if it extends past the end of the window.
492 if (!this->vscroll
->IsVisible(scroll_position
)) break;
494 if (scroll_position
% 2 == 0) {
495 DrawOrderString(vehicle
, order
, order_id
, y
, scroll_position
== selected
, true, rect
.left
+ WD_FRAMERECT_LEFT
, middle
, rect
.right
- WD_FRAMERECT_RIGHT
);
499 if (order_id
>= vehicle
->GetNumOrders()) {
500 order
= vehicle
->GetOrder(0);
507 TextColour colour
= (scroll_position
== selected
) ? TC_WHITE
: TC_BLACK
;
508 if (order
->IsType(OT_CONDITIONAL
)) {
509 string
= STR_TIMETABLE_NO_TRAVEL
;
510 } else if (order
->IsType(OT_IMPLICIT
)) {
511 string
= STR_TIMETABLE_NOT_TIMETABLEABLE
;
512 colour
= ((scroll_position
== selected
) ? TC_SILVER
: TC_GREY
) | TC_NO_SHADE
;
513 } else if (!order
->IsTravelTimetabled()) {
514 if (order
->GetTravelTime() > 0) {
515 SetTimetableParams(order
->GetTravelTime());
516 string
= order
->GetMaxSpeed() != UINT16_MAX
?
517 STR_TIMETABLE_TRAVEL_FOR_MINUTES_SPEED_ESTIMATED
:
518 STR_TIMETABLE_TRAVEL_FOR_MINUTES_ESTIMATED
;
520 string
= order
->GetMaxSpeed() != UINT16_MAX
?
521 STR_TIMETABLE_TRAVEL_NOT_TIMETABLED_SPEED
:
522 STR_TIMETABLE_TRAVEL_NOT_TIMETABLED
;
525 SetTimetableParams(order
->GetTimetabledTravel());
526 string
= order
->GetMaxSpeed() != UINT16_MAX
?
527 STR_TIMETABLE_TRAVEL_FOR_MINUTES_SPEED
:
528 STR_TIMETABLE_TRAVEL_FOR_MINUTES
;
531 SetDParam(3, order
->GetMaxSpeed());
532 DrawString(rtl
? rect
.left
+ WD_FRAMERECT_LEFT
: middle
, rtl
? middle
: rect
.right
- WD_FRAMERECT_LEFT
, y
, string
, colour
);
534 if (final_order
) break;
538 y
+= FONT_HEIGHT_NORMAL
;
543 case WID_VT_ARRIVAL_DEPARTURE_PANEL
: {
544 // Arrival and departure times are handled in an all-or-nothing approach,
545 // i.e. are only shown if we can calculate all times.
546 // Excluding order lists with only one order makes some things easier.
547 const Ticks total_time
= vehicle
->GetTimetableDurationIncomplete();
549 if (total_time
<= 0 || vehicle
->GetNumOrders() <= 1 || !HasBit(vehicle
->vehicle_flags
, VF_TIMETABLE_STARTED
)) break;
551 TimetableArrivalDeparture
* arr_dep
= AllocaM(TimetableArrivalDeparture
, vehicle
->GetNumOrders());
552 const VehicleOrderID cur_order
= vehicle
->cur_real_order_index
% vehicle
->GetNumOrders();
554 const VehicleOrderID earlyID
= BuildArrivalDepartureList(vehicle
, arr_dep
) ?
556 static_cast<VehicleOrderID
>(INVALID_VEH_ORDER_ID
);
558 int y
= rect
.top
+ WD_FRAMERECT_TOP
;
560 bool show_late
= this->show_expected
&& vehicle
->lateness_counter
> _settings_client
.gui
.ticks_per_minute
;
561 const Ticks offset
= show_late
? 0 : -vehicle
->lateness_counter
;
563 if (HasBit(vehicle
->vehicle_flags
, VF_SEPARATION_IN_PROGRESS
)) {
567 const bool rtl
= _current_text_dir
== TD_RTL
;
568 const int abbr_left
= rtl
? rect
.right
- WD_FRAMERECT_RIGHT
- this->deparr_abbr_width
: rect
.left
+ WD_FRAMERECT_LEFT
;
569 const int abbr_right
= rtl
? rect
.right
- WD_FRAMERECT_RIGHT
: rect
.left
+ WD_FRAMERECT_LEFT
+ this->deparr_abbr_width
;
570 const int time_left
= rtl
? rect
.right
- WD_FRAMERECT_RIGHT
- rect
.left
- WD_FRAMERECT_RIGHT
- this->deparr_abbr_width
- 10 - this->deparr_time_width
: rect
.left
+ WD_FRAMERECT_RIGHT
+ this->deparr_abbr_width
+ 10;
571 const int time_right
= rtl
? rect
.right
- WD_FRAMERECT_RIGHT
- this->deparr_abbr_width
- 10 : rect
.left
+ WD_FRAMERECT_RIGHT
+ rect
.left
+ WD_FRAMERECT_RIGHT
+ this->deparr_abbr_width
+ 10 + this->deparr_time_width
;
573 for (int i
= this->vscroll
->GetPosition(); i
/ 2 < vehicle
->GetNumOrders(); ++i
) {
574 // note: i is also incremented in the loop
575 // Don't draw anything if it extends past the end of the window.
576 if (!this->vscroll
->IsVisible(i
)) break;
579 if (arr_dep
[i
/ 2].arrival
!= INVALID_TICKS
) {
580 DrawString(abbr_left
, abbr_right
, y
, STR_TIMETABLE_ARRIVAL_ABBREVIATION
, i
== selected
? TC_WHITE
: TC_BLACK
);
581 if (this->show_expected
&& i
/ 2 == earlyID
) {
582 SetDParam(0, arr_dep
[i
/ 2].arrival
);
583 DrawString(time_left
, time_right
, y
, STR_JUST_TIME
, TC_GREEN
);
585 SetDParam(0, arr_dep
[i
/ 2].arrival
+ offset
);
586 DrawString(time_left
, time_right
, y
, STR_JUST_TIME
, show_late
? TC_RED
: i
== selected
? TC_WHITE
: TC_BLACK
);
590 if (arr_dep
[i
/ 2].departure
!= INVALID_TICKS
) {
591 DrawString(abbr_left
, abbr_right
, y
, STR_TIMETABLE_DEPARTURE_ABBREVIATION
, i
== selected
? TC_WHITE
: TC_BLACK
);
592 SetDParam(0, arr_dep
[i
/ 2].departure
+ offset
);
593 DrawString(time_left
, time_right
, y
, STR_JUST_TIME
,
594 show_late
? TC_RED
: i
== selected
? TC_WHITE
: TC_BLACK
);
598 y
+= FONT_HEIGHT_NORMAL
;
603 case WID_VT_SUMMARY_PANEL
: {
604 int y
= rect
.top
+ WD_FRAMERECT_TOP
;
605 const Ticks total_time
= vehicle
->GetTimetableDurationIncomplete();
607 if (total_time
!= 0) {
608 SetTimetableParams(total_time
);
609 DrawString(rect
.left
+ WD_FRAMERECT_LEFT
, rect
.right
- WD_FRAMERECT_RIGHT
, y
, vehicle
->HasCompleteTimetable() ? STR_TIMETABLE_TOTAL_TIME_MINUTES
: STR_TIMETABLE_TOTAL_TIME_MINUTES_INCOMPLETE
);
612 y
+= FONT_HEIGHT_NORMAL
;
614 const auto lateness_counter
= HasBit(vehicle
->vehicle_flags
, VF_SEPARATION_IN_PROGRESS
) ?
616 vehicle
->lateness_counter
;
618 if (vehicle
->timetable_start
!= 0) {
619 // We are running towards the first station so we can start the
620 // timetable at the given time.
621 SetDParam(0, STR_JUST_DATE
);
622 SetDParam(1, vehicle
->timetable_start
);
623 DrawString(rect
.left
+ WD_FRAMERECT_LEFT
, rect
.right
- WD_FRAMERECT_RIGHT
, y
, STR_TIMETABLE_STATUS_START_AT
);
624 } else if (!HasBit(vehicle
->vehicle_flags
, VF_TIMETABLE_STARTED
)) {
625 // We aren't running on a timetable yet, so how can we be "on time"
626 // when we aren't even "on service"/"on duty"?
627 DrawString(rect
.left
+ WD_FRAMERECT_LEFT
, rect
.right
- WD_FRAMERECT_RIGHT
, y
, STR_TIMETABLE_STATUS_NOT_STARTED
);
628 } else if (lateness_counter
== 0) {
629 DrawString(rect
.left
+ WD_FRAMERECT_LEFT
, rect
.right
- WD_FRAMERECT_RIGHT
, y
, STR_TIMETABLE_STATUS_ON_TIME
);
631 SetTimetableParams(abs(lateness_counter
));
632 DrawString(rect
.left
+ WD_FRAMERECT_LEFT
, rect
.right
- WD_FRAMERECT_RIGHT
, y
, lateness_counter
< 0 ? STR_TIMETABLE_STATUS_EARLY_MINUTES
: STR_TIMETABLE_STATUS_LATE_MINUTES
);
635 y
+= FONT_HEIGHT_NORMAL
;
637 DrawWarnings(rect
, y
, vehicle
);
641 case WID_VT_TTSEP_PANEL_TEXT
: {
642 // Represents the current vertical position.
643 int y
= rect
.top
+ WD_FRAMERECT_TOP
;
645 // Represents the left border of the separation display frame.
646 const int left_border
= rect
.left
+ WD_FRAMERECT_LEFT
;
648 // Represents the right border of the separation display frame.
649 const int right_border
= rect
.right
- WD_FRAMERECT_RIGHT
;
651 // If separation is inactive, we can stop here.
652 if (!_settings_game
.order
.automatic_timetable_separation
|| !this->vehicle
->HasOrdersList()) break;
654 const bool is_off
= this->new_sep_settings
.mode
== TTS_MODE_OFF
;
655 const bool is_auto
= this->new_sep_settings
.mode
== TTS_MODE_AUTO
||
656 this->new_sep_settings
.mode
== TTS_MODE_BUFFERED_AUTO
;
657 const bool is_manual_number
= this->new_sep_settings
.mode
== TTS_MODE_MAN_N
;
658 const bool is_manual_time
= this->new_sep_settings
.mode
== TTS_MODE_MAN_T
;
660 const bool is_complete
= this->vehicle
->HasCompleteTimetable();
661 const bool is_valid
= this->vehicle
->IsTimetableSeparationValid();
663 if (!is_off
&& (!is_auto
|| (is_complete
&& is_valid
))) {
666 // If separation hasn't just been switched off, we need to draw various description lines.
667 // The first line is the amount of separation which is either saved in the struct or must
668 // be calculated on the fly.
669 if (is_manual_time
|| is_auto
) {
670 par
= this->new_sep_settings
.sep_ticks
;
672 par
= this->vehicle
->GetTimetableTotalDuration() / max(1u, this->new_sep_settings
.num_veh
);
675 if (!is_manual_number
&& (is_manual_time
|| (is_auto
&& (is_complete
&& is_valid
)))) {
678 DrawString(left_border
, right_border
, y
, STR_TTSEPARATION_REQ_TIME_DESC
, TC_BLACK
);
679 y
+= GetStringBoundingBox(STR_TTSEPARATION_REQ_TIME_DESC
).height
;
682 if (!is_off
&& is_auto
&& (is_complete
&& is_valid
)) {
683 DrawString(left_border
, right_border
, y
, STR_TTSEPARATION_BETWEEN
, TC_BLACK
);
684 y
+= GetStringBoundingBox(STR_TTSEPARATION_BETWEEN
).height
;
687 par
= this->new_sep_settings
.num_veh
;
689 if (is_manual_number
|| (is_auto
&& (is_complete
&& is_valid
))) {
691 DrawString(left_border
, right_border
, y
, STR_TTSEPARATION_REQ_NUM_DESC
, TC_BLACK
);
692 y
+= GetStringBoundingBox(STR_TTSEPARATION_REQ_NUM_DESC
).height
;
696 // If separation is switched on at all...
697 if (this->vehicle
->IsTimetableSeparationOn()) {
699 SetDParam(0, STR_TTSEPARATION_STATUS_WAITING_FOR_TIMETABLE
);
700 } else if (!this->vehicle
->HasSharedOrdersList()) {
701 SetDParam(0, STR_TTSEPARATION_STATUS_WAITING_FOR_VEHICLES
);
703 // ... set displayed status to either "Running" or "Initializing"
704 SetDParam(0, (this->vehicle
->IsTimetableSeparationValid()) ? STR_TTSEPARATION_STATUS_RUNNING
: STR_TTSEPARATION_STATUS_INIT
);
707 // If separation is switched off, show this instead.
708 SetDParam(0, STR_TTSEPARATION_STATUS_OFF
);
711 y
+= FONT_HEIGHT_NORMAL
;
713 // Print status description.
714 DrawStringMultiLine(left_border
, right_border
, y
, rect
.bottom
- WD_FRAMERECT_BOTTOM
, STR_TTSEPARATION_STATUS_DESC
);
722 static uint32
PackTimetableArgs(const Vehicle
* v
, uint selected
, bool speed
)
724 uint order_number
= (selected
+ 1) / 2;
725 ModifyTimetableFlags mtf
= (selected
% 2 == 1) ? (speed
? MTF_TRAVEL_SPEED
: MTF_TRAVEL_TIME
) : MTF_WAIT_TIME
;
727 if (order_number
>= v
->GetNumOrders()) order_number
= 0;
729 return v
->index
| (order_number
<< 20) | (mtf
<< 28);
732 static bool IsActionDisabled(const Vehicle
* vehicle
, int selected
)
734 bool disabled
= true;
735 if (selected
!= -1) {
736 const Order
* order
= vehicle
->GetOrder(((selected
+ 1) / 2) % vehicle
->GetNumOrders());
737 if (selected
% 2 == 1) {
738 disabled
= order
!= nullptr && (order
->IsType(OT_CONDITIONAL
) || order
->IsType(OT_IMPLICIT
));
740 disabled
= order
== nullptr || ((!(order
->IsType(OT_GOTO_STATION
) || (order
->IsType(OT_GOTO_DEPOT
) && !(order
->GetDepotActionType() & ODATFB_HALT
))) || (order
->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION
)) && !order
->IsType(OT_CONDITIONAL
));
746 void OnClick(Point point
, int widget
, int click_count
) override
748 const Vehicle
* v
= this->vehicle
;
750 this->DeleteChildWindows(WC_QUERY_STRING
);
753 case WID_VT_ORDER_VIEW
:
758 case WID_VT_TIMETABLE_PANEL
: {
760 const int selected
= GetOrderFromTimetableWndPt(point
.y
, v
);
762 // Allow change time by double-clicking order.
763 if (click_count
== 2) {
764 this->sel_index
= selected
== INVALID_ORDER
? -1 : selected
;
765 this->OnClick(point
, WID_VT_CHANGE_TIME
, click_count
);
768 this->sel_index
= (selected
== INVALID_ORDER
|| selected
== this->sel_index
) ? -1 : selected
;
771 this->DeleteChildWindows();
775 case WID_VT_CONFIRM_ALL
: {
776 // Confirm all estimated times as timetabled.
777 DoCommandP(0, v
->index
, 0, CMD_CONFIRM_ALL
| CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE
));
781 case WID_VT_CHANGE_TIME
: {
782 // "Wait For" button.
783 const int selected
= this->sel_index
;
784 VehicleOrderID real
= (selected
+ 1) / 2;
786 if (real
>= v
->GetNumOrders()) real
= 0;
788 const Order
* order
= v
->GetOrder(real
);
789 StringID current
= STR_EMPTY
;
791 if (order
!= nullptr) {
792 const uint time
= (selected
% 2 == 1) ? order
->GetTravelTime() : order
->GetWaitTime();
796 current
= STR_JUST_INT
;
800 this->query_widget
= WID_VT_CHANGE_TIME
;
801 this->query_is_speed_query
= false;
802 this->query_is_bulk_query
= _ctrl_pressed
;
803 ShowQueryString(current
, STR_TIMETABLE_CHANGE_TIME
, 31, this, CS_NUMERAL
, QSF_ACCEPT_UNCHANGED
);
807 case WID_VT_CHANGE_SPEED
: {
808 // Change max speed button.
809 const int selected
= this->sel_index
;
810 VehicleOrderID real
= (selected
+ 1) / 2;
812 if (real
>= v
->GetNumOrders()) real
= 0;
814 StringID current
= STR_EMPTY
;
815 const Order
* order
= v
->GetOrder(real
);
816 if (order
!= nullptr) {
817 if (order
->GetMaxSpeed() != UINT16_MAX
) {
818 SetDParam(0, ConvertKmhishSpeedToDisplaySpeed(order
->GetMaxSpeed()));
819 current
= STR_JUST_INT
;
823 this->query_widget
= WID_VT_CHANGE_SPEED
;
824 this->query_is_speed_query
= true;
825 this->query_is_bulk_query
= _ctrl_pressed
;
826 ShowQueryString(current
, STR_TIMETABLE_CHANGE_SPEED
, 31, this, CS_NUMERAL
, QSF_NONE
);
830 case WID_VT_CLEAR_TIME
: {
831 // Clear waiting time.
832 const uint32 p1
= PackTimetableArgs(v
, this->sel_index
, false);
833 DoCommandP(0, p1
, 0, (_ctrl_pressed
? CMD_BULK_CHANGE_TIMETABLE
: CMD_CHANGE_TIMETABLE
) | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE
));
837 case WID_VT_CLEAR_SPEED
: {
838 // Clear max speed button.
839 const uint32 p1
= PackTimetableArgs(v
, this->sel_index
, true);
840 DoCommandP(0, p1
, UINT16_MAX
, (_ctrl_pressed
? CMD_BULK_CHANGE_TIMETABLE
: CMD_CHANGE_TIMETABLE
) | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE
));
844 case WID_VT_RESET_LATENESS
:
845 // Reset the vehicle's late counter.
846 DoCommandP(0, v
->index
, 0, CMD_SET_VEHICLE_ON_TIME
| CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE
));
849 case WID_VT_AUTOMATE
: {
850 // Automate the timetable.
852 if (!HasBit(v
->vehicle_flags
, VF_AUTOMATE_TIMETABLE
)) SetBit(p2
, 0);
853 if (_ctrl_pressed
) SetBit(p2
, 1);
854 DoCommandP(0, v
->index
, p2
, CMD_AUTOMATE_TIMETABLE
| CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE
));
858 case WID_VT_EXPECTED
:
859 this->show_expected
= !this->show_expected
;
862 case WID_VT_SHARED_ORDER_LIST
:
863 ShowVehicleListWindow(v
);
866 case WID_VT_TTSEP_MODE_DROPDOWN
: {
867 ShowDropDownMenu(this, TimetableSeparationDropdownOptions
, this->new_sep_settings
.mode
, WID_VT_TTSEP_MODE_DROPDOWN
, 0, 0);
871 case WID_VT_TTSEP_SET_PARAMETER
: {
872 this->query_widget
= WID_VT_TTSEP_SET_PARAMETER
;
873 SetDParam(0, (this->new_sep_settings
.mode
== TTS_MODE_MAN_N
) ? this->new_sep_settings
.num_veh
: this->new_sep_settings
.sep_ticks
);
874 ShowQueryString(STR_JUST_INT
, STR_TIMETABLE_CHANGE_TIME
, 31, this, CS_NUMERAL
, QSF_NONE
);
885 void UpdateVehicleSeparationSettings()
887 uint32 p2
= GB
<uint32
>(this->new_sep_settings
.mode
, 0, 3);
888 AB
<uint32
, uint
>(p2
, 3, 29, (this->new_sep_settings
.mode
== TTS_MODE_MAN_N
) ?
889 this->new_sep_settings
.num_veh
:
890 this->new_sep_settings
.sep_ticks
);
892 DoCommandP(0, this->vehicle
->FirstShared()->index
, p2
, CMD_REINIT_SEPARATION
);
895 void OnDropdownSelect(int widget
, int index
) override
897 assert(widget
== WID_VT_TTSEP_MODE_DROPDOWN
);
899 this->new_sep_settings
= this->vehicle
->GetTimetableSeparationSettings();
900 this->new_sep_settings
.mode
= static_cast<TTSepMode
>(index
);
902 UpdateVehicleSeparationSettings();
904 this->InvalidateData();
907 void OnQueryTextFinished(char* str
) override
909 if (str
== nullptr || StrEmpty(str
)) return;
911 switch (this->query_widget
) {
912 case WID_VT_CHANGE_TIME
:
913 case WID_VT_CHANGE_SPEED
: {
914 const Vehicle
* vehicle
= this->vehicle
;
916 const uint32 p1
= PackTimetableArgs(vehicle
, this->sel_index
, this->query_is_speed_query
);
918 uint64 speed
= StrEmpty(str
) ? 0 : strtoul(str
, nullptr, 10);
919 if (this->query_is_speed_query
) {
920 speed
= ConvertDisplaySpeedToKmhishSpeed(speed
);
923 const uint32 p2
= minu(speed
, UINT16_MAX
);
925 DoCommandP(0, p1
, p2
, (this->query_is_bulk_query
? CMD_BULK_CHANGE_TIMETABLE
: CMD_CHANGE_TIMETABLE
) | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE
));
929 case WID_VT_TTSEP_SET_PARAMETER
: {
930 const int value
= strtol(str
, nullptr, 10);
932 switch (this->new_sep_settings
.mode
) {
934 case TTS_MODE_BUFFERED_AUTO
:
939 this->new_sep_settings
.num_veh
= Clamp(value
, 1, 65535);
943 this->new_sep_settings
.sep_ticks
= Clamp(value
, 1, 65535);
950 UpdateVehicleSeparationSettings();
951 this->InvalidateData();
960 void OnResize() override
962 // Update the scroll bar.
963 this->vscroll
->SetCapacityFromWidget(this, WID_VT_TIMETABLE_PANEL
, WD_FRAMERECT_TOP
+ WD_FRAMERECT_BOTTOM
);
966 //! Update the selection state of the arrival/departure data
967 void UpdateSelectionStates()
969 this->GetWidget
<NWidgetStacked
>(WID_VT_ARRIVAL_DEPARTURE_SELECTION
)->SetDisplayedPlane(_settings_client
.gui
.timetable_arrival_departure
? 0 : SZSP_NONE
);
970 this->GetWidget
<NWidgetStacked
>(WID_VT_EXPECTED_SELECTION
)->SetDisplayedPlane(_settings_client
.gui
.timetable_arrival_departure
? 0 : 1);
973 void OnFocus(Window
* previously_focused_window
) override
975 if (HasFocusedVehicleChanged(this->window_number
, previously_focused_window
)) {
976 MarkAllRoutePathsDirty(this->vehicle
);
977 MarkAllRouteStepsDirty(this->vehicle
);
981 void OnFocusLost(Window
* newly_focused_window
) override
983 if (HasFocusedVehicleChanged(this->window_number
, newly_focused_window
)) {
984 MarkAllRoutePathsDirty(this->vehicle
);
985 MarkAllRouteStepsDirty(this->vehicle
);
989 const Vehicle
* GetVehicle() const
991 return this->vehicle
;
995 static const NWidgetPart _nested_timetable_widgets
[] = {
996 NWidget(NWID_HORIZONTAL
),
997 NWidget(WWT_CLOSEBOX
, COLOUR_GREY
),
998 NWidget(WWT_CAPTION
, COLOUR_GREY
, WID_VT_CAPTION
), SetDataTip(STR_TIMETABLE_TITLE
, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS
),
999 NWidget(WWT_PUSHTXTBTN
, COLOUR_GREY
, WID_VT_ORDER_VIEW
), SetMinimalSize(61, 14), SetDataTip( STR_TIMETABLE_ORDER_VIEW
, STR_TIMETABLE_ORDER_VIEW_TOOLTIP
),
1000 NWidget(WWT_SHADEBOX
, COLOUR_GREY
),
1001 NWidget(WWT_DEFSIZEBOX
, COLOUR_GREY
),
1002 NWidget(WWT_STICKYBOX
, COLOUR_GREY
),
1004 NWidget(NWID_HORIZONTAL
),
1005 NWidget(WWT_PANEL
, COLOUR_GREY
, WID_VT_TIMETABLE_PANEL
), SetMinimalSize(388, 82), SetResize(1, 10), SetDataTip(STR_NULL
, STR_TIMETABLE_TOOLTIP
), SetScrollbar(WID_VT_SCROLLBAR
), EndContainer(),
1006 NWidget(NWID_SELECTION
, INVALID_COLOUR
, WID_VT_ARRIVAL_DEPARTURE_SELECTION
),
1007 NWidget(WWT_PANEL
, COLOUR_GREY
, WID_VT_ARRIVAL_DEPARTURE_PANEL
), SetMinimalSize(85, 0), SetFill(0, 1), SetDataTip(STR_NULL
, STR_TIMETABLE_TOOLTIP
), SetScrollbar(WID_VT_SCROLLBAR
), EndContainer(),
1009 NWidget(NWID_VSCROLLBAR
, COLOUR_GREY
, WID_VT_SCROLLBAR
),
1010 NWidget(WWT_PANEL
, COLOUR_GREY
),
1011 NWidget(WWT_FRAME
, COLOUR_GREY
), SetDataTip(STR_TTSEPARATION_SETTINGS_DESC
, STR_NULL
), SetPadding(3),
1012 NWidget(WWT_DROPDOWN
, COLOUR_GREY
, WID_VT_TTSEP_MODE_DROPDOWN
), SetDataTip(STR_JUST_STRING
, STR_TIMETABLE_TOOLTIP
),
1013 NWidget(WWT_PUSHTXTBTN
, COLOUR_GREY
, WID_VT_TTSEP_SET_PARAMETER
), SetFill(1, 0), SetDataTip(STR_TTSEPARATION_SET_XX
, STR_TIMETABLE_TOOLTIP
),
1014 NWidget(WWT_PANEL
, COLOUR_GREY
, WID_VT_TTSEP_PANEL_TEXT
), SetFill(1, 1), SetResize(0, 1), SetMinimalSize(225, 44), EndContainer(),
1018 NWidget(WWT_PANEL
, COLOUR_GREY
, WID_VT_SUMMARY_PANEL
), SetMinimalSize(400, 22), SetResize(1, 0), EndContainer(),
1019 NWidget(NWID_HORIZONTAL
),
1020 NWidget(NWID_HORIZONTAL
, NC_EQUALSIZE
),
1021 NWidget(NWID_VERTICAL
, NC_EQUALSIZE
),
1022 NWidget(WWT_PUSHTXTBTN
, COLOUR_GREY
, WID_VT_CHANGE_TIME
), SetResize(1, 0), SetFill(1, 1), SetDataTip(STR_TIMETABLE_CHANGE_TIME
, STR_TIMETABLE_WAIT_TIME_TOOLTIP
),
1023 NWidget(WWT_PUSHTXTBTN
, COLOUR_GREY
, WID_VT_CLEAR_TIME
), SetResize(1, 0), SetFill(1, 1), SetDataTip(STR_TIMETABLE_CLEAR_TIME
, STR_TIMETABLE_CLEAR_TIME_TOOLTIP
),
1025 NWidget(NWID_VERTICAL
, NC_EQUALSIZE
),
1026 NWidget(WWT_PUSHTXTBTN
, COLOUR_GREY
, WID_VT_CHANGE_SPEED
), SetResize(1, 0), SetFill(1, 1), SetDataTip(STR_TIMETABLE_CHANGE_SPEED
, STR_TIMETABLE_CHANGE_SPEED_TOOLTIP
),
1027 NWidget(WWT_PUSHTXTBTN
, COLOUR_GREY
, WID_VT_CLEAR_SPEED
), SetResize(1, 0), SetFill(1, 1), SetDataTip(STR_TIMETABLE_CLEAR_SPEED
, STR_TIMETABLE_CLEAR_SPEED_TOOLTIP
),
1029 NWidget(NWID_VERTICAL
, NC_EQUALSIZE
),
1030 NWidget(WWT_PUSHTXTBTN
, COLOUR_GREY
, WID_VT_CONFIRM_ALL
), SetResize(1, 0), SetFill(1, 1), SetDataTip(STR_TIMETABLE_CONFIRM_ALL
, STR_TIMETABLE_CONFIRM_ALL_TOOLTIP
),
1031 NWidget(WWT_PUSHTXTBTN
, COLOUR_GREY
, WID_VT_RESET_LATENESS
), SetResize(1, 0), SetFill(1, 1), SetDataTip(STR_TIMETABLE_RESET_LATENESS
, STR_TIMETABLE_RESET_LATENESS_TOOLTIP
),
1033 NWidget(NWID_VERTICAL
, NC_EQUALSIZE
),
1034 NWidget(WWT_PUSHTXTBTN
, COLOUR_GREY
, WID_VT_AUTOMATE
), SetResize(1, 0), SetFill(1, 1), SetDataTip(STR_TIMETABLE_AUTOMATE
, STR_TIMETABLE_AUTOMATE_TOOLTIP
),
1035 NWidget(NWID_SELECTION
, INVALID_COLOUR
, WID_VT_EXPECTED_SELECTION
),
1036 NWidget(WWT_PUSHTXTBTN
, COLOUR_GREY
, WID_VT_EXPECTED
), SetResize(1, 0), SetFill(1, 1), SetDataTip(STR_BLACK_STRING
, STR_TIMETABLE_EXPECTED_TOOLTIP
),
1037 NWidget(WWT_PANEL
, COLOUR_GREY
), SetResize(1, 0), SetFill(1, 1), EndContainer(),
1041 NWidget(NWID_VERTICAL
, NC_EQUALSIZE
),
1042 NWidget(WWT_PUSHIMGBTN
, COLOUR_GREY
, WID_VT_SHARED_ORDER_LIST
), SetFill(0, 1), SetDataTip(SPR_SHARED_ORDERS_ICON
, STR_ORDERS_VEH_WITH_SHARED_ORDERS_LIST_TOOLTIP
),
1043 NWidget(WWT_RESIZEBOX
, COLOUR_GREY
), SetFill(0, 1),
1048 static WindowDesc
_timetable_desc(
1049 WDP_AUTO
, "view_vehicle_timetable", 400, 130,
1050 WC_VEHICLE_TIMETABLE
, WC_VEHICLE_VIEW
,
1052 _nested_timetable_widgets
, lengthof(_nested_timetable_widgets
)
1055 //! Show the timetable for a given vehicle.
1056 //! @param vehicle The vehicle to show the timetable for.
1057 void ShowTimetableWindow(const Vehicle
* vehicle
)
1059 DeleteWindowById(WC_VEHICLE_DETAILS
, vehicle
->index
, false);
1060 DeleteWindowById(WC_VEHICLE_ORDERS
, vehicle
->index
, false);
1061 AllocateWindowDescFront
<TimetableWindow
>(&_timetable_desc
, vehicle
->index
);