diff --git a/src/lang/extra/english.txt b/src/lang/extra/english.txt index d55e70883e..ec850ca802 100644 --- a/src/lang/extra/english.txt +++ b/src/lang/extra/english.txt @@ -2078,6 +2078,7 @@ STR_SCHDISPATCH_ADJUST :{BLACK}Adjust S STR_SCHDISPATCH_ADJUST_TOOLTIP :{BLACK}Adjust all departure slots in this schedule. STR_SCHDISPATCH_REMOVE :{BLACK}Remove Slots STR_SCHDISPATCH_REMOVE_TOOLTIP :{BLACK}Enable removing departure slots from this schedule. +STR_SCHDISPATCH_REMOVE_SLOT :{BLACK}Remove departure slot STR_SCHDISPATCH_ADJUST_CAPTION_MINUTE :{BLACK}Adjustment to add (minutes) STR_SCHDISPATCH_ADJUST_CAPTION_DAY :{BLACK}Adjustment to add (days) STR_SCHDISPATCH_ADJUST_CAPTION_TICKS :{BLACK}Adjustment to add (ticks) @@ -2112,6 +2113,12 @@ STR_SCHDISPATCH_RENAME_SCHEDULE_CAPTION :{WHITE}Name sch STR_ERROR_CAN_T_RENAME_SCHEDULE :{WHITE}Can't name schedule... STR_SCHDISPATCH_MOVE_SCHEDULE :{BLACK}Change position of current schedule in schedule list STR_SCHDISPATCH_DATE_WALLCLOCK_TINY_FLAGGED :{DATE_WALLCLOCK_TINY}* +STR_SCHDISPATCH_SLOT_TOOLTIP :{BLACK}{DATE_WALLCLOCK_TINY} +STR_SCHDISPATCH_SLOT_TOOLTIP_RELATIVE : ({TIME_HHMM} from start) +STR_SCHDISPATCH_SLOT_TOOLTIP_LAST :{}Last departure slot +STR_SCHDISPATCH_SLOT_TOOLTIP_NEXT :{}Next available departure slot +STR_SCHDISPATCH_SLOT_TOOLTIP_REUSE :{}Departure slot may be used more than once +STR_SCHDISPATCH_SLOT_TOOLTIP_TIME_SUFFIX : ({DATE_WALLCLOCK_TINY}) STR_SCHDISPATCH_SUMMARY_NO_LAST_DEPARTURE :{BLACK}No previous departures. STR_SCHDISPATCH_SUMMARY_LAST_DEPARTURE_PAST :{BLACK}Last departure at {DATE_WALLCLOCK_TINY}. diff --git a/src/schdispatch_gui.cpp b/src/schdispatch_gui.cpp index bcd2bf783f..3942fd96e7 100644 --- a/src/schdispatch_gui.cpp +++ b/src/schdispatch_gui.cpp @@ -455,6 +455,69 @@ struct SchdispatchWindow : GeneralVehicleWindow { return true; } + case WID_SCHDISPATCH_MATRIX: { + NWidgetBase *nwi = this->GetWidget(WID_SCHDISPATCH_MATRIX); + const DispatchSlot *slot; + bool is_header; + std::tie(slot, is_header) = this->GetSlotFromMatrixPoint(pt.x - nwi->pos_x, pt.y - nwi->pos_y); + if (slot == nullptr) return false; + + if (is_header && this->remove_slot_mode) { + GuiShowTooltips(this, STR_SCHDISPATCH_REMOVE_SLOT, close_cond); + } else { + const DispatchSchedule &ds = this->GetSelectedSchedule(); + const DateTicksScaled start_tick = ds.GetScheduledDispatchStartTick(); + + SetDParam(0, start_tick + slot->offset); + _temp_special_strings[0] = GetString(STR_SCHDISPATCH_SLOT_TOOLTIP); + if (_settings_time.time_in_minutes) { + ClockFaceMinutes start_minutes = _settings_time.ToTickMinutes(start_tick).ToClockFaceMinutes(); + if (start_minutes != 0) { + TickMinutes offset_minutes = slot->offset / _settings_time.ticks_per_minute; + SetDParam(0, offset_minutes.ClockHHMM()); + _temp_special_strings[0] += GetString(STR_SCHDISPATCH_SLOT_TOOLTIP_RELATIVE); + } + } + + bool have_last = false; + if (ds.GetScheduledDispatchLastDispatch() % ds.GetScheduledDispatchDuration() == slot->offset) { + _temp_special_strings[0] += '\n'; + _temp_special_strings[0] += GetString(STR_SCHDISPATCH_SLOT_TOOLTIP_LAST); + if (_settings_time.time_in_minutes) { + ClockFaceMinutes mins = _settings_time.ToTickMinutes(start_tick + ds.GetScheduledDispatchLastDispatch()).ToClockFaceMinutes(); + if (mins != _settings_time.ToTickMinutes(start_tick + slot->offset).ToClockFaceMinutes()) { + SetDParam(0, start_tick + ds.GetScheduledDispatchLastDispatch()); + _temp_special_strings[0] += GetString(STR_SCHDISPATCH_SLOT_TOOLTIP_TIME_SUFFIX); + } + } + have_last = true; + } + DateTicksScaled next_slot = GetScheduledDispatchTime(ds, _scaled_date_ticks); + if ((next_slot - ds.GetScheduledDispatchStartTick()).AsTicks() % ds.GetScheduledDispatchDuration() == slot->offset) { + if (!have_last) _temp_special_strings[0] += '\n'; + _temp_special_strings[0] += GetString(STR_SCHDISPATCH_SLOT_TOOLTIP_NEXT); + if (_settings_time.time_in_minutes) { + ClockFaceMinutes mins = _settings_time.ToTickMinutes(next_slot).ToClockFaceMinutes(); + if (mins != _settings_time.ToTickMinutes(start_tick + slot->offset).ToClockFaceMinutes()) { + SetDParam(0, next_slot); + _temp_special_strings[0] += GetString(STR_SCHDISPATCH_SLOT_TOOLTIP_TIME_SUFFIX); + } + } + } + + auto flags = slot->flags; + if (ds.GetScheduledDispatchReuseSlots()) ClrBit(flags, DispatchSlot::SDSF_REUSE_SLOT); + if (flags != 0) { + _temp_special_strings[0] += '\n'; + if (HasBit(flags, DispatchSlot::SDSF_REUSE_SLOT)) { + _temp_special_strings[0] += GetString(STR_SCHDISPATCH_SLOT_TOOLTIP_REUSE); + } + } + GuiShowTooltips(this, SPECSTR_TEMP_START, close_cond); + } + return true; + } + default: break; } @@ -763,13 +826,14 @@ struct SchdispatchWindow : GeneralVehicleWindow { } /** - * Handle click in the departure time matrix. + * Get slot and whether it's in the header section in the departure time matrix. * @param x Horizontal position in the matrix widget in pixels. * @param y Vertical position in the matrix widget in pixels. + * @return slot, and whether the position was in the header section */ - void TimeClick(int x, int y) + std::pair GetSlotFromMatrixPoint(int x, int y) const { - if (!this->IsScheduleSelected()) return; + if (!this->IsScheduleSelected()) return { nullptr, false }; const NWidgetCore *matrix_widget = this->GetWidget(WID_SCHDISPATCH_MATRIX); /* In case of RTL the widgets are swapped as a whole */ @@ -777,16 +841,31 @@ struct SchdispatchWindow : GeneralVehicleWindow { uint xt = x / this->resize.step_width; int xm = x % this->resize.step_width; - if (xt >= this->num_columns) return; + if (xt >= this->num_columns) return { nullptr, false }; uint row = y / this->resize.step_height; - if (row >= this->vscroll->GetCapacity()) return; + if (row >= this->vscroll->GetCapacity()) return { nullptr, false }; uint pos = ((row + this->vscroll->GetPosition()) * this->num_columns) + xt; const DispatchSchedule &ds = this->GetSelectedSchedule(); + if (pos >= this->item_count || pos >= ds.GetScheduledDispatch().size()) return { nullptr, false }; - if (pos >= this->item_count || pos >= ds.GetScheduledDispatch().size()) { + return { &ds.GetScheduledDispatch()[pos], xm <= this->header_width }; + } + + /** + * Handle click in the departure time matrix. + * @param x Horizontal position in the matrix widget in pixels. + * @param y Vertical position in the matrix widget in pixels. + */ + void TimeClick(int x, int y) + { + const DispatchSlot *slot; + bool is_header; + std::tie(slot, is_header) = this->GetSlotFromMatrixPoint(x, y); + + if (slot == nullptr) { if (this->selected_slot != UINT32_MAX) { this->selected_slot = UINT32_MAX; this->SetWidgetDirty(WID_SCHDISPATCH_MATRIX); @@ -794,16 +873,15 @@ struct SchdispatchWindow : GeneralVehicleWindow { return; } - uint32_t offset = ds.GetScheduledDispatch()[pos].offset; - - if (xm <= this->header_width && this->remove_slot_mode) { - DoCommandP(0, this->vehicle->index | (this->schedule_index << 20), offset, CMD_SCHEDULED_DISPATCH_REMOVE | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE)); + if (is_header && this->remove_slot_mode) { + DoCommandP(0, this->vehicle->index | (this->schedule_index << 20), slot->offset, CMD_SCHEDULED_DISPATCH_REMOVE | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE)); + return; } - if (this->selected_slot == offset) { + if (this->selected_slot == slot->offset) { this->selected_slot = UINT32_MAX; } else { - this->selected_slot = offset; + this->selected_slot = slot->offset; } this->SetWidgetDirty(WID_SCHDISPATCH_MATRIX); }