(svn r1894) - Codechange: cleaned up the console a bit, wholly unified handling of text with that of editboxes
authorDarkvater
Mon, 21 Feb 2005 18:59:54 +0000
changeset 1390 53a5713cf3f9
parent 1389 238570e5c3d3
child 1391 1325047def14
(svn r1894) - Codechange: cleaned up the console a bit, wholly unified handling of text with that of editboxes
- Codechange: Introduction of Textbuf struct which not only holds physical data as length but also pixel-constrains (width) and information about the caret
- Codechange: Move Clipboard function to OS specific file. Currently only Windows has clipboard actions
- Feature: Editboxes, console and exit screen also accept the numeric-enter as a yes
- Feature: Navigation through text with cursor keys is possible, as well as arbitrary insertion (also paste) and deletion; both backspace and del keys. Functions DeleteTextBufferChar, InsertTextBufferChar and InsertTextBufferClipboard handle input and deletion. Navigation is done through MoveTextBufferPos.
- Fix: OTTD crash when opening 'add server' editbox
- CodeChange: fix up some stringwidth calculations in gfx.c. You can get the width in pixels of a character by calling GetCharacterWidth().
console.c
gfx.c
gfx.h
gui.h
intro_gui.c
main_gui.c
misc_gui.c
network_gui.c
news_gui.c
os2.c
unix.c
viewport.c
win32.c
window.h
--- a/console.c	Sun Feb 20 09:05:28 2005 +0000
+++ b/console.c	Mon Feb 21 18:59:54 2005 +0000
@@ -6,6 +6,7 @@
 #include "gfx.h"
 #include "player.h"
 #include "variables.h"
+#include "string.h"
 #include "hal.h"
 #include <stdarg.h>
 #include <string.h>
@@ -14,10 +15,6 @@
 #include "network_data.h"
 #include "network_server.h"
 
-#ifdef WIN32
-#include <windows.h>
-#endif
-
 #define ICON_BUFFER 79
 #define ICON_CMDBUF_SIZE 20
 #define ICON_CMDLN_SIZE 255
@@ -30,8 +27,7 @@
 static bool _iconsole_inited;
 static char* _iconsole_buffer[ICON_BUFFER + 1];
 static uint16 _iconsole_cbuffer[ICON_BUFFER + 1];
-static char _iconsole_cmdline[ICON_CMDLN_SIZE];
-static byte _iconsole_cmdpos;
+static Textbuf _iconsole_cmdline;
 static byte _iconsole_scroll;
 
 // ** console cursor ** //
@@ -48,56 +44,25 @@
 static char* _iconsole_cmdbuffer[ICON_CMDBUF_SIZE];
 static byte _iconsole_cmdbufferpos;
 
-// ** console window ** //
-static void IConsoleWndProc(Window* w, WindowEvent* e);
-static const Widget _iconsole_window_widgets[] = {
-	{WIDGETS_END}
-};
-static const WindowDesc _iconsole_window_desc = {
-	0, 0, 2, 2,
-	WC_CONSOLE, 0,
-	WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
-	_iconsole_window_widgets,
-	IConsoleWndProc,
-};
-
 /* *************** */
 /*  end of header  */
 /* *************** */
 
-static void IConsoleAppendClipboard(void)
-{
-#ifdef WIN32
-	if (IsClipboardFormatAvailable(CF_TEXT)) {
-		const char* data;
-		HGLOBAL cbuf;
-
-		OpenClipboard(NULL);
-		cbuf = GetClipboardData(CF_TEXT);
-		data = GlobalLock(cbuf);
-
-		/* IS_INT_INSIDE = filter for ascii-function codes like BELL and so on [we need an special filter here later] */
-		for (; (IS_INT_INSIDE(*data, ' ', 256)) && (_iconsole_cmdpos < lengthof(_iconsole_cmdline) - 1); ++data)
-			_iconsole_cmdline[_iconsole_cmdpos++] = *data;
-
-		GlobalUnlock(cbuf);
-		CloseClipboard();
-	}
-#endif
-}
-
 static void IConsoleClearCommand(void)
 {
-	memset(_iconsole_cmdline, 0, sizeof(_iconsole_cmdline));
-	_iconsole_cmdpos = 0;
+	memset(_iconsole_cmdline.buf, 0, ICON_CMDLN_SIZE);
+	_iconsole_cmdline.length = 0;
+	_iconsole_cmdline.width = 0;
+	_iconsole_cmdline.caretpos = 0;
+	_iconsole_cmdline.caretxoffs = 0;
 	SetWindowDirty(_iconsole_win);
 }
 
