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

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

misc updates

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 171.6 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 *@@ winhLboxFindItemFromHandle:
1495 * finds the list box item with the specified
1496 * handle.
1497 *
1498 * Of course this only makes sense if each item
1499 * has a unique handle indeed.
1500 *
1501 * Returns the index of the item found or -1.
1502 *
1503 *@@added V0.9.12 (2001-05-18) [umoeller]
1504 */
1505
1506ULONG winhLboxFindItemFromHandle(HWND hwndListBox,
1507 ULONG ulHandle)
1508{
1509 LONG cItems = WinQueryLboxCount(hwndListBox);
1510 if (cItems)
1511 {
1512 ULONG ul;
1513 for (ul = 0;
1514 ul < cItems;
1515 ul++)
1516 {
1517 if (ulHandle == winhQueryLboxItemHandle(hwndListBox,
1518 ul))
1519 return (ul);
1520 }
1521 }
1522
1523 return (-1);
1524}
1525
1526/*
1527 *@@category: Helpers\PM helpers\Scroll bar helpers
1528 */
1529
1530/* ******************************************************************
1531 *
1532 * Scroll bar helpers
1533 *
1534 ********************************************************************/
1535
1536/*
1537 *@@ winhUpdateScrollBar:
1538 * updates the given scroll bar according to the given
1539 * values. This updates the scroll bar's thumb size,
1540 * extension, and position, all in one shot.
1541 *
1542 * This function usually gets called when the window is
1543 * created and later when the window is resized.
1544 *
1545 * This simplifies the typical functionality of a scroll
1546 * bar in a client window which is to be scrolled. I am
1547 * wondering why IBM never included such a function, since
1548 * it is so damn basic and still writing it cost me a whole
1549 * day.
1550 *
1551 * Terminology:
1552 *
1553 * -- "window": the actual window with scroll bars which displays
1554 * a subrectangle of the available data. With a typical PM
1555 * application, this will be your client window.
1556 *
1557 * The width or height of this must be passed in ulWinPels.
1558 *
1559 * -- "viewport": the entire data to be displayed, of which the
1560 * "window" can only display a subrectangle, if the viewport
1561 * is larger than the window.
1562 *
1563 * The width or height of this must be passed in ulViewportPels.
1564 * This can be smaller than ulWinPels (if the window is larger
1565 * than the data) or the same or larger than ulWinPels
1566 * (if the window is too small to show all the data).
1567 *
1568 * -- "window offset": the offset of the current window within
1569 * the viewport.
1570 *
1571 * For horizontal scroll bars, this is the X coordinate,
1572 * counting from the left of the window (0 means leftmost).
1573 *
1574 * For vertical scroll bars, this is counted from the _top_
1575 * of the viewport (0 means topmost, as opposed to OS/2
1576 * window coordinates!). This is because for vertical scroll
1577 * bars controls, higher values move the thumb _down_. Yes
1578 * indeed, this conflicts with PM's coordinate system.
1579 *
1580 * The window offset is therefore always positive.
1581 *
1582 * The scroll bar gets disabled if the entire viewport is visible,
1583 * that is, if ulViewportPels <= ulWinPels. In that case
1584 * FALSE is returned. If (fAutoHide == TRUE), the scroll
1585 * bar is not only disabled, but also hidden from the display.
1586 * In that case, you will need to reformat your output because
1587 * your viewport becomes larger without the scroll bar.
1588 *
1589 * This function will set the range of the scroll bar to 0 up
1590 * to a value depending on the viewport size. For vertical scroll
1591 * bars, 0 means topmost (which is kinda sick with the OS/2
1592 * coordinate system), for horizontal scroll bars, 0 means leftmost.
1593 *
1594 * The maximum value of the scroll bar will be
1595 *
1596 + (ulViewportPels - ulWinPels) / usScrollUnitPels
1597 *
1598 * The thumb size of the scroll bar will also be adjusted
1599 * based on the viewport and window size, as it should be.
1600 *
1601 *@@added V0.9.1 (2000-02-14) [umoeller]
1602 *@@changed V0.9.3 (2000-04-30) [umoeller]: fixed pels/unit confusion
1603 *@@changed V0.9.3 (2000-05-08) [umoeller]: now handling scroll units automatically
1604 */
1605
1606BOOL winhUpdateScrollBar(HWND hwndScrollBar, // in: scroll bar (vertical or horizontal)
1607 ULONG ulWinPels, // in: vertical or horizontal dimension of
1608 // visible window part (in pixels),
1609 // excluding the scroll bar!
1610 ULONG ulViewportPels, // in: dimension of total data part, of
1611 // which ulWinPels is a sub-dimension
1612 // (in pixels);
1613 // if <= ulWinPels, the scrollbar will be
1614 // disabled
1615 ULONG ulCurPelsOfs, // in: current offset of visible part
1616 // (in pixels)
1617 BOOL fAutoHide) // in: hide scroll bar if disabled
1618{
1619 BOOL brc = FALSE;
1620
1621 // _Pmpf(("Entering winhUpdateScrollBar"));
1622
1623 // for large viewports, adjust scroll bar units
1624 USHORT usScrollUnitPels = 1;
1625 if (ulViewportPels > 10000)
1626 usScrollUnitPels = 100;
1627
1628 if (ulViewportPels > ulWinPels)
1629 {
1630 // scrollbar needed:
1631 USHORT usThumbDivisorUnits = usScrollUnitPels;
1632 USHORT lMaxAllowedUnitOfs;
1633 // _Pmpf(("winhUpdateScrollBar: ulViewportPels > ulWinPels, enabling scroller"));
1634 // divisor for thumb size (below)
1635 if (ulViewportPels > 10000)
1636 // for very large viewports, we need to
1637 // raise the divisor, because we only
1638 // have a USHORT
1639 usThumbDivisorUnits = usScrollUnitPels * 100;
1640
1641 // viewport is larger than window:
1642 WinEnableWindow(hwndScrollBar, TRUE);
1643 if (fAutoHide)
1644 WinShowWindow(hwndScrollBar, TRUE);
1645
1646 // calculate limit
1647 lMaxAllowedUnitOfs = ((ulViewportPels - ulWinPels + usScrollUnitPels)
1648 // scroll unit is 10
1649 / usScrollUnitPels);
1650
1651 // _Pmpf((" usCurUnitOfs: %d", ulCurUnitOfs));
1652 // _Pmpf((" usMaxUnits: %d", lMaxAllowedUnitOfs));
1653
1654 // set thumb position and limit
1655 WinSendMsg(hwndScrollBar,
1656 SBM_SETSCROLLBAR,
1657 (MPARAM)(ulCurPelsOfs), // / usThumbDivisorUnits), // position: 0 means top
1658 MPFROM2SHORT(0, // minimum
1659 lMaxAllowedUnitOfs)); // maximum
1660
1661 // set thumb size based on ulWinPels and
1662 // ulViewportPels
1663 WinSendMsg(hwndScrollBar,
1664 SBM_SETTHUMBSIZE,
1665 MPFROM2SHORT( ulWinPels / usThumbDivisorUnits, // visible
1666 ulViewportPels / usThumbDivisorUnits), // total
1667 0);
1668 brc = TRUE;
1669 }
1670 else
1671 {
1672 // _Pmpf(("winhUpdateScrollBar: ulViewportPels <= ulWinPels"));
1673 // entire viewport is visible:
1674 WinEnableWindow(hwndScrollBar, FALSE);
1675 if (fAutoHide)
1676 WinShowWindow(hwndScrollBar, FALSE);
1677 }
1678
1679 // _Pmpf(("End of winhUpdateScrollBar"));
1680
1681 return (brc);
1682}
1683
1684/*
1685 *@@ winhHandleScrollMsg:
1686 * this helper handles a WM_VSCROLL or WM_HSCROLL
1687 * message posted to a client window when the user
1688 * has worked on a client scroll bar. Calling this
1689 * function is ALL you need to do to handle those
1690 * two messages.
1691 *
1692 * This is most useful in conjunction with winhUpdateScrollBar.
1693 * See that function for the terminology also.
1694 *
1695 * This function calculates the new scrollbar position
1696 * (from the mp2 value, which can be line up/down,
1697 * page up/down, or slider track) and calls WinScrollWindow
1698 * accordingly. The window part which became invalid
1699 * because of the scrolling is automatically invalidated
1700 * (using WinInvalidateRect), so expect a WM_PAINT after
1701 * calling this function.
1702 *
1703 * This function assumes that the scrollbar operates
1704 * on values starting from zero. The maximum value
1705 * of the scroll bar is:
1706 *
1707 + ulViewportPels - (prcl2Scroll->yTop - prcl2Scroll->yBottom)
1708 *
1709 * This function also automatically changes the scroll bar
1710 * units, should you have a viewport size which doesn't fit
1711 * into the SHORT's that the scroll bar uses internally. As
1712 * a result, this function handles a the complete range of
1713 * a ULONG for the viewport.
1714 *
1715 * Replace "bottom" and "top" with "right" and "left" for
1716 * horizontal scrollbars in the above formula.
1717 *
1718 *@@added V0.9.1 (2000-02-13) [umoeller]
1719 *@@changed V0.9.3 (2000-04-30) [umoeller]: changed prototype, fixed pels/unit confusion
1720 *@@changed V0.9.3 (2000-05-08) [umoeller]: now handling scroll units automatically
1721 *@@changed V0.9.7 (2001-01-17) [umoeller]: changed PLONG to PULONG
1722 */
1723
1724BOOL winhHandleScrollMsg(HWND hwnd2Scroll, // in: client window to scroll
1725 HWND hwndScrollBar, // in: vertical or horizontal scroll bar window
1726 PULONG pulCurPelsOfs, // in/out: current viewport offset;
1727 // this is updated with the proper scroll units
1728 PRECTL prcl2Scroll, // in: hwnd2Scroll rectangle to scroll
1729 // (in window coordinates);
1730 // this is passed to WinScrollWindow,
1731 // which considers this inclusive!
1732 LONG ulViewportPels, // in: total viewport dimension,
1733 // into which *pulCurPelsOfs is an offset
1734 USHORT usLineStepPels, // in: pixels to scroll line-wise
1735 // (scroll bar buttons pressed)
1736 ULONG msg, // in: either WM_VSCROLL or WM_HSCROLL
1737 MPARAM mp2) // in: complete mp2 of WM_VSCROLL/WM_HSCROLL;
1738 // this has two SHORT's (usPos and usCmd),
1739 // see PMREF for details
1740{
1741 ULONG ulOldPelsOfs = *pulCurPelsOfs;
1742 USHORT usPosUnits = SHORT1FROMMP(mp2), // in scroll units
1743 usCmd = SHORT2FROMMP(mp2);
1744 LONG lMaxAllowedUnitOfs;
1745 ULONG ulWinPels;
1746
1747 // for large viewports, adjust scroll bar units
1748 USHORT usScrollUnitPels = 1;
1749 if (ulViewportPels > 10000)
1750 usScrollUnitPels = 100;
1751
1752 // calculate window size (vertical or horizontal)
1753 if (msg == WM_VSCROLL)
1754 ulWinPels = (prcl2Scroll->yTop - prcl2Scroll->yBottom);
1755 else
1756 ulWinPels = (prcl2Scroll->xRight - prcl2Scroll->xLeft);
1757
1758 lMaxAllowedUnitOfs = ((LONG)ulViewportPels - ulWinPels) / usScrollUnitPels;
1759
1760 // _Pmpf(("Entering winhHandleScrollMsg"));
1761
1762 switch (usCmd)
1763 {
1764 case SB_LINEUP:
1765 if (*pulCurPelsOfs > usLineStepPels)
1766 *pulCurPelsOfs -= usLineStepPels; // * usScrollUnitPels);
1767 else
1768 *pulCurPelsOfs = 0;
1769 break;
1770
1771 case SB_LINEDOWN:
1772 *pulCurPelsOfs += usLineStepPels; // * usScrollUnitPels);
1773 break;
1774
1775 case SB_PAGEUP:
1776 if (*pulCurPelsOfs > ulWinPels)
1777 *pulCurPelsOfs -= ulWinPels; // convert to units
1778 else
1779 *pulCurPelsOfs = 0;
1780 break;
1781
1782 case SB_PAGEDOWN:
1783 *pulCurPelsOfs += ulWinPels; // convert to units
1784 break;
1785
1786 case SB_SLIDERTRACK:
1787 *pulCurPelsOfs = (usPosUnits * usScrollUnitPels);
1788 // _Pmpf((" SB_SLIDERTRACK: usUnits = %d", usPosUnits));
1789 break;
1790
1791 case SB_SLIDERPOSITION:
1792 *pulCurPelsOfs = (usPosUnits * usScrollUnitPels);
1793 break;
1794 }
1795
1796 // are we close to the lower limit?
1797 /* if (*plCurUnitOfs < usLineStepUnits) // usScrollUnit)
1798 *plCurUnitOfs = 0;
1799 // are we close to the upper limit?
1800 else if (*plCurUnitOfs + usLineStepUnits > lMaxUnitOfs)
1801 {
1802 _Pmpf((" !!! limiting: %d to %d", *plCurUnitOfs, lMaxUnitOfs));
1803 *plCurUnitOfs = lMaxUnitOfs;
1804 } */
1805
1806 /* if (*plCurPelsOfs < 0)
1807 *plCurPelsOfs = 0; */ // checked above
1808 if (*pulCurPelsOfs > (lMaxAllowedUnitOfs * usScrollUnitPels))
1809 {
1810 *pulCurPelsOfs = (lMaxAllowedUnitOfs * usScrollUnitPels);
1811 }
1812 if ( (*pulCurPelsOfs != ulOldPelsOfs)
1813 || (*pulCurPelsOfs == 0)
1814 || (*pulCurPelsOfs == (lMaxAllowedUnitOfs * usScrollUnitPels))
1815 )
1816 {
1817 RECTL rcl2Scroll,
1818 rcl2Update;
1819
1820 // changed:
1821 WinSendMsg(hwndScrollBar,
1822 SBM_SETPOS,
1823 (MPARAM)(*pulCurPelsOfs / usScrollUnitPels), // / usScrollUnit),
1824 0);
1825 // scroll window rectangle:
1826 rcl2Scroll.xLeft = prcl2Scroll->xLeft;
1827 rcl2Scroll.xRight = prcl2Scroll->xRight;
1828 rcl2Scroll.yBottom = prcl2Scroll->yBottom;
1829 rcl2Scroll.yTop = prcl2Scroll->yTop;
1830
1831 if (msg == WM_VSCROLL)
1832 WinScrollWindow(hwnd2Scroll,
1833 0,
1834 (*pulCurPelsOfs - ulOldPelsOfs) // scroll units changed
1835 , // * usScrollUnitPels, // convert to pels
1836 &rcl2Scroll, // rcl to scroll
1837 prcl2Scroll, // clipping rect
1838 NULLHANDLE, // no region
1839 &rcl2Update,
1840 0);
1841 else
1842 WinScrollWindow(hwnd2Scroll,
1843 -(LONG)(*pulCurPelsOfs - ulOldPelsOfs) // scroll units changed
1844 , // * usScrollUnitPels,
1845 0,
1846 &rcl2Scroll, // rcl to scroll
1847 prcl2Scroll, // clipping rect
1848 NULLHANDLE, // no region
1849 &rcl2Update,
1850 0);
1851
1852 // WinScrollWindow has stored the invalid window
1853 // rectangle which needs to be repainted in rcl2Update:
1854 WinInvalidateRect(hwnd2Scroll, &rcl2Update, FALSE);
1855 }
1856
1857 // _Pmpf(("End of winhHandleScrollMsg"));
1858
1859 return (TRUE);
1860}
1861
1862/*
1863 *@@ winhProcessScrollChars:
1864 * helper for processing WM_CHAR messages for
1865 * client windows with scroll bars.
1866 *
1867 * If your window has scroll bars, you normally
1868 * need to process a number of keystrokes to be
1869 * able to scroll the window contents. This is
1870 * tiresome to code, so here is a helper.
1871 *
1872 * When receiving WM_CHAR, call this function.
1873 * If this returns TRUE, the keystroke has been
1874 * a scroll keystroke, and the window has been
1875 * updated (by sending WM_VSCROLL or WM_HSCROLL
1876 * to hwndClient). Otherwise, you should process
1877 * the keystroke as usual because it's not a
1878 * scroll keystroke.
1879 *
1880 * The following keystrokes are processed here:
1881 *
1882 * -- "cursor up, down, right, left": scroll one
1883 * line in the proper direction.
1884 * -- "page up, down": scroll one page up or down.
1885 * -- "Home": scroll leftmost.
1886 * -- "Ctrl+ Home": scroll topmost.
1887 * -- "End": scroll rightmost.
1888 * -- "Ctrl+ End": scroll bottommost.
1889 * -- "Ctrl + page up, down": scroll one screen left or right.
1890 *
1891 * This is CUA behavior.
1892 *
1893 * Returns TRUE if the message has been
1894 * processed.
1895 *
1896 *@@added V0.9.3 (2000-04-29) [umoeller]
1897 *@@changed V0.9.9 (2001-02-01) [lafaix]: Ctrl+PgUp/Dn now do one screen left/right
1898 */
1899
1900BOOL winhProcessScrollChars(HWND hwndClient, // in: client window
1901 HWND hwndVScroll, // in: vertical scroll bar
1902 HWND hwndHScroll, // in: horizontal scroll bar
1903 MPARAM mp1, // in: WM_CHAR mp1
1904 MPARAM mp2, // in: WM_CHAR mp2
1905 ULONG ulVertMax, // in: maximum viewport cy
1906 ULONG ulHorzMax) // in: maximum viewport cx
1907{
1908 BOOL fProcessed = FALSE;
1909 USHORT usFlags = SHORT1FROMMP(mp1);
1910 // USHORT usch = SHORT1FROMMP(mp2);
1911 USHORT usvk = SHORT2FROMMP(mp2);
1912
1913 // _Pmpf(("Entering winhProcessScrollChars"));
1914
1915 if (usFlags & KC_VIRTUALKEY)
1916 {
1917 ULONG ulMsg = 0;
1918 SHORT sPos = 0;
1919 SHORT usCmd = 0;
1920 fProcessed = TRUE;
1921
1922 switch (usvk)
1923 {
1924 case VK_UP:
1925 ulMsg = WM_VSCROLL;
1926 usCmd = SB_LINEUP;
1927 break;
1928
1929 case VK_DOWN:
1930 ulMsg = WM_VSCROLL;
1931 usCmd = SB_LINEDOWN;
1932 break;
1933
1934 case VK_RIGHT:
1935 ulMsg = WM_HSCROLL;
1936 usCmd = SB_LINERIGHT;
1937 break;
1938
1939 case VK_LEFT:
1940 ulMsg = WM_HSCROLL;
1941 usCmd = SB_LINELEFT;
1942 break;
1943
1944 case VK_PAGEUP:
1945 if (usFlags & KC_CTRL)
1946 ulMsg = WM_HSCROLL;
1947 else
1948 ulMsg = WM_VSCROLL;
1949 usCmd = SB_PAGEUP;
1950 break;
1951
1952 case VK_PAGEDOWN:
1953 if (usFlags & KC_CTRL)
1954 ulMsg = WM_HSCROLL;
1955 else
1956 ulMsg = WM_VSCROLL;
1957 usCmd = SB_PAGEDOWN;
1958 break;
1959
1960 case VK_HOME:
1961 if (usFlags & KC_CTRL)
1962 // vertical:
1963 ulMsg = WM_VSCROLL;
1964 else
1965 ulMsg = WM_HSCROLL;
1966
1967 sPos = 0;
1968 usCmd = SB_SLIDERPOSITION;
1969 break;
1970
1971 case VK_END:
1972 if (usFlags & KC_CTRL)
1973 {
1974 // vertical:
1975 ulMsg = WM_VSCROLL;
1976 sPos = ulVertMax;
1977 }
1978 else
1979 {
1980 ulMsg = WM_HSCROLL;
1981 sPos = ulHorzMax;
1982 }
1983
1984 usCmd = SB_SLIDERPOSITION;
1985 break;
1986
1987 default:
1988 // other:
1989 fProcessed = FALSE;
1990 }
1991
1992 if ( ((usFlags & KC_KEYUP) == 0)
1993 && (ulMsg)
1994 )
1995 {
1996 HWND hwndScrollBar = ((ulMsg == WM_VSCROLL)
1997 ? hwndVScroll
1998 : hwndHScroll);
1999 if (WinIsWindowEnabled(hwndScrollBar))
2000 {
2001 USHORT usID = WinQueryWindowUShort(hwndScrollBar,
2002 QWS_ID);
2003 WinSendMsg(hwndClient,
2004 ulMsg,
2005 MPFROMSHORT(usID),
2006 MPFROM2SHORT(sPos,
2007 usCmd));
2008 }
2009 }
2010 }
2011
2012 // _Pmpf(("End of winhProcessScrollChars"));
2013
2014 return (fProcessed);
2015}
2016
2017/*
2018 *@@category: Helpers\PM helpers\Window positioning
2019 */
2020
2021/* ******************************************************************
2022 *
2023 * Window positioning helpers
2024 *
2025 ********************************************************************/
2026
2027/*
2028 *@@ winhSaveWindowPos:
2029 * saves the position of a certain window. As opposed
2030 * to the barely documented WinStoreWindowPos API, this
2031 * one only saves one regular SWP structure for the given
2032 * window, as returned by WinQueryWindowPos for hwnd.
2033 *
2034 * If the window is currently maximized or minimized,
2035 * we won't store the current window size and position
2036 * (which wouldn't make much sense), but retrieve the
2037 * "restored" window position from the window words
2038 * instead.
2039 *
2040 * The window should still be visible on the screen
2041 * when calling this function. Do not call it in WM_DESTROY,
2042 * because then the SWP data is no longer valid.
2043 *
2044 * This returns TRUE if saving was successful.
2045 *
2046 *@@changed V0.9.1 (99-12-19) [umoeller]: added minimize/maximize support
2047 */
2048
2049BOOL winhSaveWindowPos(HWND hwnd, // in: window to save
2050 HINI hIni, // in: INI file (or HINI_USER/SYSTEM)
2051 const char *pcszApp, // in: INI application name
2052 const char *pcszKey) // in: INI key name
2053{
2054 BOOL brc = FALSE;
2055 SWP swp;
2056 if (WinQueryWindowPos(hwnd, &swp))
2057 {
2058 if (swp.fl & (SWP_MAXIMIZE | SWP_MINIMIZE))
2059 {
2060 // window currently maximized or minimized:
2061 // retrieve "restore" position from window words
2062 swp.x = WinQueryWindowUShort(hwnd, QWS_XRESTORE);
2063 swp.y = WinQueryWindowUShort(hwnd, QWS_YRESTORE);
2064 swp.cx = WinQueryWindowUShort(hwnd, QWS_CXRESTORE);
2065 swp.cy = WinQueryWindowUShort(hwnd, QWS_CYRESTORE);
2066 }
2067
2068 brc = PrfWriteProfileData(hIni, (PSZ)pcszApp, (PSZ)pcszKey, &swp, sizeof(swp));
2069 }
2070 return (brc);
2071}
2072
2073/*
2074 *@@ winhRestoreWindowPos:
2075 * this will retrieve a window position which was
2076 * previously stored using winhSaveWindowPos.
2077 *
2078 * The window should not be visible to avoid flickering.
2079 * "fl" must contain the SWP_flags as in WinSetWindowPos.
2080 *
2081 * Note that only the following may be used:
2082 * -- SWP_MOVE reposition the window
2083 * -- SWP_SIZE also resize the window to
2084 * the stored position; this might
2085 * lead to problems with different
2086 * video resolutions, so be careful.
2087 * -- SWP_SHOW make window visible too
2088 * -- SWP_NOREDRAW changes are not redrawn
2089 * -- SWP_NOADJUST do not send a WM_ADJUSTWINDOWPOS message
2090 * before moving or sizing
2091 * -- SWP_ACTIVATE activate window (make topmost)
2092 * -- SWP_DEACTIVATE deactivate window (make bottommost)
2093 *
2094 * Do not specify any other SWP_* flags.
2095 *
2096 * If SWP_SIZE is not set, the window will be moved only.
2097 *
2098 * This returns TRUE if INI data was found.
2099 *
2100 * This function automatically checks for whether the
2101 * window would be positioned outside the visible screen
2102 * area and will adjust coordinates accordingly. This can
2103 * happen when changing video resolutions.
2104 *
2105 *@@changed V0.9.7 (2000-12-20) [umoeller]: fixed invalid params if INI key not found
2106 */
2107
2108BOOL winhRestoreWindowPos(HWND hwnd, // in: window to restore
2109 HINI hIni, // in: INI file (or HINI_USER/SYSTEM)
2110 const char *pcszApp, // in: INI application name
2111 const char *pcszKey, // in: INI key name
2112 ULONG fl) // in: "fl" parameter for WinSetWindowPos
2113{
2114 BOOL brc = FALSE;
2115 SWP swp;
2116 ULONG cbswp = sizeof(swp);
2117 ULONG fl2 = (fl & ~SWP_ZORDER);
2118
2119 if (PrfQueryProfileData(hIni, (PSZ)pcszApp, (PSZ)pcszKey, &swp, &cbswp))
2120 {
2121 ULONG ulScreenCX = WinQuerySysValue(HWND_DESKTOP, SV_CXSCREEN);
2122 ULONG ulScreenCY = WinQuerySysValue(HWND_DESKTOP, SV_CYSCREEN);
2123
2124 brc = TRUE;
2125
2126 if ((fl & SWP_SIZE) == 0)
2127 {
2128 // if no resize, we need to get the current position
2129 SWP swpNow;
2130 brc = WinQueryWindowPos(hwnd, &swpNow);
2131 swp.cx = swpNow.cx;
2132 swp.cy = swpNow.cy;
2133 }
2134
2135 if (brc)
2136 {
2137 // check for full visibility
2138 if ( (swp.x + swp.cx) > ulScreenCX)
2139 swp.x = ulScreenCX - swp.cx;
2140 if ( (swp.y + swp.cy) > ulScreenCY)
2141 swp.y = ulScreenCY - swp.cy;
2142 }
2143
2144 brc = TRUE;
2145
2146 }
2147 else
2148 {
2149 // window pos not found in INI: unset SWP_MOVE etc.
2150 WinQueryWindowPos(hwnd, &swp);
2151 fl2 &= ~(SWP_MOVE | SWP_SIZE);
2152 }
2153
2154 WinSetWindowPos(hwnd,
2155 NULLHANDLE, // insert-behind window
2156 swp.x,
2157 swp.y,
2158 swp.cx,
2159 swp.cy,
2160 fl2); // SWP_* flags
2161
2162 return (brc);
2163}
2164
2165/*
2166 *@@ winhAdjustControls:
2167 * helper function for dynamically adjusting a window's
2168 * controls when the window is resized.
2169 *
2170 * This is most useful with dialogs loaded from resources
2171 * which should be sizeable. Normally, when the dialog
2172 * frame is resized, the controls stick to their positions,
2173 * and for dialogs with many controls, programming the
2174 * changes can be tiresome.
2175 *
2176 * Enter this function. ;-) Basically, this takes a
2177 * static array of MPARAM's as input (plus one dynamic
2178 * storage area for the window positions).
2179 *
2180 * This function must get called in three contexts:
2181 * during WM_INITDLG, during WM_WINDOWPOSCHANGED, and
2182 * during WM_DESTROY, with varying parameters.
2183 *
2184 * In detail, there are four things you need to do to make
2185 * this work:
2186 *
2187 * 1) Set up a static array (as a global variable) of
2188 * MPARAM's, one for each control in your array.
2189 * Each MPARAM will have the control's ID and the
2190 * XAF* flags (winh.h) how the control shall be moved.
2191 * Use MPFROM2SHORT to easily create this. Example:
2192 *
2193 + MPARAM ampControlFlags[] =
2194 + { MPFROM2SHORT(ID_CONTROL_1, XAC_MOVEX),
2195 + MPFROM2SHORT(ID_CONTROL_2, XAC_SIZEY),
2196 + ...
2197 + }
2198 *
2199 * This can safely be declared as a global variable
2200 * because this data will only be read and never
2201 * changed by this function.
2202 *
2203 * 2) In WM_INITDLG of your dialog function, set up
2204 * an XADJUSTCTRLS structure, preferrably in your
2205 * window words (QWL_USER).
2206 *
2207 * ZERO THAT STRUCTURE (memset(&xac, 0, sizeof(XADJUSTCTRLS),
2208 * or this func will not work.
2209 *
2210 * Call this function with pswpNew == NULL and
2211 * pxac pointing to that new structure. This will
2212 * query the positions of all the controls listed
2213 * in the MPARAMs array and store them in the
2214 * XADJUSTCTRLS area.
2215 *
2216 * 3) Intercept WM_WINDOWPOSCHANGED:
2217 *
2218 + case WM_WINDOWPOSCHANGED:
2219 + {
2220 + // this msg is passed two SWP structs:
2221 + // one for the old, one for the new data
2222 + // (from PM docs)
2223 + PSWP pswpNew = PVOIDFROMMP(mp1);
2224 + PSWP pswpOld = pswpNew + 1;
2225 +
2226 + // resizing?
2227 + if (pswpNew->fl & SWP_SIZE)
2228 + {
2229 + PXADJUSTCTRLS pxac = ... // get it from your window words
2230 +
2231 + winhAdjustControls(hwndDlg, // dialog
2232 + ampControlFlags, // MPARAMs array
2233 + sizeof(ampControlFlags) / sizeof(MPARAM),
2234 + // items count
2235 + pswpNew, // mp1
2236 + pxac); // storage area
2237 + }
2238 + mrc = WinDefDlgProc(hwnd, msg, mp1, mp2); ...
2239 *
2240 * 4) In WM_DESTROY, call this function again with pmpFlags,
2241 * pswpNew, and pswpNew set to NULL. This will clean up the
2242 * data which has been allocated internally (pointed to from
2243 * the XADJUSTCTRLS structure).
2244 * Don't forget to free your storage for XADJUSTCTLRS
2245 * _itself_, that's the job of the caller.
2246 *
2247 * This might sound complicated, but it's a lot easier than
2248 * having to write dozens of WinSetWindowPos calls oneself.
2249 *
2250 *@@added V0.9.0 [umoeller]
2251 */
2252
2253BOOL winhAdjustControls(HWND hwndDlg, // in: dialog (req.)
2254 MPARAM *pmpFlags, // in: init flags or NULL for cleanup
2255 ULONG ulCount, // in: item count (req.)
2256 PSWP pswpNew, // in: pswpNew from WM_WINDOWPOSCHANGED or NULL for cleanup
2257 PXADJUSTCTRLS pxac) // in: adjust-controls storage area (req.)
2258{
2259 BOOL brc = FALSE;
2260 ULONG ul = 0;
2261
2262 /* if (!WinIsWindowVisible(hwndDlg))
2263 return (FALSE); */
2264
2265 if ((pmpFlags) && (pxac))
2266 {
2267 PSWP pswpThis;
2268 MPARAM *pmpThis;
2269 LONG ldcx, ldcy;
2270 ULONG cWindows = 0;
2271
2272 // setup mode:
2273 if (pxac->fInitialized == FALSE)
2274 {
2275 // first call: get all the SWP's
2276 WinQueryWindowPos(hwndDlg, &pxac->swpMain);
2277 // _Pmpf(("winhAdjustControls: queried main cx = %d, cy = %d",
2278 // pxac->swpMain.cx, pxac->swpMain.cy));
2279
2280 pxac->paswp = (PSWP)malloc(sizeof(SWP) * ulCount);
2281
2282 pswpThis = pxac->paswp;
2283 pmpThis = pmpFlags;
2284
2285 for (ul = 0;
2286 ul < ulCount;
2287 ul++)
2288 {
2289 HWND hwndThis = WinWindowFromID(hwndDlg, SHORT1FROMMP(*pmpThis));
2290 if (hwndThis)
2291 {
2292 WinQueryWindowPos(hwndThis, pswpThis);
2293 cWindows++;
2294 }
2295
2296 pswpThis++;
2297 pmpThis++;
2298 }
2299
2300 pxac->fInitialized = TRUE;
2301 // _Pmpf(("winhAdjustControls: queried %d controls", cWindows));
2302 }
2303
2304 if (pswpNew)
2305 {
2306 // compute width and height delta
2307 ldcx = (pswpNew->cx - pxac->swpMain.cx);
2308 ldcy = (pswpNew->cy - pxac->swpMain.cy);
2309
2310 // _Pmpf(("winhAdjustControls: new cx = %d, cy = %d",
2311 // pswpNew->cx, pswpNew->cy));
2312
2313 // now adjust the controls
2314 cWindows = 0;
2315 pswpThis = pxac->paswp;
2316 pmpThis = pmpFlags;
2317 for (ul = 0;
2318 ul < ulCount;
2319 ul++)
2320 {
2321 HWND hwndThis = WinWindowFromID(hwndDlg, SHORT1FROMMP(*pmpThis));
2322 if (hwndThis)
2323 {
2324 LONG x = pswpThis->x,
2325 y = pswpThis->y,
2326 cx = pswpThis->cx,
2327 cy = pswpThis->cy;
2328
2329 ULONG ulSwpFlags = 0;
2330 // get flags for this control
2331 USHORT usFlags = SHORT2FROMMP(*pmpThis);
2332
2333 if (usFlags & XAC_MOVEX)
2334 {
2335 x += ldcx;
2336 ulSwpFlags |= SWP_MOVE;
2337 }
2338 if (usFlags & XAC_MOVEY)
2339 {
2340 y += ldcy;
2341 ulSwpFlags |= SWP_MOVE;
2342 }
2343 if (usFlags & XAC_SIZEX)
2344 {
2345 cx += ldcx;
2346 ulSwpFlags |= SWP_SIZE;
2347 }
2348 if (usFlags & XAC_SIZEY)
2349 {
2350 cy += ldcy;
2351 ulSwpFlags |= SWP_SIZE;
2352 }
2353
2354 if (ulSwpFlags)
2355 {
2356 WinSetWindowPos(hwndThis,
2357 NULLHANDLE, // hwndInsertBehind
2358 x, y, cx, cy,
2359 ulSwpFlags);
2360 cWindows++;
2361 brc = TRUE;
2362 }
2363 }
2364
2365 pswpThis++;
2366 pmpThis++;
2367 }
2368
2369 // _Pmpf(("winhAdjustControls: set %d windows", cWindows));
2370 }
2371 }
2372 else
2373 {
2374 // pxac == NULL:
2375 // cleanup mode
2376 if (pxac->paswp)
2377 free(pxac->paswp);
2378 }
2379
2380 return (brc);
2381}
2382
2383/*
2384 *@@ winhCenterWindow:
2385 * centers a window within its parent window. If that's
2386 * the PM desktop, it will be centered according to the
2387 * whole screen.
2388 * For dialog boxes, use WinCenteredDlgBox as a one-shot
2389 * function.
2390 *
2391 * Note: When calling this function, the window should
2392 * not be visible to avoid flickering.
2393 * This func does not show the window either, so call
2394 * WinShowWindow afterwards.
2395 */
2396
2397void winhCenterWindow(HWND hwnd)
2398{
2399 RECTL rclParent;
2400 RECTL rclWindow;
2401
2402 WinQueryWindowRect(hwnd, &rclWindow);
2403 WinQueryWindowRect(WinQueryWindow(hwnd, QW_PARENT), &rclParent);
2404
2405 rclWindow.xLeft = (rclParent.xRight - rclWindow.xRight) / 2;
2406 rclWindow.yBottom = (rclParent.yTop - rclWindow.yTop ) / 2;
2407
2408 WinSetWindowPos(hwnd, NULLHANDLE, rclWindow.xLeft, rclWindow.yBottom,
2409 0, 0, SWP_MOVE);
2410}
2411
2412/*
2413 *@@ winhCenteredDlgBox:
2414 * just like WinDlgBox, but the dlg box is centered on the screen;
2415 * you should mark the dlg template as not visible in the dlg
2416 * editor, or display will flicker.
2417 * As opposed to winhCenterWindow, this _does_ show the window.
2418 */
2419
2420ULONG winhCenteredDlgBox(HWND hwndParent,
2421 HWND hwndOwner,
2422 PFNWP pfnDlgProc,
2423 HMODULE hmod,
2424 ULONG idDlg,
2425 PVOID pCreateParams)
2426{
2427 ULONG ulReply;
2428 HWND hwndDlg = WinLoadDlg(hwndParent,
2429 hwndOwner,
2430 pfnDlgProc,
2431 hmod,
2432 idDlg,
2433 pCreateParams);
2434 winhCenterWindow(hwndDlg);
2435 ulReply = WinProcessDlg(hwndDlg);
2436 WinDestroyWindow(hwndDlg);
2437 return (ulReply);
2438}
2439
2440/*
2441 *@@ winhFindWindowBelow:
2442 * finds the window with the same parent
2443 * which sits right below hwndFind in the
2444 * window Z-order.
2445 *
2446 *@@added V0.9.7 (2000-12-04) [umoeller]
2447 */
2448
2449HWND winhFindWindowBelow(HWND hwndFind)
2450{
2451 HWND hwnd = NULLHANDLE,
2452 hwndParent = WinQueryWindow(hwndFind, QW_PARENT);
2453
2454 if (hwndParent)
2455 {
2456 HENUM henum = WinBeginEnumWindows(hwndParent);
2457 HWND hwndThis;
2458 while (hwndThis = WinGetNextWindow(henum))
2459 {
2460 SWP swp;
2461 WinQueryWindowPos(hwndThis, &swp);
2462 if (swp.hwndInsertBehind == hwndFind)
2463 {
2464 hwnd = hwndThis;
2465 break;
2466 }
2467 }
2468 WinEndEnumWindows(henum);
2469 }
2470
2471 return (hwnd);
2472}
2473
2474/*
2475 *@@category: Helpers\PM helpers\Presentation parameters
2476 */
2477
2478/* ******************************************************************
2479 *
2480 * Presparams helpers
2481 *
2482 ********************************************************************/
2483
2484/*
2485 *@@ winhQueryWindowFont:
2486 * returns the window font presentation parameter
2487 * in a newly allocated buffer.
2488 *
2489 * Returns NULL on error. Use free()
2490 * to free the return value.
2491 *
2492 *@@added V0.9.1 (2000-02-14) [umoeller]
2493 */
2494
2495PSZ winhQueryWindowFont(HWND hwnd)
2496{
2497 CHAR szNewFont[100] = "";
2498 WinQueryPresParam(hwnd,
2499 PP_FONTNAMESIZE,
2500 0,
2501 NULL,
2502 (ULONG)sizeof(szNewFont),
2503 (PVOID)&szNewFont,
2504 QPF_NOINHERIT);
2505 if (szNewFont[0] != 0)
2506 return (strdup(szNewFont));
2507
2508 return (NULL);
2509}
2510
2511/*
2512 *@@ winhSetWindowFont:
2513 * this sets a window's font by invoking
2514 * WinSetPresParam on it.
2515 *
2516 * If (pszFont == NULL), a default font will be set
2517 * (on Warp 4, "9.WarpSans", on Warp 3, "8.Helv").
2518 *
2519 * winh.h also defines the winhSetDlgItemFont macro.
2520 *
2521 * Returns TRUE if successful or FALSE otherwise.
2522 *
2523 *@@added V0.9.0 [umoeller]
2524 */
2525
2526BOOL winhSetWindowFont(HWND hwnd,
2527 const char *pcszFont)
2528{
2529 CHAR szFont[256];
2530
2531 if (pcszFont == NULL)
2532 {
2533 if (doshIsWarp4())
2534 strhncpy0(szFont, "9.WarpSans", sizeof(szFont));
2535 else
2536 strhncpy0(szFont, "8.Helv", sizeof(szFont));
2537 }
2538 else
2539 strhncpy0(szFont, pcszFont, sizeof(szFont));
2540
2541 return (WinSetPresParam(hwnd,
2542 PP_FONTNAMESIZE,
2543 strlen(szFont)+1,
2544 szFont));
2545}
2546
2547/*
2548 *@@ winhSetControlsFont:
2549 * this sets the font for all the controls of hwndDlg
2550 * which have a control ID in the range of usIDMin to
2551 * usIDMax. "Unused" IDs (i.e. -1) will also be set.
2552 *
2553 * If (pszFont == NULL), a default font will be set
2554 * (on Warp 4, "9.WarpSans", on Warp 3, "8.Helv").
2555 *
2556 * Returns the no. of controls set.
2557 *
2558 *@@added V0.9.0 [umoeller]
2559 */
2560
2561ULONG winhSetControlsFont(HWND hwndDlg, // in: dlg to set
2562 SHORT usIDMin, // in: minimum control ID to be set (inclusive)
2563 SHORT usIDMax, // in: maximum control ID to be set (inclusive)
2564 const char *pcszFont) // in: font to use (e.g. "9.WarpSans") or NULL
2565{
2566 ULONG ulrc = 0;
2567 HENUM henum;
2568 HWND hwndItem;
2569 CHAR szFont[256];
2570 ULONG cbFont;
2571
2572 if (pcszFont == NULL)
2573 {
2574 if (doshIsWarp4())
2575 strhncpy0(szFont, "9.WarpSans", sizeof(szFont));
2576 else
2577 strhncpy0(szFont, "8.Helv", sizeof(szFont));
2578 }
2579 else
2580 strhncpy0(szFont, pcszFont, sizeof(szFont));
2581 cbFont = strlen(szFont)+1;
2582
2583 // set font for all the dialog controls
2584 henum = WinBeginEnumWindows(hwndDlg);
2585 while ((hwndItem = WinGetNextWindow(henum)))
2586 {
2587 SHORT sID = WinQueryWindowUShort(hwndItem, QWS_ID);
2588 if ( (sID == -1)
2589 || ((sID >= usIDMin) && (sID <= usIDMax))
2590 )
2591 if (WinSetPresParam(hwndItem,
2592 PP_FONTNAMESIZE,
2593 cbFont,
2594 szFont))
2595 // successful:
2596 ulrc++;
2597 }
2598 WinEndEnumWindows(henum);
2599 return (ulrc);
2600}
2601
2602/*
2603 *@@ winhStorePresParam:
2604 * this appends a new presentation parameter to an
2605 * array of presentation parameters which can be
2606 * passed to WinCreateWindow. This is preferred
2607 * over setting the presparams using WinSetPresParams,
2608 * because that call will cause a lot of messages.
2609 *
2610 * On the first call, pppp _must_ be NULL. This
2611 * will allocate memory for storing the given
2612 * data as necessary and modify *pppp to point
2613 * to the new array.
2614 *
2615 * On subsequent calls with the same pppp, memory
2616 * will be reallocated, the old data will be copied,
2617 * and the new given data will be appended.
2618 *
2619 * Use free() on your PPRESPARAMS pointer (whose
2620 * address was passed) after WinCreateWindow.
2621 *
2622 * See winhQueryPresColor for typical presparams
2623 * used in OS/2.
2624 *
2625 * Example:
2626 *
2627 + PPRESPARAMS ppp = NULL;
2628 + CHAR szFont[] = "9.WarpSans";
2629 + LONG lColor = CLR_WHITE;
2630 + winhStorePresParam(&ppp, PP_FONTNAMESIZE, sizeof(szFont), szFont);
2631 + winhStorePresParam(&ppp, PP_BACKGROUNDCOLOR, sizeof(lColor), &lColor);
2632 + WinCreateWindow(...., ppp);
2633 + free(ppp);
2634 *
2635 *@@added V0.9.0 [umoeller]
2636 */
2637
2638BOOL winhStorePresParam(PPRESPARAMS *pppp, // in: data pointer (modified)
2639 ULONG ulAttrType, // in: PP_* index
2640 ULONG cbData, // in: sizeof(*pData), e.g. sizeof(LONG)
2641 PVOID pData) // in: presparam data (e.g. a PLONG to a color)
2642{
2643 BOOL brc = FALSE;
2644 if (pppp)
2645 {
2646 ULONG cbOld = 0,
2647 cbNew;
2648 PBYTE pbTemp = 0;
2649 PPRESPARAMS pppTemp = 0;
2650 PPARAM pppCopyTo = 0;
2651
2652 if (*pppp != NULL)
2653 // subsequent calls:
2654 cbOld = (**pppp).cb;
2655
2656 cbNew = sizeof(ULONG) // PRESPARAMS.cb
2657 + cbOld // old count, which does not include PRESPARAMS.cb
2658 + sizeof(ULONG) // PRESPARAMS.aparam[0].id
2659 + sizeof(ULONG) // PRESPARAMS.aparam[0].cb
2660 + cbData; // PRESPARAMS.aparam[0].ab[]
2661
2662 pbTemp = (PBYTE)malloc(cbNew);
2663 if (pbTemp)
2664 {
2665 pppTemp = (PPRESPARAMS)pbTemp;
2666
2667 if (*pppp != NULL)
2668 {
2669 // copy old data
2670 memcpy(pbTemp, *pppp, cbOld + sizeof(ULONG)); // including PRESPARAMS.cb
2671 pppCopyTo = (PPARAM)(pbTemp // new buffer
2672 + sizeof(ULONG) // skipping PRESPARAMS.cb
2673 + cbOld); // old PARAM array
2674 }
2675 else
2676 // first call:
2677 pppCopyTo = pppTemp->aparam;
2678
2679 pppTemp->cb = cbNew - sizeof(ULONG); // excluding PRESPARAMS.cb
2680 pppCopyTo->id = ulAttrType;
2681 pppCopyTo->cb = cbData; // byte count of PARAM.ab[]
2682 memcpy(pppCopyTo->ab, pData, cbData);
2683
2684 free(*pppp);
2685 *pppp = pppTemp;
2686
2687 brc = TRUE;
2688 }
2689 }
2690 return (brc);
2691}
2692
2693/*
2694 *@@ winhQueryPresColor:
2695 * returns the specified color. This is queried in the
2696 * following order:
2697 *
2698 * 1) hwnd's pres params are searched for ulPP
2699 * (which should be a PP_* index);
2700 * 2) if (fInherit == TRUE), the parent windows
2701 * are searched also;
2702 * 3) if this fails or (fInherit == FALSE), WinQuerySysColor
2703 * is called to get lSysColor (which should be a SYSCLR_*
2704 * index), if lSysColor != -1;
2705 * 4) if (lSysColor == -1), -1 is returned.
2706 *
2707 * The return value is always an RGB LONG, _not_ a color index.
2708 * This is even true for the returned system colors, which are
2709 * converted to RGB.
2710 *
2711 * If you do any painting with this value, you should switch
2712 * the HPS you're using to RGB mode (use gpihSwitchToRGB for that).
2713 *
2714 * Some useful ulPP / lSysColor pairs
2715 * (default values as in PMREF):
2716 *
2717 + -- PP_FOREGROUNDCOLOR SYSCLR_WINDOWTEXT (for most controls also)
2718 + SYSCLR_WINDOWSTATICTEXT (for static controls)
2719 + Foreground color (default: black)
2720 + -- PP_BACKGROUNDCOLOR SYSCLR_BACKGROUND
2721 + SYSCLR_DIALOGBACKGROUND
2722 + SYSCLR_FIELDBACKGROUND (for disabled scrollbars)
2723 + SYSCLR_WINDOW (application surface -- empty clients)
2724 + Background color (default: light gray)
2725 + -- PP_ACTIVETEXTFGNDCOLOR
2726 + -- PP_HILITEFOREGROUNDCOLOR SYSCLR_HILITEFOREGROUND
2727 + Highlighted foreground color, for example for selected menu
2728 + (def.: white)
2729 + -- PP_ACTIVETEXTBGNDCOLOR
2730 + -- PP_HILITEBACKGROUNDCOLOR SYSCLR_HILITEBACKGROUND
2731 + Highlighted background color (def.: dark gray)
2732 + -- PP_INACTIVETEXTFGNDCOLOR
2733 + -- PP_DISABLEDFOREGROUNDCOLOR SYSCLR_MENUDISABLEDTEXT
2734 + Disabled foreground color (dark gray)
2735 + -- PP_INACTIVETEXTBGNDCOLOR
2736 + -- PP_DISABLEDBACKGROUNDCOLOR
2737 + Disabled background color
2738 + -- PP_BORDERCOLOR SYSCLR_WINDOWFRAME
2739 + SYSCLR_INACTIVEBORDER
2740 + Border color (around pushbuttons, in addition to
2741 + the 3D colors)
2742 + -- PP_ACTIVECOLOR SYSCLR_ACTIVETITLE
2743 + Active color
2744 + -- PP_INACTIVECOLOR SYSCLR_INACTIVETITLE
2745 + Inactive color
2746 *
2747 * For menus:
2748 + -- PP_MENUBACKGROUNDCOLOR SYSCLR_MENU
2749 + -- PP_MENUFOREGROUNDCOLOR SYSCLR_MENUTEXT
2750 + -- PP_MENUHILITEBGNDCOLOR SYSCLR_MENUHILITEBGND
2751 + -- PP_MENUHILITEFGNDCOLOR SYSCLR_MENUHILITE
2752 + -- ?? SYSCLR_MENUDISABLEDTEXT
2753 +
2754 * For containers (according to the API ref. at EDM/2):
2755 + -- PP_FOREGROUNDCOLOR SYSCLR_WINDOWTEXT
2756 + -- PP_BACKGROUNDCOLOR SYSCLR_WINDOW
2757 + -- PP_HILITEFOREGROUNDCOLOR SYSCLR_HILITEFOREGROUND
2758 + -- PP_HILITEBACKGROUNDCOLOR SYSCLR_HILITEBACKGROUND
2759 + -- PP_BORDERCOLOR
2760 + (used for separator lines, eg. in Details view)
2761 + -- PP_ICONTEXTBACKGROUNDCOLOR
2762 + (column titles in Details view?!?)
2763 +
2764 * For listboxes / entryfields / MLE's:
2765 + -- PP_BACKGROUNDCOLOR SYSCLR_ENTRYFIELD
2766 *
2767 * PMREF has more of these.
2768 *
2769 *@@changed V0.9.0 [umoeller]: removed INI key query, using SYSCLR_* instead; function prototype changed
2770 *@@changed V0.9.0 [umoeller]: added fInherit parameter
2771 *@@changed V0.9.7 (2000-12-02) [umoeller]: added lSysColor == -1 support
2772 */
2773
2774LONG winhQueryPresColor(HWND hwnd, // in: window to query
2775 ULONG ulPP, // in: PP_* index
2776 BOOL fInherit, // in: search parent windows too?
2777 LONG lSysColor) // in: SYSCLR_* index
2778{
2779 ULONG ul,
2780 attrFound,
2781 abValue[32];
2782
2783 if (ulPP != (ULONG)-1)
2784 if ((ul = WinQueryPresParam(hwnd,
2785 ulPP,
2786 0,
2787 &attrFound,
2788 (ULONG)sizeof(abValue),
2789 (PVOID)&abValue,
2790 (fInherit)
2791 ? 0
2792 : QPF_NOINHERIT)))
2793 return (abValue[0]);
2794
2795 // not found: get system color
2796 if (lSysColor != -1)
2797 return (WinQuerySysColor(HWND_DESKTOP, lSysColor, 0));
2798
2799 return -1;
2800}
2801
2802/*
2803 *@@category: Helpers\PM helpers\Help (IPF)
2804 */
2805
2806/* ******************************************************************
2807 *
2808 * Help instance helpers
2809 *
2810 ********************************************************************/
2811
2812/*
2813 *@@ winhCreateHelp:
2814 * creates a help instance and connects it with the
2815 * given frame window.
2816 *
2817 * If (pszFileName == NULL), we'll retrieve the
2818 * executable's fully qualified file name and
2819 * replace the extension with .HLP simply. This
2820 * avoids the typical "Help not found" errors if
2821 * the program isn't started in its own directory.
2822 *
2823 * If you have created a help table in memory, specify it
2824 * with pHelpTable. To load a help table from the resources,
2825 * specify hmod (or NULLHANDLE) and set pHelpTable to the
2826 * following:
2827 +
2828 + (PHELPTABLE)MAKELONG(usTableID, 0xffff)
2829 *
2830 * Returns the help window handle or NULLHANDLE on errors.
2831 *
2832 * Based on an EDM/2 code snippet.
2833 *
2834 *@@added V0.9.4 (2000-07-03) [umoeller]
2835 */
2836
2837HWND winhCreateHelp(HWND hwndFrame, // in: app's frame window handle; can be NULLHANDLE
2838 const char *pcszFileName, // in: help file name or NULL
2839 HMODULE hmod, // in: module with help table or NULLHANDLE (current)
2840 PHELPTABLE pHelpTable, // in: help table or resource ID
2841 const char *pcszWindowTitle) // in: help window title or NULL
2842{
2843 HELPINIT hi;
2844 PSZ pszExt;
2845 CHAR szName[CCHMAXPATH];
2846 HWND hwndHelp;
2847
2848 if (pcszFileName == NULL)
2849 {
2850 PPIB ppib;
2851 PTIB ptib;
2852 DosGetInfoBlocks(&ptib, &ppib);
2853 DosQueryModuleName(ppib->pib_hmte, sizeof(szName), szName);
2854
2855 pszExt = strrchr(szName, '.');
2856 if (pszExt)
2857 strcpy(pszExt, ".hlp");
2858 else
2859 strcat(szName, ".hlp");
2860
2861 pcszFileName = szName;
2862 }
2863
2864 hi.cb = sizeof(HELPINIT);
2865 hi.ulReturnCode = 0;
2866 hi.pszTutorialName = NULL;
2867 hi.phtHelpTable = pHelpTable;
2868 hi.hmodHelpTableModule = hmod;
2869 hi.hmodAccelActionBarModule = NULLHANDLE;
2870 hi.idAccelTable = 0;
2871 hi.idActionBar = 0;
2872 hi.pszHelpWindowTitle = (PSZ)pcszWindowTitle;
2873 hi.fShowPanelId = CMIC_HIDE_PANEL_ID;
2874 hi.pszHelpLibraryName = (PSZ)pcszFileName;
2875
2876 hwndHelp = WinCreateHelpInstance(WinQueryAnchorBlock(hwndFrame),
2877 &hi);
2878 if ((hwndFrame) && (hwndHelp))
2879 {
2880 WinAssociateHelpInstance(hwndHelp, hwndFrame);
2881 }
2882
2883 return (hwndHelp);
2884}
2885
2886/*
2887 *@@ winhDisplayHelpPanel:
2888 * displays the specified help panel ID.
2889 *
2890 * If (ulHelpPanel == 0), this displays the
2891 * standard OS/2 "Using help" panel.
2892 *
2893 * Returns zero on success or one of the
2894 * help manager error codes on failure.
2895 * See HM_ERROR for those.
2896 *
2897 *@@added V0.9.7 (2001-01-21) [umoeller]
2898 */
2899
2900ULONG winhDisplayHelpPanel(HWND hwndHelpInstance, // in: from winhCreateHelp
2901 ULONG ulHelpPanel) // in: help panel ID
2902{
2903 return (ULONG)(WinSendMsg(hwndHelpInstance,
2904 HM_DISPLAY_HELP,
2905 (MPARAM)ulHelpPanel,
2906 (MPARAM)( (ulHelpPanel != 0)
2907 ? HM_RESOURCEID
2908 : 0)));
2909}
2910
2911/*
2912 *@@ winhDestroyHelp:
2913 * destroys the help instance created by winhCreateHelp.
2914 *
2915 * Based on an EDM/2 code snippet.
2916 *
2917 *@@added V0.9.4 (2000-07-03) [umoeller]
2918 */
2919
2920void winhDestroyHelp(HWND hwndHelp,
2921 HWND hwndFrame) // can be NULLHANDLE if not used with winhCreateHelp
2922{
2923 if (hwndHelp)
2924 {
2925 if (hwndFrame)
2926 WinAssociateHelpInstance(NULLHANDLE, hwndFrame);
2927 WinDestroyHelpInstance(hwndHelp);
2928 }
2929}
2930
2931/*
2932 *@@category: Helpers\PM helpers\Application control
2933 */
2934
2935/* ******************************************************************
2936 *
2937 * Application control
2938 *
2939 ********************************************************************/
2940
2941/*
2942 *@@ CallBatchCorrectly:
2943 * fixes the specified PROGDETAILS for
2944 * command files in the executable part
2945 * by inserting /C XXX into the parameters
2946 * and setting the executable according
2947 * to an environment variable.
2948 *
2949 *@@added V0.9.6 (2000-10-16) [umoeller]
2950 *@@changed V0.9.7 (2001-01-15) [umoeller]: now using XSTRING
2951 */
2952
2953VOID CallBatchCorrectly(PPROGDETAILS pProgDetails,
2954 PXSTRING pstrParams, // in/out: modified parameters (reallocated)
2955 const char *pcszEnvVar, // in: env var spec'g command proc
2956 // (e.g. "OS2_SHELL"); can be NULL
2957 const char *pcszDefProc) // in: def't command proc (e.g. "CMD.EXE")
2958{
2959 // XXX.CMD file as executable:
2960 // fix args to /C XXX.CMD
2961
2962 PSZ pszOldParams = NULL;
2963 ULONG ulOldParamsLength = pstrParams->ulLength;
2964 if (ulOldParamsLength)
2965 // we have parameters already:
2966 // make a backup... we'll append that later
2967 pszOldParams = strdup(pstrParams->psz);
2968
2969 // set new params to "/C filename.cmd"
2970 xstrcpy(pstrParams, "/C ", 0);
2971 xstrcat(pstrParams,
2972 pProgDetails->pszExecutable,
2973 0);
2974
2975 if (pszOldParams)
2976 {
2977 // .cmd had params:
2978 // append space and old params
2979 xstrcatc(pstrParams, ' ');
2980 xstrcat(pstrParams,
2981 pszOldParams,
2982 ulOldParamsLength);
2983 free(pszOldParams);
2984 }
2985
2986 // set executable to $(OS2_SHELL)
2987 pProgDetails->pszExecutable = NULL;
2988 if (pcszEnvVar)
2989 pProgDetails->pszExecutable = getenv(pcszEnvVar);
2990 if (!pProgDetails->pszExecutable)
2991 pProgDetails->pszExecutable = (PSZ)pcszDefProc;
2992 // should be on PATH
2993}
2994
2995/*
2996 *@@ winhQueryAppType:
2997 * returns the Control Program (Dos) and
2998 * Win* PROG_* application types for the
2999 * specified executable. Essentially, this
3000 * is a wrapper around DosQueryAppType.
3001 *
3002 * pcszExecutable must be fully qualified.
3003 * You can use doshFindExecutable to qualify
3004 * it.
3005 *
3006 * This returns the APIRET of DosQueryAppType.
3007 * If this is NO_ERROR; *pulDosAppType receives
3008 * the app type of DosQueryAppType. In addition,
3009 * *pulWinAppType is set to one of the following:
3010 *
3011 * -- PROG_FULLSCREEN
3012 *
3013 * -- PROG_PDD
3014 *
3015 * -- PROG_VDD
3016 *
3017 * -- PROG_XWP_DLL: new apptype defined in winh.h for
3018 * dynamic link libraries.
3019 *
3020 * -- PROG_WINDOWEDVDM
3021 *
3022 * -- PROG_PM
3023 *
3024 * -- PROG_31_ENH
3025 *
3026 * -- PROG_WINDOWABLEVIO
3027 *
3028 *@@added V0.9.9 (2001-03-07) [umoeller]
3029 */
3030
3031APIRET winhQueryAppType(const char *pcszExecutable,
3032 PULONG pulDosAppType,
3033 PULONG pulWinAppType)
3034{
3035 APIRET arc = DosQueryAppType((PSZ)pcszExecutable, pulDosAppType);
3036 if (arc == NO_ERROR)
3037 {
3038 ULONG _ulDosAppType = *pulDosAppType;
3039
3040 if (_ulDosAppType == 0)
3041 *pulWinAppType = PROG_FULLSCREEN;
3042 else if (_ulDosAppType & 0x40)
3043 *pulWinAppType = PROG_PDD;
3044 else if (_ulDosAppType & 0x80)
3045 *pulWinAppType = PROG_VDD;
3046 else if ((_ulDosAppType & 0xF0) == 0x10)
3047 // DLL bit set
3048 *pulWinAppType = PROG_XWP_DLL;
3049 else if (_ulDosAppType & 0x20)
3050 // DOS bit set?
3051 *pulWinAppType = PROG_WINDOWEDVDM;
3052 else if ((_ulDosAppType & 0x0003) == 0x0003) // "Window-API" == PM
3053 *pulWinAppType = PROG_PM;
3054 else if ( ((_ulDosAppType & 0xFFFF) == 0x1000) // windows program (?!?)
3055 || ((_ulDosAppType & 0xFFFF) == 0x0400) // windows program (?!?)
3056 )
3057 *pulWinAppType = PROG_31_ENH;
3058 else if ((_ulDosAppType & 0x03) == 0x02)
3059 *pulWinAppType = PROG_WINDOWABLEVIO;
3060 else if ((_ulDosAppType & 0x03) == 0x01)
3061 *pulWinAppType = PROG_FULLSCREEN;
3062 }
3063
3064 return (arc);
3065}
3066
3067/*
3068 *@@ winhStartApp:
3069 * wrapper around WinStartApp which fixes the
3070 * specified PROGDETAILS to (hopefully) work
3071 * work with all executable types.
3072 *
3073 * This fixes the executable info to support:
3074 *
3075 * -- starting "*" executables (command prompts
3076 * for OS/2, DOS, Win-OS/2);
3077 *
3078 * -- starting ".CMD" and ".BAT" files as
3079 * PROGDETAILS.pszExecutable.
3080 *
3081 * Unless it is "*", PROGDETAILS.pszExecutable must
3082 * be a proper file name. The full path may be omitted
3083 * if it is on the PATH, but the extension (.EXE etc.)
3084 * must be given. You can use doshFindExecutable to
3085 * find executables if you don't know the extension.
3086 *
3087 * This also handles and merges special and default
3088 * environments for the app to be started.
3089 * If PROGDETAILS.pszEnvironment is empty
3090 * and the application is a Win-OS/2 app,
3091 * this uses the default Win-OS/2 settings
3092 * as specified in the "Win-OS/2" WPS settings
3093 * object.
3094 *
3095 * Even though this isn't clearly said in PMREF,
3096 * PROGDETAILS.swpInitial is important:
3097 *
3098 * -- To start a session minimized, set SWP_MINIMIZE.
3099 *
3100 * -- To start a VIO session without auto-close, set
3101 * the half-documented SWP_NOAUTOCLOSE flag (0x8000)
3102 * This flag is now in the newer toolkit headers.
3103 *
3104 * Since this calls WinStartApp in turn, this
3105 * requires a message queue on the calling thread.
3106 *
3107 *@@added V0.9.6 (2000-10-16) [umoeller]
3108 *@@changed V0.9.7 (2000-12-10) [umoeller]: PROGDETAILS.swpInitial no longer zeroed... this broke VIOs
3109 *@@changed V0.9.7 (2000-12-17) [umoeller]: PROGDETAILS.pszEnvironment no longer zeroed
3110 *@@changed V0.9.9 (2001-01-27) [umoeller]: crashed if PROGDETAILS.pszExecutable was NULL
3111 *@@todo PROG_DEFAULT ain't working; test winhQueryAppType
3112 */
3113
3114HAPP winhStartApp(HWND hwndNotify, // in: notify window (as with WinStartApp)
3115 const PROGDETAILS *pcProgDetails) // in: program data
3116{
3117 HAPP happ = NULLHANDLE;
3118 XSTRING strParamsPatched;
3119 BOOL fIsWindowsApp = FALSE,
3120 fIsWindowsEnhApp = FALSE;
3121 PROGDETAILS ProgDetails;
3122 PSZ pszWinOS2Env = 0;
3123
3124 memcpy(&ProgDetails, pcProgDetails, sizeof(PROGDETAILS));
3125 // pointers still point into old prog details buffer
3126 ProgDetails.Length = sizeof(PROGDETAILS);
3127 ProgDetails.progt.fbVisible = SHE_VISIBLE;
3128 // ProgDetails.pszEnvironment = 0;
3129
3130 // all this only makes sense if this contains something...
3131 // besides, this crashed on string comparisons V0.9.9 (2001-01-27) [umoeller]
3132 if (ProgDetails.pszExecutable)
3133 {
3134 // memset(&ProgDetails.swpInitial, 0, sizeof(SWP));
3135 // this wasn't a good idea... WPProgram stores stuff
3136 // in here, such as the "minimize on startup" -> SWP_MINIMIZE
3137
3138 // duplicate parameters...
3139 // we need this for string manipulations below...
3140 if (ProgDetails.pszParameters)
3141 xstrInitCopy(&strParamsPatched,
3142 ProgDetails.pszParameters,
3143 100);
3144 else
3145 // no old params:
3146 xstrInit(&strParamsPatched, 100);
3147
3148 // _Pmpf((__FUNCTION__ ": old progc: 0x%lX", pcProgDetails->progt.progc));
3149 // _Pmpf((" pszTitle: %s", (ProgDetails.pszTitle) ? ProgDetails.pszTitle : NULL));
3150 // _Pmpf((" pszIcon: %s", (ProgDetails.pszIcon) ? ProgDetails.pszIcon : NULL));
3151
3152 // program type fixups
3153 switch (ProgDetails.progt.progc) // that's a ULONG
3154 {
3155 case ((ULONG)-1): // we get that sometimes...
3156 case PROG_DEFAULT:
3157 // @@todo
3158 break;
3159 }
3160
3161 // now try again...
3162 switch (ProgDetails.progt.progc)
3163 {
3164 case PROG_31_ENHSEAMLESSVDM: // 17
3165 case PROG_31_ENHSEAMLESSCOMMON: // 18
3166 case PROG_31_ENH: // 19
3167 fIsWindowsApp = TRUE;
3168 fIsWindowsEnhApp = TRUE;
3169 break;
3170
3171 #ifndef PROG_30_STD
3172 #define PROG_30_STD (PROGCATEGORY)11
3173 #endif
3174
3175 #ifndef PROG_30_STDSEAMLESSVDM
3176 #define PROG_30_STDSEAMLESSVDM (PROGCATEGORY)13
3177 #endif
3178
3179 case PROG_WINDOW_REAL: // 10
3180 case PROG_30_STD: // 11
3181 case PROG_WINDOW_AUTO: // 12
3182 case PROG_30_STDSEAMLESSVDM: // 13
3183 case PROG_30_STDSEAMLESSCOMMON: // 14
3184 case PROG_31_STDSEAMLESSVDM: // 15
3185 case PROG_31_STDSEAMLESSCOMMON: // 16
3186 case PROG_31_STD: // 20
3187 fIsWindowsApp = TRUE;
3188 break;
3189 }
3190
3191 /*
3192 * command lines fixups:
3193 *
3194 */
3195
3196 if (strcmp(ProgDetails.pszExecutable, "*") == 0)
3197 {
3198 /*
3199 * "*" for command sessions:
3200 *
3201 */
3202
3203 if (fIsWindowsEnhApp)
3204 {
3205 // enhanced Win-OS/2 session:
3206 PSZ psz = NULL;
3207 if (strParamsPatched.ulLength)
3208 // "/3 " + existing params
3209 psz = strdup(strParamsPatched.psz);
3210
3211 xstrcpy(&strParamsPatched, "/3 ", 0);
3212
3213 if (psz)
3214 {
3215 xstrcat(&strParamsPatched, psz, 0);
3216 free(psz);
3217 }
3218 }
3219
3220 if (fIsWindowsApp)
3221 {
3222 // cheat: WinStartApp doesn't support NULL
3223 // for Win-OS2 sessions, so manually start winos2.com
3224 ProgDetails.pszExecutable = "WINOS2.COM";
3225 // this is a DOS app, so fix this to DOS fullscreen
3226 ProgDetails.progt.progc = PROG_VDM;
3227 }
3228 else
3229 // for all other executable types
3230 // (including OS/2 and DOS sessions),
3231 // set pszExecutable to NULL; this will
3232 // have WinStartApp start a cmd shell
3233 ProgDetails.pszExecutable = NULL;
3234
3235 } // end if (strcmp(pProgDetails->pszExecutable, "*") == 0)
3236 else
3237 switch (ProgDetails.progt.progc)
3238 {
3239 /*
3240 * .CMD files fixups
3241 *
3242 */
3243
3244 case PROG_FULLSCREEN: // OS/2 fullscreen
3245 case PROG_WINDOWABLEVIO: // OS/2 window
3246 {
3247 PSZ pszExtension = doshGetExtension(ProgDetails.pszExecutable);
3248 if (pszExtension)
3249 {
3250 if (stricmp(pszExtension, "CMD") == 0)
3251 {
3252 CallBatchCorrectly(&ProgDetails,
3253 &strParamsPatched,
3254 "OS2_SHELL",
3255 "CMD.EXE");
3256 }
3257 }
3258 break; }
3259
3260 case PROG_VDM: // DOS fullscreen
3261 case PROG_WINDOWEDVDM: // DOS window
3262 {
3263 PSZ pszExtension = doshGetExtension(ProgDetails.pszExecutable);
3264 if (pszExtension)
3265 {
3266 if (stricmp(pszExtension, "BAT") == 0)
3267 {
3268 CallBatchCorrectly(&ProgDetails,
3269 &strParamsPatched,
3270 NULL,
3271 "COMMAND.COM");
3272 }
3273 }
3274 break; }
3275 } // end switch (ProgDetails.progt.progc)
3276
3277 /*
3278 * Fix environment for Win-OS/2
3279 *
3280 */
3281
3282 if ( !(xstrIsString(ProgDetails.pszEnvironment)) // env empty
3283 && (fIsWindowsApp) // and win-os2 app
3284 )
3285 {
3286 ULONG ulSize = 0;
3287 // get default environment (from Win-OS/2 settings object)
3288 // from OS2.INI
3289 PSZ pszDefEnv = prfhQueryProfileData(HINI_USER,
3290 "WINOS2",
3291 "PM_GlobalWindows31Settings",
3292 &ulSize);
3293 if (pszDefEnv)
3294 {
3295 PSZ pszDefEnv2 = (PSZ)malloc(ulSize + 2);
3296 if (pszDefEnv2)
3297 {
3298 PSZ p = pszDefEnv2;
3299 memset(pszDefEnv2, 0, ulSize + 2);
3300 memcpy(pszDefEnv2, pszDefEnv, ulSize);
3301
3302 for (p = pszDefEnv2;
3303 p < pszDefEnv2 + ulSize;
3304 p++)
3305 if (*p == ';')
3306 *p = 0;
3307
3308 // okay.... now we got an OS/2-style environment
3309 // with 0, 0, 00 strings
3310
3311 pszWinOS2Env = pszDefEnv2; // freed below
3312
3313 // use this
3314 ProgDetails.pszEnvironment = pszWinOS2Env;
3315 }
3316
3317 free(pszDefEnv);
3318 }
3319 }
3320
3321 ProgDetails.pszParameters = strParamsPatched.psz;
3322
3323 happ = WinStartApp(hwndNotify,
3324 // receives WM_APPTERMINATENOTIFY
3325 &ProgDetails,
3326 strParamsPatched.psz,
3327 NULL, // "reserved", PMREF says...
3328 SAF_INSTALLEDCMDLINE);
3329 // we MUST use SAF_INSTALLEDCMDLINE
3330 // or no Win-OS/2 session will start...
3331 // whatever is going on here... Warp 4 FP11
3332
3333 // do not use SAF_STARTCHILDAPP, or the
3334 // app will be terminated automatically
3335 // when the WPS terminates!
3336
3337 // _Pmpf((__FUNCTION__ ": got happ 0x%lX", happ));
3338
3339 xstrClear(&strParamsPatched);
3340 if (pszWinOS2Env)
3341 free(pszWinOS2Env);
3342 } // end if (ProgDetails.pszExecutable)
3343
3344 return (happ);
3345}
3346
3347/*
3348 *@@ winhAnotherInstance:
3349 * this tests whether another instance of the same
3350 * application is already running.
3351 *
3352 * To identify instances of the same application, the
3353 * application must call this function during startup
3354 * with the unique name of an OS/2 semaphore. As with
3355 * all OS/2 semaphores, the semaphore name must begin
3356 * with "\\SEM32\\". The semaphore isn't really used
3357 * except for testing for its existence, since that
3358 * name is unique among all processes.
3359 *
3360 * If another instance is found, TRUE is returned. If
3361 * (fSwitch == TRUE), that instance is switched to,
3362 * using the tasklist.
3363 *
3364 * If no other instance is found, FALSE is returned only.
3365 *
3366 * Based on an EDM/2 code snippet.
3367 *
3368 *@@added V0.9.0 (99-10-22) [umoeller]
3369 */
3370
3371BOOL winhAnotherInstance(const char *pcszSemName, // in: semaphore ID
3372 BOOL fSwitch) // in: if TRUE, switch to first instance if running
3373{
3374 HMTX hmtx;
3375
3376 if (DosCreateMutexSem((PSZ)pcszSemName,
3377 &hmtx,
3378 DC_SEM_SHARED,
3379 TRUE)
3380 == NO_ERROR)
3381 // semapore created: this doesn't happen if the semaphore
3382 // exists already, so no other instance is running
3383 return (FALSE);
3384
3385 // else: instance running
3386 hmtx = NULLHANDLE;
3387
3388 // switch to other instance?
3389 if (fSwitch)
3390 {
3391 // yes: query mutex creator
3392 if (DosOpenMutexSem((PSZ)pcszSemName,
3393 &hmtx)
3394 == NO_ERROR)
3395 {
3396 PID pid = 0;
3397 TID tid = 0; // unused
3398 ULONG ulCount; // unused
3399
3400 if (DosQueryMutexSem(hmtx, &pid, &tid, &ulCount) == NO_ERROR)
3401 {
3402 HSWITCH hswitch = WinQuerySwitchHandle(NULLHANDLE, pid);
3403 if (hswitch != NULLHANDLE)
3404 WinSwitchToProgram(hswitch);
3405 }
3406
3407 DosCloseMutexSem(hmtx);
3408 }
3409 }
3410
3411 return (TRUE); // another instance exists
3412}
3413
3414/*
3415 *@@ winhAddToTasklist:
3416 * this adds the specified window to the tasklist
3417 * with hIcon as its program icon (which is also
3418 * set for the main window). This is useful for
3419 * the old "dialog as main window" trick.
3420 *
3421 * Returns the HSWITCH of the added entry.
3422 */
3423
3424HSWITCH winhAddToTasklist(HWND hwnd, // in: window to add
3425 HPOINTER hIcon) // in: icon for main window
3426{
3427 SWCNTRL swctl;
3428 HSWITCH hswitch = 0;
3429 swctl.hwnd = hwnd; // window handle
3430 swctl.hwndIcon = hIcon; // icon handle
3431 swctl.hprog = NULLHANDLE; // program handle (use default)
3432 WinQueryWindowProcess(hwnd, &(swctl.idProcess), NULL);
3433 // process identifier
3434 swctl.idSession = 0; // session identifier ?
3435 swctl.uchVisibility = SWL_VISIBLE; // visibility
3436 swctl.fbJump = SWL_JUMPABLE; // jump indicator
3437 // get window title from window titlebar
3438 if (hwnd)
3439 WinQueryWindowText(hwnd, sizeof(swctl.szSwtitle), swctl.szSwtitle);
3440 swctl.bProgType = PROG_DEFAULT; // program type
3441 hswitch = WinAddSwitchEntry(&swctl);
3442
3443 // give the main window the icon
3444 if ((hwnd) && (hIcon))
3445 WinSendMsg(hwnd,
3446 WM_SETICON,
3447 (MPARAM)hIcon,
3448 NULL);
3449
3450 return (hswitch);
3451}
3452
3453/*
3454 *@@category: Helpers\PM helpers\Miscellaneous
3455 */
3456
3457/* ******************************************************************
3458 *
3459 * Miscellaneous
3460 *
3461 ********************************************************************/
3462
3463/*
3464 *@@ winhMyAnchorBlock:
3465 * returns the proper anchor block (HAB)
3466 * for the calling thread.
3467 *
3468 * Many Win* functions require an HAB to be
3469 * passed in. While many of them will work
3470 * when passing in NULLHANDLE, some (such as
3471 * WinGetMsg) won't. If you don't know the
3472 * anchor block of the calling thread, use
3473 * this function.
3474 *
3475 * This creates a temporary object window to
3476 * find out the anchor block. This is quite
3477 * expensive so only use this if there's no
3478 * other way to find out.
3479 *
3480 *@@added V0.9.11 (2001-04-20) [umoeller]
3481 */
3482
3483HAB winhMyAnchorBlock(VOID)
3484{
3485 HAB hab = NULLHANDLE;
3486 HWND hwnd = winhCreateObjectWindow(WC_BUTTON, NULL);
3487 if (hwnd)
3488 {
3489 hab = WinQueryAnchorBlock(hwnd);
3490 WinDestroyWindow(hwnd);
3491 }
3492
3493 return (hab);
3494}
3495
3496/*
3497 *@@ winhFree:
3498 * frees a block of memory allocated by the
3499 * winh* functions.
3500 *
3501 * Since the winh* functions use malloc(),
3502 * you can also use free() directly on such
3503 * blocks. However, you must use winhFree
3504 * if the winh* functions are in a module
3505 * with a different C runtime.
3506 *
3507 *@@added V0.9.7 (2000-12-06) [umoeller]
3508 */
3509
3510VOID winhFree(PVOID p)
3511{
3512 if (p)
3513 free(p);
3514}
3515
3516/*
3517 *@@ winhSleep:
3518 * sleeps at least the specified amount of time,
3519 * without blocking the message queue.
3520 *
3521 * NOTE: This function is a bit expensive because
3522 * it creates a temporary object window. If you
3523 * need to sleep several times, you should rather
3524 * use a private timer.
3525 *
3526 *@@added V0.9.4 (2000-07-11) [umoeller]
3527 *@@changed V0.9.9 (2001-03-11) [umoeller]: rewritten
3528 */
3529
3530VOID winhSleep(ULONG ulSleep) // in: sleep time in milliseconds
3531{
3532 HWND hwnd = winhCreateObjectWindow(WC_STATIC, NULL);
3533 if (hwnd)
3534 {
3535 QMSG qmsg;
3536 HAB hab = WinQueryAnchorBlock(hwnd);
3537 if (WinStartTimer(hab,
3538 hwnd,
3539 1,
3540 ulSleep))
3541 {
3542 while (WinGetMsg(hab, &qmsg, NULLHANDLE, 0, 0))
3543 {
3544 if ( (qmsg.hwnd == hwnd)
3545 && (qmsg.msg == WM_TIMER)
3546 && (qmsg.mp1 == (MPARAM)1) // timer ID
3547 )
3548 break;
3549
3550 WinDispatchMsg(hab, &qmsg);
3551 }
3552 WinStopTimer(hab,
3553 hwnd,
3554 1);
3555 }
3556 else
3557 // timer creation failed:
3558 DosSleep(ulSleep);
3559
3560 WinDestroyWindow(hwnd);
3561 }
3562 else
3563 DosSleep(ulSleep);
3564}
3565
3566/*
3567 *@@ winhFileDlg:
3568 * one-short function for opening an "Open" file
3569 * dialog.
3570 *
3571 * On input, pszFile specifies the directory and
3572 * file specification (e.g. "F:\*.txt").
3573 *
3574 * Returns TRUE if the user pressed OK. In that
3575 * case, the fully qualified filename is written
3576 * into pszFile again.
3577 *
3578 * Returns FALSE if the user pressed Cancel.
3579 *
3580 * Notes about flFlags:
3581 *
3582 * -- WINH_FOD_SAVEDLG: display a "Save As" dialog.
3583 * Otherwise an "Open" dialog is displayed.
3584 *
3585 * -- WINH_FOD_INILOADDIR: load a directory from the
3586 * specified INI key and switch the dlg to it.
3587 * In that case, on input, pszFile must only
3588 * contain the file filter without any path
3589 * specification, because that is loaded from
3590 * the INI key. If the INI key does not exist,
3591 * the current process directory will be used.
3592 *
3593 * -- WINH_FOD_INISAVEDIR: if the user presses OK,
3594 * the directory of the selected file is written
3595 * to the specified INI key so that it can be
3596 * reused later. This flag is independent of
3597 * WINH_FOD_INISAVEDIR: you can specify none,
3598 * one, or both of them.
3599 *
3600 *@@added V0.9.3 (2000-04-29) [umoeller]
3601 *@@changed V0.9.12 (2001-05-21) [umoeller]: this failed if INI data had root dir, fixed
3602 */
3603
3604BOOL winhFileDlg(HWND hwndOwner, // in: owner for file dlg
3605 PSZ pszFile, // in: file mask; out: fully q'd filename
3606 // (should be CCHMAXPATH in size)
3607 ULONG flFlags, // in: any combination of the following:
3608 // -- WINH_FOD_SAVEDLG: save dlg; else open dlg
3609 // -- WINH_FOD_INILOADDIR: load FOD path from INI
3610 // -- WINH_FOD_INISAVEDIR: store FOD path to INI on OK
3611 HINI hini, // in: INI file to load/store last path from (can be HINI_USER)
3612 const char *pcszApplication, // in: INI application to load/store last path from
3613 const char *pcszKey) // in: INI key to load/store last path from
3614{
3615 FILEDLG fd;
3616 FILESTATUS3 fs3;
3617
3618 memset(&fd, 0, sizeof(FILEDLG));
3619 fd.cbSize = sizeof(FILEDLG);
3620 fd.fl = FDS_CENTER;
3621
3622 if (flFlags & WINH_FOD_SAVEDLG)
3623 fd.fl |= FDS_SAVEAS_DIALOG;
3624 else
3625 fd.fl |= FDS_OPEN_DIALOG;
3626
3627 if ( (hini)
3628 && (flFlags & WINH_FOD_INILOADDIR)
3629 && (PrfQueryProfileString(hini,
3630 (PSZ)pcszApplication,
3631 (PSZ)pcszKey,
3632 "", // default string V0.9.9 (2001-02-10) [umoeller]
3633 fd.szFullFile,
3634 sizeof(fd.szFullFile)-10)
3635 > 2)
3636 // added these checks V0.9.12 (2001-05-21) [umoeller]
3637 && (!DosQueryPathInfo(fd.szFullFile,
3638 FIL_STANDARD,
3639 &fs3,
3640 sizeof(fs3)))
3641 && (fs3.attrFile & FILE_DIRECTORY)
3642 )
3643 {
3644 // found: append "\*"
3645 strcat(fd.szFullFile, "\\");
3646 strcat(fd.szFullFile, pszFile);
3647 }
3648 else
3649 // default: copy pszFile
3650 strcpy(fd.szFullFile, pszFile);
3651 // fixed V0.9.12 (2001-05-21) [umoeller]
3652
3653 if ( WinFileDlg(HWND_DESKTOP, // parent
3654 hwndOwner, // owner
3655 &fd)
3656 && (fd.lReturn == DID_OK)
3657 )
3658 {
3659 // save path back?
3660 if ( (hini)
3661 && (flFlags & WINH_FOD_INISAVEDIR)
3662 )
3663 {
3664 // get the directory that was used
3665 PSZ p = strrchr(fd.szFullFile, '\\');
3666 if (p)
3667 {
3668 // contains directory:
3669 // copy to OS2.INI
3670 PSZ pszDir = strhSubstr(fd.szFullFile, p);
3671 if (pszDir)
3672 {
3673 PrfWriteProfileString(hini,
3674 (PSZ)pcszApplication,
3675 (PSZ)pcszKey,
3676 pszDir);
3677 free(pszDir);
3678 }
3679 }
3680 }
3681
3682 strcpy(pszFile, fd.szFullFile);
3683
3684 return (TRUE);
3685 }
3686
3687 return (FALSE);
3688}
3689
3690/*
3691 *@@ winhSetWaitPointer:
3692 * this sets the mouse pointer to "Wait".
3693 * Returns the previous pointer (HPOINTER),
3694 * which should be stored somewhere to be
3695 * restored later. Example:
3696 + HPOINTER hptrOld = winhSetWaitPointer();
3697 + ...
3698 + WinSetPointer(HWND_DESKTOP, hptrOld);
3699 */
3700
3701HPOINTER winhSetWaitPointer(VOID)
3702{
3703 HPOINTER hptr = WinQueryPointer(HWND_DESKTOP);
3704 WinSetPointer(HWND_DESKTOP,
3705 WinQuerySysPointer(HWND_DESKTOP,
3706 SPTR_WAIT,
3707 FALSE)); // no copy
3708 return (hptr);
3709}
3710
3711/*
3712 *@@ winhQueryWindowText:
3713 * this returns the window text of the specified
3714 * HWND in a newly allocated buffer.
3715 *
3716 * Returns NULL on error. Use free()
3717 * to free the return value.
3718 */
3719
3720PSZ winhQueryWindowText(HWND hwnd)
3721{
3722 PSZ pszText = NULL;
3723 ULONG cbText = WinQueryWindowTextLength(hwnd);
3724 // additional null character
3725 if (cbText)
3726 {
3727 pszText = (PSZ)malloc(cbText + 1);
3728 if (pszText)
3729 WinQueryWindowText(hwnd,
3730 cbText + 1,
3731 pszText);
3732 }
3733 return (pszText);
3734}
3735
3736/*
3737 *@@ winhReplaceWindowText:
3738 * this is a combination of winhQueryWindowText
3739 * and strhFindReplace to replace substrings in a window.
3740 *
3741 * This is useful for filling in placeholders
3742 * a la "%1" in control windows, e.g. static
3743 * texts.
3744 *
3745 * This replaces only the first occurence of
3746 * pszSearch.
3747 *
3748 * Returns TRUE only if the window exists and
3749 * the search string was replaced.
3750 *
3751 *@@added V0.9.0 [umoeller]
3752 */
3753
3754BOOL winhReplaceWindowText(HWND hwnd, // in: window whose text is to be modified
3755 const char *pcszSearch, // in: search string (e.g. "%1")
3756 const char *pcszReplaceWith) // in: replacement string for pszSearch
3757{
3758 BOOL brc = FALSE;
3759 PSZ pszText = winhQueryWindowText(hwnd);
3760 if (pszText)
3761 {
3762 ULONG ulOfs = 0;
3763 if (strhFindReplace(&pszText, &ulOfs, pcszSearch, pcszReplaceWith) > 0)
3764 {
3765 WinSetWindowText(hwnd, pszText);
3766 brc = TRUE;
3767 }
3768 free(pszText);
3769 }
3770 return (brc);
3771}
3772
3773/*
3774 *@@ winhEnableDlgItems:
3775 * this enables/disables a whole range of controls
3776 * in a window by enumerating the child windows
3777 * until usIDFirst is found. If so, that subwindow
3778 * is enabled/disabled and all the following windows
3779 * in the enumeration also, until usIDLast is found.
3780 *
3781 * Note that this affects _all_ controls following
3782 * the usIDFirst window, no matter what ID they have
3783 * (even if "-1"), until usIDLast is found.
3784 *
3785 * Returns the no. of controls which were enabled/disabled
3786 * (null if none).
3787 *
3788 *@@added V0.9.0 [umoeller]
3789 *@@changed V0.9.1 (99-12-20) [umoeller]: renamed from winhEnableDlgItems
3790 */
3791
3792ULONG winhEnableControls(HWND hwndDlg, // in: dialog window
3793 USHORT usIDFirst, // in: first affected control ID
3794 USHORT usIDLast, // in: last affected control ID (inclusive)
3795 BOOL fEnable)
3796{
3797 HENUM henum1 = NULLHANDLE;
3798 HWND hwndThis = NULLHANDLE;
3799 ULONG ulCount = 0;
3800
3801 henum1 = WinBeginEnumWindows(hwndDlg);
3802 while ((hwndThis = WinGetNextWindow(henum1)) != NULLHANDLE)
3803 {
3804 USHORT usIDCheckFirst = WinQueryWindowUShort(hwndThis, QWS_ID),
3805 usIDCheckLast;
3806 if (usIDCheckFirst == usIDFirst)
3807 {
3808 WinEnableWindow(hwndThis, fEnable);
3809 ulCount++;
3810
3811 while ((hwndThis = WinGetNextWindow(henum1)) != NULLHANDLE)
3812 {
3813 WinEnableWindow(hwndThis, fEnable);
3814 ulCount++;
3815 usIDCheckLast = WinQueryWindowUShort(hwndThis, QWS_ID);
3816 if (usIDCheckLast == usIDLast)
3817 break;
3818 }
3819
3820 break; // outer loop
3821 }
3822 }
3823 WinEndEnumWindows(henum1);
3824 return (ulCount);
3825}
3826
3827/*
3828 *@@ winhCreateStdWindow:
3829 * much like WinCreateStdWindow, but this one
3830 * allows you to have the standard window
3831 * positioned automatically, using a given
3832 * SWP structure (*pswpFrame).
3833 *
3834 * The frame is created with the specified parent
3835 * (usually HWND_DESKTOP), but no owner.
3836 *
3837 * The client window is created with the frame as
3838 * its parent and owner and gets an ID of FID_CLIENT.
3839 *
3840 * Alternatively, you can set pswpFrame to NULL
3841 * and specify FCF_SHELLPOSITION with flFrameCreateFlags.
3842 * If you want the window to be shown, specify
3843 * SWP_SHOW (and maybe SWP_ACTIVATE) in *pswpFrame.
3844 *
3845 *@@added V0.9.0 [umoeller]
3846 *@@changed V0.9.5 (2000-08-13) [umoeller]: flStyleClient never worked, fixed
3847 *@@changed V0.9.7 (2000-12-08) [umoeller]: fixed client calc for invisible window
3848 */
3849
3850HWND winhCreateStdWindow(HWND hwndFrameParent, // in: normally HWND_DESKTOP
3851 PSWP pswpFrame, // in: frame wnd pos
3852 ULONG flFrameCreateFlags, // in: FCF_* flags
3853 ULONG ulFrameStyle, // in: WS_* flags (e.g. WS_VISIBLE, WS_ANIMATE)
3854 const char *pcszFrameTitle, // in: frame title (title bar)
3855 ULONG ulResourcesID, // in: according to FCF_* flags
3856 const char *pcszClassClient, // in: client class name
3857 ULONG flStyleClient, // in: client style
3858 ULONG ulID, // in: frame window ID
3859 PVOID pClientCtlData, // in: pCtlData structure pointer for client
3860 PHWND phwndClient) // out: created client wnd
3861{
3862 FRAMECDATA fcdata;
3863 HWND hwndFrame;
3864 RECTL rclClient;
3865
3866 fcdata.cb = sizeof(FRAMECDATA);
3867 fcdata.flCreateFlags = flFrameCreateFlags;
3868 fcdata.hmodResources = (HMODULE)NULL;
3869 fcdata.idResources = ulResourcesID;
3870
3871 /* Create the frame and client windows. */
3872 hwndFrame = WinCreateWindow(hwndFrameParent,
3873 WC_FRAME,
3874 (PSZ)pcszFrameTitle,
3875 ulFrameStyle,
3876 0,0,0,0, // size and position = 0
3877 NULLHANDLE, // no owner
3878 HWND_TOP, // z-order
3879 ulID, // frame window ID
3880 &fcdata, // frame class data
3881 NULL); // no presparams
3882
3883 if (hwndFrame)
3884 {
3885 *phwndClient = WinCreateWindow(hwndFrame, // parent
3886 (PSZ)pcszClassClient, // class
3887 NULL, // no title
3888 flStyleClient, // style
3889 0,0,0,0, // size and position = 0
3890 hwndFrame, // owner
3891 HWND_BOTTOM, // bottom z-order
3892 FID_CLIENT, // frame window ID
3893 pClientCtlData, // class data
3894 NULL); // no presparams
3895
3896 if (*phwndClient)
3897 {
3898 if (pswpFrame)
3899 {
3900 // position frame
3901 WinSetWindowPos(hwndFrame,
3902 pswpFrame->hwndInsertBehind,
3903 pswpFrame->x,
3904 pswpFrame->y,
3905 pswpFrame->cx,
3906 pswpFrame->cy,
3907 pswpFrame->fl);
3908
3909 // position client
3910 // WinQueryWindowRect(hwndFrame, &rclClient);
3911 // doesn't work because it might be invisible V0.9.7 (2000-12-08) [umoeller]
3912 rclClient.xLeft = 0;
3913 rclClient.yBottom = 0;
3914 rclClient.xRight = pswpFrame->cx;
3915 rclClient.yTop = pswpFrame->cy;
3916 WinCalcFrameRect(hwndFrame,
3917 &rclClient,
3918 TRUE); // calc client from frame
3919 WinSetWindowPos(*phwndClient,
3920 HWND_TOP,
3921 rclClient.xLeft,
3922 rclClient.yBottom,
3923 rclClient.xRight - rclClient.xLeft,
3924 rclClient.yTop - rclClient.yBottom,
3925 SWP_MOVE | SWP_SIZE | SWP_SHOW);
3926 }
3927 }
3928 }
3929 return (hwndFrame);
3930}
3931
3932/*
3933 *@@ winhCreateObjectWindow:
3934 * creates an object window of the specified
3935 * window class, which you should have registered
3936 * before calling this. pvCreateParam will be
3937 * given to the window on WM_CREATE.
3938 *
3939 * Returns the HWND of the object window or
3940 * NULLHANDLE on errors.
3941 *
3942 *@@added V0.9.3 (2000-04-17) [umoeller]
3943 *@@changed V0.9.7 (2001-01-17) [umoeller]: made this a function from a macro
3944 */
3945
3946HWND winhCreateObjectWindow(const char *pcszWindowClass, // in: PM window class name
3947 PVOID pvCreateParam) // in: create param
3948{
3949 return (WinCreateWindow(HWND_OBJECT,
3950 (PSZ)pcszWindowClass,
3951 (PSZ)"",
3952 0,
3953 0,0,0,0,
3954 0,
3955 HWND_BOTTOM,
3956 0,
3957 pvCreateParam,
3958 NULL));
3959}
3960
3961/*
3962 *@@ winhCreateControl:
3963 * creates a control with a size and position of 0.
3964 *
3965 *@@added V0.9.9 (2001-03-13) [umoeller]
3966 */
3967
3968HWND winhCreateControl(HWND hwndParentAndOwner, // in: owner and parent window
3969 const char *pcszClass, // in: window class (e.g. WC_BUTTON)
3970 const char *pcszText, // in: window title
3971 ULONG ulStyle, // in: control style
3972 ULONG ulID) // in: control ID
3973{
3974 return (WinCreateWindow(hwndParentAndOwner,
3975 (PSZ)pcszClass,
3976 (PSZ)pcszText,
3977 ulStyle,
3978 0, 0, 0, 0,
3979 hwndParentAndOwner,
3980 HWND_TOP,
3981 ulID,
3982 NULL,
3983 NULL));
3984}
3985
3986/*
3987 *@@ winhRepaintWindows:
3988 * this repaints all children of hwndParent.
3989 * If this is passed as HWND_DESKTOP, the
3990 * whole screen is repainted.
3991 *
3992 *@@changed V0.9.7 (2000-12-13) [umoeller]: hwndParent was never respected, fixed
3993 */
3994
3995VOID winhRepaintWindows(HWND hwndParent)
3996{
3997 HWND hwndTop;
3998 HENUM henum = WinBeginEnumWindows(hwndParent);
3999 while ((hwndTop = WinGetNextWindow(henum)))
4000 if (WinIsWindowShowing(hwndTop))
4001 WinInvalidateRect(hwndTop, NULL, TRUE);
4002 WinEndEnumWindows(henum);
4003}
4004
4005/*
4006 *@@ winhFindMsgQueue:
4007 * returns the message queue which matches
4008 * the given process and thread IDs. Since,
4009 * per IBM definition, every thread may only
4010 * have one MQ, this should be unique.
4011 *
4012 *@@added V0.9.2 (2000-03-08) [umoeller]
4013 */
4014
4015HMQ winhFindMsgQueue(PID pid, // in: process ID
4016 TID tid, // in: thread ID
4017 HAB* phab) // out: anchor block
4018{
4019 HWND hwndThis = 0,
4020 rc = 0;
4021 HENUM henum = WinBeginEnumWindows(HWND_OBJECT);
4022 while ((hwndThis = WinGetNextWindow(henum)))
4023 {
4024 CHAR szClass[200];
4025 if (WinQueryClassName(hwndThis, sizeof(szClass), szClass))
4026 {
4027 if (strcmp(szClass, "#32767") == 0)
4028 {
4029 // message queue window:
4030 PID pidWin = 0;
4031 TID tidWin = 0;
4032 WinQueryWindowProcess(hwndThis,
4033 &pidWin,
4034 &tidWin);
4035 if ( (pidWin == pid)
4036 && (tidWin == tid)
4037 )
4038 {
4039 // get HMQ from window words
4040 rc = WinQueryWindowULong(hwndThis, QWL_HMQ);
4041 if (rc)
4042 if (phab)
4043 *phab = WinQueryAnchorBlock(hwndThis);
4044 break;
4045 }
4046 }
4047 }
4048 }
4049 WinEndEnumWindows(henum);
4050
4051 return (rc);
4052}
4053
4054/*
4055 *@@ winhFindHardErrorWindow:
4056 * this searches all children of HWND_OBJECT
4057 * for the PM hard error windows, which are
4058 * invisible most of the time. When a hard
4059 * error occurs, that window is made a child
4060 * of HWND_DESKTOP instead.
4061 *
4062 * Stolen from ProgramCommander/2 (C) Roman Stangl.
4063 *
4064 *@@added V0.9.3 (2000-04-27) [umoeller]
4065 */
4066
4067VOID winhFindPMErrorWindows(HWND *phwndHardError, // out: hard error window
4068 HWND *phwndSysError) // out: system error window
4069{
4070 PID pidObject; // HWND_OBJECT's process and thread id
4071 TID tidObject;
4072 PID pidObjectChild; // HWND_OBJECT's child window process and thread id
4073 TID tidObjectChild;
4074 HENUM henumObject; // HWND_OBJECT enumeration handle
4075 HWND hwndObjectChild; // Window handle of current HWND_OBJECT child
4076 UCHAR ucClassName[32]; // Window class e.g. #1 for WC_FRAME
4077 CLASSINFO classinfoWindow; // Class info of current HWND_OBJECT child
4078
4079 *phwndHardError = NULLHANDLE;
4080 *phwndSysError = NULLHANDLE;
4081
4082 // query HWND_OBJECT's window process
4083 WinQueryWindowProcess(WinQueryObjectWindow(HWND_DESKTOP), &pidObject, &tidObject);
4084 // enumerate all child windows of HWND_OBJECT
4085 henumObject = WinBeginEnumWindows(HWND_OBJECT);
4086 while ((hwndObjectChild = WinGetNextWindow(henumObject)) != NULLHANDLE)
4087 {
4088 // see if the current HWND_OBJECT child window runs in the
4089 // process of HWND_OBJECT (PM)
4090 WinQueryWindowProcess(hwndObjectChild, &pidObjectChild, &tidObjectChild);
4091 if (pidObject == pidObjectChild)
4092 {
4093 // get the child window's data
4094 WinQueryClassName(hwndObjectChild,
4095 sizeof(ucClassName),
4096 (PCH)ucClassName);
4097 WinQueryClassInfo(WinQueryAnchorBlock(hwndObjectChild),
4098 (PSZ)ucClassName,
4099 &classinfoWindow);
4100 if ( (!strcmp((PSZ)ucClassName, "#1")
4101 || (classinfoWindow.flClassStyle & CS_FRAME))
4102 )
4103 {
4104 // if the child window is a frame window and running in
4105 // HWND_OBJECT's (PM's) window process, it must be the
4106 // PM Hard Error or System Error window
4107 WinQueryClassName(WinWindowFromID(hwndObjectChild,
4108 FID_CLIENT),
4109 sizeof(ucClassName),
4110 (PSZ)ucClassName);
4111 if (!strcmp((PSZ)ucClassName, "PM Hard Error"))
4112 {
4113 *phwndHardError = hwndObjectChild;
4114 if (*phwndSysError)
4115 // we found the other one already:
4116 // stop searching, we got both
4117 break;
4118 }
4119 else
4120 {
4121 printf("Utility: Found System Error %08X\n", (int)hwndObjectChild);
4122 *phwndSysError = hwndObjectChild;
4123 if (*phwndHardError)
4124 // we found the other one already:
4125 // stop searching, we got both
4126 break;
4127 }
4128 }
4129 } // end if (pidObject == pidObjectChild)
4130 } // end while ((hwndObjectChild = WinGetNextWindow(henumObject)) != NULLHANDLE)
4131 WinEndEnumWindows(henumObject);
4132}
4133
4134/*
4135 *@@ winhCreateFakeDesktop:
4136 * this routine creates and displays a frameless window over
4137 * the whole screen in the color of PM's Desktop to fool the
4138 * user that all windows have been closed (which in fact might
4139 * not be the case).
4140 *
4141 * This window's background color is set to the Desktop's
4142 * (PM's one, not the WPS's one).
4143 *
4144 * Returns the HWND of this window.
4145 */
4146
4147HWND winhCreateFakeDesktop(HWND hwndSibling)
4148{
4149 // presparam for background
4150 typedef struct _BACKGROUND
4151 {
4152 ULONG cb; // length of the aparam parameter, in bytes
4153 ULONG id; // attribute type identity
4154 ULONG cb2; // byte count of the ab parameter
4155 RGB rgb; // attribute value
4156 } BACKGROUND;
4157
4158 BACKGROUND background;
4159 LONG lDesktopColor;
4160
4161 // create fake desktop window = empty window with
4162 // the size of full screen
4163 lDesktopColor = WinQuerySysColor(HWND_DESKTOP,
4164 SYSCLR_BACKGROUND,
4165 0);
4166 background.cb = sizeof(background.id)
4167 + sizeof(background.cb)
4168 + sizeof(background.rgb);
4169 background.id = PP_BACKGROUNDCOLOR;
4170 background.cb2 = sizeof(RGB);
4171 background.rgb.bBlue = (CHAR1FROMMP(lDesktopColor));
4172 background.rgb.bGreen= (CHAR2FROMMP(lDesktopColor));
4173 background.rgb.bRed = (CHAR3FROMMP(lDesktopColor));
4174
4175 return (WinCreateWindow(HWND_DESKTOP, // parent window
4176 WC_FRAME, // class name
4177 "", // window text
4178 WS_VISIBLE, // window style
4179 0, 0, // position and size
4180 WinQuerySysValue(HWND_DESKTOP, SV_CXSCREEN),
4181 WinQuerySysValue(HWND_DESKTOP, SV_CYSCREEN),
4182 NULLHANDLE, // owner window
4183 hwndSibling, // sibling window
4184 1, // window id
4185 NULL, // control data
4186 &background)); // presentation parms
4187}
4188
4189/*
4190 *@@ winhAssertWarp4Notebook:
4191 * this takes hwndDlg as a notebook dialog page and
4192 * goes thru all its controls. If a control with an
4193 * ID <= udIdThreshold is found, this is assumed to
4194 * be a button which is to be given the BS_NOTEBOOKBUTTON
4195 * style. You should therefore give all your button
4196 * controls which should be moved such an ID.
4197 *
4198 * You can also specify how many dialog units
4199 * all the other controls will be moved downward in
4200 * ulDownUnits; this is useful to fill up the space
4201 * which was used by the buttons before moving them.
4202 * Returns TRUE if anything was changed.
4203 *
4204 * This function is useful if you wish to create
4205 * notebook pages using dlgedit.exe which are compatible
4206 * with both Warp 3 and Warp 4. This should be executed
4207 * in WM_INITDLG of the notebook dlg function if the app
4208 * has determined that it is running on Warp 4.
4209 */
4210
4211BOOL winhAssertWarp4Notebook(HWND hwndDlg,
4212 USHORT usIdThreshold, // in: ID threshold
4213 ULONG ulDownUnits) // in: dialog units or 0
4214{
4215 BOOL brc = FALSE;
4216
4217 if (doshIsWarp4())
4218 {
4219 POINTL ptl;
4220 HWND hwndItem;
4221 HENUM henum = 0;
4222
4223 BOOL fIsVisible = WinIsWindowVisible(hwndDlg);
4224 if (ulDownUnits)
4225 {
4226 ptl.x = 0;
4227 ptl.y = ulDownUnits;
4228 WinMapDlgPoints(hwndDlg, &ptl, 1, TRUE);
4229 }
4230
4231 if (fIsVisible)
4232 WinEnableWindowUpdate(hwndDlg, FALSE);
4233
4234 henum = WinBeginEnumWindows(hwndDlg);
4235 while ((hwndItem = WinGetNextWindow(henum)))
4236 {
4237 USHORT usId = WinQueryWindowUShort(hwndItem, QWS_ID);
4238 // _Pmpf(("hwndItem: 0x%lX, ID: 0x%lX", hwndItem, usId));
4239 if (usId <= usIdThreshold)
4240 {
4241 // pushbutton to change:
4242 // _Pmpf((" Setting bit"));
4243 WinSetWindowBits(hwndItem,
4244 QWL_STYLE,
4245 BS_NOTEBOOKBUTTON, BS_NOTEBOOKBUTTON);
4246 brc = TRUE;
4247 }
4248 else
4249 // no pushbutton to change: move downwards
4250 // if desired
4251 if (ulDownUnits)
4252 {
4253 SWP swp;
4254 // _Pmpf(("Moving downwards %d pixels", ptl.y));
4255 WinQueryWindowPos(hwndItem, &swp);
4256 WinSetWindowPos(hwndItem, 0,
4257 swp.x,
4258 swp.y - ptl.y,
4259 0, 0,
4260 SWP_MOVE);
4261 }
4262 }
4263 WinEndEnumWindows(henum);
4264
4265 if (fIsVisible)
4266 WinShowWindow(hwndDlg, TRUE);
4267 }
4268
4269 return (brc);
4270}
4271
4272/*
4273 *@@ winhDrawFormattedText:
4274 * this func takes a rectangle and draws pszText into
4275 * it, breaking the words as neccessary. The line spacing
4276 * is determined from the font currently selected in hps.
4277 *
4278 * As opposed to WinDrawText, this can draw several lines
4279 * at once, and format the _complete_ text according to the
4280 * flCmd parameter, which is like with WinDrawText.
4281 *
4282 * After this function returns, *prcl is modified like this:
4283 *
4284 * -- yTop and yBottom contain the upper and lower boundaries
4285 * which were needed to draw the text. This depends on
4286 * whether DT_TOP etc. were specified.
4287 * To get the height of the rectangle used, calculate the
4288 * delta between yTop and yBottom.
4289 *
4290 * -- xLeft and xRight are modified to contain the outmost
4291 * left and right coordinates which were needed to draw
4292 * the text. This will be set to the longest line which
4293 * was encountered.
4294 *
4295 * You can specify DT_QUERYEXTENT with flDraw to only have
4296 * these text boundaries calculated without actually drawing.
4297 *
4298 * This returns the number of lines drawn.
4299 *
4300 * Note that this calls WinDrawText with DT_TEXTATTRS set,
4301 * that is, the current text primitive attributes will be
4302 * used (fonts and colors).
4303 *
4304 *@@changed V0.9.0 [umoeller]: prcl.xLeft and xRight are now updated too upon return
4305 */
4306
4307ULONG winhDrawFormattedText(HPS hps, // in: presentation space; its settings
4308 // are used, but not altered
4309 PRECTL prcl, // in/out: rectangle to use for drawing
4310 // (modified)
4311 const char *pcszText, // in: text to draw (zero-terminated)
4312 ULONG flCmd) // in: flags like in WinDrawText; I have
4313 // only tested DT_TOP and DT_LEFT though.
4314 // DT_WORDBREAK | DT_TEXTATTRS are always
4315 // set.
4316 // You can specify DT_QUERYEXTENT to only
4317 // have prcl calculated without drawing.
4318{
4319 PSZ p = (PSZ)pcszText;
4320 LONG lDrawn = 1,
4321 lTotalDrawn = 0,
4322 lLineCount = 0,
4323 lOrigYTop = prcl->yTop;
4324 ULONG ulTextLen = strlen(pcszText),
4325 ulCharHeight,
4326 flCmd2,
4327 xLeftmost = prcl->xRight,
4328 xRightmost = prcl->xLeft;
4329 RECTL rcl2;
4330
4331 flCmd2 = flCmd | DT_WORDBREAK | DT_TEXTATTRS;
4332
4333 ulCharHeight = gpihQueryLineSpacing(hps);
4334
4335 while ( (lDrawn)
4336 && (lTotalDrawn < ulTextLen)
4337 )
4338 {
4339 memcpy(&rcl2, prcl, sizeof(rcl2));
4340 lDrawn = WinDrawText(hps,
4341 ulTextLen-lTotalDrawn,
4342 p,
4343 &rcl2,
4344 0, 0, // colors
4345 flCmd2);
4346
4347 // update char counters
4348 p += lDrawn;
4349 lTotalDrawn += lDrawn;
4350
4351 // update x extents
4352 if (rcl2.xLeft < xLeftmost)
4353 xLeftmost = rcl2.xLeft;
4354 if (rcl2.xRight > xRightmost)
4355 xRightmost = rcl2.xRight;
4356
4357 // update y for next line
4358 prcl->yTop -= ulCharHeight;
4359
4360 // increase line count
4361 lLineCount++;
4362 }
4363 prcl->xLeft = xLeftmost;
4364 prcl->xRight = xRightmost;
4365 prcl->yBottom = prcl->yTop;
4366 prcl->yTop = lOrigYTop;
4367
4368 return (lLineCount);
4369}
4370
4371/*
4372 *@@ winhQuerySwitchList:
4373 * returns the switch list in a newly
4374 * allocated buffer. This does the
4375 * regular double WinQuerySwitchList
4376 * call to first get the no. of items
4377 * and then get the items.
4378 *
4379 * The no. of items can be found in
4380 * the returned SWBLOCK.cwsentry.
4381 *
4382 * Returns NULL on errors. Use
4383 * free() to free the return value.
4384 *
4385 *@@added V0.9.7 (2000-12-06) [umoeller]
4386 */
4387
4388PSWBLOCK winhQuerySwitchList(HAB hab)
4389{
4390 ULONG cItems = WinQuerySwitchList(hab, NULL, 0);
4391 ULONG ulBufSize = (cItems * sizeof(SWENTRY)) + sizeof(HSWITCH);
4392 PSWBLOCK pSwBlock = (PSWBLOCK)malloc(ulBufSize);
4393 if (pSwBlock)
4394 {
4395 cItems = WinQuerySwitchList(hab, pSwBlock, ulBufSize);
4396 if (!cItems)
4397 {
4398 free(pSwBlock);
4399 pSwBlock = NULL;
4400 }
4401 }
4402
4403 return (pSwBlock);
4404}
4405
4406/*
4407 *@@ winhQueryTasklistWindow:
4408 * returns the window handle of the PM task list.
4409 *
4410 *@@added V0.9.7 (2000-12-07) [umoeller]
4411 */
4412
4413HWND winhQueryTasklistWindow(VOID)
4414{
4415 SWBLOCK swblock;
4416 HWND hwndTasklist = winhQueryTasklistWindow();
4417 // the tasklist has entry #0 in the SWBLOCK
4418 WinQuerySwitchList(NULLHANDLE, &swblock, sizeof(SWBLOCK));
4419 return (swblock.aswentry[0].swctl.hwnd);
4420}
4421
4422/*
4423 *@@ winhKillTasklist:
4424 * this will destroy the Tasklist (window list) window.
4425 * Note: you will only be able to get it back after a
4426 * reboot, not a WPS restart. Only for use at shutdown and such.
4427 * This trick by Uri J. Stern at
4428 * http://zebra.asta.fh-weingarten.de/os2/Snippets/Howt8881.HTML
4429 */
4430
4431VOID winhKillTasklist(VOID)
4432{
4433 HWND hwndTasklist = winhQueryTasklistWindow();
4434 WinPostMsg(hwndTasklist,
4435 0x0454, // undocumented msg for killing tasklist
4436 NULL, NULL);
4437}
4438
4439// the following must be added for EMX (99-10-22) [umoeller]
4440#ifndef NERR_BufTooSmall
4441 #define NERR_BASE 2100
4442 #define NERR_BufTooSmall (NERR_BASE+23)
4443 // the API return buffer is too small
4444#endif
4445
4446/*
4447 *@@ winhQueryPendingSpoolJobs:
4448 * returns the number of pending print jobs in the spooler
4449 * or 0 if none. Useful for testing before shutdown.
4450 */
4451
4452ULONG winhQueryPendingSpoolJobs(VOID)
4453{
4454 // BOOL rcPending = FALSE;
4455 ULONG ulTotalJobCount = 0;
4456
4457 SPLERR splerr;
4458 USHORT jobCount;
4459 ULONG cbBuf;
4460 ULONG cTotal;
4461 ULONG cReturned;
4462 ULONG cbNeeded;
4463 ULONG ulLevel;
4464 ULONG i,j;
4465 PSZ pszComputerName;
4466 PBYTE pBuf = NULL;
4467 PPRQINFO3 prq;
4468 PPRJINFO2 prj2;
4469
4470 ulLevel = 4L;
4471 pszComputerName = (PSZ)NULL;
4472 splerr = SplEnumQueue(pszComputerName, ulLevel, pBuf, 0L, // cbBuf
4473 &cReturned, &cTotal,
4474 &cbNeeded, NULL);
4475 if ( (splerr == ERROR_MORE_DATA)
4476 || (splerr == NERR_BufTooSmall)
4477 )
4478 {
4479 if (!DosAllocMem((PPVOID)&pBuf,
4480 cbNeeded,
4481 PAG_READ | PAG_WRITE | PAG_COMMIT))
4482 {
4483 cbBuf = cbNeeded;
4484 splerr = SplEnumQueue(pszComputerName, ulLevel, pBuf, cbBuf,
4485 &cReturned, &cTotal,
4486 &cbNeeded, NULL);
4487 if (splerr == NO_ERROR)
4488 {
4489 // set pointer to point to the beginning of the buffer
4490 prq = (PPRQINFO3)pBuf;
4491
4492 // cReturned has the count of the number of PRQINFO3 structures
4493 for (i = 0;
4494 i < cReturned;
4495 i++)
4496 {
4497 // save the count of jobs; there are this many PRJINFO2
4498 // structures following the PRQINFO3 structure
4499 jobCount = prq->cJobs;
4500 // _Pmpf(( "Job count in this queue is %d",jobCount ));
4501
4502 // increment the pointer past the PRQINFO3 structure
4503 prq++;
4504
4505 // set a pointer to point to the first PRJINFO2 structure
4506 prj2=(PPRJINFO2)prq;
4507 for (j = 0;
4508 j < jobCount;
4509 j++)
4510 {
4511 // increment the pointer to point to the next structure
4512 prj2++;
4513 // increase the job count, which we'll return
4514 ulTotalJobCount++;
4515
4516 } // endfor jobCount
4517
4518 // after doing all the job structures, prj2 points to the next
4519 // queue structure; set the pointer for a PRQINFO3 structure
4520 prq = (PPRQINFO3)prj2;
4521 } //endfor cReturned
4522 } // endif NO_ERROR
4523 DosFreeMem(pBuf);
4524 }
4525 } // end if Q level given
4526
4527 return (ulTotalJobCount);
4528}
4529
4530/*
4531 *@@ winhSetNumLock:
4532 * this sets the NumLock key on or off, depending
4533 * on fState.
4534 *
4535 * Based on code from WarpEnhancer, (C) Achim Hasenmller.
4536 *
4537 *@@added V0.9.1 (99-12-18) [umoeller]
4538 */
4539
4540VOID winhSetNumLock(BOOL fState)
4541{
4542 // BOOL fRestoreKBD = FALSE; // Assume we're not going to close Kbd
4543 BYTE KeyStateTable[256];
4544 ULONG ulActionTaken; // Used by DosOpen
4545 HFILE hKbd;
4546
4547 // read keyboard state table
4548 if (WinSetKeyboardStateTable(HWND_DESKTOP, &KeyStateTable[0],
4549 FALSE))
4550 {
4551 // first set the PM state
4552 if (fState)
4553 KeyStateTable[VK_NUMLOCK] |= 0x01; // Turn numlock on
4554 else
4555 KeyStateTable[VK_NUMLOCK] &= 0xFE; // Turn numlock off
4556
4557 // set keyboard state table with new state values
4558 WinSetKeyboardStateTable(HWND_DESKTOP, &KeyStateTable[0], TRUE);
4559 }
4560
4561 // now set the OS/2 keyboard state
4562
4563 // try to open OS/2 keyboard driver
4564 if (!DosOpen("KBD$",
4565 &hKbd, &ulActionTaken,
4566 0, // cbFile
4567 FILE_NORMAL,
4568 OPEN_ACTION_FAIL_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS,
4569 OPEN_SHARE_DENYNONE | OPEN_ACCESS_READWRITE,
4570 NULL))
4571 {
4572 SHIFTSTATE ShiftState;
4573 ULONG DataLen = sizeof(SHIFTSTATE);
4574
4575 memset(&ShiftState, '\0', DataLen);
4576 DosDevIOCtl(hKbd, IOCTL_KEYBOARD, KBD_GETSHIFTSTATE,
4577 NULL, 0L, NULL,
4578 &ShiftState, DataLen, &DataLen);
4579
4580 if (fState)
4581 ShiftState.fsState |= 0x0020; // turn NumLock on
4582 else
4583 ShiftState.fsState &= 0xFFDF; // turn NumLock off
4584
4585 DosDevIOCtl(hKbd, IOCTL_KEYBOARD, KBD_SETSHIFTSTATE,
4586 &ShiftState, DataLen, &DataLen,
4587 NULL, 0L, NULL);
4588 // now close OS/2 keyboard driver
4589 DosClose(hKbd);
4590 }
4591 return;
4592}
4593
4594/*
4595 *@@category: Helpers\PM helpers\Workplace Shell\WPS class list
4596 */
4597
4598/* ******************************************************************
4599 *
4600 * WPS Class List helpers
4601 *
4602 ********************************************************************/
4603
4604/*
4605 *@@ winhQueryWPSClassList:
4606 * this returns the WPS class list in a newly
4607 * allocated buffer. This is just a shortcut to
4608 * the usual double WinEnumObjectClasses call.
4609 *
4610 * The return value is actually of the POBJCLASS type,
4611 * so you better cast this manually. We declare this
4612 * this as PBYTE though because POBJCLASS requires
4613 * INCL_WINWORKPLACE.
4614 * See WinEnumObjectClasses() for details.
4615 *
4616 * Returns NULL on error. Use free()
4617 * to free the return value.
4618 *
4619 *@@added V0.9.0 [umoeller]
4620 */
4621
4622PBYTE winhQueryWPSClassList(VOID)
4623{
4624 ULONG ulSize;
4625 POBJCLASS pObjClass = 0;
4626
4627 // get WPS class list size
4628 if (WinEnumObjectClasses(NULL, &ulSize))
4629 {
4630 // allocate buffer
4631 pObjClass = (POBJCLASS)malloc(ulSize+1);
4632 // and load the classes into it
4633 WinEnumObjectClasses(pObjClass, &ulSize);
4634 }
4635
4636 return ((PBYTE)pObjClass);
4637}
4638
4639/*
4640 *@@ winhQueryWPSClass:
4641 * this returns the POBJCLASS item if pszClass is registered
4642 * with the WPS or NULL if the class could not be found.
4643 *
4644 * The return value is actually of the POBJCLASS type,
4645 * so you better cast this manually. We declare this
4646 * this as PBYTE though because POBJCLASS requires
4647 * INCL_WINWORKPLACE.
4648 *
4649 * This takes as input the return value of winhQueryWPSClassList,
4650 * which you must call first.
4651 *
4652 * <B>Usage:</B>
4653 + PBYTE pClassList = winhQueryWPSClassList(),
4654 + pWPFolder;
4655 + if (pClassList)
4656 + {
4657 + if (pWPFolder = winhQueryWPSClass(pClassList, "WPFolder"))
4658 + ...
4659 + free(pClassList);
4660 + }
4661 *
4662 *@@added V0.9.0 [umoeller]
4663 */
4664
4665PBYTE winhQueryWPSClass(PBYTE pObjClass, // in: buffer returned by
4666 // winhQueryWPSClassList
4667 const char *pszClass) // in: class name to query
4668{
4669 PBYTE pbReturn = 0;
4670
4671 POBJCLASS pocThis = (POBJCLASS)pObjClass;
4672 // now go thru the WPS class list
4673 while (pocThis)
4674 {
4675 if (strcmp(pocThis->pszClassName, pszClass) == 0)
4676 {
4677 pbReturn = (PBYTE)pocThis;
4678 break;
4679 }
4680 // next class
4681 pocThis = pocThis->pNext;
4682 } // end while (pocThis)
4683
4684 return (pbReturn);
4685}
4686
4687/*
4688 *@@ winhRegisterClass:
4689 * this works just like WinRegisterObjectClass,
4690 * except that it returns a more meaningful
4691 * error code than just FALSE in case registering
4692 * fails.
4693 *
4694 * This returns NO_ERROR if the class was successfully
4695 * registered (WinRegisterObjectClass returned TRUE).
4696 *
4697 * Otherwise, we do a DosLoadModule if maybe the DLL
4698 * couldn't be loaded in the first place. If DosLoadModule
4699 * did not return NO_ERROR, this function returns that
4700 * return code, which can be:
4701 *
4702 * -- 2 ERROR_FILE_NOT_FOUND: pcszModule does not exist
4703 * -- 2 ERROR_FILE_NOT_FOUND
4704 * -- 3 ERROR_PATH_NOT_FOUND
4705 * -- 4 ERROR_TOO_MANY_OPEN_FILES
4706 * -- 5 ERROR_ACCESS_DENIED
4707 * -- 8 ERROR_NOT_ENOUGH_MEMORY
4708 * -- 11 ERROR_BAD_FORMAT
4709 * -- 26 ERROR_NOT_DOS_DISK (unknown media type)
4710 * -- 32 ERROR_SHARING_VIOLATION
4711 * -- 33 ERROR_LOCK_VIOLATION
4712 * -- 36 ERROR_SHARING_BUFFER_EXCEEDED
4713 * -- 95 ERROR_INTERRUPT (interrupted system call)
4714 * -- 108 ERROR_DRIVE_LOCKED (by another process)
4715 * -- 123 ERROR_INVALID_NAME (illegal character or FS name not valid)
4716 * -- 127 ERROR_PROC_NOT_FOUND (DosQueryProcAddr error)
4717 * -- 180 ERROR_INVALID_SEGMENT_NUMBER
4718 * -- 182 ERROR_INVALID_ORDINAL
4719 * -- 190 ERROR_INVALID_MODULETYPE (probably an application)
4720 * -- 191 ERROR_INVALID_EXE_SIGNATURE (probably not LX DLL)
4721 * -- 192 ERROR_EXE_MARKED_INVALID (by linker)
4722 * -- 194 ERROR_ITERATED_DATA_EXCEEDS_64K (in a DLL segment)
4723 * -- 195 ERROR_INVALID_MINALLOCSIZE
4724 * -- 196 ERROR_DYNLINK_FROM_INVALID_RING
4725 * -- 198 ERROR_INVALID_SEGDPL
4726 * -- 199 ERROR_AUTODATASEG_EXCEEDS_64K
4727 * -- 201 ERROR_RELOCSRC_CHAIN_EXCEEDS_SEGLIMIT
4728 * -- 206 ERROR_FILENAME_EXCED_RANGE (not matching 8+3 spec)
4729 * -- 295 ERROR_INIT_ROUTINE_FAILED (DLL init routine failed)
4730 *
4731 * In all these cases, pszBuf may contain a meaningful
4732 * error message from DosLoadModule, especially if an import
4733 * could not be resolved.
4734 *
4735 * Still worse, if DosLoadModule returned NO_ERROR, we
4736 * probably have some SOM internal error. A probable
4737 * reason is that the parent class of pcszClassName
4738 * is not installed, but that's WPS/SOM internal
4739 * and cannot be queried from outside the WPS context.
4740 *
4741 * In that case, ERROR_OPEN_FAILED (110) is returned.
4742 * That one sounded good to me. ;-)
4743 */
4744
4745APIRET winhRegisterClass(const char* pcszClassName, // in: e.g. "XFolder"
4746 const char* pcszModule, // in: e.g. "C:\XFOLDER\XFLDR.DLL"
4747 PSZ pszBuf, // out: error message from DosLoadModule
4748 ULONG cbBuf) // in: sizeof(*pszBuf), passed to DosLoadModule
4749{
4750 APIRET arc = NO_ERROR;
4751
4752 if (!WinRegisterObjectClass((PSZ)pcszClassName, (PSZ)pcszModule))
4753 {
4754 // failed: do more error checking then, try DosLoadModule
4755 HMODULE hmod = NULLHANDLE;
4756 arc = DosLoadModule(pszBuf, cbBuf,
4757 (PSZ)pcszModule,
4758 &hmod);
4759 if (arc == NO_ERROR)
4760 {
4761 // DosLoadModule succeeded:
4762 // some SOM error then
4763 DosFreeModule(hmod);
4764 arc = ERROR_OPEN_FAILED;
4765 }
4766 }
4767 // else: ulrc still 0 (== no error)
4768
4769 return (arc);
4770}
4771
4772/*
4773 *@@ winhIsClassRegistered:
4774 * quick one-shot function which checks if
4775 * a class is currently registered. Calls
4776 * winhQueryWPSClassList and winhQueryWPSClass
4777 * in turn.
4778 *
4779 *@@added V0.9.2 (2000-02-26) [umoeller]
4780 */
4781
4782BOOL winhIsClassRegistered(const char *pcszClass)
4783{
4784 BOOL brc = FALSE;
4785 PBYTE pClassList = winhQueryWPSClassList();
4786 if (pClassList)
4787 {
4788 if (winhQueryWPSClass(pClassList, pcszClass))
4789 brc = TRUE;
4790 free(pClassList);
4791 }
4792
4793 return (brc);
4794}
4795
4796/*
4797 *@@category: Helpers\PM helpers\Workplace Shell
4798 */
4799
4800/*
4801 *@@ winhResetWPS:
4802 * restarts the WPS using PrfReset. Returns
4803 * one of the following:
4804 *
4805 * -- 0: no error.
4806 * -- 1: PrfReset failed.
4807 * -- 2 or 4: PrfQueryProfile failed.
4808 * -- 3: malloc() failed.
4809 *
4810 *@@added V0.9.4 (2000-07-01) [umoeller]
4811 */
4812
4813ULONG winhResetWPS(HAB hab)
4814{
4815 ULONG ulrc = 0;
4816 // find out current profile names
4817 PRFPROFILE Profiles;
4818 Profiles.cchUserName = Profiles.cchSysName = 0;
4819 // first query their file name lengths
4820 if (PrfQueryProfile(hab, &Profiles))
4821 {
4822 // allocate memory for filenames
4823 Profiles.pszUserName = (PSZ)malloc(Profiles.cchUserName);
4824 Profiles.pszSysName = (PSZ)malloc(Profiles.cchSysName);
4825
4826 if (Profiles.pszSysName)
4827 {
4828 // get filenames
4829 if (PrfQueryProfile(hab, &Profiles))
4830 {
4831
4832 // "change" INIs to these filenames:
4833 // THIS WILL RESET THE WPS
4834 if (PrfReset(hab, &Profiles) == FALSE)
4835 ulrc = 1;
4836 free(Profiles.pszSysName);
4837 free(Profiles.pszUserName);
4838 }
4839 else
4840 ulrc = 2;
4841 }
4842 else
4843 ulrc = 3;
4844 }
4845 else
4846 ulrc = 4;
4847
4848 return (ulrc);
4849}
Note: See TracBrowser for help on using the repository browser.