~ubuntu-branches/debian/squeeze/openttd/squeeze

« back to all changes in this revision

Viewing changes to src/industry_gui.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Jordi Mallach, Matthijs Kooijman, Jordi Mallach
  • Date: 2009-04-15 18:22:10 UTC
  • mfrom: (1.1.6 upstream) (2.1.3 squeeze)
  • Revision ID: james.westby@ubuntu.com-20090415182210-22ktb8kdbp2tf3bm
[ Matthijs Kooijman ]
* New upstream release.
* Remove Debian specific desktop file, upstream provides one now. 
* Add debian/watch file.

[ Jordi Mallach ]
* Bump Standards-Version to 3.8.1, with no changes required.
* Move to debhelper compat 7. Bump Build-Depends accordingly.
* Use dh_prep.
* Add "set -e" to config script.
* Remove a few extra doc files that get installed by upstream Makefile.
* Add more complete copyright information.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* $Id: industry_gui.cpp 14266 2008-09-07 21:26:26Z rubidium $ */
 
1
/* $Id: industry_gui.cpp 15726 2009-03-15 16:04:39Z smatz $ */
2
2
 
3
 
/** @file industry_gui.cpp */
 
3
/** @file industry_gui.cpp GUIs related to industries. */
4
4
 
5
5
#include "stdafx.h"
6
6
#include "openttd.h"
14
14
#include "industry.h"
15
15
#include "town.h"
16
16
#include "variables.h"
17
 
#include "cargotype.h"
 
17
#include "cheat_type.h"
18
18
#include "newgrf.h"
19
 
#include "newgrf_callbacks.h"
20
19
#include "newgrf_industries.h"
21
20
#include "newgrf_text.h"
22
21
#include "strings_func.h"
23
22
#include "map_func.h"
24
 
#include "player_func.h"
25
 
#include "settings_type.h"
 
23
#include "company_func.h"
 
24
#include "tilehighlight_func.h"
 
25
#include "string_func.h"
 
26
#include "sortlist_type.h"
 
27
#include "widgets/dropdown_func.h"
 
28
#include "company_base.h"
26
29
 
27
30
#include "table/strings.h"
28
31
#include "table/sprites.h"
29
32
 
30
33
bool _ignore_restrictions;
31
34
 
 
35
enum CargoSuffixType {
 
36
        CST_FUND,
 
37
        CST_VIEW,
 
38
        CST_DIR,
 
39
};
 
40
 
 
41
/**
 
42
 * Gets the string to display after the cargo name (using callback 37)
 
43
 * @param cargo the cargo for which the suffix is requested
 
44
 * - 00 - first accepted cargo type
 
45
 * - 01 - second accepted cargo type
 
46
 * - 02 - third accepted cargo type
 
47
 * - 03 - first produced cargo type
 
48
 * - 04 - second produced cargo type
 
49
 * @param cst the cargo suffix type (for which window is it requested)
 
50
 * @param ind the industry (NULL if in fund window)
 
51
 * @param ind_type the industry type
 
52
 * @param indspec the industry spec
 
53
 * @return the string to display
 
54
 */
 
55
static StringID GetCargoSuffix(uint cargo, CargoSuffixType cst, Industry *ind, IndustryType ind_type, const IndustrySpec *indspec)
 
56
{
 
57
        if (HasBit(indspec->callback_flags, CBM_IND_CARGO_SUFFIX)) {
 
58
                uint16 callback = GetIndustryCallback(CBID_INDUSTRY_CARGO_SUFFIX, 0, (cst << 8) | cargo, ind, ind_type, (cst != CST_FUND) ? ind->xy : INVALID_TILE);
 
59
                if (GB(callback, 0, 8) != 0xFF) return GetGRFStringID(indspec->grf_prop.grffile->grfid, 0xD000 + callback);
 
60
        }
 
61
        return STR_EMPTY;
 
62
}
 
63
 
32
64
/** Names of the widgets of the dynamic place industries gui */
33
65
enum DynamicPlaceIndustriesWidgets {
34
66
        DPIW_CLOSEBOX = 0,
40
72
        DPIW_RESIZE_WIDGET,
41
73
};
42
74
 
43
 
/** Attached struct to the window extended data */
44
 
struct fnd_d {
45
 
        int index;             ///< index of the element in the matrix
46
 
        IndustryType select;   ///< industry corresponding to the above index
47
 
        uint16 callback_timer; ///< timer counter for callback eventual verification
48
 
        bool timer_enabled;    ///< timer can be used
 
75
/** Widget definition of the dynamic place industries gui */
 
76
static const Widget _build_industry_widgets[] = {
 
77
{   WWT_CLOSEBOX,    RESIZE_NONE,  COLOUR_DARK_GREEN,     0,    10,     0,    13, STR_00C5,                       STR_018B_CLOSE_WINDOW},            // DPIW_CLOSEBOX
 
78
{    WWT_CAPTION,   RESIZE_RIGHT,  COLOUR_DARK_GREEN,    11,   169,     0,    13, STR_0314_FUND_NEW_INDUSTRY,     STR_018C_WINDOW_TITLE_DRAG_THIS},  // DPIW_CAPTION
 
79
{     WWT_MATRIX,      RESIZE_RB,  COLOUR_DARK_GREEN,     0,   157,    14,   118, 0x801,                          STR_INDUSTRY_SELECTION_HINT},      // DPIW_MATRIX_WIDGET
 
80
{  WWT_SCROLLBAR,     RESIZE_LRB,  COLOUR_DARK_GREEN,   158,   169,    14,   118, 0x0,                            STR_0190_SCROLL_BAR_SCROLLS_LIST}, // DPIW_SCROLLBAR
 
81
{      WWT_PANEL,     RESIZE_RTB,  COLOUR_DARK_GREEN,     0,   169,   119,   199, 0x0,                            STR_NULL},                         // DPIW_INFOPANEL
 
82
{    WWT_TEXTBTN,     RESIZE_RTB,  COLOUR_DARK_GREEN,     0,   157,   200,   211, STR_FUND_NEW_INDUSTRY,          STR_NULL},                         // DPIW_FUND_WIDGET
 
83
{  WWT_RESIZEBOX,    RESIZE_LRTB,  COLOUR_DARK_GREEN,   158,   169,   200,   211, 0x0,                            STR_RESIZE_BUTTON},                // DPIW_RESIZE_WIDGET
 
84
{   WIDGETS_END},
49
85
};
50
 
assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(fnd_d));
51
 
 
52
 
/** Helper struct holding the available industries for current situation */
53
 
