(svn r12425) -Feature [FS#1846]: On Screen Keyboard for input fields so someone without a keyboard can enter text too. Patch by Dominik.
authorrubidium
Wed, 26 Mar 2008 10:08:17 +0000
changeset 9233 4daa9bb8dbf7
parent 9232 37291ba2aa2a
child 9234 8123d1c8e796
(svn r12425) -Feature [FS#1846]: On Screen Keyboard for input fields so someone without a keyboard can enter text too. Patch by Dominik.
bin/data/openttdd.grf
bin/data/openttdw.grf
projects/openttd_vs80.vcproj
projects/openttd_vs90.vcproj
source.list
src/genworld_gui.cpp
src/gfx.cpp
src/gfx_func.h
src/lang/english.txt
src/lang/german.txt
src/misc_gui.cpp
src/network/network_gui.cpp
src/osk_gui.cpp
src/settings.cpp
src/signs_gui.cpp
src/table/files.h
src/table/sprites.h
src/textbuf_gui.h
src/widget.cpp
src/window_gui.h
src/window_type.h
Binary file bin/data/openttdd.grf has changed
Binary file bin/data/openttdw.grf has changed
--- a/projects/openttd_vs80.vcproj	Tue Mar 25 21:58:13 2008 +0000
+++ b/projects/openttd_vs80.vcproj	Wed Mar 26 10:08:17 2008 +0000
@@ -1524,6 +1524,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\osk_gui.cpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\player_gui.cpp"
 				>
 			</File>
--- a/projects/openttd_vs90.vcproj	Tue Mar 25 21:58:13 2008 +0000
+++ b/projects/openttd_vs90.vcproj	Wed Mar 26 10:08:17 2008 +0000
@@ -1521,6 +1521,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\osk_gui.cpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\player_gui.cpp"
 				>
 			</File>
--- a/source.list	Tue Mar 25 21:58:13 2008 +0000
+++ b/source.list	Wed Mar 26 10:08:17 2008 +0000
@@ -290,6 +290,7 @@
 newgrf_gui.cpp
 news_gui.cpp
 order_gui.cpp
+osk_gui.cpp
 player_gui.cpp
 rail_gui.cpp
 road_gui.cpp
--- a/src/genworld_gui.cpp	Tue Mar 25 21:58:13 2008 +0000
+++ b/src/genworld_gui.cpp	Wed Mar 26 10:08:17 2008 +0000
@@ -127,7 +127,7 @@
 {   WWT_DROPDOWN, RESIZE_NONE, 12, 114, 175, 130, 141, 0x0,                          STR_NULL}, // Number of industries
 
 {       WWT_TEXT, RESIZE_NONE,  0,  12, 110, 153, 163, STR_RANDOM_SEED,              STR_NULL},
-{      WWT_PANEL, RESIZE_NONE, 15, 114, 207, 152, 163, 0x0,                          STR_RANDOM_SEED_HELP}, // Edit box for seed
+{    WWT_EDITBOX, RESIZE_NONE, 15, 114, 207, 152, 163, STR_RANDOM_SEED_OSKTITLE,     STR_RANDOM_SEED_HELP}, // Edit box for seed
 {    WWT_TEXTBTN, RESIZE_NONE, 12, 216, 326, 152, 163, STR_RANDOM,                   STR_RANDOM_HELP},
 
 {    WWT_TEXTBTN, RESIZE_NONE,  6, 243, 326, 228, 257, STR_GENERATE,                 STR_NULL}, // Generate button
@@ -181,7 +181,7 @@
 {   WWT_DROPDOWN, RESIZE_NONE, 12, 114, 175, 152, 163, 0x0,                          STR_NULL}, // Number of industries
 
 {       WWT_TEXT, RESIZE_NONE,  0,  12, 110, 175, 185, STR_RANDOM_SEED,              STR_NULL},
-{      WWT_PANEL, RESIZE_NONE, 15, 114, 207, 174, 185, 0x0,                          STR_RANDOM_SEED_HELP}, // Edit box for seed
+{    WWT_EDITBOX, RESIZE_NONE, 15, 114, 207, 174, 185, STR_RANDOM_SEED_OSKTITLE,     STR_RANDOM_SEED_HELP}, // Edit box for seed
 {    WWT_TEXTBTN, RESIZE_NONE, 12, 216, 326, 174, 185, STR_RANDOM,                   STR_RANDOM_HELP},
 
 {    WWT_TEXTBTN, RESIZE_NONE,  6, 243, 326, 196, 225, STR_GENERATE,                 STR_NULL}, // Generate button
@@ -374,8 +374,11 @@
 					SetWindowDirty(w);
 					break;
 
+				case GLAND_RANDOM_EDITBOX: // edit box for random seed
+					ShowOnScreenKeyboard(w, & _genseed_query, GLAND_RANDOM_EDITBOX, 0, 0);
+					break;
+
 				case GLAND_GENERATE_BUTTON: // Generate
-
 					UpdatePatches();
 
 					if (_patches.town_layout == TL_NO_ROADS) {
--- a/src/gfx.cpp	Tue Mar 25 21:58:13 2008 +0000
+++ b/src/gfx.cpp	Wed Mar 26 10:08:17 2008 +0000
@@ -561,6 +561,20 @@
 	return br;
 }
 
+void DrawCharCentered(WChar c, int x, int y, uint16 real_color)
+{
+	FontSize size = FS_NORMAL;
+	byte color = real_color & 0xFF;
+	uint palette = _use_dos_palette ? 1 : 0;
+	int w = GetCharacterWidth(size, c);
+
+	_string_colorremap[1] = _string_colormap[palette][color].text;
+	_string_colorremap[2] = _string_colormap[palette][color].shadow;
+	_color_remap_ptr = _string_colorremap;
+
+	GfxMainBlitter(GetGlyph(size, c), x - w / 2, y, BM_COLOUR_REMAP);
+}
+
 /** Draw a string at the given coordinates with the given colour
  * @param string the string to draw
  * @param x offset from left side of the screen, if negative offset from the right side
--- a/src/gfx_func.h	Tue Mar 25 21:58:13 2008 +0000
+++ b/src/gfx_func.h	Wed Mar 26 10:08:17 2008 +0000
@@ -99,6 +99,8 @@
 void DrawStringRightAlignedTruncated(int x, int y, StringID str, uint16 color, uint maxw);
 void DrawStringRightAlignedUnderline(int x, int y, StringID str, uint16 color);
 
+void DrawCharCentered(uint32 c, int x, int y, uint16 color);
+
 void GfxFillRect(int left, int top, int right, int bottom, int color);
 void GfxDrawLine(int left, int top, int right, int bottom, int color);
 void DrawBox(int x, int y, int dx1, int dy1, int dx2, int dy2, int dx3, int dy3);
--- a/src/lang/english.txt	Tue Mar 25 21:58:13 2008 +0000
+++ b/src/lang/english.txt	Wed Mar 26 10:08:17 2008 +0000
@@ -1352,6 +1352,7 @@
 
 STR_NETWORK_PLAYER_NAME                                         :{BLACK}Player name:
 STR_NETWORK_ENTER_NAME_TIP                                      :{BLACK}This is the name other players will identify you by
+STR_NETWORK_PLAYER_NAME_OSKTITLE                                :{BLACK}Enter your name
 STR_NETWORK_CONNECTION                                          :{BLACK}Connection:
 STR_NETWORK_CONNECTION_TIP                                      :{BLACK}Choose between an internet game or a Local Area Network (LAN) game
 
@@ -1395,6 +1396,7 @@
 
 STR_NETWORK_NEW_GAME_NAME                                       :{BLACK}Game name:
 STR_NETWORK_NEW_GAME_NAME_TIP                                   :{BLACK}The game name will be displayed to other players in the multiplayer game selection menu
+STR_NETWORK_NEW_GAME_NAME_OSKTITLE                              :{BLACK}Enter a name for the network game
 STR_NETWORK_SET_PASSWORD                                        :{BLACK}Set password
 STR_NETWORK_PASSWORD_TIP                                        :{BLACK}Protect your game with a password if you don't want it to be publicly accessible
 STR_NETWORK_SELECT_MAP                                          :{BLACK}Select a map:
@@ -1556,6 +1558,7 @@
 STR_NETWORK_CHAT_TO_CLIENT                                      :[Private] To {STRING}: {GRAY}{STRING}
 STR_NETWORK_CHAT_ALL_CAPTION                                    :[All] :
 STR_NETWORK_CHAT_ALL                                            :[All] {STRING}: {GRAY}{STRING}
+STR_NETWORK_CHAT_OSKTITLE                                       :{BLACK}Enter text for network chat
 STR_NETWORK_NAME_CHANGE                                         :has changed his/her name to
 STR_NETWORK_SERVER_SHUTDOWN                                     :{WHITE} The server closed the session
 STR_NETWORK_SERVER_REBOOT                                       :{WHITE} The server is restarting...{}Please wait...
@@ -1959,6 +1962,7 @@
 STR_400F_SELECT_SCENARIO_GREEN_PRE                              :{BLACK}Select scenario (green), pre-set game (blue), or random new game
 STR_4010_GENERATE_RANDOM_NEW_GAME                               :Generate random new game
 STR_LOAD_HEIGHTMAP                                              :{WHITE}Load Heightmap
+STR_SAVE_OSKTITLE                                               :{BLACK}Enter a name for the savegame
 
 ##id 0x4800
 STR_4800_IN_THE_WAY                                             :{WHITE}{STRING} in the way
@@ -3273,6 +3277,7 @@
 STR_WORLD_GENERATION_CAPTION                                    :{WHITE}World generation
 STR_RANDOM_SEED                                                 :{BLACK}Random Seed:
 STR_RANDOM_SEED_HELP                                            :{BLACK}Click to enter a random seed
+STR_RANDOM_SEED_OSKTITLE                                        :{BLACK}Enter a random seed
 STR_LAND_GENERATOR                                              :{BLACK}Land generator:
 STR_TREE_PLACER                                                 :{BLACK}Tree algorithm:
 STR_HEIGHTMAP_ROTATION                                          :{BLACK}Heightmap rotation:
@@ -3420,6 +3425,7 @@
 #### Improved sign GUI
 STR_NEXT_SIGN_TOOLTIP                                           :{BLACK}Go to next sign
 STR_PREVIOUS_SIGN_TOOLTIP                                       :{BLACK}Go to previous sign
+STR_SIGN_OSKTITLE                                               :{BLACK}Enter a name for the sign
 
 ########
 
@@ -3492,3 +3498,8 @@
 STR_DRAG_SIGNALS_DENSITY_DECREASE_TIP                           :{BLACK}Decrease dragging signal density
 STR_DRAG_SIGNALS_DENSITY_INCREASE_TIP                           :{BLACK}Increase dragging signal density
 ########
+
+############ on screen keyboard
+STR_OSK_KEYBOARD_LAYOUT                                         :`1234567890-=\qwertyuiop[]asdfghjkl;'  zxcvbnm,./ .
+STR_OSK_KEYBOARD_LAYOUT_CAPS                                    :~!@#$%^&*()_+|QWERTYUIOP{{}}ASDFGHJKL:"  ZXCVBNM<>? .
+########
--- a/src/lang/german.txt	Tue Mar 25 21:58:13 2008 +0000
+++ b/src/lang/german.txt	Wed Mar 26 10:08:17 2008 +0000
@@ -3485,3 +3485,8 @@
 STR_DRAG_SIGNALS_DENSITY_DECREASE_TIP                           :{BLACK}Signalabstand verringern
 STR_DRAG_SIGNALS_DENSITY_INCREASE_TIP                           :{BLACK}Signalabstand erhöhen
 ########
+
+############ on screen keyboard
+STR_OSK_KEYBOARD_LAYOUT                                         :^1234567890ß'€qwertzuiopü+asdfghjklöä#<yxcvbnm,.- .
+STR_OSK_KEYBOARD_LAYOUT_CAPS                                    :°!"§$%&/()=?`@QWERTZUIOPÜ*ASDFGHJKLÖÄ'>YXCVBNM;:_ .
+########
--- a/src/misc_gui.cpp	Tue Mar 25 21:58:13 2008 +0000
+++ b/src/misc_gui.cpp	Wed Mar 26 10:08:17 2008 +0000
@@ -1084,6 +1084,8 @@
 	const Widget *wi = &w->widget[wid];
 	const Textbuf *tb = &string->text;
 
+	assert((wi->type & WWT_MASK) == WWT_EDITBOX);
+
 	GfxFillRect(wi->left + 1, wi->top + 1, wi->right - 1, wi->bottom - 1, 215);
 
 	/* Limit the drawing of the string inside the widget boundaries */
@@ -1135,6 +1137,10 @@
 
 		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) {
@@ -1188,7 +1194,7 @@
 {   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_PANEL,   RESIZE_NONE,    14,     2,   257,    16,    27, 0x0,             STR_NULL},
+{    WWT_EDITBOX,   RESIZE_NONE,    14,     2,   257,    16,    27, 0x0,             STR_NULL},
 {    WWT_TEXTBTN,   RESIZE_NONE,    14,     0,   129,    30,    41, STR_012E_CANCEL, STR_NULL},
 {    WWT_TEXTBTN,   RESIZE_NONE,    14,   130,   259,    30,    41, STR_012F_OK,     STR_NULL},
 {   WIDGETS_END},
@@ -1202,7 +1208,8 @@
 	QueryStringWndProc
 };
 
-static char _edit_str_buf[64];
+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
@@ -1215,7 +1222,6 @@
  * @param afilter filters out unwanted character input */
 void ShowQueryString(StringID str, StringID caption, uint maxlen, uint maxwidth, Window *parent, CharSetFilter afilter)
 {
-	static char orig_str_buf[lengthof(_edit_str_buf)];
 	Window *w;
 	uint realmaxlen = maxlen & ~0x1000;
 
@@ -1233,8 +1239,8 @@
 	if (maxlen & 0x1000) {
 		WP(w, querystr_d).orig = NULL;
 	} else {
-		strecpy(orig_str_buf, _edit_str_buf, lastof(orig_str_buf));
-		WP(w, querystr_d).orig = orig_str_buf;
+		strecpy(_orig_str_buf, _edit_str_buf, lastof(_orig_str_buf));
+		WP(w, querystr_d).orig = _orig_str_buf;
 	}
 
 	w->LowerWidget(QUERY_STR_WIDGET_TEXT);
@@ -1379,7 +1385,7 @@
 {      WWT_INSET,     RESIZE_RB,    14,     2,   243,    50,   150, 0x0,              STR_400A_LIST_OF_DRIVES_DIRECTORIES},
 {  WWT_SCROLLBAR,    RESIZE_LRB,    14,   245,   256,    60,   151, 0x0,              STR_0190_SCROLL_BAR_SCROLLS_LIST},
 {      WWT_PANEL,    RESIZE_RTB,    14,     0,   256,   152,   167, 0x0,              STR_NULL},
-{      WWT_PANEL,    RESIZE_RTB,    14,     2,   254,   154,   165, 0x0,              STR_400B_CURRENTLY_SELECTED_NAME},
+{    WWT_EDITBOX,    RESIZE_RTB,    14,     2,   254,   154,   165, STR_SAVE_OSKTITLE,STR_400B_CURRENTLY_SELECTED_NAME},
 { WWT_PUSHTXTBTN,     RESIZE_TB,    14,     0,   127,   168,   179, STR_4003_DELETE,  STR_400C_DELETE_THE_CURRENTLY_SELECTED},
 { WWT_PUSHTXTBTN,     RESIZE_TB,    14,   128,   244,   168,   179, STR_4002_SAVE,    STR_400D_SAVE_THE_CURRENT_GAME_USING},
 {  WWT_RESIZEBOX,   RESIZE_LRTB,    14,   245,   256,   168,   179, 0x0,              STR_RESIZE_BUTTON},
@@ -1587,6 +1593,10 @@
 			break;
 		}
 
+		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;
 		}
