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

Last change on this file since 242 was 242, checked in by umoeller, 23 years ago

First attempt at new container contol.

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