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

Last change on this file since 290 was 245, 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: 66.6 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 if (fl & DDFL_WINDOWSIZECHANGED)
489 WinSetWindowPos(pData->dwdContent.hwnd,
490 HWND_TOP,
491 0,
492 y,
493 cx,
494 cy,
495 SWP_MOVE | SWP_SIZE);
496 // SWP_MOVE is required or PM will move our
497 // subwindow to some adjustment position
498
499 if (pData->scrw.hwndVScroll)
500 {
501 WinSetWindowPos(pData->scrw.hwndVScroll,
502 HWND_TOP,
503 cx,
504 y,
505 pData->scrw.cxScrollBar,
506 cy,
507 SWP_MOVE | SWP_SIZE);
508 }
509
510 if (pData->scrw.hwndHScroll)
511 {
512 WinSetWindowPos(pData->scrw.hwndHScroll,
513 HWND_TOP,
514 0,
515 0,
516 cx,
517 pData->scrw.cyScrollBar,
518 SWP_MOVE | SWP_SIZE);
519 }
520
521 ctnrUpdateScrollbars(pData,
522 cx,
523 cy);
524 }
525 }
526}
527
528/*
529 *@@ CnrPaint:
530 * implementation for WM_PAINT in fnwpCnr.
531 *
532 * This paints only the part of the container that belongs
533 * to the main container window, which is the title area
534 * and/or the details column headings.
535 */
536
537STATIC VOID CnrPaint(PCNRDATA pData)
538{
539 HPS hps;
540 RECTL rclPaint;
541
542 if ( (pData)
543 && (hps = WinBeginPaint(pData->dwdMain.hwnd, NULLHANDLE, &rclPaint))
544 )
545 {
546 gpihSwitchToRGB(hps);
547
548 if ( (pData->CnrInfo.flWindowAttr & (CV_DETAIL | CA_DETAILSVIEWTITLES))
549 == (CV_DETAIL | CA_DETAILSVIEWTITLES)
550 )
551 {
552 PLISTNODE pColNode;
553
554 POINTL ptl;
555 RECTL rcl;
556 LONG yPadding = pData->CnrInfo.cyLineSpacing / 2;
557
558 // lowest y that we are allowed to paint at
559 LONG yLowest = pData->dwdMain.szlWin.cy - pData->cyColTitlesBox;
560
561 if (rclPaint.yTop > yLowest)
562 {
563 // clip paint rect so we don't paint into details wnd
564 if (rclPaint.yBottom < yLowest)
565 rclPaint.yBottom = yLowest;
566
567 WinFillRect(hps,
568 &rclPaint,
569 ctlQueryColor(&pData->dwdMain, CTLCOL_BGND));
570
571 FOR_ALL_NODES(&pData->llColumns, pColNode)
572 {
573 RECTL rcl2;
574 PDETAILCOLUMN pCol = (PDETAILCOLUMN)pColNode->pItemData;
575 const FIELDINFO *pfi = pCol->pfi;
576 PLISTNODE pRecNode;
577 ULONG cRow;
578
579 rcl.xLeft = pCol->xLeft + COLUMN_PADDING_X - pData->scrw.ptlScrollOfs.x;
580 rcl.xRight = rcl.xLeft + pCol->cxContent;
581
582 // we start out at the top and work our way down,
583 // decrementing rcl.yTop with every item we painted
584 rcl.yTop = pData->dwdMain.szlWin.cy;
585
586 rcl.yTop -= COLUMN_PADDING_Y + yPadding;
587 rcl.yBottom = rcl.yTop
588 - pData->cyColTitlesContent;
589
590 if (pfi->flTitle & CFA_BITMAPORICON)
591 // @@todo
592 ;
593 else
594 {
595 ctnrDrawString(hps,
596 (PCSZ)pfi->pTitleData,
597 &rcl,
598 pfi->flTitle,
599 &pData->fm);
600 }
601
602 rcl.yBottom -= COLUMN_PADDING_Y + yPadding;
603
604 if (pfi->flData & CFA_HORZSEPARATOR)
605 {
606 ptl.x = rcl.xLeft;
607 ptl.y = rcl.yBottom;
608
609 GpiMove(hps,
610 &ptl);
611 ptl.x = rcl.xRight;
612 GpiLine(hps,
613 &ptl);
614 }
615
616 rcl.yTop = rcl.yBottom;
617
618 } // FOR_ALL_NODES(&pData->llColumns, pColNode)
619 }
620 }
621
622 WinEndPaint(hps);
623 }
624}
625
626/*
627 *@@ CnrWindowPosChanged:
628 * implementation for WM_WINDOWPOSCHANGED in fnwpCnr.
629 */
630
631STATIC MRESULT CnrWindowPosChanged(PCNRDATA pData,
632 MPARAM mp1,
633 MPARAM mp2)
634{
635 MRESULT mrc = 0;
636
637 if (pData)
638 {
639 mrc = ctlDefWindowProc(&pData->dwdMain, WM_WINDOWPOSCHANGED, mp1, mp2);
640
641 if (((PSWP)mp1)->fl & SWP_SIZE)
642 {
643 WinPostMsg(pData->dwdMain.hwnd,
644 WM_SEM2,
645 (MPARAM)DDFL_WINDOWSIZECHANGED,
646 0);
647 }
648 }
649
650 return mrc;
651}
652
653/*
654 *@@ ctnrPresParamChanged:
655 * implementation for WM_PRESPARAMCHANGED for both
656 * fnwpCnr and fnwpCnrDetails.
657 */
658
659VOID ctnrPresParamChanged(HWND hwnd, // in: either main cnr or content subwindow
660 ULONG ulpp)
661{
662 PCNRDATA pData;
663 if (pData = (PCNRDATA)WinQueryWindowPtr(hwnd, QWL_USER + 1))
664 {
665 // note, no matter what hwnd is passed in,
666 // we use the dwdMain buffer here cos we share presparams
667 // between all cnr child windows
668 ctlRefreshColors(&pData->dwdMain);
669
670 switch (ulpp)
671 {
672 case 0: // layout palette thing dropped
673 case PP_FONTNAMESIZE:
674 if (!pData->fSettingPP)
675 {
676 pData->fSettingPP = TRUE;
677
678 if (pData->CnrInfo.flWindowAttr & CV_DETAIL)
679 {
680 // if we got this on the contents window,
681 // set it on the main cnr as well, and
682 // vice versa
683 PSZ pszFont;
684 if (pszFont = winhQueryWindowFont(hwnd))
685 {
686 HWND hwndOther;
687 if (hwnd == pData->dwdMain.hwnd)
688 hwndOther = pData->dwdContent.hwnd;
689 else
690 hwndOther = pData->dwdMain.hwnd;
691
692 winhSetWindowFont(hwndOther, pszFont);
693 free(pszFont);
694 }
695
696 WinPostMsg(pData->dwdMain.hwnd,
697 WM_SEM2,
698 (MPARAM)(DDFL_INVALIDATECOLUMNS | DDFL_INVALIDATERECORDS),
699 0);
700 }
701
702 pData->fSettingPP = FALSE;
703 }
704 break;
705
706 default:
707 // just repaint everything
708 WinInvalidateRect(pData->dwdMain.hwnd, NULL, TRUE);
709 }
710 }
711}
712
713/*
714 *@@ CnrScroll:
715 * implementation for WM_HSCROLL and WM_VSCROLL in fnwpCnr.
716 */
717
718STATIC VOID CnrScroll(PCNRDATA pData,
719 ULONG msg,
720 MPARAM mp1,
721 MPARAM mp2)
722{
723 if (pData)
724 {
725 BOOL fNotify = FALSE;
726
727 if (pData->CnrInfo.flWindowAttr & CV_DETAIL)
728 {
729 POINTL ptlScroll;
730 NOTIFYSCROLL ns;
731 ns.hwndCnr = pData->dwdMain.hwnd;
732
733 if (msg == WM_HSCROLL)
734 {
735 ptlScroll.y = 0;
736 if (ptlScroll.x = winhHandleScrollMsg(pData->scrw.hwndHScroll,
737 &pData->scrw.ptlScrollOfs.x,
738 pData->dwdContent.szlWin.cx,
739 pData->scrw.szlWorkarea.cx,
740 5,
741 msg,
742 mp2))
743 {
744 winhScrollWindow(pData->dwdContent.hwnd,
745 NULL,
746 &ptlScroll);
747
748 // scroll main cnr with detail titles too
749 if (pData->CnrInfo.flWindowAttr & CA_DETAILSVIEWTITLES)
750 {
751 RECTL rclClip;
752 rclClip.xLeft = 0;
753 rclClip.xRight = pData->dwdMain.szlWin.cx;
754 rclClip.yBottom = pData->dwdMain.szlWin.cy - pData->cyColTitlesBox;
755 rclClip.yTop = pData->dwdMain.szlWin.cy;
756 winhScrollWindow(pData->dwdMain.hwnd,
757 &rclClip,
758 &ptlScroll);
759 }
760
761 ns.lScrollInc = ptlScroll.y;
762 ns.fScroll = CMA_HORIZONTAL; // @@todo details flags
763 ctlSendWmControl(pData->dwdMain.hwnd,
764 CN_SCROLL,
765 &ns);
766
767 fNotify = TRUE;
768 }
769 }
770 else
771 {
772 ptlScroll.x = 0;
773 if (ptlScroll.y = winhHandleScrollMsg(pData->scrw.hwndVScroll,
774 &pData->scrw.ptlScrollOfs.y,
775 pData->dwdContent.szlWin.cy,
776 pData->scrw.szlWorkarea.cy,
777 5,
778 msg,
779 mp2))
780 {
781 winhScrollWindow(pData->dwdContent.hwnd,
782 NULL,
783 &ptlScroll);
784
785 ns.lScrollInc = ptlScroll.x;
786 ns.fScroll = CMA_VERTICAL; // @@todo details flags
787 ctlSendWmControl(pData->dwdMain.hwnd,
788 CN_SCROLL,
789 &ns);
790
791 fNotify = TRUE;
792 }
793 }
794 }
795
796 if (fNotify)
797 SendViewportChanged(pData);
798 }
799}
800
801/*
802 *@@ CnrDestroy:
803 * implementation for WM_DESTROY in fnwpCnr.
804 */
805
806STATIC VOID CnrDestroy(PCNRDATA pData)
807{
808 if (pData)
809 {
810 // free all data that we ever allocated;
811 // all these lists are auto-free
812 lstClear(&pData->llColumns);
813 lstClear(&pData->llAllocatedFIs);
814 lstClear(&pData->llAllocatedRecs);
815 lstClear(&pData->llRootRecords);
816
817 free(pData);
818 }
819}
820
821/* ******************************************************************
822 *
823 * General container messages
824 *
825 ********************************************************************/
826
827/*
828 *@@ CnrQueryCnrInfo:
829 * implementation for CM_QUERYCNRINFO in fnwpCnr.
830 *
831 * Returns no. of bytes copied.
832 */
833
834STATIC USHORT CnrQueryCnrInfo(PCNRDATA pData,
835 PCNRINFO pci, // out: target buffer (mp1 of CM_QUERYCNRINFO)
836 USHORT cb) // in: mp2 of CM_QUERYCNRINFO
837{
838 if (pData)
839 {
840 USHORT cbCopied = max(cb, sizeof(CNRINFO));
841 memcpy(pci,
842 &pData->CnrInfo,
843 cbCopied);
844 return cbCopied;
845 }
846
847 return 0;
848}
849
850/*
851 *@@ CnrSetCnrInfo:
852 * implementation for CM_SETCNRINFO in fnwpCnr.
853 *
854 * Returns no. of bytes copied.
855 */
856
857STATIC BOOL CnrSetCnrInfo(PCNRDATA pData,
858 PCNRINFO pci, // in: mp1 of CM_SETCNRINFO
859 ULONG flCI) // in: CMA_* flags in mp2 of CM_SETCNRINFO
860{
861 if (pData)
862 {
863 ULONG flDirty = 0;
864
865 if (flCI & CMA_PSORTRECORD)
866 /* Pointer to the comparison function for sorting container records. If NULL,
867 which is the default condition, no sorting is performed. Sorting only occurs
868 during record insertion and when changing the value of this field. The third
869 parameter of the comparison function, pStorage, must be NULL. See
870 CM_SORTRECORD for a further description of the comparison function. */
871 pData->CnrInfo.pSortRecord = pci->pSortRecord;
872
873 if (flCI & CMA_PFIELDINFOLAST)
874 /* Pointer to the last column in the left window of the split details view. The
875 default is NULL, causing all columns to be positioned in the left window. */
876 pData->CnrInfo.pFieldInfoLast = pci->pFieldInfoLast;
877
878 if (flCI & CMA_PFIELDINFOOBJECT)
879 /* Pointer to a column that represents an object in the details view. This
880 FIELDINFO structure must contain icons or bit maps. In-use emphasis is applied
881 to this column of icons or bit maps only. The default is the leftmost column
882 in the unsplit details view, or the leftmost column in the left window of the
883 split details view. */
884 pData->CnrInfo.pFieldInfoObject = pci->pFieldInfoObject;
885
886 if (flCI & CMA_CNRTITLE)
887 {
888 // Text for the container title. The default is NULL.
889 pData->CnrInfo.pszCnrTitle = pci->pszCnrTitle;
890
891 // @@todo recalc window components, repaint
892 }
893
894 if (flCI & CMA_FLWINDOWATTR)
895 {
896 // Container window attributes.
897 if (pData->CnrInfo.flWindowAttr != pci->flWindowAttr)
898 {
899 HWND hwndSwitchOwner = NULLHANDLE;
900
901 pData->CnrInfo.flWindowAttr = pci->flWindowAttr;
902
903 // if switching to details view, then create
904 // details subwindow
905 if (pData->CnrInfo.flWindowAttr & CV_DETAIL)
906 {
907 if (!pData->dwdContent.hwnd)
908 {
909 if (pData->dwdContent.hwnd = CreateChild(pData,
910 WC_CCTL_CNR_DETAILS,
911 CID_LEFTDVWND))
912 {
913 flDirty = DDFL_ALL;
914 }
915 }
916 }
917 else
918 {
919 winhDestroyWindow(&pData->dwdContent.hwnd);
920 }
921 }
922 }
923
924 if (flCI & CMA_PTLORIGIN)
925 {
926 // Lower-left origin of the container window in virtual workspace coordinates,
927 // used in the icon view. The default origin is (0,0).
928 memcpy(&pData->CnrInfo.ptlOrigin,
929 &pci->ptlOrigin,
930 sizeof(POINTL));
931
932 // @@todo recalc window components, repaint
933 }
934
935 if (flCI & CMA_DELTA)
936 {
937 // An application-defined threshold, or number of records, from either end of
938 // the list of available records. Used when a container needs to handle large
939 // amounts of data. The default is 0. Refer to the description of the container
940 // control in the OS/2 Programming Guide for more information about specifying
941 // deltas.
942 pData->CnrInfo.cDelta = pci->cDelta;
943 }
944
945 if (flCI & CMA_SLBITMAPORICON)
946 {
947 // The size (in pels) of icons or bit maps. The default is the system size.
948 memcpy(&pData->CnrInfo.slBitmapOrIcon,
949 &pci->slBitmapOrIcon,
950 sizeof(SIZEL));
951
952 // @@todo recalc window components, repaint
953 }
954
955 if (flCI & CMA_SLTREEBITMAPORICON)
956 {
957 // The size (in pels) of the expanded and collapsed icons or bit maps in the
958 // tree icon and tree text views.
959 memcpy(&pData->CnrInfo.slTreeBitmapOrIcon,
960 &pci->slTreeBitmapOrIcon,
961 sizeof(SIZEL));
962
963 // @@todo recalc window components, repaint
964 }
965
966 if (flCI & CMA_TREEBITMAP)
967 {
968 // Expanded and collapsed bit maps in the tree icon and tree text views.
969 pData->CnrInfo.hbmExpanded = pci->hbmExpanded;
970 pData->CnrInfo.hbmCollapsed = pci->hbmCollapsed;
971
972 // @@todo recalc window components, repaint
973 }
974
975 if (flCI & CMA_TREEICON)
976 {
977 // Expanded and collapsed icons in the tree icon and tree text views.
978 pData->CnrInfo.hptrExpanded = pci->hptrExpanded;
979 pData->CnrInfo.hptrCollapsed = pci->hptrCollapsed;
980
981 // @@todo recalc window components, repaint
982 }
983
984 if (flCI & CMA_LINESPACING)
985 {
986 // The amount of vertical space (in pels) between the records. If this value is
987 // less than 0, a default value is used.
988 pData->CnrInfo.cyLineSpacing = pci->cyLineSpacing;
989
990 // @@todo recalc window components, repaint
991 }
992
993 if (flCI & CMA_CXTREEINDENT)
994 {
995 // Horizontal distance (in pels) between levels in the tree view. If this value is
996 // less than 0, a default value is used.
997 pData->CnrInfo.cxTreeIndent = pci->cxTreeIndent;
998
999 // @@todo recalc window components, repaint
1000 }
1001
1002 if (flCI & CMA_CXTREELINE)
1003 {
1004 // Width of the lines (in pels) that show the relationship between items in the
1005 // tree view. If this value is less than 0, a default value is used. Also, if the
1006 // CA_TREELINE container attribute of the CNRINFO data structure's
1007 // flWindowAttr field is not specified, these lines are not drawn.
1008 pData->CnrInfo.cxTreeLine = pci->cxTreeLine;
1009
1010 // @@todo recalc window components, repaint
1011 }
1012
1013 if (flCI & CMA_XVERTSPLITBAR)
1014 {
1015 // The initial position of the split bar relative to the container, used in the
1016 // details view. If this value is less than 0, the split bar is not used. The default
1017 // value is negative one (-1).
1018 pData->CnrInfo.xVertSplitbar = pci->xVertSplitbar;
1019
1020 // @@todo recalc window components, repaint
1021 }
1022
1023 if (flDirty)
1024 // post semaphore to force resize of details wnd
1025 WinPostMsg(pData->dwdMain.hwnd,
1026 WM_SEM2,
1027 (MPARAM)flDirty,
1028 0);
1029
1030 return TRUE;
1031 }
1032
1033 return FALSE;
1034}
1035
1036/*
1037 *@@ CnrQueryViewportRect:
1038 * implementation for CM_QUERYVIEWPORTRECT in fnwpCnr.
1039 *
1040 * From my testing, this simply returns the extensions
1041 * of the container content window, which the cnr docs
1042 * dub the "viewport". This never returns the workarea
1043 * size, that is, the total size of the container's
1044 * viewable content.
1045 */
1046
1047STATIC BOOL CnrQueryViewportRect(PCNRDATA pData,
1048 PRECTL prcl,
1049 USHORT usIndicator,
1050 BOOL fRightSplitView) // @@todo
1051{
1052 if (pData && prcl)
1053 {
1054 prcl->xLeft = 0;
1055 prcl->yBottom = 0;
1056 prcl->xRight = pData->dwdContent.szlWin.cx;
1057 prcl->yTop = pData->dwdContent.szlWin.cy;
1058
1059 switch (usIndicator)
1060 {
1061 case CMA_WORKSPACE:
1062 // for CMA_WORKSPACE, the PM cnr returns a 0 yBottom when
1063 // the cnr is scrolled to the very top and negative y
1064 // values when it has been scrolled down to some extent;
1065 // wonder what the use for this would be
1066 prcl->xLeft += pData->scrw.ptlScrollOfs.x;
1067 prcl->xRight += pData->scrw.ptlScrollOfs.x;
1068 prcl->yBottom -= pData->scrw.ptlScrollOfs.y;
1069 prcl->yTop -= pData->scrw.ptlScrollOfs.y;
1070 break;
1071
1072 case CMA_WINDOW:
1073 // for CMA_WINDOW, the PM cnr returns a constant
1074 // rectangle without scrolling taken into account
1075 WinMapWindowPoints(pData->dwdContent.hwnd,
1076 pData->dwdMain.hwnd,
1077 (PPOINTL)prcl,
1078 2);
1079 break;
1080 }
1081
1082 return TRUE;
1083 }
1084
1085 return FALSE;
1086}
1087
1088/* ******************************************************************
1089 *
1090 * FIELDINFO-related messages
1091 *
1092 ********************************************************************/
1093
1094/*
1095 *@@ CnrAllocDetailFieldInfo:
1096 * implementation for CM_ALLOCDETAILFIELDINFO in fnwpCnr.
1097 *
1098 * Returns: PFIELDINFO linked list or NULL.
1099 */
1100
1101STATIC PFIELDINFO CnrAllocDetailFieldInfo(PCNRDATA pData,
1102 USHORT cFieldInfos) // in: no. of fieldinfos to allocate (> 0)
1103{
1104 PFIELDINFO pfiFirst = NULL,
1105 pfiPrev = NULL;
1106
1107 if (pData)
1108 {
1109 ULONG ul;
1110 for (ul = 0;
1111 ul < cFieldInfos;
1112 ++ul)
1113 {
1114 // we must allocate each one separately or we cannot
1115 // free them separately with CM_FREEDETAILFIELDINFO
1116 PFIELDINFO pfiThis;
1117 if (pfiThis = NEW(FIELDINFO))
1118 {
1119 ZERO(pfiThis);
1120
1121 // link into list
1122 if (!pfiPrev)
1123 // first round:
1124 pfiFirst = pfiThis;
1125 else
1126 pfiPrev->pNextFieldInfo = pfiThis;
1127
1128 // put into private linklist
1129 lstAppendItem(&pData->llAllocatedFIs,
1130 pfiThis);
1131
1132 pfiPrev = pfiThis;
1133 }
1134 }
1135 }
1136
1137 return pfiFirst;
1138}
1139
1140/*
1141 *@@ CnrInsertDetailFieldInfo:
1142 * implementation for CM_INSERTDETAILFIELDINFO in fnwpCnr.
1143 */
1144
1145STATIC USHORT CnrInsertDetailFieldInfo(PCNRDATA pData,
1146 PFIELDINFO pfiFirst,
1147 PFIELDINFOINSERT pfii)
1148{
1149 USHORT usrc = 0;
1150
1151 if ( (pData)
1152 && (pfiFirst)
1153 && (pfii)
1154 && (pfii->cb = sizeof(FIELDINFOINSERT))
1155 )
1156 {
1157 ULONG ul;
1158 PFIELDINFO pfiThis = pfiFirst;
1159 PLISTNODE pNodeInsertAfter;
1160
1161 usrc = lstCountItems(&pData->llColumns);
1162
1163 switch ((ULONG)pfii->pFieldInfoOrder)
1164 {
1165 case CMA_FIRST:
1166 pNodeInsertAfter = NULL; // first
1167 break;
1168
1169 case CMA_END:
1170 pNodeInsertAfter = lstQueryLastNode(&pData->llColumns);
1171 // can return NULL also
1172 break;
1173
1174 default:
1175 if (!FindColumnFromFI(pData,
1176 pfii->pFieldInfoOrder,
1177 &pNodeInsertAfter))
1178 pNodeInsertAfter = NULL;
1179 }
1180
1181 for (ul = 0;
1182 ul < pfii->cFieldInfoInsert;
1183 ++ul)
1184 {
1185 PDETAILCOLUMN pdc;
1186 if (pdc = NEW(DETAILCOLUMN))
1187 {
1188 ZERO(pdc);
1189
1190 pdc->pfi = pfiThis;
1191
1192 if (pNodeInsertAfter = lstInsertItemAfterNode(&pData->llColumns,
1193 pdc,
1194 pNodeInsertAfter))
1195 {
1196 ++usrc;
1197 ++pData->CnrInfo.cFields;
1198 pfiThis = pfiThis->pNextFieldInfo;
1199 continue;
1200 }
1201 }
1202
1203 free(pdc);
1204
1205 usrc = 0;
1206 break;
1207
1208 } // for (ul = 0; ul < pfii->cFieldInfoInsert; ...
1209
1210 if ( (usrc)
1211 && (pfii->fInvalidateFieldInfo)
1212 )
1213 {
1214 // post semaphore to force resize of details wnd
1215 WinPostMsg(pData->dwdMain.hwnd,
1216 WM_SEM2,
1217 (MPARAM)DDFL_ALL,
1218 0);
1219 }
1220 }
1221
1222 return usrc;
1223}
1224
1225/*
1226 *@@ CnrInvalidateDetailFieldInfo:
1227 * implementation for CM_INVALIDATEDETAILFIELDINFO in fnwpCnr.
1228 */
1229
1230STATIC BOOL CnrInvalidateDetailFieldInfo(PCNRDATA pData)
1231{
1232 if (pData)
1233 return WinPostMsg(pData->dwdMain.hwnd,
1234 WM_SEM2,
1235 (MPARAM)DDFL_INVALIDATECOLUMNS,
1236 0);
1237
1238 return FALSE;
1239}
1240
1241/*
1242 *@@ CnrQueryDetailFieldInfo:
1243 * implementation for CM_QUERYDETAILFIELDINFO in fnwpCnr.
1244 */
1245
1246STATIC const FIELDINFO* CnrQueryDetailFieldInfo(PCNRDATA pData,
1247 PFIELDINFO pfiIn,
1248 USHORT cmd) // in: mp2 (CMA_FIRST, CMA_LAST, CMA_NEXT, CMA_PREV)
1249{
1250 const FIELDINFO *pfiReturn = NULL;
1251
1252 if (pData)
1253 {
1254 PLISTNODE pNode;
1255
1256 switch (cmd)
1257 {
1258 case CMA_FIRST:
1259 pNode = lstQueryFirstNode(&pData->llColumns);
1260 break;
1261
1262 case CMA_LAST:
1263 pNode = lstQueryLastNode(&pData->llColumns);
1264 break;
1265
1266 case CMA_NEXT:
1267 case CMA_PREV:
1268 if (FindColumnFromFI(pData,
1269 pfiIn,
1270 &pNode))
1271 if (cmd == CMA_NEXT)
1272 pNode = pNode->pNext;
1273 else
1274 pNode = pNode->pPrevious;
1275 break;
1276 }
1277
1278 if (pNode)
1279 pfiReturn = ((PDETAILCOLUMN)pNode->pItemData)->pfi;
1280 }
1281
1282 return pfiReturn;
1283}
1284
1285/*
1286 *@@ CnrRemoveDetailFieldInfo:
1287 * implementation for CM_REMOVEDETAILFIELDINFO in fnwpCnr.
1288 */
1289
1290STATIC SHORT CnrRemoveDetailFieldInfo(PCNRDATA pData,
1291 PFIELDINFO* ppafi,
1292 USHORT cfi,
1293 USHORT fl)
1294{
1295 if (pData)
1296 {
1297 SHORT rc = lstCountItems(&pData->llColumns);
1298 ULONG fAnythingFound = FALSE,
1299 ul;
1300
1301 for (ul = 0;
1302 ul < cfi;
1303 ++ul)
1304 {
1305 PDETAILCOLUMN pCol;
1306 PLISTNODE pNodeCol;
1307 if (!(pCol = FindColumnFromFI(pData,
1308 ppafi[ul],
1309 &pNodeCol)))
1310 {
1311 rc = -1;
1312 break;
1313 }
1314
1315 // found:
1316 lstRemoveNode(&pData->llColumns,
1317 pNodeCol); // auto-free, so this frees the DETAILCOLUMN
1318
1319 if (fl & CMA_FREE)
1320 lstRemoveItem(&pData->llAllocatedFIs,
1321 ppafi[ul]); // auto-free, so this frees the FIELDINFO
1322
1323 fAnythingFound = TRUE;
1324
1325 --rc;
1326 --pData->CnrInfo.cFields;
1327 }
1328
1329 if ( (fAnythingFound)
1330 && (fl & CMA_INVALIDATE)
1331 )
1332 {
1333 WinPostMsg(pData->dwdMain.hwnd,
1334 WM_SEM2,
1335 (MPARAM)DDFL_INVALIDATECOLUMNS,
1336 0);
1337 }
1338
1339 return rc;
1340 }
1341
1342 return -1;
1343}
1344
1345/*
1346 *@@ CnrFreeDetailFieldInfo:
1347 * implementation for CM_FREEDETAILFIELDINFO in fnwpCnr.
1348 */
1349
1350STATIC BOOL CnrFreeDetailFieldInfo(PCNRDATA pData,
1351 PFIELDINFO *ppafi, // in: mp1 of CM_FREEDETAILFIELDINFO
1352 USHORT cFieldInfos) // in: no. of items in array
1353{
1354 BOOL brc = FALSE;
1355
1356 if (pData)
1357 {
1358 ULONG ul;
1359
1360 // @@todo return FALSE if the FI is currently inserted
1361
1362 if (1)
1363 {
1364 for (ul = 0;
1365 ul < cFieldInfos;
1366 ++ul)
1367 {
1368 PFIELDINFO pfiThis = ppafi[ul];
1369 lstRemoveItem(&pData->llAllocatedFIs,
1370 pfiThis);
1371 }
1372
1373 brc = TRUE;
1374 }
1375 }
1376
1377 return brc;
1378}
1379
1380/* ******************************************************************
1381 *
1382 * Record insertion/removal
1383 *
1384 ********************************************************************/
1385
1386/*
1387 *@@ CnrAllocRecord:
1388 * implementation for CM_ALLOCRECORD in fnwpCnr.
1389 */
1390
1391STATIC PRECORDCORE CnrAllocRecord(PCNRDATA pData,
1392 ULONG cbExtra,
1393 USHORT cRecords)
1394{
1395 PRECORDCORE preccFirst = NULL;
1396
1397 if (pData)
1398 {
1399 ULONG ul;
1400 ULONG cbAlloc = ( (pData->fMiniRecords)
1401 ? sizeof(MINIRECORDCORE)
1402 : sizeof(RECORDCORE)
1403 ) + cbExtra;
1404
1405 PRECORDCORE preccPrev = NULL;
1406
1407 for (ul = 0;
1408 ul < cRecords;
1409 ++ul)
1410 {
1411 PRECORDCORE preccThis;
1412 if (!(preccThis = (PRECORDCORE)malloc(cbAlloc)))
1413 {
1414 preccFirst = NULL;
1415 break;
1416 }
1417
1418 memset(preccThis, 0, cbAlloc);
1419
1420 preccThis->cb = cbAlloc;
1421
1422 // link into list
1423 if (!preccPrev)
1424 // first round:
1425 preccFirst = preccThis;
1426 else
1427 preccPrev->preccNextRecord = preccThis;
1428
1429 // put into private linklist
1430 lstAppendItem(&pData->llAllocatedRecs,
1431 preccThis);
1432
1433 preccPrev = preccThis;
1434 }
1435 }
1436
1437 return preccFirst;
1438}
1439
1440/*
1441 *@@ CnrInsertRecord:
1442 * implementation for CM_INSERTRECORD in fnwpCnr.
1443 */
1444
1445STATIC ULONG CnrInsertRecord(PCNRDATA pData,
1446 PRECORDCORE preccFirst,
1447 PRECORDINSERT pri)
1448{
1449 ULONG cReturn = 0;
1450
1451 if ( (pData)
1452 && (preccFirst)
1453 && (pri)
1454 && (pri->cb = sizeof(RECORDINSERT))
1455 )
1456 {
1457 PRECORDCORE preccThis = preccFirst;
1458 ULONG ul;
1459 PLINKLIST pll;
1460 PLISTNODE pNodeInsertAfter;
1461
1462 cReturn = lstCountItems(&pData->llRootRecords);
1463
1464 if (pri->pRecordParent)
1465 {
1466 // @@todo
1467 }
1468 else
1469 // insert at root:
1470 pll = &pData->llRootRecords;
1471
1472 switch ((ULONG)pri->pRecordOrder)
1473 {
1474 case CMA_FIRST:
1475 pNodeInsertAfter = NULL; // first
1476 break;
1477
1478 case CMA_END:
1479 pNodeInsertAfter = lstQueryLastNode(pll);
1480 // can return NULL also
1481 break;
1482
1483 default:
1484 pNodeInsertAfter = ctnrFindListNodeForRecc(pData,
1485 pri->pRecordOrder);
1486 }
1487
1488 for (ul = 0;
1489 ul < pri->cRecordsInsert;
1490 ++ul)
1491 {
1492 PRECORDLISTITEM prli;
1493
1494 if (prli = NEW(RECORDLISTITEM))
1495 {
1496 ZERO(prli);
1497
1498 prli->precc = preccThis;
1499 prli->preccParent = pri->pRecordParent;
1500
1501 // make private copy of record attributes
1502 prli->flRecordAttr = preccThis->flRecordAttr;
1503
1504 // PM container gives the first record in the cnr
1505 // "cursored" and "selected" emphasis, so that's
1506 // what we'll do too
1507 if ( (!cReturn) // @@todo filtered records
1508 && (!(prli->flRecordAttr & CRA_FILTERED))
1509 )
1510 {
1511 prli->flRecordAttr |= CRA_CURSORED | CRA_SELECTED;
1512
1513 pData->prliCursored = prli;
1514 }
1515
1516 if (pNodeInsertAfter = lstInsertItemAfterNode(pll,
1517 prli,
1518 pNodeInsertAfter))
1519 {
1520 PRECORDTREEITEM pTreeItem;
1521
1522 if (pTreeItem = NEW(RECORDTREEITEM))
1523 {
1524 ZERO(pTreeItem);
1525
1526 pTreeItem->Tree.ulKey = (ULONG)preccThis;
1527 pTreeItem->pListNode = pNodeInsertAfter; // newly created list node
1528
1529 // the following will fail if the record
1530 // is already inserted!
1531 if (!treeInsert(&pData->RecordsTree,
1532 (PLONG)&pData->CnrInfo.cRecords,
1533 (TREE*)pTreeItem,
1534 treeCompareKeys))
1535 {
1536 ++cReturn;
1537 preccThis = preccThis->preccNextRecord;
1538 continue;
1539 }
1540
1541 free(pTreeItem);
1542 }
1543
1544 lstRemoveNode(pll,
1545 pNodeInsertAfter);
1546 }
1547
1548 free(prli);
1549 }
1550
1551 free(prli);
1552
1553 cReturn = 0;
1554 break; // for
1555 } // for (ul = 0; ul < pri->cRecordsInsert; ...
1556
1557 if ( (cReturn)
1558 && (pri->fInvalidateRecord)
1559 )
1560 {
1561 WinPostMsg(pData->dwdMain.hwnd,
1562 WM_SEM2,
1563 (MPARAM)DDFL_INVALIDATERECORDS,
1564 0);
1565 }
1566 }
1567
1568 return cReturn;
1569}
1570
1571/*
1572 *@@ CnrInsertRecordArray:
1573 * implementation for CM_INSERTRECORDARRAY in fnwpCnr.
1574 */
1575
1576STATIC ULONG CnrInsertRecordArray(PCNRDATA pData,
1577 PRECORDCORE *papRecords,
1578 PRECORDINSERT pri)
1579{
1580 ULONG cReturn = 0;
1581
1582 if ( (pData)
1583 && (papRecords)
1584 && (pri)
1585 && (pri->cb = sizeof(RECORDINSERT))
1586 )
1587 {
1588 // produce a linked list off the array and call
1589 // the CM_INSERTRECORD implementation
1590 ULONG ul;
1591 for (ul = 0;
1592 ul < (pri->cRecordsInsert - 1);
1593 ++ul)
1594 {
1595 papRecords[ul]->preccNextRecord = papRecords[ul + 1];
1596 }
1597
1598 papRecords[pri->cRecordsInsert - 1]->preccNextRecord = (PRECORDCORE)NULL;
1599
1600 cReturn = CnrInsertRecord(pData,
1601 papRecords[0],
1602 pri);
1603 }
1604
1605 return cReturn;
1606}
1607
1608/*
1609 *@@ CnrSetRecordEmphasis:
1610 * implementation for CM_SETRECORDEMPHASIS in fnwpCnr.
1611 */
1612
1613STATIC BOOL CnrSetRecordEmphasis(PCNRDATA pData,
1614 PRECORDCORE precc,
1615 BOOL fTurnOn,
1616 USHORT fsEmphasis)
1617{
1618 BOOL brc = FALSE;
1619
1620 if (pData)
1621 {
1622 if (precc)
1623 {
1624 PLISTNODE pNode;
1625 if (pNode = ctnrFindListNodeForRecc(pData,
1626 precc))
1627 {
1628 PRECORDLISTITEM prli = (PRECORDLISTITEM)pNode->pItemData;
1629
1630 ctnrChangeEmphasis(pData,
1631 prli,
1632 fTurnOn,
1633 fsEmphasis);
1634
1635 // update caller's buffer too
1636 precc->flRecordAttr = prli->flRecordAttr;
1637
1638 brc = TRUE;
1639 }
1640 } // if (precc)
1641 // @@todo else set emphasis on entire cnr
1642 }
1643
1644 return brc;
1645}
1646
1647/*
1648 *@@ CnrQueryRecordEmphasis:
1649 * implementation for CM_QUERYRECORDEMPHASIS in fnwpCnr.
1650 *
1651 * Note, if several flags are set in fsEmphasis, all
1652 * of them must be set in the record to match.
1653 */
1654
1655PRECORDCORE CnrQueryRecordEmphasis(PCNRDATA pData,
1656 PRECORDCORE preccSearchAfter,
1657 USHORT fsEmphasis)
1658{
1659 if (pData)
1660 {
1661 PLISTNODE pNode = NULL;
1662 if (preccSearchAfter == (PRECORDCORE)CMA_FIRST)
1663 pNode = lstQueryFirstNode(&pData->llRootRecords);
1664 else
1665 if (pNode = ctnrFindListNodeForRecc(pData, preccSearchAfter))
1666 pNode = pNode->pNext;
1667 else
1668 return (PRECORDCORE)-1;
1669 // @@todo how does this search tree subrecords?
1670
1671 while (pNode)
1672 {
1673 PRECORDLISTITEM prli = (PRECORDLISTITEM)pNode->pItemData;
1674 if ((prli->flRecordAttr & fsEmphasis) == fsEmphasis)
1675 return (PRECORDCORE)prli->precc;
1676
1677 pNode = pNode->pNext;
1678 }
1679 }
1680
1681 return NULL;
1682}
1683
1684/*
1685 *@@ CnrInvalidateRecord:
1686 * implementation for CM_INVALIDATERECORD in fnwpCnr.
1687 *
1688 */
1689
1690STATIC BOOL CnrInvalidateRecord(PCNRDATA pData,
1691 PRECORDCORE *papRecs,
1692 USHORT cRecs,
1693 USHORT fsInvalidate)
1694{
1695 BOOL brc = TRUE;
1696
1697 if (pData)
1698 {
1699 if ( (!papRecs)
1700 || (!cRecs)
1701 )
1702 // invalidate all:
1703 CnrSem2(pData, DDFL_INVALIDATERECORDS);
1704 else
1705 {
1706 ULONG ul;
1707 for (ul = 0;
1708 ul < cRecs;
1709 ++cRecs)
1710 {
1711 PRECORDCORE precc = papRecs[ul];
1712 PLISTNODE pRecNode;
1713 if (!(pRecNode = ctnrFindListNodeForRecc(pData,
1714 precc)))
1715 {
1716 brc = FALSE;
1717 break;
1718 }
1719
1720 // set special flag for recompute
1721 ((PRECORDLISTITEM)pRecNode->pItemData)->flInvalidate = fsInvalidate;
1722 }
1723
1724 if (brc)
1725 CnrSem2(pData, DDFL_INVALIDATESOME);
1726 // @@todo optimize: post sem only if a column size has
1727 // actually changed
1728 }
1729 }
1730
1731 return brc;
1732}
1733
1734/* ******************************************************************
1735 *
1736 * Container window proc
1737 *
1738 ********************************************************************/
1739
1740/*
1741 *@@ fnwpCnr:
1742 *
1743 */
1744
1745MRESULT EXPENTRY fnwpCnr(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
1746{
1747 MRESULT mrc = 0;
1748
1749 PCNRDATA pData = (PCNRDATA)WinQueryWindowPtr(hwnd, QWL_USER + 1);
1750
1751 switch (msg)
1752 {
1753 /* ******************************************************************
1754 *
1755 * Standard window messages
1756 *
1757 ********************************************************************/
1758
1759 case WM_CREATE:
1760 mrc = CnrCreate(hwnd, mp1, mp2);
1761 break;
1762
1763 case WM_SEM2:
1764 CnrSem2(pData, (ULONG)mp1);
1765 break;
1766
1767 case WM_PAINT:
1768 CnrPaint(pData);
1769 break;
1770
1771 case WM_WINDOWPOSCHANGED:
1772 mrc = CnrWindowPosChanged(pData, mp1, mp2);
1773 break;
1774
1775 case WM_SYSCOLORCHANGE:
1776 ctnrPresParamChanged(hwnd, 0);
1777 break;
1778
1779 case WM_PRESPARAMCHANGED:
1780 ctnrPresParamChanged(hwnd, (ULONG)mp1);
1781 break;
1782
1783 case WM_HSCROLL:
1784 case WM_VSCROLL:
1785 CnrScroll(pData, msg, mp1, mp2);
1786 break;
1787
1788 case WM_DESTROY:
1789 CnrDestroy(pData);
1790 break;
1791
1792 /* ******************************************************************
1793 *
1794 * Mouse and keyboard input
1795 *
1796 ********************************************************************/
1797
1798 /*
1799 * WM_OPEN:
1800 * when the user double-clicks on the _main_ cnr
1801 * (i.e. details titles), we notify the owner
1802 * of a "whitespace" enter event.
1803 */
1804
1805 case WM_OPEN:
1806 ctnrRecordEnter(pData,
1807 NULL,
1808 FALSE); // mouse, not keyboard
1809 mrc = (MRESULT)TRUE;
1810 break;
1811
1812 /* ******************************************************************
1813 *
1814 * General container messages
1815 *
1816 ********************************************************************/
1817
1818 /*
1819 * CM_QUERYCNRINFO:
1820 *
1821 * Parameters:
1822 *
1823 * -- PCNRINFO mp1: buffer to copy to.
1824 * -- USHORT mp2: size of buffer.
1825 *
1826 * Returns no. of bytes copied or 0 on errors.
1827 */
1828
1829 case CM_QUERYCNRINFO: // done
1830 mrc = (MRESULT)CnrQueryCnrInfo(pData,
1831 (PCNRINFO)mp1,
1832 SHORT1FROMMP(mp2));
1833 break;
1834
1835 /*
1836 * CM_SETCNRINFO:
1837 *
1838 * Parameters:
1839 *
1840 * -- PCNRINFO mp1: buffer to copy fields from.
1841 *
1842 * -- ULONG fl: CMA_* flags for fields that changed.
1843 *
1844 * Returns BOOL.
1845 */
1846
1847 case CM_SETCNRINFO: // parlty done
1848 mrc = (MRESULT)CnrSetCnrInfo(pData,
1849 (PCNRINFO)mp1,
1850 (ULONG)mp2);
1851 break;
1852
1853 case CM_PAINTBACKGROUND: // @@todo
1854 break;
1855
1856 case CM_SCROLLWINDOW: // @@todo
1857 break;
1858
1859 /*
1860 * CM_QUERYVIEWPORTRECT:
1861 *
1862 * Parameters:
1863 *
1864 * -- PRECTL mp1
1865 *
1866 * -- SHORT1FROMMP(mp2): -- CMA_WINDOW: return window coordinates
1867 * -- CMA_WORKSPACE: return workspace coordinates
1868 *
1869 * -- BOOL SHORT2FROMMP(mp2): if TRUE, return right split details view
1870 */
1871
1872 case CM_QUERYVIEWPORTRECT: // done
1873 mrc = (MRESULT)CnrQueryViewportRect(pData,
1874 (PRECTL)mp1,
1875 SHORT1FROMMP(mp2),
1876 SHORT2FROMMP(mp2));
1877 break;
1878
1879 case CM_SETTEXTVISIBILITY: // @@todo
1880 break;
1881
1882 /* ******************************************************************
1883 *
1884 * Record allocation/insertion/removal
1885 *
1886 ********************************************************************/
1887
1888 /*
1889 * CM_ALLOCRECORD:
1890 *
1891 * Parameters:
1892 *
1893 * -- ULONG mp1: record size in addition to (MINI)RECORDCORE size.
1894 *
1895 * -- USHORT mp2: no. of records to allocate.
1896 *
1897 * Returns linked list of RECORDCORE's or NULL on errors.
1898 */
1899
1900 case CM_ALLOCRECORD: // done
1901 mrc = (MRESULT)CnrAllocRecord(pData,
1902 (ULONG)mp1,
1903 SHORT1FROMMP(mp2));
1904 break;
1905
1906 /*
1907 * CM_INSERTRECORD:
1908 * inserts one or more records. If there's more
1909 * than one record, we assume it's a linked list.
1910 *
1911 * Parameters:
1912 *
1913 * -- PRECORDCORE mp1: first record
1914 *
1915 * -- PRECORDINSERT pri
1916 *
1917 * Returns the no. of records in the container or 0 on errors.
1918 */
1919
1920 case CM_INSERTRECORD: // done
1921 mrc = (MRESULT)CnrInsertRecord(pData,
1922 (PRECORDCORE)mp1,
1923 (PRECORDINSERT)mp2);
1924 break;
1925
1926 /*
1927 * CM_INSERTRECORDARRAY:
1928 * inserts one or more records. As opposed to with
1929 * CM_INSERTRECORD, mp1 points to an array of
1930 * record pointers instead of to a linked list
1931 * of records.
1932 *
1933 * Parameters:
1934 *
1935 * -- PRECORDCORE mp1: first record
1936 *
1937 * -- PRECORDINSERT pri
1938 *
1939 * Returns the no. of records in the container or 0 on errors.
1940 */
1941
1942 case CM_INSERTRECORDARRAY: // done
1943 mrc = (MRESULT)CnrInsertRecordArray(pData,
1944 (PRECORDCORE*)mp1,
1945 (PRECORDINSERT)mp2);
1946 break;
1947
1948 /*
1949 * CM_QUERYRECORD:
1950 *
1951 * Parameters:
1952 *
1953 * -- PRECORDCORE mp1: preccSearch
1954 *
1955 * -- SHORT1FROMMP(mp1): CMA_FIRST, CMA_LAST, CMA_NEXT, CMA_PREV
1956 *
1957 * or for tree views: CMA_FIRSTCHILD, CMA_LASTCHILD, CMA_PARENT
1958 *
1959 * -- SHORT2FROMMP(mp1): CMA_ITEMORDER or CMA_ZORDER
1960 */
1961
1962 case CM_QUERYRECORD: // @@todo
1963 break;
1964
1965 /*
1966 * CM_SETRECORDEMPHASIS:
1967 *
1968 * Parameters:
1969 *
1970 * -- PRECORDCORE mp1: record to change emphasis for.
1971 *
1972 * -- SHORT1FROMMP(mp2): TRUE == turn flags on, FALSE == turn flags off.
1973 *
1974 * -- SHORT2FROMMP(mp2): any combination of CRA_CURSORED, CRA_DISABLED,
1975 * CRA_INUSE, CRA_PICKED, CRA_SELECTED, CRA_SOURCE
1976 *
1977 * Returns BOOL.
1978 */
1979
1980 case CM_SETRECORDEMPHASIS:
1981 mrc = (MRESULT)CnrSetRecordEmphasis(pData,
1982 (PRECORDCORE)mp1,
1983 SHORT1FROMMP(mp2),
1984 SHORT2FROMMP(mp2));
1985 break;
1986
1987 /*
1988 * CM_QUERYRECORDEMPHASIS:
1989 *
1990 * Parameters:
1991 *
1992 * -- PRECORDCORE mp1: record after which to start search
1993 * or NULL to start search from beginning.
1994 *
1995 * -- USHORT mp2: any combination of CRA_COLLAPSED, CRA_CURSORED,
1996 * CRA_DISABLED, CRA_DROPONABLE, CRA_EXPANDED, CRA_FILTERED,
1997 * CRA_INUSE, CRA_PICKED, CRA_SELECTED, CRA_SOURCE
1998 */
1999
2000 case CM_QUERYRECORDEMPHASIS: // done
2001 mrc = (MRESULT)CnrQueryRecordEmphasis(pData,
2002 (PRECORDCORE)mp1,
2003 SHORT1FROMMP(mp2));
2004 break;
2005
2006 case CM_QUERYRECORDFROMRECT: // @@todo
2007 break;
2008
2009 case CM_QUERYRECORDINFO: // @@todo
2010 break;
2011
2012 case CM_QUERYRECORDRECT: // @@todo
2013 break;
2014
2015 /*
2016 * CM_INVALIDATERECORD:
2017 *
2018 * Parameters:
2019 *
2020 * -- PRECORDCORE* mp1: ptr to array of record pointers
2021 *
2022 * -- SHORT1FROMMP(mp2): no. of records in array
2023 *
2024 * -- SHORT2FROMMP(mp2): CMA_ERASE, CMA_REPOSITION,
2025 * CMA_NOREPOSITION, CMA_TEXTCHANGED
2026 *
2027 * Returns BOOL.
2028 */
2029
2030 case CM_INVALIDATERECORD: // done
2031 mrc = (MRESULT)CnrInvalidateRecord(pData,
2032 (PRECORDCORE*)mp1,
2033 SHORT1FROMMP(mp2),
2034 SHORT2FROMMP(mp2));
2035 break;
2036
2037 case CM_REMOVERECORD: // @@todo
2038 case CM_FREERECORD: // @@todo
2039 break;
2040
2041 case CM_ERASERECORD: // @@todo
2042 break;
2043
2044 case CM_ARRANGE: // @@todo
2045 break;
2046
2047 case CM_FILTER: // @@todo
2048 break;
2049
2050 case CM_QUERYDRAGIMAGE: // @@todo
2051
2052 case CM_SEARCHSTRING:
2053
2054 case CM_SORTRECORD: // @@todo
2055
2056 /* ******************************************************************
2057 *
2058 * Details view
2059 *
2060 ********************************************************************/
2061
2062 /*
2063 * CM_ALLOCDETAILFIELDINFO:
2064 *
2065 * Parameters:
2066 *
2067 * -- USHORT mp1: no. of fieldinfos to allocate
2068 * -- mp2: reserved
2069 *
2070 * Returns PFIELDINFO linked list of fieldinfos,
2071 * or NULL on errors.
2072 */
2073
2074 case CM_ALLOCDETAILFIELDINFO: // done
2075 mrc = (MRESULT)CnrAllocDetailFieldInfo(pData,
2076 SHORT1FROMMP(mp1));
2077 break;
2078
2079 /*
2080 * CM_INSERTDETAILFIELDINFO:
2081 *
2082 * Parameters:
2083 *
2084 * -- PFIELDINFO mp1
2085 *
2086 * -- PFIELDINFOINSERT mp2
2087 *
2088 * Returns the no. of FI's in the cnr or 0 on errors.
2089 */
2090
2091 case CM_INSERTDETAILFIELDINFO: // done
2092 mrc = (MRESULT)CnrInsertDetailFieldInfo(pData,
2093 (PFIELDINFO)mp1,
2094 (PFIELDINFOINSERT)mp2);
2095 break;
2096
2097 /*
2098 * CM_INVALIDATEDETAILFIELDINFO:
2099 * No parameters.
2100 *
2101 * Returns BOOL.
2102 */
2103
2104 case CM_INVALIDATEDETAILFIELDINFO: // done
2105 mrc = (MRESULT)CnrInvalidateDetailFieldInfo(pData);
2106 break;
2107
2108 /*
2109 * CM_QUERYDETAILFIELDINFO:
2110 *
2111 * Parameters:
2112 *
2113 * -- PFIELDINFO mp1
2114 *
2115 * -- USHORT mp2: CMA_FIRST, CMA_LAST, CMA_NEXT, CMA_PREV
2116 */
2117
2118 case CM_QUERYDETAILFIELDINFO: // done
2119 mrc = (MRESULT)CnrQueryDetailFieldInfo(pData,
2120 (PFIELDINFO)mp1,
2121 SHORT1FROMMP(mp2));
2122 break;
2123
2124 /*
2125 * CM_REMOVEDETAILFIELDINFO:
2126 *
2127 * Parameters:
2128 *
2129 * -- PFIELDINFO* mp1: ptr to array of PFIELDINFO's
2130 *
2131 * -- SHORT1FROMMP(mp1): no. of fieldinfos in array
2132 *
2133 * -- SHORT2FROMMP(mp2): flRemove (CMA_FREE, CMA_INVALIDATE)
2134 *
2135 * Returns the no. of FI's in the cnr or -1 on errors.
2136 */
2137
2138 case CM_REMOVEDETAILFIELDINFO: // done
2139 mrc = (MRESULT)CnrRemoveDetailFieldInfo(pData,
2140 (PFIELDINFO*)mp1,
2141 SHORT1FROMMP(mp2),
2142 SHORT2FROMMP(mp2));
2143 break;
2144
2145 /*
2146 * CM_FREEDETAILFIELDINFO:
2147 *
2148 * Paramters:
2149 *
2150 * -- PFIELDINFO* mp1: ptr to array of PFIELDINFO's
2151 * -- USHORT mp2: no. of ptrs in array
2152 *
2153 * Returns BOOL.
2154 */
2155
2156 case CM_FREEDETAILFIELDINFO: // done
2157 mrc = (MRESULT)CnrFreeDetailFieldInfo(pData,
2158 (PFIELDINFO*)mp1,
2159 SHORT1FROMMP(mp2));
2160 break;
2161
2162 case CM_HORZSCROLLSPLITWINDOW:
2163 break;
2164
2165 /* ******************************************************************
2166 *
2167 * Icon view
2168 *
2169 ********************************************************************/
2170
2171 case CM_SETGRIDINFO: // @@todo
2172 case CM_QUERYGRIDINFO: // @@todo
2173 case CM_SNAPTOGRID: // @@todo
2174 break;
2175
2176 /* ******************************************************************
2177 *
2178 * Tree management
2179 *
2180 ********************************************************************/
2181
2182 case CM_COLLAPSETREE: // @@todo
2183 case CM_EXPANDTREE:
2184 case CM_MOVETREE: // @@todo
2185 break;
2186
2187 /* ******************************************************************
2188 *
2189 * Direct editing
2190 *
2191 ********************************************************************/
2192
2193 case CM_OPENEDIT: // @@todo
2194
2195 case CM_CLOSEEDIT: // @@todo
2196 break;
2197
2198
2199 default:
2200 if (pData)
2201 mrc = ctlDefWindowProc(&pData->dwdMain, msg, mp1, mp2);
2202 break;
2203
2204 }
2205
2206 return mrc;
2207}
2208
2209/*
2210 *@@ ctlRegisterXCnr:
2211 *
2212 */
2213
2214BOOL ctlRegisterXCnr(HAB hab)
2215{
2216 return ( WinRegisterClass(hab,
2217 WC_CCTL_CNR,
2218 fnwpCnr,
2219 0, // CS_SYNCPAINT, // CS_CLIPSIBLINGS CS_CLIPCHILDREN
2220 sizeof(PVOID) * 2)
2221 && WinRegisterClass(hab,
2222 WC_CCTL_CNR_DETAILS,
2223 fnwpCnrDetails,
2224 0, // CS_SYNCPAINT, // | CS_PARENTCLIP, // CS_CLIPSIBLINGS CS_CLIPCHILDREN
2225 sizeof(PVOID) * 2)
2226 );
2227}
2228
Note: See TracBrowser for help on using the repository browser.