Files @ r8781:bd977c904a17
Branch filter:

Location: cpp/openttd-patchpack/source/src/timetable_gui.cpp - annotation

bjarni
(svn r12482) -Fix: [build train] we don't need to have a depot in order to just check the price of a rail vehicle so don't check for compatible rails on the tile either
also wagons will only need tracks they can drive on in order to be build so there is no reason to check for power in the depot
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r8116:df67d3c5e4fd
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r8107:82461791b7a2
r8107:82461791b7a2
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r8114:866ed489ed98
r8144:1432edd15267
r8214:9a3935f9ef4e
r8224:194097dc7288
r8254:b96be9ebfc6e
r8270:c2ec53375c9d
r6981:8e1c715f9e05
r8264:d493cb51fe8a
r8264:d493cb51fe8a
r8722:c0369f37b042
r8722:c0369f37b042
r8722:c0369f37b042
r8745:257109de999e
r8722:c0369f37b042
r8722:c0369f37b042
r8722:c0369f37b042
r8722:c0369f37b042
r8722:c0369f37b042
r8722:c0369f37b042
r8722:c0369f37b042
r8722:c0369f37b042
r8722:c0369f37b042
r8722:c0369f37b042
r8722:c0369f37b042
r8722:c0369f37b042
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r8082:9d654f9b59ea
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r8722:c0369f37b042
r8722:c0369f37b042
r6981:8e1c715f9e05
r8722:c0369f37b042
r8722:c0369f37b042
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r8302:06593768326c
r6981:8e1c715f9e05
r8722:c0369f37b042
r8722:c0369f37b042
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r8722:c0369f37b042
r8722:c0369f37b042
r6981:8e1c715f9e05
r8722:c0369f37b042
r8722:c0369f37b042
r8722:c0369f37b042
r8722:c0369f37b042
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r8722:c0369f37b042
r7066:7459a71fc283
r7049:0c15b0ef5319
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r7312:7037b4794eab
r7312:7037b4794eab
r7312:7037b4794eab
r7312:7037b4794eab
r6981:8e1c715f9e05
r8424:8ec8538eb70c
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r8302:06593768326c
r8424:8ec8538eb70c
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r8302:06593768326c
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r8302:06593768326c
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r7824:e4e35ff9ee2c
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r7824:e4e35ff9ee2c
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r8722:c0369f37b042
r6981:8e1c715f9e05
r7073:20c5bc176074
r7073:20c5bc176074
r7073:20c5bc176074
r7073:20c5bc176074
r7073:20c5bc176074
r7073:20c5bc176074
r7073:20c5bc176074
r8302:06593768326c
r7073:20c5bc176074
r7073:20c5bc176074
r7073:20c5bc176074
r7073:20c5bc176074
r7824:e4e35ff9ee2c
r7073:20c5bc176074
r7073:20c5bc176074
r7073:20c5bc176074
r7073:20c5bc176074
r6981:8e1c715f9e05
r7824:e4e35ff9ee2c
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r7824:e4e35ff9ee2c
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r8745:257109de999e
r8745:257109de999e
r8745:257109de999e
r8745:257109de999e
r8722:c0369f37b042
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r8082:9d654f9b59ea
r6981:8e1c715f9e05
r8082:9d654f9b59ea
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r8082:9d654f9b59ea
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r8722:c0369f37b042
r8082:9d654f9b59ea
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r8722:c0369f37b042
r8082:9d654f9b59ea
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r8722:c0369f37b042
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r7066:7459a71fc283
r8722:c0369f37b042
r7928:a80e7e05d6c5
r7066:7459a71fc283
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r8082:9d654f9b59ea
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r8722:c0369f37b042
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r8722:c0369f37b042
r8745:257109de999e
r8745:257109de999e
r8722:c0369f37b042
r6981:8e1c715f9e05
r8722:c0369f37b042
r8722:c0369f37b042
r6981:8e1c715f9e05
r8722:c0369f37b042
r6981:8e1c715f9e05
r8722:c0369f37b042
r8722:c0369f37b042
r8722:c0369f37b042
r8722:c0369f37b042
r6981:8e1c715f9e05
r8722:c0369f37b042
r8722:c0369f37b042
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r7341:72820788b006
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r6981:8e1c715f9e05
r7057:aead86f3e967
r6981:8e1c715f9e05
r8082:9d654f9b59ea
r6981:8e1c715f9e05
r6981:8e1c715f9e05
/* $Id$ */

