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

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

misc fixes

  • 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 {
569 lstFree(&G_pllCnrOwners);
570 }
571 DosReleaseMutexSem(G_hmtxCnrOwnersList);
572 }
573
574 mrc = pfnwpOrig(hwndCnr, msg, mp1, mp2);
575 break;
576
577 default:
578 mrc = pfnwpOrig(hwndCnr, msg, mp1, mp2);
579 }
580 }
581 else
582 mrc = WinDefWindowProc(hwndCnr, msg, mp1, mp2);
583
584 return (mrc);
585}
586
587
588/*
589 *@@ fnwpSubclCheckboxCnrOwner:
590 * window proc for subclassed container owners.
591 * See ctlMakeCheckboxContainer for details.
592 *
593 *@@added V0.9.0 (99-11-28) [umoeller]
594 */
595
596static MRESULT EXPENTRY fnwpSubclCheckboxCnrOwner(HWND hwndOwner, ULONG msg, MPARAM mp1, MPARAM mp2)
597{
598 MRESULT mrc = 0;
599 PCHECKBOXCNROWNER pcbco = 0;
600 PFNWP pfnwpOrig = 0;
601
602 if (WinRequestMutexSem(G_hmtxCnrOwnersList, 5000) == NO_ERROR)
603 {
604 PLISTNODE pNode = lstQueryFirstNode(G_pllCnrOwners);
605 while (pNode)
606 {
607 pcbco = (PCHECKBOXCNROWNER)pNode->pItemData;
608 if (pcbco->hwndOwner == hwndOwner)
609 {
610 pfnwpOrig = pcbco->pfnwpOwnerOrig;
611 break;
612 }
613 pNode = pNode->pNext;
614 }
615 DosReleaseMutexSem(G_hmtxCnrOwnersList);
616 }
617
618 if (pfnwpOrig)
619 {
620 switch (msg)
621 {
622 /*
623 * WM_CONTROL:
624 *
625 */
626
627 case WM_CONTROL:
628 {
629 USHORT usItemID = SHORT1FROMMP(mp1),
630 usNotifyCode = SHORT2FROMMP(mp1);
631
632 if (usItemID == pcbco->usCnrID)
633 {
634 switch (usNotifyCode)
635 {
636 case CN_ENTER:
637 {
638 PNOTIFYRECORDENTER pnre = (PNOTIFYRECORDENTER)mp2;
639 PCHECKBOXRECORDCORE precc = (PCHECKBOXRECORDCORE)pnre->pRecord;
640
641 if (precc)
642 CnrCheckboxClicked(pcbco,
643 precc,
644 TRUE);
645 }
646 break;
647 }
648 }
649
650 mrc = pfnwpOrig(hwndOwner, msg, mp1, mp2);
651 }
652 break;
653
654 /*
655 * WM_DRAWITEM:
656 * cnr owner draw; this is where we draw
657 * the checkboxes
658 */
659
660 case WM_DRAWITEM:
661 {
662 if (SHORT1FROMMP(mp1) == pcbco->usCnrID)
663 mrc = ctlDrawCheckBoxRecord(mp2);
664 else
665 mrc = pfnwpOrig(hwndOwner, msg, mp1, mp2);
666 }
667 break;
668
669 default:
670 mrc = pfnwpOrig(hwndOwner, msg, mp1, mp2);
671 }
672 }
673 else
674 mrc = WinDefWindowProc(hwndOwner, msg, mp1, mp2);
675
676 return (mrc);
677}
678
679/*
680 *@@ ctlInitCheckboxContainer:
681 *
682 *@@added V0.9.18 (2002-03-03) [umoeller]
683 */
684
685VOID ctlInitCheckboxContainer(HWND hwndCnr)
686{
687 if (G_hbmCheckboxes == NULLHANDLE)
688 {
689 // first call:
690 BITMAPINFOHEADER bmih;
691 // load checkboxes bitmap
692 G_hbmCheckboxes = WinGetSysBitmap(HWND_DESKTOP,
693 SBMP_CHECKBOXES);
694
695 // _Pmpf(("hbmCheckboxes: 0x%lX", G_hbmCheckboxes));
696
697 // and compute size of one checkbox
698 // (4 columns, 3 rows)
699 GpiQueryBitmapParameters(G_hbmCheckboxes,
700 &bmih);
701 G_cxCheckbox = bmih.cx / 4;
702 }
703
704 BEGIN_CNRINFO()
705 {
706 cnrhSetView(CV_TREE | CV_ICON | CA_TREELINE | CA_OWNERDRAW | CV_MINI);
707 cnrhSetTreeIndent(20);
708 cnrhSetBmpOrIconSize(G_cxCheckbox, G_cxCheckbox);
709 } END_CNRINFO(hwndCnr);
710}
711
712/*
713 *@@ ctlSubclassCheckboxContainer:
714 *
715 *@@added V0.9.18 (2002-03-03) [umoeller]
716 */
717
718PCHECKBOXCNROWNER ctlSubclassCheckboxContainer(HWND hwndCnr)
719{
720 PFNWP pfnwpCnrOrig;
721 if (pfnwpCnrOrig = WinSubclassWindow(hwndCnr, fnwpSubclCheckboxCnr))
722 {
723 // cnr successfully subclassed:
724 // create storage for both subclassed cnr and owner
725 PCHECKBOXCNROWNER pcbco;
726 if (pcbco = (PCHECKBOXCNROWNER)malloc(sizeof(CHECKBOXCNROWNER)))
727 {
728 memset(pcbco, 0, sizeof(CHECKBOXCNROWNER));
729 pcbco->hwndCnr = hwndCnr;
730 pcbco->usCnrID = WinQueryWindowUShort(hwndCnr, QWS_ID);
731 pcbco->hwndOwner = WinQueryWindow(hwndCnr, QW_OWNER);
732 pcbco->pfnwpCnrOrig = pfnwpCnrOrig;
733
734 pcbco->habCnr = WinQueryAnchorBlock(hwndCnr);
735
736 return (pcbco);
737 }
738 }
739
740 return NULL;
741}
742
743/*
744 *@@ ctlMakeCheckboxContainer:
745 * this turns a regular container into a "checkbox"
746 * container. This means that the container record
747 * icons are painted as checkboxes, whose status
748 * is determined using an extended record core
749 * structure (CHECKBOXRECORDCORE).
750 *
751 * This function assumes that all records in the
752 * container use this special extended record core
753 * ONLY. You cannot use regular RECORDCORE's when
754 * using this function. Of course, you can extend
755 * the CHECKBOXRECORDCORE structure to the bottom
756 * if you need more fields.
757 *
758 * This subclasses BOTH the container and the container
759 * owner to intercept a number of messages and to
760 * implement owner draw. This is totally transparent
761 * to the caller; QWL_USER is not used for this, so
762 * you can still store your own stuff in there.
763 *
764 * Returns FALSE upon errors.
765 *
766 * To set up a checkbox container, call this function
767 * (which switches the container to Tree view automatically).
768 * Then, allocate and insert CHECKBOXRECORDCORE's as usual.
769 *
770 * To set and query a record's check state easily, use
771 * ctlSetRecordChecked and ctlQueryRecordChecked.
772 *
773 * The checkboxes work almost identically to regular
774 * checkboxes. The user can interact with the checkbox
775 * by clicking on the checkbox itself or by double-clicking
776 * on the record text or by pressing the Enter or Space keys.
777 *
778 * Set CHECKBOXRECORDCORE.ulStyle to one of the regular
779 * checkbox button style bits; if one of the "auto" flags
780 * is set, the checkbox is toggled automatically.
781 *
782 * Even if "auto" is off, when the user changes the check
783 * box selection, hwndCnrOwner gets sent a WM_CONTROL message
784 * with the following parameters:
785 *
786 * -- mp1: USHORT id: usCnrID, as passed to this func
787 * -- mp1: USHORT usNotifyCode: CN_RECORDCHECKED (999)
788 * -- mp2: PCHECKRECORDCORE precc: the record which was (un)checked.
789 *
790 * Note that the subclassed container owner proc filters out
791 * CN_ENTER, so you'll never receive that (but CN_RECORDCHECKED
792 * instead).
793 *
794 * If the "auto" style bits have not been set, you must call
795 * ctlSetRecordChecked yourself then.
796 *
797 *@@added V0.9.0 (99-11-28) [umoeller]
798 */
799
800BOOL ctlMakeCheckboxContainer(HWND hwndCnrOwner, // in: owner (and parent) of container
801 USHORT usCnrID) // in: ID of container in hwndCnrOwner
802{
803 BOOL brc = FALSE;
804
805 HWND hwndCnr = WinWindowFromID(hwndCnrOwner, usCnrID);
806
807 // create mutex-protected list of container owners
808 if (G_hmtxCnrOwnersList == 0)
809 if (DosCreateMutexSem(NULL,
810 &G_hmtxCnrOwnersList, 0, FALSE) != NO_ERROR)
811 return (FALSE);
812
813 if (G_pllCnrOwners == NULL)
814 G_pllCnrOwners = lstCreate(TRUE);
815
816 if (hwndCnr)
817 {
818 ctlInitCheckboxContainer(hwndCnr);
819
820 if ( (hwndCnrOwner)
821 && (G_hmtxCnrOwnersList)
822 )
823 {
824 // subclass container owner
825 PFNWP pfnwpOwnerOrig = WinSubclassWindow(hwndCnrOwner, fnwpSubclCheckboxCnrOwner);
826 /* _Pmpf(("Subclassed hwnd 0x%lX: orig 0x%lX",
827 hwndCnrOwner, pfnwpOwnerOrig)); */
828 if (pfnwpOwnerOrig)
829 {
830 // owner successfully subclassed:
831 // subclass container too
832 PCHECKBOXCNROWNER pcbco;
833 if (pcbco = ctlSubclassCheckboxContainer(hwndCnr))
834 {
835 if (WinRequestMutexSem(G_hmtxCnrOwnersList, 5000) == NO_ERROR)
836 {
837 lstAppendItem(G_pllCnrOwners, pcbco);
838 DosReleaseMutexSem(G_hmtxCnrOwnersList);
839 brc = TRUE;
840
841 pcbco->pfnwpOwnerOrig = pfnwpOwnerOrig;
842 }
843 }
844 }
845 }
846 }
847
848 return (brc);
849}
850
851/*
852 *@@ fncbFindCheckRecord:
853 * helper callback for finding a checkbox
854 * record according to an item ID. Used
855 * with cnrhForAllRecords.
856 *
857 * Returns 1 if found to make
858 * cnrhForAllRecords stop searching.
859 *
860 *@@added V0.9.0 (99-11-28) [umoeller]
861 */
862
863static ULONG EXPENTRY fncbFindCheckRecord(HWND hwndCnr, // in: container
864 PRECORDCORE preccThis, // in: current record (from cnrhForAllRecords)
865 ULONG ulItemID, // in: item ID to find
866 ULONG ulppRecc) // out: PRECORDCORE* if found
867{
868 ULONG ulrc = 0;
869 PCHECKBOXRECORDCORE precc = (PCHECKBOXRECORDCORE)preccThis;
870 if (precc)
871 {
872 if (precc->ulItemID == ulItemID)
873 {
874 // found:
875 PCHECKBOXRECORDCORE *pprecc = (PCHECKBOXRECORDCORE*)ulppRecc;
876 // store
877 *pprecc = precc;
878 // and stop
879 ulrc = 1;
880 }
881 }
882 return (ulrc);
883}
884
885/*
886 *@@ ctlFindCheckRecord:
887 * this searches the given checkbox container
888 * for the record which matches the given item
889 * ID. Returns NULL if not found.
890 *
891 *@@added V0.9.1 (99-12-03) [umoeller]
892 */
893
894PCHECKBOXRECORDCORE ctlFindCheckRecord(HWND hwndCnr,
895 ULONG ulItemID)
896{
897 PCHECKBOXRECORDCORE precc = 0;
898
899 cnrhForAllRecords(hwndCnr,
900 NULL, // start with root
901 fncbFindCheckRecord,
902 (ULONG)ulItemID, // input
903 (ULONG)&precc);
904
905 return (precc);
906}
907
908/*
909 *@@ ctlSetRecordChecked:
910 * this searches the given checkbox container
911 * for the record which matches the given item
912 * ID and updates that record check state.
913 *
914 * The check state may be:
915 * -- 0: not checked (as with regular checkboxes)
916 * -- 1: checked (as with regular checkboxes)
917 * -- 2: indeterminate (as with regular checkboxes)
918 *
919 * Returns FALSE if the record could not be found.
920 *
921 *@@added V0.9.0 (99-11-28) [umoeller]
922 */
923
924BOOL ctlSetRecordChecked(HWND hwndCnr, // in: container prepared with
925 // ctlMakeCheckboxContainer
926 ULONG ulItemID, // in: record item ID
927 USHORT usCheckState) // in: 0, 1, 2, 3
928{
929 BOOL brc = FALSE;
930 PCHECKBOXRECORDCORE precc = ctlFindCheckRecord(hwndCnr, ulItemID);
931
932 if (precc)
933 {
934 precc->usCheckState = usCheckState;
935 WinSendMsg(hwndCnr,
936 CM_INVALIDATERECORD,
937 (MPARAM)&precc,
938 MPFROM2SHORT(1,
939 CMA_NOREPOSITION));
940 brc = TRUE;
941 }
942
943 return (brc);
944}
945
946/*
947 *@@ ctlQueryRecordChecked:
948 * this searches the given checkbox container
949 * for the record which matches the given item
950 * ID and returns that record's check state.
951 *
952 * Returns:
953 * -- 0: not checked (as with regular checkboxes)
954 * -- 1: checked (as with regular checkboxes)
955 * -- 2: indeterminate (as with regular checkboxes)
956 * -- -1: record not found.
957 *
958 *@@added V0.9.0 (99-11-28) [umoeller]
959 */
960
961ULONG ctlQueryRecordChecked(HWND hwndCnr, // in: container prepared with
962 // ctlMakeCheckboxContainer
963 ULONG ulItemID, // in: record item ID
964 USHORT usCheckState) // in: 0, 1, 2, 3
965{
966 ULONG ulrc = -1;
967 PCHECKBOXRECORDCORE precc = ctlFindCheckRecord(hwndCnr, ulItemID);
968
969 if (precc)
970 ulrc = precc->usCheckState;
971
972 return (ulrc);
973}
974
975/*
976 *@@ ctlEnableRecord:
977 * this searches the given checkbox container
978 * for the record which matches the given item
979 * ID and updates that record enablement. If
980 * the record is disabled, it's painted halftoned
981 * by fnwpSubclCheckboxCnr.
982 *
983 *@@added V0.9.1 (99-12-03) [umoeller]
984 */
985
986BOOL ctlEnableRecord(HWND hwndCnr,
987 ULONG ulItemID,
988 BOOL fEnable)
989{
990 BOOL brc = FALSE;
991 PCHECKBOXRECORDCORE precc = ctlFindCheckRecord(hwndCnr, ulItemID);
992
993 if (precc)
994 {
995 ULONG ulAttr = precc->recc.flRecordAttr;
996 if (fEnable)
997 precc->recc.flRecordAttr &= ~CRA_DISABLED;
998 else
999 precc->recc.flRecordAttr |= CRA_DISABLED;
1000
1001 if (precc->recc.flRecordAttr != ulAttr)
1002 // attrs changed: repaint
1003 WinSendMsg(hwndCnr,
1004 CM_INVALIDATERECORD,
1005 (MPARAM)&precc,
1006 MPFROM2SHORT(1,
1007 CMA_NOREPOSITION));
1008
1009 brc = TRUE;
1010 }
1011
1012 return (brc);
1013}
1014
1015
Note: See TracBrowser for help on using the repository browser.