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

Last change on this file since 13 was 8, checked in by umoeller, 25 years ago

Initial checkin of helpers code which used to be in WarpIN.

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