/** @file timetable_gui.cpp */

#include "stdafx.h"
#include "openttd.h"
#include "variables.h"
#include "command_func.h"
#include "engine.h"
#include "gui.h"
#include "window_gui.h"
#include "textbuf_gui.h"
#include "cargotype.h"
#include "depot.h"
#include "strings_func.h"
#include "vehicle_base.h"
#include "string_func.h"
#include "gfx_func.h"
#include "player_func.h"
#include "settings_type.h"

#include "table/strings.h"

enum TimetableViewWindowWidgets {
	TTV_WIDGET_CLOSEBOX = 0,
	TTV_CAPTION,
	TTV_ORDER_VIEW,
	TTV_STICKY,
	TTV_TIMETABLE_PANEL,
	TTV_SCROLLBAR,
	TTV_SUMMARY_PANEL,
	TTV_CHANGE_TIME,
	TTV_CLEAR_TIME,
	TTV_RESET_LATENESS,
	TTV_AUTOFILL,
	TTV_EMPTY,
	TTV_RESIZE,
};

static int GetOrderFromTimetableWndPt(Window *w, int y, const Vehicle *v)
{
	/*
	 * Calculation description:
	 * 15 = 14 (w->widget[ORDER_WIDGET_ORDER_LIST].top) + 1 (frame-line)
	 * 10 = order text hight
	 */
	int sel = (y - 15) / 10;

	if ((uint)sel >= w->vscroll.cap) return INVALID_ORDER;

	sel += w->vscroll.pos;

	return (sel <= v->num_orders * 2 && sel >= 0) ? sel : INVALID_ORDER;
}

static inline void SetTimetableParams(int param1, int param2, uint32 time)
{
	if (_patches.timetable_in_ticks) {
		SetDParam(param1, STR_TIMETABLE_TICKS);
		SetDParam(param2, time);
	} else {
		SetDParam(param1, STR_TIMETABLE_DAYS);
		SetDParam(param2, time / DAY_TICKS);
	}
}

