summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDarkvater <darkvater@openttd.org>2005-02-21 18:59:54 +0000
committerDarkvater <darkvater@openttd.org>2005-02-21 18:59:54 +0000
commitd865c55007321366d49b9b3d49eb0d61715dfc98 (patch)
treeac473756965e88790f53e13ff4589035499ce9c1
parent0d8dcacee2dbd1ae639f8851a2847b3806c7da16 (diff)
downloadopenttd-d865c55007321366d49b9b3d49eb0d61715dfc98.tar.xz
(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().
-rw-r--r--console.c171
-rw-r--r--gfx.c19
-rw-r--r--gfx.h2
-rw-r--r--gui.h12
-rw-r--r--intro_gui.c2
-rw-r--r--main_gui.c2
-rw-r--r--misc_gui.c274
-rw-r--r--network_gui.c43
-rw-r--r--news_gui.c2
-rw-r--r--os2.c5
-rw-r--r--unix.c2
-rw-r--r--viewport.c5
-rw-r--r--win32.c48
-rw-r--r--window.h13
14 files changed, 348 insertions, 252 deletions
diff --git a/console.c b/console.c
index 598683cfc..dd3b629bc 100644
--- a/console.c
+++ b/console.c
@@ -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 @@ FILE* _iconsole_output_file;
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 @@ static void IConsoleWndProc(Window* w, WindowEvent* e)
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 @@ static void IConsoleWndProc(Window* w, WindowEvent* e)
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++;
- SetWindowDirty(w);
+ InsertTextBufferChar(&_iconsole_cmdline, e->keypress.ascii);
_iconsole_cmdbufferpos = ICON_CMDBUF_SIZE - 1;
- }
- else
+ SetWindowDirty(w);
+ } 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 @@ void IConsoleInit(void)
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 @@ void IConsoleClear(void)
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 IConsoleOpen(void)
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 @@ void IConsoleCmdBufferNavigate(signed char direction)
{
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)
diff --git a/gfx.c b/gfx.c
index b4bf72894..4bdf40441 100644
--- a/gfx.c
+++ b/gfx.c
@@ -244,8 +244,6 @@ void GfxDrawLine(int x, int y, int x2, int y2, int color)
enum {
- ASCII_LETTERSTART = 32,
-
ASCII_SETX = 1,
ASCII_SETXY = 2,
@@ -309,10 +307,10 @@ static uint32 FormatStringLinebreaks(char *str, int maxw)
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 @@ void DrawStringMultiLine(int x, int y, uint16 str, int maxw) {
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 @@ int GetStringWidth(const char *str)
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 @@ skip_cont:;
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;
diff --git a/gfx.h b/gfx.h
index 9dcf01296..58cf6dea9 100644
--- a/gfx.h
+++ b/gfx.h
@@ -69,8 +69,10 @@ bool ChangeResInGame(int w, int h);
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;
diff --git a/gui.h b/gui.h
index 0038f5152..dff7b535d 100644
--- a/gui.h
+++ b/gui.h
@@ -64,6 +64,7 @@ void PlaceProc_LevelLand(uint tile);
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 ShowExtraViewPortWindow(void);
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);
diff --git a/intro_gui.c b/intro_gui.c
index 39d8b124d..9850f5f4c 100644
--- a/intro_gui.c
+++ b/intro_gui.c
@@ -218,7 +218,7 @@ static void AskAbandonGameWndProc(Window *w, WindowEvent *e) {
}
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;
}
diff --git a/main_gui.c b/main_gui.c
index a6be5e26a..869ad9bbe 100644
--- a/main_gui.c
+++ b/main_gui.c
@@ -2252,7 +2252,7 @@ static const int8 scrollamt[16][2] = {
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);
}
diff --git a/misc_gui.c b/misc_gui.c
index 68d0ad668..2bee2029b 100644
--- a/misc_gui.c
+++ b/misc_gui.c
@@ -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 @@ void SetHScrollCount(Window *w, int num)
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;
-
- do {
- if (*++buf == 0)
- break;
- (*count)++;
- (*width) += _stringwidth_table[(byte)*buf - 32];
- } while (1);
+ tb->width -= GetCharacterWidth(tb->buf[tb->caretpos]);
+ memmove(tb->buf + tb->caretpos, tb->buf + tb->caretpos + 1, tb->length - tb->caretpos);
+ tb->length--;
}
-int HandleEditBoxKey(Window *w, int wid, WindowEvent *we)
+/**
+ * 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)
{
- int width,count;
- int key = we->keypress.ascii;
+ if (delmode == WKC_BACKSPACE && tb->caretpos != 0) {
+ tb->caretpos--;
+ tb->caretxoffs -= GetCharacterWidth(tb->buf[tb->caretpos]);
- we->keypress.cont = false;
+ DelChar(tb);
+ return true;
+ } else if (delmode == WKC_DELETE && tb->caretpos < tb->length) {
+ DelChar(tb);
+ return true;
+ }
- 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';
+ return false;
+}
- GlobalUnlock(cbuf);
- CloseClipboard();
- InvalidateWidget(w, wid);
+/**
+ * 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;
}
-#endif
- } else {
- GetCurrentStringSize(WP(w,querystr_d).buf - 1, &count, &width);
+ 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;
+ }
- if (we->keypress.keycode == WKC_BACKSPACE) {
- if (count != 0) {
- WP(w,querystr_d).buf[count-1] = 0;
- 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';
+ 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)
+{
+ we->keypress.cont = false;
+
+ 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);
+ 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 // key wasn't caught
we->keypress.cont = true;
}
@@ -857,42 +916,45 @@ int HandleEditBoxKey(Window *w, int wid, WindowEvent *we)
return 0;
}
-void HandleEditBox(Window *w, int wid)
+bool HandleCaret(Textbuf *tb)
{
- bool b;
-
/* caret changed? */
- b = !!(_caret_timer & 0x20);
- if (b != WP(w,querystr_d).caret) {
- WP(w,querystr_d).caret = b;
- InvalidateWidget(w, wid);
+ bool b = !!(_caret_timer & 0x20);
+
+ if (b != tb->caret) {
+ tb->caret = b;
+ return true;
}
+ return false;
+}
+
+void HandleEditBox(Window *w, int wid)
+{
+ 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 @@ static void QueryStringWndProc(Window *w, WindowEvent *e)
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 @@ press_ok:;
case WE_CREATE:
closed = false;
+ _editbox_win = w;
break;
case WE_DESTROY:
@@ -958,6 +1021,7 @@ press_ok:;
}
}
_query_string_active = false;
+ _editbox_win = NULL;
break;
}
}
@@ -986,8 +1050,9 @@ static char _orig_str_buf[lengthof(_edit_str_buf)];
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 @@ void ShowQueryString(StringID str, StringID caption, uint maxlen, uint maxwidth,
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 @@ static void SaveLoadDlgWndProc(Window *w, WindowEvent *e)
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 @@ static void SaveLoadDlgWndProc(Window *w, WindowEvent *e)
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 @@ void ShowSaveLoadDialog(int mode)
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.
diff --git a/network_gui.c b/network_gui.c
index d2f1bd345..c749f37c2 100644
--- a/network_gui.c
+++ b/network_gui.c
@@ -449,10 +449,11 @@ void ShowNetworkGameWindow(void)
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 @@ static void NetworkStartServerWindowWndProc(Window *w, WindowEvent *e)
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 @@ static void NetworkStartServerWindowWndProc(Window *w, WindowEvent *e)
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 @@ static void ShowNetworkStartServerWindow(void)
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 @@ static void ChatWindowWndProc(Window *w, WindowEvent *e)
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 @@ void ShowChatWindow(StringID str, StringID caption, int maxlen, int maxwidth, by
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 @@ void ShowChatWindow(StringID str, StringID caption, int maxlen, int maxwidth, by
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
diff --git a/news_gui.c b/news_gui.c
index 608b3e9d1..1f22f6078 100644
--- a/news_gui.c
+++ b/news_gui.c
@@ -539,7 +539,7 @@ static void GetNewsString(NewsItem *ni, char *buffer, uint max)
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;
}
}
diff --git a/os2.c b/os2.c
index c66df09ee..7667eb89a 100644
--- a/os2.c
+++ b/os2.c
@@ -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 @@ int CDECL main(int argc, char* argv[])
return ttd_main(argc, argv);
}
-void DeterminePaths()
+void DeterminePaths(void)
{
char *s;
@@ -678,4 +679,4 @@ const HalMusicDriver _os2_music_driver = {
OS2MidiSetVolume,
};
-
+bool InsertTextBufferClipboard(Textbuf *tb) {return false;}
diff --git a/unix.c b/unix.c
index d49743581..b343a1b55 100644
--- a/unix.c
+++ b/unix.c
@@ -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 @@ void DeterminePaths(void)
mkdir(_path.scenario_dir, 0755);
}
+bool InsertTextBufferClipboard(Textbuf *tb) {return false;}
diff --git a/viewport.c b/viewport.c
index 96b3bb177..6bcdf4dc1 100644
--- a/viewport.c
+++ b/viewport.c
@@ -1056,10 +1056,11 @@ void UpdateViewportSignPos(ViewportSign *sign, int left, int top, StringID str)
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;
}
diff --git a/win32.c b/win32.c
index 788d7271f..10b6d4212 100644
--- a/win32.c
+++ b/win32.c
@@ -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 @@ int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPTSTR lpCmdLin
return 0;
}
-void DeterminePaths()
+void DeterminePaths(void)
{
char *s;
char *cfg;
@@ -2108,3 +2109,48 @@ int CDECL vsnprintf(char *str, size_t size, const char *format, va_list ap)
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;
+}
diff --git a/window.h b/window.h
index 59b91897a..8259b2852 100644
--- a/window.h
+++ b/window.h
@@ -222,13 +222,20 @@ enum {
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;