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