static void DrawTimetableWindow(Window *w)
{
	const Vehicle *v = GetVehicle(w->window_number);
	int selected = WP(w, order_d).sel;

	SetVScrollCount(w, v->num_orders * 2);

	if (v->owner == _local_player) {
		if (selected == -1) {
			w->DisableWidget(TTV_CHANGE_TIME);
			w->DisableWidget(TTV_CLEAR_TIME);
		} else if (selected % 2 == 1) {
			w->EnableWidget(TTV_CHANGE_TIME);
			w->EnableWidget(TTV_CLEAR_TIME);
		} else {
			const Order *order = GetVehicleOrder(v, (selected + 1) / 2);
			bool disable = order == NULL || order->type != OT_GOTO_STATION || (_patches.new_nonstop && (order->flags & OFB_NON_STOP));

			w->SetWidgetDisabledState(TTV_CHANGE_TIME, disable);
			w->SetWidgetDisabledState(TTV_CLEAR_TIME, disable);
		}

		w->EnableWidget(TTV_RESET_LATENESS);
		w->EnableWidget(TTV_AUTOFILL);
	} else {
		w->DisableWidget(TTV_CHANGE_TIME);
		w->DisableWidget(TTV_CLEAR_TIME);
		w->DisableWidget(TTV_RESET_LATENESS);
		w->DisableWidget(TTV_AUTOFILL);
	}

	w->SetWidgetLoweredState(TTV_AUTOFILL, HasBit(v->vehicle_flags, VF_AUTOFILL_TIMETABLE));

	SetDParam(0, v->index);
	DrawWindowWidgets(w);

	int y = 15;
	int i = w->vscroll.pos;
	VehicleOrderID order_id = (i + 1) / 2;
	bool final_order = false;

	const Order *order = GetVehicleOrder(v, order_id);

	while (order != NULL) {
		/* Don't draw anything if it extends past the end of the window. */
		if (i - w->vscroll.pos >= w->vscroll.cap) break;

		if (i % 2 == 0) {
			SetDParam(2, STR_EMPTY);

			switch (order->type) {
				case OT_DUMMY:
					SetDParam(0, STR_INVALID_ORDER);
					break;

				case OT_GOTO_STATION:
					SetDParam(0, (order->flags & OFB_NON_STOP) ? STR_880A_GO_NON_STOP_TO : STR_8806_GO_TO);
					SetDParam(1, order->dest);

					if (order->wait_time > 0) {
						SetDParam(2, STR_TIMETABLE_STAY_FOR);
						SetTimetableParams(3, 4, order->wait_time);
					}

					break;

				case OT_GOTO_DEPOT: {
					StringID string = STR_EMPTY;

					if (v->type == VEH_AIRCRAFT) {
						string = STR_GO_TO_AIRPORT_HANGAR;
						SetDParam(1, order->dest);
					} else {
						SetDParam(1, GetDepot(order->dest)->town_index);

						switch (v->type) {
							case VEH_TRAIN: string = (order->flags & OFB_NON_STOP) ? STR_880F_GO_NON_STOP_TO_TRAIN_DEPOT : STR_GO_TO_TRAIN_DEPOT; break;
							case VEH_ROAD:  string = STR_GO_TO_ROADVEH_DEPOT; break;
							case VEH_SHIP:  string = STR_GO_TO_SHIP_DEPOT; break;
							default: break;
						}
					}

					if (order->flags & OFB_FULL_LOAD) string++; // Service at orders

					SetDParam(0, string);
				} break;

				case OT_GOTO_WAYPOINT:
					SetDParam(0, (order->flags & OFB_NON_STOP) ? STR_GO_NON_STOP_TO_WAYPOINT : STR_GO_TO_WAYPOINT);
					SetDParam(1, order->dest);
					break;

				default: break;
			}

			DrawString(2, y, STR_TIMETABLE_GO_TO, (i == selected) ? TC_WHITE : TC_BLACK);

			order_id++;

			if (order_id >= v->num_orders) {
				order = GetVehicleOrder(v, 0);
				final_order = true;
			} else {
				order = order->next;
			}
		} else {
			StringID string;

			if (order->travel_time == 0) {
				string = STR_TIMETABLE_TRAVEL_NOT_TIMETABLED;
			} else {
				SetTimetableParams(0, 1, order->travel_time);
				string = STR_TIMETABLE_TRAVEL_FOR;
			}

			DrawString(12, y, string, (i == selected) ? TC_WHITE : TC_BLACK);

			if (final_order) break;
		}

		i++;
		y += 10;
	}

	y = w->widget[TTV_SUMMARY_PANEL].top + 1;

	{
		uint total_time = 0;
		bool complete = true;

		for (const Order *order = GetVehicleOrder(v, 0); order != NULL; order = order->next) {
			total_time += order->travel_time + order->wait_time;
			if (order->travel_time == 0) complete = false;
			if (order->wait_time == 0 && order->type == OT_GOTO_STATION && !(_patches.new_nonstop && (order->flags & OFB_NON_STOP))) complete = false;
		}

		if (total_time != 0) {
			SetTimetableParams(0, 1, total_time);
			DrawString(2, y, complete ? STR_TIMETABLE_TOTAL_TIME : STR_TIMETABLE_TOTAL_TIME_INCOMPLETE, TC_BLACK);
		}
	}
	y += 10;

	if (v->lateness_counter == 0 || (!_patches.timetable_in_ticks && v->lateness_counter / DAY_TICKS == 0)) {
		DrawString(2, y, STR_TIMETABLE_STATUS_ON_TIME, TC_BLACK);
	} else {
		SetTimetableParams(0, 1, abs(v->lateness_counter));
		DrawString(2, y, v->lateness_counter < 0 ? STR_TIMETABLE_STATUS_EARLY : STR_TIMETABLE_STATUS_LATE, TC_BLACK);
	}
}

