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

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

Lots of updates from the last week for conditional compiles and other stuff.

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