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

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

Lots of changes for icons and refresh.

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