9629
|
1 |
/* $Id$ */
|
|
2 |
|
|
3 |
/** @file timetable_gui.cpp */
|
|
4 |
|
|
5 |
#include "stdafx.h"
|
|
6 |
#include "openttd.h"
|
|
7 |
#include "functions.h"
|
|
8 |
#include "variables.h"
|
|
9 |
#include "table/strings.h"
|
|
10 |
#include "command.h"
|
|
11 |
#include "date.h"
|
|
12 |
#include "engine.h"
|
|
13 |
#include "gui.h"
|
|
14 |
#include "string.h"
|
|
15 |
#include "window.h"
|
|
16 |
#include "vehicle.h"
|
|
17 |
#include "cargotype.h"
|
|
18 |
#include "depot.h"
|
|
19 |
|
|
20 |
static int GetOrderFromTimetableWndPt(Window *w, int y, const Vehicle *v)
|
|
21 |
{
|
|
22 |
/*
|
|
23 |
* Calculation description:
|
|
24 |
* 15 = 14 (w->widget[ORDER_WIDGET_ORDER_LIST].top) + 1 (frame-line)
|
|
25 |
* 10 = order text hight
|
|
26 |
*/
|
|
27 |
int sel = (y - 15) / 10;
|
|
28 |
|
|
29 |
if ((uint)sel >= w->vscroll.cap) return INVALID_ORDER;
|
|
30 |
|
|
31 |
sel += w->vscroll.pos;
|
|
32 |
|
|
33 |
return (sel <= v->num_orders * 2 && sel >= 0) ? sel : INVALID_ORDER;
|
|
34 |
}
|
|
35 |
|
|
36 |
static inline void SetTimetableParams(int param1, int param2, uint32 time)
|
|
37 |
{
|
|
38 |
if (_patches.timetable_in_ticks) {
|
|
39 |
SetDParam(param1, STR_TIMETABLE_TICKS);
|
|
40 |
SetDParam(param2, time);
|
|
41 |
} else {
|
|
42 |
SetDParam(param1, STR_TIMETABLE_DAYS);
|
|
43 |
SetDParam(param2, time / DAY_TICKS);
|
|
44 |
}
|
|
45 |
}
|
|
46 |
|
|
47 |
static void DrawTimetableWindow(Window *w)
|
|
48 |
{
|
|
49 |
const Vehicle *v = GetVehicle(w->window_number);
|
|
50 |
int selected = WP(w,order_d).sel;
|
|
51 |
|
|
52 |
SetVScrollCount(w, v->num_orders * 2);
|
|
53 |
|
|
54 |
if (v->owner == _local_player) {
|
|
55 |
if (selected == -1) {
|
|
56 |
DisableWindowWidget(w, 6);
|
|
57 |
DisableWindowWidget(w, 7);
|
|
58 |
} else if (selected % 2 == 1) {
|
|
59 |
EnableWindowWidget(w, 6);
|
|
60 |
EnableWindowWidget(w, 7);
|
|
61 |
} else {
|
|
62 |
const Order *order = GetVehicleOrder(v, (selected + 1) / 2);
|
|
63 |
bool disable = order == NULL || order->type != OT_GOTO_STATION || (_patches.new_nonstop && (order->flags & OF_NON_STOP));
|
|
64 |
|
|
65 |
SetWindowWidgetDisabledState(w, 6, disable);
|
|
66 |
SetWindowWidgetDisabledState(w, 7, disable);
|
|
67 |
}
|
|
68 |
|
|
69 |
EnableWindowWidget(w, 8);
|
|
70 |
EnableWindowWidget(w, 9);
|
|
71 |
} else {
|
|
72 |
DisableWindowWidget(w, 6);
|
|
73 |
DisableWindowWidget(w, 7);
|
|
74 |
DisableWindowWidget(w, 8);
|
|
75 |
DisableWindowWidget(w, 9);
|
|
76 |
}
|
|
77 |
|
|
78 |
SetWindowWidgetLoweredState(w, 9, HASBIT(v->vehicle_flags, VF_AUTOFILL_TIMETABLE));
|
|
79 |
|
|
80 |
SetDParam(0, v->index);
|
|
81 |
DrawWindowWidgets(w);
|
|
82 |
|
|
83 |
int y = 15;
|
|
84 |
int i = w->vscroll.pos;
|
|
85 |
VehicleOrderID order_id = (i + 1) / 2;
|
|
86 |
bool final_order = false;
|
|
87 |
|
|
88 |
const Order *order = GetVehicleOrder(v, order_id);
|
|
89 |
|
|
90 |
while (order != NULL) {
|
|
91 |
/* Don't draw anything if it extends past the end of the window. */
|
|
92 |
if (i - w->vscroll.pos >= w->vscroll.cap) break;
|
|
93 |
|
|
94 |
if (i % 2 == 0) {
|
|
95 |
SetDParam(2, STR_EMPTY);
|
|
96 |
|
|
97 |
switch (order->type) {
|
|
98 |
case OT_GOTO_STATION:
|
|
99 |
SetDParam(0, (order->flags & OF_NON_STOP) ? STR_880C_GO_NON_STOP_TO : STR_8806_GO_TO);
|
|
100 |
SetDParam(1, order->dest);
|
|
101 |
|
|
102 |
if (order->wait_time > 0) {
|
|
103 |
SetDParam(2, STR_TIMETABLE_STAY_FOR);
|
|
104 |
SetTimetableParams(3, 4, order->wait_time);
|
|
105 |
}
|
|
106 |
|
|
107 |
break;
|
|
108 |
|
|
109 |
case OT_GOTO_DEPOT: {
|
|
110 |
StringID string = STR_EMPTY;
|
|
111 |
|
|
112 |
if (v->type == VEH_AIRCRAFT) {
|
|
113 |
string = STR_GO_TO_AIRPORT_HANGAR;
|
|
114 |
SetDParam(1, order->dest);
|
|
115 |
} else {
|
|
116 |
SetDParam(1, GetDepot(order->dest)->town_index);
|
|
117 |
|
|
118 |
switch (v->type) {
|
|
119 |
case VEH_TRAIN: string = (order->flags & OF_NON_STOP) ? STR_880F_GO_NON_STOP_TO_TRAIN_DEPOT : STR_GO_TO_TRAIN_DEPOT; break;
|
|
120 |
case VEH_ROAD: string = STR_9038_GO_TO_ROADVEH_DEPOT; break;
|
|
121 |
case VEH_SHIP: string = STR_GO_TO_SHIP_DEPOT; break;
|
|
122 |
default: break;
|
|
123 |
}
|
|
124 |
}
|
|
125 |
|
|
126 |
if (order->flags & OF_FULL_LOAD) string++; // Service at orders
|
|
127 |
|
|
128 |
SetDParam(0, string);
|
|
129 |
} break;
|
|
130 |
|
|
131 |
case OT_GOTO_WAYPOINT:
|
|
132 |
SetDParam(0, (order->flags & OF_NON_STOP) ? STR_GO_NON_STOP_TO_WAYPOINT : STR_GO_TO_WAYPOINT);
|
|
133 |
SetDParam(1, order->dest);
|
|
134 |
break;
|
|
135 |
|
|
136 |
default: break;
|
|
137 |
}
|
|
138 |
|
|
139 |
byte colour = (i == selected) ? 0xC : 0x10;
|
|
140 |
|
|
141 |
if (order->type != OT_DUMMY) {
|
|
142 |
DrawString(2, y, STR_TIMETABLE_GO_TO, colour);
|
|
143 |
} else {
|
|
144 |
SetDParam(0, STR_INVALID_ORDER);
|
|
145 |
DrawString(2, y, STR_TIMETABLE_GO_TO, colour);
|
|
146 |
}
|
|
147 |
|
|
148 |
order_id++;
|
|
149 |
|
|
150 |
if (order_id >= v->num_orders) {
|
|
151 |
order = GetVehicleOrder(v, 0);
|
|
152 |
final_order = true;
|
|
153 |
} else {
|
|
154 |
order = order->next;
|
|
155 |
}
|
|
156 |
} else {
|
|
157 |
StringID string;
|
|
158 |
|
|
159 |
if (order->travel_time == 0) {
|
|
160 |
string = STR_TIMETABLE_TRAVEL_NOT_TIMETABLED;
|
|
161 |
} else {
|
|
162 |
SetTimetableParams(0, 1, order->travel_time);
|
|
163 |
string = STR_TIMETABLE_TRAVEL_FOR;
|
|
164 |
}
|
|
165 |
|
|
166 |
byte colour = (i == selected) ? 0xC : 0x10;
|
|
167 |
DrawString(12, y, string, colour);
|
|
168 |
|
|
169 |
if (final_order) break;
|
|
170 |
}
|
|
171 |
|
|
172 |
i++;
|
|
173 |
y += 10;
|
|
174 |
}
|
|
175 |
|
|
176 |
y = w->widget[5].top + 1;
|
|
177 |
|
|
178 |
{
|
|
179 |
uint total_time = 0;
|
|
180 |
bool complete = true;
|
|
181 |
|
|
182 |
for (const Order *order = GetVehicleOrder(v, 0); order != NULL; order = order->next) {
|
|
183 |
total_time += order->travel_time + order->wait_time;
|
|
184 |
if (order->travel_time == 0) complete = false;
|
|
185 |
if (order->wait_time == 0 && order->type == OT_GOTO_STATION && !(_patches.new_nonstop && (order->flags & OF_NON_STOP))) complete = false;
|
|
186 |
}
|
|
187 |
|
|
188 |
if (total_time != 0) {
|
|
189 |
SetTimetableParams(0, 1, total_time);
|
|
190 |
DrawString(2, y, complete ? STR_TIMETABLE_TOTAL_TIME : STR_TIMETABLE_TOTAL_TIME_INCOMPLETE, 0x10);
|
|
191 |
}
|
|
192 |
}
|
|
193 |
y += 10;
|
|
194 |
|
|
195 |
if (v->lateness_counter == 0 || (!_patches.timetable_in_ticks && v->lateness_counter / DAY_TICKS == 0)) {
|
|
196 |
DrawString(2, y, STR_TIMETABLE_STATUS_ON_TIME, 0x10);
|
|
197 |
} else {
|
|
198 |
SetTimetableParams(0, 1, abs(v->lateness_counter));
|
|
199 |
DrawString(2, y, v->lateness_counter < 0 ? STR_TIMETABLE_STATUS_EARLY : STR_TIMETABLE_STATUS_LATE, 0x10);
|
|
200 |
}
|
|
201 |
}
|
|
202 |
|
|
203 |
static inline uint32 PackTimetableArgs(const Vehicle *v, uint selected)
|
|
204 |
{
|
|
205 |
uint order_number = (selected + 1) / 2;
|
|
206 |
uint is_journey = (selected % 2 == 1) ? 1 : 0;
|
|
207 |
|
|
208 |
if (order_number >= v->num_orders) order_number = 0;
|
|
209 |
|
|
210 |
return v->index | (order_number << 16) | (is_journey << 24);
|
|
211 |
}
|
|
212 |
|
|
213 |
static void TimetableWndProc(Window *w, WindowEvent *we)
|
|
214 |
{
|
|
215 |
switch (we->event) {
|
|
216 |
case WE_PAINT:
|
|
217 |
DrawTimetableWindow(w);
|
|
218 |
break;
|
|
219 |
|
|
220 |
case WE_CLICK: {
|
|
221 |
const Vehicle *v = GetVehicle(w->window_number);
|
|
222 |
|
|
223 |
switch (we->we.click.widget) {
|
|
224 |
case 3: { /* Main panel. */
|
|
225 |
int selected = GetOrderFromTimetableWndPt(w, we->we.click.pt.y, v);
|
|
226 |
|
|
227 |
if (selected == INVALID_ORDER || selected == WP(w,order_d).sel) {
|
|
228 |
/* Deselect clicked order */
|
|
229 |
WP(w,order_d).sel = -1;
|
|
230 |
} else {
|
|
231 |
/* Select clicked order */
|
|
232 |
WP(w,order_d).sel = selected;
|
|
233 |
}
|
|
234 |
} break;
|
|
235 |
|
|
236 |
case 6: { /* "Wait For" button. */
|
|
237 |
int selected = WP(w,order_d).sel;
|
|
238 |
VehicleOrderID real = (selected + 1) / 2;
|
|
239 |
|
|
240 |
if (real >= v->num_orders) real = 0;
|
|
241 |
|
|
242 |
const Order *order = GetVehicleOrder(v, real);
|
|
243 |
StringID current = STR_EMPTY;
|
|
244 |
|
|
245 |
if (order != NULL) {
|
|
246 |
uint time = (selected % 2 == 1) ? order->travel_time : order->wait_time;
|
|
247 |
if (!_patches.timetable_in_ticks) time /= DAY_TICKS;
|
|
248 |
|
|
249 |
if (time != 0) {
|
|
250 |
SetDParam(0, time);
|
|
251 |
current = STR_CONFIG_PATCHES_INT32;
|
|
252 |
}
|
|
253 |
}
|
|
254 |
|
|
255 |
ShowQueryString(current, STR_TIMETABLE_CHANGE_TIME, 31, 150, w, CS_NUMERAL);
|
|
256 |
} break;
|
|
257 |
|
|
258 |
case 7: { /* Clear waiting time button. */
|
|
259 |
uint32 p1 = PackTimetableArgs(v, WP(w,order_d).sel);
|
|
260 |
DoCommandP(0, p1, 0, NULL, CMD_CHANGE_TIMETABLE | CMD_MSG(STR_CAN_T_TIMETABLE_VEHICLE));
|
|
261 |
} break;
|
|
262 |
|
|
263 |
case 8: /* Reset the vehicle's late counter. */
|
|
264 |
DoCommandP(0, v->index, 0, NULL, CMD_SET_VEHICLE_ON_TIME | CMD_MSG(STR_CAN_T_TIMETABLE_VEHICLE));
|
|
265 |
break;
|
|
266 |
|
|
267 |
case 9: /* Autofill the timetable. */
|
|
268 |
DoCommandP(0, v->index, HASBIT(v->vehicle_flags, VF_AUTOFILL_TIMETABLE) ? 0 : 1, NULL, CMD_AUTOFILL_TIMETABLE | CMD_MSG(STR_CAN_T_TIMETABLE_VEHICLE));
|
|
269 |
break;
|
|
270 |
}
|
|
271 |
|
|
272 |
SetWindowDirty(w);
|
|
273 |
} break;
|
|
274 |
|
|
275 |
case WE_ON_EDIT_TEXT: {
|
|
276 |
const Vehicle *v = GetVehicle(w->window_number);
|
|
277 |
|
|
278 |
uint32 p1 = PackTimetableArgs(v, WP(w,order_d).sel);
|
|
279 |
|
|
280 |
uint64 time = StrEmpty(we->we.edittext.str) ? 0 : strtoul(we->we.edittext.str, NULL, 10);
|
|
281 |
if (!_patches.timetable_in_ticks) time *= DAY_TICKS;
|
|
282 |
|
|
283 |
uint32 p2 = minu(time, MAX_UVALUE(uint16));
|
|
284 |
|
|
285 |
DoCommandP(0, p1, p2, NULL, CMD_CHANGE_TIMETABLE | CMD_MSG(STR_CAN_T_TIMETABLE_VEHICLE));
|
|
286 |
} break;
|
|
287 |
|
|
288 |
case WE_RESIZE:
|
|
289 |
/* Update the scroll + matrix */
|
|
290 |
w->vscroll.cap = (w->widget[3].bottom - w->widget[3].top) / 10;
|
|
291 |
break;
|
|
292 |
|
|
293 |
}
|
|
294 |
}
|
|
295 |
|
|
296 |
static const Widget _timetable_widgets[] = {
|
|
297 |
{ WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
|
|
298 |
{ WWT_CAPTION, RESIZE_RIGHT, 14, 11, 387, 0, 13, STR_TIMETABLE_TITLE, STR_018C_WINDOW_TITLE_DRAG_THIS},
|
|
299 |
{ WWT_STICKYBOX, RESIZE_LR, 14, 388, 399, 0, 13, STR_NULL, STR_STICKY_BUTTON},
|
|
300 |
|
|
301 |
{ WWT_PANEL, RESIZE_RB, 14, 0, 387, 14, 95, STR_NULL, STR_TIMETABLE_TOOLTIP},
|
|
302 |
{ WWT_SCROLLBAR, RESIZE_LRB, 14, 388, 399, 14, 95, STR_NULL, STR_0190_SCROLL_BAR_SCROLLS_LIST},
|
|
303 |
|
|
304 |
{ WWT_PANEL, RESIZE_RTB, 14, 0, 399, 96, 117, STR_NULL, STR_NULL},
|
|
305 |
|
|
306 |
{ WWT_PUSHTXTBTN, RESIZE_TB, 14, 0, 109, 118, 129, STR_TIMETABLE_CHANGE_TIME, STR_TIMETABLE_WAIT_TIME_TOOLTIP},
|
|
307 |
{ WWT_PUSHTXTBTN, RESIZE_TB, 14, 110, 219, 118, 129, STR_CLEAR_TIME, STR_TIMETABLE_CLEAR_TIME_TOOLTIP},
|
|
308 |
{ WWT_PUSHTXTBTN, RESIZE_TB, 14, 220, 337, 118, 129, STR_RESET_LATENESS, STR_TIMETABLE_RESET_LATENESS_TOOLTIP},
|
|
309 |
{ WWT_PUSHTXTBTN, RESIZE_TB, 14, 338, 387, 118, 129, STR_TIMETABLE_AUTOFILL, STR_TIMETABLE_AUTOFILL_TOOLTIP},
|
|
310 |
|
|
311 |
{ WWT_PANEL, RESIZE_RTB, 14, 388, 387, 118, 129, STR_NULL, STR_NULL},
|
|
312 |
{ WWT_RESIZEBOX, RESIZE_LRTB, 14, 388, 399, 118, 129, STR_NULL, STR_RESIZE_BUTTON},
|
|
313 |
|
|
314 |
{ WIDGETS_END }
|
|
315 |
};
|
|
316 |
|
|
317 |
static const WindowDesc _timetable_desc = {
|
|
318 |
WDP_AUTO, WDP_AUTO, 400, 130,
|
|
319 |
WC_VEHICLE_TIMETABLE, WC_VEHICLE_VIEW,
|
|
320 |
WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
|
|
321 |
_timetable_widgets,
|
|
322 |
TimetableWndProc
|
|
323 |
};
|
|
324 |
|
|
325 |
void ShowTimetableWindow(const Vehicle *v)
|
|
326 |
{
|
|
327 |
Window *w = AllocateWindowDescFront(&_timetable_desc, v->index);
|
|
328 |
|
|
329 |
if (w != NULL) {
|
|
330 |
w->caption_color = v->owner;
|
|
331 |
w->vscroll.cap = 8;
|
|
332 |
w->resize.step_height = 10;
|
|
333 |
WP(w,order_d).sel = -1;
|
|
334 |
}
|
|
335 |
}
|