static struct IndustryData {
 
86
 
 
87
/** Window definition of the dynamic place industries gui */
 
88
static const WindowDesc _build_industry_desc(
 
89
        WDP_AUTO, WDP_AUTO, 170, 212, 170, 212,
 
90
        WC_BUILD_INDUSTRY, WC_NONE,
 
91
        WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_RESIZABLE | WDF_CONSTRUCTION,
 
92
        _build_industry_widgets
 
93
);
 
94
 
 
95
class BuildIndustryWindow : public Window {
 
96
        int selected_index;                         ///< index of the element in the matrix
 
97
        IndustryType selected_type;                 ///< industry corresponding to the above index
 
98
        uint16 callback_timer;                      ///< timer counter for callback eventual verification
 
99
        bool timer_enabled;                         ///< timer can be used
54
100
        uint16 count;                               ///< How many industries are loaded
55
101
        IndustryType index[NUM_INDUSTRYTYPES + 1];  ///< Type of industry, in the order it was loaded
56
102
        StringID text[NUM_INDUSTRYTYPES + 1];       ///< Text coming from CBM_IND_FUND_MORE_TEXT (if ever)
57
103
        bool enabled[NUM_INDUSTRYTYPES + 1];        ///< availability state, coming from CBID_INDUSTRY_AVAILABLE (if ever)
58
 
} _fund_gui;
59
 
 
60
 
assert_compile(lengthof(_fund_gui.index) == lengthof(_fund_gui.text));
61
 
assert_compile(lengthof(_fund_gui.index) == lengthof(_fund_gui.enabled));
62
 
 
63
 
static void SetupFundArrays(Window *w)
64
 
{
65
 
        IndustryType ind;
66
 
        const IndustrySpec *indsp;
67
 
 
68
 
        _fund_gui.count = 0;
69
 
 
70
 
        for (uint i = 0; i < lengthof(_fund_gui.index); i++) {
71
 
                _fund_gui.index[i]   = INVALID_INDUSTRYTYPE;
72
 
                _fund_gui.text[i]    = STR_NULL;
73
 
                _fund_gui.enabled[i] = false;
74
 
        }
75
 
 
76
 
        if (_game_mode == GM_EDITOR) { // give room for the Many Random "button"
77
 
                _fund_gui.index[_fund_gui.count] = INVALID_INDUSTRYTYPE;
78
 
                _fund_gui.count++;
79
 
                WP(w, fnd_d).timer_enabled = false;
80
 
        }
81
 
        /* Fill the arrays with industries.
82
 
         * The tests performed after the enabled allow to load the industries
83
 
         * In the same way they are inserted by grf (if any)
84
 
         */
85
 
        for (ind = 0; ind < NUM_INDUSTRYTYPES; ind++) {
86
 
                indsp = GetIndustrySpec(ind);
87
 
                if (indsp->enabled){
88
 
                        /* Rule is that editor mode loads all industries.
89
 
                         * In game mode, all non raw industries are loaded too
90
 
                         * and raw ones are loaded only when setting allows it */
91
 
                        if (_game_mode != GM_EDITOR && indsp->IsRawIndustry() && _patches.raw_industry_construction == 0) {
92
 
                                /* Unselect if the industry is no longer in the list */
93
 
                                if (WP(w, fnd_d).select == ind) WP(w, fnd_d).index = -1;
 
104
 
 
105
        void SetupArrays()
 
106
        {
 
107
                IndustryType ind;
 
108
                const IndustrySpec *indsp;
 
109
 
 
110
                this->count = 0;
 
111
 
 
112
                for (uint i = 0; i < lengthof(this->index); i++) {
 
113
                        this->index[i]   = INVALID_INDUSTRYTYPE;
 
114
                        this->text[i]    = STR_NULL;
 
115
                        this->enabled[i] = false;
 
116
                }
 
117
 
 
118
                if (_game_mode == GM_EDITOR) { // give room for the Many Random "button"
 
119
                        this->index[this->count] = INVALID_INDUSTRYTYPE;
 
120
                        this->count++;
 
121
                        this->timer_enabled = false;
 
122
                }
 
123
                /* Fill the arrays with industries.
 
124
                 * The tests performed after the enabled allow to load the industries
 
125
                 * In the same way they are inserted by grf (if any)
 
126
                 */
 
127
                for (ind = 0; ind < NUM_INDUSTRYTYPES; ind++) {
 
128
                        indsp = GetIndustrySpec(ind);
 
129
                        if (indsp->enabled){
 
130
                                /* Rule is that editor mode loads all industries.
 
131
                                 * In game mode, all non raw industries are loaded too
 
132
                                 * and raw ones are loaded only when setting allows it */
 
133
                                if (_game_mode != GM_EDITOR && indsp->IsRawIndustry() && _settings_game.construction.raw_industry_construction == 0) {
 
134
                                        /* Unselect if the industry is no longer in the list */
 
135
                                        if (this->selected_type == ind) this->selected_index = -1;
 
136
                                        continue;
 
137
                                }
 
138
                                this->index[this->count] = ind;
 
139
                                this->enabled[this->count] = (_game_mode == GM_EDITOR) || CheckIfCallBackAllowsAvailability(ind, IACT_USERCREATION);
 
140
                                /* Keep the selection to the correct line */
 
141
                                if (this->selected_type == ind) this->selected_index = this->count;
 
142
                                this->count++;
 
143
                        }
 
144
                }
 
145
 
 
146
                /* first indutry type is selected if the current selection is invalid.
 
147
                 * I'll be damned if there are none available ;) */
 
148
                if (this->selected_index == -1) {
 
149
                        this->selected_index = 0;
 
150
                        this->selected_type = this->index[0];
 
151
                }
 
152
        }
 
153
 
 
154
public:
 
155
        BuildIndustryWindow() : Window(&_build_industry_desc)
 
156
        {
 
157
                /* Shorten the window to the equivalant of the additionnal purchase
 
158
                 * info coming from the callback.  SO it will only be available to its full
 
159
                 * height when newindistries are loaded */
 
160
                if (!_loaded_newgrf_features.has_newindustries) {
 
161
                        this->widget[DPIW_INFOPANEL].bottom -= 44;
 
162
                        this->widget[DPIW_FUND_WIDGET].bottom -= 44;
 
163
                        this->widget[DPIW_FUND_WIDGET].top -= 44;
 
164
                        this->widget[DPIW_RESIZE_WIDGET].bottom -= 44;
 
165
                        this->widget[DPIW_RESIZE_WIDGET].top -= 44;
 
166
                        this->resize.height = this->height -= 44;
 
167
                }
 
168
 
 
169
                this->timer_enabled = _loaded_newgrf_features.has_newindustries;
 
170
 
 
171
                this->vscroll.cap = 8; // rows in grid, same in scroller
 
172
                this->resize.step_height = 13;
 
173
 
 
174
                this->selected_index = -1;
 
175
                this->selected_type = INVALID_INDUSTRYTYPE;
 
176
 
 
177
                /* Initialize arrays */
 
178
                this->SetupArrays();
 
179
 
 
180
                this->callback_timer = DAY_TICKS;
 
181
 
 
182
                this->FindWindowPlacementAndResize(&_build_industry_desc);
 
183
        }
 
184
 
 
185
        virtual void OnPaint()
 
186
        {
 
187
                const IndustrySpec *indsp = (this->selected_type == INVALID_INDUSTRYTYPE) ? NULL : GetIndustrySpec(this->selected_type);
 
188
                int x_str = this->widget[DPIW_INFOPANEL].left + 3;
 
189
                int y_str = this->widget[DPIW_INFOPANEL].top + 3;
 
190
                const Widget *wi = &this->widget[DPIW_INFOPANEL];
 
191
                int max_width = wi->right - wi->left - 4;
 
192
 
 
193
                /* Raw industries might be prospected. Show this fact by changing the string
 
194
                 * In Editor, you just build, while ingame, or you fund or you prospect */
 
195
                if (_game_mode == GM_EDITOR) {
 
196
                        /* We've chosen many random industries but no industries have been specified */
 
197
                        if (indsp == NULL) this->enabled[this->selected_index] = _settings_game.difficulty.number_industries != 0;
 
198
                        this->widget[DPIW_FUND_WIDGET].data = STR_BUILD_NEW_INDUSTRY;
 
199
                } else {
 
200
                        this->widget[DPIW_FUND_WIDGET].data = (_settings_game.construction.raw_industry_construction == 2 && indsp->IsRawIndustry()) ? STR_PROSPECT_NEW_INDUSTRY : STR_FUND_NEW_INDUSTRY;
 
201
                }
 
202
                this->SetWidgetDisabledState(DPIW_FUND_WIDGET, !this->enabled[this->selected_index]);
 
203
 
 
204
                SetVScrollCount(this, this->count);
 
205
 
 
206
                this->DrawWidgets();
 
207
 
 
208
                /* and now with the matrix painting */
 
209
                for (byte i = 0; i < this->vscroll.cap && ((i + this->vscroll.pos) < this->count); i++) {
 
210
                        int offset = i * 13;
 
211
                        int x = 3;
 
212
                        int y = 16;
 
213
                        bool selected = this->selected_index == i + this->vscroll.pos;
 
214
 
 
215
                        if (this->index[i + this->vscroll.pos] == INVALID_INDUSTRYTYPE) {
 
216
                                DrawStringTruncated(20, y + offset, STR_MANY_RANDOM_INDUSTRIES, selected ? TC_WHITE : TC_ORANGE, max_width - 25);
94
217
                                continue;
95
218
                        }
96
 
                        _fund_gui.index[_fund_gui.count] = ind;
97
 
                        _fund_gui.enabled[_fund_gui.count] = (_game_mode == GM_EDITOR) || CheckIfCallBackAllowsAvailability(ind, IACT_USERCREATION);
98
 
                        /* Keep the selection to the correct line */
99
 
                        if (WP(w, fnd_d).select == ind) WP(w, fnd_d).index = _fund_gui.count;
100
 
                        _fund_gui.count++;
101
 
                }
102
 
        }
103
 
 
104
 
        /* first indutry type is selected if the current selection is invalid.
105
 
         * I'll be damned if there are none available ;) */
106
 
        if (WP(w, fnd_d).index == -1) {
107
 
                WP(w, fnd_d).index = 0;
108
 
                WP(w, fnd_d).select = _fund_gui.index[0];
109
 
        }
110
 
}
111
 
 
112
 
static void BuildDynamicIndustryWndProc(Window *w, WindowEvent *e)
113
 
{
114
 
        switch (e->event) {
115
 
                case WE_CREATE: {
116
 
                        /* Shorten the window to the equivalant of the additionnal purchase
117
 
                         * info coming from the callback.  SO it will only be available to tis full
118
 
                         * height when newindistries are loaded */
119
 
                        if (!_loaded_newgrf_features.has_newindustries) {
120
 
                                w->widget[DPIW_INFOPANEL].bottom -= 44;
121
 
                                w->widget[DPIW_FUND_WIDGET].bottom -= 44;
122
 
                                w->widget[DPIW_FUND_WIDGET].top -= 44;
123
 
                                w->widget[DPIW_RESIZE_WIDGET].bottom -= 44;
124
 
                                w->widget[DPIW_RESIZE_WIDGET].top -= 44;
125
 
                                w->resize.height = w->height -= 44;
126
 
                        }
127
 
 
128
 
                        WP(w, fnd_d).timer_enabled = _loaded_newgrf_features.has_newindustries;
129
 
 
130
 
                        w->vscroll.cap = 8; // rows in grid, same in scroller
131
 
                        w->resize.step_height = 13;
132
 
 
133
 
                        WP(w, fnd_d).index = -1;
134
 
                        WP(w, fnd_d).select = INVALID_INDUSTRYTYPE;
135
 
 
136
 
                        /* Initialize arrays */
137
 
                        SetupFundArrays(w);
138
 
 
139
 
                        WP(w, fnd_d).callback_timer = DAY_TICKS;
140
 
                } break;
141
 
 
142
 
                case WE_PAINT: {
143
 
                        const IndustrySpec *indsp = (WP(w, fnd_d).select == INVALID_INDUSTRYTYPE) ? NULL : GetIndustrySpec(WP(w, fnd_d).select);
144
 
                        int x_str = w->widget[DPIW_INFOPANEL].left + 3;
145
 
                        int y_str = w->widget[DPIW_INFOPANEL].top + 3;
146
 
                        const Widget *wi = &w->widget[DPIW_INFOPANEL];
147
 
                        int max_width = wi->right - wi->left - 4;
148
 
 
149
 
                        /* Raw industries might be prospected. Show this fact by changing the string
150
 
                         * In Editor, you just build, while ingame, or you fund or you prospect */
151
 
                        if (_game_mode == GM_EDITOR) {
152
 
                                /* We've chosen many random industries but no industries have been specified */
153
 
                                if (indsp == NULL) _fund_gui.enabled[WP(w, fnd_d).index] = _opt.diff.number_industries != 0;
154
 
                                w->widget[DPIW_FUND_WIDGET].data = STR_BUILD_NEW_INDUSTRY;
155
 
                        } else {
156
 
                                w->widget[DPIW_FUND_WIDGET].data = (_patches.raw_industry_construction == 2 && indsp->IsRawIndustry()) ? STR_PROSPECT_NEW_INDUSTRY : STR_FUND_NEW_INDUSTRY;
157
 
                        }
158
 
                        w->SetWidgetDisabledState(DPIW_FUND_WIDGET, !_fund_gui.enabled[WP(w, fnd_d).index]);
159
 
 
160
 
                        SetVScrollCount(w, _fund_gui.count);
161
 
 
162
 
                        DrawWindowWidgets(w);
163
 
 
164
 
                        /* and now with the matrix painting */
165
 
                        for (byte i = 0; i < w->vscroll.cap && ((i + w->vscroll.pos) < _fund_gui.count); i++) {
166
 
                                int offset = i * 13;
167
 
                                int x = 3;
168
 
                                int y = 16;
169
 
                                bool selected = WP(w, fnd_d).index == i + w->vscroll.pos;
170
 
 
171
 
                                if (_fund_gui.index[i + w->vscroll.pos] == INVALID_INDUSTRYTYPE) {
172
 
                                        DrawStringTruncated(20, y + offset, STR_MANY_RANDOM_INDUSTRIES, selected ? TC_WHITE : TC_ORANGE, max_width - 25);
173
 
                                        continue;
174
 
                                }
175
 
                                const IndustrySpec *indsp = GetIndustrySpec(_fund_gui.index[i + w->vscroll.pos]);
176
 
 
177
 
                                /* Draw the name of the industry in white is selected, otherwise, in orange */
178
 
                                DrawStringTruncated(20, y + offset, indsp->name, selected ? TC_WHITE : TC_ORANGE, max_width - 25);
179
 
                                GfxFillRect(x,     y + 1 + offset,  x + 10, y + 7 + offset, selected ? 15 : 0);
180
 
                                GfxFillRect(x + 1, y + 2 + offset,  x +  9, y + 6 + offset, indsp->map_colour);
181
 
                        }
182
 
 
183
 
                        if (WP(w, fnd_d).select == INVALID_INDUSTRYTYPE) {
184
 
                                DrawStringMultiLine(x_str, y_str, STR_RANDOM_INDUSTRIES_TIP, max_width, wi->bottom - wi->top - 40);
185
 
                                break;
186
 
                        }
187
 
 
188
 
                        if (_game_mode != GM_EDITOR) {
189
 
                                SetDParam(0, indsp->GetConstructionCost());
190
 
                                DrawStringTruncated(x_str, y_str, STR_482F_COST, TC_FROMSTRING, max_width);
191
 
                                y_str += 11;
192
 
                        }
193
 
 
194
 
                        /* Draw the accepted cargos, if any. Otherwhise, will print "Nothing" */
195
 
                        StringID str = STR_4827_REQUIRES;
196
 
                        byte p = 0;
197
 
                        SetDParam(0, STR_00D0_NOTHING);
198
 
                        for (byte j = 0; j < lengthof(indsp->accepts_cargo); j++) {
199
 
                                if (indsp->accepts_cargo[j] == CT_INVALID) continue;
200
 
                                if (p > 0) str++;
201
 
                                SetDParam(p++, GetCargo(indsp->accepts_cargo[j])->name);
202
 
                        }
203
 
                        DrawStringTruncated(x_str, y_str, str, TC_FROMSTRING, max_width);
204
 
                        y_str += 11;
205
 
 
206
 
                        /* Draw the produced cargos, if any. Otherwhise, will print "Nothing" */
207
 
                        str = STR_4827_PRODUCES;
208
 
                        p = 0;
209
 
                        SetDParam(0, STR_00D0_NOTHING);
210
 
                        for (byte j = 0; j < lengthof(indsp->produced_cargo); j++) {
211
 
                                if (indsp->produced_cargo[j] == CT_INVALID) continue;
212
 
                                if (p > 0) str++;
213
 
                                SetDParam(p++, GetCargo(indsp->produced_cargo[j])->name);
214
 
                        }
215
 
                        DrawStringTruncated(x_str, y_str, str, TC_FROMSTRING, max_width);
216
 
                        y_str += 11;
217
 
 
218
 
                        /* Get the additional purchase info text, if it has not already been */
219
 
                        if (_fund_gui.text[WP(w, fnd_d).index] == STR_NULL) {   // Have i been called already?
220
 
                                if (HasBit(indsp->callback_flags, CBM_IND_FUND_MORE_TEXT)) {          // No. Can it be called?
221
 
                                        uint16 callback_res = GetIndustryCallback(CBID_INDUSTRY_FUND_MORE_TEXT, 0, 0, NULL, WP(w, fnd_d).select, INVALID_TILE);
222
 
                                        if (callback_res != CALLBACK_FAILED) {  // Did it failed?
223
 
                                                StringID newtxt = GetGRFStringID(indsp->grf_prop.grffile->grfid, 0xD000 + callback_res);  // No. here's the new string
224
 
                                                _fund_gui.text[WP(w, fnd_d).index] = newtxt;   // Store it for further usage
225
 
                                        }
226
 
                                }
227
 
                        }
228
 
 
229
 
                        /* Draw the Additional purchase text, provided by newgrf callback, if any.
230
 
                         * Otherwhise, will print Nothing */
231
 
                        str = _fund_gui.text[WP(w, fnd_d).index];
232
 
                        if (str != STR_NULL && str != STR_UNDEFINED) {
233
 
                                SetDParam(0, str);
234
 
                                DrawStringMultiLine(x_str, y_str, STR_JUST_STRING, max_width, wi->bottom - wi->top - 40);
235
 
                        }
236
 
                } break;
237
 
 
238
 
                case WE_DOUBLE_CLICK:
239
 
                        if (e->we.click.widget != DPIW_MATRIX_WIDGET) break;
240
 
                        e->we.click.widget = DPIW_FUND_WIDGET;
241
 
                        /* Fall through */
242
 
 
243
 
                case WE_CLICK:
244
 
                        switch (e->we.click.widget) {
245
 
                                case DPIW_MATRIX_WIDGET: {
246
 
                                        const IndustrySpec *indsp;
247
 
                                        int y = (e->we.click.pt.y - w->widget[DPIW_MATRIX_WIDGET].top) / 13 + w->vscroll.pos ;
248
 
 
249
 
                                        if (y >= 0 && y < _fund_gui.count) { // Is it within the boundaries of available data?
250
 
                                                WP(w, fnd_d).index = y;
251
 
                                                WP(w, fnd_d).select = _fund_gui.index[WP(w, fnd_d).index];
252
 
                                                indsp = (WP(w, fnd_d).select == INVALID_INDUSTRYTYPE) ? NULL : GetIndustrySpec(WP(w, fnd_d).select);
253
 
 
254
 
                                                SetWindowDirty(w);
255
 
 
256
 
                                                if ((_game_mode != GM_EDITOR && _patches.raw_industry_construction == 2 && indsp != NULL && indsp->IsRawIndustry()) ||
257
 
                                                                WP(w, fnd_d).select == INVALID_INDUSTRYTYPE) {
258
 
                                                        /* Reset the button state if going to prospecting or "build many industries" */
259
 
                                                        w->RaiseButtons();
260
 
                                                        ResetObjectToPlace();
261
 
                                                }
262
 
                                        }
263
 
                                } break;
264
 
 
265
 
                                case DPIW_FUND_WIDGET: {
266
 
                                        if (WP(w, fnd_d).select == INVALID_INDUSTRYTYPE) {
267
 
                                                w->HandleButtonClick(DPIW_FUND_WIDGET);
268
 
 
269
 
                                                if (GetNumTowns() == 0) {
270
 
                                                        ShowErrorMessage(STR_0286_MUST_BUILD_TOWN_FIRST, STR_CAN_T_GENERATE_INDUSTRIES, 0, 0);
271
 
                                                } else {
272
 
                                                        extern void GenerateIndustries();
273
 
                                                        _generating_world = true;
274
 
                                                        GenerateIndustries();
275
 
                                                        _generating_world = false;
276
 
                                                }
277
 
                                        } else if (_game_mode != GM_EDITOR && _patches.raw_industry_construction == 2 && GetIndustrySpec(WP(w, fnd_d).select)->IsRawIndustry()) {
278
 
                                                DoCommandP(0, WP(w, fnd_d).select, InteractiveRandom(), NULL, CMD_BUILD_INDUSTRY | CMD_MSG(STR_4830_CAN_T_CONSTRUCT_THIS_INDUSTRY));
279
 
                                                w->HandleButtonClick(DPIW_FUND_WIDGET);
 
219
                        const IndustrySpec *indsp = GetIndustrySpec(this->index[i + this->vscroll.pos]);
 
220
 
 
221
                        /* Draw the name of the industry in white is selected, otherwise, in orange */
 
222
                        DrawStringTruncated(20, y + offset, indsp->name, selected ? TC_WHITE : TC_ORANGE, max_width - 25);
 
223
                        GfxFillRect(x,     y + 1 + offset,  x + 10, y + 7 + offset, selected ? 15 : 0);
 
224
                        GfxFillRect(x + 1, y + 2 + offset,  x +  9, y + 6 + offset, indsp->map_colour);
 
225
                }
 
226
 
 
227
                if (this->selected_type == INVALID_INDUSTRYTYPE) {
 
228
                        DrawStringMultiLine(x_str, y_str, STR_RANDOM_INDUSTRIES_TIP, max_width, wi->bottom - wi->top - 40);
 
229
                        return;
 
230
                }
 
231
 
 
232
                if (_game_mode != GM_EDITOR) {
 
233
                        SetDParam(0, indsp->GetConstructionCost());
 
234
                        DrawStringTruncated(x_str, y_str, STR_482F_COST, TC_FROMSTRING, max_width);
 
235
                        y_str += 11;
 
236
                }
 
237
 
 
238
                /* Draw the accepted cargos, if any. Otherwhise, will print "Nothing" */
 
239
                StringID str = STR_4827_REQUIRES;
 
240
                byte p = 0;
 
241
                SetDParam(0, STR_00D0_NOTHING);
 
242
                SetDParam(1, STR_EMPTY);
 
243
                for (byte j = 0; j < lengthof(indsp->accepts_cargo); j++) {
 
244
                        if (indsp->accepts_cargo[j] == CT_INVALID) continue;
 
245
                        if (p > 0) str++;
 
246
                        SetDParam(p++, GetCargo(indsp->accepts_cargo[j])->name);
 
247
                        SetDParam(p++, GetCargoSuffix(j, CST_FUND, NULL, this->selected_type, indsp));
 
248
                }
 
249
                DrawStringTruncated(x_str, y_str, str, TC_FROMSTRING, max_width);
 
250
                y_str += 11;
 
251
 
 
252
                /* Draw the produced cargos, if any. Otherwhise, will print "Nothing" */
 
253
                str = STR_4827_PRODUCES;
 
254
                p = 0;
 
255
                SetDParam(0, STR_00D0_NOTHING);
 
256
                SetDParam(1, STR_EMPTY);
 
257
                for (byte j = 0; j < lengthof(indsp->produced_cargo); j++) {
 
258
                        if (indsp->produced_cargo[j] == CT_INVALID) continue;
 
259
                        if (p > 0) str++;
 
260
                        SetDParam(p++, GetCargo(indsp->produced_cargo[j])->name);
 
261
                        SetDParam(p++, GetCargoSuffix(j + 3, CST_FUND, NULL, this->selected_type, indsp));
 
262
                }
 
263
                DrawStringTruncated(x_str, y_str, str, TC_FROMSTRING, max_width);
 
264
                y_str += 11;
 
265
 
 
266
                /* Get the additional purchase info text, if it has not already been */
 
267
                if (this->text[this->selected_index] == STR_NULL) {   // Have i been called already?
 
268
                        if (HasBit(indsp->callback_flags, CBM_IND_FUND_MORE_TEXT)) {          // No. Can it be called?
 
269
                                uint16 callback_res = GetIndustryCallback(CBID_INDUSTRY_FUND_MORE_TEXT, 0, 0, NULL, this->selected_type, INVALID_TILE);
 
270
                                if (callback_res != CALLBACK_FAILED) {  // Did it failed?
 
271
                                        StringID newtxt = GetGRFStringID(indsp->grf_prop.grffile->grfid, 0xD000 + callback_res);  // No. here's the new string
 
272
                                        this->text[this->selected_index] = newtxt;   // Store it for further usage
 
273
                                }
 
274
                        }
 
275
                }
 
276
 
 
277
                /* Draw the Additional purchase text, provided by newgrf callback, if any.
 
278
                 * Otherwhise, will print Nothing */
 
279
                str = this->text[this->selected_index];
 
280
                if (str != STR_NULL && str != STR_UNDEFINED) {
 
281
                        SetDParam(0, str);
 
282
                        DrawStringMultiLine(x_str, y_str, STR_JUST_STRING, max_width, wi->bottom - wi->top - 40);
 
283
                }
 
284
        }
 
285
 
 
286
        virtual void OnDoubleClick(Point pt, int widget)
 
287
        {
 
288
                if (widget != DPIW_MATRIX_WIDGET) return;
 
289
                this->OnClick(pt, DPIW_FUND_WIDGET);
 
290
        }
 
291
 
 
292
        virtual void OnClick(Point pt, int widget)
 
293
        {
 
294
                switch (widget) {
 
295
                        case DPIW_MATRIX_WIDGET: {
 
296
                                const IndustrySpec *indsp;
 
297
                                int y = (pt.y - this->widget[DPIW_MATRIX_WIDGET].top) / 13 + this->vscroll.pos ;
 
298
 
 
299
                                if (y >= 0 && y < count) { // Is it within the boundaries of available data?
 
300
                                        this->selected_index = y;
 
301
                                        this->selected_type = this->index[y];
 
302
                                        indsp = (this->selected_type == INVALID_INDUSTRYTYPE) ? NULL : GetIndustrySpec(this->selected_type);
 
303
 
 
304
                                        this->SetDirty();
 
305
 
 
306
                                        if ((_game_mode != GM_EDITOR && _settings_game.construction.raw_industry_construction == 2 && indsp != NULL && indsp->IsRawIndustry()) ||
 
307
                                                        this->selected_type == INVALID_INDUSTRYTYPE) {
 
308
                                                /* Reset the button state if going to prospecting or "build many industries" */
 
309
                                                this->RaiseButtons();
 
310
                                                ResetObjectToPlace();
 
311
                                        }
 
312
                                }
 
313
                        } break;
 
314
 
 
315
                        case DPIW_FUND_WIDGET: {
 
316
                                if (this->selected_type == INVALID_INDUSTRYTYPE) {
 
317
                                        this->HandleButtonClick(DPIW_FUND_WIDGET);
 
318
 
 
319
                                        if (GetNumTowns() == 0) {
 
320
                                                ShowErrorMessage(STR_0286_MUST_BUILD_TOWN_FIRST, STR_CAN_T_GENERATE_INDUSTRIES, 0, 0);
280
321
                                        } else {
281
 
                                                HandlePlacePushButton(w, DPIW_FUND_WIDGET, SPR_CURSOR_INDUSTRY, VHM_RECT, NULL);
282
 
                                        }
283
 
                                } break;
284
 
                        }
285
 
                        break;
286
 
 
287
 
                case WE_RESIZE: {
288
 
                        /* Adjust the number of items in the matrix depending of the rezise */
289
 
                        w->vscroll.cap  += e->we.sizing.diff.y / (int)w->resize.step_height;
290
 
                        w->widget[DPIW_MATRIX_WIDGET].data = (w->vscroll.cap << 8) + 1;
291
 
                } break;
292
 
 
293
 
                case WE_PLACE_OBJ: {
294
 
                        bool success = true;
295
 
                        /* We do not need to protect ourselves against "Random Many Industries" in this mode */
296
 
                        const IndustrySpec *indsp = GetIndustrySpec(WP(w, fnd_d).select);
297
 
                        uint32 seed = InteractiveRandom();
298
 
 
299
 
                        if (_game_mode == GM_EDITOR) {
300
 
                                /* Show error if no town exists at all */
301
 
                                if (GetNumTowns() == 0) {
302
 
                                        SetDParam(0, indsp->name);
303
 
                                        ShowErrorMessage(STR_0286_MUST_BUILD_TOWN_FIRST, STR_0285_CAN_T_BUILD_HERE, e->we.place.pt.x, e->we.place.pt.y);
304
 
                                        return;
305
 
                                }
306
 
 
307
 
                                _current_player = OWNER_NONE;
308
 
                                _generating_world = true;
309
 
                                _ignore_restrictions = true;
310
 
                                success = DoCommandP(e->we.place.tile, (InteractiveRandomRange(indsp->num_table) << 16) | WP(w, fnd_d).select, seed, NULL, CMD_BUILD_INDUSTRY | CMD_MSG(STR_4830_CAN_T_CONSTRUCT_THIS_INDUSTRY));
311
 
                                if (!success) {
312
 
                                        SetDParam(0, indsp->name);
313
 
                                        ShowErrorMessage(_error_message, STR_0285_CAN_T_BUILD_HERE, e->we.place.pt.x, e->we.place.pt.y);
314
 
                                }
315
 
 
316
 
                                _ignore_restrictions = false;
317
 
                                _generating_world = false;
318
 
                        } else {
319
 
                                success = DoCommandP(e->we.place.tile, (InteractiveRandomRange(indsp->num_table) << 16) | WP(w, fnd_d).select, seed, NULL, CMD_BUILD_INDUSTRY | CMD_MSG(STR_4830_CAN_T_CONSTRUCT_THIS_INDUSTRY));
320
 
                        }
321
 
 
322
 
                        /* If an industry has been built, just reset the cursor and the system */
323
 
                        if (success) ResetObjectToPlace();
324
 
                } break;
325
 
 
326
 
                case WE_TICK:
327
 
                        if (_pause_game != 0) break;
328
 
                        if (!WP(w, fnd_d).timer_enabled) break;
329
 
                        if (--WP(w, fnd_d).callback_timer == 0) {
330
 
                                /* We have just passed another day.
331
 
                                 * See if we need to update availability of currently selected industry */
332
 
                                WP(w, fnd_d).callback_timer = DAY_TICKS;  //restart counter
333
 
 
334
 
                                const IndustrySpec *indsp = GetIndustrySpec(WP(w, fnd_d).select);
335
 
 
336
 
                                if (indsp->enabled) {
337
 
                                        bool call_back_result = CheckIfCallBackAllowsAvailability(WP(w, fnd_d).select, IACT_USERCREATION);
338
 
 
339
 
                                        /* Only if result does match the previous state would it require a redraw. */
340
 
                                        if (call_back_result != _fund_gui.enabled[WP(w, fnd_d).index]) {
341
 
                                                _fund_gui.enabled[WP(w, fnd_d).index] = call_back_result;
342
 
                                                SetWindowDirty(w);
343
 
                                        }
344
 
                                }
345
 
                        }
346
 
                        break;
347
 
 
348
 
                case WE_TIMEOUT:
349
 
                case WE_ABORT_PLACE_OBJ:
350
 
                        w->RaiseButtons();
351
 
                        break;
352
 
 
353
 
                case WE_INVALIDATE_DATA:
354
 
                        SetupFundArrays(w);
355
 
                        SetWindowDirty(w);
356
 
        }
357
 
}
358
 
 
359
 
/** Widget definition of the dynamic place industries gui */
360
 
static const Widget _build_dynamic_industry_widgets[] = {
361
 
{   WWT_CLOSEBOX,    RESIZE_NONE,    7,     0,    10,     0,    13, STR_00C5,                       STR_018B_CLOSE_WINDOW},            // DPIW_CLOSEBOX
362
 
{    WWT_CAPTION,   RESIZE_RIGHT,    7,    11,   169,     0,    13, STR_0314_FUND_NEW_INDUSTRY,     STR_018C_WINDOW_TITLE_DRAG_THIS},  // DPIW_CAPTION
363
 
{     WWT_MATRIX,      RESIZE_RB,    7,     0,   157,    14,   118, 0x801,                          STR_INDUSTRY_SELECTION_HINT},      // DPIW_MATRIX_WIDGET
364
 
{  WWT_SCROLLBAR,     RESIZE_LRB,    7,   158,   169,    14,   118, 0x0,                            STR_0190_SCROLL_BAR_SCROLLS_LIST}, // DPIW_SCROLLBAR
365
 
{      WWT_PANEL,     RESIZE_RTB,    7,     0,   169,   119,   199, 0x0,                            STR_NULL},                         // DPIW_INFOPANEL
366
 
{    WWT_TEXTBTN,     RESIZE_RTB,    7,     0,   157,   200,   211, STR_FUND_NEW_INDUSTRY,          STR_NULL},                         // DPIW_FUND_WIDGET
367
 
{  WWT_RESIZEBOX,    RESIZE_LRTB,    7,   158,   169,   200,   211, 0x0,                            STR_RESIZE_BUTTON},                // DPIW_RESIZE_WIDGET
368
 
{   WIDGETS_END},
369
 
};
370
 
 
371
 
/** Window definition of the dynamic place industries gui */
372
 
static const WindowDesc _build_industry_dynamic_desc = {
373
 
        WDP_AUTO, WDP_AUTO, 170, 212, 170, 212,
374
 
        WC_BUILD_INDUSTRY, WC_NONE,
375
 
        WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_RESIZABLE,
376
 
        _build_dynamic_industry_widgets,
377
 
        BuildDynamicIndustryWndProc,
 
322
                                                extern void GenerateIndustries();
 
323
                                                _generating_world = true;
 
324
                                                GenerateIndustries();
 
325
                                                _generating_world = false;
 
326
                                        }
 
327
                                } else if (_game_mode != GM_EDITOR && _settings_game.construction.raw_industry_construction == 2 && GetIndustrySpec(this->selected_type)->IsRawIndustry()) {
 
328
                                        DoCommandP(0, this->selected_type, InteractiveRandom(), CMD_BUILD_INDUSTRY | CMD_MSG(STR_4830_CAN_T_CONSTRUCT_THIS_INDUSTRY));
 
329
                                        this->HandleButtonClick(DPIW_FUND_WIDGET);
 
330
                                } else {
 
331
                                        HandlePlacePushButton(this, DPIW_FUND_WIDGET, SPR_CURSOR_INDUSTRY, VHM_RECT, NULL);
 
332
                                }
 
333
                        } break;
 
334
                }
 
335
        }
 
336
 
 
337
        virtual void OnResize(Point new_size, Point delta)
 
338
        {
 
339
                /* Adjust the number of items in the matrix depending of the rezise */
 
340
                this->vscroll.cap  += delta.y / (int)this->resize.step_height;
 
341
                this->widget[DPIW_MATRIX_WIDGET].data = (this->vscroll.cap << 8) + 1;
 
342
        }
 
343
 
 
344
        virtual void OnPlaceObject(Point pt, TileIndex tile)
 
345
        {
 
346
                bool success = true;
 
347
                /* We do not need to protect ourselves against "Random Many Industries" in this mode */
 
348
                const IndustrySpec *indsp = GetIndustrySpec(this->selected_type);
 
349
                uint32 seed = InteractiveRandom();
 
350
 
 
351
                if (_game_mode == GM_EDITOR) {
 
352
                        /* Show error if no town exists at all */
 
353
                        if (GetNumTowns() == 0) {
 
354
                                SetDParam(0, indsp->name);
 
355
                                ShowErrorMessage(STR_0286_MUST_BUILD_TOWN_FIRST, STR_0285_CAN_T_BUILD_HERE, pt.x, pt.y);
 
356
                                return;
 
357
                        }
 
358
 
 
359
                        _current_company = OWNER_NONE;
 
360
                        _generating_world = true;
 
361
                        _ignore_restrictions = true;
 
362
                        success = DoCommandP(tile, (InteractiveRandomRange(indsp->num_table) << 16) | this->selected_type, seed, CMD_BUILD_INDUSTRY | CMD_MSG(STR_4830_CAN_T_CONSTRUCT_THIS_INDUSTRY));
 
363
                        if (!success) {
 
364
                                SetDParam(0, indsp->name);
 
365
                                ShowErrorMessage(_error_message, STR_0285_CAN_T_BUILD_HERE, pt.x, pt.y);
 
366
                        }
 
367
 
 
368
                        _ignore_restrictions = false;
 
369
                        _generating_world = false;
 
370
                } else {
 
371
                        success = DoCommandP(tile, (InteractiveRandomRange(indsp->num_table) << 16) | this->selected_type, seed, CMD_BUILD_INDUSTRY | CMD_MSG(STR_4830_CAN_T_CONSTRUCT_THIS_INDUSTRY));
 
372
                }
 
373
 
 
374
                /* If an industry has been built, just reset the cursor and the system */
 
375
                if (success && !_settings_client.gui.persistent_buildingtools) ResetObjectToPlace();
 
376
        }
 
377
 
 
378
        virtual void OnTick()
 
379
        {
 
380
                if (_pause_game != 0) return;
 
381
                if (!this->timer_enabled) return;
 
382
                if (--this->callback_timer == 0) {
 
383
                        /* We have just passed another day.
 
384
                         * See if we need to update availability of currently selected industry */
 
385
                        this->callback_timer = DAY_TICKS; // restart counter
 
386
 
 
387
                        const IndustrySpec *indsp = GetIndustrySpec(this->selected_type);
 
388
 
 
389
                        if (indsp->enabled) {
 
390
                                bool call_back_result = CheckIfCallBackAllowsAvailability(this->selected_type, IACT_USERCREATION);
 
391
 
 
392
                                /* Only if result does match the previous state would it require a redraw. */
 
393
                                if (call_back_result != this->enabled[this->selected_index]) {
 
394
                                        this->enabled[this->selected_index] = call_back_result;
 
395
                                        this->SetDirty();
 
396
                                }
 
397
                        }
 
398
                }
 
399
        }
 
400
 
 
401
        virtual void OnTimeout()
 
402
        {
 
403
                this->RaiseButtons();
 
404
        }
 
405
 
 
406
        virtual void OnPlaceObjectAbort()
 
407
        {
 
408
                this->RaiseButtons();
 
409
        }
 
410
 
 
411
        virtual void OnInvalidateData(int data = 0)
 
412
        {
 
413
                this->SetupArrays();
 
414
                this->SetDirty();
 
415
        }
378
416
};
379
417
 
380
418
void ShowBuildIndustryWindow()
381
419
{
382
 
        if (_game_mode != GM_EDITOR && !IsValidPlayer(_current_player)) return;
383
 
        AllocateWindowDescFront(&_build_industry_dynamic_desc, 0);
 
420
        if (_game_mode != GM_EDITOR && !IsValidCompanyID(_local_company)) return;
 
421
        if (BringWindowToFrontById(WC_BUILD_INDUSTRY, 0)) return;
 
422
        new BuildIndustryWindow();
384
423
}
385
424
 
386
425
static void UpdateIndustryProduction(Industry *i);
387
426
 
388
 
static inline bool isProductionMinimum(const Industry *i, int pt)
 
427
static inline bool IsProductionMinimum(const Industry *i, int pt)
389
428
{
390
429
        return i->production_rate[pt] == 0;
391
430
}
392
431
 
393
 
static inline bool isProductionMaximum(const Industry *i, int pt)
 
432
static inline bool IsProductionMaximum(const Industry *i, int pt)
394
433
{
395
434
        return i->production_rate[pt] >= 255;
396
435
}
411
450
        IVW_INFO,
412
451
        IVW_GOTO,
413
452
        IVW_SPACER,
414
 
        IVW_RESIZE_WIDGET,
 
453
        IVW_RESIZE,
415
454
};
416
455
 
417
 
/** Information to store about the industry window */
418
 
struct indview_d : public vp_d {
 
456
class IndustryViewWindow : public Window
 
457
{
419
458
        byte editbox_line;        ///< The line clicked to open the edit box
420
459
        byte clicked_line;        ///< The line of the button that has been clicked
421
460
        byte clicked_button;      ///< The button that has been clicked (to raise)
422
461
        byte production_offset_y; ///< The offset of the production texts/buttons
423
 
};
424
 
assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(indview_d));
425
 
 
426
 
 
427
 
static void IndustryViewWndProc(Window *w, WindowEvent *e)
428
 
{
429
 
        switch (e->event) {
430
 
        case WE_CREATE: {
431
 
                /* Count the number of lines that we need to resize the GUI with */
432
 
                const Industry *i = GetIndustry(w->window_number);
433
 
                const IndustrySpec *ind = GetIndustrySpec(i->type);
434
 
                int lines = -3;
435
 
                bool first = true;
436
 
                bool has_accept = false;
437
 
 
438
 
                if (HasBit(ind->callback_flags, CBM_IND_PRODUCTION_CARGO_ARRIVAL) || HasBit(ind->callback_flags, CBM_IND_PRODUCTION_256_TICKS)) {
439
 
                        for (byte j = 0; j < lengthof(i->accepts_cargo); j++) {
440
 
                                if (i->accepts_cargo[j] == CT_INVALID) continue;
441
 
                                has_accept = true;
442
 
                                if (first) {
443
 
                                        lines++;
444
 
                                        first = false;
445
 
                                }
446
 
                                lines++;
447
 
                        }
448
 
                } else {
449
 
                        for (byte j = 0; j < lengthof(i->accepts_cargo); j++) {
450
 
                                if (i->accepts_cargo[j] == CT_INVALID) continue;
451
 
                                has_accept = true;
452
 
                                lines++;
453
 
                                break;
454
 
                        }
455
 
                }
456
 
 
457
 
                first = true;
458
 
                for (byte j = 0; j < lengthof(i->produced_cargo); j++) {
459
 
                        if (i->produced_cargo[j] == CT_INVALID) continue;
460
 
                        if (first) {
461
 
                                if (has_accept) lines++;
462
 
                                lines++;
463
 
                                first = false;
464
 
                        }
465
 
                        lines++;
466
 
                }
467
 
 
468
 
                if (HasBit(ind->callback_flags, CBM_IND_WINDOW_MORE_TEXT)) {
469
 
                        lines += 2;
470
 
                } else {
471
 
                        /* Remove the resizing option from the widgets. Do it before the Hiding since it will be overwritten */
472
 
                        for (byte j = IVW_INFO; j <= IVW_RESIZE_WIDGET; j++) {
473
 
                                w->widget[j].display_flags = RESIZE_NONE;
474
 
                        }
475
 
                        /* Hide the resize button and enlarge the spacer so it will take its place */
476
 
                        w->HideWidget(IVW_RESIZE_WIDGET);
477
 
                        w->widget[IVW_SPACER].right = w->widget[IVW_RESIZE_WIDGET].right;
478
 
                }
479
 
 
480
 
                lines *= 10;
481
 
 
482
 
                /* Resize the widgets for the new size, given by the addition of cargos */
483
 
                for (byte j = IVW_INFO; j <= IVW_RESIZE_WIDGET; j++) {
484
 
                        if (j != IVW_INFO) w->widget[j].top += lines;
485
 
                        w->widget[j].bottom += lines;
486
 
                }
487
 
                w->height += lines;
488
 
                w->resize.height += lines;
489
 
        } break;
490
 
 
491
 
        case WE_PAINT: {
492
 
                Industry *i = GetIndustry(w->window_number);
493
 
                const IndustrySpec *ind = GetIndustrySpec(i->type);
494
 
                int y = 111;
495
 
                bool first = true;
496
 
                bool has_accept = false;
497
 
 
498
 
                SetDParam(0, w->window_number);
499
 
                DrawWindowWidgets(w);
500
 
 
501
 
                if (HasBit(ind->callback_flags, CBM_IND_PRODUCTION_CARGO_ARRIVAL) || HasBit(ind->callback_flags, CBM_IND_PRODUCTION_256_TICKS)) {
502
 
                        for (byte j = 0; j < lengthof(i->accepts_cargo); j++) {
503
 
                                if (i->accepts_cargo[j] == CT_INVALID) continue;
504
 
                                has_accept = true;
505
 
                                if (first) {
506
 
                                        DrawString(2, y, STR_INDUSTRY_WINDOW_WAITING_FOR_PROCESSING, TC_FROMSTRING);
 
462
 
 
463
public:
 
464
        IndustryViewWindow(const WindowDesc *desc, WindowNumber window_number) : Window(desc, window_number)
 
465
        {
 
466
                this->flags4 |= WF_DISABLE_VP_SCROLL;
 
467
                this->editbox_line = 0;
 
468
                this->clicked_line = 0;
 
469
                this->clicked_button = 0;
 
470
                InitializeWindowViewport(this, 3, 17, 254, 86, GetIndustry(window_number)->xy + TileDiffXY(1, 1), ZOOM_LVL_INDUSTRY);
 
471
                this->FindWindowPlacementAndResize(desc);
 
472
        }
 
473
 
 
474
        virtual void OnPaint()
 
475
        {
 
476
                Industry *i = GetIndustry(this->window_number);
 
477
                const IndustrySpec *ind = GetIndustrySpec(i->type);
 
478
                int y = this->widget[IVW_INFO].top + 1;
 
479
                bool first = true;
 
480
                bool has_accept = false;
 
481
 
 
482
                SetDParam(0, this->window_number);
 
483
                this->DrawWidgets();
 
484
 
 
485
                if (HasBit(ind->callback_flags, CBM_IND_PRODUCTION_CARGO_ARRIVAL) || HasBit(ind->callback_flags, CBM_IND_PRODUCTION_256_TICKS)) {
 
486
                        for (byte j = 0; j < lengthof(i->accepts_cargo); j++) {
 
487
                                if (i->accepts_cargo[j] == CT_INVALID) continue;
 
488
                                has_accept = true;
 
489
                                if (first) {
 
490
                                        DrawStringTruncated(2, y, STR_INDUSTRY_WINDOW_WAITING_FOR_PROCESSING, TC_FROMSTRING, this->widget[IVW_INFO].right - 2);
507
491
                                        y += 10;
508
492
                                        first = false;
509
493
                                }
510
494
                                SetDParam(0, i->accepts_cargo[j]);
511
495
                                SetDParam(1, i->incoming_cargo_waiting[j]);
512
 
                                DrawString(4, y, STR_INDUSTRY_WINDOW_WAITING_STOCKPILE_CARGO, TC_FROMSTRING);
 
496
                                SetDParam(2, GetCargoSuffix(j, CST_VIEW, i, i->type, ind));
 
497
                                DrawStringTruncated(4, y, STR_INDUSTRY_WINDOW_WAITING_STOCKPILE_CARGO, TC_FROMSTRING, this->widget[IVW_INFO].right - 4);
513
498
                                y += 10;
514
499
                        }
515
500
                } else {
520
505
                                has_accept = true;
521
506
                                if (p > 0) str++;
522
507
                                SetDParam(p++, GetCargo(i->accepts_cargo[j])->name);
 
508
                                SetDParam(p++, GetCargoSuffix(j, CST_VIEW, i, i->type, ind));
523
509
                        }
524
510
                        if (has_accept) {
525
 
                                DrawString(2, y, str, TC_FROMSTRING);
 
511
                                DrawStringTruncated(2, y, str, TC_FROMSTRING, this->widget[IVW_INFO].right - 2);
526
512
                                y += 10;
527
513
                        }
528
514
                }
532
518
                        if (i->produced_cargo[j] == CT_INVALID) continue;
533
519
                        if (first) {
534
520
                                if (has_accept) y += 10;
535
 
                                DrawString(2, y, STR_482A_PRODUCTION_LAST_MONTH, TC_FROMSTRING);
 
521
                                DrawStringTruncated(2, y, STR_482A_PRODUCTION_LAST_MONTH, TC_FROMSTRING, this->widget[IVW_INFO].right - 2);
536
522
                                y += 10;
537
 
                                WP(w, indview_d).production_offset_y = y;
 
523
                                this->production_offset_y = y;
538
524
                                first = false;
539
525
                        }
540
526
 
541
527
                        SetDParam(0, i->produced_cargo[j]);
542
528
                        SetDParam(1, i->last_month_production[j]);
 
529
                        SetDParam(2, GetCargoSuffix(j + 3, CST_VIEW, i, i->type, ind));
543
530
 
544
 
                        SetDParam(2, i->last_month_pct_transported[j] * 100 >> 8);
545
 
                        DrawString(4 + (IsProductionAlterable(i) ? 30 : 0), y, STR_482B_TRANSPORTED, TC_FROMSTRING);
 
531
                        SetDParam(3, i->last_month_pct_transported[j] * 100 >> 8);
 
532
                        uint x = 4 + (IsProductionAlterable(i) ? 30 : 0);
 
533
                        DrawStringTruncated(x, y, STR_482B_TRANSPORTED, TC_FROMSTRING, this->widget[IVW_INFO].right - x);
546
534
                        /* Let's put out those buttons.. */
547
535
                        if (IsProductionAlterable(i)) {
548
 
                                DrawArrowButtons(5, y, 3, (WP(w, indview_d).clicked_line == j + 1) ? WP(w, indview_d).clicked_button : 0,
549
 
                                                !isProductionMinimum(i, j), !isProductionMaximum(i, j));
 
536
                                DrawArrowButtons(5, y, COLOUR_YELLOW, (this->clicked_line == j + 1) ? this->clicked_button : 0,
 
537
                                                !IsProductionMinimum(i, j), !IsProductionMaximum(i, j));
550
538
                        }
551
539
                        y += 10;
552
540
                }
557
545
                        if (callback_res != CALLBACK_FAILED) {
558
546
                                StringID message = GetGRFStringID(ind->grf_prop.grffile->grfid, 0xD000 + callback_res);
559
547
                                if (message != STR_NULL && message != STR_UNDEFINED) {
560
 
                                        const Widget *wi = &w->widget[IVW_INFO];
 
548
                                        const Widget *wi = &this->widget[IVW_INFO];
561
549
                                        y += 10;
562
550
 
563
551
                                        PrepareTextRefStackUsage(6);
564
552
                                        /* Use all the available space left from where we stand up to the end of the window */
565
 
                                        DrawStringMultiLine(2, y, message, wi->right - wi->left - 4, wi->bottom - y);
 
553
                                        y += DrawStringMultiLine(2, y, message, wi->right - wi->left - 4, -1);
566
554
                                        StopTextRefStackUsage();
567
555
                                }
568
556
                        }
569
557
                }
570
558
 
571
 
                DrawWindowViewport(w);
572
 
        } break;
573
 
 
574
 
        case WE_CLICK: {
 
559
                if (y > this->widget[IVW_INFO].bottom) {
 
560
                        this->SetDirty();
 
561
                        ResizeWindowForWidget(this, IVW_INFO, 0, y - this->widget[IVW_INFO].top);
 
562
                        this->SetDirty();
 
563
                        return;
 
564
                }
 
565
 
 
566
                this->DrawViewport();
 
567
        }
 
568
 
 
569
        virtual void OnClick(Point pt, int widget)
 
570
        {
575
571
                Industry *i;
576
572
 
577
 
                switch (e->we.click.widget) {
578
 
                case IVW_INFO: {
579
 
                        int line, x;
580
 
 
581
 
                        i = GetIndustry(w->window_number);
582
 
 
583
 
                        /* We should work if needed.. */
584
 
                        if (!IsProductionAlterable(i)) return;
585
 
                        x = e->we.click.pt.x;
586
 
                        line = (e->we.click.pt.y - WP(w, indview_d).production_offset_y) / 10;
587
 
                        if (e->we.click.pt.y >= WP(w, indview_d).production_offset_y && IsInsideMM(line, 0, 2) && i->produced_cargo[line] != CT_INVALID) {
588
 
                                if (IsInsideMM(x, 5, 25) ) {
589
 
                                        /* Clicked buttons, decrease or increase production */
590
 
                                        if (x < 15) {
591
 
                                                if (isProductionMinimum(i, line)) return;
592
 
                                                i->production_rate[line] = max(i->production_rate[line] / 2, 0);
593
 
                                        } else {
594
 
                                                /* a zero production industry is unlikely to give anything but zero, so push it a little bit */
595
 
                                                int new_prod = i->production_rate[line] == 0 ? 1 : i->production_rate[line] * 2;
596
 
                                                if (isProductionMaximum(i, line)) return;
597
 
                                                i->production_rate[line] = minu(new_prod, 255);
 
573
                switch (widget) {
 
574
                        case IVW_INFO: {
 
575
                                int line, x;
 
576
 
 
577
                                i = GetIndustry(this->window_number);
 
578
 
 
579
                                /* We should work if needed.. */
 
580
                                if (!IsProductionAlterable(i)) return;
 
581
                                x = pt.x;
 
582
                                line = (pt.y - this->production_offset_y) / 10;
 
583
                                if (pt.y >= this->production_offset_y && IsInsideMM(line, 0, 2) && i->produced_cargo[line] != CT_INVALID) {
 
584
                                        if (IsInsideMM(x, 5, 25) ) {
 
585
                                                /* Clicked buttons, decrease or increase production */
 
586
                                                if (x < 15) {
 
587
                                                        if (IsProductionMinimum(i, line)) return;
 
588
                                                        i->production_rate[line] = max(i->production_rate[line] / 2, 0);
 
589
                                                } else {
 
590
                                                        /* a zero production industry is unlikely to give anything but zero, so push it a little bit */
 
591
                                                        int new_prod = i->production_rate[line] == 0 ? 1 : i->production_rate[line] * 2;
 
592
                                                        if (IsProductionMaximum(i, line)) return;
 
593
                                                        i->production_rate[line] = minu(new_prod, 255);
 
594
                                                }
 
595
 
 
596
                                                UpdateIndustryProduction(i);
 
597
                                                this->SetDirty();
 
598
                                                this->flags4 |= WF_TIMEOUT_BEGIN;
 
599
                                                this->clicked_line = line + 1;
 
600
                                                this->clicked_button = (x < 15 ? 1 : 2);
 
601
                                        } else if (IsInsideMM(x, 34, 160)) {
 
602
                                                /* clicked the text */
 
603
                                                this->editbox_line = line;
 
604
                                                SetDParam(0, i->production_rate[line] * 8);
 
605
                                                ShowQueryString(STR_CONFIG_SETTING_INT32, STR_CONFIG_GAME_PRODUCTION, 10, 100, this, CS_ALPHANUMERAL, QSF_NONE);
598
606
                                        }
599
 
 
600
 
                                        UpdateIndustryProduction(i);
601
 
                                        SetWindowDirty(w);
602
 
                                        w->flags4 |= 5 << WF_TIMEOUT_SHL;
603
 
                                        WP(w, indview_d).clicked_line = line + 1;
604
 
                                        WP(w, indview_d).clicked_button = (x < 15 ? 1 : 2);
605
 
                                } else if (IsInsideMM(x, 34, 160)) {
606
 
                                        /* clicked the text */
607
 
                                        WP(w, indview_d).editbox_line = line;
608
 
                                        SetDParam(0, i->production_rate[line] * 8);
609
 
                                        ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_CONFIG_GAME_PRODUCTION, 10, 100, w, CS_ALPHANUMERAL);
610
 
                                }
611
 
                        }
612
 
                } break;
613
 
                case IVW_GOTO:
614
 
                        i = GetIndustry(w->window_number);
615
 
                        ScrollMainWindowToTile(i->xy + TileDiffXY(1, 1));
616
 
                } break;
617
 
 
618
 
                }
619
 
                break;
620
 
        case WE_TIMEOUT:
621
 
                WP(w, indview_d).clicked_line = 0;
622
 
                WP(w, indview_d).clicked_button = 0;
623
 
                SetWindowDirty(w);
624
 
                break;
625
 
 
626
 
        case WE_ON_EDIT_TEXT:
627
 
                if (e->we.edittext.str[0] != '\0') {
628
 
                        Industry* i = GetIndustry(w->window_number);
629
 
                        int line = WP(w, indview_d).editbox_line;
630
 
 
631
 
                        i->production_rate[line] = ClampU(atoi(e->we.edittext.str), 0, 255);
632
 
                        UpdateIndustryProduction(i);
633
 
                        SetWindowDirty(w);
634
 
                }
635
 
        }
636
 
}
 
607
                                }
 
608
                        } break;
 
609
 
 
610
                        case IVW_GOTO:
 
611
                                i = GetIndustry(this->window_number);
 
612
                                if (_ctrl_pressed) {
 
613
                                        ShowExtraViewPortWindow(i->xy + TileDiffXY(1, 1));
 
614
                                } else {
 
615
                                        ScrollMainWindowToTile(i->xy + TileDiffXY(1, 1));
 
616
                                }
 
617
                                break;
 
618
                }
 
619
        }
 
620
 
 
621
        virtual void OnTimeout()
 
622
        {
 
623
                this->clicked_line = 0;
 
624
                this->clicked_button = 0;
 
625
                this->SetDirty();
 
626
        }
 
627
 
 
628
        virtual void OnResize(Point new_size, Point delta)
 
629
        {
 
630
                this->viewport->width            += delta.x;
 
631
                this->viewport->height           += delta.y;
 
632
                this->viewport->virtual_width    += delta.x;
 
633
                this->viewport->virtual_height   += delta.y;
 
634
                this->viewport->dest_scrollpos_x -= delta.x;
 
635
                this->viewport->dest_scrollpos_y -= delta.y;
 
636
                UpdateViewportPosition(this);
 
637
        }
 
638
 
 
639
        virtual void OnQueryTextFinished(char *str)
 
640
        {
 
641
                if (StrEmpty(str)) return;
 
642
 
 
643
                Industry *i = GetIndustry(this->window_number);
 
644
                int line = this->editbox_line;
 
645
 
 
646
                i->production_rate[line] = ClampU(atoi(str), 0, 255);
 
647
                UpdateIndustryProduction(i);
 
648
                this->SetDirty();
 
649
        }
 
650
};
637
651
 
