Changeset - r9179:660680ae613d
[Not reviewed]
master
0 6 1
rubidium - 17 years ago 2008-05-11 12:26:20
rubidium@openttd.org
(svn r13042) -Codechange: make a class of CreateScenarioWindow, GenerateLandscapeWindow, NetworkChatWindow, NetworkCompanyPasswordWindow, NetworkGameWindow, NetworkStartServerWindow, QueryStringWindow, SaveLoadWindow. All these classes depended on the 'querystr_d' object which is now put into QueryStringBaseWindow. As a side effect this removes quite a lot of WP macro usages and a few global variables.
7 files changed with 1894 insertions and 1823 deletions:
0 comments (0 inline, 0 general)
src/genworld_gui.cpp
Show inline comments
 
@@ -26,12 +26,13 @@
 
#include "gfx_func.h"
 
#include "settings_type.h"
 
#include "widgets/dropdown_type.h"
 
#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"
 

	
 
/**
 
 * In what 'mode' the GenerateLandscapeWindowProc is.
 
@@ -40,20 +41,12 @@ enum glwp_modes {
 
	GLWP_GENERATE,
 
	GLWP_HEIGHTMAP,
 
	GLWP_SCENARIO,
 
	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)
 
{
 
	_opt_newgame.landscape = landscape;
 
	InvalidateWindowClasses(WC_SELECT_GAME);
 
@@ -241,328 +234,338 @@ static DropDownList *BuildMapsizeDropDow
 
		list->push_back(item);
 
	}
 

	
 
	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 = {
 
	WDP_CENTER, WDP_CENTER, 338, 236, 338, 236,
 
	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)
 
{
 
	uint x = 0;
 
	uint y = 0;
 
@@ -574,20 +577,18 @@ static void _ShowGenerateLandscape(glwp_
 

	
 
	if (mode == GLWP_HEIGHTMAP) {
 
		/* If the function returns negative, it means there was a problem loading the heightmap */
 
		if (!GetHeightmapDimensions(_file_to_saveload.name, &x, &y)) return;
 
	}
 

	
 
	Window *w = AllocateWindowDescFront<Window>((mode == GLWP_HEIGHTMAP) ? &_heightmap_load_desc : &_generate_landscape_desc, mode);
 

	
 
	if (w == NULL) return;
 
	GenerateLandscapeWindow *w = AllocateWindowDescFront<GenerateLandscapeWindow>((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);
 
}
 

	
 
void ShowGenerateLandscape()
 
@@ -636,133 +637,137 @@ enum CreateScenarioWindowWidgets {
 
	CSCEN_FLAT_LAND_HEIGHT_DOWN,
 
	CSCEN_FLAT_LAND_HEIGHT_TEXT,
 
	CSCEN_FLAT_LAND_HEIGHT_UP
 
};
 

	
 

	
 
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},
 
{    WWT_CAPTION, RESIZE_NONE, 13,  11, 337,   0,  13, STR_SE_CAPTION,          STR_NULL},
 
{      WWT_PANEL, RESIZE_NONE, 13,   0, 337,  14, 169, 0x0,                     STR_NULL},
 

	
 
@@ -793,19 +798,19 @@ static const Widget _create_scenario_wid
 

	
 
static const WindowDesc _create_scenario_desc = {
 
	WDP_CENTER, WDP_CENTER, 338, 170, 338, 170,
 
	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<Window>(&_create_scenario_desc, GLWP_SCENARIO);
 
	new CreateScenarioWindow(&_create_scenario_desc, GLWP_SCENARIO);
 
}
 

	
 

	
 
