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

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

Major updates; timers, LVM, miscellaneous.

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