--- a/src/network/network_gui.cpp	Tue Mar 25 21:58:13 2008 +0000
+++ b/src/network/network_gui.cpp	Wed Mar 26 10:08:17 2008 +0000
@@ -426,6 +426,9 @@
 	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;
@@ -574,7 +577,7 @@
 {       WWT_TEXT,   RESIZE_NONE,   BGC,     9,    85,    23,    35, STR_NETWORK_CONNECTION,         STR_NULL},
 { WWT_DROPDOWNIN,   RESIZE_NONE,   BGC,    90,   181,    22,    33, STR_NETWORK_LAN_INTERNET_COMBO, STR_NETWORK_CONNECTION_TIP},       // NGWW_CONN_BTN
 
-{      WWT_PANEL,   RESIZE_LR,     BGC,   290,   440,    22,    33, 0x0,                            STR_NETWORK_ENTER_NAME_TIP},       // NGWW_PLAYER
+{    WWT_EDITBOX,   RESIZE_LR,     BGC,   290,   440,    22,    33, STR_NETWORK_PLAYER_NAME_OSKTITLE, STR_NETWORK_ENTER_NAME_TIP},       // NGWW_PLAYER
 
 /* LEFT SIDE */
 { WWT_PUSHTXTBTN,   RESIZE_RIGHT,  BTC,    10,    70,    42,    53, STR_NETWORK_GAME_NAME,          STR_NETWORK_GAME_NAME_TIP},        // NGWW_NAME
