# HG changeset patch # User Darkvater # Date 1109012394 0 # Node ID 53a5713cf3f97af3c3516209d6b73f46d128dafd # Parent 238570e5c3d36e5080fe2c563f0104728eb6d59a (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(). diff -r 238570e5c3d3 -r 53a5713cf3f9 console.c --- 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 #include @@ -14,10 +15,6 @@ #include "network_data.h" #include "network_server.h" -#ifdef WIN32 -#include -#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) diff -r 238570e5c3d3 -r 53a5713cf3f9 gfx.c --- 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; diff -r 238570e5c3d3 -r 53a5713cf3f9 gfx.h --- 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; diff -r 238570e5c3d3 -r 53a5713cf3f9 gui.h --- 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); diff -r 238570e5c3d3 -r 53a5713cf3f9 intro_gui.c --- 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; } diff -r 238570e5c3d3 -r 53a5713cf3f9 main_gui.c --- 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); } diff -r 238570e5c3d3 -r 53a5713cf3f9 misc_gui.c --- 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 -#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. diff -r 238570e5c3d3 -r 53a5713cf3f9 network_gui.c --- 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 diff -r 238570e5c3d3 -r 53a5713cf3f9 news_gui.c --- 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; } } diff -r 238570e5c3d3 -r 53a5713cf3f9 os2.c --- 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;} diff -r 238570e5c3d3 -r 53a5713cf3f9 unix.c --- 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;} diff -r 238570e5c3d3 -r 53a5713cf3f9 viewport.c --- 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; } diff -r 238570e5c3d3 -r 53a5713cf3f9 win32.c --- 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 #include #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; +} diff -r 238570e5c3d3 -r 53a5713cf3f9 window.h --- 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;