summaryrefslogtreecommitdiff
path: root/misc_gui.c
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
commitfb78ca8a62c69a51903416f5a4062a8972c1e98d (patch)
treeac473756965e88790f53e13ff4589035499ce9c1 /misc_gui.c
parentf13bfccc37237e612d1eb5194d41246072bbc20f (diff)
downloadopenttd-fb78ca8a62c69a51903416f5a4062a8972c1e98d.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().
Diffstat (limited to 'misc_gui.c')
-rw-r--r--misc_gui.c274
1 files changed, 170 insertions, 104 deletions
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.