source: branches/branch-1-0/src/helpers/cctl_combo.c

Last change on this file was 222, checked in by umoeller, 23 years ago

Minor adjustments for new static handling.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 23.2 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 WinPostMsg(WinQueryWindow(hwnd, QW_OWNER),
411 WM_CONTROL,
412 MPFROM2SHORT(WinQueryWindowUShort(hwnd, QWS_ID),
413 uscode),
414 mp2);
415
416 // do not call parent
417 break;
418
419 } // end if (usid == ID_COMBO_LISTBOX)
420
421 mrc = pcd->pfnwpOrigEntryField(hwnd, msg, mp1, mp2);
422 }
423 break;
424
425 /*
426 * WM_CHAR:
427 *
428 */
429
430 case WM_CHAR:
431 if (pcd = (PCOMBODATA)WinQueryWindowPtr(hwnd, QWL_USER))
432 {
433 USHORT usFlags = SHORT1FROMMP(mp1);
434 // USHORT usch = SHORT1FROMMP(mp2);
435 USHORT usvk = SHORT2FROMMP(mp2);
436
437 if ((usFlags & KC_KEYUP) == 0)
438 {
439 if (usFlags & KC_VIRTUALKEY)
440 {
441 switch (usvk)
442 {
443 case VK_DOWN:
444 case VK_UP:
445 // if alt is pressed with these, show/hide listbox
446 if (usFlags & KC_ALT)
447 WinPostMsg(hwnd,
448 CBM_SHOWLIST,
449 (MPARAM)(WinQueryWindow(pcd->hwndListbox, QW_PARENT)
450 == WinQueryObjectWindow(HWND_DESKTOP)),
451 0);
452 else
453 {
454 // just up or down, no alt:
455 // select next or previous item in list box
456 SHORT sSelected = winhQueryLboxSelectedItem(pcd->hwndListbox,
457 LIT_FIRST),
458 sNew = 0;
459
460 if (usvk == VK_DOWN)
461 {
462 if (sSelected != LIT_NONE)
463 {
464 if (sSelected < WinQueryLboxCount(pcd->hwndListbox))
465 sNew = sSelected + 1;
466 }
467 // else: sNew still 0
468 }
469 else
470 {
471 // up:
472 if ( (sSelected != LIT_NONE)
473 && (sSelected > 0)
474 )
475 sNew = sSelected - 1;
476 }
477
478 winhSetLboxSelectedItem(pcd->hwndListbox,
479 sNew,
480 TRUE);
481 }
482 break;
483 }
484 }
485 }
486
487 // call parent only if this is not a drop-down list
488 if ((pcd->flStyle & CBS_DROPDOWNLIST) == 0)
489 mrc = pcd->pfnwpOrigEntryField(hwnd, msg, mp1, mp2);
490 else
491 // forward to owner
492 WinSendMsg(WinQueryWindow(hwnd, QW_OWNER),
493 msg,
494 mp1,
495 mp2);
496 }
497 break;
498
499 /*
500 * CBM_ISLISTSHOWING:
501 * implementation of the original combobox msg.
502 */
503
504 case CBM_ISLISTSHOWING:
505 if (pcd = (PCOMBODATA)WinQueryWindowPtr(hwnd, QWL_USER))
506 {
507 mrc = (MPARAM)(WinQueryWindow(pcd->hwndListbox, QW_PARENT)
508 == WinQueryObjectWindow(HWND_DESKTOP));
509 }
510 break;
511
512 /*
513 * CBM_SHOWLIST:
514 * implementation of the original combobox msg.
515 */
516
517 case CBM_SHOWLIST:
518 if (pcd = (PCOMBODATA)WinQueryWindowPtr(hwnd, QWL_USER))
519 {
520 ShowListbox(hwnd,
521 pcd,
522 (BOOL)mp1);
523 }
524 break;
525
526 /*
527 * list box messages:
528 * forward all these to the listbox and
529 * return the listbox return value.
530 */
531
532 case LM_INSERTITEM:
533 case LM_SETTOPINDEX:
534 case LM_QUERYTOPINDEX:
535 case LM_DELETEITEM:
536 case LM_SELECTITEM:
537 case LM_QUERYSELECTION:
538 case LM_SETITEMTEXT:
539 case LM_QUERYITEMTEXT:
540 case LM_SEARCHSTRING:
541 case LM_DELETEALL:
542 if (pcd = (PCOMBODATA)WinQueryWindowPtr(hwnd, QWL_USER))
543 mrc = WinSendMsg(pcd->hwndListbox, msg, mp1, mp2);
544 break;
545
546 /*
547 * WM_DESTROY:
548 *
549 */
550
551 case WM_DESTROY:
552 if (pcd = (PCOMBODATA)WinQueryWindowPtr(hwnd, QWL_USER))
553 {
554 WinDestroyWindow(pcd->hwndButton);
555 WinDestroyWindow(pcd->hwndListbox);
556
557 mrc = pcd->pfnwpOrigEntryField(hwnd, msg, mp1, mp2);
558
559 free(pcd);
560 }
561 break;
562
563 /*
564 * default:
565 *
566 */
567
568 default:
569 if (pcd = (PCOMBODATA)WinQueryWindowPtr(hwnd, QWL_USER))
570 mrc = pcd->pfnwpOrigEntryField(hwnd, msg, mp1, mp2);
571 break;
572 }
573
574 return mrc;
575}
576
577/*
578 *@@ ctlComboFromEntryField:
579 * turns a standard entry field control into an
580 * XComboBox.
581 *
582 * The XComboBox is intended to work like a standard
583 * combobox, but it doesn't have the silly limitation
584 * that the size of the combobox is assumed to be
585 * the size of the dropped-down combobox. This limitation
586 * makes it impossible to use standard comboboxes in
587 * windows which have the WS_CLIPCHILDREN style because
588 * the entire combo area will always be clipped out.
589 *
590 * This is not a full reimplementation. Only drop-down
591 * and drop-down list comboboxes are supported. Besides,
592 * the XComboBox is essentially a subclassed entryfield,
593 * so there might be limitations.
594 *
595 * On input to this function, with flStyle, specify
596 * either CBS_DROPDOWN or CBS_DROPDOWNLIST. CBS_SIMPLE
597 * is not supported.
598 *
599 * Supported messages to the XComboBox after this funcion
600 * has been called:
601 *
602 * -- CBM_ISLISTSHOWING
603 *
604 * -- CBM_SHOWLIST
605 *
606 * -- LM_QUERYITEMCOUNT
607 *
608 * -- LM_INSERTITEM
609 *
610 * -- LM_SETTOPINDEX
611 *
612 * -- LM_QUERYTOPINDEX
613 *
614 * -- LM_DELETEITEM
615 *
616 * -- LM_SELECTITEM
617 *
618 * -- LM_QUERYSELECTION
619 *
620 * -- LM_SETITEMTEXT
621 *
622 * -- LM_QUERYITEMTEXT
623 *
624 * -- LM_SEARCHSTRING
625 *
626 * -- LM_DELETEALL
627 *
628 * NOTE: This occupies QWL_USER of the entryfield.
629 *
630 *@@added V0.9.9 (2001-03-17) [umoeller]
631 */
632
633BOOL ctlComboFromEntryField(HWND hwnd, // in: entry field to be converted
634 ULONG flStyle) // in: combo box styles
635{
636 BOOL brc = FALSE;
637 PFNWP pfnwpOrig;
638 if (pfnwpOrig = WinSubclassWindow(hwnd,
639 fnwpComboSubclass))
640 {
641 PCOMBODATA pcd;
642 if (pcd = (PCOMBODATA)malloc(sizeof(*pcd)))
643 {
644 SWP swp;
645 BITMAPINFOHEADER2 bmih2;
646
647 memset(pcd, 0, sizeof(*pcd));
648 pcd->pfnwpOrigEntryField = pfnwpOrig;
649 pcd->flStyle = flStyle;
650
651 WinSetWindowPtr(hwnd, QWL_USER, pcd);
652
653 WinQueryWindowPos(hwnd, &swp);
654 pcd->x = swp.x;
655 pcd->y = swp.y;
656 pcd->cx = swp.cx;
657 pcd->cy = swp.cy;
658
659 swp.cx -= COMBO_BUTTON_WIDTH;
660 WinSetWindowPos(hwnd,
661 0,
662 0, 0,
663 swp.cx, swp.cy,
664 SWP_SIZE | SWP_NOADJUST); // circumvent subclassing
665
666 pcd->hbmButton = WinGetSysBitmap(HWND_DESKTOP,
667 SBMP_COMBODOWN);
668 bmih2.cbFix = sizeof(bmih2);
669 GpiQueryBitmapInfoHeader(pcd->hbmButton,
670 &bmih2);
671 pcd->szlButton.cx = bmih2.cx;
672 pcd->szlButton.cy = bmih2.cy;
673
674 pcd->hwndButton = WinCreateWindow(WinQueryWindow(hwnd, QW_PARENT),
675 WC_BUTTON,
676 "",
677 WS_VISIBLE
678 | BS_PUSHBUTTON | BS_NOPOINTERFOCUS,
679 swp.x + swp.cx - COMBO_BUTTON_WIDTH,
680 swp.y,
681 COMBO_BUTTON_WIDTH,
682 swp.cy,
683 hwnd, // owner == entry field!
684 hwnd, // insert behind entry field
685 ID_COMBO_BUTTON,
686 NULL,
687 NULL);
688 WinSetWindowPtr(pcd->hwndButton, QWL_USER, pcd);
689 pcd->pfnwpOrigButton = WinSubclassWindow(pcd->hwndButton,
690 fnwpSubclassedComboButton);
691
692 pcd->hwndListbox = WinCreateWindow(HWND_OBJECT, // parent, for now
693 WC_LISTBOX,
694 "?",
695 WS_VISIBLE | WS_SAVEBITS | WS_CLIPSIBLINGS
696 | LS_NOADJUSTPOS,
697 0,
698 0,
699 0,
700 0,
701 hwnd, // owner == entry field!
702 HWND_TOP, // insert behind entry field
703 ID_COMBO_LISTBOX,
704 NULL,
705 NULL);
706
707 // finally, set style of entry field... we force
708 // these flags no matter what the original style
709 // was
710 /* WinSetWindowBits(hwnd,
711 QWL_STYLE,
712 // bits to set:
713 (flStyle & CBS_DROPDOWNLIST)
714 ? ES_READONLY
715 : 0,
716 // mask:
717 ES_READONLY); */
718 }
719 }
720
721 return brc;
722}
723
724
Note: See TracBrowser for help on using the repository browser.