diff -r 67db0d431d5e -r d8811e327d12 src/industry_gui.cpp --- 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 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,