1 /*
2 * Copyright (C) 2007-2010 Geometer Plus <contact@geometerplus.com>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 * 02110-1301, USA.
18 */
19
20 #include <algorithm>
21
22 #include <windows.h>
23 #include <commctrl.h>
24
25 #include "../../../../core/src/util/ZLKeyUtil.h"
26
27 #include "W32Control.h"
28 #include "W32ControlCollection.h"
29 #include "../../../../core/src/win32/util/W32WCHARUtil.h"
30
31 #undef max
32 #undef min
33
34 static const WORD CLASS_BUTTON = 0x0080;
35 static const WORD CLASS_EDIT = 0x0081;
36 static const WORD CLASS_STATIC = 0x0082;
37 static const WORD CLASS_LISTBOX = 0x0083;
38 static const WORD CLASS_SCROLLBAR = 0x0084;
39 static const WORD CLASS_COMBOBOX = 0x0085;
40
41 static const WCHAR CLASSNAME_SPINNER[] = UPDOWN_CLASSW;
42
W32Control(DWORD style)43 W32Control::W32Control(DWORD style) : myStyle(style | WS_CHILD), myX(1), myY(1), mySize(Size(1, 1)), myEnabled(true), myOwner(0), myWindow(0) {
44 }
45
setEnabled(bool enabled)46 void W32Control::setEnabled(bool enabled) {
47 if (myEnabled != enabled) {
48 myEnabled = enabled;
49 if (myWindow != 0) {
50 EnableWindow(myWindow, myEnabled);
51 }
52 }
53 }
54
isEnabled() const55 bool W32Control::isEnabled() const {
56 return myEnabled;
57 }
58
setVisible(bool visible)59 void W32Control::setVisible(bool visible) {
60 if (visible != isVisible()) {
61 if (visible) {
62 myStyle |= WS_VISIBLE;
63 } else {
64 myStyle &= ~WS_VISIBLE;
65 }
66 if (myWindow != 0) {
67 ShowWindow(myWindow, visible ? SW_SHOW : SW_HIDE);
68 }
69 if (myOwner != 0) {
70 myOwner->invalidate();
71 }
72 }
73 }
74
isVisible() const75 bool W32Control::isVisible() const {
76 return (myStyle & WS_VISIBLE) == WS_VISIBLE;
77 }
78
init(HWND parent,W32ControlCollection * collection)79 void W32Control::init(HWND parent, W32ControlCollection *collection) {
80 myOwner = collection;
81 myWindow = GetDlgItem(parent, collection->addControl(this));
82 if (!myEnabled) {
83 EnableWindow(myWindow, false);
84 }
85 }
86
W32StandardControl(DWORD style)87 W32StandardControl::W32StandardControl(DWORD style) : W32Control(style) {
88 }
89
controlNumber() const90 int W32StandardControl::controlNumber() const {
91 return 1;
92 }
93
allocationSize() const94 int W32StandardControl::allocationSize() const {
95 return 14;
96 }
97
allocate(WORD * & p,short & id) const98 void W32StandardControl::allocate(WORD *&p, short &id) const {
99 *p++ = LOWORD(myStyle);
100 *p++ = HIWORD(myStyle);
101 *p++ = 0;
102 *p++ = 0;
103 *p++ = myX;
104 *p++ = myY;
105 /*
106 if ((mySize.Width == 0) || (mySize.Height == 0)) {
107 mySize = minimumSize();
108 }
109 */
110 *p++ = mySize.Width;
111 *p++ = mySize.Height;
112 *p++ = id++;
113 *p++ = 0xFFFF;
114 *p++ = classId();
115 *p++ = 0;
116 *p++ = 0;
117 *p++ = 0;
118 }
119
setPosition(int x,int y,Size size)120 void W32Control::setPosition(int x, int y, Size size) {
121 bool doMove = (myX != x) || (myY != y);
122 bool doResize = mySize != size;
123 myX = x;
124 myY = y;
125 mySize = size;
126 if ((doResize || doMove) && (myWindow != 0)) {
127 RECT r;
128 r.left = x;
129 r.top = y;
130 r.right = x + size.Width;
131 r.bottom = y + size.Height;
132 MapDialogRect(GetParent(myWindow), &r);
133 DWORD flags = SWP_NOZORDER | SWP_NOOWNERZORDER;
134 if (!doResize) {
135 flags |= SWP_NOSIZE;
136 }
137 if (!doMove) {
138 flags |= SWP_NOMOVE;
139 }
140 SetWindowPos(myWindow, 0, r.left, r.top, r.right - r.left, r.bottom - r.top, flags);
141 }
142 }
143
commandCallback(DWORD)144 void W32Control::commandCallback(DWORD) {
145 }
146
notificationCallback(LPARAM)147 void W32Control::notificationCallback(LPARAM) {
148 }
149
drawItemCallback(DRAWITEMSTRUCT &)150 void W32Control::drawItemCallback(DRAWITEMSTRUCT&) {
151 }
152
153 const std::string W32PushButton::RELEASED_EVENT = "PushButton: released";
154
W32PushButton(const std::string & text,ButtonType type)155 W32PushButton::W32PushButton(const std::string &text, ButtonType type) : W32StandardControl(BS_PUSHBUTTON | WS_TABSTOP), myText(text), myType(type) {
156 //DWORD style = (it == myButtons.begin()) ? BS_DEFPUSHBUTTON : BS_PUSHBUTTON;
157 }
158
minimumSize() const159 W32Widget::Size W32PushButton::minimumSize() const {
160 return Size(4 * (ZLUnicodeUtil::utf8Length(myText) + 3), 15);
161 }
162
allocate(WORD * & p,short & id) const163 void W32PushButton::allocate(WORD *&p, short &id) const {
164 if (myType == NORMAL_BUTTON) {
165 W32StandardControl::allocate(p, id);
166 } else {
167 short specialId = (myType == OK_BUTTON) ? IDOK : IDCANCEL;
168 W32StandardControl::allocate(p, specialId);
169 }
170 }
171
classId() const172 WORD W32PushButton::classId() const {
173 return CLASS_BUTTON;
174 }
175
init(HWND parent,W32ControlCollection * collection)176 void W32PushButton::init(HWND parent, W32ControlCollection *collection) {
177 myOwner = collection;
178 short id = -1;
179 if (myType == OK_BUTTON) {
180 id = IDOK;
181 } else if (myType == CANCEL_BUTTON) {
182 id = IDCANCEL;
183 }
184 myWindow = GetDlgItem(parent, collection->addControl(this, id));
185 if (!myEnabled) {
186 EnableWindow(myWindow, false);
187 }
188 ::setWindowText(myWindow, myText);
189 }
190
commandCallback(DWORD)191 void W32PushButton::commandCallback(DWORD) {
192 fireEvent(RELEASED_EVENT);
193 }
194
W32Label(const std::string & text,Alignment alignment)195 W32Label::W32Label(const std::string &text, Alignment alignment) : W32StandardControl(alignment), myText(text) {
196 myStringCounter = 1;
197 myMaxLength = 0;
198 int index = 0;
199 int newIndex;
200 while ((newIndex = myText.find('\n', index)) != -1) {
201 ++myStringCounter;
202 myMaxLength = std::max(
203 myMaxLength,
204 (unsigned short)ZLUnicodeUtil::utf8Length(myText.data() + index, newIndex - index)
205 );
206 index = newIndex + 1;
207 }
208 myMaxLength = std::max(
209 myMaxLength,
210 (unsigned short)ZLUnicodeUtil::utf8Length(myText.data() + index, myText.length() - index)
211 );
212 }
213
minimumSize() const214 W32Widget::Size W32Label::minimumSize() const {
215 return Size(4 * myMaxLength, 12 * myStringCounter);
216 }
217
setPosition(int x,int y,Size size)218 void W32Label::setPosition(int x, int y, Size size) {
219 ::setWindowText(myWindow, "");
220 W32StandardControl::setPosition(x, y + 2, Size(size.Width, size.Height - 2));
221 ::setWindowText(myWindow, myText);
222 }
223
classId() const224 WORD W32Label::classId() const {
225 return CLASS_STATIC;
226 }
227
init(HWND parent,W32ControlCollection * collection)228 void W32Label::init(HWND parent, W32ControlCollection *collection) {
229 W32StandardControl::init(parent, collection);
230 ::setWindowText(myWindow, myText);
231 }
232
W32StandardIcon(IconId iconId)233 W32StandardIcon::W32StandardIcon(IconId iconId) : W32StandardControl(SS_CENTER | SS_ICON), myIconId(iconId) {
234 }
235
minimumSize() const236 W32Widget::Size W32StandardIcon::minimumSize() const {
237 // TODO: why multiplier 2?
238 return Size(SM_CXICON * 2, SM_CYICON * 2);
239 }
240
classId() const241 WORD W32StandardIcon::classId() const {
242 return CLASS_STATIC;
243 }
244
init(HWND parent,W32ControlCollection * collection)245 void W32StandardIcon::init(HWND parent, W32ControlCollection *collection) {
246 W32StandardControl::init(parent, collection);
247 WCHAR *icon;
248 switch (myIconId) {
249 case ID_INFORMATION:
250 icon = IDI_INFORMATION;
251 break;
252 case ID_QUESTION:
253 icon = IDI_QUESTION;
254 break;
255 case ID_ERROR:
256 icon = IDI_ERROR;
257 break;
258 default:
259 icon = IDI_APPLICATION;
260 break;
261 }
262 SendMessage(myWindow, STM_SETICON, (WPARAM)LoadIcon(0, icon), 0);
263 }
264
265 const std::string W32CheckBox::STATE_CHANGED_EVENT = "CheckBox: state changed";
266
W32CheckBox(const std::string & text)267 W32CheckBox::W32CheckBox(const std::string &text) : W32StandardControl(BS_AUTOCHECKBOX | WS_TABSTOP), myText(text), myChecked(false) {
268 }
269
minimumSize() const270 W32Widget::Size W32CheckBox::minimumSize() const {
271 return Size(4 * (ZLUnicodeUtil::utf8Length(myText) + 1), 12);
272 }
273
classId() const274 WORD W32CheckBox::classId() const {
275 return CLASS_BUTTON;
276 }
277
init(HWND parent,W32ControlCollection * collection)278 void W32CheckBox::init(HWND parent, W32ControlCollection *collection) {
279 W32StandardControl::init(parent, collection);
280 ::setWindowText(myWindow, myText);
281 SendMessage(myWindow, BM_SETCHECK, myChecked ? BST_CHECKED : BST_UNCHECKED, 0);
282 }
283
setEditable(bool editable)284 void W32CheckBox::setEditable(bool editable) {
285 if (editable) {
286 myStyle &= ~WS_DISABLED;
287 } else {
288 myStyle |= WS_DISABLED;
289 }
290 if (myWindow != 0) {
291 // TODO: check
292 SetWindowLong(myWindow, GWL_STYLE, myStyle);
293 }
294 }
295
setChecked(bool checked)296 void W32CheckBox::setChecked(bool checked) {
297 if (checked != myChecked) {
298 myChecked = checked;
299 if (myWindow != 0) {
300 SendMessage(myWindow, BM_SETCHECK, myChecked ? BST_CHECKED : BST_UNCHECKED, 0);
301 }
302 fireEvent(STATE_CHANGED_EVENT);
303 }
304 }
305
isChecked() const306 bool W32CheckBox::isChecked() const {
307 return myChecked;
308 }
309
commandCallback(DWORD)310 void W32CheckBox::commandCallback(DWORD) {
311 if (myChecked != (SendMessage(myWindow, BM_GETCHECK, 0, 0) == BST_CHECKED)) {
312 myChecked = !myChecked;
313 fireEvent(STATE_CHANGED_EVENT);
314 }
315 }
316
317 const std::string W32TristateBox::STATE_CHANGED_EVENT = "TristateBox: state changed";
318
buttonState(ZLBoolean3 state)319 int W32TristateBox::buttonState(ZLBoolean3 state) {
320 switch (state) {
321 case B3_TRUE:
322 return BST_CHECKED;
323 case B3_FALSE:
324 return BST_UNCHECKED;
325 default:
326 return BST_INDETERMINATE;
327 }
328 }
329
b3State(int state)330 ZLBoolean3 W32TristateBox::b3State(int state) {
331 switch (state) {
332 case BST_CHECKED:
333 return B3_TRUE;
334 case BST_UNCHECKED:
335 return B3_FALSE;
336 default:
337 return B3_UNDEFINED;
338 }
339 }
340
W32TristateBox(const std::string & text)341 W32TristateBox::W32TristateBox(const std::string &text) : W32StandardControl(BS_AUTO3STATE | WS_TABSTOP), myText(text), myState(B3_UNDEFINED) {
342 }
343
minimumSize() const344 W32Widget::Size W32TristateBox::minimumSize() const {
345 return Size(4 * (ZLUnicodeUtil::utf8Length(myText) + 1), 12);
346 }
347
classId() const348 WORD W32TristateBox::classId() const {
349 return CLASS_BUTTON;
350 }
351
init(HWND parent,W32ControlCollection * collection)352 void W32TristateBox::init(HWND parent, W32ControlCollection *collection) {
353 W32StandardControl::init(parent, collection);
354 ::setWindowText(myWindow, myText);
355 SendMessage(myWindow, BM_SETCHECK, buttonState(myState), 0);
356 }
357
setEditable(bool editable)358 void W32TristateBox::setEditable(bool editable) {
359 if (editable) {
360 myStyle &= ~WS_DISABLED;
361 } else {
362 myStyle |= WS_DISABLED;
363 }
364 if (myWindow != 0) {
365 // TODO: check
366 SetWindowLong(myWindow, GWL_STYLE, myStyle);
367 }
368 }
369
setState(ZLBoolean3 state)370 void W32TristateBox::setState(ZLBoolean3 state) {
371 if (state != myState) {
372 myState = state;
373 if (myWindow != 0) {
374 SendMessage(myWindow, BM_SETCHECK, buttonState(myState), 0);
375 }
376 fireEvent(STATE_CHANGED_EVENT);
377 }
378 }
379
state() const380 ZLBoolean3 W32TristateBox::state() const {
381 return myState;
382 }
383
commandCallback(DWORD)384 void W32TristateBox::commandCallback(DWORD) {
385 ZLBoolean3 state = b3State(SendMessage(myWindow, BM_GETCHECK, 0, 0));
386 if (state != myState) {
387 myState = state;
388 fireEvent(STATE_CHANGED_EVENT);
389 }
390 }
391
W32AbstractEditor(DWORD style)392 W32AbstractEditor::W32AbstractEditor(DWORD style) : W32StandardControl(style | WS_BORDER | WS_TABSTOP | ES_NOHIDESEL) {
393 }
394
classId() const395 WORD W32AbstractEditor::classId() const {
396 return CLASS_EDIT;
397 }
398
init(HWND parent,W32ControlCollection * collection)399 void W32AbstractEditor::init(HWND parent, W32ControlCollection *collection) {
400 W32StandardControl::init(parent, collection);
401 }
402
getTextFromBuffer(const ZLUnicodeUtil::Ucs2String & buffer)403 static std::string getTextFromBuffer(const ZLUnicodeUtil::Ucs2String &buffer) {
404 ZLUnicodeUtil::Ucs2String copy = buffer;
405 copy.pop_back();
406 std::string txt;
407 ZLUnicodeUtil::ucs2ToUtf8(txt, copy);
408 return txt;
409 }
410
411 const std::string W32LineEditor::VALUE_EDITED_EVENT = "LineEditor: value edited";
412
W32LineEditor(const std::string & text,bool passwordMode)413 W32LineEditor::W32LineEditor(const std::string &text, bool passwordMode) : W32AbstractEditor(passwordMode ? (ES_AUTOHSCROLL | ES_PASSWORD) : ES_AUTOHSCROLL), myBlocked(true) {
414 ::createNTWCHARString(myBuffer, text);
415 }
416
commandCallback(DWORD hiWParam)417 void W32LineEditor::commandCallback(DWORD hiWParam) {
418 if ((hiWParam == EN_CHANGE) && !myBlocked) {
419 const int length = SendMessage(myWindow, EM_LINELENGTH, 0, 0);
420 myBuffer.clear();
421 myBuffer.insert(myBuffer.end(), length + 1, 0);
422 if (length > 0) {
423 myBuffer[0] = length + 1;
424 SendMessage(myWindow, EM_GETLINE, 0, (LPARAM)&myBuffer.front());
425 }
426 fireEvent(VALUE_EDITED_EVENT);
427 }
428 }
429
setText(const std::string & text)430 void W32LineEditor::setText(const std::string &text) {
431 ::createNTWCHARString(myBuffer, text);
432 if (myWindow != 0) {
433 SetWindowTextW(myWindow, ::wchar(myBuffer));
434 }
435 }
436
text() const437 std::string W32LineEditor::text() const {
438 return getTextFromBuffer(myBuffer);
439 }
440
minimumSize() const441 W32Widget::Size W32LineEditor::minimumSize() const {
442 return Size(4 * std::max(std::min((int)myBuffer.size() + 3, 25), 10), 12);
443 }
444
init(HWND parent,W32ControlCollection * collection)445 void W32LineEditor::init(HWND parent, W32ControlCollection *collection) {
446 W32AbstractEditor::init(parent, collection);
447 SetWindowTextW(myWindow, ::wchar(myBuffer));
448 myBlocked = false;
449 }
450
setEditable(bool editable)451 void W32LineEditor::setEditable(bool editable) {
452 if (editable) {
453 myStyle &= ~ES_READONLY;
454 } else {
455 myStyle |= ES_READONLY;
456 }
457 if (myWindow != 0) {
458 // TODO: check
459 //PostMessage(myWindow, EM_SETREADONLY, !editable, 0);
460 SendMessage(myWindow, EM_SETREADONLY, (editable ? FALSE : TRUE), 0);
461 }
462 }
463
464 std::map<HWND,W32KeyNameEditor*> W32KeyNameEditor::ourEditors;
465 const std::string W32KeyNameEditor::TEXT_CHANGED_EVENT = "KeyNameEditor: text changed";
466
W32KeyNameEditor()467 W32KeyNameEditor::W32KeyNameEditor() : W32AbstractEditor(ES_AUTOHSCROLL), myKeyboardModifierMask(0) {
468 ::createNTWCHARString(myBuffer, "");
469 }
470
~W32KeyNameEditor()471 W32KeyNameEditor::~W32KeyNameEditor() {
472 if (myWindow != 0) {
473 ourEditors.erase(myWindow);
474 }
475 }
476
clear()477 void W32KeyNameEditor::clear() {
478 setText(std::string());
479 }
480
setText(const std::string & text)481 void W32KeyNameEditor::setText(const std::string &text) {
482 if (text != this->text()) {
483 ::createNTWCHARString(myBuffer, text);
484 if (myWindow != 0) {
485 SetWindowTextW(myWindow, ::wchar(myBuffer));
486 }
487 fireEvent(TEXT_CHANGED_EVENT);
488 }
489 }
490
text() const491 std::string W32KeyNameEditor::text() const {
492 return getTextFromBuffer(myBuffer);
493 }
494
minimumSize() const495 W32Widget::Size W32KeyNameEditor::minimumSize() const {
496 return Size(40, 12);
497 }
498
Callback(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)499 LRESULT CALLBACK W32KeyNameEditor::Callback(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
500 W32KeyNameEditor *editor = ourEditors[hWnd];
501 if (editor == 0) {
502 return 0;
503 }
504 int &mask = editor->myKeyboardModifierMask;
505 switch (uMsg) {
506 case WM_GETDLGCODE:
507 return DLGC_WANTALLKEYS;
508 case WM_KEYDOWN:
509 case WM_SYSKEYDOWN:
510 if (wParam == 0x10) {
511 mask |= 0x1;
512 } else if (wParam == 0x11) {
513 mask |= 0x2;
514 } else if (wParam == 0x12) {
515 mask |= 0x4;
516 } else {
517 editor->setText(ZLKeyUtil::keyName(wParam, wParam, mask));
518 }
519 return 0;
520 case WM_KEYUP:
521 case WM_SYSKEYUP:
522 if (wParam == 0x10) {
523 mask &= ~0x1;
524 } else if (wParam == 0x11) {
525 mask &= ~0x2;
526 } else if (wParam == 0x12) {
527 mask &= ~0x4;
528 }
529 return 0;
530 case WM_CHAR:
531 return 0;
532 case WM_SETFOCUS:
533 editor->setText("");
534 mask = 0;
535 default:
536 return editor->myOriginalWndProc(hWnd, uMsg, wParam, lParam);
537 }
538 }
539
init(HWND parent,W32ControlCollection * collection)540 void W32KeyNameEditor::init(HWND parent, W32ControlCollection *collection) {
541 W32AbstractEditor::init(parent, collection);
542 myOriginalWndProc = (WndProc)SetWindowLong(myWindow, GWL_WNDPROC, (LONG)Callback);
543 ourEditors[myWindow] = this;
544 }
545
setEditable(bool editable)546 void W32KeyNameEditor::setEditable(bool editable) {
547 if (editable) {
548 myStyle &= ~ES_READONLY;
549 } else {
550 myStyle |= ES_READONLY;
551 }
552 if (myWindow != 0) {
553 // TODO: check
554 //PostMessage(myWindow, EM_SETREADONLY, !editable, 0);
555 SendMessage(myWindow, EM_SETREADONLY, (editable ? FALSE : TRUE), 0);
556 }
557 }
558
W32SpinBox(WORD min,WORD max,WORD initial)559 W32SpinBox::W32SpinBox(WORD min, WORD max, WORD initial) : W32AbstractEditor(ES_NUMBER), myMin(min), myMax(max), myValue(initial), myControlWindow(0) {
560 }
561
minimumSize() const562 W32Widget::Size W32SpinBox::minimumSize() const {
563 return Size(36, 12);
564 }
565
allocate(WORD * & p,short & id) const566 void W32SpinBox::allocate(WORD *&p, short &id) const {
567 W32AbstractEditor::allocate(p, id);
568
569 WORD *start = p;
570
571 DWORD style = UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_ARROWKEYS;
572 if (isVisible()) {
573 style |= WS_VISIBLE;
574 }
575 *p++ = LOWORD(style);
576 *p++ = HIWORD(style);
577 *p++ = 0;
578 *p++ = 0;
579 *p++ = myX + mySize.Width;
580 *p++ = myY;
581 *p++ = mySize.Width;
582 *p++ = mySize.Height;
583 *p++ = id++;
584
585 static const int classNameLength = lstrlenW(CLASSNAME_SPINNER);
586 memcpy(p, CLASSNAME_SPINNER, classNameLength * 2);
587 p += classNameLength;
588 *p++ = 0;
589 *p++ = 0;
590 *p++ = 0;
591 if ((p - start) % 2 == 1) {
592 p++;
593 }
594 }
595
setPosition(int x,int y,Size size)596 void W32SpinBox::setPosition(int x, int y, Size size) {
597 W32StandardControl::setPosition(x, y, size);
598 if (myControlWindow != 0) {
599 RECT r;
600 r.left = x + size.Width;
601 r.top = y;
602 r.right = x + 2 * size.Width;
603 r.bottom = y + size.Height;
604 MapDialogRect(GetParent(myControlWindow), &r);
605 SetWindowPos(myControlWindow, 0, r.left, r.top, r.right - r.left, r.bottom - r.top, 0);
606 SendMessage(myControlWindow, UDM_SETBUDDY, (WPARAM)myWindow, 0);
607 if (isVisible()) {
608 ShowWindow(myControlWindow, SW_SHOW);
609 }
610 }
611 }
612
init(HWND parent,W32ControlCollection * collection)613 void W32SpinBox::init(HWND parent, W32ControlCollection *collection) {
614 W32AbstractEditor::init(parent, collection);
615 myControlWindow = GetDlgItem(parent, collection->addControl(this));
616 SendMessage(myControlWindow, UDM_SETBUDDY, (WPARAM)myWindow, 0);
617 SendMessage(myControlWindow, UDM_SETRANGE, 0, MAKELONG(myMax, myMin));
618 SendMessage(myControlWindow, UDM_SETPOS, 0, MAKELONG(myValue, 0));
619 }
620
allocationSize() const621 int W32SpinBox::allocationSize() const {
622 static int const classNameLength = lstrlenW(CLASSNAME_SPINNER);
623 int size = W32AbstractEditor::allocationSize() + 12 + classNameLength;
624 return size + size % 2;
625 }
626
controlNumber() const627 int W32SpinBox::controlNumber() const {
628 return W32AbstractEditor::controlNumber() + 1;
629 }
630
setVisible(bool visible)631 void W32SpinBox::setVisible(bool visible) {
632 W32StandardControl::setVisible(visible);
633 if (myControlWindow != 0) {
634 ShowWindow(myControlWindow, visible ? SW_SHOW : SW_HIDE);
635 }
636 }
637
commandCallback(DWORD hiWParam)638 void W32SpinBox::commandCallback(DWORD hiWParam) {
639 if (hiWParam == EN_CHANGE) {
640 myValue = SendMessage(myControlWindow, UDM_GETPOS, 0, 0);
641 }
642 }
643
value() const644 unsigned short W32SpinBox::value() const {
645 return myValue;
646 }
647
648 const std::string W32ComboBox::SELECTION_CHANGED_EVENT = "ComboBox: selection changed";
649 const std::string W32ComboBox::VALUE_EDITED_EVENT = "ComboBox: value edited";
650
W32ComboBox(const std::vector<std::string> & list,int initialIndex)651 W32ComboBox::W32ComboBox(const std::vector<std::string> &list, int initialIndex) : W32StandardControl(CBS_DROPDOWNLIST | CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP), myList(list), myIndex(initialIndex) {
652 if ((initialIndex >= 0) && ((size_t)initialIndex < myList.size())) {
653 ::createNTWCHARString(myBuffer, list[initialIndex]);
654 }
655 }
656
minimumSize() const657 W32Widget::Size W32ComboBox::minimumSize() const {
658 int len = ((myStyle & CBS_SIMPLE) == CBS_SIMPLE) ? 0 : 10;
659 for (std::vector<std::string>::const_iterator it = myList.begin(); it != myList.end(); ++it) {
660 len = std::max(ZLUnicodeUtil::utf8Length(*it), len);
661 }
662 return Size(4 * std::min(len, 25), 12);
663 }
664
classId() const665 WORD W32ComboBox::classId() const {
666 return CLASS_COMBOBOX;
667 }
668
init(HWND parent,W32ControlCollection * collection)669 void W32ComboBox::init(HWND parent, W32ControlCollection *collection) {
670 W32StandardControl::init(parent, collection);
671 ZLUnicodeUtil::Ucs2String buffer;
672 for (std::vector<std::string>::const_iterator it = myList.begin(); it != myList.end(); ++it) {
673 SendMessage(myWindow, CB_ADDSTRING, 0, (LPARAM)::wchar(::createNTWCHARString(buffer, *it)));
674 }
675 if (myList.size() > 0) {
676 SendMessage(myWindow, CB_SETCURSEL, myIndex, 0);
677 SendMessage(myWindow, CB_SETMINVISIBLE, std::min((int)myList.size(), 7), 0);
678 }
679 }
680
setPosition(int x,int y,Size size)681 void W32ComboBox::setPosition(int x, int y, Size size) {
682 W32Control::setPosition(x, y, size);
683 if (myWindow != 0) {
684 SendMessage(myWindow, CB_SETMINVISIBLE, std::min((int)myList.size(), 7), 0);
685 }
686 }
687
setEditable(bool editable)688 void W32ComboBox::setEditable(bool editable) {
689 if (editable) {
690 myStyle &= ~CBS_SIMPLE;
691 } else {
692 myStyle |= CBS_SIMPLE;
693 }
694 if (myWindow != 0) {
695 // TODO: check
696 SetWindowLong(myWindow, GWL_STYLE, myStyle);
697 }
698 }
699
commandCallback(DWORD hiWParam)700 void W32ComboBox::commandCallback(DWORD hiWParam) {
701 if (hiWParam == CBN_SELCHANGE) {
702 myIndex = SendMessage(myWindow, CB_GETCURSEL, 0, 0);
703 const int length = SendMessage(myWindow, CB_GETLBTEXTLEN, myIndex, 0);
704 myBuffer.clear();
705 myBuffer.insert(myBuffer.end(), length + 1, 0);
706 if (length > 0) {
707 SendMessage(myWindow, CB_GETLBTEXT, myIndex, (LPARAM)&myBuffer.front());
708 }
709 fireEvent(SELECTION_CHANGED_EVENT);
710 } else if (hiWParam == CBN_EDITCHANGE) {
711 const int length = SendMessage(myWindow, WM_GETTEXTLENGTH, 0, 0);
712 myBuffer.clear();
713 myBuffer.insert(myBuffer.end(), length + 1, 0);
714 if (length > 0) {
715 SendMessage(myWindow, WM_GETTEXT, length + 1, (LPARAM)&myBuffer.front());
716 }
717 fireEvent(VALUE_EDITED_EVENT);
718 }
719 }
720
index() const721 int W32ComboBox::index() const {
722 return myIndex;
723 }
724
text() const725 std::string W32ComboBox::text() const {
726 return getTextFromBuffer(myBuffer);
727 }
728
setList(const std::vector<std::string> & list)729 void W32ComboBox::setList(const std::vector<std::string> &list) {
730 myList = list;
731 if (myWindow != 0) {
732 SendMessage(myWindow, CB_RESETCONTENT, 0, 0);
733 ZLUnicodeUtil::Ucs2String buffer;
734 for (std::vector<std::string>::const_iterator it = myList.begin(); it != myList.end(); ++it) {
735 SendMessage(myWindow, CB_ADDSTRING, 0, (LPARAM)::wchar(::createNTWCHARString(buffer, *it)));
736 }
737 if (myList.size() > 0) {
738 SendMessage(myWindow, CB_SETMINVISIBLE, std::min((int)myList.size(), 7), 0);
739 }
740 }
741 }
742
setSelection(const std::string & value)743 void W32ComboBox::setSelection(const std::string &value) {
744 std::vector<std::string>::const_iterator it = std::find(myList.begin(), myList.end(), value);
745 if (it != myList.end()) {
746 setSelection(it - myList.begin());
747 }
748 }
749
setSelection(int index)750 void W32ComboBox::setSelection(int index) {
751 if ((index >= 0) && (index < (short)myList.size())) {
752 myIndex = index;
753 myBuffer.clear();
754 ::createNTWCHARString(myBuffer, myList[index]);
755 if (myWindow != 0) {
756 SendMessage(myWindow, CB_SETCURSEL, myIndex, 0);
757 }
758 }
759 }
760
W32RadioButton(W32RadioButtonGroup & group,const std::string & text)761 W32RadioButton::W32RadioButton(W32RadioButtonGroup &group, const std::string &text) : W32StandardControl(BS_RADIOBUTTON | WS_TABSTOP), myGroup(group), myText(text) {
762 }
763
classId() const764 WORD W32RadioButton::classId() const {
765 return CLASS_BUTTON;
766 }
767
minimumSize() const768 W32Widget::Size W32RadioButton::minimumSize() const {
769 return Size(4 * (ZLUnicodeUtil::utf8Length(myText) + 2), 12);
770 }
771
init(HWND parent,W32ControlCollection * collection)772 void W32RadioButton::init(HWND parent, W32ControlCollection *collection) {
773 W32StandardControl::init(parent, collection);
774 ::setWindowText(myWindow, myText);
775 }
776
commandCallback(DWORD hiWParam)777 void W32RadioButton::commandCallback(DWORD hiWParam) {
778 if (hiWParam == BN_CLICKED) {
779 if (SendMessage(myWindow, BM_GETCHECK, 0, 0) != BST_CHECKED) {
780 myGroup.setChecked(*this);
781 }
782 }
783 }
784
setEditable(bool editable)785 void W32RadioButton::setEditable(bool editable) {
786 if (editable) {
787 myStyle &= ~WS_DISABLED;
788 } else {
789 myStyle |= WS_DISABLED;
790 }
791 if (myWindow != 0) {
792 // TODO: check
793 SetWindowLong(myWindow, GWL_STYLE, myStyle);
794 }
795 }
796
setChecked(bool checked)797 void W32RadioButton::setChecked(bool checked) {
798 if (myWindow != 0) {
799 SendMessage(myWindow, BM_SETCHECK, checked ? BST_CHECKED : BST_UNCHECKED, 0);
800 }
801 }
802
W32RadioButtonGroup(const std::string & caption,const std::vector<std::string> & buttonTexts)803 W32RadioButtonGroup::W32RadioButtonGroup(const std::string &caption, const std::vector<std::string> &buttonTexts) : W32StandardControl(BS_GROUPBOX), myCaption(caption), myCheckedIndex(-1) {
804 myButtons.reserve(buttonTexts.size());
805 for (std::vector<std::string>::const_iterator it = buttonTexts.begin(); it != buttonTexts.end(); ++it) {
806 myButtons.push_back(new W32RadioButton(*this, *it));
807 }
808 }
809
classId() const810 WORD W32RadioButtonGroup::classId() const {
811 return CLASS_BUTTON;
812 }
813
allocate(WORD * & p,short & id) const814 void W32RadioButtonGroup::allocate(WORD *&p, short &id) const {
815 W32StandardControl::allocate(p, id);
816 for (W32WidgetList::const_iterator it = myButtons.begin(); it != myButtons.end(); ++it) {
817 (*it)->allocate(p, id);
818 }
819 }
820
allocationSize() const821 int W32RadioButtonGroup::allocationSize() const {
822 int size = W32StandardControl::allocationSize();
823 for (W32WidgetList::const_iterator it = myButtons.begin(); it != myButtons.end(); ++it) {
824 size += (*it)->allocationSize();
825 }
826 return size;
827 }
828
setEditable(bool editable)829 void W32RadioButtonGroup::setEditable(bool editable) {
830 if (editable) {
831 myStyle &= ~WS_DISABLED;
832 } else {
833 myStyle |= WS_DISABLED;
834 }
835 if (myWindow != 0) {
836 // TODO: check
837 SetWindowLong(myWindow, GWL_STYLE, myStyle);
838 }
839 for (W32WidgetList::const_iterator it = myButtons.begin(); it != myButtons.end(); ++it) {
840 ((W32RadioButton&)**it).setEditable(editable);
841 }
842 }
843
setVisible(bool visible)844 void W32RadioButtonGroup::setVisible(bool visible) {
845 W32StandardControl::setVisible(visible);
846 for (W32WidgetList::const_iterator it = myButtons.begin(); it != myButtons.end(); ++it) {
847 (*it)->setVisible(visible);
848 }
849 }
850
controlNumber() const851 int W32RadioButtonGroup::controlNumber() const {
852 int counter = W32StandardControl::controlNumber();
853 for (W32WidgetList::const_iterator it = myButtons.begin(); it != myButtons.end(); ++it) {
854 counter += (*it)->controlNumber();
855 }
856 return counter;
857 }
858
setPosition(int x,int y,Size size)859 void W32RadioButtonGroup::setPosition(int x, int y, Size size) {
860 W32StandardControl::setPosition(x, y, size);
861 const short deltaY = size.Height / (myButtons.size() + 1);
862 size.Width -= 16;
863 size.Height = deltaY;
864 for (W32WidgetList::iterator it = myButtons.begin(); it != myButtons.end(); ++it) {
865 y += deltaY;
866 (*it)->setPosition(x + 8, y, size) ;
867 }
868 }
869
minimumSize() const870 W32Widget::Size W32RadioButtonGroup::minimumSize() const {
871 Size size;
872 for (W32WidgetList::const_iterator it = myButtons.begin(); it != myButtons.end(); ++it) {
873 size.Width = std::max(size.Width, (*it)->minimumSize().Width);
874 }
875 size.Width += 16;
876 size.Height = 12 * myButtons.size() + 16;
877 return size;
878 }
879
init(HWND parent,W32ControlCollection * collection)880 void W32RadioButtonGroup::init(HWND parent, W32ControlCollection *collection) {
881 W32StandardControl::init(parent, collection);
882 ::setWindowText(myWindow, myCaption);
883 for (W32WidgetList::const_iterator it = myButtons.begin(); it != myButtons.end(); ++it) {
884 (*it)->init(parent, collection);
885 ((W32RadioButton&)**it).setChecked(it == myButtons.begin() + myCheckedIndex);
886 }
887 }
888
setChecked(W32RadioButton & button)889 void W32RadioButtonGroup::setChecked(W32RadioButton &button) {
890 myCheckedIndex = -1;
891 for (W32WidgetList::const_iterator it = myButtons.begin(); it != myButtons.end(); ++it) {
892 W32RadioButton &rb = (W32RadioButton&)**it;
893 if (&rb == &button) {
894 rb.setChecked(true);
895 myCheckedIndex = it - myButtons.begin();
896 } else {
897 rb.setChecked(false);
898 }
899 }
900 }
901
setChecked(int index)902 void W32RadioButtonGroup::setChecked(int index) {
903 myCheckedIndex = index;
904 for (W32WidgetList::const_iterator it = myButtons.begin(); it != myButtons.end(); ++it) {
905 W32RadioButton &rb = (W32RadioButton&)**it;
906 rb.setChecked(it == myButtons.begin() + index);
907 }
908 }
909
checkedIndex() const910 int W32RadioButtonGroup::checkedIndex() const {
911 return myCheckedIndex;
912 }
913