static const Widget _show_terrain_progress_widgets[] = {
 
{    WWT_CAPTION,   RESIZE_NONE,    14,     0,   180,     0,    13, STR_GENERATION_WORLD,   STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     0,   180,    14,    96, 0x0,                    STR_NULL},
src/misc_gui.cpp
Show inline comments
 
@@ -41,12 +41,13 @@
 
#include "string_func.h"
 
#include "player_gui.h"
 
#include "settings_type.h"
 
#include "newgrf_cargo.h"
 
#include "rail_gui.h"
 
#include "tilehighlight_func.h"
 
#include "querystring_gui.h"
 

	
 
#include "table/sprites.h"
 
#include "table/strings.h"
 

	
 
/* Variables to display file lists */
 
FiosItem *_fios_list;
 
@@ -875,67 +876,67 @@ void UpdateTextBufferSize(Textbuf *tb)
 
	}
 

	
 
	tb->caretpos = tb->length;
 
	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? */
 
	bool b = !!(_caret_timer & 0x20);
 

	
 
	if (b != tb->caret) {
 
		tb->caret = b;
 
		return true;
 
	}
 
	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];
 

	
 
	assert((wi->type & WWT_MASK) == WWT_EDITBOX);
 

	
 
	GfxFillRect(wi->left + 1, wi->top + 1, wi->right - 1, wi->bottom - 1, 215);
 
@@ -954,95 +955,120 @@ void DrawEditBox(Window *w, querystr_d *
 

	
 
	DrawPixelInfo *old_dpi = _cur_dpi;
 
	_cur_dpi = &dpi;
 

	
 
	/* 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;
 

	
 
	if (tb->caretxoffs + delta < 0) delta = -tb->caretxoffs;
 

	
 
	DoDrawString(tb->buf, delta, 0, TC_YELLOW);
 
	if (tb->caret) DoDrawString("_", tb->caretxoffs + delta, 0, TC_WHITE);
 

	
 
	_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,
 
	QUERY_STR_WIDGET_OK
 
};
 

	
 

	
 
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},
 
{    WWT_CAPTION,   RESIZE_NONE,    14,    11,   259,     0,    13, STR_012D,        STR_NULL},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     0,   259,    14,    29, 0x0,             STR_NULL},
 
{    WWT_EDITBOX,   RESIZE_NONE,    14,     2,   257,    16,    27, 0x0,             STR_NULL},
 
@@ -1053,18 +1079,15 @@ static const Widget _query_string_widget
 

	
 
static const WindowDesc _query_string_desc = {
 
	190, 219, 260, 42, 260, 42,
 
	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
 
 * @param maxlen maximum length in characters allowed. If bit 12 is set we
 
 * will not check the resulting string against to original string to return success
 
 * @param maxwidth maximum width in pixels allowed
 
@@ -1072,34 +1095,33 @@ char _orig_str_buf[lengthof(_edit_str_bu
 
 * window. If NULL, results are handled by global function HandleOnEditText
 
 * @param afilter filters out unwanted character input */
 
void ShowQueryString(StringID str, StringID caption, uint maxlen, uint maxwidth, Window *parent, CharSetFilter afilter)
 
{
 
	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);
 
}
 

	
 

	
 
enum QueryWidgets {
 
	QUERY_WIDGET_CAPTION = 1,
 
	QUERY_WIDGET_NO = 3,
 
@@ -1303,244 +1325,290 @@ static void MakeSortedSaveGameList()
 
	uint s_amount = _fios_num - sort_start - sort_end;
 
	if (s_amount > 0) {
 
		qsort(_fios_list + sort_start, s_amount, sizeof(FiosItem), compare_FiosItems);
 
	}
 
}
 

	
 
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 = {
 
	WDP_CENTER, WDP_CENTER, 257, 180, 257, 320,
 
	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.
 
 * So each entry, as expressed by the related comment, is based on the enum   */
 
static const FileType _file_modetotype[] = {
 
	FT_SAVEGAME,  ///< used for SLD_LOAD_GAME
 
@@ -1550,56 +1618,28 @@ static const FileType _file_modetotype[]
 
	FT_HEIGHTMAP, ///< used for SLD_LOAD_HEIGHTMAP
 
	FT_SAVEGAME,  ///< SLD_NEW_GAME
 
};
 

	
 
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()
 
{
 
	SetWindowDirty(FindWindowById(WC_STATUS_BAR, 0));
 
}
src/network/network_gui.cpp
Show inline comments
 
@@ -26,45 +26,41 @@
 
#include "../core/alloc_func.hpp"
 
#include "../string_func.h"
 
#include "../gfx_func.h"
 
#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"
 

	
 
#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
 
};
 
assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(network_d));
 

	
 
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();
 
static void ShowNetworkLobbyWindow(NetworkGameList *ngl);
 
