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

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

Misc changes.

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