source: trunk/src/helpers/cctl_cnr_dtls.c

Last change on this file 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: 40.4 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_DOSMISC
29
30#define INCL_WINWINDOWMGR
31#define INCL_WINFRAMEMGR
32#define INCL_WINSYS
33#define INCL_WININPUT
34#define INCL_WINSTDCNR
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 * Helper funcs
62 *
63 ********************************************************************/
64
65/*
66 *@@ InvalidateColumn:
67 *
68 */
69
70VOID InvalidateColumn(PCNRDATA pData,
71 const DETAILCOLUMN *pColumn)
72{
73 RECTL rcl;
74 rcl.xLeft = pColumn->xLeft - pData->scrw.ptlScrollOfs.x;
75 rcl.xRight = rcl.xLeft + pColumn->cxContent + 2 * COLUMN_PADDING_X + 1;
76 rcl.yBottom = 0;
77 rcl.yTop = pData->dwdContent.szlWin.cy;
78
79 WinInvalidateRect(pData->dwdContent.hwnd, &rcl, FALSE);
80
81 // and titles too
82 rcl.yTop = pData->dwdMain.szlWin.cy;
83 WinInvalidateRect(pData->dwdMain.hwnd, &rcl, FALSE);
84}
85
86/*
87 *@@ cdtlRecalcDetails:
88 * worker routine for refreshing all internal DETAILCOLUMN
89 * data when important stuff has changed.
90 *
91 * This only gets called from CnrSem2 when WM_SEM2 comes
92 * in with sufficient update flags.
93 *
94 * This sets the following flags in *pfl:
95 *
96 * -- DDFL_INVALIDATECOLUMNS: columns have to be
97 * repositioned, so details window needs a full
98 * repaint.
99 *
100 * -- DDFL_WINDOWSIZECHANGED: cnr titles area changed,
101 * so details window might need adjustment.
102 *
103 * -- DDFL_WORKAREACHANGED: cnr workarea changed, so
104 * scroll bars need adjustment.
105 */
106
107VOID cdtlRecalcDetails(PCNRDATA pData,
108 HPS hps,
109 PULONG pfl) // in/out: DDFL_* flags
110{
111 LONG xCurrent = 0;
112 PLISTNODE pColNode;
113 ULONG cColumn = 0;
114
115 // compute total padding for one row:
116 LONG cxPaddingRow = pData->CnrInfo.cFields * 2 * COLUMN_PADDING_X;
117 LONG cyColTitlesContent = 0,
118 cyColTitlesBox = 0;
119
120 BOOL fRefreshAll = (*pfl & (DDFL_INVALIDATECOLUMNS | DDFL_INVALIDATERECORDS));
121
122 LONG yNow = 0;
123
124 // list of records to invalidate while we're running;
125 // this only gets appended to if we're not invalidating
126 // all records anyway
127 LINKLIST llInvalidateRecords;
128 lstInit(&llInvalidateRecords, FALSE); // receives RECORDLISTITEM's
129
130 GpiQueryFontMetrics(hps, sizeof(FONTMETRICS), &pData->fm);
131
132 // outer loop: columns (go RIGHT)
133 FOR_ALL_NODES(&pData->llColumns, pColNode)
134 {
135 PDETAILCOLUMN pColumn = (PDETAILCOLUMN)pColNode->pItemData;
136 const FIELDINFO *pfi = pColumn->pfi;
137 PLISTNODE pRecNode;
138
139 LONG cxContent;
140
141 if (fRefreshAll)
142 pColumn->cxWidestRecord = 0;
143
144 // skip invisible columns
145 if (!(pfi->flData & CFA_INVISIBLE))
146 {
147 yNow = 0;
148
149 // inner loop: records (go UP)
150 // (we start with the LAST record so we can calculate
151 // the y coordinates correctly)
152 pRecNode = lstQueryLastNode(&pData->llRootRecords);
153 while (pRecNode)
154 {
155 PRECORDLISTITEM prliThis = (PRECORDLISTITEM)pRecNode->pItemData;
156 const RECORDCORE *preccThis = prliThis->precc;
157 PVOID pColumnData = (PVOID)((PBYTE)preccThis + pfi->offStruct);
158
159 if (!(prliThis->flRecordAttr & CRA_FILTERED))
160 {
161 // skip filtered records
162 if ( (*pfl & DDFL_INVALIDATERECORDS)
163 || (prliThis->flInvalidate)
164 )
165 {
166 ULONG cLines;
167 SIZEL szlThis = {10, 10};
168 PCSZ pcsz = NULL;
169 CHAR szTemp[30];
170
171 if (!cColumn)
172 {
173 // we're in the leftmost column:
174 prliThis->szlContent.cx
175 = prliThis->szlContent.cy
176 = prliThis->szlBox.cx
177 = prliThis->szlBox.cy
178 = 0;
179 }
180
181 switch (pfi->flData & ( CFA_BITMAPORICON
182 | CFA_DATE
183 | CFA_STRING
184 | CFA_TIME
185 | CFA_ULONG))
186 {
187 case CFA_BITMAPORICON:
188 // @@todo
189 break;
190
191 case CFA_STRING:
192 pcsz = *(PSZ*)pColumnData;
193 break;
194
195 case CFA_DATE:
196 nlsDate(&pData->cs,
197 szTemp,
198 ((PCDATE)pColumnData)->year,
199 ((PCDATE)pColumnData)->month,
200 ((PCDATE)pColumnData)->day);
201 pcsz = szTemp;
202 break;
203
204 case CFA_TIME:
205 nlsTime(&pData->cs,
206 szTemp,
207 ((PCTIME)pColumnData)->hours,
208 ((PCTIME)pColumnData)->minutes,
209 ((PCTIME)pColumnData)->seconds);
210 pcsz = szTemp;
211 break;
212
213 case CFA_ULONG:
214 nlsThousandsULong(szTemp,
215 *((PULONG)pColumnData),
216 pData->cs.cs.cThousands);
217 pcsz = szTemp;
218 break;
219 }
220
221 if (pcsz)
222 {
223 gpihCalcTextExtent(hps,
224 pcsz,
225 &szlThis.cx,
226 &cLines);
227 szlThis.cy = cLines * (pData->fm.lMaxBaselineExt + pData->fm.lExternalLeading);
228 }
229
230 // increment record's total width
231 prliThis->szlContent.cx += szlThis.cx;
232 prliThis->szlBox.cx += szlThis.cx
233 + cxPaddingRow; // computed at top
234
235 // record's content height = height of tallest column of that record
236 if (szlThis.cy > prliThis->szlContent.cy)
237 prliThis->szlContent.cy = szlThis.cy;
238
239 // remember max width of this record's column
240 // for the entire column
241 if (szlThis.cx > pColumn->cxWidestRecord)
242 pColumn->cxWidestRecord = szlThis.cx;
243
244 if (!pColNode->pNext)
245 {
246 // last column of outer loop:
247
248 // clear refresh flags, if any
249 prliThis->flInvalidate = 0;
250
251 // compute box cy from content
252 prliThis->szlBox.cy = prliThis->szlContent.cy
253 + pData->CnrInfo.cyLineSpacing;
254
255 // store record's position in workarea coords
256 prliThis->ptl.x = 0; // we're in Details view
257 prliThis->ptl.y = yNow;
258
259 if (!fRefreshAll)
260 // invalidate this record's rectangle
261 lstAppendItem(&llInvalidateRecords,
262 prliThis);
263 }
264 }
265
266 // go down for next record
267 yNow += prliThis->szlBox.cy;
268
269 } // if (!(prliThis->flRecordAttr & CRA_FILTERED))
270
271 pRecNode = pRecNode->pPrevious;
272
273 } // while (pRecNode)
274
275 if (!(cxContent = pfi->cxWidth))
276 {
277 // this is an auto-size column:
278
279 if (pData->CnrInfo.flWindowAttr & CA_DETAILSVIEWTITLES)
280 {
281 if (*pfl & DDFL_INVALIDATECOLUMNS)
282 {
283 // compute space needed for title
284
285 pColumn->szlTitleData.cx
286 = pColumn->szlTitleData.cy
287 = 0;
288
289 if (pfi->flTitle & CFA_BITMAPORICON)
290 // @@todo
291 ;
292 else
293 {
294 ULONG cLines;
295 gpihCalcTextExtent(hps,
296 (PCSZ)pfi->pTitleData,
297 &pColumn->szlTitleData.cx,
298 &cLines);
299 pColumn->szlTitleData.cy = cLines * (pData->fm.lMaxBaselineExt + pData->fm.lExternalLeading);
300 }
301 }
302
303 cxContent = max(pColumn->cxWidestRecord, pColumn->szlTitleData.cx);
304
305 if (pColumn->szlTitleData.cy > cyColTitlesContent)
306 cyColTitlesContent = pColumn->szlTitleData.cy;
307 }
308 else
309 {
310 pColumn->szlTitleData.cx
311 = 0;
312 }
313 }
314
315 // check if column needs invalidating
316 if ( (fRefreshAll)
317 || (pColumn->xLeft != xCurrent)
318 || (pColumn->cxContent != cxContent)
319 )
320 {
321 pColumn->xLeft = xCurrent;
322 pColumn->cxContent = cxContent;
323
324 if (!fRefreshAll)
325 InvalidateColumn(pData,
326 pColumn);
327 }
328
329 // go one column to the right
330 xCurrent += cxContent + 2 * COLUMN_PADDING_X;
331
332 if ( (pfi->flData & CFA_SEPARATOR)
333 && (pColNode->pNext)
334 )
335 xCurrent += DEFAULT_BORDER_WIDTH;
336
337 ++cColumn;
338 }
339 }
340
341 // has workarea changed?
342 if ( (pData->scrw.szlWorkarea.cx != xCurrent)
343 || (pData->scrw.szlWorkarea.cy != yNow)
344 )
345 {
346 pData->scrw.szlWorkarea.cx = xCurrent;
347 pData->scrw.szlWorkarea.cy = yNow;
348 *pfl |= DDFL_WORKAREACHANGED;
349 }
350
351 // has details title box changed?
352 if (pData->CnrInfo.flWindowAttr & CA_DETAILSVIEWTITLES)
353 cyColTitlesBox = cyColTitlesContent
354 + 2 * COLUMN_PADDING_Y
355 + pData->CnrInfo.cyLineSpacing;
356
357 if ( (cyColTitlesContent != pData->cyColTitlesContent)
358 || (cyColTitlesBox != pData->cyColTitlesBox)
359 )
360 {
361 pData->cyColTitlesContent = cyColTitlesContent;
362 pData->cyColTitlesBox = cyColTitlesBox;
363
364 *pfl |= DDFL_WINDOWSIZECHANGED | DDFL_WORKAREACHANGED;
365 }
366
367 // now invalidate records
368 if (fRefreshAll)
369 WinInvalidateRect(pData->dwdMain.hwnd, NULL, TRUE);
370 else
371 {
372 PLISTNODE pNode = lstQueryFirstNode(&llInvalidateRecords);
373 while (pNode)
374 {
375 ctnrRepaintRecord(pData, (PRECORDLISTITEM)pNode->pItemData);
376 pNode = pNode->pNext;
377 }
378 }
379
380 lstClear(&llInvalidateRecords);
381}
382
383/*
384 *@@ DetailsCreate:
385 * implementation for WM_CREATE in fnwpCnrDetails.
386 *
387 * We receive the CNRDATA as the create param.
388 */
389
390MRESULT DetailsCreate(HWND hwnd, MPARAM mp1, MPARAM mp2)
391{
392 HWND hwndParent;
393
394 if ( (!mp1)
395 || (!mp2)
396 || (!(hwndParent = (((PCREATESTRUCT)mp2)->hwndParent)))
397 )
398 return (MRESULT)TRUE;
399
400 WinSetWindowPtr(hwnd, QWL_USER + 1, mp1);
401
402 // initialize DEFWINDOWDATA
403 ctnrInit(hwnd,
404 mp2,
405 winhQueryWindowStyle(hwndParent), // main container
406 &((PCNRDATA)mp1)->dwdContent);
407
408 // the cnr appears to always grab the focus on creation
409 WinSetFocus(HWND_DESKTOP, hwnd); // @@todo how do we do this on creation only?
410
411 return (MRESULT)FALSE;
412}
413
414/*
415 *@@ DetailsPaint:
416 * implementation for WM_PAINT in fnwpCnrDetails.
417 *
418 */
419
420VOID DetailsPaint(PCNRDATA pData)
421{
422 HPS hps;
423 RECTL rclClip;
424
425 if (hps = WinBeginPaint(pData->dwdContent.hwnd, NULLHANDLE, &rclClip))
426 {
427 PLISTNODE pColNode,
428 pRecNodeFirst2Paint = NULL; // first one to paint
429
430 LONG yPadding = pData->CnrInfo.cyLineSpacing / 2;
431 ULONG cColumn = 0;
432 BOOL fFirstCol = TRUE;
433
434 BOOL fHasFocus;
435 HWND hwndFocus;
436
437 if ( (hwndFocus = WinQueryFocus(HWND_DESKTOP))
438 && (hwndFocus == pData->dwdContent.hwnd)
439 )
440 fHasFocus = TRUE;
441 else
442 fHasFocus = FALSE;
443
444 gpihSwitchToRGB(hps);
445
446#if 0
447 {
448 ULONG ulTimeNow;
449 DosQuerySysInfo(QSV_MS_COUNT, QSV_MS_COUNT,
450 &ulTimeNow,
451 sizeof(ulTimeNow));
452 WinFillRect(hps,
453 &rclClip,
454 ulTimeNow & 0xFFFFFF);
455 }
456#else
457 WinFillRect(hps,
458 &rclClip,
459 ctlQueryColor(&pData->dwdMain, CTLCOL_BGND));
460#endif
461
462 // skip columns that are not in paint rectangle
463 pColNode = lstQueryFirstNode(&pData->llColumns);
464 while (pColNode)
465 {
466 PDETAILCOLUMN pCol = (PDETAILCOLUMN)pColNode->pItemData;
467 const FIELDINFO *pfi = pCol->pfi;
468 if ( (!(pfi->flData & CFA_INVISIBLE))
469 && (pCol->xLeft + pCol->cxContent - pData->scrw.ptlScrollOfs.x >= rclClip.xLeft)
470 )
471 break;
472
473 pColNode = pColNode->pNext;
474 }
475
476 // now loop for the remaining columns (go RIGHT)
477 while (pColNode)
478 {
479 PDETAILCOLUMN pCol = (PDETAILCOLUMN)pColNode->pItemData;
480 const FIELDINFO *pfi = pCol->pfi;
481 PLISTNODE pRecNode;
482
483 if (!(pfi->flData & CFA_INVISIBLE))
484 {
485 RECTL rclRecc,
486 rcl;
487
488 rcl.xLeft = pCol->xLeft + COLUMN_PADDING_X - pData->scrw.ptlScrollOfs.x;
489 rcl.xRight = rcl.xLeft + pCol->cxContent;
490
491 if (fFirstCol)
492 {
493 // first time we get here: skip all records that
494 // are outside the paint rectangle
495
496 pRecNode = lstQueryFirstNode(&pData->llRootRecords);
497 while (pRecNode)
498 {
499 PRECORDLISTITEM prliThis = (PRECORDLISTITEM)pRecNode->pItemData;
500
501 if (!(prliThis->flRecordAttr & CRA_FILTERED))
502 {
503 ctnrGetRecordRect(pData, &rclRecc, prliThis);
504 if (rclRecc.yBottom <= rclClip.yTop)
505 {
506 pRecNodeFirst2Paint = pRecNode;
507
508 break;
509 }
510 }
511
512 pRecNode = pRecNode->pNext;
513 }
514 }
515
516 // now inner loop for the remaining records (go DOWN)
517 pRecNode = pRecNodeFirst2Paint;
518 while (pRecNode)
519 {
520 PRECORDLISTITEM prliThis = (PRECORDLISTITEM)pRecNode->pItemData;
521
522 if (!(prliThis->flRecordAttr & CRA_FILTERED))
523 {
524 const RECORDCORE *preccThis = prliThis->precc;
525 PVOID pColumnData = (PVOID)((PBYTE)preccThis + pfi->offStruct);
526 CHAR szTemp[100];
527 PCSZ pcsz = NULL;
528 LONG lcolBackground,
529 lcolForeground;
530
531 ctnrGetRecordRect(pData, &rclRecc, prliThis);
532
533 PMPF_RECT("rclRecc: ", &rclRecc);
534
535 if (prliThis->flRecordAttr & CRA_SELECTED)
536 {
537 lcolBackground = ctlQueryColor(&pData->dwdMain, CNRCOL_HILITEBGND);
538 lcolForeground = ctlQueryColor(&pData->dwdMain, CNRCOL_HILITEFGND);
539
540 if (fFirstCol)
541 WinFillRect(hps,
542 &rclRecc,
543 lcolBackground);
544 }
545 else
546 {
547 lcolBackground = ctlQueryColor(&pData->dwdMain, CTLCOL_BGND);
548 lcolForeground = ctlQueryColor(&pData->dwdMain, CTLCOL_FGND);
549 }
550
551 GpiSetColor(hps, lcolForeground);
552 GpiSetBackColor(hps, lcolBackground);
553
554 rcl.yTop = rclRecc.yTop - yPadding;
555 rcl.yBottom = rcl.yTop - prliThis->szlContent.cy;
556
557 if ( (fFirstCol)
558 && (prliThis->flRecordAttr & CRA_CURSORED)
559 )
560 {
561 RECTL rcl2;
562 rcl2.xLeft = rclRecc.xLeft + 1;
563 rcl2.xRight = rclRecc.xRight - 2;
564 rcl2.yBottom = rclRecc.yBottom + 1;
565 rcl2.yTop = rclRecc.yTop - 2;
566 GpiSetLineType(hps, LINETYPE_ALTERNATE);
567 gpihBox(hps,
568 DRO_OUTLINE,
569 &rcl2);
570 GpiSetLineType(hps, LINETYPE_DEFAULT);
571 }
572
573 switch (pfi->flData & ( CFA_BITMAPORICON
574 | CFA_DATE
575 | CFA_STRING
576 | CFA_TIME
577 | CFA_ULONG))
578 {
579 case CFA_BITMAPORICON:
580 // @@todo
581 break;
582
583 case CFA_STRING:
584 pcsz = *(PSZ*)pColumnData;
585 break;
586
587 case CFA_DATE:
588 nlsDate(&pData->cs,
589 szTemp,
590 ((PCDATE)pColumnData)->year,
591 ((PCDATE)pColumnData)->month,
592 ((PCDATE)pColumnData)->day);
593 pcsz = szTemp;
594 break;
595
596 case CFA_TIME:
597 nlsTime(&pData->cs,
598 szTemp,
599 ((PCTIME)pColumnData)->hours,
600 ((PCTIME)pColumnData)->minutes,
601 ((PCTIME)pColumnData)->seconds);
602 pcsz = szTemp;
603 break;
604
605 case CFA_ULONG:
606 nlsThousandsULong(szTemp,
607 *((PULONG)pColumnData),
608 pData->cs.cs.cThousands);
609 pcsz = szTemp;
610 break;
611 }
612
613 if (pcsz)
614 ctnrDrawString(hps,
615 pcsz,
616 &rcl,
617 pfi->flData,
618 &pData->fm);
619
620 // if we're outside the paint rect now,
621 // we can quit
622 // _Pmpf(("rcl.yBottom: %d, rclClip.yBottom: %d",
623 // rcl.yBottom,
624 // rclClip.yBottom));
625 if (rclRecc.yBottom <= rclClip.yBottom)
626 break;
627
628 } // if (!(prliThis->flRecordAttr & CRA_FILTERED))
629
630 pRecNode = pRecNode->pNext;
631
632 } // while (pRecNode)
633
634 // paint vertical separators after this column?
635 if (pfi->flData & CFA_SEPARATOR)
636 {
637 POINTL ptl;
638 GpiSetColor(hps, ctlQueryColor(&pData->dwdMain, CNRCOL_BORDER));
639 ptl.x = rcl.xRight + COLUMN_PADDING_X;
640 ptl.y = pData->dwdContent.szlWin.cy;
641 GpiMove(hps,
642 &ptl);
643 ptl.y = 0;
644 GpiLine(hps,
645 &ptl);
646 }
647
648 fFirstCol = FALSE;
649
650 ++cColumn;
651
652 // we're done if this column is outside the
653 // paint rectangle
654 if ( (!(pCol->pfi->flData & CFA_INVISIBLE))
655 && (pCol->xLeft + pCol->cxContent - pData->scrw.ptlScrollOfs.x >= rclClip.xRight)
656 )
657 break; // while (pColNode)
658
659 } // if (!(pfi->flData & CFA_INVISIBLE))
660
661 pColNode = pColNode->pNext;
662
663 } // while (pColNode)
664
665 WinEndPaint(hps);
666 }
667}
668
669/*
670 *@@ FindRecordFromMouseY:
671 *
672 */
673
674PRECORDLISTITEM FindRecordFromMouseY(PCNRDATA pData,
675 SHORT y)
676{
677 // convert y clickpos from window to workspace coordinates
678 LONG deltaWorkspace = ( pData->scrw.szlWorkarea.cy
679 - pData->dwdContent.szlWin.cy)
680 - pData->scrw.ptlScrollOfs.y;
681 LONG yWorkspace = (LONG)y + deltaWorkspace;
682
683 PLISTNODE pRecNode = lstQueryFirstNode(&pData->llRootRecords);
684 while (pRecNode)
685 {
686 PRECORDLISTITEM prliThis = (PRECORDLISTITEM)pRecNode->pItemData;
687
688 if (!(prliThis->flRecordAttr & CRA_FILTERED))
689 {
690 if (prliThis->ptl.y < yWorkspace)
691 return prliThis;
692 }
693
694 pRecNode = pRecNode->pNext;
695 }
696
697 return NULL;
698}
699
700/*
701 *@@ DeselectExcept:
702 *
703 */
704
705VOID DeselectExcept(PCNRDATA pData,
706 PRECORDLISTITEM prliMouse)
707{
708 // deselect all selected
709 PLISTNODE pRecNode;
710 pRecNode = lstQueryFirstNode(&pData->llRootRecords);
711 while (pRecNode)
712 {
713 PRECORDLISTITEM prliThis = (PRECORDLISTITEM)pRecNode->pItemData;
714
715 if ( (prliThis != prliMouse)
716 && (prliThis->flRecordAttr & CRA_SELECTED)
717 )
718 {
719 ctnrChangeEmphasis(pData,
720 prliThis,
721 FALSE,
722 CRA_SELECTED);
723 }
724
725 pRecNode = pRecNode->pNext;
726 }
727}
728
729/*
730 *@@ DetailsSingleSelect:
731 * implementation for WM_SINGLESELECT in fnwpCnrDetails.
732 */
733
734MRESULT DetailsSingleSelect(PCNRDATA pData,
735 SHORT y)
736{
737 if (pData)
738 {
739 BOOL fCtrl = WinGetKeyState(HWND_DESKTOP, VK_CTRL) & 0x8000;
740 ULONG ulSel = ctnrQuerySelMode(pData);
741
742 // find record under mouse
743 PRECORDLISTITEM prliMouse = FindRecordFromMouseY(pData, y);
744
745 if ( (ulSel == CCS_SINGLESEL)
746 || (!fCtrl)
747 )
748 {
749 DeselectExcept(pData, prliMouse);
750 }
751
752 if (prliMouse) // can be null with click on whitespace
753 {
754 // when ctrl is pressed, toggle the state of
755 // the record under the mouse; otherwise select it
756 ctnrChangeEmphasis(pData,
757 prliMouse,
758 (!fCtrl) || (!(prliMouse->flRecordAttr & CRA_SELECTED)),
759 CRA_SELECTED | CRA_CURSORED);
760 }
761
762 return (MRESULT)TRUE;
763 }
764
765 return (MRESULT)FALSE;
766}
767
768/*
769 *@@ DetailsBeginSelect:
770 * implementation for WM_BEGINSELECT in fnwpCnrDetails.
771 *
772 */
773
774MRESULT DetailsBeginSelect(PCNRDATA pData,
775 SHORT y)
776{
777 if (pData)
778 {
779 BOOL fCtrl = WinGetKeyState(HWND_DESKTOP, VK_CTRL) & 0x8000;
780 ULONG ulSel = ctnrQuerySelMode(pData);
781 PRECORDLISTITEM prliMouse;
782
783 // find record under mouse
784 prliMouse
785 = pData->prliSwipingFirst
786 = FindRecordFromMouseY(pData, y);
787
788 if ( (ulSel == CCS_SINGLESEL)
789 || (!fCtrl)
790 )
791 {
792 DeselectExcept(pData, prliMouse );
793 }
794
795 if (prliMouse )
796 {
797 // when ctrl is pressed, toggle the state of
798 // the record under the mouse; otherwise select it;
799 // perform this operation on all the records that
800 // user swipes over (seems to be how pm cnr does it)
801 pData->fSwipeTurnOn = (!fCtrl) || (!(prliMouse->flRecordAttr & CRA_SELECTED));
802
803 ctnrChangeEmphasis(pData,
804 prliMouse ,
805 pData->fSwipeTurnOn,
806 CRA_SELECTED | CRA_CURSORED);
807 WinSetCapture(HWND_DESKTOP, pData->dwdContent.hwnd);
808 }
809
810 return (MRESULT)TRUE;
811 }
812
813 return (MRESULT)FALSE;
814}
815
816/*
817 *@@ DetailsMouseMove:
818 * implementation for WM_MOUSEMOVE in fnwpCnrDetails.
819 */
820
821MRESULT DetailsMouseMove(PCNRDATA pData,
822 MPARAM mp1,
823 MPARAM mp2)
824{
825 if (pData)
826 {
827 if (pData->prliSwipingFirst)
828 {
829 // we're swiping:
830 PRECORDLISTITEM prliThis;
831 if (prliThis = FindRecordFromMouseY(pData,
832 SHORT2FROMMP(mp1)))
833 ctnrChangeEmphasis(pData,
834 prliThis,
835 pData->fSwipeTurnOn,
836 CRA_SELECTED | CRA_CURSORED);
837 }
838
839 return ctlDefWindowProc(&pData->dwdContent, WM_MOUSEMOVE, mp1, mp2);
840 }
841
842 return (MRESULT)FALSE;
843}
844
845/*
846 *@@ DetailsEndSelect:
847 * implementation for WM_ENDSELECT in fnwpCnrDetails.
848 */
849
850MRESULT DetailsEndSelect(PCNRDATA pData)
851{
852 if (pData)
853 {
854 if (pData->prliSwipingFirst)
855 {
856 WinSetCapture(HWND_DESKTOP, NULLHANDLE);
857 pData->prliSwipingFirst = NULL;
858 }
859
860 return (MRESULT)TRUE;
861 }
862
863 return (MRESULT)FALSE;
864}
865
866/*
867 *@@ DetailsOpen:
868 * implementation for WM_OPEN in fnwpCnrDetails.
869 */
870
871MRESULT DetailsOpen(PCNRDATA pData)
872{
873 if (pData)
874 {
875 ctnrRecordEnter(pData,
876 pData->prliCursored,
877 FALSE); // mouse, not keyboard
878
879 return (MRESULT)TRUE;
880 }
881
882 return (MRESULT)FALSE;
883}
884
885/*
886 *@@ ScrollToRecord:
887 * scrolls the content area so that the given record becomes
888 * visible.
889 *
890 + If fMakeTop == TRUE, we scroll so that the top of the
891 * record's box is scrolled to the top of the window.
892 * Otherwise we scroll so that the bottom of the record's
893 * box is at the bottom of the rectangle.
894 */
895
896VOID ScrollToRecord(PCNRDATA pData,
897 PRECORDLISTITEM prliThis,
898 BOOL fMakeTop,
899 BOOL fForce) // in: if TRUE, we scroll even if the record is already visible
900{
901 RECTL rcl;
902 LONG lNewY = -100;
903
904 ctnrGetRecordRect(pData, &rcl, prliThis);
905 if (fMakeTop)
906 {
907 if ( (rcl.yTop > pData->dwdContent.szlWin.cy)
908 || (fForce)
909 )
910 {
911/*
912ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ Ä¿
913³ ³ ³ pData->scrw.ptlScrollOfs.y
914+-----------------------------+ ÄÙ
915+-----------------------------+
916º º
917º º
918ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍŒ
919³ ³
920ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
921*/
922 lNewY = pData->scrw.szlWorkarea.cy
923 - (prliThis->ptl.y + prliThis->szlBox.cy);
924 }
925 }
926 else if ( (rcl.yBottom < 0)
927 || (fForce)
928 )
929 {
930/*
931ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ Ä¿
932³ ³ ³ pData->scrw.ptlScrollOfs.y
933ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ» ³
934º º ³
935+-----------------------------+ ³
936+-----------------------------+ ÄÙ
937³ ³
938ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
939*/
940 lNewY = pData->scrw.szlWorkarea.cy
941 - pData->dwdContent.szlWin.cy
942 - prliThis->ptl.y;
943 }
944
945 if (lNewY != -100)
946 {
947 POINTL ptlScroll;
948
949 if (lNewY < 0)
950 lNewY = 0;
951 else if (lNewY >= pData->scrw.szlWorkarea.cy)
952 lNewY = pData->scrw.szlWorkarea.cy - 1;
953
954 ptlScroll.x = 0;
955 ptlScroll.y = lNewY - pData->scrw.ptlScrollOfs.y;
956
957 winhScrollWindow(pData->dwdContent.hwnd,
958 NULL,
959 &ptlScroll);
960
961 pData->scrw.ptlScrollOfs.y = lNewY;
962
963 WinPostMsg(pData->dwdMain.hwnd,
964 WM_SEM2,
965 (MPARAM)DDFL_WORKAREACHANGED,
966 0);
967 }
968}
969
970/*
971 *@@ DetailsChar:
972 * implementation for WM_CHAR in fnwpCnrDetails.
973 */
974
975MRESULT DetailsChar(PCNRDATA pData,
976 MPARAM mp1,
977 MPARAM mp2)
978{
979 if (pData)
980 {
981 USHORT usFlags = SHORT1FROMMP(mp1);
982 USHORT usch = SHORT1FROMMP(mp2);
983 USHORT usvk = SHORT2FROMMP(mp2);
984
985 PRECORDLISTITEM prliThis;
986 PLISTNODE pNode;
987 PRECORDLISTITEM prliScrollTo = NULL;
988 BOOL fMakeTop = FALSE,
989 fForce = FALSE;
990
991 if (usFlags & KC_VIRTUALKEY)
992 {
993 switch (usvk)
994 {
995 case VK_ENTER:
996 case VK_NEWLINE:
997 if (!(usFlags & KC_KEYUP))
998 if (pData->prliCursored)
999 ctnrRecordEnter(pData,
1000 pData->prliCursored,
1001 TRUE); // keyboard
1002
1003 return (MRESULT)TRUE;
1004
1005 case VK_SPACE:
1006 if (!(usFlags & KC_KEYUP))
1007 {
1008 if ( (CCS_SINGLESEL != ctnrQuerySelMode(pData))
1009 && (prliThis = pData->prliCursored)
1010 )
1011 {
1012 if (prliThis->flRecordAttr & CRA_SELECTED)
1013 DeselectExcept(pData, NULL);
1014 else
1015 ctnrChangeEmphasis(pData,
1016 prliThis,
1017 TRUE,
1018 CRA_SELECTED);
1019 }
1020 }
1021
1022 return (MRESULT)TRUE;
1023
1024 case VK_DOWN:
1025 case VK_UP:
1026 if (!(usFlags & KC_KEYUP))
1027 {
1028 if ( (prliThis = pData->prliCursored)
1029 && (pNode = ctnrFindListNodeForRecc(pData, prliThis->precc))
1030 )
1031 {
1032 while (TRUE)
1033 {
1034 if (usvk == VK_UP)
1035 {
1036 if (!(pNode = pNode->pPrevious))
1037 break;
1038 }
1039 else
1040 if (!(pNode = pNode->pNext))
1041 break;
1042
1043 prliThis = (PRECORDLISTITEM)pNode->pItemData;
1044 if (!(prliThis->flRecordAttr & CRA_FILTERED))
1045 {
1046 prliScrollTo = prliThis;
1047 fMakeTop = (usvk == VK_UP);
1048 break;
1049 }
1050 }
1051 }
1052 }
1053 break;
1054
1055 case VK_HOME:
1056 case VK_END:
1057 if (!(usFlags & KC_KEYUP))
1058 {
1059 if (usvk == VK_HOME)
1060 pNode = lstQueryFirstNode(&pData->llRootRecords);
1061 else
1062 pNode = lstQueryLastNode(&pData->llRootRecords);
1063
1064 while (pNode)
1065 {
1066 prliThis = (PRECORDLISTITEM)pNode->pItemData;
1067 if (!(prliThis->flRecordAttr & CRA_FILTERED))
1068 {
1069 prliScrollTo = prliThis;
1070 if (usvk == VK_HOME)
1071 fMakeTop = TRUE;
1072 fForce = TRUE;
1073 break;
1074 }
1075
1076 if (usvk == VK_HOME)
1077 pNode = pNode->pNext;
1078 else
1079 pNode = pNode->pPrevious;
1080 }
1081 }
1082 break;
1083
1084 case VK_PAGEDOWN:
1085 if (!(usFlags & KC_KEYUP))
1086 {
1087 // find the bottommost record currently visible in the
1088 // viewport and make that one the topmost
1089 prliScrollTo = FindRecordFromMouseY(pData,
1090 0); // window y
1091 fMakeTop = TRUE;
1092 fForce = TRUE;
1093 }
1094 break;
1095
1096 case VK_PAGEUP:
1097 if (!(usFlags & KC_KEYUP))
1098 {
1099 // find the topmost record currently visible in the
1100 // viewport
1101 prliScrollTo = FindRecordFromMouseY(pData,
1102 pData->dwdContent.szlWin.cy); // window y
1103 // @@todo this is slightly different from the pm cnr behavior
1104 fForce = TRUE;
1105 }
1106 break;
1107 }
1108
1109 if (prliScrollTo)
1110 {
1111 DeselectExcept(pData, prliScrollTo);
1112
1113 ctnrChangeEmphasis(pData,
1114 prliScrollTo,
1115 TRUE,
1116 CRA_SELECTED | CRA_CURSORED);
1117
1118 // now make sure the new record is visible
1119 // in the viewport
1120 ScrollToRecord(pData,
1121 prliScrollTo,
1122 fMakeTop,
1123 fForce);
1124
1125 return (MRESULT)TRUE;
1126 }
1127 }
1128 }
1129
1130 return (MRESULT)FALSE;
1131}
1132
1133/* ******************************************************************
1134 *
1135 * Container details window proc
1136 *
1137 ********************************************************************/
1138
1139/*
1140 *@@ fnwpCnrDetails:
1141 *
1142 */
1143
1144MRESULT EXPENTRY fnwpCnrDetails(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
1145{
1146 MRESULT mrc = 0;
1147 PCNRDATA pData = (PCNRDATA)WinQueryWindowPtr(hwnd, QWL_USER + 1);
1148
1149 switch (msg)
1150 {
1151 /* ******************************************************************
1152 *
1153 * Standard window messages
1154 *
1155 ********************************************************************/
1156
1157 case WM_CREATE:
1158 mrc = DetailsCreate(hwnd, mp1, mp2);
1159 break;
1160
1161 case WM_PAINT:
1162 DetailsPaint(pData);
1163 break;
1164
1165 case WM_SYSCOLORCHANGE:
1166 ctnrPresParamChanged(hwnd, 0);
1167 break;
1168
1169 case WM_PRESPARAMCHANGED:
1170 ctnrPresParamChanged(hwnd, (ULONG)mp1);
1171 break;
1172
1173 /* ******************************************************************
1174 *
1175 * Mouse and keyboard input
1176 *
1177 ********************************************************************/
1178
1179 case WM_MOUSEMOVE:
1180 mrc = DetailsMouseMove(pData,
1181 mp1,
1182 mp2);
1183 break;
1184
1185 case WM_BUTTON1DOWN:
1186 case WM_BUTTON2DOWN:
1187 case WM_BUTTON3DOWN:
1188 WinSetFocus(HWND_DESKTOP, pData->dwdContent.hwnd);
1189 mrc = (MPARAM)TRUE;
1190 break;
1191
1192 case WM_SINGLESELECT:
1193 mrc = DetailsSingleSelect(pData,
1194 SHORT2FROMMP(mp1)); // we only need y in details view
1195 break;
1196
1197 case WM_BEGINSELECT:
1198 mrc = DetailsBeginSelect(pData,
1199 SHORT2FROMMP(mp1));
1200 break;
1201
1202 case WM_ENDSELECT:
1203 mrc = DetailsEndSelect(pData);
1204 break;
1205
1206 case WM_OPEN:
1207 mrc = DetailsOpen(pData);
1208 break;
1209
1210 case WM_CHAR:
1211 mrc = DetailsChar(pData, mp1, mp2);
1212 break;
1213
1214 /* ******************************************************************
1215 *
1216 * Direct manipulation
1217 *
1218 ********************************************************************/
1219
1220 case DM_DRAGOVER:
1221 case DM_DRAGLEAVE:
1222 case DM_DROPNOTIFY:
1223 case DM_DROP:
1224 case DM_DROPHELP:
1225
1226 default:
1227 if (pData)
1228 mrc = ctlDefWindowProc(&pData->dwdContent, msg, mp1, mp2);
1229 break;
1230 }
1231
1232 return mrc;
1233}
1234
Note: See TracBrowser for help on using the repository browser.