00001
00002
00003
00004
00005
00006
00007
00008
00009
00012 #include "stdafx.h"
00013 #include "string_func.h"
00014 #include "strings_func.h"
00015 #include "debug.h"
00016 #include "window_func.h"
00017 #include "gfx_func.h"
00018 #include "querystring_gui.h"
00019
00020 #include "table/sprites.h"
00021 #include "table/strings.h"
00022
00024 enum OskWidgets {
00025 OSK_WIDGET_CAPTION,
00026 OSK_WIDGET_TEXT,
00027 OSK_WIDGET_CANCEL,
00028 OSK_WIDGET_OK,
00029 OSK_WIDGET_BACKSPACE,
00030 OSK_WIDGET_SPECIAL,
00031 OSK_WIDGET_CAPS,
00032 OSK_WIDGET_SHIFT,
00033 OSK_WIDGET_SPACE,
00034 OSK_WIDGET_LEFT,
00035 OSK_WIDGET_RIGHT,
00036 OSK_WIDGET_LETTERS,
00037
00038 OSK_WIDGET_NUMBERS_FIRST = OSK_WIDGET_LETTERS,
00039 OSK_WIDGET_NUMBERS_LAST = OSK_WIDGET_NUMBERS_FIRST + 13,
00040
00041 OSK_WIDGET_QWERTY_FIRST,
00042 OSK_WIDGET_QWERTY_LAST = OSK_WIDGET_QWERTY_FIRST + 11,
00043
00044 OSK_WIDGET_ASDFG_FIRST,
00045 OSK_WIDGET_ASDFG_LAST = OSK_WIDGET_ASDFG_FIRST + 11,
00046
00047 OSK_WIDGET_ZXCVB_FIRST,
00048 OSK_WIDGET_ZXCVB_LAST = OSK_WIDGET_ZXCVB_FIRST + 11,
00049 };
00050
00051 char _keyboard_opt[2][OSK_KEYBOARD_ENTRIES * 4 + 1];
00052 static WChar _keyboard[2][OSK_KEYBOARD_ENTRIES];
00053
00054 enum KeyStateBits {
00055 KEYS_NONE,
00056 KEYS_SHIFT,
00057 KEYS_CAPS
00058 };
00059 static byte _keystate = KEYS_NONE;
00060
00061 struct OskWindow : public Window {
00062 StringID caption;
00063 QueryString *qs;
00064 int text_btn;
00065 int ok_btn;
00066 int cancel_btn;
00067 Textbuf *text;
00068 char *orig_str_buf;
00069 bool shift;
00070
00071 OskWindow(const WindowDesc *desc, QueryStringBaseWindow *parent, int button, int cancel, int ok) : Window()
00072 {
00073 this->parent = parent;
00074 assert(parent != NULL);
00075
00076 NWidgetCore *par_wid = parent->GetWidget<NWidgetCore>(button);
00077 assert(par_wid != NULL);
00078 this->caption = (par_wid->widget_data != STR_NULL) ? par_wid->widget_data : parent->caption;
00079
00080 this->qs = parent;
00081 this->text_btn = button;
00082 this->cancel_btn = cancel;
00083 this->ok_btn = ok;
00084 this->text = &parent->text;
00085
00086
00087 this->orig_str_buf = strdup(this->qs->text.buf);
00088
00089 this->InitNested(desc, 0);
00090
00091
00092 this->DisableWidget(OSK_WIDGET_SPECIAL);
00093
00094 this->UpdateOskState();
00095 }
00096
00097 ~OskWindow()
00098 {
00099 free(this->orig_str_buf);
00100 }
00101
00107 void UpdateOskState()
00108 {
00109 this->shift = HasBit(_keystate, KEYS_CAPS) ^ HasBit(_keystate, KEYS_SHIFT);
00110
00111 for (uint i = 0; i < OSK_KEYBOARD_ENTRIES; i++) {
00112 this->SetWidgetDisabledState(OSK_WIDGET_LETTERS + i,
00113 !IsValidChar(_keyboard[this->shift][i], this->qs->afilter) || _keyboard[this->shift][i] == ' ');
00114 }
00115 this->SetWidgetDisabledState(OSK_WIDGET_SPACE, !IsValidChar(' ', this->qs->afilter));
00116
00117 this->LowerWidget(OSK_WIDGET_TEXT);
00118 this->SetWidgetLoweredState(OSK_WIDGET_SHIFT, HasBit(_keystate, KEYS_SHIFT));
00119 this->SetWidgetLoweredState(OSK_WIDGET_CAPS, HasBit(_keystate, KEYS_CAPS));
00120 }
00121
00122 virtual void SetStringParameters(int widget) const
00123 {
00124 if (widget == OSK_WIDGET_CAPTION) SetDParam(0, this->caption);
00125 }
00126
00127 virtual void DrawWidget(const Rect &r, int widget) const
00128 {
00129 if (widget < OSK_WIDGET_LETTERS) return;
00130
00131 widget -= OSK_WIDGET_LETTERS;
00132 DrawCharCentered(_keyboard[this->shift][widget],
00133 r.left + 8,
00134 r.top + 3,
00135 TC_BLACK);
00136 }
00137
00138 virtual void OnPaint()
00139 {
00140 this->DrawWidgets();
00141
00142 this->qs->DrawEditBox(this, OSK_WIDGET_TEXT);
00143 }
00144
00145 virtual void OnClick(Point pt, int widget, int click_count)
00146 {
00147
00148 if (widget >= OSK_WIDGET_LETTERS) {
00149 WChar c = _keyboard[this->shift][widget - OSK_WIDGET_LETTERS];
00150
00151 if (!IsValidChar(c, this->qs->afilter)) return;
00152
00153 if (InsertTextBufferChar(&this->qs->text, c)) this->InvalidateParent();
00154
00155 if (HasBit(_keystate, KEYS_SHIFT)) {
00156 ToggleBit(_keystate, KEYS_SHIFT);
00157 this->GetWidget<NWidgetCore>(OSK_WIDGET_SHIFT)->colour = HasBit(_keystate, KEYS_SHIFT) ? COLOUR_WHITE : COLOUR_GREY;
00158 this->SetDirty();
00159 }
00160
00161 this->parent->SetFocusedWidget(this->text_btn);
00162 SetFocusedWindow(this->parent);
00163 return;
00164 }
00165
00166 switch (widget) {
00167 case OSK_WIDGET_BACKSPACE:
00168 if (DeleteTextBufferChar(&this->qs->text, WKC_BACKSPACE)) this->InvalidateParent();
00169 break;
00170
00171 case OSK_WIDGET_SPECIAL:
00172
00173
00174
00175
00176
00177 break;
00178
00179 case OSK_WIDGET_CAPS:
00180 ToggleBit(_keystate, KEYS_CAPS);
00181 this->UpdateOskState();
00182 this->SetDirty();
00183 break;
00184
00185 case OSK_WIDGET_SHIFT:
00186 ToggleBit(_keystate, KEYS_SHIFT);
00187 this->UpdateOskState();
00188 this->SetDirty();
00189 break;
00190
00191 case OSK_WIDGET_SPACE:
00192 if (InsertTextBufferChar(&this->qs->text, ' ')) this->InvalidateParent();
00193 break;
00194
00195 case OSK_WIDGET_LEFT:
00196 if (MoveTextBufferPos(&this->qs->text, WKC_LEFT)) this->InvalidateParent();
00197 break;
00198
00199 case OSK_WIDGET_RIGHT:
00200 if (MoveTextBufferPos(&this->qs->text, WKC_RIGHT)) this->InvalidateParent();
00201 break;
00202
00203 case OSK_WIDGET_OK:
00204 if (this->qs->orig == NULL || strcmp(this->qs->text.buf, this->qs->orig) != 0) {
00205
00206 if (this->ok_btn != 0) {
00207 this->parent->OnClick(pt, this->ok_btn, 1);
00208
00209 return;
00210 }
00211 }
00212 delete this;
00213 break;
00214
00215 case OSK_WIDGET_CANCEL:
00216 if (this->cancel_btn != 0) {
00217 this->parent->OnClick(pt, this->cancel_btn, 1);
00218
00219 return;
00220 } else {
00221 strcpy(qs->text.buf, this->orig_str_buf);
00222 UpdateTextBufferSize(&qs->text);
00223 MoveTextBufferPos(&qs->text, WKC_END);
00224 this->InvalidateParent();
00225 delete this;
00226 }
00227 break;
00228 }
00229
00230 this->parent->SetFocusedWidget(this->text_btn);
00231 SetFocusedWindow(this->parent);
00232 }
00233
00234 void InvalidateParent()
00235 {
00236 QueryStringBaseWindow *w = dynamic_cast<QueryStringBaseWindow*>(this->parent);
00237 if (w != NULL) w->OnOSKInput(this->text_btn);
00238
00239 this->SetWidgetDirty(OSK_WIDGET_TEXT);
00240 if (this->parent != NULL) this->parent->SetWidgetDirty(this->text_btn);
00241 }
00242
00243 virtual void OnMouseLoop()
00244 {
00245 this->qs->HandleEditBox(this, OSK_WIDGET_TEXT);
00246
00247 this->parent->SetWidgetDirty(this->text_btn);
00248 }
00249
00255 virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
00256 {
00257 if (!gui_scope) return;
00258 this->SetWidgetDirty(OSK_WIDGET_TEXT);
00259 }
00260 };
00261
00262 static const int HALF_KEY_WIDTH = 7;
00263 static const int INTER_KEY_SPACE = 2;
00264
00276 static void AddKey(NWidgetHorizontal *hor, int height, int num_half, WidgetType widtype, int widnum, uint16 widdata, int *biggest_index)
00277 {
00278 int key_width = HALF_KEY_WIDTH + (INTER_KEY_SPACE + HALF_KEY_WIDTH) * (num_half - 1);
00279
00280 if (widtype == NWID_SPACER) {
00281 if (!hor->IsEmpty()) key_width += INTER_KEY_SPACE;
00282 NWidgetSpacer *spc = new NWidgetSpacer(key_width, height);
00283 hor->Add(spc);
00284 } else {
00285 if (!hor->IsEmpty()) {
00286 NWidgetSpacer *spc = new NWidgetSpacer(INTER_KEY_SPACE, height);
00287 hor->Add(spc);
00288 }
00289 NWidgetLeaf *leaf = new NWidgetLeaf(widtype, COLOUR_GREY, widnum, widdata, STR_NULL);
00290 leaf->SetMinimalSize(key_width, height);
00291 hor->Add(leaf);
00292 }
00293
00294 *biggest_index = max(*biggest_index, widnum);
00295 }
00296
00298 static NWidgetBase *MakeTopKeys(int *biggest_index)
00299 {
00300 NWidgetHorizontal *hor = new NWidgetHorizontal();
00301 int key_height = FONT_HEIGHT_NORMAL + 2;
00302
00303 AddKey(hor, key_height, 6 * 2, WWT_TEXTBTN, OSK_WIDGET_CANCEL, STR_BUTTON_CANCEL, biggest_index);
00304 AddKey(hor, key_height, 6 * 2, WWT_TEXTBTN, OSK_WIDGET_OK, STR_BUTTON_OK, biggest_index);
00305 AddKey(hor, key_height, 2 * 2, WWT_PUSHIMGBTN, OSK_WIDGET_BACKSPACE, SPR_OSK_BACKSPACE, biggest_index);
00306 return hor;
00307 }
00308
00310 static NWidgetBase *MakeNumberKeys(int *biggest_index)
00311 {
00312 NWidgetHorizontal *hor = new NWidgetHorizontalLTR();
00313 int key_height = FONT_HEIGHT_NORMAL + 6;
00314
00315 for (int widnum = OSK_WIDGET_NUMBERS_FIRST; widnum <= OSK_WIDGET_NUMBERS_LAST; widnum++) {
00316 AddKey(hor, key_height, 2, WWT_PUSHBTN, widnum, 0x0, biggest_index);
00317 }
00318 return hor;
00319 }
00320
00322 static NWidgetBase *MakeQwertyKeys(int *biggest_index)
00323 {
00324 NWidgetHorizontal *hor = new NWidgetHorizontalLTR();
00325 int key_height = FONT_HEIGHT_NORMAL + 6;
00326
00327 AddKey(hor, key_height, 3, WWT_PUSHIMGBTN, OSK_WIDGET_SPECIAL, SPR_OSK_SPECIAL, biggest_index);
00328 for (int widnum = OSK_WIDGET_QWERTY_FIRST; widnum <= OSK_WIDGET_QWERTY_LAST; widnum++) {
00329 AddKey(hor, key_height, 2, WWT_PUSHBTN, widnum, 0x0, biggest_index);
00330 }
00331 AddKey(hor, key_height, 1, NWID_SPACER, 0, 0, biggest_index);
00332 return hor;
00333 }
00334
00336 static NWidgetBase *MakeAsdfgKeys(int *biggest_index)
00337 {
00338 NWidgetHorizontal *hor = new NWidgetHorizontalLTR();
00339 int key_height = FONT_HEIGHT_NORMAL + 6;
00340
00341 AddKey(hor, key_height, 4, WWT_IMGBTN, OSK_WIDGET_CAPS, SPR_OSK_CAPS, biggest_index);
00342 for (int widnum = OSK_WIDGET_ASDFG_FIRST; widnum <= OSK_WIDGET_ASDFG_LAST; widnum++) {
00343 AddKey(hor, key_height, 2, WWT_PUSHBTN, widnum, 0x0, biggest_index);
00344 }
00345 return hor;
00346 }
00347
00349 static NWidgetBase *MakeZxcvbKeys(int *biggest_index)
00350 {
00351 NWidgetHorizontal *hor = new NWidgetHorizontalLTR();
00352 int key_height = FONT_HEIGHT_NORMAL + 6;
00353
00354 AddKey(hor, key_height, 3, WWT_IMGBTN, OSK_WIDGET_SHIFT, SPR_OSK_SHIFT, biggest_index);
00355 for (int widnum = OSK_WIDGET_ZXCVB_FIRST; widnum <= OSK_WIDGET_ZXCVB_LAST; widnum++) {
00356 AddKey(hor, key_height, 2, WWT_PUSHBTN, widnum, 0x0, biggest_index);
00357 }
00358 AddKey(hor, key_height, 1, NWID_SPACER, 0, 0, biggest_index);
00359 return hor;
00360 }
00361
00363 static NWidgetBase *MakeSpacebarKeys(int *biggest_index)
00364 {
00365 NWidgetHorizontal *hor = new NWidgetHorizontal();
00366 int key_height = FONT_HEIGHT_NORMAL + 6;
00367
00368 AddKey(hor, key_height, 8, NWID_SPACER, 0, 0, biggest_index);
00369 AddKey(hor, key_height, 13, WWT_PUSHTXTBTN, OSK_WIDGET_SPACE, STR_EMPTY, biggest_index);
00370 AddKey(hor, key_height, 3, NWID_SPACER, 0, 0, biggest_index);
00371 AddKey(hor, key_height, 2, WWT_PUSHIMGBTN, OSK_WIDGET_LEFT, SPR_OSK_LEFT, biggest_index);
00372 AddKey(hor, key_height, 2, WWT_PUSHIMGBTN, OSK_WIDGET_RIGHT, SPR_OSK_RIGHT, biggest_index);
00373 return hor;
00374 }
00375
00376
00377 static const NWidgetPart _nested_osk_widgets[] = {
00378 NWidget(WWT_CAPTION, COLOUR_GREY, OSK_WIDGET_CAPTION), SetDataTip(STR_WHITE_STRING, STR_NULL),
00379 NWidget(WWT_PANEL, COLOUR_GREY),
00380 NWidget(WWT_EDITBOX, COLOUR_GREY, OSK_WIDGET_TEXT), SetMinimalSize(252, 12), SetPadding(2, 2, 2, 2),
00381 EndContainer(),
00382 NWidget(WWT_PANEL, COLOUR_GREY), SetPIP(5, 2, 3),
00383 NWidgetFunction(MakeTopKeys), SetPadding(0, 3, 0, 3),
00384 NWidgetFunction(MakeNumberKeys), SetPadding(0, 3, 0, 3),
00385 NWidgetFunction(MakeQwertyKeys), SetPadding(0, 3, 0, 3),
00386 NWidgetFunction(MakeAsdfgKeys), SetPadding(0, 3, 0, 3),
00387 NWidgetFunction(MakeZxcvbKeys), SetPadding(0, 3, 0, 3),
00388 NWidgetFunction(MakeSpacebarKeys), SetPadding(0, 3, 0, 3),
00389 EndContainer(),
00390 };
00391
00392 static const WindowDesc _osk_desc(
00393 WDP_CENTER, 0, 0,
00394 WC_OSK, WC_NONE,
00395 WDF_UNCLICK_BUTTONS,
00396 _nested_osk_widgets, lengthof(_nested_osk_widgets)
00397 );
00398
00403 void GetKeyboardLayout()
00404 {
00405 char keyboard[2][OSK_KEYBOARD_ENTRIES * 4 + 1];
00406 char errormark[2][OSK_KEYBOARD_ENTRIES + 1];
00407 bool has_error = false;
00408
00409 if (StrEmpty(_keyboard_opt[0])) {
00410 GetString(keyboard[0], STR_OSK_KEYBOARD_LAYOUT, lastof(keyboard[0]));
00411 } else {
00412 strecpy(keyboard[0], _keyboard_opt[0], lastof(keyboard[0]));
00413 }
00414
00415 if (StrEmpty(_keyboard_opt[1])) {
00416 GetString(keyboard[1], STR_OSK_KEYBOARD_LAYOUT_CAPS, lastof(keyboard[1]));
00417 } else {
00418 strecpy(keyboard[1], _keyboard_opt[1], lastof(keyboard[1]));
00419 }
00420
00421 for (uint j = 0; j < 2; j++) {
00422 const char *kbd = keyboard[j];
00423 bool ended = false;
00424 for (uint i = 0; i < OSK_KEYBOARD_ENTRIES; i++) {
00425 _keyboard[j][i] = Utf8Consume(&kbd);
00426
00427
00428 if (_keyboard[j][i] == '\0' || ended) {
00429 ended = true;
00430 _keyboard[j][i] = ' ';
00431 continue;
00432 }
00433
00434 if (IsPrintable(_keyboard[j][i])) {
00435 errormark[j][i] = ' ';
00436 } else {
00437 has_error = true;
00438 errormark[j][i] = '^';
00439 _keyboard[j][i] = ' ';
00440 }
00441 }
00442 }
00443
00444 if (has_error) {
00445 ShowInfoF("The keyboard layout you selected contains invalid chars. Please check those chars marked with ^.");
00446 ShowInfoF("Normal keyboard: %s", keyboard[0]);
00447 ShowInfoF(" %s", errormark[0]);
00448 ShowInfoF("Caps Lock: %s", keyboard[1]);
00449 ShowInfoF(" %s", errormark[1]);
00450 }
00451 }
00452
00462 void ShowOnScreenKeyboard(QueryStringBaseWindow *parent, int button, int cancel, int ok)
00463 {
00464 DeleteWindowById(WC_OSK, 0);
00465
00466 GetKeyboardLayout();
00467 new OskWindow(&_osk_desc, parent, button, cancel, ok);
00468 }
00469
00477 void UpdateOSKOriginalText(const QueryStringBaseWindow *parent, int button)
00478 {
00479 OskWindow *osk = dynamic_cast<OskWindow *>(FindWindowById(WC_OSK, 0));
00480 if (osk == NULL || osk->qs != parent || osk->text_btn != button) return;
00481
00482 free(osk->orig_str_buf);
00483 osk->orig_str_buf = strdup(osk->qs->text.buf);
00484
00485 osk->SetDirty();
00486 }