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