48 KEYS_SHIFT, |
39 KEYS_SHIFT, |
49 KEYS_CAPS |
40 KEYS_CAPS |
50 }; |
41 }; |
51 static byte _keystate = KEYS_NONE; |
42 static byte _keystate = KEYS_NONE; |
52 |
43 |
53 /* |
44 struct OskWindow : public Window { |
54 * Only show valid characters; do not show characters that would |
45 QueryString *qs; ///< text-input |
55 * only insert a space when we have a spacebar to do that or |
46 int text_btn; ///< widget number of parent's text field |
56 * characters that are not allowed to be entered. |
47 int ok_btn; ///< widget number of parent's ok button (=0 when ok shouldn't be passed on) |
57 */ |
48 int cancel_btn; ///< widget number of parent's cancel button (=0 when cancel shouldn't be passed on; text will be reverted to original) |
58 static void ChangeOskDiabledState(Window *w, const querystr_d *qs, bool shift) |
49 Textbuf *text; ///< pointer to parent's textbuffer (to update caret position) |
59 { |
50 char orig_str_buf[64]; ///< Original string. |
60 for (uint i = 0; i < OSK_KEYBOARD_ENTRIES; i++) { |
51 |
61 w->SetWidgetDisabledState(OSK_WIDGET_LETTERS + i, |
52 OskWindow(const WindowDesc *desc, QueryStringBaseWindow *parent, int button, int cancel, int ok) : Window(desc) |
62 !IsValidChar(_keyboard[shift][i], qs->afilter) || _keyboard[shift][i] == ' '); |
53 { |
63 } |
54 this->parent = parent; |
64 w->SetWidgetDisabledState(OSK_WIDGET_SPACE, !IsValidChar(' ', qs->afilter)); |
55 assert(parent != NULL); |
65 } |
56 |
66 |
57 if (parent->widget[button].data != 0) parent->caption = parent->widget[button].data; |
67 /* on screen keyboard */ |
58 |
68 static void OskWndProc(Window *w, WindowEvent *e) |
59 this->qs = parent; |
69 { |
60 this->text_btn = button; |
70 querystr_d *qs = WP(w, osk_d).qs; |
61 this->cancel_btn = cancel; |
71 |
62 this->ok_btn = ok; |
72 switch (e->event) { |
63 this->text = &parent->text; |
73 case WE_CREATE: |
64 |
74 SetBit(_no_scroll, SCROLL_EDIT); |
65 /* make a copy in case we need to reset later */ |
75 /* Not needed by default. */ |
66 strcpy(this->orig_str_buf, this->qs->text.buf); |
76 w->DisableWidget(OSK_WIDGET_SPECIAL); |
67 |
77 break; |
68 SetBit(_no_scroll, SCROLL_EDIT); |
78 |
69 /* Not needed by default. */ |
79 case WE_PAINT: { |
70 this->DisableWidget(OSK_WIDGET_SPECIAL); |
|
71 |
|
72 this->FindWindowPlacementAndResize(desc); |
|
73 } |
|
74 |
|
75 /** |
|
76 * Only show valid characters; do not show characters that would |
|
77 * only insert a space when we have a spacebar to do that or |
|
78 * characters that are not allowed to be entered. |
|
79 */ |
|
80 void ChangeOskDiabledState(bool shift) |
|
81 { |
|
82 for (uint i = 0; i < OSK_KEYBOARD_ENTRIES; i++) { |
|
83 this->SetWidgetDisabledState(OSK_WIDGET_LETTERS + i, |
|
84 !IsValidChar(_keyboard[shift][i], this->qs->afilter) || _keyboard[shift][i] == ' '); |
|
85 } |
|
86 this->SetWidgetDisabledState(OSK_WIDGET_SPACE, !IsValidChar(' ', this->qs->afilter)); |
|
87 } |
|
88 |
|
89 virtual void OnPaint() |
|
90 { |
|
91 bool shift = HasBit(_keystate, KEYS_CAPS) ^ HasBit(_keystate, KEYS_SHIFT); |
|
92 |
|
93 this->LowerWidget(OSK_WIDGET_TEXT); |
|
94 this->SetWidgetLoweredState(OSK_WIDGET_SHIFT, HasBit(_keystate, KEYS_SHIFT)); |
|
95 this->SetWidgetLoweredState(OSK_WIDGET_CAPS, HasBit(_keystate, KEYS_CAPS)); |
|
96 |
|
97 this->ChangeOskDiabledState(shift); |
|
98 |
|
99 SetDParam(0, this->qs->caption); |
|
100 DrawWindowWidgets(this); |
|
101 |
|
102 for (uint i = 0; i < OSK_KEYBOARD_ENTRIES; i++) { |
|
103 DrawCharCentered(_keyboard[shift][i], |
|
104 this->widget[OSK_WIDGET_LETTERS + i].left + 8, |
|
105 this->widget[OSK_WIDGET_LETTERS + i].top + 3, |
|
106 TC_BLACK); |
|
107 } |
|
108 |
|
109 this->qs->DrawEditBox(this, OSK_WIDGET_TEXT); |
|
110 } |
|
111 |
|
112 virtual void OnClick(Point pt, int widget) |
|
113 { |
|
114 /* clicked a letter */ |
|
115 if (widget >= OSK_WIDGET_LETTERS) { |
80 bool shift = HasBit(_keystate, KEYS_CAPS) ^ HasBit(_keystate, KEYS_SHIFT); |
116 bool shift = HasBit(_keystate, KEYS_CAPS) ^ HasBit(_keystate, KEYS_SHIFT); |
81 |
117 |
82 w->LowerWidget(OSK_WIDGET_TEXT); |
118 WChar c = _keyboard[shift][widget - OSK_WIDGET_LETTERS]; |
83 w->SetWidgetLoweredState(OSK_WIDGET_SHIFT, HasBit(_keystate, KEYS_SHIFT)); |
119 |
84 w->SetWidgetLoweredState(OSK_WIDGET_CAPS, HasBit(_keystate, KEYS_CAPS)); |
120 if (!IsValidChar(c, this->qs->afilter)) return; |
85 |
121 |
86 ChangeOskDiabledState(w, qs, shift); |
122 if (InsertTextBufferChar(&this->qs->text, c)) this->InvalidateWidget(OSK_WIDGET_TEXT); |
87 |
123 |
88 SetDParam(0, qs->caption); |
124 if (HasBit(_keystate, KEYS_SHIFT)) { |
89 DrawWindowWidgets(w); |
125 ToggleBit(_keystate, KEYS_SHIFT); |
90 |
126 this->widget[OSK_WIDGET_SHIFT].color = HasBit(_keystate, KEYS_SHIFT) ? 15 : 14; |
91 for (uint i = 0; i < OSK_KEYBOARD_ENTRIES; i++) { |
127 this->SetDirty(); |
92 DrawCharCentered(_keyboard[shift][i], |
|
93 w->widget[OSK_WIDGET_LETTERS + i].left + 8, |
|
94 w->widget[OSK_WIDGET_LETTERS + i].top + 3, |
|
95 TC_BLACK); |
|
96 } |
128 } |
97 |
129 return; |
98 DrawEditBox(w, qs, OSK_WIDGET_TEXT); |
130 } |
99 break; |
131 |
100 } |
132 switch (widget) { |
101 |
133 case OSK_WIDGET_BACKSPACE: |
102 case WE_CLICK: |
134 if (DeleteTextBufferChar(&this->qs->text, WKC_BACKSPACE)) this->InvalidateWidget(OSK_WIDGET_TEXT); |
103 /* clicked a letter */ |
135 break; |
104 if (e->we.click.widget >= OSK_WIDGET_LETTERS) { |
136 |
105 bool shift = HasBit(_keystate, KEYS_CAPS) ^ HasBit(_keystate, KEYS_SHIFT); |
137 case OSK_WIDGET_SPECIAL: |
106 |
138 /* |
107 WChar c = _keyboard[shift][e->we.click.widget - OSK_WIDGET_LETTERS]; |
139 * Anything device specific can go here. |
108 |
140 * The button itself is hidden by default, and when you need it you |
109 if (!IsValidChar(c, qs->afilter)) break; |
141 * can not hide it in the create event. |
110 |
142 */ |
111 if (InsertTextBufferChar(&qs->text, c)) w->InvalidateWidget(OSK_WIDGET_TEXT); |
143 break; |
112 |
144 |
113 if (HasBit(_keystate, KEYS_SHIFT)) { |
145 case OSK_WIDGET_CAPS: |
114 ToggleBit(_keystate, KEYS_SHIFT); |
146 ToggleBit(_keystate, KEYS_CAPS); |
115 w->widget[OSK_WIDGET_SHIFT].color = HasBit(_keystate, KEYS_SHIFT) ? 15 : 14; |
147 this->SetDirty(); |
116 w->SetDirty(); |
148 break; |
|
149 |
|
150 case OSK_WIDGET_SHIFT: |
|
151 ToggleBit(_keystate, KEYS_SHIFT); |
|
152 this->SetDirty(); |
|
153 break; |
|
154 |
|
155 case OSK_WIDGET_SPACE: |
|
156 if (InsertTextBufferChar(&this->qs->text, ' ')) this->InvalidateWidget(OSK_WIDGET_TEXT); |
|
157 break; |
|
158 |
|
159 case OSK_WIDGET_LEFT: |
|
160 if (MoveTextBufferPos(&this->qs->text, WKC_LEFT)) this->InvalidateWidget(OSK_WIDGET_TEXT); |
|
161 break; |
|
162 |
|
163 case OSK_WIDGET_RIGHT: |
|
164 if (MoveTextBufferPos(&this->qs->text, WKC_RIGHT)) this->InvalidateWidget(OSK_WIDGET_TEXT); |
|
165 break; |
|
166 |
|
167 case OSK_WIDGET_OK: |
|
168 if (this->qs->orig == NULL || strcmp(this->qs->text.buf, this->qs->orig) != 0) { |
|
169 /* pass information by simulating a button press on parent window */ |
|
170 if (this->ok_btn != 0) { |
|
171 this->parent->OnClick(pt, this->ok_btn); |
|
172 } |
117 } |
173 } |
118 break; |
174 delete this; |
119 } |
175 break; |
120 |
176 |
121 switch (e->we.click.widget) { |
177 case OSK_WIDGET_CANCEL: |
122 case OSK_WIDGET_BACKSPACE: |
178 if (this->cancel_btn != 0) { // pass a cancel event to the parent window |
123 if (DeleteTextBufferChar(&qs->text, WKC_BACKSPACE)) w->InvalidateWidget(OSK_WIDGET_TEXT); |
179 this->parent->OnClick(pt, this->cancel_btn); |
124 break; |
180 /* Window gets deleted when the parent window removes itself. */ |
125 |
181 } else { // or reset to original string |
126 case OSK_WIDGET_SPECIAL: |
182 strcpy(qs->text.buf, this->orig_str_buf); |
127 /* |
183 UpdateTextBufferSize(&qs->text); |
128 * Anything device specific can go here. |
184 MoveTextBufferPos(&qs->text, WKC_END); |
129 * The button itself is hidden by default, and when you need it you |
185 delete this; |
130 * can not hide it in the create event. |
186 } |
131 */ |
187 break; |
132 break; |
188 } |
133 |
189 /* make sure that the parent window's textbox also gets updated */ |
134 case OSK_WIDGET_CAPS: |
190 if (this->parent != NULL) this->parent->InvalidateWidget(this->text_btn); |
135 ToggleBit(_keystate, KEYS_CAPS); |
191 } |
136 w->SetDirty(); |
192 |
137 break; |
193 virtual void OnMouseLoop() |
138 |
194 { |
139 case OSK_WIDGET_SHIFT: |
195 this->qs->HandleEditBox(this, OSK_WIDGET_TEXT); |
140 ToggleBit(_keystate, KEYS_SHIFT); |
196 /* make the caret of the parent window also blink */ |
141 w->SetDirty(); |
197 this->parent->InvalidateWidget(this->text_btn); |
142 break; |
198 } |
143 |
199 }; |
144 case OSK_WIDGET_SPACE: |
|
145 if (InsertTextBufferChar(&qs->text, ' ')) w->InvalidateWidget(OSK_WIDGET_TEXT); |
|
146 break; |
|
147 |
|
148 case OSK_WIDGET_LEFT: |
|
149 if (MoveTextBufferPos(&qs->text, WKC_LEFT)) w->InvalidateWidget(OSK_WIDGET_TEXT); |
|
150 break; |
|
151 |
|
152 case OSK_WIDGET_RIGHT: |
|
153 if (MoveTextBufferPos(&qs->text, WKC_RIGHT)) w->InvalidateWidget(OSK_WIDGET_TEXT); |
|
154 break; |
|
155 |
|
156 case OSK_WIDGET_OK: |
|
157 if (qs->orig == NULL || strcmp(qs->text.buf, qs->orig) != 0) { |
|
158 /* pass information by simulating a button press on parent window */ |
|
159 if (WP(w, osk_d).ok_btn != 0) { |
|
160 w->parent->OnClick(e->we.click.pt, WP(w, osk_d).ok_btn); |
|
161 } |
|
162 } |
|
163 delete w; |
|
164 break; |
|
165 |
|
166 case OSK_WIDGET_CANCEL: |
|
167 if (WP(w, osk_d).cancel_btn != 0) { // pass a cancel event to the parent window |
|
168 w->parent->OnClick(e->we.click.pt, WP(w, osk_d).cancel_btn); |
|
169 /* Window gets deleted when the parent window removes itself. */ |
|
170 } else { // or reset to original string |
|
171 strcpy(qs->text.buf, WP(w, osk_d).orig); |
|
172 UpdateTextBufferSize(&qs->text); |
|
173 MoveTextBufferPos(&qs->text, WKC_END); |
|
174 delete w; |
|
175 } |
|
176 break; |
|
177 } |
|
178 /* make sure that the parent window's textbox also gets updated */ |
|
179 if (w->parent != NULL) w->parent->InvalidateWidget(WP(w, osk_d).text_btn); |
|
180 break; |
|
181 |
|
182 case WE_MOUSELOOP: |
|
183 HandleEditBox(w, qs, OSK_WIDGET_TEXT); |
|
184 /* make the caret of the parent window also blink */ |
|
185 w->parent->InvalidateWidget(WP(w, osk_d).text_btn); |
|
186 break; |
|
187 } |
|
188 } |
|
189 |
200 |
190 static const Widget _osk_widgets[] = { |
201 static const Widget _osk_widgets[] = { |
191 { WWT_EMPTY, RESIZE_NONE, 0, 0, 0, 0, 0, 0x0, STR_NULL}, |
202 { WWT_EMPTY, RESIZE_NONE, 0, 0, 0, 0, 0, 0x0, STR_NULL}, |
192 { WWT_CAPTION, RESIZE_NONE, 14, 0, 255, 0, 13, STR_012D, STR_NULL}, |
203 { WWT_CAPTION, RESIZE_NONE, 14, 0, 255, 0, 13, STR_012D, STR_NULL}, |
193 { WWT_PANEL, RESIZE_NONE, 14, 0, 255, 14, 29, 0x0, STR_NULL}, |
204 { WWT_PANEL, RESIZE_NONE, 14, 0, 255, 14, 29, 0x0, STR_NULL}, |