source: trunk/src/helpers/cctl_combo.c@ 290

Last change on this file since 290 was 232, checked in by umoeller, 23 years ago

New toolbar control.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 23.1 KB
Line 
1
2/*
3 *@@sourcefile cctl_combo.c:
4 * super combo box control, which is, essentially, a
5 * subclassed entry field with a list box attached to
6 * it.
7 *
8 * Compared to the standard PM combo box, this one has a
9 * number of advantages:
10 *
11 * -- It doesn't require the parent window to have the
12 * WS_CLIPCHILDREN style bit cleared.
13 *
14 * -- It has proper window positioning; the size of the
15 * enhanced combo is the size of the entry field, while
16 * the PM combo wants to have the size of the expanded
17 * list box too, which isn't easy to handle.
18 *
19 * See ctlComboFromEntryField for details.
20 *
21 * Note: Version numbering in this file relates to XWorkplace version
22 * numbering.
23 *
24 *@@header "helpers\comctl.h"
25 *@@added V0.9.16 (2002-01-01) [umoeller]
26 */
27
28/*
29 * Copyright (C) 2001-2002 Ulrich M”ller.
30 * This file is part of the "XWorkplace helpers" source package.
31 * This is free software; you can redistribute it and/or modify
32 * it under the terms of the GNU General Public License as published
33 * by the Free Software Foundation, in version 2 as it comes in the
34 * "COPYING" file of the XWorkplace main distribution.
35 * This program is distributed in the hope that it will be useful,
36 * but WITHOUT ANY WARRANTY; without even the implied warranty of
37 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
38 * GNU General Public License for more details.
39 */
40
41#define OS2EMX_PLAIN_CHAR
42 // this is needed for "os2emx.h"; if this is defined,
43 // emx will define PSZ as _signed_ char, otherwise
44 // as unsigned char
45
46#define INCL_DOSEXCEPTIONS
47#define INCL_DOSPROCESS
48#define INCL_DOSSEMAPHORES
49#define INCL_DOSERRORS
50
51#define INCL_WINWINDOWMGR
52#define INCL_WINFRAMEMGR
53#define INCL_WININPUT
54#define INCL_WINPOINTERS
55
56#define INCL_WINBUTTONS
57#define INCL_WINENTRYFIELDS
58#define INCL_WINLISTBOXES
59
60#define INCL_GPILOGCOLORTABLE
61#define INCL_GPIBITMAPS
62#include <os2.h>
63
64#include <stdlib.h>
65#include <stdio.h>
66#include <string.h>
67#include <setjmp.h> // needed for except.h
68#include <assert.h> // needed for except.h
69
70#include "setup.h" // code generation and debugging options
71
72#include "helpers\except.h" // exception handling
73#include "helpers\winh.h"
74
75#include "helpers\comctl.h"
76
77#pragma hdrstop
78
79/*
80 *@@category: Helpers\PM helpers\Window classes\Super combo box
81 * See cctl_combo.c.
82 */
83
84/* ******************************************************************
85 *
86 * Super Combination Box control
87 *
88 ********************************************************************/
89
90#define COMBO_BUTTON_WIDTH 20
91
92#define ID_COMBO_BUTTON 1001
93#define ID_COMBO_LISTBOX 1002
94
95/*
96 *@@ COMBODATA:
97 *
98 *@@added V0.9.9 (2001-03-17) [umoeller]
99 */
100
101typedef struct _COMBODATA
102{
103 PFNWP pfnwpOrigEntryField,
104 pfnwpOrigButton;
105 ULONG flStyle;
106
107 // position of entire combo
108 LONG x,
109 y,
110 cx,
111 cy;
112
113 HWND hwndButton,
114 hwndListbox;
115
116 HBITMAP hbmButton;
117 SIZEL szlButton; // bitmap dimensions
118
119} COMBODATA, *PCOMBODATA;
120
121/*
122 *@@ PaintButtonBitmap:
123 *
124 *@@added V0.9.9 (2001-03-17) [umoeller]
125 */
126
127STATIC VOID PaintButtonBitmap(HWND hwnd,
128 PCOMBODATA pcd)
129{
130 HPS hps;
131 RECTL rcl;
132 POINTL ptlDest;
133
134 hps = WinGetPS(hwnd);
135 WinQueryWindowRect(hwnd, &rcl);
136
137 ptlDest.x = (rcl.xRight - pcd->szlButton.cx) / 2;
138 ptlDest.y = (rcl.yTop - pcd->szlButton.cy) / 2;
139 WinDrawBitmap(hps,
140 pcd->hbmButton,
141 NULL,
142 &ptlDest,
143 0, 0,
144 DBM_NORMAL);
145
146 WinReleasePS(hps);
147}
148
149/*
150 *@@ fnwpSubclassedComboButton:
151 * window proc the combobox's button is subclassed with.
152 * This is only for WM_PAINT because BN_PAINT is really
153 * not that great for painting a button that looks like
154 * a standard button.
155 *
156 *@@added V0.9.9 (2001-03-17) [umoeller]
157 */
158
159STATIC MRESULT EXPENTRY fnwpSubclassedComboButton(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
160{
161 MRESULT mrc = 0;
162 PCOMBODATA pcd;
163
164 switch (msg)
165 {
166 case WM_PAINT:
167 if (pcd = (PCOMBODATA)WinQueryWindowPtr(hwnd, QWL_USER))
168 {
169 mrc = pcd->pfnwpOrigButton(hwnd, msg, mp1, mp2);
170
171 PaintButtonBitmap(hwnd, pcd);
172 }
173 break;
174
175 /*
176 * default:
177 *
178 */
179
180 default:
181 if (pcd = (PCOMBODATA)WinQueryWindowPtr(hwnd, QWL_USER))
182 mrc = pcd->pfnwpOrigButton(hwnd, msg, mp1, mp2);
183 break;
184 }
185
186 return mrc;
187}
188
189/*
190 *@@ ShowListbox:
191 *
192 *@@added V0.9.9 (2001-03-17) [umoeller]
193 */
194
195STATIC VOID ShowListbox(HWND hwnd, // in: subclassed entry field
196 PCOMBODATA pcd,
197 BOOL fShow) // in: TRUE == show, FALSE == hide
198{
199 BOOL fHilite = FALSE;
200
201 if (fShow)
202 {
203 // list box is invisible:
204 SWP swp;
205 POINTL ptl;
206 WinQueryWindowPos(hwnd, &swp);
207
208 _Pmpf(("showing lb"));
209
210 // convert to desktop
211 ptl.x = swp.x;
212 ptl.y = swp.y;
213 WinMapWindowPoints(WinQueryWindow(hwnd, QW_PARENT), // from
214 HWND_DESKTOP, // to
215 &ptl,
216 // SWP.y comes before SWP.x
217 1);
218
219 WinSetWindowPos(pcd->hwndListbox,
220 HWND_TOP,
221 ptl.x + COMBO_BUTTON_WIDTH,
222 ptl.y - 100,
223 swp.cx,
224 100,
225 SWP_MOVE | SWP_SIZE | SWP_ZORDER | SWP_NOREDRAW);
226 WinSetParent(pcd->hwndListbox,
227 HWND_DESKTOP,
228 TRUE); // redraw
229
230 // set focus to subclassed entry field in any case;
231 // we never let the listbox get the focus
232 WinSetFocus(HWND_DESKTOP, hwnd);
233
234 fHilite = TRUE;
235 }
236 else
237 {
238 // list box is showing:
239 HWND hwndFocus = WinQueryFocus(HWND_DESKTOP);
240 _Pmpf(("hiding listbox"));
241
242 WinSetParent(pcd->hwndListbox,
243 HWND_OBJECT,
244 TRUE); // redraw now
245 // give focus back to entry field
246 if (hwndFocus == pcd->hwndListbox)
247 WinSetFocus(HWND_DESKTOP, hwnd);
248 }
249
250 WinSendMsg(pcd->hwndButton,
251 BM_SETHILITE,
252 (MPARAM)fHilite,
253 0);
254 PaintButtonBitmap(pcd->hwndButton, pcd);
255}
256
257/*
258 *@@ fnwpComboSubclass:
259 *
260 *@@added V0.9.9 (2001-03-17) [umoeller]
261 */
262
263STATIC MRESULT EXPENTRY fnwpComboSubclass(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
264{
265 MRESULT mrc = 0;
266 PCOMBODATA pcd;
267
268 switch (msg)
269 {
270 /*
271 * WM_ADJUSTWINDOWPOS:
272 *
273 */
274
275 case WM_ADJUSTWINDOWPOS:
276 if (pcd = (PCOMBODATA)WinQueryWindowPtr(hwnd, QWL_USER))
277 {
278 PSWP pswp = (PSWP)mp1;
279
280 if (pswp->fl & SWP_SIZE)
281 // if we're being sized, make us smaller so that
282 // there's room for the button
283 pswp->cx -= COMBO_BUTTON_WIDTH;
284
285 mrc = pcd->pfnwpOrigEntryField(hwnd, msg, mp1, mp2);
286 }
287 break;
288
289 /*
290 * WM_WINDOWPOSCHANGED:
291 *
292 */
293
294 case WM_WINDOWPOSCHANGED:
295 if (pcd = (PCOMBODATA)WinQueryWindowPtr(hwnd, QWL_USER))
296 {
297 PSWP pswpNew = (PSWP)mp1;
298
299 if (pswpNew->fl & (SWP_SIZE | SWP_MOVE))
300 {
301 // moved or sized:
302 SWP swp;
303 WinQueryWindowPos(hwnd, &swp);
304 WinSetWindowPos(pcd->hwndButton,
305 0,
306 pswpNew->x + pswpNew->cx, // has already been truncated!
307 pswpNew->y,
308 COMBO_BUTTON_WIDTH,
309 pswpNew->cy,
310 SWP_MOVE | SWP_SIZE);
311 }
312
313 mrc = pcd->pfnwpOrigEntryField(hwnd, msg, mp1, mp2);
314 }
315 break;
316
317 /*
318 * WM_SETFOCUS:
319 * hide listbox if focus is going away from us
320 */
321
322 case WM_SETFOCUS:
323 if (pcd = (PCOMBODATA)WinQueryWindowPtr(hwnd, QWL_USER))
324 {
325 if (!mp2)
326 // we're losing focus:
327 // is listbox currently showing?
328 ShowListbox(hwnd,
329 pcd,
330 FALSE);
331
332 mrc = pcd->pfnwpOrigEntryField(hwnd, msg, mp1, mp2);
333 }
334 break;
335
336 /*
337 * WM_COMMAND:
338 * show/hide listbox if the button gets pressed.
339 */
340
341 case WM_COMMAND:
342 if (pcd = (PCOMBODATA)WinQueryWindowPtr(hwnd, QWL_USER))
343 {
344 if (SHORT1FROMMP(mp1) == ID_COMBO_BUTTON)
345 {
346 // button clicked:
347 ShowListbox(hwnd,
348 pcd,
349 // check state of list box
350 (WinQueryWindow(pcd->hwndListbox, QW_PARENT)
351 == WinQueryObjectWindow(HWND_DESKTOP)));
352
353 // do not call parent
354 break;
355
356 } // end if ((SHORT)mp1 == ID_COMBO_BUTTON)
357
358 mrc = pcd->pfnwpOrigEntryField(hwnd, msg, mp1, mp2);
359 }
360 break;
361
362 /*
363 * WM_CONTROL:
364 * handle notifications from listbox.
365 */
366
367 case WM_CONTROL:
368 if (pcd = (PCOMBODATA)WinQueryWindowPtr(hwnd, QWL_USER))
369 {
370 USHORT usid = SHORT1FROMMP(mp1),
371 uscode = SHORT2FROMMP(mp1);
372 if (usid == ID_COMBO_LISTBOX)
373 {
374 switch (uscode)
375 {
376 case LN_ENTER:
377 break;
378
379 case LN_SELECT:
380 {
381 SHORT sSelected = winhQueryLboxSelectedItem(pcd->hwndListbox,
382 LIT_FIRST);
383 PSZ psz = NULL;
384 if (sSelected != LIT_NONE)
385 {
386 psz = winhQueryLboxItemText(pcd->hwndListbox,
387 sSelected);
388 }
389 WinSetWindowText(hwnd, psz);
390 if (psz)
391 {
392 WinPostMsg(hwnd,
393 EM_SETSEL,
394 MPFROM2SHORT(0, strlen(psz)),
395 0);
396 free(psz);
397 }
398 break; }
399
400 case LN_SETFOCUS:
401 // when the list box gets the focus, always
402 // set focus to ourselves
403 WinSetFocus(HWND_DESKTOP, hwnd);
404 break;
405 }
406
407 // forward list box notifications to
408 // our own owner, but replace the id
409 // with the combo box id
410 ctlPostWmControl(hwnd,
411 uscode,
412 mp2);
413
414 // do not call parent
415 break;
416
417 } // end if (usid == ID_COMBO_LISTBOX)
418
419 mrc = pcd->pfnwpOrigEntryField(hwnd, msg, mp1, mp2);
420 }
421 break;
422
423 /*
424 * WM_CHAR:
425 *
426 */
427
428 case WM_CHAR:
429 if (pcd = (PCOMBODATA)WinQueryWindowPtr(hwnd, QWL_USER))
430 {
431 USHORT usFlags = SHORT1FROMMP(mp1);
432 // USHORT usch = SHORT1FROMMP(mp2);
433 USHORT usvk = SHORT2FROMMP(mp2);
434
435 if ((usFlags & KC_KEYUP) == 0)
436 {
437 if (usFlags & KC_VIRTUALKEY)
438 {
439 switch (usvk)
440 {
441 case VK_DOWN:
442 case VK_UP:
443 // if alt is pressed with these, show/hide listbox
444 if (usFlags & KC_ALT)
445 WinPostMsg(hwnd,
446 CBM_SHOWLIST,
447 (MPARAM)(WinQueryWindow(pcd->hwndListbox, QW_PARENT)
448 == WinQueryObjectWindow(HWND_DESKTOP)),
449 0);
450 else
451 {
452 // just up or down, no alt:
453 // select next or previous item in list box
454 SHORT sSelected = winhQueryLboxSelectedItem(pcd->hwndListbox,
455 LIT_FIRST),
456 sNew = 0;
457
458 if (usvk == VK_DOWN)
459 {
460 if (sSelected != LIT_NONE)
461 {
462 if (sSelected < WinQueryLboxCount(pcd->hwndListbox))
463 sNew = sSelected + 1;
464 }
465 // else: sNew still 0
466 }
467 else
468 {
469 // up:
470 if ( (sSelected != LIT_NONE)
471 && (sSelected > 0)
472 )
473 sNew = sSelected - 1;
474 }
475
476 winhSetLboxSelectedItem(pcd->hwndListbox,
477 sNew,
478 TRUE);
479 }
480 break;
481 }
482 }
483 }
484
485 // call parent only if this is not a drop-down list
486 if ((pcd->flStyle & CBS_DROPDOWNLIST) == 0)
487 mrc = pcd->pfnwpOrigEntryField(hwnd, msg, mp1, mp2);
488 else
489 // forward to owner
490 WinSendMsg(WinQueryWindow(hwnd, QW_OWNER),
491 msg,
492 mp1,
493 mp2);
494 }
495 break;
496
497 /*
498 * CBM_ISLISTSHOWING:
499 * implementation of the original combobox msg.
500 */
501
502 case CBM_ISLISTSHOWING:
503 if (pcd = (PCOMBODATA)WinQueryWindowPtr(hwnd, QWL_USER))
504 {
505 mrc = (MPARAM)(WinQueryWindow(pcd->hwndListbox, QW_PARENT)
506 == WinQueryObjectWindow(HWND_DESKTOP));
507 }
508 break;
509
510 /*
511 * CBM_SHOWLIST:
512 * implementation of the original combobox msg.
513 */
514
515 case CBM_SHOWLIST:
516 if (pcd = (PCOMBODATA)WinQueryWindowPtr(hwnd, QWL_USER))
517 {
518 ShowListbox(hwnd,
519 pcd,
520 (BOOL)mp1);
521 }
522 break;
523
524 /*
525 * list box messages:
526 * forward all these to the listbox and
527 * return the listbox return value.
528 */
529
530 case LM_INSERTITEM:
531 case LM_SETTOPINDEX:
532 case LM_QUERYTOPINDEX:
533 case LM_DELETEITEM:
534 case LM_SELECTITEM:
535 case LM_QUERYSELECTION:
536 case LM_SETITEMTEXT:
537 case LM_QUERYITEMTEXT:
538 case LM_SEARCHSTRING:
539 case LM_DELETEALL:
540 if (pcd = (PCOMBODATA)WinQueryWindowPtr(hwnd, QWL_USER))
541 mrc = WinSendMsg(pcd->hwndListbox, msg, mp1, mp2);
542 break;
543
544 /*
545 * WM_DESTROY:
546 *
547 */
548
549 case WM_DESTROY:
550 if (pcd = (PCOMBODATA)WinQueryWindowPtr(hwnd, QWL_USER))
551 {
552 WinDestroyWindow(pcd->hwndButton);
553 WinDestroyWindow(pcd->hwndListbox);
554
555 mrc = pcd->pfnwpOrigEntryField(hwnd, msg, mp1, mp2);
556
557 free(pcd);
558 }
559 break;
560
561 /*
562 * default:
563 *
564 */
565
566 default:
567 if (pcd = (PCOMBODATA)WinQueryWindowPtr(hwnd, QWL_USER))
568 mrc = pcd->pfnwpOrigEntryField(hwnd, msg, mp1, mp2);
569 break;
570 }
571
572 return mrc;
573}
574
575/*
576 *@@ ctlComboFromEntryField:
577 * turns a standard entry field control into an
578 * XComboBox.
579 *
580 * The XComboBox is intended to work like a standard
581 * combobox, but it doesn't have the silly limitation
582 * that the size of the combobox is assumed to be
583 * the size of the dropped-down combobox. This limitation
584 * makes it impossible to use standard comboboxes in
585 * windows which have the WS_CLIPCHILDREN style because
586 * the entire combo area will always be clipped out.
587 *
588 * This is not a full reimplementation. Only drop-down
589 * and drop-down list comboboxes are supported. Besides,
590 * the XComboBox is essentially a subclassed entryfield,
591 * so there might be limitations.
592 *
593 * On input to this function, with flStyle, specify
594 * either CBS_DROPDOWN or CBS_DROPDOWNLIST. CBS_SIMPLE
595 * is not supported.
596 *
597 * Supported messages to the XComboBox after this funcion
598 * has been called:
599 *
600 * -- CBM_ISLISTSHOWING
601 *
602 * -- CBM_SHOWLIST
603 *
604 * -- LM_QUERYITEMCOUNT
605 *
606 * -- LM_INSERTITEM
607 *
608 * -- LM_SETTOPINDEX
609 *
610 * -- LM_QUERYTOPINDEX
611 *
612 * -- LM_DELETEITEM
613 *
614 * -- LM_SELECTITEM
615 *
616 * -- LM_QUERYSELECTION
617 *
618 * -- LM_SETITEMTEXT
619 *
620 * -- LM_QUERYITEMTEXT
621 *
622 * -- LM_SEARCHSTRING
623 *
624 * -- LM_DELETEALL
625 *
626 * NOTE: This occupies QWL_USER of the entryfield.
627 *
628 *@@added V0.9.9 (2001-03-17) [umoeller]
629 */
630
631BOOL ctlComboFromEntryField(HWND hwnd, // in: entry field to be converted
632 ULONG flStyle) // in: combo box styles
633{
634 BOOL brc = FALSE;
635 PFNWP pfnwpOrig;
636 if (pfnwpOrig = WinSubclassWindow(hwnd,
637 fnwpComboSubclass))
638 {
639 PCOMBODATA pcd;
640 if (pcd = (PCOMBODATA)malloc(sizeof(*pcd)))
641 {
642 SWP swp;
643 BITMAPINFOHEADER2 bmih2;
644
645 memset(pcd, 0, sizeof(*pcd));
646 pcd->pfnwpOrigEntryField = pfnwpOrig;
647 pcd->flStyle = flStyle;
648
649 WinSetWindowPtr(hwnd, QWL_USER, pcd);
650
651 WinQueryWindowPos(hwnd, &swp);
652 pcd->x = swp.x;
653 pcd->y = swp.y;
654 pcd->cx = swp.cx;
655 pcd->cy = swp.cy;
656
657 swp.cx -= COMBO_BUTTON_WIDTH;
658 WinSetWindowPos(hwnd,
659 0,
660 0, 0,
661 swp.cx, swp.cy,
662 SWP_SIZE | SWP_NOADJUST); // circumvent subclassing
663
664 pcd->hbmButton = WinGetSysBitmap(HWND_DESKTOP,
665 SBMP_COMBODOWN);
666 bmih2.cbFix = sizeof(bmih2);
667 GpiQueryBitmapInfoHeader(pcd->hbmButton,
668 &bmih2);
669 pcd->szlButton.cx = bmih2.cx;
670 pcd->szlButton.cy = bmih2.cy;
671
672 pcd->hwndButton = WinCreateWindow(WinQueryWindow(hwnd, QW_PARENT),
673 WC_BUTTON,
674 "",
675 WS_VISIBLE
676 | BS_PUSHBUTTON | BS_NOPOINTERFOCUS,
677 swp.x + swp.cx - COMBO_BUTTON_WIDTH,
678 swp.y,
679 COMBO_BUTTON_WIDTH,
680 swp.cy,
681 hwnd, // owner == entry field!
682 hwnd, // insert behind entry field
683 ID_COMBO_BUTTON,
684 NULL,
685 NULL);
686 WinSetWindowPtr(pcd->hwndButton, QWL_USER, pcd);
687 pcd->pfnwpOrigButton = WinSubclassWindow(pcd->hwndButton,
688 fnwpSubclassedComboButton);
689
690 pcd->hwndListbox = WinCreateWindow(HWND_OBJECT, // parent, for now
691 WC_LISTBOX,
692 "?",
693 WS_VISIBLE | WS_SAVEBITS | WS_CLIPSIBLINGS
694 | LS_NOADJUSTPOS,
695 0,
696 0,
697 0,
698 0,
699 hwnd, // owner == entry field!
700 HWND_TOP, // insert behind entry field
701 ID_COMBO_LISTBOX,
702 NULL,
703 NULL);
704
705 // finally, set style of entry field... we force
706 // these flags no matter what the original style
707 // was
708 /* WinSetWindowBits(hwnd,
709 QWL_STYLE,
710 // bits to set:
711 (flStyle & CBS_DROPDOWNLIST)
712 ? ES_READONLY
713 : 0,
714 // mask:
715 ES_READONLY); */
716 }
717 }
718
719 return brc;
720}
721
722
Note: See TracBrowser for help on using the repository browser.