static inline uint32 PackTimetableArgs(const Vehicle *v, uint selected)
{
	uint order_number = (selected + 1) / 2;
	uint is_journey   = (selected % 2 == 1) ? 1 : 0;

	if (order_number >= v->num_orders) order_number = 0;

	return v->index | (order_number << 16) | (is_journey << 24);
}

static void TimetableWndProc(Window *w, WindowEvent *we)
{
	switch (we->event) {
		case WE_PAINT:
			DrawTimetableWindow(w);
			break;

		case WE_CLICK: {
			const Vehicle *v = GetVehicle(w->window_number);

			switch (we->we.click.widget) {
				case TTV_ORDER_VIEW: /* Order view button */
					ShowOrdersWindow(v);
					break;

				case TTV_TIMETABLE_PANEL: { /* Main panel. */
					int selected = GetOrderFromTimetableWndPt(w, we->we.click.pt.y, v);

					if (selected == INVALID_ORDER || selected == WP(w, order_d).sel) {
						/* Deselect clicked order */
						WP(w, order_d).sel = -1;
					} else {
						/* Select clicked order */
						WP(w, order_d).sel = selected;
					}
				} break;

				case TTV_CHANGE_TIME: { /* "Wait For" button. */
					int selected = WP(w, order_d).sel;
					VehicleOrderID real = (selected + 1) / 2;

					if (real >= v->num_orders) real = 0;

					const Order *order = GetVehicleOrder(v, real);
					StringID current = STR_EMPTY;

					if (order != NULL) {
						uint time = (selected % 2 == 1) ? order->travel_time : order->wait_time;
						if (!_patches.timetable_in_ticks) time /= DAY_TICKS;

						if (time != 0) {
							SetDParam(0, time);
							current = STR_CONFIG_PATCHES_INT32;
						}
					}

					ShowQueryString(current, STR_TIMETABLE_CHANGE_TIME, 31, 150, w, CS_NUMERAL);
				} break;

				case TTV_CLEAR_TIME: { /* Clear waiting time button. */
					uint32 p1 = PackTimetableArgs(v, WP(w, order_d).sel);
					DoCommandP(0, p1, 0, NULL, CMD_CHANGE_TIMETABLE | CMD_MSG(STR_CAN_T_TIMETABLE_VEHICLE));
				} break;

				case TTV_RESET_LATENESS: /* Reset the vehicle's late counter. */
					DoCommandP(0, v->index, 0, NULL, CMD_SET_VEHICLE_ON_TIME | CMD_MSG(STR_CAN_T_TIMETABLE_VEHICLE));
					break;

				case TTV_AUTOFILL: /* Autofill the timetable. */
					DoCommandP(0, v->index, HasBit(v->vehicle_flags, VF_AUTOFILL_TIMETABLE) ? 0 : 1, NULL, CMD_AUTOFILL_TIMETABLE | CMD_MSG(STR_CAN_T_TIMETABLE_VEHICLE));
					break;
			}

			SetWindowDirty(w);
		} break;

		case WE_ON_EDIT_TEXT: {
			const Vehicle *v = GetVehicle(w->window_number);

			uint32 p1 = PackTimetableArgs(v, WP(w, order_d).sel);

			uint64 time = StrEmpty(we->we.edittext.str) ? 0 : strtoul(we->we.edittext.str, NULL, 10);
			if (!_patches.timetable_in_ticks) time *= DAY_TICKS;

			uint32 p2 = minu(time, MAX_UVALUE(uint16));

			DoCommandP(0, p1, p2, NULL, CMD_CHANGE_TIMETABLE | CMD_MSG(STR_CAN_T_TIMETABLE_VEHICLE));
		} break;

		case WE_RESIZE:
			/* Update the scroll + matrix */
			w->vscroll.cap = (w->widget[TTV_TIMETABLE_PANEL].bottom - w->widget[TTV_TIMETABLE_PANEL].top) / 10;
			break;

	}
}

