OpenTTD
timetable_gui.cpp
Go to the documentation of this file.
1 /* $Id: timetable_gui.cpp 26639 2014-06-10 16:29:03Z frosch $ */
2 
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  */
9 
12 #include "stdafx.h"
13 #include "command_func.h"
14 #include "gui.h"
15 #include "window_gui.h"
16 #include "window_func.h"
17 #include "textbuf_gui.h"
18 #include "strings_func.h"
19 #include "vehicle_base.h"
20 #include "string_func.h"
21 #include "gfx_func.h"
22 #include "company_func.h"
23 #include "date_func.h"
24 #include "date_gui.h"
25 #include "vehicle_gui.h"
26 #include "settings_type.h"
27 
29 
30 #include "table/sprites.h"
31 #include "table/strings.h"
32 
33 #include "safeguards.h"
34 
39 };
40 
47 void SetTimetableParams(int param1, int param2, Ticks ticks)
48 {
50  SetDParam(param1, STR_TIMETABLE_TICKS);
51  SetDParam(param2, ticks);
52  } else {
53  SetDParam(param1, STR_TIMETABLE_DAYS);
54  SetDParam(param2, ticks / DAY_TICKS);
55  }
56 }
57 
64 static bool CanDetermineTimeTaken(const Order *order, bool travelling)
65 {
66  /* Current order is conditional */
67  if (order->IsType(OT_CONDITIONAL) || order->IsType(OT_IMPLICIT)) return false;
68  /* No travel time and we have not already finished travelling */
69  if (travelling && !order->IsTravelTimetabled()) return false;
70  /* No wait time but we are loading at this timetabled station */
71  if (!travelling && !order->IsWaitTimetabled() && order->IsType(OT_GOTO_STATION) &&
73  return false;
74  }
75 
76  return true;
77 }
78 
79 
88 static void FillTimetableArrivalDepartureTable(const Vehicle *v, VehicleOrderID start, bool travelling, TimetableArrivalDeparture *table, Ticks offset)
89 {
90  assert(table != NULL);
91  assert(v->GetNumOrders() >= 2);
92  assert(start < v->GetNumOrders());
93 
94  Ticks sum = offset;
95  VehicleOrderID i = start;
96  const Order *order = v->GetOrder(i);
97 
98  /* Pre-initialize with unknown time */
99  for (int i = 0; i < v->GetNumOrders(); ++i) {
100  table[i].arrival = table[i].departure = INVALID_TICKS;
101  }
102 
103  /* Cyclically loop over all orders until we reach the current one again.
104  * As we may start at the current order, do a post-checking loop */
105  do {
106  /* Automatic orders don't influence the overall timetable;
107  * they just add some untimetabled entries, but the time till
108  * the next non-implicit order can still be known. */
109  if (!order->IsType(OT_IMPLICIT)) {
110  if (travelling || i != start) {
111  if (!CanDetermineTimeTaken(order, true)) return;
112  sum += order->GetTimetabledTravel();
113  table[i].arrival = sum;
114  }
115 
116  if (!CanDetermineTimeTaken(order, false)) return;
117  sum += order->GetTimetabledWait();
118  table[i].departure = sum;
119  }
120 
121  ++i;
122  order = order->next;
123  if (i >= v->GetNumOrders()) {
124  i = 0;
125  assert(order == NULL);
126  order = v->orders.list->GetFirstOrder();
127  }
128  } while (i != start);
129 
130  /* When loading at a scheduled station we still have to treat the
131  * travelling part of the first order. */
132  if (!travelling) {
133  if (!CanDetermineTimeTaken(order, true)) return;
134  sum += order->GetTimetabledTravel();
135  table[i].arrival = sum;
136  }
137 }
138 
139 
145 static void ChangeTimetableStartCallback(const Window *w, Date date)
146 {
147  DoCommandP(0, w->window_number, date, CMD_SET_TIMETABLE_START | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE));
148 }
149 
150 
152  int sel_index;
153  const Vehicle *vehicle;
157  Scrollbar *vscroll;
159 
161  Window(desc),
162  sel_index(-1),
163  vehicle(Vehicle::Get(window_number)),
164  show_expected(true)
165  {
166  this->CreateNestedTree();
167  this->vscroll = this->GetScrollbar(WID_VT_SCROLLBAR);
168  this->UpdateSelectionStates();
169  this->FinishInitNested(window_number);
170 
171  this->owner = this->vehicle->owner;
172  }
173 
181  {
183 
184  bool travelling = (!v->current_order.IsType(OT_LOADING) || v->current_order.GetNonStopType() == ONSF_STOP_EVERYWHERE);
185  Ticks start_time = _date_fract - v->current_order_time;
186 
187  FillTimetableArrivalDepartureTable(v, v->cur_real_order_index % v->GetNumOrders(), travelling, table, start_time);
188 
189  return (travelling && v->lateness_counter < 0);
190  }
191 
192  virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
193  {
194  switch (widget) {
197  this->deparr_time_width = GetStringBoundingBox(STR_JUST_DATE_TINY).width;
198  this->deparr_abbr_width = max(GetStringBoundingBox(STR_TIMETABLE_ARRIVAL_ABBREVIATION).width, GetStringBoundingBox(STR_TIMETABLE_DEPARTURE_ABBREVIATION).width);
199  size->width = WD_FRAMERECT_LEFT + this->deparr_abbr_width + 10 + this->deparr_time_width + WD_FRAMERECT_RIGHT;
200  /* FALL THROUGH */
203  resize->height = FONT_HEIGHT_NORMAL;
204  size->height = WD_FRAMERECT_TOP + 8 * resize->height + WD_FRAMERECT_BOTTOM;
205  break;
206 
209  break;
210  }
211  }
212 
213  int GetOrderFromTimetableWndPt(int y, const Vehicle *v)
214  {
215  int sel = (y - this->GetWidget<NWidgetBase>(WID_VT_TIMETABLE_PANEL)->pos_y - WD_FRAMERECT_TOP) / FONT_HEIGHT_NORMAL;
216 
217  if ((uint)sel >= this->vscroll->GetCapacity()) return INVALID_ORDER;
218 
219  sel += this->vscroll->GetPosition();
220 
221  return (sel < v->GetNumOrders() * 2 && sel >= 0) ? sel : INVALID_ORDER;
222  }
223 
229  virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
230  {
231  switch (data) {
232  case VIWD_AUTOREPLACE:
233  /* Autoreplace replaced the vehicle */
234  this->vehicle = Vehicle::Get(this->window_number);
235  break;
236 
238  /* Removed / replaced all orders (after deleting / sharing) */
239  if (this->sel_index == -1) break;
240 
241  this->DeleteChildWindows();
242  this->sel_index = -1;
243  break;
244 
245  case VIWD_MODIFY_ORDERS:
246  if (!gui_scope) break;
247  this->UpdateSelectionStates();
248  this->ReInit();
249  break;
250 
251  default: {
252  if (gui_scope) break; // only do this once; from command scope
253 
254  /* Moving an order. If one of these is INVALID_VEH_ORDER_ID, then
255  * the order is being created / removed */
256  if (this->sel_index == -1) break;
257 
258  VehicleOrderID from = GB(data, 0, 8);
259  VehicleOrderID to = GB(data, 8, 8);
260 
261  if (from == to) break; // no need to change anything
262 
263  /* if from == INVALID_VEH_ORDER_ID, one order was added; if to == INVALID_VEH_ORDER_ID, one order was removed */
264  uint old_num_orders = this->vehicle->GetNumOrders() - (uint)(from == INVALID_VEH_ORDER_ID) + (uint)(to == INVALID_VEH_ORDER_ID);
265 
266  VehicleOrderID selected_order = (this->sel_index + 1) / 2;
267  if (selected_order == old_num_orders) selected_order = 0; // when last travel time is selected, it belongs to order 0
268 
269  bool travel = HasBit(this->sel_index, 0);
270 
271  if (from != selected_order) {
272  /* Moving from preceding order? */
273  selected_order -= (int)(from <= selected_order);
274  /* Moving to preceding order? */
275  selected_order += (int)(to <= selected_order);
276  } else {
277  /* Now we are modifying the selected order */
278  if (to == INVALID_VEH_ORDER_ID) {
279  /* Deleting selected order */
280  this->DeleteChildWindows();
281  this->sel_index = -1;
282  break;
283  } else {
284  /* Moving selected order */
285  selected_order = to;
286  }
287  }
288 
289  /* recompute new sel_index */
290  this->sel_index = 2 * selected_order - (int)travel;
291  /* travel time of first order needs special handling */
292  if (this->sel_index == -1) this->sel_index = this->vehicle->GetNumOrders() * 2 - 1;
293  break;
294  }
295  }
296  }
297 
298 
299  virtual void OnPaint()
300  {
301  const Vehicle *v = this->vehicle;
302  int selected = this->sel_index;
303 
304  this->vscroll->SetCount(v->GetNumOrders() * 2);
305 
306  if (v->owner == _local_company) {
307  bool disable = true;
308  if (selected != -1) {
309  const Order *order = v->GetOrder(((selected + 1) / 2) % v->GetNumOrders());
310  if (selected % 2 == 1) {
311  disable = order != NULL && (order->IsType(OT_CONDITIONAL) || order->IsType(OT_IMPLICIT));
312  } else {
313  disable = order == NULL || ((!order->IsType(OT_GOTO_STATION) || (order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION)) && !order->IsType(OT_CONDITIONAL));
314  }
315  }
316  bool disable_speed = disable || selected % 2 != 1 || v->type == VEH_AIRCRAFT;
317 
320  this->SetWidgetDisabledState(WID_VT_CHANGE_SPEED, disable_speed);
321  this->SetWidgetDisabledState(WID_VT_CLEAR_SPEED, disable_speed);
323 
327  } else {
336  }
337 
339 
340  this->DrawWidgets();
341  }
342 
343  virtual void SetStringParameters(int widget) const
344  {
345  switch (widget) {
346  case WID_VT_CAPTION: SetDParam(0, this->vehicle->index); break;
347  case WID_VT_EXPECTED: SetDParam(0, this->show_expected ? STR_TIMETABLE_EXPECTED : STR_TIMETABLE_SCHEDULED); break;
348  }
349  }
350 
351  virtual void DrawWidget(const Rect &r, int widget) const
352  {
353  const Vehicle *v = this->vehicle;
354  int selected = this->sel_index;
355 
356  switch (widget) {
357  case WID_VT_TIMETABLE_PANEL: {
358  int y = r.top + WD_FRAMERECT_TOP;
359  int i = this->vscroll->GetPosition();
360  VehicleOrderID order_id = (i + 1) / 2;
361  bool final_order = false;
362 
363  bool rtl = _current_text_dir == TD_RTL;
364  SetDParamMaxValue(0, v->GetNumOrders(), 2);
365  int index_column_width = GetStringBoundingBox(STR_ORDER_INDEX).width + 2 * GetSpriteSize(rtl ? SPR_ARROW_RIGHT : SPR_ARROW_LEFT).width + 3;
366  int middle = rtl ? r.right - WD_FRAMERECT_RIGHT - index_column_width : r.left + WD_FRAMERECT_LEFT + index_column_width;
367 
368  const Order *order = v->GetOrder(order_id);
369  while (order != NULL) {
370  /* Don't draw anything if it extends past the end of the window. */
371  if (!this->vscroll->IsVisible(i)) break;
372 
373  if (i % 2 == 0) {
374  DrawOrderString(v, order, order_id, y, i == selected, true, r.left + WD_FRAMERECT_LEFT, middle, r.right - WD_FRAMERECT_RIGHT);
375 
376  order_id++;
377 
378  if (order_id >= v->GetNumOrders()) {
379  order = v->GetOrder(0);
380  final_order = true;
381  } else {
382  order = order->next;
383  }
384  } else {
385  StringID string;
386  TextColour colour = (i == selected) ? TC_WHITE : TC_BLACK;
387  if (order->IsType(OT_CONDITIONAL)) {
388  string = STR_TIMETABLE_NO_TRAVEL;
389  } else if (order->IsType(OT_IMPLICIT)) {
390  string = STR_TIMETABLE_NOT_TIMETABLEABLE;
391  colour = ((i == selected) ? TC_SILVER : TC_GREY) | TC_NO_SHADE;
392  } else if (!order->IsTravelTimetabled()) {
393  if (order->GetTravelTime() > 0) {
394  SetTimetableParams(0, 1, order->GetTravelTime());
395  string = order->GetMaxSpeed() != UINT16_MAX ?
396  STR_TIMETABLE_TRAVEL_FOR_SPEED_ESTIMATED :
397  STR_TIMETABLE_TRAVEL_FOR_ESTIMATED;
398  } else {
399  string = order->GetMaxSpeed() != UINT16_MAX ?
400  STR_TIMETABLE_TRAVEL_NOT_TIMETABLED_SPEED :
401  STR_TIMETABLE_TRAVEL_NOT_TIMETABLED;
402  }
403  } else {
404  SetTimetableParams(0, 1, order->GetTimetabledTravel());
405  string = order->GetMaxSpeed() != UINT16_MAX ?
406  STR_TIMETABLE_TRAVEL_FOR_SPEED : STR_TIMETABLE_TRAVEL_FOR;
407  }
408  SetDParam(2, order->GetMaxSpeed());
409 
410  DrawString(rtl ? r.left + WD_FRAMERECT_LEFT : middle, rtl ? middle : r.right - WD_FRAMERECT_LEFT, y, string, colour);
411 
412  if (final_order) break;
413  }
414 
415  i++;
416  y += FONT_HEIGHT_NORMAL;
417  }
418  break;
419  }
420 
422  /* Arrival and departure times are handled in an all-or-nothing approach,
423  * i.e. are only shown if we can calculate all times.
424  * Excluding order lists with only one order makes some things easier.
425  */
426  Ticks total_time = v->orders.list != NULL ? v->orders.list->GetTimetableDurationIncomplete() : 0;
427  if (total_time <= 0 || v->GetNumOrders() <= 1 || !HasBit(v->vehicle_flags, VF_TIMETABLE_STARTED)) break;
428 
430  const VehicleOrderID cur_order = v->cur_real_order_index % v->GetNumOrders();
431 
433 
434  int y = r.top + WD_FRAMERECT_TOP;
435 
436  bool show_late = this->show_expected && v->lateness_counter > DAY_TICKS;
437  Ticks offset = show_late ? 0 : -v->lateness_counter;
438 
439  bool rtl = _current_text_dir == TD_RTL;
440  int abbr_left = rtl ? r.right - WD_FRAMERECT_RIGHT - this->deparr_abbr_width : r.left + WD_FRAMERECT_LEFT;
441  int abbr_right = rtl ? r.right - WD_FRAMERECT_RIGHT : r.left + WD_FRAMERECT_LEFT + this->deparr_abbr_width;
442  int time_left = rtl ? r.left + WD_FRAMERECT_LEFT : r.right - WD_FRAMERECT_RIGHT - this->deparr_time_width;
443  int time_right = rtl ? r.left + WD_FRAMERECT_LEFT + this->deparr_time_width : r.right - WD_FRAMERECT_RIGHT;
444 
445  for (int i = this->vscroll->GetPosition(); i / 2 < v->GetNumOrders(); ++i) { // note: i is also incremented in the loop
446  /* Don't draw anything if it extends past the end of the window. */
447  if (!this->vscroll->IsVisible(i)) break;
448 
449  if (i % 2 == 0) {
450  if (arr_dep[i / 2].arrival != INVALID_TICKS) {
451  DrawString(abbr_left, abbr_right, y, STR_TIMETABLE_ARRIVAL_ABBREVIATION, i == selected ? TC_WHITE : TC_BLACK);
452  if (this->show_expected && i / 2 == earlyID) {
453  SetDParam(0, _date + arr_dep[i / 2].arrival / DAY_TICKS);
454  DrawString(time_left, time_right, y, STR_JUST_DATE_TINY, TC_GREEN);
455  } else {
456  SetDParam(0, _date + (arr_dep[i / 2].arrival + offset) / DAY_TICKS);
457  DrawString(time_left, time_right, y, STR_JUST_DATE_TINY,
458  show_late ? TC_RED : i == selected ? TC_WHITE : TC_BLACK);
459  }
460  }
461  } else {
462  if (arr_dep[i / 2].departure != INVALID_TICKS) {
463  DrawString(abbr_left, abbr_right, y, STR_TIMETABLE_DEPARTURE_ABBREVIATION, i == selected ? TC_WHITE : TC_BLACK);
464  SetDParam(0, _date + (arr_dep[i/2].departure + offset) / DAY_TICKS);
465  DrawString(time_left, time_right, y, STR_JUST_DATE_TINY,
466  show_late ? TC_RED : i == selected ? TC_WHITE : TC_BLACK);
467  }
468  }
469  y += FONT_HEIGHT_NORMAL;
470  }
471  break;
472  }
473 
474  case WID_VT_SUMMARY_PANEL: {
475  int y = r.top + WD_FRAMERECT_TOP;
476 
477  Ticks total_time = v->orders.list != NULL ? v->orders.list->GetTimetableDurationIncomplete() : 0;
478  if (total_time != 0) {
479  SetTimetableParams(0, 1, total_time);
480  DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, v->orders.list->IsCompleteTimetable() ? STR_TIMETABLE_TOTAL_TIME : STR_TIMETABLE_TOTAL_TIME_INCOMPLETE);
481  }
482  y += FONT_HEIGHT_NORMAL;
483 
484  if (v->timetable_start != 0) {
485  /* We are running towards the first station so we can start the
486  * timetable at the given time. */
487  SetDParam(0, STR_JUST_DATE_TINY);
488  SetDParam(1, v->timetable_start);
489  DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_TIMETABLE_STATUS_START_AT);
490  } else if (!HasBit(v->vehicle_flags, VF_TIMETABLE_STARTED)) {
491  /* We aren't running on a timetable yet, so how can we be "on time"
492  * when we aren't even "on service"/"on duty"? */
493  DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_TIMETABLE_STATUS_NOT_STARTED);
494  } else if (v->lateness_counter == 0 || (!_settings_client.gui.timetable_in_ticks && v->lateness_counter / DAY_TICKS == 0)) {
495  DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_TIMETABLE_STATUS_ON_TIME);
496  } else {
498  DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, v->lateness_counter < 0 ? STR_TIMETABLE_STATUS_EARLY : STR_TIMETABLE_STATUS_LATE);
499  }
500  break;
501  }
502  }
503  }
504 
505  static inline uint32 PackTimetableArgs(const Vehicle *v, uint selected, bool speed)
506  {
507  uint order_number = (selected + 1) / 2;
508  ModifyTimetableFlags mtf = (selected % 2 == 1) ? (speed ? MTF_TRAVEL_SPEED : MTF_TRAVEL_TIME) : MTF_WAIT_TIME;
509 
510  if (order_number >= v->GetNumOrders()) order_number = 0;
511 
512  return v->index | (order_number << 20) | (mtf << 28);
513  }
514 
515  virtual void OnClick(Point pt, int widget, int click_count)
516  {
517  const Vehicle *v = this->vehicle;
518 
519  switch (widget) {
520  case WID_VT_ORDER_VIEW: // Order view button
521  ShowOrdersWindow(v);
522  break;
523 
524  case WID_VT_TIMETABLE_PANEL: { // Main panel.
525  int selected = GetOrderFromTimetableWndPt(pt.y, v);
526 
527  this->DeleteChildWindows();
528  this->sel_index = (selected == INVALID_ORDER || selected == this->sel_index) ? -1 : selected;
529  break;
530  }
531 
532  case WID_VT_START_DATE: // Change the date that the timetable starts.
534  break;
535 
536  case WID_VT_CHANGE_TIME: { // "Wait For" button.
537  int selected = this->sel_index;
538  VehicleOrderID real = (selected + 1) / 2;
539 
540  if (real >= v->GetNumOrders()) real = 0;
541 
542  const Order *order = v->GetOrder(real);
543  StringID current = STR_EMPTY;
544 
545  if (order != NULL) {
546  uint time = (selected % 2 == 1) ? order->GetTravelTime() : order->GetWaitTime();
548 
549  if (time != 0) {
550  SetDParam(0, time);
551  current = STR_JUST_INT;
552  }
553  }
554 
555  this->query_is_speed_query = false;
556  ShowQueryString(current, STR_TIMETABLE_CHANGE_TIME, 31, this, CS_NUMERAL, QSF_ACCEPT_UNCHANGED);
557  break;
558  }
559 
560  case WID_VT_CHANGE_SPEED: { // Change max speed button.
561  int selected = this->sel_index;
562  VehicleOrderID real = (selected + 1) / 2;
563 
564  if (real >= v->GetNumOrders()) real = 0;
565 
566  StringID current = STR_EMPTY;
567  const Order *order = v->GetOrder(real);
568  if (order != NULL) {
569  if (order->GetMaxSpeed() != UINT16_MAX) {
571  current = STR_JUST_INT;
572  }
573  }
574 
575  this->query_is_speed_query = true;
576  ShowQueryString(current, STR_TIMETABLE_CHANGE_SPEED, 31, this, CS_NUMERAL, QSF_NONE);
577  break;
578  }
579 
580  case WID_VT_CLEAR_TIME: { // Clear waiting time.
581  uint32 p1 = PackTimetableArgs(v, this->sel_index, false);
582  DoCommandP(0, p1, 0, CMD_CHANGE_TIMETABLE | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE));
583  break;
584  }
585 
586  case WID_VT_CLEAR_SPEED: { // Clear max speed button.
587  uint32 p1 = PackTimetableArgs(v, this->sel_index, true);
588  DoCommandP(0, p1, UINT16_MAX, CMD_CHANGE_TIMETABLE | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE));
589  break;
590  }
591 
592  case WID_VT_RESET_LATENESS: // Reset the vehicle's late counter.
593  DoCommandP(0, v->index, 0, CMD_SET_VEHICLE_ON_TIME | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE));
594  break;
595 
596  case WID_VT_AUTOFILL: { // Autofill the timetable.
597  uint32 p2 = 0;
599  if (_ctrl_pressed) SetBit(p2, 1);
600  DoCommandP(0, v->index, p2, CMD_AUTOFILL_TIMETABLE | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE));
601  break;
602  }
603 
604  case WID_VT_EXPECTED:
605  this->show_expected = !this->show_expected;
606  break;
607 
609  ShowVehicleListWindow(v);
610  break;
611  }
612 
613  this->SetDirty();
614  }
615 
616  virtual void OnQueryTextFinished(char *str)
617  {
618  if (str == NULL) return;
619 
620  const Vehicle *v = this->vehicle;
621 
622  uint32 p1 = PackTimetableArgs(v, this->sel_index, this->query_is_speed_query);
623 
624  uint64 val = StrEmpty(str) ? 0 : strtoul(str, NULL, 10);
625  if (this->query_is_speed_query) {
627  } else {
629  }
630 
631  uint32 p2 = minu(val, UINT16_MAX);
632 
633  DoCommandP(0, p1, p2, CMD_CHANGE_TIMETABLE | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE));
634  }
635 
636  virtual void OnResize()
637  {
638  /* Update the scroll bar */
640  }
641 
646  {
647  this->GetWidget<NWidgetStacked>(WID_VT_ARRIVAL_DEPARTURE_SELECTION)->SetDisplayedPlane(_settings_client.gui.timetable_arrival_departure ? 0 : SZSP_NONE);
648  this->GetWidget<NWidgetStacked>(WID_VT_EXPECTED_SELECTION)->SetDisplayedPlane(_settings_client.gui.timetable_arrival_departure ? 0 : 1);
649  }
650 };
651 
652 static const NWidgetPart _nested_timetable_widgets[] = {
654  NWidget(WWT_CLOSEBOX, COLOUR_GREY),
655  NWidget(WWT_CAPTION, COLOUR_GREY, WID_VT_CAPTION), SetDataTip(STR_TIMETABLE_TITLE, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
656  NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_VT_ORDER_VIEW), SetMinimalSize(61, 14), SetDataTip( STR_TIMETABLE_ORDER_VIEW, STR_TIMETABLE_ORDER_VIEW_TOOLTIP),
657  NWidget(WWT_SHADEBOX, COLOUR_GREY),
658  NWidget(WWT_DEFSIZEBOX, COLOUR_GREY),
659  NWidget(WWT_STICKYBOX, COLOUR_GREY),
660  EndContainer(),
662  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(),
664  NWidget(WWT_PANEL, COLOUR_GREY, WID_VT_ARRIVAL_DEPARTURE_PANEL), SetMinimalSize(110, 0), SetFill(0, 1), SetDataTip(STR_NULL, STR_TIMETABLE_TOOLTIP), SetScrollbar(WID_VT_SCROLLBAR), EndContainer(),
665  EndContainer(),
667  EndContainer(),
668  NWidget(WWT_PANEL, COLOUR_GREY, WID_VT_SUMMARY_PANEL), SetMinimalSize(400, 22), SetResize(1, 0), EndContainer(),
672  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),
673  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),
674  EndContainer(),
676  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),
677  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),
678  EndContainer(),
680  NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_VT_START_DATE), SetResize(1, 0), SetFill(1, 1), SetDataTip(STR_TIMETABLE_STARTING_DATE, STR_TIMETABLE_STARTING_DATE_TOOLTIP),
681  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),
682  EndContainer(),
684  NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_VT_AUTOFILL), SetResize(1, 0), SetFill(1, 1), SetDataTip(STR_TIMETABLE_AUTOFILL, STR_TIMETABLE_AUTOFILL_TOOLTIP),
686  NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_VT_EXPECTED), SetResize(1, 0), SetFill(1, 1), SetDataTip(STR_BLACK_STRING, STR_TIMETABLE_EXPECTED_TOOLTIP),
687  NWidget(WWT_PANEL, COLOUR_GREY), SetResize(1, 0), SetFill(1, 1), EndContainer(),
688  EndContainer(),
689  EndContainer(),
690  EndContainer(),
692  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),
693  NWidget(WWT_RESIZEBOX, COLOUR_GREY), SetFill(0, 1),
694  EndContainer(),
695  EndContainer(),
696 };
697 
698 static WindowDesc _timetable_desc(
699  WDP_AUTO, "view_vehicle_timetable", 400, 130,
702  _nested_timetable_widgets, lengthof(_nested_timetable_widgets)
703 );
704 
710 {
713  AllocateWindowDescFront<TimetableWindow>(&_timetable_desc, v->index);
714 }