source: branches/branch-1-0/src/helpers/cnrh.c@ 472

Last change on this file since 472 was 264, checked in by pr, 21 years ago

Fix spelling errors.

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