638
652
static void UpdateIndustryProduction(Industry *i)
639
653
{
646
660
 
647
661
/** Widget definition of the view industy gui */
648
662
static const Widget _industry_view_widgets[] = {
649
 
{   WWT_CLOSEBOX,   RESIZE_NONE,     9,     0,    10,     0,    13, STR_00C5,          STR_018B_CLOSE_WINDOW},            // IVW_CLOSEBOX
650
 
{    WWT_CAPTION,   RESIZE_NONE,     9,    11,   247,     0,    13, STR_4801,          STR_018C_WINDOW_TITLE_DRAG_THIS},  // IVW_CAPTION
651
 
{  WWT_STICKYBOX,   RESIZE_NONE,     9,   248,   259,     0,    13, 0x0,               STR_STICKY_BUTTON},                // IVW_STICKY
652
 
{      WWT_PANEL,   RESIZE_NONE,     9,     0,   259,    14,   105, 0x0,               STR_NULL},                         // IVW_BACKGROUND
653
 
{      WWT_INSET,   RESIZE_NONE,     9,     2,   257,    16,   103, 0x0,               STR_NULL},                         // IVW_VIEWPORT
654
 
{      WWT_PANEL, RESIZE_BOTTOM,     9,     0,   259,   106,   147, 0x0,               STR_NULL},                         // IVW_INFO
655
 
{ WWT_PUSHTXTBTN,     RESIZE_TB,     9,     0,   129,   148,   159, STR_00E4_LOCATION, STR_482C_CENTER_THE_MAIN_VIEW_ON}, // IVW_GOTO
656
 
{      WWT_PANEL,     RESIZE_TB,     9,   130,   247,   148,   159, 0x0,               STR_NULL},                         // IVW_SPACER
657
 
{  WWT_RESIZEBOX,     RESIZE_TB,     9,   248,   259,   148,   159, 0x0,               STR_RESIZE_BUTTON},                // IVW_RESIZE_WIDGET
 
663
{   WWT_CLOSEBOX,   RESIZE_NONE,  COLOUR_CREAM,     0,    10,     0,    13, STR_00C5,          STR_018B_CLOSE_WINDOW},            // IVW_CLOSEBOX
 
664
{    WWT_CAPTION,  RESIZE_RIGHT,  COLOUR_CREAM,    11,   247,     0,    13, STR_4801,          STR_018C_WINDOW_TITLE_DRAG_THIS},  // IVW_CAPTION
 
665
{  WWT_STICKYBOX,     RESIZE_LR,  COLOUR_CREAM,   248,   259,     0,    13, 0x0,               STR_STICKY_BUTTON},                // IVW_STICKY
 
666
{      WWT_PANEL,     RESIZE_RB,  COLOUR_CREAM,     0,   259,    14,   105, 0x0,               STR_NULL},                         // IVW_BACKGROUND
 
667
{      WWT_INSET,     RESIZE_RB,  COLOUR_CREAM,     2,   257,    16,   103, 0x0,               STR_NULL},                         // IVW_VIEWPORT
 
668
{      WWT_PANEL,    RESIZE_RTB,  COLOUR_CREAM,     0,   259,   106,   107, 0x0,               STR_NULL},                         // IVW_INFO
 
669
{ WWT_PUSHTXTBTN,     RESIZE_TB,  COLOUR_CREAM,     0,   129,   108,   119, STR_00E4_LOCATION, STR_482C_CENTER_THE_MAIN_VIEW_ON}, // IVW_GOTO
 
670
{      WWT_PANEL,    RESIZE_RTB,  COLOUR_CREAM,   130,   247,   108,   119, 0x0,               STR_NULL},                         // IVW_SPACER
 
671
{  WWT_RESIZEBOX,   RESIZE_LRTB,  COLOUR_CREAM,   248,   259,   108,   119, 0x0,               STR_RESIZE_BUTTON},                // IVW_RESIZE
658
672
{   WIDGETS_END},
659
673
};
660
674
 
661
675
/** Window definition of the view industy gui */
662
 
static const WindowDesc _industry_view_desc = {
663
 
        WDP_AUTO, WDP_AUTO, 260, 160, 260, 160,
 
676
static const WindowDesc _industry_view_desc(
 
677
        WDP_AUTO, WDP_AUTO, 260, 120, 260, 120,
664
678
        WC_INDUSTRY_VIEW, WC_NONE,
665
679
        WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
666
 
        _industry_view_widgets,
667
 
        IndustryViewWndProc
668
 
};
 
680
        _industry_view_widgets
 
681
);
669
682
 
670
683
void ShowIndustryViewWindow(int industry)
671
684
{
672
 
        Window *w = AllocateWindowDescFront(&_industry_view_desc, industry);
673
 
 
674
 
        if (w != NULL) {
675
 
                w->flags4 |= WF_DISABLE_VP_SCROLL;
676
 
                WP(w, indview_d).editbox_line = 0;
677
 
                WP(w, indview_d).clicked_line = 0;
678
 
                WP(w, indview_d).clicked_button = 0;
679
 
                AssignWindowViewport(w, 3, 17, 0xFE, 0x56, GetIndustry(w->window_number)->xy + TileDiffXY(1, 1), ZOOM_LVL_INDUSTRY);
680
 
        }
 
685
        AllocateWindowDescFront<IndustryViewWindow>(&_industry_view_desc, industry);
681
686
}
682
687
 
683
688
/** Names of the widgets of the industry directory gui */
685
690
        IDW_CLOSEBOX = 0,
686
691
        IDW_CAPTION,
687
692
        IDW_STICKY,
688
 
        IDW_SORTBYNAME,
689
 
        IDW_SORTBYTYPE,
690
 
        IDW_SORTBYPROD,
691
 
        IDW_SORTBYTRANSPORT,
 
693
        IDW_DROPDOWN_ORDER,
 
694
        IDW_DROPDOWN_CRITERIA,
692
695
        IDW_SPACER,
693
 
        IDW_INDUSRTY_LIST,
 
696
        IDW_INDUSTRY_LIST,
694
697
        IDW_SCROLLBAR,
695
698
        IDW_RESIZE,
696
699
};
697
700
 
