source: trunk/src/helpers/winh.c@ 69

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

New folder sorting. Updated folder refresh. Misc other changes.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 170.4 KB
Line 
1
2/*
3 *@@sourcefile winh.c:
4 * contains Presentation Manager helper functions that are
5 * independent of a single application, i.e. these can be
6 * used w/out the rest of the XWorkplace source in any PM
7 * program.
8 *
9 * Usage: All PM programs.
10 *
11 * Function prefixes (new with V0.81):
12 * -- winh* Win (Presentation Manager) helper functions
13 *
14 * Note: Version numbering in this file relates to XWorkplace version
15 * numbering.
16 *
17 *@@header "helpers\winh.h"
18 */
19
20/*
21 * Copyright (C) 1997-2000 Ulrich M”ller.
22 * This file is part of the "XWorkplace helpers" source package.
23 * This is free software; you can redistribute it and/or modify
24 * it under the terms of the GNU General Public License as published
25 * by the Free Software Foundation, in version 2 as it comes in the
26 * "COPYING" file of the XWorkplace main distribution.
27 * This program is distributed in the hope that it will be useful,
28 * but WITHOUT ANY WARRANTY; without even the implied warranty of
29 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30 * GNU General Public License for more details.
31 */
32
33#define OS2EMX_PLAIN_CHAR
34 // this is needed for "os2emx.h"; if this is defined,
35 // emx will define PSZ as _signed_ char, otherwise
36 // as unsigned char
37
38#define INCL_DOSPROCESS
39#define INCL_DOSMODULEMGR
40#define INCL_DOSSEMAPHORES
41#define INCL_DOSDEVICES
42#define INCL_DOSDEVIOCTL
43#define INCL_DOSSESMGR
44#define INCL_DOSERRORS
45
46#define INCL_WINWINDOWMGR
47#define INCL_WINMESSAGEMGR
48#define INCL_WINFRAMEMGR
49#define INCL_WININPUT
50#define INCL_WINDIALOGS
51#define INCL_WINPOINTERS
52#define INCL_WINRECTANGLES
53#define INCL_WINSHELLDATA
54#define INCL_WINTIMER
55#define INCL_WINSYS
56#define INCL_WINHELP
57#define INCL_WINPROGRAMLIST
58#define INCL_WINSWITCHLIST
59#define INCL_WINBUTTONS
60#define INCL_WINMENUS
61#define INCL_WINSCROLLBARS
62#define INCL_WINLISTBOXES
63#define INCL_WINSTDSPIN
64#define INCL_WINSTDSLIDER
65#define INCL_WINCIRCULARSLIDER
66#define INCL_WINSTDFILE
67
68#define INCL_SPL
69#define INCL_SPLDOSPRINT
70#define INCL_SPLERRORS
71
72#define INCL_GPIBITMAPS
73#define INCL_GPIPRIMITIVES
74#include <os2.h>
75
76#include <stdlib.h>
77#include <string.h>
78#include <stdio.h>
79
80#include "setup.h" // code generation and debugging options
81
82#include "helpers\dosh.h"
83#include "helpers\winh.h"
84#include "helpers\prfh.h"
85#include "helpers\gpih.h"
86#include "helpers\stringh.h"
87#include "helpers\undoc.h"
88#include "helpers\xstring.h" // extended string helpers
89
90/*
91 *@@category: Helpers\PM helpers\Wrappers
92 */
93
94/* ******************************************************************
95 *
96 * Wrappers
97 *
98 ********************************************************************/
99
100#ifdef WINH_STANDARDWRAPPERS
101
102 /*
103 *@@ winhSendMsg:
104 * wrapper for WinSendMsg.
105 *
106 * If WINH_STANDARDWRAPPERS is #defined before
107 * including win.h, all WinSendMsg calls are
108 * redefined to use this wrapper instead. This
109 * reduces the amount of external fixups required
110 * for loading the module.
111 *
112 *@@added V0.9.12 (2001-05-18) [umoeller]
113 */
114
115 MRESULT winhSendMsg(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
116 {
117 // put the call in brackets so the macro won't apply here
118 return ((WinSendMsg)(hwnd, msg, mp1, mp2));
119 }
120
121 /*
122 *@@ winhPostMsg:
123 * wrapper for WinPostMsg.
124 *
125 * If WINH_STANDARDWRAPPERS is #defined before
126 * including win.h, all WinSendMsg calls are
127 * redefined to use this wrapper instead. This
128 * reduces the amount of external fixups required
129 * for loading the module.
130 *
131 *@@added V0.9.12 (2001-05-18) [umoeller]
132 */
133
134 BOOL winhPostMsg(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
135 {
136 // put the call in brackets so the macro won't apply here
137 return ((WinPostMsg)(hwnd, msg, mp1, mp2));
138 }
139
140 /*
141 *@@ winhWindowFromID:
142 *
143 *@@added V0.9.12 (2001-05-18) [umoeller]
144 */
145
146 HWND winhWindowFromID(HWND hwnd, ULONG id)
147 {
148 // put the call in brackets so the macro won't apply here
149 return ((WinWindowFromID)(hwnd, id));
150 }
151
152 /*
153 *@@ winhQueryWindow:
154 *
155 *@@added V0.9.12 (2001-05-18) [umoeller]
156 */
157
158 HWND winhQueryWindow(HWND hwnd, LONG lCode)
159 {
160 // put the call in brackets so the macro won't apply here
161 return ((WinQueryWindow)(hwnd, lCode));
162 }
163
164#endif // WINH_STANDARDWRAPPERS
165
166/*
167 *@@category: Helpers\PM helpers\Rectangle helpers
168 */
169
170/* ******************************************************************
171 *
172 * Rectangle helpers
173 *
174 ********************************************************************/
175
176/*
177 *@@ winhOffsetRect:
178 * like WinOffsetRect, but doesn't require
179 * an anchor block to be passed in. Why
180 * the original would need an anchor block
181 * for this awfully complicated task is
182 * a mystery to me anyway.
183 *
184 *@@added V0.9.9 (2001-03-13) [umoeller]
185 */
186
187VOID winhOffsetRect(PRECTL prcl,
188 LONG lx,
189 LONG ly)
190{
191 prcl->xLeft += lx;
192 prcl->xRight += lx;
193 prcl->yBottom += ly;
194 prcl->yTop += ly;
195}
196
197/*
198 *@@category: Helpers\PM helpers\Generics
199 */
200
201/* ******************************************************************
202 *
203 * Generics
204 *
205 ********************************************************************/
206
207/*
208 *@@ winhSetDlgItemChecked:
209 * checks a check box.
210 *
211 * This has been turned into a real function
212 * because this is used hundreds of times in
213 * XWP, and each WinSendDlgItemMsg in each
214 * macro produced a fixup relocation record.
215 *
216 *@@added V0.9.12 (2001-05-18) [umoeller]
217 */
218
219BOOL winhSetDlgItemChecked(HWND hwnd, // in: dialog
220 SHORT id, // in: dialog item ID
221 SHORT bCheck) // in: 0, 1, or (for tri-state) 2
222{
223 return ((BOOL)WinSendDlgItemMsg(hwnd,
224 id,
225 BM_SETCHECK,
226 MPFROMSHORT(bCheck),
227 MPNULL));
228}
229
230/*
231 *@@ winhIsDlgItemChecked:
232 * returns the current check state of the
233 * specified check box, which can be 0, 1,
234 * or (for tri-state checkboxes) 2.
235 *
236 *@@added V0.9.12 (2001-05-18) [umoeller]
237 */
238
239SHORT winhIsDlgItemChecked(HWND hwnd, // in: dialog
240 SHORT id) // in: dialog item ID
241{
242 return (SHORT1FROMMR(WinSendDlgItemMsg(hwnd,
243 id,
244 BM_QUERYCHECK,
245 MPNULL,
246 MPNULL)));
247}
248
249/*
250 *@@ winhEnableDlgItem:
251 *
252 *@@added V0.9.12 (2001-05-18) [umoeller]
253 */
254
255BOOL winhEnableDlgItem(HWND hwndDlg,
256 SHORT id,
257 BOOL fEnable)
258{
259 return (WinEnableWindow(WinWindowFromID(hwndDlg, id), fEnable));
260}
261
262/*
263 *@@ winhIsDlgItemEnabled:
264 *
265 *@@added V0.9.12 (2001-05-18) [umoeller]
266 */
267
268BOOL winhIsDlgItemEnabled(HWND hwndDlg,
269 SHORT id)
270{
271 return (WinIsWindowEnabled(WinWindowFromID(hwndDlg, id)));
272}
273
274
275/*
276 *@@category: Helpers\PM helpers\Menu helpers
277 */
278
279/* ******************************************************************
280 *
281 * Menu helpers
282 *
283 ********************************************************************/
284
285/*
286 *@@ winhQueryMenuItem:
287 * wrapper around MM_QUERYITEM.
288 *
289 *@@added V0.9.12 (2001-05-18) [umoeller]
290 */
291
292BOOL winhQueryMenuItem(HWND hwndMenu,
293 USHORT usItemID,
294 BOOL fSearchSubmenus,
295 PMENUITEM pmi) // out: MENUITEM data
296{
297 return ((BOOL)WinSendMsg(hwndMenu,
298 MM_QUERYITEM,
299 MPFROM2SHORT(usItemID, fSearchSubmenus),
300 (MPARAM)pmi));
301}
302
303/*
304 *@@ winhInsertMenuItem:
305 * this inserts one one menu item into a given menu.
306 *
307 * Returns the return value of the MM_INSERTITEM msg:
308 * -- MIT_MEMERROR: space allocation for menu item failed
309 * -- MIT_ERROR: other error
310 * -- other: zero-based index of new item in menu.
311 */
312
313SHORT winhInsertMenuItem(HWND hwndMenu, // in: menu to insert item into
314 SHORT iPosition, // in: zero-based index of where to
315 // insert or MIT_END
316 SHORT sItemId, // in: ID of new menu item
317 const char *pcszItemTitle, // in: title of new menu item
318 SHORT afStyle,
319 // in: MIS_* style flags.
320 // Valid menu item styles are:
321 // -- MIS_SUBMENU
322 // -- MIS_SEPARATOR
323 // -- MIS_BITMAP: the display object is a bit map.
324 // -- MIS_TEXT: the display object is a text string.
325 // -- MIS_BUTTONSEPARATOR:
326 // The item is a menu button. Any menu can have zero,
327 // one, or two items of this type. These are the last
328 // items in a menu and are automatically displayed after
329 // a separator bar. The user cannot move the cursor to
330 // these items, but can select them with the pointing
331 // device or with the appropriate key.
332 // -- MIS_BREAK: the item begins a new row or column.
333 // -- MIS_BREAKSEPARATOR:
334 // Same as MIS_BREAK, except that it draws a separator
335 // between rows or columns of a pull-down menu.
336 // This style can only be used within a submenu.
337 // -- MIS_SYSCOMMAND:
338 // menu posts a WM_SYSCOMMAND message rather than a
339 // WM_COMMAND message.
340 // -- MIS_OWNERDRAW:
341 // WM_DRAWITEM and WM_MEASUREITEM notification messages
342 // are sent to the owner to draw the item or determine its size.
343 // -- MIS_HELP:
344 // menu posts a WM_HELP message rather than a
345 // WM_COMMAND message.
346 // -- MIS_STATIC
347 // This type of item exists for information purposes only.
348 // It cannot be selected with the pointing device or
349 // keyboard.
350 SHORT afAttr)
351 // in: MIA_* attribute flags
352 // Valid menu item attributes (afAttr) are:
353 // -- MIA_HILITED: if and only if, the item is selected.
354 // -- MIA_CHECKED: a check mark appears next to the item (submenu only).
355 // -- MIA_DISABLED: item is disabled and cannot be selected.
356 // The item is drawn in a disabled state (gray).
357 // -- MIA_FRAMED: a frame is drawn around the item (top-level menu only).
358 // -- MIA_NODISMISS:
359 // if the item is selected, the submenu remains down. A menu
360 // with this attribute is not hidden until the application
361 // or user explicitly does so, for example by selecting either
362 // another menu on the action bar or by pressing the escape key.
363{
364 MENUITEM mi;
365 SHORT src = MIT_ERROR;
366
367 mi.iPosition = iPosition;
368 mi.afStyle = afStyle;
369 mi.afAttribute = afAttr;
370 mi.id = sItemId;
371 mi.hwndSubMenu = 0;
372 mi.hItem = 0;
373 src = SHORT1FROMMR(WinSendMsg(hwndMenu,
374 MM_INSERTITEM,
375 (MPARAM)&mi,
376 (MPARAM)pcszItemTitle));
377 return (src);
378}
379
380/*
381 *@@ winhInsertSubmenu:
382 * this inserts a submenu into a given menu and, if
383 * sItemId != 0, inserts one item into this new submenu also.
384 *
385 * See winhInsertMenuItem for valid menu item styles and
386 * attributes.
387 *
388 * Returns the HWND of the new submenu.
389 */
390
391HWND winhInsertSubmenu(HWND hwndMenu, // in: menu to add submenu to
392 ULONG iPosition, // in: index where to add submenu or MIT_END
393 SHORT sMenuId, // in: menu ID of new submenu
394 const char *pcszSubmenuTitle, // in: title of new submenu
395 USHORT afMenuStyle, // in: MIS* style flags for submenu;
396 // MIS_SUBMENU will always be added
397 SHORT sItemId, // in: ID of first item to add to submenu;
398 // if 0, no first item is inserted
399 const char *pcszItemTitle, // in: title of this item
400 // (if sItemID != 0)
401 USHORT afItemStyle, // in: style flags for this item, e.g. MIS_TEXT
402 // (this is ignored if sItemID == 0)
403 USHORT afAttribute) // in: attributes for this item, e.g. MIA_DISABLED
404 // (this is ignored if sItemID == 0)
405{
406 MENUITEM mi;
407 SHORT src = MIT_ERROR;
408 HWND hwndNewMenu;
409
410 // create new, empty menu
411 hwndNewMenu = WinCreateMenu(hwndMenu,
412 NULL); // no menu template
413 if (hwndNewMenu)
414 {
415 // add "submenu item" to this empty menu;
416 // for some reason, PM always needs submenus
417 // to be a menu item
418 mi.iPosition = iPosition;
419 mi.afStyle = afMenuStyle | MIS_SUBMENU;
420 mi.afAttribute = 0;
421 mi.id = sMenuId;
422 mi.hwndSubMenu = hwndNewMenu;
423 mi.hItem = 0;
424 src = SHORT1FROMMR(WinSendMsg(hwndMenu, MM_INSERTITEM, (MPARAM)&mi, (MPARAM)pcszSubmenuTitle));
425 if ( (src != MIT_MEMERROR)
426 && (src != MIT_ERROR)
427 )
428 {
429 // set the new menu's ID to the same as the
430 // submenu item
431 WinSetWindowUShort(hwndNewMenu, QWS_ID, sMenuId);
432
433 if (sItemId)
434 {
435 // item id given: insert first menu item also
436 mi.iPosition = 0;
437 mi.afStyle = afItemStyle;
438 mi.afAttribute = afAttribute;
439 mi.id = sItemId;
440 mi.hwndSubMenu = 0;
441 mi.hItem = 0;
442 WinSendMsg(hwndNewMenu,
443 MM_INSERTITEM,
444 (MPARAM)&mi,
445 (MPARAM)pcszItemTitle);
446 }
447 }
448 }
449 return (hwndNewMenu);
450}
451
452/*
453 *@@ winhInsertMenuSeparator:
454 * this inserts a separator into a given menu at
455 * the given position (which may be MIT_END);
456 * returns the position at which the item was
457 * inserted.
458 */
459
460SHORT winhInsertMenuSeparator(HWND hMenu, // in: menu to add separator to
461 SHORT iPosition, // in: index where to add separator or MIT_END
462 SHORT sId) // in: separator menu ID (doesn't really matter)
463{
464 MENUITEM mi;
465 mi.iPosition = iPosition;
466 mi.afStyle = MIS_SEPARATOR; // append separator
467 mi.afAttribute = 0;
468 mi.id = sId;
469 mi.hwndSubMenu = 0;
470 mi.hItem = 0;
471
472 return (SHORT1FROMMR(WinSendMsg(hMenu,
473 MM_INSERTITEM,
474 (MPARAM)&mi,
475 (MPARAM)"")));
476}
477
478/*
479 *@@ winhCopyMenuItem:
480 * copies a menu item from hmenuSource to hmenuTarget.
481 *
482 * This creates a full duplicate. If usID specifies
483 * a submenu, the entire submenu is copied as well
484 * (this will then recurse).
485 *
486 * NOTE: Copying submenus will work only if each item
487 * in the submenu has a unique menu ID. This is due
488 * to the dumb implementation of menus in PM where
489 * it is impossible to query menu items without
490 * knowing their ID.
491 *
492 *@@added V0.9.9 (2001-03-09) [umoeller]
493 */
494
495BOOL winhCopyMenuItem(HWND hmenuTarget,
496 HWND hmenuSource,
497 USHORT usID,
498 SHORT sTargetPosition) // in: position to insert at or MIT_END
499{
500 BOOL brc = FALSE;
501 MENUITEM mi = {0};
502 if (WinSendMsg(hmenuSource,
503 MM_QUERYITEM,
504 MPFROM2SHORT(usID,
505 FALSE),
506 (MPARAM)&mi))
507 {
508 // found in source:
509 // is it a separator?
510 if (mi.afStyle & MIS_SEPARATOR)
511 winhInsertMenuSeparator(hmenuTarget,
512 sTargetPosition,
513 usID);
514 else
515 {
516 // no separator:
517 // get item text
518 PSZ pszSource = winhQueryMenuItemText(hmenuSource,
519 usID);
520 if (pszSource)
521 {
522 if ( (mi.afStyle & MIS_SUBMENU)
523 && (mi.hwndSubMenu)
524 )
525 {
526 // this is the top of a submenu:
527 HWND hwndSubMenu = winhInsertSubmenu(hmenuTarget,
528 sTargetPosition,
529 mi.id,
530 pszSource,
531 mi.afStyle,
532 0,
533 NULL,
534 0,
535 0);
536 if (hwndSubMenu)
537 {
538 // now copy all the items in the submenu
539 SHORT cMenuItems = SHORT1FROMMR(WinSendMsg(mi.hwndSubMenu,
540 MM_QUERYITEMCOUNT,
541 0, 0));
542 // loop through all entries in the original submenu
543 ULONG i;
544 for (i = 0;
545 i < cMenuItems;
546 i++)
547 {
548 CHAR szItemText[100];
549 SHORT id = SHORT1FROMMR(WinSendMsg(mi.hwndSubMenu,
550 MM_ITEMIDFROMPOSITION,
551 MPFROMSHORT(i),
552 0));
553 // recurse
554 winhCopyMenuItem(hwndSubMenu,
555 mi.hwndSubMenu,
556 id,
557 MIT_END);
558 }
559
560 // now check... was the original submenu
561 // "conditional cascade"?
562 if (WinQueryWindowULong(mi.hwndSubMenu,
563 QWL_STYLE)
564 & MS_CONDITIONALCASCADE)
565 // yes:
566 {
567 // get the original default item
568 SHORT sDefID = SHORT1FROMMR(WinSendMsg(mi.hwndSubMenu,
569 MM_QUERYDEFAULTITEMID,
570 0, 0));
571 // set "conditional cascade style" on target too
572 WinSetWindowBits(hwndSubMenu,
573 QWL_STYLE,
574 MS_CONDITIONALCASCADE,
575 MS_CONDITIONALCASCADE);
576 // and set default item id
577 WinSendMsg(hwndSubMenu,
578 MM_SETDEFAULTITEMID,
579 (MPARAM)sDefID,
580 0);
581 }
582 } // end if (hwndSubmenu)
583 } // end if ( (mi.afStyle & MIS_SUBMENU)
584 else
585 {
586 // no submenu:
587 // just copy that item
588 SHORT s;
589 mi.iPosition = sTargetPosition;
590 s = SHORT1FROMMR(WinSendMsg(hmenuTarget,
591 MM_INSERTITEM,
592 MPFROMP(&mi),
593 MPFROMP(pszSource)));
594 if (s != MIT_MEMERROR && s != MIT_ERROR)
595 brc = TRUE;
596 }
597
598 free(pszSource);
599 } // end if (pszSource)
600 } // end else if (mi.afStyle & MIS_SEPARATOR)
601 } // end if (WinSendMsg(hmenuSource, MM_QUERYITEM,...
602
603 return (brc);
604}
605
606/*
607 *@@ winhMergeIntoSubMenu:
608 * creates a new submenu in hmenuTarget with the
609 * specified title at the specified position
610 * and copies the entire contents of hmenuSource
611 * into that.
612 *
613 * Returns the window handle of the new submenu
614 * or NULLHANDLE on errors.
615 *
616 * NOTE: Copying submenus will work only if each item
617 * in the submenu has a unique menu ID. This is due
618 * to the dumb implementation of menus in PM where
619 * it is impossible to query menu items without
620 * knowing their ID.
621 *
622 *@@added V0.9.9 (2001-03-09) [umoeller]
623 */
624
625HWND winhMergeIntoSubMenu(HWND hmenuTarget, // in: menu where to create submenu
626 SHORT sTargetPosition, // in: position to insert at or MIT_END
627 const char *pcszTitle, // in: title of new submenu
628 SHORT sID, // in: ID of new submenu
629 HWND hmenuSource) // in: menu to merge
630{
631 HWND hwndNewSubmenu = WinCreateMenu(hmenuTarget, NULL);
632 if (hwndNewSubmenu)
633 {
634 MENUITEM mi = {0};
635 SHORT src = 0;
636 SHORT s = 0;
637 mi.iPosition = MIT_END;
638 mi.afStyle = MIS_TEXT | MIS_SUBMENU;
639 mi.id = 2000;
640 mi.hwndSubMenu = hwndNewSubmenu;
641
642 WinSetWindowUShort(hwndNewSubmenu, QWS_ID, sID);
643
644 // insert new submenu into hmenuTarget
645 src = SHORT1FROMMR(WinSendMsg(hmenuTarget,
646 MM_INSERTITEM,
647 (MPARAM)&mi,
648 (MPARAM)pcszTitle));
649 if ( (src != MIT_MEMERROR)
650 && (src != MIT_ERROR)
651 )
652 {
653 int i;
654 SHORT cMenuItems = SHORT1FROMMR(WinSendMsg(hmenuSource,
655 MM_QUERYITEMCOUNT,
656 0, 0));
657
658 // loop through all entries in the original menu
659 for (i = 0; i < cMenuItems; i++)
660 {
661 SHORT id = SHORT1FROMMR(WinSendMsg(hmenuSource,
662 MM_ITEMIDFROMPOSITION,
663 MPFROMSHORT(i),
664 0));
665 winhCopyMenuItem(hwndNewSubmenu,
666 hmenuSource,
667 id,
668 MIT_END);
669 }
670 }
671 else
672 {
673 // error:
674 WinDestroyWindow(hwndNewSubmenu);
675 hwndNewSubmenu = NULLHANDLE;
676 }
677 }
678
679 return (hwndNewSubmenu);
680}
681
682/*
683 *@@ winhQueryMenuItemText:
684 * this returns a menu item text as a PSZ
685 * to a newly allocated buffer or NULL if
686 * not found.
687 *
688 * Returns NULL on error. Use free()
689 * to free the return value.
690 *
691 * Use the WinSetMenuItemText macro to
692 * set the menu item text.
693 */
694
695PSZ winhQueryMenuItemText(HWND hwndMenu,
696 USHORT usItemID) // in: menu item ID (not index)
697{
698 PSZ prc = NULL;
699
700 SHORT sLength = SHORT1FROMMR(WinSendMsg(hwndMenu, MM_QUERYITEMTEXTLENGTH,
701 (MPARAM)(ULONG)usItemID,
702 (MPARAM)NULL));
703 if (sLength)
704 {
705 prc = (PSZ)malloc(sLength + 1);
706 WinSendMsg(hwndMenu,
707 MM_QUERYITEMTEXT,
708 MPFROM2SHORT(usItemID, sLength + 1),
709 (MPARAM)prc);
710 }
711
712 return (prc);
713}
714
715/*
716 *@@ winhAppend2MenuItemText:
717 *
718 *@@added V0.9.2 (2000-03-08) [umoeller]
719 */
720
721BOOL winhAppend2MenuItemText(HWND hwndMenu,
722 USHORT usItemID, // in: menu item ID (not index)
723 const char *pcszAppend, // in: text to append
724 BOOL fTab) // in: if TRUE, add \t before pcszAppend
725{
726 BOOL brc = FALSE;
727 CHAR szItemText[400];
728 if (WinSendMsg(hwndMenu,
729 MM_QUERYITEMTEXT,
730 MPFROM2SHORT(usItemID,
731 sizeof(szItemText)),
732 (MPARAM)szItemText))
733 {
734 // text copied:
735 if (fTab)
736 {
737 if (strchr(szItemText, '\t'))
738 // we already have a tab:
739 strcat(szItemText, " ");
740 else
741 strcat(szItemText, "\t");
742 }
743 strcat(szItemText, pcszAppend);
744
745 brc = (BOOL)WinSendMsg(hwndMenu,
746 MM_SETITEMTEXT,
747 MPFROMSHORT(usItemID),
748 (MPARAM)szItemText);
749 }
750
751 return (brc);
752}
753
754/*
755 *@@ winhMenuRemoveEllipse:
756 * removes a "..." substring from a menu item
757 * title, if found. This is useful if confirmations
758 * have been turned off for a certain menu item, which
759 * should be reflected in the menu.
760 */
761
762VOID winhMenuRemoveEllipse(HWND hwndMenu,
763 USHORT usItemId) // in: item to remove "..." from
764{
765 CHAR szBuf[255];
766 CHAR *p;
767 WinSendMsg(hwndMenu,
768 MM_QUERYITEMTEXT,
769 MPFROM2SHORT(usItemId, sizeof(szBuf)-1),
770 (MPARAM)&szBuf);
771 if ((p = strstr(szBuf, "...")))
772 strcpy(p, p+3);
773 WinSendMsg(hwndMenu,
774 MM_SETITEMTEXT,
775 MPFROMSHORT(usItemId),
776 (MPARAM)&szBuf);
777}
778
779/*
780 *@@ winhQueryItemUnderMouse:
781 * this queries the menu item which corresponds
782 * to the given mouse coordinates.
783 * Returns the ID of the menu item and stores its
784 * rectangle in *prtlItem; returns (-1) upon errors.
785 */
786
787SHORT winhQueryItemUnderMouse(HWND hwndMenu, // in: menu handle
788 POINTL *pptlMouse, // in: mouse coordinates
789 RECTL *prtlItem) // out: rectangle of menu item
790{
791 SHORT s, sItemId, sItemCount;
792 HAB habDesktop = WinQueryAnchorBlock(HWND_DESKTOP);
793
794 sItemCount = SHORT1FROMMR(WinSendMsg(hwndMenu, MM_QUERYITEMCOUNT, MPNULL, MPNULL));
795
796 for (s = 0;
797 s <= sItemCount;
798 s++)
799 {
800 sItemId = SHORT1FROMMR(WinSendMsg(hwndMenu,
801 MM_ITEMIDFROMPOSITION,
802 (MPARAM)(ULONG)s, MPNULL));
803 WinSendMsg(hwndMenu,
804 MM_QUERYITEMRECT,
805 MPFROM2SHORT(sItemId, FALSE),
806 (MPARAM)prtlItem);
807 if (WinPtInRect(habDesktop, prtlItem, pptlMouse))
808 return (sItemId);
809 }
810 /* sItemId = (SHORT)WinSendMsg(hwndMenu, MM_ITEMIDFROMPOSITION, (MPARAM)(sItemCount-1), MPNULL);
811 return (sItemId); */
812 return (-1); // error: no valid menu item
813}
814
815/*
816 *@@category: Helpers\PM helpers\Slider helpers
817 */
818
819/* ******************************************************************
820 *
821 * Slider helpers
822 *
823 ********************************************************************/
824
825/*
826 *@@ winhReplaceWithLinearSlider:
827 * this destroys the control with the ID ulID in hwndDlg
828 * and creates a linear slider at the same position with the
829 * same ID (effectively replacing it).
830 *
831 * This is needed because the IBM dialog editor (DLGEDIT.EXE)
832 * keeps crashing when creating sliders. So the way to do
833 * this easily is to create some other control with DLGEDIT
834 * where the slider should be later and call this function
835 * on that control when the dialog is initialized.
836 *
837 * You need to specify _one_ of the following with ulSliderStyle:
838 * -- SLS_HORIZONTAL: horizontal slider (default)
839 * -- SLS_VERTICAL: vertical slider
840 *
841 * plus _one_ additional common slider style for positioning:
842 * -- for horizontal sliders: SLS_BOTTOM, SLS_CENTER, or SLS_TOP
843 * -- for vertical sliders: SLS_LEFT, SLS_CENTER, or SLS_RIGHT
844 *
845 * Additional common slider styles are:
846 * -- SLS_PRIMARYSCALE1: determines the location of the scale
847 * on the slider shaft by using increment
848 * and spacing specified for scale 1 as
849 * the incremental value for positioning
850 * the slider arm. Scale 1 is displayed
851 * above the slider shaft of a horizontal
852 * slider and to the right of the slider
853 * shaft of a vertical slider. This is
854 * the default for a slider.
855 * -- SLS_PRIMARYSCALE2: not supported by this function
856 * -- SLS_READONLY: creates a read-only slider, which
857 * presents information to the user but
858 * allows no interaction with the user.
859 * -- SLS_RIBBONSTRIP: fills, as the slider arm moves, the
860 * slider shaft between the home position
861 * and the slider arm with a color value
862 * different from slider shaft color,
863 * similar to mercury in a thermometer.
864 * -- SLS_OWNERDRAW: notifies the application whenever the
865 * slider shaft, the ribbon strip, the
866 * slider arm, and the slider background
867 * are to be drawn.
868 * -- SLS_SNAPTOINCREMENT: causes the slider arm, when positioned
869 * between two values, to be positioned
870 * to the nearest value and redrawn at
871 * that position.
872 *
873 * Additionally, for horizontal sliders:
874 * -- SLS_BUTTONSLEFT: specifies that the optional slider
875 * buttons are to be used and places them
876 * to the left of the slider shaft. The
877 * buttons move the slider arm by one
878 * position, left or right, in the
879 * direction selected.
880 * -- SLS_BUTTONSRIGHT: specifies that the optional slider
881 * buttons are to be used and places them
882 * to the right of the slider shaft. The
883 * buttons move the slider arm by one
884 * position, left or right, in the
885 * direction selected.
886 * -- SLS_HOMELEFT: specifies the slider arm's home
887 * position. The left edge is used as the
888 * base value for incrementing (default).
889 * -- SLS_HOMERIGHT: specifies the slider arm's home
890 * position. The right edge is used as
891 * the base value for incrementing.
892 *
893 * Instead, for vertical sliders:
894 * -- SLS_BUTTONSBOTTOM: specifies that the optional slider
895 * buttons are to be used and places them
896 * at the bottom of the slider shaft. The
897 * buttons move the slider arm by one
898 * position, up or down, in the direction
899 * selected.
900 * -- SLS_BUTTONSTOP: specifies that the optional slider
901 * buttons are to be used and places them
902 * at the top of the slider shaft. The
903 * buttons move the slider arm by one
904 * position, up or down, in the direction
905 * selected.
906 * -- SLS_HOMEBOTTOM: specifies the slider arm's home
907 * position. The bottom of the slider is
908 * used as the base value for
909 * incrementing.
910 * -- SLS_HOMETOP: specifies the slider arm's home
911 * position. The top of the slider is
912 * used as the base value for
913 * incrementing.
914 *
915 * Notes: This function automatically adds WS_PARENTCLIP,
916 * WS_TABSTOP, and WS_SYNCPAINT to the specified styles.
917 * For the WS_TABSTOP style, hwndInsertAfter is important.
918 * If you specify HWND_TOP, your window will be the first
919 * in the tab stop list.
920 *
921 * It also shows the slider after having done all the
922 * processing in here by calling WinShowWindow.
923 *
924 * Also, we only provide support for scale 1 here, so
925 * do not specify SLS_PRIMARYSCALE2 with ulSliderStyle,
926 * and we have the slider calculate all the spacings.
927 *
928 * This returns the HWND of the slider or NULLHANDLE upon
929 * errors.
930 *
931 *@@added V0.9.0 [umoeller]
932 */
933
934HWND winhReplaceWithLinearSlider(HWND hwndParent, // in: parent of old control and slider
935 HWND hwndOwner, // in: owner of old control and slider
936 HWND hwndInsertAfter, // in: the control after which the slider should
937 // come up, or HWND_TOP, or HWND_BOTTOM
938 ULONG ulID, // in: ID of old control and slider
939 ULONG ulSliderStyle, // in: SLS_* styles
940 ULONG ulTickCount) // in: number of ticks (scale 1)
941{
942 HWND hwndSlider = NULLHANDLE;
943 HWND hwndKill = WinWindowFromID(hwndParent, ulID);
944 if (hwndKill)
945 {
946 SWP swpControl;
947 if (WinQueryWindowPos(hwndKill, &swpControl))
948 {
949 SLDCDATA slcd;
950
951 // destroy the old control
952 WinDestroyWindow(hwndKill);
953
954 // initialize slider control data
955 slcd.cbSize = sizeof(SLDCDATA);
956 slcd.usScale1Increments = ulTickCount;
957 slcd.usScale1Spacing = 0; // have slider calculate it
958 slcd.usScale2Increments = 0;
959 slcd.usScale2Spacing = 0;
960
961 // create a slider with the same ID at the same
962 // position
963 hwndSlider = WinCreateWindow(hwndParent,
964 WC_SLIDER,
965 NULL, // no window text
966 ulSliderStyle
967 | WS_PARENTCLIP
968 | WS_SYNCPAINT
969 | WS_TABSTOP,
970 swpControl.x,
971 swpControl.y,
972 swpControl.cx,
973 swpControl.cy,
974 hwndOwner,
975 hwndInsertAfter,
976 ulID, // same ID as destroyed control
977 &slcd, // slider control data
978 NULL); // presparams
979
980 WinSendMsg(hwndSlider,
981 SLM_SETTICKSIZE,
982 MPFROM2SHORT(SMA_SETALLTICKS,
983 6), // 15 pixels high
984 NULL);
985
986 WinShowWindow(hwndSlider, TRUE);
987 }
988 }
989
990 return (hwndSlider);
991}
992
993/*
994 *@@ winhSetSliderTicks:
995 * this adds ticks to the given linear slider,
996 * which are ulPixels pixels high. A useful
997 * value for this is 4.
998 *
999 * This queries the slider for the primary
1000 * scale values. Only the primary scale is
1001 * supported.
1002 *
1003 * This function goes sets the ticks twice,
1004 * once with mpEveryOther1 and ulPixels1,
1005 * and then a second time with mpEveryOther2
1006 * and ulPixels2. This allows you to quickly
1007 * give, say, every tenth item a taller tick.
1008 *
1009 * For every set, if mpEveryOther is 0, this sets
1010 * all ticks on the primary slider scale.
1011 *
1012 * If mpEveryOther is != 0, SHORT1FROMMP
1013 * specifies the first tick to set, and
1014 * SHORT2FROMMP specifies every other tick
1015 * to set from there. For example:
1016 *
1017 + MPFROM2SHORT(9, 10)
1018 *
1019 * would set tick 9, 19, 29, and so forth.
1020 *
1021 * If both mpEveryOther and ulPixels are -1,
1022 * that set is skipped.
1023 *
1024 * Example: Considering a slider with a
1025 * primary scale from 0 to 30, using
1026 *
1027 + winhSetSliderTicks(hwndSlider,
1028 + 0, // every tick
1029 + 3, // to three pixels
1030 + MPFROM2SHORT(9, 10) // then every tenth
1031 + 6); // to six pixels.
1032 *
1033 * Returns FALSE upon errors.
1034 *
1035 *@@added V0.9.1 (99-12-04) [umoeller]
1036 *@@changed V0.9.7 (2001-01-18) [umoeller]: added second set
1037 */
1038
1039BOOL winhSetSliderTicks(HWND hwndSlider, // in: linear slider
1040 MPARAM mpEveryOther1, // in: set 1
1041 ULONG ulPixels1,
1042 MPARAM mpEveryOther2, // in: set 2
1043 ULONG ulPixels2)
1044{
1045 BOOL brc = FALSE;
1046
1047 ULONG ulSet;
1048 MPARAM mpEveryOther = mpEveryOther1;
1049 ULONG ulPixels = ulPixels1;
1050
1051 // do this twice
1052 for (ulSet = 0;
1053 ulSet < 2;
1054 ulSet++)
1055 {
1056 if (mpEveryOther == 0)
1057 {
1058 // set all ticks:
1059 brc = (BOOL)WinSendMsg(hwndSlider,
1060 SLM_SETTICKSIZE,
1061 MPFROM2SHORT(SMA_SETALLTICKS,
1062 ulPixels),
1063 NULL);
1064 }
1065 else if ( (mpEveryOther != (MPARAM)-1) && (ulPixels != -1) )
1066 {
1067 SLDCDATA slcd;
1068 WNDPARAMS wp;
1069 memset(&wp, 0, sizeof(WNDPARAMS));
1070 wp.fsStatus = WPM_CTLDATA;
1071 wp.cbCtlData = sizeof(slcd);
1072 wp.pCtlData = &slcd;
1073 // get primary scale data from the slider
1074 if (WinSendMsg(hwndSlider,
1075 WM_QUERYWINDOWPARAMS,
1076 (MPARAM)&wp,
1077 0))
1078 {
1079 USHORT usStart = SHORT1FROMMP(mpEveryOther),
1080 usEveryOther = SHORT2FROMMP(mpEveryOther);
1081
1082 USHORT usScale1Max = slcd.usScale1Increments,
1083 us;
1084
1085 brc = TRUE;
1086
1087 for (us = usStart; us < usScale1Max; us += usEveryOther)
1088 {
1089 if (!(BOOL)WinSendMsg(hwndSlider,
1090 SLM_SETTICKSIZE,
1091 MPFROM2SHORT(us,
1092 ulPixels),
1093 NULL))
1094 {
1095 brc = FALSE;
1096 break;
1097 }
1098 }
1099 }
1100 }
1101
1102 // for the second loop, use second value set
1103 mpEveryOther = mpEveryOther2;
1104 ulPixels = ulPixels2;
1105 // we only loop twice
1106 } // end for (ulSet = 0; ulSet < 2;
1107
1108 return (brc);
1109}
1110
1111/*
1112 *@@ winhReplaceWithCircularSlider:
1113 * this destroys the control with the ID ulID in hwndDlg
1114 * and creates a linear slider at the same position with the
1115 * same ID (effectively replacing it).
1116 *
1117 * This is needed because the IBM dialog editor (DLGEDIT.EXE)
1118 * cannot create circular sliders. So the way to do this
1119 * easily is to create some other control with DLGEDIT
1120 * where the slider should be later and call this function
1121 * on that control when the dialog is initialized.
1122 *
1123 * You need to specify the following with ulSliderStyle:
1124 * -- CSS_CIRCULARVALUE: draws a circular thumb, rather than a line,
1125 * for the value indicator.
1126 * -- CSS_MIDPOINT: makes the mid-point tick mark larger.
1127 * -- CSS_NOBUTTON: does not display value buttons. Per default, the
1128 * slider displays "-" and "+" buttons to the bottom left
1129 * and bottom right of the knob. (BTW, these bitmaps can be
1130 * changed using CSM_SETBITMAPDATA.)
1131 * -- CSS_NONUMBER: does not display the value on the dial.
1132 * -- CSS_NOTEXT: does not display title text under the dial.
1133 * Otherwise, the text in the pszTitle parameter
1134 * will be used.
1135 * -- CSS_NOTICKS (only listed in pmstddlg.h, not in PMREF):
1136 * obviously, this prevents tick marks from being drawn.
1137 * -- CSS_POINTSELECT: permits the values on the circular slider
1138 * to change immediately when dragged.
1139 * Direct manipulation is performed by using a mouse to
1140 * click on and drag the circular slider. There are two
1141 * modes of direct manipulation for the circular slider:
1142 * <BR><B>1)</B> The default direct manipulation mode is to scroll to
1143 * the value indicated by the position of the mouse.
1144 * This could be important if you used a circular slider
1145 * for a volume control, for example. Increasing the volume
1146 * from 0% to 100% too quickly could result in damage to
1147 * both the user's ears and the equipment.
1148 * <BR><B>2)</B>The other mode of direct manipulation permits
1149 * the value on the circular slider to change immediately when dragged.
1150 * This mode is enabled using the CSS_POINTSELECT style bit. When this
1151 * style is used, the value of the dial can be changed by tracking
1152 * the value with the mouse, which changes values quickly.
1153 * -- CSS_PROPORTIONALTICKS: allow the length of the tick marks to be calculated
1154 * as a percentage of the radius (for small sliders).
1155 * -- CSS_360: permits the scroll range to extend 360 degrees.
1156 * CSS_360 forces the CSS_NONUMBER style on. This is necessary
1157 * to keep the value indicator from corrupting the number value.
1158 *
1159 * FYI: The most commonly known circular slider in OS/2, the one in the
1160 * default "Sound" object, has a style of 0x9002018a, meaning
1161 * CSS_NOTEXT | CSS_POINTSELECT | CSS_NOTICKS.
1162 *
1163 * Notes: This function automatically adds WS_PARENTCLIP,
1164 * WS_TABSTOP, and WS_SYNCPAINT to the specified styles.
1165 * For the WS_TABSTOP style, hwndInsertAfter is important.
1166 * If you specify HWND_TOP, your window will be the first
1167 * in the tab stop list.
1168 *
1169 * It also shows the slider after having done all the
1170 * processing in here by calling WinShowWindow.
1171 *
1172 * This returns the HWND of the slider or NULLHANDLE upon
1173 * errors.
1174 *
1175 *@@added V0.9.0 [umoeller]
1176 */
1177
1178HWND winhReplaceWithCircularSlider(HWND hwndParent, // in: parent of old control and slider
1179 HWND hwndOwner, // in: owner of old control and slider
1180 HWND hwndInsertAfter, // in: the control after which the slider should
1181 // come up, or HWND_TOP, or HWND_BOTTOM
1182 ULONG ulID, // in: ID of old control and slider
1183 ULONG ulSliderStyle, // in: SLS_* styles
1184 SHORT sMin, // in: minimum value (e.g. 0)
1185 SHORT sMax, // in: maximum value (e.g. 100)
1186 USHORT usIncrement, // in: minimum increment (e.g. 1)
1187 USHORT usTicksEvery) // in: ticks ever x values (e.g. 20)
1188{
1189 HWND hwndSlider = NULLHANDLE;
1190 HWND hwndKill = WinWindowFromID(hwndParent, ulID);
1191 if (hwndKill)
1192 {
1193 SWP swpControl;
1194 if (WinQueryWindowPos(hwndKill, &swpControl))
1195 {
1196 // destroy the old control
1197 WinDestroyWindow(hwndKill);
1198
1199 // WinRegisterCircularSlider();
1200
1201 // create a slider with the same ID at the same
1202 // position
1203 hwndSlider = WinCreateWindow(hwndParent,
1204 WC_CIRCULARSLIDER,
1205 "dummy", // no window text
1206 ulSliderStyle
1207 // | WS_PARENTCLIP
1208 // | WS_SYNCPAINT
1209 | WS_TABSTOP,
1210 swpControl.x,
1211 swpControl.y,
1212 swpControl.cx,
1213 swpControl.cy,
1214 hwndOwner,
1215 hwndInsertAfter,
1216 ulID, // same ID as destroyed control
1217 NULL, // control data
1218 NULL); // presparams
1219
1220 if (hwndSlider)
1221 {
1222 // set slider range
1223 WinSendMsg(hwndSlider,
1224 CSM_SETRANGE,
1225 (MPARAM)(ULONG)sMin,
1226 (MPARAM)(ULONG)sMax);
1227
1228 // set slider increments
1229 WinSendMsg(hwndSlider,
1230 CSM_SETINCREMENT,
1231 (MPARAM)(ULONG)usIncrement,
1232 (MPARAM)(ULONG)usTicksEvery);
1233
1234 // set slider value
1235 WinSendMsg(hwndSlider,
1236 CSM_SETVALUE,
1237 (MPARAM)0,
1238 (MPARAM)0);
1239
1240 // for some reason, the slider always has
1241 // WS_CLIPSIBLINGS set, even though we don't
1242 // set this; we must unset this now, or
1243 // the slider won't draw itself (%&$&%"$&%!!!)
1244 WinSetWindowBits(hwndSlider,
1245 QWL_STYLE,
1246 0, // unset bit
1247 WS_CLIPSIBLINGS);
1248
1249 WinShowWindow(hwndSlider, TRUE);
1250 }
1251 }
1252 }
1253
1254 return (hwndSlider);
1255}
1256
1257/*
1258 *@@category: Helpers\PM helpers\Spin button helpers
1259 */
1260
1261/* ******************************************************************
1262 *
1263 * Spin button helpers
1264 *
1265 ********************************************************************/
1266
1267/*
1268 *@@ winhSetDlgItemSpinData:
1269 * sets a spin button's limits and data within a dialog window.
1270 * This only works for decimal spin buttons.
1271 */
1272
1273VOID winhSetDlgItemSpinData(HWND hwndDlg, // in: dlg window
1274 ULONG idSpinButton, // in: item ID of spin button
1275 ULONG min, // in: minimum allowed value
1276 ULONG max, // in: maximum allowed value
1277 ULONG current) // in: new current value
1278{
1279 HWND hwndSpinButton = WinWindowFromID(hwndDlg, idSpinButton);
1280 if (hwndSpinButton)
1281 {
1282 WinSendMsg(hwndSpinButton,
1283 SPBM_SETLIMITS, // Set limits message
1284 (MPARAM)max, // Spin Button maximum setting
1285 (MPARAM)min); // Spin Button minimum setting
1286
1287 WinSendMsg(hwndSpinButton,
1288 SPBM_SETCURRENTVALUE, // Set current value message
1289 (MPARAM)current,
1290 (MPARAM)NULL);
1291 }
1292}
1293
1294/*
1295 *@@ winhAdjustDlgItemSpinData:
1296 * this can be called on a spin button control to
1297 * have its current data snap to a grid. This only
1298 * works for LONG integer values.
1299 *
1300 * For example, if you specify 100 for the grid and call
1301 * this func after you have received SPBN_UP/DOWNARROW,
1302 * the spin button's value will always in/decrease
1303 * in steps of 100.
1304 *
1305 * This returns the "snapped" value to which the spin
1306 * button was set.
1307 *
1308 * If you specify lGrid == 0, this returns the spin
1309 * button's value only (V0.9.0).
1310 *
1311 *@@changed V0.9.0 [umoeller]: added check for lGrid == 0 (caused division by zero previously)
1312 */
1313
1314LONG winhAdjustDlgItemSpinData(HWND hwndDlg, // in: dlg window
1315 USHORT usItemID, // in: item ID of spin button
1316 LONG lGrid, // in: grid
1317 USHORT usNotifyCode) // in: SPBN_UP* or *DOWNARROW of WM_CONTROL message
1318{
1319 HWND hwndSpin = WinWindowFromID(hwndDlg, usItemID);
1320 LONG lBottom, lTop, lValue;
1321 // get value, which has already increased /
1322 // decreased by 1
1323 WinSendMsg(hwndSpin,
1324 SPBM_QUERYVALUE,
1325 (MPARAM)&lValue,
1326 MPFROM2SHORT(0, SPBQ_ALWAYSUPDATE));
1327
1328 if ((lGrid)
1329 && ( (usNotifyCode == SPBN_UPARROW)
1330 || (usNotifyCode == SPBN_DOWNARROW)
1331 )
1332 )
1333 {
1334 // only if the up/down buttons were pressed,
1335 // snap to the nearest grid; if the user
1336 // manually enters something (SPBN_CHANGE),
1337 // we'll accept that value
1338 lValue = (lValue / lGrid) * lGrid;
1339 // add /subtract grid
1340 if (usNotifyCode == SPBN_UPARROW)
1341 lValue += lGrid;
1342 // else we'll have -= lGrid already
1343
1344 // balance with spin button limits
1345 WinSendMsg(hwndSpin,
1346 SPBM_QUERYLIMITS,
1347 (MPARAM)&lTop,
1348 (MPARAM)&lBottom);
1349 if (lValue < lBottom)
1350 lValue = lTop;
1351 else if (lValue > lTop)
1352 lValue = lBottom;
1353
1354 WinSendMsg(hwndSpin,
1355 SPBM_SETCURRENTVALUE,
1356 (MPARAM)(lValue),
1357 MPNULL);
1358 }
1359 return (lValue);
1360}
1361
1362/*
1363 *@@category: Helpers\PM helpers\List box helpers
1364 */
1365
1366/* ******************************************************************
1367 *
1368 * List box helpers
1369 *
1370 ********************************************************************/
1371
1372/*
1373 *@@ winhQueryLboxItemText:
1374 * returns the text of the specified
1375 * list box item in a newly allocated
1376 * buffer.
1377 *
1378 * Returns NULL on error. Use fre()
1379 * to free the return value.
1380 *
1381 *@@added V0.9.1 (99-12-14) [umoeller]
1382 */
1383
1384PSZ winhQueryLboxItemText(HWND hwndListbox,
1385 SHORT sIndex)
1386{
1387 PSZ pszReturn = 0;
1388 SHORT cbText = SHORT1FROMMR(WinSendMsg(hwndListbox,
1389 LM_QUERYITEMTEXTLENGTH,
1390 (MPARAM)(ULONG)sIndex,
1391 0));
1392 if ((cbText) && (cbText != LIT_ERROR))
1393 {
1394 pszReturn = (PSZ)malloc(cbText + 1); // add zero terminator
1395 WinSendMsg(hwndListbox,
1396 LM_QUERYITEMTEXT,
1397 MPFROM2SHORT(sIndex,
1398 cbText + 1),
1399 (MPARAM)pszReturn);
1400 }
1401
1402 return (pszReturn);
1403}
1404
1405/*
1406 *@@ winhMoveLboxItem:
1407 * this moves one list box item from one
1408 * list box to another, including the
1409 * item text and the item "handle"
1410 * (see LM_QUERYITEMHANDLE).
1411 *
1412 * sTargetIndex can either be a regular
1413 * item index or one of the following
1414 * (as in LM_INSERTITEM):
1415 * -- LIT_END
1416 * -- LIT_SORTASCENDING
1417 * -- LIT_SORTDESCENDING
1418 *
1419 * If (fSelectTarget == TRUE), the new
1420 * item is also selected in the target
1421 * list box.
1422 *
1423 * Returns FALSE if moving failed. In
1424 * that case, the list boxes are unchanged.
1425 *
1426 *@@added V0.9.1 (99-12-14) [umoeller]
1427 */
1428
1429BOOL winhMoveLboxItem(HWND hwndSource,
1430 SHORT sSourceIndex,
1431 HWND hwndTarget,
1432 SHORT sTargetIndex,
1433 BOOL fSelectTarget)
1434{
1435 BOOL brc = FALSE;
1436
1437 PSZ pszItemText = winhQueryLboxItemText(hwndSource, sSourceIndex);
1438 if (pszItemText)
1439 {
1440 ULONG ulItemHandle = winhQueryLboxItemHandle(hwndSource,
1441 sSourceIndex);
1442 // probably 0, if not used
1443 LONG lTargetIndex = WinInsertLboxItem(hwndTarget,
1444 sTargetIndex,
1445 pszItemText);
1446 if ( (lTargetIndex != LIT_ERROR)
1447 && (lTargetIndex != LIT_MEMERROR)
1448 )
1449 {
1450 // successfully inserted:
1451 winhSetLboxItemHandle(hwndTarget, lTargetIndex, ulItemHandle);
1452 if (fSelectTarget)
1453 winhSetLboxSelectedItem(hwndTarget, lTargetIndex, TRUE);
1454
1455 // remove source
1456 WinDeleteLboxItem(hwndSource,
1457 sSourceIndex);
1458
1459 brc = TRUE;
1460 }
1461
1462 free(pszItemText);
1463 }
1464
1465 return (brc);
1466}
1467
1468/*
1469 *@@ winhLboxSelectAll:
1470 * this selects or deselects all items in the
1471 * given list box, depending on fSelect.
1472 *
1473 * Returns the number of items in the list box.
1474 */
1475
1476ULONG winhLboxSelectAll(HWND hwndListBox, // in: list box
1477 BOOL fSelect) // in: TRUE = select, FALSE = deselect
1478{
1479 LONG lItemCount = WinQueryLboxCount(hwndListBox);
1480 ULONG ul;
1481
1482 for (ul = 0; ul < lItemCount; ul++)
1483 {
1484 WinSendMsg(hwndListBox,
1485 LM_SELECTITEM,
1486 (MPARAM)ul, // index
1487 (MPARAM)fSelect);
1488 }
1489
1490 return (lItemCount);
1491}
1492
1493/*
1494 *@@category: Helpers\PM helpers\Scroll bar helpers
1495 */
1496
1497/* ******************************************************************
1498 *
1499 * Scroll bar helpers
1500 *
1501 ********************************************************************/
1502
1503/*
1504 *@@ winhUpdateScrollBar:
1505 * updates the given scroll bar according to the given
1506 * values. This updates the scroll bar's thumb size,
1507 * extension, and position, all in one shot.
1508 *
1509 * This function usually gets called when the window is
1510 * created and later when the window is resized.
1511 *
1512 * This simplifies the typical functionality of a scroll
1513 * bar in a client window which is to be scrolled. I am
1514 * wondering why IBM never included such a function, since
1515 * it is so damn basic and still writing it cost me a whole
1516 * day.
1517 *
1518 * Terminology:
1519 *
1520 * -- "window": the actual window with scroll bars which displays
1521 * a subrectangle of the available data. With a typical PM
1522 * application, this will be your client window.
1523 *
1524 * The width or height of this must be passed in ulWinPels.
1525 *
1526 * -- "viewport": the entire data to be displayed, of which the
1527 * "window" can only display a subrectangle, if the viewport
1528 * is larger than the window.
1529 *
1530 * The width or height of this must be passed in ulViewportPels.
1531 * This can be smaller than ulWinPels (if the window is larger
1532 * than the data) or the same or larger than ulWinPels
1533 * (if the window is too small to show all the data).
1534 *
1535 * -- "window offset": the offset of the current window within
1536 * the viewport.
1537 *
1538 * For horizontal scroll bars, this is the X coordinate,
1539 * counting from the left of the window (0 means leftmost).
1540 *
1541 * For vertical scroll bars, this is counted from the _top_
1542 * of the viewport (0 means topmost, as opposed to OS/2
1543 * window coordinates!). This is because for vertical scroll
1544 * bars controls, higher values move the thumb _down_. Yes
1545 * indeed, this conflicts with PM's coordinate system.
1546 *
1547 * The window offset is therefore always positive.
1548 *
1549 * The scroll bar gets disabled if the entire viewport is visible,
1550 * that is, if ulViewportPels <= ulWinPels. In that case
1551 * FALSE is returned. If (fAutoHide == TRUE), the scroll
1552 * bar is not only disabled, but also hidden from the display.
1553 * In that case, you will need to reformat your output because
1554 * your viewport becomes larger without the scroll bar.
1555 *
1556 * This function will set the range of the scroll bar to 0 up
1557 * to a value depending on the viewport size. For vertical scroll
1558 * bars, 0 means topmost (which is kinda sick with the OS/2
1559 * coordinate system), for horizontal scroll bars, 0 means leftmost.
1560 *
1561 * The maximum value of the scroll bar will be
1562 *
1563 + (ulViewportPels - ulWinPels) / usScrollUnitPels
1564 *
1565 * The thumb size of the scroll bar will also be adjusted
1566 * based on the viewport and window size, as it should be.
1567 *
1568 *@@added V0.9.1 (2000-02-14) [umoeller]
1569 *@@changed V0.9.3 (2000-04-30) [umoeller]: fixed pels/unit confusion
1570 *@@changed V0.9.3 (2000-05-08) [umoeller]: now handling scroll units automatically
1571 */
1572
1573BOOL winhUpdateScrollBar(HWND hwndScrollBar, // in: scroll bar (vertical or horizontal)
1574 ULONG ulWinPels, // in: vertical or horizontal dimension of
1575 // visible window part (in pixels),
1576 // excluding the scroll bar!
1577 ULONG ulViewportPels, // in: dimension of total data part, of
1578 // which ulWinPels is a sub-dimension
1579 // (in pixels);
1580 // if <= ulWinPels, the scrollbar will be
1581 // disabled
1582 ULONG ulCurPelsOfs, // in: current offset of visible part
1583 // (in pixels)
1584 BOOL fAutoHide) // in: hide scroll bar if disabled
1585{
1586 BOOL brc = FALSE;
1587
1588 // _Pmpf(("Entering winhUpdateScrollBar"));
1589
1590 // for large viewports, adjust scroll bar units
1591 USHORT usScrollUnitPels = 1;
1592 if (ulViewportPels > 10000)
1593 usScrollUnitPels = 100;
1594
1595 if (ulViewportPels > ulWinPels)
1596 {
1597 // scrollbar needed:
1598 USHORT usThumbDivisorUnits = usScrollUnitPels;
1599 USHORT lMaxAllowedUnitOfs;
1600 // _Pmpf(("winhUpdateScrollBar: ulViewportPels > ulWinPels, enabling scroller"));
1601 // divisor for thumb size (below)
1602 if (ulViewportPels > 10000)
1603 // for very large viewports, we need to
1604 // raise the divisor, because we only
1605 // have a USHORT
1606 usThumbDivisorUnits = usScrollUnitPels * 100;
1607
1608 // viewport is larger than window:
1609 WinEnableWindow(hwndScrollBar, TRUE);
1610 if (fAutoHide)
1611 WinShowWindow(hwndScrollBar, TRUE);
1612
1613 // calculate limit
1614 lMaxAllowedUnitOfs = ((ulViewportPels - ulWinPels + usScrollUnitPels)
1615 // scroll unit is 10
1616 / usScrollUnitPels);
1617
1618 // _Pmpf((" usCurUnitOfs: %d", ulCurUnitOfs));
1619 // _Pmpf((" usMaxUnits: %d", lMaxAllowedUnitOfs));
1620
1621 // set thumb position and limit
1622 WinSendMsg(hwndScrollBar,
1623 SBM_SETSCROLLBAR,
1624 (MPARAM)(ulCurPelsOfs), // / usThumbDivisorUnits), // position: 0 means top
1625 MPFROM2SHORT(0, // minimum
1626 lMaxAllowedUnitOfs)); // maximum
1627
1628 // set thumb size based on ulWinPels and
1629 // ulViewportPels
1630 WinSendMsg(hwndScrollBar,
1631 SBM_SETTHUMBSIZE,
1632 MPFROM2SHORT( ulWinPels / usThumbDivisorUnits, // visible
1633 ulViewportPels / usThumbDivisorUnits), // total
1634 0);
1635 brc = TRUE;
1636 }
1637 else
1638 {
1639 // _Pmpf(("winhUpdateScrollBar: ulViewportPels <= ulWinPels"));
1640 // entire viewport is visible:
1641 WinEnableWindow(hwndScrollBar, FALSE);
1642 if (fAutoHide)
1643 WinShowWindow(hwndScrollBar, FALSE);
1644 }
1645
1646 // _Pmpf(("End of winhUpdateScrollBar"));
1647
1648 return (brc);
1649}
1650
1651/*
1652 *@@ winhHandleScrollMsg:
1653 * this helper handles a WM_VSCROLL or WM_HSCROLL
1654 * message posted to a client window when the user
1655 * has worked on a client scroll bar. Calling this
1656 * function is ALL you need to do to handle those
1657 * two messages.
1658 *
1659 * This is most useful in conjunction with winhUpdateScrollBar.
1660 * See that function for the terminology also.
1661 *
1662 * This function calculates the new scrollbar position
1663 * (from the mp2 value, which can be line up/down,
1664 * page up/down, or slider track) and calls WinScrollWindow
1665 * accordingly. The window part which became invalid
1666 * because of the scrolling is automatically invalidated
1667 * (using WinInvalidateRect), so expect a WM_PAINT after
1668 * calling this function.
1669 *
1670 * This function assumes that the scrollbar operates
1671 * on values starting from zero. The maximum value
1672 * of the scroll bar is:
1673 *
1674 + ulViewportPels - (prcl2Scroll->yTop - prcl2Scroll->yBottom)
1675 *
1676 * This function also automatically changes the scroll bar
1677 * units, should you have a viewport size which doesn't fit
1678 * into the SHORT's that the scroll bar uses internally. As
1679 * a result, this function handles a the complete range of
1680 * a ULONG for the viewport.
1681 *
1682 * Replace "bottom" and "top" with "right" and "left" for
1683 * horizontal scrollbars in the above formula.
1684 *
1685 *@@added V0.9.1 (2000-02-13) [umoeller]
1686 *@@changed V0.9.3 (2000-04-30) [umoeller]: changed prototype, fixed pels/unit confusion
1687 *@@changed V0.9.3 (2000-05-08) [umoeller]: now handling scroll units automatically
1688 *@@changed V0.9.7 (2001-01-17) [umoeller]: changed PLONG to PULONG
1689 */
1690
1691BOOL winhHandleScrollMsg(HWND hwnd2Scroll, // in: client window to scroll
1692 HWND hwndScrollBar, // in: vertical or horizontal scroll bar window
1693 PULONG pulCurPelsOfs, // in/out: current viewport offset;
1694 // this is updated with the proper scroll units
1695 PRECTL prcl2Scroll, // in: hwnd2Scroll rectangle to scroll
1696 // (in window coordinates);
1697 // this is passed to WinScrollWindow,
1698 // which considers this inclusive!
1699 LONG ulViewportPels, // in: total viewport dimension,
1700 // into which *pulCurPelsOfs is an offset
1701 USHORT usLineStepPels, // in: pixels to scroll line-wise
1702 // (scroll bar buttons pressed)
1703 ULONG msg, // in: either WM_VSCROLL or WM_HSCROLL
1704 MPARAM mp2) // in: complete mp2 of WM_VSCROLL/WM_HSCROLL;
1705 // this has two SHORT's (usPos and usCmd),
1706 // see PMREF for details
1707{
1708 ULONG ulOldPelsOfs = *pulCurPelsOfs;
1709 USHORT usPosUnits = SHORT1FROMMP(mp2), // in scroll units
1710 usCmd = SHORT2FROMMP(mp2);
1711 LONG lMaxAllowedUnitOfs;
1712 ULONG ulWinPels;
1713
1714 // for large viewports, adjust scroll bar units
1715 USHORT usScrollUnitPels = 1;
1716 if (ulViewportPels > 10000)
1717 usScrollUnitPels = 100;
1718
1719 // calculate window size (vertical or horizontal)
1720 if (msg == WM_VSCROLL)
1721 ulWinPels = (prcl2Scroll->yTop - prcl2Scroll->yBottom);
1722 else
1723 ulWinPels = (prcl2Scroll->xRight - prcl2Scroll->xLeft);
1724
1725 lMaxAllowedUnitOfs = ((LONG)ulViewportPels - ulWinPels) / usScrollUnitPels;
1726
1727 // _Pmpf(("Entering winhHandleScrollMsg"));
1728
1729 switch (usCmd)
1730 {
1731 case SB_LINEUP:
1732 if (*pulCurPelsOfs > usLineStepPels)
1733 *pulCurPelsOfs -= usLineStepPels; // * usScrollUnitPels);
1734 else
1735 *pulCurPelsOfs = 0;
1736 break;
1737
1738 case SB_LINEDOWN:
1739 *pulCurPelsOfs += usLineStepPels; // * usScrollUnitPels);
1740 break;
1741
1742 case SB_PAGEUP:
1743 if (*pulCurPelsOfs > ulWinPels)
1744 *pulCurPelsOfs -= ulWinPels; // convert to units
1745 else
1746 *pulCurPelsOfs = 0;
1747 break;
1748
1749 case SB_PAGEDOWN:
1750 *pulCurPelsOfs += ulWinPels; // convert to units
1751 break;
1752
1753 case SB_SLIDERTRACK:
1754 *pulCurPelsOfs = (usPosUnits * usScrollUnitPels);
1755 // _Pmpf((" SB_SLIDERTRACK: usUnits = %d", usPosUnits));
1756 break;
1757
1758 case SB_SLIDERPOSITION:
1759 *pulCurPelsOfs = (usPosUnits * usScrollUnitPels);
1760 break;
1761 }
1762
1763 // are we close to the lower limit?
1764 /* if (*plCurUnitOfs < usLineStepUnits) // usScrollUnit)
1765 *plCurUnitOfs = 0;
1766 // are we close to the upper limit?
1767 else if (*plCurUnitOfs + usLineStepUnits > lMaxUnitOfs)
1768 {
1769 _Pmpf((" !!! limiting: %d to %d", *plCurUnitOfs, lMaxUnitOfs));
1770 *plCurUnitOfs = lMaxUnitOfs;
1771 } */
1772
1773 /* if (*plCurPelsOfs < 0)
1774 *plCurPelsOfs = 0; */ // checked above
1775 if (*pulCurPelsOfs > (lMaxAllowedUnitOfs * usScrollUnitPels))
1776 {
1777 *pulCurPelsOfs = (lMaxAllowedUnitOfs * usScrollUnitPels);
1778 }
1779 if ( (*pulCurPelsOfs != ulOldPelsOfs)
1780 || (*pulCurPelsOfs == 0)
1781 || (*pulCurPelsOfs == (lMaxAllowedUnitOfs * usScrollUnitPels))
1782 )
1783 {
1784 RECTL rcl2Scroll,
1785 rcl2Update;
1786
1787 // changed:
1788 WinSendMsg(hwndScrollBar,
1789 SBM_SETPOS,
1790 (MPARAM)(*pulCurPelsOfs / usScrollUnitPels), // / usScrollUnit),
1791 0);
1792 // scroll window rectangle:
1793 rcl2Scroll.xLeft = prcl2Scroll->xLeft;
1794 rcl2Scroll.xRight = prcl2Scroll->xRight;
1795 rcl2Scroll.yBottom = prcl2Scroll->yBottom;
1796 rcl2Scroll.yTop = prcl2Scroll->yTop;
1797
1798 if (msg == WM_VSCROLL)
1799 WinScrollWindow(hwnd2Scroll,
1800 0,
1801 (*pulCurPelsOfs - ulOldPelsOfs) // scroll units changed
1802 , // * usScrollUnitPels, // convert to pels
1803 &rcl2Scroll, // rcl to scroll
1804 prcl2Scroll, // clipping rect
1805 NULLHANDLE, // no region
1806 &rcl2Update,
1807 0);
1808 else
1809 WinScrollWindow(hwnd2Scroll,
1810 -(LONG)(*pulCurPelsOfs - ulOldPelsOfs) // scroll units changed
1811 , // * usScrollUnitPels,
1812 0,
1813 &rcl2Scroll, // rcl to scroll
1814 prcl2Scroll, // clipping rect
1815 NULLHANDLE, // no region
1816 &rcl2Update,
1817 0);
1818
1819 // WinScrollWindow has stored the invalid window
1820 // rectangle which needs to be repainted in rcl2Update:
1821 WinInvalidateRect(hwnd2Scroll, &rcl2Update, FALSE);
1822 }
1823
1824 // _Pmpf(("End of winhHandleScrollMsg"));
1825
1826 return (TRUE);
1827}
1828
1829/*
1830 *@@ winhProcessScrollChars:
1831 * helper for processing WM_CHAR messages for
1832 * client windows with scroll bars.
1833 *
1834 * If your window has scroll bars, you normally
1835 * need to process a number of keystrokes to be
1836 * able to scroll the window contents. This is
1837 * tiresome to code, so here is a helper.
1838 *
1839 * When receiving WM_CHAR, call this function.
1840 * If this returns TRUE, the keystroke has been
1841 * a scroll keystroke, and the window has been
1842 * updated (by sending WM_VSCROLL or WM_HSCROLL
1843 * to hwndClient). Otherwise, you should process
1844 * the keystroke as usual because it's not a
1845 * scroll keystroke.
1846 *
1847 * The following keystrokes are processed here:
1848 *
1849 * -- "cursor up, down, right, left": scroll one
1850 * line in the proper direction.
1851 * -- "page up, down": scroll one page up or down.
1852 * -- "Home": scroll leftmost.
1853 * -- "Ctrl+ Home": scroll topmost.
1854 * -- "End": scroll rightmost.
1855 * -- "Ctrl+ End": scroll bottommost.
1856 * -- "Ctrl + page up, down": scroll one screen left or right.
1857 *
1858 * This is CUA behavior.
1859 *
1860 * Returns TRUE if the message has been
1861 * processed.
1862 *
1863 *@@added V0.9.3 (2000-04-29) [umoeller]
1864 *@@changed V0.9.9 (2001-02-01) [lafaix]: Ctrl+PgUp/Dn now do one screen left/right
1865 */
1866
1867BOOL winhProcessScrollChars(HWND hwndClient, // in: client window
1868 HWND hwndVScroll, // in: vertical scroll bar
1869 HWND hwndHScroll, // in: horizontal scroll bar
1870 MPARAM mp1, // in: WM_CHAR mp1
1871 MPARAM mp2, // in: WM_CHAR mp2
1872 ULONG ulVertMax, // in: maximum viewport cy
1873 ULONG ulHorzMax) // in: maximum viewport cx
1874{
1875 BOOL fProcessed = FALSE;
1876 USHORT usFlags = SHORT1FROMMP(mp1);
1877 // USHORT usch = SHORT1FROMMP(mp2);
1878 USHORT usvk = SHORT2FROMMP(mp2);
1879
1880 // _Pmpf(("Entering winhProcessScrollChars"));
1881
1882 if (usFlags & KC_VIRTUALKEY)
1883 {
1884 ULONG ulMsg = 0;
1885 SHORT sPos = 0;
1886 SHORT usCmd = 0;
1887 fProcessed = TRUE;
1888
1889 switch (usvk)
1890 {
1891 case VK_UP:
1892 ulMsg = WM_VSCROLL;
1893 usCmd = SB_LINEUP;
1894 break;
1895
1896 case VK_DOWN:
1897 ulMsg = WM_VSCROLL;
1898 usCmd = SB_LINEDOWN;
1899 break;
1900
1901 case VK_RIGHT:
1902 ulMsg = WM_HSCROLL;
1903 usCmd = SB_LINERIGHT;
1904 break;
1905
1906 case VK_LEFT:
1907 ulMsg = WM_HSCROLL;
1908 usCmd = SB_LINELEFT;
1909 break;
1910
1911 case VK_PAGEUP:
1912 if (usFlags & KC_CTRL)
1913 ulMsg = WM_HSCROLL;
1914 else
1915 ulMsg = WM_VSCROLL;
1916 usCmd = SB_PAGEUP;
1917 break;
1918
1919 case VK_PAGEDOWN:
1920 if (usFlags & KC_CTRL)
1921 ulMsg = WM_HSCROLL;
1922 else
1923 ulMsg = WM_VSCROLL;
1924 usCmd = SB_PAGEDOWN;
1925 break;
1926
1927 case VK_HOME:
1928 if (usFlags & KC_CTRL)
1929 // vertical:
1930 ulMsg = WM_VSCROLL;
1931 else
1932 ulMsg = WM_HSCROLL;
1933
1934 sPos = 0;
1935 usCmd = SB_SLIDERPOSITION;
1936 break;
1937
1938 case VK_END:
1939 if (usFlags & KC_CTRL)
1940 {
1941 // vertical:
1942 ulMsg = WM_VSCROLL;
1943 sPos = ulVertMax;
1944 }
1945 else
1946 {
1947 ulMsg = WM_HSCROLL;
1948 sPos = ulHorzMax;
1949 }
1950
1951 usCmd = SB_SLIDERPOSITION;
1952 break;
1953
1954 default:
1955 // other:
1956 fProcessed = FALSE;
1957 }
1958
1959 if ( ((usFlags & KC_KEYUP) == 0)
1960 && (ulMsg)
1961 )
1962 {
1963 HWND hwndScrollBar = ((ulMsg == WM_VSCROLL)
1964 ? hwndVScroll
1965 : hwndHScroll);
1966 if (WinIsWindowEnabled(hwndScrollBar))
1967 {
1968 USHORT usID = WinQueryWindowUShort(hwndScrollBar,
1969 QWS_ID);
1970 WinSendMsg(hwndClient,
1971 ulMsg,
1972 MPFROMSHORT(usID),
1973 MPFROM2SHORT(sPos,
1974 usCmd));
1975 }
1976 }
1977 }
1978
1979 // _Pmpf(("End of winhProcessScrollChars"));
1980
1981 return (fProcessed);
1982}
1983
1984/*
1985 *@@category: Helpers\PM helpers\Window positioning
1986 */
1987
1988/* ******************************************************************
1989 *
1990 * Window positioning helpers
1991 *
1992 ********************************************************************/
1993
1994/*
1995 *@@ winhSaveWindowPos:
1996 * saves the position of a certain window. As opposed
1997 * to the barely documented WinStoreWindowPos API, this
1998 * one only saves one regular SWP structure for the given
1999 * window, as returned by WinQueryWindowPos for hwnd.
2000 *
2001 * If the window is currently maximized or minimized,
2002 * we won't store the current window size and position
2003 * (which wouldn't make much sense), but retrieve the
2004 * "restored" window position from the window words
2005 * instead.
2006 *
2007 * The window should still be visible on the screen
2008 * when calling this function. Do not call it in WM_DESTROY,
2009 * because then the SWP data is no longer valid.
2010 *
2011 * This returns TRUE if saving was successful.
2012 *
2013 *@@changed V0.9.1 (99-12-19) [umoeller]: added minimize/maximize support
2014 */
2015
2016BOOL winhSaveWindowPos(HWND hwnd, // in: window to save
2017 HINI hIni, // in: INI file (or HINI_USER/SYSTEM)
2018 const char *pcszApp, // in: INI application name
2019 const char *pcszKey) // in: INI key name
2020{
2021 BOOL brc = FALSE;
2022 SWP swp;
2023 if (WinQueryWindowPos(hwnd, &swp))
2024 {
2025 if (swp.fl & (SWP_MAXIMIZE | SWP_MINIMIZE))
2026 {
2027 // window currently maximized or minimized:
2028 // retrieve "restore" position from window words
2029 swp.x = WinQueryWindowUShort(hwnd, QWS_XRESTORE);
2030 swp.y = WinQueryWindowUShort(hwnd, QWS_YRESTORE);
2031 swp.cx = WinQueryWindowUShort(hwnd, QWS_CXRESTORE);
2032 swp.cy = WinQueryWindowUShort(hwnd, QWS_CYRESTORE);
2033 }
2034
2035 brc = PrfWriteProfileData(hIni, (PSZ)pcszApp, (PSZ)pcszKey, &swp, sizeof(swp));
2036 }
2037 return (brc);
2038}
2039
2040/*
2041 *@@ winhRestoreWindowPos:
2042 * this will retrieve a window position which was
2043 * previously stored using winhSaveWindowPos.
2044 *
2045 * The window should not be visible to avoid flickering.
2046 * "fl" must contain the SWP_flags as in WinSetWindowPos.
2047 *
2048 * Note that only the following may be used:
2049 * -- SWP_MOVE reposition the window
2050 * -- SWP_SIZE also resize the window to
2051 * the stored position; this might
2052 * lead to problems with different
2053 * video resolutions, so be careful.
2054 * -- SWP_SHOW make window visible too
2055 * -- SWP_NOREDRAW changes are not redrawn
2056 * -- SWP_NOADJUST do not send a WM_ADJUSTWINDOWPOS message
2057 * before moving or sizing
2058 * -- SWP_ACTIVATE activate window (make topmost)
2059 * -- SWP_DEACTIVATE deactivate window (make bottommost)
2060 *
2061 * Do not specify any other SWP_* flags.
2062 *
2063 * If SWP_SIZE is not set, the window will be moved only.
2064 *
2065 * This returns TRUE if INI data was found.
2066 *
2067 * This function automatically checks for whether the
2068 * window would be positioned outside the visible screen
2069 * area and will adjust coordinates accordingly. This can
2070 * happen when changing video resolutions.
2071 *
2072 *@@changed V0.9.7 (2000-12-20) [umoeller]: fixed invalid params if INI key not found
2073 */
2074
2075BOOL winhRestoreWindowPos(HWND hwnd, // in: window to restore
2076 HINI hIni, // in: INI file (or HINI_USER/SYSTEM)
2077 const char *pcszApp, // in: INI application name
2078 const char *pcszKey, // in: INI key name
2079 ULONG fl) // in: "fl" parameter for WinSetWindowPos
2080{
2081 BOOL brc = FALSE;
2082 SWP swp;
2083 ULONG cbswp = sizeof(swp);
2084 ULONG fl2 = (fl & ~SWP_ZORDER);
2085
2086 if (PrfQueryProfileData(hIni, (PSZ)pcszApp, (PSZ)pcszKey, &swp, &cbswp))
2087 {
2088 ULONG ulScreenCX = WinQuerySysValue(HWND_DESKTOP, SV_CXSCREEN);
2089 ULONG ulScreenCY = WinQuerySysValue(HWND_DESKTOP, SV_CYSCREEN);
2090
2091 brc = TRUE;
2092
2093 if ((fl & SWP_SIZE) == 0)
2094 {
2095 // if no resize, we need to get the current position
2096 SWP swpNow;
2097 brc = WinQueryWindowPos(hwnd, &swpNow);
2098 swp.cx = swpNow.cx;
2099 swp.cy = swpNow.cy;
2100 }
2101
2102 if (brc)
2103 {
2104 // check for full visibility
2105 if ( (swp.x + swp.cx) > ulScreenCX)
2106 swp.x = ulScreenCX - swp.cx;
2107 if ( (swp.y + swp.cy) > ulScreenCY)
2108 swp.y = ulScreenCY - swp.cy;
2109 }
2110
2111 brc = TRUE;
2112
2113 }
2114 else
2115 {
2116 // window pos not found in INI: unset SWP_MOVE etc.
2117 WinQueryWindowPos(hwnd, &swp);
2118 fl2 &= ~(SWP_MOVE | SWP_SIZE);
2119 }
2120
2121 WinSetWindowPos(hwnd,
2122 NULLHANDLE, // insert-behind window
2123 swp.x,
2124 swp.y,
2125 swp.cx,
2126 swp.cy,
2127 fl2); // SWP_* flags
2128
2129 return (brc);
2130}
2131
2132/*
2133 *@@ winhAdjustControls:
2134 * helper function for dynamically adjusting a window's
2135 * controls when the window is resized.
2136 *
2137 * This is most useful with dialogs loaded from resources
2138 * which should be sizeable. Normally, when the dialog
2139 * frame is resized, the controls stick to their positions,
2140 * and for dialogs with many controls, programming the
2141 * changes can be tiresome.
2142 *
2143 * Enter this function. ;-) Basically, this takes a
2144 * static array of MPARAM's as input (plus one dynamic
2145 * storage area for the window positions).
2146 *
2147 * This function must get called in three contexts:
2148 * during WM_INITDLG, during WM_WINDOWPOSCHANGED, and
2149 * during WM_DESTROY, with varying parameters.
2150 *
2151 * In detail, there are four things you need to do to make
2152 * this work:
2153 *
2154 * 1) Set up a static array (as a global variable) of
2155 * MPARAM's, one for each control in your array.
2156 * Each MPARAM will have the control's ID and the
2157 * XAF* flags (winh.h) how the control shall be moved.
2158 * Use MPFROM2SHORT to easily create this. Example:
2159 *
2160 + MPARAM ampControlFlags[] =
2161 + { MPFROM2SHORT(ID_CONTROL_1, XAC_MOVEX),
2162 + MPFROM2SHORT(ID_CONTROL_2, XAC_SIZEY),
2163 + ...
2164 + }
2165 *
2166 * This can safely be declared as a global variable
2167 * because this data will only be read and never
2168 * changed by this function.
2169 *
2170 * 2) In WM_INITDLG of your dialog function, set up
2171 * an XADJUSTCTRLS structure, preferrably in your
2172 * window words (QWL_USER).
2173 *
2174 * ZERO THAT STRUCTURE (memset(&xac, 0, sizeof(XADJUSTCTRLS),
2175 * or this func will not work.
2176 *
2177 * Call this function with pswpNew == NULL and
2178 * pxac pointing to that new structure. This will
2179 * query the positions of all the controls listed
2180 * in the MPARAMs array and store them in the
2181 * XADJUSTCTRLS area.
2182 *
2183 * 3) Intercept WM_WINDOWPOSCHANGED:
2184 *
2185 + case WM_WINDOWPOSCHANGED:
2186 + {
2187 + // this msg is passed two SWP structs:
2188 + // one for the old, one for the new data
2189 + // (from PM docs)
2190 + PSWP pswpNew = PVOIDFROMMP(mp1);
2191 + PSWP pswpOld = pswpNew + 1;
2192 +
2193 + // resizing?
2194 + if (pswpNew->fl & SWP_SIZE)
2195 + {
2196 + PXADJUSTCTRLS pxac = ... // get it from your window words
2197 +
2198 + winhAdjustControls(hwndDlg, // dialog
2199 + ampControlFlags, // MPARAMs array
2200 + sizeof(ampControlFlags) / sizeof(MPARAM),
2201 + // items count
2202 + pswpNew, // mp1
2203 + pxac); // storage area
2204 + }
2205 + mrc = WinDefDlgProc(hwnd, msg, mp1, mp2); ...
2206 *
2207 * 4) In WM_DESTROY, call this function again with pmpFlags,
2208 * pswpNew, and pswpNew set to NULL. This will clean up the
2209 * data which has been allocated internally (pointed to from
2210 * the XADJUSTCTRLS structure).
2211 * Don't forget to free your storage for XADJUSTCTLRS
2212 * _itself_, that's the job of the caller.
2213 *
2214 * This might sound complicated, but it's a lot easier than
2215 * having to write dozens of WinSetWindowPos calls oneself.
2216 *
2217 *@@added V0.9.0 [umoeller]
2218 */
2219
2220BOOL winhAdjustControls(HWND hwndDlg, // in: dialog (req.)
2221 MPARAM *pmpFlags, // in: init flags or NULL for cleanup
2222 ULONG ulCount, // in: item count (req.)
2223 PSWP pswpNew, // in: pswpNew from WM_WINDOWPOSCHANGED or NULL for cleanup
2224 PXADJUSTCTRLS pxac) // in: adjust-controls storage area (req.)
2225{
2226 BOOL brc = FALSE;
2227 ULONG ul = 0;
2228
2229 /* if (!WinIsWindowVisible(hwndDlg))
2230 return (FALSE); */
2231
2232 if ((pmpFlags) && (pxac))
2233 {
2234 PSWP pswpThis;
2235 MPARAM *pmpThis;
2236 LONG ldcx, ldcy;
2237 ULONG cWindows = 0;
2238
2239 // setup mode:
2240 if (pxac->fInitialized == FALSE)
2241 {
2242 // first call: get all the SWP's
2243 WinQueryWindowPos(hwndDlg, &pxac->swpMain);
2244 // _Pmpf(("winhAdjustControls: queried main cx = %d, cy = %d",
2245 // pxac->swpMain.cx, pxac->swpMain.cy));
2246
2247 pxac->paswp = (PSWP)malloc(sizeof(SWP) * ulCount);
2248
2249 pswpThis = pxac->paswp;
2250 pmpThis = pmpFlags;
2251
2252 for (ul = 0;
2253 ul < ulCount;
2254 ul++)
2255 {
2256 HWND hwndThis = WinWindowFromID(hwndDlg, SHORT1FROMMP(*pmpThis));
2257 if (hwndThis)
2258 {
2259 WinQueryWindowPos(hwndThis, pswpThis);
2260 cWindows++;
2261 }
2262
2263 pswpThis++;
2264 pmpThis++;
2265 }
2266
2267 pxac->fInitialized = TRUE;
2268 // _Pmpf(("winhAdjustControls: queried %d controls", cWindows));
2269 }
2270
2271 if (pswpNew)
2272 {
2273 // compute width and height delta
2274 ldcx = (pswpNew->cx - pxac->swpMain.cx);
2275 ldcy = (pswpNew->cy - pxac->swpMain.cy);
2276
2277 // _Pmpf(("winhAdjustControls: new cx = %d, cy = %d",
2278 // pswpNew->cx, pswpNew->cy));
2279
2280 // now adjust the controls
2281 cWindows = 0;
2282 pswpThis = pxac->paswp;
2283 pmpThis = pmpFlags;
2284 for (ul = 0;
2285 ul < ulCount;
2286 ul++)
2287 {
2288 HWND hwndThis = WinWindowFromID(hwndDlg, SHORT1FROMMP(*pmpThis));
2289 if (hwndThis)
2290 {
2291 LONG x = pswpThis->x,
2292 y = pswpThis->y,
2293 cx = pswpThis->cx,
2294 cy = pswpThis->cy;
2295
2296 ULONG ulSwpFlags = 0;
2297 // get flags for this control
2298 USHORT usFlags = SHORT2FROMMP(*pmpThis);
2299
2300 if (usFlags & XAC_MOVEX)
2301 {
2302 x += ldcx;
2303 ulSwpFlags |= SWP_MOVE;
2304 }
2305 if (usFlags & XAC_MOVEY)
2306 {
2307 y += ldcy;
2308 ulSwpFlags |= SWP_MOVE;
2309 }
2310 if (usFlags & XAC_SIZEX)
2311 {
2312 cx += ldcx;
2313 ulSwpFlags |= SWP_SIZE;
2314 }
2315 if (usFlags & XAC_SIZEY)
2316 {
2317 cy += ldcy;
2318 ulSwpFlags |= SWP_SIZE;
2319 }
2320
2321 if (ulSwpFlags)
2322 {
2323 WinSetWindowPos(hwndThis,
2324 NULLHANDLE, // hwndInsertBehind
2325 x, y, cx, cy,
2326 ulSwpFlags);
2327 cWindows++;
2328 brc = TRUE;
2329 }
2330 }
2331
2332 pswpThis++;
2333 pmpThis++;
2334 }
2335
2336 // _Pmpf(("winhAdjustControls: set %d windows", cWindows));
2337 }
2338 }
2339 else
2340 {
2341 // pxac == NULL:
2342 // cleanup mode
2343 if (pxac->paswp)
2344 free(pxac->paswp);
2345 }
2346
2347 return (brc);
2348}
2349
2350/*
2351 *@@ winhCenterWindow:
2352 * centers a window within its parent window. If that's
2353 * the PM desktop, it will be centered according to the
2354 * whole screen.
2355 * For dialog boxes, use WinCenteredDlgBox as a one-shot
2356 * function.
2357 *
2358 * Note: When calling this function, the window should
2359 * not be visible to avoid flickering.
2360 * This func does not show the window either, so call
2361 * WinShowWindow afterwards.
2362 */
2363
2364void winhCenterWindow(HWND hwnd)
2365{
2366 RECTL rclParent;
2367 RECTL rclWindow;
2368
2369 WinQueryWindowRect(hwnd, &rclWindow);
2370 WinQueryWindowRect(WinQueryWindow(hwnd, QW_PARENT), &rclParent);
2371
2372 rclWindow.xLeft = (rclParent.xRight - rclWindow.xRight) / 2;
2373 rclWindow.yBottom = (rclParent.yTop - rclWindow.yTop ) / 2;
2374
2375 WinSetWindowPos(hwnd, NULLHANDLE, rclWindow.xLeft, rclWindow.yBottom,
2376 0, 0, SWP_MOVE);
2377}
2378
2379/*
2380 *@@ winhCenteredDlgBox:
2381 * just like WinDlgBox, but the dlg box is centered on the screen;
2382 * you should mark the dlg template as not visible in the dlg
2383 * editor, or display will flicker.
2384 * As opposed to winhCenterWindow, this _does_ show the window.
2385 */
2386
2387ULONG winhCenteredDlgBox(HWND hwndParent,
2388 HWND hwndOwner,
2389 PFNWP pfnDlgProc,
2390 HMODULE hmod,
2391 ULONG idDlg,
2392 PVOID pCreateParams)
2393{
2394 ULONG ulReply;
2395 HWND hwndDlg = WinLoadDlg(hwndParent,
2396 hwndOwner,
2397 pfnDlgProc,
2398 hmod,
2399 idDlg,
2400 pCreateParams);
2401 winhCenterWindow(hwndDlg);
2402 ulReply = WinProcessDlg(hwndDlg);
2403 WinDestroyWindow(hwndDlg);
2404 return (ulReply);
2405}
2406
2407/*
2408 *@@ winhFindWindowBelow:
2409 * finds the window with the same parent
2410 * which sits right below hwndFind in the
2411 * window Z-order.
2412 *
2413 *@@added V0.9.7 (2000-12-04) [umoeller]
2414 */
2415
2416HWND winhFindWindowBelow(HWND hwndFind)
2417{
2418 HWND hwnd = NULLHANDLE,
2419 hwndParent = WinQueryWindow(hwndFind, QW_PARENT);
2420
2421 if (hwndParent)
2422 {
2423 HENUM henum = WinBeginEnumWindows(hwndParent);
2424 HWND hwndThis;
2425 while (hwndThis = WinGetNextWindow(henum))
2426 {
2427 SWP swp;
2428 WinQueryWindowPos(hwndThis, &swp);
2429 if (swp.hwndInsertBehind == hwndFind)
2430 {
2431 hwnd = hwndThis;
2432 break;
2433 }
2434 }
2435 WinEndEnumWindows(henum);
2436 }
2437
2438 return (hwnd);
2439}
2440
2441/*
2442 *@@category: Helpers\PM helpers\Presentation parameters
2443 */
2444
2445/* ******************************************************************
2446 *
2447 * Presparams helpers
2448 *
2449 ********************************************************************/
2450
2451/*
2452 *@@ winhQueryWindowFont:
2453 * returns the window font presentation parameter
2454 * in a newly allocated buffer.
2455 *
2456 * Returns NULL on error. Use free()
2457 * to free the return value.
2458 *
2459 *@@added V0.9.1 (2000-02-14) [umoeller]
2460 */
2461
2462PSZ winhQueryWindowFont(HWND hwnd)
2463{
2464 CHAR szNewFont[100] = "";
2465 WinQueryPresParam(hwnd,
2466 PP_FONTNAMESIZE,
2467 0,
2468 NULL,
2469 (ULONG)sizeof(szNewFont),
2470 (PVOID)&szNewFont,
2471 QPF_NOINHERIT);
2472 if (szNewFont[0] != 0)
2473 return (strdup(szNewFont));
2474
2475 return (NULL);
2476}
2477
2478/*
2479 *@@ winhSetWindowFont:
2480 * this sets a window's font by invoking
2481 * WinSetPresParam on it.
2482 *
2483 * If (pszFont == NULL), a default font will be set
2484 * (on Warp 4, "9.WarpSans", on Warp 3, "8.Helv").
2485 *
2486 * winh.h also defines the winhSetDlgItemFont macro.
2487 *
2488 * Returns TRUE if successful or FALSE otherwise.
2489 *
2490 *@@added V0.9.0 [umoeller]
2491 */
2492
2493BOOL winhSetWindowFont(HWND hwnd,
2494 const char *pcszFont)
2495{
2496 CHAR szFont[256];
2497
2498 if (pcszFont == NULL)
2499 {
2500 if (doshIsWarp4())
2501 strhncpy0(szFont, "9.WarpSans", sizeof(szFont));
2502 else
2503 strhncpy0(szFont, "8.Helv", sizeof(szFont));
2504 }
2505 else
2506 strhncpy0(szFont, pcszFont, sizeof(szFont));
2507
2508 return (WinSetPresParam(hwnd,
2509 PP_FONTNAMESIZE,
2510 strlen(szFont)+1,
2511 szFont));
2512}
2513
2514/*
2515 *@@ winhSetControlsFont:
2516 * this sets the font for all the controls of hwndDlg
2517 * which have a control ID in the range of usIDMin to
2518 * usIDMax. "Unused" IDs (i.e. -1) will also be set.
2519 *
2520 * If (pszFont == NULL), a default font will be set
2521 * (on Warp 4, "9.WarpSans", on Warp 3, "8.Helv").
2522 *
2523 * Returns the no. of controls set.
2524 *
2525 *@@added V0.9.0 [umoeller]
2526 */
2527
2528ULONG winhSetControlsFont(HWND hwndDlg, // in: dlg to set
2529 SHORT usIDMin, // in: minimum control ID to be set (inclusive)
2530 SHORT usIDMax, // in: maximum control ID to be set (inclusive)
2531 const char *pcszFont) // in: font to use (e.g. "9.WarpSans") or NULL
2532{
2533 ULONG ulrc = 0;
2534 HENUM henum;
2535 HWND hwndItem;
2536 CHAR szFont[256];
2537 ULONG cbFont;
2538
2539 if (pcszFont == NULL)
2540 {
2541 if (doshIsWarp4())
2542 strhncpy0(szFont, "9.WarpSans", sizeof(szFont));
2543 else
2544 strhncpy0(szFont, "8.Helv", sizeof(szFont));
2545 }
2546 else
2547 strhncpy0(szFont, pcszFont, sizeof(szFont));
2548 cbFont = strlen(szFont)+1;
2549
2550 // set font for all the dialog controls
2551 henum = WinBeginEnumWindows(hwndDlg);
2552 while ((hwndItem = WinGetNextWindow(henum)))
2553 {
2554 SHORT sID = WinQueryWindowUShort(hwndItem, QWS_ID);
2555 if ( (sID == -1)
2556 || ((sID >= usIDMin) && (sID <= usIDMax))
2557 )
2558 if (WinSetPresParam(hwndItem,
2559 PP_FONTNAMESIZE,
2560 cbFont,
2561 szFont))
2562 // successful:
2563 ulrc++;
2564 }
2565 WinEndEnumWindows(henum);
2566 return (ulrc);
2567}
2568
2569/*
2570 *@@ winhStorePresParam:
2571 * this appends a new presentation parameter to an
2572 * array of presentation parameters which can be
2573 * passed to WinCreateWindow. This is preferred
2574 * over setting the presparams using WinSetPresParams,
2575 * because that call will cause a lot of messages.
2576 *
2577 * On the first call, pppp _must_ be NULL. This
2578 * will allocate memory for storing the given
2579 * data as necessary and modify *pppp to point
2580 * to the new array.
2581 *
2582 * On subsequent calls with the same pppp, memory
2583 * will be reallocated, the old data will be copied,
2584 * and the new given data will be appended.
2585 *
2586 * Use free() on your PPRESPARAMS pointer (whose
2587 * address was passed) after WinCreateWindow.
2588 *
2589 * See winhQueryPresColor for typical presparams
2590 * used in OS/2.
2591 *
2592 * Example:
2593 *
2594 + PPRESPARAMS ppp = NULL;
2595 + CHAR szFont[] = "9.WarpSans";
2596 + LONG lColor = CLR_WHITE;
2597 + winhStorePresParam(&ppp, PP_FONTNAMESIZE, sizeof(szFont), szFont);
2598 + winhStorePresParam(&ppp, PP_BACKGROUNDCOLOR, sizeof(lColor), &lColor);
2599 + WinCreateWindow(...., ppp);
2600 + free(ppp);
2601 *
2602 *@@added V0.9.0 [umoeller]
2603 */
2604
2605BOOL winhStorePresParam(PPRESPARAMS *pppp, // in: data pointer (modified)
2606 ULONG ulAttrType, // in: PP_* index
2607 ULONG cbData, // in: sizeof(*pData), e.g. sizeof(LONG)
2608 PVOID pData) // in: presparam data (e.g. a PLONG to a color)
2609{
2610 BOOL brc = FALSE;
2611 if (pppp)
2612 {
2613 ULONG cbOld = 0,
2614 cbNew;
2615 PBYTE pbTemp = 0;
2616 PPRESPARAMS pppTemp = 0;
2617 PPARAM pppCopyTo = 0;
2618
2619 if (*pppp != NULL)
2620 // subsequent calls:
2621 cbOld = (**pppp).cb;
2622
2623 cbNew = sizeof(ULONG) // PRESPARAMS.cb
2624 + cbOld // old count, which does not include PRESPARAMS.cb
2625 + sizeof(ULONG) // PRESPARAMS.aparam[0].id
2626 + sizeof(ULONG) // PRESPARAMS.aparam[0].cb
2627 + cbData; // PRESPARAMS.aparam[0].ab[]
2628
2629 pbTemp = (PBYTE)malloc(cbNew);
2630 if (pbTemp)
2631 {
2632 pppTemp = (PPRESPARAMS)pbTemp;
2633
2634 if (*pppp != NULL)
2635 {
2636 // copy old data
2637 memcpy(pbTemp, *pppp, cbOld + sizeof(ULONG)); // including PRESPARAMS.cb
2638 pppCopyTo = (PPARAM)(pbTemp // new buffer
2639 + sizeof(ULONG) // skipping PRESPARAMS.cb
2640 + cbOld); // old PARAM array
2641 }
2642 else
2643 // first call:
2644 pppCopyTo = pppTemp->aparam;
2645
2646 pppTemp->cb = cbNew - sizeof(ULONG); // excluding PRESPARAMS.cb
2647 pppCopyTo->id = ulAttrType;
2648 pppCopyTo->cb = cbData; // byte count of PARAM.ab[]
2649 memcpy(pppCopyTo->ab, pData, cbData);
2650
2651 free(*pppp);
2652 *pppp = pppTemp;
2653
2654 brc = TRUE;
2655 }
2656 }
2657 return (brc);
2658}
2659
2660/*
2661 *@@ winhQueryPresColor:
2662 * returns the specified color. This is queried in the
2663 * following order:
2664 *
2665 * 1) hwnd's pres params are searched for ulPP
2666 * (which should be a PP_* index);
2667 * 2) if (fInherit == TRUE), the parent windows
2668 * are searched also;
2669 * 3) if this fails or (fInherit == FALSE), WinQuerySysColor
2670 * is called to get lSysColor (which should be a SYSCLR_*
2671 * index), if lSysColor != -1;
2672 * 4) if (lSysColor == -1), -1 is returned.
2673 *
2674 * The return value is always an RGB LONG, _not_ a color index.
2675 * This is even true for the returned system colors, which are
2676 * converted to RGB.
2677 *
2678 * If you do any painting with this value, you should switch
2679 * the HPS you're using to RGB mode (use gpihSwitchToRGB for that).
2680 *
2681 * Some useful ulPP / lSysColor pairs
2682 * (default values as in PMREF):
2683 *
2684 + -- PP_FOREGROUNDCOLOR SYSCLR_WINDOWTEXT (for most controls also)
2685 + SYSCLR_WINDOWSTATICTEXT (for static controls)
2686 + Foreground color (default: black)
2687 + -- PP_BACKGROUNDCOLOR SYSCLR_BACKGROUND
2688 + SYSCLR_DIALOGBACKGROUND
2689 + SYSCLR_FIELDBACKGROUND (for disabled scrollbars)
2690 + SYSCLR_WINDOW (application surface -- empty clients)
2691 + Background color (default: light gray)
2692 + -- PP_ACTIVETEXTFGNDCOLOR
2693 + -- PP_HILITEFOREGROUNDCOLOR SYSCLR_HILITEFOREGROUND
2694 + Highlighted foreground color, for example for selected menu
2695 + (def.: white)
2696 + -- PP_ACTIVETEXTBGNDCOLOR
2697 + -- PP_HILITEBACKGROUNDCOLOR SYSCLR_HILITEBACKGROUND
2698 + Highlighted background color (def.: dark gray)
2699 + -- PP_INACTIVETEXTFGNDCOLOR
2700 + -- PP_DISABLEDFOREGROUNDCOLOR SYSCLR_MENUDISABLEDTEXT
2701 + Disabled foreground color (dark gray)
2702 + -- PP_INACTIVETEXTBGNDCOLOR
2703 + -- PP_DISABLEDBACKGROUNDCOLOR
2704 + Disabled background color
2705 + -- PP_BORDERCOLOR SYSCLR_WINDOWFRAME
2706 + SYSCLR_INACTIVEBORDER
2707 + Border color (around pushbuttons, in addition to
2708 + the 3D colors)
2709 + -- PP_ACTIVECOLOR SYSCLR_ACTIVETITLE
2710 + Active color
2711 + -- PP_INACTIVECOLOR SYSCLR_INACTIVETITLE
2712 + Inactive color
2713 *
2714 * For menus:
2715 + -- PP_MENUBACKGROUNDCOLOR SYSCLR_MENU
2716 + -- PP_MENUFOREGROUNDCOLOR SYSCLR_MENUTEXT
2717 + -- PP_MENUHILITEBGNDCOLOR SYSCLR_MENUHILITEBGND
2718 + -- PP_MENUHILITEFGNDCOLOR SYSCLR_MENUHILITE
2719 + -- ?? SYSCLR_MENUDISABLEDTEXT
2720 +
2721 * For containers (according to the API ref. at EDM/2):
2722 + -- PP_FOREGROUNDCOLOR SYSCLR_WINDOWTEXT
2723 + -- PP_BACKGROUNDCOLOR SYSCLR_WINDOW
2724 + -- PP_HILITEFOREGROUNDCOLOR SYSCLR_HILITEFOREGROUND
2725 + -- PP_HILITEBACKGROUNDCOLOR SYSCLR_HILITEBACKGROUND
2726 + -- PP_BORDERCOLOR
2727 + (used for separator lines, eg. in Details view)
2728 + -- PP_ICONTEXTBACKGROUNDCOLOR
2729 + (column titles in Details view?!?)
2730 +
2731 * For listboxes / entryfields / MLE's:
2732 + -- PP_BACKGROUNDCOLOR SYSCLR_ENTRYFIELD
2733 *
2734 * PMREF has more of these.
2735 *
2736 *@@changed V0.9.0 [umoeller]: removed INI key query, using SYSCLR_* instead; function prototype changed
2737 *@@changed V0.9.0 [umoeller]: added fInherit parameter
2738 *@@changed V0.9.7 (2000-12-02) [umoeller]: added lSysColor == -1 support
2739 */
2740
2741LONG winhQueryPresColor(HWND hwnd, // in: window to query
2742 ULONG ulPP, // in: PP_* index
2743 BOOL fInherit, // in: search parent windows too?
2744 LONG lSysColor) // in: SYSCLR_* index
2745{
2746 ULONG ul,
2747 attrFound,
2748 abValue[32];
2749
2750 if (ulPP != (ULONG)-1)
2751 if ((ul = WinQueryPresParam(hwnd,
2752 ulPP,
2753 0,
2754 &attrFound,
2755 (ULONG)sizeof(abValue),
2756 (PVOID)&abValue,
2757 (fInherit)
2758 ? 0
2759 : QPF_NOINHERIT)))
2760 return (abValue[0]);
2761
2762 // not found: get system color
2763 if (lSysColor != -1)
2764 return (WinQuerySysColor(HWND_DESKTOP, lSysColor, 0));
2765
2766 return -1;
2767}
2768
2769/*
2770 *@@category: Helpers\PM helpers\Help (IPF)
2771 */
2772
2773/* ******************************************************************
2774 *
2775 * Help instance helpers
2776 *
2777 ********************************************************************/
2778
2779/*
2780 *@@ winhCreateHelp:
2781 * creates a help instance and connects it with the
2782 * given frame window.
2783 *
2784 * If (pszFileName == NULL), we'll retrieve the
2785 * executable's fully qualified file name and
2786 * replace the extension with .HLP simply. This
2787 * avoids the typical "Help not found" errors if
2788 * the program isn't started in its own directory.
2789 *
2790 * If you have created a help table in memory, specify it
2791 * with pHelpTable. To load a help table from the resources,
2792 * specify hmod (or NULLHANDLE) and set pHelpTable to the
2793 * following:
2794 +
2795 + (PHELPTABLE)MAKELONG(usTableID, 0xffff)
2796 *
2797 * Returns the help window handle or NULLHANDLE on errors.
2798 *
2799 * Based on an EDM/2 code snippet.
2800 *
2801 *@@added V0.9.4 (2000-07-03) [umoeller]
2802 */
2803
2804HWND winhCreateHelp(HWND hwndFrame, // in: app's frame window handle; can be NULLHANDLE
2805 const char *pcszFileName, // in: help file name or NULL
2806 HMODULE hmod, // in: module with help table or NULLHANDLE (current)
2807 PHELPTABLE pHelpTable, // in: help table or resource ID
2808 const char *pcszWindowTitle) // in: help window title or NULL
2809{
2810 HELPINIT hi;
2811 PSZ pszExt;
2812 CHAR szName[CCHMAXPATH];
2813 HWND hwndHelp;
2814
2815 if (pcszFileName == NULL)
2816 {
2817 PPIB ppib;
2818 PTIB ptib;
2819 DosGetInfoBlocks(&ptib, &ppib);
2820 DosQueryModuleName(ppib->pib_hmte, sizeof(szName), szName);
2821
2822 pszExt = strrchr(szName, '.');
2823 if (pszExt)
2824 strcpy(pszExt, ".hlp");
2825 else
2826 strcat(szName, ".hlp");
2827
2828 pcszFileName = szName;
2829 }
2830
2831 hi.cb = sizeof(HELPINIT);
2832 hi.ulReturnCode = 0;
2833 hi.pszTutorialName = NULL;
2834 hi.phtHelpTable = pHelpTable;
2835 hi.hmodHelpTableModule = hmod;
2836 hi.hmodAccelActionBarModule = NULLHANDLE;
2837 hi.idAccelTable = 0;
2838 hi.idActionBar = 0;
2839 hi.pszHelpWindowTitle = (PSZ)pcszWindowTitle;
2840 hi.fShowPanelId = CMIC_HIDE_PANEL_ID;
2841 hi.pszHelpLibraryName = (PSZ)pcszFileName;
2842
2843 hwndHelp = WinCreateHelpInstance(WinQueryAnchorBlock(hwndFrame),
2844 &hi);
2845 if ((hwndFrame) && (hwndHelp))
2846 {
2847 WinAssociateHelpInstance(hwndHelp, hwndFrame);
2848 }
2849
2850 return (hwndHelp);
2851}
2852
2853/*
2854 *@@ winhDisplayHelpPanel:
2855 * displays the specified help panel ID.
2856 *
2857 * If (ulHelpPanel == 0), this displays the
2858 * standard OS/2 "Using help" panel.
2859 *
2860 * Returns zero on success or one of the
2861 * help manager error codes on failure.
2862 * See HM_ERROR for those.
2863 *
2864 *@@added V0.9.7 (2001-01-21) [umoeller]
2865 */
2866
2867ULONG winhDisplayHelpPanel(HWND hwndHelpInstance, // in: from winhCreateHelp
2868 ULONG ulHelpPanel) // in: help panel ID
2869{
2870 return (ULONG)(WinSendMsg(hwndHelpInstance,
2871 HM_DISPLAY_HELP,
2872 (MPARAM)ulHelpPanel,
2873 (MPARAM)( (ulHelpPanel != 0)
2874 ? HM_RESOURCEID
2875 : 0)));
2876}
2877
2878/*
2879 *@@ winhDestroyHelp:
2880 * destroys the help instance created by winhCreateHelp.
2881 *
2882 * Based on an EDM/2 code snippet.
2883 *
2884 *@@added V0.9.4 (2000-07-03) [umoeller]
2885 */
2886
2887void winhDestroyHelp(HWND hwndHelp,
2888 HWND hwndFrame) // can be NULLHANDLE if not used with winhCreateHelp
2889{
2890 if (hwndHelp)
2891 {
2892 if (hwndFrame)
2893 WinAssociateHelpInstance(NULLHANDLE, hwndFrame);
2894 WinDestroyHelpInstance(hwndHelp);
2895 }
2896}
2897
2898/*
2899 *@@category: Helpers\PM helpers\Application control
2900 */
2901
2902/* ******************************************************************
2903 *
2904 * Application control
2905 *
2906 ********************************************************************/
2907
2908/*
2909 *@@ CallBatchCorrectly:
2910 * fixes the specified PROGDETAILS for
2911 * command files in the executable part
2912 * by inserting /C XXX into the parameters
2913 * and setting the executable according
2914 * to an environment variable.
2915 *
2916 *@@added V0.9.6 (2000-10-16) [umoeller]
2917 *@@changed V0.9.7 (2001-01-15) [umoeller]: now using XSTRING
2918 */
2919
2920VOID CallBatchCorrectly(PPROGDETAILS pProgDetails,
2921 PXSTRING pstrParams, // in/out: modified parameters (reallocated)
2922 const char *pcszEnvVar, // in: env var spec'g command proc
2923 // (e.g. "OS2_SHELL"); can be NULL
2924 const char *pcszDefProc) // in: def't command proc (e.g. "CMD.EXE")
2925{
2926 // XXX.CMD file as executable:
2927 // fix args to /C XXX.CMD
2928
2929 PSZ pszOldParams = NULL;
2930 ULONG ulOldParamsLength = pstrParams->ulLength;
2931 if (ulOldParamsLength)
2932 // we have parameters already:
2933 // make a backup... we'll append that later
2934 pszOldParams = strdup(pstrParams->psz);
2935
2936 // set new params to "/C filename.cmd"
2937 xstrcpy(pstrParams, "/C ", 0);
2938 xstrcat(pstrParams,
2939 pProgDetails->pszExecutable,
2940 0);
2941
2942 if (pszOldParams)
2943 {
2944 // .cmd had params:
2945 // append space and old params
2946 xstrcatc(pstrParams, ' ');
2947 xstrcat(pstrParams,
2948 pszOldParams,
2949 ulOldParamsLength);
2950 free(pszOldParams);
2951 }
2952
2953 // set executable to $(OS2_SHELL)
2954 pProgDetails->pszExecutable = NULL;
2955 if (pcszEnvVar)
2956 pProgDetails->pszExecutable = getenv(pcszEnvVar);
2957 if (!pProgDetails->pszExecutable)
2958 pProgDetails->pszExecutable = (PSZ)pcszDefProc;
2959 // should be on PATH
2960}
2961
2962/*
2963 *@@ winhQueryAppType:
2964 * returns the Control Program (Dos) and
2965 * Win* PROG_* application types for the
2966 * specified executable. Essentially, this
2967 * is a wrapper around DosQueryAppType.
2968 *
2969 * pcszExecutable must be fully qualified.
2970 * You can use doshFindExecutable to qualify
2971 * it.
2972 *
2973 * This returns the APIRET of DosQueryAppType.
2974 * If this is NO_ERROR; *pulDosAppType receives
2975 * the app type of DosQueryAppType. In addition,
2976 * *pulWinAppType is set to one of the following:
2977 *
2978 * -- PROG_FULLSCREEN
2979 *
2980 * -- PROG_PDD
2981 *
2982 * -- PROG_VDD
2983 *
2984 * -- PROG_XWP_DLL: new apptype defined in winh.h for
2985 * dynamic link libraries.
2986 *
2987 * -- PROG_WINDOWEDVDM
2988 *
2989 * -- PROG_PM
2990 *
2991 * -- PROG_31_ENH
2992 *
2993 * -- PROG_WINDOWABLEVIO
2994 *
2995 *@@added V0.9.9 (2001-03-07) [umoeller]
2996 */
2997
2998APIRET winhQueryAppType(const char *pcszExecutable,
2999 PULONG pulDosAppType,
3000 PULONG pulWinAppType)
3001{
3002 APIRET arc = DosQueryAppType((PSZ)pcszExecutable, pulDosAppType);
3003 if (arc == NO_ERROR)
3004 {
3005 ULONG _ulDosAppType = *pulDosAppType;
3006
3007 if (_ulDosAppType == 0)
3008 *pulWinAppType = PROG_FULLSCREEN;
3009 else if (_ulDosAppType & 0x40)
3010 *pulWinAppType = PROG_PDD;
3011 else if (_ulDosAppType & 0x80)
3012 *pulWinAppType = PROG_VDD;
3013 else if ((_ulDosAppType & 0xF0) == 0x10)
3014 // DLL bit set
3015 *pulWinAppType = PROG_XWP_DLL;
3016 else if (_ulDosAppType & 0x20)
3017 // DOS bit set?
3018 *pulWinAppType = PROG_WINDOWEDVDM;
3019 else if ((_ulDosAppType & 0x0003) == 0x0003) // "Window-API" == PM
3020 *pulWinAppType = PROG_PM;
3021 else if ( ((_ulDosAppType & 0xFFFF) == 0x1000) // windows program (?!?)
3022 || ((_ulDosAppType & 0xFFFF) == 0x0400) // windows program (?!?)
3023 )
3024 *pulWinAppType = PROG_31_ENH;
3025 else if ((_ulDosAppType & 0x03) == 0x02)
3026 *pulWinAppType = PROG_WINDOWABLEVIO;
3027 else if ((_ulDosAppType & 0x03) == 0x01)
3028 *pulWinAppType = PROG_FULLSCREEN;
3029 }
3030
3031 return (arc);
3032}
3033
3034/*
3035 *@@ winhStartApp:
3036 * wrapper around WinStartApp which fixes the
3037 * specified PROGDETAILS to (hopefully) work
3038 * work with all executable types.
3039 *
3040 * This fixes the executable info to support:
3041 *
3042 * -- starting "*" executables (command prompts
3043 * for OS/2, DOS, Win-OS/2);
3044 *
3045 * -- starting ".CMD" and ".BAT" files as
3046 * PROGDETAILS.pszExecutable.
3047 *
3048 * Unless it is "*", PROGDETAILS.pszExecutable must
3049 * be a proper file name. The full path may be omitted
3050 * if it is on the PATH, but the extension (.EXE etc.)
3051 * must be given. You can use doshFindExecutable to
3052 * find executables if you don't know the extension.
3053 *
3054 * This also handles and merges special and default
3055 * environments for the app to be started.
3056 * If PROGDETAILS.pszEnvironment is empty
3057 * and the application is a Win-OS/2 app,
3058 * this uses the default Win-OS/2 settings
3059 * as specified in the "Win-OS/2" WPS settings
3060 * object.
3061 *
3062 * Even though this isn't clearly said in PMREF,
3063 * PROGDETAILS.swpInitial is important:
3064 *
3065 * -- To start a session minimized, set SWP_MINIMIZE.
3066 *
3067 * -- To start a VIO session without auto-close, set
3068 * the half-documented SWP_NOAUTOCLOSE flag (0x8000)
3069 * This flag is now in the newer toolkit headers.
3070 *
3071 * Since this calls WinStartApp in turn, this
3072 * requires a message queue on the calling thread.
3073 *
3074 *@@added V0.9.6 (2000-10-16) [umoeller]
3075 *@@changed V0.9.7 (2000-12-10) [umoeller]: PROGDETAILS.swpInitial no longer zeroed... this broke VIOs
3076 *@@changed V0.9.7 (2000-12-17) [umoeller]: PROGDETAILS.pszEnvironment no longer zeroed
3077 *@@changed V0.9.9 (2001-01-27) [umoeller]: crashed if PROGDETAILS.pszExecutable was NULL
3078 *@@todo PROG_DEFAULT ain't working; test winhQueryAppType
3079 */
3080
3081HAPP winhStartApp(HWND hwndNotify, // in: notify window (as with WinStartApp)
3082 const PROGDETAILS *pcProgDetails) // in: program data
3083{
3084 HAPP happ = NULLHANDLE;
3085 XSTRING strParamsPatched;
3086 BOOL fIsWindowsApp = FALSE,
3087 fIsWindowsEnhApp = FALSE;
3088 PROGDETAILS ProgDetails;
3089 PSZ pszWinOS2Env = 0;
3090
3091 memcpy(&ProgDetails, pcProgDetails, sizeof(PROGDETAILS));
3092 // pointers still point into old prog details buffer
3093 ProgDetails.Length = sizeof(PROGDETAILS);
3094 ProgDetails.progt.fbVisible = SHE_VISIBLE;
3095 // ProgDetails.pszEnvironment = 0;
3096
3097 // all this only makes sense if this contains something...
3098 // besides, this crashed on string comparisons V0.9.9 (2001-01-27) [umoeller]
3099 if (ProgDetails.pszExecutable)
3100 {
3101 // memset(&ProgDetails.swpInitial, 0, sizeof(SWP));
3102 // this wasn't a good idea... WPProgram stores stuff
3103 // in here, such as the "minimize on startup" -> SWP_MINIMIZE
3104
3105 // duplicate parameters...
3106 // we need this for string manipulations below...
3107 if (ProgDetails.pszParameters)
3108 xstrInitCopy(&strParamsPatched,
3109 ProgDetails.pszParameters,
3110 100);
3111 else
3112 // no old params:
3113 xstrInit(&strParamsPatched, 100);
3114
3115 // _Pmpf((__FUNCTION__ ": old progc: 0x%lX", pcProgDetails->progt.progc));
3116 // _Pmpf((" pszTitle: %s", (ProgDetails.pszTitle) ? ProgDetails.pszTitle : NULL));
3117 // _Pmpf((" pszIcon: %s", (ProgDetails.pszIcon) ? ProgDetails.pszIcon : NULL));
3118
3119 // program type fixups
3120 switch (ProgDetails.progt.progc) // that's a ULONG
3121 {
3122 case ((ULONG)-1): // we get that sometimes...
3123 case PROG_DEFAULT:
3124 // @@todo
3125 break;
3126 }
3127
3128 // now try again...
3129 switch (ProgDetails.progt.progc)
3130 {
3131 case PROG_31_ENHSEAMLESSVDM: // 17
3132 case PROG_31_ENHSEAMLESSCOMMON: // 18
3133 case PROG_31_ENH: // 19
3134 fIsWindowsApp = TRUE;
3135 fIsWindowsEnhApp = TRUE;
3136 break;
3137
3138 #ifndef PROG_30_STD
3139 #define PROG_30_STD (PROGCATEGORY)11
3140 #endif
3141
3142 #ifndef PROG_30_STDSEAMLESSVDM
3143 #define PROG_30_STDSEAMLESSVDM (PROGCATEGORY)13
3144 #endif
3145
3146 case PROG_WINDOW_REAL: // 10
3147 case PROG_30_STD: // 11
3148 case PROG_WINDOW_AUTO: // 12
3149 case PROG_30_STDSEAMLESSVDM: // 13
3150 case PROG_30_STDSEAMLESSCOMMON: // 14
3151 case PROG_31_STDSEAMLESSVDM: // 15
3152 case PROG_31_STDSEAMLESSCOMMON: // 16
3153 case PROG_31_STD: // 20
3154 fIsWindowsApp = TRUE;
3155 break;
3156 }
3157
3158 /*
3159 * command lines fixups:
3160 *
3161 */
3162
3163 if (strcmp(ProgDetails.pszExecutable, "*") == 0)
3164 {
3165 /*
3166 * "*" for command sessions:
3167 *
3168 */
3169
3170 if (fIsWindowsEnhApp)
3171 {
3172 // enhanced Win-OS/2 session:
3173 PSZ psz = NULL;
3174 if (strParamsPatched.ulLength)
3175 // "/3 " + existing params
3176 psz = strdup(strParamsPatched.psz);
3177
3178 xstrcpy(&strParamsPatched, "/3 ", 0);
3179
3180 if (psz)
3181 {
3182 xstrcat(&strParamsPatched, psz, 0);
3183 free(psz);
3184 }
3185 }
3186
3187 if (fIsWindowsApp)
3188 {
3189 // cheat: WinStartApp doesn't support NULL
3190 // for Win-OS2 sessions, so manually start winos2.com
3191 ProgDetails.pszExecutable = "WINOS2.COM";
3192 // this is a DOS app, so fix this to DOS fullscreen
3193 ProgDetails.progt.progc = PROG_VDM;
3194 }
3195 else
3196 // for all other executable types
3197 // (including OS/2 and DOS sessions),
3198 // set pszExecutable to NULL; this will
3199 // have WinStartApp start a cmd shell
3200 ProgDetails.pszExecutable = NULL;
3201
3202 } // end if (strcmp(pProgDetails->pszExecutable, "*") == 0)
3203 else
3204 switch (ProgDetails.progt.progc)
3205 {
3206 /*
3207 * .CMD files fixups
3208 *
3209 */
3210
3211 case PROG_FULLSCREEN: // OS/2 fullscreen
3212 case PROG_WINDOWABLEVIO: // OS/2 window
3213 {
3214 PSZ pszExtension = doshGetExtension(ProgDetails.pszExecutable);
3215 if (pszExtension)
3216 {
3217 if (stricmp(pszExtension, "CMD") == 0)
3218 {
3219 CallBatchCorrectly(&ProgDetails,
3220 &strParamsPatched,
3221 "OS2_SHELL",
3222 "CMD.EXE");
3223 }
3224 }
3225 break; }
3226
3227 case PROG_VDM: // DOS fullscreen
3228 case PROG_WINDOWEDVDM: // DOS window
3229 {
3230 PSZ pszExtension = doshGetExtension(ProgDetails.pszExecutable);
3231 if (pszExtension)
3232 {
3233 if (stricmp(pszExtension, "BAT") == 0)
3234 {
3235 CallBatchCorrectly(&ProgDetails,
3236 &strParamsPatched,
3237 NULL,
3238 "COMMAND.COM");
3239 }
3240 }
3241 break; }
3242 } // end switch (ProgDetails.progt.progc)
3243
3244 /*
3245 * Fix environment for Win-OS/2
3246 *
3247 */
3248
3249 if ( !(xstrIsString(ProgDetails.pszEnvironment)) // env empty
3250 && (fIsWindowsApp) // and win-os2 app
3251 )
3252 {
3253 ULONG ulSize = 0;
3254 // get default environment (from Win-OS/2 settings object)
3255 // from OS2.INI
3256 PSZ pszDefEnv = prfhQueryProfileData(HINI_USER,
3257 "WINOS2",
3258 "PM_GlobalWindows31Settings",
3259 &ulSize);
3260 if (pszDefEnv)
3261 {
3262 PSZ pszDefEnv2 = (PSZ)malloc(ulSize + 2);
3263 if (pszDefEnv2)
3264 {
3265 PSZ p = pszDefEnv2;
3266 memset(pszDefEnv2, 0, ulSize + 2);
3267 memcpy(pszDefEnv2, pszDefEnv, ulSize);
3268
3269 for (p = pszDefEnv2;
3270 p < pszDefEnv2 + ulSize;
3271 p++)
3272 if (*p == ';')
3273 *p = 0;
3274
3275 // okay.... now we got an OS/2-style environment
3276 // with 0, 0, 00 strings
3277
3278 pszWinOS2Env = pszDefEnv2; // freed below
3279
3280 // use this
3281 ProgDetails.pszEnvironment = pszWinOS2Env;
3282 }
3283
3284 free(pszDefEnv);
3285 }
3286 }
3287
3288 ProgDetails.pszParameters = strParamsPatched.psz;
3289
3290 happ = WinStartApp(hwndNotify,
3291 // receives WM_APPTERMINATENOTIFY
3292 &ProgDetails,
3293 strParamsPatched.psz,
3294 NULL, // "reserved", PMREF says...
3295 SAF_INSTALLEDCMDLINE);
3296 // we MUST use SAF_INSTALLEDCMDLINE
3297 // or no Win-OS/2 session will start...
3298 // whatever is going on here... Warp 4 FP11
3299
3300 // do not use SAF_STARTCHILDAPP, or the
3301 // app will be terminated automatically
3302 // when the WPS terminates!
3303
3304 // _Pmpf((__FUNCTION__ ": got happ 0x%lX", happ));
3305
3306 xstrClear(&strParamsPatched);
3307 if (pszWinOS2Env)
3308 free(pszWinOS2Env);
3309 } // end if (ProgDetails.pszExecutable)
3310
3311 return (happ);
3312}
3313
3314/*
3315 *@@ winhAnotherInstance:
3316 * this tests whether another instance of the same
3317 * application is already running.
3318 *
3319 * To identify instances of the same application, the
3320 * application must call this function during startup
3321 * with the unique name of an OS/2 semaphore. As with
3322 * all OS/2 semaphores, the semaphore name must begin
3323 * with "\\SEM32\\". The semaphore isn't really used
3324 * except for testing for its existence, since that
3325 * name is unique among all processes.
3326 *
3327 * If another instance is found, TRUE is returned. If
3328 * (fSwitch == TRUE), that instance is switched to,
3329 * using the tasklist.
3330 *
3331 * If no other instance is found, FALSE is returned only.
3332 *
3333 * Based on an EDM/2 code snippet.
3334 *
3335 *@@added V0.9.0 (99-10-22) [umoeller]
3336 */
3337
3338BOOL winhAnotherInstance(const char *pcszSemName, // in: semaphore ID
3339 BOOL fSwitch) // in: if TRUE, switch to first instance if running
3340{
3341 HMTX hmtx;
3342
3343 if (DosCreateMutexSem((PSZ)pcszSemName,
3344 &hmtx,
3345 DC_SEM_SHARED,
3346 TRUE)
3347 == NO_ERROR)
3348 // semapore created: this doesn't happen if the semaphore
3349 // exists already, so no other instance is running
3350 return (FALSE);
3351
3352 // else: instance running
3353 hmtx = NULLHANDLE;
3354
3355 // switch to other instance?
3356 if (fSwitch)
3357 {
3358 // yes: query mutex creator
3359 if (DosOpenMutexSem((PSZ)pcszSemName,
3360 &hmtx)
3361 == NO_ERROR)
3362 {
3363 PID pid = 0;
3364 TID tid = 0; // unused
3365 ULONG ulCount; // unused
3366
3367 if (DosQueryMutexSem(hmtx, &pid, &tid, &ulCount) == NO_ERROR)
3368 {
3369 HSWITCH hswitch = WinQuerySwitchHandle(NULLHANDLE, pid);
3370 if (hswitch != NULLHANDLE)
3371 WinSwitchToProgram(hswitch);
3372 }
3373
3374 DosCloseMutexSem(hmtx);
3375 }
3376 }
3377
3378 return (TRUE); // another instance exists
3379}
3380
3381/*
3382 *@@ winhAddToTasklist:
3383 * this adds the specified window to the tasklist
3384 * with hIcon as its program icon (which is also
3385 * set for the main window). This is useful for
3386 * the old "dialog as main window" trick.
3387 *
3388 * Returns the HSWITCH of the added entry.
3389 */
3390
3391HSWITCH winhAddToTasklist(HWND hwnd, // in: window to add
3392 HPOINTER hIcon) // in: icon for main window
3393{
3394 SWCNTRL swctl;
3395 HSWITCH hswitch = 0;
3396 swctl.hwnd = hwnd; // window handle
3397 swctl.hwndIcon = hIcon; // icon handle
3398 swctl.hprog = NULLHANDLE; // program handle (use default)
3399 WinQueryWindowProcess(hwnd, &(swctl.idProcess), NULL);
3400 // process identifier
3401 swctl.idSession = 0; // session identifier ?
3402 swctl.uchVisibility = SWL_VISIBLE; // visibility
3403 swctl.fbJump = SWL_JUMPABLE; // jump indicator
3404 // get window title from window titlebar
3405 if (hwnd)
3406 WinQueryWindowText(hwnd, sizeof(swctl.szSwtitle), swctl.szSwtitle);
3407 swctl.bProgType = PROG_DEFAULT; // program type
3408 hswitch = WinAddSwitchEntry(&swctl);
3409
3410 // give the main window the icon
3411 if ((hwnd) && (hIcon))
3412 WinSendMsg(hwnd,
3413 WM_SETICON,
3414 (MPARAM)hIcon,
3415 NULL);
3416
3417 return (hswitch);
3418}
3419
3420/*
3421 *@@category: Helpers\PM helpers\Miscellaneous
3422 */
3423
3424/* ******************************************************************
3425 *
3426 * Miscellaneous
3427 *
3428 ********************************************************************/
3429
3430/*
3431 *@@ winhMyAnchorBlock:
3432 * returns the proper anchor block (HAB)
3433 * for the calling thread.
3434 *
3435 * Many Win* functions require an HAB to be
3436 * passed in. While many of them will work
3437 * when passing in NULLHANDLE, some (such as
3438 * WinGetMsg) won't. If you don't know the
3439 * anchor block of the calling thread, use
3440 * this function.
3441 *
3442 * This creates a temporary object window to
3443 * find out the anchor block. This is quite
3444 * expensive so only use this if there's no
3445 * other way to find out.
3446 *
3447 *@@added V0.9.11 (2001-04-20) [umoeller]
3448 */
3449
3450HAB winhMyAnchorBlock(VOID)
3451{
3452 HAB hab = NULLHANDLE;
3453 HWND hwnd = winhCreateObjectWindow(WC_BUTTON, NULL);
3454 if (hwnd)
3455 {
3456 hab = WinQueryAnchorBlock(hwnd);
3457 WinDestroyWindow(hwnd);
3458 }
3459
3460 return (hab);
3461}
3462
3463/*
3464 *@@ winhFree:
3465 * frees a block of memory allocated by the
3466 * winh* functions.
3467 *
3468 * Since the winh* functions use malloc(),
3469 * you can also use free() directly on such
3470 * blocks. However, you must use winhFree
3471 * if the winh* functions are in a module
3472 * with a different C runtime.
3473 *
3474 *@@added V0.9.7 (2000-12-06) [umoeller]
3475 */
3476
3477VOID winhFree(PVOID p)
3478{
3479 if (p)
3480 free(p);
3481}
3482
3483/*
3484 *@@ winhSleep:
3485 * sleeps at least the specified amount of time,
3486 * without blocking the message queue.
3487 *
3488 * NOTE: This function is a bit expensive because
3489 * it creates a temporary object window. If you
3490 * need to sleep several times, you should rather
3491 * use a private timer.
3492 *
3493 *@@added V0.9.4 (2000-07-11) [umoeller]
3494 *@@changed V0.9.9 (2001-03-11) [umoeller]: rewritten
3495 */
3496
3497VOID winhSleep(ULONG ulSleep) // in: sleep time in milliseconds
3498{
3499 HWND hwnd = winhCreateObjectWindow(WC_STATIC, NULL);
3500 if (hwnd)
3501 {
3502 QMSG qmsg;
3503 HAB hab = WinQueryAnchorBlock(hwnd);
3504 if (WinStartTimer(hab,
3505 hwnd,
3506 1,
3507 ulSleep))
3508 {
3509 while (WinGetMsg(hab, &qmsg, NULLHANDLE, 0, 0))
3510 {
3511 if ( (qmsg.hwnd == hwnd)
3512 && (qmsg.msg == WM_TIMER)
3513 && (qmsg.mp1 == (MPARAM)1) // timer ID
3514 )
3515 break;
3516
3517 WinDispatchMsg(hab, &qmsg);
3518 }
3519 WinStopTimer(hab,
3520 hwnd,
3521 1);
3522 }
3523 else
3524 // timer creation failed:
3525 DosSleep(ulSleep);
3526
3527 WinDestroyWindow(hwnd);
3528 }
3529 else
3530 DosSleep(ulSleep);
3531}
3532
3533/*
3534 *@@ winhFileDlg:
3535 * one-short function for opening an "Open" file
3536 * dialog.
3537 *
3538 * On input, pszFile specifies the directory and
3539 * file specification (e.g. "F:\*.txt").
3540 *
3541 * Returns TRUE if the user pressed OK. In that
3542 * case, the fully qualified filename is written
3543 * into pszFile again.
3544 *
3545 * Returns FALSE if the user pressed Cancel.
3546 *
3547 * Notes about flFlags:
3548 *
3549 * -- WINH_FOD_SAVEDLG: display a "Save As" dialog.
3550 * Otherwise an "Open" dialog is displayed.
3551 *
3552 * -- WINH_FOD_INILOADDIR: load a directory from the
3553 * specified INI key and switch the dlg to it.
3554 * In that case, on input, pszFile must only
3555 * contain the file filter without any path
3556 * specification, because that is loaded from
3557 * the INI key. If the INI key does not exist,
3558 * the current process directory will be used.
3559 *
3560 * -- WINH_FOD_INISAVEDIR: if the user presses OK,
3561 * the directory of the selected file is written
3562 * to the specified INI key so that it can be
3563 * reused later. This flag is independent of
3564 * WINH_FOD_INISAVEDIR: you can specify none,
3565 * one, or both of them.
3566 *
3567 *@@added V0.9.3 (2000-04-29) [umoeller]
3568 */
3569
3570BOOL winhFileDlg(HWND hwndOwner, // in: owner for file dlg
3571 PSZ pszFile, // in: file mask; out: fully q'd filename
3572 // (should be CCHMAXPATH in size)
3573 ULONG flFlags, // in: any combination of the following:
3574 // -- WINH_FOD_SAVEDLG: save dlg; else open dlg
3575 // -- WINH_FOD_INILOADDIR: load FOD path from INI
3576 // -- WINH_FOD_INISAVEDIR: store FOD path to INI on OK
3577 HINI hini, // in: INI file to load/store last path from (can be HINI_USER)
3578 const char *pcszApplication, // in: INI application to load/store last path from
3579 const char *pcszKey) // in: INI key to load/store last path from
3580{
3581 FILEDLG fd;
3582 memset(&fd, 0, sizeof(FILEDLG));
3583 fd.cbSize = sizeof(FILEDLG);
3584 fd.fl = FDS_CENTER;
3585
3586 if (flFlags & WINH_FOD_SAVEDLG)
3587 fd.fl |= FDS_SAVEAS_DIALOG;
3588 else
3589 fd.fl |= FDS_OPEN_DIALOG;
3590
3591 // default: copy pszFile
3592 strcpy(fd.szFullFile, pszFile);
3593
3594 if ( (hini) && (flFlags & WINH_FOD_INILOADDIR) )
3595 {
3596 // overwrite with initial directory for FOD from OS2.INI
3597 if (PrfQueryProfileString(hini,
3598 (PSZ)pcszApplication,
3599 (PSZ)pcszKey,
3600 "", // default string V0.9.9 (2001-02-10) [umoeller]
3601 fd.szFullFile,
3602 sizeof(fd.szFullFile)-10)
3603 >= 2)
3604 {
3605 // found: append "\*"
3606 strcat(fd.szFullFile, "\\");
3607 strcat(fd.szFullFile, pszFile);
3608 }
3609 }
3610
3611 if ( WinFileDlg(HWND_DESKTOP, // parent
3612 hwndOwner, // owner
3613 &fd)
3614 && (fd.lReturn == DID_OK)
3615 )
3616 {
3617 // save path back?
3618 if ( (hini)
3619 && (flFlags & WINH_FOD_INISAVEDIR)
3620 )
3621 {
3622 // get the directory that was used
3623 PSZ p = strrchr(fd.szFullFile, '\\');
3624 if (p)
3625 {
3626 // contains directory:
3627 // copy to OS2.INI
3628 PSZ pszDir = strhSubstr(fd.szFullFile, p);
3629 if (pszDir)
3630 {
3631 PrfWriteProfileString(hini,
3632 (PSZ)pcszApplication,
3633 (PSZ)pcszKey,
3634 pszDir);
3635 free(pszDir);
3636 }
3637 }
3638 }
3639
3640 strcpy(pszFile, fd.szFullFile);
3641
3642 return (TRUE);
3643 }
3644
3645 return (FALSE);
3646}
3647
3648/*
3649 *@@ winhSetWaitPointer:
3650 * this sets the mouse pointer to "Wait".
3651 * Returns the previous pointer (HPOINTER),
3652 * which should be stored somewhere to be
3653 * restored later. Example:
3654 + HPOINTER hptrOld = winhSetWaitPointer();
3655 + ...
3656 + WinSetPointer(HWND_DESKTOP, hptrOld);
3657 */
3658
3659HPOINTER winhSetWaitPointer(VOID)
3660{
3661 HPOINTER hptr = WinQueryPointer(HWND_DESKTOP);
3662 WinSetPointer(HWND_DESKTOP,
3663 WinQuerySysPointer(HWND_DESKTOP,
3664 SPTR_WAIT,
3665 FALSE)); // no copy
3666 return (hptr);
3667}
3668
3669/*
3670 *@@ winhQueryWindowText:
3671 * this returns the window text of the specified
3672 * HWND in a newly allocated buffer.
3673 *
3674 * Returns NULL on error. Use free()
3675 * to free the return value.
3676 */
3677
3678PSZ winhQueryWindowText(HWND hwnd)
3679{
3680 PSZ pszText = NULL;
3681 ULONG cbText = WinQueryWindowTextLength(hwnd);
3682 // additional null character
3683 if (cbText)
3684 {
3685 pszText = (PSZ)malloc(cbText + 1);
3686 if (pszText)
3687 WinQueryWindowText(hwnd,
3688 cbText + 1,
3689 pszText);
3690 }
3691 return (pszText);
3692}
3693
3694/*
3695 *@@ winhReplaceWindowText:
3696 * this is a combination of winhQueryWindowText
3697 * and strhFindReplace to replace substrings in a window.
3698 *
3699 * This is useful for filling in placeholders
3700 * a la "%1" in control windows, e.g. static
3701 * texts.
3702 *
3703 * This replaces only the first occurence of
3704 * pszSearch.
3705 *
3706 * Returns TRUE only if the window exists and
3707 * the search string was replaced.
3708 *
3709 *@@added V0.9.0 [umoeller]
3710 */
3711
3712BOOL winhReplaceWindowText(HWND hwnd, // in: window whose text is to be modified
3713 const char *pcszSearch, // in: search string (e.g. "%1")
3714 const char *pcszReplaceWith) // in: replacement string for pszSearch
3715{
3716 BOOL brc = FALSE;
3717 PSZ pszText = winhQueryWindowText(hwnd);
3718 if (pszText)
3719 {
3720 ULONG ulOfs = 0;
3721 if (strhFindReplace(&pszText, &ulOfs, pcszSearch, pcszReplaceWith) > 0)
3722 {
3723 WinSetWindowText(hwnd, pszText);
3724 brc = TRUE;
3725 }
3726 free(pszText);
3727 }
3728 return (brc);
3729}
3730
3731/*
3732 *@@ winhEnableDlgItems:
3733 * this enables/disables a whole range of controls
3734 * in a window by enumerating the child windows
3735 * until usIDFirst is found. If so, that subwindow
3736 * is enabled/disabled and all the following windows
3737 * in the enumeration also, until usIDLast is found.
3738 *
3739 * Note that this affects _all_ controls following
3740 * the usIDFirst window, no matter what ID they have
3741 * (even if "-1"), until usIDLast is found.
3742 *
3743 * Returns the no. of controls which were enabled/disabled
3744 * (null if none).
3745 *
3746 *@@added V0.9.0 [umoeller]
3747 *@@changed V0.9.1 (99-12-20) [umoeller]: renamed from winhEnableDlgItems
3748 */
3749
3750ULONG winhEnableControls(HWND hwndDlg, // in: dialog window
3751 USHORT usIDFirst, // in: first affected control ID
3752 USHORT usIDLast, // in: last affected control ID (inclusive)
3753 BOOL fEnable)
3754{
3755 HENUM henum1 = NULLHANDLE;
3756 HWND hwndThis = NULLHANDLE;
3757 ULONG ulCount = 0;
3758
3759 henum1 = WinBeginEnumWindows(hwndDlg);
3760 while ((hwndThis = WinGetNextWindow(henum1)) != NULLHANDLE)
3761 {
3762 USHORT usIDCheckFirst = WinQueryWindowUShort(hwndThis, QWS_ID),
3763 usIDCheckLast;
3764 if (usIDCheckFirst == usIDFirst)
3765 {
3766 WinEnableWindow(hwndThis, fEnable);
3767 ulCount++;
3768
3769 while ((hwndThis = WinGetNextWindow(henum1)) != NULLHANDLE)
3770 {
3771 WinEnableWindow(hwndThis, fEnable);
3772 ulCount++;
3773 usIDCheckLast = WinQueryWindowUShort(hwndThis, QWS_ID);
3774 if (usIDCheckLast == usIDLast)
3775 break;
3776 }
3777
3778 break; // outer loop
3779 }
3780 }
3781 WinEndEnumWindows(henum1);
3782 return (ulCount);
3783}
3784
3785/*
3786 *@@ winhCreateStdWindow:
3787 * much like WinCreateStdWindow, but this one
3788 * allows you to have the standard window
3789 * positioned automatically, using a given
3790 * SWP structure (*pswpFrame).
3791 *
3792 * The frame is created with the specified parent
3793 * (usually HWND_DESKTOP), but no owner.
3794 *
3795 * The client window is created with the frame as
3796 * its parent and owner and gets an ID of FID_CLIENT.
3797 *
3798 * Alternatively, you can set pswpFrame to NULL
3799 * and specify FCF_SHELLPOSITION with flFrameCreateFlags.
3800 * If you want the window to be shown, specify
3801 * SWP_SHOW (and maybe SWP_ACTIVATE) in *pswpFrame.
3802 *
3803 *@@added V0.9.0 [umoeller]
3804 *@@changed V0.9.5 (2000-08-13) [umoeller]: flStyleClient never worked, fixed
3805 *@@changed V0.9.7 (2000-12-08) [umoeller]: fixed client calc for invisible window
3806 */
3807
3808HWND winhCreateStdWindow(HWND hwndFrameParent, // in: normally HWND_DESKTOP
3809 PSWP pswpFrame, // in: frame wnd pos
3810 ULONG flFrameCreateFlags, // in: FCF_* flags
3811 ULONG ulFrameStyle, // in: WS_* flags (e.g. WS_VISIBLE, WS_ANIMATE)
3812 const char *pcszFrameTitle, // in: frame title (title bar)
3813 ULONG ulResourcesID, // in: according to FCF_* flags
3814 const char *pcszClassClient, // in: client class name
3815 ULONG flStyleClient, // in: client style
3816 ULONG ulID, // in: frame window ID
3817 PVOID pClientCtlData, // in: pCtlData structure pointer for client
3818 PHWND phwndClient) // out: created client wnd
3819{
3820 FRAMECDATA fcdata;
3821 HWND hwndFrame;
3822 RECTL rclClient;
3823
3824 fcdata.cb = sizeof(FRAMECDATA);
3825 fcdata.flCreateFlags = flFrameCreateFlags;
3826 fcdata.hmodResources = (HMODULE)NULL;
3827 fcdata.idResources = ulResourcesID;
3828
3829 /* Create the frame and client windows. */
3830 hwndFrame = WinCreateWindow(hwndFrameParent,
3831 WC_FRAME,
3832 (PSZ)pcszFrameTitle,
3833 ulFrameStyle,
3834 0,0,0,0, // size and position = 0
3835 NULLHANDLE, // no owner
3836 HWND_TOP, // z-order
3837 ulID, // frame window ID
3838 &fcdata, // frame class data
3839 NULL); // no presparams
3840
3841 if (hwndFrame)
3842 {
3843 *phwndClient = WinCreateWindow(hwndFrame, // parent
3844 (PSZ)pcszClassClient, // class
3845 NULL, // no title
3846 flStyleClient, // style
3847 0,0,0,0, // size and position = 0
3848 hwndFrame, // owner
3849 HWND_BOTTOM, // bottom z-order
3850 FID_CLIENT, // frame window ID
3851 pClientCtlData, // class data
3852 NULL); // no presparams
3853
3854 if (*phwndClient)
3855 {
3856 if (pswpFrame)
3857 {
3858 // position frame
3859 WinSetWindowPos(hwndFrame,
3860 pswpFrame->hwndInsertBehind,
3861 pswpFrame->x,
3862 pswpFrame->y,
3863 pswpFrame->cx,
3864 pswpFrame->cy,
3865 pswpFrame->fl);
3866
3867 // position client
3868 // WinQueryWindowRect(hwndFrame, &rclClient);
3869 // doesn't work because it might be invisible V0.9.7 (2000-12-08) [umoeller]
3870 rclClient.xLeft = 0;
3871 rclClient.yBottom = 0;
3872 rclClient.xRight = pswpFrame->cx;
3873 rclClient.yTop = pswpFrame->cy;
3874 WinCalcFrameRect(hwndFrame,
3875 &rclClient,
3876 TRUE); // calc client from frame
3877 WinSetWindowPos(*phwndClient,
3878 HWND_TOP,
3879 rclClient.xLeft,
3880 rclClient.yBottom,
3881 rclClient.xRight - rclClient.xLeft,
3882 rclClient.yTop - rclClient.yBottom,
3883 SWP_MOVE | SWP_SIZE | SWP_SHOW);
3884 }
3885 }
3886 }
3887 return (hwndFrame);
3888}
3889
3890/*
3891 *@@ winhCreateObjectWindow:
3892 * creates an object window of the specified
3893 * window class, which you should have registered
3894 * before calling this. pvCreateParam will be
3895 * given to the window on WM_CREATE.
3896 *
3897 * Returns the HWND of the object window or
3898 * NULLHANDLE on errors.
3899 *
3900 *@@added V0.9.3 (2000-04-17) [umoeller]
3901 *@@changed V0.9.7 (2001-01-17) [umoeller]: made this a function from a macro
3902 */
3903
3904HWND winhCreateObjectWindow(const char *pcszWindowClass, // in: PM window class name
3905 PVOID pvCreateParam) // in: create param
3906{
3907 return (WinCreateWindow(HWND_OBJECT,
3908 (PSZ)pcszWindowClass,
3909 (PSZ)"",
3910 0,
3911 0,0,0,0,
3912 0,
3913 HWND_BOTTOM,
3914 0,
3915 pvCreateParam,
3916 NULL));
3917}
3918
3919/*
3920 *@@ winhCreateControl:
3921 * creates a control with a size and position of 0.
3922 *
3923 *@@added V0.9.9 (2001-03-13) [umoeller]
3924 */
3925
3926HWND winhCreateControl(HWND hwndParentAndOwner, // in: owner and parent window
3927 const char *pcszClass, // in: window class (e.g. WC_BUTTON)
3928 const char *pcszText, // in: window title
3929 ULONG ulStyle, // in: control style
3930 ULONG ulID) // in: control ID
3931{
3932 return (WinCreateWindow(hwndParentAndOwner,
3933 (PSZ)pcszClass,
3934 (PSZ)pcszText,
3935 ulStyle,
3936 0, 0, 0, 0,
3937 hwndParentAndOwner,
3938 HWND_TOP,
3939 ulID,
3940 NULL,
3941 NULL));
3942}
3943
3944/*
3945 *@@ winhRepaintWindows:
3946 * this repaints all children of hwndParent.
3947 * If this is passed as HWND_DESKTOP, the
3948 * whole screen is repainted.
3949 *
3950 *@@changed V0.9.7 (2000-12-13) [umoeller]: hwndParent was never respected, fixed
3951 */
3952
3953VOID winhRepaintWindows(HWND hwndParent)
3954{
3955 HWND hwndTop;
3956 HENUM henum = WinBeginEnumWindows(hwndParent);
3957 while ((hwndTop = WinGetNextWindow(henum)))
3958 if (WinIsWindowShowing(hwndTop))
3959 WinInvalidateRect(hwndTop, NULL, TRUE);
3960 WinEndEnumWindows(henum);
3961}
3962
3963/*
3964 *@@ winhFindMsgQueue:
3965 * returns the message queue which matches
3966 * the given process and thread IDs. Since,
3967 * per IBM definition, every thread may only
3968 * have one MQ, this should be unique.
3969 *
3970 *@@added V0.9.2 (2000-03-08) [umoeller]
3971 */
3972
3973HMQ winhFindMsgQueue(PID pid, // in: process ID
3974 TID tid, // in: thread ID
3975 HAB* phab) // out: anchor block
3976{
3977 HWND hwndThis = 0,
3978 rc = 0;
3979 HENUM henum = WinBeginEnumWindows(HWND_OBJECT);
3980 while ((hwndThis = WinGetNextWindow(henum)))
3981 {
3982 CHAR szClass[200];
3983 if (WinQueryClassName(hwndThis, sizeof(szClass), szClass))
3984 {
3985 if (strcmp(szClass, "#32767") == 0)
3986 {
3987 // message queue window:
3988 PID pidWin = 0;
3989 TID tidWin = 0;
3990 WinQueryWindowProcess(hwndThis,
3991 &pidWin,
3992 &tidWin);
3993 if ( (pidWin == pid)
3994 && (tidWin == tid)
3995 )
3996 {
3997 // get HMQ from window words
3998 rc = WinQueryWindowULong(hwndThis, QWL_HMQ);
3999 if (rc)
4000 if (phab)
4001 *phab = WinQueryAnchorBlock(hwndThis);
4002 break;
4003 }
4004 }
4005 }
4006 }
4007 WinEndEnumWindows(henum);
4008
4009 return (rc);
4010}
4011
4012/*
4013 *@@ winhFindHardErrorWindow:
4014 * this searches all children of HWND_OBJECT
4015 * for the PM hard error windows, which are
4016 * invisible most of the time. When a hard
4017 * error occurs, that window is made a child
4018 * of HWND_DESKTOP instead.
4019 *
4020 * Stolen from ProgramCommander/2 (C) Roman Stangl.
4021 *
4022 *@@added V0.9.3 (2000-04-27) [umoeller]
4023 */
4024
4025VOID winhFindPMErrorWindows(HWND *phwndHardError, // out: hard error window
4026 HWND *phwndSysError) // out: system error window
4027{
4028 PID pidObject; // HWND_OBJECT's process and thread id
4029 TID tidObject;
4030 PID pidObjectChild; // HWND_OBJECT's child window process and thread id
4031 TID tidObjectChild;
4032 HENUM henumObject; // HWND_OBJECT enumeration handle
4033 HWND hwndObjectChild; // Window handle of current HWND_OBJECT child
4034 UCHAR ucClassName[32]; // Window class e.g. #1 for WC_FRAME
4035 CLASSINFO classinfoWindow; // Class info of current HWND_OBJECT child
4036
4037 *phwndHardError = NULLHANDLE;
4038 *phwndSysError = NULLHANDLE;
4039
4040 // query HWND_OBJECT's window process
4041 WinQueryWindowProcess(WinQueryObjectWindow(HWND_DESKTOP), &pidObject, &tidObject);
4042 // enumerate all child windows of HWND_OBJECT
4043 henumObject = WinBeginEnumWindows(HWND_OBJECT);
4044 while ((hwndObjectChild = WinGetNextWindow(henumObject)) != NULLHANDLE)
4045 {
4046 // see if the current HWND_OBJECT child window runs in the
4047 // process of HWND_OBJECT (PM)
4048 WinQueryWindowProcess(hwndObjectChild, &pidObjectChild, &tidObjectChild);
4049 if (pidObject == pidObjectChild)
4050 {
4051 // get the child window's data
4052 WinQueryClassName(hwndObjectChild,
4053 sizeof(ucClassName),
4054 (PCH)ucClassName);
4055 WinQueryClassInfo(WinQueryAnchorBlock(hwndObjectChild),
4056 (PSZ)ucClassName,
4057 &classinfoWindow);
4058 if ( (!strcmp((PSZ)ucClassName, "#1")
4059 || (classinfoWindow.flClassStyle & CS_FRAME))
4060 )
4061 {
4062 // if the child window is a frame window and running in
4063 // HWND_OBJECT's (PM's) window process, it must be the
4064 // PM Hard Error or System Error window
4065 WinQueryClassName(WinWindowFromID(hwndObjectChild,
4066 FID_CLIENT),
4067 sizeof(ucClassName),
4068 (PSZ)ucClassName);
4069 if (!strcmp((PSZ)ucClassName, "PM Hard Error"))
4070 {
4071 *phwndHardError = hwndObjectChild;
4072 if (*phwndSysError)
4073 // we found the other one already:
4074 // stop searching, we got both
4075 break;
4076 }
4077 else
4078 {
4079 printf("Utility: Found System Error %08X\n", (int)hwndObjectChild);
4080 *phwndSysError = hwndObjectChild;
4081 if (*phwndHardError)
4082 // we found the other one already:
4083 // stop searching, we got both
4084 break;
4085 }
4086 }
4087 } // end if (pidObject == pidObjectChild)
4088 } // end while ((hwndObjectChild = WinGetNextWindow(henumObject)) != NULLHANDLE)
4089 WinEndEnumWindows(henumObject);
4090}
4091
4092/*
4093 *@@ winhCreateFakeDesktop:
4094 * this routine creates and displays a frameless window over
4095 * the whole screen in the color of PM's Desktop to fool the
4096 * user that all windows have been closed (which in fact might
4097 * not be the case).
4098 *
4099 * This window's background color is set to the Desktop's
4100 * (PM's one, not the WPS's one).
4101 *
4102 * Returns the HWND of this window.
4103 */
4104
4105HWND winhCreateFakeDesktop(HWND hwndSibling)
4106{
4107 // presparam for background
4108 typedef struct _BACKGROUND
4109 {
4110 ULONG cb; // length of the aparam parameter, in bytes
4111 ULONG id; // attribute type identity
4112 ULONG cb2; // byte count of the ab parameter
4113 RGB rgb; // attribute value
4114 } BACKGROUND;
4115
4116 BACKGROUND background;
4117 LONG lDesktopColor;
4118
4119 // create fake desktop window = empty window with
4120 // the size of full screen
4121 lDesktopColor = WinQuerySysColor(HWND_DESKTOP,
4122 SYSCLR_BACKGROUND,
4123 0);
4124 background.cb = sizeof(background.id)
4125 + sizeof(background.cb)
4126 + sizeof(background.rgb);
4127 background.id = PP_BACKGROUNDCOLOR;
4128 background.cb2 = sizeof(RGB);
4129 background.rgb.bBlue = (CHAR1FROMMP(lDesktopColor));
4130 background.rgb.bGreen= (CHAR2FROMMP(lDesktopColor));
4131 background.rgb.bRed = (CHAR3FROMMP(lDesktopColor));
4132
4133 return (WinCreateWindow(HWND_DESKTOP, // parent window
4134 WC_FRAME, // class name
4135 "", // window text
4136 WS_VISIBLE, // window style
4137 0, 0, // position and size
4138 WinQuerySysValue(HWND_DESKTOP, SV_CXSCREEN),
4139 WinQuerySysValue(HWND_DESKTOP, SV_CYSCREEN),
4140 NULLHANDLE, // owner window
4141 hwndSibling, // sibling window
4142 1, // window id
4143 NULL, // control data
4144 &background)); // presentation parms
4145}
4146
4147/*
4148 *@@ winhAssertWarp4Notebook:
4149 * this takes hwndDlg as a notebook dialog page and
4150 * goes thru all its controls. If a control with an
4151 * ID <= udIdThreshold is found, this is assumed to
4152 * be a button which is to be given the BS_NOTEBOOKBUTTON
4153 * style. You should therefore give all your button
4154 * controls which should be moved such an ID.
4155 *
4156 * You can also specify how many dialog units
4157 * all the other controls will be moved downward in
4158 * ulDownUnits; this is useful to fill up the space
4159 * which was used by the buttons before moving them.
4160 * Returns TRUE if anything was changed.
4161 *
4162 * This function is useful if you wish to create
4163 * notebook pages using dlgedit.exe which are compatible
4164 * with both Warp 3 and Warp 4. This should be executed
4165 * in WM_INITDLG of the notebook dlg function if the app
4166 * has determined that it is running on Warp 4.
4167 */
4168
4169BOOL winhAssertWarp4Notebook(HWND hwndDlg,
4170 USHORT usIdThreshold, // in: ID threshold
4171 ULONG ulDownUnits) // in: dialog units or 0
4172{
4173 BOOL brc = FALSE;
4174
4175 if (doshIsWarp4())
4176 {
4177 POINTL ptl;
4178 HWND hwndItem;
4179 HENUM henum = 0;
4180
4181 BOOL fIsVisible = WinIsWindowVisible(hwndDlg);
4182 if (ulDownUnits)
4183 {
4184 ptl.x = 0;
4185 ptl.y = ulDownUnits;
4186 WinMapDlgPoints(hwndDlg, &ptl, 1, TRUE);
4187 }
4188
4189 if (fIsVisible)
4190 WinEnableWindowUpdate(hwndDlg, FALSE);
4191
4192 henum = WinBeginEnumWindows(hwndDlg);
4193 while ((hwndItem = WinGetNextWindow(henum)))
4194 {
4195 USHORT usId = WinQueryWindowUShort(hwndItem, QWS_ID);
4196 // _Pmpf(("hwndItem: 0x%lX, ID: 0x%lX", hwndItem, usId));
4197 if (usId <= usIdThreshold)
4198 {
4199 // pushbutton to change:
4200 // _Pmpf((" Setting bit"));
4201 WinSetWindowBits(hwndItem,
4202 QWL_STYLE,
4203 BS_NOTEBOOKBUTTON, BS_NOTEBOOKBUTTON);
4204 brc = TRUE;
4205 }
4206 else
4207 // no pushbutton to change: move downwards
4208 // if desired
4209 if (ulDownUnits)
4210 {
4211 SWP swp;
4212 // _Pmpf(("Moving downwards %d pixels", ptl.y));
4213 WinQueryWindowPos(hwndItem, &swp);
4214 WinSetWindowPos(hwndItem, 0,
4215 swp.x,
4216 swp.y - ptl.y,
4217 0, 0,
4218 SWP_MOVE);
4219 }
4220 }
4221 WinEndEnumWindows(henum);
4222
4223 if (fIsVisible)
4224 WinShowWindow(hwndDlg, TRUE);
4225 }
4226
4227 return (brc);
4228}
4229
4230/*
4231 *@@ winhDrawFormattedText:
4232 * this func takes a rectangle and draws pszText into
4233 * it, breaking the words as neccessary. The line spacing
4234 * is determined from the font currently selected in hps.
4235 *
4236 * As opposed to WinDrawText, this can draw several lines
4237 * at once, and format the _complete_ text according to the
4238 * flCmd parameter, which is like with WinDrawText.
4239 *
4240 * After this function returns, *prcl is modified like this:
4241 *
4242 * -- yTop and yBottom contain the upper and lower boundaries
4243 * which were needed to draw the text. This depends on
4244 * whether DT_TOP etc. were specified.
4245 * To get the height of the rectangle used, calculate the
4246 * delta between yTop and yBottom.
4247 *
4248 * -- xLeft and xRight are modified to contain the outmost
4249 * left and right coordinates which were needed to draw
4250 * the text. This will be set to the longest line which
4251 * was encountered.
4252 *
4253 * You can specify DT_QUERYEXTENT with flDraw to only have
4254 * these text boundaries calculated without actually drawing.
4255 *
4256 * This returns the number of lines drawn.
4257 *
4258 * Note that this calls WinDrawText with DT_TEXTATTRS set,
4259 * that is, the current text primitive attributes will be
4260 * used (fonts and colors).
4261 *
4262 *@@changed V0.9.0 [umoeller]: prcl.xLeft and xRight are now updated too upon return
4263 */
4264
4265ULONG winhDrawFormattedText(HPS hps, // in: presentation space; its settings
4266 // are used, but not altered
4267 PRECTL prcl, // in/out: rectangle to use for drawing
4268 // (modified)
4269 const char *pcszText, // in: text to draw (zero-terminated)
4270 ULONG flCmd) // in: flags like in WinDrawText; I have
4271 // only tested DT_TOP and DT_LEFT though.
4272 // DT_WORDBREAK | DT_TEXTATTRS are always
4273 // set.
4274 // You can specify DT_QUERYEXTENT to only
4275 // have prcl calculated without drawing.
4276{
4277 PSZ p = (PSZ)pcszText;
4278 LONG lDrawn = 1,
4279 lTotalDrawn = 0,
4280 lLineCount = 0,
4281 lOrigYTop = prcl->yTop;
4282 ULONG ulTextLen = strlen(pcszText),
4283 ulCharHeight,
4284 flCmd2,
4285 xLeftmost = prcl->xRight,
4286 xRightmost = prcl->xLeft;
4287 RECTL rcl2;
4288
4289 flCmd2 = flCmd | DT_WORDBREAK | DT_TEXTATTRS;
4290
4291 ulCharHeight = gpihQueryLineSpacing(hps);
4292
4293 while ( (lDrawn)
4294 && (lTotalDrawn < ulTextLen)
4295 )
4296 {
4297 memcpy(&rcl2, prcl, sizeof(rcl2));
4298 lDrawn = WinDrawText(hps,
4299 ulTextLen-lTotalDrawn,
4300 p,
4301 &rcl2,
4302 0, 0, // colors
4303 flCmd2);
4304
4305 // update char counters
4306 p += lDrawn;
4307 lTotalDrawn += lDrawn;
4308
4309 // update x extents
4310 if (rcl2.xLeft < xLeftmost)
4311 xLeftmost = rcl2.xLeft;
4312 if (rcl2.xRight > xRightmost)
4313 xRightmost = rcl2.xRight;
4314
4315 // update y for next line
4316 prcl->yTop -= ulCharHeight;
4317
4318 // increase line count
4319 lLineCount++;
4320 }
4321 prcl->xLeft = xLeftmost;
4322 prcl->xRight = xRightmost;
4323 prcl->yBottom = prcl->yTop;
4324 prcl->yTop = lOrigYTop;
4325
4326 return (lLineCount);
4327}
4328
4329/*
4330 *@@ winhQuerySwitchList:
4331 * returns the switch list in a newly
4332 * allocated buffer. This does the
4333 * regular double WinQuerySwitchList
4334 * call to first get the no. of items
4335 * and then get the items.
4336 *
4337 * The no. of items can be found in
4338 * the returned SWBLOCK.cwsentry.
4339 *
4340 * Returns NULL on errors. Use
4341 * free() to free the return value.
4342 *
4343 *@@added V0.9.7 (2000-12-06) [umoeller]
4344 */
4345
4346PSWBLOCK winhQuerySwitchList(HAB hab)
4347{
4348 ULONG cItems = WinQuerySwitchList(hab, NULL, 0);
4349 ULONG ulBufSize = (cItems * sizeof(SWENTRY)) + sizeof(HSWITCH);
4350 PSWBLOCK pSwBlock = (PSWBLOCK)malloc(ulBufSize);
4351 if (pSwBlock)
4352 {
4353 cItems = WinQuerySwitchList(hab, pSwBlock, ulBufSize);
4354 if (!cItems)
4355 {
4356 free(pSwBlock);
4357 pSwBlock = NULL;
4358 }
4359 }
4360
4361 return (pSwBlock);
4362}
4363
4364/*
4365 *@@ winhQueryTasklistWindow:
4366 * returns the window handle of the PM task list.
4367 *
4368 *@@added V0.9.7 (2000-12-07) [umoeller]
4369 */
4370
4371HWND winhQueryTasklistWindow(VOID)
4372{
4373 SWBLOCK swblock;
4374 HWND hwndTasklist = winhQueryTasklistWindow();
4375 // the tasklist has entry #0 in the SWBLOCK
4376 WinQuerySwitchList(NULLHANDLE, &swblock, sizeof(SWBLOCK));
4377 return (swblock.aswentry[0].swctl.hwnd);
4378}
4379
4380/*
4381 *@@ winhKillTasklist:
4382 * this will destroy the Tasklist (window list) window.
4383 * Note: you will only be able to get it back after a
4384 * reboot, not a WPS restart. Only for use at shutdown and such.
4385 * This trick by Uri J. Stern at
4386 * http://zebra.asta.fh-weingarten.de/os2/Snippets/Howt8881.HTML
4387 */
4388
4389VOID winhKillTasklist(VOID)
4390{
4391 HWND hwndTasklist = winhQueryTasklistWindow();
4392 WinPostMsg(hwndTasklist,
4393 0x0454, // undocumented msg for killing tasklist
4394 NULL, NULL);
4395}
4396
4397// the following must be added for EMX (99-10-22) [umoeller]
4398#ifndef NERR_BufTooSmall
4399 #define NERR_BASE 2100
4400 #define NERR_BufTooSmall (NERR_BASE+23)
4401 // the API return buffer is too small
4402#endif
4403
4404/*
4405 *@@ winhQueryPendingSpoolJobs:
4406 * returns the number of pending print jobs in the spooler
4407 * or 0 if none. Useful for testing before shutdown.
4408 */
4409
4410ULONG winhQueryPendingSpoolJobs(VOID)
4411{
4412 // BOOL rcPending = FALSE;
4413 ULONG ulTotalJobCount = 0;
4414
4415 SPLERR splerr;
4416 USHORT jobCount;
4417 ULONG cbBuf;
4418 ULONG cTotal;
4419 ULONG cReturned;
4420 ULONG cbNeeded;
4421 ULONG ulLevel;
4422 ULONG i,j;
4423 PSZ pszComputerName;
4424 PBYTE pBuf = NULL;
4425 PPRQINFO3 prq;
4426 PPRJINFO2 prj2;
4427
4428 ulLevel = 4L;
4429 pszComputerName = (PSZ)NULL;
4430 splerr = SplEnumQueue(pszComputerName, ulLevel, pBuf, 0L, // cbBuf
4431 &cReturned, &cTotal,
4432 &cbNeeded, NULL);
4433 if ( (splerr == ERROR_MORE_DATA)
4434 || (splerr == NERR_BufTooSmall)
4435 )
4436 {
4437 if (!DosAllocMem((PPVOID)&pBuf,
4438 cbNeeded,
4439 PAG_READ | PAG_WRITE | PAG_COMMIT))
4440 {
4441 cbBuf = cbNeeded;
4442 splerr = SplEnumQueue(pszComputerName, ulLevel, pBuf, cbBuf,
4443 &cReturned, &cTotal,
4444 &cbNeeded, NULL);
4445 if (splerr == NO_ERROR)
4446 {
4447 // set pointer to point to the beginning of the buffer
4448 prq = (PPRQINFO3)pBuf;
4449
4450 // cReturned has the count of the number of PRQINFO3 structures
4451 for (i = 0;
4452 i < cReturned;
4453 i++)
4454 {
4455 // save the count of jobs; there are this many PRJINFO2
4456 // structures following the PRQINFO3 structure
4457 jobCount = prq->cJobs;
4458 // _Pmpf(( "Job count in this queue is %d",jobCount ));
4459
4460 // increment the pointer past the PRQINFO3 structure
4461 prq++;
4462
4463 // set a pointer to point to the first PRJINFO2 structure
4464 prj2=(PPRJINFO2)prq;
4465 for (j = 0;
4466 j < jobCount;
4467 j++)
4468 {
4469 // increment the pointer to point to the next structure
4470 prj2++;
4471 // increase the job count, which we'll return
4472 ulTotalJobCount++;
4473
4474 } // endfor jobCount
4475
4476 // after doing all the job structures, prj2 points to the next
4477 // queue structure; set the pointer for a PRQINFO3 structure
4478 prq = (PPRQINFO3)prj2;
4479 } //endfor cReturned
4480 } // endif NO_ERROR
4481 DosFreeMem(pBuf);
4482 }
4483 } // end if Q level given
4484
4485 return (ulTotalJobCount);
4486}
4487
4488/*
4489 *@@ winhSetNumLock:
4490 * this sets the NumLock key on or off, depending
4491 * on fState.
4492 *
4493 * Based on code from WarpEnhancer, (C) Achim Hasenmller.
4494 *
4495 *@@added V0.9.1 (99-12-18) [umoeller]
4496 */
4497
4498VOID winhSetNumLock(BOOL fState)
4499{
4500 // BOOL fRestoreKBD = FALSE; // Assume we're not going to close Kbd
4501 BYTE KeyStateTable[256];
4502 ULONG ulActionTaken; // Used by DosOpen
4503 HFILE hKbd;
4504
4505 // read keyboard state table
4506 if (WinSetKeyboardStateTable(HWND_DESKTOP, &KeyStateTable[0],
4507 FALSE))
4508 {
4509 // first set the PM state
4510 if (fState)
4511 KeyStateTable[VK_NUMLOCK] |= 0x01; // Turn numlock on
4512 else
4513 KeyStateTable[VK_NUMLOCK] &= 0xFE; // Turn numlock off
4514
4515 // set keyboard state table with new state values
4516 WinSetKeyboardStateTable(HWND_DESKTOP, &KeyStateTable[0], TRUE);
4517 }
4518
4519 // now set the OS/2 keyboard state
4520
4521 // try to open OS/2 keyboard driver
4522 if (!DosOpen("KBD$",
4523 &hKbd, &ulActionTaken,
4524 0, // cbFile
4525 FILE_NORMAL,
4526 OPEN_ACTION_FAIL_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS,
4527 OPEN_SHARE_DENYNONE | OPEN_ACCESS_READWRITE,
4528 NULL))
4529 {
4530 SHIFTSTATE ShiftState;
4531 ULONG DataLen = sizeof(SHIFTSTATE);
4532
4533 memset(&ShiftState, '\0', DataLen);
4534 DosDevIOCtl(hKbd, IOCTL_KEYBOARD, KBD_GETSHIFTSTATE,
4535 NULL, 0L, NULL,
4536 &ShiftState, DataLen, &DataLen);
4537
4538 if (fState)
4539 ShiftState.fsState |= 0x0020; // turn NumLock on
4540 else
4541 ShiftState.fsState &= 0xFFDF; // turn NumLock off
4542
4543 DosDevIOCtl(hKbd, IOCTL_KEYBOARD, KBD_SETSHIFTSTATE,
4544 &ShiftState, DataLen, &DataLen,
4545 NULL, 0L, NULL);
4546 // now close OS/2 keyboard driver
4547 DosClose(hKbd);
4548 }
4549 return;
4550}
4551
4552/*
4553 *@@category: Helpers\PM helpers\Workplace Shell\WPS class list
4554 */
4555
4556/* ******************************************************************
4557 *
4558 * WPS Class List helpers
4559 *
4560 ********************************************************************/
4561
4562/*
4563 *@@ winhQueryWPSClassList:
4564 * this returns the WPS class list in a newly
4565 * allocated buffer. This is just a shortcut to
4566 * the usual double WinEnumObjectClasses call.
4567 *
4568 * The return value is actually of the POBJCLASS type,
4569 * so you better cast this manually. We declare this
4570 * this as PBYTE though because POBJCLASS requires
4571 * INCL_WINWORKPLACE.
4572 * See WinEnumObjectClasses() for details.
4573 *
4574 * Returns NULL on error. Use free()
4575 * to free the return value.
4576 *
4577 *@@added V0.9.0 [umoeller]
4578 */
4579
4580PBYTE winhQueryWPSClassList(VOID)
4581{
4582 ULONG ulSize;
4583 POBJCLASS pObjClass = 0;
4584
4585 // get WPS class list size
4586 if (WinEnumObjectClasses(NULL, &ulSize))
4587 {
4588 // allocate buffer
4589 pObjClass = (POBJCLASS)malloc(ulSize+1);
4590 // and load the classes into it
4591 WinEnumObjectClasses(pObjClass, &ulSize);
4592 }
4593
4594 return ((PBYTE)pObjClass);
4595}
4596
4597/*
4598 *@@ winhQueryWPSClass:
4599 * this returns the POBJCLASS item if pszClass is registered
4600 * with the WPS or NULL if the class could not be found.
4601 *
4602 * The return value is actually of the POBJCLASS type,
4603 * so you better cast this manually. We declare this
4604 * this as PBYTE though because POBJCLASS requires
4605 * INCL_WINWORKPLACE.
4606 *
4607 * This takes as input the return value of winhQueryWPSClassList,
4608 * which you must call first.
4609 *
4610 * <B>Usage:</B>
4611 + PBYTE pClassList = winhQueryWPSClassList(),
4612 + pWPFolder;
4613 + if (pClassList)
4614 + {
4615 + if (pWPFolder = winhQueryWPSClass(pClassList, "WPFolder"))
4616 + ...
4617 + free(pClassList);
4618 + }
4619 *
4620 *@@added V0.9.0 [umoeller]
4621 */
4622
4623PBYTE winhQueryWPSClass(PBYTE pObjClass, // in: buffer returned by
4624 // winhQueryWPSClassList
4625 const char *pszClass) // in: class name to query
4626{
4627 PBYTE pbReturn = 0;
4628
4629 POBJCLASS pocThis = (POBJCLASS)pObjClass;
4630 // now go thru the WPS class list
4631 while (pocThis)
4632 {
4633 if (strcmp(pocThis->pszClassName, pszClass) == 0)
4634 {
4635 pbReturn = (PBYTE)pocThis;
4636 break;
4637 }
4638 // next class
4639 pocThis = pocThis->pNext;
4640 } // end while (pocThis)
4641
4642 return (pbReturn);
4643}
4644
4645/*
4646 *@@ winhRegisterClass:
4647 * this works just like WinRegisterObjectClass,
4648 * except that it returns a more meaningful
4649 * error code than just FALSE in case registering
4650 * fails.
4651 *
4652 * This returns NO_ERROR if the class was successfully
4653 * registered (WinRegisterObjectClass returned TRUE).
4654 *
4655 * Otherwise, we do a DosLoadModule if maybe the DLL
4656 * couldn't be loaded in the first place. If DosLoadModule
4657 * did not return NO_ERROR, this function returns that
4658 * return code, which can be:
4659 *
4660 * -- 2 ERROR_FILE_NOT_FOUND: pcszModule does not exist
4661 * -- 2 ERROR_FILE_NOT_FOUND
4662 * -- 3 ERROR_PATH_NOT_FOUND
4663 * -- 4 ERROR_TOO_MANY_OPEN_FILES
4664 * -- 5 ERROR_ACCESS_DENIED
4665 * -- 8 ERROR_NOT_ENOUGH_MEMORY
4666 * -- 11 ERROR_BAD_FORMAT
4667 * -- 26 ERROR_NOT_DOS_DISK (unknown media type)
4668 * -- 32 ERROR_SHARING_VIOLATION
4669 * -- 33 ERROR_LOCK_VIOLATION
4670 * -- 36 ERROR_SHARING_BUFFER_EXCEEDED
4671 * -- 95 ERROR_INTERRUPT (interrupted system call)
4672 * -- 108 ERROR_DRIVE_LOCKED (by another process)
4673 * -- 123 ERROR_INVALID_NAME (illegal character or FS name not valid)
4674 * -- 127 ERROR_PROC_NOT_FOUND (DosQueryProcAddr error)
4675 * -- 180 ERROR_INVALID_SEGMENT_NUMBER
4676 * -- 182 ERROR_INVALID_ORDINAL
4677 * -- 190 ERROR_INVALID_MODULETYPE (probably an application)
4678 * -- 191 ERROR_INVALID_EXE_SIGNATURE (probably not LX DLL)
4679 * -- 192 ERROR_EXE_MARKED_INVALID (by linker)
4680 * -- 194 ERROR_ITERATED_DATA_EXCEEDS_64K (in a DLL segment)
4681 * -- 195 ERROR_INVALID_MINALLOCSIZE
4682 * -- 196 ERROR_DYNLINK_FROM_INVALID_RING
4683 * -- 198 ERROR_INVALID_SEGDPL
4684 * -- 199 ERROR_AUTODATASEG_EXCEEDS_64K
4685 * -- 201 ERROR_RELOCSRC_CHAIN_EXCEEDS_SEGLIMIT
4686 * -- 206 ERROR_FILENAME_EXCED_RANGE (not matching 8+3 spec)
4687 * -- 295 ERROR_INIT_ROUTINE_FAILED (DLL init routine failed)
4688 *
4689 * In all these cases, pszBuf may contain a meaningful
4690 * error message from DosLoadModule, especially if an import
4691 * could not be resolved.
4692 *
4693 * Still worse, if DosLoadModule returned NO_ERROR, we
4694 * probably have some SOM internal error. A probable
4695 * reason is that the parent class of pcszClassName
4696 * is not installed, but that's WPS/SOM internal
4697 * and cannot be queried from outside the WPS context.
4698 *
4699 * In that case, ERROR_OPEN_FAILED (110) is returned.
4700 * That one sounded good to me. ;-)
4701 */
4702
4703APIRET winhRegisterClass(const char* pcszClassName, // in: e.g. "XFolder"
4704 const char* pcszModule, // in: e.g. "C:\XFOLDER\XFLDR.DLL"
4705 PSZ pszBuf, // out: error message from DosLoadModule
4706 ULONG cbBuf) // in: sizeof(*pszBuf), passed to DosLoadModule
4707{
4708 APIRET arc = NO_ERROR;
4709
4710 if (!WinRegisterObjectClass((PSZ)pcszClassName, (PSZ)pcszModule))
4711 {
4712 // failed: do more error checking then, try DosLoadModule
4713 HMODULE hmod = NULLHANDLE;
4714 arc = DosLoadModule(pszBuf, cbBuf,
4715 (PSZ)pcszModule,
4716 &hmod);
4717 if (arc == NO_ERROR)
4718 {
4719 // DosLoadModule succeeded:
4720 // some SOM error then
4721 DosFreeModule(hmod);
4722 arc = ERROR_OPEN_FAILED;
4723 }
4724 }
4725 // else: ulrc still 0 (== no error)
4726
4727 return (arc);
4728}
4729
4730/*
4731 *@@ winhIsClassRegistered:
4732 * quick one-shot function which checks if
4733 * a class is currently registered. Calls
4734 * winhQueryWPSClassList and winhQueryWPSClass
4735 * in turn.
4736 *
4737 *@@added V0.9.2 (2000-02-26) [umoeller]
4738 */
4739
4740BOOL winhIsClassRegistered(const char *pcszClass)
4741{
4742 BOOL brc = FALSE;
4743 PBYTE pClassList = winhQueryWPSClassList();
4744 if (pClassList)
4745 {
4746 if (winhQueryWPSClass(pClassList, pcszClass))
4747 brc = TRUE;
4748 free(pClassList);
4749 }
4750
4751 return (brc);
4752}
4753
4754/*
4755 *@@category: Helpers\PM helpers\Workplace Shell
4756 */
4757
4758/*
4759 *@@ winhResetWPS:
4760 * restarts the WPS using PrfReset. Returns
4761 * one of the following:
4762 *
4763 * -- 0: no error.
4764 * -- 1: PrfReset failed.
4765 * -- 2 or 4: PrfQueryProfile failed.
4766 * -- 3: malloc() failed.
4767 *
4768 *@@added V0.9.4 (2000-07-01) [umoeller]
4769 */
4770
4771ULONG winhResetWPS(HAB hab)
4772{
4773 ULONG ulrc = 0;
4774 // find out current profile names
4775 PRFPROFILE Profiles;
4776 Profiles.cchUserName = Profiles.cchSysName = 0;
4777 // first query their file name lengths
4778 if (PrfQueryProfile(hab, &Profiles))
4779 {
4780 // allocate memory for filenames
4781 Profiles.pszUserName = (PSZ)malloc(Profiles.cchUserName);
4782 Profiles.pszSysName = (PSZ)malloc(Profiles.cchSysName);
4783
4784 if (Profiles.pszSysName)
4785 {
4786 // get filenames
4787 if (PrfQueryProfile(hab, &Profiles))
4788 {
4789
4790 // "change" INIs to these filenames:
4791 // THIS WILL RESET THE WPS
4792 if (PrfReset(hab, &Profiles) == FALSE)
4793 ulrc = 1;
4794 free(Profiles.pszSysName);
4795 free(Profiles.pszUserName);
4796 }
4797 else
4798 ulrc = 2;
4799 }
4800 else
4801 ulrc = 3;
4802 }
4803 else
4804 ulrc = 4;
4805
4806 return (ulrc);
4807}
Note: See TracBrowser for help on using the repository browser.