source: trunk/src/helpers/cnrh.c@ 142

Last change on this file since 142 was 129, checked in by umoeller, 24 years ago

Executable updates, mostly.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 80.9 KB
Line 
1
2/*
3 *@@sourcefile cnrh.c:
4 * contains various PM container helper functions.
5 *
6 * These functions used to be in winh.c, but they became
7 * too many finally, so these were moved to this file with
8 * V0.9.0.
9 *
10 * Usage: All PM programs.
11 *
12 * Function prefixes:
13 * -- cnrh* container helper functions (was: winh* before V0.9.0)
14 *
15 * Note: Version numbering in this file relates to XWorkplace version
16 * numbering.
17 *
18 *@@added V0.9.0 [umoeller]
19 *@@header "helpers\cnrh.h"
20 */
21
22/*
23 * Copyright (C) 1997-2000 Ulrich M”ller.
24 * This file is part of the "XWorkplace helpers" source package.
25 * This is free software; you can redistribute it and/or modify
26 * it under the terms of the GNU General Public License as published
27 * by the Free Software Foundation, in version 2 as it comes in the
28 * "COPYING" file of the XWorkplace main distribution.
29 * This program is distributed in the hope that it will be useful,
30 * but WITHOUT ANY WARRANTY; without even the implied warranty of
31 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
32 * GNU General Public License for more details.
33 */
34
35#define OS2EMX_PLAIN_CHAR
36 // this is needed for "os2emx.h"; if this is defined,
37 // emx will define PSZ as _signed_ char, otherwise
38 // as unsigned char
39
40#define INCL_WINWINDOWMGR
41#define INCL_WINRECTANGLES
42#define INCL_WININPUT
43#define INCL_WINPOINTERS
44#define INCL_WINMENUS
45#define INCL_WINSYS
46#define INCL_WINSTDDRAG
47#define INCL_WINSTDCNR
48
49#define INCL_GPILOGCOLORTABLE
50#define INCL_GPIPRIMITIVES
51#include <os2.h>
52
53#include <stdlib.h>
54#include <string.h>
55#include <stdio.h>
56
57#include "setup.h" // code generation and debugging options
58
59#include "helpers\winh.h"
60#include "helpers\cnrh.h"
61
62#pragma hdrstop
63
64/*
65 *@@category: Helpers\PM helpers\Container helpers
66 * See cnrh.c.
67 */
68
69/*
70 *@@category: Helpers\PM helpers\Container helpers\Details view helpers
71 * these functions aid you in setting up the FIELDINFO structures
72 * for a container Details view. Use cnrhSetFieldInfos for that.
73 */
74
75/* ******************************************************************
76 *
77 * Details view field infos
78 *
79 ********************************************************************/
80
81/*
82 *@@ cnrhClearFieldInfos:
83 * removes all existing FIELDINFO's from
84 * the given container. Returns the number
85 * of remaining FIELDINFO's, which should
86 * be 0, or -1 upon errors.
87 *
88 *@@added V0.9.1 (2000-02-13) [umoeller]
89 */
90
91ULONG cnrhClearFieldInfos(HWND hwndCnr,
92 BOOL fInvalidate) // in: if TRUE, invalidate container
93{
94 ULONG ulFlags = CMA_FREE;
95 if (fInvalidate)
96 ulFlags |= CMA_INVALIDATE;
97
98 return (ULONG)(WinSendMsg(hwndCnr,
99 CM_REMOVEDETAILFIELDINFO,
100 (MPARAM)NULL,
101 MPFROM2SHORT(0, // all
102 ulFlags)));
103}
104
105/*
106 *@@ cnrhSetFieldInfo:
107 * this sets a FIELDINFO structure to the given
108 * data. Note that ppFieldInfo is a double pointer
109 * to the actual FIELDINFO data.
110 *
111 * This gets called from cnrhSetFieldInfos to set
112 * each individual field info.
113 * You can also use this function separately with
114 * the cnrhAllocFieldInfos macro defined in cnrh.h.
115 *
116 * After setting the data, <B>*ppFieldInfo</B> is advanced
117 * to the next FIELDINFO structure so that you can
118 * call this function several times for all the
119 * FIELDINFOS that you have allocated. After the last
120 * column, this pointer will be NULL.
121 *
122 * Since the pointer is modified, do not invoke this
123 * function on the original pointer returned from
124 * cnrhAllocFieldInfos, because you'll need that
125 * pointer for cnrhAllocFieldInfos later.
126 *
127 * <B>Example usage</B>:
128 *
129 + PFIELDINFO pFieldInfoFirst, pFieldInfo2;
130 + if (pFieldInfoFirst = cnrhAllocFieldInfos(hwndFilesCnr, NO_OF_COLUMNS))
131 + // macro defined in cnrh.h
132 + {
133 + pFieldInfo2 = pFieldInfoFirst;
134 +
135 + // "File name" column
136 + cnrhSetFieldInfo(&pFieldInfo2,
137 + FIELDOFFSET(RECORDCORE, pszIcon),
138 + // icon text offset in original RECORDCORE
139 + "File name",
140 + CFA_STRING,
141 + CFA_LEFT,
142 + FALSE); // no draw lines
143 + // "Size" column
144 + cnrhSetFieldInfo(&pFieldInfo2,
145 + FIELDOFFSET(FILERECORDCORE, ulSize),
146 + // size data field in sample extended RECORDCORE
147 + "Size",
148 + CFA_ULONG,
149 + CFA_RIGHT,
150 + FALSE); // no draw lines
151 + ... // set other field infos
152 + }
153 +
154 + // insert field infos
155 + cnrhInsertFieldInfos(hwndFilesCnr,
156 + pFieldInfoFirst,
157 + NO_OF_COLUMNS);
158 *
159 *@@added V0.9.0 [umoeller]
160 *@@changed V0.9.1 (99-12-18) [umoeller]: fixed memory leak
161 */
162
163VOID cnrhSetFieldInfo(PFIELDINFO *ppFieldInfo2, // in/out: double ptr to FIELDINFO
164 ULONG ulFieldOffset, // in: FIELDOFFSET(YOURRECORDCORE, yourField)
165 PSZ pszColumnTitle, // in: column title; this must be a static string!!
166 ULONG ulDataType, // in: column data type (CFA_* flags)
167 ULONG ulOrientation, // in: vertical and horizontal orientation (CFA_* flags)
168 BOOL fDrawLines) // in: if TRUE, we'll draw lines around the columns
169{
170 PFIELDINFO pInfo;
171 if ( (ppFieldInfo2)
172 && (pInfo = *ppFieldInfo2)
173 )
174 {
175 ULONG flData = ulDataType | ulOrientation;
176 if (fDrawLines)
177 flData |= CFA_HORZSEPARATOR | CFA_SEPARATOR;
178
179 pInfo->cb = sizeof(FIELDINFO);
180 pInfo->flData = flData;
181 pInfo->flTitle = CFA_FITITLEREADONLY | ulOrientation;
182 pInfo->offStruct = ulFieldOffset;
183 pInfo->pTitleData = pszColumnTitle; // strdup removed, V0.9.1 (99-12-18) [umoeller]
184 pInfo->pUserData = NULL;
185 pInfo->cxWidth = 0;
186 *ppFieldInfo2 = pInfo->pNextFieldInfo;
187 }
188}
189
190/*
191 *@@ cnrhInsertFieldInfos:
192 * this inserts field infos for Details view
193 * into the specified container.
194 *
195 * pFieldInfoFirst should be the PFIELDINFO
196 * returned by cnrhAllocFieldInfos.
197 *
198 * This inserts the FIELDINFOs at the end,
199 * should any columns already exist in the container.
200 * Also, the container is invalidated.
201 *
202 * Returns the return value of CM_INSERTDETAILFIELDINFO,
203 * which is the total no. of field infos in the container
204 * or null upon errors.
205 *
206 *@@added V0.9.0 [umoeller]
207 */
208
209ULONG cnrhInsertFieldInfos(HWND hwndCnr, // in: cnr for Details view
210 PFIELDINFO pFieldInfoFirst, // in: first field info as returned
211 // by cnrhAllocFieldInfos
212 ULONG ulFieldCount) // in: no. of field infos
213{
214 FIELDINFOINSERT fii;
215 fii.cb = sizeof(FIELDINFOINSERT);
216 fii.pFieldInfoOrder = (PFIELDINFO)CMA_END;
217 fii.fInvalidateFieldInfo = TRUE;
218 fii.cFieldInfoInsert = ulFieldCount;
219
220 return ((ULONG)WinSendMsg(hwndCnr,
221 CM_INSERTDETAILFIELDINFO,
222 (MPARAM)pFieldInfoFirst,
223 (MPARAM)&fii));
224}
225
226/*
227 *@@ cnrhSetFieldInfos:
228 * this combines cnrhAllocFieldInfos,
229 * cnrhSetFieldInfo, and cnrhInsertFieldInfos
230 * into a one-shot func for setting details view
231 * column field infos. This one has proven to
232 * be EXTREMELY useful.
233 *
234 * To pass all the arguments normally passed to
235 * cnrhSetFieldInfo, we use an array of XFIELDINFO
236 * structures, which takes the same parameters.
237 *
238 * <B>XFIELDINFO.ulDataType</B> specifies the data type of the record
239 * core field. This can be one of the following:
240 * -- CFA_BITMAPORICON: bit-map or icon data
241 * -- CFA_DATE: date format (CDATE structure)
242 * -- CFA_STRING: null-terminated string
243 * -- CFA_TIME: time format (CTIME structure)
244 * -- CFA_ULONG: unsigned number data
245 *
246 * You can add the following optional flags to
247 * ulDataType:
248 * -- CFA_FIREADONLY (CFA_STRING only): disable editing
249 * -- CFA_INVISIBLE: make column invisible
250 * -- CFA_OWNER: enable owner draw for this column
251 *
252 * If (fDrawLines == TRUE), we'll automatically add
253 * CFA_HORZSEPARATOR | CFA_SEPARATOR.
254 *
255 * <B>XFIELDINFO.ulOrientation</B> specifies the vertical and
256 * horizontal orientation of both the column title
257 * and the column data. This should be OR'ed from
258 * the following
259 * -- CFA_CENTER, CFA_LEFT, CFA_RIGHT (horizontal);
260 * the default is CFA_LEFT
261 * -- CFA_BOTTOM, CFA_TOP, CFA_VCENTER (vertical);
262 * the default is CFA_VCENTER.
263 *
264 * Note that the container automatically displays
265 * data according to the settings in the "Country" object.
266 *
267 * The column title will always be set to string format
268 * and CFA_FITITLEREADONLY.
269 *
270 * <B>XFIELDINFO.ulFieldOffset</B> should be set to the return value
271 * of the FIELDOFFSET macro, which is also redefined in
272 * cnrh.h (to work with C++).
273 *
274 * The return value is the PFIELDINFO which corresponds
275 * to the column index specified in ulFieldReturn, or
276 * NULL upon errors. This is useful for setting the
277 * position of the split bar afterwards (using the
278 * cnrhSetSplitBarAfter macro).
279 *
280 * Example usage:
281 + XFIELDINFO xfi[3];
282 + PFIELDINFO pfi;
283 +
284 + xfi[0].ulFieldOffset = FIELDOFFSET(DATABASERECORD, pszApplication);
285 + xfi[0].pszColumnTitle = "Application";
286 + xfi[0].ulDataType = CFA_STRING;
287 + xfi[0].ulOrientation = CFA_LEFT;
288 +
289 + xfi[1].ulFieldOffset = FIELDOFFSET(RECORDCORE, pszIcon);
290 + xfi[1].pszColumnTitle = "Package name";
291 + xfi[1].ulDataType = CFA_STRING;
292 + xfi[1].ulOrientation = CFA_LEFT;
293 +
294 + xfi[2].ulFieldOffset = FIELDOFFSET(DATABASERECORD, pszAuthor);
295 + xfi[2].pszColumnTitle = "Author";
296 + xfi[2].ulDataType = CFA_STRING;
297 + xfi[2].ulOrientation = CFA_CENTER;
298 +
299 + pfi = cnrhSetFieldInfos(hwndCnr,
300 + &xfi[0],
301 + (sizeof(xfi) / sizeof(XFIELDINFO)),
302 + // smart way of calculating the array item count
303 + FALSE, // no draw lines
304 + 0); // return first column
305 *
306 *@@added V0.9.0 [umoeller]
307 */
308
309PFIELDINFO cnrhSetFieldInfos(HWND hwndCnr, // in: container hwnd
310 PXFIELDINFO paxfi, // in: pointer to an array of ulFieldCount XFIELDINFO structures
311 ULONG ulFieldCount, // in: no. of items in paxfi array (> 0)
312 BOOL fDrawLines, // in: if TRUE, we'll draw lines around the columns
313 ULONG ulFieldReturn) // in: the column index to return as PFIELDINFO
314{
315 PFIELDINFO pFieldInfoFirst,
316 pFieldInfo2,
317 pFieldInfoReturn = NULL;
318
319 if ((pFieldInfoFirst = cnrhAllocFieldInfos(hwndCnr, ulFieldCount)))
320 {
321 ULONG ul = 0;
322 PXFIELDINFO pxfi = NULL;
323
324 pFieldInfo2 = pFieldInfoFirst;
325 pxfi = paxfi;
326 for (ul = 0; ul < ulFieldCount; ul++)
327 {
328 if (ul == ulFieldReturn)
329 // set return value
330 pFieldInfoReturn = pFieldInfo2;
331
332 // set current field info;
333 // this will modify pFieldInfo to point to the next
334 cnrhSetFieldInfo(&pFieldInfo2,
335 pxfi->ulFieldOffset,
336 pxfi->pszColumnTitle,
337 pxfi->ulDataType,
338 pxfi->ulOrientation,
339 fDrawLines);
340 pxfi++;
341 }
342
343 // insert field infos
344 if (cnrhInsertFieldInfos(hwndCnr,
345 pFieldInfoFirst,
346 ulFieldCount) == 0)
347 pFieldInfoReturn = NULL;
348 }
349
350 return (pFieldInfoReturn);
351}
352
353/*
354 *@@category: Helpers\PM helpers\Container helpers\Record core helpers
355 * functions for allocating and inserting records more easily.
356 *
357 * Use cnrhAllocRecords first to allocate, then cnrhInsertRecords
358 * to insert the records.
359 */
360
361/* ******************************************************************
362 *
363 * Record core management
364 *
365 ********************************************************************/
366
367/*
368 *@@ cnrhAllocRecords:
369 * this is a shortcut to allocating memory for
370 * container record cores.
371 *
372 * If (ulCount == 1), this returns the new record core.
373 * If (ulCount > 1), this returns the _first_ record
374 * core; the following record cores may be reached
375 * by following the RECORDCORE.preccNextRecord pointers
376 * (i.e. we have a linked list here).
377 *
378 * Returns NULL on errors (what CM_ALLOCRECORD returns).
379 *
380 * The record cores returned by the container are
381 * automatically zeroed out, and their "cb" field
382 * is automatically set to the size of the record core.
383 *
384 * Note that this function presently does _not_ work
385 * with MINIRECORDCOREs.
386 *
387 *@@changed V0.9.0 [umoeller]: function prototype changed to allocate more than one record
388 */
389
390PRECORDCORE cnrhAllocRecords(HWND hwndCnr, // in: cnr to allocate from
391 ULONG cbrecc,
392 // in: total size of your record core.
393 // If you're using the default recc's, this
394 // must be sizeof(RECORDCORE).
395 ULONG ulCount) // in: number of records to allocate (> 0)
396{
397 PRECORDCORE precc;
398 precc = (PRECORDCORE)WinSendMsg(hwndCnr,
399 CM_ALLOCRECORD,
400 (MPARAM)(cbrecc-sizeof(RECORDCORE)),
401 (MPARAM)ulCount);
402
403 return (precc);
404}
405
406/*
407 *@@ cnrhInsertRecords:
408 * shortcut to inserting record cores into a container
409 * which have been allocated using cnrhAllocRecords.
410 *
411 * If (<B>ulCount</B> == 1), this inserts precc.
412 * If (ulCount > 1), this assumes precc to be the first
413 * record to be inserted, while precc->preccNextRecord
414 * must point to the next record in a linked list (as
415 * returned with cnrhAllocRecords). (changed V0.9.0)
416 *
417 * Note that ulCount here must be exactly the same as
418 * specified with cnrhAllocRecords.
419 *
420 * If (<B>pszText</B> != NULL), this will automatically set precc->pszIcon,
421 * precc->pszText, precc->pszName, precc->pszTree to pszText to have
422 * the same record titles in all views. If (pszText == NULL), those
423 * fields will be left alone. (changed V0.9.0)
424 *
425 * <B>flRecordAttr</B> should have the record attributes as
426 * specified in the RECORDCORE structure.
427 *
428 * Emphasis flags:
429 * -- CRA_SELECTED: record is selected
430 * (other records will be deselected in Tree views
431 * and when single selection is enabled)
432 * -- CRA_CURSORED: cursor (keyboard focus) is on the record
433 * (other records will be de-cursored)
434 * -- CRA_SOURCE: record has source emphasis (drag'n'drop,
435 * open context menus)
436 * -- CRA_TARGET: record has target emphasis (drag'n'drop)
437 * -- CRA_PICKED: record picked (Lazy Drag)
438 * -- CRA_INUSE: record has in-use emphasis (diagonal lines,
439 * as with open objects with the WPS)
440 *
441 * Miscellaneous flags:
442 * -- CRA_FILTERED: record has been filtered (is invisible)
443 * -- CRA_DROPONABLE: record can be dropped something upon
444 * -- CRA_RECORDREADONLY: record is read-only (no text edit with Alt+MB1)
445 * -- CRA_EXPANDED: record is expanded (Tree view)
446 * -- CRA_COLLAPSED: record is collapsed (Tree view)
447 *
448 * plus the following half-documented from PMSTDDLG.H (haven't tested these):
449 * -- CRA_IGNORE record is to be ignored
450 * -- CRA_DISABLED has no visible effect, but can be used with
451 * cnrhOwnerDrawRecord
452 * -- CRA_LOCKED maybe "locked in place"?!?
453 * -- CRA_OWNERFREE owner must free record
454 * -- CRA_OWNERDRAW owner must draw record; I'd recommend using
455 * CA_OWNERDRAW in CNRINFO.flWindowAttr instead.
456 *
457 * This func returns the total number of records in the container
458 * or NULL upon errors (return value of CM_INSERTRECORD, changed V0.9.0).
459 *
460 *@@changed V0.9.0 [umoeller]: function prototype changed to insert more than one record
461 *@@changed V0.9.0 [umoeller]: added error checking for pszText
462 *@@changed V0.9.2 (2000-02-19) [umoeller]: added fInvalidate field
463 */
464
465ULONG cnrhInsertRecords(HWND hwndCnr, // in: container to insert into
466 PRECORDCORE preccParent,
467 // in: record core below which precc should
468 // be inserted (tree view only). If NULL, precc
469 // is inserted at "root" level
470 PRECORDCORE precc,
471 // in: record core to insert (allocated using
472 // cnrhAllocRecords)
473 BOOL fInvalidate,
474 const char *pcszText,
475 // in: text for recc. in all views (or NULL)
476 ULONG flRecordAttr,
477 // in: CRA_* flags
478 ULONG ulCount) // in: number of records to insert (> 0)
479{
480 ULONG ulrc = 0;
481 RECORDINSERT ri;
482
483 if (precc)
484 {
485 // RECORDCORE stuff
486 precc->flRecordAttr = flRecordAttr;
487 // precc->preccNextRecord = NULL;
488
489 if (pcszText) // V0.9.0
490 {
491 precc->pszIcon = (PSZ)pcszText;
492 precc->pszText = (PSZ)pcszText;
493 precc->pszName = (PSZ)pcszText;
494 precc->pszTree = (PSZ)pcszText;
495 }
496
497 // setup RECORDINSERT struct
498 ri.cb = sizeof(RECORDINSERT);
499 ri.pRecordOrder = (PRECORDCORE)CMA_END;
500 ri.pRecordParent = (PRECORDCORE)preccParent;
501 ri.zOrder = CMA_TOP;
502 ri.fInvalidateRecord = fInvalidate; // V0.9.2 (2000-02-19) [umoeller]
503 ri.cRecordsInsert = ulCount; // V0.9.0
504
505 ulrc = (ULONG)WinSendMsg(hwndCnr,
506 CM_INSERTRECORD,
507 (MPARAM)precc,
508 (MPARAM)&ri);
509 }
510 return (ulrc);
511}
512
513/*
514 *@@ cnrhInsertRecordAfter:
515 * similar to cnrhInsertRecords, but this inserts
516 * a single record _after_ another one. Parent records
517 * and multiple records are not supported.
518 *
519 *@@added V0.9.0 [umoeller]
520 *@@changed V0.9.4 (2000-06-14) [umoeller]: added fInvalidate
521 */
522
523ULONG cnrhInsertRecordAfter(HWND hwndCnr,
524 PRECORDCORE precc,
525 PSZ pszText,
526 ULONG flRecordAttr,
527 PRECORDCORE preccAfter,
528 BOOL fInvalidate) // in: invalidate records?
529{
530 ULONG ulrc = 0;
531 RECORDINSERT ri;
532
533 if (precc)
534 {
535 // RECORDCORE stuff
536 precc->flRecordAttr = flRecordAttr;
537 precc->preccNextRecord = NULL;
538
539 if (pszText) // V0.9.0
540 {
541 precc->pszIcon = pszText;
542 precc->pszText = pszText;
543 precc->pszName = pszText;
544 precc->pszTree = pszText;
545
546 // setup RECORDINSERT struct
547 ri.cb = sizeof(RECORDINSERT);
548 ri.pRecordOrder = (PRECORDCORE)preccAfter;
549 ri.pRecordParent = 0; // no parent here
550 ri.zOrder = CMA_TOP;
551 ri.fInvalidateRecord = fInvalidate; // V0.9.4 (2000-06-14) [umoeller]
552 ri.cRecordsInsert = 1;
553
554 ulrc = (ULONG)WinSendMsg(hwndCnr,
555 CM_INSERTRECORD,
556 (MPARAM)precc,
557 (MPARAM)&ri);
558 }
559 }
560 return (ulrc);
561}
562
563/*
564 *@@ cnrhMoveRecord:
565 * this moves a single record within the same container
566 * window. This only makes sense in ordered views (i.e.
567 * Name, Text, and Details views).
568 *
569 * For moving records with child records, use cnrhMoveTree
570 * instead.
571 */
572
573BOOL cnrhMoveRecord(HWND hwndCnr,
574 PRECORDCORE preccMove, // in: record to move
575 PRECORDCORE preccInsertAfter)
576 // in: where to move the record to
577 // (can be CMA_FIRST or CMA_END)
578{
579 // rule out possible errors:
580 if ( (preccMove) // this better be valid
581 && (preccInsertAfter) // this also, because otherwise the record just disappears
582 && (preccMove != preccInsertAfter)
583 // same here
584 )
585 {
586 RECORDINSERT ri;
587 ULONG ulrc = 0;
588
589 // remove record
590 WinSendMsg(hwndCnr,
591 CM_REMOVERECORD,
592 (MPARAM)(&preccMove),
593 MPFROM2SHORT(1,
594 CMA_INVALIDATE)); // no free
595
596 // and re-insert that record at the new position
597 ri.cb = sizeof(RECORDINSERT);
598 ri.pRecordOrder = (PRECORDCORE)preccInsertAfter;
599 ri.pRecordParent = 0; // no parent here
600 ri.zOrder = CMA_TOP;
601 ri.fInvalidateRecord = TRUE;
602 ri.cRecordsInsert = 1;
603 ulrc = (ULONG)WinSendMsg(hwndCnr,
604 CM_INSERTRECORD,
605 (MPARAM)preccMove,
606 (MPARAM)&ri);
607
608 return (ulrc != 0);
609 }
610 else return (FALSE);
611}
612
613/*
614 *@@ cnrhMoveTree:
615 * this function moves a container record core and all its
616 * children from one tree to another.
617 * See the CM_MOVETREE message for more explanations.
618 *
619 * The problem with that message is that automatic container
620 * sorting does not work in tree view. This function however
621 * is smart enough to maintain container sorting and insert
622 * the moved record core at the correct position below the
623 * target record core, if you specify a container sort function
624 * (pfnCnrSort, which works just in CNRINFO).
625 *
626 * Otherwise, we'll just insert the item as the first child
627 * of preccNewParent.
628 *
629 * This function also has a workaround for a nasty container
630 * bug which results in a crash in PMMERGE.DLL when a tree
631 * is moved to the last position in the target parent record
632 * (at least with Warp 4 FP 7).
633 *
634 *@@added V0.9.0 [umoeller]
635 */
636
637BOOL cnrhMoveTree(HWND hwndCnr, // in: container control
638 PRECORDCORE preccMove, // in: record core to move
639 PRECORDCORE preccNewParent, // in: new parent for preccMove
640 // or NULL if move to root
641 PFNCNRSORT pfnCnrSort) // in: sort function to use or NULL
642{
643 TREEMOVE tm;
644 PRECORDCORE preccInsertAfter = (PRECORDCORE)CMA_FIRST;
645 BOOL fBugWorkAround = FALSE,
646 brc = FALSE;
647
648 if (pfnCnrSort)
649 {
650 // permanent sort activated:
651
652 // since the automatic container sort does
653 // not work when moving a record core tree in
654 // Tree view, we must find the recc after
655 // which we'll insert the tree ourselves
656 PRECORDCORE preccSearch = preccNewParent;
657 BOOL fFirstRun = TRUE;
658 ULONG ulFirstCode;
659
660 // set the code for first-loop query:
661 if (preccNewParent)
662 // if new parent is non-root
663 ulFirstCode = CMA_FIRSTCHILD;
664 else
665 // if new parent is root
666 ulFirstCode = CMA_FIRST;
667
668 while (TRUE)
669 {
670 preccSearch =
671 (PRECORDCORE)WinSendMsg(hwndCnr,
672 CM_QUERYRECORD,
673 // the following gets either the
674 // first child recc of the target
675 // record core or the next child
676 // for consecutive loops
677 (MPARAM)preccSearch,
678 MPFROM2SHORT(
679 ((fFirstRun)
680 ? ulFirstCode // first loop
681 : CMA_NEXT // works for next child too
682 ),
683 CMA_ITEMORDER)
684 );
685 fFirstRun = FALSE;
686
687 if ( (preccSearch == NULL)
688 || ((ULONG)preccSearch == -1)
689 )
690 {
691 // no more items found:
692 // keep preccInsertAfter, which might be CMA_FIRST
693 // or the preccSearch we have found previously.
694
695 if (preccInsertAfter != (PRECORDCORE)CMA_FIRST)
696 {
697 // Unfortunately, there is a bug in the container
698 // control which prohibits CM_MOVETREE from working
699 // if preccInsertAfter turns out to be the last
700 // record core in preccNewParent. This applies to
701 // preccInsertAfter == CMA_LAST also, and CMA_LASTCHILD
702 // doesn't work either.
703 // Duh.
704 // We'll fix this later.
705
706 fBugWorkAround = TRUE;
707 }
708 break;
709 }
710
711 if (((*pfnCnrSort)(preccSearch, preccMove, 0)) < 0)
712 {
713 // found record core is < our tree:
714 // mark current as "insert after" for later and go on
715 preccInsertAfter = preccSearch;
716 }
717 else
718 break;
719 }
720
721 /* _Pmpf(("preccInsertAfter %s",
722 (preccInsertAfter == (PRECORDCORE)CMA_FIRST) ? "CMA_FIRST"
723 : (preccInsertAfter == (PRECORDCORE)CMA_LAST) ? "CMA_LAST"
724 : (preccInsertAfter == NULL) ? "NULL"
725 : preccInsertAfter->pszIcon
726 )); */
727 } // end if (CnrInfo.pSortRecord)
728
729 if (fBugWorkAround)
730 // this is TRUE only if preccInsertAfter has turned
731 // out to be the last child of preccNewParent. This
732 // will make the container crash, so we insert as
733 // first and sort the whole damn container later.
734 // Not terribly fast, but better than crashing. ;-)
735 preccInsertAfter = (PRECORDCORE)CMA_FIRST;
736
737 // set record to be moved
738 tm.preccMove = preccMove;
739 // set target record core
740 tm.preccNewParent = preccNewParent;
741 tm.pRecordOrder = preccInsertAfter;
742 tm.flMoveSiblings = FALSE;
743 // move only preccMove
744 brc = (BOOL)WinSendMsg(hwndCnr,
745 CM_MOVETREE,
746 (MPARAM)&tm,
747 (MPARAM)NULL);
748
749 if (brc)
750 if (fBugWorkAround)
751 WinSendMsg(hwndCnr, CM_SORTRECORD, (MPARAM)pfnCnrSort, (MPARAM)NULL);
752
753 return (brc);
754}
755
756/*
757 *@@category: Helpers\PM helpers\Container helpers\View management
758 * functions for easily managing container views.
759 *
760 * Most importantly, this has the BEGIN_CNRINFO and END_CNRINFO
761 * macros which make CNRINFO handling easier. Also see
762 * cnrhSetView for that.
763 */
764
765/* ******************************************************************
766 *
767 * View management
768 *
769 ********************************************************************/
770
771/*
772 *@@ cnrhSelectAll:
773 * this selects or deselects all records in hwndCnr,
774 * depending on fSelect. Since multiple selections are
775 * not supported in Tree views in the first place, we
776 * only go for the "root" records (and no child records).
777 *
778 * Invoking this function on a container in Tree view
779 * will result in not much but display flickering anyway.
780 *
781 * This returns the number of records which were processed.
782 */
783
784ULONG cnrhSelectAll(HWND hwndCnr,
785 BOOL fSelect)
786{
787 ULONG ulrc = 0;
788 PRECORDCORE precc2 = NULL;
789
790 do {
791 precc2 =
792 (PRECORDCORE)WinSendMsg(hwndCnr,
793 CM_QUERYRECORD,
794 (MPARAM)precc2,
795 MPFROM2SHORT(
796 ((precc2 == NULL) ? CMA_FIRST : CMA_NEXT),
797 CMA_ITEMORDER)
798 );
799 if ((precc2 == 0) || ((LONG)precc2 == -1))
800 // last record or error:
801 break;
802
803 // select this one
804 cnrhSelectRecord(hwndCnr, precc2, fSelect);
805 ulrc++;
806
807 } while (TRUE);
808
809 return (ulrc);
810}
811
812/*
813 *@@ cnrhFindRecordFromPoint:
814 * this returns the record under the given
815 * point or NULL if none could be found
816 * (e.g. point is on whitespace).
817 *
818 * The point must be specified in container
819 * coordinates, relative to the container
820 * origin (which might be invisible if the
821 * container viewport has been scrolled up).
822 *
823 * With fsExtent, specify what part of the
824 * record should be checked. See
825 * cnrhScrollToRecord for more.
826 *
827 * This is not terribly fast, because it
828 * will send at least one message for each
829 * record which is currently visible in the
830 * container.
831 *
832 * With fl, specify any or none of the following:
833 *
834 * -- FRFP_RIGHTSPLITWINDOW: test right split Details view
835 * instead of main cnr.
836 *
837 * -- FRFP_SCREENCOORDS: *pptl specifies the point to
838 * check in _screen_ coordinates, instead of
839 * container ccordinates.
840 *
841 *@@added V0.9.1 (99-11-29) [umoeller]
842 *@@changed V0.9.4 (2000-08-08) [umoeller]: fixed viewport/window confusion; now works after cnr resize
843 */
844
845PRECORDCORE cnrhFindRecordFromPoint(HWND hwndCnr,
846 PPOINTL pptl, // in: point to check for
847 PRECTL prclFoundRecord,
848 // out: if != NULL and record was
849 // found, this receives the record rectl
850 // (or subrectl, depending on fsExtent)
851 ULONG fsExtent,
852 // in: one or several of
853 // CMA_ICON, CMA_TEXT, CMA_TREEICON
854 ULONG fl)
855{
856 PRECORDCORE preccReturn = NULL;
857 RECTL rclViewport;
858 HAB habCnr = WinQueryAnchorBlock(hwndCnr);
859
860 POINTL ptlCheck;
861
862 // _Pmpf(("Entering cnrhFindRecordFromPoint"));
863
864 ptlCheck.x = pptl->x;
865 ptlCheck.y = pptl->y;
866
867 if (fl & FRFP_SCREENCOORDS)
868 {
869 // _Pmpf((" mapping screen %d/%d to cnr", ptlCheck.x, ptlCheck.y));
870 WinMapWindowPoints(HWND_DESKTOP,
871 hwndCnr,
872 &ptlCheck,
873 1);
874 // _Pmpf((" got %d/%d", ptlCheck.x, ptlCheck.y));
875 }
876
877 // get cnr viewport (currently visible client area,
878 // relative to cnr origin)
879 if (WinSendMsg(hwndCnr,
880 CM_QUERYVIEWPORTRECT,
881 (MPARAM)&rclViewport,
882 MPFROM2SHORT(CMA_WINDOW, // CMA_WORKSPACE, V0.9.4 (2000-08-08) [umoeller]
883 ((fl & FRFP_RIGHTSPLITWINDOW) != 0))))
884 // right split window?
885 {
886 PRECORDCORE preccFound = (PRECORDCORE)CMA_FIRST;
887 QUERYRECFROMRECT qrfr;
888 QUERYRECORDRECT qrr;
889
890 // now enumerate all records which are visible in
891 // the viewport
892 do
893 {
894 qrfr.cb = sizeof(qrfr);
895 qrfr.rect.xLeft = rclViewport.xLeft;
896 qrfr.rect.yBottom = rclViewport.yBottom;
897 qrfr.rect.xRight = rclViewport.xRight;
898 qrfr.rect.yTop = rclViewport.yTop;
899 qrfr.fsSearch = CMA_PARTIAL | CMA_ITEMORDER;
900
901 // _Pmpf((" Enumerating recs in viewport %d, %d, %d, %d",
902 // rclViewport.xLeft, rclViewport.yBottom, rclViewport.xRight, rclViewport.yTop));
903
904 preccFound = (PRECORDCORE)WinSendMsg(hwndCnr,
905 CM_QUERYRECORDFROMRECT,
906 (MPARAM)preccFound, // CMA_FIRST on first loop
907 (MPARAM)&qrfr);
908 // _Pmpf((" CM_QUERYRECORDFROMRECT returned 0x%lX", preccFound));
909
910 if ( (preccFound != NULL)
911 && (preccFound != (PRECORDCORE)-1)
912 )
913 {
914 RECTL rclRecord;
915 qrr.cb = sizeof(qrr);
916 qrr.pRecord = preccFound;
917 qrr.fRightSplitWindow = ((fl & FRFP_RIGHTSPLITWINDOW) != 0);
918 qrr.fsExtent = fsExtent;
919 if (WinSendMsg(hwndCnr,
920 CM_QUERYRECORDRECT,
921 (MPARAM)&rclRecord,
922 (MPARAM)&qrr))
923 {
924 if (WinPtInRect(habCnr,
925 &rclRecord,
926 &ptlCheck))
927 {
928 // yes, it's in the record:
929 // return that
930 preccReturn = preccFound;
931
932 // if rectangle is queried,
933 // copy that
934 if (prclFoundRecord)
935 memcpy(prclFoundRecord, &rclRecord, sizeof(RECTL));
936 break;
937 }
938 }
939 }
940 else
941 // last one or error:
942 break;
943 } while (TRUE);
944 }
945 // else
946 // _Pmpf((" CM_QUERYVIEWPORTRECT failed."));
947
948 return (preccReturn);
949}
950
951/*
952 *@@ cnrhExpandFromRoot:
953 * expands prec and climbs up all parent
954 * records and expands them all as well.
955 *
956 * Returns the no. of records expanded.
957 *
958 *@@added V0.9.9 (2001-03-13) [umoeller]
959 */
960
961ULONG cnrhExpandFromRoot(HWND hwndCnr,
962 PRECORDCORE prec)
963{
964 ULONG ul = 0;
965 PRECORDCORE preccParent = prec;
966 while (preccParent)
967 {
968 WinSendMsg(hwndCnr, CM_EXPANDTREE, (MPARAM)preccParent, MPNULL);
969 ul++;
970
971 preccParent = (PRECORDCORE)WinSendMsg(hwndCnr,
972 CM_QUERYRECORD,
973 (MPARAM)preccParent,
974 MPFROM2SHORT(CMA_PARENT,
975 CMA_ITEMORDER));
976
977 if (preccParent == (PRECORDCORE)-1)
978 preccParent = NULL;
979 }
980
981 return (ul);
982}
983
984/*
985 *@@ cnrhScrollToRecord:
986 * scrolls a given container control to make a given
987 * record visible.
988 *
989 * Returns:
990 * -- 0: OK, scrolled
991 * -- 1: record rectangle query failed (error)
992 * -- 2: cnr viewport query failed (error)
993 * -- 3: record is already visible (scrolling not necessary)
994 * -- 4: cnrinfo query failed (error)
995 *
996 * Note: All messages are _sent_ to the container, not posted.
997 * Scrolling therefore occurs synchroneously before this
998 * function returns.
999 *
1000 * This function an improved version of the one (W)(C) Dan Libby, found at
1001 * http://zebra.asta.fh-weingarten.de/os2/Snippets/Howt6364.HTML
1002 * Improvements (C) 1998 Ulrich M”ller.
1003 *
1004 *@@changed V0.9.4 (2000-08-07) [umoeller]: now posting scroll messages to avoid sync errors
1005 *@@changed V0.9.9 (2001-03-12) [umoeller]: this never scrolled for root records in tree view if KeepParent == TRUE, fixed
1006 *@@changed V0.9.9 (2001-03-13) [umoeller]: largely rewritten; this now scrolls x properly too and is faster
1007 */
1008
1009ULONG cnrhScrollToRecord(HWND hwndCnr, // in: container window
1010 PRECORDCORE pRec, // in: record to scroll to
1011 ULONG fsExtent,
1012 // in: this determines what parts of pRec
1013 // should be made visible. OR the following
1014 // flags:
1015 // -- CMA_ICON: the icon rectangle
1016 // -- CMA_TEXT: the record text
1017 // -- CMA_TREEICON: the "+" sign in tree view
1018 BOOL fKeepParent)
1019 // for tree views only: whether to keep
1020 // the parent record of pRec visible when scrolling.
1021 // If scrolling to pRec would make the parent
1022 // record invisible, we instead scroll so that
1023 // the parent record appears at the top of the
1024 // container workspace (Win95 style).
1025
1026{
1027 QUERYRECORDRECT qRect;
1028 RECTL rclRecord,
1029 rclCnr;
1030 LONG lXOfs = 0,
1031 lYOfs = 0;
1032
1033 qRect.cb = sizeof(qRect);
1034 qRect.pRecord = (PRECORDCORE)pRec;
1035 qRect.fsExtent = fsExtent;
1036
1037 if (fKeepParent)
1038 {
1039 CNRINFO CnrInfo;
1040 // this is only valid in tree view, so check
1041 if (!WinSendMsg(hwndCnr,
1042 CM_QUERYCNRINFO,
1043 (MPARAM)&CnrInfo,
1044 (MPARAM)sizeof(CnrInfo)))
1045 return 4;
1046 else
1047 // disable if not tree view
1048 fKeepParent = ((CnrInfo.flWindowAttr & CV_TREE) != 0);
1049 }
1050
1051 // query record location and size of container
1052 if (!WinSendMsg(hwndCnr,
1053 CM_QUERYRECORDRECT,
1054 &rclRecord,
1055 &qRect))
1056 return 1;
1057
1058 if (!WinSendMsg(hwndCnr,
1059 CM_QUERYVIEWPORTRECT,
1060 &rclCnr,
1061 MPFROM2SHORT(CMA_WINDOW,
1062 // returns the client area rectangle
1063 // in container window coordinates
1064 FALSE)) )
1065 return 2;
1066
1067 // check if left bottom point of pRec is currently visible in container
1068
1069 #define IS_BETWEEN(a, b, c) (((a) >= (b)) && ((a) <= (c)))
1070
1071 // 1) set lXOfs if we need to scroll horizontally
1072 if (!IS_BETWEEN(rclRecord.xLeft, rclCnr.xLeft, rclCnr.xRight))
1073 // record xLeft is outside viewport:
1074 // scroll horizontally so that xLeft is exactly on left of viewport
1075 lXOfs = (rclRecord.xLeft - rclCnr.xLeft);
1076 else if (!IS_BETWEEN(rclRecord.xRight, rclCnr.xLeft, rclCnr.xRight))
1077 // record xRight is outside viewport:
1078 // scroll horizontally so that xRight is exactly on right of viewport
1079 lXOfs = (rclRecord.xRight - rclCnr.xRight);
1080
1081 // 2) set lYOfs if we need to scroll vertically
1082 if (!IS_BETWEEN(rclRecord.yBottom, rclCnr.yBottom, rclCnr.yTop))
1083 // record yBottom is outside viewport:
1084 // scroll horizontally so that yBottom is exactly on bottom of viewport
1085 lYOfs = (rclCnr.yBottom - rclRecord.yBottom) // this would suffice
1086 + (rclRecord.yTop - rclRecord.yBottom);
1087 // but we make the next rcl visible too
1088 else if (!IS_BETWEEN(rclRecord.yTop, rclCnr.yBottom, rclCnr.yTop))
1089 // record yTop is outside viewport:
1090 // scroll horizontally so that yTop is exactly on top of viewport
1091 lYOfs = (rclRecord.yTop - rclCnr.yTop);
1092
1093 if (fKeepParent && (lXOfs || lYOfs))
1094 {
1095 // keep parent enabled, and we're scrolling:
1096 // find the parent record then
1097 qRect.cb = sizeof(qRect);
1098 qRect.pRecord = (PRECORDCORE)WinSendMsg(hwndCnr,
1099 CM_QUERYRECORD,
1100 (MPARAM)pRec,
1101 MPFROM2SHORT(CMA_PARENT,
1102 CMA_ITEMORDER));
1103 if (qRect.pRecord) // V0.9.9 (2001-03-12) [umoeller]
1104 {
1105 // parent exists:
1106 // get PARENT record rectangle then
1107 RECTL rclParentRecord;
1108 qRect.fsExtent = fsExtent;
1109 if (WinSendMsg(hwndCnr,
1110 CM_QUERYRECORDRECT,
1111 &rclParentRecord,
1112 &qRect))
1113 {
1114 // check if parent record WOULD still be visible
1115 // if we scrolled what we calculated above
1116 RECTL rclCnr2;
1117 memcpy(&rclCnr2, &rclCnr, sizeof(rclCnr2));
1118 winhOffsetRect(&rclCnr2, lXOfs, -lYOfs);
1119
1120 if ( lXOfs
1121 && (!IS_BETWEEN(rclParentRecord.xLeft, rclCnr2.xLeft, rclCnr2.xRight))
1122 )
1123 // record xLeft is outside viewport:
1124 // scroll horizontally so that xLeft is exactly on left of viewport
1125 lXOfs = (rclParentRecord.xLeft - rclCnr.xLeft);
1126
1127 if ( lYOfs
1128 && (!IS_BETWEEN(rclParentRecord.yBottom, rclCnr2.yBottom, rclCnr2.yTop))
1129 )
1130 // record yBottom is outside viewport:
1131 // recalculate y ofs so that we scroll so
1132 // that parent record is on top of cnr viewport
1133 lYOfs = (rclCnr.yTop - rclParentRecord.yTop) // this would suffice
1134 - (rclRecord.yTop - rclRecord.yBottom); // but we make the previous rcl visible too
1135 }
1136 }
1137 }
1138
1139 // V0.9.14 (2001-07-28) [umoeller]
1140 // tried WinSendMsg (instead of post) because
1141 // otherwise we can't get auto-edit on wpshCreateFromTemplate
1142 // to work... but this causes two problems:
1143 // -- for some reason, the WPS selects the recc under
1144 // the mouse then, which is very wrong in most situations;
1145 // -- since the WPS expands the root record in tree views
1146 // after the root has been populated, this can cause
1147 // totally garbled display for complex trees.
1148 // So I'm back to WinPostMsg... // @@todo
1149
1150 if (lXOfs)
1151 // scroll horizontally
1152 WinPostMsg(hwndCnr,
1153 CM_SCROLLWINDOW,
1154 (MPARAM)CMA_HORIZONTAL,
1155 (MPARAM)lXOfs);
1156
1157 // scroll vertically
1158 if (lYOfs)
1159 WinPostMsg(hwndCnr,
1160 CM_SCROLLWINDOW,
1161 (MPARAM)CMA_VERTICAL,
1162 (MPARAM)lYOfs);
1163
1164 return 0;
1165}
1166
1167/*
1168 *@@ cnrhShowContextMenu:
1169 * this function shows the given menu as a context
1170 * menu for the given record core (using WinPopupMenu).
1171 * This function may be used when receiving WM_CONTROL
1172 * with CN_CONTEXTMENU from the container, which has
1173 * the preccSource in mp2.
1174 *
1175 * In detail, this function does the following:
1176 *
1177 * 1) query the coordinates where to show the menu;
1178 * if (preccSource), this will be next to the
1179 * record core, otherwise (i.e. menu requested
1180 * for whitespace) the mouse coordinates over
1181 * the container;
1182 *
1183 * 2) give preccSource (or, if NULL, the whole
1184 * container) source emphasis;
1185 *
1186 * 3) call WinPopupMenu.
1187 *
1188 * Note: It is the responsibility of the caller to catch
1189 * WM_MENUEND in the window procedure of hwndMenuOwner later
1190 * to remove the source emphasis for preccSource again.
1191 *
1192 * This function returns FALSE if an error occured.
1193 *
1194 *@@added V0.9.0 [umoeller]
1195 */
1196
1197BOOL cnrhShowContextMenu(HWND hwndCnr,
1198 PRECORDCORE preccSource, // in: mp2 of CN_CONTEXTMENU
1199 HWND hMenu, // in: menu to show
1200 HWND hwndMenuOwner) // in: menu owner (where the
1201 // WM_COMMAND will go to)
1202{
1203 BOOL brc = FALSE;
1204 if (hMenu)
1205 {
1206 BOOL fQueried = FALSE;
1207
1208 POINTL ptl;
1209 if (preccSource)
1210 {
1211 CNRINFO CnrInfo;
1212 cnrhQueryCnrInfo(hwndCnr, &CnrInfo);
1213
1214 if ((CnrInfo.flWindowAttr & CV_DETAIL) == 0)
1215 {
1216 // if we're not in Details view:
1217 // calculate the point where to show the context
1218 // menu; we use the lower-right corner of the
1219 // source record core
1220 QUERYRECORDRECT qRect;
1221 RECTL rclRecc;
1222 qRect.cb = sizeof(qRect);
1223 qRect.pRecord = preccSource;
1224 qRect.fsExtent = CMA_TEXT;
1225 WinSendMsg(hwndCnr,
1226 CM_QUERYRECORDRECT,
1227 &rclRecc,
1228 &qRect);
1229 ptl.x = rclRecc.xRight;
1230 ptl.y = rclRecc.yBottom;
1231 // now we have the lower-right corner in cnr coords
1232
1233 // clip if this is outside the container window
1234 WinQueryWindowRect(hwndCnr, &rclRecc);
1235 if (ptl.x > rclRecc.xRight)
1236 ptl.x = rclRecc.xRight;
1237 if (ptl.y > rclRecc.yTop)
1238 ptl.y = rclRecc.yTop;
1239
1240 // convert this to screen coordinates
1241 WinMapWindowPoints(hwndCnr,
1242 HWND_DESKTOP,
1243 &ptl,
1244 1);
1245 fQueried = TRUE;
1246 }
1247 }
1248
1249 if (!fQueried)
1250 // else: use mouse coordinates for context menu
1251 WinQueryPointerPos(HWND_DESKTOP, &ptl);
1252
1253 // give preccSource source emphasis;
1254 // if preccSource == NULL, the whole container will be
1255 // given source emphasis
1256 WinSendMsg(hwndCnr,
1257 CM_SETRECORDEMPHASIS,
1258 (MPARAM)preccSource, // might be NULL for whole container
1259 MPFROM2SHORT(TRUE, // set emphasis
1260 CRA_SOURCE));
1261
1262 // finally, show context menu
1263 brc = WinPopupMenu(HWND_DESKTOP, // menu parent
1264 hwndMenuOwner, // menu owner
1265 hMenu,
1266 (SHORT)ptl.x,
1267 (SHORT)ptl.y,
1268 0, // ID
1269 PU_NONE
1270 | PU_MOUSEBUTTON1
1271 | PU_KEYBOARD
1272 | PU_HCONSTRAIN
1273 | PU_VCONSTRAIN);
1274 }
1275
1276 return (brc);
1277}
1278
1279/*
1280 *@@ cnrhQuerySourceRecord:
1281 * this helper function evaluates a given container
1282 * to find out which records have been selected while
1283 * a context menu is open.
1284 *
1285 * This is for implementing a WPS-like (probably CUA) behavior
1286 * when invoking actions on container records. That is:
1287 *
1288 * 1) If the user opens a context menu on a selected object,
1289 * the selected action should be invoked on _all_ selected
1290 * objects.
1291 *
1292 * 2) If the user opens a context menu on an object which is
1293 * _not_ selected, the action should be invoked on that
1294 * object only (which should be given source emphasis),
1295 * no matter what other objects are selected.
1296 *
1297 * This function expects in preccSource the record core which
1298 * currently has (or just had) source emphasis.
1299 *
1300 * 1) In your own programs, you should have used cnrhShowContextMenu
1301 * above, which sets record source emphasis correctly. Unfortunately,
1302 * PM posts WM_MENUEND _before_ WM_COMMAND, so if you remove source
1303 * emphasis in WM_MENUEND, it is unknown which record was selected at
1304 * the time WM_COMMAND comes in... so you need to store that record
1305 * and pass it to this function later. Sorry.
1306 *
1307 * 2) With the WPS, this function works within the wpMenuItemSelected
1308 * method, because that one is invoked during the processing of
1309 * WM_COMMAND and the WPS automatically does the source emphasis
1310 * stuff right.
1311 *
1312 * The result of this evaluation is stored in *pulSelection,
1313 * which can be:
1314 *
1315 * -- SEL_WHITESPACE (1): the context menu was opened on the
1316 * whitespace of the container (preccSource
1317 * is NULL);
1318 * this func then returns NULL also.
1319 * -- SEL_SINGLESEL (2): the context menu was opened for a
1320 * single selected object:
1321 * this func then returns that record core
1322 * (which is preccSource).
1323 * -- SEL_MULTISEL (3): the context menu was opened on one
1324 * of a multitude of selected record;
1325 * this func then returns the first of the
1326 * selected records. Use
1327 * cnrhQueryNextSelectedRecord to get the others.
1328 * -- SEL_SINGLEOTHER (4): the context menu was opened for a
1329 * single record _other_ than the selected
1330 * records:
1331 * this func then returns that record
1332 * (which is preccSource).
1333 *
1334 *@@added V0.9.0 [umoeller]
1335 */
1336
1337PRECORDCORE cnrhQuerySourceRecord(HWND hwndCnr, // in: cnr
1338 PRECORDCORE preccSource, // in: record which had source emphasis
1339 PULONG pulSelection) // out: selection flags
1340{
1341 PRECORDCORE preccReturn = NULL;
1342
1343 if (preccSource == NULL)
1344 {
1345 // this probably means that the whole container has
1346 // source emphasis --> context menu on folder whitespace
1347 *pulSelection = SEL_WHITESPACE;
1348 // and return NULL
1349 }
1350 else if (((LONG)preccSource) != -1) // no error?
1351 {
1352 // check whether the source record is also selected
1353 if ((preccSource->flRecordAttr & CRA_SELECTED) == 0)
1354 {
1355 // no:
1356 // only one object, but not one of the selected ones
1357 // (if any at all)
1358 preccReturn = preccSource;
1359 *pulSelection = SEL_SINGLEOTHER;
1360 }
1361 else
1362 {
1363 // yes, source record _is_ selected:
1364 // check whether we have more than one selected record?
1365
1366 // get first selected record
1367 PRECORDCORE preccSelected = (PRECORDCORE)WinSendMsg(hwndCnr,
1368 CM_QUERYRECORDEMPHASIS,
1369 (MPARAM)CMA_FIRST,
1370 (MPARAM)CRA_SELECTED);
1371 // return that one
1372 preccReturn = preccSelected;
1373
1374 // are several objects selected?
1375 preccSelected = (PRECORDCORE)WinSendMsg(hwndCnr,
1376 CM_QUERYRECORDEMPHASIS,
1377 (MPARAM)preccSelected,
1378 // get next selected
1379 (MPARAM)CRA_SELECTED);
1380 if (preccSelected)
1381 // several objects
1382 *pulSelection = SEL_MULTISEL;
1383 else
1384 // only one object
1385 *pulSelection = SEL_SINGLESEL;
1386 }
1387 }
1388
1389 return (preccReturn);
1390}
1391
1392/*
1393 *@@ cnrhQueryNextSelectedRecord:
1394 * if cnrhQuerySourceRecord above returns SEL_MULTISEL
1395 * you can use this helper func to loop thru all the
1396 * selected records. This will return the next record
1397 * after preccCurrent which is selected or NULL if it's the last.
1398 *
1399 * If you're not using cnrhQuerySourceRecord (because your
1400 * records do not have source emphasis), you can also call
1401 * this function with (preccCurrent == CMA_FIRST) to get the
1402 * first selected record core.
1403 */
1404
1405PRECORDCORE cnrhQueryNextSelectedRecord(HWND hwndCnr,
1406 PRECORDCORE preccCurrent)
1407{
1408 PRECORDCORE preccReturn = 0;
1409 if (preccCurrent)
1410 {
1411 PRECORDCORE preccNext = (PRECORDCORE)WinSendMsg(hwndCnr,
1412 CM_QUERYRECORDEMPHASIS,
1413 (MPARAM)preccCurrent,
1414 (MPARAM)CRA_SELECTED);
1415 if ((preccNext) && ((LONG)preccNext != -1) )
1416 preccReturn = preccNext;
1417 }
1418 return (preccReturn);
1419}
1420
1421/*
1422 *@@category: Helpers\PM helpers\Container helpers\Record relations/iteration
1423 * functions for querying the relation of records and
1424 * iterating through records.
1425 */
1426
1427/* ******************************************************************
1428 *
1429 * Record relations/iteration
1430 *
1431 ********************************************************************/
1432
1433/*
1434 *@@ cnrhQueryRecordIndex:
1435 * returns the "index" of the specified record
1436 * in the specified container. That is, if the
1437 * specified record is the first record in the
1438 * container, 0 is returned. If it's the second,
1439 * 1 is returned, and so on.
1440 *
1441 * Returns -1 if precc could not be found.
1442 *
1443 * Doesn't work in Tree view.
1444 *
1445 *@@added V0.9.3 (2000-04-19) [umoeller]
1446 */
1447
1448LONG cnrhQueryRecordIndex(HWND hwndCnr,
1449 PRECORDCORE precc)
1450{
1451 PRECORDCORE precc2 = (PRECORDCORE)CMA_FIRST;
1452 BOOL fFirstCall = TRUE;
1453 LONG lrc = -1,
1454 lCount = 0;
1455
1456 while (TRUE)
1457 {
1458 precc2 =
1459 (PRECORDCORE)WinSendMsg(hwndCnr,
1460 CM_QUERYRECORD,
1461 (MPARAM)precc2, // ignored on first call
1462 MPFROM2SHORT(
1463 (fFirstCall)
1464 ? CMA_FIRST
1465 : CMA_NEXT,
1466 CMA_ITEMORDER)
1467 );
1468 if ( (precc2 == NULL)
1469 || (precc2 == (PRECORDCORE)-1)
1470 )
1471 // error:
1472 // return -1
1473 break;
1474 else
1475 if (precc2 == precc)
1476 {
1477 // same as search record:
1478 lrc = lCount;
1479 break;
1480 }
1481
1482 // else search on
1483 fFirstCall = FALSE;
1484 lCount++;
1485 }
1486
1487 return (lrc);
1488}
1489
1490/*
1491 *@@ cnrhIsChildOf:
1492 * returns TRUE only if precTest is a child record
1493 * of precParent in a tree view.
1494 *
1495 *@@added V0.9.7 (2000-12-13) [umoeller]
1496 */
1497
1498BOOL cnrhIsChildOf(HWND hwndCnr,
1499 PRECORDCORE precTest, // in: recc to test
1500 PRECORDCORE precParent) // in: parent to test
1501{
1502 BOOL brc = FALSE;
1503 if ((precTest) && (precParent))
1504 {
1505 PRECORDCORE precParentThis = precTest;
1506
1507 while (precParentThis)
1508 {
1509 // first call: get parent of precTest;
1510 // subsequent calls: climb up
1511 precParentThis = (PRECORDCORE)WinSendMsg(hwndCnr,
1512 CM_QUERYRECORD,
1513 precParentThis,
1514 MPFROM2SHORT(CMA_PARENT,
1515 CMA_ITEMORDER));
1516 if (precParentThis == (PRECORDCORE)-1)
1517 break;
1518 else
1519 if (precParentThis == precParent)
1520 {
1521 brc = TRUE;
1522 break;
1523 }
1524 }
1525 }
1526
1527 return (brc);
1528}
1529
1530/*
1531 *@@ cnrhForAllRecords:
1532 * this monster function calls pfnwpCallback
1533 * for really all the records in the container,
1534 * including child records in tree view.
1535 *
1536 * This is extremely useful for cleaning up
1537 * all record cores before a container window
1538 * gets destroyed.
1539 *
1540 * This function recurses for child records.
1541 * On the first call, preccParent should be
1542 * NULL; you may however specify a certain
1543 * record, and this function will call the
1544 * callback only for that record and children.
1545 *
1546 * pfnwpCallback gets called with the following
1547 * parameters:
1548 *
1549 * -- HWND hwnd: hwndCnr, as passed to this func
1550 * -- PRECORDCORE precc: current record core, as
1551 * determined by this func.
1552 * -- ULONG ulUser1/2: what you have specified here.
1553 *
1554 * It must be declared as follows:
1555 *
1556 + ULONG EXPENTRY fncb(HWND hwndCnr,
1557 + PRECORDCORE precc,
1558 + ULONG ulUser1,
1559 + ULONG ulUser2)
1560 *
1561 * If the callback returns anything != 0, this
1562 * function stops even before all records have
1563 * been processed. You can use ulUser for a
1564 * pointer to a return value.
1565 *
1566 * This always returns the no. of records which
1567 * were processed.
1568 *
1569 * If you use this function for deleting record
1570 * cores, you can be sure that you can delete
1571 * every record, because your callback gets called
1572 * for the child records before the parent record.
1573 *
1574 *@@added V0.9.0 [umoeller]
1575 */
1576
1577ULONG cnrhForAllRecords(HWND hwndCnr,
1578 PRECORDCORE preccParent, // in: NULL for root
1579 PFNCBRECC pfncbRecc, // in: callback
1580 ULONG ulUser1,
1581 ULONG ulUser2)
1582{
1583 PRECORDCORE precc2 = preccParent;
1584 ULONG ulrc = 0;
1585 USHORT usQuery;
1586 BOOL fFirstCall = TRUE;
1587
1588 while (TRUE)
1589 {
1590 if (fFirstCall)
1591 {
1592 // first call:
1593 if (preccParent)
1594 // non-root:
1595 usQuery = CMA_FIRSTCHILD;
1596 else
1597 // NULL == root:
1598 usQuery = CMA_FIRST;
1599 }
1600 else
1601 // subsequent calls:
1602 usQuery = CMA_NEXT; // works as CMA_NEXTCHILD also
1603
1604 precc2 =
1605 (PRECORDCORE)WinSendMsg(hwndCnr,
1606 CM_QUERYRECORD,
1607 (MPARAM)((fFirstCall)
1608 // first call (CMA_FIRSTCHILD or CMA_FIRST):
1609 ? preccParent // ignored for CMA_FIRST
1610 // subsequent calls (CMA_NEXTCHILD or CMA_NEXT):
1611 : precc2), // what we queried last
1612 MPFROM2SHORT(
1613 usQuery, // set above
1614 CMA_ITEMORDER)
1615 );
1616
1617 if ((precc2) && ((ULONG)precc2 != -1))
1618 {
1619 // record found:
1620 // recurse for that record
1621 ulrc += cnrhForAllRecords(hwndCnr,
1622 precc2, // new parent to search
1623 pfncbRecc,
1624 ulUser1,
1625 ulUser2);
1626
1627 // _Pmpf(("Calling callback for %s", precc2->pszIcon));
1628
1629 // call callback
1630 if (pfncbRecc)
1631 if ((*pfncbRecc)(hwndCnr, precc2, ulUser1, ulUser2))
1632 // returns something != NULL:
1633 // stop
1634 break;
1635 ulrc++;
1636 }
1637 else
1638 // no more records or error: get outta here
1639 break;
1640
1641 fFirstCall = FALSE;
1642 }
1643
1644 return (ulrc);
1645}
1646
1647/*
1648 * cnrhForAllChildRecords:
1649 * calls the specified fncbRecc callback for
1650 * the specified recc and all its child records.
1651 *
1652 *@@added V0.9.0 [umoeller]
1653 */
1654
1655/* VOID cnrhForAllChildRecords(HWND hwndCnr,
1656 PRECORDCORE precc,
1657 PFNCBRECC pfncbRecc,
1658 ULONG ulp1,
1659 ULONG ulp2)
1660{
1661 PRECORDCORE precc2 = precc;
1662 (*pfncbRecc)(precc, ulp1, ulp2);
1663 do {
1664 precc2 =
1665 (PRECORDCORE)WinSendMsg(hwndCnr,
1666 CM_QUERYRECORD,
1667 (MPARAM)precc2,
1668 MPFROM2SHORT(
1669 (precc2 == precc)
1670 ? CMA_FIRSTCHILD : CMA_NEXT,
1671 CMA_ITEMORDER)
1672 );
1673 if ((LONG)precc2 == -1)
1674 precc2 = NULL;
1675 if (precc2)
1676 // recurse again
1677 cnrhForAllChildRecords(hwndCnr, precc2, pfncbRecc, ulp1, ulp2);
1678 } while (precc2);
1679} */
1680
1681/*
1682 * cnrhForAllRecords2:
1683 * this is a useful function which calls
1684 * the specified callback function for
1685 * really all records in the container of
1686 * the main window, including child records.
1687 *
1688 * xxx
1689 *
1690 *@@added V0.9.0 [umoeller]
1691 */
1692
1693/* VOID cnrhForAllRecords2(HWND hwndCnr,
1694 PFNCBRECC pfncbRecc,
1695 ULONG ulp1,
1696 ULONG ulp2)
1697{
1698 PRECORDCORE precc2 = NULL;
1699 do {
1700 precc2 =
1701 (PRECORDCORE)WinSendMsg(hwndCnr,
1702 CM_QUERYRECORD,
1703 (MPARAM)precc2,
1704 MPFROM2SHORT(
1705 ((precc2 == NULL) ? CMA_FIRST : CMA_NEXT),
1706 CMA_ITEMORDER)
1707 );
1708 if ((LONG)precc2 == -1)
1709 precc2 = NULL;
1710 if (precc2)
1711 // recurse again
1712 cnrhForAllChildRecords(hwndCnr, precc2, pfncbRecc, ulp1, ulp2);
1713 } while (precc2);
1714} */
1715
1716/*
1717 * cnrhForAllParentRecords:
1718 * just as above, but climbs up instead.
1719 * The first call of the callback will be
1720 * for the parent record of precc.
1721 *
1722 *@@added V0.9.0 [umoeller]
1723 */
1724
1725/* VOID cnrhForAllParentRecords(HWND hwndCnr,
1726 PRECORDCORE precc,
1727 PFNCBRECC pfncbRecc,
1728 ULONG ulp1,
1729 ULONG ulp2)
1730{
1731 PRECORDCORE precc2 = precc;
1732 do {
1733 precc2 =
1734 (PRECORDCORE)WinSendMsg(hwndCnr,
1735 CM_QUERYRECORD,
1736 (MPARAM)precc2,
1737 MPFROM2SHORT(CMA_PARENT,
1738 CMA_ITEMORDER)
1739 );
1740 if ((LONG)precc2 == -1)
1741 precc2 = NULL;
1742 if (precc2)
1743 (*pfncbRecc)(precc2, ulp1, ulp2);
1744 } while (precc2);
1745} */
1746
1747/*
1748 *@@category: Helpers\PM helpers\Container helpers\Miscellaneous
1749 */
1750
1751/* ******************************************************************
1752 *
1753 * Miscellaneous
1754 *
1755 ********************************************************************/
1756
1757/*
1758 *@@ cnrhQueryCnrFromFrame:
1759 * find the handle of a frame window's container; we do this
1760 * by enumerating all the frame's child windows and comparing
1761 * their window classes to the WC_CONTAINER code.
1762 * This is not terribly fast, so use this func economically.
1763 * Returns NULLHANDLE if not found.
1764 */
1765
1766HWND cnrhQueryCnrFromFrame(HWND hwndFrame)
1767{
1768 HENUM henum;
1769 CHAR szClassName[256];
1770 HWND hwndCnr = NULLHANDLE,
1771 hwndTemp = NULLHANDLE;
1772
1773 if (hwndFrame)
1774 {
1775 henum = WinBeginEnumWindows(hwndFrame);
1776 if (henum)
1777 {
1778 do
1779 {
1780 hwndTemp = WinGetNextWindow(henum);
1781 if (hwndTemp)
1782 {
1783 if (WinQueryClassName(hwndTemp, 250, szClassName))
1784 if (strcmp(szClassName, "#37") == 0)
1785 // unbelievable, this is PM's code for WC_CONTAINER...
1786 hwndCnr = hwndTemp;
1787 }
1788 } while (hwndTemp);
1789 }
1790 WinEndEnumWindows(henum);
1791 }
1792
1793 return hwndCnr;
1794}
1795
1796/*
1797 *@@ cnrhInitDrag:
1798 * this sets up the necessary structures to begin dragging
1799 * a record core from a container. This helper func should be
1800 * called if your container should support being the source
1801 * window of a direct manipulation.
1802 *
1803 * This should get called in three situations:
1804 *
1805 * -- the container sends CN_INITDRAG ("real" d'n'd desired by user);
1806 *
1807 * -- the container sends us CN_PICKUP (Alt+MB2 pressed);
1808 *
1809 * -- the user has selected "Pickup" from a record core's
1810 * context menu (ID_XSMI_FILETYPES_PICKUP command).
1811 * In that case, you can also call this function with
1812 * usNotifyCode == CN_PICKUP.
1813 *
1814 * In all three cases, preccDrag must be the record core
1815 * which is to be dragged.
1816 *
1817 * Depending on usNotifyCode, this function, after having allocated
1818 * the necessary data, will do the following:
1819 *
1820 * 1) If (usNotifyCode == CN_PICKUP), we will initiate a non-modal
1821 * (lazy) drag (DrgLazyDrag). This function will then return
1822 * after the records have been given "picked" emphasis.
1823 *
1824 * Note: You must intercept CN_DROPNOTIFY in your window proc
1825 * to clean up resources later. For this, call:
1826 *
1827 + DrgDeleteDraginfoStrHandles(pdrgInfo);
1828 + DrgFreeDraginfo(pdrgInfo);
1829 *
1830 * 2) However, if (usNotifyCode == CN_INITDRAG), we will start
1831 * a regular modal drag here by calling DrgDrag. This function
1832 * will _not_ return until the object has been dropped or d'n'd
1833 * has been cancelled. PM establishes another message loop
1834 * internally for this. In this case, this function managed
1835 * cleanup automatically.
1836 *
1837 * This function supports one single record core only. The following
1838 * information will be set in the DRAGITEM structure:
1839 *
1840 * -- ulItemID will be set to the preccDrag so that the target
1841 * window can access the dragged record.
1842 *
1843 * -- hstrSourceName == hstrTargetName gets the RECORDCORE.pszIcon.
1844 *
1845 * The drag icon will be a default system file icon.
1846 *
1847 * Preconditions:
1848 *
1849 * -- pszIcon must be set in the RECORDCORE. This is used for
1850 * the item source name. This must not be NULL.
1851 */
1852
1853PDRAGINFO cnrhInitDrag(HWND hwndCnr,
1854 // in: source container window
1855 PRECORDCORE preccDrag,
1856 // in: record to be dragged (only one supported)
1857 USHORT usNotifyCode,
1858 // in: CN_INITDRAG or CN_PICKUP
1859 PSZ pszRMF,
1860 // in: rendering mechanism and format,
1861 // e.g. "(DRM_MYFILETYPE)x(DRF_UNKNOWN)"
1862 USHORT usSupportedOps)
1863 // stored in DRAGITEM.fsSupportedOps,
1864 // one or several of DO_COPYABLE, DO_MOVEABLE, DO_LINKABLE
1865{
1866 DRAGIMAGE drgImage;
1867 PDRAGINFO pdrgInfo = NULL;
1868
1869 memset(&drgImage, 0, sizeof(drgImage));
1870
1871 if (pdrgInfo = DrgAllocDraginfo(1)) // one item only
1872 {
1873 DRAGITEM drgItem;
1874 memset(&drgItem, 0, sizeof(drgItem));
1875
1876 drgItem.hwndItem = hwndCnr;
1877 drgItem.ulItemID = (ULONG)preccDrag;
1878 // we use this to store the container rec
1879 drgItem.hstrType = DrgAddStrHandle(DRT_UNKNOWN);
1880 // application defined
1881 drgItem.hstrRMF = DrgAddStrHandle(pszRMF);
1882 drgItem.hstrContainerName = 0;
1883 drgItem.hstrSourceName = DrgAddStrHandle(preccDrag->pszIcon);
1884 drgItem.hstrTargetName = drgItem.hstrSourceName;
1885 drgItem.fsSupportedOps = usSupportedOps;
1886
1887 // set the DRAGITEM struct into the memory
1888 // allocated by DrgAllocDraginfo()
1889 DrgSetDragitem(pdrgInfo,
1890 &drgItem,
1891 sizeof(DRAGITEM),
1892 0); // item index
1893
1894 // fill in the DRAGIMAGE structure
1895 drgImage.cb = sizeof(DRAGIMAGE);
1896 drgImage.hImage = WinQuerySysPointer(HWND_DESKTOP,
1897 SPTR_FILE,
1898 FALSE);
1899 // pointer used for dragging; we
1900 // use a dull default file icon
1901 drgImage.fl = DRG_ICON; // hImage is an HPOINTER
1902 drgImage.cxOffset = 0; // 5 * iOffset; // Image offset from mouse pointer
1903 drgImage.cyOffset = 0; // 5 * iOffset; // Image offset from mouse pointer
1904
1905 // set source emphasis for the container record
1906 WinSendMsg(hwndCnr,
1907 CM_SETRECORDEMPHASIS,
1908 (MPARAM)preccDrag,
1909 MPFROM2SHORT(TRUE,
1910 (usNotifyCode == CN_INITDRAG)
1911 // for dragging: source emphasis
1912 ? CRA_SOURCE
1913 // for lazy drag: picked emphasis
1914 : CRA_PICKED));
1915
1916 if (usNotifyCode == CN_INITDRAG)
1917 {
1918 // "real" dragging:
1919 // call modal function for drag'n'drop.
1920 // This does not return until either
1921 // d'n'd has been cancelled or the
1922 // record core has been dropped.
1923 /* HWND hwndTarget = */ DrgDrag(hwndCnr,
1924 pdrgInfo,
1925 &drgImage,
1926 1, // drag image count
1927 VK_ENDDRAG, // system drag button
1928 NULL); // reserved
1929
1930 // since it's the source which is responsible
1931 // for cleaning up the resources, this is
1932 // what we do now (we had a modal d'n'd)
1933 DrgDeleteDraginfoStrHandles(pdrgInfo);
1934 DrgFreeDraginfo(pdrgInfo);
1935
1936 // remove source emphasis for the container record
1937 WinSendMsg(hwndCnr,
1938 CM_SETRECORDEMPHASIS,
1939 (MPARAM)preccDrag,
1940 MPFROM2SHORT(FALSE,
1941 CRA_SOURCE));
1942 } // end if (usNotifyCode == CN_INITDRAG)
1943 else
1944 {
1945 // "lazy drag" (CN_PICKUP):
1946 // initiate lazy drag. This function returns
1947 // immediately, since lazy drag is non-modal.
1948 // As a result, we cannot clean up the resources
1949 // here, but need to do this later (CN_DROPNOTIFY).
1950 DrgLazyDrag(hwndCnr,
1951 pdrgInfo,
1952 &drgImage,
1953 1, // drag image count
1954 0); // reserved
1955 }
1956 } // end if (pdrgInfo)
1957
1958 return (pdrgInfo);
1959}
1960
1961/*
1962 *@@ cnrhOwnerDrawRecord:
1963 * this helper func can be called upon receiving
1964 * WM_DRAWITEM for owner-drawing container record
1965 * cores.
1966 *
1967 * What this draws depends on flFlags:
1968 *
1969 * -- If CODFL_DISABLEDTEXT is set, this function supports
1970 * drawing record cores in gray color if the record has
1971 * the CRA_DISABLED style (which, AFAIK, PM ignores per
1972 * default).
1973 *
1974 * -- If CODFL_MINIICON is set, this function paints the
1975 * record's mini-icon properly. So far I have failed
1976 * to find out how to get a container to paint the
1977 * correct mini-icon if the CV_MINI style is set...
1978 * the container normally _does_ paint a small icon,
1979 * but it won't use the "real" mini icon data if that
1980 * is present.
1981 *
1982 * For painting the mini-icon, WinDrawPointer is used
1983 * with DP_MINI set properly and the RECORDCORE's
1984 * hptrMiniIcon field.
1985 *
1986 * This returns either TRUE or FALSE as an MPARAM, depending
1987 * on whether we have drawn the item. This return value should
1988 * also be the return value of your window procedure.
1989 *
1990 *@@changed V0.9.16 (2001-09-29) [umoeller]: added flFlags, icon draw support
1991 */
1992
1993MRESULT cnrhOwnerDrawRecord(MPARAM mp2, // in: mp2 of WM_DRAWITEM (POWNERITEM)
1994 ULONG flFlags) // in: CODFL_* flags
1995{
1996 MRESULT mrc = (MPARAM)FALSE; // tell cnr to draw the item
1997
1998 // get generic DRAWITEM structure
1999 POWNERITEM poi;
2000
2001 if (poi = (POWNERITEM)mp2)
2002 {
2003 // get container-specific draw-item struct
2004 PCNRDRAWITEMINFO pcdii = (PCNRDRAWITEMINFO)poi->hItem;
2005
2006 // check if we're to draw the text
2007 // (and not the icon)
2008 if ( (poi->idItem == CMA_TEXT)
2009 && (flFlags & CODFL_DISABLEDTEXT)
2010 )
2011 {
2012 if (((pcdii->pRecord->flRecordAttr) & CRA_DISABLED) == 0)
2013 {
2014 /*
2015 // not disabled == valid WPS class
2016 if ((pcdii->pRecord->flRecordAttr) & CRA_SELECTED)
2017 {
2018 // not disabled, but selected:
2019 lBackground = winhQueryPresColor(hwndDlg, PP_HILITEBACKGROUNDCOLOR, SYSCLR_HILITEBACKGROUND);
2020 lForeground = winhQueryPresColor(hwndDlg, PP_HILITEFOREGROUNDCOLOR, SYSCLR_HILITEFOREGROUND);
2021 }
2022 else
2023 {
2024 // not disabled, not selected:
2025 lBackground = winhQueryPresColor(hwndDlg, PP_BACKGROUNDCOLOR, SYSCLR_BACKGROUND);
2026 lForeground = winhQueryPresColor(hwndDlg, PP_FOREGROUNDCOLOR, SYSCLR_WINDOWTEXT);
2027 } */
2028 mrc = FALSE;
2029 // let cnr draw the thing
2030 }
2031 else
2032 {
2033 // CRA_DISABLED:
2034
2035 ULONG flCmd = DT_LEFT | DT_TOP | DT_ERASERECT;
2036 RECTL rcl2;
2037
2038 // set draw colors
2039 LONG lBackground,
2040 lForeground;
2041
2042 // switch to RGB
2043 GpiCreateLogColorTable(poi->hps, 0, LCOLF_RGB, 0, 0, NULL);
2044
2045 if ((pcdii->pRecord->flRecordAttr) & CRA_SELECTED)
2046 {
2047 // disabled and selected:
2048 lBackground = WinQuerySysColor(HWND_DESKTOP,
2049 SYSCLR_SHADOWTEXT, 0);
2050 lForeground = winhQueryPresColor(poi->hwnd,
2051 PP_BACKGROUNDCOLOR,
2052 FALSE, // no inherit
2053 SYSCLR_WINDOW);
2054 }
2055 else
2056 {
2057 // disabled and not selected:
2058 lBackground = winhQueryPresColor(poi->hwnd,
2059 PP_BACKGROUNDCOLOR,
2060 FALSE,
2061 SYSCLR_WINDOW);
2062 lForeground = WinQuerySysColor(HWND_DESKTOP,
2063 SYSCLR_SHADOWTEXT, 0);
2064 }
2065
2066 memcpy(&rcl2, &(poi->rclItem), sizeof(rcl2));
2067 /* WinDrawText(poi->hps,
2068 strlen(pcdii->pRecord->pszText),
2069 pcdii->pRecord->pszText,
2070 &rcl2,
2071 lForeground, // foreground
2072 lBackground,
2073 flCmd); */
2074
2075 GpiSetBackColor(poi->hps, lBackground);
2076 GpiSetColor(poi->hps, lForeground);
2077
2078 winhDrawFormattedText(poi->hps,
2079 &rcl2,
2080 pcdii->pRecord->pszText,
2081 flCmd);
2082
2083 mrc = (MPARAM)TRUE; // tell cnr that we've drawn the item
2084 }
2085 }
2086 else if ( (poi->idItem == CMA_ICON)
2087 && (flFlags & CODFL_MINIICON)
2088 )
2089 {
2090 WinDrawPointer(poi->hps,
2091 // center the icon in the rectangle
2092 ( poi->rclItem.xLeft
2093 + (poi->rclItem.xRight - poi->rclItem.xLeft
2094 - WinQuerySysValue(HWND_DESKTOP, SV_CXICON) / 2
2095 ) / 2
2096 ),
2097 ( poi->rclItem.yBottom
2098 + (poi->rclItem.yTop - poi->rclItem.yBottom
2099 - WinQuerySysValue(HWND_DESKTOP, SV_CYICON) / 2
2100 ) / 2
2101 ),
2102 pcdii->pRecord->hptrMiniIcon,
2103 DP_MINI);
2104
2105 mrc = (MPARAM)TRUE; // tell cnr that we've drawn the item
2106 }
2107 }
2108
2109 return (mrc);
2110}
2111
2112/*
2113 *@@ cnrhDateTimeDos2Win:
2114 * this converts the information in a Control Program
2115 * DATETIME structure (as returned by DosGetDateTime)
2116 * to the CDATE and CTIME structures used by containers.
2117 *
2118 * If any of the target pointers is NULL, that data is
2119 * not converted.
2120 */
2121
2122BOOL cnrhDateTimeDos2Win(DATETIME* pdt, // in: Dos format date and time
2123 CDATE* pcdate, // out: container format date
2124 CTIME* pctime) // out: container format time
2125{
2126 if (pdt)
2127 {
2128 if (pcdate)
2129 {
2130 pcdate->day = pdt->day;
2131 pcdate->month = pdt->month;
2132 pcdate->year = pdt->year;
2133 }
2134 if (pctime)
2135 {
2136 pctime->seconds = pdt->seconds;
2137 pctime->minutes = pdt->minutes;
2138 pctime->hours = pdt->hours;
2139 }
2140 return (TRUE);
2141 }
2142 else
2143 return (FALSE);
2144}
2145
2146/*
2147 *@@ cnrhDateDos2Win:
2148 * converts an FDATE structure (Control program)
2149 * to a container CDATE structure.
2150 */
2151
2152BOOL cnrhDateDos2Win(FDATE* pfd, // in: DOS date
2153 CDATE* pcd) // out: container date
2154{
2155 if ((pfd) && (pcd))
2156 {
2157 pcd->day = pfd->day;
2158 pcd->month = pfd->month;
2159 pcd->year = pfd->year + 1980;
2160 return (TRUE);
2161 }
2162 else
2163 return (FALSE);
2164}
2165
2166/*
2167 *@@ cnrhTimeDos2Win:
2168 * converts an FTIME structure (Control program)
2169 * to a container CTIME structure.
2170 */
2171
2172BOOL cnrhTimeDos2Win(FTIME* pft, // in: DOS time
2173 CTIME* pct) // out: container time
2174{
2175 if ((pft) && (pct))
2176 {
2177 pct->seconds = pft->twosecs * 2;
2178 pct->minutes = pft->minutes;
2179 pct->hours = pft->hours;
2180 pct->ucReserved = 0;
2181 return (TRUE);
2182 }
2183 else
2184 return (FALSE);
2185}
Note: See TracBrowser for help on using the repository browser.