@@ -736,7 +739,9 @@
 		case NSSW_CANCEL: // Cancel button
 			ShowNetworkGameWindow();
 			break;
-
+		case NSSW_GAMENAME:
+			ShowOnScreenKeyboard(w, &WP(w, network_ql_d).q,  NSSW_GAMENAME, 0, 0);
+			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);
@@ -887,7 +892,7 @@
 
 /* Set game name and password widgets */
 {       WWT_TEXT,   RESIZE_NONE,   BGC,    10,    90,    22,    34, STR_NETWORK_NEW_GAME_NAME,        STR_NULL},
-{      WWT_PANEL,   RESIZE_NONE,   BGC,   100,   272,    22,    33, 0x0,                              STR_NETWORK_NEW_GAME_NAME_TIP},        // NSSW_GAMENAME
+{    WWT_EDITBOX,   RESIZE_NONE,   BGC,   100,   272,    22,    33, STR_NETWORK_NEW_GAME_NAME_OSKTITLE, STR_NETWORK_NEW_GAME_NAME_TIP},        // NSSW_GAMENAME
 { WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,   285,   405,    22,    33, STR_NETWORK_SET_PASSWORD,         STR_NETWORK_PASSWORD_TIP},             // NSSW_SETPWD
 
 /* List of playable scenarios */
