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

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

Mistc. updates for WarpIN, new features.

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