Changeset - r11657:8035a6ada5a3
[Not reviewed]
master
0 3 0
frosch - 16 years ago 2009-04-12 17:38:01
frosch@openttd.org
(svn r16042) -Feature [FS#1941]: Allow filtering of vehicle purchase lists by cargo. Based on patch by sbr.
3 files changed with 129 insertions and 16 deletions:
0 comments (0 inline, 0 general)
src/build_vehicle_gui.cpp
Show inline comments
 
@@ -26,47 +26,59 @@
 
#include "table/sprites.h"
 
#include "table/strings.h"
 

	
 
enum BuildVehicleWidgets {
 
	BUILD_VEHICLE_WIDGET_CLOSEBOX = 0,
 
	BUILD_VEHICLE_WIDGET_CAPTION,
 
	BUILD_VEHICLE_WIDGET_LIST_CONTROL,
 
	BUILD_VEHICLE_WIDGET_SORT_ASSENDING_DESCENDING,
 
	BUILD_VEHICLE_WIDGET_SORT_DROPDOWN,
 
	BUILD_VEHICLE_WIDGET_CARGO_FILTER_DROPDOWN,
 
	BUILD_VEHICLE_WIDGET_LIST,
 
	BUILD_VEHICLE_WIDGET_SCROLLBAR,
 
	BUILD_VEHICLE_WIDGET_PANEL,
 
	BUILD_VEHICLE_WIDGET_BUILD,
 
	BUILD_VEHICLE_WIDGET_RENAME,
 
	BUILD_VEHICLE_WIDGET_RESIZE,
 
	BUILD_VEHICLE_WIDGET_END
 
};
 

	
 
static const Widget _build_vehicle_widgets[] = {
 
	{   WWT_CLOSEBOX,   RESIZE_NONE,  COLOUR_GREY,     0,    10,     0,    13, STR_00C5,     STR_018B_CLOSE_WINDOW },            // BUILD_VEHICLE_WIDGET_CLOSEBOX
 
	{    WWT_CAPTION,  RESIZE_RIGHT,  COLOUR_GREY,    11,   239,     0,    13, 0x0,          STR_018C_WINDOW_TITLE_DRAG_THIS },  // BUILD_VEHICLE_WIDGET_CAPTION
 
	{      WWT_PANEL,  RESIZE_RIGHT,  COLOUR_GREY,     0,   239,    14,    37, 0x0,          STR_NULL },                         // BUILD_VEHICLE_WIDGET_LIST_CONTROL
 
	{ WWT_PUSHTXTBTN,   RESIZE_NONE,  COLOUR_GREY,     0,    80,    14,    25, STR_SORT_BY,  STR_SORT_ORDER_TIP},                // BUILD_VEHICLE_WIDGET_SORT_ASSENDING_DESCENDING
 
	{   WWT_DROPDOWN,  RESIZE_RIGHT,  COLOUR_GREY,    81,   239,    14,    25, 0x0,          STR_SORT_CRITERIA_TIP},             // BUILD_VEHICLE_WIDGET_SORT_DROPDOWN
 
	{     WWT_MATRIX,     RESIZE_RB,  COLOUR_GREY,     0,   227,    26,    39, 0x101,        STR_NULL },                         // BUILD_VEHICLE_WIDGET_LIST
 
	{  WWT_SCROLLBAR,    RESIZE_LRB,  COLOUR_GREY,   228,   239,    26,    39, 0x0,          STR_0190_SCROLL_BAR_SCROLLS_LIST }, // BUILD_VEHICLE_WIDGET_SCROLLBAR
 
	{      WWT_PANEL,    RESIZE_RTB,  COLOUR_GREY,     0,   239,    40,   161, 0x0,          STR_NULL },                         // BUILD_VEHICLE_WIDGET_PANEL
 
	{   WWT_DROPDOWN,  RESIZE_RIGHT,  COLOUR_GREY,    81,   239,    26,    37, 0x0,          STR_FILTER_CRITERIA_TIP},           // BUILD_VEHICLE_WIDGET_CARGO_FILTER_DROPDOWN
 
	{     WWT_MATRIX,     RESIZE_RB,  COLOUR_GREY,     0,   227,    38,    51, 0x101,        STR_NULL },                         // BUILD_VEHICLE_WIDGET_LIST
 
	{  WWT_SCROLLBAR,    RESIZE_LRB,  COLOUR_GREY,   228,   239,    38,    51, 0x0,          STR_0190_SCROLL_BAR_SCROLLS_LIST }, // BUILD_VEHICLE_WIDGET_SCROLLBAR
 

	
 
	{ WWT_PUSHTXTBTN,     RESIZE_TB,  COLOUR_GREY,     0,   114,   162,   173, 0x0,          STR_NULL },                         // BUILD_VEHICLE_WIDGET_BUILD
 
	{ WWT_PUSHTXTBTN,    RESIZE_RTB,  COLOUR_GREY,   115,   227,   162,   173, 0x0,          STR_NULL },                         // BUILD_VEHICLE_WIDGET_RENAME
 
	{  WWT_RESIZEBOX,   RESIZE_LRTB,  COLOUR_GREY,   228,   239,   162,   173, 0x0,          STR_RESIZE_BUTTON },                // BUILD_VEHICLE_WIDGET_RESIZE
 
	{      WWT_PANEL,    RESIZE_RTB,  COLOUR_GREY,     0,   239,    52,   173, 0x0,          STR_NULL },                         // BUILD_VEHICLE_WIDGET_PANEL
 

	
 
	{ WWT_PUSHTXTBTN,     RESIZE_TB,  COLOUR_GREY,     0,   114,   174,   185, 0x0,          STR_NULL },                         // BUILD_VEHICLE_WIDGET_BUILD
 
	{ WWT_PUSHTXTBTN,    RESIZE_RTB,  COLOUR_GREY,   115,   227,   174,   185, 0x0,          STR_NULL },                         // BUILD_VEHICLE_WIDGET_RENAME
 
	{  WWT_RESIZEBOX,   RESIZE_LRTB,  COLOUR_GREY,   228,   239,   174,   185, 0x0,          STR_RESIZE_BUTTON },                // BUILD_VEHICLE_WIDGET_RESIZE
 
	{   WIDGETS_END},
 
};
 

	
 
static const NWidgetPart _nested_build_vehicle_widgets[] = {
 
	NWidget(NWID_HORIZONTAL),
 
		NWidget(WWT_CLOSEBOX, COLOUR_GREY, BUILD_VEHICLE_WIDGET_CLOSEBOX),
 
		NWidget(WWT_CAPTION, COLOUR_GREY, BUILD_VEHICLE_WIDGET_CAPTION), SetFill(1, 0), SetResize(1, 0), SetDataTip(0x0, STR_018C_WINDOW_TITLE_DRAG_THIS),
 
	EndContainer(),
 
	/* Sort order + criteria button row. */
 
	NWidget(NWID_HORIZONTAL),
 
		NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, BUILD_VEHICLE_WIDGET_SORT_ASSENDING_DESCENDING), SetMinimalSize(81, 12), SetDataTip(STR_SORT_BY, STR_SORT_ORDER_TIP),
 
		NWidget(WWT_DROPDOWN, COLOUR_GREY, BUILD_VEHICLE_WIDGET_SORT_DROPDOWN), SetMinimalSize(159, 12), SetResize(1, 0), SetDataTip(0x0, STR_SORT_CRITERIA_TIP),
 
	NWidget(WWT_PANEL, COLOUR_GREY, BUILD_VEHICLE_WIDGET_LIST_CONTROL),
 
		/* Sort order + criteria button row. */
 
		NWidget(NWID_HORIZONTAL),
 
			NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, BUILD_VEHICLE_WIDGET_SORT_ASSENDING_DESCENDING), SetMinimalSize(81, 12), SetDataTip(STR_SORT_BY, STR_SORT_ORDER_TIP),
 
			NWidget(WWT_DROPDOWN, COLOUR_GREY, BUILD_VEHICLE_WIDGET_SORT_DROPDOWN), SetMinimalSize(159, 12), SetResize(1, 0), SetDataTip(0x0, STR_SORT_CRITERIA_TIP),
 
		EndContainer(),
 
		/* Filter criteria row. */
 
		NWidget(NWID_HORIZONTAL),
 
			NWidget(NWID_SPACER), SetFill(1, 0),
 
			NWidget(WWT_DROPDOWN, COLOUR_GREY, BUILD_VEHICLE_WIDGET_CARGO_FILTER_DROPDOWN), SetMinimalSize(159, 12), SetResize(1, 0), SetDataTip(0x0, STR_FILTER_CRITERIA_TIP),
 
		EndContainer(),
 
	EndContainer(),
 
	/* Vehicle list. */
 
	NWidget(NWID_HORIZONTAL),
 
		NWidget(WWT_MATRIX, COLOUR_GREY, BUILD_VEHICLE_WIDGET_LIST), SetMinimalSize(228, 14), SetResize(1, 1), SetDataTip(0x101, STR_NULL),
 
		NWidget(WWT_SCROLLBAR, COLOUR_GREY, BUILD_VEHICLE_WIDGET_SCROLLBAR),
 
	EndContainer(),
 
