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

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

Miscellanous updates.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 74.2 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 *@@ cnrhIsChildOf:
1379 * returns TRUE only if precTest is a child record
1380 * of precParent in a tree view.
1381 *
1382 *@@added V0.9.7 (2000-12-13) [umoeller]
1383 */
1384
1385BOOL cnrhIsChildOf(HWND hwndCnr,
1386 PRECORDCORE precTest, // in: recc to test
1387 PRECORDCORE precParent) // in: parent to test
1388{
1389 BOOL brc = FALSE;
1390 if ((precTest) && (precParent))
1391 {
1392 PRECORDCORE precParentThis = precTest;
1393
1394 while (precParentThis)
1395 {
1396 // first call: get parent of precTest;
1397 // subsequent calls: climb up
1398 precParentThis = WinSendMsg(hwndCnr,
1399 CM_QUERYRECORD,
1400 precParentThis,
1401 MPFROM2SHORT(CMA_PARENT,
1402 CMA_ITEMORDER));
1403 if (precParentThis == (PRECORDCORE)-1)
1404 break;
1405 else
1406 if (precParentThis == precParent)
1407 {
1408 brc = TRUE;
1409 break;
1410 }
1411 }
1412 }
1413
1414 return (brc);
1415}
1416
1417/*
1418 *@@ cnrhForAllRecords:
1419 * this monster function calls pfnwpCallback
1420 * for really all the records in the container,
1421 * including child records in tree view.
1422 *
1423 * This is extremely useful for cleaning up
1424 * all record cores before a container window
1425 * gets destroyed.
1426 *
1427 * This function recurses for child records.
1428 * On the first call, preccParent should be
1429 * NULL; you may however specify a certain
1430 * record, and this function will call the
1431 * callback only for that record and children.
1432 *
1433 * pfnwpCallback gets called with the following
1434 * parameters:
1435 *
1436 * -- HWND hwnd: hwndCnr, as passed to this func
1437 * -- PRECORDCORE precc: current record core, as
1438 * determined by this func.
1439 * -- ULONG ulUser: what you have specified here.
1440 *
1441 * If the callback returns anything != 0, this
1442 * function stops even before all records have
1443 * been processed. You can use ulUser for a
1444 * pointer to a return value.
1445 *
1446 * This always returns the no. of records which
1447 * were processed.
1448 *
1449 * If you use this function for deleting record
1450 * cores, you can be sure that you can delete
1451 * every record, because your callback gets called
1452 * for the child records before the parent record.
1453 *
1454 *@@added V0.9.0 [umoeller]
1455 */
1456
1457ULONG cnrhForAllRecords(HWND hwndCnr,
1458 PRECORDCORE preccParent,
1459 PFNCBRECC pfncbRecc,
1460 ULONG ulUser1,
1461 ULONG ulUser2)
1462{
1463 PRECORDCORE precc2 = preccParent;
1464 ULONG ulrc = 0;
1465 USHORT usQuery;
1466 BOOL fFirstCall = TRUE;
1467
1468 while (TRUE)
1469 {
1470 if (fFirstCall)
1471 {
1472 // first call:
1473 if (preccParent)
1474 // non-root:
1475 usQuery = CMA_FIRSTCHILD;
1476 else
1477 // NULL == root:
1478 usQuery = CMA_FIRST;
1479 }
1480 else
1481 // subsequent calls:
1482 usQuery = CMA_NEXT; // works as CMA_NEXTCHILD also
1483
1484 precc2 =
1485 (PRECORDCORE)WinSendMsg(hwndCnr,
1486 CM_QUERYRECORD,
1487 (MPARAM)((fFirstCall)
1488 // first call (CMA_FIRSTCHILD or CMA_FIRST):
1489 ? preccParent // ignored for CMA_FIRST
1490 // subsequent calls (CMA_NEXTCHILD or CMA_NEXT):
1491 : precc2), // what we queried last
1492 MPFROM2SHORT(
1493 usQuery, // set above
1494 CMA_ITEMORDER)
1495 );
1496
1497 if ((precc2) && ((ULONG)precc2 != -1))
1498 {
1499 // record found:
1500 // recurse for that record
1501 ulrc += cnrhForAllRecords(hwndCnr,
1502 precc2, // new parent to search
1503 pfncbRecc,
1504 ulUser1,
1505 ulUser2);
1506
1507 // _Pmpf(("Calling callback for %s", precc2->pszIcon));
1508
1509 // call callback
1510 if (pfncbRecc)
1511 if ((*pfncbRecc)(hwndCnr, precc2, ulUser1, ulUser2))
1512 // returns something != NULL:
1513 // stop
1514 break;
1515 ulrc++;
1516 }
1517 else
1518 // no more records or error: get outta here
1519 break;
1520
1521 fFirstCall = FALSE;
1522 }
1523
1524 return (ulrc);
1525}
1526
1527/*
1528 * cnrhForAllChildRecords:
1529 * calls the specified fncbRecc callback for
1530 * the specified recc and all its child records.
1531 *
1532 *@@added V0.9.0 [umoeller]
1533 */
1534
1535/* VOID cnrhForAllChildRecords(HWND hwndCnr,
1536 PRECORDCORE precc,
1537 PFNCBRECC pfncbRecc,
1538 ULONG ulp1,
1539 ULONG ulp2)
1540{
1541 PRECORDCORE precc2 = precc;
1542 (*pfncbRecc)(precc, ulp1, ulp2);
1543 do {
1544 precc2 =
1545 (PRECORDCORE)WinSendMsg(hwndCnr,
1546 CM_QUERYRECORD,
1547 (MPARAM)precc2,
1548 MPFROM2SHORT(
1549 (precc2 == precc)
1550 ? CMA_FIRSTCHILD : CMA_NEXT,
1551 CMA_ITEMORDER)
1552 );
1553 if ((LONG)precc2 == -1)
1554 precc2 = NULL;
1555 if (precc2)
1556 // recurse again
1557 cnrhForAllChildRecords(hwndCnr, precc2, pfncbRecc, ulp1, ulp2);
1558 } while (precc2);
1559} */
1560
1561/*
1562 * cnrhForAllRecords2:
1563 * this is a useful function which calls
1564 * the specified callback function for
1565 * really all records in the container of
1566 * the main window, including child records.
1567 *
1568 * xxx
1569 *
1570 *@@added V0.9.0 [umoeller]
1571 */
1572
1573/* VOID cnrhForAllRecords2(HWND hwndCnr,
1574 PFNCBRECC pfncbRecc,
1575 ULONG ulp1,
1576 ULONG ulp2)
1577{
1578 PRECORDCORE precc2 = NULL;
1579 do {
1580 precc2 =
1581 (PRECORDCORE)WinSendMsg(hwndCnr,
1582 CM_QUERYRECORD,
1583 (MPARAM)precc2,
1584 MPFROM2SHORT(
1585 ((precc2 == NULL) ? CMA_FIRST : CMA_NEXT),
1586 CMA_ITEMORDER)
1587 );
1588 if ((LONG)precc2 == -1)
1589 precc2 = NULL;
1590 if (precc2)
1591 // recurse again
1592 cnrhForAllChildRecords(hwndCnr, precc2, pfncbRecc, ulp1, ulp2);
1593 } while (precc2);
1594} */
1595
1596/*
1597 * cnrhForAllParentRecords:
1598 * just as above, but climbs up instead.
1599 * The first call of the callback will be
1600 * for the parent record of precc.
1601 *
1602 *@@added V0.9.0 [umoeller]
1603 */
1604
1605/* VOID cnrhForAllParentRecords(HWND hwndCnr,
1606 PRECORDCORE precc,
1607 PFNCBRECC pfncbRecc,
1608 ULONG ulp1,
1609 ULONG ulp2)
1610{
1611 PRECORDCORE precc2 = precc;
1612 do {
1613 precc2 =
1614 (PRECORDCORE)WinSendMsg(hwndCnr,
1615 CM_QUERYRECORD,
1616 (MPARAM)precc2,
1617 MPFROM2SHORT(CMA_PARENT,
1618 CMA_ITEMORDER)
1619 );
1620 if ((LONG)precc2 == -1)
1621 precc2 = NULL;
1622 if (precc2)
1623 (*pfncbRecc)(precc2, ulp1, ulp2);
1624 } while (precc2);
1625} */
1626
1627/*
1628 *@@category: Helpers\PM helpers\Container helpers\Miscellaneous
1629 */
1630
1631/* ******************************************************************
1632 *
1633 * Miscellaneous
1634 *
1635 ********************************************************************/
1636
1637/*
1638 *@@ cnrhQueryCnrFromFrame:
1639 * find the handle of a frame window's container; we do this
1640 * by enumerating all the frame's child windows and comparing
1641 * their window classes to the WC_CONTAINER code.
1642 * This is not terribly fast, so use this func economically.
1643 * Returns NULLHANDLE if not found.
1644 */
1645
1646HWND cnrhQueryCnrFromFrame(HWND hwndFrame)
1647{
1648 HENUM henum;
1649 CHAR szClassName[256];
1650 HWND hwndCnr = NULLHANDLE,
1651 hwndTemp = NULLHANDLE;
1652
1653 if (hwndFrame)
1654 {
1655 henum = WinBeginEnumWindows(hwndFrame);
1656 if (henum)
1657 {
1658 do
1659 {
1660 hwndTemp = WinGetNextWindow(henum);
1661 if (hwndTemp)
1662 {
1663 if (WinQueryClassName(hwndTemp, 250, szClassName))
1664 if (strcmp(szClassName, "#37") == 0)
1665 // unbelievable, this is PM's code for WC_CONTAINER...
1666 hwndCnr = hwndTemp;
1667 }
1668 } while (hwndTemp);
1669 }
1670 WinEndEnumWindows(henum);
1671 }
1672
1673 return hwndCnr;
1674}
1675
1676/*
1677 *@@ cnrhInitDrag:
1678 * this sets up the necessary structures to begin dragging
1679 * a record core from a container. This helper func should be
1680 * called if your container should support being the source
1681 * window of a direct manipulation.
1682 *
1683 * This should get called in three situations:
1684 *
1685 * -- the container sends CN_INITDRAG ("real" d'n'd desired by user);
1686 *
1687 * -- the container sends us CN_PICKUP (Alt+MB2 pressed);
1688 *
1689 * -- the user has selected "Pickup" from a record core's
1690 * context menu (ID_XSMI_FILETYPES_PICKUP command).
1691 * In that case, you can also call this function with
1692 * usNotifyCode == CN_PICKUP.
1693 *
1694 * In all three cases, preccDrag must be the record core
1695 * which is to be dragged.
1696 *
1697 * Depending on usNotifyCode, this function, after having allocated
1698 * the necessary data, will do the following:
1699 *
1700 * 1) If (usNotifyCode == CN_PICKUP), we will initiate a non-modal
1701 * (lazy) drag (DrgLazyDrag). This function will then return
1702 * after the records have been given "picked" emphasis.
1703 *
1704 * Note: You must intercept CN_DROPNOTIFY in your window proc
1705 * to clean up resources later. For this, call:
1706 *
1707 + DrgDeleteDraginfoStrHandles(pdrgInfo);
1708 + DrgFreeDraginfo(pdrgInfo);
1709 *
1710 * 2) However, if (usNotifyCode == CN_INITDRAG), we will start
1711 * a regular modal drag here by calling DrgDrag. This function
1712 * will _not_ return until the object has been dropped or d'n'd
1713 * has been cancelled. PM establishes another message loop
1714 * internally for this. In this case, this function managed
1715 * cleanup automatically.
1716 *
1717 * This function supports one single record core only. The following
1718 * information will be set in the DRAGITEM structure:
1719 *
1720 * -- ulItemID will be set to the preccDrag so that the target
1721 * window can access the dragged record.
1722 *
1723 * -- hstrSourceName == hstrTargetName gets the RECORDCORE.pszIcon.
1724 *
1725 * The drag icon will be a default system file icon.
1726 *
1727 * Preconditions:
1728 *
1729 * -- pszIcon must be set in the RECORDCORE. This is used for
1730 * the item source name. This must not be NULL.
1731 */
1732
1733PDRAGINFO cnrhInitDrag(HWND hwndCnr,
1734 // in: source container window
1735 PRECORDCORE preccDrag,
1736 // in: record to be dragged (only one supported)
1737 USHORT usNotifyCode,
1738 // in: CN_INITDRAG or CN_PICKUP
1739 PSZ pszRMF,
1740 // in: rendering mechanism and format,
1741 // e.g. "(DRM_MYFILETYPE)x(DRF_UNKNOWN)"
1742 USHORT usSupportedOps)
1743 // stored in DRAGITEM.fsSupportedOps,
1744 // one or several of DO_COPYABLE, DO_MOVEABLE, DO_LINKABLE
1745{
1746 DRAGIMAGE drgImage;
1747 PDRAGINFO pdrgInfo = NULL;
1748
1749 memset(&drgImage, 0, sizeof(drgImage));
1750 pdrgInfo = DrgAllocDraginfo(1); // one item only
1751 if (pdrgInfo)
1752 {
1753 DRAGITEM drgItem;
1754 memset(&drgItem, 0, sizeof(drgItem));
1755
1756 drgItem.hwndItem = hwndCnr;
1757 drgItem.ulItemID = (ULONG)preccDrag;
1758 // we use this to store the container rec
1759 drgItem.hstrType = DrgAddStrHandle(DRT_UNKNOWN);
1760 // application defined
1761 drgItem.hstrRMF = DrgAddStrHandle(pszRMF);
1762 drgItem.hstrContainerName = 0;
1763 drgItem.hstrSourceName = DrgAddStrHandle(preccDrag->pszIcon);
1764 drgItem.hstrTargetName = drgItem.hstrSourceName;
1765 drgItem.fsSupportedOps = usSupportedOps;
1766
1767 // set the DRAGITEM struct into the memory
1768 // allocated by DrgAllocDraginfo()
1769 DrgSetDragitem(pdrgInfo,
1770 &drgItem,
1771 sizeof(DRAGITEM),
1772 0); // item index
1773
1774 // fill in the DRAGIMAGE structure
1775 drgImage.cb = sizeof(DRAGIMAGE);
1776 drgImage.hImage = WinQuerySysPointer(HWND_DESKTOP,
1777 SPTR_FILE,
1778 FALSE);
1779 // pointer used for dragging; we
1780 // use a dull default file icon
1781 drgImage.fl = DRG_ICON; // hImage is an HPOINTER
1782 drgImage.cxOffset = 0; // 5 * iOffset; // Image offset from mouse pointer
1783 drgImage.cyOffset = 0; // 5 * iOffset; // Image offset from mouse pointer
1784
1785 // set source emphasis for the container record
1786 WinSendMsg(hwndCnr,
1787 CM_SETRECORDEMPHASIS,
1788 (MPARAM)preccDrag,
1789 MPFROM2SHORT(TRUE,
1790 (usNotifyCode == CN_INITDRAG)
1791 // for dragging: source emphasis
1792 ? CRA_SOURCE
1793 // for lazy drag: picked emphasis
1794 : CRA_PICKED));
1795
1796 if (usNotifyCode == CN_INITDRAG)
1797 {
1798 // "real" dragging:
1799 // call modal function for drag'n'drop.
1800 // This does not return until either
1801 // d'n'd has been cancelled or the
1802 // record core has been dropped.
1803 /* HWND hwndTarget = */ DrgDrag(hwndCnr,
1804 pdrgInfo,
1805 &drgImage,
1806 1, // drag image count
1807 VK_ENDDRAG, // system drag button
1808 NULL); // reserved
1809
1810 // since it's the source which is responsible
1811 // for cleaning up the resources, this is
1812 // what we do now (we had a modal d'n'd)
1813 DrgDeleteDraginfoStrHandles(pdrgInfo);
1814 DrgFreeDraginfo(pdrgInfo);
1815
1816 // remove source emphasis for the container record
1817 WinSendMsg(hwndCnr,
1818 CM_SETRECORDEMPHASIS,
1819 (MPARAM)preccDrag,
1820 MPFROM2SHORT(FALSE,
1821 CRA_SOURCE));
1822 } // end if (usNotifyCode == CN_INITDRAG)
1823 else
1824 {
1825 // "lazy drag" (CN_PICKUP):
1826 // initiate lazy drag. This function returns
1827 // immediately, since lazy drag is non-modal.
1828 // As a result, we cannot clean up the resources
1829 // here, but need to do this later (CN_DROPNOTIFY).
1830 DrgLazyDrag(hwndCnr,
1831 pdrgInfo,
1832 &drgImage,
1833 1, // drag image count
1834 0); // reserved
1835 }
1836 } // end if (pdrgInfo)
1837
1838 return (pdrgInfo);
1839}
1840
1841/*
1842 *@@ cnrhOwnerDrawRecord:
1843 * this helper func can be called upon receiving
1844 * WM_DRAWITEM for owner-drawing container record
1845 * cores.
1846 *
1847 * Presently, this function supports drawing record
1848 * cores in gray color if the record has the CRA_DISABLED
1849 * style (which, AFAIK, PM ignores per default).
1850 *
1851 * This returns either TRUE or FALSE as an MPARAM, depending
1852 * on whether we have drawn the item. This return value should
1853 * also be the return value of your window procedure.
1854 */
1855
1856MRESULT cnrhOwnerDrawRecord(MPARAM mp2) // in: mp2 of WM_DRAWITEM (POWNERITEM)
1857{
1858 MRESULT mrc = 0;
1859
1860 // get generic DRAWITEM structure
1861 POWNERITEM poi = (POWNERITEM)mp2;
1862
1863 // check if we're to draw the text
1864 // (and not the icon)
1865 if (poi->idItem == CMA_TEXT)
1866 {
1867 // get container-specific draw-item struct
1868 PCNRDRAWITEMINFO pcdii = (PCNRDRAWITEMINFO)poi->hItem;
1869
1870 if (((pcdii->pRecord->flRecordAttr) & CRA_DISABLED) == 0)
1871 {
1872 /*
1873 // not disabled == valid WPS class
1874 if ((pcdii->pRecord->flRecordAttr) & CRA_SELECTED)
1875 {
1876 // not disabled, but selected:
1877 lBackground = winhQueryPresColor(hwndDlg, PP_HILITEBACKGROUNDCOLOR, SYSCLR_HILITEBACKGROUND);
1878 lForeground = winhQueryPresColor(hwndDlg, PP_HILITEFOREGROUNDCOLOR, SYSCLR_HILITEFOREGROUND);
1879 }
1880 else
1881 {
1882 // not disabled, not selected:
1883 lBackground = winhQueryPresColor(hwndDlg, PP_BACKGROUNDCOLOR, SYSCLR_BACKGROUND);
1884 lForeground = winhQueryPresColor(hwndDlg, PP_FOREGROUNDCOLOR, SYSCLR_WINDOWTEXT);
1885 } */
1886 mrc = FALSE;
1887 // let cnr draw the thing
1888 }
1889 else
1890 {
1891 // CRA_DISABLED:
1892
1893 ULONG flCmd = DT_LEFT | DT_TOP | DT_ERASERECT;
1894 RECTL rcl2;
1895
1896 // set draw colors
1897 LONG lBackground,
1898 lForeground;
1899
1900 // switch to RGB
1901 GpiCreateLogColorTable(poi->hps, 0, LCOLF_RGB, 0, 0, NULL);
1902
1903 if ((pcdii->pRecord->flRecordAttr) & CRA_SELECTED)
1904 {
1905 // disabled and selected:
1906 lBackground = WinQuerySysColor(HWND_DESKTOP,
1907 SYSCLR_SHADOWTEXT, 0);
1908 lForeground = winhQueryPresColor(poi->hwnd,
1909 PP_BACKGROUNDCOLOR,
1910 FALSE, // no inherit
1911 SYSCLR_WINDOW);
1912 }
1913 else
1914 {
1915 // disabled and not selected:
1916 lBackground = winhQueryPresColor(poi->hwnd,
1917 PP_BACKGROUNDCOLOR,
1918 FALSE,
1919 SYSCLR_WINDOW);
1920 lForeground = WinQuerySysColor(HWND_DESKTOP,
1921 SYSCLR_SHADOWTEXT, 0);
1922 }
1923
1924 memcpy(&rcl2, &(poi->rclItem), sizeof(rcl2));
1925 /* WinDrawText(poi->hps,
1926 strlen(pcdii->pRecord->pszText),
1927 pcdii->pRecord->pszText,
1928 &rcl2,
1929 lForeground, // foreground
1930 lBackground,
1931 flCmd); */
1932
1933 GpiSetBackColor(poi->hps, lBackground);
1934 GpiSetColor(poi->hps, lForeground);
1935
1936 winhDrawFormattedText(poi->hps,
1937 &rcl2,
1938 pcdii->pRecord->pszText,
1939 flCmd);
1940
1941 mrc = (MPARAM)TRUE; // tell cnr that we've drawn the item
1942 }
1943 } else
1944 mrc = (MPARAM)FALSE; // tell cnr to draw the item
1945
1946 return (mrc);
1947}
1948
1949/*
1950 *@@ cnrhDateTimeDos2Win:
1951 * this converts the information in a Control Program
1952 * DATETIME structure (as returned by DosGetDateTime)
1953 * to the CDATE and CTIME structures used by containers.
1954 *
1955 * If any of the target pointers is NULL, that data is
1956 * not converted.
1957 */
1958
1959BOOL cnrhDateTimeDos2Win(DATETIME* pdt, // in: Dos format date and time
1960 CDATE* pcdate, // out: container format date
1961 CTIME* pctime) // out: container format time
1962{
1963 if (pdt)
1964 {
1965 if (pcdate)
1966 {
1967 pcdate->day = pdt->day;
1968 pcdate->month = pdt->month;
1969 pcdate->year = pdt->year;
1970 }
1971 if (pctime)
1972 {
1973 pctime->seconds = pdt->seconds;
1974 pctime->minutes = pdt->minutes;
1975 pctime->hours = pdt->hours;
1976 }
1977 return (TRUE);
1978 }
1979 else
1980 return (FALSE);
1981}
1982
1983/*
1984 *@@ cnrhDateDos2Win:
1985 * converts an FDATE structure (Control program)
1986 * to a container CDATE structure.
1987 */
1988
1989BOOL cnrhDateDos2Win(FDATE* pfd, // in: DOS date
1990 CDATE* pcd) // out: container date
1991{
1992 if ((pfd) && (pcd))
1993 {
1994 pcd->day = pfd->day;
1995 pcd->month = pfd->month;
1996 pcd->year = pfd->year + 1980;
1997 return (TRUE);
1998 }
1999 else
2000 return (FALSE);
2001}
2002
2003/*
2004 *@@ cnrhTimeDos2Win:
2005 * converts an FTIME structure (Control program)
2006 * to a container CTIME structure.
2007 */
2008
2009BOOL cnrhTimeDos2Win(FTIME* pft, // in: DOS time
2010 CTIME* pct) // out: container time
2011{
2012 if ((pft) && (pct))
2013 {
2014 pct->seconds = pft->twosecs * 2;
2015 pct->minutes = pft->minutes;
2016 pct->hours = pft->hours;
2017 pct->ucReserved = 0;
2018 return (TRUE);
2019 }
2020 else
2021 return (FALSE);
2022}
Note: See TracBrowser for help on using the repository browser.