extern void SwitchMode(int new_mode);
 

	
 
@@ -156,70 +152,12 @@ static int CDECL NGameAllowedSorter(cons
 
	/* Finally sort on the name of the server */
 
	if (r == 0) r = strcasecmp(cmp1->info.server_name, cmp2->info.server_name);
 

	
 
	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<NetworkGameList*>(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
 
	NGWW_CAPTION,       ///< Caption of the window
 
	NGWW_RESIZE,        ///< Resize button
 

	
 
@@ -245,372 +183,438 @@ enum NetworkGameWindowWidgets {
 
	NGWW_FIND,          ///< 'Find server' button
 
	NGWW_ADD,           ///< 'Add server' button
 
	NGWW_START,         ///< 'Start server' button
 
	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 */
 
{   WWT_CLOSEBOX,   RESIZE_NONE,   BGC,     0,    10,     0,    13, STR_00C5,                         STR_018B_CLOSE_WINDOW},            // NGWW_CLOSE
 
{    WWT_CAPTION,   RESIZE_RIGHT,  BGC,    11,   449,     0,    13, STR_NETWORK_MULTIPLAYER,          STR_NULL},                         // NGWW_CAPTION
 
{      WWT_PANEL,   RESIZE_RB,     BGC,     0,   449,    14,   263, 0x0,                              STR_NULL},                         // NGWW_RESIZE
 
@@ -651,13 +655,13 @@ static const Widget _network_game_window
 

	
 
static const WindowDesc _network_game_window_desc = {
 
	WDP_CENTER, WDP_CENTER, 450, 264, 550, 264,
 
	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()
 
{
 
	static bool first = true;
 
	DeleteWindowById(WC_NETWORK_WINDOW, 0);
 
@@ -673,22 +677,13 @@ void ShowNetworkGameWindow()
 
		}
 

	
 
		_ng_sorting.criteria = 2; // sort default by collectivity (green-dots on top)
 
		_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 {
 
	NSSWND_START = 64,
 
	NSSWND_ROWSIZE = 12
 
};
 
@@ -712,231 +707,243 @@ enum NetworkStartServerWidgets {
 
	NSSW_LANGUAGE_BTN    = 24,   ///< 'Language spoken' droplist button
 
	NSSW_START           = 25,   ///< 'Start' button
 
	NSSW_LOAD            = 26,   ///< 'Load' button
 
	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 */
 
{   WWT_CLOSEBOX,   RESIZE_NONE,   BGC,     0,    10,     0,    13, STR_00C5,                           STR_018B_CLOSE_WINDOW },               // NSSW_CLOSE
 
{    WWT_CAPTION,   RESIZE_NONE,   BGC,    11,   419,     0,    13, STR_NETWORK_START_GAME_WINDOW,      STR_NULL},
 
{      WWT_PANEL,   RESIZE_NONE,   BGC,     0,   419,    14,   243, 0x0,                                STR_NULL},
 
@@ -983,29 +990,20 @@ static const Widget _network_start_serve
 

	
 
static const WindowDesc _network_start_server_window_desc = {
 
	WDP_CENTER, WDP_CENTER, 420, 244, 420, 244,
 
	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)
 
{
 
	/* Scroll through all _network_player_info and get the 'pos' item that is not empty */
 
	for (PlayerID i = PLAYER_FIRST; i < MAX_PLAYERS; i++) {
 
@@ -1033,13 +1031,12 @@ struct NetworkLobbyWindow : public Windo
 
	PlayerID company;        ///< Select company
 
	NetworkGameList *server; ///< Selected server
 

	
 
	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);
 
	}
 

	
 
	virtual void OnPaint()
 
@@ -1367,16 +1364,16 @@ static bool CheckClientListHeight(Window
 

	
 
	num *= CLNWND_ROWSIZE;
 

	
 
	/* 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;
 
}
 

	
 
/**
 
@@ -1505,13 +1502,13 @@ static void ClientListPopupWndProc(Windo
 
			int index = (_cursor.pos.y - w->top) / CLNWND_ROWSIZE;
 

	
 
			if (_left_button_down) {
 
				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);
 
				}
 

	
 
				DeleteWindowById(WC_TOOLBAR_MENU, 0);
 
@@ -1570,13 +1567,13 @@ static void ClientListWndProc(Window *w,
 

	
 
		case WE_MOUSEOVER:
 
			/* -1 means we left the current window */
 
			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! */
 
			if (e->we.mouseover.pt.y == _selected_clientlist_y) break;
 

	
 
			/* Find the new selected item (if any) */
 
@@ -1585,13 +1582,13 @@ static void ClientListWndProc(Window *w,
 
				_selected_clientlist_item = (e->we.mouseover.pt.y - CLNWND_OFFSET) / CLNWND_ROWSIZE;
 
			} else {
 
				_selected_clientlist_item = 255;
 
			}
 

	
 
			/* Repaint */
 
			SetWindowDirty(w);
 
			w->SetDirty();
 
			break;
 

	
 
		case WE_DESTROY: case WE_CREATE:
 
			/* When created or destroyed, data is reset */
 
			_selected_clientlist_item = 255;
 
			_selected_clientlist_y = 0;
 
@@ -1703,208 +1700,223 @@ static void SendChat(const char *buf, De
 
		SEND_COMMAND(PACKET_CLIENT_CHAT)((NetworkAction)(NETWORK_ACTION_CHAT + type), type, dest, buf);
 
	} else {
 
		NetworkServer_HandleChat((NetworkAction)(NETWORK_ACTION_CHAT + type), type, dest, buf, NETWORK_SERVER_INDEX);
 
	}
 
}
 

	
 
/**
 
 * 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},
 
{      WWT_PANEL, RESIZE_RIGHT, 14,  11, 319,  0, 13, 0x0,                       STR_NULL}, // background
 
{    WWT_EDITBOX, RESIZE_RIGHT, 14,  75, 257,  1, 12, STR_NETWORK_CHAT_OSKTITLE, STR_NULL}, // text box
 
{ WWT_PUSHTXTBTN, RESIZE_LR,    14, 258, 319,  1, 12, STR_NETWORK_SEND,          STR_NULL}, // send button
 
@@ -1913,29 +1925,19 @@ static const Widget _chat_window_widgets
 

	
 
static const WindowDesc _chat_window_desc = {
 
	WDP_CENTER, -26, 320, 14, 640, 14, // x, y, width, height
 
	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 */
 
enum NetworkCompanyPasswordWindowWidgets {
 
	NCPWW_CLOSE,                    ///< Close 'X' button
 
	NCPWW_CAPTION,                  ///< Caption of the whole window
 
@@ -1944,67 +1946,81 @@ enum NetworkCompanyPasswordWindowWidgets
 
	NCPWW_PASSWORD,                 ///< Input field for the password
 
	NCPWW_SAVE_AS_DEFAULT_PASSWORD, ///< Toggle 'button' for saving the current password as default password
 
	NCPWW_CANCEL,                   ///< Close the window without changing anything
 
	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},
 
{    WWT_CAPTION, RESIZE_NONE, 14,  11, 299,  0, 13, STR_COMPANY_PASSWORD_CAPTION,      STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{      WWT_PANEL, RESIZE_NONE, 14,   0, 299, 14, 50, 0x0,                               STR_NULL},
 
{       WWT_TEXT, RESIZE_NONE, 14,   5, 100, 19, 30, STR_COMPANY_PASSWORD,              STR_NULL},
 
@@ -2017,20 +2033,17 @@ static const Widget _ncp_window_widgets[
 

	
 
static const WindowDesc _ncp_window_desc = {
 
	WDP_AUTO, WDP_AUTO, 300, 63, 300, 63,
 
	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 */
src/osk_gui.cpp
Show inline comments
 
@@ -9,26 +9,17 @@
 
#include "window_gui.h"
 
#include "string_func.h"
 
#include "strings_func.h"
 
#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,
 
	OSK_WIDGET_OK,
 
	OSK_WIDGET_BACKSPACE,
 
	OSK_WIDGET_SPECIAL,
 
@@ -47,148 +38,168 @@ enum {
 
	KEYS_NONE,
 
	KEYS_SHIFT,
 
	KEYS_CAPS
 
};
 
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},
 
{    WWT_CAPTION, RESIZE_NONE,    14,     0,   255,     0,    13, STR_012D,          STR_NULL},
 
{      WWT_PANEL, RESIZE_NONE,    14,     0,   255,    14,    29, 0x0,               STR_NULL},
 
{    WWT_EDITBOX, RESIZE_NONE,    14,     2,   253,    16,    27, 0x0,               STR_NULL},
 
@@ -267,13 +278,13 @@ static const Widget _osk_widgets[] = {
 

	
 
WindowDesc _osk_desc = {
 
	WDP_CENTER, WDP_CENTER, 256, 140, 256, 140,
 
	WC_OSK, WC_NONE,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
 
	_osk_widgets,
 
	OskWndProc
 
	NULL
 
};
 

	
 
/**
 
 * Retrieve keyboard layout from language string or (if set) config file.
 
 * Also check for invalid characters.
 
 */
 
@@ -335,29 +346,13 @@ void GetKeyboardLayout()
 
 * @param button widget number of parent's textbox
 
 * @param cancel widget number of parent's cancel button (0 if cancel events
 
 *               should not be passed)
 
 * @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);
 
}
src/querystring_gui.h
Show inline comments
 
new file 100644
 
/* $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 */
src/signs_gui.cpp
Show inline comments
 
@@ -16,12 +16,13 @@
 
#include "strings_func.h"
 
#include "core/alloc_func.hpp"
 
#include "window_func.h"
 
#include "map_func.h"
 
#include "gfx_func.h"
 
#include "viewport_func.h"
 
#include "querystring_gui.h"
 

	
 
#include "table/strings.h"
 
#include "table/sprites.h"
 

	
 
static const Sign **_sign_sort;
 
static uint _num_sign_sort;
 
@@ -140,141 +141,143 @@ void ShowSignList()
 
		w->vscroll.cap = 12;
 
		w->resize.step_height = 10;
 
		w->resize.height = w->height - 10 * 7; // minimum if 5 in the list
 
	}
 
}
 

	
 
/** 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,
 
	QUERY_EDIT_SIGN_WIDGET_OK,
 
	QUERY_EDIT_SIGN_WIDGET_CANCEL,
 
	QUERY_EDIT_SIGN_WIDGET_DELETE,
 
	QUERY_EDIT_SIGN_WIDGET_PREVIOUS = QUERY_EDIT_SIGN_WIDGET_DELETE + 2,
 
	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},
 
{  WWT_CAPTION, RESIZE_NONE,  14,  11, 259,   0,  13, STR_012D,          STR_NULL },
 
{    WWT_PANEL, RESIZE_NONE,  14,   0, 259,  14,  29, STR_NULL,          STR_NULL },
 
{  WWT_EDITBOX, RESIZE_NONE,  14,   2, 257,  16,  27, STR_SIGN_OSKTITLE, STR_NULL },  // Text field
 
@@ -289,23 +292,17 @@ static const Widget _query_sign_edit_wid
 

	
 
static const WindowDesc _query_sign_edit_desc = {
 
	190, 170, 260, 42, 260, 42,
 
	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)
 
{
 
	/* Delete all other edit windows and the save window */
 
	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);
 
}
src/textbuf_gui.h
Show inline comments
 
@@ -15,27 +15,12 @@ struct Textbuf {
 
	uint16 length, width;       ///< the current size of the string. Width specifies screensize in pixels, length is in bytes
 
	bool caret;                 ///< is the caret ("_") visible or not
 
	uint16 caretpos;            ///< the current position of the caret in the buffer, in bytes
 
	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);
 
bool DeleteTextBufferChar(Textbuf *tb, int delmode);
 
bool InsertTextBufferChar(Textbuf *tb, uint32 key);
 
bool InsertTextBufferClipboard(Textbuf *tb);
 
@@ -53,9 +38,7 @@ static const uint OSK_KEYBOARD_ENTRIES =
 
 * The number of characters has to be OSK_KEYBOARD_ENTRIES. However, these
 
 * have to be UTF-8 encoded, which means up to 4 bytes per character.
 
 * Furthermore the string needs to be '\0'-terminated.
 
 */
 
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 */
0 comments (0 inline, 0 general)