source: trunk/src/helpers/cctl_cnr.c@ 243

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

New build system, multimedia, other misc fixes.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 67.0 KB
Line 
1
2/*
3 *@@sourcefile cctl_cnr.c:
4 * implementation for the replacement container control.
5 *
6 *@@header "helpers\comctl.h"
7 *@@added V1.0.1 (2003-01-17) [umoeller]
8 */
9
10/*
11 * Copyright (C) 2003 Ulrich M”ller.
12 * This file is part of the "XWorkplace helpers" source package.
13 * This is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published
15 * by the Free Software Foundation, in version 2 as it comes in the
16 * "COPYING" file of the XWorkplace main distribution.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 */
22
23#define OS2EMX_PLAIN_CHAR
24 // this is needed for "os2emx.h"; if this is defined,
25 // emx will define PSZ as _signed_ char, otherwise
26 // as unsigned char
27
28#define INCL_WINWINDOWMGR
29#define INCL_WINFRAMEMGR
30#define INCL_WININPUT
31#define INCL_WINSYS
32#define INCL_WINSCROLLBARS
33#define INCL_WINSTDCNR
34
35#define INCL_GPIPRIMITIVES
36#define INCL_GPILCIDS
37#include <os2.h>
38
39#include "setup.h" // code generation and debugging options
40
41#include "helpers\comctl.h"
42#include "helpers\linklist.h"
43#include "helpers\gpih.h"
44#include "helpers\nls.h"
45#include "helpers\standards.h"
46#include "helpers\stringh.h"
47#include "helpers\tree.h"
48#include "helpers\winh.h"
49
50#include "private\cnr.h"
51
52#pragma hdrstop
53
54/*
55 *@@category: Helpers\PM helpers\Window classes\Container control replacement
56 * See cctl_cnr.c.
57 */
58
59/* ******************************************************************
60 *
61 * Global variables
62 *
63 ********************************************************************/
64
65extern const CCTLCOLOR G_scsCnr[] =
66 {
67 TRUE, PP_BACKGROUNDCOLOR, SYSCLR_WINDOW,
68 TRUE, PP_FOREGROUNDCOLOR, SYSCLR_WINDOWTEXT,
69 TRUE, PP_HILITEBACKGROUNDCOLOR, SYSCLR_HILITEBACKGROUND,
70 TRUE, PP_HILITEFOREGROUNDCOLOR, SYSCLR_HILITEFOREGROUND,
71 TRUE, PP_BORDERCOLOR, SYSCLR_WINDOWFRAME,
72 TRUE, PP_PAGEBACKGROUNDCOLOR, RGBCOL_WHITE,
73 TRUE, PP_PAGEFOREGROUNDCOLOR, RGBCOL_BLACK,
74 TRUE, PP_FIELDBACKGROUNDCOLOR, SYSCLR_SCROLLBAR
75 };
76
77/* ******************************************************************
78 *
79 * Helper funcs
80 *
81 ********************************************************************/
82
83/*
84 *@@ ctnrInit:
85 *
86 */
87
88VOID ctnrInit(HWND hwnd,
89 MPARAM mp2,
90 ULONG flMainCnrStyle, // in: window style bits of main cnr
91 PDEFWINDATA pdwd)
92{
93 ctlInitDWD(hwnd,
94 mp2,
95 pdwd,
96 WinDefWindowProc,
97 (flMainCnrStyle & CCS_NOCONTROLPTR)
98 ? CCS_NOSENDCTLPTR
99 : 0,
100 G_scsCnr,
101 ARRAYITEMCOUNT(G_scsCnr));
102}
103
104/*
105 *@@ ctnrDrawString:
106 *
107 */
108
109VOID ctnrDrawString(HPS hps,
110 PCSZ pcsz, // in: string to test
111 PRECTL prcl, // in: clipping rectangle (inclusive!)
112 ULONG fl, // in: alignment flags
113 PFONTMETRICS pfm)
114{
115 ULONG fl2 = 0;
116/*
117 #define CFA_LEFT 0x00000001L
118 #define CFA_RIGHT 0x00000002L
119 #define CFA_CENTER 0x00000004L
120 #define CFA_TOP 0x00000008L
121 #define CFA_VCENTER 0x00000010L
122 #define CFA_BOTTOM 0x00000020L
123
124 * -- DT_LEFT 0x00000000
125 * -- DT_CENTER 0x00000100
126 * -- DT_RIGHT 0x00000200
127 * -- DT_TOP 0x00000000
128 * -- DT_VCENTER 0x00000400
129 * -- DT_BOTTOM 0x00000800
130*/
131 if (fl & CFA_RIGHT)
132 fl2 = DT_RIGHT;
133 else if (fl & CFA_CENTER)
134 fl2 = DT_CENTER;
135
136 if (fl & CFA_BOTTOM)
137 fl2 = DT_BOTTOM;
138 else if (fl & CFA_VCENTER)
139 fl2 = DT_VCENTER;
140
141 gpihDrawString(hps,
142 pcsz,
143 prcl,
144 fl2,
145 pfm);
146}
147
148/*
149 *@@ ctnrGetRecordRect:
150 * returns the rectangle of the given record
151 * converted to window coordinates. Works for
152 * all views.
153 */
154
155VOID ctnrGetRecordRect(PCNRDATA pData,
156 PRECTL prcl,
157 const RECORDLISTITEM *prli)
158{
159 LONG deltaWorkspace = ( pData->scrw.szlWorkarea.cy
160 - pData->dwdContent.szlWin.cy)
161 - pData->scrw.ptlScrollOfs.y;
162 prcl->xLeft = 0;
163 prcl->xRight = pData->dwdContent.szlWin.cx;
164 prcl->yBottom = prli->ptl.y - deltaWorkspace;
165 prcl->yTop = prcl->yBottom + prli->szlBox.cy;
166}
167
168/*
169 *@@ ctnrRepaintRecord:
170 *
171 */
172
173BOOL ctnrRepaintRecord(PCNRDATA pData,
174 const RECORDLISTITEM *prli)
175{
176 RECTL rcl;
177 ctnrGetRecordRect(pData, &rcl, prli);
178 ++rcl.xRight; // for separators, if any
179 return WinInvalidateRect(pData->dwdContent.hwnd, &rcl, FALSE);
180}
181
182/*
183 *@@ ctnrQuerySelMode:
184 * returns one of CCS_EXTENDSEL, CCS_MULTIPLESEL, or
185 * CCS_SINGLESEL, depending on the cnr's current view
186 * and window style bits.
187 */
188
189ULONG ctnrQuerySelMode(PCNRDATA pData)
190{
191 // in tree view, there is always only single-sel mode
192 if (!(pData->CnrInfo.flWindowAttr & CV_TREE))
193 {
194 // not tree view: then check window style bits
195 ULONG flStyle = winhQueryWindowStyle(pData->dwdMain.hwnd);
196 if (flStyle & CCS_EXTENDSEL)
197 return CCS_EXTENDSEL;
198 else if (flStyle & CCS_MULTIPLESEL)
199 return CCS_MULTIPLESEL;
200
201 // this appears to be what the pm cnr does...
202 // the CCS_SINGLESEL is totally superfluous cos
203 // if none of the "selection style" bits are
204 // set, the cnr operates in "single sel" mode
205 }
206
207 return CCS_SINGLESEL;
208}
209
210/*
211 *@@ ctnrChangeEmphasis:
212 *
213 */
214
215BOOL ctnrChangeEmphasis(PCNRDATA pData,
216 PRECORDLISTITEM prliSet,
217 BOOL fTurnOn,
218 ULONG fsEmphasis)
219{
220 PRECORDLISTITEM prliOld;
221 ULONG flRecordAttrOld = prliSet->flRecordAttr;
222
223/* #define CCS_EXTENDSEL 0x00000001L
224#define CCS_MULTIPLESEL 0x00000002L
225#define CCS_SINGLESEL 0x00000004L */
226
227 ULONG ulSel = ctnrQuerySelMode(pData);
228
229 if (fTurnOn)
230 {
231 ULONG flMask;
232 if (ulSel == CCS_SINGLESEL)
233 flMask = CRA_CURSORED | CRA_SELECTED;
234 else
235 flMask = CRA_CURSORED;
236
237 if (fsEmphasis & flMask)
238 {
239 if (prliOld = pData->prliCursored)
240 {
241 prliOld->flRecordAttr &= ~flMask;
242 ctnrRepaintRecord(pData, prliOld);
243 }
244
245 pData->prliCursored = prliSet;
246 }
247
248 prliSet->flRecordAttr |= fsEmphasis;
249 }
250 else
251 prliSet->flRecordAttr &= ~fsEmphasis;
252
253 if (flRecordAttrOld != prliSet->flRecordAttr)
254 ctnrRepaintRecord(pData, prliSet);
255
256 return TRUE;
257}
258
259/*
260 *@@ ctnrRecordEnter:
261 * helper function when a record gets double-clicked
262 * upon or when "Enter" key gets pressed.
263 */
264
265VOID ctnrRecordEnter(PCNRDATA pData,
266 const RECORDLISTITEM *prli,
267 BOOL fKeyboard)
268{
269 NOTIFYRECORDENTER nre;
270 nre.hwndCnr = pData->dwdMain.hwnd;
271 nre.fKey = fKeyboard;
272 nre.pRecord = (prli) ? (PRECORDCORE)prli->precc : NULL;
273
274 ctlSendWmControl(nre.hwndCnr,
275 CN_ENTER,
276 &nre);
277}
278
279/*
280 *@@ CreateChild:
281 * creates a container child window.
282 */
283
284STATIC HWND CreateChild(PCNRDATA pData,
285 PCSZ pcszClass,
286 ULONG id)
287{
288 return WinCreateWindow(pData->dwdMain.hwnd,
289 (PSZ)pcszClass,
290 NULL,
291 WS_VISIBLE,
292 0,
293 0,
294 0,
295 0,
296 pData->dwdMain.hwnd,
297 HWND_TOP,
298 id,
299 pData,
300 NULL);
301}
302
303/*
304 *@@ FindColumnFromFI:
305 *
306 */
307
308STATIC PDETAILCOLUMN FindColumnFromFI(PCNRDATA pData,
309 const FIELDINFO *pfi,
310 PLISTNODE *ppNode) // out: listnode of column
311{
312 PLISTNODE pNode;
313 FOR_ALL_NODES(&pData->llColumns, pNode)
314 {
315 PDETAILCOLUMN pCol = (PDETAILCOLUMN)pNode->pItemData;
316 if (pCol->pfi == pfi)
317 {
318 if (ppNode)
319 *ppNode = pNode;
320
321 return pCol;
322 }
323 }
324
325 return NULL;
326}
327
328/*
329 *@@ ctnrFindListNodeForRecc:
330 *
331 */
332
333PLISTNODE ctnrFindListNodeForRecc(PCNRDATA pData,
334 const RECORDCORE *precc)
335{
336 RECORDTREEITEM *pti;
337 if (pti = (PRECORDTREEITEM)treeFind(pData->RecordsTree,
338 (ULONG)precc,
339 treeCompareKeys))
340 return pti->pListNode;
341
342 return NULL;
343}
344
345VOID SendViewportChanged(PCNRDATA pData)
346{
347 CNRVIEWPORT cvp;
348 cvp.hwndCnr = pData->dwdMain.hwnd;
349 cvp.szlWorkarea = pData->scrw.szlWorkarea;
350 cvp.szlWin = pData->dwdContent.szlWin;
351 cvp.ptlScroll = pData->scrw.ptlScrollOfs;
352
353 ctlSendWmControl(pData->dwdMain.hwnd,
354 CN_VIEWPORTCHANGED,
355 &cvp);
356}
357
358/*
359 *@@ ctnrUpdateScrollbars:
360 *
361 */
362
363VOID ctnrUpdateScrollbars(PCNRDATA pData,
364 LONG cx,
365 LONG cy)
366{
367 BOOL fNotify = FALSE;
368
369 if (cx && pData->scrw.hwndHScroll)
370 {
371 winhUpdateScrollBar(pData->scrw.hwndHScroll,
372 cx,
373 pData->scrw.szlWorkarea.cx,
374 pData->scrw.ptlScrollOfs.x,
375 FALSE);
376 fNotify = TRUE;
377 }
378
379 if (cy && pData->scrw.hwndVScroll)
380 {
381 winhUpdateScrollBar(pData->scrw.hwndVScroll,
382 cy,
383 pData->scrw.szlWorkarea.cy,
384 pData->scrw.ptlScrollOfs.y,
385 FALSE);
386 fNotify = TRUE;
387 }
388
389 if (fNotify)
390 SendViewportChanged(pData);
391}
392
393/* ******************************************************************
394 *
395 * Standard window messages
396 *
397 ********************************************************************/
398
399/*
400 *@@ CnrCreate:
401 * implementation for WM_CREATE in fnwpCnr.
402 */
403
404STATIC MRESULT CnrCreate(HWND hwnd,
405 MPARAM mp1,
406 MPARAM mp2)
407{
408 PCNRDATA pData;
409
410 if (!(pData = NEW(CNRDATA)))
411 return (MRESULT)TRUE; // stop window creation
412
413 WinSetWindowPtr(hwnd, QWL_USER + 1, pData);
414 ZERO(pData);
415
416 // initialize DEFWINDOWDATA
417 ctnrInit(hwnd,
418 mp2,
419 ((PCREATESTRUCT)mp2)->flStyle,
420 &pData->dwdMain);
421
422 if (((PCREATESTRUCT)mp2)->flStyle & CCS_MINIRECORDCORE)
423 pData->fMiniRecords = TRUE;
424
425 // set up non-zero default values in cnrinfo
426 pData->CnrInfo.cb = sizeof(CNRINFO);
427 pData->CnrInfo.xVertSplitbar = -1;
428
429 lstInit(&pData->llAllocatedFIs,
430 TRUE);
431 lstInit(&pData->llColumns,
432 TRUE);
433 lstInit(&pData->llAllocatedRecs,
434 TRUE);
435 lstInit(&pData->llRootRecords,
436 TRUE);
437
438 treeInit(&pData->RecordsTree,
439 (PLONG)&pData->CnrInfo.cRecords);
440
441 nlsQueryCountrySettings(&pData->cs);
442
443 winhCreateScroller(hwnd,
444 &pData->scrw,
445 CID_VSCROLL,
446 CID_HSCROLL);
447
448 return (MRESULT)FALSE;
449}
450
451/*
452 *@@ CnrSem2:
453 * implementation for WM_SEM2 in fnwpCnr.
454 *
455 * The DDFL_* semaphore bits get set whenever the
456 * view needs to recompute something. In the worst
457 * case, we resize and/or invalidate the window.
458 */
459
460STATIC VOID CnrSem2(PCNRDATA pData,
461 ULONG fl)
462{
463 HWND hwnd = pData->dwdMain.hwnd;
464
465 if (pData->CnrInfo.flWindowAttr & CV_DETAIL)
466 {
467 if (fl & (DDFL_INVALIDATECOLUMNS | DDFL_INVALIDATERECORDS | DDFL_INVALIDATESOME))
468 {
469 HPS hps = WinGetPS(hwnd);
470 cdtlRecalcDetails(pData, hps, &fl);
471 WinReleasePS(hps);
472 }
473
474 if (fl & (DDFL_WINDOWSIZECHANGED | DDFL_WORKAREACHANGED))
475 {
476 LONG y = 0,
477 cx = pData->dwdMain.szlWin.cx,
478 cy = pData->dwdMain.szlWin.cy - pData->cyColTitlesBox;
479
480 if (pData->scrw.hwndVScroll)
481 cx -= pData->scrw.cxScrollBar;
482 if (pData->scrw.hwndHScroll)
483 {
484 y += pData->scrw.cyScrollBar;
485 cy -= pData->scrw.cyScrollBar;
486 }
487
488 _PmpfF(("cyColTitlesBox %d, new cy: %d", pData->cyColTitlesBox, cy));
489
490 if (fl & DDFL_WINDOWSIZECHANGED)
491 WinSetWindowPos(pData->dwdContent.hwnd,
492 HWND_TOP,
493 0,
494 y,
495 cx,
496 cy,
497 SWP_MOVE | SWP_SIZE);
498 // SWP_MOVE is required or PM will move our
499 // subwindow to some adjustment position
500
501 if (pData->scrw.hwndVScroll)
502 {
503 WinSetWindowPos(pData->scrw.hwndVScroll,
504 HWND_TOP,
505 cx,
506 y,
507 pData->scrw.cxScrollBar,
508 cy,
509 SWP_MOVE | SWP_SIZE);
510
511 _PmpfF(("updating VScroll, cy: %d, scrw.szlWorkarea.cy: %d",
512 cy,
513 pData->scrw.szlWorkarea.cy));
514 }
515
516 if (pData->scrw.hwndHScroll)
517 {
518 WinSetWindowPos(pData->scrw.hwndHScroll,
519 HWND_TOP,
520 0,
521 0,
522 cx,
523 pData->scrw.cyScrollBar,
524 SWP_MOVE | SWP_SIZE);
525
526 _PmpfF(("updating HScroll, cx: %d, scrw.szlWorkarea.cx: %d",
527 cx,
528 pData->scrw.szlWorkarea.cx));
529 }
530
531 ctnrUpdateScrollbars(pData,
532 cx,
533 cy);
534 }
535 }
536}
537
538/*
539 *@@ CnrPaint:
540 * implementation for WM_PAINT in fnwpCnr.
541 *
542 * This paints only the part of the container that belongs
543 * to the main container window, which is the title area
544 * and/or the details column headings.
545 */
546
547STATIC VOID CnrPaint(PCNRDATA pData)
548{
549 HPS hps;
550 RECTL rclPaint;
551
552 if ( (pData)
553 && (hps = WinBeginPaint(pData->dwdMain.hwnd, NULLHANDLE, &rclPaint))
554 )
555 {
556 gpihSwitchToRGB(hps);
557
558 if ( (pData->CnrInfo.flWindowAttr & (CV_DETAIL | CA_DETAILSVIEWTITLES))
559 == (CV_DETAIL | CA_DETAILSVIEWTITLES)
560 )
561 {
562 PLISTNODE pColNode;
563
564 POINTL ptl;
565 RECTL rcl;
566 LONG yPadding = pData->CnrInfo.cyLineSpacing / 2;
567
568 // lowest y that we are allowed to paint at
569 LONG yLowest = pData->dwdMain.szlWin.cy - pData->cyColTitlesBox;
570
571 if (rclPaint.yTop > yLowest)
572 {
573 // clip paint rect so we don't paint into details wnd
574 if (rclPaint.yBottom < yLowest)
575 rclPaint.yBottom = yLowest;
576
577 WinFillRect(hps,
578 &rclPaint,
579 ctlQueryColor(&pData->dwdMain, CTLCOL_BGND));
580
581 FOR_ALL_NODES(&pData->llColumns, pColNode)
582 {
583 RECTL rcl2;
584 PDETAILCOLUMN pCol = (PDETAILCOLUMN)pColNode->pItemData;
585 const FIELDINFO *pfi = pCol->pfi;
586 PLISTNODE pRecNode;
587 ULONG cRow;
588
589 rcl.xLeft = pCol->xLeft + COLUMN_PADDING_X - pData->scrw.ptlScrollOfs.x;
590 rcl.xRight = rcl.xLeft + pCol->cxContent;
591
592 // we start out at the top and work our way down,
593 // decrementing rcl.yTop with every item we painted
594 rcl.yTop = pData->dwdMain.szlWin.cy;
595
596 rcl.yTop -= COLUMN_PADDING_Y + yPadding;
597 rcl.yBottom = rcl.yTop
598 - pData->cyColTitlesContent;
599
600 if (pfi->flTitle & CFA_BITMAPORICON)
601 // @@todo
602 ;
603 else
604 {
605 ctnrDrawString(hps,
606 (PCSZ)pfi->pTitleData,
607 &rcl,
608 pfi->flTitle,
609 &pData->fm);
610 }
611
612 rcl.yBottom -= COLUMN_PADDING_Y + yPadding;
613
614 if (pfi->flData & CFA_HORZSEPARATOR)
615 {
616 ptl.x = rcl.xLeft;
617 ptl.y = rcl.yBottom;
618
619 GpiMove(hps,
620 &ptl);
621 ptl.x = rcl.xRight;
622 GpiLine(hps,
623 &ptl);
624 }
625
626 rcl.yTop = rcl.yBottom;
627
628 } // FOR_ALL_NODES(&pData->llColumns, pColNode)
629 }
630 }
631
632 WinEndPaint(hps);
633 }
634}
635
636/*
637 *@@ CnrWindowPosChanged:
638 * implementation for WM_WINDOWPOSCHANGED in fnwpCnr.
639 */
640
641STATIC MRESULT CnrWindowPosChanged(PCNRDATA pData,
642 MPARAM mp1,
643 MPARAM mp2)
644{
645 MRESULT mrc = 0;
646
647 if (pData)
648 {
649 mrc = ctlDefWindowProc(&pData->dwdMain, WM_WINDOWPOSCHANGED, mp1, mp2);
650
651 if (((PSWP)mp1)->fl & SWP_SIZE)
652 {
653 WinPostMsg(pData->dwdMain.hwnd,
654 WM_SEM2,
655 (MPARAM)DDFL_WINDOWSIZECHANGED,
656 0);
657 }
658 }
659
660 return mrc;
661}
662
663/*
664 *@@ ctnrPresParamChanged:
665 * implementation for WM_PRESPARAMCHANGED for both
666 * fnwpCnr and fnwpCnrDetails.
667 */
668
669VOID ctnrPresParamChanged(HWND hwnd, // in: either main cnr or content subwindow
670 ULONG ulpp)
671{
672 PCNRDATA pData;
673 if (pData = (PCNRDATA)WinQueryWindowPtr(hwnd, QWL_USER + 1))
674 {
675 // note, no matter what hwnd is passed in,
676 // we use the dwdMain buffer here cos we share presparams
677 // between all cnr child windows
678 ctlRefreshColors(&pData->dwdMain);
679
680 switch (ulpp)
681 {
682 case 0: // layout palette thing dropped
683 case PP_FONTNAMESIZE:
684 if (!pData->fSettingPP)
685 {
686 pData->fSettingPP = TRUE;
687
688 if (pData->CnrInfo.flWindowAttr & CV_DETAIL)
689 {
690 // if we got this on the contents window,
691 // set it on the main cnr as well, and
692 // vice versa
693 PSZ pszFont;
694 if (pszFont = winhQueryWindowFont(hwnd))
695 {
696 HWND hwndOther;
697 if (hwnd == pData->dwdMain.hwnd)
698 hwndOther = pData->dwdContent.hwnd;
699 else
700 hwndOther = pData->dwdMain.hwnd;
701
702 winhSetWindowFont(hwndOther, pszFont);
703 free(pszFont);
704 }
705
706 WinPostMsg(pData->dwdMain.hwnd,
707 WM_SEM2,
708 (MPARAM)(DDFL_INVALIDATECOLUMNS | DDFL_INVALIDATERECORDS),
709 0);
710 }
711
712 pData->fSettingPP = FALSE;
713 }
714 break;
715
716 default:
717 // just repaint everything
718 WinInvalidateRect(pData->dwdMain.hwnd, NULL, TRUE);
719 }
720 }
721}
722
723/*
724 *@@ CnrScroll:
725 * implementation for WM_HSCROLL and WM_VSCROLL in fnwpCnr.
726 */
727
728STATIC VOID CnrScroll(PCNRDATA pData,
729 ULONG msg,
730 MPARAM mp1,
731 MPARAM mp2)
732{
733 if (pData)
734 {
735 BOOL fNotify = FALSE;
736
737 if (pData->CnrInfo.flWindowAttr & CV_DETAIL)
738 {
739 POINTL ptlScroll;
740 NOTIFYSCROLL ns;
741 ns.hwndCnr = pData->dwdMain.hwnd;
742
743 if (msg == WM_HSCROLL)
744 {
745 ptlScroll.y = 0;
746 if (ptlScroll.x = winhHandleScrollMsg(pData->scrw.hwndHScroll,
747 &pData->scrw.ptlScrollOfs.x,
748 pData->dwdContent.szlWin.cx,
749 pData->scrw.szlWorkarea.cx,
750 5,
751 msg,
752 mp2))
753 {
754 winhScrollWindow(pData->dwdContent.hwnd,
755 NULL,
756 &ptlScroll);
757
758 // scroll main cnr with detail titles too
759 if (pData->CnrInfo.flWindowAttr & CA_DETAILSVIEWTITLES)
760 {
761 RECTL rclClip;
762 rclClip.xLeft = 0;
763 rclClip.xRight = pData->dwdMain.szlWin.cx;
764 rclClip.yBottom = pData->dwdMain.szlWin.cy - pData->cyColTitlesBox;
765 rclClip.yTop = pData->dwdMain.szlWin.cy;
766 winhScrollWindow(pData->dwdMain.hwnd,
767 &rclClip,
768 &ptlScroll);
769 }
770
771 ns.lScrollInc = ptlScroll.y;
772 ns.fScroll = CMA_HORIZONTAL; // @@todo details flags
773 ctlSendWmControl(pData->dwdMain.hwnd,
774 CN_SCROLL,
775 &ns);
776
777 fNotify = TRUE;
778 }
779 }
780 else
781 {
782 ptlScroll.x = 0;
783 if (ptlScroll.y = winhHandleScrollMsg(pData->scrw.hwndVScroll,
784 &pData->scrw.ptlScrollOfs.y,
785 pData->dwdContent.szlWin.cy,
786 pData->scrw.szlWorkarea.cy,
787 5,
788 msg,
789 mp2))
790 {
791 winhScrollWindow(pData->dwdContent.hwnd,
792 NULL,
793 &ptlScroll);
794
795 ns.lScrollInc = ptlScroll.x;
796 ns.fScroll = CMA_VERTICAL; // @@todo details flags
797 ctlSendWmControl(pData->dwdMain.hwnd,
798 CN_SCROLL,
799 &ns);
800
801 fNotify = TRUE;
802 }
803 }
804 }
805
806 if (fNotify)
807 SendViewportChanged(pData);
808 }
809}
810
811/*
812 *@@ CnrDestroy:
813 * implementation for WM_DESTROY in fnwpCnr.
814 */
815
816STATIC VOID CnrDestroy(PCNRDATA pData)
817{
818 if (pData)
819 {
820 // free all data that we ever allocated;
821 // all these lists are auto-free
822 lstClear(&pData->llColumns);
823 lstClear(&pData->llAllocatedFIs);
824 lstClear(&pData->llAllocatedRecs);
825 lstClear(&pData->llRootRecords);
826
827 free(pData);
828 }
829}
830
831/* ******************************************************************
832 *
833 * General container messages
834 *
835 ********************************************************************/
836
837/*
838 *@@ CnrQueryCnrInfo:
839 * implementation for CM_QUERYCNRINFO in fnwpCnr.
840 *
841 * Returns no. of bytes copied.
842 */
843
844STATIC USHORT CnrQueryCnrInfo(PCNRDATA pData,
845 PCNRINFO pci, // out: target buffer (mp1 of CM_QUERYCNRINFO)
846 USHORT cb) // in: mp2 of CM_QUERYCNRINFO
847{
848 if (pData)
849 {
850 USHORT cbCopied = max(cb, sizeof(CNRINFO));
851 memcpy(pci,
852 &pData->CnrInfo,
853 cbCopied);
854 return cbCopied;
855 }
856
857 return 0;
858}
859
860/*
861 *@@ CnrSetCnrInfo:
862 * implementation for CM_SETCNRINFO in fnwpCnr.
863 *
864 * Returns no. of bytes copied.
865 */
866
867STATIC BOOL CnrSetCnrInfo(PCNRDATA pData,
868 PCNRINFO pci, // in: mp1 of CM_SETCNRINFO
869 ULONG flCI) // in: CMA_* flags in mp2 of CM_SETCNRINFO
870{
871 if (pData)
872 {
873 ULONG flDirty = 0;
874
875 if (flCI & CMA_PSORTRECORD)
876 /* Pointer to the comparison function for sorting container records. If NULL,
877 which is the default condition, no sorting is performed. Sorting only occurs
878 during record insertion and when changing the value of this field. The third
879 parameter of the comparison function, pStorage, must be NULL. See
880 CM_SORTRECORD for a further description of the comparison function. */
881 pData->CnrInfo.pSortRecord = pci->pSortRecord;
882
883 if (flCI & CMA_PFIELDINFOLAST)
884 /* Pointer to the last column in the left window of the split details view. The
885 default is NULL, causing all columns to be positioned in the left window. */
886 pData->CnrInfo.pFieldInfoLast = pci->pFieldInfoLast;
887
888 if (flCI & CMA_PFIELDINFOOBJECT)
889 /* Pointer to a column that represents an object in the details view. This
890 FIELDINFO structure must contain icons or bit maps. In-use emphasis is applied
891 to this column of icons or bit maps only. The default is the leftmost column
892 in the unsplit details view, or the leftmost column in the left window of the
893 split details view. */
894 pData->CnrInfo.pFieldInfoObject = pci->pFieldInfoObject;
895
896 if (flCI & CMA_CNRTITLE)
897 {
898 // Text for the container title. The default is NULL.
899 pData->CnrInfo.pszCnrTitle = pci->pszCnrTitle;
900
901 // @@todo recalc window components, repaint
902 }
903
904 if (flCI & CMA_FLWINDOWATTR)
905 {
906 // Container window attributes.
907 if (pData->CnrInfo.flWindowAttr != pci->flWindowAttr)
908 {
909 HWND hwndSwitchOwner = NULLHANDLE;
910
911 pData->CnrInfo.flWindowAttr = pci->flWindowAttr;
912
913 // if switching to details view, then create
914 // details subwindow
915 if (pData->CnrInfo.flWindowAttr & CV_DETAIL)
916 {
917 if (!pData->dwdContent.hwnd)
918 {
919 if (pData->dwdContent.hwnd = CreateChild(pData,
920 WC_CCTL_CNR_DETAILS,
921 CID_LEFTDVWND))
922 {
923 flDirty = DDFL_ALL;
924 }
925 }
926 }
927 else
928 {
929 winhDestroyWindow(&pData->dwdContent.hwnd);
930 }
931 }
932 }
933
934 if (flCI & CMA_PTLORIGIN)
935 {
936 // Lower-left origin of the container window in virtual workspace coordinates,
937 // used in the icon view. The default origin is (0,0).
938 memcpy(&pData->CnrInfo.ptlOrigin,
939 &pci->ptlOrigin,
940 sizeof(POINTL));
941
942 // @@todo recalc window components, repaint
943 }
944
945 if (flCI & CMA_DELTA)
946 {
947 // An application-defined threshold, or number of records, from either end of
948 // the list of available records. Used when a container needs to handle large
949 // amounts of data. The default is 0. Refer to the description of the container
950 // control in the OS/2 Programming Guide for more information about specifying
951 // deltas.
952 pData->CnrInfo.cDelta = pci->cDelta;
953 }
954
955 if (flCI & CMA_SLBITMAPORICON)
956 {
957 // The size (in pels) of icons or bit maps. The default is the system size.
958 memcpy(&pData->CnrInfo.slBitmapOrIcon,
959 &pci->slBitmapOrIcon,
960 sizeof(SIZEL));
961
962 // @@todo recalc window components, repaint
963 }
964
965 if (flCI & CMA_SLTREEBITMAPORICON)
966 {
967 // The size (in pels) of the expanded and collapsed icons or bit maps in the
968 // tree icon and tree text views.
969 memcpy(&pData->CnrInfo.slTreeBitmapOrIcon,
970 &pci->slTreeBitmapOrIcon,
971 sizeof(SIZEL));
972
973 // @@todo recalc window components, repaint
974 }
975
976 if (flCI & CMA_TREEBITMAP)
977 {
978 // Expanded and collapsed bit maps in the tree icon and tree text views.
979 pData->CnrInfo.hbmExpanded = pci->hbmExpanded;
980 pData->CnrInfo.hbmCollapsed = pci->hbmCollapsed;
981
982 // @@todo recalc window components, repaint
983 }
984
985 if (flCI & CMA_TREEICON)
986 {
987 // Expanded and collapsed icons in the tree icon and tree text views.
988 pData->CnrInfo.hptrExpanded = pci->hptrExpanded;
989 pData->CnrInfo.hptrCollapsed = pci->hptrCollapsed;
990
991 // @@todo recalc window components, repaint
992 }
993
994 if (flCI & CMA_LINESPACING)
995 {
996 // The amount of vertical space (in pels) between the records. If this value is
997 // less than 0, a default value is used.
998 pData->CnrInfo.cyLineSpacing = pci->cyLineSpacing;
999
1000 // @@todo recalc window components, repaint
1001 }
1002
1003 if (flCI & CMA_CXTREEINDENT)
1004 {
1005 // Horizontal distance (in pels) between levels in the tree view. If this value is
1006 // less than 0, a default value is used.
1007 pData->CnrInfo.cxTreeIndent = pci->cxTreeIndent;
1008
1009 // @@todo recalc window components, repaint
1010 }
1011
1012 if (flCI & CMA_CXTREELINE)
1013 {
1014 // Width of the lines (in pels) that show the relationship between items in the
1015 // tree view. If this value is less than 0, a default value is used. Also, if the
1016 // CA_TREELINE container attribute of the CNRINFO data structure's
1017 // flWindowAttr field is not specified, these lines are not drawn.
1018 pData->CnrInfo.cxTreeLine = pci->cxTreeLine;
1019
1020 // @@todo recalc window components, repaint
1021 }
1022
1023 if (flCI & CMA_XVERTSPLITBAR)
1024 {
1025 // The initial position of the split bar relative to the container, used in the
1026 // details view. If this value is less than 0, the split bar is not used. The default
1027 // value is negative one (-1).
1028 pData->CnrInfo.xVertSplitbar = pci->xVertSplitbar;
1029
1030 // @@todo recalc window components, repaint
1031 }
1032
1033 if (flDirty)
1034 // post semaphore to force resize of details wnd
1035 WinPostMsg(pData->dwdMain.hwnd,
1036 WM_SEM2,
1037 (MPARAM)flDirty,
1038 0);
1039
1040 return TRUE;
1041 }
1042
1043 return FALSE;
1044}
1045
1046/*
1047 *@@ CnrQueryViewportRect:
1048 * implementation for CM_QUERYVIEWPORTRECT in fnwpCnr.
1049 *
1050 * From my testing, this simply returns the extensions
1051 * of the container content window, which the cnr docs
1052 * dub the "viewport". This never returns the workarea
1053 * size, that is, the total size of the container's
1054 * viewable content.
1055 */
1056
1057STATIC BOOL CnrQueryViewportRect(PCNRDATA pData,
1058 PRECTL prcl,
1059 USHORT usIndicator,
1060 BOOL fRightSplitView) // @@todo
1061{
1062 if (pData && prcl)
1063 {
1064 prcl->xLeft = 0;
1065 prcl->yBottom = 0;
1066 prcl->xRight = pData->dwdContent.szlWin.cx;
1067 prcl->yTop = pData->dwdContent.szlWin.cy;
1068
1069 switch (usIndicator)
1070 {
1071 case CMA_WORKSPACE:
1072 // for CMA_WORKSPACE, the PM cnr returns a 0 yBottom when
1073 // the cnr is scrolled to the very top and negative y
1074 // values when it has been scrolled down to some extent;
1075 // wonder what the use for this would be
1076 prcl->xLeft += pData->scrw.ptlScrollOfs.x;
1077 prcl->xRight += pData->scrw.ptlScrollOfs.x;
1078 prcl->yBottom -= pData->scrw.ptlScrollOfs.y;
1079 prcl->yTop -= pData->scrw.ptlScrollOfs.y;
1080 break;
1081
1082 case CMA_WINDOW:
1083 // for CMA_WINDOW, the PM cnr returns a constant
1084 // rectangle without scrolling taken into account
1085 WinMapWindowPoints(pData->dwdContent.hwnd,
1086 pData->dwdMain.hwnd,
1087 (PPOINTL)prcl,
1088 2);
1089 break;
1090 }
1091
1092 return TRUE;
1093 }
1094
1095 return FALSE;
1096}
1097
1098/* ******************************************************************
1099 *
1100 * FIELDINFO-related messages
1101 *
1102 ********************************************************************/
1103
1104/*
1105 *@@ CnrAllocDetailFieldInfo:
1106 * implementation for CM_ALLOCDETAILFIELDINFO in fnwpCnr.
1107 *
1108 * Returns: PFIELDINFO linked list or NULL.
1109 */
1110
1111STATIC PFIELDINFO CnrAllocDetailFieldInfo(PCNRDATA pData,
1112 USHORT cFieldInfos) // in: no. of fieldinfos to allocate (> 0)
1113{
1114 PFIELDINFO pfiFirst = NULL,
1115 pfiPrev = NULL;
1116
1117 if (pData)
1118 {
1119 ULONG ul;
1120 for (ul = 0;
1121 ul < cFieldInfos;
1122 ++ul)
1123 {
1124 // we must allocate each one separately or we cannot
1125 // free them separately with CM_FREEDETAILFIELDINFO
1126 PFIELDINFO pfiThis;
1127 if (pfiThis = NEW(FIELDINFO))
1128 {
1129 ZERO(pfiThis);
1130
1131 // link into list
1132 if (!pfiPrev)
1133 // first round:
1134 pfiFirst = pfiThis;
1135 else
1136 pfiPrev->pNextFieldInfo = pfiThis;
1137
1138 // put into private linklist
1139 lstAppendItem(&pData->llAllocatedFIs,
1140 pfiThis);
1141
1142 pfiPrev = pfiThis;
1143 }
1144 }
1145 }
1146
1147 return pfiFirst;
1148}
1149
1150/*
1151 *@@ CnrInsertDetailFieldInfo:
1152 * implementation for CM_INSERTDETAILFIELDINFO in fnwpCnr.
1153 */
1154
1155STATIC USHORT CnrInsertDetailFieldInfo(PCNRDATA pData,
1156 PFIELDINFO pfiFirst,
1157 PFIELDINFOINSERT pfii)
1158{
1159 USHORT usrc = 0;
1160
1161 if ( (pData)
1162 && (pfiFirst)
1163 && (pfii)
1164 && (pfii->cb = sizeof(FIELDINFOINSERT))
1165 )
1166 {
1167 ULONG ul;
1168 PFIELDINFO pfiThis = pfiFirst;
1169 PLISTNODE pNodeInsertAfter;
1170
1171 usrc = lstCountItems(&pData->llColumns);
1172
1173 switch ((ULONG)pfii->pFieldInfoOrder)
1174 {
1175 case CMA_FIRST:
1176 pNodeInsertAfter = NULL; // first
1177 break;
1178
1179 case CMA_END:
1180 pNodeInsertAfter = lstQueryLastNode(&pData->llColumns);
1181 // can return NULL also
1182 break;
1183
1184 default:
1185 if (!FindColumnFromFI(pData,
1186 pfii->pFieldInfoOrder,
1187 &pNodeInsertAfter))
1188 pNodeInsertAfter = NULL;
1189 }
1190
1191 for (ul = 0;
1192 ul < pfii->cFieldInfoInsert;
1193 ++ul)
1194 {
1195 PDETAILCOLUMN pdc;
1196 if (pdc = NEW(DETAILCOLUMN))
1197 {
1198 ZERO(pdc);
1199
1200 pdc->pfi = pfiThis;
1201
1202 if (pNodeInsertAfter = lstInsertItemAfterNode(&pData->llColumns,
1203 pdc,
1204 pNodeInsertAfter))
1205 {
1206 ++usrc;
1207 ++pData->CnrInfo.cFields;
1208 pfiThis = pfiThis->pNextFieldInfo;
1209 continue;
1210 }
1211 }
1212
1213 free(pdc);
1214
1215 usrc = 0;
1216 break;
1217
1218 } // for (ul = 0; ul < pfii->cFieldInfoInsert; ...
1219
1220 if ( (usrc)
1221 && (pfii->fInvalidateFieldInfo)
1222 )
1223 {
1224 // post semaphore to force resize of details wnd
1225 WinPostMsg(pData->dwdMain.hwnd,
1226 WM_SEM2,
1227 (MPARAM)DDFL_ALL,
1228 0);
1229 }
1230 }
1231
1232 return usrc;
1233}
1234
1235/*
1236 *@@ CnrInvalidateDetailFieldInfo:
1237 * implementation for CM_INVALIDATEDETAILFIELDINFO in fnwpCnr.
1238 */
1239
1240STATIC BOOL CnrInvalidateDetailFieldInfo(PCNRDATA pData)
1241{
1242 if (pData)
1243 return WinPostMsg(pData->dwdMain.hwnd,
1244 WM_SEM2,
1245 (MPARAM)DDFL_INVALIDATECOLUMNS,
1246 0);
1247
1248 return FALSE;
1249}
1250
1251/*
1252 *@@ CnrQueryDetailFieldInfo:
1253 * implementation for CM_QUERYDETAILFIELDINFO in fnwpCnr.
1254 */
1255
1256STATIC const FIELDINFO* CnrQueryDetailFieldInfo(PCNRDATA pData,
1257 PFIELDINFO pfiIn,
1258 USHORT cmd) // in: mp2 (CMA_FIRST, CMA_LAST, CMA_NEXT, CMA_PREV)
1259{
1260 const FIELDINFO *pfiReturn = NULL;
1261
1262 if (pData)
1263 {
1264 PLISTNODE pNode;
1265
1266 switch (cmd)
1267 {
1268 case CMA_FIRST:
1269 pNode = lstQueryFirstNode(&pData->llColumns);
1270 break;
1271
1272 case CMA_LAST:
1273 pNode = lstQueryLastNode(&pData->llColumns);
1274 break;
1275
1276 case CMA_NEXT:
1277 case CMA_PREV:
1278 if (FindColumnFromFI(pData,
1279 pfiIn,
1280 &pNode))
1281 if (cmd == CMA_NEXT)
1282 pNode = pNode->pNext;
1283 else
1284 pNode = pNode->pPrevious;
1285 break;
1286 }
1287
1288 if (pNode)
1289 pfiReturn = ((PDETAILCOLUMN)pNode->pItemData)->pfi;
1290 }
1291
1292 return pfiReturn;
1293}
1294
1295/*
1296 *@@ CnrRemoveDetailFieldInfo:
1297 * implementation for CM_REMOVEDETAILFIELDINFO in fnwpCnr.
1298 */
1299
1300STATIC SHORT CnrRemoveDetailFieldInfo(PCNRDATA pData,
1301 PFIELDINFO* ppafi,
1302 USHORT cfi,
1303 USHORT fl)
1304{
1305 if (pData)
1306 {
1307 SHORT rc = lstCountItems(&pData->llColumns);
1308 ULONG fAnythingFound = FALSE,
1309 ul;
1310
1311 for (ul = 0;
1312 ul < cfi;
1313 ++ul)
1314 {
1315 PDETAILCOLUMN pCol;
1316 PLISTNODE pNodeCol;
1317 if (!(pCol = FindColumnFromFI(pData,
1318 ppafi[ul],
1319 &pNodeCol)))
1320 {
1321 rc = -1;
1322 break;
1323 }
1324
1325 // found:
1326 lstRemoveNode(&pData->llColumns,
1327 pNodeCol); // auto-free, so this frees the DETAILCOLUMN
1328
1329 if (fl & CMA_FREE)
1330 lstRemoveItem(&pData->llAllocatedFIs,
1331 ppafi[ul]); // auto-free, so this frees the FIELDINFO
1332
1333 fAnythingFound = TRUE;
1334
1335 --rc;
1336 --pData->CnrInfo.cFields;
1337 }
1338
1339 if ( (fAnythingFound)
1340 && (fl & CMA_INVALIDATE)
1341 )
1342 {
1343 WinPostMsg(pData->dwdMain.hwnd,
1344 WM_SEM2,
1345 (MPARAM)DDFL_INVALIDATECOLUMNS,
1346 0);
1347 }
1348
1349 return rc;
1350 }
1351
1352 return -1;
1353}
1354
1355/*
1356 *@@ CnrFreeDetailFieldInfo:
1357 * implementation for CM_FREEDETAILFIELDINFO in fnwpCnr.
1358 */
1359
1360STATIC BOOL CnrFreeDetailFieldInfo(PCNRDATA pData,
1361 PFIELDINFO *ppafi, // in: mp1 of CM_FREEDETAILFIELDINFO
1362 USHORT cFieldInfos) // in: no. of items in array
1363{
1364 BOOL brc = FALSE;
1365
1366 if (pData)
1367 {
1368 ULONG ul;
1369
1370 // @@todo return FALSE if the FI is currently inserted
1371
1372 if (1)
1373 {
1374 for (ul = 0;
1375 ul < cFieldInfos;
1376 ++ul)
1377 {
1378 PFIELDINFO pfiThis = ppafi[ul];
1379 lstRemoveItem(&pData->llAllocatedFIs,
1380 pfiThis);
1381 }
1382
1383 brc = TRUE;
1384 }
1385 }
1386
1387 return brc;
1388}
1389
1390/* ******************************************************************
1391 *
1392 * Record insertion/removal
1393 *
1394 ********************************************************************/
1395
1396/*
1397 *@@ CnrAllocRecord:
1398 * implementation for CM_ALLOCRECORD in fnwpCnr.
1399 */
1400
1401STATIC PRECORDCORE CnrAllocRecord(PCNRDATA pData,
1402 ULONG cbExtra,
1403 USHORT cRecords)
1404{
1405 PRECORDCORE preccFirst = NULL;
1406
1407 if (pData)
1408 {
1409 ULONG ul;
1410 ULONG cbAlloc = ( (pData->fMiniRecords)
1411 ? sizeof(MINIRECORDCORE)
1412 : sizeof(RECORDCORE)
1413 ) + cbExtra;
1414
1415 PRECORDCORE preccPrev = NULL;
1416
1417 for (ul = 0;
1418 ul < cRecords;
1419 ++ul)
1420 {
1421 PRECORDCORE preccThis;
1422 if (!(preccThis = (PRECORDCORE)malloc(cbAlloc)))
1423 {
1424 preccFirst = NULL;
1425 break;
1426 }
1427
1428 memset(preccThis, 0, cbAlloc);
1429
1430 preccThis->cb = cbAlloc;
1431
1432 // link into list
1433 if (!preccPrev)
1434 // first round:
1435 preccFirst = preccThis;
1436 else
1437 preccPrev->preccNextRecord = preccThis;
1438
1439 // put into private linklist
1440 lstAppendItem(&pData->llAllocatedRecs,
1441 preccThis);
1442
1443 preccPrev = preccThis;
1444 }
1445 }
1446
1447 return preccFirst;
1448}
1449
1450/*
1451 *@@ CnrInsertRecord:
1452 * implementation for CM_INSERTRECORD in fnwpCnr.
1453 */
1454
1455STATIC ULONG CnrInsertRecord(PCNRDATA pData,
1456 PRECORDCORE preccFirst,
1457 PRECORDINSERT pri)
1458{
1459 ULONG cReturn = 0;
1460
1461 if ( (pData)
1462 && (preccFirst)
1463 && (pri)
1464 && (pri->cb = sizeof(RECORDINSERT))
1465 )
1466 {
1467 PRECORDCORE preccThis = preccFirst;
1468 ULONG ul;
1469 PLINKLIST pll;
1470 PLISTNODE pNodeInsertAfter;
1471
1472 cReturn = lstCountItems(&pData->llRootRecords);
1473
1474 if (pri->pRecordParent)
1475 {
1476 // @@todo
1477 }
1478 else
1479 // insert at root:
1480 pll = &pData->llRootRecords;
1481
1482 switch ((ULONG)pri->pRecordOrder)
1483 {
1484 case CMA_FIRST:
1485 pNodeInsertAfter = NULL; // first
1486 break;
1487
1488 case CMA_END:
1489 pNodeInsertAfter = lstQueryLastNode(pll);
1490 // can return NULL also
1491 break;
1492
1493 default:
1494 pNodeInsertAfter = ctnrFindListNodeForRecc(pData,
1495 pri->pRecordOrder);
1496 }
1497
1498 for (ul = 0;
1499 ul < pri->cRecordsInsert;
1500 ++ul)
1501 {
1502 PRECORDLISTITEM prli;
1503
1504 if (prli = NEW(RECORDLISTITEM))
1505 {
1506 ZERO(prli);
1507
1508 prli->precc = preccThis;
1509 prli->preccParent = pri->pRecordParent;
1510
1511 // make private copy of record attributes
1512 prli->flRecordAttr = preccThis->flRecordAttr;
1513
1514 // PM container gives the first record in the cnr
1515 // "cursored" and "selected" emphasis, so that's
1516 // what we'll do too
1517 if ( (!cReturn) // @@todo filtered records
1518 && (!(prli->flRecordAttr & CRA_FILTERED))
1519 )
1520 {
1521 prli->flRecordAttr |= CRA_CURSORED | CRA_SELECTED;
1522
1523 pData->prliCursored = prli;
1524 }
1525
1526 if (pNodeInsertAfter = lstInsertItemAfterNode(pll,
1527 prli,
1528 pNodeInsertAfter))
1529 {
1530 PRECORDTREEITEM pTreeItem;
1531
1532 if (pTreeItem = NEW(RECORDTREEITEM))
1533 {
1534 ZERO(pTreeItem);
1535
1536 pTreeItem->Tree.ulKey = (ULONG)preccThis;
1537 pTreeItem->pListNode = pNodeInsertAfter; // newly created list node
1538
1539 // the following will fail if the record
1540 // is already inserted!
1541 if (!treeInsert(&pData->RecordsTree,
1542 (PLONG)&pData->CnrInfo.cRecords,
1543 (TREE*)pTreeItem,
1544 treeCompareKeys))
1545 {
1546 ++cReturn;
1547 preccThis = preccThis->preccNextRecord;
1548 continue;
1549 }
1550
1551 free(pTreeItem);
1552 }
1553
1554 lstRemoveNode(pll,
1555 pNodeInsertAfter);
1556 }
1557
1558 free(prli);
1559 }
1560
1561 free(prli);
1562
1563 cReturn = 0;
1564 break; // for
1565 } // for (ul = 0; ul < pri->cRecordsInsert; ...
1566
1567 if ( (cReturn)
1568 && (pri->fInvalidateRecord)
1569 )
1570 {
1571 WinPostMsg(pData->dwdMain.hwnd,
1572 WM_SEM2,
1573 (MPARAM)DDFL_INVALIDATERECORDS,
1574 0);
1575 }
1576 }
1577
1578 return cReturn;
1579}
1580
1581/*
1582 *@@ CnrInsertRecordArray:
1583 * implementation for CM_INSERTRECORDARRAY in fnwpCnr.
1584 */
1585
1586STATIC ULONG CnrInsertRecordArray(PCNRDATA pData,
1587 PRECORDCORE *papRecords,
1588 PRECORDINSERT pri)
1589{
1590 ULONG cReturn = 0;
1591
1592 if ( (pData)
1593 && (papRecords)
1594 && (pri)
1595 && (pri->cb = sizeof(RECORDINSERT))
1596 )
1597 {
1598 // produce a linked list off the array and call
1599 // the CM_INSERTRECORD implementation
1600 ULONG ul;
1601 for (ul = 0;
1602 ul < (pri->cRecordsInsert - 1);
1603 ++ul)
1604 {
1605 papRecords[ul]->preccNextRecord = papRecords[ul + 1];
1606 }
1607
1608 papRecords[pri->cRecordsInsert - 1]->preccNextRecord = (PRECORDCORE)NULL;
1609
1610 cReturn = CnrInsertRecord(pData,
1611 papRecords[0],
1612 pri);
1613 }
1614
1615 return cReturn;
1616}
1617
1618/*
1619 *@@ CnrSetRecordEmphasis:
1620 * implementation for CM_SETRECORDEMPHASIS in fnwpCnr.
1621 */
1622
1623STATIC BOOL CnrSetRecordEmphasis(PCNRDATA pData,
1624 PRECORDCORE precc,
1625 BOOL fTurnOn,
1626 USHORT fsEmphasis)
1627{
1628 BOOL brc = FALSE;
1629
1630 if (pData)
1631 {
1632 if (precc)
1633 {
1634 PLISTNODE pNode;
1635 if (pNode = ctnrFindListNodeForRecc(pData,
1636 precc))
1637 {
1638 PRECORDLISTITEM prli = (PRECORDLISTITEM)pNode->pItemData;
1639
1640 ctnrChangeEmphasis(pData,
1641 prli,
1642 fTurnOn,
1643 fsEmphasis);
1644
1645 // update caller's buffer too
1646 precc->flRecordAttr = prli->flRecordAttr;
1647
1648 brc = TRUE;
1649 }
1650 } // if (precc)
1651 // @@todo else set emphasis on entire cnr
1652 }
1653
1654 return brc;
1655}
1656
1657/*
1658 *@@ CnrQueryRecordEmphasis:
1659 * implementation for CM_QUERYRECORDEMPHASIS in fnwpCnr.
1660 *
1661 * Note, if several flags are set in fsEmphasis, all
1662 * of them must be set in the record to match.
1663 */
1664
1665PRECORDCORE CnrQueryRecordEmphasis(PCNRDATA pData,
1666 PRECORDCORE preccSearchAfter,
1667 USHORT fsEmphasis)
1668{
1669 if (pData)
1670 {
1671 PLISTNODE pNode = NULL;
1672 if (preccSearchAfter == (PRECORDCORE)CMA_FIRST)
1673 pNode = lstQueryFirstNode(&pData->llRootRecords);
1674 else
1675 if (pNode = ctnrFindListNodeForRecc(pData, preccSearchAfter))
1676 pNode = pNode->pNext;
1677 else
1678 return (PRECORDCORE)-1;
1679 // @@todo how does this search tree subrecords?
1680
1681 while (pNode)
1682 {
1683 PRECORDLISTITEM prli = (PRECORDLISTITEM)pNode->pItemData;
1684 if ((prli->flRecordAttr & fsEmphasis) == fsEmphasis)
1685 return (PRECORDCORE)prli->precc;
1686
1687 pNode = pNode->pNext;
1688 }
1689 }
1690
1691 return NULL;
1692}
1693
1694/*
1695 *@@ CnrInvalidateRecord:
1696 * implementation for CM_INVALIDATERECORD in fnwpCnr.
1697 *
1698 */
1699
1700STATIC BOOL CnrInvalidateRecord(PCNRDATA pData,
1701 PRECORDCORE *papRecs,
1702 USHORT cRecs,
1703 USHORT fsInvalidate)
1704{
1705 BOOL brc = TRUE;
1706
1707 if (pData)
1708 {
1709 if ( (!papRecs)
1710 || (!cRecs)
1711 )
1712 // invalidate all:
1713 CnrSem2(pData, DDFL_INVALIDATERECORDS);
1714 else
1715 {
1716 ULONG ul;
1717 for (ul = 0;
1718 ul < cRecs;
1719 ++cRecs)
1720 {
1721 PRECORDCORE precc = papRecs[ul];
1722 PLISTNODE pRecNode;
1723 if (!(pRecNode = ctnrFindListNodeForRecc(pData,
1724 precc)))
1725 {
1726 brc = FALSE;
1727 break;
1728 }
1729
1730 // set special flag for recompute
1731 ((PRECORDLISTITEM)pRecNode->pItemData)->flInvalidate = fsInvalidate;
1732 }
1733
1734 if (brc)
1735 CnrSem2(pData, DDFL_INVALIDATESOME);
1736 // @@todo optimize: post sem only if a column size has
1737 // actually changed
1738 }
1739 }
1740
1741 return brc;
1742}
1743
1744/* ******************************************************************
1745 *
1746 * Container window proc
1747 *
1748 ********************************************************************/
1749
1750/*
1751 *@@ fnwpCnr:
1752 *
1753 */
1754
1755MRESULT EXPENTRY fnwpCnr(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
1756{
1757 MRESULT mrc = 0;
1758
1759 PCNRDATA pData = (PCNRDATA)WinQueryWindowPtr(hwnd, QWL_USER + 1);
1760
1761 switch (msg)
1762 {
1763 /* ******************************************************************
1764 *
1765 * Standard window messages
1766 *
1767 ********************************************************************/
1768
1769 case WM_CREATE:
1770 mrc = CnrCreate(hwnd, mp1, mp2);
1771 break;
1772
1773 case WM_SEM2:
1774 CnrSem2(pData, (ULONG)mp1);
1775 break;
1776
1777 case WM_PAINT:
1778 CnrPaint(pData);
1779 break;
1780
1781 case WM_WINDOWPOSCHANGED:
1782 mrc = CnrWindowPosChanged(pData, mp1, mp2);
1783 break;
1784
1785 case WM_SYSCOLORCHANGE:
1786 ctnrPresParamChanged(hwnd, 0);
1787 break;
1788
1789 case WM_PRESPARAMCHANGED:
1790 ctnrPresParamChanged(hwnd, (ULONG)mp1);
1791 break;
1792
1793 case WM_HSCROLL:
1794 case WM_VSCROLL:
1795 CnrScroll(pData, msg, mp1, mp2);
1796 break;
1797
1798 case WM_DESTROY:
1799 CnrDestroy(pData);
1800 break;
1801
1802 /* ******************************************************************
1803 *
1804 * Mouse and keyboard input
1805 *
1806 ********************************************************************/
1807
1808 /*
1809 * WM_OPEN:
1810 * when the user double-clicks on the _main_ cnr
1811 * (i.e. details titles), we notify the owner
1812 * of a "whitespace" enter event.
1813 */
1814
1815 case WM_OPEN:
1816 ctnrRecordEnter(pData,
1817 NULL,
1818 FALSE); // mouse, not keyboard
1819 mrc = (MRESULT)TRUE;
1820 break;
1821
1822 /* ******************************************************************
1823 *
1824 * General container messages
1825 *
1826 ********************************************************************/
1827
1828 /*
1829 * CM_QUERYCNRINFO:
1830 *
1831 * Parameters:
1832 *
1833 * -- PCNRINFO mp1: buffer to copy to.
1834 * -- USHORT mp2: size of buffer.
1835 *
1836 * Returns no. of bytes copied or 0 on errors.
1837 */
1838
1839 case CM_QUERYCNRINFO: // done
1840 mrc = (MRESULT)CnrQueryCnrInfo(pData,
1841 (PCNRINFO)mp1,
1842 SHORT1FROMMP(mp2));
1843 break;
1844
1845 /*
1846 * CM_SETCNRINFO:
1847 *
1848 * Parameters:
1849 *
1850 * -- PCNRINFO mp1: buffer to copy fields from.
1851 *
1852 * -- ULONG fl: CMA_* flags for fields that changed.
1853 *
1854 * Returns BOOL.
1855 */
1856
1857 case CM_SETCNRINFO: // parlty done
1858 mrc = (MRESULT)CnrSetCnrInfo(pData,
1859 (PCNRINFO)mp1,
1860 (ULONG)mp2);
1861 break;
1862
1863 case CM_PAINTBACKGROUND: // @@todo
1864 break;
1865
1866 case CM_SCROLLWINDOW: // @@todo
1867 break;
1868
1869 /*
1870 * CM_QUERYVIEWPORTRECT:
1871 *
1872 * Parameters:
1873 *
1874 * -- PRECTL mp1
1875 *
1876 * -- SHORT1FROMMP(mp2): -- CMA_WINDOW: return window coordinates
1877 * -- CMA_WORKSPACE: return workspace coordinates
1878 *
1879 * -- BOOL SHORT2FROMMP(mp2): if TRUE, return right split details view
1880 */
1881
1882 case CM_QUERYVIEWPORTRECT: // done
1883 mrc = (MRESULT)CnrQueryViewportRect(pData,
1884 (PRECTL)mp1,
1885 SHORT1FROMMP(mp2),
1886 SHORT2FROMMP(mp2));
1887 break;
1888
1889 case CM_SETTEXTVISIBILITY: // @@todo
1890 break;
1891
1892 /* ******************************************************************
1893 *
1894 * Record allocation/insertion/removal
1895 *
1896 ********************************************************************/
1897
1898 /*
1899 * CM_ALLOCRECORD:
1900 *
1901 * Parameters:
1902 *
1903 * -- ULONG mp1: record size in addition to (MINI)RECORDCORE size.
1904 *
1905 * -- USHORT mp2: no. of records to allocate.
1906 *
1907 * Returns linked list of RECORDCORE's or NULL on errors.
1908 */
1909
1910 case CM_ALLOCRECORD: // done
1911 mrc = (MRESULT)CnrAllocRecord(pData,
1912 (ULONG)mp1,
1913 SHORT1FROMMP(mp2));
1914 break;
1915
1916 /*
1917 * CM_INSERTRECORD:
1918 * inserts one or more records. If there's more
1919 * than one record, we assume it's a linked list.
1920 *
1921 * Parameters:
1922 *
1923 * -- PRECORDCORE mp1: first record
1924 *
1925 * -- PRECORDINSERT pri
1926 *
1927 * Returns the no. of records in the container or 0 on errors.
1928 */
1929
1930 case CM_INSERTRECORD: // done
1931 mrc = (MRESULT)CnrInsertRecord(pData,
1932 (PRECORDCORE)mp1,
1933 (PRECORDINSERT)mp2);
1934 break;
1935
1936 /*
1937 * CM_INSERTRECORDARRAY:
1938 * inserts one or more records. As opposed to with
1939 * CM_INSERTRECORD, mp1 points to an array of
1940 * record pointers instead of to a linked list
1941 * of records.
1942 *
1943 * Parameters:
1944 *
1945 * -- PRECORDCORE mp1: first record
1946 *
1947 * -- PRECORDINSERT pri
1948 *
1949 * Returns the no. of records in the container or 0 on errors.
1950 */
1951
1952 case CM_INSERTRECORDARRAY: // done
1953 mrc = (MRESULT)CnrInsertRecordArray(pData,
1954 (PRECORDCORE*)mp1,
1955 (PRECORDINSERT)mp2);
1956 break;
1957
1958 /*
1959 * CM_QUERYRECORD:
1960 *
1961 * Parameters:
1962 *
1963 * -- PRECORDCORE mp1: preccSearch
1964 *
1965 * -- SHORT1FROMMP(mp1): CMA_FIRST, CMA_LAST, CMA_NEXT, CMA_PREV
1966 *
1967 * or for tree views: CMA_FIRSTCHILD, CMA_LASTCHILD, CMA_PARENT
1968 *
1969 * -- SHORT2FROMMP(mp1): CMA_ITEMORDER or CMA_ZORDER
1970 */
1971
1972 case CM_QUERYRECORD: // @@todo
1973 break;
1974
1975 /*
1976 * CM_SETRECORDEMPHASIS:
1977 *
1978 * Parameters:
1979 *
1980 * -- PRECORDCORE mp1: record to change emphasis for.
1981 *
1982 * -- SHORT1FROMMP(mp2): TRUE == turn flags on, FALSE == turn flags off.
1983 *
1984 * -- SHORT2FROMMP(mp2): any combination of CRA_CURSORED, CRA_DISABLED,
1985 * CRA_INUSE, CRA_PICKED, CRA_SELECTED, CRA_SOURCE
1986 *
1987 * Returns BOOL.
1988 */
1989
1990 case CM_SETRECORDEMPHASIS:
1991 mrc = (MRESULT)CnrSetRecordEmphasis(pData,
1992 (PRECORDCORE)mp1,
1993 SHORT1FROMMP(mp2),
1994 SHORT2FROMMP(mp2));
1995 break;
1996
1997 /*
1998 * CM_QUERYRECORDEMPHASIS:
1999 *
2000 * Parameters:
2001 *
2002 * -- PRECORDCORE mp1: record after which to start search
2003 * or NULL to start search from beginning.
2004 *
2005 * -- USHORT mp2: any combination of CRA_COLLAPSED, CRA_CURSORED,
2006 * CRA_DISABLED, CRA_DROPONABLE, CRA_EXPANDED, CRA_FILTERED,
2007 * CRA_INUSE, CRA_PICKED, CRA_SELECTED, CRA_SOURCE
2008 */
2009
2010 case CM_QUERYRECORDEMPHASIS: // done
2011 mrc = (MRESULT)CnrQueryRecordEmphasis(pData,
2012 (PRECORDCORE)mp1,
2013 SHORT1FROMMP(mp2));
2014 break;
2015
2016 case CM_QUERYRECORDFROMRECT: // @@todo
2017 break;
2018
2019 case CM_QUERYRECORDINFO: // @@todo
2020 break;
2021
2022 case CM_QUERYRECORDRECT: // @@todo
2023 break;
2024
2025 /*
2026 * CM_INVALIDATERECORD:
2027 *
2028 * Parameters:
2029 *
2030 * -- PRECORDCORE* mp1: ptr to array of record pointers
2031 *
2032 * -- SHORT1FROMMP(mp2): no. of records in array
2033 *
2034 * -- SHORT2FROMMP(mp2): CMA_ERASE, CMA_REPOSITION,
2035 * CMA_NOREPOSITION, CMA_TEXTCHANGED
2036 *
2037 * Returns BOOL.
2038 */
2039
2040 case CM_INVALIDATERECORD: // done
2041 mrc = (MRESULT)CnrInvalidateRecord(pData,
2042 (PRECORDCORE*)mp1,
2043 SHORT1FROMMP(mp2),
2044 SHORT2FROMMP(mp2));
2045 break;
2046
2047 case CM_REMOVERECORD: // @@todo
2048 case CM_FREERECORD: // @@todo
2049 break;
2050
2051 case CM_ERASERECORD: // @@todo
2052 break;
2053
2054 case CM_ARRANGE: // @@todo
2055 break;
2056
2057 case CM_FILTER: // @@todo
2058 break;
2059
2060 case CM_QUERYDRAGIMAGE: // @@todo
2061
2062 case CM_SEARCHSTRING:
2063
2064 case CM_SORTRECORD: // @@todo
2065
2066 /* ******************************************************************
2067 *
2068 * Details view
2069 *
2070 ********************************************************************/
2071
2072 /*
2073 * CM_ALLOCDETAILFIELDINFO:
2074 *
2075 * Parameters:
2076 *
2077 * -- USHORT mp1: no. of fieldinfos to allocate
2078 * -- mp2: reserved
2079 *
2080 * Returns PFIELDINFO linked list of fieldinfos,
2081 * or NULL on errors.
2082 */
2083
2084 case CM_ALLOCDETAILFIELDINFO: // done
2085 mrc = (MRESULT)CnrAllocDetailFieldInfo(pData,
2086 SHORT1FROMMP(mp1));
2087 break;
2088
2089 /*
2090 * CM_INSERTDETAILFIELDINFO:
2091 *
2092 * Parameters:
2093 *
2094 * -- PFIELDINFO mp1
2095 *
2096 * -- PFIELDINFOINSERT mp2
2097 *
2098 * Returns the no. of FI's in the cnr or 0 on errors.
2099 */
2100
2101 case CM_INSERTDETAILFIELDINFO: // done
2102 mrc = (MRESULT)CnrInsertDetailFieldInfo(pData,
2103 (PFIELDINFO)mp1,
2104 (PFIELDINFOINSERT)mp2);
2105 break;
2106
2107 /*
2108 * CM_INVALIDATEDETAILFIELDINFO:
2109 * No parameters.
2110 *
2111 * Returns BOOL.
2112 */
2113
2114 case CM_INVALIDATEDETAILFIELDINFO: // done
2115 mrc = (MRESULT)CnrInvalidateDetailFieldInfo(pData);
2116 break;
2117
2118 /*
2119 * CM_QUERYDETAILFIELDINFO:
2120 *
2121 * Parameters:
2122 *
2123 * -- PFIELDINFO mp1
2124 *
2125 * -- USHORT mp2: CMA_FIRST, CMA_LAST, CMA_NEXT, CMA_PREV
2126 */
2127
2128 case CM_QUERYDETAILFIELDINFO: // done
2129 mrc = (MRESULT)CnrQueryDetailFieldInfo(pData,
2130 (PFIELDINFO)mp1,
2131 SHORT1FROMMP(mp2));
2132 break;
2133
2134 /*
2135 * CM_REMOVEDETAILFIELDINFO:
2136 *
2137 * Parameters:
2138 *
2139 * -- PFIELDINFO* mp1: ptr to array of PFIELDINFO's
2140 *
2141 * -- SHORT1FROMMP(mp1): no. of fieldinfos in array
2142 *
2143 * -- SHORT2FROMMP(mp2): flRemove (CMA_FREE, CMA_INVALIDATE)
2144 *
2145 * Returns the no. of FI's in the cnr or -1 on errors.
2146 */
2147
2148 case CM_REMOVEDETAILFIELDINFO: // done
2149 mrc = (MRESULT)CnrRemoveDetailFieldInfo(pData,
2150 (PFIELDINFO*)mp1,
2151 SHORT1FROMMP(mp2),
2152 SHORT2FROMMP(mp2));
2153 break;
2154
2155 /*
2156 * CM_FREEDETAILFIELDINFO:
2157 *
2158 * Paramters:
2159 *
2160 * -- PFIELDINFO* mp1: ptr to array of PFIELDINFO's
2161 * -- USHORT mp2: no. of ptrs in array
2162 *
2163 * Returns BOOL.
2164 */
2165
2166 case CM_FREEDETAILFIELDINFO: // done
2167 mrc = (MRESULT)CnrFreeDetailFieldInfo(pData,
2168 (PFIELDINFO*)mp1,
2169 SHORT1FROMMP(mp2));
2170 break;
2171
2172 case CM_HORZSCROLLSPLITWINDOW:
2173 break;
2174
2175 /* ******************************************************************
2176 *
2177 * Icon view
2178 *
2179 ********************************************************************/
2180
2181 case CM_SETGRIDINFO: // @@todo
2182 case CM_QUERYGRIDINFO: // @@todo
2183 case CM_SNAPTOGRID: // @@todo
2184 break;
2185
2186 /* ******************************************************************
2187 *
2188 * Tree management
2189 *
2190 ********************************************************************/
2191
2192 case CM_COLLAPSETREE: // @@todo
2193 case CM_EXPANDTREE:
2194 case CM_MOVETREE: // @@todo
2195 break;
2196
2197 /* ******************************************************************
2198 *
2199 * Direct editing
2200 *
2201 ********************************************************************/
2202
2203 case CM_OPENEDIT: // @@todo
2204
2205 case CM_CLOSEEDIT: // @@todo
2206 break;
2207
2208
2209 default:
2210 if (pData)
2211 mrc = ctlDefWindowProc(&pData->dwdMain, msg, mp1, mp2);
2212 break;
2213
2214 }
2215
2216 return mrc;
2217}
2218
2219/*
2220 *@@ ctlRegisterXCnr:
2221 *
2222 */
2223
2224BOOL ctlRegisterXCnr(HAB hab)
2225{
2226 return ( WinRegisterClass(hab,
2227 WC_CCTL_CNR,
2228 fnwpCnr,
2229 0, // CS_SYNCPAINT, // CS_CLIPSIBLINGS CS_CLIPCHILDREN
2230 sizeof(PVOID) * 2)
2231 && WinRegisterClass(hab,
2232 WC_CCTL_CNR_DETAILS,
2233 fnwpCnrDetails,
2234 0, // CS_SYNCPAINT, // | CS_PARENTCLIP, // CS_CLIPSIBLINGS CS_CLIPCHILDREN
2235 sizeof(PVOID) * 2)
2236 );
2237}
2238
Note: See TracBrowser for help on using the repository browser.