698
701
/** Widget definition of the industy directory gui */
699
702
static const Widget _industry_directory_widgets[] = {
700
 
{   WWT_CLOSEBOX,   RESIZE_NONE,    13,     0,    10,     0,    13, STR_00C5,                STR_018B_CLOSE_WINDOW},             // IDW_CLOSEBOX
701
 
{    WWT_CAPTION,   RESIZE_NONE,    13,    11,   495,     0,    13, STR_INDUSTRYDIR_CAPTION, STR_018C_WINDOW_TITLE_DRAG_THIS},   // IDW_CAPTION
702
 
{  WWT_STICKYBOX,   RESIZE_NONE,    13,   496,   507,     0,    13, 0x0,                     STR_STICKY_BUTTON},                 // IDW_STICKY
703
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    13,     0,   100,    14,    25, STR_SORT_BY_NAME,        STR_SORT_ORDER_TIP},                // IDW_SORTBYNAME
704
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    13,   101,   200,    14,    25, STR_SORT_BY_TYPE,        STR_SORT_ORDER_TIP},                // IDW_SORTBYTYPE
705
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    13,   201,   300,    14,    25, STR_SORT_BY_PRODUCTION,  STR_SORT_ORDER_TIP},                // IDW_SORTBYPROD
706
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    13,   301,   400,    14,    25, STR_SORT_BY_TRANSPORTED, STR_SORT_ORDER_TIP},                // IDW_SORTBYTRANSPORT
707
 