static const Widget _timetable_widgets[] = {
	{   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,                   STR_018B_CLOSE_WINDOW},                // TTV_WIDGET_CLOSEBOX
	{    WWT_CAPTION,   RESIZE_RIGHT,   14,    11,   326,     0,    13, STR_TIMETABLE_TITLE,        STR_018C_WINDOW_TITLE_DRAG_THIS},      // TTV_CAPTION
	{ WWT_PUSHTXTBTN,   RESIZE_LR,      14,   327,   387,     0,    13, STR_ORDER_VIEW,             STR_ORDER_VIEW_TOOLTIP},               // TTV_ORDER_VIEW
	{  WWT_STICKYBOX,   RESIZE_LR,      14,   388,   399,     0,    13, STR_NULL,                   STR_STICKY_BUTTON},                    // TTV_STICKY

	{      WWT_PANEL,   RESIZE_RB,      14,     0,   387,    14,    95, STR_NULL,                   STR_TIMETABLE_TOOLTIP},                // TTV_TIMETABLE_PANEL
	{  WWT_SCROLLBAR,   RESIZE_LRB,     14,   388,   399,    14,    95, STR_NULL,                   STR_0190_SCROLL_BAR_SCROLLS_LIST},     // TTV_SCROLLBAR

	{      WWT_PANEL,   RESIZE_RTB,     14,     0,   399,    96,   117, STR_NULL,                   STR_NULL},                             // TTV_SUMMARY_PANEL

	{ WWT_PUSHTXTBTN,   RESIZE_TB,      14,     0,   109,   118,   129, STR_TIMETABLE_CHANGE_TIME,  STR_TIMETABLE_WAIT_TIME_TOOLTIP},      // TTV_CHANGE_TIME
	{ WWT_PUSHTXTBTN,   RESIZE_TB,      14,   110,   219,   118,   129, STR_CLEAR_TIME,             STR_TIMETABLE_CLEAR_TIME_TOOLTIP},     // TTV_CLEAR_TIME
	{ WWT_PUSHTXTBTN,   RESIZE_TB,      14,   220,   337,   118,   129, STR_RESET_LATENESS,         STR_TIMETABLE_RESET_LATENESS_TOOLTIP}, // TTV_RESET_LATENESS
	{ WWT_PUSHTXTBTN,   RESIZE_TB,      14,   338,   387,   118,   129, STR_TIMETABLE_AUTOFILL,     STR_TIMETABLE_AUTOFILL_TOOLTIP},       // TTV_AUTOFILL

	{      WWT_PANEL,   RESIZE_RTB,     14,   388,   387,   118,   129, STR_NULL,                   STR_NULL},                             // TTV_EMPTY
	{  WWT_RESIZEBOX,   RESIZE_LRTB,    14,   388,   399,   118,   129, STR_NULL,                   STR_RESIZE_BUTTON},                    // TTV_RESIZE

	{    WIDGETS_END }
};

static const WindowDesc _timetable_desc = {
	WDP_AUTO, WDP_AUTO, 400, 130, 400, 130,
	WC_VEHICLE_TIMETABLE, WC_VEHICLE_VIEW,
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
	_timetable_widgets,
	TimetableWndProc
};

void ShowTimetableWindow(const Vehicle *v)
{
	Window *w = AllocateWindowDescFront(&_timetable_desc, v->index);

	if (w != NULL) {
		w->caption_color = v->owner;
		w->vscroll.cap = 8;
		w->resize.step_height = 10;
		WP(w, order_d).sel = -1;
	}
}