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

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

Sources as of 1.0.1.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 34.2 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-2002 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 *@@changed V1.0.1 (2003-01-22) [umoeller]: fixed wrong mouse pointers on WM_MOUSEMOVE
360 */
361
362STATIC MRESULT EXPENTRY fnwpSubclCheckboxCnr(HWND hwndCnr, ULONG msg, MPARAM mp1, MPARAM mp2)
363{
364 MRESULT mrc = 0;
365 PCHECKBOXCNROWNER pcbco = 0;
366 PFNWP pfnwpOrig = 0;
367
368 if (!DosRequestMutexSem(G_hmtxCnrOwnersList, 5000))
369 {
370 PLISTNODE pNode = lstQueryFirstNode(G_pllCnrOwners);
371 while (pNode)
372 {
373 pcbco = (PCHECKBOXCNROWNER)pNode->pItemData;
374 if (pcbco->hwndCnr == hwndCnr)
375 {
376 pfnwpOrig = pcbco->pfnwpCnrOrig; // fixed V0.9.18 (2002-03-03) [umoeller]
377 break;
378 }
379 pNode = pNode->pNext;
380 }
381 DosReleaseMutexSem(G_hmtxCnrOwnersList);
382 }
383
384 if (pfnwpOrig)
385 {
386 switch (msg)
387 {
388 case WM_BUTTON1DOWN:
389 {
390 POINTL ptlClick;
391 ptlClick.x = SHORT1FROMMP(mp1);
392 ptlClick.y = SHORT2FROMMP(mp1);
393
394 // find record whose icon has this point
395 pcbco->preccClicked
396 = (PCHECKBOXRECORDCORE)cnrhFindRecordFromPoint(hwndCnr,
397 &ptlClick,
398 &pcbco->rclReccClicked,
399 CMA_ICON,
400 0);
401
402 if (pcbco->preccClicked)
403 {
404 if (pcbco->preccClicked->ulStyle & WS_VISIBLE)
405 {
406 // add "depressed" style
407 pcbco->preccClicked->ulStyle |= BS_BITMAP;
408 // mouse was clicked on icon (checkbox):
409 CnrCheckboxClicked(pcbco,
410 pcbco->preccClicked,
411 FALSE);
412 }
413 else
414 // not visible:
415 pcbco->preccClicked = NULL;
416 }
417
418 mrc = pfnwpOrig(hwndCnr, msg, mp1, mp2);
419 // apperently, the cnr captures the mouse
420 }
421 break;
422
423 case WM_MOUSEMOVE:
424 if (pcbco->preccClicked)
425 {
426 // mouse moved while checkbox is depressed:
427 POINTL ptlMove;
428 ptlMove.x = SHORT1FROMMP(mp1);
429 ptlMove.y = SHORT2FROMMP(mp1);
430
431 if (WinPtInRect(pcbco->habCnr,
432 &pcbco->rclReccClicked,
433 &ptlMove))
434 {
435 // mouse is in checkbox:
436 if ((pcbco->preccClicked->ulStyle & BS_BITMAP) == 0)
437 {
438 // checkbox is not drawn depressed:
439 // add "depressed" style
440 pcbco->preccClicked->ulStyle |= BS_BITMAP;
441 CnrCheckboxClicked(pcbco,
442 pcbco->preccClicked,
443 FALSE);
444 }
445 }
446 else
447 {
448 // mouse is outside of checkbox:
449 if (pcbco->preccClicked->ulStyle & BS_BITMAP)
450 {
451 // checkbox is drawn depressed:
452 // remove "depressed" style
453 pcbco->preccClicked->ulStyle &= ~BS_BITMAP;
454 CnrCheckboxClicked(pcbco,
455 pcbco->preccClicked,
456 FALSE);
457 }
458 }
459 }
460
461 // always call parent cos container changes mouse pointers
462 // V1.0.1 (2003-01-22) [umoeller]
463 mrc = pfnwpOrig(hwndCnr, msg, mp1, mp2);
464 break;
465
466 case WM_BUTTON1UP:
467 if (pcbco->preccClicked)
468 {
469 if (pcbco->preccClicked->ulStyle & BS_BITMAP)
470 {
471 pcbco->preccClicked->ulStyle &= ~BS_BITMAP;
472 CnrCheckboxClicked(pcbco,
473 pcbco->preccClicked,
474 TRUE);
475 }
476
477 pcbco->preccClicked = NULL;
478 }
479 mrc = pfnwpOrig(hwndCnr, msg, mp1, mp2);
480 break;
481
482 /*
483 * WM_CHAR:
484 *
485 */
486
487 case WM_CHAR:
488 {
489 USHORT fsFlags = SHORT1FROMMP(mp1);
490 CHAR ch = CHAR1FROMMP(mp2);
491 // never use the USHORT, because for
492 // the "key-up" message, the hi-char
493 // is different (duh...)
494
495 // _Pmpf(("WM_CHAR fsFlags %lX, usch %d", fsFlags, ch));
496
497 if (ch == ' ')
498 {
499 // space: toggle checkbox
500 if ((fsFlags & KC_KEYUP) == 0)
501 {
502 // space down:
503 // filter up repetitive key msgs
504 if (pcbco->preccSpace == NULL)
505 {
506 PCHECKBOXRECORDCORE precc = (PCHECKBOXRECORDCORE)WinSendMsg(
507 hwndCnr,
508 CM_QUERYRECORDEMPHASIS,
509 (MPARAM)CMA_FIRST,
510 (MPARAM)CRA_CURSORED);
511 if ((precc) && (precc != (PCHECKBOXRECORDCORE)-1))
512 {
513 if (precc->ulStyle & WS_VISIBLE)
514 {
515 pcbco->preccSpace = precc;
516
517 // add "depressed" style
518 pcbco->preccSpace->ulStyle |= BS_BITMAP;
519 CnrCheckboxClicked(pcbco,
520 pcbco->preccSpace,
521 FALSE);
522 }
523 }
524 }
525 }
526 else
527 {
528 // space up:
529 if (pcbco->preccSpace)
530 {
531 if (pcbco->preccSpace->ulStyle & BS_BITMAP)
532 {
533 pcbco->preccSpace->ulStyle &= ~BS_BITMAP;
534 CnrCheckboxClicked(pcbco,
535 pcbco->preccSpace,
536 TRUE);
537 }
538
539 pcbco->preccSpace = NULL;
540 }
541 }
542
543 // do not pass spaces on
544 mrc = (MPARAM)TRUE; // processed
545 break;
546 }
547
548 mrc = pfnwpOrig(hwndCnr, msg, mp1, mp2);
549 }
550 break;
551
552 /*
553 * WM_DESTROY:
554 * remove list item; this gets sent
555 * to the parent first, then the children,
556 * so we remove this in the container
557 * (this way the list item is still valid
558 * in ctl_fnwpSubclCheckboxCnrOwner)
559 */
560
561 case WM_DESTROY:
562 if (!DosRequestMutexSem(G_hmtxCnrOwnersList, 5000))
563 {
564 if (WinIsWindow(pcbco->habCnr,
565 pcbco->hwndOwner))
566 // un-subclass the owner
567 WinSubclassWindow(pcbco->hwndOwner,
568 pcbco->pfnwpOwnerOrig);
569
570 lstRemoveItem(G_pllCnrOwners, pcbco);
571
572 if (lstCountItems(G_pllCnrOwners) == 0)
573 lstFree(&G_pllCnrOwners);
574
575 DosReleaseMutexSem(G_hmtxCnrOwnersList);
576 }
577
578 mrc = pfnwpOrig(hwndCnr, msg, mp1, mp2);
579 break;
580
581 default:
582 mrc = pfnwpOrig(hwndCnr, msg, mp1, mp2);
583 }
584 }
585 else
586 mrc = WinDefWindowProc(hwndCnr, msg, mp1, mp2);
587
588 return mrc;
589}
590
591
592/*
593 *@@ fnwpSubclCheckboxCnrOwner:
594 * window proc for subclassed container owners.
595 * See ctlMakeCheckboxContainer for details.
596 *
597 *@@added V0.9.0 (99-11-28) [umoeller]
598 */
599
600STATIC MRESULT EXPENTRY fnwpSubclCheckboxCnrOwner(HWND hwndOwner, ULONG msg, MPARAM mp1, MPARAM mp2)
601{
602 MRESULT mrc = 0;
603 PCHECKBOXCNROWNER pcbco = 0;
604 PFNWP pfnwpOrig = 0;
605
606 if (!DosRequestMutexSem(G_hmtxCnrOwnersList, 5000))
607 {
608 PLISTNODE pNode = lstQueryFirstNode(G_pllCnrOwners);
609 while (pNode)
610 {
611 pcbco = (PCHECKBOXCNROWNER)pNode->pItemData;
612 if (pcbco->hwndOwner == hwndOwner)
613 {
614 pfnwpOrig = pcbco->pfnwpOwnerOrig;
615 break;
616 }
617 pNode = pNode->pNext;
618 }
619 DosReleaseMutexSem(G_hmtxCnrOwnersList);
620 }
621
622 if (pfnwpOrig)
623 {
624 switch (msg)
625 {
626 /*
627 * WM_CONTROL:
628 *
629 */
630
631 case WM_CONTROL:
632 {
633 if (SHORT1FROMMP(mp1) == pcbco->usCnrID)
634 {
635 switch (SHORT2FROMMP(mp1))
636 {
637 case CN_ENTER:
638 {
639 PNOTIFYRECORDENTER pnre = (PNOTIFYRECORDENTER)mp2;
640 PCHECKBOXRECORDCORE precc = (PCHECKBOXRECORDCORE)pnre->pRecord;
641
642 if (precc)
643 CnrCheckboxClicked(pcbco,
644 precc,
645 TRUE);
646 }
647 break;
648
649 default:
650 mrc = pfnwpOrig(hwndOwner, msg, mp1, mp2);
651 }
652 }
653 else
654 mrc = pfnwpOrig(hwndOwner, msg, mp1, mp2);
655 }
656 break;
657
658 /*
659 * WM_DRAWITEM:
660 * cnr owner draw; this is where we draw
661 * the checkboxes
662 */
663
664 case WM_DRAWITEM:
665 {
666 if (SHORT1FROMMP(mp1) == pcbco->usCnrID)
667 mrc = ctlDrawCheckBoxRecord(mp2);
668 else
669 mrc = pfnwpOrig(hwndOwner, msg, mp1, mp2);
670 }
671 break;
672
673 default:
674 mrc = pfnwpOrig(hwndOwner, msg, mp1, mp2);
675 }
676 }
677 else
678 mrc = WinDefWindowProc(hwndOwner, msg, mp1, mp2);
679
680 return mrc;
681}
682
683/*
684 *@@ ctlQueryCheckboxSize:
685 *
686 *@@added V0.9.19 (2002-04-24) [umoeller]
687 */
688
689ULONG ctlQueryCheckboxSize(VOID)
690{
691 if (G_hbmCheckboxes == NULLHANDLE)
692 {
693 // first call:
694 BITMAPINFOHEADER bmih;
695 // load checkboxes bitmap
696 G_hbmCheckboxes = WinGetSysBitmap(HWND_DESKTOP,
697 SBMP_CHECKBOXES);
698
699 // and compute size of one checkbox
700 // (4 columns, 3 rows)
701 bmih.cbFix = sizeof(bmih); // V0.9.19 (2002-04-24) [umoeller]
702 GpiQueryBitmapParameters(G_hbmCheckboxes,
703 &bmih);
704 G_cxCheckbox = bmih.cx / 4;
705 }
706
707 return G_cxCheckbox;
708}
709
710/*
711 *@@ ctlInitCheckboxContainer:
712 *
713 *@@added V0.9.18 (2002-03-03) [umoeller]
714 */
715
716VOID ctlInitCheckboxContainer(HWND hwndCnr)
717{
718 ctlQueryCheckboxSize();
719
720 BEGIN_CNRINFO()
721 {
722 cnrhSetView(CV_TREE | CV_ICON | CA_TREELINE | CA_OWNERDRAW | CV_MINI);
723 cnrhSetTreeIndent(20);
724 cnrhSetBmpOrIconSize(G_cxCheckbox, G_cxCheckbox);
725 } END_CNRINFO(hwndCnr);
726}
727
728/*
729 *@@ ctlSubclassCheckboxContainer:
730 *
731 *@@added V0.9.18 (2002-03-03) [umoeller]
732 */
733
734PCHECKBOXCNROWNER ctlSubclassCheckboxContainer(HWND hwndCnr)
735{
736 PFNWP pfnwpCnrOrig;
737 if (pfnwpCnrOrig = WinSubclassWindow(hwndCnr, fnwpSubclCheckboxCnr))
738 {
739 // cnr successfully subclassed:
740 // create storage for both subclassed cnr and owner
741 PCHECKBOXCNROWNER pcbco;
742 if (pcbco = (PCHECKBOXCNROWNER)malloc(sizeof(CHECKBOXCNROWNER)))
743 {
744 memset(pcbco, 0, sizeof(CHECKBOXCNROWNER));
745 pcbco->hwndCnr = hwndCnr;
746 pcbco->usCnrID = WinQueryWindowUShort(hwndCnr, QWS_ID);
747 pcbco->hwndOwner = WinQueryWindow(hwndCnr, QW_OWNER);
748 pcbco->pfnwpCnrOrig = pfnwpCnrOrig;
749
750 pcbco->habCnr = WinQueryAnchorBlock(hwndCnr);
751
752 return pcbco;
753 }
754 }
755
756 return NULL;
757}
758
759/*
760 *@@ ctlMakeCheckboxContainer:
761 * this turns a regular container into a "checkbox"
762 * container. This means that the container record
763 * icons are painted as checkboxes, whose status
764 * is determined using an extended record core
765 * structure (CHECKBOXRECORDCORE).
766 *
767 * This function assumes that all records in the
768 * container use this special extended record core
769 * ONLY. You cannot use regular RECORDCORE's when
770 * using this function. Of course, you can extend
771 * the CHECKBOXRECORDCORE structure to the bottom
772 * if you need more fields.
773 *
774 * This subclasses BOTH the container and the container
775 * owner to intercept a number of messages and to
776 * implement owner draw. This is totally transparent
777 * to the caller; QWL_USER is not used for this, so
778 * you can still store your own stuff in there.
779 *
780 * Returns FALSE upon errors.
781 *
782 * To set up a checkbox container, call this function
783 * (which switches the container to Tree view automatically).
784 * Then, allocate and insert CHECKBOXRECORDCORE's as usual.
785 *
786 * To set and query a record's check state easily, use
787 * ctlSetRecordChecked and ctlQueryRecordChecked.
788 *
789 * The checkboxes work almost identically to regular
790 * checkboxes. The user can interact with the checkbox
791 * by clicking on the checkbox itself or by double-clicking
792 * on the record text or by pressing the Enter or Space keys.
793 *
794 * Set CHECKBOXRECORDCORE.ulStyle to one of the regular
795 * checkbox button style bits; if one of the "auto" flags
796 * is set, the checkbox is toggled automatically.
797 *
798 * Even if "auto" is off, when the user changes the check
799 * box selection, hwndCnrOwner gets sent a WM_CONTROL message
800 * with the following parameters:
801 *
802 * -- mp1: USHORT id: usCnrID, as passed to this func
803 * -- mp1: USHORT usNotifyCode: CN_RECORDCHECKED (999)
804 * -- mp2: PCHECKRECORDCORE precc: the record which was (un)checked.
805 *
806 * Note that the subclassed container owner proc filters out
807 * CN_ENTER, so you'll never receive that (but CN_RECORDCHECKED
808 * instead).
809 *
810 * If the "auto" style bits have not been set, you must call
811 * ctlSetRecordChecked yourself then.
812 *
813 *@@added V0.9.0 (99-11-28) [umoeller]
814 */
815
816BOOL ctlMakeCheckboxContainer(HWND hwndCnrOwner, // in: owner (and parent) of container
817 USHORT usCnrID) // in: ID of container in hwndCnrOwner
818{
819 BOOL brc = FALSE;
820
821 HWND hwndCnr = WinWindowFromID(hwndCnrOwner, usCnrID);
822
823 // create mutex-protected list of container owners
824 if (G_hmtxCnrOwnersList == 0)
825 if (DosCreateMutexSem(NULL,
826 &G_hmtxCnrOwnersList, 0, FALSE) != NO_ERROR)
827 return (FALSE);
828
829 if (G_pllCnrOwners == NULL)
830 G_pllCnrOwners = lstCreate(TRUE);
831
832 if (hwndCnr)
833 {
834 ctlInitCheckboxContainer(hwndCnr);
835
836 if ( (hwndCnrOwner)
837 && (G_hmtxCnrOwnersList)
838 )
839 {
840 // subclass container owner
841 PFNWP pfnwpOwnerOrig = WinSubclassWindow(hwndCnrOwner, fnwpSubclCheckboxCnrOwner);
842 /* _Pmpf(("Subclassed hwnd 0x%lX: orig 0x%lX",
843 hwndCnrOwner, pfnwpOwnerOrig)); */
844 if (pfnwpOwnerOrig)
845 {
846 // owner successfully subclassed:
847 // subclass container too
848 PCHECKBOXCNROWNER pcbco;
849 if (pcbco = ctlSubclassCheckboxContainer(hwndCnr))
850 {
851 if (!DosRequestMutexSem(G_hmtxCnrOwnersList, 5000))
852 {
853 lstAppendItem(G_pllCnrOwners, pcbco);
854 DosReleaseMutexSem(G_hmtxCnrOwnersList);
855 brc = TRUE;
856
857 pcbco->pfnwpOwnerOrig = pfnwpOwnerOrig;
858 }
859 }
860 }
861 }
862 }
863
864 return brc;
865}
866
867/*
868 *@@ FINDCHECKRECORD:
869 *
870 *@@added V1.0.0 (2002-09-09) [umoeller]
871 */
872
873typedef struct _FINDCHECKRECORD
874{
875 ULONG ulItemID;
876 PCHECKBOXRECORDCORE precFound;
877} FINDCHECKRECORD, *PFINDCHECKRECORD;
878
879/*
880 *@@ fncbFindCheckRecord:
881 * helper callback for finding a checkbox
882 * record according to an item ID. Used
883 * with cnrhForAllRecords.
884 *
885 * Returns 1 if found to make
886 * cnrhForAllRecords stop searching.
887 *
888 *@@added V0.9.0 (99-11-28) [umoeller]
889 *@@changed V1.0.0 (2002-09-09) [umoeller]: adjusted for cnrhForAllRecords updates
890 */
891
892STATIC ULONG XWPENTRY fncbFindCheckRecord(HWND hwndCnr, // in: container
893 PRECORDCORE preccThis, // in: current record (from cnrhForAllRecords)
894 ULONG ulUser)
895{
896 if (((PCHECKBOXRECORDCORE)preccThis)->ulItemID == ((PFINDCHECKRECORD)ulUser)->ulItemID)
897 {
898 // found: store found record
899 ((PFINDCHECKRECORD)ulUser)->precFound = (PCHECKBOXRECORDCORE)preccThis;
900 // and stop
901 return 1;
902 }
903
904 return 0;
905}
906
907/*
908 *@@ ctlFindCheckRecord:
909 * this searches the given checkbox container
910 * for the record which matches the given item
911 * ID. Returns NULL if not found.
912 *
913 *@@added V0.9.1 (99-12-03) [umoeller]
914 *@@changed V1.0.0 (2002-09-09) [umoeller]: adjusted for cnrhForAllRecords updates
915 */
916
917PCHECKBOXRECORDCORE ctlFindCheckRecord(HWND hwndCnr,
918 ULONG ulItemID)
919{
920 FINDCHECKRECORD fcr;
921
922 fcr.ulItemID = ulItemID;
923 fcr.precFound = NULL;
924
925 cnrhForAllRecords(hwndCnr,
926 NULL, // start with root
927 fncbFindCheckRecord,
928 (ULONG)&fcr);
929
930 return fcr.precFound;
931}
932
933/*
934 *@@ ctlSetRecordChecked:
935 * this searches the given checkbox container
936 * for the record which matches the given item
937 * ID and updates that record check state.
938 *
939 * The check state may be:
940 * -- 0: not checked (as with regular checkboxes)
941 * -- 1: checked (as with regular checkboxes)
942 * -- 2: indeterminate (as with regular checkboxes)
943 *
944 * Returns FALSE if the record could not be found.
945 *
946 *@@added V0.9.0 (99-11-28) [umoeller]
947 */
948
949BOOL ctlSetRecordChecked(HWND hwndCnr, // in: container prepared with
950 // ctlMakeCheckboxContainer
951 ULONG ulItemID, // in: record item ID
952 USHORT usCheckState) // in: 0, 1, 2, 3
953{
954 PCHECKBOXRECORDCORE precc;
955 if (precc = ctlFindCheckRecord(hwndCnr, ulItemID))
956 {
957 precc->usCheckState = usCheckState;
958 WinSendMsg(hwndCnr,
959 CM_INVALIDATERECORD,
960 (MPARAM)&precc,
961 MPFROM2SHORT(1,
962 CMA_NOREPOSITION));
963 return TRUE;
964 }
965
966 return FALSE;
967}
968
969/*
970 *@@ ctlQueryRecordChecked:
971 * this searches the given checkbox container
972 * for the record which matches the given item
973 * ID and returns that record's check state.
974 *
975 * Returns:
976 * -- 0: not checked (as with regular checkboxes)
977 * -- 1: checked (as with regular checkboxes)
978 * -- 2: indeterminate (as with regular checkboxes)
979 * -- -1: record not found.
980 *
981 *@@added V0.9.0 (99-11-28) [umoeller]
982 */
983
984ULONG ctlQueryRecordChecked(HWND hwndCnr, // in: container prepared with
985 // ctlMakeCheckboxContainer
986 ULONG ulItemID, // in: record item ID
987 USHORT usCheckState) // in: 0, 1, 2, 3
988{
989 PCHECKBOXRECORDCORE precc;
990 if (precc = ctlFindCheckRecord(hwndCnr, ulItemID))
991 return precc->usCheckState;
992
993 return -1;
994}
995
996/*
997 *@@ ctlEnableRecord:
998 * this searches the given checkbox container
999 * for the record which matches the given item
1000 * ID and updates that record enablement. If
1001 * the record is disabled, it's painted halftoned
1002 * by fnwpSubclCheckboxCnr.
1003 *
1004 *@@added V0.9.1 (99-12-03) [umoeller]
1005 */
1006
1007BOOL ctlEnableRecord(HWND hwndCnr,
1008 ULONG ulItemID,
1009 BOOL fEnable)
1010{
1011 PCHECKBOXRECORDCORE precc;
1012
1013 if (precc = ctlFindCheckRecord(hwndCnr, ulItemID))
1014 {
1015 ULONG ulAttr = precc->recc.flRecordAttr;
1016 if (fEnable)
1017 precc->recc.flRecordAttr &= ~CRA_DISABLED;
1018 else
1019 precc->recc.flRecordAttr |= CRA_DISABLED;
1020
1021 if (precc->recc.flRecordAttr != ulAttr)
1022 // attrs changed: repaint
1023 WinSendMsg(hwndCnr,
1024 CM_INVALIDATERECORD,
1025 (MPARAM)&precc,
1026 MPFROM2SHORT(1,
1027 CMA_NOREPOSITION));
1028
1029 return TRUE;
1030 }
1031
1032 return FALSE;
1033}
1034
1035
Note: See TracBrowser for help on using the repository browser.