+// ** console window ** //
 static void IConsoleWndProc(Window* w, WindowEvent* e)
 {
 	switch(e->event) {
-		case WE_PAINT:
-		{
+		case WE_PAINT: {
 			int i = _iconsole_scroll;
 			int max = (w->height / ICON_LINE_HEIGHT) - 1;
 			int delta = 0;
@@ -107,47 +72,30 @@
 					w->height - (_iconsole_scroll + 2 - i) * ICON_LINE_HEIGHT, _iconsole_cbuffer[i]);
 				i--;
 			}
-			delta = w->width - 10 - GetStringWidth(_iconsole_cmdline) - ICON_RIGHT_BORDERWIDTH;
+			/* If the text is longer than the window, don't show the starting ']' */
+			delta = w->width - 10 - _iconsole_cmdline.width - ICON_RIGHT_BORDERWIDTH;
 			if (delta > 0) {
 				DoDrawString("]", 5, w->height - ICON_LINE_HEIGHT, _iconsole_color_commands);
 				delta = 0;
 			}
 
-			DoDrawString(_iconsole_cmdline, 10 + delta, w->height - ICON_LINE_HEIGHT, _iconsole_color_commands);
+			DoDrawString(_iconsole_cmdline.buf, 10 + delta, w->height - ICON_LINE_HEIGHT, _iconsole_color_commands);
+
+			if (_iconsole_cmdline.caret)
+				DoDrawString("_", 10 + delta + _iconsole_cmdline.caretxoffs, w->height - ICON_LINE_HEIGHT, 12);
 			break;
 		}
-		case WE_TICK:
-			_icursor_counter++;
-			if (_icursor_counter > _icursor_rate) {
-				int posx;
-				int posy;
-				int delta;
-
-				_icursor_state = !_icursor_state;
-
-				_cur_dpi = &_screen;
-				delta = w->width - 10 - GetStringWidth(_iconsole_cmdline) - ICON_RIGHT_BORDERWIDTH;
-				if (delta > 0)
-					delta = 0;
-				posx = 10 + GetStringWidth(_iconsole_cmdline) + delta;
-				posy = w->height - 3;
-				GfxFillRect(posx, posy, posx + 5, posy + 1, _icursor_state ? 14 : 0);
-				_video_driver->make_dirty(posx, posy, 5, 1);
-				_icursor_counter = 0;
-			}
+		case WE_MOUSELOOP:
+			if (HandleCaret(&_iconsole_cmdline))
+				SetWindowDirty(w);
 			break;
 		case WE_DESTROY:
 			_iconsole_win = NULL;
 			_iconsole_mode = ICONSOLE_CLOSED;
 			break;
 		case WE_KEYPRESS:
-		{
 			e->keypress.cont = false;
 			switch (e->keypress.keycode) {
-				case WKC_CTRL | 'V':
-					IConsoleAppendClipboard();
-					SetWindowDirty(w);
-					break;
 				case WKC_UP:
 					IConsoleCmdBufferNavigate(+1);
 					SetWindowDirty(w);
@@ -187,47 +135,57 @@
 				case WKC_BACKQUOTE:
 					IConsoleSwitch();
 					break;
-				case WKC_RETURN:
-					IConsolePrintF(_iconsole_color_commands, "] %s", _iconsole_cmdline);
-					_iconsole_cmdbufferpos = 19;
-					IConsoleCmdBufferAdd(_iconsole_cmdline);
+				case WKC_RETURN: case WKC_NUM_ENTER:
+					IConsolePrintF(_iconsole_color_commands, "] %s", _iconsole_cmdline.buf);
+					_iconsole_cmdbufferpos = ICON_CMDBUF_SIZE - 1;
+					IConsoleCmdBufferAdd(_iconsole_cmdline.buf);
 
-					IConsoleCmdExec(_iconsole_cmdline);
+					IConsoleCmdExec(_iconsole_cmdline.buf);
 					IConsoleClearCommand();
 					break;
 				case WKC_CTRL | WKC_RETURN:
-					if (_iconsole_mode == ICONSOLE_FULL) {
-						_iconsole_mode = ICONSOLE_OPENED;
-					} else {
-						_iconsole_mode = ICONSOLE_FULL;
-					}
+					_iconsole_mode = (_iconsole_mode == ICONSOLE_FULL) ? ICONSOLE_OPENED : ICONSOLE_FULL;
 					IConsoleResize();
 					MarkWholeScreenDirty();
 					break;
-				case WKC_BACKSPACE:
-					if (_iconsole_cmdpos != 0) _iconsole_cmdpos--;
-					_iconsole_cmdline[_iconsole_cmdpos] = 0;
-					SetWindowDirty(w);
-					_iconsole_cmdbufferpos = 19;
+				case (WKC_CTRL | 'V'):
+					if (InsertTextBufferClipboard(&_iconsole_cmdline))
+						SetWindowDirty(w);
+					break;
+				case WKC_BACKSPACE: case WKC_DELETE:
+					if (DeleteTextBufferChar(&_iconsole_cmdline, e->keypress.keycode))
+						SetWindowDirty(w);
+					_iconsole_cmdbufferpos = ICON_CMDBUF_SIZE - 1;
+					break;
+				case WKC_LEFT: case WKC_RIGHT: case WKC_END: case WKC_HOME:
+					if (MoveTextBufferPos(&_iconsole_cmdline, e->keypress.keycode))
+						SetWindowDirty(w);
 					break;
 				default:
-					/* IS_INT_INSIDE = filter for ascii-function codes like BELL and so on [we need an special filter here later] */
-					if (IS_INT_INSIDE(e->keypress.ascii, ' ', 256)) {
+					if (IsValidAsciiChar(e->keypress.ascii)) {
 						_iconsole_scroll = ICON_BUFFER;
-						_iconsole_cmdline[_iconsole_cmdpos] = e->keypress.ascii;
-						if (_iconsole_cmdpos != lengthof(_iconsole_cmdline))
-							_iconsole_cmdpos++;
+						InsertTextBufferChar(&_iconsole_cmdline, e->keypress.ascii);
+						_iconsole_cmdbufferpos = ICON_CMDBUF_SIZE - 1;
 						SetWindowDirty(w);
-						_iconsole_cmdbufferpos = ICON_CMDBUF_SIZE - 1;
-					}
-					else
+					} else
 						e->keypress.cont = true;
-			}
 			break;
 		}
 	}
 }
 
+static const Widget _iconsole_window_widgets[] = {
+	{WIDGETS_END}
+};
+
+static const WindowDesc _iconsole_window_desc = {
+	0, 0, 2, 2,
+	WC_CONSOLE, 0,
+	WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
+	_iconsole_window_widgets,
+	IConsoleWndProc,
+};
+
 extern const char _openttd_revision[];
 
 void IConsoleInit(void)
@@ -254,6 +212,8 @@
 	memset(_iconsole_cmdbuffer, 0, sizeof(_iconsole_cmdbuffer));
 	memset(_iconsole_buffer, 0, sizeof(_iconsole_buffer));
 	memset(_iconsole_cbuffer, 0, sizeof(_iconsole_cbuffer));
+	_iconsole_cmdline.buf = calloc(ICON_CMDLN_SIZE, sizeof(*_iconsole_cmdline.buf)); // create buffer and zero it
+	_iconsole_cmdline.maxlength = ICON_CMDLN_SIZE - 1;
 
 	IConsoleStdLibRegister();
 	IConsolePrintF(13, "OpenTTD Game Console Revision 6 - %s", _openttd_revision);