@@ -1818,6 +1823,10 @@
 
 	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 */
@@ -1851,10 +1860,10 @@
 }
 
 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_PANEL, RESIZE_RIGHT, 14,  75, 257,  1, 12, 0x0,              STR_NULL}, // text box
-{ WWT_PUSHTXTBTN, RESIZE_LR,    14, 258, 319,  1, 12, STR_NETWORK_SEND, STR_NULL}, // send button
+{   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
 {   WIDGETS_END},
 };
 
@@ -1926,6 +1935,9 @@
 					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;
 
@@ -1954,7 +1966,7 @@
 {    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},
-{      WWT_PANEL, RESIZE_NONE, 14, 101, 294, 19, 30, 0x0,                               STR_NULL},
+{    WWT_EDITBOX, RESIZE_NONE, 14, 101, 294, 19, 30, STR_SET_COMPANY_PASSWORD,          STR_NULL},
 {    WWT_TEXTBTN, RESIZE_NONE, 14, 101, 294, 35, 46, STR_MAKE_DEFAULT_COMPANY_PASSWORD, STR_MAKE_DEFAULT_COMPANY_PASSWORD_TIP},
 { WWT_PUSHTXTBTN, RESIZE_NONE, 14,   0, 149, 51, 62, STR_012E_CANCEL,                   STR_COMPANY_PASSWORD_CANCEL},
 { WWT_PUSHTXTBTN, RESIZE_NONE, 14, 150, 299, 51, 62, STR_012F_OK,                       STR_COMPANY_PASSWORD_OK},
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/osk_gui.cpp	Wed Mar 26 10:08:17 2008 +0000
@@ -0,0 +1,362 @@
+/* $Id$ */
+
+/** @file osk_gui.cpp The On Screen Keyboard GUI */
+
+#include "stdafx.h"
+#include "openttd.h"
+
+#include "textbuf_gui.h"
+#include "window_gui.h"
+#include "string_func.h"
+#include "strings_func.h"
+#include "debug.h"
+#include "window_func.h"
+#include "gfx_func.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,
+	OSK_WIDGET_CAPS,
+	OSK_WIDGET_SHIFT,
+	OSK_WIDGET_SPACE,
+	OSK_WIDGET_LEFT,
+	OSK_WIDGET_RIGHT,
+	OSK_WIDGET_LETTERS
+};
+
+char _keyboard_opt[2][OSK_KEYBOARD_ENTRIES * 4 + 1];
+static WChar _keyboard[2][OSK_KEYBOARD_ENTRIES];
+
+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] == ' ');
+	}
+	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;
+
+	switch (e->event) {
+		case WE_CREATE:
+			SetBit(_no_scroll, SCROLL_EDIT);
+			/* Not needed by default. */
+			w->DisableWidget(OSK_WIDGET_SPECIAL);
+			break;
+
+		case WE_PAINT: {
+			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));
+
+			ChangeOskDiabledState(w, qs, shift);
+
+			SetDParam(0, qs->caption);
+			DrawWindowWidgets(w);
+
+			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);
+			}
+
+			DrawEditBox(w, qs, OSK_WIDGET_TEXT);
+			break;
+		}
+
+		case WE_CLICK:
+			/* clicked a letter */
+			if (e->we.click.widget >= OSK_WIDGET_LETTERS) {
+				bool shift = HasBit(_keystate, KEYS_CAPS) ^ HasBit(_keystate, KEYS_SHIFT);
+
+				WChar c = _keyboard[shift][e->we.click.widget - OSK_WIDGET_LETTERS];
+
+				if (!IsValidChar(c, qs->afilter)) break;
+
+				if (InsertTextBufferChar(&qs->text, c)) w->InvalidateWidget(OSK_WIDGET_TEXT);
+
+				if (HasBit(_keystate, KEYS_SHIFT)) {
+					ToggleBit(_keystate, KEYS_SHIFT);
+					w->widget[OSK_WIDGET_SHIFT].color = HasBit(_keystate, KEYS_SHIFT) ? 15 : 14;
+					SetWindowDirty(w);
+				}
+				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);
+					SetWindowDirty(w);
+					break;
+
+				case OSK_WIDGET_SHIFT:
+					ToggleBit(_keystate, KEYS_SHIFT);
+					SetWindowDirty(w);
+					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;
+
+				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) {
+							Window *parent = w->parent;
+							WindowEvent e;
+							e.event = WE_CLICK;
+							e.we.click.widget = WP(w, osk_d).ok_btn;
+							parent->wndproc(parent, &e);
+						}
+					}
+					DeleteWindow(w);
+					break;
+
+				case OSK_WIDGET_CANCEL:
+					if (WP(w, osk_d).cancel_btn != 0) { // pass a cancel event to the parent window
+						Window *parent = w->parent;
+						WindowEvent e;
+						e.event = WE_CLICK;
+						e.we.click.widget = WP(w, osk_d).cancel_btn;
+						parent->wndproc(parent, &e);
+					} else { // or reset to original string
+						strcpy(qs->text.buf, WP(w, osk_d).orig);
+						UpdateTextBufferSize(&qs->text);
+						MoveTextBufferPos(&qs->text, WKC_END);
+					}
+					DeleteWindow(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;
+	}
+}
+
+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},
+
+{      WWT_PANEL, RESIZE_NONE,    14,     0,   255,    30,   139, 0x0,               STR_NULL},
+
+{    WWT_TEXTBTN, RESIZE_NONE,    14,     3,   108,    35,    46, STR_012E_CANCEL,   STR_NULL},
+{    WWT_TEXTBTN, RESIZE_NONE,    14,   111,   216,    35,    46, STR_012F_OK,       STR_NULL},
+{ WWT_PUSHIMGBTN, RESIZE_NONE,    14,   219,   252,    35,    46, SPR_OSK_BACKSPACE, STR_NULL},
+
+{ WWT_PUSHIMGBTN, RESIZE_NONE,    14,     3,    27,    67,    82, SPR_OSK_SPECIAL,   STR_NULL},
+{     WWT_IMGBTN, RESIZE_NONE,    14,     3,    36,    85,   100, SPR_OSK_CAPS,      STR_NULL},
+{     WWT_IMGBTN, RESIZE_NONE,    14,     3,    27,   103,   118, SPR_OSK_SHIFT,     STR_NULL},
+
+{ WWT_PUSHTXTBTN, RESIZE_NONE,    14,    75,   189,   121,   136, STR_EMPTY,         STR_NULL},
+
+{ WWT_PUSHIMGBTN, RESIZE_NONE,    14,   219,   234,   121,   136, SPR_OSK_LEFT,      STR_NULL},
+{ WWT_PUSHIMGBTN, RESIZE_NONE,    14,   237,   252,   121,   136, SPR_OSK_RIGHT,     STR_NULL},
+
+{    WWT_PUSHBTN, RESIZE_NONE,    14,     3,    18,    49,    64, 0x0,    STR_NULL},
+{    WWT_PUSHBTN, RESIZE_NONE,    14,    21,    36,    49,    64, 0x0,    STR_NULL},
+{    WWT_PUSHBTN, RESIZE_NONE,    14,    39,    54,    49,    64, 0x0,    STR_NULL},
+{    WWT_PUSHBTN, RESIZE_NONE,    14,    57,    72,    49,    64, 0x0,    STR_NULL},
+{    WWT_PUSHBTN, RESIZE_NONE,    14,    75,    90,    49,    64, 0x0,    STR_NULL},
+{    WWT_PUSHBTN, RESIZE_NONE,    14,    93,   108,    49,    64, 0x0,    STR_NULL},
+{    WWT_PUSHBTN, RESIZE_NONE,    14,   111,   126,    49,    64, 0x0,    STR_NULL},
+{    WWT_PUSHBTN, RESIZE_NONE,    14,   129,   144,    49,    64, 0x0,    STR_NULL},
+{    WWT_PUSHBTN, RESIZE_NONE,    14,   147,   162,    49,    64, 0x0,    STR_NULL},
+{    WWT_PUSHBTN, RESIZE_NONE,    14,   165,   180,    49,    64, 0x0,    STR_NULL},
+{    WWT_PUSHBTN, RESIZE_NONE,    14,   183,   198,    49,    64, 0x0,    STR_NULL},
+{    WWT_PUSHBTN, RESIZE_NONE,    14,   201,   216,    49,    64, 0x0,    STR_NULL},
+{    WWT_PUSHBTN, RESIZE_NONE,    14,   219,   234,    49,    64, 0x0,    STR_NULL},
+{    WWT_PUSHBTN, RESIZE_NONE,    14,   237,   252,    49,    64, 0x0,    STR_NULL},
+
+{    WWT_PUSHBTN, RESIZE_NONE,    14,    30,    45,    67,    82, 0x0,    STR_NULL},
+{    WWT_PUSHBTN, RESIZE_NONE,    14,    48,    63,    67,    82, 0x0,    STR_NULL},
+{    WWT_PUSHBTN, RESIZE_NONE,    14,    66,    81,    67,    82, 0x0,    STR_NULL},
+{    WWT_PUSHBTN, RESIZE_NONE,    14,    84,    99,    67,    82, 0x0,    STR_NULL},
+{    WWT_PUSHBTN, RESIZE_NONE,    14,   102,   117,    67,    82, 0x0,    STR_NULL},
+{    WWT_PUSHBTN, RESIZE_NONE,    14,   120,   135,    67,    82, 0x0,    STR_NULL},
+{    WWT_PUSHBTN, RESIZE_NONE,    14,   138,   153,    67,    82, 0x0,    STR_NULL},
+{    WWT_PUSHBTN, RESIZE_NONE,    14,   156,   171,    67,    82, 0x0,    STR_NULL},
+{    WWT_PUSHBTN, RESIZE_NONE,    14,   174,   189,    67,    82, 0x0,    STR_NULL},
+{    WWT_PUSHBTN, RESIZE_NONE,    14,   192,   207,    67,    82, 0x0,    STR_NULL},
+{    WWT_PUSHBTN, RESIZE_NONE,    14,   210,   225,    67,    82, 0x0,    STR_NULL},
+{    WWT_PUSHBTN, RESIZE_NONE,    14,   228,   243,    67,    82, 0x0,    STR_NULL},
+
+{    WWT_PUSHBTN, RESIZE_NONE,    14,    39,    54,    85,   100, 0x0,    STR_NULL},
+{    WWT_PUSHBTN, RESIZE_NONE,    14,    57,    72,    85,   100, 0x0,    STR_NULL},
+{    WWT_PUSHBTN, RESIZE_NONE,    14,    75,    90,    85,   100, 0x0,    STR_NULL},
+{    WWT_PUSHBTN, RESIZE_NONE,    14,    93,   108,    85,   100, 0x0,    STR_NULL},
+{    WWT_PUSHBTN, RESIZE_NONE,    14,   111,   126,    85,   100, 0x0,    STR_NULL},
+{    WWT_PUSHBTN, RESIZE_NONE,    14,   129,   144,    85,   100, 0x0,    STR_NULL},
+{    WWT_PUSHBTN, RESIZE_NONE,    14,   147,   162,    85,   100, 0x0,    STR_NULL},
+{    WWT_PUSHBTN, RESIZE_NONE,    14,   165,   180,    85,   100, 0x0,    STR_NULL},
+{    WWT_PUSHBTN, RESIZE_NONE,    14,   183,   198,    85,   100, 0x0,    STR_NULL},
+{    WWT_PUSHBTN, RESIZE_NONE,    14,   201,   216,    85,   100, 0x0,    STR_NULL},
+{    WWT_PUSHBTN, RESIZE_NONE,    14,   219,   234,    85,   100, 0x0,    STR_NULL},
+{    WWT_PUSHBTN, RESIZE_NONE,    14,   237,   252,    85,   100, 0x0,    STR_NULL},
+
+{    WWT_PUSHBTN, RESIZE_NONE,    14,    30,    45,   103,   118, 0x0,    STR_NULL},
+{    WWT_PUSHBTN, RESIZE_NONE,    14,    48,    63,   103,   118, 0x0,    STR_NULL},
+{    WWT_PUSHBTN, RESIZE_NONE,    14,    66,    81,   103,   118, 0x0,    STR_NULL},
+{    WWT_PUSHBTN, RESIZE_NONE,    14,    84,    99,   103,   118, 0x0,    STR_NULL},
+{    WWT_PUSHBTN, RESIZE_NONE,    14,   102,   117,   103,   118, 0x0,    STR_NULL},
+{    WWT_PUSHBTN, RESIZE_NONE,    14,   120,   135,   103,   118, 0x0,    STR_NULL},
+{    WWT_PUSHBTN, RESIZE_NONE,    14,   138,   153,   103,   118, 0x0,    STR_NULL},
+{    WWT_PUSHBTN, RESIZE_NONE,    14,   156,   171,   103,   118, 0x0,    STR_NULL},
+{    WWT_PUSHBTN, RESIZE_NONE,    14,   174,   189,   103,   118, 0x0,    STR_NULL},
+{    WWT_PUSHBTN, RESIZE_NONE,    14,   192,   207,   103,   118, 0x0,    STR_NULL},
+{    WWT_PUSHBTN, RESIZE_NONE,    14,   210,   225,   103,   118, 0x0,    STR_NULL},
+{    WWT_PUSHBTN, RESIZE_NONE,    14,   228,   243,   103,   118, 0x0,    STR_NULL},
+
+{   WIDGETS_END},
+};
+
+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
+};
+
+/**
+ * Retrieve keyboard layout from language string or (if set) config file.
+ * Also check for invalid characters.
+ */
+void GetKeyboardLayout()
+{
+	char keyboard[2][OSK_KEYBOARD_ENTRIES * 4 + 1];
+	char errormark[2][OSK_KEYBOARD_ENTRIES + 1]; // used for marking invalid chars
+	bool has_error = false; // true when an invalid char is detected
+
+	if (StrEmpty(_keyboard_opt[0])) {
+		GetString(keyboard[0], STR_OSK_KEYBOARD_LAYOUT, lastof(keyboard[0]));
+	} else {
+		strncpy(keyboard[0], _keyboard_opt[0], lengthof(keyboard[0]));
+	}
+
+	if (StrEmpty(_keyboard_opt[1])) {
+		GetString(keyboard[1], STR_OSK_KEYBOARD_LAYOUT_CAPS, lastof(keyboard[1]));
+	} else {
+		strncpy(keyboard[0], _keyboard_opt[0], lengthof(keyboard[1]));
+	}
+
+	for (uint j = 0; j < 2; j++) {
+		const char *kbd = keyboard[j];
+		for (uint i = 0; i < OSK_KEYBOARD_ENTRIES; i++) {
+			_keyboard[j][i] = Utf8Consume(&kbd);
+
+			if (IsPrintable(_keyboard[j][i])) {
+				errormark[j][i] = ' ';
+			} else {
+				has_error = true;
+				errormark[j][i] = '^';
+				_keyboard[j][i] = ' ';
+			}
+		}
+	}
+
+	if (has_error) {
+		ShowInfoF("The keyboard layout you selected contains invalid chars. Please check those chars marked with ^.");
+		ShowInfoF("Normal keyboard:  %s", keyboard[0]);
+		ShowInfoF("                  %s", errormark[0]);
+		ShowInfoF("Caps Lock:        %s", keyboard[1]);
+		ShowInfoF("                  %s", errormark[1]);
+	}
+}
+
+/**
+ * Show the osk associated with a given textbox
+ * @param parent pointer to the Window where this keyboard originated from
+ * @param q      querystr_d pointer to the query string of the parent, which is
+ *               shared for both windows
+ * @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)
+{
+	DeleteWindowById(WC_OSK, 0);
+
+	Window *w = AllocateWindowDesc(&_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;
+}
--- a/src/settings.cpp	Tue Mar 25 21:58:13 2008 +0000
+++ b/src/settings.cpp	Wed Mar 26 10:08:17 2008 +0000
@@ -49,6 +49,7 @@
 #endif
 #include "spritecache.h"
 #include "transparency.h"
+#include "textbuf_gui.h"
 #include "string_func.h"
 #include "gui.h"
 #include "town.h"
@@ -1295,6 +1296,8 @@
 	  SDTG_VAR("player_face",    SLE_UINT32, S, 0, _player_face,      0,0,0xFFFFFFFF,0, STR_NULL, NULL),
 	  SDTG_VAR("transparency_options", SLE_UINT, S, 0, _transparency_opt,  0,0,0x1FF,0, STR_NULL, NULL),
 	  SDTG_VAR("transparency_locks", SLE_UINT, S, 0, _transparency_lock,   0,0,0x1FF,0, STR_NULL, NULL),
+	  SDTG_STR("keyboard",         SLE_STRB, S, 0, _keyboard_opt[0],       NULL,    STR_NULL, NULL),
+	  SDTG_STR("keyboard_caps",    SLE_STRB, S, 0, _keyboard_opt[1],       NULL,    STR_NULL, NULL),
 	  SDTG_END()
 };
 
--- a/src/signs_gui.cpp	Tue Mar 25 21:58:13 2008 +0000
+++ b/src/signs_gui.cpp	Wed Mar 26 10:08:17 2008 +0000
@@ -164,8 +164,6 @@
 	SignID cur_sign;
 };
 
-static char _edit_str_buf[64];
-
 enum QueryEditSignWidgets {
 	QUERY_EDIT_SIGN_WIDGET_TEXT = 3,
 	QUERY_EDIT_SIGN_WIDGET_OK,
@@ -249,6 +247,10 @@
 					UpdateSignEditWindow(w, 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_DELETE:
 					/* Only need to set the buffer to null, the rest is handled as the OK button */
 					DeleteTextBufferAll(&qs->text);
