|
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 } |