@@ -77,15 +89,22 @@ static const NWidgetPart _nested_build_v
 
		NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, BUILD_VEHICLE_WIDGET_BUILD), SetMinimalSize(115, 12),
 
		NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, BUILD_VEHICLE_WIDGET_RENAME), SetMinimalSize(113, 12), SetResize(1, 0),
 
		NWidget(WWT_RESIZEBOX, COLOUR_GREY, BUILD_VEHICLE_WIDGET_RESIZE),
 
	EndContainer(),
 
};
 

	
 
/** Special cargo filter criteria */
 
enum {
 
	CF_ANY  = CT_NO_REFIT, ///< Show all vehicles independent of carried cargo (i.e. no filtering)
 
	CF_NONE = CT_INVALID,  ///< Show only vehicles which do not carry cargo (e.g. train engines)
 
};
 

	
 
static bool _internal_sort_order; // descending/ascending
 
static byte _last_sort_criteria[]    = {0, 0, 0, 0};
 
static bool _last_sort_order[]       = {false, false, false, false};
 
static byte _last_filter_criteria[]  = {0, 0, 0, 0};
 

	
 
static int CDECL EngineNumberSorter(const void *a, const void *b)
 
{
 
	const EngineID va = *(const EngineID*)a;
 
	const EngineID vb = *(const EngineID*)b;
 
	int r = ListPositionOfEngine(va) - ListPositionOfEngine(vb);
 
@@ -374,12 +393,24 @@ static const StringID _sort_listing[][11
 
	STR_ENGINE_SORT_RUNNING_COST,
 
	STR_SORT_BY_RELIABILITY,
 
	STR_ENGINE_SORT_CARGO_CAPACITY,
 
	INVALID_STRING_ID
 
}};
 

	
 