@@ -287,16 +289,16 @@
 }
 
 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_PANEL, RESIZE_NONE,  14,   2, 257,  16,  27, STR_NULL,        STR_NULL },  // Text field
-{  WWT_TEXTBTN, RESIZE_NONE,  14,   0,  60,  30,  41, STR_012F_OK,     STR_NULL },
-{  WWT_TEXTBTN, RESIZE_NONE,  14,  61, 120,  30,  41, STR_012E_CANCEL, STR_NULL },
-{  WWT_TEXTBTN, RESIZE_NONE,  14, 121, 180,  30,  41, STR_0290_DELETE, STR_NULL },
-{    WWT_PANEL, RESIZE_NONE,  14, 181, 237,  30,  41, STR_NULL,        STR_NULL },
-{  WWT_TEXTBTN, RESIZE_NONE,  14, 238, 248,  30,  41, STR_6819,        STR_PREVIOUS_SIGN_TOOLTIP },
-{  WWT_TEXTBTN, RESIZE_NONE,  14, 249, 259,  30,  41, STR_681A,        STR_NEXT_SIGN_TOOLTIP },
+{ 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
+{  WWT_TEXTBTN, RESIZE_NONE,  14,   0,  60,  30,  41, STR_012F_OK,       STR_NULL },
+{  WWT_TEXTBTN, RESIZE_NONE,  14,  61, 120,  30,  41, STR_012E_CANCEL,   STR_NULL },
+{  WWT_TEXTBTN, RESIZE_NONE,  14, 121, 180,  30,  41, STR_0290_DELETE,   STR_NULL },
+{    WWT_PANEL, RESIZE_NONE,  14, 181, 237,  30,  41, STR_NULL,          STR_NULL },
+{  WWT_TEXTBTN, RESIZE_NONE,  14, 238, 248,  30,  41, STR_6819,          STR_PREVIOUS_SIGN_TOOLTIP },
+{  WWT_TEXTBTN, RESIZE_NONE,  14, 249, 259,  30,  41, STR_681A,          STR_NEXT_SIGN_TOOLTIP },
 { WIDGETS_END },
 };
 
