Codechange: Use std::map instead of fixed array to store refit options.

This simplifies handling of available refit options.
This commit is contained in:
Peter Nelson 2023-10-02 08:12:56 +01:00 committed by Peter Nelson
parent e4f94747f3
commit 90351578a6
1 changed files with 85 additions and 119 deletions

View File

@ -550,18 +550,18 @@ struct RefitOption {
}
};
typedef std::vector<RefitOption> SubtypeList; ///< List of refit subtypes associated to a cargo.
using RefitOptions = std::map<CargoID, std::vector<RefitOption>, CargoIDComparator>; ///< Available refit options (subtype and string) associated with each cargo type.
/**
* Draw the list of available refit options for a consist and highlight the selected refit option (if any).
* @param list List of subtype options for each (sorted) cargo.
* @param sel Selected refit cargo-type in the window
* @param refits Available refit options for each (sorted) cargo.
* @param sel Selected refit option in the window
* @param pos Position of the selected item in caller widow
* @param rows Number of rows(capacity) in caller window
* @param delta Step height in caller window
* @param r Rectangle of the matrix widget.
*/
static void DrawVehicleRefitWindow(const SubtypeList list[NUM_CARGO], const int sel[2], uint pos, uint rows, uint delta, const Rect &r)
static void DrawVehicleRefitWindow(const RefitOptions &refits, const RefitOption *sel, uint pos, uint rows, uint delta, const Rect &r)
{
Rect ir = r.Shrink(WidgetDimensions::scaled.matrix);
uint current = 0;
@ -578,12 +578,13 @@ static void DrawVehicleRefitWindow(const SubtypeList list[NUM_CARGO], const int
Rect tr = ir.Indent(iconwidth + WidgetDimensions::scaled.hsep_wide, rtl);
/* Draw the list of subtypes for each cargo, and find the selected refit option (by its position). */
for (uint i = 0; current < pos + rows && i < NUM_CARGO; i++) {
for (uint j = 0; current < pos + rows && j < list[i].size(); j++) {
const RefitOption &refit = list[i][j];
for (const auto &pair : refits) {
bool has_subtypes = pair.second.size() > 1;
for (const RefitOption &refit : pair.second) {
if (current >= pos + rows) break;
/* Hide subtypes if sel[0] does not match */
if (sel[0] != (int)i && refit.subtype != 0xFF) continue;
/* Hide subtypes if selected cargo type does not match */
if ((sel == nullptr || sel->cargo != refit.cargo) && refit.subtype != UINT8_MAX) continue;
/* Refit options with a position smaller than pos don't have to be drawn. */
if (current < pos) {
@ -591,19 +592,19 @@ static void DrawVehicleRefitWindow(const SubtypeList list[NUM_CARGO], const int
continue;
}
if (list[i].size() > 1) {
if (refit.subtype != 0xFF) {
if (has_subtypes) {
if (refit.subtype != UINT8_MAX) {
/* Draw tree lines */
int ycenter = tr.top + FONT_HEIGHT_NORMAL / 2;
GfxDrawLine(iconcenter, tr.top - WidgetDimensions::scaled.matrix.top, iconcenter, j == list[i].size() - 1 ? ycenter : tr.top - WidgetDimensions::scaled.matrix.top + delta - 1, linecolour);
GfxDrawLine(iconcenter, tr.top - WidgetDimensions::scaled.matrix.top, iconcenter, (&refit == &pair.second.back()) ? ycenter : tr.top - WidgetDimensions::scaled.matrix.top + delta - 1, linecolour);
GfxDrawLine(iconcenter, ycenter, iconinner, ycenter, linecolour);
} else {
/* Draw expand/collapse icon */
DrawSprite(sel[0] == (int)i ? SPR_CIRCLE_UNFOLDED : SPR_CIRCLE_FOLDED, PAL_NONE, iconleft, tr.top + (FONT_HEIGHT_NORMAL - iconheight) / 2);
DrawSprite((sel != nullptr && sel->cargo == refit.cargo) ? SPR_CIRCLE_UNFOLDED : SPR_CIRCLE_FOLDED, PAL_NONE, iconleft, tr.top + (FONT_HEIGHT_NORMAL - iconheight) / 2);
}
}
TextColour colour = (sel[0] == (int)i && (uint)sel[1] == j) ? TC_WHITE : TC_BLACK;
TextColour colour = (sel != nullptr && sel->cargo == refit.cargo && sel->subtype == refit.subtype) ? TC_WHITE : TC_BLACK;
/* Get the cargo name. */
SetDParam(0, CargoSpec::Get(refit.cargo)->name);
SetDParam(1, refit.string);
@ -617,9 +618,8 @@ static void DrawVehicleRefitWindow(const SubtypeList list[NUM_CARGO], const int
/** Refit cargo window. */
struct RefitWindow : public Window {
int sel[2]; ///< Index in refit options, sel[0] == -1 if nothing is selected.
RefitOption *cargo; ///< Refit option selected by #sel.
SubtypeList list[NUM_CARGO]; ///< List of refit subtypes available for each sorted cargo.
const RefitOption *selected_refit; ///< Selected refit option.
RefitOptions refit_list; ///< List of refit subtypes available for each sorted cargo.
VehicleOrderID order; ///< If not #INVALID_VEH_ORDER_ID, selection is part of a refit order (rather than execute directly).
uint information_width; ///< Width required for correctly displaying all cargoes in the information panel.
Scrollbar *vscroll; ///< The main scrollbar.
@ -638,7 +638,12 @@ struct RefitWindow : public Window {
*/
void BuildRefitList()
{
for (uint i = 0; i < NUM_CARGO; i++) this->list[i].clear();
/* Store the currently selected RefitOption. */
std::optional<RefitOption> current_refit_option;
if (this->selected_refit != nullptr) current_refit_option = *(this->selected_refit);
this->selected_refit = nullptr;
this->refit_list.clear();
Vehicle *v = Vehicle::Get(this->window_number);
/* Check only the selected vehicles. */
@ -657,19 +662,16 @@ struct RefitWindow : public Window {
if (this->auto_refit && !HasBit(e->info.misc_flags, EF_AUTO_REFIT)) continue;
/* Loop through all cargoes in the refit mask */
int current_index = 0;
for (const auto &cs : _sorted_cargo_specs) {
CargoID cid = cs->Index();
/* Skip cargo type if it's not listed */
if (!HasBit(cmask, cid)) {
current_index++;
continue;
}
if (!HasBit(cmask, cid)) continue;
bool first_vehicle = this->list[current_index].size() == 0;
auto &list = this->refit_list[cid];
bool first_vehicle = list.size() == 0;
if (first_vehicle) {
/* Keeping the current subtype is always an option. It also serves as the option in case of no subtypes */
this->list[current_index].push_back({cid, 0xFF, STR_EMPTY});
list.push_back({cid, UINT8_MAX, STR_EMPTY});
}
/* Check the vehicle's callback mask for cargo suffixes.
@ -701,16 +703,15 @@ struct RefitWindow : public Window {
option.cargo = cid;
option.subtype = refit_cyc;
option.string = subtype;
include(this->list[current_index], option);
include(list, option);
} else {
/* Intersect the subtypes of earlier vehicles with the subtypes of this vehicle */
if (subtype == STR_EMPTY) {
/* No more subtypes for this vehicle, delete all subtypes >= refit_cyc */
SubtypeList &l = this->list[current_index];
/* 0xFF item is in front, other subtypes are sorted. So just truncate the list in the right spot */
for (uint i = 1; i < l.size(); i++) {
if (l[i].subtype >= refit_cyc) {
l.resize(i);
/* UINT8_MAX item is in front, other subtypes are sorted. So just truncate the list in the right spot */
for (uint i = 1; i < list.size(); i++) {
if (list[i].subtype >= refit_cyc) {
list.resize(i);
break;
}
}
@ -718,11 +719,10 @@ struct RefitWindow : public Window {
} else {
/* Check whether the subtype matches with the subtype of earlier vehicles. */
uint pos = 1;
SubtypeList &l = this->list[current_index];
while (pos < l.size() && l[pos].subtype != refit_cyc) pos++;
if (pos < l.size() && l[pos].string != subtype) {
while (pos < list.size() && list[pos].subtype != refit_cyc) pos++;
if (pos < list.size() && list[pos].string != subtype) {
/* String mismatch, remove item keeping the order */
l.erase(l.begin() + pos);
list.erase(list.begin() + pos);
}
}
}
@ -736,9 +736,23 @@ struct RefitWindow : public Window {
v->First()->InvalidateNewGRFCache();
v->InvalidateNewGRFCache();
}
current_index++;
}
} while (v->IsGroundVehicle() && (v = v->Next()) != nullptr);
/* Restore the previously selected RefitOption. */
if (current_refit_option.has_value()) {
for (const auto &pair : this->refit_list) {
for (const auto &refit : pair.second) {
if (refit.cargo == current_refit_option->cargo && refit.subtype == current_refit_option->subtype) {
this->selected_refit = &refit;
break;
}
}
if (this->selected_refit != nullptr) break;
}
}
this->SetWidgetDisabledState(WID_VR_REFIT, this->selected_refit == nullptr);
}
/**
@ -746,24 +760,22 @@ struct RefitWindow : public Window {
*/
void RefreshScrollbar()
{
uint scroll_row = 0;
uint row = 0;
size_t scroll_row = 0;
size_t rows = 0;
CargoID cargo = this->selected_refit == nullptr ? (CargoID)CT_INVALID : this->selected_refit->cargo;
for (uint i = 0; i < NUM_CARGO; i++) {
for (uint j = 0; j < this->list[i].size(); j++) {
const RefitOption &refit = this->list[i][j];
/* Hide subtypes if sel[0] does not match */
if (this->sel[0] != (int)i && refit.subtype != 0xFF) continue;
if (this->sel[0] == (int)i && (uint)this->sel[1] == j) scroll_row = row;
row++;
for (const auto &pair : this->refit_list) {
if (pair.first == cargo) {
/* selected_refit points to an element in the vector so no need to search for it. */
scroll_row = rows + (this->selected_refit - pair.second.data());
rows += pair.second.size();
} else {
rows++; /* Unselected cargo type is collapsed into one row. */
}
}
this->vscroll->SetCount(row);
if (scroll_row < row) this->vscroll->ScrollTowards(scroll_row);
this->vscroll->SetCount(rows);
this->vscroll->ScrollTowards(static_cast<int>(scroll_row));
}
/**
@ -774,45 +786,24 @@ struct RefitWindow : public Window {
{
uint row = 0;
for (uint i = 0; i < NUM_CARGO; i++) {
for (uint j = 0; j < this->list[i].size(); j++) {
const RefitOption &refit = this->list[i][j];
/* Hide subtypes if sel[0] does not match */
if (this->sel[0] != (int)i && refit.subtype != 0xFF) continue;
for (const auto &pair : refit_list) {
for (const RefitOption &refit : pair.second) {
if (row == click_row) {
this->sel[0] = i;
this->sel[1] = j;
this->selected_refit = &refit;
return;
}
row++;
/* If this cargo type is not already selected then its subtypes are not visible, so skip the rest. */
if (this->selected_refit == nullptr || this->selected_refit->cargo != refit.cargo) break;
}
}
this->sel[0] = -1;
this->sel[1] = 0;
}
/**
* Gets the #RefitOption placed in the selected index.
* @return Pointer to the #RefitOption currently in use.
*/
RefitOption *GetRefitOption()
{
if (this->sel[0] < 0) return nullptr;
SubtypeList &l = this->list[this->sel[0]];
if ((uint)this->sel[1] >= l.size()) return nullptr;
return &l[this->sel[1]];
/* No selection made */
this->selected_refit = nullptr;
}
RefitWindow(WindowDesc *desc, const Vehicle *v, VehicleOrderID order, bool auto_refit) : Window(desc)
{
this->sel[0] = -1;
this->sel[1] = 0;
this->auto_refit = auto_refit;
this->order = order;
this->CreateNestedTree();
@ -830,37 +821,13 @@ struct RefitWindow : public Window {
this->FinishInitNested(v->index);
this->owner = v->owner;
this->SetWidgetDisabledState(WID_VR_REFIT, this->sel[0] < 0);
this->SetWidgetDisabledState(WID_VR_REFIT, this->selected_refit == nullptr);
}
void OnInit() override
{
if (this->cargo != nullptr) {
/* Store the RefitOption currently in use. */
RefitOption current_refit_option = *(this->cargo);
/* Rebuild the refit list */
this->BuildRefitList();
this->sel[0] = -1;
this->sel[1] = 0;
this->cargo = nullptr;
for (uint i = 0; this->cargo == nullptr && i < NUM_CARGO; i++) {
for (uint j = 0; j < list[i].size(); j++) {
if (list[i][j] == current_refit_option) {
this->sel[0] = i;
this->sel[1] = j;
this->cargo = &list[i][j];
break;
}
}
}
this->SetWidgetDisabledState(WID_VR_REFIT, this->sel[0] < 0);
this->RefreshScrollbar();
} else {
/* Rebuild the refit list */
this->OnInvalidateData(VIWD_CONSIST_CHANGED);
}
/* (Re)build the refit list */
this->OnInvalidateData(VIWD_CONSIST_CHANGED);
}
void OnPaint() override
@ -913,14 +880,14 @@ struct RefitWindow : public Window {
* @return INVALID_STRING_ID if there is no capacity. StringID to use in any other case.
* @post String parameters have been set.
*/
StringID GetCapacityString(RefitOption *option) const
StringID GetCapacityString(const RefitOption &option) const
{
assert(_current_company == _local_company);
auto [cost, refit_capacity, mail_capacity, cargo_capacities] = Command<CMD_REFIT_VEHICLE>::Do(DC_QUERY_COST, this->selected_vehicle, option->cargo, option->subtype, this->auto_refit, false, this->num_vehicles);
auto [cost, refit_capacity, mail_capacity, cargo_capacities] = Command<CMD_REFIT_VEHICLE>::Do(DC_QUERY_COST, this->selected_vehicle, option.cargo, option.subtype, this->auto_refit, false, this->num_vehicles);
if (cost.Failed()) return INVALID_STRING_ID;
SetDParam(0, option->cargo);
SetDParam(0, option.cargo);
SetDParam(1, refit_capacity);
Money money = cost.GetCost();
@ -1021,12 +988,12 @@ struct RefitWindow : public Window {
}
case WID_VR_MATRIX:
DrawVehicleRefitWindow(this->list, this->sel, this->vscroll->GetPosition(), this->vscroll->GetCapacity(), this->resize.step_height, r);
DrawVehicleRefitWindow(this->refit_list, this->selected_refit, this->vscroll->GetPosition(), this->vscroll->GetCapacity(), this->resize.step_height, r);
break;
case WID_VR_INFO:
if (this->cargo != nullptr) {
StringID string = this->GetCapacityString(this->cargo);
if (this->selected_refit != nullptr) {
StringID string = this->GetCapacityString(*this->selected_refit);
if (string != INVALID_STRING_ID) {
DrawStringMultiLine(r.Shrink(WidgetDimensions::scaled.framerect), string);
}
@ -1061,9 +1028,9 @@ struct RefitWindow : public Window {
uint max_width = 0;
/* Check the width of all cargo information strings. */
for (uint i = 0; i < NUM_CARGO; i++) {
for (uint j = 0; j < this->list[i].size(); j++) {
StringID string = this->GetCapacityString(&list[i][j]);
for (const auto &list : this->refit_list) {
for (const RefitOption &refit : list.second) {
StringID string = this->GetCapacityString(refit);
if (string != INVALID_STRING_ID) {
Dimension dim = GetStringBoundingBox(string);
max_width = std::max(dim.width, max_width);
@ -1080,7 +1047,6 @@ struct RefitWindow : public Window {
case 1: // A new cargo has been selected.
if (!gui_scope) break;
this->cargo = GetRefitOption();
this->RefreshScrollbar();
break;
}
@ -1168,7 +1134,7 @@ struct RefitWindow : public Window {
case WID_VR_MATRIX: { // listbox
this->SetSelection(this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_VR_MATRIX));
this->SetWidgetDisabledState(WID_VR_REFIT, this->sel[0] < 0);
this->SetWidgetDisabledState(WID_VR_REFIT, this->selected_refit == nullptr);
this->InvalidateData(1);
if (click_count == 1) break;
@ -1176,14 +1142,14 @@ struct RefitWindow : public Window {
}
case WID_VR_REFIT: // refit button
if (this->cargo != nullptr) {
if (this->selected_refit != nullptr) {
const Vehicle *v = Vehicle::Get(this->window_number);
if (this->order == INVALID_VEH_ORDER_ID) {
bool delete_window = this->selected_vehicle == v->index && this->num_vehicles == UINT8_MAX;
if (Command<CMD_REFIT_VEHICLE>::Post(GetCmdRefitVehMsg(v), v->tile, this->selected_vehicle, this->cargo->cargo, this->cargo->subtype, false, false, this->num_vehicles) && delete_window) this->Close();
if (Command<CMD_REFIT_VEHICLE>::Post(GetCmdRefitVehMsg(v), v->tile, this->selected_vehicle, this->selected_refit->cargo, this->selected_refit->subtype, false, false, this->num_vehicles) && delete_window) this->Close();
} else {
if (Command<CMD_ORDER_REFIT>::Post(v->tile, v->index, this->order, this->cargo->cargo)) this->Close();
if (Command<CMD_ORDER_REFIT>::Post(v->tile, v->index, this->order, this->selected_refit->cargo)) this->Close();
}
}
break;