@@ -269,6 +229,8 @@
 	uint i;
 	for (i = 0; i <= ICON_BUFFER; i++)
 		free(_iconsole_buffer[i]);
+
+	free(_iconsole_cmdline.buf);
 }
 
 static void IConsoleWriteToLogFile(const char* string)
@@ -357,9 +319,9 @@
 void IConsoleCmdBufferAdd(const char* cmd)
 {
 	int i;
-	if (_iconsole_cmdbufferpos != 19) return;
-	free(_iconsole_cmdbuffer[18]);
-	for (i = 18; i > 0; i--) _iconsole_cmdbuffer[i] = _iconsole_cmdbuffer[i - 1];
+	if (_iconsole_cmdbufferpos != (ICON_CMDBUF_SIZE - 1)) return;
+	free(_iconsole_cmdbuffer[ICON_CMDBUF_SIZE - 2]);
+	for (i = (ICON_CMDBUF_SIZE - 2); i > 0; i--) _iconsole_cmdbuffer[i] = _iconsole_cmdbuffer[i - 1];
 	_iconsole_cmdbuffer[0] = strdup(cmd);
 }
 
@@ -367,23 +329,22 @@
 {
 	int i;
 	i = _iconsole_cmdbufferpos + direction;
-	if (i < 0) i = 19;
-	if (i > 19) i = 0;
+	if (i < 0) i = ICON_CMDBUF_SIZE - 1;
+	if (i >= ICON_CMDBUF_SIZE) i = 0;
 	if (direction > 0)
 		while (_iconsole_cmdbuffer[i] == NULL) {
-			++i;
-			if (i > 19) i = 0;
+			i++;
+			if (i >= ICON_CMDBUF_SIZE) i = 0;
 		}
 	if (direction < 0)
 		while (_iconsole_cmdbuffer[i] == NULL) {
 			--i;
-			if (i < 0) i = 19;
+			if (i < 0) i = ICON_CMDBUF_SIZE - 1;
 		}
 	_iconsole_cmdbufferpos = i;
 	IConsoleClearCommand();
-	memcpy(_iconsole_cmdline, _iconsole_cmdbuffer[i],
-		strlen(_iconsole_cmdbuffer[i]));
-	_iconsole_cmdpos = strlen(_iconsole_cmdbuffer[i]);
+	ttd_strlcpy(_iconsole_cmdline.buf, _iconsole_cmdbuffer[i], _iconsole_cmdline.maxlength);
+	UpdateTextBufferSize(&_iconsole_cmdline);
 }
 
 void IConsolePrint(uint16 color_code, const char* string)