--- a/src/table/files.h	Tue Mar 25 21:58:13 2008 +0000
+++ b/src/table/files.h	Wed Mar 26 10:08:17 2008 +0000
@@ -33,7 +33,7 @@
 		{ "TRGT.GRF",      {0xfc, 0xde, 0x1d, 0x7e, 0x8a, 0x74, 0x19, 0x7d, 0x72, 0xa6, 0x26, 0x95, 0x88, 0x4b, 0x90, 0x9e} }
 	},
 	{ "SAMPLE.CAT",    {0x42, 0x2e, 0xa3, 0xdd, 0x07, 0x4d, 0x28, 0x59, 0xbb, 0x51, 0x63, 0x9a, 0x6e, 0x0e, 0x85, 0xda} },
-	{ "OPENTTDD.GRF",  {0x61, 0x47, 0x47, 0x15, 0xa9, 0x06, 0x10, 0xb6, 0xed, 0x1f, 0xe1, 0x4d, 0x03, 0x6c, 0xa8, 0x02} }
+	{ "OPENTTDD.GRF",  {0x32, 0xb4, 0xec, 0x0c, 0xc9, 0x5d, 0xa0, 0x14, 0x3a, 0x2f, 0xe1, 0xd4, 0x20, 0x63, 0x49, 0x74} }
 };
 
 