{      WWT_PANEL,   RESIZE_NONE,    13,   401,   495,    14,    25, 0x0,                     STR_NULL},                          // IDW_SPACER
708
 
{      WWT_PANEL, RESIZE_BOTTOM,    13,     0,   495,    26,   189, 0x0,                     STR_INDUSTRYDIR_LIST_CAPTION},      // IDW_INDUSRTY_LIST
709
 
{  WWT_SCROLLBAR, RESIZE_BOTTOM,    13,   496,   507,    14,   177, 0x0,                     STR_0190_SCROLL_BAR_SCROLLS_LIST},  // IDW_SCROLLBAR
710
 
{  WWT_RESIZEBOX,     RESIZE_TB,    13,   496,   507,   178,   189, 0x0,                     STR_RESIZE_BUTTON},                 // IDW_RESIZE
 
703
{   WWT_CLOSEBOX,   RESIZE_NONE,  COLOUR_BROWN,     0,    10,     0,    13, STR_00C5,                STR_018B_CLOSE_WINDOW},             // IDW_CLOSEBOX
 
704
{    WWT_CAPTION,  RESIZE_RIGHT,  COLOUR_BROWN,    11,   415,     0,    13, STR_INDUSTRYDIR_CAPTION, STR_018C_WINDOW_TITLE_DRAG_THIS},   // IDW_CAPTION
 
