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