uint DeparturesWindow<Twaypoint>::GetMinWidth() const { uint result = 0; /* Time */ result = cached_date_width; /* Vehicle type icon */ result += _settings_client.gui.departure_show_vehicle_type ? (GetStringBoundingBox(STR_DEPARTURES_TYPE_PLANE)).width : 0; /* Status */ result += cached_status_width; /* Find the maximum company name width. */ int toc_width = 0; /* Find the maximum company name width. */ int group_width = 0; /* Find the maximum vehicle name width. */ int veh_width = 0; if (_settings_client.gui.departure_show_vehicle || _settings_client.gui.departure_show_company || _settings_client.gui.departure_show_group) { for (uint i = 0; i < 4; ++i) { VehicleList vehicles; /* MAX_COMPANIES is probably the wrong thing to put here, but it works. GenerateVehicleSortList doesn't check the company when the type of list is VL_STATION_LIST (r20801). */ if (!GenerateVehicleSortList(&vehicles, VehicleListIdentifier(VL_STATION_LIST, (VehicleType)(VEH_TRAIN + i), MAX_COMPANIES, station).Pack())) { /* Something went wrong: panic! */ continue; } for (const Vehicle **v = vehicles.Begin(); v != vehicles.End(); v++) { SetDParam(0, (uint64)((*v)->index)); int width = (GetStringBoundingBox(STR_DEPARTURES_VEH)).width; if (_settings_client.gui.departure_show_vehicle && width > veh_width) veh_width = width; if ((*v)->group_id != INVALID_GROUP && (*v)->group_id != DEFAULT_GROUP) { SetDParam(0, (uint64)((*v)->group_id)); width = (GetStringBoundingBox(STR_DEPARTURES_GROUP)).width; if (_settings_client.gui.departure_show_group && width > group_width) group_width = width; } SetDParam(0, (uint64)((*v)->owner)); width = (GetStringBoundingBox(STR_DEPARTURES_TOC)).width; if (_settings_client.gui.departure_show_company && width > toc_width) toc_width = width; } } } result += toc_width + veh_width + group_width; return result + 140; }
void DeparturesWindow<Twaypoint>::DrawDeparturesListItems(const Rect &r) const { int left = r.left + WD_MATRIX_LEFT; int right = r.right - WD_MATRIX_RIGHT; bool rtl = _current_text_dir == TD_RTL; bool ltr = !rtl; int text_offset = WD_FRAMERECT_RIGHT; int text_left = left + (rtl ? 0 : text_offset); int text_right = right - (rtl ? text_offset : 0); int y = r.top + 1; uint max_departures = min(this->vscroll->GetPosition() + this->vscroll->GetCapacity(), this->departures->Length() + this->arrivals->Length()); if (max_departures > _settings_client.gui.max_departures) { max_departures = _settings_client.gui.max_departures; } byte small_font_size = _settings_client.gui.departure_larger_font ? FONT_HEIGHT_NORMAL : FONT_HEIGHT_SMALL; /* Draw the black background. */ GfxFillRect(r.left + 1, r.top, r.right - 1, r.bottom, PC_BLACK); /* Nothing selected? Then display the information text. */ bool none_selected[2] = {true, true}; for (uint i = 0; i < 4; ++i) { if (this->show_types[i]) { none_selected[0] = false; break; } } for (uint i = 0; i < 2; ++i) { if (this->departure_types[i]) { none_selected[1] = false; break; } } if (none_selected[0] || none_selected[1]) { DrawString(text_left, text_right, y + 1, STR_DEPARTURES_NONE_SELECTED); return; } /* No scheduled departures? Then display the information text. */ if (max_departures == 0) { DrawString(text_left, text_right, y + 1, STR_DEPARTURES_EMPTY); return; } /* Find the maximum possible width of the departure time and "Expt <time>" fields. */ int time_width = cached_date_width; if (!_settings_client.gui.departure_show_both) { time_width += (departure_types[0] && departure_types[1] ? cached_date_arrow_width : 0); } /* Vehicle type icon */ int type_width = _settings_client.gui.departure_show_vehicle_type ? (GetStringBoundingBox(STR_DEPARTURES_TYPE_PLANE)).width : 0; /* Find the maximum width of the status field */ int status_width = cached_status_width; /* Find the width of the "Calling at:" field. */ int calling_at_width = (GetStringBoundingBox(_settings_client.gui.departure_larger_font ? STR_DEPARTURES_CALLING_AT_LARGE : STR_DEPARTURES_CALLING_AT)).width; /* Find the maximum company name width. */ int toc_width = 0; /* Find the maximum group name width. */ int group_width = 0; /* Find the maximum vehicle name width. */ int veh_width = 0; if (_settings_client.gui.departure_show_vehicle || _settings_client.gui.departure_show_company || _settings_client.gui.departure_show_group) { for (uint i = 0; i < 4; ++i) { VehicleList vehicles; /* MAX_COMPANIES is probably the wrong thing to put here, but it works. GenerateVehicleSortList doesn't check the company when the type of list is VL_STATION_LIST (r20801). */ if (!GenerateVehicleSortList(&vehicles, VehicleListIdentifier(VL_STATION_LIST, (VehicleType)(VEH_TRAIN + i), MAX_COMPANIES, station).Pack())) { /* Something went wrong: panic! */ continue; } for (const Vehicle **v = vehicles.Begin(); v != vehicles.End(); v++) { SetDParam(0, (uint64)((*v)->index)); int width = (GetStringBoundingBox(STR_DEPARTURES_VEH)).width; if (_settings_client.gui.departure_show_vehicle && width > veh_width) veh_width = width; if ((*v)->group_id != INVALID_GROUP && (*v)->group_id != DEFAULT_GROUP) { SetDParam(0, (uint64)((*v)->group_id)); width = (GetStringBoundingBox(STR_DEPARTURES_GROUP)).width; if (_settings_client.gui.departure_show_group && width > group_width) group_width = width; } SetDParam(0, (uint64)((*v)->owner)); width = (GetStringBoundingBox(STR_DEPARTURES_TOC)).width; if (_settings_client.gui.departure_show_company && width > toc_width) toc_width = width; } } } uint departure = 0; uint arrival = 0; /* Draw each departure. */ for (uint i = 0; i < max_departures; ++i) { const Departure *d; if (arrival == this->arrivals->Length()) { d = (*(this->departures))[departure++]; } else if (departure == this->departures->Length()) { d = (*(this->arrivals))[arrival++]; } else { d = (*(this->departures))[departure]; const Departure *a = (*(this->arrivals))[arrival]; if (a->scheduled_date < d->scheduled_date) { d = a; arrival++; } else { departure++; } } if (i < this->vscroll->GetPosition()) { continue; } /* If for some reason the departure is too far in the future or is at a negative time, skip it. */ if ((d->scheduled_date / DAY_TICKS) > (_date + _settings_client.gui.max_departure_time) || d->scheduled_date < 0) { continue; } if (d->terminus == INVALID_STATION) continue; StringID time_str = (departure_types[0] && departure_types[1]) ? (d->type == D_DEPARTURE ? STR_DEPARTURES_TIME_DEP : STR_DEPARTURES_TIME_ARR) : STR_DEPARTURES_TIME; if (_settings_client.gui.departure_show_both) time_str = STR_DEPARTURES_TIME_BOTH; /* Time */ SetDParam(0, d->scheduled_date); SetDParam(1, d->scheduled_date - d->order->wait_time); ltr ? DrawString( text_left, text_left + time_width, y + 1, time_str) : DrawString(text_right - time_width, text_right, y + 1, time_str); /* Vehicle type icon, with thanks to sph */ if (_settings_client.gui.departure_show_vehicle_type) { StringID type = STR_DEPARTURES_TYPE_TRAIN; int offset = (_settings_client.gui.departure_show_vehicle_color ? 1 : 0); switch (d->vehicle->type) { case VEH_TRAIN: type = STR_DEPARTURES_TYPE_TRAIN; break; case VEH_ROAD: type = IsCargoInClass(d->vehicle->cargo_type, CC_PASSENGERS) ? STR_DEPARTURES_TYPE_BUS : STR_DEPARTURES_TYPE_LORRY; break; case VEH_SHIP: type = STR_DEPARTURES_TYPE_SHIP; break; case VEH_AIRCRAFT: type = STR_DEPARTURES_TYPE_PLANE; break; default: break; } type += offset; DrawString(text_left + time_width + 3, text_left + time_width + type_width + 3, y, type); } /* The icons to show with the destination and via stations. */ StringID icon = STR_DEPARTURES_STATION_NONE; StringID icon_via = STR_DEPARTURES_STATION_NONE; if (_settings_client.gui.departure_destination_type) { Station *t = Station::Get(d->terminus.station); if (t->facilities & FACIL_DOCK && t->facilities & FACIL_AIRPORT && d->vehicle->type != VEH_SHIP && d->vehicle->type != VEH_AIRCRAFT) { icon = STR_DEPARTURES_STATION_PORTAIRPORT; } else if (t->facilities & FACIL_DOCK && d->vehicle->type != VEH_SHIP) { icon = STR_DEPARTURES_STATION_PORT; } else if (t->facilities & FACIL_AIRPORT && d->vehicle->type != VEH_AIRCRAFT) { icon = STR_DEPARTURES_STATION_AIRPORT; } } if (_settings_client.gui.departure_destination_type && d->via != INVALID_STATION) { Station *t = Station::Get(d->via); if (t->facilities & FACIL_DOCK && t->facilities & FACIL_AIRPORT && d->vehicle->type != VEH_SHIP && d->vehicle->type != VEH_AIRCRAFT) { icon_via = STR_DEPARTURES_STATION_PORTAIRPORT; } else if (t->facilities & FACIL_DOCK && d->vehicle->type != VEH_SHIP) { icon_via = STR_DEPARTURES_STATION_PORT; } else if (t->facilities & FACIL_AIRPORT && d->vehicle->type != VEH_AIRCRAFT) { icon_via = STR_DEPARTURES_STATION_AIRPORT; } } /* Destination */ if (d->via == INVALID_STATION) { /* Only show the terminus. */ SetDParam(0, d->terminus.station); SetDParam(1, icon); ltr ? DrawString( text_left + time_width + type_width + 6, text_right - status_width - (toc_width + veh_width + group_width + 2) - 2, y + 1, STR_DEPARTURES_TERMINUS) : DrawString(text_left + status_width + (toc_width + veh_width + group_width + 2) + 2, text_right - time_width - type_width - 6, y + 1, STR_DEPARTURES_TERMINUS); } else { /* Show the terminus and the via station. */ SetDParam(0, d->terminus.station); SetDParam(1, icon); SetDParam(2, d->via); SetDParam(3, icon_via); int text_width = (GetStringBoundingBox(STR_DEPARTURES_TERMINUS_VIA_STATION)).width; if (text_width < text_right - status_width - (toc_width + veh_width + group_width + 2) - 2 - (text_left + time_width + type_width + 6)) { /* They will both fit, so show them both. */ SetDParam(0, d->terminus.station); SetDParam(1, icon); SetDParam(2, d->via); SetDParam(3, icon_via); ltr ? DrawString( text_left + time_width + type_width + 6, text_right - status_width - (toc_width + veh_width + group_width + 2) - 2, y + 1, STR_DEPARTURES_TERMINUS_VIA_STATION) : DrawString(text_left + status_width + (toc_width + veh_width + group_width + 2) + 2, text_right - time_width - type_width - 6, y + 1, STR_DEPARTURES_TERMINUS_VIA_STATION); } else { /* They won't both fit, so switch between showing the terminus and the via station approximately every 4 seconds. */ if (this->tick_count & (1 << 7)) { SetDParam(0, d->via); SetDParam(1, icon_via); ltr ? DrawString( text_left + time_width + type_width + 6, text_right - status_width - (toc_width + veh_width + group_width + 2) - 2, y + 1, STR_DEPARTURES_VIA) : DrawString(text_left + status_width + (toc_width + veh_width + group_width + 2) + 2, text_right - time_width - type_width - 6, y + 1, STR_DEPARTURES_VIA); } else { SetDParam(0, d->terminus.station); SetDParam(1, icon); ltr ? DrawString( text_left + time_width + type_width + 6, text_right - status_width - (toc_width + veh_width + group_width + 2) - 2, y + 1, STR_DEPARTURES_TERMINUS_VIA) : DrawString(text_left + status_width + (toc_width + veh_width + group_width + 2) + 2, text_right - time_width - type_width - 6, y + 1, STR_DEPARTURES_TERMINUS_VIA); } } } /* Status */ { int status_left = ltr ? text_right - status_width - 2 - (toc_width + veh_width + group_width + 2) : text_left + (toc_width + veh_width + group_width + 2) + 7; int status_right = ltr ? text_right - (toc_width + veh_width + group_width + 2) + 2 : text_left + status_width + 2 + (toc_width + veh_width + group_width + 7); if (d->status == D_ARRIVED) { /* The vehicle has arrived. */ DrawString(status_left, status_right, y + 1, STR_DEPARTURES_ARRIVED); } else if(d->status == D_CANCELLED) { /* The vehicle has been cancelled. */ DrawString(status_left, status_right, y + 1, STR_DEPARTURES_CANCELLED); } else{ if (d->lateness <= DAY_TICKS && d->scheduled_date > ((_date * DAY_TICKS) + _date_fract)) { /* We have no evidence that the vehicle is late, so assume it is on time. */ DrawString(status_left, status_right, y + 1, STR_DEPARTURES_ON_TIME); } else { if ((d->scheduled_date + d->lateness) < ((_date * DAY_TICKS) + _date_fract)) { /* The vehicle was expected to have arrived by now, even if we knew it was going to be late. */ /* We assume that the train stays at least a day at a station so it won't accidentally be marked as delayed for a fraction of a day. */ DrawString(status_left, status_right, y + 1, STR_DEPARTURES_DELAYED); } else { /* The vehicle is expected to be late and is not yet due to arrive. */ SetDParam(0, d->scheduled_date + d->lateness); DrawString(status_left, status_right, y + 1, STR_DEPARTURES_EXPECTED); } } } } /* Vehicle name */ if (_settings_client.gui.departure_show_vehicle) { SetDParam(0, (uint64)(d->vehicle->index)); ltr ? DrawString(text_right - (toc_width + veh_width + group_width + 2), text_right - toc_width - group_width - 2, y + 1, STR_DEPARTURES_VEH) : DrawString( text_left + toc_width + group_width + 2, text_left + (toc_width + veh_width + group_width + 2), y + 1, STR_DEPARTURES_VEH); } /* Group name */ if (_settings_client.gui.departure_show_group && d->vehicle->group_id != INVALID_GROUP && d->vehicle->group_id != DEFAULT_GROUP) { SetDParam(0, (uint64)(d->vehicle->group_id)); ltr ? DrawString(text_right - (toc_width + group_width + 2), text_right - toc_width - 2, y + 1, STR_DEPARTURES_GROUP) : DrawString( text_left + toc_width + 2, text_left + (toc_width + group_width + 2), y + 1, STR_DEPARTURES_GROUP); } /* Operating company */ if (_settings_client.gui.departure_show_company) { SetDParam(0, (uint64)(d->vehicle->owner)); ltr ? DrawString(text_right - toc_width, text_right, y + 1, STR_DEPARTURES_TOC, TC_FROMSTRING, SA_RIGHT) : DrawString( text_left, text_left + toc_width, y + 1, STR_DEPARTURES_TOC, TC_FROMSTRING, SA_LEFT); } int bottom_y = y + this->entry_height - small_font_size - (_settings_client.gui.departure_larger_font ? 1 : 3); /* Calling at */ ltr ? DrawString( text_left, text_left + calling_at_width, bottom_y, _settings_client.gui.departure_larger_font ? STR_DEPARTURES_CALLING_AT_LARGE : STR_DEPARTURES_CALLING_AT) : DrawString(text_right - calling_at_width, text_right, bottom_y, _settings_client.gui.departure_larger_font ? STR_DEPARTURES_CALLING_AT_LARGE : STR_DEPARTURES_CALLING_AT); /* List of stations */ /* RTL languages can be handled in the language file, e.g. by having the following: */ /* STR_DEPARTURES_CALLING_AT_STATION :{STATION}, {RAW_STRING} */ /* STR_DEPARTURES_CALLING_AT_LAST_STATION :{STATION} & {RAW_STRING}*/ char buffer[512], scratch[512]; if (d->calling_at.Length() != 0) { SetDParam(0, (uint64)(*d->calling_at.Get(0)).station); GetString(scratch, STR_DEPARTURES_CALLING_AT_FIRST_STATION, lastof(scratch)); StationID continuesTo = INVALID_STATION; if (d->calling_at.Get(0)->station == d->terminus.station && d->calling_at.Length() > 1) { continuesTo = d->calling_at.Get(d->calling_at.Length() - 1)->station; } else if (d->calling_at.Length() > 1) { /* There's more than one stop. */ uint i; /* For all but the last station, write out ", <station>". */ for (i = 1; i < d->calling_at.Length() - 1; ++i) { StationID s = d->calling_at.Get(i)->station; if (s == d->terminus.station) { continuesTo = d->calling_at.Get(d->calling_at.Length() - 1)->station; break; } SetDParam(0, (uint64)scratch); SetDParam(1, (uint64)s); GetString(buffer, STR_DEPARTURES_CALLING_AT_STATION, lastof(buffer)); strncpy(scratch, buffer, sizeof(scratch)); } /* Finally, finish off with " and <station>". */ SetDParam(0, (uint64)scratch); SetDParam(1, (uint64)d->calling_at.Get(i)->station); GetString(buffer, STR_DEPARTURES_CALLING_AT_LAST_STATION, lastof(buffer)); strncpy(scratch, buffer, sizeof(scratch)); } SetDParam(0, (uint64)scratch); StringID string; if (continuesTo == INVALID_STATION) { string = _settings_client.gui.departure_larger_font ? STR_DEPARTURES_CALLING_AT_LIST_LARGE : STR_DEPARTURES_CALLING_AT_LIST; } else { SetDParam(1, continuesTo); string = _settings_client.gui.departure_larger_font ? STR_DEPARTURES_CALLING_AT_LIST_SMART_TERMINUS_LARGE : STR_DEPARTURES_CALLING_AT_LIST_SMART_TERMINUS; } GetString(buffer, string, lastof(buffer)); } else { buffer[0] = 0; //SetDParam(0, d->terminus); //GetString(scratch, STR_DEPARTURES_CALLING_AT_FIRST_STATION, lastof(scratch)); } int list_width = (GetStringBoundingBox(buffer, _settings_client.gui.departure_larger_font ? FS_NORMAL : FS_SMALL)).width; /* Draw the whole list if it will fit. Otherwise scroll it. */ if (list_width < text_right - (text_left + calling_at_width + 2)) { ltr ? DrawString(text_left + calling_at_width + 2, text_right, bottom_y, buffer) : DrawString( text_left, text_right - calling_at_width - 2, bottom_y, buffer); } else { DrawPixelInfo tmp_dpi; if (ltr ? !FillDrawPixelInfo(&tmp_dpi, text_left + calling_at_width + 2, bottom_y, text_right - (text_left + calling_at_width + 2), small_font_size + 3) : !FillDrawPixelInfo(&tmp_dpi, text_left , bottom_y, text_right - (text_left + calling_at_width + 2), small_font_size + 3)) { y += this->entry_height; continue; } DrawPixelInfo *old_dpi = _cur_dpi; _cur_dpi = &tmp_dpi; /* The scrolling text starts out of view at the right of the screen and finishes when it is out of view at the left of the screen. */ int pos = ltr ? text_right - (this->tick_count % (list_width + text_right - text_left)) : text_left + (this->tick_count % (list_width + text_right - text_left)); ltr ? DrawString( pos, INT16_MAX, 0, buffer, TC_FROMSTRING, SA_LEFT | SA_FORCE) : DrawString(-INT16_MAX, pos, 0, buffer, TC_FROMSTRING, SA_RIGHT | SA_FORCE); _cur_dpi = old_dpi; } y += this->entry_height; } }