705
{  WWT_STICKYBOX,     RESIZE_LR,  COLOUR_BROWN,   416,   427,     0,    13, 0x0,                     STR_STICKY_BUTTON},                 // IDW_STICKY
 
706
 
 
707
{    WWT_TEXTBTN,   RESIZE_NONE,  COLOUR_BROWN,     0,    80,    14,    25, STR_SORT_BY,             STR_SORT_ORDER_TIP},                // IDW_DROPDOWN_ORDER
 
708
{   WWT_DROPDOWN,   RESIZE_NONE,  COLOUR_BROWN,    81,   243,    14,    25, 0x0,                     STR_SORT_CRITERIA_TIP},             // IDW_DROPDOWN_CRITERIA
 
709
{      WWT_PANEL,  RESIZE_RIGHT,  COLOUR_BROWN,   244,   415,    14,    25, 0x0,                     STR_NULL},                          // IDW_SPACER
 
710
 
 
711
{      WWT_PANEL,     RESIZE_RB,  COLOUR_BROWN,     0,   415,    26,   189, 0x0,                     STR_INDUSTRYDIR_LIST_CAPTION},      // IDW_INDUSRTY_LIST
 
712
{  WWT_SCROLLBAR,    RESIZE_LRB,  COLOUR_BROWN,   416,   427,    14,   177, 0x0,                     STR_0190_SCROLL_BAR_SCROLLS_LIST},  // IDW_SCROLLBAR
 
