Changeset - r28020:5e93e1d4a866
[Not reviewed]
master
0 1 0
Peter Nelson - 14 months ago 2023-10-02 07:12:56
peter1138@openttd.org
Codechange: Use std::map instead of fixed array to store refit options.

This simplifies handling of available refit options.
1 file changed with 88 insertions and 122 deletions:
0 comments (0 inline, 0 general)
src/vehicle_gui.cpp
Show inline comments
 
@@ -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
 
	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];
 

	
 
			/* Hide subtypes if sel[0] does not match */
 
			if (sel[0] != (int)i && refit.subtype != 0xFF) continue;
 
	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 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
 
				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
 

	
 
/** 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;
 
				}
 

	
 
				bool first_vehicle = this->list[current_index].size() == 0;
 
				if (!HasBit(cmask, cid)) continue;
 

	
 
				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;
 

	
 
		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++;
 
		size_t scroll_row = 0;
 
		size_t rows = 0;
 
		CargoID cargo = this->selected_refit == nullptr ? (CargoID)CT_INVALID : this->selected_refit->cargo;
 

	
 
		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;
0 comments (0 inline, 0 general)