osk_gui.cpp

Go to the documentation of this file.
00001 /* $Id$ */
00002 
00003 /*
00004  * This file is part of OpenTTD.
00005  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
00006  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00007  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
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 KeyStateBits {
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     /* make a copy in case we need to reset later */
00087     this->orig_str_buf = strdup(this->qs->text.buf);
00088 
00089     this->InitNested(desc, 0);
00090 
00091     /* Not needed by default. */
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     /* clicked a letter */
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         /* Return focus to the parent widget and window. */
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          * Anything device specific can go here.
00177          * The button itself is hidden by default, and when you need it you
00178          * can not hide it in the create event.
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           /* pass information by simulating a button press on parent window */
00209           if (this->ok_btn != 0) {
00210             this->parent->OnClick(pt, this->ok_btn, 1);
00211             /* Window gets deleted when the parent window removes itself. */
00212             return;
00213           }
00214         }
00215         delete this;
00216         break;
00217 
00218       case OSK_WIDGET_CANCEL:
00219         if (this->cancel_btn != 0) { // pass a cancel event to the parent window
00220           this->parent->OnClick(pt, this->cancel_btn, 1);
00221           /* Window gets deleted when the parent window removes itself. */
00222           return;
00223         } else { // or reset to original string
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     /* make the caret of the parent window also blink */
00247     this->parent->SetWidgetDirty(this->text_btn);
00248   }
00249 
00255   virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
00256   {
00257     if (!gui_scope) return;
00258     this->SetWidgetDirty(OSK_WIDGET_TEXT);
00259   }
00260 };
00261 
00262 static const int HALF_KEY_WIDTH = 7;  // Width of 1/2 key in pixels.
00263 static const int INTER_KEY_SPACE = 2; // Number of pixels between two keys.
00264 
00276 static void AddKey(NWidgetHorizontal *hor, int height, int num_half, WidgetType widtype, int widnum, uint16 widdata, int *biggest_index)
00277 {
00278   int key_width = HALF_KEY_WIDTH + (INTER_KEY_SPACE + HALF_KEY_WIDTH) * (num_half - 1);
00279 
00280   if (widtype == NWID_SPACER) {
00281     if (!hor->IsEmpty()) key_width += INTER_KEY_SPACE;
00282     NWidgetSpacer *spc = new NWidgetSpacer(key_width, height);
00283     hor->Add(spc);
00284   } else {
00285     if (!hor->IsEmpty()) {
00286       NWidgetSpacer *spc = new NWidgetSpacer(INTER_KEY_SPACE, height);
00287       hor->Add(spc);
00288     }
00289     NWidgetLeaf *leaf = new NWidgetLeaf(widtype, COLOUR_GREY, widnum, widdata, STR_NULL);
00290     leaf->SetMinimalSize(key_width, height);
00291     hor->Add(leaf);
00292   }
00293 
00294   *biggest_index = max(*biggest_index, widnum);
00295 }
00296 
00298 static NWidgetBase *MakeTopKeys(int *biggest_index)
00299 {
00300   NWidgetHorizontal *hor = new NWidgetHorizontal();
00301   int key_height = FONT_HEIGHT_NORMAL + 2;
00302 
00303   AddKey(hor, key_height, 6 * 2, WWT_TEXTBTN,    OSK_WIDGET_CANCEL,    STR_BUTTON_CANCEL,  biggest_index);
00304   AddKey(hor, key_height, 6 * 2, WWT_TEXTBTN,    OSK_WIDGET_OK,        STR_BUTTON_OK,      biggest_index);
00305   AddKey(hor, key_height, 2 * 2, WWT_PUSHIMGBTN, OSK_WIDGET_BACKSPACE, SPR_OSK_BACKSPACE, biggest_index);
00306   return hor;
00307 }
00308 
00310 static NWidgetBase *MakeNumberKeys(int *biggest_index)
00311 {
00312   NWidgetHorizontal *hor = new NWidgetHorizontalLTR();
00313   int key_height = FONT_HEIGHT_NORMAL + 6;
00314 
00315   for (int widnum = OSK_WIDGET_NUMBERS_FIRST; widnum <= OSK_WIDGET_NUMBERS_LAST; widnum++) {
00316     AddKey(hor, key_height, 2, WWT_PUSHBTN, widnum, 0x0, biggest_index);
00317   }
00318   return hor;
00319 }
00320 
00322 static NWidgetBase *MakeQwertyKeys(int *biggest_index)
00323 {
00324   NWidgetHorizontal *hor = new NWidgetHorizontalLTR();
00325   int key_height = FONT_HEIGHT_NORMAL + 6;
00326 
00327   AddKey(hor, key_height, 3, WWT_PUSHIMGBTN, OSK_WIDGET_SPECIAL, SPR_OSK_SPECIAL, biggest_index);
00328   for (int widnum = OSK_WIDGET_QWERTY_FIRST; widnum <= OSK_WIDGET_QWERTY_LAST; widnum++) {
00329     AddKey(hor, key_height, 2, WWT_PUSHBTN, widnum, 0x0, biggest_index);
00330   }
00331   AddKey(hor, key_height, 1, NWID_SPACER, 0, 0, biggest_index);
00332   return hor;
00333 }
00334 
00336 static NWidgetBase *MakeAsdfgKeys(int *biggest_index)
00337 {
00338   NWidgetHorizontal *hor = new NWidgetHorizontalLTR();
00339   int key_height = FONT_HEIGHT_NORMAL + 6;
00340 
00341   AddKey(hor, key_height, 4, WWT_IMGBTN, OSK_WIDGET_CAPS, SPR_OSK_CAPS, biggest_index);
00342   for (int widnum = OSK_WIDGET_ASDFG_FIRST; widnum <= OSK_WIDGET_ASDFG_LAST; widnum++) {
00343     AddKey(hor, key_height, 2, WWT_PUSHBTN, widnum, 0x0, biggest_index);
00344   }
00345   return hor;
00346 }
00347 
00349 static NWidgetBase *MakeZxcvbKeys(int *biggest_index)
00350 {
00351   NWidgetHorizontal *hor = new NWidgetHorizontalLTR();
00352   int key_height = FONT_HEIGHT_NORMAL + 6;
00353 
00354   AddKey(hor, key_height, 3, WWT_IMGBTN, OSK_WIDGET_SHIFT, SPR_OSK_SHIFT, biggest_index);
00355   for (int widnum = OSK_WIDGET_ZXCVB_FIRST; widnum <= OSK_WIDGET_ZXCVB_LAST; widnum++) {
00356     AddKey(hor, key_height, 2, WWT_PUSHBTN, widnum, 0x0, biggest_index);
00357   }
00358   AddKey(hor, key_height, 1, NWID_SPACER, 0, 0, biggest_index);
00359   return hor;
00360 }
00361 
00363 static NWidgetBase *MakeSpacebarKeys(int *biggest_index)
00364 {
00365   NWidgetHorizontal *hor = new NWidgetHorizontal();
00366   int key_height = FONT_HEIGHT_NORMAL + 6;
00367 
00368   AddKey(hor, key_height,  8, NWID_SPACER, 0, 0, biggest_index);
00369   AddKey(hor, key_height, 13, WWT_PUSHTXTBTN, OSK_WIDGET_SPACE, STR_EMPTY, biggest_index);
00370   AddKey(hor, key_height,  3, NWID_SPACER, 0, 0, biggest_index);
00371   AddKey(hor, key_height,  2, WWT_PUSHIMGBTN, OSK_WIDGET_LEFT,  SPR_OSK_LEFT, biggest_index);
00372   AddKey(hor, key_height,  2, WWT_PUSHIMGBTN, OSK_WIDGET_RIGHT, SPR_OSK_RIGHT, biggest_index);
00373   return hor;
00374 }
00375 
00376 
00377 static const NWidgetPart _nested_osk_widgets[] = {
00378   NWidget(WWT_CAPTION, COLOUR_GREY, OSK_WIDGET_CAPTION), SetDataTip(STR_WHITE_STRING, STR_NULL),
00379   NWidget(WWT_PANEL, COLOUR_GREY),
00380     NWidget(WWT_EDITBOX, COLOUR_GREY, OSK_WIDGET_TEXT), SetMinimalSize(252, 12), SetPadding(2, 2, 2, 2),
00381   EndContainer(),
00382   NWidget(WWT_PANEL, COLOUR_GREY), SetPIP(5, 2, 3),
00383     NWidgetFunction(MakeTopKeys), SetPadding(0, 3, 0, 3),
00384     NWidgetFunction(MakeNumberKeys), SetPadding(0, 3, 0, 3),
00385     NWidgetFunction(MakeQwertyKeys), SetPadding(0, 3, 0, 3),
00386     NWidgetFunction(MakeAsdfgKeys), SetPadding(0, 3, 0, 3),
00387     NWidgetFunction(MakeZxcvbKeys), SetPadding(0, 3, 0, 3),
00388     NWidgetFunction(MakeSpacebarKeys), SetPadding(0, 3, 0, 3),
00389   EndContainer(),
00390 };
00391 
00392 static const WindowDesc _osk_desc(
00393   WDP_CENTER, 0, 0,
00394   WC_OSK, WC_NONE,
00395   WDF_UNCLICK_BUTTONS,
00396   _nested_osk_widgets, lengthof(_nested_osk_widgets)
00397 );
00398 
00403 void GetKeyboardLayout()
00404 {
00405   char keyboard[2][OSK_KEYBOARD_ENTRIES * 4 + 1];
00406   char errormark[2][OSK_KEYBOARD_ENTRIES + 1]; // used for marking invalid chars
00407   bool has_error = false; // true when an invalid char is detected
00408 
00409   if (StrEmpty(_keyboard_opt[0])) {
00410     GetString(keyboard[0], STR_OSK_KEYBOARD_LAYOUT, lastof(keyboard[0]));
00411   } else {
00412     strecpy(keyboard[0], _keyboard_opt[0], lastof(keyboard[0]));
00413   }
00414 
00415   if (StrEmpty(_keyboard_opt[1])) {
00416     GetString(keyboard[1], STR_OSK_KEYBOARD_LAYOUT_CAPS, lastof(keyboard[1]));
00417   } else {
00418     strecpy(keyboard[1], _keyboard_opt[1], lastof(keyboard[1]));
00419   }
00420 
00421   for (uint j = 0; j < 2; j++) {
00422     const char *kbd = keyboard[j];
00423     bool ended = false;
00424     for (uint i = 0; i < OSK_KEYBOARD_ENTRIES; i++) {
00425       _keyboard[j][i] = Utf8Consume(&kbd);
00426 
00427       /* Be lenient when the last characters are missing (is quite normal) */
00428       if (_keyboard[j][i] == '\0' || ended) {
00429         ended = true;
00430         _keyboard[j][i] = ' ';
00431         continue;
00432       }
00433 
00434       if (IsPrintable(_keyboard[j][i])) {
00435         errormark[j][i] = ' ';
00436       } else {
00437         has_error = true;
00438         errormark[j][i] = '^';
00439         _keyboard[j][i] = ' ';
00440       }
00441     }
00442   }
00443 
00444   if (has_error) {
00445     ShowInfoF("The keyboard layout you selected contains invalid chars. Please check those chars marked with ^.");
00446     ShowInfoF("Normal keyboard:  %s", keyboard[0]);
00447     ShowInfoF("                  %s", errormark[0]);
00448     ShowInfoF("Caps Lock:        %s", keyboard[1]);
00449     ShowInfoF("                  %s", errormark[1]);
00450   }
00451 }
00452 
00462 void ShowOnScreenKeyboard(QueryStringBaseWindow *parent, int button, int cancel, int ok)
00463 {
00464   DeleteWindowById(WC_OSK, 0);
00465 
00466   GetKeyboardLayout();
00467   new OskWindow(&_osk_desc, parent, button, cancel, ok);
00468 }
00469 
00477 void UpdateOSKOriginalText(const QueryStringBaseWindow *parent, int button)
00478 {
00479   OskWindow *osk = dynamic_cast<OskWindow *>(FindWindowById(WC_OSK, 0));
00480   if (osk == NULL || osk->qs != parent || osk->text_btn != button) return;
00481 
00482   free(osk->orig_str_buf);
00483   osk->orig_str_buf = strdup(osk->qs->text.buf);
00484 
00485   osk->SetDirty();
00486 }

Generated on Mon May 9 05:18:57 2011 for OpenTTD by  doxygen 1.6.1