713
{  WWT_RESIZEBOX,   RESIZE_LRTB,  COLOUR_BROWN,   416,   427,   178,   189, 0x0,                     STR_RESIZE_BUTTON},                 // IDW_RESIZE
711
714
{   WIDGETS_END},
712
715
};
713
716
 
714
 
static uint _num_industry_sort;
715
 
 
716
 
static char _bufcache[96];
717
 
static const Industry* _last_industry;
718
 
 
719
 
static byte _industry_sort_order;
720
 
 
721
 
static int CDECL GeneralIndustrySorter(const void *a, const void *b)
722
 
{
723
 
        const Industry* i = *(const Industry**)a;
724
 
        const Industry* j = *(const Industry**)b;
725
 
        int r;
726
 
 
727
 
        switch (_industry_sort_order >> 1) {
728
 
                default: NOT_REACHED();
729
 
                case 0: /* Sort by Name (handled later) */
730
 
                        r = 0;
731
 
                        break;
732
 
 
733
 
                case 1: /* Sort by Type */
734
 
                        r = i->type - j->type;
735
 
                        break;
736
 
 
737
 
                case 2: /* Sort by Production */
738
 
                        if (i->produced_cargo[0] == CT_INVALID) {
739
 
                                r = (j->produced_cargo[0] == CT_INVALID ? 0 : -1);
740
 
                        } else {
741
 
                                if (j->produced_cargo[0] == CT_INVALID) {
742
 
                                        r = 1;
743
 
                                } else {
744
 
                                        r =
745
 
                                                (i->last_month_production[0] + i->last_month_production[1]) -
746
 
                                                (j->last_month_production[0] + j->last_month_production[1]);
747
 
                                }
748
 
                        }
749
 
                        break;
750
 
 
751
 
                case 3: /* Sort by transported fraction */
752
 
                        if (i->produced_cargo[0] == CT_INVALID) {
753
 
                                r = (j->produced_cargo[0] == CT_INVALID ? 0 : -1);
754
 
                        } else {
755
 
                                if (j->produced_cargo[0] == CT_INVALID) {
756
 
                                        r = 1;
757
 
                                } else {
758
 
                                        int pi;
759
 
                                        int pj;
760
 
 
761
 
                                        pi = i->last_month_pct_transported[0] * 100 >> 8;
762
 
                                        if (i->produced_cargo[1] != CT_INVALID) {
763
 
                                                int p = i->last_month_pct_transported[1] * 100 >> 8;
764
 
                                                if (p < pi) pi = p;
765
 
                                        }
766
 
 
767
 
                                        pj = j->last_month_pct_transported[0] * 100 >> 8;
768
 
                                        if (j->produced_cargo[1] != CT_INVALID) {
769
 
                                                int p = j->last_month_pct_transported[1] * 100 >> 8;
770
 
                                                if (p < pj) pj = p;
771
 
                                        }
772
 
 
773
 
                                        r = pi - pj;
774
 
                                }
775
 
                        }
776
 
                        break;
777
 
        }
778
 
 
779
 
        /* default to string sorting if they are otherwise equal */
780
 
        if (r == 0) {
781
 
                char buf1[96];
782
 
 
783
 
                SetDParam(0, i->town->index);
784
 
                GetString(buf1, STR_TOWN, lastof(buf1));
785
 
 
786
 
                if (j != _last_industry) {
787
 
                        _last_industry = j;
788
 
                        SetDParam(0, j->town->index);
789
 
                        GetString(_bufcache, STR_TOWN, lastof(_bufcache));
790
 
                }
791
 
                r = strcmp(buf1, _bufcache);
792
 
        }
793
 
 
794
 
        if (_industry_sort_order & 1) r = -r;
795
 
        return r;
796
 
}
 
717
typedef GUIList<const Industry*> GUIIndustryList;
 
718
 
797
719
 
798
720
/**
799
 
 * Makes a sorted industry list.
800
 
 * When there are no industries, the list has to be made. This so when one
801
 
 * starts a new game without industries after playing a game with industries
802
 
 * the list is not populated with invalid industries from the previous game.
 
721
 * The list of industries.
803
722
 */
804
 
static void MakeSortedIndustryList()
805
 
{
806
 
        const Industry* i;
807
 
        int n = 0;
808
 
 
809
 
        /* Create array for sorting */
810
 
        _industry_sort = ReallocT(_industry_sort, GetMaxIndustryIndex() + 1);
811
 
 
812
 
        /* Don't attempt a sort if there are no industries */
813
 
        if (GetNumIndustries() != 0) {
814
 
                FOR_ALL_INDUSTRIES(i) _industry_sort[n++] = i;
815
 
                qsort((void*)_industry_sort, n, sizeof(_industry_sort[0]), GeneralIndustrySorter);
816
 
        }
817
 
 
818
 
        _num_industry_sort = n;
819
 
        _last_industry = NULL; // used for "cache"
820
 
 
821
 
        DEBUG(misc, 3, "Resorting industries list");
822
 
}
823
 
 
824
 
 
825
 
static void IndustryDirectoryWndProc(Window *w, WindowEvent *e)
826
 
