maedhros@7477: /* $Id$ */ maedhros@7477: rubidium@10429: /** @file timetable_gui.cpp GUI for time tabling. */ maedhros@7477: maedhros@7477: #include "stdafx.h" maedhros@7477: #include "openttd.h" maedhros@7477: #include "variables.h" rubidium@8612: #include "command_func.h" maedhros@7477: #include "gui.h" rubidium@8603: #include "window_gui.h" rubidium@8603: #include "textbuf_gui.h" maedhros@7477: #include "cargotype.h" rubidium@8610: #include "strings_func.h" rubidium@8640: #include "vehicle_base.h" rubidium@8710: #include "string_func.h" rubidium@8720: #include "gfx_func.h" rubidium@8750: #include "player_func.h" maedhros@10168: #include "order_func.h" rubidium@8766: #include "settings_type.h" maedhros@7477: rubidium@8760: #include "table/strings.h" rubidium@8760: rubidium@9218: enum TimetableViewWindowWidgets { rubidium@9218: TTV_WIDGET_CLOSEBOX = 0, rubidium@9218: TTV_CAPTION, rubidium@9241: TTV_ORDER_VIEW, rubidium@9218: TTV_STICKY, rubidium@9218: TTV_TIMETABLE_PANEL, rubidium@9218: TTV_SCROLLBAR, rubidium@9218: TTV_SUMMARY_PANEL, rubidium@9218: TTV_CHANGE_TIME, rubidium@9218: TTV_CLEAR_TIME, rubidium@9218: TTV_RESET_LATENESS, rubidium@9218: TTV_AUTOFILL, rubidium@9218: TTV_EMPTY, rubidium@9218: TTV_RESIZE, rubidium@9218: }; rubidium@9218: maedhros@10168: void SetTimetableParams(int param1, int param2, uint32 time) maedhros@7477: { rubidium@10775: if (_settings_client.gui.timetable_in_ticks) { maedhros@7477: SetDParam(param1, STR_TIMETABLE_TICKS); maedhros@7477: SetDParam(param2, time); maedhros@7477: } else { maedhros@7477: SetDParam(param1, STR_TIMETABLE_DAYS); maedhros@7477: SetDParam(param2, time / DAY_TICKS); maedhros@7477: } maedhros@7477: } maedhros@7477: rubidium@10575: struct TimetableWindow : Window { rubidium@10575: int sel_index; maedhros@7477: rubidium@10575: TimetableWindow(const WindowDesc *desc, WindowNumber window_number) : Window(desc, window_number) rubidium@10575: { rubidium@10575: this->caption_color = GetVehicle(window_number)->owner; rubidium@10575: this->vscroll.cap = 8; rubidium@10575: this->resize.step_height = 10; rubidium@10575: this->sel_index = -1; peter1138@10681: peter1138@10681: this->FindWindowPlacementAndResize(desc); maedhros@7477: } maedhros@7477: rubidium@10575: int GetOrderFromTimetableWndPt(int y, const Vehicle *v) rubidium@10575: { rubidium@10575: /* rubidium@10575: * Calculation description: rubidium@10575: * 15 = 14 (this->widget[TTV_ORDER_VIEW].top) + 1 (frame-line) rubidium@10575: * 10 = order text hight rubidium@10575: */ rubidium@10575: int sel = (y - 15) / 10; maedhros@7477: rubidium@10575: if ((uint)sel >= this->vscroll.cap) return INVALID_ORDER; maedhros@7477: rubidium@10575: sel += this->vscroll.pos; maedhros@7477: rubidium@10575: return (sel <= v->num_orders * 2 && sel >= 0) ? sel : INVALID_ORDER; rubidium@10575: } rubidium@10575: rubidium@10575: void OnPaint() rubidium@10575: { rubidium@10575: const Vehicle *v = GetVehicle(this->window_number); rubidium@10575: int selected = this->sel_index; rubidium@10575: rubidium@10575: SetVScrollCount(this, v->num_orders * 2); rubidium@10575: rubidium@10575: if (v->owner == _local_player) { rubidium@10575: if (selected == -1) { rubidium@10575: this->DisableWidget(TTV_CHANGE_TIME); rubidium@10575: this->DisableWidget(TTV_CLEAR_TIME); rubidium@10575: } else if (selected % 2 == 1) { rubidium@10575: this->EnableWidget(TTV_CHANGE_TIME); rubidium@10575: this->EnableWidget(TTV_CLEAR_TIME); maedhros@7477: } else { rubidium@10575: const Order *order = GetVehicleOrder(v, (selected + 1) / 2); rubidium@10575: bool disable = order == NULL || !order->IsType(OT_GOTO_STATION) || (order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION); maedhros@7477: rubidium@10575: this->SetWidgetDisabledState(TTV_CHANGE_TIME, disable); rubidium@10575: this->SetWidgetDisabledState(TTV_CLEAR_TIME, disable); maedhros@7477: } maedhros@7477: rubidium@10575: this->EnableWidget(TTV_RESET_LATENESS); rubidium@10575: this->EnableWidget(TTV_AUTOFILL); rubidium@10575: } else { rubidium@10575: this->DisableWidget(TTV_CHANGE_TIME); rubidium@10575: this->DisableWidget(TTV_CLEAR_TIME); rubidium@10575: this->DisableWidget(TTV_RESET_LATENESS); rubidium@10575: this->DisableWidget(TTV_AUTOFILL); maedhros@7477: } maedhros@7477: rubidium@10575: this->SetWidgetLoweredState(TTV_AUTOFILL, HasBit(v->vehicle_flags, VF_AUTOFILL_TIMETABLE)); maedhros@7477: rubidium@10575: SetDParam(0, v->index); rubidium@10595: this->DrawWidgets(); maedhros@7569: rubidium@10575: int y = 15; rubidium@10575: int i = this->vscroll.pos; rubidium@10575: VehicleOrderID order_id = (i + 1) / 2; rubidium@10575: bool final_order = false; rubidium@10575: rubidium@10575: const Order *order = GetVehicleOrder(v, order_id); rubidium@10575: rubidium@10575: while (order != NULL) { rubidium@10575: /* Don't draw anything if it extends past the end of the window. */ rubidium@10575: if (i - this->vscroll.pos >= this->vscroll.cap) break; rubidium@10575: rubidium@10575: if (i % 2 == 0) { rubidium@10575: DrawOrderString(v, order, order_id, y, i == selected, true); rubidium@10575: rubidium@10575: order_id++; rubidium@10575: rubidium@10575: if (order_id >= v->num_orders) { rubidium@10575: order = GetVehicleOrder(v, 0); rubidium@10575: final_order = true; rubidium@10575: } else { rubidium@10575: order = order->next; rubidium@10575: } rubidium@10575: } else { rubidium@10575: StringID string; rubidium@10575: rubidium@10575: if (order->travel_time == 0) { rubidium@10575: string = STR_TIMETABLE_TRAVEL_NOT_TIMETABLED; rubidium@10575: } else { rubidium@10575: SetTimetableParams(0, 1, order->travel_time); rubidium@10575: string = STR_TIMETABLE_TRAVEL_FOR; rubidium@10575: } rubidium@10575: rubidium@10575: DrawString(22, y, string, (i == selected) ? TC_WHITE : TC_BLACK); rubidium@10575: rubidium@10575: if (final_order) break; rubidium@10575: } rubidium@10575: rubidium@10575: i++; rubidium@10575: y += 10; maedhros@7569: } maedhros@7569: rubidium@10575: y = this->widget[TTV_SUMMARY_PANEL].top + 1; rubidium@10575: rubidium@10575: { rubidium@10575: uint total_time = 0; rubidium@10575: bool complete = true; rubidium@10575: rubidium@10575: for (const Order *order = GetVehicleOrder(v, 0); order != NULL; order = order->next) { rubidium@10575: total_time += order->travel_time + order->wait_time; rubidium@10575: if (order->travel_time == 0) complete = false; rubidium@10575: if (order->wait_time == 0 && order->IsType(OT_GOTO_STATION) && !(order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION)) complete = false; rubidium@10575: } rubidium@10575: rubidium@10575: if (total_time != 0) { rubidium@10575: SetTimetableParams(0, 1, total_time); rubidium@10575: DrawString(2, y, complete ? STR_TIMETABLE_TOTAL_TIME : STR_TIMETABLE_TOTAL_TIME_INCOMPLETE, TC_BLACK); rubidium@10575: } rubidium@10575: } rubidium@10575: y += 10; rubidium@10575: rubidium@10775: if (v->lateness_counter == 0 || (!_settings_client.gui.timetable_in_ticks && v->lateness_counter / DAY_TICKS == 0)) { rubidium@10575: DrawString(2, y, STR_TIMETABLE_STATUS_ON_TIME, TC_BLACK); rubidium@10575: } else { rubidium@10575: SetTimetableParams(0, 1, abs(v->lateness_counter)); rubidium@10575: DrawString(2, y, v->lateness_counter < 0 ? STR_TIMETABLE_STATUS_EARLY : STR_TIMETABLE_STATUS_LATE, TC_BLACK); maedhros@7569: } maedhros@7569: } maedhros@7477: rubidium@10575: static inline uint32 PackTimetableArgs(const Vehicle *v, uint selected) rubidium@10575: { rubidium@10575: uint order_number = (selected + 1) / 2; rubidium@10575: uint is_journey = (selected % 2 == 1) ? 1 : 0; maedhros@7562: rubidium@10575: if (order_number >= v->num_orders) order_number = 0; maedhros@7477: rubidium@10575: return v->index | (order_number << 16) | (is_journey << 24); rubidium@10575: } maedhros@7477: rubidium@10575: virtual void OnClick(Point pt, int widget) rubidium@10575: { rubidium@10575: const Vehicle *v = GetVehicle(this->window_number); maedhros@7477: rubidium@10575: switch (widget) { rubidium@10575: case TTV_ORDER_VIEW: /* Order view button */ rubidium@10575: ShowOrdersWindow(v); rubidium@10575: break; maedhros@7477: rubidium@10575: case TTV_TIMETABLE_PANEL: { /* Main panel. */ rubidium@10575: int selected = GetOrderFromTimetableWndPt(pt.y, v); rubidium@10575: rubidium@10575: if (selected == INVALID_ORDER || selected == this->sel_index) { rubidium@10575: /* Deselect clicked order */ rubidium@10575: this->sel_index = -1; rubidium@10575: } else { rubidium@10575: /* Select clicked order */ rubidium@10575: this->sel_index = selected; rubidium@10575: } rubidium@10575: } break; rubidium@10575: rubidium@10575: case TTV_CHANGE_TIME: { /* "Wait For" button. */ rubidium@10575: int selected = this->sel_index; rubidium@10575: VehicleOrderID real = (selected + 1) / 2; rubidium@10575: rubidium@10575: if (real >= v->num_orders) real = 0; rubidium@10575: rubidium@10575: const Order *order = GetVehicleOrder(v, real); rubidium@10575: StringID current = STR_EMPTY; rubidium@10575: rubidium@10575: if (order != NULL) { rubidium@10575: uint time = (selected % 2 == 1) ? order->travel_time : order->wait_time; rubidium@10775: if (!_settings_client.gui.timetable_in_ticks) time /= DAY_TICKS; rubidium@10575: rubidium@10575: if (time != 0) { rubidium@10575: SetDParam(0, time); rubidium@10575: current = STR_CONFIG_PATCHES_INT32; rubidium@10575: } rubidium@10575: } rubidium@10575: rubidium@10575: ShowQueryString(current, STR_TIMETABLE_CHANGE_TIME, 31, 150, this, CS_NUMERAL); rubidium@10575: } break; rubidium@10575: rubidium@10575: case TTV_CLEAR_TIME: { /* Clear waiting time button. */ rubidium@10575: uint32 p1 = PackTimetableArgs(v, this->sel_index); rubidium@10575: DoCommandP(0, p1, 0, NULL, CMD_CHANGE_TIMETABLE | CMD_MSG(STR_CAN_T_TIMETABLE_VEHICLE)); rubidium@10575: } break; rubidium@10575: rubidium@10575: case TTV_RESET_LATENESS: /* Reset the vehicle's late counter. */ rubidium@10575: DoCommandP(0, v->index, 0, NULL, CMD_SET_VEHICLE_ON_TIME | CMD_MSG(STR_CAN_T_TIMETABLE_VEHICLE)); rubidium@10575: break; rubidium@10575: rubidium@10575: case TTV_AUTOFILL: /* Autofill the timetable. */ rubidium@10575: DoCommandP(0, v->index, HasBit(v->vehicle_flags, VF_AUTOFILL_TIMETABLE) ? 0 : 1, NULL, CMD_AUTOFILL_TIMETABLE | CMD_MSG(STR_CAN_T_TIMETABLE_VEHICLE)); rubidium@10575: break; rubidium@10575: } rubidium@10575: rubidium@10575: this->SetDirty(); maedhros@7477: } rubidium@10575: rubidium@10575: virtual void OnQueryTextFinished(char *str) rubidium@10575: { rubidium@10575: if (str == NULL) return; rubidium@10575: rubidium@10575: const Vehicle *v = GetVehicle(this->window_number); rubidium@10575: rubidium@10575: uint32 p1 = PackTimetableArgs(v, this->sel_index); rubidium@10575: rubidium@10575: uint64 time = StrEmpty(str) ? 0 : strtoul(str, NULL, 10); rubidium@10775: if (!_settings_client.gui.timetable_in_ticks) time *= DAY_TICKS; rubidium@10575: skidd13@11090: uint32 p2 = minu(time, UINT16_MAX); rubidium@10575: rubidium@10575: DoCommandP(0, p1, p2, NULL, CMD_CHANGE_TIMETABLE | CMD_MSG(STR_CAN_T_TIMETABLE_VEHICLE)); rubidium@10575: } rubidium@10575: rubidium@10575: virtual void OnResize(Point new_size, Point delta) rubidium@10575: { rubidium@10575: /* Update the scroll + matrix */ rubidium@10575: this->vscroll.cap = (this->widget[TTV_TIMETABLE_PANEL].bottom - this->widget[TTV_TIMETABLE_PANEL].top) / 10; rubidium@10575: } rubidium@10575: }; maedhros@7477: maedhros@7477: static const Widget _timetable_widgets[] = { rubidium@9218: { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, // TTV_WIDGET_CLOSEBOX rubidium@9241: { WWT_CAPTION, RESIZE_RIGHT, 14, 11, 326, 0, 13, STR_TIMETABLE_TITLE, STR_018C_WINDOW_TITLE_DRAG_THIS}, // TTV_CAPTION rubidium@9241: { WWT_PUSHTXTBTN, RESIZE_LR, 14, 327, 387, 0, 13, STR_ORDER_VIEW, STR_ORDER_VIEW_TOOLTIP}, // TTV_ORDER_VIEW rubidium@9218: { WWT_STICKYBOX, RESIZE_LR, 14, 388, 399, 0, 13, STR_NULL, STR_STICKY_BUTTON}, // TTV_STICKY maedhros@7477: rubidium@9218: { WWT_PANEL, RESIZE_RB, 14, 0, 387, 14, 95, STR_NULL, STR_TIMETABLE_TOOLTIP}, // TTV_TIMETABLE_PANEL rubidium@9218: { WWT_SCROLLBAR, RESIZE_LRB, 14, 388, 399, 14, 95, STR_NULL, STR_0190_SCROLL_BAR_SCROLLS_LIST}, // TTV_SCROLLBAR maedhros@7477: rubidium@9218: { WWT_PANEL, RESIZE_RTB, 14, 0, 399, 96, 117, STR_NULL, STR_NULL}, // TTV_SUMMARY_PANEL maedhros@7553: rubidium@9218: { WWT_PUSHTXTBTN, RESIZE_TB, 14, 0, 109, 118, 129, STR_TIMETABLE_CHANGE_TIME, STR_TIMETABLE_WAIT_TIME_TOOLTIP}, // TTV_CHANGE_TIME rubidium@9218: { WWT_PUSHTXTBTN, RESIZE_TB, 14, 110, 219, 118, 129, STR_CLEAR_TIME, STR_TIMETABLE_CLEAR_TIME_TOOLTIP}, // TTV_CLEAR_TIME rubidium@9218: { WWT_PUSHTXTBTN, RESIZE_TB, 14, 220, 337, 118, 129, STR_RESET_LATENESS, STR_TIMETABLE_RESET_LATENESS_TOOLTIP}, // TTV_RESET_LATENESS rubidium@9218: { WWT_PUSHTXTBTN, RESIZE_TB, 14, 338, 387, 118, 129, STR_TIMETABLE_AUTOFILL, STR_TIMETABLE_AUTOFILL_TOOLTIP}, // TTV_AUTOFILL rubidium@9218: rubidium@9218: { WWT_PANEL, RESIZE_RTB, 14, 388, 387, 118, 129, STR_NULL, STR_NULL}, // TTV_EMPTY rubidium@9218: { WWT_RESIZEBOX, RESIZE_LRTB, 14, 388, 399, 118, 129, STR_NULL, STR_RESIZE_BUTTON}, // TTV_RESIZE maedhros@7477: maedhros@7477: { WIDGETS_END } maedhros@7477: }; maedhros@7477: maedhros@7477: static const WindowDesc _timetable_desc = { rubidium@7837: WDP_AUTO, WDP_AUTO, 400, 130, 400, 130, maedhros@7477: WC_VEHICLE_TIMETABLE, WC_VEHICLE_VIEW, maedhros@7477: WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE, maedhros@7477: _timetable_widgets, maedhros@7477: }; maedhros@7477: maedhros@7477: void ShowTimetableWindow(const Vehicle *v) maedhros@7477: { rubidium@10575: AllocateWindowDescFront(&_timetable_desc, v->index); maedhros@7477: }