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 "widgets/osk_widget.h"
00021
00022 #include "table/sprites.h"
00023 #include "table/strings.h"
00024
00025 char _keyboard_opt[2][OSK_KEYBOARD_ENTRIES * 4 + 1];
00026 static WChar _keyboard[2][OSK_KEYBOARD_ENTRIES];
00027
00028 enum KeyStateBits {
00029 KEYS_NONE,
00030 KEYS_SHIFT,
00031 KEYS_CAPS
00032 };
00033 static byte _keystate = KEYS_NONE;
00034
00035 struct OskWindow : public Window {
00036 StringID caption;
00037 QueryString *qs;
00038 int text_btn;
00039 int ok_btn;
00040 int cancel_btn;
00041 Textbuf *text;
00042 char *orig_str_buf;
00043 bool shift;
00044
00045 OskWindow(const WindowDesc *desc, QueryStringBaseWindow *parent, int button, int cancel, int ok) : Window()
00046 {
00047 this->parent = parent;
00048 assert(parent != NULL);
00049
00050 NWidgetCore *par_wid = parent->GetWidget<NWidgetCore>(button);
00051 assert(par_wid != NULL);
00052 this->caption = (par_wid->widget_data != STR_NULL) ? par_wid->widget_data : parent->caption;
00053
00054 this->qs = parent;
00055 this->text_btn = button;
00056 this->cancel_btn = cancel;
00057 this->ok_btn = ok;
00058 this->text = &parent->text;
00059
00060
00061 this->orig_str_buf = strdup(this->qs->text.buf);
00062
00063 this->InitNested(desc, 0);
00064
00065
00066 this->DisableWidget(WID_OSK_SPECIAL);
00067
00068 this->UpdateOskState();
00069 }
00070
00071 ~OskWindow()
00072 {
00073 free(this->orig_str_buf);
00074 }
00075
00081 void UpdateOskState()
00082 {
00083 this->shift = HasBit(_keystate, KEYS_CAPS) ^ HasBit(_keystate, KEYS_SHIFT);
00084
00085 for (uint i = 0; i < OSK_KEYBOARD_ENTRIES; i++) {
00086 this->SetWidgetDisabledState(WID_OSK_LETTERS + i,
00087 !IsValidChar(_keyboard[this->shift][i], this->qs->afilter) || _keyboard[this->shift][i] == ' ');
00088 }
00089 this->SetWidgetDisabledState(WID_OSK_SPACE, !IsValidChar(' ', this->qs->afilter));
00090
00091 this->LowerWidget(WID_OSK_TEXT);
00092 this->SetWidgetLoweredState(WID_OSK_SHIFT, HasBit(_keystate, KEYS_SHIFT));
00093 this->SetWidgetLoweredState(WID_OSK_CAPS, HasBit(_keystate, KEYS_CAPS));
00094 }
00095
00096 virtual void SetStringParameters(int widget) const
00097 {
00098 if (widget == WID_OSK_CAPTION) SetDParam(0, this->caption);
00099 }
00100
00101 virtual void DrawWidget(const Rect &r, int widget) const
00102 {
00103 if (widget < WID_OSK_LETTERS) return;
00104
00105 widget -= WID_OSK_LETTERS;
00106 DrawCharCentered(_keyboard[this->shift][widget],
00107 r.left + 8,
00108 r.top + 3,
00109 TC_BLACK);
00110 }
00111
00112 virtual void OnPaint()
00113 {
00114 this->DrawWidgets();
00115
00116 this->qs->DrawEditBox(this, WID_OSK_TEXT);
00117 }
00118
00119 virtual void OnClick(Point pt, int widget, int click_count)
00120 {
00121
00122 if (widget >= WID_OSK_LETTERS) {
00123 WChar c = _keyboard[this->shift][widget - WID_OSK_LETTERS];
00124
00125 if (!IsValidChar(c, this->qs->afilter)) return;
00126
00127 if (this->qs->text.InsertChar(c)) this->InvalidateParent();
00128
00129 if (HasBit(_keystate, KEYS_SHIFT)) {
00130 ToggleBit(_keystate, KEYS_SHIFT);
00131 this->GetWidget<NWidgetCore>(WID_OSK_SHIFT)->colour = HasBit(_keystate, KEYS_SHIFT) ? COLOUR_WHITE : COLOUR_GREY;
00132 this->SetDirty();
00133 }
00134
00135 this->parent->SetFocusedWidget(this->text_btn);
00136 SetFocusedWindow(this->parent);
00137 return;
00138 }
00139
00140 switch (widget) {
00141 case WID_OSK_BACKSPACE:
00142 if (this->qs->text.DeleteChar(WKC_BACKSPACE)) this->InvalidateParent();
00143 break;
00144
00145 case WID_OSK_SPECIAL:
00146
00147
00148
00149
00150
00151 break;
00152
00153 case WID_OSK_CAPS:
00154 ToggleBit(_keystate, KEYS_CAPS);
00155 this->UpdateOskState();
00156 this->SetDirty();
00157 break;
00158
00159 case WID_OSK_SHIFT:
00160 ToggleBit(_keystate, KEYS_SHIFT);
00161 this->UpdateOskState();
00162 this->SetDirty();
00163 break;
00164
00165 case WID_OSK_SPACE:
00166 if (this->qs->text.InsertChar(' ')) this->InvalidateParent();
00167 break;
00168
00169 case WID_OSK_LEFT:
00170 if (this->qs->text.MovePos(WKC_LEFT)) this->InvalidateParent();
00171 break;
00172
00173 case WID_OSK_RIGHT:
00174 if (this->qs->text.MovePos(WKC_RIGHT)) this->InvalidateParent();
00175 break;
00176
00177 case WID_OSK_OK:
00178 if (this->qs->orig == NULL || strcmp(this->qs->text.buf, this->qs->orig) != 0) {
00179
00180 if (this->ok_btn != 0) {
00181 this->parent->OnClick(pt, this->ok_btn, 1);
00182
00183 return;
00184 }
00185 }
00186 delete this;
00187 break;
00188
00189 case WID_OSK_CANCEL:
00190 if (this->cancel_btn != 0) {
00191 this->parent->OnClick(pt, this->cancel_btn, 1);
00192
00193 return;
00194 } else {
00195 strcpy(qs->text.buf, this->orig_str_buf);
00196 qs->text.UpdateSize();
00197 qs->text.MovePos(WKC_END);
00198 this->InvalidateParent();
00199 delete this;
00200 }
00201 break;
00202 }
00203
00204 this->parent->SetFocusedWidget(this->text_btn);
00205 SetFocusedWindow(this->parent);
00206 }
00207
00208 void InvalidateParent()
00209 {
00210 QueryStringBaseWindow *w = dynamic_cast<QueryStringBaseWindow*>(this->parent);
00211 if (w != NULL) w->OnOSKInput(this->text_btn);
00212
00213 this->SetWidgetDirty(WID_OSK_TEXT);
00214 if (this->parent != NULL) this->parent->SetWidgetDirty(this->text_btn);
00215 }
00216
00217 virtual void OnMouseLoop()
00218 {
00219 this->qs->HandleEditBox(this, WID_OSK_TEXT);
00220
00221 this->parent->SetWidgetDirty(this->text_btn);
00222 }
00223
00229 virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
00230 {
00231 if (!gui_scope) return;
00232 this->SetWidgetDirty(WID_OSK_TEXT);
00233 }
00234 };
00235
00236 static const int HALF_KEY_WIDTH = 7;
00237 static const int INTER_KEY_SPACE = 2;
00238
00250 static void AddKey(NWidgetHorizontal *hor, int height, int num_half, WidgetType widtype, int widnum, uint16 widdata, int *biggest_index)
00251 {
00252 int key_width = HALF_KEY_WIDTH + (INTER_KEY_SPACE + HALF_KEY_WIDTH) * (num_half - 1);
00253
00254 if (widtype == NWID_SPACER) {
00255 if (!hor->IsEmpty()) key_width += INTER_KEY_SPACE;
00256 NWidgetSpacer *spc = new NWidgetSpacer(key_width, height);
00257 hor->Add(spc);
00258 } else {
00259 if (!hor->IsEmpty()) {
00260 NWidgetSpacer *spc = new NWidgetSpacer(INTER_KEY_SPACE, height);
00261 hor->Add(spc);
00262 }
00263 NWidgetLeaf *leaf = new NWidgetLeaf(widtype, COLOUR_GREY, widnum, widdata, STR_NULL);
00264 leaf->SetMinimalSize(key_width, height);
00265 hor->Add(leaf);
00266 }
00267
00268 *biggest_index = max(*biggest_index, widnum);
00269 }
00270
00272 static NWidgetBase *MakeTopKeys(int *biggest_index)
00273 {
00274 NWidgetHorizontal *hor = new NWidgetHorizontal();
00275 int key_height = FONT_HEIGHT_NORMAL + 2;
00276
00277 AddKey(hor, key_height, 6 * 2, WWT_TEXTBTN, WID_OSK_CANCEL, STR_BUTTON_CANCEL, biggest_index);
00278 AddKey(hor, key_height, 6 * 2, WWT_TEXTBTN, WID_OSK_OK, STR_BUTTON_OK, biggest_index);
00279 AddKey(hor, key_height, 2 * 2, WWT_PUSHIMGBTN, WID_OSK_BACKSPACE, SPR_OSK_BACKSPACE, biggest_index);
00280 return hor;
00281 }
00282
00284 static NWidgetBase *MakeNumberKeys(int *biggest_index)
00285 {
00286 NWidgetHorizontal *hor = new NWidgetHorizontalLTR();
00287 int key_height = FONT_HEIGHT_NORMAL + 6;
00288
00289 for (int widnum = WID_OSK_NUMBERS_FIRST; widnum <= WID_OSK_NUMBERS_LAST; widnum++) {
00290 AddKey(hor, key_height, 2, WWT_PUSHBTN, widnum, 0x0, biggest_index);
00291 }
00292 return hor;
00293 }
00294
00296 static NWidgetBase *MakeQwertyKeys(int *biggest_index)
00297 {
00298 NWidgetHorizontal *hor = new NWidgetHorizontalLTR();
00299 int key_height = FONT_HEIGHT_NORMAL + 6;
00300
00301 AddKey(hor, key_height, 3, WWT_PUSHIMGBTN, WID_OSK_SPECIAL, SPR_OSK_SPECIAL, biggest_index);
00302 for (int widnum = WID_OSK_QWERTY_FIRST; widnum <= WID_OSK_QWERTY_LAST; widnum++) {
00303 AddKey(hor, key_height, 2, WWT_PUSHBTN, widnum, 0x0, biggest_index);
00304 }
00305 AddKey(hor, key_height, 1, NWID_SPACER, 0, 0, biggest_index);
00306 return hor;
00307 }
00308
00310 static NWidgetBase *MakeAsdfgKeys(int *biggest_index)
00311 {
00312 NWidgetHorizontal *hor = new NWidgetHorizontalLTR();
00313 int key_height = FONT_HEIGHT_NORMAL + 6;
00314
00315 AddKey(hor, key_height, 4, WWT_IMGBTN, WID_OSK_CAPS, SPR_OSK_CAPS, biggest_index);
00316 for (int widnum = WID_OSK_ASDFG_FIRST; widnum <= WID_OSK_ASDFG_LAST; widnum++) {
00317 AddKey(hor, key_height, 2, WWT_PUSHBTN, widnum, 0x0, biggest_index);
00318 }
00319 return hor;
00320 }
00321
00323 static NWidgetBase *MakeZxcvbKeys(int *biggest_index)
00324 {
00325 NWidgetHorizontal *hor = new NWidgetHorizontalLTR();
00326 int key_height = FONT_HEIGHT_NORMAL + 6;
00327
00328 AddKey(hor, key_height, 3, WWT_IMGBTN, WID_OSK_SHIFT, SPR_OSK_SHIFT, biggest_index);
00329 for (int widnum = WID_OSK_ZXCVB_FIRST; widnum <= WID_OSK_ZXCVB_LAST; widnum++) {
00330 AddKey(hor, key_height, 2, WWT_PUSHBTN, widnum, 0x0, biggest_index);
00331 }
00332 AddKey(hor, key_height, 1, NWID_SPACER, 0, 0, biggest_index);
00333 return hor;
00334 }
00335
00337 static NWidgetBase *MakeSpacebarKeys(int *biggest_index)
00338 {
00339 NWidgetHorizontal *hor = new NWidgetHorizontal();
00340 int key_height = FONT_HEIGHT_NORMAL + 6;
00341
00342 AddKey(hor, key_height, 8, NWID_SPACER, 0, 0, biggest_index);
00343 AddKey(hor, key_height, 13, WWT_PUSHTXTBTN, WID_OSK_SPACE, STR_EMPTY, biggest_index);
00344 AddKey(hor, key_height, 3, NWID_SPACER, 0, 0, biggest_index);
00345 AddKey(hor, key_height, 2, WWT_PUSHIMGBTN, WID_OSK_LEFT, SPR_OSK_LEFT, biggest_index);
00346 AddKey(hor, key_height, 2, WWT_PUSHIMGBTN, WID_OSK_RIGHT, SPR_OSK_RIGHT, biggest_index);
00347 return hor;
00348 }
00349
00350
00351 static const NWidgetPart _nested_osk_widgets[] = {
00352 NWidget(WWT_CAPTION, COLOUR_GREY, WID_OSK_CAPTION), SetDataTip(STR_WHITE_STRING, STR_NULL),
00353 NWidget(WWT_PANEL, COLOUR_GREY),
00354 NWidget(WWT_EDITBOX, COLOUR_GREY, WID_OSK_TEXT), SetMinimalSize(252, 12), SetPadding(2, 2, 2, 2),
00355 EndContainer(),
00356 NWidget(WWT_PANEL, COLOUR_GREY), SetPIP(5, 2, 3),
00357 NWidgetFunction(MakeTopKeys), SetPadding(0, 3, 0, 3),
00358 NWidgetFunction(MakeNumberKeys), SetPadding(0, 3, 0, 3),
00359 NWidgetFunction(MakeQwertyKeys), SetPadding(0, 3, 0, 3),
00360 NWidgetFunction(MakeAsdfgKeys), SetPadding(0, 3, 0, 3),
00361 NWidgetFunction(MakeZxcvbKeys), SetPadding(0, 3, 0, 3),
00362 NWidgetFunction(MakeSpacebarKeys), SetPadding(0, 3, 0, 3),
00363 EndContainer(),
00364 };
00365
00366 static const WindowDesc _osk_desc(
00367 WDP_CENTER, 0, 0,
00368 WC_OSK, WC_NONE,
00369 WDF_UNCLICK_BUTTONS,
00370 _nested_osk_widgets, lengthof(_nested_osk_widgets)
00371 );
00372
00377 void GetKeyboardLayout()
00378 {
00379 char keyboard[2][OSK_KEYBOARD_ENTRIES * 4 + 1];
00380 char errormark[2][OSK_KEYBOARD_ENTRIES + 1];
00381 bool has_error = false;
00382
00383 if (StrEmpty(_keyboard_opt[0])) {
00384 GetString(keyboard[0], STR_OSK_KEYBOARD_LAYOUT, lastof(keyboard[0]));
00385 } else {
00386 strecpy(keyboard[0], _keyboard_opt[0], lastof(keyboard[0]));
00387 }
00388
00389 if (StrEmpty(_keyboard_opt[1])) {
00390 GetString(keyboard[1], STR_OSK_KEYBOARD_LAYOUT_CAPS, lastof(keyboard[1]));
00391 } else {
00392 strecpy(keyboard[1], _keyboard_opt[1], lastof(keyboard[1]));
00393 }
00394
00395 for (uint j = 0; j < 2; j++) {
00396 const char *kbd = keyboard[j];
00397 bool ended = false;
00398 for (uint i = 0; i < OSK_KEYBOARD_ENTRIES; i++) {
00399 _keyboard[j][i] = Utf8Consume(&kbd);
00400
00401
00402 if (_keyboard[j][i] == '\0' || ended) {
00403 ended = true;
00404 _keyboard[j][i] = ' ';
00405 continue;
00406 }
00407
00408 if (IsPrintable(_keyboard[j][i])) {
00409 errormark[j][i] = ' ';
00410 } else {
00411 has_error = true;
00412 errormark[j][i] = '^';
00413 _keyboard[j][i] = ' ';
00414 }
00415 }
00416 }
00417
00418 if (has_error) {
00419 ShowInfoF("The keyboard layout you selected contains invalid chars. Please check those chars marked with ^.");
00420 ShowInfoF("Normal keyboard: %s", keyboard[0]);
00421 ShowInfoF(" %s", errormark[0]);
00422 ShowInfoF("Caps Lock: %s", keyboard[1]);
00423 ShowInfoF(" %s", errormark[1]);
00424 }
00425 }
00426
00436 void ShowOnScreenKeyboard(QueryStringBaseWindow *parent, int button, int cancel, int ok)
00437 {
00438 DeleteWindowById(WC_OSK, 0);
00439
00440 GetKeyboardLayout();
00441 new OskWindow(&_osk_desc, parent, button, cancel, ok);
00442 }
00443
00451 void UpdateOSKOriginalText(const QueryStringBaseWindow *parent, int button)
00452 {
00453 OskWindow *osk = dynamic_cast<OskWindow *>(FindWindowById(WC_OSK, 0));
00454 if (osk == NULL || osk->qs != parent || osk->text_btn != button) return;
00455
00456 free(osk->orig_str_buf);
00457 osk->orig_str_buf = strdup(osk->qs->text.buf);
00458
00459 osk->SetDirty();
00460 }