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 return;
00161 }
00162
00163 switch (widget) {
00164 case OSK_WIDGET_TEXT:
00165
00166 this->parent->SetFocusedWidget(this->text_btn);
00167 SetFocusedWindow(this->parent);
00168 break;
00169
00170 case OSK_WIDGET_BACKSPACE:
00171 if (DeleteTextBufferChar(&this->qs->text, WKC_BACKSPACE)) this->InvalidateParent();
00172 break;
00173
00174 case OSK_WIDGET_SPECIAL:
00175
00176
00177
00178
00179
00180 break;
00181
00182 case OSK_WIDGET_CAPS:
00183 ToggleBit(_keystate, KEYS_CAPS);
00184 this->UpdateOskState();
00185 this->SetDirty();
00186 break;
00187
00188 case OSK_WIDGET_SHIFT:
00189 ToggleBit(_keystate, KEYS_SHIFT);
00190 this->UpdateOskState();
00191 this->SetDirty();
00192 break;
00193
00194 case OSK_WIDGET_SPACE:
00195 if (InsertTextBufferChar(&this->qs->text, ' ')) this->InvalidateParent();
00196 break;
00197
00198 case OSK_WIDGET_LEFT:
00199 if (MoveTextBufferPos(&this->qs->text, WKC_LEFT)) this->InvalidateParent();
00200 break;
00201
00202 case OSK_WIDGET_RIGHT:
00203 if (MoveTextBufferPos(&this->qs->text, WKC_RIGHT)) this->InvalidateParent();
00204 break;
00205
00206 case OSK_WIDGET_OK:
00207 if (this->qs->orig == NULL || strcmp(this->qs->text.buf, this->qs->orig) != 0) {
00208
00209 if (this->ok_btn != 0) {
00210 this->parent->OnClick(pt, this->ok_btn, 1);
00211
00212 return;
00213 }
00214 }
00215 delete this;
00216 break;
00217
00218 case OSK_WIDGET_CANCEL:
00219 if (this->cancel_btn != 0) {
00220 this->parent->OnClick(pt, this->cancel_btn, 1);
00221
00222 return;
00223 } else {
00224 strcpy(qs->text.buf, this->orig_str_buf);
00225 UpdateTextBufferSize(&qs->text);
00226 MoveTextBufferPos(&qs->text, WKC_END);
00227 this->InvalidateParent();
00228 delete this;
00229 }
00230 break;
00231 }
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 }