@@ -47,5 +47,5 @@
 		{ "TRGTR.GRF",     {0xde, 0x53, 0x65, 0x05, 0x17, 0xfe, 0x66, 0x1c, 0xea, 0xa3, 0x13, 0x8c, 0x6e, 0xdb, 0x0e, 0xb8} }
 	},
 	{ "SAMPLE.CAT",    {0x92, 0x12, 0xe8, 0x1e, 0x72, 0xba, 0xdd, 0x4b, 0xbe, 0x1e, 0xae, 0xae, 0x66, 0x45, 0x8e, 0x10} },
-	{ "OPENTTDW.GRF",  {0xc8, 0xc0, 0x01, 0x73, 0x60, 0xa7, 0x5e, 0xb1, 0xef, 0x9b, 0xe6, 0xba, 0xad, 0x3a, 0x7b, 0xd4} }
+	{ "OPENTTDW.GRF",  {0xc6, 0x1f, 0xcc, 0x4e, 0x83, 0x98, 0x5b, 0x67, 0xb7, 0x03, 0xa0, 0x31, 0x39, 0x2e, 0x75, 0xfc} }
 };
--- a/src/table/sprites.h	Tue Mar 25 21:58:13 2008 +0000
+++ b/src/table/sprites.h	Wed Mar 26 10:08:17 2008 +0000
@@ -49,7 +49,7 @@
 
 	/* Extra graphic spritenumbers */
 	SPR_OPENTTD_BASE     = 4896,
