source: trunk/src/helpers/cctl_checkcnr.c@ 154

Last change on this file since 154 was 147, checked in by umoeller, 23 years ago

Misc updates for Unicode.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 33.7 KB
Line 
1
2/*
3 *@@sourcefile cctl_checkcnr.c:
4 * implementation for the checkbox container common control.
5 * See comctl.c for an overview.
6 *
7 * This has been extracted from comctl.c with V0.9.3 (2000-05-21) [umoeller].
8 *
9 * Note: Version numbering in this file relates to XWorkplace version
10 * numbering.
11 *
12 *@@header "helpers\comctl.h"
13 *@@added V0.9.3 (2000-05-21) [umoeller].
14 */
15
16/*
17 * Copyright (C) 1997-2000 Ulrich M”ller.
18 * This file is part of the "XWorkplace helpers" source package.
19 * This is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published
21 * by the Free Software Foundation, in version 2 as it comes in the
22 * "COPYING" file of the XWorkplace main distribution.
23 * This program is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
27 */
28
29#define OS2EMX_PLAIN_CHAR
30 // this is needed for "os2emx.h"; if this is defined,
31 // emx will define PSZ as _signed_ char, otherwise
32 // as unsigned char
33
34#define INCL_DOSEXCEPTIONS
35#define INCL_DOSPROCESS
36#define INCL_DOSSEMAPHORES
37#define INCL_DOSERRORS
38
39#define INCL_WINWINDOWMGR
40#define INCL_WINFRAMEMGR
41#define INCL_WINMESSAGEMGR
42#define INCL_WININPUT
43#define INCL_WINPOINTERS
44#define INCL_WINTRACKRECT
45#define INCL_WINTIMER
46#define INCL_WINSYS
47
48#define INCL_WINRECTANGLES /// xxx temporary
49
50#define INCL_WINMENUS
51#define INCL_WINSTATICS
52#define INCL_WINBUTTONS
53#define INCL_WINSTDCNR
54
55#define INCL_GPIPRIMITIVES
56#define INCL_GPILOGCOLORTABLE
57#define INCL_GPILCIDS
58#define INCL_GPIPATHS
59#define INCL_GPIREGIONS
60#define INCL_GPIBITMAPS // added V0.9.1 (2000-01-04) [umoeller]: needed for EMX headers
61#include <os2.h>
62
63#include <stdlib.h>
64#include <stdio.h>
65#include <string.h>
66#include <setjmp.h> // needed for except.h
67#include <assert.h> // needed for except.h
68
69#include "setup.h" // code generation and debugging options
70
71#include "helpers\cnrh.h"
72#include "helpers\except.h" // exception handling
73#include "helpers\gpih.h"
74#include "helpers\linklist.h"
75#include "helpers\winh.h"
76
77#include "helpers\comctl.h"
78
79#pragma hdrstop
80
81/*
82 *@@category: Helpers\PM helpers\Window classes\Checkbox containers
83 * See cctl_checkcnr.c.
84 */
85
86/* ******************************************************************
87 *
88 * Global variables
89 *
90 ********************************************************************/
91
92// linked list of CHEXKBOXCNROWNER struct pointers
93HMTX G_hmtxCnrOwnersList = 0;
94PLINKLIST G_pllCnrOwners = 0;
95
96/* ******************************************************************
97 *
98 * Checkbox container record cores
99 *
100 ********************************************************************/
101
102HBITMAP G_hbmCheckboxes = NULLHANDLE;
103ULONG G_cxCheckbox = 0;
104
105/*
106 *@@ ctlDrawCheckbox:
107 *
108 * The system checkbox bitmap (G_hbmCheckboxes)
109 * is organized into 4 columns and three rows as
110 * follows:
111 *
112 + not depressed depressed
113 + ÚÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄ¿
114 + checkbox ³ ³ chk ³ ³ chk ³
115 + ÃÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄŽ
116 + radio button ³ ³ chk ³ ³ chk ³
117 + ÃÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄŽ
118 + chbx indeterm.³ ³ ind ³ ³ ind ³
119 + ÀÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÙ
120 *
121 *@@added V0.9.0 (99-11-28) [umoeller]
122 */
123
124BOOL ctlDrawCheckbox(HPS hps, // in: paint PS
125 LONG x, // in: target lower left corner x
126 LONG y, // in: target lower left corner y
127 USHORT usRow, // in: 0 - 2
128 USHORT usColumn, // in: 0 - 3
129 BOOL fHalftoned) // in: if TRUE, the checkbox will be halftoned (grayed)
130{
131 POINTL aptl[4];
132 BOOL brc = FALSE;
133
134 if (G_hbmCheckboxes)
135 {
136 // paint checkbox;
137 LONG lHits;
138
139 memset(aptl, 0, sizeof(POINTL) * 4);
140
141 // aptl[0]: target bottom-left
142 aptl[0].x = x;
143 aptl[0].y = y;
144
145 // aptl[1]: target top-right (inclusive!)
146 aptl[1].x = aptl[0].x + G_cxCheckbox - 1;
147 aptl[1].y = aptl[0].y + G_cxCheckbox - 1;
148
149 // aptl[2]: source bottom-left:
150 // depends on record selection
151 aptl[2].x = usColumn * G_cxCheckbox;
152 aptl[2].y = usRow * G_cxCheckbox; // top checkbox row
153
154 // aptl[3]: source top-right (non-inclusive!)
155 aptl[3].x = aptl[2].x + G_cxCheckbox;
156 aptl[3].y = aptl[2].y + G_cxCheckbox;
157
158 if (fHalftoned)
159 {
160 HDC hdc = GpiQueryDevice(hps);
161 HWND hwnd = WinWindowFromDC(hdc);
162 HAB hab = WinQueryAnchorBlock(hwnd);
163 HBITMAP hbmHalftoned = gpihCreateHalftonedBitmap(hab,
164 G_hbmCheckboxes,
165 CLR_WHITE);
166 lHits = GpiWCBitBlt(hps, // hpsTarget
167 hbmHalftoned,
168 4,
169 &aptl[0],
170 ROP_SRCCOPY, // mix
171 0); // options
172 GpiDeleteBitmap(hbmHalftoned);
173 }
174 else
175 lHits = GpiWCBitBlt(hps, // hpsTarget
176 G_hbmCheckboxes,
177 4,
178 &aptl[0],
179 ROP_SRCCOPY, // mix
180 0); // options
181 if (lHits == GPI_OK)
182 brc = TRUE;
183 }
184
185 return (brc);
186}
187
188/*
189 *@@ CnrCheckboxClicked:
190 *
191 *
192 *@@added V0.9.0 (99-11-28) [umoeller]
193 */
194
195static VOID CnrCheckboxClicked(PCHECKBOXCNROWNER pcbco,
196 PCHECKBOXRECORDCORE precc,
197 BOOL fToggleAndNotify) // if TRUE, toggle state and notify owner (CN_RECORDCHECKED)
198{
199 if (precc->ulStyle & WS_VISIBLE)
200 {
201 if (precc->recc.flRecordAttr & CRA_DISABLED)
202 // disabled:
203 WinAlarm(HWND_DESKTOP, WA_WARNING);
204 else
205 {
206 if (precc->ulStyle & BS_AUTOCHECKBOX)
207 {
208 if (fToggleAndNotify)
209 precc->usCheckState = 1 - precc->usCheckState;
210
211 WinSendMsg(pcbco->hwndCnr,
212 CM_INVALIDATERECORD,
213 (MPARAM)&precc,
214 MPFROM2SHORT(1,
215 CMA_NOREPOSITION));
216 }
217
218 if (fToggleAndNotify)
219 WinPostMsg(pcbco->hwndOwner, // was: WinSendMsg
220 WM_CONTROL,
221 MPFROM2SHORT(pcbco->usCnrID,
222 CN_RECORDCHECKED),
223 (MPARAM)precc);
224 }
225 }
226}
227
228/*
229 *@@ ctlDrawCheckBoxRecord:
230 *
231 *@@added V0.9.18 (2002-03-03) [umoeller]
232 */
233
234MRESULT ctlDrawCheckBoxRecord(MPARAM mp2)
235{
236 MRESULT mrc = 0;
237
238 // get generic DRAWITEM structure
239 POWNERITEM poi = (POWNERITEM)mp2;
240
241 // _Pmpf(("WM_DRAWITEM poi->idItem %d", poi->idItem));
242
243 // check if we're to draw the icon
244 // (and not the text)
245 if (poi->idItem == CMA_ICON)
246 {
247 PCNRDRAWITEMINFO pcdi = (PCNRDRAWITEMINFO)poi->hItem;
248 PCHECKBOXRECORDCORE precc = (PCHECKBOXRECORDCORE)pcdi->pRecord;
249
250 if (precc->ulStyle & WS_VISIBLE)
251 {
252 USHORT usRow,
253 usColumn;
254
255 switch (precc->usCheckState)
256 {
257 case 0: // unchecked
258 usRow = 2;
259 usColumn = 0;
260 break;
261
262 case 1: // checked
263 usRow = 2;
264 usColumn = 1;
265 break;
266
267 case 2: // indeterminate
268 usRow = 0;
269 usColumn = 1;
270 break;
271 }
272
273 if (precc->ulStyle & BS_BITMAP)
274 // button currently depressed:
275 // add two to column
276 usColumn += 2;
277
278 ctlDrawCheckbox(poi->hps,
279 poi->rclItem.xLeft,
280 poi->rclItem.yBottom,
281 usRow,
282 usColumn,
283 // halftoned?
284 ((precc->recc.flRecordAttr & CRA_DISABLED) != 0));
285 mrc = (MPARAM)FALSE;
286 // we still need the cnr to draw the
287 // emphasis
288 }
289 else
290 mrc = (MPARAM)TRUE; // tell cnr that we've drawn the item;
291 // don't even draw emphasis
292 }
293 else if (poi->idItem == CMA_TEXT)
294 {
295 // for text, buttons etc.:
296 PCNRDRAWITEMINFO pcdi = (PCNRDRAWITEMINFO)poi->hItem;
297 PCHECKBOXRECORDCORE precc = (PCHECKBOXRECORDCORE)pcdi->pRecord;
298 if (precc->recc.flRecordAttr & CRA_DISABLED)
299 {
300 RECTL rcl2;
301 LONG lBackground, lForeground;
302 ULONG flCmd = DT_LEFT | DT_TOP | DT_ERASERECT;
303 if ((pcdi->pRecord->flRecordAttr) & CRA_SELECTED)
304 {
305 // disabled and selected:
306 lBackground = WinQuerySysColor(HWND_DESKTOP,
307 SYSCLR_SHADOWTEXT, 0);
308 lForeground = winhQueryPresColor(poi->hwnd,
309 PP_BACKGROUNDCOLOR,
310 FALSE, // no inherit
311 SYSCLR_WINDOW);
312 }
313 else
314 {
315 // disabled and not selected:
316 lBackground = winhQueryPresColor(poi->hwnd,
317 PP_BACKGROUNDCOLOR,
318 FALSE,
319 SYSCLR_WINDOW);
320 lForeground = winhQueryPresColor(poi->hwnd,
321 PP_FOREGROUNDCOLOR,
322 FALSE, // no inherit
323 SYSCLR_WINDOWTEXT);
324 flCmd |= DT_HALFTONE;
325 }
326
327 // _Pmpf(("back: 0x%lX, fore: 0x%lX", lBackground, lForeground));
328
329 GpiCreateLogColorTable(poi->hps, 0, LCOLF_RGB, 0, 0, NULL);
330 GpiSetBackColor(poi->hps, lBackground);
331 GpiSetColor(poi->hps, lForeground);
332
333 memcpy(&rcl2, &(poi->rclItem), sizeof(rcl2));
334
335 winhDrawFormattedText(poi->hps,
336 &rcl2,
337 precc->recc.pszTree,
338 flCmd);
339 mrc = (MPARAM)TRUE;
340 }
341 else
342 // tell cnr to draw the item
343 mrc = (MPARAM)FALSE;
344 }
345 else
346 // tell cnr to draw the item
347 mrc = (MPARAM)FALSE;
348
349 return mrc;
350}
351
352/*
353 *@@ fnwpSubclCheckboxCnr:
354 * window proc for subclassed containers.
355 * See ctlMakeCheckboxContainer for details.
356 *
357 *@@added V0.9.0 (99-11-29) [umoeller]
358 *@@changed V0.9.18 (2002-03-03) [umoeller]: fixed bad orig win msg, other optimizations
359 */
360
361static MRESULT EXPENTRY fnwpSubclCheckboxCnr(HWND hwndCnr, ULONG msg, MPARAM mp1, MPARAM mp2)
362{
363 MRESULT mrc = 0;
364 PCHECKBOXCNROWNER pcbco = 0;
365 PFNWP pfnwpOrig = 0;
366
367 if (WinRequestMutexSem(G_hmtxCnrOwnersList, 5000) == NO_ERROR)
368 {
369 PLISTNODE pNode = lstQueryFirstNode(G_pllCnrOwners);
370 while (pNode)
371 {
372 pcbco = (PCHECKBOXCNROWNER)pNode->pItemData;
373 if (pcbco->hwndCnr == hwndCnr)
374 {
375 pfnwpOrig = pcbco->pfnwpCnrOrig; // fixed V0.9.18 (2002-03-03) [umoeller]
376 break;
377 }
378 pNode = pNode->pNext;
379 }
380 DosReleaseMutexSem(G_hmtxCnrOwnersList);
381 }
382
383 if (pfnwpOrig)
384 {
385 switch (msg)
386 {
387 case WM_BUTTON1DOWN:
388 {
389 POINTL ptlClick;
390 ptlClick.x = SHORT1FROMMP(mp1);
391 ptlClick.y = SHORT2FROMMP(mp1);
392
393 // find record whose icon has this point
394 pcbco->preccClicked
395 = (PCHECKBOXRECORDCORE)cnrhFindRecordFromPoint(hwndCnr,
396 &ptlClick,
397 &pcbco->rclReccClicked,
398 CMA_ICON,
399 0);
400
401 if (pcbco->preccClicked)
402 {
403 if (pcbco->preccClicked->ulStyle & WS_VISIBLE)
404 {
405 // add "depressed" style
406 pcbco->preccClicked->ulStyle |= BS_BITMAP;
407 // mouse was clicked on icon (checkbox):
408 CnrCheckboxClicked(pcbco,
409 pcbco->preccClicked,
410 FALSE);
411 }
412 else
413 // not visible:
414 pcbco->preccClicked = NULL;
415 }
416
417 mrc = pfnwpOrig(hwndCnr, msg, mp1, mp2);
418 // apperently, the cnr captures the mouse
419 }
420 break;
421
422 case WM_MOUSEMOVE:
423 if (pcbco->preccClicked)
424 {
425 // mouse moved while checkbox is depressed:
426 POINTL ptlMove;
427 ptlMove.x = SHORT1FROMMP(mp1);
428 ptlMove.y = SHORT2FROMMP(mp1);
429
430 if (WinPtInRect(pcbco->habCnr,
431 &pcbco->rclReccClicked,
432 &ptlMove))
433 {
434 // mouse is in checkbox:
435 if ((pcbco->preccClicked->ulStyle & BS_BITMAP) == 0)
436 {
437 // checkbox is not drawn depressed:
438 // add "depressed" style
439 pcbco->preccClicked->ulStyle |= BS_BITMAP;
440 CnrCheckboxClicked(pcbco,
441 pcbco->preccClicked,
442 FALSE);
443 }
444 }
445 else
446 {
447 // mouse is outside of checkbox:
448 if (pcbco->preccClicked->ulStyle & BS_BITMAP)
449 {
450 // checkbox is drawn depressed:
451 // remove "depressed" style
452 pcbco->preccClicked->ulStyle &= ~BS_BITMAP;
453 CnrCheckboxClicked(pcbco,
454 pcbco->preccClicked,
455 FALSE);
456 }
457 }
458 }
459 break;
460
461 case WM_BUTTON1UP:
462 if (pcbco->preccClicked)
463 {
464 if (pcbco->preccClicked->ulStyle & BS_BITMAP)
465 {
466 pcbco->preccClicked->ulStyle &= ~BS_BITMAP;
467 CnrCheckboxClicked(pcbco,
468 pcbco->preccClicked,
469 TRUE);
470 }
471
472 pcbco->preccClicked = NULL;
473 }
474 mrc = pfnwpOrig(hwndCnr, msg, mp1, mp2);
475 break;
476
477 /*
478 * WM_CHAR:
479 *
480 */
481
482 case WM_CHAR:
483 {
484 USHORT fsFlags = SHORT1FROMMP(mp1);
485 CHAR ch = CHAR1FROMMP(mp2);
486 // never use the USHORT, because for
487 // the "key-up" message, the hi-char
488 // is different (duh...)
489
490 // _Pmpf(("WM_CHAR fsFlags %lX, usch %d", fsFlags, ch));
491
492 if (ch == ' ')
493 {
494 // space: toggle checkbox
495 if ((fsFlags & KC_KEYUP) == 0)
496 {
497 // space down:
498 // filter up repetitive key msgs
499 if (pcbco->preccSpace == NULL)
500 {
501 PCHECKBOXRECORDCORE precc = (PCHECKBOXRECORDCORE)WinSendMsg(
502 hwndCnr,
503 CM_QUERYRECORDEMPHASIS,
504 (MPARAM)CMA_FIRST,
505 (MPARAM)CRA_CURSORED);
506 if ((precc) && (precc != (PCHECKBOXRECORDCORE)-1))
507 {
508 if (precc->ulStyle & WS_VISIBLE)
509 {
510 pcbco->preccSpace = precc;
511
512 // add "depressed" style
513 pcbco->preccSpace->ulStyle |= BS_BITMAP;
514 CnrCheckboxClicked(pcbco,
515 pcbco->preccSpace,
516 FALSE);
517 }
518 }
519 }
520 }
521 else
522 {
523 // space up:
524 if (pcbco->preccSpace)
525 {
526 if (pcbco->preccSpace->ulStyle & BS_BITMAP)
527 {
528 pcbco->preccSpace->ulStyle &= ~BS_BITMAP;
529 CnrCheckboxClicked(pcbco,
530 pcbco->preccSpace,
531 TRUE);
532 }
533
534 pcbco->preccSpace = NULL;
535 }
536 }
537
538 // do not pass spaces on
539 mrc = (MPARAM)TRUE; // processed
540 break;
541 }
542
543 mrc = pfnwpOrig(hwndCnr, msg, mp1, mp2);
544 }
545 break;
546
547 /*
548 * WM_DESTROY:
549 * remove list item; this gets sent
550 * to the parent first, then the children,
551 * so we remove this in the container
552 * (this way the list item is still valid
553 * in ctl_fnwpSubclCheckboxCnrOwner)
554 */
555
556 case WM_DESTROY:
557 if (WinRequestMutexSem(G_hmtxCnrOwnersList, 5000) == NO_ERROR)
558 {
559 if (WinIsWindow(pcbco->habCnr,
560 pcbco->hwndOwner))
561 // un-subclass the owner
562 WinSubclassWindow(pcbco->hwndOwner,
563 pcbco->pfnwpOwnerOrig);
564
565 lstRemoveItem(G_pllCnrOwners, pcbco);
566
567 if (lstCountItems(G_pllCnrOwners) == 0)
568 lstFree(&G_pllCnrOwners);
569
570 DosReleaseMutexSem(G_hmtxCnrOwnersList);
571 }
572
573 mrc = pfnwpOrig(hwndCnr, msg, mp1, mp2);
574 break;
575
576 default:
577 mrc = pfnwpOrig(hwndCnr, msg, mp1, mp2);
578 }
579 }
580 else
581 mrc = WinDefWindowProc(hwndCnr, msg, mp1, mp2);
582
583 return (mrc);
584}
585
586
587/*
588 *@@ fnwpSubclCheckboxCnrOwner:
589 * window proc for subclassed container owners.
590 * See ctlMakeCheckboxContainer for details.
591 *
592 *@@added V0.9.0 (99-11-28) [umoeller]
593 */
594
595static MRESULT EXPENTRY fnwpSubclCheckboxCnrOwner(HWND hwndOwner, ULONG msg, MPARAM mp1, MPARAM mp2)
596{
597 MRESULT mrc = 0;
598 PCHECKBOXCNROWNER pcbco = 0;
599 PFNWP pfnwpOrig = 0;
600
601 if (WinRequestMutexSem(G_hmtxCnrOwnersList, 5000) == NO_ERROR)
602 {
603 PLISTNODE pNode = lstQueryFirstNode(G_pllCnrOwners);
604 while (pNode)
605 {
606 pcbco = (PCHECKBOXCNROWNER)pNode->pItemData;
607 if (pcbco->hwndOwner == hwndOwner)
608 {
609 pfnwpOrig = pcbco->pfnwpOwnerOrig;
610 break;
611 }
612 pNode = pNode->pNext;
613 }
614 DosReleaseMutexSem(G_hmtxCnrOwnersList);
615 }
616
617 if (pfnwpOrig)
618 {
619 switch (msg)
620 {
621 /*
622 * WM_CONTROL:
623 *
624 */
625
626 case WM_CONTROL:
627 {
628 if (SHORT1FROMMP(mp1) == pcbco->usCnrID)
629 {
630 switch (SHORT2FROMMP(mp1))
631 {
632 case CN_ENTER:
633 {
634 PNOTIFYRECORDENTER pnre = (PNOTIFYRECORDENTER)mp2;
635 PCHECKBOXRECORDCORE precc = (PCHECKBOXRECORDCORE)pnre->pRecord;
636
637 if (precc)
638 CnrCheckboxClicked(pcbco,
639 precc,
640 TRUE);
641 }
642 break;
643
644 default:
645 mrc = pfnwpOrig(hwndOwner, msg, mp1, mp2);
646 }
647 }
648 else
649 mrc = pfnwpOrig(hwndOwner, msg, mp1, mp2);
650 }
651 break;
652
653 /*
654 * WM_DRAWITEM:
655 * cnr owner draw; this is where we draw
656 * the checkboxes
657 */
658
659 case WM_DRAWITEM:
660 {
661 if (SHORT1FROMMP(mp1) == pcbco->usCnrID)
662 mrc = ctlDrawCheckBoxRecord(mp2);
663 else
664 mrc = pfnwpOrig(hwndOwner, msg, mp1, mp2);
665 }
666 break;
667
668 default:
669 mrc = pfnwpOrig(hwndOwner, msg, mp1, mp2);
670 }
671 }
672 else
673 mrc = WinDefWindowProc(hwndOwner, msg, mp1, mp2);
674
675 return (mrc);
676}
677
678/*
679 *@@ ctlInitCheckboxContainer:
680 *
681 *@@added V0.9.18 (2002-03-03) [umoeller]
682 */
683
684VOID ctlInitCheckboxContainer(HWND hwndCnr)
685{
686 if (G_hbmCheckboxes == NULLHANDLE)
687 {
688 // first call:
689 BITMAPINFOHEADER bmih;
690 // load checkboxes bitmap
691 G_hbmCheckboxes = WinGetSysBitmap(HWND_DESKTOP,
692 SBMP_CHECKBOXES);
693
694 // _Pmpf(("hbmCheckboxes: 0x%lX", G_hbmCheckboxes));
695
696 // and compute size of one checkbox
697 // (4 columns, 3 rows)
698 GpiQueryBitmapParameters(G_hbmCheckboxes,
699 &bmih);
700 G_cxCheckbox = bmih.cx / 4;
701 }
702
703 BEGIN_CNRINFO()
704 {
705 cnrhSetView(CV_TREE | CV_ICON | CA_TREELINE | CA_OWNERDRAW | CV_MINI);
706 cnrhSetTreeIndent(20);
707 cnrhSetBmpOrIconSize(G_cxCheckbox, G_cxCheckbox);
708 } END_CNRINFO(hwndCnr);
709}
710
711/*
712 *@@ ctlSubclassCheckboxContainer:
713 *
714 *@@added V0.9.18 (2002-03-03) [umoeller]
715 */
716
717PCHECKBOXCNROWNER ctlSubclassCheckboxContainer(HWND hwndCnr)
718{
719 PFNWP pfnwpCnrOrig;
720 if (pfnwpCnrOrig = WinSubclassWindow(hwndCnr, fnwpSubclCheckboxCnr))
721 {
722 // cnr successfully subclassed:
723 // create storage for both subclassed cnr and owner
724 PCHECKBOXCNROWNER pcbco;
725 if (pcbco = (PCHECKBOXCNROWNER)malloc(sizeof(CHECKBOXCNROWNER)))
726 {
727 memset(pcbco, 0, sizeof(CHECKBOXCNROWNER));
728 pcbco->hwndCnr = hwndCnr;
729 pcbco->usCnrID = WinQueryWindowUShort(hwndCnr, QWS_ID);
730 pcbco->hwndOwner = WinQueryWindow(hwndCnr, QW_OWNER);
731 pcbco->pfnwpCnrOrig = pfnwpCnrOrig;
732
733 pcbco->habCnr = WinQueryAnchorBlock(hwndCnr);
734
735 return (pcbco);
736 }
737 }
738
739 return NULL;
740}
741
742/*
743 *@@ ctlMakeCheckboxContainer:
744 * this turns a regular container into a "checkbox"
745 * container. This means that the container record
746 * icons are painted as checkboxes, whose status
747 * is determined using an extended record core
748 * structure (CHECKBOXRECORDCORE).
749 *
750 * This function assumes that all records in the
751 * container use this special extended record core
752 * ONLY. You cannot use regular RECORDCORE's when
753 * using this function. Of course, you can extend
754 * the CHECKBOXRECORDCORE structure to the bottom
755 * if you need more fields.
756 *
757 * This subclasses BOTH the container and the container
758 * owner to intercept a number of messages and to
759 * implement owner draw. This is totally transparent
760 * to the caller; QWL_USER is not used for this, so
761 * you can still store your own stuff in there.
762 *
763 * Returns FALSE upon errors.
764 *
765 * To set up a checkbox container, call this function
766 * (which switches the container to Tree view automatically).
767 * Then, allocate and insert CHECKBOXRECORDCORE's as usual.
768 *
769 * To set and query a record's check state easily, use
770 * ctlSetRecordChecked and ctlQueryRecordChecked.
771 *
772 * The checkboxes work almost identically to regular
773 * checkboxes. The user can interact with the checkbox
774 * by clicking on the checkbox itself or by double-clicking
775 * on the record text or by pressing the Enter or Space keys.
776 *
777 * Set CHECKBOXRECORDCORE.ulStyle to one of the regular
778 * checkbox button style bits; if one of the "auto" flags
779 * is set, the checkbox is toggled automatically.
780 *
781 * Even if "auto" is off, when the user changes the check
782 * box selection, hwndCnrOwner gets sent a WM_CONTROL message
783 * with the following parameters:
784 *
785 * -- mp1: USHORT id: usCnrID, as passed to this func
786 * -- mp1: USHORT usNotifyCode: CN_RECORDCHECKED (999)
787 * -- mp2: PCHECKRECORDCORE precc: the record which was (un)checked.
788 *
789 * Note that the subclassed container owner proc filters out
790 * CN_ENTER, so you'll never receive that (but CN_RECORDCHECKED
791 * instead).
792 *
793 * If the "auto" style bits have not been set, you must call
794 * ctlSetRecordChecked yourself then.
795 *
796 *@@added V0.9.0 (99-11-28) [umoeller]
797 */
798
799BOOL ctlMakeCheckboxContainer(HWND hwndCnrOwner, // in: owner (and parent) of container
800 USHORT usCnrID) // in: ID of container in hwndCnrOwner
801{
802 BOOL brc = FALSE;
803
804 HWND hwndCnr = WinWindowFromID(hwndCnrOwner, usCnrID);
805
806 // create mutex-protected list of container owners
807 if (G_hmtxCnrOwnersList == 0)
808 if (DosCreateMutexSem(NULL,
809 &G_hmtxCnrOwnersList, 0, FALSE) != NO_ERROR)
810 return (FALSE);
811
812 if (G_pllCnrOwners == NULL)
813 G_pllCnrOwners = lstCreate(TRUE);
814
815 if (hwndCnr)
816 {
817 ctlInitCheckboxContainer(hwndCnr);
818
819 if ( (hwndCnrOwner)
820 && (G_hmtxCnrOwnersList)
821 )
822 {
823 // subclass container owner
824 PFNWP pfnwpOwnerOrig = WinSubclassWindow(hwndCnrOwner, fnwpSubclCheckboxCnrOwner);
825 /* _Pmpf(("Subclassed hwnd 0x%lX: orig 0x%lX",
826 hwndCnrOwner, pfnwpOwnerOrig)); */
827 if (pfnwpOwnerOrig)
828 {
829 // owner successfully subclassed:
830 // subclass container too
831 PCHECKBOXCNROWNER pcbco;
832 if (pcbco = ctlSubclassCheckboxContainer(hwndCnr))
833 {
834 if (WinRequestMutexSem(G_hmtxCnrOwnersList, 5000) == NO_ERROR)
835 {
836 lstAppendItem(G_pllCnrOwners, pcbco);
837 DosReleaseMutexSem(G_hmtxCnrOwnersList);
838 brc = TRUE;
839
840 pcbco->pfnwpOwnerOrig = pfnwpOwnerOrig;
841 }
842 }
843 }
844 }
845 }
846
847 return (brc);
848}
849
850/*
851 *@@ fncbFindCheckRecord:
852 * helper callback for finding a checkbox
853 * record according to an item ID. Used
854 * with cnrhForAllRecords.
855 *
856 * Returns 1 if found to make
857 * cnrhForAllRecords stop searching.
858 *
859 *@@added V0.9.0 (99-11-28) [umoeller]
860 */
861
862static ULONG EXPENTRY fncbFindCheckRecord(HWND hwndCnr, // in: container
863 PRECORDCORE preccThis, // in: current record (from cnrhForAllRecords)
864 ULONG ulItemID, // in: item ID to find
865 ULONG ulppRecc) // out: PRECORDCORE* if found
866{
867 ULONG ulrc = 0;
868 PCHECKBOXRECORDCORE precc = (PCHECKBOXRECORDCORE)preccThis;
869 if (precc)
870 {
871 if (precc->ulItemID == ulItemID)
872 {
873 // found:
874 PCHECKBOXRECORDCORE *pprecc = (PCHECKBOXRECORDCORE*)ulppRecc;
875 // store
876 *pprecc = precc;
877 // and stop
878 ulrc = 1;
879 }
880 }
881 return (ulrc);
882}
883
884/*
885 *@@ ctlFindCheckRecord:
886 * this searches the given checkbox container
887 * for the record which matches the given item
888 * ID. Returns NULL if not found.
889 *
890 *@@added V0.9.1 (99-12-03) [umoeller]
891 */
892
893PCHECKBOXRECORDCORE ctlFindCheckRecord(HWND hwndCnr,
894 ULONG ulItemID)
895{
896 PCHECKBOXRECORDCORE precc = 0;
897
898 cnrhForAllRecords(hwndCnr,
899 NULL, // start with root
900 fncbFindCheckRecord,
901 (ULONG)ulItemID, // input
902 (ULONG)&precc);
903
904 return (precc);
905}
906
907/*
908 *@@ ctlSetRecordChecked:
909 * this searches the given checkbox container
910 * for the record which matches the given item
911 * ID and updates that record check state.
912 *
913 * The check state may be:
914 * -- 0: not checked (as with regular checkboxes)
915 * -- 1: checked (as with regular checkboxes)
916 * -- 2: indeterminate (as with regular checkboxes)
917 *
918 * Returns FALSE if the record could not be found.
919 *
920 *@@added V0.9.0 (99-11-28) [umoeller]
921 */
922
923BOOL ctlSetRecordChecked(HWND hwndCnr, // in: container prepared with
924 // ctlMakeCheckboxContainer
925 ULONG ulItemID, // in: record item ID
926 USHORT usCheckState) // in: 0, 1, 2, 3
927{
928 BOOL brc = FALSE;
929 PCHECKBOXRECORDCORE precc = ctlFindCheckRecord(hwndCnr, ulItemID);
930
931 if (precc)
932 {
933 precc->usCheckState = usCheckState;
934 WinSendMsg(hwndCnr,
935 CM_INVALIDATERECORD,
936 (MPARAM)&precc,
937 MPFROM2SHORT(1,
938 CMA_NOREPOSITION));
939 brc = TRUE;
940 }
941
942 return (brc);
943}
944
945/*
946 *@@ ctlQueryRecordChecked:
947 * this searches the given checkbox container
948 * for the record which matches the given item
949 * ID and returns that record's check state.
950 *
951 * Returns:
952 * -- 0: not checked (as with regular checkboxes)
953 * -- 1: checked (as with regular checkboxes)
954 * -- 2: indeterminate (as with regular checkboxes)
955 * -- -1: record not found.
956 *
957 *@@added V0.9.0 (99-11-28) [umoeller]
958 */
959
960ULONG ctlQueryRecordChecked(HWND hwndCnr, // in: container prepared with
961 // ctlMakeCheckboxContainer
962 ULONG ulItemID, // in: record item ID
963 USHORT usCheckState) // in: 0, 1, 2, 3
964{
965 ULONG ulrc = -1;
966 PCHECKBOXRECORDCORE precc = ctlFindCheckRecord(hwndCnr, ulItemID);
967
968 if (precc)
969 ulrc = precc->usCheckState;
970
971 return (ulrc);
972}
973
974/*
975 *@@ ctlEnableRecord:
976 * this searches the given checkbox container
977 * for the record which matches the given item
978 * ID and updates that record enablement. If
979 * the record is disabled, it's painted halftoned
980 * by fnwpSubclCheckboxCnr.
981 *
982 *@@added V0.9.1 (99-12-03) [umoeller]
983 */
984
985BOOL ctlEnableRecord(HWND hwndCnr,
986 ULONG ulItemID,
987 BOOL fEnable)
988{
989 BOOL brc = FALSE;
990 PCHECKBOXRECORDCORE precc = ctlFindCheckRecord(hwndCnr, ulItemID);
991
992 if (precc)
993 {
994 ULONG ulAttr = precc->recc.flRecordAttr;
995 if (fEnable)
996 precc->recc.flRecordAttr &= ~CRA_DISABLED;
997 else
998 precc->recc.flRecordAttr |= CRA_DISABLED;
999
1000 if (precc->recc.flRecordAttr != ulAttr)
1001 // attrs changed: repaint
1002 WinSendMsg(hwndCnr,
1003 CM_INVALIDATERECORD,
1004 (MPARAM)&precc,
1005 MPFROM2SHORT(1,
1006 CMA_NOREPOSITION));
1007
1008 brc = TRUE;
1009 }
1010
1011 return (brc);
1012}
1013
1014
Note: See TracBrowser for help on using the repository browser.