diff --git a/src/genworld_gui.cpp b/src/genworld_gui.cpp --- a/src/genworld_gui.cpp +++ b/src/genworld_gui.cpp @@ -29,6 +29,7 @@ #include "widgets/dropdown_func.h" #include "core/random_func.hpp" #include "landscape_type.h" +#include "querystring_gui.h" #include "table/strings.h" #include "table/sprites.h" @@ -43,14 +44,6 @@ enum glwp_modes { GLWP_END }; -struct generate_d { - uint widget_id; - uint x; - uint y; - char name[64]; -}; -assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(generate_d)); - extern void SwitchMode(int new_mode); static inline void SetNewLandscapeType(byte landscape) @@ -244,314 +237,324 @@ static DropDownList *BuildMapsizeDropDow return list; } -static void GenerateLandscapeWndProc(Window *w, WindowEvent *e) -{ - static const StringID elevations[] = {STR_682A_VERY_FLAT, STR_682B_FLAT, STR_682C_HILLY, STR_682D_MOUNTAINOUS, INVALID_STRING_ID}; - static const StringID sea_lakes[] = {STR_VERY_LOW, STR_6820_LOW, STR_6821_MEDIUM, STR_6822_HIGH, INVALID_STRING_ID}; - static const StringID smoothness[] = {STR_CONFIG_PATCHES_ROUGHNESS_OF_TERRAIN_VERY_SMOOTH, STR_CONFIG_PATCHES_ROUGHNESS_OF_TERRAIN_SMOOTH, STR_CONFIG_PATCHES_ROUGHNESS_OF_TERRAIN_ROUGH, STR_CONFIG_PATCHES_ROUGHNESS_OF_TERRAIN_VERY_ROUGH, INVALID_STRING_ID}; - static const StringID tree_placer[] = {STR_CONFIG_PATCHES_TREE_PLACER_NONE, STR_CONFIG_PATCHES_TREE_PLACER_ORIGINAL, STR_CONFIG_PATCHES_TREE_PLACER_IMPROVED, INVALID_STRING_ID}; - static const StringID rotation[] = {STR_CONFIG_PATCHES_HEIGHTMAP_ROTATION_COUNTER_CLOCKWISE, STR_CONFIG_PATCHES_HEIGHTMAP_ROTATION_CLOCKWISE, INVALID_STRING_ID}; - static const StringID landscape[] = {STR_CONFIG_PATCHES_LAND_GENERATOR_ORIGINAL, STR_CONFIG_PATCHES_LAND_GENERATOR_TERRA_GENESIS, INVALID_STRING_ID}; - static const StringID num_towns[] = {STR_NUM_VERY_LOW, STR_6816_LOW, STR_6817_NORMAL, STR_6818_HIGH, INVALID_STRING_ID}; - static const StringID num_inds[] = {STR_NONE, STR_NUM_VERY_LOW, STR_6816_LOW, STR_6817_NORMAL, STR_6818_HIGH, INVALID_STRING_ID}; +static const StringID _elevations[] = {STR_682A_VERY_FLAT, STR_682B_FLAT, STR_682C_HILLY, STR_682D_MOUNTAINOUS, INVALID_STRING_ID}; +static const StringID _sea_lakes[] = {STR_VERY_LOW, STR_6820_LOW, STR_6821_MEDIUM, STR_6822_HIGH, INVALID_STRING_ID}; +static const StringID _smoothness[] = {STR_CONFIG_PATCHES_ROUGHNESS_OF_TERRAIN_VERY_SMOOTH, STR_CONFIG_PATCHES_ROUGHNESS_OF_TERRAIN_SMOOTH, STR_CONFIG_PATCHES_ROUGHNESS_OF_TERRAIN_ROUGH, STR_CONFIG_PATCHES_ROUGHNESS_OF_TERRAIN_VERY_ROUGH, INVALID_STRING_ID}; +static const StringID _tree_placer[] = {STR_CONFIG_PATCHES_TREE_PLACER_NONE, STR_CONFIG_PATCHES_TREE_PLACER_ORIGINAL, STR_CONFIG_PATCHES_TREE_PLACER_IMPROVED, INVALID_STRING_ID}; +static const StringID _rotation[] = {STR_CONFIG_PATCHES_HEIGHTMAP_ROTATION_COUNTER_CLOCKWISE, STR_CONFIG_PATCHES_HEIGHTMAP_ROTATION_CLOCKWISE, INVALID_STRING_ID}; +static const StringID _landscape[] = {STR_CONFIG_PATCHES_LAND_GENERATOR_ORIGINAL, STR_CONFIG_PATCHES_LAND_GENERATOR_TERRA_GENESIS, INVALID_STRING_ID}; +static const StringID _num_towns[] = {STR_NUM_VERY_LOW, STR_6816_LOW, STR_6817_NORMAL, STR_6818_HIGH, INVALID_STRING_ID}; +static const StringID _num_inds[] = {STR_NONE, STR_NUM_VERY_LOW, STR_6816_LOW, STR_6817_NORMAL, STR_6818_HIGH, INVALID_STRING_ID}; + +struct GenerateLandscapeWindow : public QueryStringBaseWindow { + uint widget_id; + uint x; + uint y; + char name[64]; + glwp_modes mode; + + GenerateLandscapeWindow(const WindowDesc *desc, void *data = NULL, WindowNumber number = 0) : QueryStringBaseWindow(desc, NULL, number) + { + this->LowerWidget(_opt_newgame.landscape + GLAND_TEMPERATE); - /* Data used for the generate seed edit box */ - static querystr_d _genseed_query; - static char _genseed_buffer[11]; + snprintf(this->edit_str_buf, sizeof(this->edit_str_buf), "%u", _patches_newgame.generation_seed); + InitializeTextBuffer(&this->text, this->edit_str_buf, lengthof(this->edit_str_buf), 120); + this->caption = STR_NULL; + this->afilter = CS_NUMERAL; - glwp_modes mode = (glwp_modes)w->window_number; + this->mode = (glwp_modes)this->window_number; + + this->FindWindowPlacementAndResize(desc); + } - switch (e->event) { - case WE_CREATE: - w->LowerWidget(_opt_newgame.landscape + GLAND_TEMPERATE); - - snprintf(_genseed_buffer, sizeof(_genseed_buffer), "%u", _patches_newgame.generation_seed); - InitializeTextBuffer(&_genseed_query.text, _genseed_buffer, lengthof(_genseed_buffer), 120); - _genseed_query.caption = STR_NULL; - _genseed_query.afilter = CS_NUMERAL; - break; + virtual void OnPaint() + { + /* You can't select smoothness if not terragenesis */ + if (mode == GLWP_GENERATE) { + this->SetWidgetDisabledState(GLAND_SMOOTHNESS_PULLDOWN, _patches_newgame.land_generator == 0); + } + /* Disable snowline if not hilly */ + this->SetWidgetDisabledState(GLAND_SNOW_LEVEL_TEXT, _opt_newgame.landscape != LT_ARCTIC); + /* Disable town, industry and trees in SE */ + this->SetWidgetDisabledState(GLAND_TOWN_PULLDOWN, _game_mode == GM_EDITOR); + this->SetWidgetDisabledState(GLAND_INDUSTRY_PULLDOWN, _game_mode == GM_EDITOR); + this->SetWidgetDisabledState(GLAND_TREE_PULLDOWN, _game_mode == GM_EDITOR); - case WE_PAINT: - /* You can't select smoothness if not terragenesis */ - if (mode == GLWP_GENERATE) { - w->SetWidgetDisabledState(GLAND_SMOOTHNESS_PULLDOWN, _patches_newgame.land_generator == 0); - } - /* Disable snowline if not hilly */ - w->SetWidgetDisabledState(GLAND_SNOW_LEVEL_TEXT, _opt_newgame.landscape != LT_ARCTIC); - /* Disable town, industry and trees in SE */ - w->SetWidgetDisabledState(GLAND_TOWN_PULLDOWN, _game_mode == GM_EDITOR); - w->SetWidgetDisabledState(GLAND_INDUSTRY_PULLDOWN, _game_mode == GM_EDITOR); - w->SetWidgetDisabledState(GLAND_TREE_PULLDOWN, _game_mode == GM_EDITOR); + this->SetWidgetDisabledState(GLAND_START_DATE_DOWN, _patches_newgame.starting_year <= MIN_YEAR); + this->SetWidgetDisabledState(GLAND_START_DATE_UP, _patches_newgame.starting_year >= MAX_YEAR); + this->SetWidgetDisabledState(GLAND_SNOW_LEVEL_DOWN, _patches_newgame.snow_line_height <= 2 || _opt_newgame.landscape != LT_ARCTIC); + this->SetWidgetDisabledState(GLAND_SNOW_LEVEL_UP, _patches_newgame.snow_line_height >= MAX_SNOWLINE_HEIGHT || _opt_newgame.landscape != LT_ARCTIC); - w->SetWidgetDisabledState(GLAND_START_DATE_DOWN, _patches_newgame.starting_year <= MIN_YEAR); - w->SetWidgetDisabledState(GLAND_START_DATE_UP, _patches_newgame.starting_year >= MAX_YEAR); - w->SetWidgetDisabledState(GLAND_SNOW_LEVEL_DOWN, _patches_newgame.snow_line_height <= 2 || _opt_newgame.landscape != LT_ARCTIC); - w->SetWidgetDisabledState(GLAND_SNOW_LEVEL_UP, _patches_newgame.snow_line_height >= MAX_SNOWLINE_HEIGHT || _opt_newgame.landscape != LT_ARCTIC); + this->SetWidgetLoweredState(GLAND_TEMPERATE, _opt_newgame.landscape == LT_TEMPERATE); + this->SetWidgetLoweredState(GLAND_ARCTIC, _opt_newgame.landscape == LT_ARCTIC); + this->SetWidgetLoweredState(GLAND_TROPICAL, _opt_newgame.landscape == LT_TROPIC); + this->SetWidgetLoweredState(GLAND_TOYLAND, _opt_newgame.landscape == LT_TOYLAND); + + if (_game_mode == GM_EDITOR) { + this->widget[GLAND_TOWN_PULLDOWN].data = STR_6836_OFF; + this->widget[GLAND_INDUSTRY_PULLDOWN].data = STR_6836_OFF; + } else { + this->widget[GLAND_TOWN_PULLDOWN].data = _num_towns[_opt_newgame.diff.number_towns]; + this->widget[GLAND_INDUSTRY_PULLDOWN].data = _num_inds[_opt_newgame.diff.number_industries]; + } - w->SetWidgetLoweredState(GLAND_TEMPERATE, _opt_newgame.landscape == LT_TEMPERATE); - w->SetWidgetLoweredState(GLAND_ARCTIC, _opt_newgame.landscape == LT_ARCTIC); - w->SetWidgetLoweredState(GLAND_TROPICAL, _opt_newgame.landscape == LT_TROPIC); - w->SetWidgetLoweredState(GLAND_TOYLAND, _opt_newgame.landscape == LT_TOYLAND); + if (mode == GLWP_GENERATE) { + this->widget[GLAND_LANDSCAPE_PULLDOWN].data = _landscape[_patches_newgame.land_generator]; + this->widget[GLAND_TREE_PULLDOWN].data = _tree_placer[_patches_newgame.tree_placer]; + this->widget[GLAND_TERRAIN_PULLDOWN].data = _elevations[_opt_newgame.diff.terrain_type]; + this->widget[GLAND_WATER_PULLDOWN].data = _sea_lakes[_opt_newgame.diff.quantity_sea_lakes]; + this->widget[GLAND_SMOOTHNESS_PULLDOWN].data = _smoothness[_patches_newgame.tgen_smoothness]; + } else { + this->widget[GLAND_TREE_PULLDOWN].data = _tree_placer[_patches_newgame.tree_placer]; + this->widget[GLAND_HEIGHTMAP_ROTATION_PULLDOWN].data = _rotation[_patches_newgame.heightmap_rotation]; + } - if (_game_mode == GM_EDITOR) { - w->widget[GLAND_TOWN_PULLDOWN].data = STR_6836_OFF; - w->widget[GLAND_INDUSTRY_PULLDOWN].data = STR_6836_OFF; + /* Set parameters for widget text that requires them. */ + SetDParam(0, ConvertYMDToDate(_patches_newgame.starting_year, 0, 1)); // GLAND_START_DATE_TEXT + SetDParam(1, 1 << _patches_newgame.map_x); // GLAND_MAPSIZE_X_PULLDOWN + SetDParam(2, 1 << _patches_newgame.map_y); // GLAND_MAPSIZE_Y_PULLDOWN + SetDParam(3, _patches_newgame.snow_line_height); // GLAND_SNOW_LEVEL_TEXT + + DrawWindowWidgets(this); + + this->DrawEditBox(GLAND_RANDOM_EDITBOX); + + if (mode != GLWP_GENERATE) { + char buffer[512]; + + if (_patches_newgame.heightmap_rotation == HM_CLOCKWISE) { + SetDParam(0, this->y); + SetDParam(1, this->x); } else { - w->widget[GLAND_TOWN_PULLDOWN].data = num_towns[_opt_newgame.diff.number_towns]; - w->widget[GLAND_INDUSTRY_PULLDOWN].data = num_inds[_opt_newgame.diff.number_industries]; - } - - if (mode == GLWP_GENERATE) { - w->widget[GLAND_LANDSCAPE_PULLDOWN].data = landscape[_patches_newgame.land_generator]; - w->widget[GLAND_TREE_PULLDOWN].data = tree_placer[_patches_newgame.tree_placer]; - w->widget[GLAND_TERRAIN_PULLDOWN].data = elevations[_opt_newgame.diff.terrain_type]; - w->widget[GLAND_WATER_PULLDOWN].data = sea_lakes[_opt_newgame.diff.quantity_sea_lakes]; - w->widget[GLAND_SMOOTHNESS_PULLDOWN].data = smoothness[_patches_newgame.tgen_smoothness]; - } else { - w->widget[GLAND_TREE_PULLDOWN].data = tree_placer[_patches_newgame.tree_placer]; - w->widget[GLAND_HEIGHTMAP_ROTATION_PULLDOWN].data = rotation[_patches_newgame.heightmap_rotation]; + SetDParam(0, this->x); + SetDParam(1, this->y); } + GetString(buffer, STR_HEIGHTMAP_SIZE, lastof(buffer)); + DrawStringRightAligned(326, 91, STR_HEIGHTMAP_SIZE, TC_BLACK); - /* Set parameters for widget text that requires them. */ - SetDParam(0, ConvertYMDToDate(_patches_newgame.starting_year, 0, 1)); // GLAND_START_DATE_TEXT - SetDParam(1, 1 << _patches_newgame.map_x); // GLAND_MAPSIZE_X_PULLDOWN - SetDParam(2, 1 << _patches_newgame.map_y); // GLAND_MAPSIZE_Y_PULLDOWN - SetDParam(3, _patches_newgame.snow_line_height); // GLAND_SNOW_LEVEL_TEXT + DrawString( 12, 91, STR_HEIGHTMAP_NAME, TC_BLACK); + SetDParamStr(0, this->name); + DrawStringTruncated(114, 91, STR_ORANGE, TC_BLACK, 326 - 114 - GetStringBoundingBox(buffer).width - 5); + } + } + + virtual void OnClick(Point pt, int widget) + { + switch (widget) { + case 0: delete this; break; + + case GLAND_TEMPERATE: + case GLAND_ARCTIC: + case GLAND_TROPICAL: + case GLAND_TOYLAND: + this->RaiseWidget(_opt_newgame.landscape + GLAND_TEMPERATE); + SetNewLandscapeType(widget - GLAND_TEMPERATE); + break; - DrawWindowWidgets(w); + case GLAND_MAPSIZE_X_PULLDOWN: // Mapsize X + ShowDropDownList(this, BuildMapsizeDropDown(), _patches_newgame.map_x, GLAND_MAPSIZE_X_PULLDOWN); + break; + + case GLAND_MAPSIZE_Y_PULLDOWN: // Mapsize Y + ShowDropDownList(this, BuildMapsizeDropDown(), _patches_newgame.map_y, GLAND_MAPSIZE_Y_PULLDOWN); + break; + + case GLAND_TOWN_PULLDOWN: // Number of towns + ShowDropDownMenu(this, _num_towns, _opt_newgame.diff.number_towns, GLAND_TOWN_PULLDOWN, 0, 0); + break; - DrawEditBox(w, &_genseed_query, GLAND_RANDOM_EDITBOX); + case GLAND_INDUSTRY_PULLDOWN: // Number of industries + ShowDropDownMenu(this, _num_inds, _opt_newgame.diff.number_industries, GLAND_INDUSTRY_PULLDOWN, 0, 0); + break; - if (mode != GLWP_GENERATE) { - char buffer[512]; + case GLAND_RANDOM_BUTTON: // Random seed + _patches_newgame.generation_seed = InteractiveRandom(); + snprintf(this->edit_str_buf, lengthof(this->edit_str_buf), "%u", _patches_newgame.generation_seed); + UpdateTextBufferSize(&this->text); + this->SetDirty(); + break; + + case GLAND_RANDOM_EDITBOX: // edit box for random seed + ShowOnScreenKeyboard(this, GLAND_RANDOM_EDITBOX, 0, 0); + break; - if (_patches_newgame.heightmap_rotation == HM_CLOCKWISE) { - SetDParam(0, WP(w, generate_d).y); - SetDParam(1, WP(w, generate_d).x); + case GLAND_GENERATE_BUTTON: // Generate + UpdatePatches(); + + if (_patches.town_layout == TL_NO_ROADS) { + ShowQuery( + STR_TOWN_LAYOUT_WARNING_CAPTION, + STR_TOWN_LAYOUT_WARNING_MESSAGE, + this, + LandscapeGenerationCallback); + } else if (mode == GLWP_HEIGHTMAP && + (this->x * 2 < (1U << _patches_newgame.map_x) || + this->x / 2 > (1U << _patches_newgame.map_x) || + this->y * 2 < (1U << _patches_newgame.map_y) || + this->y / 2 > (1U << _patches_newgame.map_y))) { + ShowQuery( + STR_HEIGHTMAP_SCALE_WARNING_CAPTION, + STR_HEIGHTMAP_SCALE_WARNING_MESSAGE, + this, + LandscapeGenerationCallback); } else { - SetDParam(0, WP(w, generate_d).x); - SetDParam(1, WP(w, generate_d).y); + StartGeneratingLandscape(mode); } - GetString(buffer, STR_HEIGHTMAP_SIZE, lastof(buffer)); - DrawStringRightAligned(326, 91, STR_HEIGHTMAP_SIZE, TC_BLACK); + break; - DrawString( 12, 91, STR_HEIGHTMAP_NAME, TC_BLACK); - SetDParamStr(0, WP(w, generate_d).name); - DrawStringTruncated(114, 91, STR_ORANGE, TC_BLACK, 326 - 114 - GetStringBoundingBox(buffer).width - 5); - } - break; + case GLAND_START_DATE_DOWN: + case GLAND_START_DATE_UP: // Year buttons + /* Don't allow too fast scrolling */ + if ((this->flags4 & WF_TIMEOUT_MASK) <= 2 << WF_TIMEOUT_SHL) { + this->HandleButtonClick(widget); + this->SetDirty(); + + _patches_newgame.starting_year = Clamp(_patches_newgame.starting_year + widget - GLAND_START_DATE_TEXT, MIN_YEAR, MAX_YEAR); + } + _left_button_clicked = false; + break; - case WE_CLICK: - switch (e->we.click.widget) { - case 0: delete w; break; + case GLAND_START_DATE_TEXT: // Year text + this->widget_id = GLAND_START_DATE_TEXT; + SetDParam(0, _patches_newgame.starting_year); + ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_START_DATE_QUERY_CAPT, 8, 100, this, CS_NUMERAL); + break; + + case GLAND_SNOW_LEVEL_DOWN: + case GLAND_SNOW_LEVEL_UP: // Snow line buttons + /* Don't allow too fast scrolling */ + if ((this->flags4 & WF_TIMEOUT_MASK) <= 2 << WF_TIMEOUT_SHL) { + this->HandleButtonClick(widget); + this->SetDirty(); + + _patches_newgame.snow_line_height = Clamp(_patches_newgame.snow_line_height + widget - GLAND_SNOW_LEVEL_TEXT, 2, MAX_SNOWLINE_HEIGHT); + } + _left_button_clicked = false; + break; - case GLAND_TEMPERATE: - case GLAND_ARCTIC: - case GLAND_TROPICAL: - case GLAND_TOYLAND: - w->RaiseWidget(_opt_newgame.landscape + GLAND_TEMPERATE); - SetNewLandscapeType(e->we.click.widget - GLAND_TEMPERATE); - break; + case GLAND_SNOW_LEVEL_TEXT: // Snow line text + this->widget_id = GLAND_SNOW_LEVEL_TEXT; + SetDParam(0, _patches_newgame.snow_line_height); + ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_SNOW_LINE_QUERY_CAPT, 3, 100, this, CS_NUMERAL); + break; + + case GLAND_TREE_PULLDOWN: // Tree placer + ShowDropDownMenu(this, _tree_placer, _patches_newgame.tree_placer, GLAND_TREE_PULLDOWN, 0, 0); + break; + + case GLAND_LANDSCAPE_PULLDOWN: // Landscape generator OR Heightmap rotation + /* case GLAND_HEIGHTMAP_ROTATION_TEXT: case GLAND_HEIGHTMAP_ROTATION_PULLDOWN:*/ + if (mode == GLWP_HEIGHTMAP) { + ShowDropDownMenu(this, _rotation, _patches_newgame.heightmap_rotation, GLAND_HEIGHTMAP_ROTATION_PULLDOWN, 0, 0); + } else { + ShowDropDownMenu(this, _landscape, _patches_newgame.land_generator, GLAND_LANDSCAPE_PULLDOWN, 0, 0); + } + break; + + case GLAND_TERRAIN_PULLDOWN: // Terrain type + ShowDropDownMenu(this, _elevations, _opt_newgame.diff.terrain_type, GLAND_TERRAIN_PULLDOWN, 0, 0); + break; - case GLAND_MAPSIZE_X_PULLDOWN: // Mapsize X - ShowDropDownList(w, BuildMapsizeDropDown(), _patches_newgame.map_x, GLAND_MAPSIZE_X_PULLDOWN); - break; + case GLAND_WATER_PULLDOWN: // Water quantity + ShowDropDownMenu(this, _sea_lakes, _opt_newgame.diff.quantity_sea_lakes, GLAND_WATER_PULLDOWN, 0, 0); + break; + + case GLAND_SMOOTHNESS_PULLDOWN: // Map smoothness + ShowDropDownMenu(this, _smoothness, _patches_newgame.tgen_smoothness, GLAND_SMOOTHNESS_PULLDOWN, 0, 0); + break; + } + } - case GLAND_MAPSIZE_Y_PULLDOWN: // Mapsize Y - ShowDropDownList(w, BuildMapsizeDropDown(), _patches_newgame.map_y, GLAND_MAPSIZE_Y_PULLDOWN); - break; + virtual void OnMouseLoop() + { + this->HandleEditBox(GLAND_RANDOM_EDITBOX); + } + + virtual bool OnKeyPress(uint16 key, uint16 keycode) + { + bool cont; + this->HandleEditBoxKey(GLAND_RANDOM_EDITBOX, key, keycode, cont); + /* the seed is unsigned, therefore atoi cannot be used. + * As 2^32 - 1 (MAX_UVALUE(uint32)) is a 'magic' value + * (use random seed) it should not be possible to be + * entered into the input field; the generate seed + * button can be used instead. */ + _patches_newgame.generation_seed = minu(strtoul(this->edit_str_buf, NULL, sizeof(this->edit_str_buf) - 1), MAX_UVALUE(uint32) - 1); + return cont; + } - case GLAND_TOWN_PULLDOWN: // Number of towns - ShowDropDownMenu(w, num_towns, _opt_newgame.diff.number_towns, GLAND_TOWN_PULLDOWN, 0, 0); - break; + virtual void OnDropdownSelect(int widget, int index) + { + switch (widget) { + case GLAND_MAPSIZE_X_PULLDOWN: _patches_newgame.map_x = index; break; + case GLAND_MAPSIZE_Y_PULLDOWN: _patches_newgame.map_y = index; break; + case GLAND_TREE_PULLDOWN: _patches_newgame.tree_placer = index; break; + case GLAND_SMOOTHNESS_PULLDOWN: _patches_newgame.tgen_smoothness = index; break; - case GLAND_INDUSTRY_PULLDOWN: // Number of industries - ShowDropDownMenu(w, num_inds, _opt_newgame.diff.number_industries, GLAND_INDUSTRY_PULLDOWN, 0, 0); - break; + case GLAND_TOWN_PULLDOWN: + _opt_newgame.diff.number_towns = index; + if (_opt_newgame.diff_level != 3) ShowErrorMessage(INVALID_STRING_ID, STR_DIFFICULTY_TO_CUSTOM, 0, 0); + DoCommandP(0, 2, _opt_newgame.diff.number_towns, NULL, CMD_CHANGE_DIFFICULTY_LEVEL); + break; + + case GLAND_INDUSTRY_PULLDOWN: + _opt_newgame.diff.number_industries = index; + if (_opt_newgame.diff_level != 3) ShowErrorMessage(INVALID_STRING_ID, STR_DIFFICULTY_TO_CUSTOM, 0, 0); + DoCommandP(0, 3, _opt_newgame.diff.number_industries, NULL, CMD_CHANGE_DIFFICULTY_LEVEL); + break; - case GLAND_RANDOM_BUTTON: // Random seed - _patches_newgame.generation_seed = InteractiveRandom(); - snprintf(_genseed_buffer, lengthof(_genseed_buffer), "%u", _patches_newgame.generation_seed); - UpdateTextBufferSize(&_genseed_query.text); - w->SetDirty(); - break; + case GLAND_LANDSCAPE_PULLDOWN: + /* case GLAND_HEIGHTMAP_PULLDOWN: */ + if (mode == GLWP_HEIGHTMAP) { + _patches_newgame.heightmap_rotation = index; + } else { + _patches_newgame.land_generator = index; + } + break; + + case GLAND_TERRAIN_PULLDOWN: + _opt_newgame.diff.terrain_type = index; + if (_opt_newgame.diff_level != 3) ShowErrorMessage(INVALID_STRING_ID, STR_DIFFICULTY_TO_CUSTOM, 0, 0); + DoCommandP(0, 12, _opt_newgame.diff.terrain_type, NULL, CMD_CHANGE_DIFFICULTY_LEVEL); + break; - case GLAND_RANDOM_EDITBOX: // edit box for random seed - ShowOnScreenKeyboard(w, & _genseed_query, GLAND_RANDOM_EDITBOX, 0, 0); + case GLAND_WATER_PULLDOWN: + _opt_newgame.diff.quantity_sea_lakes = index; + if (_opt_newgame.diff_level != 3) ShowErrorMessage(INVALID_STRING_ID, STR_DIFFICULTY_TO_CUSTOM, 0, 0); + DoCommandP(0, 13, _opt_newgame.diff.quantity_sea_lakes, NULL, CMD_CHANGE_DIFFICULTY_LEVEL); + break; + } + this->SetDirty(); + } + + virtual void OnQueryTextFinished(char *str) + { + if (!StrEmpty(str)) { + int32 value = atoi(str); + + switch (this->widget_id) { + case GLAND_START_DATE_TEXT: + this->InvalidateWidget(GLAND_START_DATE_TEXT); + _patches_newgame.starting_year = Clamp(value, MIN_YEAR, MAX_YEAR); break; - case GLAND_GENERATE_BUTTON: // Generate - UpdatePatches(); - - if (_patches.town_layout == TL_NO_ROADS) { - ShowQuery( - STR_TOWN_LAYOUT_WARNING_CAPTION, - STR_TOWN_LAYOUT_WARNING_MESSAGE, - w, - LandscapeGenerationCallback); - } else if (mode == GLWP_HEIGHTMAP && - (WP(w, generate_d).x * 2 < (1U << _patches_newgame.map_x) || - WP(w, generate_d).x / 2 > (1U << _patches_newgame.map_x) || - WP(w, generate_d).y * 2 < (1U << _patches_newgame.map_y) || - WP(w, generate_d).y / 2 > (1U << _patches_newgame.map_y))) { - ShowQuery( - STR_HEIGHTMAP_SCALE_WARNING_CAPTION, - STR_HEIGHTMAP_SCALE_WARNING_MESSAGE, - w, - LandscapeGenerationCallback); - } else { - StartGeneratingLandscape(mode); - } - break; - - case GLAND_START_DATE_DOWN: - case GLAND_START_DATE_UP: // Year buttons - /* Don't allow too fast scrolling */ - if ((w->flags4 & WF_TIMEOUT_MASK) <= 2 << WF_TIMEOUT_SHL) { - w->HandleButtonClick(e->we.click.widget); - w->SetDirty(); - - _patches_newgame.starting_year = Clamp(_patches_newgame.starting_year + e->we.click.widget - GLAND_START_DATE_TEXT, MIN_YEAR, MAX_YEAR); - } - _left_button_clicked = false; - break; - - case GLAND_START_DATE_TEXT: // Year text - WP(w, generate_d).widget_id = GLAND_START_DATE_TEXT; - SetDParam(0, _patches_newgame.starting_year); - ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_START_DATE_QUERY_CAPT, 8, 100, w, CS_NUMERAL); - break; - - case GLAND_SNOW_LEVEL_DOWN: - case GLAND_SNOW_LEVEL_UP: // Snow line buttons - /* Don't allow too fast scrolling */ - if ((w->flags4 & WF_TIMEOUT_MASK) <= 2 << WF_TIMEOUT_SHL) { - w->HandleButtonClick(e->we.click.widget); - w->SetDirty(); - - _patches_newgame.snow_line_height = Clamp(_patches_newgame.snow_line_height + e->we.click.widget - GLAND_SNOW_LEVEL_TEXT, 2, MAX_SNOWLINE_HEIGHT); - } - _left_button_clicked = false; - break; - - case GLAND_SNOW_LEVEL_TEXT: // Snow line text - WP(w, generate_d).widget_id = GLAND_SNOW_LEVEL_TEXT; - SetDParam(0, _patches_newgame.snow_line_height); - ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_SNOW_LINE_QUERY_CAPT, 3, 100, w, CS_NUMERAL); - break; - - case GLAND_TREE_PULLDOWN: // Tree placer - ShowDropDownMenu(w, tree_placer, _patches_newgame.tree_placer, GLAND_TREE_PULLDOWN, 0, 0); - break; - - case GLAND_LANDSCAPE_PULLDOWN: // Landscape generator OR Heightmap rotation - /* case GLAND_HEIGHTMAP_ROTATION_TEXT: case GLAND_HEIGHTMAP_ROTATION_PULLDOWN:*/ - if (mode == GLWP_HEIGHTMAP) { - ShowDropDownMenu(w, rotation, _patches_newgame.heightmap_rotation, GLAND_HEIGHTMAP_ROTATION_PULLDOWN, 0, 0); - } else { - ShowDropDownMenu(w, landscape, _patches_newgame.land_generator, GLAND_LANDSCAPE_PULLDOWN, 0, 0); - } - break; - - case GLAND_TERRAIN_PULLDOWN: // Terrain type - ShowDropDownMenu(w, elevations, _opt_newgame.diff.terrain_type, GLAND_TERRAIN_PULLDOWN, 0, 0); - break; - - case GLAND_WATER_PULLDOWN: // Water quantity - ShowDropDownMenu(w, sea_lakes, _opt_newgame.diff.quantity_sea_lakes, GLAND_WATER_PULLDOWN, 0, 0); - break; - - case GLAND_SMOOTHNESS_PULLDOWN: // Map smoothness - ShowDropDownMenu(w, smoothness, _patches_newgame.tgen_smoothness, GLAND_SMOOTHNESS_PULLDOWN, 0, 0); + case GLAND_SNOW_LEVEL_TEXT: + this->InvalidateWidget(GLAND_SNOW_LEVEL_TEXT); + _patches_newgame.snow_line_height = Clamp(value, 2, MAX_SNOWLINE_HEIGHT); break; } - break; - case WE_MOUSELOOP: - HandleEditBox(w, &_genseed_query, GLAND_RANDOM_EDITBOX); - break; - - case WE_KEYPRESS: - HandleEditBoxKey(w, &_genseed_query, GLAND_RANDOM_EDITBOX, e); - /* the seed is unsigned, therefore atoi cannot be used. - * As 2^32 - 1 (MAX_UVALUE(uint32)) is a 'magic' value - * (use random seed) it should not be possible to be - * entered into the input field; the generate seed - * button can be used instead. */ - _patches_newgame.generation_seed = minu(strtoul(_genseed_buffer, NULL, sizeof(_genseed_buffer) - 1), MAX_UVALUE(uint32) - 1); - break; - - case WE_DROPDOWN_SELECT: - switch (e->we.dropdown.button) { - case GLAND_MAPSIZE_X_PULLDOWN: _patches_newgame.map_x = e->we.dropdown.index; break; - case GLAND_MAPSIZE_Y_PULLDOWN: _patches_newgame.map_y = e->we.dropdown.index; break; - case GLAND_TREE_PULLDOWN: _patches_newgame.tree_placer = e->we.dropdown.index; break; - case GLAND_SMOOTHNESS_PULLDOWN: _patches_newgame.tgen_smoothness = e->we.dropdown.index; break; - - case GLAND_TOWN_PULLDOWN: - _opt_newgame.diff.number_towns = e->we.dropdown.index; - if (_opt_newgame.diff_level != 3) ShowErrorMessage(INVALID_STRING_ID, STR_DIFFICULTY_TO_CUSTOM, 0, 0); - DoCommandP(0, 2, _opt_newgame.diff.number_towns, NULL, CMD_CHANGE_DIFFICULTY_LEVEL); - break; - - case GLAND_INDUSTRY_PULLDOWN: - _opt_newgame.diff.number_industries = e->we.dropdown.index; - if (_opt_newgame.diff_level != 3) ShowErrorMessage(INVALID_STRING_ID, STR_DIFFICULTY_TO_CUSTOM, 0, 0); - DoCommandP(0, 3, _opt_newgame.diff.number_industries, NULL, CMD_CHANGE_DIFFICULTY_LEVEL); - break; - - case GLAND_LANDSCAPE_PULLDOWN: - /* case GLAND_HEIGHTMAP_PULLDOWN: */ - if (mode == GLWP_HEIGHTMAP) { - _patches_newgame.heightmap_rotation = e->we.dropdown.index; - } else { - _patches_newgame.land_generator = e->we.dropdown.index; - } - break; - - case GLAND_TERRAIN_PULLDOWN: - _opt_newgame.diff.terrain_type = e->we.dropdown.index; - if (_opt_newgame.diff_level != 3) ShowErrorMessage(INVALID_STRING_ID, STR_DIFFICULTY_TO_CUSTOM, 0, 0); - DoCommandP(0, 12, _opt_newgame.diff.terrain_type, NULL, CMD_CHANGE_DIFFICULTY_LEVEL); - break; - - case GLAND_WATER_PULLDOWN: - _opt_newgame.diff.quantity_sea_lakes = e->we.dropdown.index; - if (_opt_newgame.diff_level != 3) ShowErrorMessage(INVALID_STRING_ID, STR_DIFFICULTY_TO_CUSTOM, 0, 0); - DoCommandP(0, 13, _opt_newgame.diff.quantity_sea_lakes, NULL, CMD_CHANGE_DIFFICULTY_LEVEL); - break; - } - w->SetDirty(); - break; - - case WE_ON_EDIT_TEXT: - if (!StrEmpty(e->we.edittext.str)) { - int32 value = atoi(e->we.edittext.str); - - switch (WP(w, generate_d).widget_id) { - case GLAND_START_DATE_TEXT: - w->InvalidateWidget(GLAND_START_DATE_TEXT); - _patches_newgame.starting_year = Clamp(value, MIN_YEAR, MAX_YEAR); - break; - - case GLAND_SNOW_LEVEL_TEXT: - w->InvalidateWidget(GLAND_SNOW_LEVEL_TEXT); - _patches_newgame.snow_line_height = Clamp(value, 2, MAX_SNOWLINE_HEIGHT); - break; - } - - w->SetDirty(); - } - break; + this->SetDirty(); + } } -} +}; static const WindowDesc _generate_landscape_desc = { WDP_CENTER, WDP_CENTER, 338, 268, 338, 268, WC_GENERATE_LANDSCAPE, WC_NONE, WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, _generate_landscape_widgets, - GenerateLandscapeWndProc, + NULL, }; static const WindowDesc _heightmap_load_desc = { @@ -559,7 +562,7 @@ static const WindowDesc _heightmap_load_ WC_GENERATE_LANDSCAPE, WC_NONE, WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_STD_BTN | WDF_UNCLICK_BUTTONS, _heightmap_load_widgets, - GenerateLandscapeWndProc, + NULL, }; static void _ShowGenerateLandscape(glwp_modes mode) @@ -577,14 +580,12 @@ static void _ShowGenerateLandscape(glwp_ if (!GetHeightmapDimensions(_file_to_saveload.name, &x, &y)) return; } - Window *w = AllocateWindowDescFront((mode == GLWP_HEIGHTMAP) ? &_heightmap_load_desc : &_generate_landscape_desc, mode); - - if (w == NULL) return; + GenerateLandscapeWindow *w = AllocateWindowDescFront((mode == GLWP_HEIGHTMAP) ? &_heightmap_load_desc : &_generate_landscape_desc, mode); if (mode == GLWP_HEIGHTMAP) { - WP(w, generate_d).x = x; - WP(w, generate_d).y = y; - strecpy(WP(w, generate_d).name, _file_to_saveload.title, lastof(WP(w, generate_d).name)); + w->x = x; + w->y = y; + strecpy(w->name, _file_to_saveload.title, lastof(w->name)); } InvalidateWindow(WC_GENERATE_LANDSCAPE, mode); @@ -639,127 +640,131 @@ enum CreateScenarioWindowWidgets { }; -static void CreateScenarioWndProc(Window *w, WindowEvent *e) +struct CreateScenarioWindow : public Window { - switch (e->event) { - case WE_CREATE: - w->LowerWidget(_opt_newgame.landscape + CSCEN_TEMPERATE); - break; + uint widget_id; + + CreateScenarioWindow(const WindowDesc *desc, WindowNumber window_number) : Window(desc, NULL, window_number) + { + this->LowerWidget(_opt_newgame.landscape + CSCEN_TEMPERATE); + } - case WE_PAINT: - w->SetWidgetDisabledState(CSCEN_START_DATE_DOWN, _patches_newgame.starting_year <= MIN_YEAR); - w->SetWidgetDisabledState(CSCEN_START_DATE_UP, _patches_newgame.starting_year >= MAX_YEAR); - w->SetWidgetDisabledState(CSCEN_FLAT_LAND_HEIGHT_DOWN, _patches_newgame.se_flat_world_height <= 0); - w->SetWidgetDisabledState(CSCEN_FLAT_LAND_HEIGHT_UP, _patches_newgame.se_flat_world_height >= MAX_TILE_HEIGHT); + virtual void OnPaint() + { + this->SetWidgetDisabledState(CSCEN_START_DATE_DOWN, _patches_newgame.starting_year <= MIN_YEAR); + this->SetWidgetDisabledState(CSCEN_START_DATE_UP, _patches_newgame.starting_year >= MAX_YEAR); + this->SetWidgetDisabledState(CSCEN_FLAT_LAND_HEIGHT_DOWN, _patches_newgame.se_flat_world_height <= 0); + this->SetWidgetDisabledState(CSCEN_FLAT_LAND_HEIGHT_UP, _patches_newgame.se_flat_world_height >= MAX_TILE_HEIGHT); + + this->SetWidgetLoweredState(CSCEN_TEMPERATE, _opt_newgame.landscape == LT_TEMPERATE); + this->SetWidgetLoweredState(CSCEN_ARCTIC, _opt_newgame.landscape == LT_ARCTIC); + this->SetWidgetLoweredState(CSCEN_TROPICAL, _opt_newgame.landscape == LT_TROPIC); + this->SetWidgetLoweredState(CSCEN_TOYLAND, _opt_newgame.landscape == LT_TOYLAND); + + /* Set parameters for widget text that requires them */ + SetDParam(0, ConvertYMDToDate(_patches_newgame.starting_year, 0, 1)); // CSCEN_START_DATE_TEXT + SetDParam(1, 1 << _patches_newgame.map_x); // CSCEN_MAPSIZE_X_PULLDOWN + SetDParam(2, 1 << _patches_newgame.map_y); // CSCEN_MAPSIZE_Y_PULLDOWN + SetDParam(3, _patches_newgame.se_flat_world_height); // CSCEN_FLAT_LAND_HEIGHT_TEXT - w->SetWidgetLoweredState(CSCEN_TEMPERATE, _opt_newgame.landscape == LT_TEMPERATE); - w->SetWidgetLoweredState(CSCEN_ARCTIC, _opt_newgame.landscape == LT_ARCTIC); - w->SetWidgetLoweredState(CSCEN_TROPICAL, _opt_newgame.landscape == LT_TROPIC); - w->SetWidgetLoweredState(CSCEN_TOYLAND, _opt_newgame.landscape == LT_TOYLAND); + DrawWindowWidgets(this); + } + + virtual void OnClick(Point pt, int widget) + { + switch (widget) { + case CSCEN_TEMPERATE: + case CSCEN_ARCTIC: + case CSCEN_TROPICAL: + case CSCEN_TOYLAND: + this->RaiseWidget(_opt_newgame.landscape + CSCEN_TEMPERATE); + SetNewLandscapeType(widget - CSCEN_TEMPERATE); + break; - /* Set parameters for widget text that requires them */ - SetDParam(0, ConvertYMDToDate(_patches_newgame.starting_year, 0, 1)); // CSCEN_START_DATE_TEXT - SetDParam(1, 1 << _patches_newgame.map_x); // CSCEN_MAPSIZE_X_PULLDOWN - SetDParam(2, 1 << _patches_newgame.map_y); // CSCEN_MAPSIZE_Y_PULLDOWN - SetDParam(3, _patches_newgame.se_flat_world_height); // CSCEN_FLAT_LAND_HEIGHT_TEXT + case CSCEN_MAPSIZE_X_PULLDOWN: // Mapsize X + ShowDropDownList(this, BuildMapsizeDropDown(), _patches_newgame.map_x, CSCEN_MAPSIZE_X_PULLDOWN); + break; + + case CSCEN_MAPSIZE_Y_PULLDOWN: // Mapsize Y + ShowDropDownList(this, BuildMapsizeDropDown(), _patches_newgame.map_y, CSCEN_MAPSIZE_Y_PULLDOWN); + break; - DrawWindowWidgets(w); + case CSCEN_EMPTY_WORLD: // Empty world / flat world + StartGeneratingLandscape(GLWP_SCENARIO); + break; + + case CSCEN_RANDOM_WORLD: // Generate + ShowGenerateLandscape(); + break; - break; + case CSCEN_START_DATE_DOWN: + case CSCEN_START_DATE_UP: // Year buttons + /* Don't allow too fast scrolling */ + if ((this->flags4 & WF_TIMEOUT_MASK) <= 2 << WF_TIMEOUT_SHL) { + this->HandleButtonClick(widget); + this->SetDirty(); + + _patches_newgame.starting_year = Clamp(_patches_newgame.starting_year + widget - CSCEN_START_DATE_TEXT, MIN_YEAR, MAX_YEAR); + } + _left_button_clicked = false; + break; - case WE_CLICK: - switch (e->we.click.widget) { - case CSCEN_TEMPERATE: - case CSCEN_ARCTIC: - case CSCEN_TROPICAL: - case CSCEN_TOYLAND: - w->RaiseWidget(_opt_newgame.landscape + CSCEN_TEMPERATE); - SetNewLandscapeType(e->we.click.widget - CSCEN_TEMPERATE); - break; + case CSCEN_START_DATE_TEXT: // Year text + this->widget_id = CSCEN_START_DATE_TEXT; + SetDParam(0, _patches_newgame.starting_year); + ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_START_DATE_QUERY_CAPT, 8, 100, this, CS_NUMERAL); + break; + + case CSCEN_FLAT_LAND_HEIGHT_DOWN: + case CSCEN_FLAT_LAND_HEIGHT_UP: // Height level buttons + /* Don't allow too fast scrolling */ + if ((this->flags4 & WF_TIMEOUT_MASK) <= 2 << WF_TIMEOUT_SHL) { + this->HandleButtonClick(widget); + this->SetDirty(); - case CSCEN_MAPSIZE_X_PULLDOWN: // Mapsize X - ShowDropDownList(w, BuildMapsizeDropDown(), _patches_newgame.map_x, CSCEN_MAPSIZE_X_PULLDOWN); - break; + _patches_newgame.se_flat_world_height = Clamp(_patches_newgame.se_flat_world_height + widget - CSCEN_FLAT_LAND_HEIGHT_TEXT, 0, MAX_TILE_HEIGHT); + } + _left_button_clicked = false; + break; + + case CSCEN_FLAT_LAND_HEIGHT_TEXT: // Height level text + this->widget_id = CSCEN_FLAT_LAND_HEIGHT_TEXT; + SetDParam(0, _patches_newgame.se_flat_world_height); + ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_FLAT_WORLD_HEIGHT_QUERY_CAPT, 3, 100, this, CS_NUMERAL); + break; + } + } - case CSCEN_MAPSIZE_Y_PULLDOWN: // Mapsize Y - ShowDropDownList(w, BuildMapsizeDropDown(), _patches_newgame.map_y, CSCEN_MAPSIZE_Y_PULLDOWN); - break; + virtual void OnDropdownSelect(int widget, int index) + { + switch (widget) { + case CSCEN_MAPSIZE_X_PULLDOWN: _patches_newgame.map_x = index; break; + case CSCEN_MAPSIZE_Y_PULLDOWN: _patches_newgame.map_y = index; break; + } + this->SetDirty(); + } - case CSCEN_EMPTY_WORLD: // Empty world / flat world - StartGeneratingLandscape(GLWP_SCENARIO); - break; + virtual void OnQueryTextFinished(char *str) + { + if (!StrEmpty(str)) { + int32 value = atoi(str); - case CSCEN_RANDOM_WORLD: // Generate - ShowGenerateLandscape(); + switch (this->widget_id) { + case CSCEN_START_DATE_TEXT: + this->InvalidateWidget(CSCEN_START_DATE_TEXT); + _patches_newgame.starting_year = Clamp(value, MIN_YEAR, MAX_YEAR); break; - case CSCEN_START_DATE_DOWN: - case CSCEN_START_DATE_UP: // Year buttons - /* Don't allow too fast scrolling */ - if ((w->flags4 & WF_TIMEOUT_MASK) <= 2 << WF_TIMEOUT_SHL) { - w->HandleButtonClick(e->we.click.widget); - w->SetDirty(); - - _patches_newgame.starting_year = Clamp(_patches_newgame.starting_year + e->we.click.widget - CSCEN_START_DATE_TEXT, MIN_YEAR, MAX_YEAR); - } - _left_button_clicked = false; - break; - - case CSCEN_START_DATE_TEXT: // Year text - WP(w, generate_d).widget_id = CSCEN_START_DATE_TEXT; - SetDParam(0, _patches_newgame.starting_year); - ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_START_DATE_QUERY_CAPT, 8, 100, w, CS_NUMERAL); - break; - - case CSCEN_FLAT_LAND_HEIGHT_DOWN: - case CSCEN_FLAT_LAND_HEIGHT_UP: // Height level buttons - /* Don't allow too fast scrolling */ - if ((w->flags4 & WF_TIMEOUT_MASK) <= 2 << WF_TIMEOUT_SHL) { - w->HandleButtonClick(e->we.click.widget); - w->SetDirty(); - - _patches_newgame.se_flat_world_height = Clamp(_patches_newgame.se_flat_world_height + e->we.click.widget - CSCEN_FLAT_LAND_HEIGHT_TEXT, 0, MAX_TILE_HEIGHT); - } - _left_button_clicked = false; - break; - - case CSCEN_FLAT_LAND_HEIGHT_TEXT: // Height level text - WP(w, generate_d).widget_id = CSCEN_FLAT_LAND_HEIGHT_TEXT; - SetDParam(0, _patches_newgame.se_flat_world_height); - ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_FLAT_WORLD_HEIGHT_QUERY_CAPT, 3, 100, w, CS_NUMERAL); + case CSCEN_FLAT_LAND_HEIGHT_TEXT: + this->InvalidateWidget(CSCEN_FLAT_LAND_HEIGHT_TEXT); + _patches_newgame.se_flat_world_height = Clamp(value, 0, MAX_TILE_HEIGHT); break; } - break; - case WE_DROPDOWN_SELECT: - switch (e->we.dropdown.button) { - case CSCEN_MAPSIZE_X_PULLDOWN: _patches_newgame.map_x = e->we.dropdown.index; break; - case CSCEN_MAPSIZE_Y_PULLDOWN: _patches_newgame.map_y = e->we.dropdown.index; break; - } - w->SetDirty(); - break; - - case WE_ON_EDIT_TEXT: - if (!StrEmpty(e->we.edittext.str)) { - int32 value = atoi(e->we.edittext.str); - - switch (WP(w, generate_d).widget_id) { - case CSCEN_START_DATE_TEXT: - w->InvalidateWidget(CSCEN_START_DATE_TEXT); - _patches_newgame.starting_year = Clamp(value, MIN_YEAR, MAX_YEAR); - break; - - case CSCEN_FLAT_LAND_HEIGHT_TEXT: - w->InvalidateWidget(CSCEN_FLAT_LAND_HEIGHT_TEXT); - _patches_newgame.se_flat_world_height = Clamp(value, 0, MAX_TILE_HEIGHT); - break; - } - - w->SetDirty(); - } - break; + this->SetDirty(); + } } -} +}; static const Widget _create_scenario_widgets[] = { { WWT_CLOSEBOX, RESIZE_NONE, 13, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, @@ -796,13 +801,13 @@ static const WindowDesc _create_scenario WC_GENERATE_LANDSCAPE, WC_NONE, WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_STD_BTN | WDF_UNCLICK_BUTTONS, _create_scenario_widgets, - CreateScenarioWndProc, + NULL, }; void ShowCreateScenario() { DeleteWindowByClass(WC_GENERATE_LANDSCAPE); - AllocateWindowDescFront(&_create_scenario_desc, GLWP_SCENARIO); + new CreateScenarioWindow(&_create_scenario_desc, GLWP_SCENARIO); } diff --git a/src/misc_gui.cpp b/src/misc_gui.cpp --- a/src/misc_gui.cpp +++ b/src/misc_gui.cpp @@ -44,6 +44,7 @@ #include "newgrf_cargo.h" #include "rail_gui.h" #include "tilehighlight_func.h" +#include "querystring_gui.h" #include "table/sprites.h" #include "table/strings.h" @@ -878,43 +879,6 @@ void UpdateTextBufferSize(Textbuf *tb) tb->caretxoffs = tb->width; } -int HandleEditBoxKey(Window *w, querystr_d *string, int wid, WindowEvent *e) -{ - e->we.keypress.cont = false; - - switch (e->we.keypress.keycode) { - case WKC_ESC: return 2; - - case WKC_RETURN: case WKC_NUM_ENTER: return 1; - - case (WKC_CTRL | 'V'): - if (InsertTextBufferClipboard(&string->text)) w->InvalidateWidget(wid); - break; - - case (WKC_CTRL | 'U'): - DeleteTextBufferAll(&string->text); - w->InvalidateWidget(wid); - break; - - case WKC_BACKSPACE: case WKC_DELETE: - if (DeleteTextBufferChar(&string->text, e->we.keypress.keycode)) w->InvalidateWidget(wid); - break; - - case WKC_LEFT: case WKC_RIGHT: case WKC_END: case WKC_HOME: - if (MoveTextBufferPos(&string->text, e->we.keypress.keycode)) w->InvalidateWidget(wid); - break; - - default: - if (IsValidChar(e->we.keypress.key, string->afilter)) { - if (InsertTextBufferChar(&string->text, e->we.keypress.key)) w->InvalidateWidget(wid); - } else { // key wasn't caught. Continue only if standard entry specified - e->we.keypress.cont = (string->afilter == CS_ALPHANUMERAL); - } - } - - return 0; -} - bool HandleCaret(Textbuf *tb) { /* caret changed? */ @@ -927,12 +891,49 @@ bool HandleCaret(Textbuf *tb) return false; } -void HandleEditBox(Window *w, querystr_d *string, int wid) +int QueryString::HandleEditBoxKey(Window *w, int wid, uint16 key, uint16 keycode, bool &cont) { - if (HandleCaret(&string->text)) w->InvalidateWidget(wid); + cont = false; + + switch (keycode) { + case WKC_ESC: return 2; + + case WKC_RETURN: case WKC_NUM_ENTER: return 1; + + case (WKC_CTRL | 'V'): + if (InsertTextBufferClipboard(&this->text)) w->InvalidateWidget(wid); + break; + + case (WKC_CTRL | 'U'): + DeleteTextBufferAll(&this->text); + w->InvalidateWidget(wid); + break; + + case WKC_BACKSPACE: case WKC_DELETE: + if (DeleteTextBufferChar(&this->text, keycode)) w->InvalidateWidget(wid); + break; + + case WKC_LEFT: case WKC_RIGHT: case WKC_END: case WKC_HOME: + if (MoveTextBufferPos(&this->text, keycode)) w->InvalidateWidget(wid); + break; + + default: + if (IsValidChar(key, this->afilter)) { + if (InsertTextBufferChar(&this->text, key)) w->InvalidateWidget(wid); + } else { // key wasn't caught. Continue only if standard entry specified + cont = (this->afilter == CS_ALPHANUMERAL); + } + } + + return 0; } -void DrawEditBox(Window *w, querystr_d *string, int wid) +void QueryString::HandleEditBox(Window *w, int wid) +{ + if (HandleCaret(&this->text)) w->InvalidateWidget(wid); +} + +void QueryString::DrawEditBox(Window *w, int wid) { const Widget *wi = &w->widget[wid]; @@ -957,7 +958,7 @@ void DrawEditBox(Window *w, querystr_d * /* We will take the current widget length as maximum width, with a small * space reserved at the end for the caret to show */ - const Textbuf *tb = &string->text; + const Textbuf *tb = &this->text; delta = (wi->right - wi->left) - tb->width - 10; if (delta > 0) delta = 0; @@ -970,6 +971,21 @@ void DrawEditBox(Window *w, querystr_d * _cur_dpi = old_dpi; } +int QueryStringBaseWindow::HandleEditBoxKey(int wid, uint16 key, uint16 keycode, bool &cont) +{ + return this->QueryString::HandleEditBoxKey(this, wid, key, keycode, cont); +} + +void QueryStringBaseWindow::HandleEditBox(int wid) +{ + this->QueryString::HandleEditBox(this, wid); +} + +void QueryStringBaseWindow::DrawEditBox(int wid) +{ + this->QueryString::DrawEditBox(this, wid); +} + enum QueryStringWidgets { QUERY_STR_WIDGET_TEXT = 3, QUERY_STR_WIDGET_CANCEL, @@ -977,69 +993,79 @@ enum QueryStringWidgets { }; -static void QueryStringWndProc(Window *w, WindowEvent *e) +struct QueryStringWindow : public QueryStringBaseWindow { - querystr_d *qs = &WP(w, querystr_d); + Window *parent; + + QueryStringWindow(const WindowDesc *desc, Window *parent) : QueryStringBaseWindow(desc), parent(parent) + { + SetBit(_no_scroll, SCROLL_EDIT); - switch (e->event) { - case WE_CREATE: - SetBit(_no_scroll, SCROLL_EDIT); - break; + this->FindWindowPlacementAndResize(desc); + } - case WE_PAINT: - SetDParam(0, qs->caption); - DrawWindowWidgets(w); + virtual void OnPaint() + { + SetDParam(0, this->caption); + DrawWindowWidgets(this); - DrawEditBox(w, qs, QUERY_STR_WIDGET_TEXT); - break; + this->DrawEditBox(QUERY_STR_WIDGET_TEXT); + } - case WE_CLICK: - switch (e->we.click.widget) { - case QUERY_STR_WIDGET_TEXT: - ShowOnScreenKeyboard(w, &WP(w, querystr_d), QUERY_STR_WIDGET_TEXT, QUERY_STR_WIDGET_CANCEL, QUERY_STR_WIDGET_OK); - break; - - case QUERY_STR_WIDGET_OK: - press_ok:; - if (qs->orig == NULL || strcmp(qs->text.buf, qs->orig) != 0) { - Window *parent = w->parent; - qs->handled = true; + void OnOk() + { + if (this->orig == NULL || strcmp(this->text.buf, this->orig) != 0) { + /* If the parent is NULL, the editbox is handled by general function + * HandleOnEditText */ + if (this->parent != NULL) { + this->parent->OnQueryTextFinished(this->text.buf); + } else { + HandleOnEditText(this->text.buf); + } + } + } - /* If the parent is NULL, the editbox is handled by general function - * HandleOnEditText */ - if (parent != NULL) { - parent->OnQueryTextFinished(qs->text.buf); - } else { - HandleOnEditText(qs->text.buf); - } - } - /* Fallthrough */ - case QUERY_STR_WIDGET_CANCEL: - delete w; - break; - } - break; + virtual void OnClick(Point pt, int widget) + { + switch (widget) { + case QUERY_STR_WIDGET_TEXT: + ShowOnScreenKeyboard(this, QUERY_STR_WIDGET_TEXT, QUERY_STR_WIDGET_CANCEL, QUERY_STR_WIDGET_OK); + break; + + case QUERY_STR_WIDGET_OK: + this->OnOk(); + /* Fallthrough */ + case QUERY_STR_WIDGET_CANCEL: + delete this; + break; + } + } + + virtual void OnMouseLoop() + { + this->HandleEditBox(QUERY_STR_WIDGET_TEXT); + } - case WE_MOUSELOOP: - HandleEditBox(w, qs, QUERY_STR_WIDGET_TEXT); - break; + virtual bool OnKeyPress(uint16 key, uint16 keycode) + { + bool cont; + switch (this->HandleEditBoxKey(QUERY_STR_WIDGET_TEXT, key, keycode, cont)) { + case 1: this->OnOk(); // Enter pressed, confirms change + /* FALL THROUGH */ + case 2: delete this; break; // ESC pressed, closes window, abandons changes + } + return cont; + } - case WE_KEYPRESS: - switch (HandleEditBoxKey(w, qs, QUERY_STR_WIDGET_TEXT, e)) { - case 1: goto press_ok; // Enter pressed, confirms change - case 2: delete w; break; // ESC pressed, closes window, abandons changes - } - break; - - case WE_DESTROY: // Call cancellation of query, if we have not handled it before - if (!qs->handled && w->parent != NULL) { - qs->handled = true; - w->parent->OnQueryTextFinished(NULL); - } - ClrBit(_no_scroll, SCROLL_EDIT); - break; + ~QueryStringWindow() + { + if (!this->handled && this->parent != NULL) { + this->handled = true; + this->parent->OnQueryTextFinished(NULL); + } + ClrBit(_no_scroll, SCROLL_EDIT); } -} +}; static const Widget _query_string_widgets[] = { { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, @@ -1056,12 +1082,9 @@ static const WindowDesc _query_string_de WC_QUERY_STRING, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET, _query_string_widgets, - QueryStringWndProc + NULL }; -char _edit_str_buf[64]; -char _orig_str_buf[lengthof(_edit_str_buf)]; - /** Show a query popup window with a textbox in it. * @param str StringID for the text shown in the textbox * @param caption StringID of text shown in caption of querywindow @@ -1075,28 +1098,27 @@ void ShowQueryString(StringID str, Strin { uint realmaxlen = maxlen & ~0x1000; - assert(realmaxlen < lengthof(_edit_str_buf)); - DeleteWindowById(WC_QUERY_STRING, 0); DeleteWindowById(WC_SAVELOAD, 0); - Window *w = new Window(&_query_string_desc); - w->parent = parent; + QueryStringWindow *w = new QueryStringWindow(&_query_string_desc, parent); - GetString(_edit_str_buf, str, lastof(_edit_str_buf)); - _edit_str_buf[realmaxlen - 1] = '\0'; + assert(realmaxlen < lengthof(w->edit_str_buf)); + + GetString(w->edit_str_buf, str, lastof(w->edit_str_buf)); + w->edit_str_buf[realmaxlen - 1] = '\0'; if (maxlen & 0x1000) { - WP(w, querystr_d).orig = NULL; + w->orig = NULL; } else { - strecpy(_orig_str_buf, _edit_str_buf, lastof(_orig_str_buf)); - WP(w, querystr_d).orig = _orig_str_buf; + strecpy(w->orig_str_buf, w->edit_str_buf, lastof(w->orig_str_buf)); + w->orig = w->orig_str_buf; } w->LowerWidget(QUERY_STR_WIDGET_TEXT); - WP(w, querystr_d).caption = caption; - WP(w, querystr_d).afilter = afilter; - InitializeTextBuffer(&WP(w, querystr_d).text, _edit_str_buf, realmaxlen, maxwidth); + w->caption = caption; + w->afilter = afilter; + InitializeTextBuffer(&w->text, w->edit_str_buf, realmaxlen, maxwidth); } @@ -1306,230 +1328,276 @@ static void MakeSortedSaveGameList() } } -static void GenerateFileName() -{ - /* Check if we are not a spectator who wants to generate a name.. - Let's use the name of player #0 for now. */ - const Player *p = GetPlayer(IsValidPlayer(_local_player) ? _local_player : PLAYER_FIRST); - - SetDParam(0, p->index); - SetDParam(1, _date); - GetString(_edit_str_buf, STR_4004, lastof(_edit_str_buf)); - SanitizeFilename(_edit_str_buf); -} - extern void StartupEngines(); -static void SaveLoadDlgWndProc(Window *w, WindowEvent *e) -{ - static FiosItem o_dir; +struct SaveLoadWindow : public QueryStringBaseWindow { + FiosItem o_dir; + + void GenerateFileName() + { + /* Check if we are not a spectator who wants to generate a name.. + Let's use the name of player #0 for now. */ + const Player *p = GetPlayer(IsValidPlayer(_local_player) ? _local_player : PLAYER_FIRST); - switch (e->event) { - case WE_CREATE: // Set up OPENTTD button - w->vscroll.cap = 10; - w->resize.step_width = 2; - w->resize.step_height = 10; + SetDParam(0, p->index); + SetDParam(1, _date); + GetString(this->edit_str_buf, STR_4004, lastof(this->edit_str_buf)); + SanitizeFilename(this->edit_str_buf); + } + + SaveLoadWindow(const WindowDesc *desc, SaveLoadDialogMode mode) : QueryStringBaseWindow(desc) + { + static const StringID saveload_captions[] = { + STR_4001_LOAD_GAME, + STR_0298_LOAD_SCENARIO, + STR_4000_SAVE_GAME, + STR_0299_SAVE_SCENARIO, + STR_LOAD_HEIGHTMAP, + }; - o_dir.type = FIOS_TYPE_DIRECT; - switch (_saveload_mode) { - case SLD_SAVE_GAME: - case SLD_LOAD_GAME: - FioGetDirectory(o_dir.name, lengthof(o_dir.name), SAVE_DIR); - break; + SetObjectToPlace(SPR_CURSOR_ZZZ, PAL_NONE, VHM_NONE, WC_MAIN_WINDOW, 0); + SetBit(_no_scroll, SCROLL_SAVE); + + /* Use an array to define what will be the current file type being handled + * by current file mode */ + switch (mode) { + case SLD_SAVE_GAME: this->GenerateFileName(); break; + case SLD_SAVE_SCENARIO: strcpy(this->edit_str_buf, "UNNAMED"); break; + default: break; + } + + assert((uint)mode < lengthof(saveload_captions)); - case SLD_SAVE_SCENARIO: - case SLD_LOAD_SCENARIO: - FioGetDirectory(o_dir.name, lengthof(o_dir.name), SCENARIO_DIR); - break; + this->widget[1].data = saveload_captions[mode]; + this->LowerWidget(7); + + this->afilter = CS_ALPHANUMERAL; + InitializeTextBuffer(&this->text, this->edit_str_buf, lengthof(this->edit_str_buf), 240); - case SLD_LOAD_HEIGHTMAP: - FioGetDirectory(o_dir.name, lengthof(o_dir.name), HEIGHTMAP_DIR); - break; + /* pause is only used in single-player, non-editor mode, non-menu mode. It + * will be unpaused in the WE_DESTROY event handler. */ + if (_game_mode != GM_MENU && !_networking && _game_mode != GM_EDITOR) { + if (_pause_game >= 0) DoCommandP(0, 1, 0, NULL, CMD_PAUSE); + } + + BuildFileList(); + + ResetObjectToPlace(); - default: - ttd_strlcpy(o_dir.name, _personal_dir, lengthof(o_dir.name)); - } - break; + o_dir.type = FIOS_TYPE_DIRECT; + switch (_saveload_mode) { + case SLD_SAVE_GAME: + case SLD_LOAD_GAME: + FioGetDirectory(o_dir.name, lengthof(o_dir.name), SAVE_DIR); + break; + + case SLD_SAVE_SCENARIO: + case SLD_LOAD_SCENARIO: + FioGetDirectory(o_dir.name, lengthof(o_dir.name), SCENARIO_DIR); + break; - case WE_PAINT: { - int pos; - int y; + case SLD_LOAD_HEIGHTMAP: + FioGetDirectory(o_dir.name, lengthof(o_dir.name), HEIGHTMAP_DIR); + break; - SetVScrollCount(w, _fios_num); - DrawWindowWidgets(w); - DrawFiosTexts(w->width); + default: + ttd_strlcpy(o_dir.name, _personal_dir, lengthof(o_dir.name)); + } + + this->vscroll.cap = 10; + this->resize.step_width = 2; + this->resize.step_height = 10; + + this->FindWindowPlacementAndResize(desc); + } - if (_savegame_sort_dirty) { - _savegame_sort_dirty = false; - MakeSortedSaveGameList(); - } + virtual ~SaveLoadWindow() + { + /* pause is only used in single-player, non-editor mode, non menu mode */ + if (!_networking && _game_mode != GM_EDITOR && _game_mode != GM_MENU) { + if (_pause_game >= 0) DoCommandP(0, 0, 0, NULL, CMD_PAUSE); + } + FiosFreeSavegameList(); + ClrBit(_no_scroll, SCROLL_SAVE); + } - GfxFillRect(w->widget[7].left + 1, w->widget[7].top + 1, w->widget[7].right, w->widget[7].bottom, 0xD7); - DrawSortButtonState(w, _savegame_sort_order & SORT_BY_NAME ? 2 : 3, _savegame_sort_order & SORT_DESCENDING ? SBS_DOWN : SBS_UP); + virtual void OnPaint() + { + int pos; + int y; - y = w->widget[7].top + 1; - for (pos = w->vscroll.pos; pos < _fios_num; pos++) { - const FiosItem *item = _fios_list + pos; + SetVScrollCount(this, _fios_num); + DrawWindowWidgets(this); + DrawFiosTexts(this->width); + + if (_savegame_sort_dirty) { + _savegame_sort_dirty = false; + MakeSortedSaveGameList(); + } - DoDrawStringTruncated(item->title, 4, y, _fios_colors[item->type], w->width - 18); - y += 10; - if (y >= w->vscroll.cap * 10 + w->widget[7].top + 1) break; - } + GfxFillRect(this->widget[7].left + 1, this->widget[7].top + 1, this->widget[7].right, this->widget[7].bottom, 0xD7); + DrawSortButtonState(this, _savegame_sort_order & SORT_BY_NAME ? 2 : 3, _savegame_sort_order & SORT_DESCENDING ? SBS_DOWN : SBS_UP); - if (_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO) { - DrawEditBox(w, &WP(w, querystr_d), 10); - } - break; + y = this->widget[7].top + 1; + for (pos = this->vscroll.pos; pos < _fios_num; pos++) { + const FiosItem *item = _fios_list + pos; + + DoDrawStringTruncated(item->title, 4, y, _fios_colors[item->type], this->width - 18); + y += 10; + if (y >= this->vscroll.cap * 10 + this->widget[7].top + 1) break; } - case WE_CLICK: - switch (e->we.click.widget) { - case 2: // Sort save names by name - _savegame_sort_order = (_savegame_sort_order == SORT_BY_NAME) ? - SORT_BY_NAME | SORT_DESCENDING : SORT_BY_NAME; - _savegame_sort_dirty = true; - w->SetDirty(); - break; + if (_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO) { + this->DrawEditBox(10); + } + } - case 3: // Sort save names by date - _savegame_sort_order = (_savegame_sort_order == SORT_BY_DATE) ? - SORT_BY_DATE | SORT_DESCENDING : SORT_BY_DATE; - _savegame_sort_dirty = true; - w->SetDirty(); - break; + virtual void OnClick(Point pt, int widget) + { + switch (widget) { + case 2: // Sort save names by name + _savegame_sort_order = (_savegame_sort_order == SORT_BY_NAME) ? + SORT_BY_NAME | SORT_DESCENDING : SORT_BY_NAME; + _savegame_sort_dirty = true; + this->SetDirty(); + break; - case 6: // OpenTTD 'button', jumps to OpenTTD directory - FiosBrowseTo(&o_dir); - w->SetDirty(); - BuildFileList(); - break; - - case 7: { // Click the listbox - int y = (e->we.click.pt.y - w->widget[e->we.click.widget].top - 1) / 10; - char *name; - const FiosItem *file; + case 3: // Sort save names by date + _savegame_sort_order = (_savegame_sort_order == SORT_BY_DATE) ? + SORT_BY_DATE | SORT_DESCENDING : SORT_BY_DATE; + _savegame_sort_dirty = true; + this->SetDirty(); + break; - if (y < 0 || (y += w->vscroll.pos) >= w->vscroll.count) return; - - file = _fios_list + y; + case 6: // OpenTTD 'button', jumps to OpenTTD directory + FiosBrowseTo(&o_dir); + this->SetDirty(); + BuildFileList(); + break; - name = FiosBrowseTo(file); - if (name != NULL) { - if (_saveload_mode == SLD_LOAD_GAME || _saveload_mode == SLD_LOAD_SCENARIO) { - _switch_mode = (_game_mode == GM_EDITOR) ? SM_LOAD_SCENARIO : SM_LOAD; - - SetFiosType(file->type); - ttd_strlcpy(_file_to_saveload.name, name, sizeof(_file_to_saveload.name)); - ttd_strlcpy(_file_to_saveload.title, file->title, sizeof(_file_to_saveload.title)); + case 7: { // Click the listbox + int y = (pt.y - this->widget[widget].top - 1) / 10; + char *name; + const FiosItem *file; - delete w; - } else if (_saveload_mode == SLD_LOAD_HEIGHTMAP) { - SetFiosType(file->type); - ttd_strlcpy(_file_to_saveload.name, name, sizeof(_file_to_saveload.name)); - ttd_strlcpy(_file_to_saveload.title, file->title, sizeof(_file_to_saveload.title)); + if (y < 0 || (y += this->vscroll.pos) >= this->vscroll.count) return; + + file = _fios_list + y; + + name = FiosBrowseTo(file); + if (name != NULL) { + if (_saveload_mode == SLD_LOAD_GAME || _saveload_mode == SLD_LOAD_SCENARIO) { + _switch_mode = (_game_mode == GM_EDITOR) ? SM_LOAD_SCENARIO : SM_LOAD; + + SetFiosType(file->type); + ttd_strlcpy(_file_to_saveload.name, name, sizeof(_file_to_saveload.name)); + ttd_strlcpy(_file_to_saveload.title, file->title, sizeof(_file_to_saveload.title)); - delete w; - ShowHeightmapLoad(); - } else { - /* SLD_SAVE_GAME, SLD_SAVE_SCENARIO copy clicked name to editbox */ - ttd_strlcpy(WP(w, querystr_d).text.buf, file->title, WP(w, querystr_d).text.maxlength); - UpdateTextBufferSize(&WP(w, querystr_d).text); - w->InvalidateWidget(10); - } + delete this; + } else if (_saveload_mode == SLD_LOAD_HEIGHTMAP) { + SetFiosType(file->type); + ttd_strlcpy(_file_to_saveload.name, name, sizeof(_file_to_saveload.name)); + ttd_strlcpy(_file_to_saveload.title, file->title, sizeof(_file_to_saveload.title)); + + delete this; + ShowHeightmapLoad(); } else { - /* Changed directory, need repaint. */ - w->SetDirty(); - BuildFileList(); + /* SLD_SAVE_GAME, SLD_SAVE_SCENARIO copy clicked name to editbox */ + ttd_strlcpy(this->text.buf, file->title, this->text.maxlength); + UpdateTextBufferSize(&this->text); + this->InvalidateWidget(10); } - break; + } else { + /* Changed directory, need repaint. */ + this->SetDirty(); + BuildFileList(); } - - case 10: // edit box - ShowOnScreenKeyboard(w, &WP(w, querystr_d), e->we.click.widget, 0, 0); - break; - - case 11: case 12: // Delete, Save game - break; - } - break; - - case WE_MOUSELOOP: - if (_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO) { - HandleEditBox(w, &WP(w, querystr_d), 10); - } - break; - - case WE_KEYPRESS: - if (e->we.keypress.keycode == WKC_ESC) { - delete w; - return; + break; } - if ((_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO) && - HandleEditBoxKey(w, &WP(w, querystr_d), 10, e) == 1) { // Press Enter - w->HandleButtonClick(12); - } - break; + case 10: // edit box + ShowOnScreenKeyboard(this, widget, 0, 0); + break; - case WE_TIMEOUT: - /* This test protects against using widgets 11 and 12 which are only available - * in those two saveload mode */ - if (!(_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO)) break; + case 11: case 12: // Delete, Save game + break; + } + } - if (w->IsWidgetLowered(11)) { // Delete button clicked - if (!FiosDelete(WP(w, querystr_d).text.buf)) { - ShowErrorMessage(INVALID_STRING_ID, STR_4008_UNABLE_TO_DELETE_FILE, 0, 0); - } else { - BuildFileList(); - /* Reset file name to current date on successful delete */ - if (_saveload_mode == SLD_SAVE_GAME) GenerateFileName(); - } + virtual void OnMouseLoop() + { + if (_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO) { + this->HandleEditBox(10); + } + } - UpdateTextBufferSize(&WP(w, querystr_d).text); - w->SetDirty(); - } else if (w->IsWidgetLowered(12)) { // Save button clicked - _switch_mode = SM_SAVE; - FiosMakeSavegameName(_file_to_saveload.name, WP(w, querystr_d).text.buf, sizeof(_file_to_saveload.name)); + virtual bool OnKeyPress(uint16 key, uint16 keycode) + { + if (keycode == WKC_ESC) { + delete this; + return false; + } - /* In the editor set up the vehicle engines correctly (date might have changed) */ - if (_game_mode == GM_EDITOR) StartupEngines(); - } - break; + bool cont = true; + if ((_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO) && + this->HandleEditBoxKey(10, key, keycode, cont) == 1) { // Press Enter + this->HandleButtonClick(12); + } + + return cont; + } - case WE_DESTROY: - /* pause is only used in single-player, non-editor mode, non menu mode */ - if (!_networking && _game_mode != GM_EDITOR && _game_mode != GM_MENU) { - if (_pause_game >= 0) DoCommandP(0, 0, 0, NULL, CMD_PAUSE); - } - FiosFreeSavegameList(); - ClrBit(_no_scroll, SCROLL_SAVE); - break; + virtual void OnTimeout() + { + /* This test protects against using widgets 11 and 12 which are only available + * in those two saveload mode */ + if (!(_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO)) return; - case WE_RESIZE: { - /* Widget 2 and 3 have to go with halve speed, make it so obiwan */ - uint diff = e->we.sizing.diff.x / 2; - w->widget[2].right += diff; - w->widget[3].left += diff; - w->widget[3].right += e->we.sizing.diff.x; - - /* Same for widget 11 and 12 in save-dialog */ - if (_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO) { - w->widget[11].right += diff; - w->widget[12].left += diff; - w->widget[12].right += e->we.sizing.diff.x; + if (this->IsWidgetLowered(11)) { // Delete button clicked + if (!FiosDelete(this->text.buf)) { + ShowErrorMessage(INVALID_STRING_ID, STR_4008_UNABLE_TO_DELETE_FILE, 0, 0); + } else { + BuildFileList(); + /* Reset file name to current date on successful delete */ + if (_saveload_mode == SLD_SAVE_GAME) GenerateFileName(); } - w->vscroll.cap += e->we.sizing.diff.y / 10; - } break; + UpdateTextBufferSize(&this->text); + this->SetDirty(); + } else if (this->IsWidgetLowered(12)) { // Save button clicked + _switch_mode = SM_SAVE; + FiosMakeSavegameName(_file_to_saveload.name, this->text.buf, sizeof(_file_to_saveload.name)); + + /* In the editor set up the vehicle engines correctly (date might have changed) */ + if (_game_mode == GM_EDITOR) StartupEngines(); + } } -} + + virtual void OnResize(Point new_size, Point delta) + { + /* Widget 2 and 3 have to go with halve speed, make it so obiwan */ + uint diff = delta.x / 2; + this->widget[2].right += diff; + this->widget[3].left += diff; + this->widget[3].right += delta.x; + + /* Same for widget 11 and 12 in save-dialog */ + if (_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO) { + this->widget[11].right += diff; + this->widget[12].left += diff; + this->widget[12].right += delta.x; + } + + this->vscroll.cap += delta.y / 10; + } +}; static const WindowDesc _load_dialog_desc = { WDP_CENTER, WDP_CENTER, 257, 154, 257, 294, WC_SAVELOAD, WC_NONE, WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_STD_BTN | WDF_UNCLICK_BUTTONS | WDF_RESIZABLE, _load_dialog_widgets, - SaveLoadDlgWndProc, + NULL, }; static const WindowDesc _save_dialog_desc = { @@ -1537,7 +1605,7 @@ static const WindowDesc _save_dialog_des WC_SAVELOAD, WC_NONE, WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_STD_BTN | WDF_UNCLICK_BUTTONS | WDF_RESIZABLE, _save_dialog_widgets, - SaveLoadDlgWndProc, + NULL, }; /** These values are used to convert the file/operations mode into a corresponding file type. @@ -1553,50 +1621,22 @@ static const FileType _file_modetotype[] void ShowSaveLoadDialog(SaveLoadDialogMode mode) { - static const StringID saveload_captions[] = { - STR_4001_LOAD_GAME, - STR_0298_LOAD_SCENARIO, - STR_4000_SAVE_GAME, - STR_0299_SAVE_SCENARIO, - STR_LOAD_HEIGHTMAP, - }; - - const WindowDesc *sld = &_save_dialog_desc; - - SetObjectToPlace(SPR_CURSOR_ZZZ, PAL_NONE, VHM_NONE, WC_MAIN_WINDOW, 0); DeleteWindowById(WC_QUERY_STRING, 0); DeleteWindowById(WC_SAVELOAD, 0); - _saveload_mode = mode; - SetBit(_no_scroll, SCROLL_SAVE); - - /* Use an array to define what will be the current file type being handled - * by current file mode */ - _file_to_saveload.filetype = _file_modetotype[mode]; + const WindowDesc *sld; switch (mode) { - case SLD_SAVE_GAME: GenerateFileName(); break; - case SLD_SAVE_SCENARIO: strcpy(_edit_str_buf, "UNNAMED"); break; - default: sld = &_load_dialog_desc; break; + case SLD_SAVE_GAME: + case SLD_SAVE_SCENARIO: + sld = &_save_dialog_desc; break; + default: + sld = &_load_dialog_desc; break; } - assert((uint)mode < lengthof(saveload_captions)); - - Window *w = new Window(sld); - w->widget[1].data = saveload_captions[mode]; - w->LowerWidget(7); - - WP(w, querystr_d).afilter = CS_ALPHANUMERAL; - InitializeTextBuffer(&WP(w, querystr_d).text, _edit_str_buf, lengthof(_edit_str_buf), 240); + _saveload_mode = mode; + _file_to_saveload.filetype = _file_modetotype[mode]; - /* pause is only used in single-player, non-editor mode, non-menu mode. It - * will be unpaused in the WE_DESTROY event handler. */ - if (_game_mode != GM_MENU && !_networking && _game_mode != GM_EDITOR) { - if (_pause_game >= 0) DoCommandP(0, 1, 0, NULL, CMD_PAUSE); - } - - BuildFileList(); - - ResetObjectToPlace(); + new SaveLoadWindow(sld, mode); } void RedrawAutosave() diff --git a/src/network/network_gui.cpp b/src/network/network_gui.cpp --- a/src/network/network_gui.cpp +++ b/src/network/network_gui.cpp @@ -29,6 +29,7 @@ #include "../player_func.h" #include "../settings_type.h" #include "../widgets/dropdown_func.h" +#include "../querystring_gui.h" #include "table/strings.h" #include "../table/sprites.h" @@ -36,15 +37,11 @@ #define BGC 5 #define BTC 15 -struct chatquerystr_d : public querystr_d { - DestType dtype; - int dest; -}; -assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(chatquerystr_d)); - +/* struct network_d { byte field; // select text-field in start-server and game-listing byte widget_id; ///< The widget that has the pop-up input menu + byte field; // select text-field in start-server and game-listing NetworkGameList *server; // selected server in lobby and game-listing FiosItem *map; // selected map in start-server }; @@ -53,15 +50,14 @@ assert_compile(WINDOW_CUSTOM_SIZE >= siz struct network_ql_d { network_d n; // see above; general stuff querystr_d q; // text-input in start-server and game-listing - NetworkGameList **sort_list; // list of games (sorted) list_d l; // accompanying list-administration }; assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(network_ql_d)); +*/ /* Global to remember sorting after window has been closed */ static Listing _ng_sorting; -static char _edit_str_net_buf[150]; static bool _chat_tab_completion_active; static void ShowNetworkStartServerWindow(); @@ -159,64 +155,6 @@ static int CDECL NGameAllowedSorter(cons return _internal_sort_order ? -r : r; } -/** (Re)build the network game list as its amount has changed because - * an item has been added or deleted for example - * @param ngl list_d struct that contains all necessary information for sorting */ -static void BuildNetworkGameList(network_ql_d *nqld) -{ - NetworkGameList *ngl_temp; - uint n = 0; - - if (!(nqld->l.flags & VL_REBUILD)) return; - - /* Count the number of games in the list */ - for (ngl_temp = _network_game_list; ngl_temp != NULL; ngl_temp = ngl_temp->next) n++; - if (n == 0) return; - - /* Create temporary array of games to use for listing */ - free(nqld->sort_list); - nqld->sort_list = MallocT(n); - nqld->l.list_length = n; - - for (n = 0, ngl_temp = _network_game_list; ngl_temp != NULL; ngl_temp = ngl_temp->next) { - nqld->sort_list[n++] = ngl_temp; - } - - /* Force resort */ - nqld->l.flags &= ~VL_REBUILD; - nqld->l.flags |= VL_RESORT; -} - -static void SortNetworkGameList(network_ql_d *nqld) -{ - static NGameNameSortFunction * const ngame_sorter[] = { - &NGameNameSorter, - &NGameClientSorter, - &NGameAllowedSorter - }; - - NetworkGameList *item; - uint i; - - if (!(nqld->l.flags & VL_RESORT)) return; - if (nqld->l.list_length == 0) return; - - _internal_sort_order = !!(nqld->l.flags & VL_DESC); - qsort(nqld->sort_list, nqld->l.list_length, sizeof(nqld->sort_list[0]), ngame_sorter[nqld->l.sort_type]); - - /* After sorting ngl->sort_list contains the sorted items. Put these back - * into the original list. Basically nothing has changed, we are only - * shuffling the ->next pointers */ - _network_game_list = nqld->sort_list[0]; - for (item = _network_game_list, i = 1; i != nqld->l.list_length; i++) { - item->next = nqld->sort_list[i]; - item = item->next; - } - item->next = NULL; - - nqld->l.flags &= ~VL_RESORT; -} - /** Enum for NetworkGameWindow, referring to _network_game_window_widgets */ enum NetworkGameWindowWidgets { NGWW_CLOSE, ///< Close 'X' button @@ -248,366 +186,432 @@ enum NetworkGameWindowWidgets { NGWW_CANCEL, ///< 'Cancel' button }; -/** - * Draw a single server line. - * @param cur_item the server to draw. - * @param y from where to draw? - * @param highlight does the line need to be highlighted? - */ -static void DrawServerLine(const Window *w, const NetworkGameList *cur_item, uint y, bool highlight) -{ - /* show highlighted item with a different colour */ - if (highlight) GfxFillRect(w->widget[NGWW_NAME].left + 1, y - 2, w->widget[NGWW_INFO].right - 1, y + 9, 10); +struct NetworkGameWindow : public QueryStringBaseWindow { + byte field; ///< selected text-field + NetworkGameList *server; ///< selected server + NetworkGameList **sort_list; ///< list of games (sorted) + list_d ld; ///< accompanying list-administration - SetDParamStr(0, cur_item->info.server_name); - DrawStringTruncated(w->widget[NGWW_NAME].left + 5, y, STR_02BD, TC_BLACK, w->widget[NGWW_NAME].right - w->widget[NGWW_NAME].left - 5); + NetworkGameWindow(const WindowDesc *desc) : QueryStringBaseWindow(desc) + { + ttd_strlcpy(this->edit_str_buf, _network_player_name, lengthof(this->edit_str_buf)); + this->afilter = CS_ALPHANUMERAL; + InitializeTextBuffer(&this->text, this->edit_str_buf, lengthof(this->edit_str_buf), 120); + + UpdateNetworkGameWindow(true); - SetDParam(0, cur_item->info.clients_on); - SetDParam(1, cur_item->info.clients_max); - SetDParam(2, cur_item->info.companies_on); - SetDParam(3, cur_item->info.companies_max); - DrawStringCentered(w->widget[NGWW_CLIENTS].left + 39, y, STR_NETWORK_GENERAL_ONLINE, TC_GOLD); + this->vscroll.cap = 11; + this->resize.step_height = NET_PRC__SIZE_OF_ROW; + + this->field = NGWW_PLAYER; + this->server = NULL; - /* only draw icons if the server is online */ - if (cur_item->online) { - /* draw a lock if the server is password protected */ - if (cur_item->info.use_password) DrawSprite(SPR_LOCK, PAL_NONE, w->widget[NGWW_INFO].left + 5, y - 1); + this->sort_list = NULL; + this->ld.flags = VL_REBUILD | (_ng_sorting.order ? VL_DESC : VL_NONE); + this->ld.sort_type = _ng_sorting.criteria; - /* draw red or green icon, depending on compatibility with server */ - DrawSprite(SPR_BLOT, (cur_item->info.compatible ? PALETTE_TO_GREEN : (cur_item->info.version_compatible ? PALETTE_TO_YELLOW : PALETTE_TO_RED)), w->widget[NGWW_INFO].left + 15, y); + this->FindWindowPlacementAndResize(desc); + } - /* draw flag according to server language */ - DrawSprite(SPR_FLAGS_BASE + cur_item->info.server_lang, PAL_NONE, w->widget[NGWW_INFO].left + 25, y); + ~NetworkGameWindow() + { + free(this->sort_list); } -} + + /** + * (Re)build the network game list as its amount has changed because + * an item has been added or deleted for example + */ + void BuildNetworkGameList() + { + NetworkGameList *ngl_temp; + uint n = 0; + + if (!(this->ld.flags & VL_REBUILD)) return; -/** - * Handler of actions done in the NetworkStartServer window - * - * @param w pointer to the Window structure - * @param e pointer to window event - * @note Uses network_ql_d (network_d, querystr_d and list_d) WP macro - * @see struct _network_game_window_widgets - * @see enum NetworkGameWindowWidgets - */ + /* Count the number of games in the list */ + for (ngl_temp = _network_game_list; ngl_temp != NULL; ngl_temp = ngl_temp->next) n++; + if (n == 0) return; + + /* Create temporary array of games to use for listing */ + this->sort_list = ReallocT(this->sort_list, n); + this->ld.list_length = n; -static void NetworkGameWindowWndProc(Window *w, WindowEvent *e) -{ - network_d *nd = &WP(w, network_ql_d).n; - list_d *ld = &WP(w, network_ql_d).l; + for (n = 0, ngl_temp = _network_game_list; ngl_temp != NULL; ngl_temp = ngl_temp->next) { + this->sort_list[n++] = ngl_temp; + } + + /* Force resort */ + this->ld.flags &= ~VL_REBUILD; + this->ld.flags |= VL_RESORT; + } - switch (e->event) { - case WE_CREATE: // Focus input box - w->vscroll.cap = 11; - w->resize.step_height = NET_PRC__SIZE_OF_ROW; + void SortNetworkGameList() + { + static NGameNameSortFunction * const ngame_sorter[] = { + &NGameNameSorter, + &NGameClientSorter, + &NGameAllowedSorter + }; - nd->field = NGWW_PLAYER; - nd->server = NULL; + NetworkGameList *item; + uint i; + + if (!(this->ld.flags & VL_RESORT)) return; + if (this->ld.list_length == 0) return; - WP(w, network_ql_d).sort_list = NULL; - ld->flags = VL_REBUILD | (_ng_sorting.order ? VL_DESC : VL_NONE); - ld->sort_type = _ng_sorting.criteria; - break; + _internal_sort_order = !!(this->ld.flags & VL_DESC); + qsort(this->sort_list, this->ld.list_length, sizeof(this->sort_list[0]), ngame_sorter[this->ld.sort_type]); - case WE_PAINT: { - const NetworkGameList *sel = nd->server; - const SortButtonState arrow = (ld->flags & VL_DESC) ? SBS_DOWN : SBS_UP; + /* After sorting ngl->sort_list contains the sorted items. Put these back + * into the original list. Basically nothing has changed, we are only + * shuffling the ->next pointers */ + _network_game_list = this->sort_list[0]; + for (item = _network_game_list, i = 1; i != this->ld.list_length; i++) { + item->next = this->sort_list[i]; + item = item->next; + } + item->next = NULL; + + this->ld.flags &= ~VL_RESORT; + } - if (ld->flags & VL_REBUILD) { - BuildNetworkGameList(&WP(w, network_ql_d)); - SetVScrollCount(w, ld->list_length); - } - if (ld->flags & VL_RESORT) SortNetworkGameList(&WP(w, network_ql_d)); + /** + * Draw a single server line. + * @param cur_item the server to draw. + * @param y from where to draw? + * @param highlight does the line need to be highlighted? + */ + void DrawServerLine(const NetworkGameList *cur_item, uint y, bool highlight) + { + /* show highlighted item with a different colour */ + if (highlight) GfxFillRect(this->widget[NGWW_NAME].left + 1, y - 2, this->widget[NGWW_INFO].right - 1, y + 9, 10); - /* 'Refresh' button invisible if no server selected */ - w->SetWidgetDisabledState(NGWW_REFRESH, sel == NULL); - /* 'Join' button disabling conditions */ - w->SetWidgetDisabledState(NGWW_JOIN, sel == NULL || // no Selected Server - !sel->online || // Server offline - sel->info.clients_on >= sel->info.clients_max || // Server full - !sel->info.compatible); // Revision mismatch + SetDParamStr(0, cur_item->info.server_name); + DrawStringTruncated(this->widget[NGWW_NAME].left + 5, y, STR_02BD, TC_BLACK, this->widget[NGWW_NAME].right - this->widget[NGWW_NAME].left - 5); + + SetDParam(0, cur_item->info.clients_on); + SetDParam(1, cur_item->info.clients_max); + SetDParam(2, cur_item->info.companies_on); + SetDParam(3, cur_item->info.companies_max); + DrawStringCentered(this->widget[NGWW_CLIENTS].left + 39, y, STR_NETWORK_GENERAL_ONLINE, TC_GOLD); + + /* only draw icons if the server is online */ + if (cur_item->online) { + /* draw a lock if the server is password protected */ + if (cur_item->info.use_password) DrawSprite(SPR_LOCK, PAL_NONE, this->widget[NGWW_INFO].left + 5, y - 1); - /* 'NewGRF Settings' button invisible if no NewGRF is used */ - w->SetWidgetHiddenState(NGWW_NEWGRF, sel == NULL || - !sel->online || - sel->info.grfconfig == NULL); + /* draw red or green icon, depending on compatibility with server */ + DrawSprite(SPR_BLOT, (cur_item->info.compatible ? PALETTE_TO_GREEN : (cur_item->info.version_compatible ? PALETTE_TO_YELLOW : PALETTE_TO_RED)), this->widget[NGWW_INFO].left + 15, y); - SetDParam(0, 0x00); - SetDParam(1, _lan_internet_types_dropdown[_network_lan_internet]); - DrawWindowWidgets(w); + /* draw flag according to server language */ + DrawSprite(SPR_FLAGS_BASE + cur_item->info.server_lang, PAL_NONE, this->widget[NGWW_INFO].left + 25, y); + } + } + + virtual void OnPaint() + { + const NetworkGameList *sel = this->server; + const SortButtonState arrow = (this->ld.flags & VL_DESC) ? SBS_DOWN : SBS_UP; - /* Edit box to set player name */ - DrawEditBox(w, &WP(w, network_ql_d).q, NGWW_PLAYER); - - DrawString(w->widget[NGWW_PLAYER].left - 100, 23, STR_NETWORK_PLAYER_NAME, TC_GOLD); + if (this->ld.flags & VL_REBUILD) { + this->BuildNetworkGameList(); + SetVScrollCount(this, this->ld.list_length); + } + if (this->ld.flags & VL_RESORT) this->SortNetworkGameList(); - /* Sort based on widgets: name, clients, compatibility */ - switch (ld->sort_type) { - case NGWW_NAME - NGWW_NAME: DrawSortButtonState(w, NGWW_NAME, arrow); break; - case NGWW_CLIENTS - NGWW_NAME: DrawSortButtonState(w, NGWW_CLIENTS, arrow); break; - case NGWW_INFO - NGWW_NAME: DrawSortButtonState(w, NGWW_INFO, arrow); break; - } + /* 'Refresh' button invisible if no server selected */ + this->SetWidgetDisabledState(NGWW_REFRESH, sel == NULL); + /* 'Join' button disabling conditions */ + this->SetWidgetDisabledState(NGWW_JOIN, sel == NULL || // no Selected Server + !sel->online || // Server offline + sel->info.clients_on >= sel->info.clients_max || // Server full + !sel->info.compatible); // Revision mismatch - uint16 y = NET_PRC__OFFSET_TOP_WIDGET + 3; - int32 n = 0; - int32 pos = w->vscroll.pos; - const NetworkGameList *cur_item = _network_game_list; + /* 'NewGRF Settings' button invisible if no NewGRF is used */ + this->SetWidgetHiddenState(NGWW_NEWGRF, sel == NULL || + !sel->online || + sel->info.grfconfig == NULL); + + SetDParam(0, 0x00); + SetDParam(1, _lan_internet_types_dropdown[_network_lan_internet]); + DrawWindowWidgets(this); + + /* Edit box to set player name */ + this->DrawEditBox(NGWW_PLAYER); - while (pos > 0 && cur_item != NULL) { - pos--; - cur_item = cur_item->next; - } + DrawString(this->widget[NGWW_PLAYER].left - 100, 23, STR_NETWORK_PLAYER_NAME, TC_GOLD); - while (cur_item != NULL) { - DrawServerLine(w, cur_item, y, cur_item == sel); + /* Sort based on widgets: name, clients, compatibility */ + switch (this->ld.sort_type) { + case NGWW_NAME - NGWW_NAME: DrawSortButtonState(this, NGWW_NAME, arrow); break; + case NGWW_CLIENTS - NGWW_NAME: DrawSortButtonState(this, NGWW_CLIENTS, arrow); break; + case NGWW_INFO - NGWW_NAME: DrawSortButtonState(this, NGWW_INFO, arrow); break; + } - cur_item = cur_item->next; - y += NET_PRC__SIZE_OF_ROW; - if (++n == w->vscroll.cap) break; // max number of games in the window - } + uint16 y = NET_PRC__OFFSET_TOP_WIDGET + 3; + int32 n = 0; + int32 pos = this->vscroll.pos; + const NetworkGameList *cur_item = _network_game_list; - const NetworkGameList *last_joined = NetworkGameListAddItem(inet_addr(_network_last_host), _network_last_port); - /* Draw the last joined server, if any */ - if (last_joined != NULL) DrawServerLine(w, last_joined, y = w->widget[NGWW_LASTJOINED].top + 3, last_joined == sel); + while (pos > 0 && cur_item != NULL) { + pos--; + cur_item = cur_item->next; + } + + while (cur_item != NULL) { + this->DrawServerLine(cur_item, y, cur_item == sel); + + cur_item = cur_item->next; + y += NET_PRC__SIZE_OF_ROW; + if (++n == this->vscroll.cap) break; // max number of games in the window + } - /* Draw the right menu */ - GfxFillRect(w->widget[NGWW_DETAILS].left + 1, 43, w->widget[NGWW_DETAILS].right - 1, 92, 157); - if (sel == NULL) { - DrawStringCentered(w->widget[NGWW_DETAILS].left + 115, 58, STR_NETWORK_GAME_INFO, TC_FROMSTRING); - } else if (!sel->online) { - SetDParamStr(0, sel->info.server_name); - DrawStringCentered(w->widget[NGWW_DETAILS].left + 115, 68, STR_ORANGE, TC_FROMSTRING); // game name + const NetworkGameList *last_joined = NetworkGameListAddItem(inet_addr(_network_last_host), _network_last_port); + /* Draw the last joined server, if any */ + if (last_joined != NULL) this->DrawServerLine(last_joined, y = this->widget[NGWW_LASTJOINED].top + 3, last_joined == sel); - DrawStringCentered(w->widget[NGWW_DETAILS].left + 115, 132, STR_NETWORK_SERVER_OFFLINE, TC_FROMSTRING); // server offline - } else { // show game info - uint16 y = 100; - const uint16 x = w->widget[NGWW_DETAILS].left + 5; + /* Draw the right menu */ + GfxFillRect(this->widget[NGWW_DETAILS].left + 1, 43, this->widget[NGWW_DETAILS].right - 1, 92, 157); + if (sel == NULL) { + DrawStringCentered(this->widget[NGWW_DETAILS].left + 115, 58, STR_NETWORK_GAME_INFO, TC_FROMSTRING); + } else if (!sel->online) { + SetDParamStr(0, sel->info.server_name); + DrawStringCentered(this->widget[NGWW_DETAILS].left + 115, 68, STR_ORANGE, TC_FROMSTRING); // game name - DrawStringCentered(w->widget[NGWW_DETAILS].left + 115, 48, STR_NETWORK_GAME_INFO, TC_FROMSTRING); + DrawStringCentered(this->widget[NGWW_DETAILS].left + 115, 132, STR_NETWORK_SERVER_OFFLINE, TC_FROMSTRING); // server offline + } else { // show game info + uint16 y = 100; + const uint16 x = this->widget[NGWW_DETAILS].left + 5; + + DrawStringCentered(this->widget[NGWW_DETAILS].left + 115, 48, STR_NETWORK_GAME_INFO, TC_FROMSTRING); - SetDParamStr(0, sel->info.server_name); - DrawStringCenteredTruncated(w->widget[NGWW_DETAILS].left, w->widget[NGWW_DETAILS].right, 62, STR_ORANGE, TC_BLACK); // game name - - SetDParamStr(0, sel->info.map_name); - DrawStringCenteredTruncated(w->widget[NGWW_DETAILS].left, w->widget[NGWW_DETAILS].right, 74, STR_02BD, TC_BLACK); // map name + SetDParamStr(0, sel->info.server_name); + DrawStringCenteredTruncated(this->widget[NGWW_DETAILS].left, this->widget[NGWW_DETAILS].right, 62, STR_ORANGE, TC_BLACK); // game name - SetDParam(0, sel->info.clients_on); - SetDParam(1, sel->info.clients_max); - SetDParam(2, sel->info.companies_on); - SetDParam(3, sel->info.companies_max); - DrawString(x, y, STR_NETWORK_CLIENTS, TC_GOLD); - y += 10; - - SetDParam(0, STR_NETWORK_LANG_ANY + sel->info.server_lang); - DrawString(x, y, STR_NETWORK_LANGUAGE, TC_GOLD); // server language - y += 10; + SetDParamStr(0, sel->info.map_name); + DrawStringCenteredTruncated(this->widget[NGWW_DETAILS].left, this->widget[NGWW_DETAILS].right, 74, STR_02BD, TC_BLACK); // map name - SetDParam(0, STR_TEMPERATE_LANDSCAPE + sel->info.map_set); - DrawString(x, y, STR_NETWORK_TILESET, TC_GOLD); // tileset - y += 10; - - SetDParam(0, sel->info.map_width); - SetDParam(1, sel->info.map_height); - DrawString(x, y, STR_NETWORK_MAP_SIZE, TC_GOLD); // map size - y += 10; - - SetDParamStr(0, sel->info.server_revision); - DrawString(x, y, STR_NETWORK_SERVER_VERSION, TC_GOLD); // server version - y += 10; - - SetDParamStr(0, sel->info.hostname); - SetDParam(1, sel->port); - DrawString(x, y, STR_NETWORK_SERVER_ADDRESS, TC_GOLD); // server address - y += 10; + SetDParam(0, sel->info.clients_on); + SetDParam(1, sel->info.clients_max); + SetDParam(2, sel->info.companies_on); + SetDParam(3, sel->info.companies_max); + DrawString(x, y, STR_NETWORK_CLIENTS, TC_GOLD); + y += 10; - SetDParam(0, sel->info.start_date); - DrawString(x, y, STR_NETWORK_START_DATE, TC_GOLD); // start date - y += 10; - - SetDParam(0, sel->info.game_date); - DrawString(x, y, STR_NETWORK_CURRENT_DATE, TC_GOLD); // current date - y += 10; - - y += 2; - - if (!sel->info.compatible) { - DrawStringCentered(w->widget[NGWW_DETAILS].left + 115, y, sel->info.version_compatible ? STR_NETWORK_GRF_MISMATCH : STR_NETWORK_VERSION_MISMATCH, TC_FROMSTRING); // server mismatch - } else if (sel->info.clients_on == sel->info.clients_max) { - /* Show: server full, when clients_on == clients_max */ - DrawStringCentered(w->widget[NGWW_DETAILS].left + 115, y, STR_NETWORK_SERVER_FULL, TC_FROMSTRING); // server full - } else if (sel->info.use_password) { - DrawStringCentered(w->widget[NGWW_DETAILS].left + 115, y, STR_NETWORK_PASSWORD, TC_FROMSTRING); // password warning - } + SetDParam(0, STR_NETWORK_LANG_ANY + sel->info.server_lang); + DrawString(x, y, STR_NETWORK_LANGUAGE, TC_GOLD); // server language + y += 10; - y += 10; - } - } break; + SetDParam(0, STR_TEMPERATE_LANDSCAPE + sel->info.map_set); + DrawString(x, y, STR_NETWORK_TILESET, TC_GOLD); // tileset + y += 10; - case WE_CLICK: - nd->field = e->we.click.widget; - switch (e->we.click.widget) { - case NGWW_PLAYER: - ShowOnScreenKeyboard(w, &WP(w, network_ql_d).q, NGWW_PLAYER, 0, 0); - break; - - case NGWW_CANCEL: // Cancel button - DeleteWindowById(WC_NETWORK_WINDOW, 0); - break; - - case NGWW_CONN_BTN: // 'Connection' droplist - ShowDropDownMenu(w, _lan_internet_types_dropdown, _network_lan_internet, NGWW_CONN_BTN, 0, 0); // do it for widget NSSW_CONN_BTN - break; + SetDParam(0, sel->info.map_width); + SetDParam(1, sel->info.map_height); + DrawString(x, y, STR_NETWORK_MAP_SIZE, TC_GOLD); // map size + y += 10; - case NGWW_NAME: // Sort by name - case NGWW_CLIENTS: // Sort by connected clients - case NGWW_INFO: // Connectivity (green dot) - if (ld->sort_type == e->we.click.widget - NGWW_NAME) ld->flags ^= VL_DESC; - ld->flags |= VL_RESORT; - ld->sort_type = e->we.click.widget - NGWW_NAME; - - _ng_sorting.order = !!(ld->flags & VL_DESC); - _ng_sorting.criteria = ld->sort_type; - SetWindowDirty(w); - break; - - case NGWW_MATRIX: { // Matrix to show networkgames - NetworkGameList *cur_item; - uint32 id_v = (e->we.click.pt.y - NET_PRC__OFFSET_TOP_WIDGET) / NET_PRC__SIZE_OF_ROW; + SetDParamStr(0, sel->info.server_revision); + DrawString(x, y, STR_NETWORK_SERVER_VERSION, TC_GOLD); // server version + y += 10; - if (id_v >= w->vscroll.cap) return; // click out of bounds - id_v += w->vscroll.pos; - - cur_item = _network_game_list; - for (; id_v > 0 && cur_item != NULL; id_v--) cur_item = cur_item->next; + SetDParamStr(0, sel->info.hostname); + SetDParam(1, sel->port); + DrawString(x, y, STR_NETWORK_SERVER_ADDRESS, TC_GOLD); // server address + y += 10; - nd->server = cur_item; - SetWindowDirty(w); - } break; - - case NGWW_LASTJOINED: { - NetworkGameList *last_joined = NetworkGameListAddItem(inet_addr(_network_last_host), _network_last_port); - if (last_joined != NULL) { - nd->server = last_joined; - SetWindowDirty(w); - } - } break; + SetDParam(0, sel->info.start_date); + DrawString(x, y, STR_NETWORK_START_DATE, TC_GOLD); // start date + y += 10; - case NGWW_FIND: // Find server automatically - switch (_network_lan_internet) { - case 0: NetworkUDPSearchGame(); break; - case 1: NetworkUDPQueryMasterServer(); break; - } - break; + SetDParam(0, sel->info.game_date); + DrawString(x, y, STR_NETWORK_CURRENT_DATE, TC_GOLD); // current date + y += 10; - case NGWW_ADD: // Add a server - ShowQueryString( - BindCString(_network_default_ip), - STR_NETWORK_ENTER_IP, - 31 | 0x1000, // maximum number of characters OR - 250, // characters up to this width pixels, whichever is satisfied first - w, CS_ALPHANUMERAL); - break; - - case NGWW_START: // Start server - ShowNetworkStartServerWindow(); - break; + y += 2; - case NGWW_JOIN: // Join Game - if (nd->server != NULL) { - snprintf(_network_last_host, sizeof(_network_last_host), "%s", inet_ntoa(*(struct in_addr *)&nd->server->ip)); - _network_last_port = nd->server->port; - ShowNetworkLobbyWindow(nd->server); - } - break; - - case NGWW_REFRESH: // Refresh - if (nd->server != NULL) NetworkUDPQueryServer(nd->server->info.hostname, nd->server->port); - break; - - case NGWW_NEWGRF: // NewGRF Settings - if (nd->server != NULL) ShowNewGRFSettings(false, false, false, &nd->server->info.grfconfig); - break; - } - break; - - case WE_DROPDOWN_SELECT: // we have selected a dropdown item in the list - switch (e->we.dropdown.button) { - case NGWW_CONN_BTN: - _network_lan_internet = e->we.dropdown.index; - break; - - default: - NOT_REACHED(); + if (!sel->info.compatible) { + DrawStringCentered(this->widget[NGWW_DETAILS].left + 115, y, sel->info.version_compatible ? STR_NETWORK_GRF_MISMATCH : STR_NETWORK_VERSION_MISMATCH, TC_FROMSTRING); // server mismatch + } else if (sel->info.clients_on == sel->info.clients_max) { + /* Show: server full, when clients_on == clients_max */ + DrawStringCentered(this->widget[NGWW_DETAILS].left + 115, y, STR_NETWORK_SERVER_FULL, TC_FROMSTRING); // server full + } else if (sel->info.use_password) { + DrawStringCentered(this->widget[NGWW_DETAILS].left + 115, y, STR_NETWORK_PASSWORD, TC_FROMSTRING); // password warning } - SetWindowDirty(w); - break; + y += 10; + } + } + + virtual void OnClick(Point pt, int widget) + { + this->field = widget; + switch (widget) { + case NGWW_PLAYER: + ShowOnScreenKeyboard(this, NGWW_PLAYER, 0, 0); + break; + + case NGWW_CANCEL: // Cancel button + DeleteWindowById(WC_NETWORK_WINDOW, 0); + break; - case WE_MOUSELOOP: - if (nd->field == NGWW_PLAYER) HandleEditBox(w, &WP(w, network_ql_d).q, NGWW_PLAYER); - break; + case NGWW_CONN_BTN: // 'Connection' droplist + ShowDropDownMenu(this, _lan_internet_types_dropdown, _network_lan_internet, NGWW_CONN_BTN, 0, 0); // do it for widget NSSW_CONN_BTN + break; + + case NGWW_NAME: // Sort by name + case NGWW_CLIENTS: // Sort by connected clients + case NGWW_INFO: // Connectivity (green dot) + if (this->ld.sort_type == widget - NGWW_NAME) this->ld.flags ^= VL_DESC; + this->ld.flags |= VL_RESORT; + this->ld.sort_type = widget - NGWW_NAME; + + _ng_sorting.order = !!(this->ld.flags & VL_DESC); + _ng_sorting.criteria = this->ld.sort_type; + this->SetDirty(); + break; + + case NGWW_MATRIX: { // Matrix to show networkgames + NetworkGameList *cur_item; + uint32 id_v = (pt.y - NET_PRC__OFFSET_TOP_WIDGET) / NET_PRC__SIZE_OF_ROW; + + if (id_v >= this->vscroll.cap) return; // click out of bounds + id_v += this->vscroll.pos; - case WE_INVALIDATE_DATA: - if (e->we.invalidate.data != 0) nd->server = NULL; - ld->flags |= VL_REBUILD; - SetWindowDirty(w); - break; + cur_item = _network_game_list; + for (; id_v > 0 && cur_item != NULL; id_v--) cur_item = cur_item->next; + + this->server = cur_item; + this->SetDirty(); + } break; + + case NGWW_LASTJOINED: { + NetworkGameList *last_joined = NetworkGameListAddItem(inet_addr(_network_last_host), _network_last_port); + if (last_joined != NULL) { + this->server = last_joined; + this->SetDirty(); + } + } break; - case WE_KEYPRESS: - if (nd->field != NGWW_PLAYER) { - if (nd->server != NULL) { - if (e->we.keypress.keycode == WKC_DELETE) { // Press 'delete' to remove servers - NetworkGameListRemoveItem(nd->server); - NetworkRebuildHostList(); - nd->server = NULL; - } + case NGWW_FIND: // Find server automatically + switch (_network_lan_internet) { + case 0: NetworkUDPSearchGame(); break; + case 1: NetworkUDPQueryMasterServer(); break; + } + break; + + case NGWW_ADD: // Add a server + ShowQueryString( + BindCString(_network_default_ip), + STR_NETWORK_ENTER_IP, + 31 | 0x1000, // maximum number of characters OR + 250, // characters up to this width pixels, whichever is satisfied first + this, CS_ALPHANUMERAL); + break; + + case NGWW_START: // Start server + ShowNetworkStartServerWindow(); + break; + + case NGWW_JOIN: // Join Game + if (this->server != NULL) { + snprintf(_network_last_host, sizeof(_network_last_host), "%s", inet_ntoa(*(struct in_addr *)&this->server->ip)); + _network_last_port = this->server->port; + ShowNetworkLobbyWindow(this->server); } break; - } + + case NGWW_REFRESH: // Refresh + if (this->server != NULL) NetworkUDPQueryServer(this->server->info.hostname, this->server->port); + break; - if (HandleEditBoxKey(w, &WP(w, network_ql_d).q, NGWW_PLAYER, e) == 1) break; // enter pressed + case NGWW_NEWGRF: // NewGRF Settings + if (this->server != NULL) ShowNewGRFSettings(false, false, false, &this->server->info.grfconfig); + break; + } + } + + virtual void OnDropdownSelect(int widget, int index) + { + switch (widget) { + case NGWW_CONN_BTN: + _network_lan_internet = index; + break; - /* The name is only allowed when it starts with a letter! */ - if (_edit_str_net_buf[0] != '\0' && _edit_str_net_buf[0] != ' ') { - ttd_strlcpy(_network_player_name, _edit_str_net_buf, lengthof(_network_player_name)); - } else { - ttd_strlcpy(_network_player_name, "Player", lengthof(_network_player_name)); - } + default: + NOT_REACHED(); + } + + this->SetDirty(); + } - break; + virtual void OnMouseLoop() + { + if (this->field == NGWW_PLAYER) this->HandleEditBox(NGWW_PLAYER); + } - case WE_ON_EDIT_TEXT: - if (!StrEmpty(e->we.edittext.str)) { - NetworkAddServer(e->we.edittext.str); - NetworkRebuildHostList(); - } - break; + virtual void OnInvalidateData(int data) + { + if (data != 0) this->server = NULL; + this->ld.flags |= VL_REBUILD; + this->SetDirty(); + } - case WE_RESIZE: { - w->vscroll.cap += e->we.sizing.diff.y / (int)w->resize.step_height; - - w->widget[NGWW_MATRIX].data = (w->vscroll.cap << 8) + 1; + virtual bool OnKeyPress(uint16 key, uint16 keycode) + { + bool cont = true; + if (this->field != NGWW_PLAYER) { + if (this->server != NULL) { + if (keycode == WKC_DELETE) { // Press 'delete' to remove servers + NetworkGameListRemoveItem(this->server); + NetworkRebuildHostList(); + this->server = NULL; + } + } + return cont; + } - SetVScrollCount(w, ld->list_length); + if (this->HandleEditBoxKey(NGWW_PLAYER, keycode, key, cont) == 1) return cont; // enter pressed - int widget_width = w->widget[NGWW_FIND].right - w->widget[NGWW_FIND].left; - int space = (w->width - 4 * widget_width - 25) / 3; + /* The name is only allowed when it starts with a letter! */ + if (StrEmpty(this->edit_str_buf) && this->edit_str_buf[0] != ' ') { + ttd_strlcpy(_network_player_name, this->edit_str_buf, lengthof(_network_player_name)); + } else { + ttd_strlcpy(_network_player_name, "Player", lengthof(_network_player_name)); + } + return cont; + } - int offset = 10; - for (uint i = 0; i < 4; i++) { - w->widget[NGWW_FIND + i].left = offset; - offset += widget_width; - w->widget[NGWW_FIND + i].right = offset; - offset += space; - } - } break; + virtual void OnQueryTextFinished(char *str) + { + if (!StrEmpty(str)) { + NetworkAddServer(str); + NetworkRebuildHostList(); + } + } + + virtual void OnResize(Point new_size, Point delta) + { + this->vscroll.cap += delta.y / (int)this->resize.step_height; + + this->widget[NGWW_MATRIX].data = (this->vscroll.cap << 8) + 1; - case WE_DESTROY: // Nicely clean up the sort-list - free(WP(w, network_ql_d).sort_list); - break; + SetVScrollCount(this, this->ld.list_length); + + int widget_width = this->widget[NGWW_FIND].right - this->widget[NGWW_FIND].left; + int space = (this->width - 4 * widget_width - 25) / 3; + + int offset = 10; + for (uint i = 0; i < 4; i++) { + this->widget[NGWW_FIND + i].left = offset; + offset += widget_width; + this->widget[NGWW_FIND + i].right = offset; + offset += space; + } } -} +}; static const Widget _network_game_window_widgets[] = { /* TOP */ @@ -654,7 +658,7 @@ static const WindowDesc _network_game_wi WC_NETWORK_WINDOW, WC_NONE, WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_STD_BTN | WDF_UNCLICK_BUTTONS | WDF_RESIZABLE, _network_game_window_widgets, - NetworkGameWindowWndProc, + NULL, }; void ShowNetworkGameWindow() @@ -676,16 +680,7 @@ void ShowNetworkGameWindow() _ng_sorting.order = 0; // sort ascending by default } - Window *w = new Window(&_network_game_window_desc); - if (w != NULL) { - querystr_d *querystr = &WP(w, network_ql_d).q; - - ttd_strlcpy(_edit_str_net_buf, _network_player_name, lengthof(_edit_str_net_buf)); - querystr->afilter = CS_ALPHANUMERAL; - InitializeTextBuffer(&querystr->text, _edit_str_net_buf, lengthof(_edit_str_net_buf), 120); - - UpdateNetworkGameWindow(true); - } + new NetworkGameWindow(&_network_game_window_desc); } enum { @@ -715,225 +710,237 @@ enum NetworkStartServerWidgets { NSSW_CANCEL = 27, ///< 'Cancel' button }; -/** - * Handler of actions done in the NetworkStartServer window - * - * @param w pointer to the Window structure - * @param e pointer to window event - * @note Uses network_ql_d (network_d, querystr_d and list_d) WP macro - * @see struct _network_start_server_window_widgets - * @see enum NetworkStartServerWidgets - */ -static void NetworkStartServerWindowWndProc(Window *w, WindowEvent *e) -{ - network_d *nd = &WP(w, network_ql_d).n; +struct NetworkStartServerWindow : public QueryStringBaseWindow { + byte field; ///< Selected text-field + FiosItem *map; ///< Selected map + byte widget_id; ///< The widget that has the pop-up input menu + + NetworkStartServerWindow(const WindowDesc *desc) : QueryStringBaseWindow(desc) + { + ttd_strlcpy(this->edit_str_buf, _network_server_name, lengthof(this->edit_str_buf)); - switch (e->event) { - case WE_CREATE: // focus input box - nd->field = NSSW_GAMENAME; - _network_game_info.use_password = (_network_server_password[0] != '\0'); - break; + _saveload_mode = SLD_NEW_GAME; + BuildFileList(); + this->vscroll.cap = 12; + this->vscroll.count = _fios_num + 1; - case WE_PAINT: { - int y = NSSWND_START, pos; - const FiosItem *item; + this->afilter = CS_ALPHANUMERAL; + InitializeTextBuffer(&this->text, this->edit_str_buf, lengthof(this->edit_str_buf), 160); + + this->field = NSSW_GAMENAME; + _network_game_info.use_password = !StrEmpty(_network_server_password); + + this->FindWindowPlacementAndResize(desc); + } - /* draw basic widgets */ - SetDParam(1, _connection_types_dropdown[_network_advertise]); - SetDParam(2, _network_game_info.clients_max); - SetDParam(3, _network_game_info.companies_max); - SetDParam(4, _network_game_info.spectators_max); - SetDParam(5, STR_NETWORK_LANG_ANY + _network_game_info.server_lang); - DrawWindowWidgets(w); + virtual void OnPaint() + { + int y = NSSWND_START, pos; + const FiosItem *item; - /* editbox to set game name */ - DrawEditBox(w, &WP(w, network_ql_d).q, NSSW_GAMENAME); - - /* if password is set, draw red '*' next to 'Set password' button */ - if (_network_game_info.use_password) DoDrawString("*", 408, 23, TC_RED); + /* draw basic widgets */ + SetDParam(1, _connection_types_dropdown[_network_advertise]); + SetDParam(2, _network_game_info.clients_max); + SetDParam(3, _network_game_info.companies_max); + SetDParam(4, _network_game_info.spectators_max); + SetDParam(5, STR_NETWORK_LANG_ANY + _network_game_info.server_lang); + DrawWindowWidgets(this); - /* draw list of maps */ - GfxFillRect(11, 63, 258, 215, 0xD7); // black background of maps list + /* editbox to set game name */ + this->DrawEditBox(NSSW_GAMENAME); + + /* if password is set, draw red '*' next to 'Set password' button */ + if (_network_game_info.use_password) DoDrawString("*", 408, 23, TC_RED); - pos = w->vscroll.pos; - while (pos < _fios_num + 1) { - item = _fios_list + pos - 1; - if (item == nd->map || (pos == 0 && nd->map == NULL)) - GfxFillRect(11, y - 1, 258, y + 10, 155); // show highlighted item with a different colour + /* draw list of maps */ + GfxFillRect(11, 63, 258, 215, 0xD7); // black background of maps list - if (pos == 0) { - DrawString(14, y, STR_4010_GENERATE_RANDOM_NEW_GAME, TC_DARK_GREEN); - } else { - DoDrawString(item->title, 14, y, _fios_colors[item->type] ); - } - pos++; - y += NSSWND_ROWSIZE; + pos = this->vscroll.pos; + while (pos < _fios_num + 1) { + item = _fios_list + pos - 1; + if (item == this->map || (pos == 0 && this->map == NULL)) + GfxFillRect(11, y - 1, 258, y + 10, 155); // show highlighted item with a different colour - if (y >= w->vscroll.cap * NSSWND_ROWSIZE + NSSWND_START) break; + if (pos == 0) { + DrawString(14, y, STR_4010_GENERATE_RANDOM_NEW_GAME, TC_DARK_GREEN); + } else { + DoDrawString(item->title, 14, y, _fios_colors[item->type] ); } - } break; + pos++; + y += NSSWND_ROWSIZE; - case WE_CLICK: - if (e->we.click.widget != NSSW_CONNTYPE_BTN && e->we.click.widget != NSSW_LANGUAGE_BTN) HideDropDownMenu(w); - nd->field = e->we.click.widget; - switch (e->we.click.widget) { - case NSSW_CLOSE: // Close 'X' - case NSSW_CANCEL: // Cancel button - ShowNetworkGameWindow(); - break; + if (y >= this->vscroll.cap * NSSWND_ROWSIZE + NSSWND_START) break; + } + } - case NSSW_GAMENAME: - ShowOnScreenKeyboard(w, &WP(w, network_ql_d).q, NSSW_GAMENAME, 0, 0); - break; + virtual void OnClick(Point pt, int widget) + { + if (widget != NSSW_CONNTYPE_BTN && widget != NSSW_LANGUAGE_BTN) HideDropDownMenu(this); + this->field = widget; + switch (widget) { + case NSSW_CLOSE: // Close 'X' + case NSSW_CANCEL: // Cancel button + ShowNetworkGameWindow(); + break; - case NSSW_SETPWD: // Set password button - nd->widget_id = NSSW_SETPWD; - ShowQueryString(BindCString(_network_server_password), STR_NETWORK_SET_PASSWORD, 20, 250, w, CS_ALPHANUMERAL); - break; + case NSSW_GAMENAME: + ShowOnScreenKeyboard(this, NSSW_GAMENAME, 0, 0); + break; - case NSSW_SELMAP: { // Select map - int y = (e->we.click.pt.y - NSSWND_START) / NSSWND_ROWSIZE; - - y += w->vscroll.pos; - if (y >= w->vscroll.count) return; + case NSSW_SETPWD: // Set password button + this->widget_id = NSSW_SETPWD; + ShowQueryString(BindCString(_network_server_password), STR_NETWORK_SET_PASSWORD, 20, 250, this, CS_ALPHANUMERAL); + break; - nd->map = (y == 0) ? NULL : _fios_list + y - 1; - SetWindowDirty(w); - } break; + case NSSW_SELMAP: { // Select map + int y = (pt.y - NSSWND_START) / NSSWND_ROWSIZE; + + y += this->vscroll.pos; + if (y >= this->vscroll.count) return; - case NSSW_CONNTYPE_BTN: // Connection type - ShowDropDownMenu(w, _connection_types_dropdown, _network_advertise, NSSW_CONNTYPE_BTN, 0, 0); // do it for widget NSSW_CONNTYPE_BTN - break; + this->map = (y == 0) ? NULL : _fios_list + y - 1; + this->SetDirty(); + } break; + + case NSSW_CONNTYPE_BTN: // Connection type + ShowDropDownMenu(this, _connection_types_dropdown, _network_advertise, NSSW_CONNTYPE_BTN, 0, 0); // do it for widget NSSW_CONNTYPE_BTN + break; - case NSSW_CLIENTS_BTND: case NSSW_CLIENTS_BTNU: // Click on up/down button for number of clients - case NSSW_COMPANIES_BTND: case NSSW_COMPANIES_BTNU: // Click on up/down button for number of companies - case NSSW_SPECTATORS_BTND: case NSSW_SPECTATORS_BTNU: // Click on up/down button for number of spectators - /* Don't allow too fast scrolling */ - if ((w->flags4 & WF_TIMEOUT_MASK) <= 2 << WF_TIMEOUT_SHL) { - w->HandleButtonClick(e->we.click.widget); - SetWindowDirty(w); - switch (e->we.click.widget) { - default: NOT_REACHED(); - case NSSW_CLIENTS_BTND: case NSSW_CLIENTS_BTNU: - _network_game_info.clients_max = Clamp(_network_game_info.clients_max + e->we.click.widget - NSSW_CLIENTS_TXT, 2, MAX_CLIENTS); - break; - case NSSW_COMPANIES_BTND: case NSSW_COMPANIES_BTNU: - _network_game_info.companies_max = Clamp(_network_game_info.companies_max + e->we.click.widget - NSSW_COMPANIES_TXT, 1, MAX_PLAYERS); - break; - case NSSW_SPECTATORS_BTND: case NSSW_SPECTATORS_BTNU: - _network_game_info.spectators_max = Clamp(_network_game_info.spectators_max + e->we.click.widget - NSSW_SPECTATORS_TXT, 0, MAX_CLIENTS); - break; - } + case NSSW_CLIENTS_BTND: case NSSW_CLIENTS_BTNU: // Click on up/down button for number of clients + case NSSW_COMPANIES_BTND: case NSSW_COMPANIES_BTNU: // Click on up/down button for number of companies + case NSSW_SPECTATORS_BTND: case NSSW_SPECTATORS_BTNU: // Click on up/down button for number of spectators + /* Don't allow too fast scrolling */ + if ((this->flags4 & WF_TIMEOUT_MASK) <= 2 << WF_TIMEOUT_SHL) { + this->HandleButtonClick(widget); + this->SetDirty(); + switch (widget) { + default: NOT_REACHED(); + case NSSW_CLIENTS_BTND: case NSSW_CLIENTS_BTNU: + _network_game_info.clients_max = Clamp(_network_game_info.clients_max + widget - NSSW_CLIENTS_TXT, 2, MAX_CLIENTS); + break; + case NSSW_COMPANIES_BTND: case NSSW_COMPANIES_BTNU: + _network_game_info.companies_max = Clamp(_network_game_info.companies_max + widget - NSSW_COMPANIES_TXT, 1, MAX_PLAYERS); + break; + case NSSW_SPECTATORS_BTND: case NSSW_SPECTATORS_BTNU: + _network_game_info.spectators_max = Clamp(_network_game_info.spectators_max + widget - NSSW_SPECTATORS_TXT, 0, MAX_CLIENTS); + break; } - _left_button_clicked = false; - break; + } + _left_button_clicked = false; + break; - case NSSW_CLIENTS_TXT: // Click on number of players - nd->widget_id = NSSW_CLIENTS_TXT; - SetDParam(0, _network_game_info.clients_max); - ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_NETWORK_NUMBER_OF_CLIENTS, 3, 50, w, CS_NUMERAL); - break; + case NSSW_CLIENTS_TXT: // Click on number of players + this->widget_id = NSSW_CLIENTS_TXT; + SetDParam(0, _network_game_info.clients_max); + ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_NETWORK_NUMBER_OF_CLIENTS, 3, 50, this, CS_NUMERAL); + break; - case NSSW_COMPANIES_TXT: // Click on number of companies - nd->widget_id = NSSW_COMPANIES_TXT; - SetDParam(0, _network_game_info.companies_max); - ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_NETWORK_NUMBER_OF_COMPANIES, 3, 50, w, CS_NUMERAL); - break; + case NSSW_COMPANIES_TXT: // Click on number of companies + this->widget_id = NSSW_COMPANIES_TXT; + SetDParam(0, _network_game_info.companies_max); + ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_NETWORK_NUMBER_OF_COMPANIES, 3, 50, this, CS_NUMERAL); + break; - case NSSW_SPECTATORS_TXT: // Click on number of spectators - nd->widget_id = NSSW_SPECTATORS_TXT; - SetDParam(0, _network_game_info.spectators_max); - ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_NETWORK_NUMBER_OF_SPECTATORS, 3, 50, w, CS_NUMERAL); - break; + case NSSW_SPECTATORS_TXT: // Click on number of spectators + this->widget_id = NSSW_SPECTATORS_TXT; + SetDParam(0, _network_game_info.spectators_max); + ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_NETWORK_NUMBER_OF_SPECTATORS, 3, 50, this, CS_NUMERAL); + break; - case NSSW_LANGUAGE_BTN: { // Language - uint sel = 0; - for (uint i = 0; i < lengthof(_language_dropdown) - 1; i++) { - if (_language_dropdown[i] == STR_NETWORK_LANG_ANY + _network_game_info.server_lang) { - sel = i; - break; - } + case NSSW_LANGUAGE_BTN: { // Language + uint sel = 0; + for (uint i = 0; i < lengthof(_language_dropdown) - 1; i++) { + if (_language_dropdown[i] == STR_NETWORK_LANG_ANY + _network_game_info.server_lang) { + sel = i; + break; } - ShowDropDownMenu(w, _language_dropdown, sel, NSSW_LANGUAGE_BTN, 0, 0); - } break; + } + ShowDropDownMenu(this, _language_dropdown, sel, NSSW_LANGUAGE_BTN, 0, 0); + } break; - case NSSW_START: // Start game - _is_network_server = true; + case NSSW_START: // Start game + _is_network_server = true; - if (nd->map == NULL) { // start random new game - ShowGenerateLandscape(); - } else { // load a scenario - char *name = FiosBrowseTo(nd->map); - if (name != NULL) { - SetFiosType(nd->map->type); - _file_to_saveload.filetype = FT_SCENARIO; - ttd_strlcpy(_file_to_saveload.name, name, sizeof(_file_to_saveload.name)); - ttd_strlcpy(_file_to_saveload.title, nd->map->title, sizeof(_file_to_saveload.title)); + if (this->map == NULL) { // start random new game + ShowGenerateLandscape(); + } else { // load a scenario + char *name = FiosBrowseTo(this->map); + if (name != NULL) { + SetFiosType(this->map->type); + _file_to_saveload.filetype = FT_SCENARIO; + ttd_strlcpy(_file_to_saveload.name, name, sizeof(_file_to_saveload.name)); + ttd_strlcpy(_file_to_saveload.title, this->map->title, sizeof(_file_to_saveload.title)); - delete w; - SwitchMode(SM_START_SCENARIO); - } + delete this; + SwitchMode(SM_START_SCENARIO); } - break; + } + break; - case NSSW_LOAD: // Load game - _is_network_server = true; - /* XXX - WC_NETWORK_WINDOW (this window) should stay, but if it stays, it gets - * copied all the elements of 'load game' and upon closing that, it segfaults */ - delete w; - ShowSaveLoadDialog(SLD_LOAD_GAME); - break; - } - break; + case NSSW_LOAD: // Load game + _is_network_server = true; + /* XXX - WC_NETWORK_WINDOW (this window) should stay, but if it stays, it gets + * copied all the elements of 'load game' and upon closing that, it segfaults */ + delete this; + ShowSaveLoadDialog(SLD_LOAD_GAME); + break; + } + } - case WE_DROPDOWN_SELECT: // we have selected a dropdown item in the list - switch (e->we.dropdown.button) { - case NSSW_CONNTYPE_BTN: - _network_advertise = (e->we.dropdown.index != 0); - break; - case NSSW_LANGUAGE_BTN: - _network_game_info.server_lang = _language_dropdown[e->we.dropdown.index] - STR_NETWORK_LANG_ANY; - break; - default: - NOT_REACHED(); - } + virtual void OnDropdownSelect(int widget, int index) + { + switch (widget) { + case NSSW_CONNTYPE_BTN: + _network_advertise = (index != 0); + break; + case NSSW_LANGUAGE_BTN: + _network_game_info.server_lang = _language_dropdown[index] - STR_NETWORK_LANG_ANY; + break; + default: + NOT_REACHED(); + } - SetWindowDirty(w); - break; + this->SetDirty(); + } - case WE_MOUSELOOP: - if (nd->field == NSSW_GAMENAME) HandleEditBox(w, &WP(w, network_ql_d).q, NSSW_GAMENAME); - break; + virtual void OnMouseLoop() + { + if (this->field == NSSW_GAMENAME) this->HandleEditBox(NSSW_GAMENAME); + } - case WE_KEYPRESS: - if (nd->field == NSSW_GAMENAME) { - if (HandleEditBoxKey(w, &WP(w, network_ql_d).q, NSSW_GAMENAME, e) == 1) break; // enter pressed + virtual bool OnKeyPress(uint16 key, uint16 keycode) + { + bool cont = true; + if (this->field == NSSW_GAMENAME) { + if (this->HandleEditBoxKey(NSSW_GAMENAME, key, keycode, cont) == 1) return cont; // enter pressed - ttd_strlcpy(_network_server_name, WP(w, network_ql_d).q.text.buf, sizeof(_network_server_name)); - } - break; + ttd_strlcpy(_network_server_name, this->text.buf, sizeof(_network_server_name)); + } - case WE_ON_EDIT_TEXT: - if (e->we.edittext.str == NULL) break; + return cont; + } + + virtual void OnQueryTextFinished(char *str) + { + if (str == NULL) return; - if (nd->widget_id == NSSW_SETPWD) { - ttd_strlcpy(_network_server_password, e->we.edittext.str, lengthof(_network_server_password)); - _network_game_info.use_password = !StrEmpty(_network_server_password); - } else { - int32 value = atoi(e->we.edittext.str); - w->InvalidateWidget(nd->widget_id); - switch (nd->widget_id) { - default: NOT_REACHED(); - case NSSW_CLIENTS_TXT: _network_game_info.clients_max = Clamp(value, 2, MAX_CLIENTS); break; - case NSSW_COMPANIES_TXT: _network_game_info.companies_max = Clamp(value, 1, MAX_PLAYERS); break; - case NSSW_SPECTATORS_TXT: _network_game_info.spectators_max = Clamp(value, 0, MAX_CLIENTS); break; - } + if (this->widget_id == NSSW_SETPWD) { + ttd_strlcpy(_network_server_password, str, lengthof(_network_server_password)); + _network_game_info.use_password = !StrEmpty(_network_server_password); + } else { + int32 value = atoi(str); + this->InvalidateWidget(this->widget_id); + switch (this->widget_id) { + default: NOT_REACHED(); + case NSSW_CLIENTS_TXT: _network_game_info.clients_max = Clamp(value, 2, MAX_CLIENTS); break; + case NSSW_COMPANIES_TXT: _network_game_info.companies_max = Clamp(value, 1, MAX_PLAYERS); break; + case NSSW_SPECTATORS_TXT: _network_game_info.spectators_max = Clamp(value, 0, MAX_CLIENTS); break; } + } - SetWindowDirty(w); - break; + this->SetDirty(); } -} +}; static const Widget _network_start_server_window_widgets[] = { /* Window decoration and background panel */ @@ -986,23 +993,14 @@ static const WindowDesc _network_start_s WC_NETWORK_WINDOW, WC_NONE, WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, _network_start_server_window_widgets, - NetworkStartServerWindowWndProc, + NULL, }; static void ShowNetworkStartServerWindow() { DeleteWindowById(WC_NETWORK_WINDOW, 0); - Window *w = new Window(&_network_start_server_window_desc); - ttd_strlcpy(_edit_str_net_buf, _network_server_name, lengthof(_edit_str_net_buf)); - - _saveload_mode = SLD_NEW_GAME; - BuildFileList(); - w->vscroll.cap = 12; - w->vscroll.count = _fios_num + 1; - - WP(w, network_ql_d).q.afilter = CS_ALPHANUMERAL; - InitializeTextBuffer(&WP(w, network_ql_d).q.text, _edit_str_net_buf, lengthof(_edit_str_net_buf), 160); + new NetworkStartServerWindow(&_network_start_server_window_desc); } static PlayerID NetworkLobbyFindCompanyIndex(byte pos) @@ -1036,7 +1034,6 @@ struct NetworkLobbyWindow : public Windo NetworkLobbyWindow(const WindowDesc *desc, NetworkGameList *ngl) : Window(desc), company(INVALID_PLAYER), server(ngl) { - strcpy(_edit_str_net_buf, ""); this->vscroll.cap = 10; this->FindWindowPlacementAndResize(desc); @@ -1370,10 +1367,10 @@ static bool CheckClientListHeight(Window /* If height is changed */ if (w->height != CLNWND_OFFSET + num + 1) { // XXX - magic unfortunately; (num + 2) has to be one bigger than heigh (num + 1) - SetWindowDirty(w); + w->SetDirty(); w->widget[3].bottom = w->widget[3].top + num + 2; w->height = CLNWND_OFFSET + num + 1; - SetWindowDirty(w); + w->SetDirty(); return false; } return true; @@ -1508,7 +1505,7 @@ static void ClientListPopupWndProc(Windo if (index == -1 || index == WP(w, menu_d).sel_index) return; WP(w, menu_d).sel_index = index; - SetWindowDirty(w); + w->SetDirty(); } else { if (index >= 0 && _cursor.pos.y >= w->top) { HandleClientListPopupClick(index, WP(w, menu_d).main_button); @@ -1573,7 +1570,7 @@ static void ClientListWndProc(Window *w, if (e->we.mouseover.pt.y == -1) { _selected_clientlist_y = 0; _selected_clientlist_item = 255; - SetWindowDirty(w); + w->SetDirty(); break; } /* It did not change.. no update! */ @@ -1588,7 +1585,7 @@ static void ClientListWndProc(Window *w, } /* Repaint */ - SetWindowDirty(w); + w->SetDirty(); break; case WE_DESTROY: case WE_CREATE: @@ -1706,202 +1703,217 @@ static void SendChat(const char *buf, De } } -/** - * Find the next item of the list of things that can be auto-completed. - * @param item The current indexed item to return. This function can, and most - * likely will, alter item, to skip empty items in the arrays. - * @return Returns the char that matched to the index. - */ -static const char *ChatTabCompletionNextItem(uint *item) -{ - static char chat_tab_temp_buffer[64]; + +struct NetworkChatWindow : public QueryStringBaseWindow { + DestType dtype; + int dest; + + NetworkChatWindow (const WindowDesc *desc, DestType type, int dest) : QueryStringBaseWindow(desc) + { + this->LowerWidget(2); + this->dtype = type; + this->dest = dest; + this->afilter = CS_ALPHANUMERAL; + InitializeTextBuffer(&this->text, this->edit_str_buf, lengthof(this->edit_str_buf), 0); + + InvalidateWindowData(WC_NEWS_WINDOW, 0, this->height); + SetBit(_no_scroll, SCROLL_CHAT); // do not scroll the game with the arrow-keys + + _chat_tab_completion_active = false; + + this->FindWindowPlacementAndResize(desc); + } + + ~NetworkChatWindow () + { + InvalidateWindowData(WC_NEWS_WINDOW, 0, 0); + ClrBit(_no_scroll, SCROLL_CHAT); + } + + /** + * Find the next item of the list of things that can be auto-completed. + * @param item The current indexed item to return. This function can, and most + * likely will, alter item, to skip empty items in the arrays. + * @return Returns the char that matched to the index. + */ + const char *ChatTabCompletionNextItem(uint *item) + { + static char chat_tab_temp_buffer[64]; - /* First, try clients */ - if (*item < MAX_CLIENT_INFO) { - /* Skip inactive clients */ - while (_network_client_info[*item].client_index == NETWORK_EMPTY_INDEX && *item < MAX_CLIENT_INFO) (*item)++; - if (*item < MAX_CLIENT_INFO) return _network_client_info[*item].client_name; + /* First, try clients */ + if (*item < MAX_CLIENT_INFO) { + /* Skip inactive clients */ + while (_network_client_info[*item].client_index == NETWORK_EMPTY_INDEX && *item < MAX_CLIENT_INFO) (*item)++; + if (*item < MAX_CLIENT_INFO) return _network_client_info[*item].client_name; + } + + /* Then, try townnames */ + /* Not that the following assumes all town indices are adjacent, ie no + * towns have been deleted. */ + if (*item <= (uint)MAX_CLIENT_INFO + GetMaxTownIndex()) { + const Town *t; + + FOR_ALL_TOWNS_FROM(t, *item - MAX_CLIENT_INFO) { + /* Get the town-name via the string-system */ + SetDParam(0, t->index); + GetString(chat_tab_temp_buffer, STR_TOWN, lastof(chat_tab_temp_buffer)); + return &chat_tab_temp_buffer[0]; + } + } + + return NULL; + } + + /** + * Find what text to complete. It scans for a space from the left and marks + * the word right from that as to complete. It also writes a \0 at the + * position of the space (if any). If nothing found, buf is returned. + */ + static char *ChatTabCompletionFindText(char *buf) + { + char *p = strrchr(buf, ' '); + if (p == NULL) return buf; + + *p = '\0'; + return p + 1; } - /* Then, try townnames */ - /* Not that the following assumes all town indices are adjacent, ie no - * towns have been deleted. */ - if (*item <= (uint)MAX_CLIENT_INFO + GetMaxTownIndex()) { - const Town *t; + /** + * See if we can auto-complete the current text of the user. + */ + void ChatTabCompletion() + { + static char _chat_tab_completion_buf[lengthof(this->edit_str_buf)]; + Textbuf *tb = &this->text; + size_t len, tb_len; + uint item; + char *tb_buf, *pre_buf; + const char *cur_name; + bool second_scan = false; + + item = 0; + + /* Copy the buffer so we can modify it without damaging the real data */ + pre_buf = (_chat_tab_completion_active) ? strdup(_chat_tab_completion_buf) : strdup(tb->buf); + + tb_buf = ChatTabCompletionFindText(pre_buf); + tb_len = strlen(tb_buf); + + while ((cur_name = ChatTabCompletionNextItem(&item)) != NULL) { + item++; + + if (_chat_tab_completion_active) { + /* We are pressing TAB again on the same name, is there an other name + * that starts with this? */ + if (!second_scan) { + size_t offset; + size_t length; + + /* If we are completing at the begin of the line, skip the ': ' we added */ + if (tb_buf == pre_buf) { + offset = 0; + length = tb->length - 2; + } else { + /* Else, find the place we are completing at */ + offset = strlen(pre_buf) + 1; + length = tb->length - offset; + } + + /* Compare if we have a match */ + if (strlen(cur_name) == length && strncmp(cur_name, tb->buf + offset, length) == 0) second_scan = true; + + continue; + } + + /* Now any match we make on _chat_tab_completion_buf after this, is perfect */ + } + + len = strlen(cur_name); + if (tb_len < len && strncasecmp(cur_name, tb_buf, tb_len) == 0) { + /* Save the data it was before completion */ + if (!second_scan) snprintf(_chat_tab_completion_buf, lengthof(_chat_tab_completion_buf), "%s", tb->buf); + _chat_tab_completion_active = true; - FOR_ALL_TOWNS_FROM(t, *item - MAX_CLIENT_INFO) { - /* Get the town-name via the string-system */ - SetDParam(0, t->index); - GetString(chat_tab_temp_buffer, STR_TOWN, lastof(chat_tab_temp_buffer)); - return &chat_tab_temp_buffer[0]; + /* Change to the found name. Add ': ' if we are at the start of the line (pretty) */ + if (pre_buf == tb_buf) { + snprintf(tb->buf, lengthof(this->edit_str_buf), "%s: ", cur_name); + } else { + snprintf(tb->buf, lengthof(this->edit_str_buf), "%s %s", pre_buf, cur_name); + } + + /* Update the textbuffer */ + UpdateTextBufferSize(&this->text); + + this->SetDirty(); + free(pre_buf); + return; + } + } + + if (second_scan) { + /* We walked all posibilities, and the user presses tab again.. revert to original text */ + strcpy(tb->buf, _chat_tab_completion_buf); + _chat_tab_completion_active = false; + + /* Update the textbuffer */ + UpdateTextBufferSize(&this->text); + + this->SetDirty(); + } + free(pre_buf); + } + + virtual void OnPaint() + { + static const StringID chat_captions[] = { + STR_NETWORK_CHAT_ALL_CAPTION, + STR_NETWORK_CHAT_COMPANY_CAPTION, + STR_NETWORK_CHAT_CLIENT_CAPTION + }; + + DrawWindowWidgets(this); + + assert((uint)this->dtype < lengthof(chat_captions)); + DrawStringRightAligned(this->widget[2].left - 2, this->widget[2].top + 1, chat_captions[this->dtype], TC_BLACK); + this->DrawEditBox(2); + } + + virtual void OnClick(Point pt, int widget) + { + switch (widget) { + case 2: + ShowOnScreenKeyboard(this, 2, 0, 3); + break; + + case 3: /* Send */ + SendChat(this->text.buf, this->dtype, this->dest); + /* FALLTHROUGH */ + case 0: /* Cancel */ delete this; break; } } - return NULL; -} - -/** - * Find what text to complete. It scans for a space from the left and marks - * the word right from that as to complete. It also writes a \0 at the - * position of the space (if any). If nothing found, buf is returned. - */ -static char *ChatTabCompletionFindText(char *buf) -{ - char *p = strrchr(buf, ' '); - if (p == NULL) return buf; - - *p = '\0'; - return p + 1; -} - -/** - * See if we can auto-complete the current text of the user. - */ -static void ChatTabCompletion(Window *w) -{ - static char _chat_tab_completion_buf[lengthof(_edit_str_net_buf)]; - Textbuf *tb = &WP(w, chatquerystr_d).text; - size_t len, tb_len; - uint item; - char *tb_buf, *pre_buf; - const char *cur_name; - bool second_scan = false; - - item = 0; - - /* Copy the buffer so we can modify it without damaging the real data */ - pre_buf = (_chat_tab_completion_active) ? strdup(_chat_tab_completion_buf) : strdup(tb->buf); - - tb_buf = ChatTabCompletionFindText(pre_buf); - tb_len = strlen(tb_buf); - - while ((cur_name = ChatTabCompletionNextItem(&item)) != NULL) { - item++; - - if (_chat_tab_completion_active) { - /* We are pressing TAB again on the same name, is there an other name - * that starts with this? */ - if (!second_scan) { - size_t offset; - size_t length; - - /* If we are completing at the begin of the line, skip the ': ' we added */ - if (tb_buf == pre_buf) { - offset = 0; - length = tb->length - 2; - } else { - /* Else, find the place we are completing at */ - offset = strlen(pre_buf) + 1; - length = tb->length - offset; - } - - /* Compare if we have a match */ - if (strlen(cur_name) == length && strncmp(cur_name, tb->buf + offset, length) == 0) second_scan = true; - - continue; - } - - /* Now any match we make on _chat_tab_completion_buf after this, is perfect */ - } - - len = strlen(cur_name); - if (tb_len < len && strncasecmp(cur_name, tb_buf, tb_len) == 0) { - /* Save the data it was before completion */ - if (!second_scan) snprintf(_chat_tab_completion_buf, lengthof(_chat_tab_completion_buf), "%s", tb->buf); - _chat_tab_completion_active = true; - - /* Change to the found name. Add ': ' if we are at the start of the line (pretty) */ - if (pre_buf == tb_buf) { - snprintf(tb->buf, lengthof(_edit_str_net_buf), "%s: ", cur_name); - } else { - snprintf(tb->buf, lengthof(_edit_str_net_buf), "%s %s", pre_buf, cur_name); - } - - /* Update the textbuffer */ - UpdateTextBufferSize(&WP(w, chatquerystr_d).text); - - SetWindowDirty(w); - free(pre_buf); - return; - } + virtual void OnMouseLoop() + { + this->HandleEditBox(2); } - if (second_scan) { - /* We walked all posibilities, and the user presses tab again.. revert to original text */ - strcpy(tb->buf, _chat_tab_completion_buf); - _chat_tab_completion_active = false; - - /* Update the textbuffer */ - UpdateTextBufferSize(&WP(w, chatquerystr_d).text); - - SetWindowDirty(w); + virtual bool OnKeyPress(uint16 key, uint16 keycode) + { + bool cont = true; + if (keycode == WKC_TAB) { + ChatTabCompletion(); + } else { + _chat_tab_completion_active = false; + switch (this->HandleEditBoxKey(2, key, keycode, cont)) { + case 1: /* Return */ + SendChat(this->text.buf, this->dtype, this->dest); + /* FALLTHROUGH */ + case 2: /* Escape */ delete this; break; + } + } + return cont; } - free(pre_buf); -} - -/* - * uses chatquerystr_d WP macro - * uses chatquerystr_d->dtype to store type of chat message (Private/Team/All) - */ -static void ChatWindowWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_CREATE: - InvalidateWindowData(WC_NEWS_WINDOW, 0, w->height); - SetBit(_no_scroll, SCROLL_CHAT); // do not scroll the game with the arrow-keys - break; - - case WE_PAINT: { - static const StringID chat_captions[] = { - STR_NETWORK_CHAT_ALL_CAPTION, - STR_NETWORK_CHAT_COMPANY_CAPTION, - STR_NETWORK_CHAT_CLIENT_CAPTION - }; - - DrawWindowWidgets(w); - - assert((uint)WP(w, chatquerystr_d).dtype < lengthof(chat_captions)); - DrawStringRightAligned(w->widget[2].left - 2, w->widget[2].top + 1, chat_captions[WP(w, chatquerystr_d).dtype], TC_BLACK); - DrawEditBox(w, &WP(w, chatquerystr_d), 2); - } break; - - case WE_CLICK: - switch (e->we.click.widget) { - case 2: - ShowOnScreenKeyboard(w, &WP(w, chatquerystr_d), 2, 0, 3); - break; - - case 3: /* Send */ - SendChat(WP(w, chatquerystr_d).text.buf, WP(w, chatquerystr_d).dtype, WP(w, chatquerystr_d).dest); - /* FALLTHROUGH */ - case 0: /* Cancel */ delete w; break; - } - break; - - case WE_MOUSELOOP: - HandleEditBox(w, &WP(w, chatquerystr_d), 2); - break; - - case WE_KEYPRESS: - if (e->we.keypress.keycode == WKC_TAB) { - ChatTabCompletion(w); - } else { - _chat_tab_completion_active = false; - switch (HandleEditBoxKey(w, &WP(w, chatquerystr_d), 2, e)) { - case 1: /* Return */ - SendChat(WP(w, chatquerystr_d).text.buf, WP(w, chatquerystr_d).dtype, WP(w, chatquerystr_d).dest); - /* FALLTHROUGH */ - case 2: /* Escape */ delete w; break; - } - } - break; - - case WE_DESTROY: - InvalidateWindowData(WC_NEWS_WINDOW, 0, 0); - ClrBit(_no_scroll, SCROLL_CHAT); - break; - } -} +}; static const Widget _chat_window_widgets[] = { { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, @@ -1916,23 +1928,13 @@ static const WindowDesc _chat_window_des WC_SEND_NETWORK_MSG, WC_NONE, WDF_STD_TOOLTIPS | WDF_DEF_WIDGET, _chat_window_widgets, - ChatWindowWndProc + NULL }; void ShowNetworkChatQueryWindow(DestType type, int dest) { DeleteWindowById(WC_SEND_NETWORK_MSG, 0); - - _edit_str_net_buf[0] = '\0'; - _chat_tab_completion_active = false; - - Window *w = new Window(&_chat_window_desc); - - w->LowerWidget(2); - WP(w, chatquerystr_d).dtype = type; - WP(w, chatquerystr_d).dest = dest; - WP(w, chatquerystr_d).afilter = CS_ALPHANUMERAL; - InitializeTextBuffer(&WP(w, chatquerystr_d).text, _edit_str_net_buf, lengthof(_edit_str_net_buf), 0); + new NetworkChatWindow (&_chat_window_desc, type, dest); } /** Enum for NetworkGameWindow, referring to _network_game_window_widgets */ @@ -1947,61 +1949,75 @@ enum NetworkCompanyPasswordWindowWidgets NCPWW_OK, ///< Safe the password etc. }; -static void NetworkCompanyPasswordWindowWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_PAINT: - DrawWindowWidgets(w); - DrawEditBox(w, &WP(w, chatquerystr_d), 4); - break; +struct NetworkCompanyPasswordWindow : public QueryStringBaseWindow { + NetworkCompanyPasswordWindow(const WindowDesc *desc) : QueryStringBaseWindow(desc) + { + this->afilter = CS_ALPHANUMERAL; + InitializeTextBuffer(&this->text, this->edit_str_buf, min(lengthof(_network_default_company_pass), lengthof(this->edit_str_buf)), 0); + + this->FindWindowPlacementAndResize(desc); + } + + void OnOk() + { + if (this->IsWidgetLowered(NCPWW_SAVE_AS_DEFAULT_PASSWORD)) { + snprintf(_network_default_company_pass, lengthof(_network_default_company_pass), "%s", this->edit_str_buf); + } - case WE_CLICK: - switch (e->we.click.widget) { - case NCPWW_OK: { - if (w->IsWidgetLowered(NCPWW_SAVE_AS_DEFAULT_PASSWORD)) { - snprintf(_network_default_company_pass, lengthof(_network_default_company_pass), "%s", _edit_str_net_buf); - } + /* empty password is a '*' because of console argument */ + if (StrEmpty(this->edit_str_buf)) snprintf(this->edit_str_buf, lengthof(this->edit_str_buf), "*"); + char *password = this->edit_str_buf; + NetworkChangeCompanyPassword(1, &password); + } - /* empty password is a '*' because of console argument */ - if (StrEmpty(_edit_str_net_buf)) snprintf(_edit_str_net_buf, lengthof(_edit_str_net_buf), "*"); - char *password = _edit_str_net_buf; - NetworkChangeCompanyPassword(1, &password); - } + virtual void OnPaint() + { + DrawWindowWidgets(this); + this->DrawEditBox(4); + } - /* FALL THROUGH */ - case NCPWW_CANCEL: - delete w; - break; + virtual void OnClick(Point pt, int widget) + { + switch (widget) { + case NCPWW_OK: + this->OnOk(); - case NCPWW_SAVE_AS_DEFAULT_PASSWORD: - w->ToggleWidgetLoweredState(NCPWW_SAVE_AS_DEFAULT_PASSWORD); - SetWindowDirty(w); - break; - case NCPWW_PASSWORD: - ShowOnScreenKeyboard(w, &WP(w, chatquerystr_d), NCPWW_PASSWORD, 2, 1); - break; - } - break; + /* FALL THROUGH */ + case NCPWW_CANCEL: + delete this; + break; - case WE_MOUSELOOP: - HandleEditBox(w, &WP(w, chatquerystr_d), 4); - break; + case NCPWW_SAVE_AS_DEFAULT_PASSWORD: + this->ToggleWidgetLoweredState(NCPWW_SAVE_AS_DEFAULT_PASSWORD); + this->SetDirty(); + break; + + case NCPWW_PASSWORD: + ShowOnScreenKeyboard(this, NCPWW_PASSWORD, 2, 1); + break; + } + } - case WE_KEYPRESS: - switch (HandleEditBoxKey(w, &WP(w, chatquerystr_d), 4, e)) { - case 1: // Return - e->event = WE_CLICK; - e->we.click.widget = NCPWW_OK; - NetworkCompanyPasswordWindowWndProc(w, e); - break; + virtual void OnMouseLoop() + { + this->HandleEditBox(4); + } - case 2: // Escape - delete w; - break; - } - break; + virtual bool OnKeyPress(uint16 key, uint16 keycode) + { + bool cont; + switch (this->HandleEditBoxKey(4, key, keycode, cont)) { + case 1: // Return + this->OnOk(); + /* FALL THROUGH */ + + case 2: // Escape + delete this; + break; + } + return cont; } -} +}; static const Widget _ncp_window_widgets[] = { { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, @@ -2020,17 +2036,14 @@ static const WindowDesc _ncp_window_desc WC_COMPANY_PASSWORD_WINDOW, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON, _ncp_window_widgets, - NetworkCompanyPasswordWindowWndProc + NULL }; void ShowNetworkCompanyPasswordWindow() { DeleteWindowById(WC_COMPANY_PASSWORD_WINDOW, 0); - _edit_str_net_buf[0] = '\0'; - Window *w = new Window(&_ncp_window_desc); - WP(w, chatquerystr_d).afilter = CS_ALPHANUMERAL; - InitializeTextBuffer(&WP(w, chatquerystr_d).text, _edit_str_net_buf, min(lengthof(_network_default_company_pass), lengthof(_edit_str_net_buf)), 0); + new NetworkCompanyPasswordWindow(&_ncp_window_desc); } #endif /* ENABLE_NETWORK */ diff --git a/src/osk_gui.cpp b/src/osk_gui.cpp --- a/src/osk_gui.cpp +++ b/src/osk_gui.cpp @@ -12,20 +12,11 @@ #include "debug.h" #include "window_func.h" #include "gfx_func.h" +#include "querystring_gui.h" #include "table/sprites.h" #include "table/strings.h" -struct osk_d { - querystr_d *qs; // text-input - int text_btn; // widget number of parent's text field - int ok_btn; // widget number of parent's ok button (=0 when ok shouldn't be passed on) - int cancel_btn; // widget number of parent's cancel button (=0 when cancel shouldn't be passed on; text will be reverted to original) - Textbuf *text; // pointer to parent's textbuffer (to update caret position) - char *orig; // the original text, in case we cancel -}; -assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(osk_d)); - enum OskWidgets { OSK_WIDGET_TEXT = 3, OSK_WIDGET_CANCEL = 5, @@ -50,142 +41,162 @@ enum { }; static byte _keystate = KEYS_NONE; -/* - * Only show valid characters; do not show characters that would - * only insert a space when we have a spacebar to do that or - * characters that are not allowed to be entered. - */ -static void ChangeOskDiabledState(Window *w, const querystr_d *qs, bool shift) -{ - for (uint i = 0; i < OSK_KEYBOARD_ENTRIES; i++) { - w->SetWidgetDisabledState(OSK_WIDGET_LETTERS + i, - !IsValidChar(_keyboard[shift][i], qs->afilter) || _keyboard[shift][i] == ' '); +struct OskWindow : public Window { + QueryString *qs; ///< text-input + int text_btn; ///< widget number of parent's text field + int ok_btn; ///< widget number of parent's ok button (=0 when ok shouldn't be passed on) + int cancel_btn; ///< widget number of parent's cancel button (=0 when cancel shouldn't be passed on; text will be reverted to original) + Textbuf *text; ///< pointer to parent's textbuffer (to update caret position) + char orig_str_buf[64]; ///< Original string. + + OskWindow(const WindowDesc *desc, QueryStringBaseWindow *parent, int button, int cancel, int ok) : Window(desc) + { + this->parent = parent; + assert(parent != NULL); + + if (parent->widget[button].data != 0) parent->caption = parent->widget[button].data; + + this->qs = parent; + this->text_btn = button; + this->cancel_btn = cancel; + this->ok_btn = ok; + this->text = &parent->text; + + /* make a copy in case we need to reset later */ + strcpy(this->orig_str_buf, this->qs->text.buf); + + SetBit(_no_scroll, SCROLL_EDIT); + /* Not needed by default. */ + this->DisableWidget(OSK_WIDGET_SPECIAL); + + this->FindWindowPlacementAndResize(desc); } - w->SetWidgetDisabledState(OSK_WIDGET_SPACE, !IsValidChar(' ', qs->afilter)); -} -/* on screen keyboard */ -static void OskWndProc(Window *w, WindowEvent *e) -{ - querystr_d *qs = WP(w, osk_d).qs; + /** + * Only show valid characters; do not show characters that would + * only insert a space when we have a spacebar to do that or + * characters that are not allowed to be entered. + */ + void ChangeOskDiabledState(bool shift) + { + for (uint i = 0; i < OSK_KEYBOARD_ENTRIES; i++) { + this->SetWidgetDisabledState(OSK_WIDGET_LETTERS + i, + !IsValidChar(_keyboard[shift][i], this->qs->afilter) || _keyboard[shift][i] == ' '); + } + this->SetWidgetDisabledState(OSK_WIDGET_SPACE, !IsValidChar(' ', this->qs->afilter)); + } + + virtual void OnPaint() + { + bool shift = HasBit(_keystate, KEYS_CAPS) ^ HasBit(_keystate, KEYS_SHIFT); - switch (e->event) { - case WE_CREATE: - SetBit(_no_scroll, SCROLL_EDIT); - /* Not needed by default. */ - w->DisableWidget(OSK_WIDGET_SPECIAL); - break; + this->LowerWidget(OSK_WIDGET_TEXT); + this->SetWidgetLoweredState(OSK_WIDGET_SHIFT, HasBit(_keystate, KEYS_SHIFT)); + this->SetWidgetLoweredState(OSK_WIDGET_CAPS, HasBit(_keystate, KEYS_CAPS)); + + this->ChangeOskDiabledState(shift); + + SetDParam(0, this->qs->caption); + DrawWindowWidgets(this); - case WE_PAINT: { + for (uint i = 0; i < OSK_KEYBOARD_ENTRIES; i++) { + DrawCharCentered(_keyboard[shift][i], + this->widget[OSK_WIDGET_LETTERS + i].left + 8, + this->widget[OSK_WIDGET_LETTERS + i].top + 3, + TC_BLACK); + } + + this->qs->DrawEditBox(this, OSK_WIDGET_TEXT); + } + + virtual void OnClick(Point pt, int widget) + { + /* clicked a letter */ + if (widget >= OSK_WIDGET_LETTERS) { bool shift = HasBit(_keystate, KEYS_CAPS) ^ HasBit(_keystate, KEYS_SHIFT); - w->LowerWidget(OSK_WIDGET_TEXT); - w->SetWidgetLoweredState(OSK_WIDGET_SHIFT, HasBit(_keystate, KEYS_SHIFT)); - w->SetWidgetLoweredState(OSK_WIDGET_CAPS, HasBit(_keystate, KEYS_CAPS)); + WChar c = _keyboard[shift][widget - OSK_WIDGET_LETTERS]; - ChangeOskDiabledState(w, qs, shift); + if (!IsValidChar(c, this->qs->afilter)) return; - SetDParam(0, qs->caption); - DrawWindowWidgets(w); + if (InsertTextBufferChar(&this->qs->text, c)) this->InvalidateWidget(OSK_WIDGET_TEXT); - for (uint i = 0; i < OSK_KEYBOARD_ENTRIES; i++) { - DrawCharCentered(_keyboard[shift][i], - w->widget[OSK_WIDGET_LETTERS + i].left + 8, - w->widget[OSK_WIDGET_LETTERS + i].top + 3, - TC_BLACK); + if (HasBit(_keystate, KEYS_SHIFT)) { + ToggleBit(_keystate, KEYS_SHIFT); + this->widget[OSK_WIDGET_SHIFT].color = HasBit(_keystate, KEYS_SHIFT) ? 15 : 14; + this->SetDirty(); } - - DrawEditBox(w, qs, OSK_WIDGET_TEXT); - break; + return; } - case WE_CLICK: - /* clicked a letter */ - if (e->we.click.widget >= OSK_WIDGET_LETTERS) { - bool shift = HasBit(_keystate, KEYS_CAPS) ^ HasBit(_keystate, KEYS_SHIFT); + switch (widget) { + case OSK_WIDGET_BACKSPACE: + if (DeleteTextBufferChar(&this->qs->text, WKC_BACKSPACE)) this->InvalidateWidget(OSK_WIDGET_TEXT); + break; + + case OSK_WIDGET_SPECIAL: + /* + * Anything device specific can go here. + * The button itself is hidden by default, and when you need it you + * can not hide it in the create event. + */ + break; - WChar c = _keyboard[shift][e->we.click.widget - OSK_WIDGET_LETTERS]; + case OSK_WIDGET_CAPS: + ToggleBit(_keystate, KEYS_CAPS); + this->SetDirty(); + break; + + case OSK_WIDGET_SHIFT: + ToggleBit(_keystate, KEYS_SHIFT); + this->SetDirty(); + break; + + case OSK_WIDGET_SPACE: + if (InsertTextBufferChar(&this->qs->text, ' ')) this->InvalidateWidget(OSK_WIDGET_TEXT); + break; - if (!IsValidChar(c, qs->afilter)) break; + case OSK_WIDGET_LEFT: + if (MoveTextBufferPos(&this->qs->text, WKC_LEFT)) this->InvalidateWidget(OSK_WIDGET_TEXT); + break; - if (InsertTextBufferChar(&qs->text, c)) w->InvalidateWidget(OSK_WIDGET_TEXT); + case OSK_WIDGET_RIGHT: + if (MoveTextBufferPos(&this->qs->text, WKC_RIGHT)) this->InvalidateWidget(OSK_WIDGET_TEXT); + break; - if (HasBit(_keystate, KEYS_SHIFT)) { - ToggleBit(_keystate, KEYS_SHIFT); - w->widget[OSK_WIDGET_SHIFT].color = HasBit(_keystate, KEYS_SHIFT) ? 15 : 14; - w->SetDirty(); + case OSK_WIDGET_OK: + if (this->qs->orig == NULL || strcmp(this->qs->text.buf, this->qs->orig) != 0) { + /* pass information by simulating a button press on parent window */ + if (this->ok_btn != 0) { + this->parent->OnClick(pt, this->ok_btn); + } + } + delete this; + break; + + case OSK_WIDGET_CANCEL: + if (this->cancel_btn != 0) { // pass a cancel event to the parent window + this->parent->OnClick(pt, this->cancel_btn); + /* Window gets deleted when the parent window removes itself. */ + } else { // or reset to original string + strcpy(qs->text.buf, this->orig_str_buf); + UpdateTextBufferSize(&qs->text); + MoveTextBufferPos(&qs->text, WKC_END); + delete this; } break; - } - - switch (e->we.click.widget) { - case OSK_WIDGET_BACKSPACE: - if (DeleteTextBufferChar(&qs->text, WKC_BACKSPACE)) w->InvalidateWidget(OSK_WIDGET_TEXT); - break; - - case OSK_WIDGET_SPECIAL: - /* - * Anything device specific can go here. - * The button itself is hidden by default, and when you need it you - * can not hide it in the create event. - */ - break; - - case OSK_WIDGET_CAPS: - ToggleBit(_keystate, KEYS_CAPS); - w->SetDirty(); - break; - - case OSK_WIDGET_SHIFT: - ToggleBit(_keystate, KEYS_SHIFT); - w->SetDirty(); - break; - - case OSK_WIDGET_SPACE: - if (InsertTextBufferChar(&qs->text, ' ')) w->InvalidateWidget(OSK_WIDGET_TEXT); - break; - - case OSK_WIDGET_LEFT: - if (MoveTextBufferPos(&qs->text, WKC_LEFT)) w->InvalidateWidget(OSK_WIDGET_TEXT); - break; + } + /* make sure that the parent window's textbox also gets updated */ + if (this->parent != NULL) this->parent->InvalidateWidget(this->text_btn); + } - case OSK_WIDGET_RIGHT: - if (MoveTextBufferPos(&qs->text, WKC_RIGHT)) w->InvalidateWidget(OSK_WIDGET_TEXT); - break; - - case OSK_WIDGET_OK: - if (qs->orig == NULL || strcmp(qs->text.buf, qs->orig) != 0) { - /* pass information by simulating a button press on parent window */ - if (WP(w, osk_d).ok_btn != 0) { - w->parent->OnClick(e->we.click.pt, WP(w, osk_d).ok_btn); - } - } - delete w; - break; - - case OSK_WIDGET_CANCEL: - if (WP(w, osk_d).cancel_btn != 0) { // pass a cancel event to the parent window - w->parent->OnClick(e->we.click.pt, WP(w, osk_d).cancel_btn); - /* Window gets deleted when the parent window removes itself. */ - } else { // or reset to original string - strcpy(qs->text.buf, WP(w, osk_d).orig); - UpdateTextBufferSize(&qs->text); - MoveTextBufferPos(&qs->text, WKC_END); - delete w; - } - break; - } - /* make sure that the parent window's textbox also gets updated */ - if (w->parent != NULL) w->parent->InvalidateWidget(WP(w, osk_d).text_btn); - break; - - case WE_MOUSELOOP: - HandleEditBox(w, qs, OSK_WIDGET_TEXT); - /* make the caret of the parent window also blink */ - w->parent->InvalidateWidget(WP(w, osk_d).text_btn); - break; + virtual void OnMouseLoop() + { + this->qs->HandleEditBox(this, OSK_WIDGET_TEXT); + /* make the caret of the parent window also blink */ + this->parent->InvalidateWidget(this->text_btn); } -} +}; static const Widget _osk_widgets[] = { { WWT_EMPTY, RESIZE_NONE, 0, 0, 0, 0, 0, 0x0, STR_NULL}, @@ -270,7 +281,7 @@ WindowDesc _osk_desc = { WC_OSK, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, _osk_widgets, - OskWndProc + NULL }; /** @@ -338,26 +349,10 @@ void GetKeyboardLayout() * @param ok widget number of parent's ok button (0 if ok events should not * be passed) */ -void ShowOnScreenKeyboard(Window *parent, querystr_d *q, int button, int cancel, int ok) +void ShowOnScreenKeyboard(QueryStringBaseWindow *parent, int button, int cancel, int ok) { DeleteWindowById(WC_OSK, 0); - Window *w = new Window(&_osk_desc); - - w->parent = parent; - assert(parent != NULL); - - if (parent->widget[button].data != 0) q->caption = parent->widget[button].data; - - WP(w, osk_d).qs = q; - WP(w, osk_d).text_btn = button; - WP(w, osk_d).cancel_btn = cancel; - WP(w, osk_d).ok_btn = ok; - WP(w, osk_d).text = &q->text; - GetKeyboardLayout(); - - /* make a copy in case we need to reset later */ - strcpy(_orig_str_buf, WP(w, osk_d).qs->text.buf); - WP(w, osk_d).orig = _orig_str_buf; + new OskWindow(&_osk_desc, parent, button, cancel, ok); } diff --git a/src/querystring_gui.h b/src/querystring_gui.h new file mode 100644 --- /dev/null +++ b/src/querystring_gui.h @@ -0,0 +1,38 @@ +/* $Id$ */ + +/** @file querystring_gui.h Base for the GUIs that have an edit box in them. */ + +#ifndef QUERYSTRING_GUI_H +#define QUERYSTRING_GUI_H + +#include "textbuf_gui.h" +#include "window_gui.h" + +struct QueryString { + StringID caption; + Textbuf text; + const char *orig; + CharSetFilter afilter; + bool handled; + + void DrawEditBox(Window *w, int wid); + void HandleEditBox(Window *w, int wid); + int HandleEditBoxKey(Window *w, int wid, uint16 key, uint16 keycode, bool &cont); +}; + +struct QueryStringBaseWindow : public Window, public QueryString { + char edit_str_buf[64]; + char orig_str_buf[64]; + + QueryStringBaseWindow(const WindowDesc *desc, void *data = NULL, WindowNumber window_number = 0) : Window(desc, data, window_number) + { + } + + void DrawEditBox(int wid); + void HandleEditBox(int wid); + int HandleEditBoxKey(int wid, uint16 key, uint16 keycode, bool &cont); +}; + +void ShowOnScreenKeyboard(QueryStringBaseWindow *parent, int button, int cancel, int ok); + +#endif /* QUERYSTRING_GUI_H */ diff --git a/src/signs_gui.cpp b/src/signs_gui.cpp --- a/src/signs_gui.cpp +++ b/src/signs_gui.cpp @@ -19,6 +19,7 @@ #include "map_func.h" #include "gfx_func.h" #include "viewport_func.h" +#include "querystring_gui.h" #include "table/strings.h" #include "table/sprites.h" @@ -143,11 +144,11 @@ void ShowSignList() } } -/** Edit sign window stuff */ -struct editsign_d : querystr_d { - SignID cur_sign; -}; -assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(editsign_d)); +static void RenameSign(SignID index, const char *text) +{ + _cmd_text = text; + DoCommandP(0, index, 0, NULL, CMD_RENAME_SIGN | CMD_MSG(STR_280C_CAN_T_CHANGE_SIGN_NAME)); +} enum QueryEditSignWidgets { QUERY_EDIT_SIGN_WIDGET_TEXT = 3, @@ -158,120 +159,122 @@ enum QueryEditSignWidgets { QUERY_EDIT_SIGN_WIDGET_NEXT, }; -static void UpdateSignEditWindow(Window *w, const Sign *si) -{ - /* Display an empty string when the sign hasnt been edited yet */ - if (si->name != NULL) { - SetDParam(0, si->index); - GetString(_edit_str_buf, STR_SIGN_NAME, lastof(_edit_str_buf)); - } else { - GetString(_edit_str_buf, STR_EMPTY, lastof(_edit_str_buf)); - } - _edit_str_buf[lengthof(_edit_str_buf) - 1] = '\0'; +struct SignWindow : QueryStringBaseWindow { + SignID cur_sign; - WP(w, editsign_d).cur_sign = si->index; - InitializeTextBuffer(&WP(w, querystr_d).text, _edit_str_buf, 31, 255); // Allow 31 characters (including \0) - - w->InvalidateWidget(QUERY_EDIT_SIGN_WIDGET_TEXT); -} + SignWindow(const WindowDesc *desc, const Sign *si) : QueryStringBaseWindow(desc) + { + SetBit(_no_scroll, SCROLL_EDIT); + this->caption = STR_280B_EDIT_SIGN_TEXT; + this->afilter = CS_ALPHANUMERAL; + this->LowerWidget(QUERY_EDIT_SIGN_WIDGET_TEXT); -static void RenameSign(SignID index, const char *text) -{ - _cmd_text = text; - DoCommandP(0, index, 0, NULL, CMD_RENAME_SIGN | CMD_MSG(STR_280C_CAN_T_CHANGE_SIGN_NAME)); -} + UpdateSignEditWindow(si); + } + + ~SignWindow() + { + ClrBit(_no_scroll, SCROLL_EDIT); + } -static void QuerySignEditWndProc(Window *w, WindowEvent *e) -{ - editsign_d *qs = &WP(w, editsign_d); - Sign *si; - uint sign_index = 0; + void UpdateSignEditWindow(const Sign *si) + { + /* Display an empty string when the sign hasnt been edited yet */ + if (si->name != NULL) { + SetDParam(0, si->index); + GetString(this->edit_str_buf, STR_SIGN_NAME, lastof(this->edit_str_buf)); + } else { + GetString(this->edit_str_buf, STR_EMPTY, lastof(this->edit_str_buf)); + } + this->edit_str_buf[lengthof(this->edit_str_buf) - 1] = '\0'; - switch (e->event) { - case WE_CREATE: - SetBit(_no_scroll, SCROLL_EDIT); - break; + this->cur_sign = si->index; + InitializeTextBuffer(&this->text, this->edit_str_buf, 31, 255); // Allow 31 characters (including \0) + + this->InvalidateWidget(QUERY_EDIT_SIGN_WIDGET_TEXT); + } - case WE_PAINT: - SetDParam(0, qs->caption); - DrawWindowWidgets(w); - DrawEditBox(w, qs, QUERY_EDIT_SIGN_WIDGET_TEXT); - break; + virtual void OnPaint() + { + SetDParam(0, this->caption); + DrawWindowWidgets(this); + this->DrawEditBox(QUERY_EDIT_SIGN_WIDGET_TEXT); + } - case WE_CLICK: - switch (e->we.click.widget) { - case QUERY_EDIT_SIGN_WIDGET_PREVIOUS: - if (_sign_sort_dirty) GlobalSortSignList(); - sign_index = _sign_sort[_num_sign_sort - 1]->index; - for (uint i = 1; i < _num_sign_sort; i++) { - if (qs->cur_sign == _sign_sort[i]->index) { - sign_index = _sign_sort[i - 1]->index; - break; - } + virtual void OnClick(Point pt, int widget) + { + switch (widget) { + case QUERY_EDIT_SIGN_WIDGET_PREVIOUS: { + if (_sign_sort_dirty) GlobalSortSignList(); + SignID sign_index = _sign_sort[_num_sign_sort - 1]->index; + for (uint i = 1; i < _num_sign_sort; i++) { + if (this->cur_sign == _sign_sort[i]->index) { + sign_index = _sign_sort[i - 1]->index; + break; } - si = GetSign(sign_index); + } + const Sign *si = GetSign(sign_index); - /* Scroll to sign and reopen window */ - ScrollMainWindowToTile(TileVirtXY(si->x, si->y)); - UpdateSignEditWindow(w, si); - break; + /* Scroll to sign and reopen window */ + ScrollMainWindowToTile(TileVirtXY(si->x, si->y)); + UpdateSignEditWindow(si); + } break; - case QUERY_EDIT_SIGN_WIDGET_NEXT: - if (_sign_sort_dirty) GlobalSortSignList(); - sign_index = _sign_sort[0]->index; - for (uint i = 0; i < _num_sign_sort - 1; i++) { - if (qs->cur_sign == _sign_sort[i]->index) { - sign_index = _sign_sort[i + 1]->index; - break; - } + case QUERY_EDIT_SIGN_WIDGET_NEXT: { + if (_sign_sort_dirty) GlobalSortSignList(); + SignID sign_index = _sign_sort[0]->index; + for (uint i = 0; i < _num_sign_sort - 1; i++) { + if (this->cur_sign == _sign_sort[i]->index) { + sign_index = _sign_sort[i + 1]->index; + break; } - si = GetSign(sign_index); + } + const Sign *si = GetSign(sign_index); - /* Scroll to sign and reopen window */ - ScrollMainWindowToTile(TileVirtXY(si->x, si->y)); - UpdateSignEditWindow(w, si); - break; + /* Scroll to sign and reopen window */ + ScrollMainWindowToTile(TileVirtXY(si->x, si->y)); + UpdateSignEditWindow(si); + } break; - case QUERY_EDIT_SIGN_WIDGET_TEXT: - ShowOnScreenKeyboard(w, qs, e->we.click.widget, QUERY_EDIT_SIGN_WIDGET_CANCEL, QUERY_EDIT_SIGN_WIDGET_OK); - break; + case QUERY_EDIT_SIGN_WIDGET_TEXT: + ShowOnScreenKeyboard(this, widget, QUERY_EDIT_SIGN_WIDGET_CANCEL, QUERY_EDIT_SIGN_WIDGET_OK); + break; - case QUERY_EDIT_SIGN_WIDGET_DELETE: - /* Only need to set the buffer to null, the rest is handled as the OK button */ - DeleteTextBufferAll(&qs->text); - /* FALL THROUGH */ + case QUERY_EDIT_SIGN_WIDGET_DELETE: + /* Only need to set the buffer to null, the rest is handled as the OK button */ + DeleteTextBufferAll(&this->text); + /* FALL THROUGH */ - case QUERY_EDIT_SIGN_WIDGET_OK: - RenameSign(qs->cur_sign, qs->text.buf); - /* FALL THROUGH */ + case QUERY_EDIT_SIGN_WIDGET_OK: + RenameSign(this->cur_sign, this->text.buf); + /* FALL THROUGH */ - case QUERY_EDIT_SIGN_WIDGET_CANCEL: - delete w; - break; - } - break; - - case WE_KEYPRESS: - switch (HandleEditBoxKey(w, qs, QUERY_EDIT_SIGN_WIDGET_TEXT, e)) { - case 1: // Enter pressed, confirms change - RenameSign(qs->cur_sign, qs->text.buf); - /* FALL THROUGH */ + case QUERY_EDIT_SIGN_WIDGET_CANCEL: + delete this; + break; + } + } - case 2: // ESC pressed, closes window, abandons changes - delete w; - break; - } - break; + virtual bool OnKeyPress(uint16 key, uint16 keycode) + { + bool cont = true; + switch (this->HandleEditBoxKey(QUERY_EDIT_SIGN_WIDGET_TEXT, key, keycode, cont)) { + case 1: // Enter pressed, confirms change + RenameSign(this->cur_sign, this->text.buf); + /* FALL THROUGH */ - case WE_MOUSELOOP: - HandleEditBox(w, qs, QUERY_EDIT_SIGN_WIDGET_TEXT); - break; + case 2: // ESC pressed, closes window, abandons changes + delete this; + break; + } + return cont; + } - case WE_DESTROY: - ClrBit(_no_scroll, SCROLL_EDIT); - break; + virtual void OnMouseLoop() + { + this->HandleEditBox(QUERY_EDIT_SIGN_WIDGET_TEXT); } -} +}; static const Widget _query_sign_edit_widgets[] = { { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, @@ -292,7 +295,7 @@ static const WindowDesc _query_sign_edit WC_QUERY_STRING, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET, _query_sign_edit_widgets, - QuerySignEditWndProc + NULL }; void ShowRenameSignWindow(const Sign *si) @@ -301,11 +304,5 @@ void ShowRenameSignWindow(const Sign *si DeleteWindowById(WC_QUERY_STRING, 0); DeleteWindowById(WC_SAVELOAD, 0); - Window *w = new Window(&_query_sign_edit_desc); - - WP(w, editsign_d).caption = STR_280B_EDIT_SIGN_TEXT; - WP(w, editsign_d).afilter = CS_ALPHANUMERAL; - w->LowerWidget(QUERY_EDIT_SIGN_WIDGET_TEXT); - - UpdateSignEditWindow(w, si); + new SignWindow(&_query_sign_edit_desc, si); } diff --git a/src/textbuf_gui.h b/src/textbuf_gui.h --- a/src/textbuf_gui.h +++ b/src/textbuf_gui.h @@ -18,21 +18,6 @@ struct Textbuf { uint16 caretxoffs; ///< the current position of the caret in pixels }; -struct querystr_d { - StringID caption; - Textbuf text; - const char *orig; - CharSetFilter afilter; - bool handled; -}; -assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(querystr_d)); - -extern char _edit_str_buf[64]; -extern char _orig_str_buf[lengthof(_edit_str_buf)]; - -void DrawEditBox(Window *w, querystr_d *string, int wid); -void HandleEditBox(Window *w, querystr_d *string, int wid); -int HandleEditBoxKey(Window *w, querystr_d *string, int wid, WindowEvent *we); bool HandleCaret(Textbuf *tb); void DeleteTextBufferAll(Textbuf *tb); @@ -56,6 +41,4 @@ static const uint OSK_KEYBOARD_ENTRIES = */ extern char _keyboard_opt[2][OSK_KEYBOARD_ENTRIES * 4 + 1]; -void ShowOnScreenKeyboard(Window *parent, querystr_d *q, int button, int cancel, int ok); - #endif /* TEXTBUF_GUI_H */