-	OPENTTD_SPRITE_COUNT = 138,
+	OPENTTD_SPRITE_COUNT = 144,
 
 	/* Halftile-selection sprites */
 	SPR_HALFTILE_SELECTION_FLAT = SPR_OPENTTD_BASE,
@@ -73,6 +73,14 @@
 	SPR_PIN_UP             = SPR_OPENTTD_BASE + 51,   // pin icon
 	SPR_PIN_DOWN           = SPR_OPENTTD_BASE + 52,
 
+	/* on screen keyboard icons */
+	SPR_OSK_LEFT           = SPR_OPENTTD_BASE + 138,
+	SPR_OSK_RIGHT          = SPR_OPENTTD_BASE + 139,
+	SPR_OSK_CAPS           = SPR_OPENTTD_BASE + 140,
+	SPR_OSK_SHIFT          = SPR_OPENTTD_BASE + 141,
+	SPR_OSK_BACKSPACE      = SPR_OPENTTD_BASE + 142,
+	SPR_OSK_SPECIAL        = SPR_OPENTTD_BASE + 143,
+
 	/* Clone vehicles stuff */
 	SPR_CLONE_TRAIN    = SPR_OPENTTD_BASE + 106,
 	SPR_CLONE_ROADVEH  = SPR_OPENTTD_BASE + 107,
--- a/src/textbuf_gui.h	Tue Mar 25 21:58:13 2008 +0000
+++ b/src/textbuf_gui.h	Wed Mar 26 10:08:17 2008 +0000
@@ -7,6 +7,7 @@
 
 #include "window_type.h"
 #include "string_type.h"
+#include "strings_type.h"
 
 struct Textbuf {
 	char *buf;                  ///< buffer in which text is saved
@@ -26,6 +27,8 @@
 };
 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);
@@ -43,4 +46,16 @@
 void ShowQueryString(StringID str, StringID caption, uint maxlen, uint maxwidth, Window *parent, CharSetFilter afilter);
 void ShowQuery(StringID caption, StringID message, Window *w, void (*callback)(Window*, bool));
 
+/** The number of 'characters' on the on-screen keyboard. */
+static const uint OSK_KEYBOARD_ENTRIES = 50;
+
+/**
+ * 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 */
--- a/src/widget.cpp	Tue Mar 25 21:58:13 2008 +0000
+++ b/src/widget.cpp	Wed Mar 26 10:08:17 2008 +0000
@@ -221,6 +221,10 @@
 			DrawFrameRect(r.left, r.top, r.right, r.bottom, wi->color, (clicked) ? FR_LOWERED : FR_NONE);
 			break;
 
+		case WWT_EDITBOX:
+			DrawFrameRect(r.left, r.top, r.right, r.bottom, wi->color, FR_LOWERED | FR_DARKENED);
+			break;
+
 		case WWT_TEXTBTN:
 		case WWT_TEXTBTN_2:
 			DrawFrameRect(r.left, r.top, r.right, r.bottom, wi->color, (clicked) ? FR_LOWERED : FR_NONE);
--- a/src/window_gui.h	Tue Mar 25 21:58:13 2008 +0000
+++ b/src/window_gui.h	Wed Mar 26 10:08:17 2008 +0000
@@ -499,6 +499,7 @@
 	WWT_CLOSEBOX,
 	WWT_DROPDOWN,   ///< Raised drop down list (regular)
 	WWT_DROPDOWNIN, ///< Inset drop down list (used on game options only)
+	WWT_EDITBOX,    ///< a textbox for typing (don't forget to call ShowOnScreenKeyboard() when clicked)
 	WWT_LAST,       ///< Last Item. use WIDGETS_END to fill up padding!!
 
 	WWT_MASK = 0x1F,
--- a/src/window_type.h	Tue Mar 25 21:58:13 2008 +0000
+++ b/src/window_type.h	Wed Mar 26 10:08:17 2008 +0000
@@ -90,6 +90,7 @@
 	WC_VEHICLE_TIMETABLE,
 	WC_BUILD_SIGNAL,
 	WC_COMPANY_PASSWORD_WINDOW,
+	WC_OSK,
 };
 
 struct Window;