OpenTTD
timetable_gui.cpp
Go to the documentation of this file.
1 /* $Id$ */
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 != nullptr);
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 == nullptr);
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 
160  TimetableWindow(WindowDesc *desc, WindowNumber window_number) :
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  void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override
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  FALLTHROUGH;
201 
204  resize->height = FONT_HEIGHT_NORMAL;
205  size->height = WD_FRAMERECT_TOP + 8 * resize->height + WD_FRAMERECT_BOTTOM;
206  break;
207 
210  break;
211  }
212  }
213 
214  int GetOrderFromTimetableWndPt(int y, const Vehicle *v)
215  {
216  int sel = (y - this->GetWidget<NWidgetBase>(WID_VT_TIMETABLE_PANEL)->pos_y - WD_FRAMERECT_TOP) / FONT_HEIGHT_NORMAL;
217 
218  if ((uint)sel >= this->vscroll->GetCapacity()) return INVALID_ORDER;
219 
220  sel += this->vscroll->GetPosition();
221 
222  return (sel < v->GetNumOrders() * 2 && sel >= 0) ? sel : INVALID_ORDER;
223  }
224 
230  void OnInvalidateData(int data = 0, bool gui_scope = true) override
231  {
232  switch (data) {
233  case VIWD_AUTOREPLACE:
234  /* Autoreplace replaced the vehicle */
235  this->vehicle = Vehicle::Get(this->window_number);
236  break;
237 
239  /* Removed / replaced all orders (after deleting / sharing) */
240  if (this->sel_index == -1) break;
241 
242  this->DeleteChildWindows();
243  this->sel_index = -1;
244  break;
245 
246  case VIWD_MODIFY_ORDERS:
247  if (!gui_scope) break;
248  this->UpdateSelectionStates();
249  this->ReInit();
250  break;
251 
252  default: {
253  if (gui_scope) break; // only do this once; from command scope
254 
255  /* Moving an order. If one of these is INVALID_VEH_ORDER_ID, then
256  * the order is being created / removed */
257  if (this->sel_index == -1) break;
258 
259  VehicleOrderID from = GB(data, 0, 8);
260  VehicleOrderID to = GB(data, 8, 8);
261 
262  if (from == to) break; // no need to change anything
263 
264  /* if from == INVALID_VEH_ORDER_ID, one order was added; if to == INVALID_VEH_ORDER_ID, one order was removed */
265  uint old_num_orders = this->vehicle->GetNumOrders() - (uint)(from == INVALID_VEH_ORDER_ID) + (uint)(to == INVALID_VEH_ORDER_ID);
266 
267  VehicleOrderID selected_order = (this->sel_index + 1) / 2;
268  if (selected_order == old_num_orders) selected_order = 0; // when last travel time is selected, it belongs to order 0
269 
270  bool travel = HasBit(this->sel_index, 0);
271 
272  if (from != selected_order) {
273  /* Moving from preceding order? */
274  selected_order -= (int)(from <= selected_order);
275  /* Moving to preceding order? */
276  selected_order += (int)(to <= selected_order);
277  } else {
278  /* Now we are modifying the selected order */
279  if (to == INVALID_VEH_ORDER_ID) {
280  /* Deleting selected order */
281  this->DeleteChildWindows();
282  this->sel_index = -1;
283  break;
284  } else {
285  /* Moving selected order */
286  selected_order = to;
287  }
288  }
289 
290  /* recompute new sel_index */
291  this->sel_index = 2 * selected_order - (int)travel;
292  /* travel time of first order needs special handling */
293  if (this->sel_index == -1) this->sel_index = this->vehicle->GetNumOrders() * 2 - 1;
294  break;
295  }
296  }
297  }
298 
299 
300  void OnPaint() override
301  {
302  const Vehicle *v = this->vehicle;
303  int selected = this->sel_index;
304 
305  this->vscroll->SetCount(v->GetNumOrders() * 2);
306 
307  if (v->owner == _local_company) {
308  bool disable = true;
309  if (selected != -1) {
310  const Order *order = v->GetOrder(((selected + 1) / 2) % v->GetNumOrders());
311  if (selected % 2 == 1) {
312  disable = order != nullptr && (order->IsType(OT_CONDITIONAL) || order->IsType(OT_IMPLICIT));
313  } else {
314  disable = order == nullptr || ((!order->IsType(OT_GOTO_STATION) || (order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION)) && !order->IsType(OT_CONDITIONAL));
315  }
316  }
317  bool disable_speed = disable || selected % 2 != 1 || v->type == VEH_AIRCRAFT;
318 
319  this->SetWidgetDisabledState(WID_VT_CHANGE_TIME, disable);
320  this->SetWidgetDisabledState(WID_VT_CLEAR_TIME, disable);
321  this->SetWidgetDisabledState(WID_VT_CHANGE_SPEED, disable_speed);
322  this->SetWidgetDisabledState(WID_VT_CLEAR_SPEED, disable_speed);
323  this->SetWidgetDisabledState(WID_VT_SHARED_ORDER_LIST, !v->IsOrderListShared());
324 
325  this->SetWidgetDisabledState(WID_VT_START_DATE, v->orders.list == nullptr);
326  this->SetWidgetDisabledState(WID_VT_RESET_LATENESS, v->orders.list == nullptr);
327  this->SetWidgetDisabledState(WID_VT_AUTOFILL, v->orders.list == nullptr);
328  } else {
329  this->DisableWidget(WID_VT_START_DATE);
330  this->DisableWidget(WID_VT_CHANGE_TIME);
331  this->DisableWidget(WID_VT_CLEAR_TIME);
332  this->DisableWidget(WID_VT_CHANGE_SPEED);
333  this->DisableWidget(WID_VT_CLEAR_SPEED);
334  this->DisableWidget(WID_VT_RESET_LATENESS);
335  this->DisableWidget(WID_VT_AUTOFILL);
336  this->DisableWidget(WID_VT_SHARED_ORDER_LIST);
337  }
338 
339  this->SetWidgetLoweredState(WID_VT_AUTOFILL, HasBit(v->vehicle_flags, VF_AUTOFILL_TIMETABLE));
340 
341  this->DrawWidgets();
342  }
343 
344  void SetStringParameters(int widget) const override
345  {
346  switch (widget) {
347  case WID_VT_CAPTION: SetDParam(0, this->vehicle->index); break;
348  case WID_VT_EXPECTED: SetDParam(0, this->show_expected ? STR_TIMETABLE_EXPECTED : STR_TIMETABLE_SCHEDULED); break;
349  }
350  }
351 
352  void DrawWidget(const Rect &r, int widget) const override
353  {
354  const Vehicle *v = this->vehicle;
355  int selected = this->sel_index;
356 
357  switch (widget) {
358  case WID_VT_TIMETABLE_PANEL: {
359  int y = r.top + WD_FRAMERECT_TOP;
360  int i = this->vscroll->GetPosition();
361  VehicleOrderID order_id = (i + 1) / 2;
362  bool final_order = false;
363 
364  bool rtl = _current_text_dir == TD_RTL;
365  SetDParamMaxValue(0, v->GetNumOrders(), 2);
366  int index_column_width = GetStringBoundingBox(STR_ORDER_INDEX).width + 2 * GetSpriteSize(rtl ? SPR_ARROW_RIGHT : SPR_ARROW_LEFT).width + 3;
367  int middle = rtl ? r.right - WD_FRAMERECT_RIGHT - index_column_width : r.left + WD_FRAMERECT_LEFT + index_column_width;
368 
369  const Order *order = v->GetOrder(order_id);
370  while (order != nullptr) {
371  /* Don't draw anything if it extends past the end of the window. */
372  if (!this->vscroll->IsVisible(i)) break;
373 
374  if (i % 2 == 0) {
375  DrawOrderString(v, order, order_id, y, i == selected, true, r.left + WD_FRAMERECT_LEFT, middle, r.right - WD_FRAMERECT_RIGHT);
376 
377  order_id++;
378 
379  if (order_id >= v->GetNumOrders()) {
380  order = v->GetOrder(0);
381  final_order = true;
382  } else {
383  order = order->next;
384  }
385  } else {
386  StringID string;
387  TextColour colour = (i == selected) ? TC_WHITE : TC_BLACK;
388  if (order->IsType(OT_CONDITIONAL)) {
389  string = STR_TIMETABLE_NO_TRAVEL;
390  } else if (order->IsType(OT_IMPLICIT)) {
391  string = STR_TIMETABLE_NOT_TIMETABLEABLE;
392  colour = ((i == selected) ? TC_SILVER : TC_GREY) | TC_NO_SHADE;
393  } else if (!order->IsTravelTimetabled()) {
394  if (order->GetTravelTime() > 0) {
395  SetTimetableParams(0, 1, order->GetTravelTime());
396  string = order->GetMaxSpeed() != UINT16_MAX ?
397  STR_TIMETABLE_TRAVEL_FOR_SPEED_ESTIMATED :
398  STR_TIMETABLE_TRAVEL_FOR_ESTIMATED;
399  } else {
400  string = order->GetMaxSpeed() != UINT16_MAX ?
401  STR_TIMETABLE_TRAVEL_NOT_TIMETABLED_SPEED :
402  STR_TIMETABLE_TRAVEL_NOT_TIMETABLED;
403  }
404  } else {
405  SetTimetableParams(0, 1, order->GetTimetabledTravel());
406  string = order->GetMaxSpeed() != UINT16_MAX ?
407  STR_TIMETABLE_TRAVEL_FOR_SPEED : STR_TIMETABLE_TRAVEL_FOR;
408  }
409  SetDParam(2, order->GetMaxSpeed());
410 
411  DrawString(rtl ? r.left + WD_FRAMERECT_LEFT : middle, rtl ? middle : r.right - WD_FRAMERECT_LEFT, y, string, colour);
412 
413  if (final_order) break;
414  }
415 
416  i++;
417  y += FONT_HEIGHT_NORMAL;
418  }
419  break;
420  }
421 
423  /* Arrival and departure times are handled in an all-or-nothing approach,
424  * i.e. are only shown if we can calculate all times.
425  * Excluding order lists with only one order makes some things easier.
426  */
427  Ticks total_time = v->orders.list != nullptr ? v->orders.list->GetTimetableDurationIncomplete() : 0;
428  if (total_time <= 0 || v->GetNumOrders() <= 1 || !HasBit(v->vehicle_flags, VF_TIMETABLE_STARTED)) break;
429 
431  const VehicleOrderID cur_order = v->cur_real_order_index % v->GetNumOrders();
432 
433  VehicleOrderID earlyID = BuildArrivalDepartureList(v, arr_dep) ? cur_order : (VehicleOrderID)INVALID_VEH_ORDER_ID;
434 
435  int y = r.top + WD_FRAMERECT_TOP;
436 
437  bool show_late = this->show_expected && v->lateness_counter > DAY_TICKS;
438  Ticks offset = show_late ? 0 : -v->lateness_counter;
439 
440  bool rtl = _current_text_dir == TD_RTL;
441  int abbr_left = rtl ? r.right - WD_FRAMERECT_RIGHT - this->deparr_abbr_width : r.left + WD_FRAMERECT_LEFT;
442  int abbr_right = rtl ? r.right - WD_FRAMERECT_RIGHT : r.left + WD_FRAMERECT_LEFT + this->deparr_abbr_width;
443  int time_left = rtl ? r.left + WD_FRAMERECT_LEFT : r.right - WD_FRAMERECT_RIGHT - this->deparr_time_width;
444  int time_right = rtl ? r.left + WD_FRAMERECT_LEFT + this->deparr_time_width : r.right - WD_FRAMERECT_RIGHT;
445 
446  for (int i = this->vscroll->GetPosition(); i / 2 < v->GetNumOrders(); ++i) { // note: i is also incremented in the loop
447  /* Don't draw anything if it extends past the end of the window. */
448  if (!this->vscroll->IsVisible(i)) break;
449 
450  if (i % 2 == 0) {
451  if (arr_dep[i / 2].arrival != INVALID_TICKS) {
452  DrawString(abbr_left, abbr_right, y, STR_TIMETABLE_ARRIVAL_ABBREVIATION, i == selected ? TC_WHITE : TC_BLACK);
453  if (this->show_expected && i / 2 == earlyID) {
454  SetDParam(0, _date + arr_dep[i / 2].arrival / DAY_TICKS);
455  DrawString(time_left, time_right, y, STR_JUST_DATE_TINY, TC_GREEN);
456  } else {
457  SetDParam(0, _date + (arr_dep[i / 2].arrival + offset) / DAY_TICKS);
458  DrawString(time_left, time_right, y, STR_JUST_DATE_TINY,
459  show_late ? TC_RED : i == selected ? TC_WHITE : TC_BLACK);
460  }
461  }
462  } else {
463  if (arr_dep[i / 2].departure != INVALID_TICKS) {
464  DrawString(abbr_left, abbr_right, y, STR_TIMETABLE_DEPARTURE_ABBREVIATION, i == selected ? TC_WHITE : TC_BLACK);
465  SetDParam(0, _date + (arr_dep[i/2].departure + offset) / DAY_TICKS);
466  DrawString(time_left, time_right, y, STR_JUST_DATE_TINY,
467  show_late ? TC_RED : i == selected ? TC_WHITE : TC_BLACK);
468  }
469  }
470  y += FONT_HEIGHT_NORMAL;
471  }
472  break;
473  }
474 
475  case WID_VT_SUMMARY_PANEL: {
476  int y = r.top + WD_FRAMERECT_TOP;
477 
478  Ticks total_time = v->orders.list != nullptr ? v->orders.list->GetTimetableDurationIncomplete() : 0;
479  if (total_time != 0) {
480  SetTimetableParams(0, 1, total_time);
481  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);
482  }
483  y += FONT_HEIGHT_NORMAL;
484 
485  if (v->timetable_start != 0) {
486  /* We are running towards the first station so we can start the
487  * timetable at the given time. */
488  SetDParam(0, STR_JUST_DATE_TINY);
489  SetDParam(1, v->timetable_start);
490  DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_TIMETABLE_STATUS_START_AT);
491  } else if (!HasBit(v->vehicle_flags, VF_TIMETABLE_STARTED)) {
492  /* We aren't running on a timetable yet, so how can we be "on time"
493  * when we aren't even "on service"/"on duty"? */
494  DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_TIMETABLE_STATUS_NOT_STARTED);
495  } else if (v->lateness_counter == 0 || (!_settings_client.gui.timetable_in_ticks && v->lateness_counter / DAY_TICKS == 0)) {
496  DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_TIMETABLE_STATUS_ON_TIME);
497  } else {
499  DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, v->lateness_counter < 0 ? STR_TIMETABLE_STATUS_EARLY : STR_TIMETABLE_STATUS_LATE);
500  }
501  break;
502  }
503  }
504  }
505 
506  static inline uint32 PackTimetableArgs(const Vehicle *v, uint selected, bool speed)
507  {
508  uint order_number = (selected + 1) / 2;
509  ModifyTimetableFlags mtf = (selected % 2 == 1) ? (speed ? MTF_TRAVEL_SPEED : MTF_TRAVEL_TIME) : MTF_WAIT_TIME;
510 
511  if (order_number >= v->GetNumOrders()) order_number = 0;
512 
513  return v->index | (order_number << 20) | (mtf << 28);
514  }
515 
516  void OnClick(Point pt, int widget, int click_count) override
517  {
518  const Vehicle *v = this->vehicle;
519 
520  switch (widget) {
521  case WID_VT_ORDER_VIEW: // Order view button
522  ShowOrdersWindow(v);
523  break;
524 
525  case WID_VT_TIMETABLE_PANEL: { // Main panel.
526  int selected = GetOrderFromTimetableWndPt(pt.y, v);
527 
528  this->DeleteChildWindows();
529  this->sel_index = (selected == INVALID_ORDER || selected == this->sel_index) ? -1 : selected;
530  break;
531  }
532 
533  case WID_VT_START_DATE: // Change the date that the timetable starts.
535  break;
536 
537  case WID_VT_CHANGE_TIME: { // "Wait For" button.
538  int selected = this->sel_index;
539  VehicleOrderID real = (selected + 1) / 2;
540 
541  if (real >= v->GetNumOrders()) real = 0;
542 
543  const Order *order = v->GetOrder(real);
544  StringID current = STR_EMPTY;
545 
546  if (order != nullptr) {
547  uint time = (selected % 2 == 1) ? order->GetTravelTime() : order->GetWaitTime();
549 
550  if (time != 0) {
551  SetDParam(0, time);
552  current = STR_JUST_INT;
553  }
554  }
555 
556  this->query_is_speed_query = false;
557  ShowQueryString(current, STR_TIMETABLE_CHANGE_TIME, 31, this, CS_NUMERAL, QSF_ACCEPT_UNCHANGED);
558  break;
559  }
560 
561  case WID_VT_CHANGE_SPEED: { // Change max speed button.
562  int selected = this->sel_index;
563  VehicleOrderID real = (selected + 1) / 2;
564 
565  if (real >= v->GetNumOrders()) real = 0;
566 
567  StringID current = STR_EMPTY;
568  const Order *order = v->GetOrder(real);
569  if (order != nullptr) {
570  if (order->GetMaxSpeed() != UINT16_MAX) {
572  current = STR_JUST_INT;
573  }
574  }
575 
576  this->query_is_speed_query = true;
577  ShowQueryString(current, STR_TIMETABLE_CHANGE_SPEED, 31, this, CS_NUMERAL, QSF_NONE);
578  break;
579  }
580 
581  case WID_VT_CLEAR_TIME: { // Clear waiting time.
582  uint32 p1 = PackTimetableArgs(v, this->sel_index, false);
583  DoCommandP(0, p1, 0, CMD_CHANGE_TIMETABLE | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE));
584  break;
585  }
586 
587  case WID_VT_CLEAR_SPEED: { // Clear max speed button.
588  uint32 p1 = PackTimetableArgs(v, this->sel_index, true);
589  DoCommandP(0, p1, UINT16_MAX, CMD_CHANGE_TIMETABLE | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE));
590  break;
591  }
592 
593  case WID_VT_RESET_LATENESS: // Reset the vehicle's late counter.
594  DoCommandP(0, v->index, 0, CMD_SET_VEHICLE_ON_TIME | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE));
595  break;
596 
597  case WID_VT_AUTOFILL: { // Autofill the timetable.
598  uint32 p2 = 0;
600  if (_ctrl_pressed) SetBit(p2, 1);
601  DoCommandP(0, v->index, p2, CMD_AUTOFILL_TIMETABLE | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE));
602  break;
603  }
604 
605  case WID_VT_EXPECTED:
606  this->show_expected = !this->show_expected;
607  break;
608 
610  ShowVehicleListWindow(v);
611  break;
612  }
613 
614  this->SetDirty();
615  }
616 
617  void OnQueryTextFinished(char *str) override
618  {
619  if (str == nullptr) return;
620 
621  const Vehicle *v = this->vehicle;
622 
623  uint32 p1 = PackTimetableArgs(v, this->sel_index, this->query_is_speed_query);
624 
625  uint64 val = StrEmpty(str) ? 0 : strtoul(str, nullptr, 10);
626  if (this->query_is_speed_query) {
628  } else {
630  }
631 
632  uint32 p2 = minu(val, UINT16_MAX);
633 
634  DoCommandP(0, p1, p2, CMD_CHANGE_TIMETABLE | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE));
635  }
636 
637  void OnResize() override
638  {
639  /* Update the scroll bar */
641  }
642 
647  {
648  this->GetWidget<NWidgetStacked>(WID_VT_ARRIVAL_DEPARTURE_SELECTION)->SetDisplayedPlane(_settings_client.gui.timetable_arrival_departure ? 0 : SZSP_NONE);
649  this->GetWidget<NWidgetStacked>(WID_VT_EXPECTED_SELECTION)->SetDisplayedPlane(_settings_client.gui.timetable_arrival_departure ? 0 : 1);
650  }
651 };
652 
653 static const NWidgetPart _nested_timetable_widgets[] = {
655  NWidget(WWT_CLOSEBOX, COLOUR_GREY),
656  NWidget(WWT_CAPTION, COLOUR_GREY, WID_VT_CAPTION), SetDataTip(STR_TIMETABLE_TITLE, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
657  NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_VT_ORDER_VIEW), SetMinimalSize(61, 14), SetDataTip( STR_TIMETABLE_ORDER_VIEW, STR_TIMETABLE_ORDER_VIEW_TOOLTIP),
658  NWidget(WWT_SHADEBOX, COLOUR_GREY),
659  NWidget(WWT_DEFSIZEBOX, COLOUR_GREY),
660  NWidget(WWT_STICKYBOX, COLOUR_GREY),
661  EndContainer(),
663  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(),
665  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(),
666  EndContainer(),
668  EndContainer(),
669  NWidget(WWT_PANEL, COLOUR_GREY, WID_VT_SUMMARY_PANEL), SetMinimalSize(400, 22), SetResize(1, 0), EndContainer(),
673  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),
674  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),
675  EndContainer(),
677  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),
678  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),
679  EndContainer(),
681  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),
682  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),
683  EndContainer(),
685  NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_VT_AUTOFILL), SetResize(1, 0), SetFill(1, 1), SetDataTip(STR_TIMETABLE_AUTOFILL, STR_TIMETABLE_AUTOFILL_TOOLTIP),
687  NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_VT_EXPECTED), SetResize(1, 0), SetFill(1, 1), SetDataTip(STR_BLACK_STRING, STR_TIMETABLE_EXPECTED_TOOLTIP),
688  NWidget(WWT_PANEL, COLOUR_GREY), SetResize(1, 0), SetFill(1, 1), EndContainer(),
689  EndContainer(),
690  EndContainer(),
691  EndContainer(),
693  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),
694  NWidget(WWT_RESIZEBOX, COLOUR_GREY), SetFill(0, 1),
695  EndContainer(),
696  EndContainer(),
697 };
698 
699 static WindowDesc _timetable_desc(
700  WDP_AUTO, "view_vehicle_timetable", 400, 130,
703  _nested_timetable_widgets, lengthof(_nested_timetable_widgets)
704 );
705 
711 {
714  AllocateWindowDescFront<TimetableWindow>(&_timetable_desc, v->index);
715 }
Functions related to OTTD&#39;s strings.
change the timetable for a vehicle
Definition: command_type.h:329
Normal push-button (no toggle button) with image caption.
Definition: widget_type.h:105
Set max travel speed.
Definition: order_type.h:175
static uint minu(const uint a, const uint b)
Returns the minimum of two unsigned integers.
Definition: math_func.hpp:70
Set wait time.
Definition: order_type.h:173
static void ChangeTimetableStartCallback(const Window *w, Date date)
Callback for when a time has been chosen to start the time table.
static const int DAYS_IN_YEAR
days per year
Definition: date_type.h:31
uint16 GetTravelTime() const
Get the time in ticks a vehicle will probably take to reach the destination (timetabled or not)...
Definition: order_base.h:187
static NWidgetPart SetResize(int16 dx, int16 dy)
Widget part function for setting the resize step.
Definition: widget_type.h:930
void SetStringParameters(int widget) const override
Initialize string parameters for a widget.
set the vehicle on time feature (timetable)
Definition: command_type.h:330
static void FillTimetableArrivalDepartureTable(const Vehicle *v, VehicleOrderID start, bool travelling, TimetableArrivalDeparture *table, Ticks offset)
Fill the table with arrivals and departures.
High level window description.
Definition: window_gui.h:168
Scrollbar for the panel.
union Vehicle::@49 orders
The orders currently assigned to the vehicle.
static Titem * Get(size_t index)
Returns Titem with given index.
Definition: pool_type.hpp:246
bool show_expected
Whether we show expected arrival or scheduled.
Whether the vehicle has started running on the timetable yet.
Definition: vehicle_base.h:47
Functions related to dates.
Caption of the window.
bool timetable_arrival_departure
show arrivals and departures in vehicle timetables
Scrollbar data structure.
Definition: widget_type.h:589
static const VehicleOrderID INVALID_VEH_ORDER_ID
Invalid vehicle order index (sentinel)
Definition: order_type.h:23
Offset at top to draw the frame rectangular area.
Definition: window_gui.h:64
Other order modifications.
Definition: vehicle_gui.h:35
static T SetBit(T &x, const uint8 y)
Set a bit in a variable.
Horizontal container.
Definition: widget_type.h:75
static const Ticks INVALID_TICKS
Representation of an invalid number of ticks.
Definition: date_type.h:111
void ShowQueryString(StringID str, StringID caption, uint maxsize, Window *parent, CharSetFilter afilter, QueryStringFlags flags)
Show a query popup window with a textbox in it.
Definition: misc_gui.cpp:1121
OrderList * list
Pointer to the order list for this vehicle.
Definition: vehicle_base.h:321
bool IsCompleteTimetable() const
Checks whether all orders of the list have a filled timetable.
Definition: order_cmd.cpp:592
bool IsTravelTimetabled() const
Does this order have an explicit travel time set?
Definition: order_base.h:178
Resize box (normally at bottom-right of a window)
Definition: widget_type.h:68
uint32 current_order_time
How many ticks have passed since this order started.
Definition: base_consist.h:23
autofill the timetable
Definition: command_type.h:331
Vehicle data structure.
Definition: vehicle_base.h:212
Tindex index
Index of this pool item.
Definition: pool_type.hpp:147
static const int DAY_TICKS
1 day is 74 ticks; _date_fract used to be uint16 and incremented by 885.
Definition: date_type.h:30
Close box (at top-left of a window)
Definition: widget_type.h:69
return success even when the text didn&#39;t change
Definition: textbuf_gui.h:22
Stuff related to the text buffer GUI.
ModifyTimetableFlags
Enumeration for the data to set in CmdChangeTimetable.
Definition: order_type.h:172
int32 lateness_counter
How many ticks late (or early if negative) this vehicle is.
Definition: base_consist.h:24
#define AllocaM(T, num_elements)
alloca() has to be called in the parent function, so define AllocaM() as a macro
Definition: alloc_func.hpp:134
Clear time button.
static T max(const T a, const T b)
Returns the maximum of two values.
Definition: math_func.hpp:26
Year _cur_year
Current year, starting at 0.
Definition: date.cpp:26
Functions related to the vehicle&#39;s GUIs.
const Vehicle * vehicle
Vehicle monitored by the window.
void OnResize() override
Called after the window got resized.
Functions, definitions and such used only by the GUI.
The vehicle will stop at any station it passes except the destination.
Definition: order_type.h:77
void ShowTimetableWindow(const Vehicle *v)
Show the timetable for a given vehicle.
void OnPaint() override
The window must be repainted.
Order * next
Pointer to next order. If nullptr, end of list.
Definition: order_base.h:51
void SetCount(int num)
Sets the number of elements in the list.
Definition: widget_type.h:670
Partial widget specification to allow NWidgets to be written nested.
Definition: widget_type.h:910
Set travel time.
Definition: order_type.h:174
Data structure for an opened window.
Definition: window_gui.h:278
bool _ctrl_pressed
Is Ctrl pressed?
Definition: gfx.cpp:37
The vehicle will stop at any station it passes and the destination.
Definition: order_type.h:75
Autoreplace replaced the vehicle.
Definition: vehicle_gui.h:37
void SetTimetableParams(int param1, int param2, Ticks ticks)
Set the timetable parameters in the format as described by the setting.
Vehicle orders; Window numbers:
Definition: window_type.h:207
void OnQueryTextFinished(char *str) override
The query window opened from this window has closed.
Functions related to low-level strings.
Only numeric ones.
Definition: string_type.h:30
uint ConvertDisplaySpeedToKmhishSpeed(uint speed)
Convert the given display speed to the km/h-ish speed.
Definition: strings.cpp:759
Default window size box (at top-right of a window, between WWT_SHADEBOX and WWT_STICKYBOX) ...
Definition: widget_type.h:65
DateFract _date_fract
Fractional part of the day.
Definition: date.cpp:29
This window is used for construction; close it whenever changing company.
Definition: window_gui.h:210
Start date button.
bool IsType(OrderType type) const
Check whether this order is of the given type.
Definition: order_base.h:63
uint deparr_abbr_width
The width of the departure/arrival abbreviation.
#define FONT_HEIGHT_NORMAL
Height of characters in the normal (FS_NORMAL) font.
Definition: gfx_func.h:178
static NWidgetPart SetDataTip(uint32 data, StringID tip)
Widget part function for setting the data and tooltip.
Definition: widget_type.h:1014
Functions related to the gfx engine.
ClientSettings _settings_client
The current settings for this game.
Definition: settings.cpp:80
Types related to global configuration settings.
static NWidgetPart SetMinimalSize(int16 x, int16 y)
Widget part function for setting the minimal size.
Definition: widget_type.h:947
Definition of base types and functions in a cross-platform compatible way.
Removed / replaced all orders (after deleting / sharing).
Definition: vehicle_gui.h:34
A number of safeguards to prevent using unsafe methods.
TextColour
Colour of the strings, see _string_colourmap in table/string_colours.h or docs/ottd-colourtext-palett...
Definition: gfx_type.h:247
Normal push-button (no toggle button) with text caption.
Definition: widget_type.h:104
Clear speed limit button.
Simple depressed panel.
Definition: widget_type.h:50
void ShowSetDateWindow(Window *parent, int window_number, Date initial_date, Year min_year, Year max_year, SetDateCallback *callback)
Create the new &#39;set date&#39; window.
Definition: date_gui.cpp:215
Autofill button.
Whether the vehicle should fill in the timetable automatically.
Definition: vehicle_base.h:48
Vehicle view; Window numbers:
Definition: window_type.h:334
set the date that a timetable should start
Definition: command_type.h:332
bool IsWaitTimetabled() const
Does this order have an explicit wait time set?
Definition: order_base.h:176
uint16 GetTimetabledTravel() const
Get the time in ticks a vehicle should take to reach the destination or 0 if it&#39;s not timetabled...
Definition: order_base.h:183
Ticks departure
The departure time.
Order * GetFirstOrder() const
Get the first order of the order chain.
Definition: order_base.h:290
static NWidgetPart NWidget(WidgetType tp, Colours col, int16 idx=-1)
Widget part function for starting a new &#39;real&#39; widget.
Definition: widget_type.h:1114
Vehicle timetable; Window numbers:
Definition: window_type.h:219
Offset at bottom to draw the frame rectangular area.
Definition: window_gui.h:65
int DrawString(int left, int right, int top, const char *str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
Draw string, possibly truncated to make it fit in its allocated space.
Definition: gfx.cpp:499
uint deparr_time_width
The width of the departure/arrival time.
void OnClick(Point pt, int widget, int click_count) override
A click with the left mouse button has been made on the window.
static bool BuildArrivalDepartureList(const Vehicle *v, TimetableArrivalDeparture *table)
Build the arrival-departure list for a given vehicle.
Owner owner
Which company owns the vehicle?
Definition: vehicle_base.h:273
bool DoCommandP(const CommandContainer *container, bool my_cmd)
Shortcut for the long DoCommandP when having a container with the data.
Definition: command.cpp:534
#define lengthof(x)
Return the length of an fixed size array.
Definition: depend.cpp:42
Display plane with zero size in both directions (none filling and resizing).
Definition: widget_type.h:390
Change time button.
uint32 StringID
Numeric value that represents a string, independent of the selected language.
Definition: strings_type.h:18
Date timetable_start
When the vehicle is supposed to start the timetable.
Definition: base_consist.h:25
bool query_is_speed_query
The currently open query window is a speed query and not a time query.
bool IsVisible(uint16 item) const
Checks whether given current item is visible in the list.
Definition: widget_type.h:641
Dimension GetStringBoundingBox(const char *str, FontSize start_fontsize)
Return the string dimension in pixels.
Definition: gfx.cpp:698
uint16 GetWaitTime() const
Get the time in ticks a vehicle will probably wait at the destination (timetabled or not)...
Definition: order_base.h:185
void DeleteWindowById(WindowClass cls, WindowNumber number, bool force)
Delete a window by its class and window number (if it is open).
Definition: window.cpp:1146
Disable/hide the arrival departure panel.
Toggle between expected and scheduled arrivals.
Functions related to companies.
uint16 GetTimetabledWait() const
Get the time in ticks a vehicle should wait at the destination or 0 if it&#39;s not timetabled.
Definition: order_base.h:181
Panel with the expected/scheduled arrivals.
Functions related to the graphical selection of a date.
bool timetable_in_ticks
whether to show the timetable in ticks rather than days
uint ConvertKmhishSpeedToDisplaySpeed(uint speed)
Convert the given km/h-ish speed to the display speed.
Definition: strings.cpp:749
Show the shared order list.
static const OrderID INVALID_ORDER
Invalid order (sentinel)
Definition: order_type.h:28
GUISettings gui
settings related to the GUI
Ticks GetTimetableDurationIncomplete() const
Gets the known duration of the vehicles timetable even if the timetable is not complete.
Definition: order_base.h:370
Window caption (window title between closebox and stickybox)
Definition: widget_type.h:61
Base class for all vehicles.
static bool StrEmpty(const char *s)
Check if a string buffer is empty.
Definition: string_func.h:59
TextDirection _current_text_dir
Text direction of the currently selected language.
Definition: strings.cpp:50
static bool CanDetermineTimeTaken(const Order *order, bool travelling)
Check whether it is possible to determine how long the order takes.
void SetDParamMaxValue(uint n, uint64 max_value, uint min_count, FontSize size)
Set DParam n to some number that is suitable for string size computations.
Definition: strings.cpp:106
int32 Ticks
The type to store ticks in.
Definition: date_type.h:18
Vertical container.
Definition: widget_type.h:77
static T abs(const T a)
Returns the absolute value of (scalar) variable.
Definition: math_func.hpp:83
static NWidgetPart EndContainer()
Widget part function for denoting the end of a container (horizontal, vertical, WWT_FRAME, WWT_INSET, or WWT_PANEL).
Definition: widget_type.h:999
static uint GB(const T x, const uint8 s, const uint8 n)
Fetch n bits from x, started at bit s.
Do not add shading to this text colour.
Definition: gfx_type.h:271
VehicleType type
Type of vehicle.
Definition: vehicle_type.h:54
Reset lateness button.
void UpdateSelectionStates()
Update the selection state of the arrival/departure data.
Timetable panel.
Vehicle details; Window numbers:
Definition: window_type.h:195
Functions related to commands.
Coordinates of a point in 2D.
Dimension GetSpriteSize(SpriteID sprid, Point *offset, ZoomLevel zoom)
Get the size of a sprite.
Definition: gfx.cpp:767
uint16 GetCapacity() const
Gets the number of visible elements of the scrollbar.
Definition: widget_type.h:622
Index of the small font in the font tables.
Definition: gfx_type.h:205
Order * GetOrder(int index) const
Returns order &#39;index&#39; of a vehicle or nullptr when it doesn&#39;t exists.
Definition: vehicle_base.h:860
VehicleOrderID GetNumOrders() const
Get the number of orders this vehicle has.
Definition: vehicle_base.h:687
int32 Date
The type to store our dates in.
Definition: date_type.h:16
Aircraft vehicle type.
Definition: vehicle_type.h:29
bool IsOrderListShared() const
Check if we share our orders with another vehicle.
Definition: vehicle_base.h:681
Offset at right to draw the frame rectangular area.
Definition: window_gui.h:63
Sticky box (at top-right of a window, after WWT_DEFSIZEBOX)
Definition: widget_type.h:66
Disable/hide the expected selection button.
uint16 vehicle_flags
Used for gradual loading and other miscellaneous things (.
Definition: base_consist.h:32
byte VehicleOrderID
The index of an order within its current vehicle (not pool related)
Definition: order_type.h:17
static NWidgetPart SetFill(uint fill_x, uint fill_y)
Widget part function for setting filling.
Definition: widget_type.h:983
static bool HasBit(const T x, const uint8 y)
Checks if a bit in a value is set.
#define CMD_MSG(x)
Used to combine a StringID with the command.
Definition: command_type.h:370
int32 WindowNumber
Number to differentiate different windows of the same class.
Definition: window_type.h:707
void SetCapacityFromWidget(Window *w, int widget, int padding=0)
Set capacity of visible elements from the size and resize properties of a widget. ...
Definition: widget.cpp:1973
Specification of a rectangle with absolute coordinates of all edges.
Vertical scrollbar.
Definition: widget_type.h:84
uint16 GetMaxSpeed() const
Get the maxmimum speed in km-ish/h a vehicle is allowed to reach on the way to the destination...
Definition: order_base.h:194
Text is written right-to-left by default.
Definition: strings_type.h:26
WindowNumber window_number
Window number within the window class.
Definition: window_gui.h:314
Window functions not directly related to making/drawing windows.
Find a place automatically.
Definition: window_gui.h:156
void DrawOrderString(const Vehicle *v, const Order *order, int order_index, int y, bool selected, bool timetable, int left, int middle, int right)
Draws an order in order or timetable GUI.
Definition: order_gui.cpp:215
Stacked widgets, only one visible at a time (eg in a panel with tabs).
Definition: widget_type.h:80
GUI functions that shouldn&#39;t be here.
Container for the arrival/departure dates of a vehicle.
Ticks arrival
The arrival time.
Date _date
Current date in days (day counter)
Definition: date.cpp:28
void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override
Update size and resize step of a widget in the window.
static NWidgetPart SetScrollbar(int index)
Attach a scrollbar to a widget.
Definition: widget_type.h:1095
CompanyID _local_company
Company controlled by the human player at this client. Can also be COMPANY_SPECTATOR.
Definition: company_cmd.cpp:46
void DrawWidget(const Rect &r, int widget) const override
Draw the contents of a nested widget.
VehicleOrderID cur_real_order_index
The index to the current real (non-implicit) order.
Definition: base_consist.h:29
Dimensions (a width and height) of a rectangle in 2D.
Value of the NCB_EQUALSIZE flag.
Definition: widget_type.h:429
Offset at left to draw the frame rectangular area.
Definition: window_gui.h:62
static const Year MAX_YEAR
MAX_YEAR, nicely rounded value of the number of years that can be encoded in a single 32 bits date...
Definition: date_type.h:94
Change speed limit button.
This file contains all sprite-related enums and defines.
Shade box (at top-right of a window, between WWT_DEBUGBOX and WWT_DEFSIZEBOX)
Definition: widget_type.h:64
OrderNonStopFlags GetNonStopType() const
At which stations must we stop?
Definition: order_base.h:133
Order current_order
The current order (+ status, like: loading)
Definition: vehicle_base.h:318
void OnInvalidateData(int data=0, bool gui_scope=true) override
Some data on this window has become invalid.
Types related to the timetable widgets.
uint16 GetPosition() const
Gets the position of the first visible element in the list.
Definition: widget_type.h:631
static void SetDParam(uint n, uint64 v)
Set a string parameter v at index n in the global string parameter array.
Definition: strings_func.h:201