/** Cargo filter functions */
 
static bool CargoFilter(const EngineID *eid, const CargoID cid)
 
{
 
	if (cid == CF_ANY) return true;
 
	uint32 refit_mask = GetUnionOfArticulatedRefitMasks(*eid, GetEngine(*eid)->type, true);
 
	return (cid == CF_NONE ? refit_mask == 0 : HasBit(refit_mask, cid));
 
}
 

	
 
static GUIEngineList::FilterFunction * const _filter_funcs[] = {
 
	&CargoFilter,
 
};
 

	
 
static int DrawCargoCapacityInfo(int left, int right, int y, EngineID engine, VehicleType type, bool refittable)
 
{
 
	uint16 *cap = GetCapacityOfArticulatedParts(engine, type);
 

	
 
	for (uint c = 0; c < NUM_CARGO; c++) {
 
		if (cap[c] == 0) continue;
 
@@ -717,12 +748,15 @@ struct BuildVehicleWindow : Window {
 
	bool descending_sort_order;
 
	byte sort_criteria;
 
	bool listview_mode;
 
	EngineID sel_engine;
 
	EngineID rename_engine;
 
	GUIEngineList eng_list;
 
	CargoID cargo_filter[NUM_CARGO + 2];        ///< Available cargo filters; CargoID or CF_ANY or CF_NONE
 
	StringID cargo_filter_texts[NUM_CARGO + 3]; ///< Texts for filter_cargo, terminated by INVALID_STRING_ID
 
	byte cargo_filter_criteria;                 ///< Selected cargo filter
 

	
 
	BuildVehicleWindow(const WindowDesc *desc, TileIndex tile, VehicleType type) : Window(desc, tile == INVALID_TILE ? (int)type : tile)
 
	{
 
		this->vehicle_type = type;
 
		int vlh = GetVehicleListHeight(this->vehicle_type);
 

	
 
@@ -737,12 +771,45 @@ struct BuildVehicleWindow : Window {
 
		this->owner = (tile != INVALID_TILE) ? GetTileOwner(tile) : _local_company;
 

	
 
		this->sel_engine      = INVALID_ENGINE;
 

	
 
		this->sort_criteria         = _last_sort_criteria[type];
 
		this->descending_sort_order = _last_sort_order[type];
 
		this->cargo_filter_criteria = _last_filter_criteria[type];
 

	
 
		/* Populate filter list */
 
		uint filter_items = 0;
 

	
 
		/* Add item for disabling filtering */
 
		this->cargo_filter[filter_items] = CF_ANY;
 
		this->cargo_filter_texts[filter_items] = STR_PURCHASE_INFO_ALL_TYPES;
 
		filter_items++;
 

	
 
		/* Add item for vehicles not carrying anything, e.g. train engines.
 
		 * This could also be useful for eyecandy vehicles of other types, but is likely too confusing for joe, */
 
		if (type == VEH_TRAIN) {
 
			this->cargo_filter[filter_items] = CF_NONE;
 
			this->cargo_filter_texts[filter_items] = STR_01A9_NONE;
 
			filter_items++;
 
		}
 

	
 
		/* Collect available cargo types for filtering */
 
		for (CargoID cid = 0; cid < NUM_CARGO; cid++) {
 
			const CargoSpec *cargo = GetCargo(cid);
 
			if (!cargo->IsValid()) continue;
 
			if (IsCargoInClass(cid, CC_SPECIAL)) continue; // exclude fake cargo types
 
			this->cargo_filter[filter_items] = cid;
 
			this->cargo_filter_texts[filter_items] = cargo->name;
 
			filter_items++;
 
		}
 

	
 
		this->cargo_filter_texts[filter_items] = INVALID_STRING_ID;
 
		if (this->cargo_filter_criteria >= filter_items) this->cargo_filter_criteria = 0;
 

	
 
		this->eng_list.SetFilterFuncs(_filter_funcs);
 
		this->eng_list.SetFilterState(this->cargo_filter[this->cargo_filter_criteria] != CF_ANY);
 

	
 
		switch (type) {
 
			default: NOT_REACHED();
 
			case VEH_TRAIN:
 
				this->filter.railtype = (tile == INVALID_TILE) ? RAILTYPE_END : GetRailType(tile);
 
				break;
 
@@ -817,12 +884,30 @@ struct BuildVehicleWindow : Window {
 
				this->widget[BUILD_VEHICLE_WIDGET_RENAME].data     = STR_A037_RENAME;
 
				this->widget[BUILD_VEHICLE_WIDGET_RENAME].tooltips = STR_A038_RENAME_AIRCRAFT_TYPE;
 
				break;
 
		}
 
	}
 

	
 
	/** Filter the engine list against the currently selected cargo filter */
 
	void FilterEngineList()
 
	{
 
		this->eng_list.Filter(this->cargo_filter[this->cargo_filter_criteria]);
 
		if (0 == this->eng_list.Length()) { // no engine passed through the filter, invalidate the previously selected engine
 
			this->sel_engine = INVALID_ENGINE;
 
		} else if (!this->eng_list.Contains(this->sel_engine)) { // previously selected engine didn't pass the filter, select the first engine of the list
 
			this->sel_engine = this->eng_list[0];
 
		}
 
	}
 

	
 
	/** Filter a single engine */
 
	bool FilterSingleEngine(EngineID eid)
 
	{
 
		CargoID filter_type = this->cargo_filter[this->cargo_filter_criteria];
 
		return (filter_type == CF_ANY || CargoFilter(&eid, filter_type));
 
	}
 

	
 
	/* Figure out what train EngineIDs to put in the list */
 
	void GenerateBuildTrainList()
 
	{
 
		EngineID sel_id = INVALID_ENGINE;
 
		int num_engines = 0;
 
		int num_wagons  = 0;
 
@@ -840,12 +925,15 @@ struct BuildVehicleWindow : Window {
 
			EngineID eid = e->index;
 
			const RailVehicleInfo *rvi = &e->u.rail;
 

	
 
			if (this->filter.railtype != RAILTYPE_END && !HasPowerOnRail(rvi->railtype, this->filter.railtype)) continue;
 
			if (!IsEngineBuildable(eid, VEH_TRAIN, _local_company)) continue;
 

	
 
			/* Filter now! So num_engines and num_wagons is valid */
 
			if (!FilterSingleEngine(eid)) continue;
 

	
 
			*this->eng_list.Append() = eid;
 

	
 
			if (rvi->railveh_type != RAILVEH_WAGON) {
 
				num_engines++;
 
			} else {
 
				num_wagons++;
 
@@ -949,12 +1037,15 @@ struct BuildVehicleWindow : Window {
 
				this->GenerateBuildShipList();
 
				break;
 
			case VEH_AIRCRAFT:
 
				this->GenerateBuildAircraftList();
 
				break;
 
		}
 

	
 
		this->FilterEngineList();
 

	
 
		_internal_sort_order = this->descending_sort_order;
 
		EngList_Sort(&this->eng_list, _sorter[this->vehicle_type][this->sort_criteria]);
 

	
 
		this->eng_list.Compact();
 
		this->eng_list.RebuildDone();
 
	}
 
@@ -978,12 +1069,16 @@ struct BuildVehicleWindow : Window {
 
			}
 

	
 
			case BUILD_VEHICLE_WIDGET_SORT_DROPDOWN: // Select sorting criteria dropdown menu
 
				ShowDropDownMenu(this, _sort_listing[this->vehicle_type], this->sort_criteria, BUILD_VEHICLE_WIDGET_SORT_DROPDOWN, 0, 0);
 
				break;
 

	
 
			case BUILD_VEHICLE_WIDGET_CARGO_FILTER_DROPDOWN: // Select cargo filtering criteria dropdown menu
 
				ShowDropDownMenu(this, this->cargo_filter_texts, this->cargo_filter_criteria, BUILD_VEHICLE_WIDGET_CARGO_FILTER_DROPDOWN, 0, 0);
 
				break;
 

	
 
			case BUILD_VEHICLE_WIDGET_BUILD: {
 
				EngineID sel_eng = this->sel_engine;
 
				if (sel_eng != INVALID_ENGINE) {
 
					switch (this->vehicle_type) {
 
						default: NOT_REACHED();
 
						case VEH_TRAIN:
 
@@ -1047,12 +1142,15 @@ struct BuildVehicleWindow : Window {
 
			}
 
		}
 

	
 
		/* Set text of sort by dropdown */
 
		this->widget[BUILD_VEHICLE_WIDGET_SORT_DROPDOWN].data = _sort_listing[this->vehicle_type][this->sort_criteria];
 

	
 
		/* Set text of 'cargo filter by' dropdown */
 
		this->widget[BUILD_VEHICLE_WIDGET_CARGO_FILTER_DROPDOWN].data = this->cargo_filter_texts[this->cargo_filter_criteria];
 

	
 
		this->DrawWidgets();
 

	
 
		DrawEngineList(this->vehicle_type, this->widget[BUILD_VEHICLE_WIDGET_LIST].left + 2, this->widget[BUILD_VEHICLE_WIDGET_LIST].right, this->widget[BUILD_VEHICLE_WIDGET_LIST].top + 1, &this->eng_list, this->vscroll.pos, max, this->sel_engine, 0, DEFAULT_GROUP);
 

	
 
		if (this->sel_engine != INVALID_ENGINE) {
 
			const Widget *wi = &this->widget[BUILD_VEHICLE_WIDGET_PANEL];
 
@@ -1090,16 +1188,30 @@ struct BuildVehicleWindow : Window {
 
		}
 
		DoCommandP(0, this->rename_engine, 0, CMD_RENAME_ENGINE | CMD_MSG(err_str), NULL, str);
 
	}
 

	
 
	virtual void OnDropdownSelect(int widget, int index)
 
	{
 
		if (this->sort_criteria != index) {
 
			this->sort_criteria = index;
 
			_last_sort_criteria[this->vehicle_type] = this->sort_criteria;
 
			this->eng_list.ForceRebuild();
 
		switch (widget) {
 
			case BUILD_VEHICLE_WIDGET_SORT_DROPDOWN:
 
				if (this->sort_criteria != index) {
 
					this->sort_criteria = index;
 
					_last_sort_criteria[this->vehicle_type] = this->sort_criteria;
 
					this->eng_list.ForceRebuild();
 
				}
 
				break;
 

	
 
			case BUILD_VEHICLE_WIDGET_CARGO_FILTER_DROPDOWN: // Select a cargo filter criteria
 
				if (this->cargo_filter_criteria != index) {
 
					this->cargo_filter_criteria = index;
 
					_last_filter_criteria[this->vehicle_type] = this->cargo_filter_criteria;
 
					/* deactivate filter if criteria is 'Show All', activate it otherwise */
 
					this->eng_list.SetFilterState(this->cargo_filter[this->cargo_filter_criteria] != CF_ANY);
 
					this->eng_list.ForceRebuild();
 
				}
 
				break;
 
		}
 
		this->SetDirty();
 
	}
 

	
 
	virtual void OnResize(Point delta)
 
	{
 
@@ -1111,13 +1223,13 @@ struct BuildVehicleWindow : Window {
 
		this->vscroll.cap += delta.y / (int)GetVehicleListHeight(this->vehicle_type);
 
		this->widget[BUILD_VEHICLE_WIDGET_LIST].data = (this->vscroll.cap << 8) + 1;
 
	}
 
};
 

	
 
static const WindowDesc _build_vehicle_desc(
 
	WDP_AUTO, WDP_AUTO, 240, 174, 240, 256,
 
	WDP_AUTO, WDP_AUTO, 240, 186, 240, 268,
 
	WC_BUILD_VEHICLE, WC_NONE,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_RESIZABLE | WDF_CONSTRUCTION,
 
	_build_vehicle_widgets, _nested_build_vehicle_widgets, lengthof(_nested_build_vehicle_widgets)
 
);
 

	
 
void ShowBuildVehicleWindow(TileIndex tile, VehicleType type)
src/engine_gui.h
Show inline comments
 
@@ -4,13 +4,13 @@
 

	
 
#ifndef ENGINE_GUI_H
 
#define ENGINE_GUI_H
 

	
 
#include "sortlist_type.h"
 

	
 
typedef GUIList<EngineID> GUIEngineList;
 
typedef GUIList<EngineID, CargoID> GUIEngineList;
 

	
 
typedef int CDECL EngList_SortTypeFunction(const void*, const void*); ///< argument type for EngList_Sort()
 
void EngList_Sort(GUIEngineList *el, EngList_SortTypeFunction compare);  ///< qsort of the engine list
 
void EngList_SortPartial(GUIEngineList *el, EngList_SortTypeFunction compare, uint begin, uint num_items); ///< qsort of specified portion of the engine list
 

	
 
#endif /* ENGINE_GUI_H */
src/lang/english.txt
Show inline comments
 
@@ -317,12 +317,13 @@ STR_015D_LOAD_GAME                      
 
STR_015E_QUIT_GAME                                              :Abandon game
 
STR_015F_QUIT                                                   :Exit
 
STR_ABANDON_GAME_QUERY                                          :{YELLOW}Are you sure you want to abandon this game?
 
STR_0161_QUIT_GAME                                              :{WHITE}Abandon Game
 
STR_SORT_ORDER_TIP                                              :{BLACK}Select sorting order (descending/ascending)
 
STR_SORT_CRITERIA_TIP                                           :{BLACK}Select sorting criteria
 
STR_FILTER_CRITERIA_TIP                                         :{BLACK}Select filtering criteria
 
STR_SORT_BY                                                     :{BLACK}Sort by
 

	
 
STR_SORT_BY_POPULATION                                          :{BLACK}Population
 
STR_SORT_BY_PRODUCTION                                          :Production
 
STR_SORT_BY_TYPE                                                :Type
 
STR_SORT_BY_TRANSPORTED                                         :Transported
0 comments (0 inline, 0 general)