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

Last change on this file since 337 was 337, checked in by pr, 19 years ago

Add winhStoreWindowPos. Bug 458.

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