00001
00002
00005 #include "stdafx.h"
00006 #include "openttd.h"
00007 #include "debug.h"
00008 #include "gui.h"
00009 #include "window_gui.h"
00010 #include "textbuf_gui.h"
00011 #include "command_func.h"
00012 #include "viewport_func.h"
00013 #include "gfx_func.h"
00014 #include "industry.h"
00015 #include "town.h"
00016 #include "variables.h"
00017 #include "cheat_type.h"
00018 #include "newgrf.h"
00019 #include "newgrf_industries.h"
00020 #include "newgrf_text.h"
00021 #include "strings_func.h"
00022 #include "map_func.h"
00023 #include "company_func.h"
00024 #include "tilehighlight_func.h"
00025 #include "string_func.h"
00026 #include "sortlist_type.h"
00027 #include "widgets/dropdown_func.h"
00028 #include "company_base.h"
00029
00030 #include "table/strings.h"
00031 #include "table/sprites.h"
00032
00033 bool _ignore_restrictions;
00034
00035 enum CargoSuffixType {
00036 CST_FUND,
00037 CST_VIEW,
00038 CST_DIR,
00039 };
00040
00055 static StringID GetCargoSuffix(uint cargo, CargoSuffixType cst, Industry *ind, IndustryType ind_type, const IndustrySpec *indspec)
00056 {
00057 if (HasBit(indspec->callback_flags, CBM_IND_CARGO_SUFFIX)) {
00058 uint16 callback = GetIndustryCallback(CBID_INDUSTRY_CARGO_SUFFIX, 0, (cst << 8) | cargo, ind, ind_type, (cst != CST_FUND) ? ind->xy : INVALID_TILE);
00059 if (GB(callback, 0, 8) != 0xFF) return GetGRFStringID(indspec->grf_prop.grffile->grfid, 0xD000 + callback);
00060 }
00061 return STR_EMPTY;
00062 }
00063
00065 enum DynamicPlaceIndustriesWidgets {
00066 DPIW_CLOSEBOX = 0,
00067 DPIW_CAPTION,
00068 DPIW_MATRIX_WIDGET,
00069 DPIW_SCROLLBAR,
00070 DPIW_INFOPANEL,
00071 DPIW_FUND_WIDGET,
00072 DPIW_RESIZE_WIDGET,
00073 };
00074
00076 static const Widget _build_industry_widgets[] = {
00077 { WWT_CLOSEBOX, RESIZE_NONE, COLOUR_DARK_GREEN, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
00078 { WWT_CAPTION, RESIZE_RIGHT, COLOUR_DARK_GREEN, 11, 169, 0, 13, STR_0314_FUND_NEW_INDUSTRY, STR_018C_WINDOW_TITLE_DRAG_THIS},
00079 { WWT_MATRIX, RESIZE_RB, COLOUR_DARK_GREEN, 0, 157, 14, 118, 0x801, STR_INDUSTRY_SELECTION_HINT},
00080 { WWT_SCROLLBAR, RESIZE_LRB, COLOUR_DARK_GREEN, 158, 169, 14, 118, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST},
00081 { WWT_PANEL, RESIZE_RTB, COLOUR_DARK_GREEN, 0, 169, 119, 199, 0x0, STR_NULL},
00082 { WWT_TEXTBTN, RESIZE_RTB, COLOUR_DARK_GREEN, 0, 157, 200, 211, STR_FUND_NEW_INDUSTRY, STR_NULL},
00083 { WWT_RESIZEBOX, RESIZE_LRTB, COLOUR_DARK_GREEN, 158, 169, 200, 211, 0x0, STR_RESIZE_BUTTON},
00084 { WIDGETS_END},
00085 };
00086
00088 static const WindowDesc _build_industry_desc(
00089 WDP_AUTO, WDP_AUTO, 170, 212, 170, 212,
00090 WC_BUILD_INDUSTRY, WC_NONE,
00091 WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_RESIZABLE | WDF_CONSTRUCTION,
00092 _build_industry_widgets
00093 );
00094
00095 class BuildIndustryWindow : public Window {
00096 int selected_index;
00097 IndustryType selected_type;
00098 uint16 callback_timer;
00099 bool timer_enabled;
00100 uint16 count;
00101 IndustryType index[NUM_INDUSTRYTYPES + 1];
00102 StringID text[NUM_INDUSTRYTYPES + 1];
00103 bool enabled[NUM_INDUSTRYTYPES + 1];
00104
00105 void SetupArrays()
00106 {
00107 IndustryType ind;
00108 const IndustrySpec *indsp;
00109
00110 this->count = 0;
00111
00112 for (uint i = 0; i < lengthof(this->index); i++) {
00113 this->index[i] = INVALID_INDUSTRYTYPE;
00114 this->text[i] = STR_NULL;
00115 this->enabled[i] = false;
00116 }
00117
00118 if (_game_mode == GM_EDITOR) {
00119 this->index[this->count] = INVALID_INDUSTRYTYPE;
00120 this->count++;
00121 this->timer_enabled = false;
00122 }
00123
00124
00125
00126
00127 for (ind = 0; ind < NUM_INDUSTRYTYPES; ind++) {
00128 indsp = GetIndustrySpec(ind);
00129 if (indsp->enabled){
00130
00131
00132
00133 if (_game_mode != GM_EDITOR && indsp->IsRawIndustry() && _settings_game.construction.raw_industry_construction == 0) {
00134
00135 if (this->selected_type == ind) this->selected_index = -1;
00136 continue;
00137 }
00138 this->index[this->count] = ind;
00139 this->enabled[this->count] = (_game_mode == GM_EDITOR) || CheckIfCallBackAllowsAvailability(ind, IACT_USERCREATION);
00140
00141 if (this->selected_type == ind) this->selected_index = this->count;
00142 this->count++;
00143 }
00144 }
00145
00146
00147
00148 if (this->selected_index == -1) {
00149 this->selected_index = 0;
00150 this->selected_type = this->index[0];
00151 }
00152 }
00153
00154 public:
00155 BuildIndustryWindow() : Window(&_build_industry_desc)
00156 {
00157
00158
00159
00160 if (!_loaded_newgrf_features.has_newindustries) {
00161 this->widget[DPIW_INFOPANEL].bottom -= 44;
00162 this->widget[DPIW_FUND_WIDGET].bottom -= 44;
00163 this->widget[DPIW_FUND_WIDGET].top -= 44;
00164 this->widget[DPIW_RESIZE_WIDGET].bottom -= 44;
00165 this->widget[DPIW_RESIZE_WIDGET].top -= 44;
00166 this->resize.height = this->height -= 44;
00167 }
00168
00169 this->timer_enabled = _loaded_newgrf_features.has_newindustries;
00170
00171 this->vscroll.cap = 8;
00172 this->resize.step_height = 13;
00173
00174 this->selected_index = -1;
00175 this->selected_type = INVALID_INDUSTRYTYPE;
00176
00177
00178 this->SetupArrays();
00179
00180 this->callback_timer = DAY_TICKS;
00181
00182 this->FindWindowPlacementAndResize(&_build_industry_desc);
00183 }
00184
00185 virtual void OnPaint()
00186 {
00187 const IndustrySpec *indsp = (this->selected_type == INVALID_INDUSTRYTYPE) ? NULL : GetIndustrySpec(this->selected_type);
00188 int x_str = this->widget[DPIW_INFOPANEL].left + 3;
00189 int y_str = this->widget[DPIW_INFOPANEL].top + 3;
00190 const Widget *wi = &this->widget[DPIW_INFOPANEL];
00191 int max_width = wi->right - wi->left - 4;
00192
00193
00194
00195 if (_game_mode == GM_EDITOR) {
00196
00197 if (indsp == NULL) this->enabled[this->selected_index] = _settings_game.difficulty.number_industries != 0;
00198 this->widget[DPIW_FUND_WIDGET].data = STR_BUILD_NEW_INDUSTRY;
00199 } else {
00200 this->widget[DPIW_FUND_WIDGET].data = (_settings_game.construction.raw_industry_construction == 2 && indsp->IsRawIndustry()) ? STR_PROSPECT_NEW_INDUSTRY : STR_FUND_NEW_INDUSTRY;
00201 }
00202 this->SetWidgetDisabledState(DPIW_FUND_WIDGET, !this->enabled[this->selected_index]);
00203
00204 SetVScrollCount(this, this->count);
00205
00206 this->DrawWidgets();
00207
00208
00209 for (byte i = 0; i < this->vscroll.cap && ((i + this->vscroll.pos) < this->count); i++) {
00210 int offset = i * 13;
00211 int x = 3;
00212 int y = 16;
00213 bool selected = this->selected_index == i + this->vscroll.pos;
00214
00215 if (this->index[i + this->vscroll.pos] == INVALID_INDUSTRYTYPE) {
00216 DrawStringTruncated(20, y + offset, STR_MANY_RANDOM_INDUSTRIES, selected ? TC_WHITE : TC_ORANGE, max_width - 25);
00217 continue;
00218 }
00219 const IndustrySpec *indsp = GetIndustrySpec(this->index[i + this->vscroll.pos]);
00220
00221
00222 DrawStringTruncated(20, y + offset, indsp->name, selected ? TC_WHITE : TC_ORANGE, max_width - 25);
00223 GfxFillRect(x, y + 1 + offset, x + 10, y + 7 + offset, selected ? 15 : 0);
00224 GfxFillRect(x + 1, y + 2 + offset, x + 9, y + 6 + offset, indsp->map_colour);
00225 }
00226
00227 if (this->selected_type == INVALID_INDUSTRYTYPE) {
00228 DrawStringMultiLine(x_str, y_str, STR_RANDOM_INDUSTRIES_TIP, max_width, wi->bottom - wi->top - 40);
00229 return;
00230 }
00231
00232 if (_game_mode != GM_EDITOR) {
00233 SetDParam(0, indsp->GetConstructionCost());
00234 DrawStringTruncated(x_str, y_str, STR_482F_COST, TC_FROMSTRING, max_width);
00235 y_str += 11;
00236 }
00237
00238
00239 StringID str = STR_4827_REQUIRES;
00240 byte p = 0;
00241 SetDParam(0, STR_00D0_NOTHING);
00242 SetDParam(1, STR_EMPTY);
00243 for (byte j = 0; j < lengthof(indsp->accepts_cargo); j++) {
00244 if (indsp->accepts_cargo[j] == CT_INVALID) continue;
00245 if (p > 0) str++;
00246 SetDParam(p++, GetCargo(indsp->accepts_cargo[j])->name);
00247 SetDParam(p++, GetCargoSuffix(j, CST_FUND, NULL, this->selected_type, indsp));
00248 }
00249 DrawStringTruncated(x_str, y_str, str, TC_FROMSTRING, max_width);
00250 y_str += 11;
00251
00252
00253 str = STR_4827_PRODUCES;
00254 p = 0;
00255 SetDParam(0, STR_00D0_NOTHING);
00256 SetDParam(1, STR_EMPTY);
00257 for (byte j = 0; j < lengthof(indsp->produced_cargo); j++) {
00258 if (indsp->produced_cargo[j] == CT_INVALID) continue;
00259 if (p > 0) str++;
00260 SetDParam(p++, GetCargo(indsp->produced_cargo[j])->name);
00261 SetDParam(p++, GetCargoSuffix(j + 3, CST_FUND, NULL, this->selected_type, indsp));
00262 }
00263 DrawStringTruncated(x_str, y_str, str, TC_FROMSTRING, max_width);
00264 y_str += 11;
00265
00266
00267 if (this->text[this->selected_index] == STR_NULL) {
00268 if (HasBit(indsp->callback_flags, CBM_IND_FUND_MORE_TEXT)) {
00269 uint16 callback_res = GetIndustryCallback(CBID_INDUSTRY_FUND_MORE_TEXT, 0, 0, NULL, this->selected_type, INVALID_TILE);
00270 if (callback_res != CALLBACK_FAILED) {
00271 StringID newtxt = GetGRFStringID(indsp->grf_prop.grffile->grfid, 0xD000 + callback_res);
00272 this->text[this->selected_index] = newtxt;
00273 }
00274 }
00275 }
00276
00277
00278
00279 str = this->text[this->selected_index];
00280 if (str != STR_NULL && str != STR_UNDEFINED) {
00281 SetDParam(0, str);
00282 DrawStringMultiLine(x_str, y_str, STR_JUST_STRING, max_width, wi->bottom - wi->top - 40);
00283 }
00284 }
00285
00286 virtual void OnDoubleClick(Point pt, int widget)
00287 {
00288 if (widget != DPIW_MATRIX_WIDGET) return;
00289 this->OnClick(pt, DPIW_FUND_WIDGET);
00290 }
00291
00292 virtual void OnClick(Point pt, int widget)
00293 {
00294 switch (widget) {
00295 case DPIW_MATRIX_WIDGET: {
00296 const IndustrySpec *indsp;
00297 int y = (pt.y - this->widget[DPIW_MATRIX_WIDGET].top) / 13 + this->vscroll.pos ;
00298
00299 if (y >= 0 && y < count) {
00300 this->selected_index = y;
00301 this->selected_type = this->index[y];
00302 indsp = (this->selected_type == INVALID_INDUSTRYTYPE) ? NULL : GetIndustrySpec(this->selected_type);
00303
00304 this->SetDirty();
00305
00306 if ((_game_mode != GM_EDITOR && _settings_game.construction.raw_industry_construction == 2 && indsp != NULL && indsp->IsRawIndustry()) ||
00307 this->selected_type == INVALID_INDUSTRYTYPE) {
00308
00309 this->RaiseButtons();
00310 ResetObjectToPlace();
00311 }
00312 }
00313 } break;
00314
00315 case DPIW_FUND_WIDGET: {
00316 if (this->selected_type == INVALID_INDUSTRYTYPE) {
00317 this->HandleButtonClick(DPIW_FUND_WIDGET);
00318
00319 if (GetNumTowns() == 0) {
00320 ShowErrorMessage(STR_0286_MUST_BUILD_TOWN_FIRST, STR_CAN_T_GENERATE_INDUSTRIES, 0, 0);
00321 } else {
00322 extern void GenerateIndustries();
00323 _generating_world = true;
00324 GenerateIndustries();
00325 _generating_world = false;
00326 }
00327 } else if (_game_mode != GM_EDITOR && _settings_game.construction.raw_industry_construction == 2 && GetIndustrySpec(this->selected_type)->IsRawIndustry()) {
00328 DoCommandP(0, this->selected_type, InteractiveRandom(), CMD_BUILD_INDUSTRY | CMD_MSG(STR_4830_CAN_T_CONSTRUCT_THIS_INDUSTRY));
00329 this->HandleButtonClick(DPIW_FUND_WIDGET);
00330 } else {
00331 HandlePlacePushButton(this, DPIW_FUND_WIDGET, SPR_CURSOR_INDUSTRY, VHM_RECT, NULL);
00332 }
00333 } break;
00334 }
00335 }
00336
00337 virtual void OnResize(Point new_size, Point delta)
00338 {
00339
00340 this->vscroll.cap += delta.y / (int)this->resize.step_height;
00341 this->widget[DPIW_MATRIX_WIDGET].data = (this->vscroll.cap << 8) + 1;
00342 }
00343
00344 virtual void OnPlaceObject(Point pt, TileIndex tile)
00345 {
00346 bool success = true;
00347
00348 const IndustrySpec *indsp = GetIndustrySpec(this->selected_type);
00349 uint32 seed = InteractiveRandom();
00350
00351 if (_game_mode == GM_EDITOR) {
00352
00353 if (GetNumTowns() == 0) {
00354 SetDParam(0, indsp->name);
00355 ShowErrorMessage(STR_0286_MUST_BUILD_TOWN_FIRST, STR_0285_CAN_T_BUILD_HERE, pt.x, pt.y);
00356 return;
00357 }
00358
00359 _current_company = OWNER_NONE;
00360 _generating_world = true;
00361 _ignore_restrictions = true;
00362 success = DoCommandP(tile, (InteractiveRandomRange(indsp->num_table) << 16) | this->selected_type, seed, CMD_BUILD_INDUSTRY | CMD_MSG(STR_4830_CAN_T_CONSTRUCT_THIS_INDUSTRY));
00363 if (!success) {
00364 SetDParam(0, indsp->name);
00365 ShowErrorMessage(_error_message, STR_0285_CAN_T_BUILD_HERE, pt.x, pt.y);
00366 }
00367
00368 _ignore_restrictions = false;
00369 _generating_world = false;
00370 } else {
00371 success = DoCommandP(tile, (InteractiveRandomRange(indsp->num_table) << 16) | this->selected_type, seed, CMD_BUILD_INDUSTRY | CMD_MSG(STR_4830_CAN_T_CONSTRUCT_THIS_INDUSTRY));
00372 }
00373
00374
00375 if (success && !_settings_client.gui.persistent_buildingtools) ResetObjectToPlace();
00376 }
00377
00378 virtual void OnTick()
00379 {
00380 if (_pause_game != 0) return;
00381 if (!this->timer_enabled) return;
00382 if (--this->callback_timer == 0) {
00383
00384
00385 this->callback_timer = DAY_TICKS;
00386
00387 const IndustrySpec *indsp = GetIndustrySpec(this->selected_type);
00388
00389 if (indsp->enabled) {
00390 bool call_back_result = CheckIfCallBackAllowsAvailability(this->selected_type, IACT_USERCREATION);
00391
00392
00393 if (call_back_result != this->enabled[this->selected_index]) {
00394 this->enabled[this->selected_index] = call_back_result;
00395 this->SetDirty();
00396 }
00397 }
00398 }
00399 }
00400
00401 virtual void OnTimeout()
00402 {
00403 this->RaiseButtons();
00404 }
00405
00406 virtual void OnPlaceObjectAbort()
00407 {
00408 this->RaiseButtons();
00409 }
00410
00411 virtual void OnInvalidateData(int data = 0)
00412 {
00413 this->SetupArrays();
00414 this->SetDirty();
00415 }
00416 };
00417
00418 void ShowBuildIndustryWindow()
00419 {
00420 if (_game_mode != GM_EDITOR && !IsValidCompanyID(_local_company)) return;
00421 if (BringWindowToFrontById(WC_BUILD_INDUSTRY, 0)) return;
00422 new BuildIndustryWindow();
00423 }
00424
00425 static void UpdateIndustryProduction(Industry *i);
00426
00427 static inline bool IsProductionMinimum(const Industry *i, int pt)
00428 {
00429 return i->production_rate[pt] == 0;
00430 }
00431
00432 static inline bool IsProductionMaximum(const Industry *i, int pt)
00433 {
00434 return i->production_rate[pt] >= 255;
00435 }
00436
00437 static inline bool IsProductionAlterable(const Industry *i)
00438 {
00439 return ((_game_mode == GM_EDITOR || _cheats.setup_prod.value) &&
00440 (i->accepts_cargo[0] == CT_INVALID || i->accepts_cargo[0] == CT_VALUABLES));
00441 }
00442
00444 enum IndustryViewWidgets {
00445 IVW_CLOSEBOX = 0,
00446 IVW_CAPTION,
00447 IVW_STICKY,
00448 IVW_BACKGROUND,
00449 IVW_VIEWPORT,
00450 IVW_INFO,
00451 IVW_GOTO,
00452 IVW_SPACER,
00453 IVW_RESIZE,
00454 };
00455
00456 class IndustryViewWindow : public Window
00457 {
00458 byte editbox_line;
00459 byte clicked_line;
00460 byte clicked_button;
00461 byte production_offset_y;
00462
00463 public:
00464 IndustryViewWindow(const WindowDesc *desc, WindowNumber window_number) : Window(desc, window_number)
00465 {
00466 this->flags4 |= WF_DISABLE_VP_SCROLL;
00467 this->editbox_line = 0;
00468 this->clicked_line = 0;
00469 this->clicked_button = 0;
00470 InitializeWindowViewport(this, 3, 17, 254, 86, GetIndustry(window_number)->xy + TileDiffXY(1, 1), ZOOM_LVL_INDUSTRY);
00471 this->FindWindowPlacementAndResize(desc);
00472 }
00473
00474 virtual void OnPaint()
00475 {
00476 Industry *i = GetIndustry(this->window_number);
00477 const IndustrySpec *ind = GetIndustrySpec(i->type);
00478 int y = this->widget[IVW_INFO].top + 1;
00479 bool first = true;
00480 bool has_accept = false;
00481
00482 SetDParam(0, this->window_number);
00483 this->DrawWidgets();
00484
00485 if (HasBit(ind->callback_flags, CBM_IND_PRODUCTION_CARGO_ARRIVAL) || HasBit(ind->callback_flags, CBM_IND_PRODUCTION_256_TICKS)) {
00486 for (byte j = 0; j < lengthof(i->accepts_cargo); j++) {
00487 if (i->accepts_cargo[j] == CT_INVALID) continue;
00488 has_accept = true;
00489 if (first) {
00490 DrawStringTruncated(2, y, STR_INDUSTRY_WINDOW_WAITING_FOR_PROCESSING, TC_FROMSTRING, this->widget[IVW_INFO].right - 2);
00491 y += 10;
00492 first = false;
00493 }
00494 SetDParam(0, i->accepts_cargo[j]);
00495 SetDParam(1, i->incoming_cargo_waiting[j]);
00496 SetDParam(2, GetCargoSuffix(j, CST_VIEW, i, i->type, ind));
00497 DrawStringTruncated(4, y, STR_INDUSTRY_WINDOW_WAITING_STOCKPILE_CARGO, TC_FROMSTRING, this->widget[IVW_INFO].right - 4);
00498 y += 10;
00499 }
00500 } else {
00501 StringID str = STR_4827_REQUIRES;
00502 byte p = 0;
00503 for (byte j = 0; j < lengthof(i->accepts_cargo); j++) {
00504 if (i->accepts_cargo[j] == CT_INVALID) continue;
00505 has_accept = true;
00506 if (p > 0) str++;
00507 SetDParam(p++, GetCargo(i->accepts_cargo[j])->name);
00508 SetDParam(p++, GetCargoSuffix(j, CST_VIEW, i, i->type, ind));
00509 }
00510 if (has_accept) {
00511 DrawStringTruncated(2, y, str, TC_FROMSTRING, this->widget[IVW_INFO].right - 2);
00512 y += 10;
00513 }
00514 }
00515
00516 first = true;
00517 for (byte j = 0; j < lengthof(i->produced_cargo); j++) {
00518 if (i->produced_cargo[j] == CT_INVALID) continue;
00519 if (first) {
00520 if (has_accept) y += 10;
00521 DrawStringTruncated(2, y, STR_482A_PRODUCTION_LAST_MONTH, TC_FROMSTRING, this->widget[IVW_INFO].right - 2);
00522 y += 10;
00523 this->production_offset_y = y;
00524 first = false;
00525 }
00526
00527 SetDParam(0, i->produced_cargo[j]);
00528 SetDParam(1, i->last_month_production[j]);
00529 SetDParam(2, GetCargoSuffix(j + 3, CST_VIEW, i, i->type, ind));
00530
00531 SetDParam(3, i->last_month_pct_transported[j] * 100 >> 8);
00532 uint x = 4 + (IsProductionAlterable(i) ? 30 : 0);
00533 DrawStringTruncated(x, y, STR_482B_TRANSPORTED, TC_FROMSTRING, this->widget[IVW_INFO].right - x);
00534
00535 if (IsProductionAlterable(i)) {
00536 DrawArrowButtons(5, y, COLOUR_YELLOW, (this->clicked_line == j + 1) ? this->clicked_button : 0,
00537 !IsProductionMinimum(i, j), !IsProductionMaximum(i, j));
00538 }
00539 y += 10;
00540 }
00541
00542
00543 if (HasBit(ind->callback_flags, CBM_IND_WINDOW_MORE_TEXT)) {
00544 uint16 callback_res = GetIndustryCallback(CBID_INDUSTRY_WINDOW_MORE_TEXT, 0, 0, i, i->type, i->xy);
00545 if (callback_res != CALLBACK_FAILED) {
00546 StringID message = GetGRFStringID(ind->grf_prop.grffile->grfid, 0xD000 + callback_res);
00547 if (message != STR_NULL && message != STR_UNDEFINED) {
00548 const Widget *wi = &this->widget[IVW_INFO];
00549 y += 10;
00550
00551 PrepareTextRefStackUsage(6);
00552
00553 y += DrawStringMultiLine(2, y, message, wi->right - wi->left - 4, -1);
00554 StopTextRefStackUsage();
00555 }
00556 }
00557 }
00558
00559 if (y > this->widget[IVW_INFO].bottom) {
00560 this->SetDirty();
00561 ResizeWindowForWidget(this, IVW_INFO, 0, y - this->widget[IVW_INFO].top);
00562 this->SetDirty();
00563 return;
00564 }
00565
00566 this->DrawViewport();
00567 }
00568
00569 virtual void OnClick(Point pt, int widget)
00570 {
00571 Industry *i;
00572
00573 switch (widget) {
00574 case IVW_INFO: {
00575 int line, x;
00576
00577 i = GetIndustry(this->window_number);
00578
00579
00580 if (!IsProductionAlterable(i)) return;
00581 x = pt.x;
00582 line = (pt.y - this->production_offset_y) / 10;
00583 if (pt.y >= this->production_offset_y && IsInsideMM(line, 0, 2) && i->produced_cargo[line] != CT_INVALID) {
00584 if (IsInsideMM(x, 5, 25) ) {
00585
00586 if (x < 15) {
00587 if (IsProductionMinimum(i, line)) return;
00588 i->production_rate[line] = max(i->production_rate[line] / 2, 0);
00589 } else {
00590
00591 int new_prod = i->production_rate[line] == 0 ? 1 : i->production_rate[line] * 2;
00592 if (IsProductionMaximum(i, line)) return;
00593 i->production_rate[line] = minu(new_prod, 255);
00594 }
00595
00596 UpdateIndustryProduction(i);
00597 this->SetDirty();
00598 this->flags4 |= WF_TIMEOUT_BEGIN;
00599 this->clicked_line = line + 1;
00600 this->clicked_button = (x < 15 ? 1 : 2);
00601 } else if (IsInsideMM(x, 34, 160)) {
00602
00603 this->editbox_line = line;
00604 SetDParam(0, i->production_rate[line] * 8);
00605 ShowQueryString(STR_CONFIG_SETTING_INT32, STR_CONFIG_GAME_PRODUCTION, 10, 100, this, CS_ALPHANUMERAL, QSF_NONE);
00606 }
00607 }
00608 } break;
00609
00610 case IVW_GOTO:
00611 i = GetIndustry(this->window_number);
00612 if (_ctrl_pressed) {
00613 ShowExtraViewPortWindow(i->xy + TileDiffXY(1, 1));
00614 } else {
00615 ScrollMainWindowToTile(i->xy + TileDiffXY(1, 1));
00616 }
00617 break;
00618 }
00619 }
00620
00621 virtual void OnTimeout()
00622 {
00623 this->clicked_line = 0;
00624 this->clicked_button = 0;
00625 this->SetDirty();
00626 }
00627
00628 virtual void OnResize(Point new_size, Point delta)
00629 {
00630 this->viewport->width += delta.x;
00631 this->viewport->height += delta.y;
00632 this->viewport->virtual_width += delta.x;
00633 this->viewport->virtual_height += delta.y;
00634 this->viewport->dest_scrollpos_x -= delta.x;
00635 this->viewport->dest_scrollpos_y -= delta.y;
00636 UpdateViewportPosition(this);
00637 }
00638
00639 virtual void OnQueryTextFinished(char *str)
00640 {
00641 if (StrEmpty(str)) return;
00642
00643 Industry *i = GetIndustry(this->window_number);
00644 int line = this->editbox_line;
00645
00646 i->production_rate[line] = ClampU(atoi(str), 0, 255);
00647 UpdateIndustryProduction(i);
00648 this->SetDirty();
00649 }
00650 };
00651
00652 static void UpdateIndustryProduction(Industry *i)
00653 {
00654 for (byte j = 0; j < lengthof(i->produced_cargo); j++) {
00655 if (i->produced_cargo[j] != CT_INVALID) {
00656 i->last_month_production[j] = 8 * i->production_rate[j];
00657 }
00658 }
00659 }
00660
00662 static const Widget _industry_view_widgets[] = {
00663 { WWT_CLOSEBOX, RESIZE_NONE, COLOUR_CREAM, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
00664 { WWT_CAPTION, RESIZE_RIGHT, COLOUR_CREAM, 11, 247, 0, 13, STR_4801, STR_018C_WINDOW_TITLE_DRAG_THIS},
00665 { WWT_STICKYBOX, RESIZE_LR, COLOUR_CREAM, 248, 259, 0, 13, 0x0, STR_STICKY_BUTTON},
00666 { WWT_PANEL, RESIZE_RB, COLOUR_CREAM, 0, 259, 14, 105, 0x0, STR_NULL},
00667 { WWT_INSET, RESIZE_RB, COLOUR_CREAM, 2, 257, 16, 103, 0x0, STR_NULL},
00668 { WWT_PANEL, RESIZE_RTB, COLOUR_CREAM, 0, 259, 106, 107, 0x0, STR_NULL},
00669 { WWT_PUSHTXTBTN, RESIZE_TB, COLOUR_CREAM, 0, 129, 108, 119, STR_00E4_LOCATION, STR_482C_CENTER_THE_MAIN_VIEW_ON},
00670 { WWT_PANEL, RESIZE_RTB, COLOUR_CREAM, 130, 247, 108, 119, 0x0, STR_NULL},
00671 { WWT_RESIZEBOX, RESIZE_LRTB, COLOUR_CREAM, 248, 259, 108, 119, 0x0, STR_RESIZE_BUTTON},
00672 { WIDGETS_END},
00673 };
00674
00676 static const WindowDesc _industry_view_desc(
00677 WDP_AUTO, WDP_AUTO, 260, 120, 260, 120,
00678 WC_INDUSTRY_VIEW, WC_NONE,
00679 WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
00680 _industry_view_widgets
00681 );
00682
00683 void ShowIndustryViewWindow(int industry)
00684 {
00685 AllocateWindowDescFront<IndustryViewWindow>(&_industry_view_desc, industry);
00686 }
00687
00689 enum IndustryDirectoryWidgets {
00690 IDW_CLOSEBOX = 0,
00691 IDW_CAPTION,
00692 IDW_STICKY,
00693 IDW_DROPDOWN_ORDER,
00694 IDW_DROPDOWN_CRITERIA,
00695 IDW_SPACER,
00696 IDW_INDUSTRY_LIST,
00697 IDW_SCROLLBAR,
00698 IDW_RESIZE,
00699 };
00700
00702 static const Widget _industry_directory_widgets[] = {
00703 { WWT_CLOSEBOX, RESIZE_NONE, COLOUR_BROWN, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
00704 { WWT_CAPTION, RESIZE_RIGHT, COLOUR_BROWN, 11, 415, 0, 13, STR_INDUSTRYDIR_CAPTION, STR_018C_WINDOW_TITLE_DRAG_THIS},
00705 { WWT_STICKYBOX, RESIZE_LR, COLOUR_BROWN, 416, 427, 0, 13, 0x0, STR_STICKY_BUTTON},
00706
00707 { WWT_TEXTBTN, RESIZE_NONE, COLOUR_BROWN, 0, 80, 14, 25, STR_SORT_BY, STR_SORT_ORDER_TIP},
00708 { WWT_DROPDOWN, RESIZE_NONE, COLOUR_BROWN, 81, 243, 14, 25, 0x0, STR_SORT_CRITERIA_TIP},
00709 { WWT_PANEL, RESIZE_RIGHT, COLOUR_BROWN, 244, 415, 14, 25, 0x0, STR_NULL},
00710
00711 { WWT_PANEL, RESIZE_RB, COLOUR_BROWN, 0, 415, 26, 189, 0x0, STR_INDUSTRYDIR_LIST_CAPTION},
00712 { WWT_SCROLLBAR, RESIZE_LRB, COLOUR_BROWN, 416, 427, 14, 177, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST},
00713 { WWT_RESIZEBOX, RESIZE_LRTB, COLOUR_BROWN, 416, 427, 178, 189, 0x0, STR_RESIZE_BUTTON},
00714 { WIDGETS_END},
00715 };
00716
00717 typedef GUIList<const Industry*> GUIIndustryList;
00718
00719
00723 class IndustryDirectoryWindow : public Window {
00724 protected:
00725
00726 static Listing last_sorting;
00727 static const Industry *last_industry;
00728
00729
00730 static const StringID sorter_names[];
00731 static GUIIndustryList::SortFunction * const sorter_funcs[];
00732
00733 GUIIndustryList industries;
00734
00736 void BuildIndustriesList()
00737 {
00738 if (!this->industries.NeedRebuild()) return;
00739
00740 this->industries.Clear();
00741
00742 DEBUG(misc, 3, "Building industry list");
00743
00744 const Industry *i;
00745 FOR_ALL_INDUSTRIES(i) {
00746 *this->industries.Append() = i;
00747 }
00748
00749 this->industries.Compact();
00750 this->industries.RebuildDone();
00751 }
00752
00760 static inline int GetCargoTransportedPercentsIfValid(const Industry *i, uint id)
00761 {
00762 assert(id < lengthof(i->produced_cargo));
00763
00764 if (i->produced_cargo[id] == CT_INVALID) return 101;
00765 return i->last_month_pct_transported[id] * 100 >> 8;
00766 }
00767
00775 static int GetCargoTransportedSortValue(const Industry *i)
00776 {
00777 int p1 = GetCargoTransportedPercentsIfValid(i, 0);
00778 int p2 = GetCargoTransportedPercentsIfValid(i, 1);
00779
00780 if (p1 > p2) Swap(p1, p2);
00781
00782 return (p1 << 8) + p2;
00783 }
00784
00786 static int CDECL IndustryNameSorter(const Industry * const *a, const Industry * const *b)
00787 {
00788 static char buf_cache[96];
00789 static char buf[96];
00790
00791 SetDParam(0, (*a)->town->index);
00792 GetString(buf, STR_TOWN, lastof(buf));
00793
00794 if (*b != last_industry) {
00795 last_industry = *b;
00796 SetDParam(0, (*b)->town->index);
00797 GetString(buf_cache, STR_TOWN, lastof(buf_cache));
00798 }
00799
00800 return strcmp(buf, buf_cache);
00801 }
00802
00804 static int CDECL IndustryTypeSorter(const Industry * const *a, const Industry * const *b)
00805 {
00806 int r = (*a)->type - (*b)->type;
00807 return (r == 0) ? IndustryNameSorter(a, b) : r;
00808 }
00809
00811 static int CDECL IndustryProductionSorter(const Industry * const *a, const Industry * const *b)
00812 {
00813 int r = 0;
00814
00815 if ((*a)->produced_cargo[0] == CT_INVALID) {
00816 if ((*b)->produced_cargo[0] != CT_INVALID) return -1;
00817 } else {
00818 if ((*b)->produced_cargo[0] == CT_INVALID) return 1;
00819
00820 r = ((*a)->last_month_production[0] + (*a)->last_month_production[1]) -
00821 ((*b)->last_month_production[0] + (*b)->last_month_production[1]);
00822 }
00823
00824 return (r == 0) ? IndustryNameSorter(a, b) : r;
00825 }
00826
00828 static int CDECL IndustryTransportedCargoSorter(const Industry * const *a, const Industry * const *b)
00829 {
00830 int r = GetCargoTransportedSortValue(*a) - GetCargoTransportedSortValue(*b);
00831 return (r == 0) ? IndustryNameSorter(a, b) : r;
00832 }
00833
00835 void SortIndustriesList()
00836 {
00837 if (!this->industries.Sort()) return;
00838
00839
00840 this->last_industry = NULL;
00841
00842
00843 this->InvalidateWidget(IDW_INDUSTRY_LIST);
00844 }
00845
00846 public:
00847 IndustryDirectoryWindow(const WindowDesc *desc, WindowNumber number) : Window(desc, number)
00848 {
00849 this->vscroll.cap = 16;
00850 this->resize.height = this->height - 6 * 10;
00851 this->resize.step_height = 10;
00852 this->FindWindowPlacementAndResize(desc);
00853
00854 this->industries.SetListing(this->last_sorting);
00855 this->industries.SetSortFuncs(this->sorter_funcs);
00856 this->industries.ForceRebuild();
00857 this->industries.NeedResort();
00858 this->SortIndustriesList();
00859
00860 this->widget[IDW_DROPDOWN_CRITERIA].data = this->sorter_names[this->industries.SortType()];
00861 }
00862
00863 ~IndustryDirectoryWindow()
00864 {
00865 this->last_sorting = this->industries.GetListing();
00866 }
00867
00868 virtual void OnPaint()
00869 {
00870 BuildIndustriesList();
00871 SortIndustriesList();
00872
00873 SetVScrollCount(this, this->industries.Length());
00874
00875 this->DrawWidgets();
00876 this->DrawSortButtonState(IDW_DROPDOWN_ORDER, this->industries.IsDescSortOrder() ? SBS_DOWN : SBS_UP);
00877
00878 int max = min(this->vscroll.pos + this->vscroll.cap, this->industries.Length());
00879 int y = 28;
00880
00881 for (int n = this->vscroll.pos; n < max; ++n) {
00882 const Industry *i = this->industries[n];
00883 const IndustrySpec *indsp = GetIndustrySpec(i->type);
00884 byte p = 0;
00885
00886
00887 SetDParam(p++, i->index);
00888
00889
00890 for (byte j = 0; j < lengthof(i->produced_cargo); j++) {
00891 if (i->produced_cargo[j] == CT_INVALID) continue;
00892 SetDParam(p++, i->produced_cargo[j]);
00893 SetDParam(p++, i->last_month_production[j]);
00894 SetDParam(p++, GetCargoSuffix(j + 3, CST_DIR, (Industry*)i, i->type, indsp));
00895 }
00896
00897
00898 for (byte j = 0; j < lengthof(i->produced_cargo); j++) {
00899 if (i->produced_cargo[j] == CT_INVALID) continue;
00900 SetDParam(p++, i->last_month_pct_transported[j] * 100 >> 8);
00901 }
00902
00903
00904 StringID str = STR_INDUSTRYDIR_ITEM_NOPROD;
00905 if (p != 1) str = (p == 5) ? STR_INDUSTRYDIR_ITEM : STR_INDUSTRYDIR_ITEM_TWO;
00906 DrawStringTruncated(4, y, str, TC_FROMSTRING, this->widget[IDW_INDUSTRY_LIST].right - 4);
00907
00908 y += 10;
00909 }
00910 }
00911
00912 virtual void OnClick(Point pt, int widget)
00913 {
00914 switch (widget) {
00915 case IDW_DROPDOWN_ORDER:
00916 this->industries.ToggleSortOrder();
00917 this->SetDirty();
00918 break;
00919
00920 case IDW_DROPDOWN_CRITERIA:
00921 ShowDropDownMenu(this, this->sorter_names, this->industries.SortType(), IDW_DROPDOWN_CRITERIA, 0, 0);
00922 break;
00923
00924 case IDW_INDUSTRY_LIST: {
00925 int y = (pt.y - 28) / 10;
00926 uint16 p;
00927
00928 if (!IsInsideMM(y, 0, this->vscroll.cap)) return;
00929 p = y + this->vscroll.pos;
00930 if (p < this->industries.Length()) {
00931 if (_ctrl_pressed) {
00932 ShowExtraViewPortWindow(this->industries[p]->xy);
00933 } else {
00934 ScrollMainWindowToTile(this->industries[p]->xy);
00935 }
00936 }
00937 } break;
00938 }
00939 }
00940
00941 virtual void OnDropdownSelect(int widget, int index)
00942 {
00943 if (this->industries.SortType() != index) {
00944 this->industries.SetSortType(index);
00945 this->widget[IDW_DROPDOWN_CRITERIA].data = this->sorter_names[this->industries.SortType()];
00946 this->SetDirty();
00947 }
00948 }
00949
00950 virtual void OnResize(Point new_size, Point delta)
00951 {
00952 this->vscroll.cap += delta.y / 10;
00953 }
00954
00955 virtual void OnInvalidateData(int data)
00956 {
00957 if (data == 0) {
00958 this->industries.ForceRebuild();
00959 } else {
00960 this->industries.ForceResort();
00961 }
00962 this->InvalidateWidget(IDW_INDUSTRY_LIST);
00963 }
00964 };
00965
00966 Listing IndustryDirectoryWindow::last_sorting = {false, 0};
00967 const Industry *IndustryDirectoryWindow::last_industry = NULL;
00968
00969
00970 GUIIndustryList::SortFunction * const IndustryDirectoryWindow::sorter_funcs[] = {
00971 &IndustryNameSorter,
00972 &IndustryTypeSorter,
00973 &IndustryProductionSorter,
00974 &IndustryTransportedCargoSorter
00975 };
00976
00977
00978 const StringID IndustryDirectoryWindow::sorter_names[] = {
00979 STR_SORT_BY_DROPDOWN_NAME,
00980 STR_SORT_BY_TYPE,
00981 STR_SORT_BY_PRODUCTION,
00982 STR_SORT_BY_TRANSPORTED,
00983 INVALID_STRING_ID
00984 };
00985
00986
00988 static const WindowDesc _industry_directory_desc(
00989 WDP_AUTO, WDP_AUTO, 428, 190, 428, 190,
00990 WC_INDUSTRY_DIRECTORY, WC_NONE,
00991 WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
00992 _industry_directory_widgets
00993 );
00994
00995 void ShowIndustryDirectory()
00996 {
00997 AllocateWindowDescFront<IndustryDirectoryWindow>(&_industry_directory_desc, 0);
00998 }