--- a/gfx.c	Sun Feb 20 09:05:28 2005 +0000
+++ b/gfx.c	Mon Feb 21 18:59:54 2005 +0000
@@ -244,8 +244,6 @@
 
 
 enum {
-	ASCII_LETTERSTART = 32,
-
 	ASCII_SETX = 1,
 	ASCII_SETXY = 2,
 
@@ -309,10 +307,10 @@
 
 		for(;;) {
 			c = *str++;
-			if (c == ' ') last_space = str;
+			if (c == ASCII_LETTERSTART) last_space = str;
 
 			if (c >= ASCII_LETTERSTART) {
-				w += _stringwidth_table[base + ((byte)c) - 0x20];
+				w += GetCharacterWidth(base + (byte)c);
 				if (w > maxw) {
 					str = last_space;
 					if (str == NULL)
@@ -428,16 +426,12 @@
 
 int GetStringWidth(const char *str)
 {
-	int w = -1;
+	int w = 0;
 	byte c;
 	int base = _stringwidth_base;
-
-	for(;;) {
-		c = *str++;
-		if (c == 0)
-			return w;
+	for (c = *str; c != '\0'; c = *(++str)) {
 		if (c >= ASCII_LETTERSTART) {
-			w += _stringwidth_table[base + c - ASCII_LETTERSTART];
+			w += GetCharacterWidth(base + c);
 		} else {
 			if (c == ASCII_SETX) str++;
 			else if (c == ASCII_SETXY) str += 2;
@@ -445,6 +439,7 @@
 			else if (c == ASCII_BIGFONT) base = 448;
 		}
 	}
+	return w;
 }
 
 void DrawFrameRect(int left, int top, int right, int bottom, int ctab, int flags) {
@@ -531,7 +526,7 @@
 			if (x + 26 >= dpi->left) {
 				GfxMainBlitter(GetSprite(base + 2 + c - ASCII_LETTERSTART), x, y, 1);
 			}
-			x += _stringwidth_table[base + c - ' '];
+			x += GetCharacterWidth(base + c);
 		} else if (c == ASCII_NL) { // newline = {}
 			x = xo;
 			y += 10;
--- a/gfx.h	Sun Feb 20 09:05:28 2005 +0000
+++ b/gfx.h	Mon Feb 21 18:59:54 2005 +0000
@@ -69,8 +69,10 @@
 void ToggleFullScreen(const bool full_screen);
 
 /* gfx.c */
+#define ASCII_LETTERSTART 32
 VARDEF int _stringwidth_base;
 VARDEF byte _stringwidth_table[0x2A0];
+static inline byte GetCharacterWidth(int key) { return _stringwidth_table[key - ASCII_LETTERSTART];}
 
 VARDEF DrawPixelInfo _screen;
 VARDEF DrawPixelInfo *_cur_dpi;
--- a/gui.h	Sun Feb 20 09:05:28 2005 +0000
+++ b/gui.h	Mon Feb 21 18:59:54 2005 +0000
@@ -64,6 +64,7 @@
 void ShowTerraformToolbar(void);
 
 /* misc_gui.c */
+VARDEF Window *_editbox_win; // pointer to querystringwindow to prevent scrolling when focussed
 void PlaceLandBlockInfo(void);
 void ShowAboutWindow(void);
 void ShowBuildTreesToolbar(void);
@@ -86,13 +87,22 @@
 void SetVScrollCount(Window *w, int num);
 void SetVScroll2Count(Window *w, int num);
 void SetHScrollCount(Window *w, int num);
-int HandleEditBoxKey(Window *w, int wid, WindowEvent *we);
 
 void ShowCheatWindow(void);
 void AskForNewGameToStart(void);
 
 void DrawEditBox(Window *w, int wid);
 void HandleEditBox(Window *w, int wid);
+int HandleEditBoxKey(Window *w, int wid, WindowEvent *we);
+bool HandleCaret(Textbuf *tb);
+
+bool DeleteTextBufferChar(Textbuf *tb, int delmode);
+bool InsertTextBufferChar(Textbuf *tb, byte key);
+bool InsertTextBufferClipboard(Textbuf *tb);
+bool MoveTextBufferPos(Textbuf *tb, int navmode);
+void UpdateTextBufferSize(Textbuf *tb);
+/* IS_INT_INSIDE = filter for ascii-function codes like BELL and so on [we need an special filter here later] */
+static inline bool IsValidAsciiChar(byte key) {return IS_INT_INSIDE(key, ' ', 256);}
 
 void BuildFileList(void);
 void SetFiosType(const byte fiostype);
--- a/intro_gui.c	Sun Feb 20 09:05:28 2005 +0000
+++ b/intro_gui.c	Mon Feb 21 18:59:54 2005 +0000
@@ -218,7 +218,7 @@
 		}
 		break;
 	case WE_KEYPRESS: /* Exit game on pressing 'Enter' */
-		if (e->keypress.keycode == WKC_RETURN)
+		if (e->keypress.keycode == WKC_RETURN || e->keypress.keycode == WKC_NUM_ENTER)
 			_exit_game = true;
 		break;
 	}
--- a/main_gui.c	Sun Feb 20 09:05:28 2005 +0000
+++ b/main_gui.c	Mon Feb 21 18:59:54 2005 +0000
@@ -2252,7 +2252,7 @@
 
 void HandleKeyScrolling(void)
 {
-	if (_dirkeys && _iconsole_win == NULL) {
+	if (_dirkeys && _iconsole_win == NULL && _editbox_win == NULL) {
 		int factor = _shift_pressed ? 50 : 10;
 		ScrollMainViewport(scrollamt[_dirkeys][0] * factor, scrollamt[_dirkeys][1] * factor);
 	}
--- a/misc_gui.c	Sun Feb 20 09:05:28 2005 +0000
+++ b/misc_gui.c	Mon Feb 21 18:59:54 2005 +0000
@@ -16,11 +16,7 @@
 #include "town.h"
 #include "sound.h"
 #include "network.h"
-
-// Windows stuff for Clipboard
-#if defined(WIN32)
-#include <windows.h>
-#endif
+#include "string.h"
 
 #include "hal.h" // for file list
 
@@ -779,77 +775,140 @@
 	if (num < w->hscroll.pos) w->hscroll.pos = num;
 }
 
-/* Get the count of characters in the string as well as the width in pixels
- * [IN]buf: string to be checked
- * [OUT]count: gets set to the count of characters
- * [OUT]width: gets set to the pixels width */
-static void GetCurrentStringSize(const char *buf, int *count, int *width)
+static void DelChar(Textbuf *tb)
 {
-	*count = 0;
-	*width = -1;
+	tb->width -= GetCharacterWidth(tb->buf[tb->caretpos]);
+	memmove(tb->buf + tb->caretpos, tb->buf + tb->caretpos + 1, tb->length - tb->caretpos);
+	tb->length--;
+}
 
-	do {
-		if (*++buf == 0)
-			break;
-		(*count)++;
-		(*width) += _stringwidth_table[(byte)*buf - 32];
-	} while (1);
+/**
+ * Delete a character from a textbuffer, either with 'Delete' or 'Backspace'
+ * The character is delete from the position the caret is at
+ * @param tb @Textbuf type to be changed
+ * @param delmode Type of deletion, either @WKC_BACKSPACE or @WKC_DELETE
+ * @return Return true on successfull change of Textbuf, or false otherwise
+ */
+bool DeleteTextBufferChar(Textbuf *tb, int delmode)
+{
+	if (delmode == WKC_BACKSPACE && tb->caretpos != 0) {
+		tb->caretpos--;
+		tb->caretxoffs -= GetCharacterWidth(tb->buf[tb->caretpos]);
+
+		DelChar(tb);
+		return true;
+	} else if (delmode == WKC_DELETE && tb->caretpos < tb->length) {
+		DelChar(tb);
+		return true;
+	}
+
+	return false;
+}
+
+/**
+ * Insert a character to a textbuffer. If maxlength is zero, we don't care about
+ * the screenlength but only about the physical length of the string
+ * @param tb @Textbuf type to be changed
+ * @param key Character to be inserted
+ * @return Return true on successfull change of Textbuf, or false otherwise
+ */
+bool InsertTextBufferChar(Textbuf *tb, byte key)
+{
+	const byte charwidth = GetCharacterWidth(key);
+	if (tb->length < tb->maxlength && (tb->maxwidth == 0 || tb->width + charwidth <= tb->maxwidth)) {
+		memmove(tb->buf + tb->caretpos + 1, tb->buf + tb->caretpos, tb->length - tb->caretpos);
+		tb->buf[tb->caretpos] = key;
+		tb->length++;
+		tb->width += charwidth;
+
+		tb->caretpos++;
+		tb->caretxoffs += charwidth;
+		return true;
+	}
+	return false;
+}
+
+/**
+ * Handle text navigation with arrow keys left/right.
+ * This defines where the caret will blink and the next characer interaction will occur
+ * @param tb @Textbuf type where navigation occurs
+ * @param navmode Direction in which navigation occurs @WKC_LEFT, @WKC_RIGHT, @WKC_END, @WKC_HOME
+ * @return Return true on successfull change of Textbuf, or false otherwise
+ */
+bool MoveTextBufferPos(Textbuf *tb, int navmode)
+{
+	switch (navmode) {
+	case WKC_LEFT:
+		if (tb->caretpos != 0) {
+			tb->caretpos--;
+			tb->caretxoffs -= GetCharacterWidth(tb->buf[tb->caretpos]);
+			return true;
+		}
+		break;
+	case WKC_RIGHT:
+		if (tb->caretpos < tb->length) {
+			tb->caretxoffs += GetCharacterWidth(tb->buf[tb->caretpos]);
+			tb->caretpos++;
+			return true;
+		}
+		break;
+	case WKC_HOME:
+		tb->caretpos = 0;
+		tb->caretxoffs = 0;
+		return true;
+	case WKC_END:
+		tb->caretpos = tb->length;
+		tb->caretxoffs = tb->width;
+		return true;
+	}
+
+	return false;
+}
+
+/**
+ * Update @Textbuf type with its actual physical character and screenlength
+ * Get the count of characters in the string as well as the width in pixels.
+ * Useful when copying in a larger amount of text at once
+ * @param tb @Textbuf type which length is calculated
+ */
+void UpdateTextBufferSize(Textbuf *tb)
+{
+	char *buf;
+	tb->length = 0;
+	tb->width = 0;
+
+	for (buf = tb->buf; *buf != '\0' && tb->length <= tb->maxlength; buf++) {
+		tb->length++;
+		tb->width += GetCharacterWidth((byte)*buf);
+	}
+
+	tb->caretpos = tb->length;
+	tb->caretxoffs = tb->width;
 }
 
 int HandleEditBoxKey(Window *w, int wid, WindowEvent *we)
 {
-	int width,count;
-	int key = we->keypress.ascii;
-
 	we->keypress.cont = false;
 
-	if (we->keypress.keycode == WKC_ESC) {
-		return 2;
-	} else if (we->keypress.keycode == WKC_RETURN) {
-		return 1;
-#ifdef WIN32
-	} else if (we->keypress.keycode == (WKC_CTRL | 'V')) {
-		if (IsClipboardFormatAvailable(CF_TEXT)) {
-			const byte* data;
-			HGLOBAL cbuf;
-
-			OpenClipboard(NULL);
-			cbuf = GetClipboardData(CF_TEXT);
-			data = GlobalLock(cbuf); // clipboard data
-
-			GetCurrentStringSize(WP(w,querystr_d).buf - 1, &count, &width);
-
-			/* IS_INT_INSIDE = filter for ascii-function codes like BELL and so on [we need an special filter here later] */
-			for (; (IS_INT_INSIDE(*data, ' ', 256)) && // valid ASCII char
-					(count < WP(w,querystr_d).maxlen - 1 && // max charcount; always allow for terminating '\0'
-					width + _stringwidth_table[(int)(*data) - 32] <= WP(w,querystr_d).maxwidth); ++data) { // max screensize
-
-				// append data and update size parameters
-				WP(w,querystr_d).buf[count] = *data;
-				count++;
-				width += _stringwidth_table[*data - 32];
-			}
-			WP(w,querystr_d).buf[count + 1] = '\0';
-
-			GlobalUnlock(cbuf);
-			CloseClipboard();
+	switch (we->keypress.keycode) {
+	case WKC_ESC: return 2;
+	case WKC_RETURN: case WKC_NUM_ENTER: return 1;
+	case (WKC_CTRL | 'V'):
+		if (InsertTextBufferClipboard(&WP(w, querystr_d).text))
 			InvalidateWidget(w, wid);
-		}
-#endif
-	} else {
-		GetCurrentStringSize(WP(w,querystr_d).buf - 1, &count, &width);
-
-		if (we->keypress.keycode == WKC_BACKSPACE) {
-			if (count != 0) {
-				WP(w,querystr_d).buf[count-1] = 0;
+		break;
+	case WKC_BACKSPACE: case WKC_DELETE:
+		if (DeleteTextBufferChar(&WP(w, querystr_d).text, we->keypress.keycode))
+			InvalidateWidget(w, wid);
+		break;
+	case WKC_LEFT: case WKC_RIGHT: case WKC_END: case WKC_HOME:
+		if (MoveTextBufferPos(&WP(w, querystr_d).text, we->keypress.keycode))
+			InvalidateWidget(w, wid);
+  	break;
+	default:
+		if (IsValidAsciiChar(we->keypress.ascii)) {
+			if (InsertTextBufferChar(&WP(w, querystr_d).text, we->keypress.ascii))
 				InvalidateWidget(w, wid);
-			}
-		} else if (IS_INT_INSIDE((key = we->keypress.ascii), 32, 256)) {
-			if (count < WP(w,querystr_d).maxlen && width + _stringwidth_table[key - 32] <= WP(w,querystr_d).maxwidth) {
-				WP(w,querystr_d).buf[count] = key;
-				WP(w,querystr_d).buf[count + 1] = '\0';
-				InvalidateWidget(w, wid);
-			}
 		} else // key wasn't caught
 			we->keypress.cont = true;
 	}
@@ -857,42 +916,45 @@
 	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, int wid)
 {
-	bool b;
-
-	/* caret changed? */
-	b = !!(_caret_timer & 0x20);
-	if (b != WP(w,querystr_d).caret) {
-		WP(w,querystr_d).caret = b;
+	if (HandleCaret(&WP(w, querystr_d).text))
 		InvalidateWidget(w, wid);
-	}
 }
 
 void DrawEditBox(Window *w, int wid)
 {
 	const Widget *wi = w->widget + wid;
-	int x;
+	const Textbuf *tb = &WP(w,querystr_d).text;
 
 	GfxFillRect(wi->left+1, wi->top+1, wi->right-1, wi->bottom-1, 215);
-	x = DoDrawString(WP(w,querystr_d).buf, wi->left+2, wi->top+1, 8);
-	if (WP(w,querystr_d).caret)
-		DoDrawString("_", x, wi->top+1, 12);
+	DoDrawString(tb->buf, wi->left+2, wi->top+1, 8);
+	if (tb->caret)
+		DoDrawString("_", wi->left + 2 + tb->caretxoffs, wi->top + 1, 12);
 }
 
-
 static void QueryStringWndProc(Window *w, WindowEvent *e)
 {
 	static bool closed = false;
 	switch(e->event) {
-	case WE_PAINT: {
-//		int x;
-
+	case WE_PAINT:
 		SetDParam(0, WP(w,querystr_d).caption);
 		DrawWindowWidgets(w);
 
 		DrawEditBox(w, 5);
-	} break;
+		break;
 
 	case WE_CLICK:
 		switch(e->click.widget) {
@@ -900,10 +962,10 @@
 		case 4:
 press_ok:;
 			if (WP(w, querystr_d).orig != NULL &&
-					strcmp(WP(w, querystr_d).buf, WP(w, querystr_d).orig) == 0) {
+					strcmp(WP(w, querystr_d).text.buf, WP(w, querystr_d).orig) == 0) {
 				DeleteWindow(w);
 			} else {
-				char *buf = WP(w,querystr_d).buf;
+				char *buf = WP(w,querystr_d).text.buf;
 				WindowClass wnd_class = WP(w,querystr_d).wnd_class;
 				WindowNumber wnd_num = WP(w,querystr_d).wnd_num;
 				Window *parent;
@@ -945,6 +1007,7 @@
 
 	case WE_CREATE:
 		closed = false;
+		_editbox_win = w;
 		break;
 
 	case WE_DESTROY:
@@ -958,6 +1021,7 @@
 			}
 		}
 		_query_string_active = false;
+		_editbox_win = NULL;
 		break;
 	}
 }
@@ -986,8 +1050,9 @@
 void ShowQueryString(StringID str, StringID caption, uint maxlen, uint maxwidth, WindowClass window_class, WindowNumber window_number)
 {
 	Window *w;
+	uint realmaxlen = maxlen & ~0x1000;
 
-	assert(maxlen < lengthof(_edit_str_buf));
+	assert(realmaxlen < lengthof(_edit_str_buf));
 
 	DeleteWindowById(WC_QUERY_STRING, 0);
 	DeleteWindowById(WC_SAVELOAD, 0);
@@ -995,24 +1060,24 @@
 	w = AllocateWindowDesc(&_query_string_desc);
 
 	GetString(_edit_str_buf, str);
-	_edit_str_buf[maxlen] = '\0';
+	_edit_str_buf[realmaxlen] = '\0';
 
 	if (maxlen & 0x1000) {
 		WP(w, querystr_d).orig = NULL;
-		maxlen &= ~0x1000;
 	} else {
 		strcpy(_orig_str_buf, _edit_str_buf);
 		WP(w, querystr_d).orig = _orig_str_buf;
 	}
 
 	w->click_state = 1 << 5;
-	WP(w,querystr_d).caption = caption;
-	WP(w,querystr_d).wnd_class = window_class;
-	WP(w,querystr_d).wnd_num = window_number;
-	WP(w,querystr_d).caret = 0;
-	WP(w,querystr_d).maxlen = maxlen;
-	WP(w,querystr_d).maxwidth = maxwidth;
-	WP(w,querystr_d).buf = _edit_str_buf;
+	WP(w, querystr_d).caption = caption;
+	WP(w, querystr_d).wnd_class = window_class;
+	WP(w, querystr_d).wnd_num = window_number;
+	WP(w, querystr_d).text.caret = false;
+	WP(w, querystr_d).text.maxlength = realmaxlen - 1;
+	WP(w, querystr_d).text.maxwidth = maxwidth;
+	WP(w, querystr_d).text.buf = _edit_str_buf;
+	UpdateTextBufferSize(&WP(w, querystr_d).text);
 
 	_query_string_active = true;
 }
@@ -1220,7 +1285,8 @@
 					DeleteWindow(w);
 				} else {
 					// SLD_SAVE_GAME, SLD_SAVE_SCENARIO copy clicked name to editbox
-					strcpy(WP(w,querystr_d).buf, file->title[0] ? file->title : file->name);
+					ttd_strlcpy(WP(w, querystr_d).text.buf, (file->title[0] != '\0') ? file->title : file->name, WP(w, querystr_d).text.maxlength);
+					UpdateTextBufferSize(&WP(w, querystr_d).text);
 					InvalidateWidget(w, 9);
 				}
 			} else {
@@ -1246,14 +1312,14 @@
 		break;
 	case WE_TIMEOUT:
 		if (HASBIT(w->click_state, 10)) { /* Delete button clicked */
-			FiosDelete(WP(w,querystr_d).buf);
+			FiosDelete(WP(w,querystr_d).text.buf);
 			SetWindowDirty(w);
 			BuildFileList();
 			if (_saveload_mode == SLD_SAVE_GAME)
 				GenerateFileName(); /* Reset file name to current date */
 		} else if (HASBIT(w->click_state, 11)) { /* Save button clicked */
 			_switch_mode = SM_SAVE;
-			FiosMakeSavegameName(_file_to_saveload.name, WP(w,querystr_d).buf);
+			FiosMakeSavegameName(_file_to_saveload.name, WP(w,querystr_d).text.buf);
 
 			/* In the editor set up the vehicle engines correctly (date might have changed) */
 			if (_game_mode == GM_EDITOR) StartupEngines();
@@ -1339,17 +1405,17 @@
 	w->resize.step_width = 2;
 	w->resize.step_height = 10;
 	w->resize.height = w->height - 14 * 10; // Minimum of 10 items
-	w->click_state |= (1 << 6);
-	WP(w,querystr_d).caret = 0;
-	WP(w,querystr_d).maxlen = lengthof(_edit_str_buf);
-	WP(w,querystr_d).maxwidth = 240;
-	WP(w,querystr_d).buf = _edit_str_buf;
+	SETBIT(w->click_state, 6);
+	WP(w,querystr_d).text.caret = false;
+	WP(w,querystr_d).text.maxlength = lengthof(_edit_str_buf) - 1;
+	WP(w,querystr_d).text.maxwidth = 240;
+	WP(w,querystr_d).text.buf = _edit_str_buf;
+	UpdateTextBufferSize(&WP(w, querystr_d).text);
 
 	if (mode == SLD_SAVE_GAME) {
 		GenerateFileName();
-	} else if (mode == SLD_SAVE_SCENARIO) {
+	} else if (mode == SLD_SAVE_SCENARIO)
 		strcpy(_edit_str_buf, "UNNAMED");
-	}
 
 	// pause is only used in single-player, non-editor mode, non-menu mode. It
 	// will be unpaused in the WE_DESTROY event handler.
--- a/network_gui.c	Sun Feb 20 09:05:28 2005 +0000
+++ b/network_gui.c	Mon Feb 21 18:59:54 2005 +0000
@@ -449,10 +449,11 @@
 	ttd_strlcpy(_edit_str_buf, _network_player_name, MAX_QUERYSTR_LEN);
 	w->vscroll.cap = 8;
 
-	WP(w,querystr_d).caret = 1;
-	WP(w,querystr_d).maxlen = MAX_QUERYSTR_LEN;
-	WP(w,querystr_d).maxwidth = 120;
-	WP(w,querystr_d).buf = _edit_str_buf;
+	WP(w, querystr_d).text.caret = true;
+	WP(w, querystr_d).text.maxlength = MAX_QUERYSTR_LEN - 1;
+	WP(w, querystr_d).text.maxwidth = 120;
+	WP(w, querystr_d).text.buf = _edit_str_buf;
+	UpdateTextBufferSize(&WP(w, querystr_d).text);
 
 	UpdateNetworkGameWindow(true);
 }
@@ -553,7 +554,8 @@
 			return;
 		case 13: /* Start game */
 			_is_network_server = true;
-			ttd_strlcpy(_network_server_name, WP(w,querystr_d).buf, sizeof(_network_server_name));
+			ttd_strlcpy(_network_server_name, WP(w, querystr_d).text.buf, sizeof(_network_server_name));
+			UpdateTextBufferSize(&WP(w, querystr_d).text);
 			if(selected_map==NULL) { // start random new game
 				DoCommandP(0, Random(), InteractiveRandom(), NULL, CMD_GEN_RANDOM_NEW_GAME);
 			} else { // load a scenario
@@ -569,7 +571,8 @@
 			break;
 		case 14: /* Load game */
 			_is_network_server = true;
-			ttd_strlcpy(_network_server_name, WP(w,querystr_d).buf, sizeof(_network_server_name));
+			ttd_strlcpy(_network_server_name, WP(w, querystr_d).text.buf, sizeof(_network_server_name));
+			UpdateTextBufferSize(&WP(w, querystr_d).text);
 			snprintf(_network_game_info.map_name, sizeof(_network_game_info.map_name), "Loaded game");
 			/* XXX - WC_NETWORK_WINDOW should stay, but if it stays, it gets
 			 * copied all the elements of 'load game' and upon closing that, it segfaults */
@@ -670,10 +673,11 @@
 	w->vscroll.cap = 9;
 	w->vscroll.count = _fios_num+1;
 
-	WP(w,querystr_d).caret = 1;
-	WP(w,querystr_d).maxlen = MAX_QUERYSTR_LEN;
-	WP(w,querystr_d).maxwidth = 160;
-	WP(w,querystr_d).buf = _edit_str_buf;
+	WP(w, querystr_d).text.caret = true;
+	WP(w, querystr_d).text.maxlength = MAX_QUERYSTR_LEN - 1;
+	WP(w, querystr_d).text.maxwidth = 160;
+	WP(w, querystr_d).text.buf = _edit_str_buf;
+	UpdateTextBufferSize(&WP(w, querystr_d).text);
 }
 
 static byte NetworkLobbyFindCompanyIndex(byte pos)
@@ -1371,12 +1375,12 @@
 		case 3: DeleteWindow(w); break; // Cancel
 		case 2: // Send
 press_ok:;
-			if (strcmp(WP(w,querystr_d).buf, WP(w,querystr_d).buf + MAX_QUERYSTR_LEN) == 0) {
+			if (strcmp(WP(w, querystr_d).text.buf, WP(w, querystr_d).text.buf + MAX_QUERYSTR_LEN) == 0) {
 				DeleteWindow(w);
 			} else {
-				char *buf = WP(w,querystr_d).buf;
-				WindowClass wnd_class = WP(w,querystr_d).wnd_class;
-				WindowNumber wnd_num = WP(w,querystr_d).wnd_num;
+				char *buf = WP(w, querystr_d).text.buf;
+				WindowClass wnd_class = WP(w, querystr_d).wnd_class;
+				WindowNumber wnd_num = WP(w, querystr_d).wnd_num;
 				Window *parent;
 
 				// Mask the edit-box as closed, so we don't send out a CANCEL
@@ -1458,7 +1462,7 @@
 
 	GetString(_orig_edit_str_buf, str);
 
-	_orig_edit_str_buf[maxlen] = 0;
+	_orig_edit_str_buf[maxlen] = '\0';
 
 	memcpy(_edit_str_buf, _orig_edit_str_buf, MAX_QUERYSTR_LEN);
 
@@ -1468,10 +1472,11 @@
 	WP(w,querystr_d).caption = caption;
 	WP(w,querystr_d).wnd_class = window_class;
 	WP(w,querystr_d).wnd_num = window_number;
-	WP(w,querystr_d).caret = 0;
-	WP(w,querystr_d).maxlen = maxlen;
-	WP(w,querystr_d).maxwidth = maxwidth;
-	WP(w,querystr_d).buf = _edit_str_buf;
+	WP(w,querystr_d).text.caret = false;
+	WP(w,querystr_d).text.maxlength = maxlen - 1;
+	WP(w,querystr_d).text.maxwidth = maxwidth;
+	WP(w,querystr_d).text.buf = _edit_str_buf;
+	UpdateTextBufferSize(&WP(w, querystr_d).text);
 }
 
 #else
--- a/news_gui.c	Sun Feb 20 09:05:28 2005 +0000
+++ b/news_gui.c	Mon Feb 21 18:59:54 2005 +0000
@@ -539,7 +539,7 @@
 			d[0] = d[1] = d[2] = d[3] = ' ';
 			d += 4;
 		} else if ((byte)*s >= ' ' && ((byte)*s < 0x88 || (byte)*s >= 0x99)) {
-			len += _stringwidth_table[(byte)*s - 32];
+			len += GetCharacterWidth((byte)*s);
 			*d++ = *s;
 		}
 	}
--- a/os2.c	Sun Feb 20 09:05:28 2005 +0000
+++ b/os2.c	Mon Feb 21 18:59:54 2005 +0000
@@ -1,5 +1,6 @@
 #include "stdafx.h"
 #include "ttd.h"
+#include "window.h"
 #include "string.h"
 #include "table/strings.h"
 #include "hal.h"
@@ -529,7 +530,7 @@
 	return ttd_main(argc, argv);
 }
 
-void DeterminePaths()
+void DeterminePaths(void)
 {
 	char *s;
 
@@ -678,4 +679,4 @@
 	OS2MidiSetVolume,
 };
 
-
+bool InsertTextBufferClipboard(Textbuf *tb) {return false;}
--- a/unix.c	Sun Feb 20 09:05:28 2005 +0000
+++ b/unix.c	Mon Feb 21 18:59:54 2005 +0000
@@ -1,5 +1,6 @@
 #include "stdafx.h"
 #include "ttd.h"
+#include "window.h"
 #include "string.h"
 #include "table/strings.h"
 #include "hal.h"
@@ -525,3 +526,4 @@
 	mkdir(_path.scenario_dir, 0755);
 }
 
+bool InsertTextBufferClipboard(Textbuf *tb) {return false;}
--- a/viewport.c	Sun Feb 20 09:05:28 2005 +0000
+++ b/viewport.c	Mon Feb 21 18:59:54 2005 +0000
@@ -1056,10 +1056,11 @@
 	sign->width_1 = w;
 	sign->left = left - (w >> 1);
 
+	// zoomed out version
 	_stringwidth_base = 0xE0;
-	w = GetStringWidth(buffer);
+	w = GetStringWidth(buffer) + 3;
 	_stringwidth_base = 0;
-	sign->width_2 = w + 1;
+	sign->width_2 = w;
 }
 
 
--- a/win32.c	Sun Feb 20 09:05:28 2005 +0000
+++ b/win32.c	Mon Feb 21 18:59:54 2005 +0000
@@ -6,6 +6,7 @@
 #include "gfx.h"
 #include "sound.h"
 #include "window.h"
+#include "gui.h"
 #include <windows.h>
 #include <mmsystem.h>
 #include "hal.h"
@@ -2061,7 +2062,7 @@
 	return 0;
 }
 
