maedhros@6981: /* $Id$ */ maedhros@6981: maedhros@6981: /** @file timetable_gui.cpp */ maedhros@6981: maedhros@6981: #include "stdafx.h" maedhros@6981: #include "openttd.h" maedhros@6981: #include "functions.h" maedhros@6981: #include "variables.h" maedhros@6981: #include "table/strings.h" rubidium@7266: #include "strings.h" maedhros@6981: #include "command.h" maedhros@6981: #include "date.h" maedhros@6981: #include "engine.h" maedhros@6981: #include "gui.h" maedhros@6981: #include "string.h" maedhros@6981: #include "window.h" maedhros@6981: #include "vehicle.h" maedhros@6981: #include "cargotype.h" maedhros@6981: #include "depot.h" maedhros@6981: maedhros@6981: static int GetOrderFromTimetableWndPt(Window *w, int y, const Vehicle *v) maedhros@6981: { maedhros@6981: /* maedhros@6981: * Calculation description: maedhros@6981: * 15 = 14 (w->widget[ORDER_WIDGET_ORDER_LIST].top) + 1 (frame-line) maedhros@6981: * 10 = order text hight maedhros@6981: */ maedhros@6981: int sel = (y - 15) / 10; maedhros@6981: maedhros@6981: if ((uint)sel >= w->vscroll.cap) return INVALID_ORDER; maedhros@6981: maedhros@6981: sel += w->vscroll.pos; maedhros@6981: maedhros@6981: return (sel <= v->num_orders * 2 && sel >= 0) ? sel : INVALID_ORDER; maedhros@6981: } maedhros@6981: maedhros@6981: static inline void SetTimetableParams(int param1, int param2, uint32 time) maedhros@6981: { maedhros@6981: if (_patches.timetable_in_ticks) { maedhros@6981: SetDParam(param1, STR_TIMETABLE_TICKS); maedhros@6981: SetDParam(param2, time); maedhros@6981: } else { maedhros@6981: SetDParam(param1, STR_TIMETABLE_DAYS); maedhros@6981: SetDParam(param2, time / DAY_TICKS); maedhros@6981: } maedhros@6981: } maedhros@6981: maedhros@6981: static void DrawTimetableWindow(Window *w) maedhros@6981: { maedhros@6981: const Vehicle *v = GetVehicle(w->window_number); maedhros@6981: int selected = WP(w,order_d).sel; maedhros@6981: maedhros@6981: SetVScrollCount(w, v->num_orders * 2); maedhros@6981: maedhros@6981: if (v->owner == _local_player) { maedhros@6981: if (selected == -1) { maedhros@6981: DisableWindowWidget(w, 6); maedhros@6981: DisableWindowWidget(w, 7); maedhros@6981: } else if (selected % 2 == 1) { maedhros@6981: EnableWindowWidget(w, 6); maedhros@6981: EnableWindowWidget(w, 7); maedhros@6981: } else { maedhros@6981: const Order *order = GetVehicleOrder(v, (selected + 1) / 2); maedhros@6981: bool disable = order == NULL || order->type != OT_GOTO_STATION || (_patches.new_nonstop && (order->flags & OF_NON_STOP)); maedhros@6981: maedhros@6981: SetWindowWidgetDisabledState(w, 6, disable); maedhros@6981: SetWindowWidgetDisabledState(w, 7, disable); maedhros@6981: } maedhros@6981: maedhros@6981: EnableWindowWidget(w, 8); maedhros@7066: EnableWindowWidget(w, 9); maedhros@6981: } else { maedhros@6981: DisableWindowWidget(w, 6); maedhros@6981: DisableWindowWidget(w, 7); maedhros@6981: DisableWindowWidget(w, 8); maedhros@7066: DisableWindowWidget(w, 9); maedhros@6981: } maedhros@6981: maedhros@7066: SetWindowWidgetLoweredState(w, 9, HASBIT(v->vehicle_flags, VF_AUTOFILL_TIMETABLE)); maedhros@7066: peter1138@7049: SetDParam(0, v->index); maedhros@6981: DrawWindowWidgets(w); maedhros@6981: maedhros@6981: int y = 15; maedhros@6981: int i = w->vscroll.pos; maedhros@6981: VehicleOrderID order_id = (i + 1) / 2; maedhros@6981: bool final_order = false; maedhros@6981: maedhros@6981: const Order *order = GetVehicleOrder(v, order_id); maedhros@6981: maedhros@6981: while (order != NULL) { maedhros@6981: /* Don't draw anything if it extends past the end of the window. */ maedhros@6981: if (i - w->vscroll.pos >= w->vscroll.cap) break; maedhros@6981: maedhros@6981: if (i % 2 == 0) { maedhros@6981: SetDParam(2, STR_EMPTY); maedhros@6981: maedhros@6981: switch (order->type) { truelight@7312: case OT_DUMMY: truelight@7312: SetDParam(0, STR_INVALID_ORDER); truelight@7312: break; truelight@7312: maedhros@6981: case OT_GOTO_STATION: maedhros@6981: SetDParam(0, (order->flags & OF_NON_STOP) ? STR_880C_GO_NON_STOP_TO : STR_8806_GO_TO); maedhros@6981: SetDParam(1, order->dest); maedhros@6981: maedhros@6981: if (order->wait_time > 0) { maedhros@6981: SetDParam(2, STR_TIMETABLE_STAY_FOR); maedhros@6981: SetTimetableParams(3, 4, order->wait_time); maedhros@6981: } maedhros@6981: maedhros@6981: break; maedhros@6981: maedhros@6981: case OT_GOTO_DEPOT: { maedhros@6981: StringID string = STR_EMPTY; maedhros@6981: maedhros@6981: if (v->type == VEH_AIRCRAFT) { maedhros@6981: string = STR_GO_TO_AIRPORT_HANGAR; maedhros@6981: SetDParam(1, order->dest); maedhros@6981: } else { maedhros@6981: SetDParam(1, GetDepot(order->dest)->town_index); maedhros@6981: maedhros@6981: switch (v->type) { maedhros@6981: case VEH_TRAIN: string = (order->flags & OF_NON_STOP) ? STR_880F_GO_NON_STOP_TO_TRAIN_DEPOT : STR_GO_TO_TRAIN_DEPOT; break; maedhros@6981: case VEH_ROAD: string = STR_9038_GO_TO_ROADVEH_DEPOT; break; maedhros@6981: case VEH_SHIP: string = STR_GO_TO_SHIP_DEPOT; break; maedhros@6981: default: break; maedhros@6981: } maedhros@6981: } maedhros@6981: maedhros@6981: if (order->flags & OF_FULL_LOAD) string++; // Service at orders maedhros@6981: maedhros@6981: SetDParam(0, string); maedhros@6981: } break; maedhros@6981: maedhros@6981: case OT_GOTO_WAYPOINT: maedhros@6981: SetDParam(0, (order->flags & OF_NON_STOP) ? STR_GO_NON_STOP_TO_WAYPOINT : STR_GO_TO_WAYPOINT); maedhros@6981: SetDParam(1, order->dest); maedhros@6981: break; maedhros@6981: maedhros@6981: default: break; maedhros@6981: } maedhros@6981: truelight@7312: const byte colour = (i == selected) ? 0xC : 0x10; truelight@7312: DrawString(2, y, STR_TIMETABLE_GO_TO, colour); maedhros@6981: maedhros@6981: order_id++; maedhros@6981: maedhros@6981: if (order_id >= v->num_orders) { maedhros@6981: order = GetVehicleOrder(v, 0); maedhros@6981: final_order = true; maedhros@6981: } else { maedhros@6981: order = order->next; maedhros@6981: } maedhros@6981: } else { maedhros@6981: StringID string; maedhros@6981: maedhros@6981: if (order->travel_time == 0) { maedhros@6981: string = STR_TIMETABLE_TRAVEL_NOT_TIMETABLED; maedhros@6981: } else { maedhros@6981: SetTimetableParams(0, 1, order->travel_time); maedhros@6981: string = STR_TIMETABLE_TRAVEL_FOR; maedhros@6981: } maedhros@6981: truelight@7312: const byte colour = (i == selected) ? 0xC : 0x10; maedhros@6981: DrawString(12, y, string, colour); maedhros@6981: maedhros@6981: if (final_order) break; maedhros@6981: } maedhros@6981: maedhros@6981: i++; maedhros@6981: y += 10; maedhros@6981: } maedhros@6981: maedhros@6981: y = w->widget[5].top + 1; maedhros@6981: maedhros@7073: { maedhros@7073: uint total_time = 0; maedhros@7073: bool complete = true; maedhros@7073: maedhros@7073: for (const Order *order = GetVehicleOrder(v, 0); order != NULL; order = order->next) { maedhros@7073: total_time += order->travel_time + order->wait_time; maedhros@7073: if (order->travel_time == 0) complete = false; maedhros@7073: if (order->wait_time == 0 && order->type == OT_GOTO_STATION && !(_patches.new_nonstop && (order->flags & OF_NON_STOP))) complete = false; maedhros@7073: } maedhros@7073: maedhros@7073: if (total_time != 0) { maedhros@7073: SetTimetableParams(0, 1, total_time); maedhros@7073: DrawString(2, y, complete ? STR_TIMETABLE_TOTAL_TIME : STR_TIMETABLE_TOTAL_TIME_INCOMPLETE, 0x10); maedhros@7073: } maedhros@7073: } maedhros@7073: y += 10; maedhros@7073: maedhros@6981: if (v->lateness_counter == 0 || (!_patches.timetable_in_ticks && v->lateness_counter / DAY_TICKS == 0)) { maedhros@6981: DrawString(2, y, STR_TIMETABLE_STATUS_ON_TIME, 0x10); maedhros@6981: } else { maedhros@6981: SetTimetableParams(0, 1, abs(v->lateness_counter)); maedhros@6981: DrawString(2, y, v->lateness_counter < 0 ? STR_TIMETABLE_STATUS_EARLY : STR_TIMETABLE_STATUS_LATE, 0x10); maedhros@6981: } maedhros@6981: } maedhros@6981: maedhros@6981: static inline uint32 PackTimetableArgs(const Vehicle *v, uint selected) maedhros@6981: { maedhros@6981: uint order_number = (selected + 1) / 2; maedhros@6981: uint is_journey = (selected % 2 == 1) ? 1 : 0; maedhros@6981: maedhros@6981: if (order_number >= v->num_orders) order_number = 0; maedhros@6981: maedhros@6981: return v->index | (order_number << 16) | (is_journey << 24); maedhros@6981: } maedhros@6981: maedhros@6981: static void TimetableWndProc(Window *w, WindowEvent *we) maedhros@6981: { maedhros@6981: switch (we->event) { maedhros@6981: case WE_PAINT: maedhros@6981: DrawTimetableWindow(w); maedhros@6981: break; maedhros@6981: maedhros@6981: case WE_CLICK: { maedhros@6981: const Vehicle *v = GetVehicle(w->window_number); maedhros@6981: maedhros@6981: switch (we->we.click.widget) { maedhros@6981: case 3: { /* Main panel. */ maedhros@6981: int selected = GetOrderFromTimetableWndPt(w, we->we.click.pt.y, v); maedhros@6981: maedhros@6981: if (selected == INVALID_ORDER || selected == WP(w,order_d).sel) { maedhros@6981: /* Deselect clicked order */ maedhros@6981: WP(w,order_d).sel = -1; maedhros@6981: } else { maedhros@6981: /* Select clicked order */ maedhros@6981: WP(w,order_d).sel = selected; maedhros@6981: } maedhros@6981: } break; maedhros@6981: maedhros@6981: case 6: { /* "Wait For" button. */ maedhros@6981: int selected = WP(w,order_d).sel; maedhros@6981: VehicleOrderID real = (selected + 1) / 2; maedhros@6981: maedhros@6981: if (real >= v->num_orders) real = 0; maedhros@6981: maedhros@6981: const Order *order = GetVehicleOrder(v, real); maedhros@6981: StringID current = STR_EMPTY; maedhros@6981: maedhros@6981: if (order != NULL) { maedhros@6981: uint time = (selected % 2 == 1) ? order->travel_time : order->wait_time; maedhros@6981: if (!_patches.timetable_in_ticks) time /= DAY_TICKS; maedhros@6981: maedhros@6981: if (time != 0) { maedhros@6981: SetDParam(0, time); maedhros@6981: current = STR_CONFIG_PATCHES_INT32; maedhros@6981: } maedhros@6981: } maedhros@6981: maedhros@6981: ShowQueryString(current, STR_TIMETABLE_CHANGE_TIME, 31, 150, w, CS_NUMERAL); maedhros@6981: } break; maedhros@6981: maedhros@6981: case 7: { /* Clear waiting time button. */ maedhros@6981: uint32 p1 = PackTimetableArgs(v, WP(w,order_d).sel); maedhros@6981: DoCommandP(0, p1, 0, NULL, CMD_CHANGE_TIMETABLE | CMD_MSG(STR_CAN_T_TIMETABLE_VEHICLE)); maedhros@6981: } break; maedhros@6981: maedhros@6981: case 8: /* Reset the vehicle's late counter. */ maedhros@6981: DoCommandP(0, v->index, 0, NULL, CMD_SET_VEHICLE_ON_TIME | CMD_MSG(STR_CAN_T_TIMETABLE_VEHICLE)); maedhros@6981: break; maedhros@7066: maedhros@7066: case 9: /* Autofill the timetable. */ maedhros@7066: DoCommandP(0, v->index, HASBIT(v->vehicle_flags, VF_AUTOFILL_TIMETABLE) ? 0 : 1, NULL, CMD_AUTOFILL_TIMETABLE | CMD_MSG(STR_CAN_T_TIMETABLE_VEHICLE)); maedhros@7066: break; maedhros@6981: } maedhros@6981: maedhros@6981: SetWindowDirty(w); maedhros@6981: } break; maedhros@6981: maedhros@6981: case WE_ON_EDIT_TEXT: { maedhros@6981: const Vehicle *v = GetVehicle(w->window_number); maedhros@6981: maedhros@6981: uint32 p1 = PackTimetableArgs(v, WP(w,order_d).sel); maedhros@6981: maedhros@6981: uint64 time = StrEmpty(we->we.edittext.str) ? 0 : strtoul(we->we.edittext.str, NULL, 10); maedhros@6981: if (!_patches.timetable_in_ticks) time *= DAY_TICKS; maedhros@6981: maedhros@6981: uint32 p2 = minu(time, MAX_UVALUE(uint16)); maedhros@6981: maedhros@6981: DoCommandP(0, p1, p2, NULL, CMD_CHANGE_TIMETABLE | CMD_MSG(STR_CAN_T_TIMETABLE_VEHICLE)); maedhros@6981: } break; maedhros@6981: maedhros@6981: case WE_RESIZE: maedhros@6981: /* Update the scroll + matrix */ maedhros@6982: w->vscroll.cap = (w->widget[3].bottom - w->widget[3].top) / 10; maedhros@6981: break; maedhros@6981: maedhros@6981: } maedhros@6981: } maedhros@6981: maedhros@6981: static const Widget _timetable_widgets[] = { maedhros@6981: { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, maedhros@7066: { WWT_CAPTION, RESIZE_RIGHT, 14, 11, 387, 0, 13, STR_TIMETABLE_TITLE, STR_018C_WINDOW_TITLE_DRAG_THIS}, maedhros@7066: { WWT_STICKYBOX, RESIZE_LR, 14, 388, 399, 0, 13, STR_NULL, STR_STICKY_BUTTON}, maedhros@6981: maedhros@7066: { WWT_PANEL, RESIZE_RB, 14, 0, 387, 14, 95, STR_NULL, STR_TIMETABLE_TOOLTIP}, maedhros@7066: { WWT_SCROLLBAR, RESIZE_LRB, 14, 388, 399, 14, 95, STR_NULL, STR_0190_SCROLL_BAR_SCROLLS_LIST}, maedhros@6981: maedhros@7073: { WWT_PANEL, RESIZE_RTB, 14, 0, 399, 96, 117, STR_NULL, STR_NULL}, maedhros@6981: maedhros@7073: { WWT_PUSHTXTBTN, RESIZE_TB, 14, 0, 109, 118, 129, STR_TIMETABLE_CHANGE_TIME, STR_TIMETABLE_WAIT_TIME_TOOLTIP}, maedhros@7073: { WWT_PUSHTXTBTN, RESIZE_TB, 14, 110, 219, 118, 129, STR_CLEAR_TIME, STR_TIMETABLE_CLEAR_TIME_TOOLTIP}, maedhros@7073: { WWT_PUSHTXTBTN, RESIZE_TB, 14, 220, 337, 118, 129, STR_RESET_LATENESS, STR_TIMETABLE_RESET_LATENESS_TOOLTIP}, maedhros@7073: { WWT_PUSHTXTBTN, RESIZE_TB, 14, 338, 387, 118, 129, STR_TIMETABLE_AUTOFILL, STR_TIMETABLE_AUTOFILL_TOOLTIP}, maedhros@7057: maedhros@7073: { WWT_PANEL, RESIZE_RTB, 14, 388, 387, 118, 129, STR_NULL, STR_NULL}, maedhros@7073: { WWT_RESIZEBOX, RESIZE_LRTB, 14, 388, 399, 118, 129, STR_NULL, STR_RESIZE_BUTTON}, maedhros@6981: maedhros@6981: { WIDGETS_END } maedhros@6981: }; maedhros@6981: maedhros@6981: static const WindowDesc _timetable_desc = { rubidium@7341: WDP_AUTO, WDP_AUTO, 400, 130, 400, 130, maedhros@6981: WC_VEHICLE_TIMETABLE, WC_VEHICLE_VIEW, maedhros@6981: WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE, maedhros@6981: _timetable_widgets, maedhros@6981: TimetableWndProc maedhros@6981: }; maedhros@6981: maedhros@6981: void ShowTimetableWindow(const Vehicle *v) maedhros@6981: { maedhros@6981: Window *w = AllocateWindowDescFront(&_timetable_desc, v->index); maedhros@6981: maedhros@6981: if (w != NULL) { maedhros@6981: w->caption_color = v->owner; maedhros@7057: w->vscroll.cap = 8; maedhros@6981: w->resize.step_height = 10; maedhros@6981: WP(w,order_d).sel = -1; maedhros@6981: } maedhros@6981: }