src/industry_gui.cpp
branchNewGRF_ports
changeset 10991 d8811e327d12
parent 10731 67db0d431d5e
--- a/src/industry_gui.cpp	Tue May 27 00:50:55 2008 +0000
+++ b/src/industry_gui.cpp	Tue Jun 17 10:32:49 2008 +0000
@@ -27,6 +27,7 @@
 #include "tilehighlight_func.h"
 #include "string_func.h"
 #include "sortlist_type.h"
+#include "widgets/dropdown_func.h"
 
 #include "table/strings.h"
 #include "table/sprites.h"
@@ -131,7 +132,7 @@
 				/* Rule is that editor mode loads all industries.
 				 * In game mode, all non raw industries are loaded too
 				 * and raw ones are loaded only when setting allows it */
-				if (_game_mode != GM_EDITOR && indsp->IsRawIndustry() && _settings.construction.raw_industry_construction == 0) {
+				if (_game_mode != GM_EDITOR && indsp->IsRawIndustry() && _settings_game.construction.raw_industry_construction == 0) {
 					/* Unselect if the industry is no longer in the list */
 					if (this->selected_type == ind) this->selected_index = -1;
 					continue;
@@ -195,10 +196,10 @@
 		 * In Editor, you just build, while ingame, or you fund or you prospect */
 		if (_game_mode == GM_EDITOR) {
 			/* We've chosen many random industries but no industries have been specified */
-			if (indsp == NULL) this->enabled[this->selected_index] = _settings.difficulty.number_industries != 0;
+			if (indsp == NULL) this->enabled[this->selected_index] = _settings_game.difficulty.number_industries != 0;
 			this->widget[DPIW_FUND_WIDGET].data = STR_BUILD_NEW_INDUSTRY;
 		} else {
-			this->widget[DPIW_FUND_WIDGET].data = (_settings.construction.raw_industry_construction == 2 && indsp->IsRawIndustry()) ? STR_PROSPECT_NEW_INDUSTRY : STR_FUND_NEW_INDUSTRY;
+			this->widget[DPIW_FUND_WIDGET].data = (_settings_game.construction.raw_industry_construction == 2 && indsp->IsRawIndustry()) ? STR_PROSPECT_NEW_INDUSTRY : STR_FUND_NEW_INDUSTRY;
 		}
 		this->SetWidgetDisabledState(DPIW_FUND_WIDGET, !this->enabled[this->selected_index]);
 
@@ -304,7 +305,7 @@
 
 					this->SetDirty();
 
-					if ((_game_mode != GM_EDITOR && _settings.construction.raw_industry_construction == 2 && indsp != NULL && indsp->IsRawIndustry()) ||
+					if ((_game_mode != GM_EDITOR && _settings_game.construction.raw_industry_construction == 2 && indsp != NULL && indsp->IsRawIndustry()) ||
 							this->selected_type == INVALID_INDUSTRYTYPE) {
 						/* Reset the button state if going to prospecting or "build many industries" */
 						this->RaiseButtons();
@@ -325,7 +326,7 @@
 						GenerateIndustries();
 						_generating_world = false;
 					}
-				} else if (_game_mode != GM_EDITOR && _settings.construction.raw_industry_construction == 2 && GetIndustrySpec(this->selected_type)->IsRawIndustry()) {
+				} else if (_game_mode != GM_EDITOR && _settings_game.construction.raw_industry_construction == 2 && GetIndustrySpec(this->selected_type)->IsRawIndustry()) {
 					DoCommandP(0, this->selected_type, InteractiveRandom(), NULL, CMD_BUILD_INDUSTRY | CMD_MSG(STR_4830_CAN_T_CONSTRUCT_THIS_INDUSTRY));
 					this->HandleButtonClick(DPIW_FUND_WIDGET);
 				} else {
@@ -691,10 +692,8 @@
 	IDW_CLOSEBOX = 0,
 	IDW_CAPTION,
 	IDW_STICKY,
-	IDW_SORTBYNAME,
-	IDW_SORTBYTYPE,
-	IDW_SORTBYPROD,
-	IDW_SORTBYTRANSPORT,
+	IDW_DROPDOWN_ORDER,
+	IDW_DROPDOWN_CRITERIA,
 	IDW_SPACER,
 	IDW_INDUSTRY_LIST,
 	IDW_SCROLLBAR,
@@ -704,154 +703,149 @@
 /** Widget definition of the industy directory gui */
 static const Widget _industry_directory_widgets[] = {
 {   WWT_CLOSEBOX,   RESIZE_NONE,    13,     0,    10,     0,    13, STR_00C5,                STR_018B_CLOSE_WINDOW},             // IDW_CLOSEBOX
-{    WWT_CAPTION,  RESIZE_RIGHT,    13,    11,   495,     0,    13, STR_INDUSTRYDIR_CAPTION, STR_018C_WINDOW_TITLE_DRAG_THIS},   // IDW_CAPTION
-{  WWT_STICKYBOX,     RESIZE_LR,    13,   496,   507,     0,    13, 0x0,                     STR_STICKY_BUTTON},                 // IDW_STICKY
-{ WWT_PUSHTXTBTN,   RESIZE_NONE,    13,     0,   100,    14,    25, STR_SORT_BY_NAME,        STR_SORT_ORDER_TIP},                // IDW_SORTBYNAME
-{ WWT_PUSHTXTBTN,   RESIZE_NONE,    13,   101,   200,    14,    25, STR_SORT_BY_TYPE,        STR_SORT_ORDER_TIP},                // IDW_SORTBYTYPE
-{ WWT_PUSHTXTBTN,   RESIZE_NONE,    13,   201,   300,    14,    25, STR_SORT_BY_PRODUCTION,  STR_SORT_ORDER_TIP},                // IDW_SORTBYPROD
-{ WWT_PUSHTXTBTN,   RESIZE_NONE,    13,   301,   400,    14,    25, STR_SORT_BY_TRANSPORTED, STR_SORT_ORDER_TIP},                // IDW_SORTBYTRANSPORT
-{      WWT_PANEL,  RESIZE_RIGHT,    13,   401,   495,    14,    25, 0x0,                     STR_NULL},                          // IDW_SPACER
-{      WWT_PANEL,     RESIZE_RB,    13,     0,   495,    26,   189, 0x0,                     STR_200A_TOWN_NAMES_CLICK_ON_NAME}, // IDW_INDUSRTY_LIST
-{  WWT_SCROLLBAR,    RESIZE_LRB,    13,   496,   507,    14,   177, 0x0,                     STR_0190_SCROLL_BAR_SCROLLS_LIST},  // IDW_SCROLLBAR
-{  WWT_RESIZEBOX,   RESIZE_LRTB,    13,   496,   507,   178,   189, 0x0,                     STR_RESIZE_BUTTON},                 // IDW_RESIZE
+{    WWT_CAPTION,  RESIZE_RIGHT,    13,    11,   415,     0,    13, STR_INDUSTRYDIR_CAPTION, STR_018C_WINDOW_TITLE_DRAG_THIS},   // IDW_CAPTION
+{  WWT_STICKYBOX,     RESIZE_LR,    13,   416,   427,     0,    13, 0x0,                     STR_STICKY_BUTTON},                 // IDW_STICKY
+
+{    WWT_TEXTBTN,   RESIZE_NONE,    13,     0,    80,    14,    25, STR_SORT_BY,             STR_SORT_ORDER_TIP},                // IDW_DROPDOWN_ORDER
+{   WWT_DROPDOWN,   RESIZE_NONE,    13,    81,   243,    14,    25, 0x0,                     STR_SORT_CRITERIA_TIP},             // IDW_DROPDOWN_CRITERIA
+{      WWT_PANEL,  RESIZE_RIGHT,    13,   244,   415,    14,    25, 0x0,                     STR_NULL},                          // IDW_SPACER
+
+{      WWT_PANEL,     RESIZE_RB,    13,     0,   415,    26,   189, 0x0,                     STR_200A_TOWN_NAMES_CLICK_ON_NAME}, // IDW_INDUSRTY_LIST
+{  WWT_SCROLLBAR,    RESIZE_LRB,    13,   416,   427,    14,   177, 0x0,                     STR_0190_SCROLL_BAR_SCROLLS_LIST},  // IDW_SCROLLBAR
+{  WWT_RESIZEBOX,   RESIZE_LRTB,    13,   416,   427,   178,   189, 0x0,                     STR_RESIZE_BUTTON},                 // IDW_RESIZE
 {   WIDGETS_END},
 };
 
-static char _bufcache[96];
-static const Industry* _last_industry;
-static int _internal_sort_order;
-
-/** Returns percents of cargo transported if industry produces this cargo, else -1
- * @param i industry to check
- * @param id cargo slot
- * @return percents of cargo transported, or -1 if industry doesn't use this cargo slot
- */
-static inline int GetCargoTransportedPercentsIfValid(const Industry *i, uint id)
-{
-	assert(id < lengthof(i->produced_cargo));
-
-	if (i->produced_cargo[id] == CT_INVALID) return 101;
-	return i->last_month_pct_transported[id] * 100 >> 8;
-}
-
-/** Returns value representing industry's transported cargo
- * percentage for industry sorting
- * @param i industry to check
- * @return value used for sorting
- */
-static int GetCargoTransportedSortValue(const Industry *i)
-{
-	int p1 = GetCargoTransportedPercentsIfValid(i, 0);
-	int p2 = GetCargoTransportedPercentsIfValid(i, 1);
-
-	if (p1 > p2) Swap(p1, p2); // lower value has higher priority
-
-	return (p1 << 8) + p2;
-}
-
-static int CDECL GeneralIndustrySorter(const void *a, const void *b)
-{
-	const Industry* i = *(const Industry**)a;
-	const Industry* j = *(const Industry**)b;
-	int r;
-
-	switch (_internal_sort_order >> 1) {
-		default: NOT_REACHED();
-		case 0: /* Sort by Name (handled later) */
-			r = 0;
-			break;
-
-		case 1: /* Sort by Type */
-			r = i->type - j->type;
-			break;
-
-		case 2: /* Sort by Production */
-			if (i->produced_cargo[0] == CT_INVALID) {
-				r = (j->produced_cargo[0] == CT_INVALID ? 0 : -1);
-			} else {
-				if (j->produced_cargo[0] == CT_INVALID) {
-					r = 1;
-				} else {
-					r =
-						(i->last_month_production[0] + i->last_month_production[1]) -
-						(j->last_month_production[0] + j->last_month_production[1]);
-				}
-			}
-			break;
-
-		case 3: /* Sort by transported fraction */
-			r = GetCargoTransportedSortValue(i) - GetCargoTransportedSortValue(j);
-			break;
-	}
-
-	/* default to string sorting if they are otherwise equal */
-	if (r == 0) {
-		char buf1[96];
-
-		SetDParam(0, i->town->index);
-		GetString(buf1, STR_TOWN, lastof(buf1));
-
-		if (j != _last_industry) {
-			_last_industry = j;
-			SetDParam(0, j->town->index);
-			GetString(_bufcache, STR_TOWN, lastof(_bufcache));
-		}
-		r = strcmp(buf1, _bufcache);
-	}
-
-	if (_internal_sort_order & 1) r = -r;
-	return r;
-}
-
 typedef GUIList<const Industry*> GUIIndustryList;
 
-/**
- * Rebuild industries list if the VL_REBUILD flag is set
- *
- * @param sl pointer to industry list
- */
-static void BuildIndustriesList(GUIIndustryList *sl)
-{
-	if (!(sl->flags & VL_REBUILD)) return;
-
-	sl->Clear();
-
-	DEBUG(misc, 3, "Building industry list");
-
-	const Industry *i;
-	FOR_ALL_INDUSTRIES(i) {
-		*sl->Append() = i;
-	}
-
-	sl->Compact();
-
-	sl->flags &= ~VL_REBUILD;
-	sl->flags |= VL_RESORT;
-}
-
-
-/**
- * Sort industry list if the VL_RESORT flag is set
- *
- * @param sl pointer to industry list
- */
-static void SortIndustriesList(GUIIndustryList *sl)
-{
-	if (!(sl->flags & VL_RESORT)) return;
-
-	_internal_sort_order = (sl->sort_type << 1) | (sl->flags & VL_DESC);
-	_last_industry = NULL; // used for "cache" in namesorting
-	qsort((void*)sl->Begin(), sl->Length(), sizeof(sl->Begin()), &GeneralIndustrySorter);
-
-	sl->flags &= ~VL_RESORT;
-}
 
 /**
  * The list of industries.
  */
-struct IndustryDirectoryWindow : public Window, public GUIIndustryList {
-	static Listing industry_sort;
+class IndustryDirectoryWindow : public Window {
+protected:
+	/* Runtime saved values */
+	static Listing last_sorting;
+	static const Industry *last_industry;
 
+	/* Constants for sorting stations */
+	static const StringID sorter_names[];
+	static GUIIndustryList::SortFunction *const sorter_funcs[];
+
+	GUIIndustryList industries;
+
+	/** (Re)Build industries list */
+	void BuildIndustriesList()
+	{
+		if (!this->industries.NeedRebuild()) return;
+
+		this->industries.Clear();
+
+		DEBUG(misc, 3, "Building industry list");
+
+		const Industry *i;
+		FOR_ALL_INDUSTRIES(i) {
+			*this->industries.Append() = i;
+		}
+
+		this->industries.Compact();
+		this->industries.RebuildDone();
+	}
+
+	/**
+	 * Returns percents of cargo transported if industry produces this cargo, else -1
+	 *
+	 * @param i industry to check
+	 * @param id cargo slot
+	 * @return percents of cargo transported, or -1 if industry doesn't use this cargo slot
+	 */
+	static inline int GetCargoTransportedPercentsIfValid(const Industry *i, uint id)
+	{
+		assert(id < lengthof(i->produced_cargo));
+
+		if (i->produced_cargo[id] == CT_INVALID) return 101;
+		return i->last_month_pct_transported[id] * 100 >> 8;
+	}
+
+	/**
+	 * Returns value representing industry's transported cargo
+	 *  percentage for industry sorting
+	 *
+	 * @param i industry to check
+	 * @return value used for sorting
+	 */
+	static int GetCargoTransportedSortValue(const Industry *i)
+	{
+		int p1 = GetCargoTransportedPercentsIfValid(i, 0);
+		int p2 = GetCargoTransportedPercentsIfValid(i, 1);
+
+		if (p1 > p2) Swap(p1, p2); // lower value has higher priority
+
+		return (p1 << 8) + p2;
+	}
+
+	/** Sort industries by name */
+	static int CDECL IndustryNameSorter(const Industry* const *a, const Industry* const *b)
+	{
+		static char buf_cache[96];
+		static char buf[96];
+
+		SetDParam(0, (*a)->town->index);
+		GetString(buf, STR_TOWN, lastof(buf));
+
+		if (*b != last_industry) {
+			last_industry = *b;
+			SetDParam(0, (*b)->town->index);
+			GetString(buf_cache, STR_TOWN, lastof(buf_cache));
+		}
+
+		return strcmp(buf, buf_cache);
+	}
+
+	/** Sort industries by type and name */
+	static int CDECL IndustryTypeSorter(const Industry* const *a, const Industry* const *b)
+	{
+		int r = (*a)->type - (*b)->type;
+		return (r == 0) ? IndustryNameSorter(a, b) : r;
+	}
+
+	/** Sort industries by production and name */
+	static int CDECL IndustryProductionSorter(const Industry* const *a, const Industry* const *b)
+	{
+		int r = 0;
+
+		if ((*a)->produced_cargo[0] == CT_INVALID) {
+			if ((*b)->produced_cargo[0] != CT_INVALID) return -1;
+		} else {
+			if ((*b)->produced_cargo[0] == CT_INVALID) return 1;
+
+			r = ((*a)->last_month_production[0] + (*a)->last_month_production[1]) -
+			    ((*b)->last_month_production[0] + (*b)->last_month_production[1]);
+		}
+
+		return (r == 0) ? IndustryNameSorter(a, b) : r;
+	}
+
+	/** Sort industries by transported cargo and name */
+	static int CDECL IndustryTransportedCargoSorter(const Industry* const *a, const Industry* const *b)
+	{
+		int r = GetCargoTransportedSortValue(*a) - GetCargoTransportedSortValue(*b);
+		return (r == 0) ? IndustryNameSorter(a, b) : r;
+	}
+
+	/** Sort the industries list */
+	void SortIndustriesList()
+	{
+		if (!this->industries.Sort()) return;
+
+		/* Reset name sorter sort cache */
+		this->last_industry = NULL;
+
+		/* Set the modified widget dirty */
+		this->InvalidateWidget(IDW_INDUSTRY_LIST);
+	}
+
+public:
 	IndustryDirectoryWindow(const WindowDesc *desc, WindowNumber number) : Window(desc, number)
 	{
 		this->vscroll.cap = 16;
@@ -859,26 +853,35 @@
 		this->resize.step_height = 10;
 		this->FindWindowPlacementAndResize(desc);
 
-		this->flags = VL_REBUILD;
-		this->sort_type = industry_sort.criteria;
-		if (industry_sort.order) this->flags |= VL_DESC;
+		this->industries.SetListing(this->last_sorting);
+		this->industries.SetSortFuncs(this->sorter_funcs);
+		this->industries.ForceRebuild();
+		this->industries.NeedResort();
+		this->SortIndustriesList();
+
+		this->widget[IDW_DROPDOWN_CRITERIA].data = this->sorter_names[this->industries.SortType()];
+	}
+
+	~IndustryDirectoryWindow()
+	{
+		this->last_sorting = this->industries.GetListing();
 	}
 
 	virtual void OnPaint()
 	{
-		BuildIndustriesList(this);
-		SortIndustriesList(this);
+		BuildIndustriesList();
+		SortIndustriesList();
 
-		SetVScrollCount(this, this->Length());
+		SetVScrollCount(this, this->industries.Length());
 
 		this->DrawWidgets();
-		this->DrawSortButtonState(IDW_SORTBYNAME + this->sort_type, this->flags & VL_DESC ? SBS_DOWN : SBS_UP);
+		this->DrawSortButtonState(IDW_DROPDOWN_ORDER, this->industries.IsDescSortOrder() ? SBS_DOWN : SBS_UP);
 
-		int max = min(this->vscroll.pos + this->vscroll.cap, this->Length());
+		int max = min(this->vscroll.pos + this->vscroll.cap, this->industries.Length());
 		int y = 28; // start of the list-widget
 
 		for (int n = this->vscroll.pos; n < max; ++n) {
-			const Industry* i = *this->Get(n);
+			const Industry* i = this->industries[n];
 			const IndustrySpec *indsp = GetIndustrySpec(i->type);
 			byte p = 0;
 
@@ -911,39 +914,41 @@
 	virtual void OnClick(Point pt, int widget)
 	{
 		switch (widget) {
-			case IDW_SORTBYNAME:
-			case IDW_SORTBYTYPE:
-			case IDW_SORTBYPROD:
-			case IDW_SORTBYTRANSPORT:
-				if (this->sort_type == (widget - IDW_SORTBYNAME)) {
-					this->flags ^= VL_DESC;
-				} else {
-					this->sort_type = widget - IDW_SORTBYNAME;
-					this->flags &= ~VL_DESC;
-				}
-				industry_sort.criteria = this->sort_type;
-				industry_sort.order = HasBit(this->flags, 0);
-				this->flags |= VL_RESORT;
+			case IDW_DROPDOWN_ORDER:
+				this->industries.ToggleSortOrder();
 				this->SetDirty();
 				break;
 
+			case IDW_DROPDOWN_CRITERIA:
+				ShowDropDownMenu(this, this->sorter_names, this->industries.SortType(), IDW_DROPDOWN_CRITERIA, 0, 0);
+				break;
+
 			case IDW_INDUSTRY_LIST: {
 				int y = (pt.y - 28) / 10;
 				uint16 p;
 
 				if (!IsInsideMM(y, 0, this->vscroll.cap)) return;
 				p = y + this->vscroll.pos;
-				if (p < this->Length()) {
+				if (p < this->industries.Length()) {
 					if (_ctrl_pressed) {
-						ShowExtraViewPortWindow((*this->Get(p))->xy);
+						ShowExtraViewPortWindow(this->industries[p]->xy);
 					} else {
-						ScrollMainWindowToTile((*this->Get(p))->xy);
+						ScrollMainWindowToTile(this->industries[p]->xy);
 					}
 				}
 			} break;
 		}
 	}
 
+	virtual void OnDropdownSelect(int widget, int index)
+	{
+		if (this->industries.SortType() != index) {
+			this->industries.SetSortType(index);
+			this->widget[IDW_DROPDOWN_CRITERIA].data = this->sorter_names[this->industries.SortType()];
+			this->SetDirty();
+		}
+	}
+
 	virtual void OnResize(Point new_size, Point delta)
 	{
 		this->vscroll.cap += delta.y / 10;
@@ -951,16 +956,39 @@
 
 	virtual void OnInvalidateData(int data)
 	{
-		this->flags |= (data == 0 ? VL_REBUILD : VL_RESORT);
+		if (data == 0) {
+			this->industries.ForceRebuild();
+		} else {
+			this->industries.ForceResort();
+		}
 		this->InvalidateWidget(IDW_INDUSTRY_LIST);
 	}
 };
 
-Listing IndustryDirectoryWindow::industry_sort = {0, 0};
+Listing IndustryDirectoryWindow::last_sorting = {false, 0};
+const Industry *IndustryDirectoryWindow::last_industry = NULL;
+
+/* Availible station sorting functions */
+GUIIndustryList::SortFunction* const IndustryDirectoryWindow::sorter_funcs[] = {
+	&IndustryNameSorter,
+	&IndustryTypeSorter,
+	&IndustryProductionSorter,
+	&IndustryTransportedCargoSorter
+};
+
+/* Names of the sorting functions */
+const StringID IndustryDirectoryWindow::sorter_names[] = {
+	STR_SORT_BY_DROPDOWN_NAME,
+	STR_SORT_BY_TYPE,
+	STR_SORT_BY_PRODUCTION,
+	STR_SORT_BY_TRANSPORTED,
+	INVALID_STRING_ID
+};
+
 
 /** Window definition of the industy directory gui */
 static const WindowDesc _industry_directory_desc = {
-	WDP_AUTO, WDP_AUTO, 508, 190, 508, 190,
+	WDP_AUTO, WDP_AUTO, 428, 190, 428, 190,
 	WC_INDUSTRY_DIRECTORY, WC_NONE,
 	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
 	_industry_directory_widgets,