00001
00002
00003
00004
00005
00006
00007
00008
00009
00012 #include "stdafx.h"
00013 #include "error.h"
00014 #include "gui.h"
00015 #include "settings_gui.h"
00016 #include "sound_func.h"
00017 #include "window_func.h"
00018 #include "textbuf_gui.h"
00019 #include "command_func.h"
00020 #include "viewport_func.h"
00021 #include "industry.h"
00022 #include "town.h"
00023 #include "cheat_type.h"
00024 #include "newgrf_industries.h"
00025 #include "newgrf_text.h"
00026 #include "newgrf_debug.h"
00027 #include "strings_func.h"
00028 #include "company_func.h"
00029 #include "tilehighlight_func.h"
00030 #include "string_func.h"
00031 #include "sortlist_type.h"
00032 #include "widgets/dropdown_func.h"
00033 #include "company_base.h"
00034 #include "core/geometry_func.hpp"
00035 #include "core/random_func.hpp"
00036 #include "core/backup_type.hpp"
00037 #include "genworld.h"
00038 #include "smallmap_gui.h"
00039 #include "widgets/dropdown_type.h"
00040 #include "widgets/industry_widget.h"
00041
00042 #include "table/strings.h"
00043
00044 bool _ignore_restrictions;
00045 uint64 _displayed_industries;
00046
00047 assert_compile(NUM_INDUSTRYTYPES <= 64);
00048
00050 enum CargoSuffixType {
00051 CST_FUND,
00052 CST_VIEW,
00053 CST_DIR,
00054 };
00055
00056 static void ShowIndustryCargoesWindow(IndustryType id);
00057
00073 static void GetCargoSuffix(uint cargo, CargoSuffixType cst, const Industry *ind, IndustryType ind_type, const IndustrySpec *indspec, char *suffix, const char *suffix_last)
00074 {
00075 suffix[0] = '\0';
00076 if (HasBit(indspec->callback_mask, CBM_IND_CARGO_SUFFIX)) {
00077 uint16 callback = GetIndustryCallback(CBID_INDUSTRY_CARGO_SUFFIX, 0, (cst << 8) | cargo, const_cast<Industry *>(ind), ind_type, (cst != CST_FUND) ? ind->location.tile : INVALID_TILE);
00078 if (callback == CALLBACK_FAILED || callback == 0x400) return;
00079 if (callback > 0x400) {
00080 ErrorUnknownCallbackResult(indspec->grf_prop.grffile->grfid, CBID_INDUSTRY_CARGO_SUFFIX, callback);
00081 } else if (indspec->grf_prop.grffile->grf_version >= 8 || GB(callback, 0, 8) != 0xFF) {
00082 StartTextRefStackUsage(6);
00083 GetString(suffix, GetGRFStringID(indspec->grf_prop.grffile->grfid, 0xD000 + callback), suffix_last);
00084 StopTextRefStackUsage();
00085 }
00086 }
00087 }
00088
00099 template <typename TC, typename TS>
00100 static inline void GetAllCargoSuffixes(uint cb_offset, CargoSuffixType cst, const Industry *ind, IndustryType ind_type, const IndustrySpec *indspec, const TC &cargoes, TS &suffixes)
00101 {
00102 assert_compile(lengthof(cargoes) <= lengthof(suffixes));
00103 for (uint j = 0; j < lengthof(cargoes); j++) {
00104 if (cargoes[j] != CT_INVALID) {
00105 GetCargoSuffix(cb_offset + j, cst, ind, ind_type, indspec, suffixes[j], lastof(suffixes[j]));
00106 } else {
00107 suffixes[j][0] = '\0';
00108 }
00109 }
00110 }
00111
00112 IndustryType _sorted_industry_types[NUM_INDUSTRYTYPES];
00113
00115 static int CDECL IndustryTypeNameSorter(const IndustryType *a, const IndustryType *b)
00116 {
00117 static char industry_name[2][64];
00118
00119 const IndustrySpec *indsp1 = GetIndustrySpec(*a);
00120 SetDParam(0, indsp1->name);
00121 GetString(industry_name[0], STR_JUST_STRING, lastof(industry_name[0]));
00122
00123 const IndustrySpec *indsp2 = GetIndustrySpec(*b);
00124 SetDParam(0, indsp2->name);
00125 GetString(industry_name[1], STR_JUST_STRING, lastof(industry_name[1]));
00126
00127 int r = strnatcmp(industry_name[0], industry_name[1]);
00128
00129
00130 return (r != 0) ? r : (*a - *b);
00131 }
00132
00136 void SortIndustryTypes()
00137 {
00138
00139 for (IndustryType i = 0; i < NUM_INDUSTRYTYPES; i++) {
00140 _sorted_industry_types[i] = i;
00141 }
00142
00143
00144 QSortT(_sorted_industry_types, NUM_INDUSTRYTYPES, &IndustryTypeNameSorter);
00145 }
00146
00154 void CcBuildIndustry(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2)
00155 {
00156 if (result.Succeeded()) return;
00157
00158 uint8 indtype = GB(p1, 0, 8);
00159 if (indtype < NUM_INDUSTRYTYPES) {
00160 const IndustrySpec *indsp = GetIndustrySpec(indtype);
00161 if (indsp->enabled) {
00162 SetDParam(0, indsp->name);
00163 ShowErrorMessage(STR_ERROR_CAN_T_BUILD_HERE, result.GetErrorMessage(), WL_INFO, TileX(tile) * TILE_SIZE, TileY(tile) * TILE_SIZE);
00164 }
00165 }
00166 }
00167
00168 static const NWidgetPart _nested_build_industry_widgets[] = {
00169 NWidget(NWID_HORIZONTAL),
00170 NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN),
00171 NWidget(WWT_CAPTION, COLOUR_DARK_GREEN), SetDataTip(STR_FUND_INDUSTRY_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
00172 NWidget(WWT_SHADEBOX, COLOUR_DARK_GREEN),
00173 NWidget(WWT_DEFSIZEBOX, COLOUR_DARK_GREEN),
00174 NWidget(WWT_STICKYBOX, COLOUR_DARK_GREEN),
00175 EndContainer(),
00176 NWidget(NWID_HORIZONTAL),
00177 NWidget(WWT_MATRIX, COLOUR_DARK_GREEN, WID_DPI_MATRIX_WIDGET), SetDataTip(0x801, STR_FUND_INDUSTRY_SELECTION_TOOLTIP), SetFill(1, 0), SetResize(1, 1), SetScrollbar(WID_DPI_SCROLLBAR),
00178 NWidget(NWID_VSCROLLBAR, COLOUR_DARK_GREEN, WID_DPI_SCROLLBAR),
00179 EndContainer(),
00180 NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_DPI_INFOPANEL), SetResize(1, 0),
00181 EndContainer(),
00182 NWidget(NWID_HORIZONTAL),
00183 NWidget(WWT_TEXTBTN, COLOUR_DARK_GREEN, WID_DPI_DISPLAY_WIDGET), SetFill(1, 0), SetResize(1, 0),
00184 SetDataTip(STR_INDUSTRY_DISPLAY_CHAIN, STR_INDUSTRY_DISPLAY_CHAIN_TOOLTIP),
00185 NWidget(WWT_TEXTBTN, COLOUR_DARK_GREEN, WID_DPI_FUND_WIDGET), SetFill(1, 0), SetResize(1, 0), SetDataTip(STR_JUST_STRING, STR_NULL),
00186 NWidget(WWT_RESIZEBOX, COLOUR_DARK_GREEN),
00187 EndContainer(),
00188 };
00189
00191 static WindowDesc _build_industry_desc(
00192 WDP_AUTO, "build_industry", 170, 212,
00193 WC_BUILD_INDUSTRY, WC_NONE,
00194 WDF_CONSTRUCTION,
00195 _nested_build_industry_widgets, lengthof(_nested_build_industry_widgets)
00196 );
00197
00199 class BuildIndustryWindow : public Window {
00200 int selected_index;
00201 IndustryType selected_type;
00202 uint16 callback_timer;
00203 bool timer_enabled;
00204 uint16 count;
00205 IndustryType index[NUM_INDUSTRYTYPES + 1];
00206 bool enabled[NUM_INDUSTRYTYPES + 1];
00207 Scrollbar *vscroll;
00208
00210 static const int MATRIX_TEXT_OFFSET = 17;
00211
00212 void SetupArrays()
00213 {
00214 this->count = 0;
00215
00216 for (uint i = 0; i < lengthof(this->index); i++) {
00217 this->index[i] = INVALID_INDUSTRYTYPE;
00218 this->enabled[i] = false;
00219 }
00220
00221 if (_game_mode == GM_EDITOR) {
00222 this->index[this->count] = INVALID_INDUSTRYTYPE;
00223 this->enabled[this->count] = true;
00224 this->count++;
00225 this->timer_enabled = false;
00226 }
00227
00228
00229
00230
00231 for (uint8 i = 0; i < NUM_INDUSTRYTYPES; i++) {
00232 IndustryType ind = _sorted_industry_types[i];
00233 const IndustrySpec *indsp = GetIndustrySpec(ind);
00234 if (indsp->enabled) {
00235
00236
00237
00238 if (_game_mode != GM_EDITOR && indsp->IsRawIndustry() && _settings_game.construction.raw_industry_construction == 0) {
00239
00240 if (this->selected_type == ind) this->selected_index = -1;
00241 continue;
00242 }
00243 this->index[this->count] = ind;
00244 this->enabled[this->count] = (_game_mode == GM_EDITOR) || GetIndustryProbabilityCallback(ind, IACT_USERCREATION, 1) > 0;
00245
00246 if (this->selected_type == ind) this->selected_index = this->count;
00247 this->count++;
00248 }
00249 }
00250
00251
00252
00253 if (this->selected_index == -1) {
00254 this->selected_index = 0;
00255 this->selected_type = this->index[0];
00256 }
00257
00258 this->vscroll->SetCount(this->count);
00259 }
00260
00262 void SetButtons()
00263 {
00264 this->SetWidgetDisabledState(WID_DPI_FUND_WIDGET, this->selected_type != INVALID_INDUSTRYTYPE && !this->enabled[this->selected_index]);
00265 this->SetWidgetDisabledState(WID_DPI_DISPLAY_WIDGET, this->selected_type == INVALID_INDUSTRYTYPE && this->enabled[this->selected_index]);
00266 }
00267
00268 public:
00269 BuildIndustryWindow() : Window(&_build_industry_desc)
00270 {
00271 this->timer_enabled = _loaded_newgrf_features.has_newindustries;
00272
00273 this->selected_index = -1;
00274 this->selected_type = INVALID_INDUSTRYTYPE;
00275
00276 this->callback_timer = DAY_TICKS;
00277
00278 this->CreateNestedTree();
00279 this->vscroll = this->GetScrollbar(WID_DPI_SCROLLBAR);
00280 this->FinishInitNested(0);
00281
00282 this->SetButtons();
00283 }
00284
00285 virtual void OnInit()
00286 {
00287 this->SetupArrays();
00288 }
00289
00290 virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
00291 {
00292 switch (widget) {
00293 case WID_DPI_MATRIX_WIDGET: {
00294 Dimension d = GetStringBoundingBox(STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES);
00295 for (byte i = 0; i < this->count; i++) {
00296 if (this->index[i] == INVALID_INDUSTRYTYPE) continue;
00297 d = maxdim(d, GetStringBoundingBox(GetIndustrySpec(this->index[i])->name));
00298 }
00299 resize->height = FONT_HEIGHT_NORMAL + WD_MATRIX_TOP + WD_MATRIX_BOTTOM;
00300 d.width += MATRIX_TEXT_OFFSET + padding.width;
00301 d.height = 5 * resize->height;
00302 *size = maxdim(*size, d);
00303 break;
00304 }
00305
00306 case WID_DPI_INFOPANEL: {
00307
00308 int height = 2 + (_game_mode == GM_EDITOR ? 0 : 1) + (_loaded_newgrf_features.has_newindustries ? 4 : 0);
00309 Dimension d = {0, 0};
00310 for (byte i = 0; i < this->count; i++) {
00311 if (this->index[i] == INVALID_INDUSTRYTYPE) continue;
00312
00313 const IndustrySpec *indsp = GetIndustrySpec(this->index[i]);
00314
00315 char cargo_suffix[3][512];
00316 GetAllCargoSuffixes(0, CST_FUND, NULL, this->index[i], indsp, indsp->accepts_cargo, cargo_suffix);
00317 StringID str = STR_INDUSTRY_VIEW_REQUIRES_CARGO;
00318 byte p = 0;
00319 SetDParam(0, STR_JUST_NOTHING);
00320 SetDParamStr(1, "");
00321 for (byte j = 0; j < lengthof(indsp->accepts_cargo); j++) {
00322 if (indsp->accepts_cargo[j] == CT_INVALID) continue;
00323 if (p > 0) str++;
00324 SetDParam(p++, CargoSpec::Get(indsp->accepts_cargo[j])->name);
00325 SetDParamStr(p++, cargo_suffix[j]);
00326 }
00327 d = maxdim(d, GetStringBoundingBox(str));
00328
00329
00330 GetAllCargoSuffixes(3, CST_FUND, NULL, this->index[i], indsp, indsp->produced_cargo, cargo_suffix);
00331 str = STR_INDUSTRY_VIEW_PRODUCES_CARGO;
00332 p = 0;
00333 SetDParam(0, STR_JUST_NOTHING);
00334 SetDParamStr(1, "");
00335 for (byte j = 0; j < lengthof(indsp->produced_cargo); j++) {
00336 if (indsp->produced_cargo[j] == CT_INVALID) continue;
00337 if (p > 0) str++;
00338 SetDParam(p++, CargoSpec::Get(indsp->produced_cargo[j])->name);
00339 SetDParamStr(p++, cargo_suffix[j]);
00340 }
00341 d = maxdim(d, GetStringBoundingBox(str));
00342 }
00343
00344
00345 size->height = height * FONT_HEIGHT_NORMAL + WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
00346 size->width = d.width + WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT;
00347 break;
00348 }
00349
00350 case WID_DPI_FUND_WIDGET: {
00351 Dimension d = GetStringBoundingBox(STR_FUND_INDUSTRY_BUILD_NEW_INDUSTRY);
00352 d = maxdim(d, GetStringBoundingBox(STR_FUND_INDUSTRY_PROSPECT_NEW_INDUSTRY));
00353 d = maxdim(d, GetStringBoundingBox(STR_FUND_INDUSTRY_FUND_NEW_INDUSTRY));
00354 d.width += padding.width;
00355 d.height += padding.height;
00356 *size = maxdim(*size, d);
00357 break;
00358 }
00359 }
00360 }
00361
00362 virtual void SetStringParameters(int widget) const
00363 {
00364 switch (widget) {
00365 case WID_DPI_FUND_WIDGET:
00366
00367
00368 if (_game_mode == GM_EDITOR) {
00369
00370 SetDParam(0, STR_FUND_INDUSTRY_BUILD_NEW_INDUSTRY);
00371 } else {
00372 const IndustrySpec *indsp = GetIndustrySpec(this->index[this->selected_index]);
00373 SetDParam(0, (_settings_game.construction.raw_industry_construction == 2 && indsp->IsRawIndustry()) ? STR_FUND_INDUSTRY_PROSPECT_NEW_INDUSTRY : STR_FUND_INDUSTRY_FUND_NEW_INDUSTRY);
00374 }
00375 break;
00376 }
00377 }
00378
00379 virtual void DrawWidget(const Rect &r, int widget) const
00380 {
00381 switch (widget) {
00382 case WID_DPI_MATRIX_WIDGET:
00383 for (byte i = 0; i < this->vscroll->GetCapacity() && i + this->vscroll->GetPosition() < this->count; i++) {
00384 int x = r.left + WD_MATRIX_LEFT;
00385 int y = r.top + WD_MATRIX_TOP + i * this->resize.step_height;
00386 bool selected = this->selected_index == i + this->vscroll->GetPosition();
00387
00388 if (this->index[i + this->vscroll->GetPosition()] == INVALID_INDUSTRYTYPE) {
00389 DrawString(x + MATRIX_TEXT_OFFSET, r.right - WD_MATRIX_RIGHT, y, STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES, selected ? TC_WHITE : TC_ORANGE);
00390 continue;
00391 }
00392 const IndustrySpec *indsp = GetIndustrySpec(this->index[i + this->vscroll->GetPosition()]);
00393
00394
00395 DrawString(x + MATRIX_TEXT_OFFSET, r.right - WD_MATRIX_RIGHT, y, indsp->name, selected ? TC_WHITE : TC_ORANGE);
00396 GfxFillRect(x, y + 1, x + 10, y + 7, selected ? PC_WHITE : PC_BLACK);
00397 GfxFillRect(x + 1, y + 2, x + 9, y + 6, indsp->map_colour);
00398 }
00399 break;
00400
00401 case WID_DPI_INFOPANEL: {
00402 int y = r.top + WD_FRAMERECT_TOP;
00403 int bottom = r.bottom - WD_FRAMERECT_BOTTOM;
00404 int left = r.left + WD_FRAMERECT_LEFT;
00405 int right = r.right - WD_FRAMERECT_RIGHT;
00406
00407 if (this->selected_type == INVALID_INDUSTRYTYPE) {
00408 DrawStringMultiLine(left, right, y, bottom, STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES_TOOLTIP);
00409 break;
00410 }
00411
00412 const IndustrySpec *indsp = GetIndustrySpec(this->selected_type);
00413
00414 if (_game_mode != GM_EDITOR) {
00415 SetDParam(0, indsp->GetConstructionCost());
00416 DrawString(left, right, y, STR_FUND_INDUSTRY_INDUSTRY_BUILD_COST);
00417 y += FONT_HEIGHT_NORMAL;
00418 }
00419
00420
00421 char cargo_suffix[3][512];
00422 GetAllCargoSuffixes(0, CST_FUND, NULL, this->selected_type, indsp, indsp->accepts_cargo, cargo_suffix);
00423 StringID str = STR_INDUSTRY_VIEW_REQUIRES_CARGO;
00424 byte p = 0;
00425 SetDParam(0, STR_JUST_NOTHING);
00426 SetDParamStr(1, "");
00427 for (byte j = 0; j < lengthof(indsp->accepts_cargo); j++) {
00428 if (indsp->accepts_cargo[j] == CT_INVALID) continue;
00429 if (p > 0) str++;
00430 SetDParam(p++, CargoSpec::Get(indsp->accepts_cargo[j])->name);
00431 SetDParamStr(p++, cargo_suffix[j]);
00432 }
00433 DrawString(left, right, y, str);
00434 y += FONT_HEIGHT_NORMAL;
00435
00436
00437 GetAllCargoSuffixes(3, CST_FUND, NULL, this->selected_type, indsp, indsp->produced_cargo, cargo_suffix);
00438 str = STR_INDUSTRY_VIEW_PRODUCES_CARGO;
00439 p = 0;
00440 SetDParam(0, STR_JUST_NOTHING);
00441 SetDParamStr(1, "");
00442 for (byte j = 0; j < lengthof(indsp->produced_cargo); j++) {
00443 if (indsp->produced_cargo[j] == CT_INVALID) continue;
00444 if (p > 0) str++;
00445 SetDParam(p++, CargoSpec::Get(indsp->produced_cargo[j])->name);
00446 SetDParamStr(p++, cargo_suffix[j]);
00447 }
00448 DrawString(left, right, y, str);
00449 y += FONT_HEIGHT_NORMAL;
00450
00451
00452 str = STR_NULL;
00453 if (HasBit(indsp->callback_mask, CBM_IND_FUND_MORE_TEXT)) {
00454 uint16 callback_res = GetIndustryCallback(CBID_INDUSTRY_FUND_MORE_TEXT, 0, 0, NULL, this->selected_type, INVALID_TILE);
00455 if (callback_res != CALLBACK_FAILED && callback_res != 0x400) {
00456 if (callback_res > 0x400) {
00457 ErrorUnknownCallbackResult(indsp->grf_prop.grffile->grfid, CBID_INDUSTRY_FUND_MORE_TEXT, callback_res);
00458 } else {
00459 str = GetGRFStringID(indsp->grf_prop.grffile->grfid, 0xD000 + callback_res);
00460 if (str != STR_UNDEFINED) {
00461 StartTextRefStackUsage(6);
00462 DrawStringMultiLine(left, right, y, bottom, str, TC_YELLOW);
00463 StopTextRefStackUsage();
00464 }
00465 }
00466 }
00467 }
00468 break;
00469 }
00470 }
00471 }
00472
00473 virtual void OnClick(Point pt, int widget, int click_count)
00474 {
00475 switch (widget) {
00476 case WID_DPI_MATRIX_WIDGET: {
00477 int y = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_DPI_MATRIX_WIDGET);
00478 if (y < this->count) {
00479 this->selected_index = y;
00480 this->selected_type = this->index[y];
00481 const IndustrySpec *indsp = (this->selected_type == INVALID_INDUSTRYTYPE) ? NULL : GetIndustrySpec(this->selected_type);
00482
00483 this->SetDirty();
00484
00485 if (_thd.GetCallbackWnd() == this &&
00486 ((_game_mode != GM_EDITOR && _settings_game.construction.raw_industry_construction == 2 && indsp != NULL && indsp->IsRawIndustry()) ||
00487 this->selected_type == INVALID_INDUSTRYTYPE ||
00488 !this->enabled[this->selected_index])) {
00489
00490 this->RaiseButtons();
00491 ResetObjectToPlace();
00492 }
00493
00494 this->SetButtons();
00495 if (this->enabled[this->selected_index] && click_count > 1) this->OnClick(pt, WID_DPI_FUND_WIDGET, 1);
00496 }
00497 break;
00498 }
00499
00500 case WID_DPI_DISPLAY_WIDGET:
00501 if (this->selected_type != INVALID_INDUSTRYTYPE) ShowIndustryCargoesWindow(this->selected_type);
00502 break;
00503
00504 case WID_DPI_FUND_WIDGET: {
00505 if (this->selected_type == INVALID_INDUSTRYTYPE) {
00506 this->HandleButtonClick(WID_DPI_FUND_WIDGET);
00507
00508 if (Town::GetNumItems() == 0) {
00509 ShowErrorMessage(STR_ERROR_CAN_T_GENERATE_INDUSTRIES, STR_ERROR_MUST_FOUND_TOWN_FIRST, WL_INFO);
00510 } else {
00511 extern void GenerateIndustries();
00512 _generating_world = true;
00513 GenerateIndustries();
00514 _generating_world = false;
00515 }
00516 } else if (_game_mode != GM_EDITOR && _settings_game.construction.raw_industry_construction == 2 && GetIndustrySpec(this->selected_type)->IsRawIndustry()) {
00517 DoCommandP(0, this->selected_type, InteractiveRandom(), CMD_BUILD_INDUSTRY | CMD_MSG(STR_ERROR_CAN_T_CONSTRUCT_THIS_INDUSTRY));
00518 this->HandleButtonClick(WID_DPI_FUND_WIDGET);
00519 } else {
00520 HandlePlacePushButton(this, WID_DPI_FUND_WIDGET, SPR_CURSOR_INDUSTRY, HT_RECT);
00521 }
00522 break;
00523 }
00524 }
00525 }
00526
00527 virtual void OnResize()
00528 {
00529
00530 this->vscroll->SetCapacityFromWidget(this, WID_DPI_MATRIX_WIDGET);
00531 this->GetWidget<NWidgetCore>(WID_DPI_MATRIX_WIDGET)->widget_data = (this->vscroll->GetCapacity() << MAT_ROW_START) + (1 << MAT_COL_START);
00532 }
00533
00534 virtual void OnPlaceObject(Point pt, TileIndex tile)
00535 {
00536 bool success = true;
00537
00538 const IndustrySpec *indsp = GetIndustrySpec(this->selected_type);
00539 uint32 seed = InteractiveRandom();
00540
00541 if (_game_mode == GM_EDITOR) {
00542
00543 if (Town::GetNumItems() == 0) {
00544 SetDParam(0, indsp->name);
00545 ShowErrorMessage(STR_ERROR_CAN_T_BUILD_HERE, STR_ERROR_MUST_FOUND_TOWN_FIRST, WL_INFO, pt.x, pt.y);
00546 return;
00547 }
00548
00549 Backup<CompanyByte> cur_company(_current_company, OWNER_NONE, FILE_LINE);
00550 _generating_world = true;
00551 _ignore_restrictions = true;
00552
00553 DoCommandP(tile, (InteractiveRandomRange(indsp->num_table) << 8) | this->selected_type, seed,
00554 CMD_BUILD_INDUSTRY | CMD_MSG(STR_ERROR_CAN_T_CONSTRUCT_THIS_INDUSTRY), &CcBuildIndustry);
00555
00556 cur_company.Restore();
00557 _ignore_restrictions = false;
00558 _generating_world = false;
00559 } else {
00560 success = DoCommandP(tile, (InteractiveRandomRange(indsp->num_table) << 8) | this->selected_type, seed, CMD_BUILD_INDUSTRY | CMD_MSG(STR_ERROR_CAN_T_CONSTRUCT_THIS_INDUSTRY));
00561 }
00562
00563
00564 if (success && !_settings_client.gui.persistent_buildingtools) ResetObjectToPlace();
00565 }
00566
00567 virtual void OnTick()
00568 {
00569 if (_pause_mode != PM_UNPAUSED) return;
00570 if (!this->timer_enabled) return;
00571 if (--this->callback_timer == 0) {
00572
00573
00574 this->callback_timer = DAY_TICKS;
00575
00576 const IndustrySpec *indsp = GetIndustrySpec(this->selected_type);
00577
00578 if (indsp->enabled) {
00579 bool call_back_result = GetIndustryProbabilityCallback(this->selected_type, IACT_USERCREATION, 1) > 0;
00580
00581
00582 if (call_back_result != this->enabled[this->selected_index]) {
00583 this->enabled[this->selected_index] = call_back_result;
00584 this->SetButtons();
00585 this->SetDirty();
00586 }
00587 }
00588 }
00589 }
00590
00591 virtual void OnTimeout()
00592 {
00593 this->RaiseButtons();
00594 }
00595
00596 virtual void OnPlaceObjectAbort()
00597 {
00598 this->RaiseButtons();
00599 }
00600
00606 virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
00607 {
00608 if (!gui_scope) return;
00609 this->SetupArrays();
00610
00611 const IndustrySpec *indsp = (this->selected_type == INVALID_INDUSTRYTYPE) ? NULL : GetIndustrySpec(this->selected_type);
00612 if (indsp == NULL) this->enabled[this->selected_index] = _settings_game.difficulty.industry_density != ID_FUND_ONLY;
00613 this->SetButtons();
00614 }
00615 };
00616
00617 void ShowBuildIndustryWindow()
00618 {
00619 if (_game_mode != GM_EDITOR && !Company::IsValidID(_local_company)) return;
00620 if (BringWindowToFrontById(WC_BUILD_INDUSTRY, 0)) return;
00621 new BuildIndustryWindow();
00622 }
00623
00624 static void UpdateIndustryProduction(Industry *i);
00625
00626 static inline bool IsProductionAlterable(const Industry *i)
00627 {
00628 const IndustrySpec *is = GetIndustrySpec(i->type);
00629 return ((_game_mode == GM_EDITOR || _cheats.setup_prod.value) &&
00630 (is->production_rate[0] != 0 || is->production_rate[1] != 0 || is->IsRawIndustry()));
00631 }
00632
00633 class IndustryViewWindow : public Window
00634 {
00636 enum Editability {
00637 EA_NONE,
00638 EA_MULTIPLIER,
00639 EA_RATE,
00640 };
00641
00643 enum InfoLine {
00644 IL_NONE,
00645 IL_MULTIPLIER,
00646 IL_RATE1,
00647 IL_RATE2,
00648 };
00649
00650 Editability editable;
00651 InfoLine editbox_line;
00652 InfoLine clicked_line;
00653 byte clicked_button;
00654 int production_offset_y;
00655 int info_height;
00656
00657 public:
00658 IndustryViewWindow(WindowDesc *desc, WindowNumber window_number) : Window(desc)
00659 {
00660 this->flags |= WF_DISABLE_VP_SCROLL;
00661 this->editbox_line = IL_NONE;
00662 this->clicked_line = IL_NONE;
00663 this->clicked_button = 0;
00664 this->info_height = WD_FRAMERECT_TOP + 2 * FONT_HEIGHT_NORMAL + WD_FRAMERECT_BOTTOM + 1;
00665
00666 this->InitNested(window_number);
00667 NWidgetViewport *nvp = this->GetWidget<NWidgetViewport>(WID_IV_VIEWPORT);
00668 nvp->InitializeViewport(this, Industry::Get(window_number)->location.GetCenterTile(), ZOOM_LVL_INDUSTRY);
00669
00670 this->InvalidateData();
00671 }
00672
00673 virtual void OnPaint()
00674 {
00675 this->DrawWidgets();
00676
00677 if (this->IsShaded()) return;
00678
00679 NWidgetBase *nwi = this->GetWidget<NWidgetBase>(WID_IV_INFO);
00680 uint expected = this->DrawInfo(nwi->pos_x, nwi->pos_x + nwi->current_x - 1, nwi->pos_y) - nwi->pos_y;
00681 if (expected > nwi->current_y - 1) {
00682 this->info_height = expected + 1;
00683 this->ReInit();
00684 return;
00685 }
00686 }
00687
00695 int DrawInfo(uint left, uint right, uint top)
00696 {
00697 Industry *i = Industry::Get(this->window_number);
00698 const IndustrySpec *ind = GetIndustrySpec(i->type);
00699 int y = top + WD_FRAMERECT_TOP;
00700 bool first = true;
00701 bool has_accept = false;
00702 char cargo_suffix[3][512];
00703
00704 if (i->prod_level == PRODLEVEL_CLOSURE) {
00705 DrawString(left + WD_FRAMERECT_LEFT, right - WD_FRAMERECT_RIGHT, y, STR_INDUSTRY_VIEW_INDUSTRY_ANNOUNCED_CLOSURE);
00706 y += 2 * FONT_HEIGHT_NORMAL;
00707 }
00708
00709 if (HasBit(ind->callback_mask, CBM_IND_PRODUCTION_CARGO_ARRIVAL) || HasBit(ind->callback_mask, CBM_IND_PRODUCTION_256_TICKS)) {
00710 GetAllCargoSuffixes(0, CST_VIEW, i, i->type, ind, i->accepts_cargo, cargo_suffix);
00711 for (byte j = 0; j < lengthof(i->accepts_cargo); j++) {
00712 if (i->accepts_cargo[j] == CT_INVALID) continue;
00713 has_accept = true;
00714 if (first) {
00715 DrawString(left + WD_FRAMERECT_LEFT, right - WD_FRAMERECT_RIGHT, y, STR_INDUSTRY_VIEW_WAITING_FOR_PROCESSING);
00716 y += FONT_HEIGHT_NORMAL;
00717 first = false;
00718 }
00719 SetDParam(0, i->accepts_cargo[j]);
00720 SetDParam(1, i->incoming_cargo_waiting[j]);
00721 SetDParamStr(2, cargo_suffix[j]);
00722 DrawString(left + WD_FRAMETEXT_LEFT, right - WD_FRAMERECT_RIGHT, y, STR_INDUSTRY_VIEW_WAITING_STOCKPILE_CARGO);
00723 y += FONT_HEIGHT_NORMAL;
00724 }
00725 } else {
00726 GetAllCargoSuffixes(0, CST_VIEW, i, i->type, ind, i->accepts_cargo, cargo_suffix);
00727 StringID str = STR_INDUSTRY_VIEW_REQUIRES_CARGO;
00728 byte p = 0;
00729 for (byte j = 0; j < lengthof(i->accepts_cargo); j++) {
00730 if (i->accepts_cargo[j] == CT_INVALID) continue;
00731 has_accept = true;
00732 if (p > 0) str++;
00733 SetDParam(p++, CargoSpec::Get(i->accepts_cargo[j])->name);
00734 SetDParamStr(p++, cargo_suffix[j]);
00735 }
00736 if (has_accept) {
00737 DrawString(left + WD_FRAMERECT_LEFT, right - WD_FRAMERECT_RIGHT, y, str);
00738 y += FONT_HEIGHT_NORMAL;
00739 }
00740 }
00741
00742 GetAllCargoSuffixes(3, CST_VIEW, i, i->type, ind, i->produced_cargo, cargo_suffix);
00743 first = true;
00744 for (byte j = 0; j < lengthof(i->produced_cargo); j++) {
00745 if (i->produced_cargo[j] == CT_INVALID) continue;
00746 if (first) {
00747 if (has_accept) y += WD_PAR_VSEP_WIDE;
00748 DrawString(left + WD_FRAMERECT_LEFT, right - WD_FRAMERECT_RIGHT, y, STR_INDUSTRY_VIEW_PRODUCTION_LAST_MONTH_TITLE);
00749 y += FONT_HEIGHT_NORMAL;
00750 if (this->editable == EA_RATE) this->production_offset_y = y;
00751 first = false;
00752 }
00753
00754 SetDParam(0, i->produced_cargo[j]);
00755 SetDParam(1, i->last_month_production[j]);
00756 SetDParamStr(2, cargo_suffix[j]);
00757 SetDParam(3, ToPercent8(i->last_month_pct_transported[j]));
00758 uint x = left + WD_FRAMETEXT_LEFT + (this->editable == EA_RATE ? SETTING_BUTTON_WIDTH + 10 : 0);
00759 DrawString(x, right - WD_FRAMERECT_RIGHT, y, STR_INDUSTRY_VIEW_TRANSPORTED);
00760
00761 if (this->editable == EA_RATE) {
00762 DrawArrowButtons(left + WD_FRAMETEXT_LEFT, y, COLOUR_YELLOW, (this->clicked_line == IL_RATE1 + j) ? this->clicked_button : 0,
00763 i->production_rate[j] > 0, i->production_rate[j] < 255);
00764 }
00765 y += FONT_HEIGHT_NORMAL;
00766 }
00767
00768
00769 if (this->editable == EA_MULTIPLIER) {
00770 y += WD_PAR_VSEP_WIDE;
00771 this->production_offset_y = y;
00772 SetDParam(0, RoundDivSU(i->prod_level * 100, PRODLEVEL_DEFAULT));
00773 uint x = left + WD_FRAMETEXT_LEFT + SETTING_BUTTON_WIDTH + 10;
00774 DrawString(x, right - WD_FRAMERECT_RIGHT, y, STR_INDUSTRY_VIEW_PRODUCTION_LEVEL);
00775 DrawArrowButtons(left + WD_FRAMETEXT_LEFT, y, COLOUR_YELLOW, (this->clicked_line == IL_MULTIPLIER) ? this->clicked_button : 0,
00776 i->prod_level > PRODLEVEL_MINIMUM, i->prod_level < PRODLEVEL_MAXIMUM);
00777 y += FONT_HEIGHT_NORMAL;
00778 }
00779
00780
00781 if (HasBit(ind->callback_mask, CBM_IND_WINDOW_MORE_TEXT)) {
00782 uint16 callback_res = GetIndustryCallback(CBID_INDUSTRY_WINDOW_MORE_TEXT, 0, 0, i, i->type, i->location.tile);
00783 if (callback_res != CALLBACK_FAILED && callback_res != 0x400) {
00784 if (callback_res > 0x400) {
00785 ErrorUnknownCallbackResult(ind->grf_prop.grffile->grfid, CBID_INDUSTRY_WINDOW_MORE_TEXT, callback_res);
00786 } else {
00787 StringID message = GetGRFStringID(ind->grf_prop.grffile->grfid, 0xD000 + callback_res);
00788 if (message != STR_NULL && message != STR_UNDEFINED) {
00789 y += WD_PAR_VSEP_WIDE;
00790
00791 StartTextRefStackUsage(6);
00792
00793
00794
00795 y = DrawStringMultiLine(left + WD_FRAMERECT_LEFT, right - WD_FRAMERECT_RIGHT, y, UINT16_MAX, message, TC_BLACK);
00796 StopTextRefStackUsage();
00797 }
00798 }
00799 }
00800 }
00801 return y + WD_FRAMERECT_BOTTOM;
00802 }
00803
00804 virtual void SetStringParameters(int widget) const
00805 {
00806 if (widget == WID_IV_CAPTION) SetDParam(0, this->window_number);
00807 }
00808
00809 virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
00810 {
00811 if (widget == WID_IV_INFO) size->height = this->info_height;
00812 }
00813
00814 virtual void OnClick(Point pt, int widget, int click_count)
00815 {
00816 switch (widget) {
00817 case WID_IV_INFO: {
00818 Industry *i = Industry::Get(this->window_number);
00819 InfoLine line = IL_NONE;
00820
00821 switch (this->editable) {
00822 case EA_NONE: break;
00823
00824 case EA_MULTIPLIER:
00825 if (IsInsideBS(pt.y, this->production_offset_y, FONT_HEIGHT_NORMAL)) line = IL_MULTIPLIER;
00826 break;
00827
00828 case EA_RATE:
00829 if (pt.y >= this->production_offset_y) {
00830 int row = (pt.y - this->production_offset_y) / FONT_HEIGHT_NORMAL;
00831 for (uint j = 0; j < lengthof(i->produced_cargo); j++) {
00832 if (i->produced_cargo[j] == CT_INVALID) continue;
00833 row--;
00834 if (row < 0) {
00835 line = (InfoLine)(IL_RATE1 + j);
00836 break;
00837 }
00838 }
00839 }
00840 break;
00841 }
00842 if (line == IL_NONE) return;
00843
00844 NWidgetBase *nwi = this->GetWidget<NWidgetBase>(widget);
00845 int left = nwi->pos_x + WD_FRAMETEXT_LEFT;
00846 int right = nwi->pos_x + nwi->current_x - 1 - WD_FRAMERECT_RIGHT;
00847 if (IsInsideMM(pt.x, left, left + SETTING_BUTTON_WIDTH)) {
00848
00849 byte button = (pt.x < left + SETTING_BUTTON_WIDTH / 2) ? 1 : 2;
00850 switch (this->editable) {
00851 case EA_MULTIPLIER:
00852 if (button == 1) {
00853 if (i->prod_level <= PRODLEVEL_MINIMUM) return;
00854 i->prod_level = max<uint>(i->prod_level / 2, PRODLEVEL_MINIMUM);
00855 } else {
00856 if (i->prod_level >= PRODLEVEL_MAXIMUM) return;
00857 i->prod_level = minu(i->prod_level * 2, PRODLEVEL_MAXIMUM);
00858 }
00859 break;
00860
00861 case EA_RATE:
00862 if (button == 1) {
00863 if (i->production_rate[line - IL_RATE1] <= 0) return;
00864 i->production_rate[line - IL_RATE1] = max(i->production_rate[line - IL_RATE1] / 2, 0);
00865 } else {
00866 if (i->production_rate[line - IL_RATE1] >= 255) return;
00867
00868 int new_prod = i->production_rate[line - IL_RATE1] == 0 ? 1 : i->production_rate[line - IL_RATE1] * 2;
00869 i->production_rate[line - IL_RATE1] = minu(new_prod, 255);
00870 }
00871 break;
00872
00873 default: NOT_REACHED();
00874 }
00875
00876 UpdateIndustryProduction(i);
00877 this->SetDirty();
00878 this->SetTimeout();
00879 this->clicked_line = line;
00880 this->clicked_button = button;
00881 } else if (IsInsideMM(pt.x, left + SETTING_BUTTON_WIDTH + 10, right)) {
00882
00883 this->editbox_line = line;
00884 switch (this->editable) {
00885 case EA_MULTIPLIER:
00886 SetDParam(0, RoundDivSU(i->prod_level * 100, PRODLEVEL_DEFAULT));
00887 ShowQueryString(STR_JUST_INT, STR_CONFIG_GAME_PRODUCTION_LEVEL, 10, this, CS_ALPHANUMERAL, QSF_NONE);
00888 break;
00889
00890 case EA_RATE:
00891 SetDParam(0, i->production_rate[line - IL_RATE1] * 8);
00892 ShowQueryString(STR_JUST_INT, STR_CONFIG_GAME_PRODUCTION, 10, this, CS_ALPHANUMERAL, QSF_NONE);
00893 break;
00894
00895 default: NOT_REACHED();
00896 }
00897 }
00898 break;
00899 }
00900
00901 case WID_IV_GOTO: {
00902 Industry *i = Industry::Get(this->window_number);
00903 if (_ctrl_pressed) {
00904 ShowExtraViewPortWindow(i->location.GetCenterTile());
00905 } else {
00906 ScrollMainWindowToTile(i->location.GetCenterTile());
00907 }
00908 break;
00909 }
00910
00911 case WID_IV_DISPLAY: {
00912 Industry *i = Industry::Get(this->window_number);
00913 ShowIndustryCargoesWindow(i->type);
00914 break;
00915 }
00916 }
00917 }
00918
00919 virtual void OnTimeout()
00920 {
00921 this->clicked_line = IL_NONE;
00922 this->clicked_button = 0;
00923 this->SetDirty();
00924 }
00925
00926 virtual void OnResize()
00927 {
00928 if (this->viewport != NULL) {
00929 NWidgetViewport *nvp = this->GetWidget<NWidgetViewport>(WID_IV_VIEWPORT);
00930 nvp->UpdateViewportCoordinates(this);
00931
00932 ScrollWindowToTile(Industry::Get(this->window_number)->location.GetCenterTile(), this, true);
00933 }
00934 }
00935
00936 virtual void OnQueryTextFinished(char *str)
00937 {
00938 if (StrEmpty(str)) return;
00939
00940 Industry *i = Industry::Get(this->window_number);
00941 uint value = atoi(str);
00942 switch (this->editbox_line) {
00943 case IL_NONE: NOT_REACHED();
00944
00945 case IL_MULTIPLIER:
00946 i->prod_level = ClampU(RoundDivSU(value * PRODLEVEL_DEFAULT, 100), PRODLEVEL_MINIMUM, PRODLEVEL_MAXIMUM);
00947 break;
00948
00949 default:
00950 i->production_rate[this->editbox_line - IL_RATE1] = ClampU(RoundDivSU(value, 8), 0, 255);
00951 break;
00952 }
00953 UpdateIndustryProduction(i);
00954 this->SetDirty();
00955 }
00956
00962 virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
00963 {
00964 if (!gui_scope) return;
00965 const Industry *i = Industry::Get(this->window_number);
00966 if (IsProductionAlterable(i)) {
00967 const IndustrySpec *ind = GetIndustrySpec(i->type);
00968 this->editable = ind->UsesSmoothEconomy() ? EA_RATE : EA_MULTIPLIER;
00969 } else {
00970 this->editable = EA_NONE;
00971 }
00972 }
00973
00974 virtual bool IsNewGRFInspectable() const
00975 {
00976 return ::IsNewGRFInspectable(GSF_INDUSTRIES, this->window_number);
00977 }
00978
00979 virtual void ShowNewGRFInspectWindow() const
00980 {
00981 ::ShowNewGRFInspectWindow(GSF_INDUSTRIES, this->window_number);
00982 }
00983 };
00984
00985 static void UpdateIndustryProduction(Industry *i)
00986 {
00987 const IndustrySpec *indspec = GetIndustrySpec(i->type);
00988 if (!indspec->UsesSmoothEconomy()) i->RecomputeProductionMultipliers();
00989
00990 for (byte j = 0; j < lengthof(i->produced_cargo); j++) {
00991 if (i->produced_cargo[j] != CT_INVALID) {
00992 i->last_month_production[j] = 8 * i->production_rate[j];
00993 }
00994 }
00995 }
00996
00998 static const NWidgetPart _nested_industry_view_widgets[] = {
00999 NWidget(NWID_HORIZONTAL),
01000 NWidget(WWT_CLOSEBOX, COLOUR_CREAM),
01001 NWidget(WWT_CAPTION, COLOUR_CREAM, WID_IV_CAPTION), SetDataTip(STR_INDUSTRY_VIEW_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
01002 NWidget(WWT_DEBUGBOX, COLOUR_CREAM),
01003 NWidget(WWT_SHADEBOX, COLOUR_CREAM),
01004 NWidget(WWT_DEFSIZEBOX, COLOUR_CREAM),
01005 NWidget(WWT_STICKYBOX, COLOUR_CREAM),
01006 EndContainer(),
01007 NWidget(WWT_PANEL, COLOUR_CREAM),
01008 NWidget(WWT_INSET, COLOUR_CREAM), SetPadding(2, 2, 2, 2),
01009 NWidget(NWID_VIEWPORT, INVALID_COLOUR, WID_IV_VIEWPORT), SetMinimalSize(254, 86), SetFill(1, 0), SetPadding(1, 1, 1, 1), SetResize(1, 1),
01010 EndContainer(),
01011 EndContainer(),
01012 NWidget(WWT_PANEL, COLOUR_CREAM, WID_IV_INFO), SetMinimalSize(260, 2), SetResize(1, 0),
01013 EndContainer(),
01014 NWidget(NWID_HORIZONTAL),
01015 NWidget(WWT_PUSHTXTBTN, COLOUR_CREAM, WID_IV_GOTO), SetFill(1, 0), SetResize(1, 0), SetDataTip(STR_BUTTON_LOCATION, STR_INDUSTRY_VIEW_LOCATION_TOOLTIP),
01016 NWidget(WWT_PUSHTXTBTN, COLOUR_CREAM, WID_IV_DISPLAY), SetFill(1, 0), SetResize(1, 0), SetDataTip(STR_INDUSTRY_DISPLAY_CHAIN, STR_INDUSTRY_DISPLAY_CHAIN_TOOLTIP),
01017 NWidget(WWT_RESIZEBOX, COLOUR_CREAM),
01018 EndContainer(),
01019 };
01020
01022 static WindowDesc _industry_view_desc(
01023 WDP_AUTO, "view_industry", 260, 120,
01024 WC_INDUSTRY_VIEW, WC_NONE,
01025 0,
01026 _nested_industry_view_widgets, lengthof(_nested_industry_view_widgets)
01027 );
01028
01029 void ShowIndustryViewWindow(int industry)
01030 {
01031 AllocateWindowDescFront<IndustryViewWindow>(&_industry_view_desc, industry);
01032 }
01033
01035 static const NWidgetPart _nested_industry_directory_widgets[] = {
01036 NWidget(NWID_HORIZONTAL),
01037 NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
01038 NWidget(WWT_CAPTION, COLOUR_BROWN), SetDataTip(STR_INDUSTRY_DIRECTORY_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
01039 NWidget(WWT_SHADEBOX, COLOUR_BROWN),
01040 NWidget(WWT_DEFSIZEBOX, COLOUR_BROWN),
01041 NWidget(WWT_STICKYBOX, COLOUR_BROWN),
01042 EndContainer(),
01043 NWidget(NWID_HORIZONTAL),
01044 NWidget(NWID_VERTICAL),
01045 NWidget(NWID_HORIZONTAL),
01046 NWidget(WWT_TEXTBTN, COLOUR_BROWN, WID_ID_DROPDOWN_ORDER), SetDataTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER),
01047 NWidget(WWT_DROPDOWN, COLOUR_BROWN, WID_ID_DROPDOWN_CRITERIA), SetDataTip(STR_JUST_STRING, STR_TOOLTIP_SORT_CRITERIA),
01048 NWidget(WWT_PANEL, COLOUR_BROWN), SetResize(1, 0), EndContainer(),
01049 EndContainer(),
01050 NWidget(WWT_PANEL, COLOUR_BROWN, WID_ID_INDUSTRY_LIST), SetDataTip(0x0, STR_INDUSTRY_DIRECTORY_LIST_CAPTION), SetResize(1, 1), SetScrollbar(WID_ID_SCROLLBAR), EndContainer(),
01051 EndContainer(),
01052 NWidget(NWID_VERTICAL),
01053 NWidget(NWID_VSCROLLBAR, COLOUR_BROWN, WID_ID_SCROLLBAR),
01054 NWidget(WWT_RESIZEBOX, COLOUR_BROWN),
01055 EndContainer(),
01056 EndContainer(),
01057 };
01058
01059 typedef GUIList<const Industry*> GUIIndustryList;
01060
01061
01065 class IndustryDirectoryWindow : public Window {
01066 protected:
01067
01068 static Listing last_sorting;
01069 static const Industry *last_industry;
01070
01071
01072 static const StringID sorter_names[];
01073 static GUIIndustryList::SortFunction * const sorter_funcs[];
01074
01075 GUIIndustryList industries;
01076 Scrollbar *vscroll;
01077
01079 void BuildSortIndustriesList()
01080 {
01081 if (this->industries.NeedRebuild()) {
01082 this->industries.Clear();
01083
01084 const Industry *i;
01085 FOR_ALL_INDUSTRIES(i) {
01086 *this->industries.Append() = i;
01087 }
01088
01089 this->industries.Compact();
01090 this->industries.RebuildDone();
01091 this->vscroll->SetCount(this->industries.Length());
01092 }
01093
01094 if (!this->industries.Sort()) return;
01095 IndustryDirectoryWindow::last_industry = NULL;
01096 this->SetWidgetDirty(WID_ID_INDUSTRY_LIST);
01097 }
01098
01106 static inline int GetCargoTransportedPercentsIfValid(const Industry *i, uint id)
01107 {
01108 assert(id < lengthof(i->produced_cargo));
01109
01110 if (i->produced_cargo[id] == CT_INVALID) return 101;
01111 return ToPercent8(i->last_month_pct_transported[id]);
01112 }
01113
01121 static int GetCargoTransportedSortValue(const Industry *i)
01122 {
01123 int p1 = GetCargoTransportedPercentsIfValid(i, 0);
01124 int p2 = GetCargoTransportedPercentsIfValid(i, 1);
01125
01126 if (p1 > p2) Swap(p1, p2);
01127
01128 return (p1 << 8) + p2;
01129 }
01130
01132 static int CDECL IndustryNameSorter(const Industry * const *a, const Industry * const *b)
01133 {
01134 static char buf_cache[96];
01135 static char buf[96];
01136
01137 SetDParam(0, (*a)->index);
01138 GetString(buf, STR_INDUSTRY_NAME, lastof(buf));
01139
01140 if (*b != last_industry) {
01141 last_industry = *b;
01142 SetDParam(0, (*b)->index);
01143 GetString(buf_cache, STR_INDUSTRY_NAME, lastof(buf_cache));
01144 }
01145
01146 return strnatcmp(buf, buf_cache);
01147 }
01148
01150 static int CDECL IndustryTypeSorter(const Industry * const *a, const Industry * const *b)
01151 {
01152 int it_a = 0;
01153 while (it_a != NUM_INDUSTRYTYPES && (*a)->type != _sorted_industry_types[it_a]) it_a++;
01154 int it_b = 0;
01155 while (it_b != NUM_INDUSTRYTYPES && (*b)->type != _sorted_industry_types[it_b]) it_b++;
01156 int r = it_a - it_b;
01157 return (r == 0) ? IndustryNameSorter(a, b) : r;
01158 }
01159
01161 static int CDECL IndustryProductionSorter(const Industry * const *a, const Industry * const *b)
01162 {
01163 uint prod_a = 0, prod_b = 0;
01164 for (uint i = 0; i < lengthof((*a)->produced_cargo); i++) {
01165 if ((*a)->produced_cargo[i] != CT_INVALID) prod_a += (*a)->last_month_production[i];
01166 if ((*b)->produced_cargo[i] != CT_INVALID) prod_b += (*b)->last_month_production[i];
01167 }
01168 int r = prod_a - prod_b;
01169
01170 return (r == 0) ? IndustryTypeSorter(a, b) : r;
01171 }
01172
01174 static int CDECL IndustryTransportedCargoSorter(const Industry * const *a, const Industry * const *b)
01175 {
01176 int r = GetCargoTransportedSortValue(*a) - GetCargoTransportedSortValue(*b);
01177 return (r == 0) ? IndustryNameSorter(a, b) : r;
01178 }
01179
01185 StringID GetIndustryString(const Industry *i) const
01186 {
01187 const IndustrySpec *indsp = GetIndustrySpec(i->type);
01188 byte p = 0;
01189
01190
01191 SetDParam(p++, i->index);
01192
01193 static char cargo_suffix[lengthof(i->produced_cargo)][512];
01194 GetAllCargoSuffixes(3, CST_DIR, i, i->type, indsp, i->produced_cargo, cargo_suffix);
01195
01196
01197 for (byte j = 0; j < lengthof(i->produced_cargo); j++) {
01198 if (i->produced_cargo[j] == CT_INVALID) continue;
01199 SetDParam(p++, i->produced_cargo[j]);
01200 SetDParam(p++, i->last_month_production[j]);
01201 SetDParamStr(p++, cargo_suffix[j]);
01202 }
01203
01204
01205 for (byte j = 0; j < lengthof(i->produced_cargo); j++) {
01206 if (i->produced_cargo[j] == CT_INVALID) continue;
01207 SetDParam(p++, ToPercent8(i->last_month_pct_transported[j]));
01208 }
01209
01210
01211 switch (p) {
01212 case 1: return STR_INDUSTRY_DIRECTORY_ITEM_NOPROD;
01213 case 5: return STR_INDUSTRY_DIRECTORY_ITEM;
01214 default: return STR_INDUSTRY_DIRECTORY_ITEM_TWO;
01215 }
01216 }
01217
01218 public:
01219 IndustryDirectoryWindow(WindowDesc *desc, WindowNumber number) : Window(desc)
01220 {
01221 this->CreateNestedTree();
01222 this->vscroll = this->GetScrollbar(WID_ID_SCROLLBAR);
01223
01224 this->industries.SetListing(this->last_sorting);
01225 this->industries.SetSortFuncs(IndustryDirectoryWindow::sorter_funcs);
01226 this->industries.ForceRebuild();
01227 this->BuildSortIndustriesList();
01228
01229 this->FinishInitNested(0);
01230 }
01231
01232 ~IndustryDirectoryWindow()
01233 {
01234 this->last_sorting = this->industries.GetListing();
01235 }
01236
01237 virtual void SetStringParameters(int widget) const
01238 {
01239 if (widget == WID_ID_DROPDOWN_CRITERIA) SetDParam(0, IndustryDirectoryWindow::sorter_names[this->industries.SortType()]);
01240 }
01241
01242 virtual void DrawWidget(const Rect &r, int widget) const
01243 {
01244 switch (widget) {
01245 case WID_ID_DROPDOWN_ORDER:
01246 this->DrawSortButtonState(widget, this->industries.IsDescSortOrder() ? SBS_DOWN : SBS_UP);
01247 break;
01248
01249 case WID_ID_INDUSTRY_LIST: {
01250 int n = 0;
01251 int y = r.top + WD_FRAMERECT_TOP;
01252 if (this->industries.Length() == 0) {
01253 DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_INDUSTRY_DIRECTORY_NONE);
01254 break;
01255 }
01256 for (uint i = this->vscroll->GetPosition(); i < this->industries.Length(); i++) {
01257 DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, this->GetIndustryString(this->industries[i]));
01258
01259 y += this->resize.step_height;
01260 if (++n == this->vscroll->GetCapacity()) break;
01261 }
01262 break;
01263 }
01264 }
01265 }
01266
01267 virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
01268 {
01269 switch (widget) {
01270 case WID_ID_DROPDOWN_ORDER: {
01271 Dimension d = GetStringBoundingBox(this->GetWidget<NWidgetCore>(widget)->widget_data);
01272 d.width += padding.width + WD_SORTBUTTON_ARROW_WIDTH * 2;
01273 d.height += padding.height;
01274 *size = maxdim(*size, d);
01275 break;
01276 }
01277
01278 case WID_ID_DROPDOWN_CRITERIA: {
01279 Dimension d = {0, 0};
01280 for (uint i = 0; IndustryDirectoryWindow::sorter_names[i] != INVALID_STRING_ID; i++) {
01281 d = maxdim(d, GetStringBoundingBox(IndustryDirectoryWindow::sorter_names[i]));
01282 }
01283 d.width += padding.width;
01284 d.height += padding.height;
01285 *size = maxdim(*size, d);
01286 break;
01287 }
01288
01289 case WID_ID_INDUSTRY_LIST: {
01290 Dimension d = GetStringBoundingBox(STR_INDUSTRY_DIRECTORY_NONE);
01291 for (uint i = 0; i < this->industries.Length(); i++) {
01292 d = maxdim(d, GetStringBoundingBox(this->GetIndustryString(this->industries[i])));
01293 }
01294 resize->height = d.height;
01295 d.height *= 5;
01296 d.width += padding.width + WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT;
01297 d.height += padding.height + WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
01298 *size = maxdim(*size, d);
01299 break;
01300 }
01301 }
01302 }
01303
01304
01305 virtual void OnClick(Point pt, int widget, int click_count)
01306 {
01307 switch (widget) {
01308 case WID_ID_DROPDOWN_ORDER:
01309 this->industries.ToggleSortOrder();
01310 this->SetDirty();
01311 break;
01312
01313 case WID_ID_DROPDOWN_CRITERIA:
01314 ShowDropDownMenu(this, IndustryDirectoryWindow::sorter_names, this->industries.SortType(), WID_ID_DROPDOWN_CRITERIA, 0, 0);
01315 break;
01316
01317 case WID_ID_INDUSTRY_LIST: {
01318 uint p = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_ID_INDUSTRY_LIST, WD_FRAMERECT_TOP);
01319 if (p < this->industries.Length()) {
01320 if (_ctrl_pressed) {
01321 ShowExtraViewPortWindow(this->industries[p]->location.tile);
01322 } else {
01323 ScrollMainWindowToTile(this->industries[p]->location.tile);
01324 }
01325 }
01326 break;
01327 }
01328 }
01329 }
01330
01331 virtual void OnDropdownSelect(int widget, int index)
01332 {
01333 if (this->industries.SortType() != index) {
01334 this->industries.SetSortType(index);
01335 this->BuildSortIndustriesList();
01336 }
01337 }
01338
01339 virtual void OnResize()
01340 {
01341 this->vscroll->SetCapacityFromWidget(this, WID_ID_INDUSTRY_LIST);
01342 }
01343
01344 virtual void OnPaint()
01345 {
01346 if (this->industries.NeedRebuild()) this->BuildSortIndustriesList();
01347 this->DrawWidgets();
01348 }
01349
01350 virtual void OnHundredthTick()
01351 {
01352 this->industries.ForceResort();
01353 this->BuildSortIndustriesList();
01354 }
01355
01361 virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
01362 {
01363 if (data == 0) {
01364
01365 this->industries.ForceRebuild();
01366 } else {
01367 this->industries.ForceResort();
01368 }
01369 }
01370 };
01371
01372 Listing IndustryDirectoryWindow::last_sorting = {false, 0};
01373 const Industry *IndustryDirectoryWindow::last_industry = NULL;
01374
01375
01376 GUIIndustryList::SortFunction * const IndustryDirectoryWindow::sorter_funcs[] = {
01377 &IndustryNameSorter,
01378 &IndustryTypeSorter,
01379 &IndustryProductionSorter,
01380 &IndustryTransportedCargoSorter
01381 };
01382
01383
01384 const StringID IndustryDirectoryWindow::sorter_names[] = {
01385 STR_SORT_BY_NAME,
01386 STR_SORT_BY_TYPE,
01387 STR_SORT_BY_PRODUCTION,
01388 STR_SORT_BY_TRANSPORTED,
01389 INVALID_STRING_ID
01390 };
01391
01392
01394 static WindowDesc _industry_directory_desc(
01395 WDP_AUTO, "list_industries", 428, 190,
01396 WC_INDUSTRY_DIRECTORY, WC_NONE,
01397 0,
01398 _nested_industry_directory_widgets, lengthof(_nested_industry_directory_widgets)
01399 );
01400
01401 void ShowIndustryDirectory()
01402 {
01403 AllocateWindowDescFront<IndustryDirectoryWindow>(&_industry_directory_desc, 0);
01404 }
01405
01407 static const NWidgetPart _nested_industry_cargoes_widgets[] = {
01408 NWidget(NWID_HORIZONTAL),
01409 NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
01410 NWidget(WWT_CAPTION, COLOUR_BROWN, WID_IC_CAPTION), SetDataTip(STR_INDUSTRY_CARGOES_INDUSTRY_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
01411 NWidget(WWT_SHADEBOX, COLOUR_BROWN),
01412 NWidget(WWT_DEFSIZEBOX, COLOUR_BROWN),
01413 NWidget(WWT_STICKYBOX, COLOUR_BROWN),
01414 EndContainer(),
01415 NWidget(NWID_HORIZONTAL),
01416 NWidget(NWID_VERTICAL),
01417 NWidget(WWT_PANEL, COLOUR_BROWN, WID_IC_PANEL), SetResize(1, 10), SetMinimalSize(200, 90), SetScrollbar(WID_IC_SCROLLBAR), EndContainer(),
01418 NWidget(NWID_HORIZONTAL),
01419 NWidget(WWT_TEXTBTN, COLOUR_BROWN, WID_IC_NOTIFY),
01420 SetDataTip(STR_INDUSTRY_CARGOES_NOTIFY_SMALLMAP, STR_INDUSTRY_CARGOES_NOTIFY_SMALLMAP_TOOLTIP),
01421 NWidget(WWT_PANEL, COLOUR_BROWN), SetFill(1, 0), SetResize(0, 0), EndContainer(),
01422 NWidget(WWT_DROPDOWN, COLOUR_BROWN, WID_IC_IND_DROPDOWN), SetFill(0, 0), SetResize(0, 0),
01423 SetDataTip(STR_INDUSTRY_CARGOES_SELECT_INDUSTRY, STR_INDUSTRY_CARGOES_SELECT_INDUSTRY_TOOLTIP),
01424 NWidget(WWT_DROPDOWN, COLOUR_BROWN, WID_IC_CARGO_DROPDOWN), SetFill(0, 0), SetResize(0, 0),
01425 SetDataTip(STR_INDUSTRY_CARGOES_SELECT_CARGO, STR_INDUSTRY_CARGOES_SELECT_CARGO_TOOLTIP),
01426 EndContainer(),
01427 EndContainer(),
01428 NWidget(NWID_VERTICAL),
01429 NWidget(NWID_VSCROLLBAR, COLOUR_BROWN, WID_IC_SCROLLBAR),
01430 NWidget(WWT_RESIZEBOX, COLOUR_BROWN),
01431 EndContainer(),
01432 EndContainer(),
01433 };
01434
01436 static WindowDesc _industry_cargoes_desc(
01437 WDP_AUTO, "industry_cargoes", 300, 210,
01438 WC_INDUSTRY_CARGOES, WC_NONE,
01439 0,
01440 _nested_industry_cargoes_widgets, lengthof(_nested_industry_cargoes_widgets)
01441 );
01442
01444 enum CargoesFieldType {
01445 CFT_EMPTY,
01446 CFT_SMALL_EMPTY,
01447 CFT_INDUSTRY,
01448 CFT_CARGO,
01449 CFT_CARGO_LABEL,
01450 CFT_HEADER,
01451 };
01452
01453 static const uint MAX_CARGOES = 3;
01454
01456 struct CargoesField {
01457 static const int VERT_INTER_INDUSTRY_SPACE;
01458 static const int HOR_CARGO_BORDER_SPACE;
01459 static const int CARGO_STUB_WIDTH;
01460 static const int HOR_CARGO_WIDTH, HOR_CARGO_SPACE;
01461 static const int CARGO_FIELD_WIDTH;
01462 static const int VERT_CARGO_SPACE, VERT_CARGO_EDGE;
01463 static const int BLOB_DISTANCE, BLOB_WIDTH, BLOB_HEIGHT;
01464
01465 static const int INDUSTRY_LINE_COLOUR;
01466 static const int CARGO_LINE_COLOUR;
01467
01468 static int small_height, normal_height;
01469 static int industry_width;
01470
01471 CargoesFieldType type;
01472 union {
01473 struct {
01474 IndustryType ind_type;
01475 CargoID other_produced[MAX_CARGOES];
01476 CargoID other_accepted[MAX_CARGOES];
01477 } industry;
01478 struct {
01479 CargoID vertical_cargoes[MAX_CARGOES];
01480 byte num_cargoes;
01481 CargoID supp_cargoes[MAX_CARGOES];
01482 byte top_end;
01483 CargoID cust_cargoes[MAX_CARGOES];
01484 byte bottom_end;
01485 } cargo;
01486 struct {
01487 CargoID cargoes[MAX_CARGOES];
01488 bool left_align;
01489 } cargo_label;
01490 StringID header;
01491 } u;
01492
01497 void MakeEmpty(CargoesFieldType type)
01498 {
01499 this->type = type;
01500 }
01501
01507 void MakeIndustry(IndustryType ind_type)
01508 {
01509 this->type = CFT_INDUSTRY;
01510 this->u.industry.ind_type = ind_type;
01511 MemSetT(this->u.industry.other_accepted, INVALID_CARGO, MAX_CARGOES);
01512 MemSetT(this->u.industry.other_produced, INVALID_CARGO, MAX_CARGOES);
01513 }
01514
01521 int ConnectCargo(CargoID cargo, bool producer)
01522 {
01523 assert(this->type == CFT_CARGO);
01524 if (cargo == INVALID_CARGO) return -1;
01525
01526
01527 int column = -1;
01528 for (int i = 0; i < this->u.cargo.num_cargoes; i++) {
01529 if (cargo == this->u.cargo.vertical_cargoes[i]) {
01530 column = i;
01531 break;
01532 }
01533 }
01534 if (column < 0) return -1;
01535
01536 if (producer) {
01537 assert(this->u.cargo.supp_cargoes[column] == INVALID_CARGO);
01538 this->u.cargo.supp_cargoes[column] = column;
01539 } else {
01540 assert(this->u.cargo.cust_cargoes[column] == INVALID_CARGO);
01541 this->u.cargo.cust_cargoes[column] = column;
01542 }
01543 return column;
01544 }
01545
01550 bool HasConnection()
01551 {
01552 assert(this->type == CFT_CARGO);
01553
01554 for (uint i = 0; i < MAX_CARGOES; i++) {
01555 if (this->u.cargo.supp_cargoes[i] != INVALID_CARGO) return true;
01556 if (this->u.cargo.cust_cargoes[i] != INVALID_CARGO) return true;
01557 }
01558 return false;
01559 }
01560
01570 void MakeCargo(const CargoID *cargoes, uint length, int count = -1, bool top_end = false, bool bottom_end = false)
01571 {
01572 this->type = CFT_CARGO;
01573 uint i;
01574 uint num = 0;
01575 for (i = 0; i < MAX_CARGOES && i < length; i++) {
01576 if (cargoes[i] != INVALID_CARGO) {
01577 this->u.cargo.vertical_cargoes[num] = cargoes[i];
01578 num++;
01579 }
01580 }
01581 this->u.cargo.num_cargoes = (count < 0) ? num : count;
01582 for (; num < MAX_CARGOES; num++) this->u.cargo.vertical_cargoes[num] = INVALID_CARGO;
01583 this->u.cargo.top_end = top_end;
01584 this->u.cargo.bottom_end = bottom_end;
01585 MemSetT(this->u.cargo.supp_cargoes, INVALID_CARGO, MAX_CARGOES);
01586 MemSetT(this->u.cargo.cust_cargoes, INVALID_CARGO, MAX_CARGOES);
01587 }
01588
01595 void MakeCargoLabel(const CargoID *cargoes, uint length, bool left_align)
01596 {
01597 this->type = CFT_CARGO_LABEL;
01598 uint i;
01599 for (i = 0; i < MAX_CARGOES && i < length; i++) this->u.cargo_label.cargoes[i] = cargoes[i];
01600 for (; i < MAX_CARGOES; i++) this->u.cargo_label.cargoes[i] = INVALID_CARGO;
01601 this->u.cargo_label.left_align = left_align;
01602 }
01603
01608 void MakeHeader(StringID textid)
01609 {
01610 this->type = CFT_HEADER;
01611 this->u.header = textid;
01612 }
01613
01619 int GetCargoBase(int xpos) const
01620 {
01621 assert(this->type == CFT_CARGO);
01622
01623 switch (this->u.cargo.num_cargoes) {
01624 case 0: return xpos + CARGO_FIELD_WIDTH / 2;
01625 case 1: return xpos + CARGO_FIELD_WIDTH / 2 - HOR_CARGO_WIDTH / 2;
01626 case 2: return xpos + CARGO_FIELD_WIDTH / 2 - HOR_CARGO_WIDTH - HOR_CARGO_SPACE / 2;
01627 case 3: return xpos + CARGO_FIELD_WIDTH / 2 - HOR_CARGO_WIDTH - HOR_CARGO_SPACE - HOR_CARGO_WIDTH / 2;
01628 default: NOT_REACHED();
01629 }
01630 }
01631
01637 void Draw(int xpos, int ypos) const
01638 {
01639 switch (this->type) {
01640 case CFT_EMPTY:
01641 case CFT_SMALL_EMPTY:
01642 break;
01643
01644 case CFT_HEADER:
01645 ypos += (small_height - FONT_HEIGHT_NORMAL) / 2;
01646 DrawString(xpos, xpos + industry_width, ypos, this->u.header, TC_WHITE, SA_HOR_CENTER);
01647 break;
01648
01649 case CFT_INDUSTRY: {
01650 int ypos1 = ypos + VERT_INTER_INDUSTRY_SPACE / 2;
01651 int ypos2 = ypos + normal_height - 1 - VERT_INTER_INDUSTRY_SPACE / 2;
01652 int xpos2 = xpos + industry_width - 1;
01653 GfxDrawLine(xpos, ypos1, xpos2, ypos1, INDUSTRY_LINE_COLOUR);
01654 GfxDrawLine(xpos, ypos1, xpos, ypos2, INDUSTRY_LINE_COLOUR);
01655 GfxDrawLine(xpos, ypos2, xpos2, ypos2, INDUSTRY_LINE_COLOUR);
01656 GfxDrawLine(xpos2, ypos1, xpos2, ypos2, INDUSTRY_LINE_COLOUR);
01657 ypos += (normal_height - FONT_HEIGHT_NORMAL) / 2;
01658 if (this->u.industry.ind_type < NUM_INDUSTRYTYPES) {
01659 const IndustrySpec *indsp = GetIndustrySpec(this->u.industry.ind_type);
01660 SetDParam(0, indsp->name);
01661 DrawString(xpos, xpos2, ypos, STR_JUST_STRING, TC_WHITE, SA_HOR_CENTER);
01662
01663
01664 int blob_left, blob_right;
01665 if (_current_text_dir == TD_RTL) {
01666 blob_right = xpos2 - BLOB_DISTANCE;
01667 blob_left = blob_right - BLOB_WIDTH;
01668 } else {
01669 blob_left = xpos + BLOB_DISTANCE;
01670 blob_right = blob_left + BLOB_WIDTH;
01671 }
01672 GfxFillRect(blob_left, ypos2 - BLOB_DISTANCE - BLOB_HEIGHT, blob_right, ypos2 - BLOB_DISTANCE, PC_BLACK);
01673 GfxFillRect(blob_left + 1, ypos2 - BLOB_DISTANCE - BLOB_HEIGHT + 1, blob_right - 1, ypos2 - BLOB_DISTANCE - 1, indsp->map_colour);
01674 } else {
01675 DrawString(xpos, xpos2, ypos, STR_INDUSTRY_CARGOES_HOUSES, TC_FROMSTRING, SA_HOR_CENTER);
01676 }
01677
01678
01679 const CargoID *other_right, *other_left;
01680 if (_current_text_dir == TD_RTL) {
01681 other_right = this->u.industry.other_accepted;
01682 other_left = this->u.industry.other_produced;
01683 } else {
01684 other_right = this->u.industry.other_produced;
01685 other_left = this->u.industry.other_accepted;
01686 }
01687 ypos1 += VERT_CARGO_EDGE;
01688 for (uint i = 0; i < MAX_CARGOES; i++) {
01689 if (other_right[i] != INVALID_CARGO) {
01690 const CargoSpec *csp = CargoSpec::Get(other_right[i]);
01691 int xp = xpos + industry_width + CARGO_STUB_WIDTH;
01692 DrawHorConnection(xpos + industry_width, xp - 1, ypos1, csp);
01693 GfxDrawLine(xp, ypos1, xp, ypos1 + FONT_HEIGHT_NORMAL - 1, CARGO_LINE_COLOUR);
01694 }
01695 if (other_left[i] != INVALID_CARGO) {
01696 const CargoSpec *csp = CargoSpec::Get(other_left[i]);
01697 int xp = xpos - CARGO_STUB_WIDTH;
01698 DrawHorConnection(xp + 1, xpos - 1, ypos1, csp);
01699 GfxDrawLine(xp, ypos1, xp, ypos1 + FONT_HEIGHT_NORMAL - 1, CARGO_LINE_COLOUR);
01700 }
01701 ypos1 += FONT_HEIGHT_NORMAL + VERT_CARGO_SPACE;
01702 }
01703 break;
01704 }
01705
01706 case CFT_CARGO: {
01707 int cargo_base = this->GetCargoBase(xpos);
01708 int top = ypos + (this->u.cargo.top_end ? VERT_INTER_INDUSTRY_SPACE / 2 + 1 : 0);
01709 int bot = ypos - (this->u.cargo.bottom_end ? VERT_INTER_INDUSTRY_SPACE / 2 + 1 : 0) + normal_height - 1;
01710 int colpos = cargo_base;
01711 for (int i = 0; i < this->u.cargo.num_cargoes; i++) {
01712 if (this->u.cargo.top_end) GfxDrawLine(colpos, top - 1, colpos + HOR_CARGO_WIDTH - 1, top - 1, CARGO_LINE_COLOUR);
01713 if (this->u.cargo.bottom_end) GfxDrawLine(colpos, bot + 1, colpos + HOR_CARGO_WIDTH - 1, bot + 1, CARGO_LINE_COLOUR);
01714 GfxDrawLine(colpos, top, colpos, bot, CARGO_LINE_COLOUR);
01715 colpos++;
01716 const CargoSpec *csp = CargoSpec::Get(this->u.cargo.vertical_cargoes[i]);
01717 GfxFillRect(colpos, top, colpos + HOR_CARGO_WIDTH - 2, bot, csp->legend_colour, FILLRECT_OPAQUE);
01718 colpos += HOR_CARGO_WIDTH - 2;
01719 GfxDrawLine(colpos, top, colpos, bot, CARGO_LINE_COLOUR);
01720 colpos += 1 + HOR_CARGO_SPACE;
01721 }
01722
01723 const CargoID *hor_left, *hor_right;
01724 if (_current_text_dir == TD_RTL) {
01725 hor_left = this->u.cargo.cust_cargoes;
01726 hor_right = this->u.cargo.supp_cargoes;
01727 } else {
01728 hor_left = this->u.cargo.supp_cargoes;
01729 hor_right = this->u.cargo.cust_cargoes;
01730 }
01731 ypos += VERT_CARGO_EDGE + VERT_INTER_INDUSTRY_SPACE / 2;
01732 for (uint i = 0; i < MAX_CARGOES; i++) {
01733 if (hor_left[i] != INVALID_CARGO) {
01734 int col = hor_left[i];
01735 int dx = 0;
01736 const CargoSpec *csp = CargoSpec::Get(this->u.cargo.vertical_cargoes[col]);
01737 for (; col > 0; col--) {
01738 int lf = cargo_base + col * HOR_CARGO_WIDTH + (col - 1) * HOR_CARGO_SPACE;
01739 DrawHorConnection(lf, lf + HOR_CARGO_SPACE - dx, ypos, csp);
01740 dx = 1;
01741 }
01742 DrawHorConnection(xpos, cargo_base - dx, ypos, csp);
01743 }
01744 if (hor_right[i] != INVALID_CARGO) {
01745 int col = hor_right[i];
01746 int dx = 0;
01747 const CargoSpec *csp = CargoSpec::Get(this->u.cargo.vertical_cargoes[col]);
01748 for (; col < this->u.cargo.num_cargoes - 1; col++) {
01749 int lf = cargo_base + (col + 1) * HOR_CARGO_WIDTH + col * HOR_CARGO_SPACE;
01750 DrawHorConnection(lf + dx - 1, lf + HOR_CARGO_SPACE - 1, ypos, csp);
01751 dx = 1;
01752 }
01753 DrawHorConnection(cargo_base + col * HOR_CARGO_SPACE + (col + 1) * HOR_CARGO_WIDTH - 1 + dx, xpos + CARGO_FIELD_WIDTH - 1, ypos, csp);
01754 }
01755 ypos += FONT_HEIGHT_NORMAL + VERT_CARGO_SPACE;
01756 }
01757 break;
01758 }
01759
01760 case CFT_CARGO_LABEL:
01761 ypos += VERT_CARGO_EDGE + VERT_INTER_INDUSTRY_SPACE / 2;
01762 for (uint i = 0; i < MAX_CARGOES; i++) {
01763 if (this->u.cargo_label.cargoes[i] != INVALID_CARGO) {
01764 const CargoSpec *csp = CargoSpec::Get(this->u.cargo_label.cargoes[i]);
01765 DrawString(xpos + WD_FRAMERECT_LEFT, xpos + industry_width - 1 - WD_FRAMERECT_RIGHT, ypos, csp->name, TC_WHITE,
01766 (this->u.cargo_label.left_align) ? SA_LEFT : SA_RIGHT);
01767 }
01768 ypos += FONT_HEIGHT_NORMAL + VERT_CARGO_SPACE;
01769 }
01770 break;
01771
01772 default:
01773 NOT_REACHED();
01774 }
01775 }
01776
01784 CargoID CargoClickedAt(const CargoesField *left, const CargoesField *right, Point pt) const
01785 {
01786 assert(this->type == CFT_CARGO);
01787
01788
01789 int cpos = this->GetCargoBase(0);
01790 uint col;
01791 for (col = 0; col < this->u.cargo.num_cargoes; col++) {
01792 if (pt.x < cpos) break;
01793 if (pt.x < cpos + CargoesField::HOR_CARGO_WIDTH) return this->u.cargo.vertical_cargoes[col];
01794 cpos += CargoesField::HOR_CARGO_WIDTH + CargoesField::HOR_CARGO_SPACE;
01795 }
01796
01797
01798 int vpos = VERT_INTER_INDUSTRY_SPACE / 2 + VERT_CARGO_EDGE;
01799 uint row;
01800 for (row = 0; row < MAX_CARGOES; row++) {
01801 if (pt.y < vpos) return INVALID_CARGO;
01802 if (pt.y < vpos + FONT_HEIGHT_NORMAL) break;
01803 vpos += FONT_HEIGHT_NORMAL + VERT_CARGO_SPACE;
01804 }
01805 if (row == MAX_CARGOES) return INVALID_CARGO;
01806
01807
01808 if (col == 0) {
01809 if (this->u.cargo.supp_cargoes[row] != INVALID_CARGO) return this->u.cargo.vertical_cargoes[this->u.cargo.supp_cargoes[row]];
01810 if (left != NULL) {
01811 if (left->type == CFT_INDUSTRY) return left->u.industry.other_produced[row];
01812 if (left->type == CFT_CARGO_LABEL && !left->u.cargo_label.left_align) return left->u.cargo_label.cargoes[row];
01813 }
01814 return INVALID_CARGO;
01815 }
01816 if (col == this->u.cargo.num_cargoes) {
01817 if (this->u.cargo.cust_cargoes[row] != INVALID_CARGO) return this->u.cargo.vertical_cargoes[this->u.cargo.cust_cargoes[row]];
01818 if (right != NULL) {
01819 if (right->type == CFT_INDUSTRY) return right->u.industry.other_accepted[row];
01820 if (right->type == CFT_CARGO_LABEL && right->u.cargo_label.left_align) return right->u.cargo_label.cargoes[row];
01821 }
01822 return INVALID_CARGO;
01823 }
01824 if (row >= col) {
01825
01826
01827
01828
01829 return (this->u.cargo.supp_cargoes[row] != INVALID_CARGO) ? this->u.cargo.vertical_cargoes[this->u.cargo.supp_cargoes[row]] : INVALID_CARGO;
01830 } else {
01831
01832 return (this->u.cargo.cust_cargoes[row] != INVALID_CARGO) ? this->u.cargo.vertical_cargoes[this->u.cargo.cust_cargoes[row]] : INVALID_CARGO;
01833 }
01834 }
01835
01841 CargoID CargoLabelClickedAt(Point pt) const
01842 {
01843 assert(this->type == CFT_CARGO_LABEL);
01844
01845 int vpos = VERT_INTER_INDUSTRY_SPACE / 2 + VERT_CARGO_EDGE;
01846 uint row;
01847 for (row = 0; row < MAX_CARGOES; row++) {
01848 if (pt.y < vpos) return INVALID_CARGO;
01849 if (pt.y < vpos + FONT_HEIGHT_NORMAL) break;
01850 vpos += FONT_HEIGHT_NORMAL + VERT_CARGO_SPACE;
01851 }
01852 if (row == MAX_CARGOES) return INVALID_CARGO;
01853 return this->u.cargo_label.cargoes[row];
01854 }
01855
01856 private:
01864 static void DrawHorConnection(int left, int right, int top, const CargoSpec *csp)
01865 {
01866 GfxDrawLine(left, top, right, top, CARGO_LINE_COLOUR);
01867 GfxFillRect(left, top + 1, right, top + FONT_HEIGHT_NORMAL - 2, csp->legend_colour, FILLRECT_OPAQUE);
01868 GfxDrawLine(left, top + FONT_HEIGHT_NORMAL - 1, right, top + FONT_HEIGHT_NORMAL - 1, CARGO_LINE_COLOUR);
01869 }
01870 };
01871
01872 assert_compile(MAX_CARGOES >= cpp_lengthof(IndustrySpec, produced_cargo));
01873 assert_compile(MAX_CARGOES >= cpp_lengthof(IndustrySpec, accepts_cargo));
01874
01875 int CargoesField::small_height;
01876 int CargoesField::normal_height;
01877 int CargoesField::industry_width;
01878 const int CargoesField::VERT_INTER_INDUSTRY_SPACE = 6;
01879
01880 const int CargoesField::HOR_CARGO_BORDER_SPACE = 15;
01881 const int CargoesField::CARGO_STUB_WIDTH = 10;
01882 const int CargoesField::HOR_CARGO_WIDTH = 15;
01883 const int CargoesField::HOR_CARGO_SPACE = 5;
01884 const int CargoesField::VERT_CARGO_EDGE = 4;
01885 const int CargoesField::VERT_CARGO_SPACE = 4;
01886
01887 const int CargoesField::BLOB_DISTANCE = 5;
01888 const int CargoesField::BLOB_WIDTH = 12;
01889 const int CargoesField::BLOB_HEIGHT = 9;
01890
01892 const int CargoesField::CARGO_FIELD_WIDTH = HOR_CARGO_BORDER_SPACE * 2 + HOR_CARGO_WIDTH * MAX_CARGOES + HOR_CARGO_SPACE * (MAX_CARGOES - 1);
01893
01894 const int CargoesField::INDUSTRY_LINE_COLOUR = PC_YELLOW;
01895 const int CargoesField::CARGO_LINE_COLOUR = PC_YELLOW;
01896
01898 struct CargoesRow {
01899 CargoesField columns[5];
01900
01905 void ConnectIndustryProduced(int column)
01906 {
01907 CargoesField *ind_fld = this->columns + column;
01908 CargoesField *cargo_fld = this->columns + column + 1;
01909 assert(ind_fld->type == CFT_INDUSTRY && cargo_fld->type == CFT_CARGO);
01910
01911 MemSetT(ind_fld->u.industry.other_produced, INVALID_CARGO, MAX_CARGOES);
01912
01913 if (ind_fld->u.industry.ind_type < NUM_INDUSTRYTYPES) {
01914 CargoID others[MAX_CARGOES];
01915 int other_count = 0;
01916
01917 const IndustrySpec *indsp = GetIndustrySpec(ind_fld->u.industry.ind_type);
01918 for (uint i = 0; i < lengthof(indsp->produced_cargo); i++) {
01919 int col = cargo_fld->ConnectCargo(indsp->produced_cargo[i], true);
01920 if (col < 0) others[other_count++] = indsp->produced_cargo[i];
01921 }
01922
01923
01924 for (uint i = 0; i < MAX_CARGOES && other_count > 0; i++) {
01925 if (cargo_fld->u.cargo.supp_cargoes[i] == INVALID_CARGO) ind_fld->u.industry.other_produced[i] = others[--other_count];
01926 }
01927 } else {
01928
01929 for (uint i = 0; i < cargo_fld->u.cargo.num_cargoes; i++) {
01930 CargoID cid = cargo_fld->u.cargo.vertical_cargoes[i];
01931 if (cid == CT_PASSENGERS || cid == CT_MAIL) cargo_fld->ConnectCargo(cid, true);
01932 }
01933 }
01934 }
01935
01941 void MakeCargoLabel(int column, bool accepting)
01942 {
01943 CargoID cargoes[MAX_CARGOES];
01944 MemSetT(cargoes, INVALID_CARGO, lengthof(cargoes));
01945
01946 CargoesField *label_fld = this->columns + column;
01947 CargoesField *cargo_fld = this->columns + (accepting ? column - 1 : column + 1);
01948
01949 assert(cargo_fld->type == CFT_CARGO && label_fld->type == CFT_EMPTY);
01950 for (uint i = 0; i < cargo_fld->u.cargo.num_cargoes; i++) {
01951 int col = cargo_fld->ConnectCargo(cargo_fld->u.cargo.vertical_cargoes[i], !accepting);
01952 cargoes[col] = cargo_fld->u.cargo.vertical_cargoes[i];
01953 }
01954 label_fld->MakeCargoLabel(cargoes, lengthof(cargoes), accepting);
01955 }
01956
01957
01962 void ConnectIndustryAccepted(int column)
01963 {
01964 CargoesField *ind_fld = this->columns + column;
01965 CargoesField *cargo_fld = this->columns + column - 1;
01966 assert(ind_fld->type == CFT_INDUSTRY && cargo_fld->type == CFT_CARGO);
01967
01968 MemSetT(ind_fld->u.industry.other_accepted, INVALID_CARGO, MAX_CARGOES);
01969
01970 if (ind_fld->u.industry.ind_type < NUM_INDUSTRYTYPES) {
01971 CargoID others[MAX_CARGOES];
01972 int other_count = 0;
01973
01974 const IndustrySpec *indsp = GetIndustrySpec(ind_fld->u.industry.ind_type);
01975 for (uint i = 0; i < lengthof(indsp->accepts_cargo); i++) {
01976 int col = cargo_fld->ConnectCargo(indsp->accepts_cargo[i], false);
01977 if (col < 0) others[other_count++] = indsp->accepts_cargo[i];
01978 }
01979
01980
01981 for (uint i = 0; i < MAX_CARGOES && other_count > 0; i++) {
01982 if (cargo_fld->u.cargo.cust_cargoes[i] == INVALID_CARGO) ind_fld->u.industry.other_accepted[i] = others[--other_count];
01983 }
01984 } else {
01985
01986 for (uint i = 0; i < cargo_fld->u.cargo.num_cargoes; i++) {
01987 for (uint h = 0; h < HOUSE_MAX; h++) {
01988 HouseSpec *hs = HouseSpec::Get(h);
01989 if (!hs->enabled) continue;
01990
01991 for (uint j = 0; j < lengthof(hs->accepts_cargo); j++) {
01992 if (cargo_fld->u.cargo.vertical_cargoes[i] == hs->accepts_cargo[j]) {
01993 cargo_fld->ConnectCargo(cargo_fld->u.cargo.vertical_cargoes[i], false);
01994 goto next_cargo;
01995 }
01996 }
01997 }
01998 next_cargo: ;
01999 }
02000 }
02001 }
02002 };
02003
02004
02032 struct IndustryCargoesWindow : public Window {
02033 static const int HOR_TEXT_PADDING, VERT_TEXT_PADDING;
02034
02035 typedef SmallVector<CargoesRow, 4> Fields;
02036
02037 Fields fields;
02038 uint ind_cargo;
02039 Dimension cargo_textsize;
02040 Dimension ind_textsize;
02041 Scrollbar *vscroll;
02042
02043 IndustryCargoesWindow(int id) : Window(&_industry_cargoes_desc)
02044 {
02045 this->OnInit();
02046 this->CreateNestedTree();
02047 this->vscroll = this->GetScrollbar(WID_IC_SCROLLBAR);
02048 this->FinishInitNested(0);
02049 this->OnInvalidateData(id);
02050 }
02051
02052 virtual void OnInit()
02053 {
02054
02055 Dimension d = GetStringBoundingBox(STR_INDUSTRY_CARGOES_PRODUCERS);
02056 d = maxdim(d, GetStringBoundingBox(STR_INDUSTRY_CARGOES_CUSTOMERS));
02057 d.width += WD_FRAMETEXT_LEFT + WD_FRAMETEXT_RIGHT;
02058 d.height += WD_FRAMETEXT_TOP + WD_FRAMETEXT_BOTTOM;
02059 CargoesField::small_height = d.height;
02060
02061
02062 this->ind_textsize.width = 0;
02063 this->ind_textsize.height = 0;
02064 for (IndustryType it = 0; it < NUM_INDUSTRYTYPES; it++) {
02065 const IndustrySpec *indsp = GetIndustrySpec(it);
02066 if (!indsp->enabled) continue;
02067 this->ind_textsize = maxdim(this->ind_textsize, GetStringBoundingBox(indsp->name));
02068 }
02069 d.width = max(d.width, this->ind_textsize.width);
02070 d.height = this->ind_textsize.height;
02071 this->ind_textsize = maxdim(this->ind_textsize, GetStringBoundingBox(STR_INDUSTRY_CARGOES_SELECT_INDUSTRY));
02072
02073
02074 this->cargo_textsize.width = 0;
02075 this->cargo_textsize.height = 0;
02076 for (uint i = 0; i < NUM_CARGO; i++) {
02077 const CargoSpec *csp = CargoSpec::Get(i);
02078 if (!csp->IsValid()) continue;
02079 this->cargo_textsize = maxdim(this->cargo_textsize, GetStringBoundingBox(csp->name));
02080 }
02081 d = maxdim(d, this->cargo_textsize);
02082 this->cargo_textsize = maxdim(this->cargo_textsize, GetStringBoundingBox(STR_INDUSTRY_CARGOES_SELECT_CARGO));
02083
02084 d.width += 2 * HOR_TEXT_PADDING;
02085
02086 uint min_ind_height = CargoesField::VERT_CARGO_EDGE * 2 + MAX_CARGOES * FONT_HEIGHT_NORMAL + (MAX_CARGOES - 1) * CargoesField::VERT_CARGO_SPACE;
02087 d.height = max(d.height + 2 * VERT_TEXT_PADDING, min_ind_height);
02088
02089 CargoesField::industry_width = d.width;
02090 CargoesField::normal_height = d.height + CargoesField::VERT_INTER_INDUSTRY_SPACE;
02091 }
02092
02093 virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
02094 {
02095 switch (widget) {
02096 case WID_IC_PANEL:
02097 size->width = WD_FRAMETEXT_LEFT + CargoesField::industry_width * 3 + CargoesField::CARGO_FIELD_WIDTH * 2 + WD_FRAMETEXT_RIGHT;
02098 break;
02099
02100 case WID_IC_IND_DROPDOWN:
02101 size->width = max(size->width, this->ind_textsize.width + padding.width);
02102 break;
02103
02104 case WID_IC_CARGO_DROPDOWN:
02105 size->width = max(size->width, this->cargo_textsize.width + padding.width);
02106 break;
02107 }
02108 }
02109
02110
02111 CargoesFieldType type;
02112 virtual void SetStringParameters (int widget) const
02113 {
02114 if (widget != WID_IC_CAPTION) return;
02115
02116 if (this->ind_cargo < NUM_INDUSTRYTYPES) {
02117 const IndustrySpec *indsp = GetIndustrySpec(this->ind_cargo);
02118 SetDParam(0, indsp->name);
02119 } else {
02120 const CargoSpec *csp = CargoSpec::Get(this->ind_cargo - NUM_INDUSTRYTYPES);
02121 SetDParam(0, csp->name);
02122 }
02123 }
02124
02133 static bool HasCommonValidCargo(const CargoID *cargoes1, uint length1, const CargoID *cargoes2, uint length2)
02134 {
02135 while (length1 > 0) {
02136 if (*cargoes1 != INVALID_CARGO) {
02137 for (uint i = 0; i < length2; i++) if (*cargoes1 == cargoes2[i]) return true;
02138 }
02139 cargoes1++;
02140 length1--;
02141 }
02142 return false;
02143 }
02144
02151 static bool HousesCanSupply(const CargoID *cargoes, uint length)
02152 {
02153 for (uint i = 0; i < length; i++) {
02154 if (cargoes[i] == INVALID_CARGO) continue;
02155 if (cargoes[i] == CT_PASSENGERS || cargoes[i] == CT_MAIL) return true;
02156 }
02157 return false;
02158 }
02159
02166 static bool HousesCanAccept(const CargoID *cargoes, uint length)
02167 {
02168 HouseZones climate_mask;
02169 switch (_settings_game.game_creation.landscape) {
02170 case LT_TEMPERATE: climate_mask = HZ_TEMP; break;
02171 case LT_ARCTIC: climate_mask = HZ_SUBARTC_ABOVE | HZ_SUBARTC_BELOW; break;
02172 case LT_TROPIC: climate_mask = HZ_SUBTROPIC; break;
02173 case LT_TOYLAND: climate_mask = HZ_TOYLND; break;
02174 default: NOT_REACHED();
02175 }
02176 for (uint i = 0; i < length; i++) {
02177 if (cargoes[i] == INVALID_CARGO) continue;
02178
02179 for (uint h = 0; h < HOUSE_MAX; h++) {
02180 HouseSpec *hs = HouseSpec::Get(h);
02181 if (!hs->enabled || !(hs->building_availability & climate_mask)) continue;
02182
02183 for (uint j = 0; j < lengthof(hs->accepts_cargo); j++) {
02184 if (cargoes[i] == hs->accepts_cargo[j]) return true;
02185 }
02186 }
02187 }
02188 return false;
02189 }
02190
02197 static int CountMatchingAcceptingIndustries(const CargoID *cargoes, uint length)
02198 {
02199 int count = 0;
02200 for (IndustryType it = 0; it < NUM_INDUSTRYTYPES; it++) {
02201 const IndustrySpec *indsp = GetIndustrySpec(it);
02202 if (!indsp->enabled) continue;
02203
02204 if (HasCommonValidCargo(cargoes, length, indsp->accepts_cargo, lengthof(indsp->accepts_cargo))) count++;
02205 }
02206 return count;
02207 }
02208
02215 static int CountMatchingProducingIndustries(const CargoID *cargoes, uint length)
02216 {
02217 int count = 0;
02218 for (IndustryType it = 0; it < NUM_INDUSTRYTYPES; it++) {
02219 const IndustrySpec *indsp = GetIndustrySpec(it);
02220 if (!indsp->enabled) continue;
02221
02222 if (HasCommonValidCargo(cargoes, length, indsp->produced_cargo, lengthof(indsp->produced_cargo))) count++;
02223 }
02224 return count;
02225 }
02226
02233 void ShortenCargoColumn(int column, int top, int bottom)
02234 {
02235 while (top < bottom && !this->fields[top].columns[column].HasConnection()) {
02236 this->fields[top].columns[column].MakeEmpty(CFT_EMPTY);
02237 top++;
02238 }
02239 this->fields[top].columns[column].u.cargo.top_end = true;
02240
02241 while (bottom > top && !this->fields[bottom].columns[column].HasConnection()) {
02242 this->fields[bottom].columns[column].MakeEmpty(CFT_EMPTY);
02243 bottom--;
02244 }
02245 this->fields[bottom].columns[column].u.cargo.bottom_end = true;
02246 }
02247
02254 void PlaceIndustry(int row, int col, IndustryType it)
02255 {
02256 assert(this->fields[row].columns[col].type == CFT_EMPTY);
02257 this->fields[row].columns[col].MakeIndustry(it);
02258 if (col == 0) {
02259 this->fields[row].ConnectIndustryProduced(col);
02260 } else {
02261 this->fields[row].ConnectIndustryAccepted(col);
02262 }
02263 }
02264
02268 void NotifySmallmap()
02269 {
02270 if (!this->IsWidgetLowered(WID_IC_NOTIFY)) return;
02271
02272
02273
02274 InvalidateWindowClassesData(WC_SMALLMAP, 0);
02275 }
02276
02281 void ComputeIndustryDisplay(IndustryType it)
02282 {
02283 this->GetWidget<NWidgetCore>(WID_IC_CAPTION)->widget_data = STR_INDUSTRY_CARGOES_INDUSTRY_CAPTION;
02284 this->ind_cargo = it;
02285 _displayed_industries = 1ULL << it;
02286
02287 this->fields.Clear();
02288 CargoesRow *row = this->fields.Append();
02289 row->columns[0].MakeHeader(STR_INDUSTRY_CARGOES_PRODUCERS);
02290 row->columns[1].MakeEmpty(CFT_SMALL_EMPTY);
02291 row->columns[2].MakeEmpty(CFT_SMALL_EMPTY);
02292 row->columns[3].MakeEmpty(CFT_SMALL_EMPTY);
02293 row->columns[4].MakeHeader(STR_INDUSTRY_CARGOES_CUSTOMERS);
02294
02295 const IndustrySpec *central_sp = GetIndustrySpec(it);
02296 bool houses_supply = HousesCanSupply(central_sp->accepts_cargo, lengthof(central_sp->accepts_cargo));
02297 bool houses_accept = HousesCanAccept(central_sp->produced_cargo, lengthof(central_sp->produced_cargo));
02298
02299 int num_supp = CountMatchingProducingIndustries(central_sp->accepts_cargo, lengthof(central_sp->accepts_cargo)) + houses_supply;
02300 int num_cust = CountMatchingAcceptingIndustries(central_sp->produced_cargo, lengthof(central_sp->produced_cargo)) + houses_accept;
02301 int num_indrows = max(3, max(num_supp, num_cust));
02302 for (int i = 0; i < num_indrows; i++) {
02303 CargoesRow *row = this->fields.Append();
02304 row->columns[0].MakeEmpty(CFT_EMPTY);
02305 row->columns[1].MakeCargo(central_sp->accepts_cargo, lengthof(central_sp->accepts_cargo));
02306 row->columns[2].MakeEmpty(CFT_EMPTY);
02307 row->columns[3].MakeCargo(central_sp->produced_cargo, lengthof(central_sp->produced_cargo));
02308 row->columns[4].MakeEmpty(CFT_EMPTY);
02309 }
02310
02311 int central_row = 1 + num_indrows / 2;
02312 this->fields[central_row].columns[2].MakeIndustry(it);
02313 this->fields[central_row].ConnectIndustryProduced(2);
02314 this->fields[central_row].ConnectIndustryAccepted(2);
02315
02316
02317 this->fields[central_row - 1].MakeCargoLabel(2, true);
02318 this->fields[central_row + 1].MakeCargoLabel(2, false);
02319
02320
02321 int supp_count = 0;
02322 int cust_count = 0;
02323 for (IndustryType it = 0; it < NUM_INDUSTRYTYPES; it++) {
02324 const IndustrySpec *indsp = GetIndustrySpec(it);
02325 if (!indsp->enabled) continue;
02326
02327 if (HasCommonValidCargo(central_sp->accepts_cargo, lengthof(central_sp->accepts_cargo), indsp->produced_cargo, lengthof(indsp->produced_cargo))) {
02328 this->PlaceIndustry(1 + supp_count * num_indrows / num_supp, 0, it);
02329 SetBit(_displayed_industries, it);
02330 supp_count++;
02331 }
02332 if (HasCommonValidCargo(central_sp->produced_cargo, lengthof(central_sp->produced_cargo), indsp->accepts_cargo, lengthof(indsp->accepts_cargo))) {
02333 this->PlaceIndustry(1 + cust_count * num_indrows / num_cust, 4, it);
02334 SetBit(_displayed_industries, it);
02335 cust_count++;
02336 }
02337 }
02338 if (houses_supply) {
02339 this->PlaceIndustry(1 + supp_count * num_indrows / num_supp, 0, NUM_INDUSTRYTYPES);
02340 supp_count++;
02341 }
02342 if (houses_accept) {
02343 this->PlaceIndustry(1 + cust_count * num_indrows / num_cust, 4, NUM_INDUSTRYTYPES);
02344 cust_count++;
02345 }
02346
02347 this->ShortenCargoColumn(1, 1, num_indrows);
02348 this->ShortenCargoColumn(3, 1, num_indrows);
02349 const NWidgetBase *nwp = this->GetWidget<NWidgetBase>(WID_IC_PANEL);
02350 this->vscroll->SetCount(CeilDiv(WD_FRAMETEXT_TOP + WD_FRAMETEXT_BOTTOM + CargoesField::small_height + num_indrows * CargoesField::normal_height, nwp->resize_y));
02351 this->SetDirty();
02352 this->NotifySmallmap();
02353 }
02354
02359 void ComputeCargoDisplay(CargoID cid)
02360 {
02361 this->GetWidget<NWidgetCore>(WID_IC_CAPTION)->widget_data = STR_INDUSTRY_CARGOES_CARGO_CAPTION;
02362 this->ind_cargo = cid + NUM_INDUSTRYTYPES;
02363 _displayed_industries = 0;
02364
02365 this->fields.Clear();
02366 CargoesRow *row = this->fields.Append();
02367 row->columns[0].MakeHeader(STR_INDUSTRY_CARGOES_PRODUCERS);
02368 row->columns[1].MakeEmpty(CFT_SMALL_EMPTY);
02369 row->columns[2].MakeHeader(STR_INDUSTRY_CARGOES_CUSTOMERS);
02370 row->columns[3].MakeEmpty(CFT_SMALL_EMPTY);
02371 row->columns[4].MakeEmpty(CFT_SMALL_EMPTY);
02372
02373 bool houses_supply = HousesCanSupply(&cid, 1);
02374 bool houses_accept = HousesCanAccept(&cid, 1);
02375 int num_supp = CountMatchingProducingIndustries(&cid, 1) + houses_supply + 1;
02376 int num_cust = CountMatchingAcceptingIndustries(&cid, 1) + houses_accept;
02377 int num_indrows = max(num_supp, num_cust);
02378 for (int i = 0; i < num_indrows; i++) {
02379 CargoesRow *row = this->fields.Append();
02380 row->columns[0].MakeEmpty(CFT_EMPTY);
02381 row->columns[1].MakeCargo(&cid, 1);
02382 row->columns[2].MakeEmpty(CFT_EMPTY);
02383 row->columns[3].MakeEmpty(CFT_EMPTY);
02384 row->columns[4].MakeEmpty(CFT_EMPTY);
02385 }
02386
02387 this->fields[num_indrows].MakeCargoLabel(0, false);
02388
02389
02390 int supp_count = 0;
02391 int cust_count = 0;
02392 for (IndustryType it = 0; it < NUM_INDUSTRYTYPES; it++) {
02393 const IndustrySpec *indsp = GetIndustrySpec(it);
02394 if (!indsp->enabled) continue;
02395
02396 if (HasCommonValidCargo(&cid, 1, indsp->produced_cargo, lengthof(indsp->produced_cargo))) {
02397 this->PlaceIndustry(1 + supp_count * num_indrows / num_supp, 0, it);
02398 SetBit(_displayed_industries, it);
02399 supp_count++;
02400 }
02401 if (HasCommonValidCargo(&cid, 1, indsp->accepts_cargo, lengthof(indsp->accepts_cargo))) {
02402 this->PlaceIndustry(1 + cust_count * num_indrows / num_cust, 2, it);
02403 SetBit(_displayed_industries, it);
02404 cust_count++;
02405 }
02406 }
02407 if (houses_supply) {
02408 this->PlaceIndustry(1 + supp_count * num_indrows / num_supp, 0, NUM_INDUSTRYTYPES);
02409 supp_count++;
02410 }
02411 if (houses_accept) {
02412 this->PlaceIndustry(1 + cust_count * num_indrows / num_cust, 2, NUM_INDUSTRYTYPES);
02413 cust_count++;
02414 }
02415
02416 this->ShortenCargoColumn(1, 1, num_indrows);
02417 const NWidgetBase *nwp = this->GetWidget<NWidgetBase>(WID_IC_PANEL);
02418 this->vscroll->SetCount(CeilDiv(WD_FRAMETEXT_TOP + WD_FRAMETEXT_BOTTOM + CargoesField::small_height + num_indrows * CargoesField::normal_height, nwp->resize_y));
02419 this->SetDirty();
02420 this->NotifySmallmap();
02421 }
02422
02430 virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
02431 {
02432 if (!gui_scope) return;
02433 if (data == NUM_INDUSTRYTYPES) {
02434 if (this->IsWidgetLowered(WID_IC_NOTIFY)) {
02435 this->RaiseWidget(WID_IC_NOTIFY);
02436 this->SetWidgetDirty(WID_IC_NOTIFY);
02437 }
02438 return;
02439 }
02440
02441 assert(data >= 0 && data < NUM_INDUSTRYTYPES);
02442 this->ComputeIndustryDisplay(data);
02443 }
02444
02445 virtual void DrawWidget(const Rect &r, int widget) const
02446 {
02447 if (widget != WID_IC_PANEL) return;
02448
02449 DrawPixelInfo tmp_dpi, *old_dpi;
02450 int width = r.right - r.left + 1;
02451 int height = r.bottom - r.top + 1 - WD_FRAMERECT_TOP - WD_FRAMERECT_BOTTOM;
02452 if (!FillDrawPixelInfo(&tmp_dpi, r.left + WD_FRAMERECT_LEFT, r.top + WD_FRAMERECT_TOP, width, height)) return;
02453 old_dpi = _cur_dpi;
02454 _cur_dpi = &tmp_dpi;
02455
02456 int left_pos = WD_FRAMERECT_LEFT;
02457 if (this->ind_cargo >= NUM_INDUSTRYTYPES) left_pos += (CargoesField::industry_width + CargoesField::CARGO_FIELD_WIDTH) / 2;
02458 int last_column = (this->ind_cargo < NUM_INDUSTRYTYPES) ? 4 : 2;
02459
02460 const NWidgetBase *nwp = this->GetWidget<NWidgetBase>(WID_IC_PANEL);
02461 int vpos = -this->vscroll->GetPosition() * nwp->resize_y;
02462 for (uint i = 0; i < this->fields.Length(); i++) {
02463 int row_height = (i == 0) ? CargoesField::small_height : CargoesField::normal_height;
02464 if (vpos + row_height >= 0) {
02465 int xpos = left_pos;
02466 int col, dir;
02467 if (_current_text_dir == TD_RTL) {
02468 col = last_column;
02469 dir = -1;
02470 } else {
02471 col = 0;
02472 dir = 1;
02473 }
02474 while (col >= 0 && col <= last_column) {
02475 this->fields[i].columns[col].Draw(xpos, vpos);
02476 xpos += (col & 1) ? CargoesField::CARGO_FIELD_WIDTH : CargoesField::industry_width;
02477 col += dir;
02478 }
02479 }
02480 vpos += row_height;
02481 if (vpos >= height) break;
02482 }
02483
02484 _cur_dpi = old_dpi;
02485 }
02486
02494 bool CalculatePositionInWidget(Point pt, Point *fieldxy, Point *xy)
02495 {
02496 const NWidgetBase *nw = this->GetWidget<NWidgetBase>(WID_IC_PANEL);
02497 pt.x -= nw->pos_x;
02498 pt.y -= nw->pos_y;
02499
02500 int vpos = WD_FRAMERECT_TOP + CargoesField::small_height - this->vscroll->GetPosition() * nw->resize_y;
02501 if (pt.y < vpos) return false;
02502
02503 int row = (pt.y - vpos) / CargoesField::normal_height;
02504 if (row + 1 >= (int)this->fields.Length()) return false;
02505 vpos = pt.y - vpos - row * CargoesField::normal_height;
02506 row++;
02507
02508 int xpos = 2 * WD_FRAMERECT_LEFT + ((this->ind_cargo < NUM_INDUSTRYTYPES) ? 0 : (CargoesField::industry_width + CargoesField::CARGO_FIELD_WIDTH) / 2);
02509 if (pt.x < xpos) return false;
02510 int column;
02511 for (column = 0; column <= 5; column++) {
02512 int width = (column & 1) ? CargoesField::CARGO_FIELD_WIDTH : CargoesField::industry_width;
02513 if (pt.x < xpos + width) break;
02514 xpos += width;
02515 }
02516 int num_columns = (this->ind_cargo < NUM_INDUSTRYTYPES) ? 4 : 2;
02517 if (column > num_columns) return false;
02518 xpos = pt.x - xpos;
02519
02520
02521 fieldxy->y = row;
02522 xy->y = vpos;
02523 if (_current_text_dir == TD_RTL) {
02524 fieldxy->x = num_columns - column;
02525 xy->x = ((column & 1) ? CargoesField::CARGO_FIELD_WIDTH : CargoesField::industry_width) - xpos;
02526 } else {
02527 fieldxy->x = column;
02528 xy->x = xpos;
02529 }
02530 return true;
02531 }
02532
02533 virtual void OnClick(Point pt, int widget, int click_count)
02534 {
02535 switch (widget) {
02536 case WID_IC_PANEL: {
02537 Point fieldxy, xy;
02538 if (!CalculatePositionInWidget(pt, &fieldxy, &xy)) return;
02539
02540 const CargoesField *fld = this->fields[fieldxy.y].columns + fieldxy.x;
02541 switch (fld->type) {
02542 case CFT_INDUSTRY:
02543 if (fld->u.industry.ind_type < NUM_INDUSTRYTYPES) this->ComputeIndustryDisplay(fld->u.industry.ind_type);
02544 break;
02545
02546 case CFT_CARGO: {
02547 CargoesField *lft = (fieldxy.x > 0) ? this->fields[fieldxy.y].columns + fieldxy.x - 1 : NULL;
02548 CargoesField *rgt = (fieldxy.x < 4) ? this->fields[fieldxy.y].columns + fieldxy.x + 1 : NULL;
02549 CargoID cid = fld->CargoClickedAt(lft, rgt, xy);
02550 if (cid != INVALID_CARGO) this->ComputeCargoDisplay(cid);
02551 break;
02552 }
02553
02554 case CFT_CARGO_LABEL: {
02555 CargoID cid = fld->CargoLabelClickedAt(xy);
02556 if (cid != INVALID_CARGO) this->ComputeCargoDisplay(cid);
02557 break;
02558 }
02559
02560 default:
02561 break;
02562 }
02563 break;
02564 }
02565
02566 case WID_IC_NOTIFY:
02567 this->ToggleWidgetLoweredState(WID_IC_NOTIFY);
02568 this->SetWidgetDirty(WID_IC_NOTIFY);
02569 if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
02570
02571 if (this->IsWidgetLowered(WID_IC_NOTIFY)) {
02572 if (FindWindowByClass(WC_SMALLMAP) == NULL) ShowSmallMap();
02573 this->NotifySmallmap();
02574 }
02575 break;
02576
02577 case WID_IC_CARGO_DROPDOWN: {
02578 DropDownList *lst = new DropDownList;
02579 const CargoSpec *cs;
02580 FOR_ALL_SORTED_STANDARD_CARGOSPECS(cs) {
02581 lst->push_back(new DropDownListStringItem(cs->name, cs->Index(), false));
02582 }
02583 if (lst->size() == 0) {
02584 delete lst;
02585 break;
02586 }
02587 int selected = (this->ind_cargo >= NUM_INDUSTRYTYPES) ? this->ind_cargo - NUM_INDUSTRYTYPES : -1;
02588 ShowDropDownList(this, lst, selected, WID_IC_CARGO_DROPDOWN, 0, true);
02589 break;
02590 }
02591
02592 case WID_IC_IND_DROPDOWN: {
02593 DropDownList *lst = new DropDownList;
02594 for (uint8 i = 0; i < NUM_INDUSTRYTYPES; i++) {
02595 IndustryType ind = _sorted_industry_types[i];
02596 const IndustrySpec *indsp = GetIndustrySpec(ind);
02597 if (!indsp->enabled) continue;
02598 lst->push_back(new DropDownListStringItem(indsp->name, ind, false));
02599 }
02600 if (lst->size() == 0) {
02601 delete lst;
02602 break;
02603 }
02604 int selected = (this->ind_cargo < NUM_INDUSTRYTYPES) ? this->ind_cargo : -1;
02605 ShowDropDownList(this, lst, selected, WID_IC_IND_DROPDOWN, 0, true);
02606 break;
02607 }
02608 }
02609 }
02610
02611 virtual void OnDropdownSelect(int widget, int index)
02612 {
02613 if (index < 0) return;
02614
02615 switch (widget) {
02616 case WID_IC_CARGO_DROPDOWN:
02617 this->ComputeCargoDisplay(index);
02618 break;
02619
02620 case WID_IC_IND_DROPDOWN:
02621 this->ComputeIndustryDisplay(index);
02622 break;
02623 }
02624 }
02625
02626 virtual void OnHover(Point pt, int widget)
02627 {
02628 if (widget != WID_IC_PANEL) return;
02629
02630 Point fieldxy, xy;
02631 if (!CalculatePositionInWidget(pt, &fieldxy, &xy)) return;
02632
02633 const CargoesField *fld = this->fields[fieldxy.y].columns + fieldxy.x;
02634 CargoID cid = INVALID_CARGO;
02635 switch (fld->type) {
02636 case CFT_CARGO: {
02637 CargoesField *lft = (fieldxy.x > 0) ? this->fields[fieldxy.y].columns + fieldxy.x - 1 : NULL;
02638 CargoesField *rgt = (fieldxy.x < 4) ? this->fields[fieldxy.y].columns + fieldxy.x + 1 : NULL;
02639 cid = fld->CargoClickedAt(lft, rgt, xy);
02640 break;
02641 }
02642
02643 case CFT_CARGO_LABEL: {
02644 cid = fld->CargoLabelClickedAt(xy);
02645 break;
02646 }
02647
02648 case CFT_INDUSTRY:
02649 if (fld->u.industry.ind_type < NUM_INDUSTRYTYPES && (this->ind_cargo >= NUM_INDUSTRYTYPES || fieldxy.x != 2)) {
02650 GuiShowTooltips(this, STR_INDUSTRY_CARGOES_INDUSTRY_TOOLTIP, 0, NULL, TCC_HOVER);
02651 }
02652 return;
02653
02654 default:
02655 break;
02656 }
02657 if (cid != INVALID_CARGO && (this->ind_cargo < NUM_INDUSTRYTYPES || cid != this->ind_cargo - NUM_INDUSTRYTYPES)) {
02658 const CargoSpec *csp = CargoSpec::Get(cid);
02659 uint64 params[5];
02660 params[0] = csp->name;
02661 GuiShowTooltips(this, STR_INDUSTRY_CARGOES_CARGO_TOOLTIP, 1, params, TCC_HOVER);
02662 }
02663 }
02664
02665 virtual void OnResize()
02666 {
02667 this->vscroll->SetCapacityFromWidget(this, WID_IC_PANEL);
02668 }
02669 };
02670
02671 const int IndustryCargoesWindow::HOR_TEXT_PADDING = 5;
02672 const int IndustryCargoesWindow::VERT_TEXT_PADDING = 5;
02673
02678 static void ShowIndustryCargoesWindow(IndustryType id)
02679 {
02680 if (id >= NUM_INDUSTRYTYPES) {
02681 for (uint8 i = 0; i < NUM_INDUSTRYTYPES; i++) {
02682 const IndustrySpec *indsp = GetIndustrySpec(_sorted_industry_types[i]);
02683 if (indsp->enabled) {
02684 id = _sorted_industry_types[i];
02685 break;
02686 }
02687 }
02688 if (id >= NUM_INDUSTRYTYPES) return;
02689 }
02690
02691 Window *w = BringWindowToFrontById(WC_INDUSTRY_CARGOES, 0);
02692 if (w != NULL) {
02693 w->InvalidateData(id);
02694 return;
02695 }
02696 new IndustryCargoesWindow(id);
02697 }
02698
02700 void ShowIndustryCargoesWindow()
02701 {
02702 ShowIndustryCargoesWindow(NUM_INDUSTRYTYPES);
02703 }