-void DeterminePaths()
+void DeterminePaths(void)
 {
 	char *s;
 	char *cfg;
@@ -2108,3 +2109,48 @@
 	if (ret < 0) str[size - 1] = '\0';
 	return ret;
 }
+
+/**
+ * Insert a chunk of text from the clipboard onto the textbuffer. Get TEXT clipboard
+ * and append this up to the maximum length (either absolute or screenlength). If maxlength
+ * is zero, we don't care about the screenlength but only about the physical length of the string
+ * @param tb @Textbuf type to be changed
+ * @return Return true on successfull change of Textbuf, or false otherwise
+ */
+bool InsertTextBufferClipboard(Textbuf *tb)
+{
+	if (IsClipboardFormatAvailable(CF_TEXT)) {
+		HGLOBAL cbuf;
+		const byte *data, *dataptr;
+		uint16 width = 0;
+		uint16 length = 0;
+
+		OpenClipboard(NULL);
+		cbuf = GetClipboardData(CF_TEXT);
+		data = GlobalLock(cbuf); // clipboard data
+		dataptr = data;
+
+		for (; IsValidAsciiChar(*dataptr) && (tb->length + length) < tb->maxlength - 1 &&
+				(tb->maxwidth == 0 || width + tb->width + GetCharacterWidth((byte)*dataptr) <= tb->maxwidth); dataptr++) {
+					width += GetCharacterWidth((byte)*dataptr);
+			length++;
+		}
+
+		if (length == 0)
+			return false;
+
+		memmove(tb->buf + tb->caretpos + length, tb->buf + tb->caretpos, tb->length - tb->caretpos);
+		memcpy(tb->buf + tb->caretpos, data, length);
+		tb->width += width;
+		tb->caretxoffs += width;
+
+		tb->length += length;
+		tb->caretpos += length;
+		tb->buf[tb->length + 1] = '\0'; // terminating zero
+
+		GlobalUnlock(cbuf);
+		CloseClipboard();
+		return true;
+	}
+	return false;
+}
--- a/window.h	Sun Feb 20 09:05:28 2005 +0000
+++ b/window.h	Mon Feb 21 18:59:54 2005 +0000
@@ -222,13 +222,20 @@
 	WDP_CENTER = -2,
 };
 
+typedef struct Textbuf {
+	char *buf;                  /* buffer in which text is saved */
+	uint16 maxlength, maxwidth; /* the maximum size of the buffer. Maxwidth specifies screensize in pixels */
+	uint16 length, width;       /* the current size of the buffer. Width specifies screensize in pixels */
+	bool caret;                 /* is the caret ("_") visible or not */
+	uint16 caretpos;            /* the current position of the caret in the buffer */
+	uint16 caretxoffs;          /* the current position of the caret in pixels */
+} Textbuf;
+
 typedef struct {
 	StringID caption;
-	bool caret;
 	WindowClass wnd_class;
 	WindowNumber wnd_num;
-	uint16 maxlen, maxwidth;
-	char *buf;
+	Textbuf text;
 	const char* orig;
 } querystr_d;