{
827
 
        switch (e->event) {
828
 
        case WE_PAINT: {
829
 
                if (_industry_sort_dirty) {
830
 
                        _industry_sort_dirty = false;
831
 
                        MakeSortedIndustryList();
832
 
                }
833
 
 
834
 
                SetVScrollCount(w, _num_industry_sort);
835
 
 
836
 
                DrawWindowWidgets(w);
837
 
                DrawSortButtonState(w, IDW_SORTBYNAME + (_industry_sort_order >> 1), _industry_sort_order & 1 ? SBS_DOWN : SBS_UP);
838
 
 
839
 
                uint p = w->vscroll.pos;
840
 
                int n = 0;
841
 
 
842
 
                while (p < _num_industry_sort) {
843
 
                        const Industry* i = _industry_sort[p];
844
 
 
845
 
                        SetDParam(0, i->index);
846
 
                        if (i->produced_cargo[0] != CT_INVALID) {
847
 
                                SetDParam(1, i->produced_cargo[0]);
848
 
                                SetDParam(2, i->last_month_production[0]);
849
 
 
850
 
                                if (i->produced_cargo[1] != CT_INVALID) {
851
 
                                        SetDParam(3, i->produced_cargo[1]);
852
 
                                        SetDParam(4, i->last_month_production[1]);
853
 
                                        SetDParam(5, i->last_month_pct_transported[0] * 100 >> 8);
854
 
                                        SetDParam(6, i->last_month_pct_transported[1] * 100 >> 8);
855
 
                                        DrawString(4, 28 + n * 10, STR_INDUSTRYDIR_ITEM_TWO, TC_FROMSTRING);
856
 
                                } else {
857
 
                                        SetDParam(3, i->last_month_pct_transported[0] * 100 >> 8);
858
 
                                        DrawString(4, 28 + n * 10, STR_INDUSTRYDIR_ITEM, TC_FROMSTRING);
859
 
                                }
860
 
                        } else {
861
 
                                DrawString(4, 28 + n * 10, STR_INDUSTRYDIR_ITEM_NOPROD, TC_FROMSTRING);
862
 
                        }
863
 
                        p++;
864
 
                        if (++n == w->vscroll.cap) break;
865
 
                }
866
 
        } break;
867
 
 
868
 
        case WE_CLICK:
869
 
                switch (e->we.click.widget) {
870
 
                        case IDW_SORTBYNAME: {
871
 
                                _industry_sort_order = _industry_sort_order == 0 ? 1 : 0;
872
 
                                _industry_sort_dirty = true;
873
 
                                SetWindowDirty(w);
874
 
                        } break;
875
 
 
876
 
                        case IDW_SORTBYTYPE: {
877
 
                                _industry_sort_order = _industry_sort_order == 2 ? 3 : 2;
878
 
                                _industry_sort_dirty = true;
879
 
                                SetWindowDirty(w);
880
 
                        } break;
881
 
 
882
 
                        case IDW_SORTBYPROD: {
883
 
                                _industry_sort_order = _industry_sort_order == 4 ? 5 : 4;
884
 
                                _industry_sort_dirty = true;
885
 
                                SetWindowDirty(w);
886
 
                        } break;
887
 
 
888
 
                        case IDW_SORTBYTRANSPORT: {
889
 
                                _industry_sort_order = _industry_sort_order == 6 ? 7 : 6;
890
 
                                _industry_sort_dirty = true;
891
 
                                SetWindowDirty(w);
892
 
                        } break;
893
 
 
894
 
                        case IDW_INDUSRTY_LIST: {
895
 
                                int y = (e->we.click.pt.y - 28) / 10;
 
723
class IndustryDirectoryWindow : public Window {
 
724
protected:
 
725
        /* Runtime saved values */
 
726
        static Listing last_sorting;
 
727
        static const Industry *last_industry;
 
728
 
 
729
        /* Constants for sorting stations */
 
730
        static const StringID sorter_names[];
 
731
        static GUIIndustryList::SortFunction * const sorter_funcs[];
 
732
 
 
733
        GUIIndustryList industries;
 
734
 
 
735
        /** (Re)Build industries list */
 
736
        void BuildIndustriesList()
 
737
        {
 
738
                if (!this->industries.NeedRebuild()) return;
 
739
 
 
740
                this->industries.Clear();
 
741
 
 
742
                DEBUG(misc, 3, "Building industry list");
 
743
 
 
744
                const Industry *i;
 
745
                FOR_ALL_INDUSTRIES(i) {
 
746
                        *this->industries.Append() = i;
 
747
                }
 
748
 
 
749
                this->industries.Compact();
 
750
                this->industries.RebuildDone();
 
751
        }
 
752
 
 
753
        /**
 
754
         * Returns percents of cargo transported if industry produces this cargo, else -1
 
755
         *
 
756
         * @param i industry to check
 
757
         * @param id cargo slot
 
758
         * @return percents of cargo transported, or -1 if industry doesn't use this cargo slot
 
759
         */
 
760
        static inline int GetCargoTransportedPercentsIfValid(const Industry *i, uint id)
 
761
        {
 
762
                assert(id < lengthof(i->produced_cargo));
 
763
 
 
764
                if (i->produced_cargo[id] == CT_INVALID) return 101;
 
765
                return i->last_month_pct_transported[id] * 100 >> 8;
 
766
        }
 
767
 
 
768
        /**
 
769
         * Returns value representing industry's transported cargo
 
770
         *  percentage for industry sorting
 
771
         *
 
772
         * @param i industry to check
 
773
         * @return value used for sorting
 
774
         */
 
775
        static int GetCargoTransportedSortValue(const Industry *i)
 
776
        {
 
777
                int p1 = GetCargoTransportedPercentsIfValid(i, 0);
 
778
                int p2 = GetCargoTransportedPercentsIfValid(i, 1);
 
779
 
 
780
                if (p1 > p2) Swap(p1, p2); // lower value has higher priority
 
781
 
 
782
                return (p1 << 8) + p2;
 
783
        }
 
784
 
 
785
        /** Sort industries by name */
 
786
        static int CDECL IndustryNameSorter(const Industry * const *a, const Industry * const *b)
 
787
        {
 
788
                static char buf_cache[96];
 
789
                static char buf[96];
 
790
 
 
791
                SetDParam(0, (*a)->town->index);
 
792
                GetString(buf, STR_TOWN, lastof(buf));
 
793
 
 
794
                if (*b != last_industry) {
 
795
                        last_industry = *b;
 
796
                        SetDParam(0, (*b)->town->index);
 
797
                        GetString(buf_cache, STR_TOWN, lastof(buf_cache));
 
798
                }
 
799
 
 
800
                return strcmp(buf, buf_cache);
 
801
        }
 
802
 
 
803
        /** Sort industries by type and name */
 
804
        static int CDECL IndustryTypeSorter(const Industry * const *a, const Industry * const *b)
 
805
        {
 
806
                int r = (*a)->type - (*b)->type;
 
807
                return (r == 0) ? IndustryNameSorter(a, b) : r;
 
808
        }
 
809
 
 
810
        /** Sort industries by production and name */
 
811
        static int CDECL IndustryProductionSorter(const Industry * const *a, const Industry * const *b)
 
812
        {
 
813
                int r = 0;
 
814
 
 
815
                if ((*a)->produced_cargo[0] == CT_INVALID) {
 
816
                        if ((*b)->produced_cargo[0] != CT_INVALID) return -1;
 
817
                } else {
 
818
                        if ((*b)->produced_cargo[0] == CT_INVALID) return 1;
 
819
 
 
820
                        r = ((*a)->last_month_production[0] + (*a)->last_month_production[1]) -
 
821
                            ((*b)->last_month_production[0] + (*b)->last_month_production[1]);
 
822
                }
 
823
 
 
824
                return (r == 0) ? IndustryNameSorter(a, b) : r;
 
825
        }
 
826
 
 
827
        /** Sort industries by transported cargo and name */
 
828
        static int CDECL IndustryTransportedCargoSorter(const Industry * const *a, const Industry * const *b)
 
829
        {
 
830
                int r = GetCargoTransportedSortValue(*a) - GetCargoTransportedSortValue(*b);
 
831
                return (r == 0) ? IndustryNameSorter(a, b) : r;
 
832
        }
 
833
 
 
834
        /** Sort the industries list */
 
835
        void SortIndustriesList()
 
836
        {
 
837
                if (!this->industries.Sort()) return;
 
838
 
 
839
                /* Reset name sorter sort cache */
 
840
                this->last_industry = NULL;
 
841
 
 
842
                /* Set the modified widget dirty */
 
843
                this->InvalidateWidget(IDW_INDUSTRY_LIST);
 
844
        }
 
845
 
 
846
public:
 
847
        IndustryDirectoryWindow(const WindowDesc *desc, WindowNumber number) : Window(desc, number)
 
848
        {
 
849
                this->vscroll.cap = 16;
 
850
                this->resize.height = this->height - 6 * 10; // minimum 10 items
 
851
                this->resize.step_height = 10;
 
852
                this->FindWindowPlacementAndResize(desc);
 
853
 
 
854
                this->industries.SetListing(this->last_sorting);
 
855
                this->industries.SetSortFuncs(this->sorter_funcs);
 
856
                this->industries.ForceRebuild();
 
857
                this->industries.NeedResort();
 
858
                this->SortIndustriesList();
 
859
 
 
860
                this->widget[IDW_DROPDOWN_CRITERIA].data = this->sorter_names[this->industries.SortType()];
 
861
        }
 
862
 
 
863
        ~IndustryDirectoryWindow()
 
864
        {
 
865
                this->last_sorting = this->industries.GetListing();
 
866
        }
 
867
 
 
868
        virtual void OnPaint()
 
869
        {
 
870
                BuildIndustriesList();
 
871
                SortIndustriesList();
 
872
 
 
873
                SetVScrollCount(this, this->industries.Length());
 
874
 
 
875
                this->DrawWidgets();
 
876
                this->DrawSortButtonState(IDW_DROPDOWN_ORDER, this->industries.IsDescSortOrder() ? SBS_DOWN : SBS_UP);
 
877
 
 
878
                int max = min(this->vscroll.pos + this->vscroll.cap, this->industries.Length());
 
879
                int y = 28; // start of the list-widget
 
880
 
 
881
                for (int n = this->vscroll.pos; n < max; ++n) {
 
882
                        const Industry *i = this->industries[n];
 
883
                        const IndustrySpec *indsp = GetIndustrySpec(i->type);
 
884
                        byte p = 0;
 
885
 
 
886
                        /* Industry name */
 
887
                        SetDParam(p++, i->index);
 
888
 
 
889
                        /* Industry productions */
 
890
                        for (byte j = 0; j < lengthof(i->produced_cargo); j++) {
 
891
                                if (i->produced_cargo[j] == CT_INVALID) continue;
 
892
                                SetDParam(p++, i->produced_cargo[j]);
 
893
                                SetDParam(p++, i->last_month_production[j]);
 
894
                                SetDParam(p++, GetCargoSuffix(j + 3, CST_DIR, (Industry*)i, i->type, indsp));
 
895
                        }
 
896
 
 
897
                        /* Transported productions */
 
898
                        for (byte j = 0; j < lengthof(i->produced_cargo); j++) {
 
899
                                if (i->produced_cargo[j] == CT_INVALID) continue;
 
900
                                SetDParam(p++, i->last_month_pct_transported[j] * 100 >> 8);
 
901
                        }
 
902
 
 
903
                        /* Drawing the right string */
 
904
                        StringID str = STR_INDUSTRYDIR_ITEM_NOPROD;
 
905
                        if (p != 1) str = (p == 5) ? STR_INDUSTRYDIR_ITEM : STR_INDUSTRYDIR_ITEM_TWO;
 
906
                        DrawStringTruncated(4, y, str, TC_FROMSTRING, this->widget[IDW_INDUSTRY_LIST].right - 4);
 
907
 
 
908
                        y += 10;
 
909
                }
 
910
        }
 
911
 
 
912
        virtual void OnClick(Point pt, int widget)
 
913
        {
 
914
                switch (widget) {
 
915
                        case IDW_DROPDOWN_ORDER:
 
916
                                this->industries.ToggleSortOrder();
 
917
                                this->SetDirty();
 
918
                                break;
 
919
 
 
920
                        case IDW_DROPDOWN_CRITERIA:
 
921
                                ShowDropDownMenu(this, this->sorter_names, this->industries.SortType(), IDW_DROPDOWN_CRITERIA, 0, 0);
 
922
                                break;
 
923
 
 
924
                        case IDW_INDUSTRY_LIST: {
 
925
                                int y = (pt.y - 28) / 10;
896
926
                                uint16 p;
897
927
 
898
 
                                if (!IsInsideMM(y, 0, w->vscroll.cap)) return;
899
 
                                p = y + w->vscroll.pos;
900
 
                                if (p < _num_industry_sort) {
901
 
                                        ScrollMainWindowToTile(_industry_sort[p]->xy);
 
928
                                if (!IsInsideMM(y, 0, this->vscroll.cap)) return;
 
929
                                p = y + this->vscroll.pos;
 
930
                                if (p < this->industries.Length()) {
 
931
                                        if (_ctrl_pressed) {
 
932
                                                ShowExtraViewPortWindow(this->industries[p]->xy);
 
933
                                        } else {
 
934
                                                ScrollMainWindowToTile(this->industries[p]->xy);
 
935
                                        }
902
936
                                }
903
937
                        } break;
904
938
                }
905
 
                break;
906
 
 
907
 
        case WE_4:
908
 
                SetWindowDirty(w);
909
 
                break;
910
 
 
911
 
        case WE_RESIZE:
912
 
                w->vscroll.cap += e->we.sizing.diff.y / 10;
913
 
                break;
914
 
        }
915
 
}
 
939
        }
 
940
 
 
941
        virtual void OnDropdownSelect(int widget, int index)
 
942
        {
 
943
                if (this->industries.SortType() != index) {
 
944
                        this->industries.SetSortType(index);
 
945
                        this->widget[IDW_DROPDOWN_CRITERIA].data = this->sorter_names[this->industries.SortType()];
 
946
                        this->SetDirty();
 
947
                }
 
948
        }
 
949
 
 
950
        virtual void OnResize(Point new_size, Point delta)
 
951
        {
 
952
                this->vscroll.cap += delta.y / 10;
 
953
        }
 
954
 
 
955
        virtual void OnInvalidateData(int data)
 
956
        {
 
957
                if (data == 0) {
 
958
                        this->industries.ForceRebuild();
 
959
                } else {
 
960
                        this->industries.ForceResort();
 
961
                }
 
962
                this->InvalidateWidget(IDW_INDUSTRY_LIST);
 
963
        }
 
964
};
 
965
 
 
966
Listing IndustryDirectoryWindow::last_sorting = {false, 0};
 
967
const Industry *IndustryDirectoryWindow::last_industry = NULL;
 
968
 
 
969
/* Availible station sorting functions */
 
970
GUIIndustryList::SortFunction * const IndustryDirectoryWindow::sorter_funcs[] = {
 
971
        &IndustryNameSorter,
 
972
        &IndustryTypeSorter,
 
973
        &IndustryProductionSorter,
 
974
        &IndustryTransportedCargoSorter
 
975
};
 
976
 
 
977
/* Names of the sorting functions */
 
978
const StringID IndustryDirectoryWindow::sorter_names[] = {
 
979
        STR_SORT_BY_DROPDOWN_NAME,
 
980
        STR_SORT_BY_TYPE,
 
981
        STR_SORT_BY_PRODUCTION,
 
982
        STR_SORT_BY_TRANSPORTED,
 
983
        INVALID_STRING_ID
 
984
};
 
985
 
916
986
 
917
987
/** Window definition of the industy directory gui */
918
 
static const WindowDesc _industry_directory_desc = {
919
 
        WDP_AUTO, WDP_AUTO, 508, 190, 508, 190,
 
988
static const WindowDesc _industry_directory_desc(
 
989
        WDP_AUTO, WDP_AUTO, 428, 190, 428, 190,
920
990
        WC_INDUSTRY_DIRECTORY, WC_NONE,
921
991
        WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
922
 
        _industry_directory_widgets,
923
 
        IndustryDirectoryWndProc
924
 
};
 
992
        _industry_directory_widgets
 
993
);
925
994
 
926
995
void ShowIndustryDirectory()
927
996
{
928
 
        Window *w = AllocateWindowDescFront(&_industry_directory_desc, 0);
929
 
 
930
 
        if (w != NULL) {
931
 
                w->vscroll.cap = 16;
932
 
                w->resize.height = w->height - 6 * 10; // minimum 10 items
933
 
                w->resize.step_height = 10;
934
 
                SetWindowDirty(w);
935
 
        }
 
997
        AllocateWindowDescFront<IndustryDirectoryWindow>(&_industry_directory_desc, 0);
936
998
}