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