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 {
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
00250 virtual void OnInvalidateData(int)
00251 {
00252 this->SetWidgetDirty(OSK_WIDGET_TEXT);
00253 }
00254 };
00255
00256 static const int HALF_KEY_WIDTH = 7;
00257 static const int INTER_KEY_SPACE = 2;
00258
00270 static void AddKey(NWidgetHorizontal *hor, int height, int num_half, WidgetType widtype, int widnum, uint16 widdata, int *biggest_index)
00271 {
00272 int key_width = HALF_KEY_WIDTH + (INTER_KEY_SPACE + HALF_KEY_WIDTH) * (num_half - 1);
00273
00274 if (widtype == NWID_SPACER) {
00275 if (!hor->IsEmpty()) key_width += INTER_KEY_SPACE;
00276 NWidgetSpacer *spc = new NWidgetSpacer(key_width, height);
00277 hor->Add(spc);
00278 } else {
00279 if (!hor->IsEmpty()) {
00280 NWidgetSpacer *spc = new NWidgetSpacer(INTER_KEY_SPACE, height);
00281 hor->Add(spc);
00282 }
00283 NWidgetLeaf *leaf = new NWidgetLeaf(widtype, COLOUR_GREY, widnum, widdata, STR_NULL);
00284 leaf->SetMinimalSize(key_width, height);
00285 hor->Add(leaf);
00286 }
00287
00288 *biggest_index = max(*biggest_index, widnum);
00289 }
00290
00292 static NWidgetBase *MakeTopKeys(int *biggest_index)
00293 {
00294 NWidgetHorizontal *hor = new NWidgetHorizontal();
00295 int key_height = FONT_HEIGHT_NORMAL + 2;
00296
00297 AddKey(hor, key_height, 6 * 2, WWT_TEXTBTN, OSK_WIDGET_CANCEL, STR_BUTTON_CANCEL, biggest_index);
00298 AddKey(hor, key_height, 6 * 2, WWT_TEXTBTN, OSK_WIDGET_OK, STR_BUTTON_OK, biggest_index);
00299 AddKey(hor, key_height, 2 * 2, WWT_PUSHIMGBTN, OSK_WIDGET_BACKSPACE, SPR_OSK_BACKSPACE, biggest_index);
00300 return hor;
00301 }
00302
00304 static NWidgetBase *MakeNumberKeys(int *biggest_index)
00305 {
00306 NWidgetHorizontal *hor = new NWidgetHorizontalLTR();
00307 int key_height = FONT_HEIGHT_NORMAL + 6;
00308
00309 for (int widnum = OSK_WIDGET_NUMBERS_FIRST; widnum <= OSK_WIDGET_NUMBERS_LAST; widnum++) {
00310 AddKey(hor, key_height, 2, WWT_PUSHBTN, widnum, 0x0, biggest_index);
00311 }
00312 return hor;
00313 }
00314
00316 static NWidgetBase *MakeQwertyKeys(int *biggest_index)
00317 {
00318 NWidgetHorizontal *hor = new NWidgetHorizontalLTR();
00319 int key_height = FONT_HEIGHT_NORMAL + 6;
00320
00321 AddKey(hor, key_height, 3, WWT_PUSHIMGBTN, OSK_WIDGET_SPECIAL, SPR_OSK_SPECIAL, biggest_index);
00322 for (int widnum = OSK_WIDGET_QWERTY_FIRST; widnum <= OSK_WIDGET_QWERTY_LAST; widnum++) {
00323 AddKey(hor, key_height, 2, WWT_PUSHBTN, widnum, 0x0, biggest_index);
00324 }
00325 AddKey(hor, key_height, 1, NWID_SPACER, 0, 0, biggest_index);
00326 return hor;
00327 }
00328
00330 static NWidgetBase *MakeAsdfgKeys(int *biggest_index)
00331 {
00332 NWidgetHorizontal *hor = new NWidgetHorizontalLTR();
00333 int key_height = FONT_HEIGHT_NORMAL + 6;
00334
00335 AddKey(hor, key_height, 4, WWT_IMGBTN, OSK_WIDGET_CAPS, SPR_OSK_CAPS, biggest_index);
00336 for (int widnum = OSK_WIDGET_ASDFG_FIRST; widnum <= OSK_WIDGET_ASDFG_LAST; widnum++) {
00337 AddKey(hor, key_height, 2, WWT_PUSHBTN, widnum, 0x0, biggest_index);
00338 }
00339 return hor;
00340 }
00341
00343 static NWidgetBase *MakeZxcvbKeys(int *biggest_index)
00344 {
00345 NWidgetHorizontal *hor = new NWidgetHorizontalLTR();
00346 int key_height = FONT_HEIGHT_NORMAL + 6;
00347
00348 AddKey(hor, key_height, 3, WWT_IMGBTN, OSK_WIDGET_SHIFT, SPR_OSK_SHIFT, biggest_index);
00349 for (int widnum = OSK_WIDGET_ZXCVB_FIRST; widnum <= OSK_WIDGET_ZXCVB_LAST; widnum++) {
00350 AddKey(hor, key_height, 2, WWT_PUSHBTN, widnum, 0x0, biggest_index);
00351 }
00352 AddKey(hor, key_height, 1, NWID_SPACER, 0, 0, biggest_index);
00353 return hor;
00354 }
00355
00357 static NWidgetBase *MakeSpacebarKeys(int *biggest_index)
00358 {
00359 NWidgetHorizontal *hor = new NWidgetHorizontal();
00360 int key_height = FONT_HEIGHT_NORMAL + 6;
00361
00362 AddKey(hor, key_height, 8, NWID_SPACER, 0, 0, biggest_index);
00363 AddKey(hor, key_height, 13, WWT_PUSHTXTBTN, OSK_WIDGET_SPACE, STR_EMPTY, biggest_index);
00364 AddKey(hor, key_height, 3, NWID_SPACER, 0, 0, biggest_index);
00365 AddKey(hor, key_height, 2, WWT_PUSHIMGBTN, OSK_WIDGET_LEFT, SPR_OSK_LEFT, biggest_index);
00366 AddKey(hor, key_height, 2, WWT_PUSHIMGBTN, OSK_WIDGET_RIGHT, SPR_OSK_RIGHT, biggest_index);
00367 return hor;
00368 }
00369
00370
00371 static const NWidgetPart _nested_osk_widgets[] = {
00372 NWidget(WWT_CAPTION, COLOUR_GREY, OSK_WIDGET_CAPTION), SetDataTip(STR_WHITE_STRING, STR_NULL),
00373 NWidget(WWT_PANEL, COLOUR_GREY),
00374 NWidget(WWT_EDITBOX, COLOUR_GREY, OSK_WIDGET_TEXT), SetMinimalSize(252, 12), SetPadding(2, 2, 2, 2),
00375 EndContainer(),
00376 NWidget(WWT_PANEL, COLOUR_GREY), SetPIP(5, 2, 3),
00377 NWidgetFunction(MakeTopKeys), SetPadding(0, 3, 0, 3),
00378 NWidgetFunction(MakeNumberKeys), SetPadding(0, 3, 0, 3),
00379 NWidgetFunction(MakeQwertyKeys), SetPadding(0, 3, 0, 3),
00380 NWidgetFunction(MakeAsdfgKeys), SetPadding(0, 3, 0, 3),
00381 NWidgetFunction(MakeZxcvbKeys), SetPadding(0, 3, 0, 3),
00382 NWidgetFunction(MakeSpacebarKeys), SetPadding(0, 3, 0, 3),
00383 EndContainer(),
00384 };
00385
00386 static const WindowDesc _osk_desc(
00387 WDP_CENTER, 0, 0,
00388 WC_OSK, WC_NONE,
00389 WDF_UNCLICK_BUTTONS,
00390 _nested_osk_widgets, lengthof(_nested_osk_widgets)
00391 );
00392
00397 void GetKeyboardLayout()
00398 {
00399 char keyboard[2][OSK_KEYBOARD_ENTRIES * 4 + 1];
00400 char errormark[2][OSK_KEYBOARD_ENTRIES + 1];
00401 bool has_error = false;
00402
00403 if (StrEmpty(_keyboard_opt[0])) {
00404 GetString(keyboard[0], STR_OSK_KEYBOARD_LAYOUT, lastof(keyboard[0]));
00405 } else {
00406 strecpy(keyboard[0], _keyboard_opt[0], lastof(keyboard[0]));
00407 }
00408
00409 if (StrEmpty(_keyboard_opt[1])) {
00410 GetString(keyboard[1], STR_OSK_KEYBOARD_LAYOUT_CAPS, lastof(keyboard[1]));
00411 } else {
00412 strecpy(keyboard[1], _keyboard_opt[1], lastof(keyboard[1]));
00413 }
00414
00415 for (uint j = 0; j < 2; j++) {
00416 const char *kbd = keyboard[j];
00417 bool ended = false;
00418 for (uint i = 0; i < OSK_KEYBOARD_ENTRIES; i++) {
00419 _keyboard[j][i] = Utf8Consume(&kbd);
00420
00421
00422 if (_keyboard[j][i] == '\0' || ended) {
00423 ended = true;
00424 _keyboard[j][i] = ' ';
00425 continue;
00426 }
00427
00428 if (IsPrintable(_keyboard[j][i])) {
00429 errormark[j][i] = ' ';
00430 } else {
00431 has_error = true;
00432 errormark[j][i] = '^';
00433 _keyboard[j][i] = ' ';
00434 }
00435 }
00436 }
00437
00438 if (has_error) {
00439 ShowInfoF("The keyboard layout you selected contains invalid chars. Please check those chars marked with ^.");
00440 ShowInfoF("Normal keyboard: %s", keyboard[0]);
00441 ShowInfoF(" %s", errormark[0]);
00442 ShowInfoF("Caps Lock: %s", keyboard[1]);
00443 ShowInfoF(" %s", errormark[1]);
00444 }
00445 }
00446
00456 void ShowOnScreenKeyboard(QueryStringBaseWindow *parent, int button, int cancel, int ok)
00457 {
00458 DeleteWindowById(WC_OSK, 0);
00459
00460 GetKeyboardLayout();
00461 new OskWindow(&_osk_desc, parent, button, cancel, ok);
00462 }
00463
00471 void UpdateOSKOriginalText(const QueryStringBaseWindow *parent, int button)
00472 {
00473 OskWindow *osk = dynamic_cast<OskWindow *>(FindWindowById(WC_OSK, 0));
00474 if (osk == NULL || osk->qs != parent || osk->text_btn != button) return;
00475
00476 free(osk->orig_str_buf);
00477 osk->orig_str_buf = strdup(osk->qs->text.buf);
00478
00479 osk->SetDirty();
00480 }