osk_gui.cpp

Go to the documentation of this file.
00001 /* $Id: osk_gui.cpp 15723 2009-03-15 15:12:06Z rubidium $ */
00002 
00005 #include "stdafx.h"
00006 #include "string_func.h"
00007 #include "strings_func.h"
00008 #include "debug.h"
00009 #include "window_func.h"
00010 #include "gfx_func.h"
00011 #include "querystring_gui.h"
00012 
00013 #include "table/sprites.h"
00014 #include "table/strings.h"
00015 
00016 enum OskWidgets {
00017   OSK_WIDGET_TEXT = 3,
00018   OSK_WIDGET_CANCEL = 5,
00019   OSK_WIDGET_OK,
00020   OSK_WIDGET_BACKSPACE,
00021   OSK_WIDGET_SPECIAL,
00022   OSK_WIDGET_CAPS,
00023   OSK_WIDGET_SHIFT,
00024   OSK_WIDGET_SPACE,
00025   OSK_WIDGET_LEFT,
00026   OSK_WIDGET_RIGHT,
00027   OSK_WIDGET_LETTERS
00028 };
00029 
00030 char _keyboard_opt[2][OSK_KEYBOARD_ENTRIES * 4 + 1];
00031 static WChar _keyboard[2][OSK_KEYBOARD_ENTRIES];
00032 
00033 enum {
00034   KEYS_NONE,
00035   KEYS_SHIFT,
00036   KEYS_CAPS
00037 };
00038 static byte _keystate = KEYS_NONE;
00039 
00040 struct OskWindow : public Window {
00041   StringID caption;      
00042   QueryString *qs;       
00043   int text_btn;          
00044   int ok_btn;            
00045   int cancel_btn;        
00046   Textbuf *text;         
00047   char *orig_str_buf;    
00048 
00049   OskWindow(const WindowDesc *desc, QueryStringBaseWindow *parent, int button, int cancel, int ok) : Window(desc)
00050   {
00051     this->parent = parent;
00052     assert(parent != NULL);
00053 
00054     this->caption = (parent->widget[button].data != STR_NULL) ? parent->widget[button].data : parent->caption;
00055 
00056     this->qs         = parent;
00057     this->text_btn   = button;
00058     this->cancel_btn = cancel;
00059     this->ok_btn     = ok;
00060     this->text       = &parent->text;
00061 
00062     /* make a copy in case we need to reset later */
00063     this->orig_str_buf = strdup(this->qs->text.buf);
00064 
00065     /* Not needed by default. */
00066     this->DisableWidget(OSK_WIDGET_SPECIAL);
00067 
00068     this->FindWindowPlacementAndResize(desc);
00069   }
00070 
00071   ~OskWindow()
00072   {
00073     free(this->orig_str_buf);
00074   }
00075 
00081   void ChangeOskDiabledState(bool shift)
00082   {
00083     for (uint i = 0; i < OSK_KEYBOARD_ENTRIES; i++) {
00084       this->SetWidgetDisabledState(OSK_WIDGET_LETTERS + i,
00085           !IsValidChar(_keyboard[shift][i], this->qs->afilter) || _keyboard[shift][i] == ' ');
00086     }
00087     this->SetWidgetDisabledState(OSK_WIDGET_SPACE, !IsValidChar(' ', this->qs->afilter));
00088   }
00089 
00090   virtual void OnPaint()
00091   {
00092     bool shift = HasBit(_keystate, KEYS_CAPS) ^ HasBit(_keystate, KEYS_SHIFT);
00093 
00094     this->LowerWidget(OSK_WIDGET_TEXT);
00095     this->SetWidgetLoweredState(OSK_WIDGET_SHIFT, HasBit(_keystate, KEYS_SHIFT));
00096     this->SetWidgetLoweredState(OSK_WIDGET_CAPS, HasBit(_keystate, KEYS_CAPS));
00097 
00098     this->ChangeOskDiabledState(shift);
00099 
00100     SetDParam(0, this->caption);
00101     this->DrawWidgets();
00102 
00103     for (uint i = 0; i < OSK_KEYBOARD_ENTRIES; i++) {
00104       DrawCharCentered(_keyboard[shift][i],
00105         this->widget[OSK_WIDGET_LETTERS + i].left + 8,
00106         this->widget[OSK_WIDGET_LETTERS + i].top + 3,
00107         TC_BLACK);
00108     }
00109 
00110     this->qs->DrawEditBox(this, OSK_WIDGET_TEXT);
00111   }
00112 
00113   virtual void OnClick(Point pt, int widget)
00114   {
00115     /* clicked a letter */
00116     if (widget >= OSK_WIDGET_LETTERS) {
00117       bool shift = HasBit(_keystate, KEYS_CAPS) ^ HasBit(_keystate, KEYS_SHIFT);
00118 
00119       WChar c = _keyboard[shift][widget - OSK_WIDGET_LETTERS];
00120 
00121       if (!IsValidChar(c, this->qs->afilter)) return;
00122 
00123       if (InsertTextBufferChar(&this->qs->text, c)) this->InvalidateParent();
00124 
00125       if (HasBit(_keystate, KEYS_SHIFT)) {
00126         ToggleBit(_keystate, KEYS_SHIFT);
00127         this->widget[OSK_WIDGET_SHIFT].colour = HasBit(_keystate, KEYS_SHIFT) ? COLOUR_WHITE : COLOUR_GREY;
00128         this->SetDirty();
00129       }
00130       return;
00131     }
00132 
00133     switch (widget) {
00134       case OSK_WIDGET_TEXT:
00135         /* Find the edit box of the parent window and give focus to that */
00136         for (uint i = 0; i < this->parent->widget_count; i++) {
00137           Widget &wi = this->parent->widget[i];
00138           if (wi.type == WWT_EDITBOX) {
00139             this->parent->focused_widget = &wi;
00140             break;
00141           }
00142         }
00143 
00144         /* Give focus to parent window */
00145         SetFocusedWindow(this->parent);
00146 
00147         break;
00148 
00149       case OSK_WIDGET_BACKSPACE:
00150         if (DeleteTextBufferChar(&this->qs->text, WKC_BACKSPACE)) this->InvalidateParent();
00151         break;
00152 
00153       case OSK_WIDGET_SPECIAL:
00154         /*
00155          * Anything device specific can go here.
00156          * The button itself is hidden by default, and when you need it you
00157          * can not hide it in the create event.
00158          */
00159         break;
00160 
00161       case OSK_WIDGET_CAPS:
00162         ToggleBit(_keystate, KEYS_CAPS);
00163         this->SetDirty();
00164         break;
00165 
00166       case OSK_WIDGET_SHIFT:
00167         ToggleBit(_keystate, KEYS_SHIFT);
00168         this->SetDirty();
00169         break;
00170 
00171       case OSK_WIDGET_SPACE:
00172         if (InsertTextBufferChar(&this->qs->text, ' ')) this->InvalidateParent();
00173         break;
00174 
00175       case OSK_WIDGET_LEFT:
00176         if (MoveTextBufferPos(&this->qs->text, WKC_LEFT)) this->InvalidateParent();
00177         break;
00178 
00179       case OSK_WIDGET_RIGHT:
00180         if (MoveTextBufferPos(&this->qs->text, WKC_RIGHT)) this->InvalidateParent();
00181         break;
00182 
00183       case OSK_WIDGET_OK:
00184         if (this->qs->orig == NULL || strcmp(this->qs->text.buf, this->qs->orig) != 0) {
00185           /* pass information by simulating a button press on parent window */
00186           if (this->ok_btn != 0) {
00187             this->parent->OnClick(pt, this->ok_btn);
00188             /* Window gets deleted when the parent window removes itself. */
00189             return;
00190           }
00191         }
00192         delete this;
00193         break;
00194 
00195       case OSK_WIDGET_CANCEL:
00196         if (this->cancel_btn != 0) { // pass a cancel event to the parent window
00197           this->parent->OnClick(pt, this->cancel_btn);
00198           /* Window gets deleted when the parent window removes itself. */
00199           return;
00200         } else { // or reset to original string
00201           strcpy(qs->text.buf, this->orig_str_buf);
00202           UpdateTextBufferSize(&qs->text);
00203           MoveTextBufferPos(&qs->text, WKC_END);
00204           this->InvalidateParent();
00205           delete this;
00206         }
00207         break;
00208     }
00209   }
00210 
00211   void InvalidateParent()
00212   {
00213     QueryStringBaseWindow *w = dynamic_cast<QueryStringBaseWindow*>(this->parent);
00214     if (w != NULL) w->OnOSKInput(this->text_btn);
00215 
00216     this->InvalidateWidget(OSK_WIDGET_TEXT);
00217     if (this->parent != NULL) this->parent->InvalidateWidget(this->text_btn);
00218   }
00219 
00220   virtual void OnMouseLoop()
00221   {
00222     this->qs->HandleEditBox(this, OSK_WIDGET_TEXT);
00223     /* make the caret of the parent window also blink */
00224     this->parent->InvalidateWidget(this->text_btn);
00225   }
00226 
00227   virtual void OnInvalidateData(int)
00228   {
00229     this->InvalidateWidget(OSK_WIDGET_TEXT);
00230   }
00231 };
00232 
00233 static const Widget _osk_widgets[] = {
00234 {      WWT_EMPTY, RESIZE_NONE,  COLOUR_GREY,     0,     0,     0,     0, 0x0,               STR_NULL},
00235 {    WWT_CAPTION, RESIZE_NONE,  COLOUR_GREY,     0,   255,     0,    13, STR_012D,          STR_NULL},
00236 {      WWT_PANEL, RESIZE_NONE,  COLOUR_GREY,     0,   255,    14,    29, 0x0,               STR_NULL},
00237 {    WWT_EDITBOX, RESIZE_NONE,  COLOUR_GREY,     2,   253,    16,    27, 0x0,               STR_NULL},
00238 
00239 {      WWT_PANEL, RESIZE_NONE,  COLOUR_GREY,     0,   255,    30,   139, 0x0,               STR_NULL},
00240 
00241 {    WWT_TEXTBTN, RESIZE_NONE,  COLOUR_GREY,     3,   108,    35,    46, STR_012E_CANCEL,   STR_NULL},
00242 {    WWT_TEXTBTN, RESIZE_NONE,  COLOUR_GREY,   111,   216,    35,    46, STR_012F_OK,       STR_NULL},
00243 { WWT_PUSHIMGBTN, RESIZE_NONE,  COLOUR_GREY,   219,   252,    35,    46, SPR_OSK_BACKSPACE, STR_NULL},
00244 
00245 { WWT_PUSHIMGBTN, RESIZE_NONE,  COLOUR_GREY,     3,    27,    67,    82, SPR_OSK_SPECIAL,   STR_NULL},
00246 {     WWT_IMGBTN, RESIZE_NONE,  COLOUR_GREY,     3,    36,    85,   100, SPR_OSK_CAPS,      STR_NULL},
00247 {     WWT_IMGBTN, RESIZE_NONE,  COLOUR_GREY,     3,    27,   103,   118, SPR_OSK_SHIFT,     STR_NULL},
00248 
00249 { WWT_PUSHTXTBTN, RESIZE_NONE,  COLOUR_GREY,    75,   189,   121,   136, STR_EMPTY,         STR_NULL},
00250 
00251 { WWT_PUSHIMGBTN, RESIZE_NONE,  COLOUR_GREY,   219,   234,   121,   136, SPR_OSK_LEFT,      STR_NULL},
00252 { WWT_PUSHIMGBTN, RESIZE_NONE,  COLOUR_GREY,   237,   252,   121,   136, SPR_OSK_RIGHT,     STR_NULL},
00253 
00254 {    WWT_PUSHBTN, RESIZE_NONE,  COLOUR_GREY,     3,    18,    49,    64, 0x0,    STR_NULL},
00255 {    WWT_PUSHBTN, RESIZE_NONE,  COLOUR_GREY,    21,    36,    49,    64, 0x0,    STR_NULL},
00256 {    WWT_PUSHBTN, RESIZE_NONE,  COLOUR_GREY,    39,    54,    49,    64, 0x0,    STR_NULL},
00257 {    WWT_PUSHBTN, RESIZE_NONE,  COLOUR_GREY,    57,    72,    49,    64, 0x0,    STR_NULL},
00258 {    WWT_PUSHBTN, RESIZE_NONE,  COLOUR_GREY,    75,    90,    49,    64, 0x0,    STR_NULL},
00259 {    WWT_PUSHBTN, RESIZE_NONE,  COLOUR_GREY,    93,   108,    49,    64, 0x0,    STR_NULL},
00260 {    WWT_PUSHBTN, RESIZE_NONE,  COLOUR_GREY,   111,   126,    49,    64, 0x0,    STR_NULL},
00261 {    WWT_PUSHBTN, RESIZE_NONE,  COLOUR_GREY,   129,   144,    49,    64, 0x0,    STR_NULL},
00262 {    WWT_PUSHBTN, RESIZE_NONE,  COLOUR_GREY,   147,   162,    49,    64, 0x0,    STR_NULL},
00263 {    WWT_PUSHBTN, RESIZE_NONE,  COLOUR_GREY,   165,   180,    49,    64, 0x0,    STR_NULL},
00264 {    WWT_PUSHBTN, RESIZE_NONE,  COLOUR_GREY,   183,   198,    49,    64, 0x0,    STR_NULL},
00265 {    WWT_PUSHBTN, RESIZE_NONE,  COLOUR_GREY,   201,   216,    49,    64, 0x0,    STR_NULL},
00266 {    WWT_PUSHBTN, RESIZE_NONE,  COLOUR_GREY,   219,   234,    49,    64, 0x0,    STR_NULL},
00267 {    WWT_PUSHBTN, RESIZE_NONE,  COLOUR_GREY,   237,   252,    49,    64, 0x0,    STR_NULL},
00268 
00269 {    WWT_PUSHBTN, RESIZE_NONE,  COLOUR_GREY,    30,    45,    67,    82, 0x0,    STR_NULL},
00270 {    WWT_PUSHBTN, RESIZE_NONE,  COLOUR_GREY,    48,    63,    67,    82, 0x0,    STR_NULL},
00271 {    WWT_PUSHBTN, RESIZE_NONE,  COLOUR_GREY,    66,    81,    67,    82, 0x0,    STR_NULL},
00272 {    WWT_PUSHBTN, RESIZE_NONE,  COLOUR_GREY,    84,    99,    67,    82, 0x0,    STR_NULL},
00273 {    WWT_PUSHBTN, RESIZE_NONE,  COLOUR_GREY,   102,   117,    67,    82, 0x0,    STR_NULL},
00274 {    WWT_PUSHBTN, RESIZE_NONE,  COLOUR_GREY,   120,   135,    67,    82, 0x0,    STR_NULL},
00275 {    WWT_PUSHBTN, RESIZE_NONE,  COLOUR_GREY,   138,   153,    67,    82, 0x0,    STR_NULL},
00276 {    WWT_PUSHBTN, RESIZE_NONE,  COLOUR_GREY,   156,   171,    67,    82, 0x0,    STR_NULL},
00277 {    WWT_PUSHBTN, RESIZE_NONE,  COLOUR_GREY,   174,   189,    67,    82, 0x0,    STR_NULL},
00278 {    WWT_PUSHBTN, RESIZE_NONE,  COLOUR_GREY,   192,   207,    67,    82, 0x0,    STR_NULL},
00279 {    WWT_PUSHBTN, RESIZE_NONE,  COLOUR_GREY,   210,   225,    67,    82, 0x0,    STR_NULL},
00280 {    WWT_PUSHBTN, RESIZE_NONE,  COLOUR_GREY,   228,   243,    67,    82, 0x0,    STR_NULL},
00281 
00282 {    WWT_PUSHBTN, RESIZE_NONE,  COLOUR_GREY,    39,    54,    85,   100, 0x0,    STR_NULL},
00283 {    WWT_PUSHBTN, RESIZE_NONE,  COLOUR_GREY,    57,    72,    85,   100, 0x0,    STR_NULL},
00284 {    WWT_PUSHBTN, RESIZE_NONE,  COLOUR_GREY,    75,    90,    85,   100, 0x0,    STR_NULL},
00285 {    WWT_PUSHBTN, RESIZE_NONE,  COLOUR_GREY,    93,   108,    85,   100, 0x0,    STR_NULL},
00286 {    WWT_PUSHBTN, RESIZE_NONE,  COLOUR_GREY,   111,   126,    85,   100, 0x0,    STR_NULL},
00287 {    WWT_PUSHBTN, RESIZE_NONE,  COLOUR_GREY,   129,   144,    85,   100, 0x0,    STR_NULL},
00288 {    WWT_PUSHBTN, RESIZE_NONE,  COLOUR_GREY,   147,   162,    85,   100, 0x0,    STR_NULL},
00289 {    WWT_PUSHBTN, RESIZE_NONE,  COLOUR_GREY,   165,   180,    85,   100, 0x0,    STR_NULL},
00290 {    WWT_PUSHBTN, RESIZE_NONE,  COLOUR_GREY,   183,   198,    85,   100, 0x0,    STR_NULL},
00291 {    WWT_PUSHBTN, RESIZE_NONE,  COLOUR_GREY,   201,   216,    85,   100, 0x0,    STR_NULL},
00292 {    WWT_PUSHBTN, RESIZE_NONE,  COLOUR_GREY,   219,   234,    85,   100, 0x0,    STR_NULL},
00293 {    WWT_PUSHBTN, RESIZE_NONE,  COLOUR_GREY,   237,   252,    85,   100, 0x0,    STR_NULL},
00294 
00295 {    WWT_PUSHBTN, RESIZE_NONE,  COLOUR_GREY,    30,    45,   103,   118, 0x0,    STR_NULL},
00296 {    WWT_PUSHBTN, RESIZE_NONE,  COLOUR_GREY,    48,    63,   103,   118, 0x0,    STR_NULL},
00297 {    WWT_PUSHBTN, RESIZE_NONE,  COLOUR_GREY,    66,    81,   103,   118, 0x0,    STR_NULL},
00298 {    WWT_PUSHBTN, RESIZE_NONE,  COLOUR_GREY,    84,    99,   103,   118, 0x0,    STR_NULL},
00299 {    WWT_PUSHBTN, RESIZE_NONE,  COLOUR_GREY,   102,   117,   103,   118, 0x0,    STR_NULL},
00300 {    WWT_PUSHBTN, RESIZE_NONE,  COLOUR_GREY,   120,   135,   103,   118, 0x0,    STR_NULL},
00301 {    WWT_PUSHBTN, RESIZE_NONE,  COLOUR_GREY,   138,   153,   103,   118, 0x0,    STR_NULL},
00302 {    WWT_PUSHBTN, RESIZE_NONE,  COLOUR_GREY,   156,   171,   103,   118, 0x0,    STR_NULL},
00303 {    WWT_PUSHBTN, RESIZE_NONE,  COLOUR_GREY,   174,   189,   103,   118, 0x0,    STR_NULL},
00304 {    WWT_PUSHBTN, RESIZE_NONE,  COLOUR_GREY,   192,   207,   103,   118, 0x0,    STR_NULL},
00305 {    WWT_PUSHBTN, RESIZE_NONE,  COLOUR_GREY,   210,   225,   103,   118, 0x0,    STR_NULL},
00306 {    WWT_PUSHBTN, RESIZE_NONE,  COLOUR_GREY,   228,   243,   103,   118, 0x0,    STR_NULL},
00307 
00308 {   WIDGETS_END},
00309 };
00310 
00311 static const WindowDesc _osk_desc(
00312   WDP_CENTER, WDP_CENTER, 256, 140, 256, 140,
00313   WC_OSK, WC_NONE,
00314   WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
00315   _osk_widgets
00316 );
00317 
00322 void GetKeyboardLayout()
00323 {
00324   char keyboard[2][OSK_KEYBOARD_ENTRIES * 4 + 1];
00325   char errormark[2][OSK_KEYBOARD_ENTRIES + 1]; // used for marking invalid chars
00326   bool has_error = false; // true when an invalid char is detected
00327 
00328   if (StrEmpty(_keyboard_opt[0])) {
00329     GetString(keyboard[0], STR_OSK_KEYBOARD_LAYOUT, lastof(keyboard[0]));
00330   } else {
00331     strecpy(keyboard[0], _keyboard_opt[0], lastof(keyboard[0]));
00332   }
00333 
00334   if (StrEmpty(_keyboard_opt[1])) {
00335     GetString(keyboard[1], STR_OSK_KEYBOARD_LAYOUT_CAPS, lastof(keyboard[1]));
00336   } else {
00337     strecpy(keyboard[1], _keyboard_opt[1], lastof(keyboard[1]));
00338   }
00339 
00340   for (uint j = 0; j < 2; j++) {
00341     const char *kbd = keyboard[j];
00342     bool ended = false;
00343     for (uint i = 0; i < OSK_KEYBOARD_ENTRIES; i++) {
00344       _keyboard[j][i] = Utf8Consume(&kbd);
00345 
00346       /* Be lenient when the last characters are missing (is quite normal) */
00347       if (_keyboard[j][i] == '\0' || ended) {
00348         ended = true;
00349         _keyboard[j][i] = ' ';
00350         continue;
00351       }
00352 
00353       if (IsPrintable(_keyboard[j][i])) {
00354         errormark[j][i] = ' ';
00355       } else {
00356         has_error = true;
00357         errormark[j][i] = '^';
00358         _keyboard[j][i] = ' ';
00359       }
00360     }
00361   }
00362 
00363   if (has_error) {
00364     ShowInfoF("The keyboard layout you selected contains invalid chars. Please check those chars marked with ^.");
00365     ShowInfoF("Normal keyboard:  %s", keyboard[0]);
00366     ShowInfoF("                  %s", errormark[0]);
00367     ShowInfoF("Caps Lock:        %s", keyboard[1]);
00368     ShowInfoF("                  %s", errormark[1]);
00369   }
00370 }
00371 
00383 void ShowOnScreenKeyboard(QueryStringBaseWindow *parent, int button, int cancel, int ok)
00384 {
00385   DeleteWindowById(WC_OSK, 0);
00386 
00387   GetKeyboardLayout();
00388   new OskWindow(&_osk_desc, parent, button, cancel, ok);
00389 }

Generated on Wed Dec 23 20:12:50 2009 for OpenTTD by  doxygen 1.5.6