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

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

Fix winhStoreWindowPos() Bug 903.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 189.4 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 *@@changed XWP V1.0.7 (2006-12-16) [pr]: detect screen height/width @@fixes 903
2665 */
2666
2667BOOL winhStoreWindowPos(HWND hwnd, // in: window to save
2668 HINI hIni, // in: INI file (or HINI_USER/SYSTEM)
2669 const char *pcszApp, // in: INI application name
2670 const char *pcszKey) // in: INI key name
2671{
2672 BOOL brc = FALSE;
2673 SWP swp;
2674
2675 if (WinQueryWindowPos(hwnd, &swp))
2676 {
2677 ULONG ulSizePP = WinGetFrameTreePPSize(hwnd);
2678 ULONG ulSize = sizeof(STOREPOS) + ulSizePP;
2679 PSTOREPOS pStorePos;
2680
2681 if ((pStorePos = malloc(ulSize)))
2682 {
2683 // This first bit is guesswork as I don't know what it all means,
2684 // but it always seems to be the same everywhere I've looked.
2685 pStorePos->usMagic = 0x7B6A;
2686 pStorePos->ulRes1 = 1;
2687 pStorePos->ulRes2 = 1;
2688 pStorePos->ulRes3 = 0xFFFFFFFF;
2689 pStorePos->ulRes4 = 0xFFFFFFFF;
2690
2691 pStorePos->ulFlags = swp.fl;
2692 pStorePos->usXPos = pStorePos->usRestoreXPos = swp.x;
2693 pStorePos->usYPos = pStorePos->usRestoreYPos = swp.y;
2694 pStorePos->usWidth = pStorePos->usRestoreWidth = swp.cx;
2695 pStorePos->usHeight = pStorePos->usRestoreHeight = swp.cy;
2696 if (swp.fl & (SWP_MAXIMIZE | SWP_MINIMIZE))
2697 {
2698 pStorePos->usRestoreXPos = WinQueryWindowUShort(hwnd, QWS_XRESTORE);
2699 pStorePos->usRestoreYPos = WinQueryWindowUShort(hwnd, QWS_YRESTORE);
2700 pStorePos->usRestoreWidth = WinQueryWindowUShort(hwnd, QWS_CXRESTORE);
2701 pStorePos->usRestoreHeight = WinQueryWindowUShort(hwnd, QWS_CYRESTORE);
2702 }
2703
2704 pStorePos->usMinXPos = WinQueryWindowUShort(hwnd, QWS_XMINIMIZE);
2705 pStorePos->usMinYPos = WinQueryWindowUShort(hwnd, QWS_YMINIMIZE);
2706 pStorePos->ulScreenWidth = WinQuerySysValue(HWND_DESKTOP, SV_CXSCREEN);;
2707 pStorePos->ulScreenHeight = WinQuerySysValue(HWND_DESKTOP, SV_CYSCREEN);;
2708 pStorePos->ulPPLen = WinGetFrameTreePPs(hwnd, ulSizePP, (PSZ)(pStorePos + 1));
2709 ulSize = pStorePos->ulPPLen + sizeof(STOREPOS);
2710 brc = PrfWriteProfileData(hIni, (PSZ)pcszApp, (PSZ)pcszKey, pStorePos, ulSize);
2711 free(pStorePos);
2712 }
2713 }
2714 return brc;
2715}
2716
2717/*
2718 *@@ winhAdjustControls:
2719 * helper function for dynamically adjusting a window's
2720 * controls when the window is resized.
2721 *
2722 * This is most useful with dialogs loaded from resources
2723 * which should be sizeable. Normally, when the dialog
2724 * frame is resized, the controls stick to their positions,
2725 * and for dialogs with many controls, programming the
2726 * changes can be tiresome.
2727 *
2728 * Enter this function. ;-) Basically, this takes a
2729 * static array of MPARAM's as input (plus one dynamic
2730 * storage area for the window positions).
2731 *
2732 * This function must get called in three contexts:
2733 * during WM_INITDLG, during WM_WINDOWPOSCHANGED, and
2734 * during WM_DESTROY, with varying parameters.
2735 *
2736 * In detail, there are four things you need to do to make
2737 * this work:
2738 *
2739 * 1) Set up a static array (as a global variable) of
2740 * MPARAM's, one for each control in your array.
2741 * Each MPARAM will have the control's ID and the
2742 * XAC_* flags (winh.h) how the control shall be moved.
2743 * Use MPFROM2SHORT to easily create this. Example:
2744 *
2745 + MPARAM ampControlFlags[] =
2746 + { MPFROM2SHORT(ID_CONTROL_1, XAC_MOVEX),
2747 + MPFROM2SHORT(ID_CONTROL_2, XAC_SIZEY),
2748 + ...
2749 + }
2750 *
2751 * This can safely be declared as a global variable
2752 * because this data will only be read and never
2753 * changed by this function.
2754 *
2755 * 2) In WM_INITDLG of your dialog function, set up
2756 * an XADJUSTCTRLS structure, preferrably in your
2757 * window words (QWL_USER).
2758 *
2759 * ZERO THAT STRUCTURE (memset(&xac, 0, sizeof(XADJUSTCTRLS),
2760 * or this func will not work (because it will intialize
2761 * things on the first WM_WINDOWPOSCHANGED).
2762 *
2763 * 3) Intercept WM_WINDOWPOSCHANGED:
2764 *
2765 + case WM_WINDOWPOSCHANGED:
2766 + {
2767 + // this msg is passed two SWP structs:
2768 + // one for the old, one for the new data
2769 + // (from PM docs)
2770 + PSWP pswpNew = PVOIDFROMMP(mp1);
2771 + PSWP pswpOld = pswpNew + 1;
2772 +
2773 + // resizing?
2774 + if (pswpNew->fl & SWP_SIZE)
2775 + {
2776 + PXADJUSTCTRLS pxac = ... // get it from your window words
2777 +
2778 + winhAdjustControls(hwndDlg, // dialog
2779 + ampControlFlags, // MPARAMs array
2780 + sizeof(ampControlFlags) / sizeof(MPARAM),
2781 + // items count
2782 + pswpNew, // mp1
2783 + pxac); // storage area
2784 + }
2785 + mrc = WinDefDlgProc(hwnd, msg, mp1, mp2); ...
2786 *
2787 * 4) In WM_DESTROY, call this function again with pmpFlags,
2788 * pswpNew, and pswpNew set to NULL. This will clean up the
2789 * data which has been allocated internally (pointed to from
2790 * the XADJUSTCTRLS structure).
2791 * Don't forget to free your storage for XADJUSTCTLRS
2792 * _itself_, that's the job of the caller.
2793 *
2794 * This might sound complicated, but it's a lot easier than
2795 * having to write dozens of WinSetWindowPos calls oneself.
2796 *
2797 *@@added V0.9.0 [umoeller]
2798 *@@changed V0.9.19 (2002-04-13) [umoeller]: added correlation for entry field repositioning, this was always off
2799 */
2800
2801BOOL winhAdjustControls(HWND hwndDlg, // in: dialog (req.)
2802 const MPARAM *pmpFlags, // in: init flags or NULL for cleanup
2803 ULONG ulCount, // in: item count (req.)
2804 PSWP pswpNew, // in: pswpNew from WM_WINDOWPOSCHANGED or NULL for cleanup
2805 PXADJUSTCTRLS pxac) // in: adjust-controls storage area (req.)
2806{
2807 BOOL brc = FALSE;
2808 ULONG ul = 0;
2809
2810 if ((pmpFlags) && (pxac))
2811 {
2812 PSWP pswpThis;
2813 const MPARAM *pmpThis;
2814 LONG ldcx, ldcy;
2815 ULONG cWindows = 0;
2816
2817 // V0.9.19 (2002-04-13) [umoeller]
2818 LONG cxMarginEF = 3 * WinQuerySysValue(HWND_DESKTOP, SV_CXBORDER);
2819 LONG cyMarginEF = 3 * WinQuerySysValue(HWND_DESKTOP, SV_CYBORDER);
2820
2821 // setup mode:
2822 if (pxac->fInitialized == FALSE)
2823 {
2824 // first call: get all the SWP's
2825 WinQueryWindowPos(hwndDlg, &pxac->swpMain);
2826 // _Pmpf(("winhAdjustControls: queried main cx = %d, cy = %d",
2827 // pxac->swpMain.cx, pxac->swpMain.cy));
2828
2829 pxac->paswp = (PSWP)malloc(sizeof(SWP) * ulCount);
2830
2831 pswpThis = pxac->paswp;
2832 pmpThis = pmpFlags;
2833
2834 for (ul = 0;
2835 ul < ulCount;
2836 ul++)
2837 {
2838 HWND hwndThis;
2839 CHAR szClass[10];
2840 if (hwndThis = WinWindowFromID(hwndDlg, SHORT1FROMMP(*pmpThis)))
2841 {
2842 WinQueryWindowPos(hwndThis, pswpThis);
2843
2844 // correlate the stupid repositioning of entry fields
2845 // V0.9.19 (2002-04-13) [umoeller]
2846 if ( (WinQueryClassName(hwndThis, sizeof(szClass), szClass)
2847 && (!strcmp(szClass, "#6"))
2848 && (WinQueryWindowULong(hwndThis, QWL_STYLE) & ES_MARGIN))
2849 )
2850 {
2851 pswpThis->x += cxMarginEF;
2852 pswpThis->y += cyMarginEF;
2853 pswpThis->cx -= 2 * cxMarginEF;
2854 pswpThis->cy -= 2 * cyMarginEF;
2855 }
2856
2857 cWindows++;
2858 }
2859
2860 pswpThis++;
2861 pmpThis++;
2862 }
2863
2864 pxac->fInitialized = TRUE;
2865 // _Pmpf(("winhAdjustControls: queried %d controls", cWindows));
2866 }
2867
2868 if (pswpNew)
2869 {
2870 // compute width and height delta
2871 ldcx = (pswpNew->cx - pxac->swpMain.cx);
2872 ldcy = (pswpNew->cy - pxac->swpMain.cy);
2873
2874 // _Pmpf(("winhAdjustControls: new cx = %d, cy = %d",
2875 // pswpNew->cx, pswpNew->cy));
2876
2877 // now adjust the controls
2878 cWindows = 0;
2879 pswpThis = pxac->paswp;
2880 pmpThis = pmpFlags;
2881 for (ul = 0;
2882 ul < ulCount;
2883 ul++)
2884 {
2885 HWND hwndThis;
2886 if (hwndThis = WinWindowFromID(hwndDlg, SHORT1FROMMP(*pmpThis)))
2887 {
2888 LONG x = pswpThis->x,
2889 y = pswpThis->y,
2890 cx = pswpThis->cx,
2891 cy = pswpThis->cy;
2892
2893 ULONG ulSwpFlags = 0;
2894 // get flags for this control
2895 USHORT usFlags = SHORT2FROMMP(*pmpThis);
2896
2897 if (usFlags & XAC_MOVEX)
2898 {
2899 x += ldcx;
2900 ulSwpFlags |= SWP_MOVE;
2901 }
2902 if (usFlags & XAC_MOVEY)
2903 {
2904 y += ldcy;
2905 ulSwpFlags |= SWP_MOVE;
2906 }
2907 if (usFlags & XAC_SIZEX)
2908 {
2909 cx += ldcx;
2910 ulSwpFlags |= SWP_SIZE;
2911 }
2912 if (usFlags & XAC_SIZEY)
2913 {
2914 cy += ldcy;
2915 ulSwpFlags |= SWP_SIZE;
2916 }
2917
2918 if (ulSwpFlags)
2919 {
2920 WinSetWindowPos(hwndThis,
2921 NULLHANDLE, // hwndInsertBehind
2922 x, y, cx, cy,
2923 ulSwpFlags);
2924 cWindows++;
2925 brc = TRUE;
2926 }
2927 }
2928
2929 pswpThis++;
2930 pmpThis++;
2931 }
2932
2933 // _Pmpf(("winhAdjustControls: set %d windows", cWindows));
2934 }
2935 }
2936 else
2937 {
2938 // pxac == NULL:
2939 // cleanup mode
2940 if (pxac->paswp)
2941 free(pxac->paswp);
2942 }
2943
2944 return brc;
2945}
2946
2947/*
2948 *@@ winhCenterWindow:
2949 * centers a window within its parent window. If that's
2950 * the PM desktop, it will be centered according to the
2951 * whole screen.
2952 * For dialog boxes, use WinCenteredDlgBox as a one-shot
2953 * function.
2954 *
2955 * Note: When calling this function, the window should
2956 * not be visible to avoid flickering.
2957 * This func does not show the window either, so call
2958 * WinShowWindow afterwards.
2959 */
2960
2961void winhCenterWindow(HWND hwnd)
2962{
2963 RECTL rclParent;
2964 RECTL rclWindow;
2965
2966 WinQueryWindowRect(hwnd, &rclWindow);
2967 WinQueryWindowRect(WinQueryWindow(hwnd, QW_PARENT), &rclParent);
2968
2969 rclWindow.xLeft = (rclParent.xRight - rclWindow.xRight) / 2;
2970 rclWindow.yBottom = (rclParent.yTop - rclWindow.yTop ) / 2;
2971
2972 WinSetWindowPos(hwnd, NULLHANDLE, rclWindow.xLeft, rclWindow.yBottom,
2973 0, 0, SWP_MOVE);
2974}
2975
2976/*
2977 *@@ winhCenteredDlgBox:
2978 * just like WinDlgBox, but the dlg box is centered on the screen;
2979 * you should mark the dlg template as not visible in the dlg
2980 * editor, or display will flicker.
2981 * As opposed to winhCenterWindow, this _does_ show the window.
2982 */
2983
2984ULONG winhCenteredDlgBox(HWND hwndParent,
2985 HWND hwndOwner,
2986 PFNWP pfnDlgProc,
2987 HMODULE hmod,
2988 ULONG idDlg,
2989 PVOID pCreateParams)
2990{
2991 ULONG ulReply;
2992 HWND hwndDlg = WinLoadDlg(hwndParent,
2993 hwndOwner,
2994 pfnDlgProc,
2995 hmod,
2996 idDlg,
2997 pCreateParams);
2998 winhCenterWindow(hwndDlg);
2999 ulReply = WinProcessDlg(hwndDlg);
3000 WinDestroyWindow(hwndDlg);
3001 return ulReply;
3002}
3003
3004/*
3005 *@@ winhPlaceBesides:
3006 * attempts to place hwnd somewhere besides
3007 * hwndRelative.
3008 *
3009 * fl is presently ignored, but should be
3010 * PLF_SMART for future extensions.
3011 *
3012 * Works only if hwnd is a desktop child.
3013 *
3014 *@@added V0.9.19 (2002-04-17) [umoeller]
3015 *@@changed V1.0.0 (2002-08-26) [umoeller]: fixed cx and cy confusion
3016 */
3017
3018BOOL winhPlaceBesides(HWND hwnd,
3019 HWND hwndRelative,
3020 ULONG fl)
3021{
3022 SWP swpRel,
3023 swpThis;
3024 LONG xNew, yNew;
3025
3026 if ( (WinQueryWindowPos(hwndRelative, &swpRel))
3027 && (WinQueryWindowPos(hwnd, &swpThis))
3028 )
3029 {
3030 HWND hwndRelParent,
3031 hwndThisParent;
3032 POINTL ptlRel = {swpRel.x, swpRel.y};
3033 if ( (hwndRelParent = WinQueryWindow(hwndRelative, QW_PARENT))
3034 && (hwndThisParent = WinQueryWindow(hwnd, QW_PARENT))
3035 && (hwndRelParent != hwndThisParent)
3036 )
3037 {
3038 WinMapWindowPoints(hwndRelParent,
3039 hwndThisParent,
3040 &ptlRel,
3041 1);
3042 }
3043
3044 // place right first
3045 xNew = ptlRel.x + swpRel.cx;
3046 // center vertically
3047 yNew = ptlRel.y + ((swpRel.cy - swpThis.cy) / 2);
3048
3049 // if (xNew + swpThis.cy > WinQuerySysValue(HWND_DESKTOP, SV_CXSCREEN))
3050 // not cy, but cx V1.0.0 (2002-08-26) [umoeller]
3051 if (xNew + swpThis.cx > G_cxScreen)
3052 {
3053 // place left then
3054 xNew = ptlRel.x - swpThis.cx;
3055
3056 if (xNew < 0)
3057 {
3058 // center then
3059 winhCenterWindow(hwnd);
3060 return TRUE;
3061 }
3062 }
3063
3064 return WinSetWindowPos(hwnd,
3065 0,
3066 xNew,
3067 yNew,
3068 0,
3069 0,
3070 SWP_MOVE);
3071 }
3072
3073 return FALSE;
3074}
3075
3076/*
3077 *@@ winhFindWindowBelow:
3078 * finds the window with the same parent
3079 * which sits right below hwndFind in the
3080 * window Z-order.
3081 *
3082 *@@added V0.9.7 (2000-12-04) [umoeller]
3083 */
3084
3085HWND winhFindWindowBelow(HWND hwndFind)
3086{
3087 HWND hwnd = NULLHANDLE,
3088 hwndParent = WinQueryWindow(hwndFind, QW_PARENT);
3089
3090 if (hwndParent)
3091 {
3092 HENUM henum = WinBeginEnumWindows(hwndParent);
3093 HWND hwndThis;
3094 while (hwndThis = WinGetNextWindow(henum))
3095 {
3096 SWP swp;
3097 WinQueryWindowPos(hwndThis, &swp);
3098 if (swp.hwndInsertBehind == hwndFind)
3099 {
3100 hwnd = hwndThis;
3101 break;
3102 }
3103 }
3104 WinEndEnumWindows(henum);
3105 }
3106
3107 return hwnd;
3108}
3109
3110/*
3111 *@@category: Helpers\PM helpers\Presentation parameters
3112 */
3113
3114/* ******************************************************************
3115 *
3116 * Presparams helpers
3117 *
3118 ********************************************************************/
3119
3120/*
3121 *@@ winhQueryWindowFont:
3122 * returns the window font presentation parameter
3123 * in a newly allocated buffer.
3124 *
3125 * Returns NULL on error. Use free()
3126 * to free the return value.
3127 *
3128 *@@added V0.9.1 (2000-02-14) [umoeller]
3129 */
3130
3131PSZ winhQueryWindowFont(HWND hwnd)
3132{
3133 CHAR szNewFont[100] = "";
3134 WinQueryPresParam(hwnd,
3135 PP_FONTNAMESIZE,
3136 0,
3137 NULL,
3138 (ULONG)sizeof(szNewFont),
3139 (PVOID)&szNewFont,
3140 QPF_NOINHERIT);
3141 if (szNewFont[0] != 0)
3142 return strdup(szNewFont);
3143
3144 return NULL;
3145}
3146
3147/*
3148 *@@ winhQueryDefaultFont:
3149 *
3150 *@@added V1.0.1 (2002-11-30) [umoeller]
3151 *@@changed V1.0.4 (2005-09-02) [bvl]: Return 'Combined' fonts on DBCS systems to show DBCS characters properly @@fixes 655
3152 */
3153
3154PCSZ winhQueryDefaultFont(VOID)
3155{
3156 if (doshIsWarp4())
3157 if(nlsDBCS())
3158 return "9.WarpSans Combined";
3159 else
3160 return "9.WarpSans";
3161 else
3162 if(nlsDBCS())
3163 return "8.Helv Combined";
3164 else
3165 return "8.Helv";
3166}
3167
3168/*
3169 *@@ winhQueryMenuSysFont:
3170 * returns the system menu font in a new buffer
3171 * to be free()'d by caller.
3172 *
3173 *@@added V1.0.1 (2003-01-05) [umoeller]
3174 */
3175
3176PSZ winhQueryMenuSysFont(VOID)
3177{
3178 PSZ pszStdMenuFont;
3179 if (!(pszStdMenuFont = prfhQueryProfileData(HINI_USER,
3180 PMINIAPP_SYSTEMFONTS, // "PM_SystemFonts",
3181 PMINIKEY_MENUSFONT, // "Menus",
3182 NULL)))
3183 pszStdMenuFont = prfhQueryProfileData(HINI_USER,
3184 PMINIAPP_SYSTEMFONTS, // "PM_SystemFonts",
3185 PMINIKEY_DEFAULTFONT, // "DefaultFont",
3186 NULL);
3187
3188 return pszStdMenuFont;
3189}
3190
3191/*
3192 *@@ winhSetWindowFont:
3193 * this sets a window's font by invoking
3194 * WinSetPresParam on it.
3195 *
3196 * If (pszFont == NULL), a default font will be set
3197 * (on Warp 4, "9.WarpSans", on Warp 3, "8.Helv").
3198 *
3199 * winh.h also defines the winhSetDlgItemFont macro.
3200 *
3201 * Returns TRUE if successful or FALSE otherwise.
3202 *
3203 *@@added V0.9.0 [umoeller]
3204 *@@changed V1.0.1 (2002-11-30) [umoeller]: optimized
3205 */
3206
3207BOOL winhSetWindowFont(HWND hwnd,
3208 const char *pcszFont)
3209{
3210 if (!pcszFont)
3211 pcszFont = winhQueryDefaultFont();
3212
3213 return WinSetPresParam(hwnd,
3214 PP_FONTNAMESIZE,
3215 strlen(pcszFont) + 1,
3216 (PSZ)pcszFont);
3217}
3218
3219/*
3220 *@@ winhSetControlsFont:
3221 * this sets the font for all the controls of hwndDlg
3222 * which have a control ID in the range of usIDMin to
3223 * usIDMax. "Unused" IDs (i.e. -1) will also be set.
3224 *
3225 * If (pszFont == NULL), a default font will be set
3226 * (on Warp 4, "9.WarpSans", on Warp 3, "8.Helv").
3227 *
3228 * Returns the no. of controls set.
3229 *
3230 *@@added V0.9.0 [umoeller]
3231 *@@changed V1.0.4 (2005-09-02) [bvl]: Return 'Combined' fonts on DBCS systems to show DBCS characters properly @@fixes 655
3232 */
3233
3234ULONG winhSetControlsFont(HWND hwndDlg, // in: dlg to set
3235 SHORT usIDMin, // in: minimum control ID to be set (inclusive)
3236 SHORT usIDMax, // in: maximum control ID to be set (inclusive)
3237 const char *pcszFont) // in: font to use (e.g. "9.WarpSans") or NULL
3238{
3239 ULONG ulrc = 0;
3240 HENUM henum;
3241 HWND hwndItem;
3242 ULONG cbFont;
3243
3244 if (!pcszFont)
3245 pcszFont = winhQueryDefaultFont();
3246
3247 cbFont = strlen(pcszFont) + 1;
3248 // set font for all the dialog controls
3249 henum = WinBeginEnumWindows(hwndDlg);
3250 while ((hwndItem = WinGetNextWindow(henum)))
3251 {
3252 SHORT sID = WinQueryWindowUShort(hwndItem, QWS_ID);
3253 if ( (sID == -1)
3254 || ((sID >= usIDMin) && (sID <= usIDMax))
3255 )
3256 if (WinSetPresParam(hwndItem,
3257 PP_FONTNAMESIZE,
3258 cbFont,
3259 (PSZ)pcszFont))
3260 // successful:
3261 ulrc++;
3262 }
3263 WinEndEnumWindows(henum);
3264
3265 return ulrc;
3266}
3267
3268/*
3269 *@@ winhStorePresParam:
3270 * this appends a new presentation parameter to an
3271 * array of presentation parameters which can be
3272 * passed to WinCreateWindow. This is preferred
3273 * over setting the presparams using WinSetPresParams,
3274 * because that call will cause a lot of messages.
3275 *
3276 * On the first call, pppp _must_ be NULL. This
3277 * will allocate memory for storing the given
3278 * data as necessary and modify *pppp to point
3279 * to the new array.
3280 *
3281 * On subsequent calls with the same pppp, memory
3282 * will be reallocated, the old data will be copied,
3283 * and the new given data will be appended.
3284 *
3285 * Use free() on your PPRESPARAMS pointer (whose
3286 * address was passed) after WinCreateWindow.
3287 *
3288 * See winhQueryPresColor for typical presparams
3289 * used in OS/2.
3290 *
3291 * Example:
3292 *
3293 + PPRESPARAMS ppp = NULL;
3294 + CHAR szFont[] = "9.WarpSans";
3295 + LONG lColor = CLR_WHITE;
3296 + winhStorePresParam(&ppp, PP_FONTNAMESIZE, sizeof(szFont), szFont);
3297 + winhStorePresParam(&ppp, PP_BACKGROUNDCOLOR, sizeof(lColor), &lColor);
3298 + WinCreateWindow(...., ppp);
3299 + free(ppp);
3300 *
3301 *@@added V0.9.0 [umoeller]
3302 */
3303
3304BOOL winhStorePresParam(PPRESPARAMS *pppp, // in: data pointer (modified)
3305 ULONG ulAttrType, // in: PP_* index
3306 ULONG cbData, // in: sizeof(*pData), e.g. sizeof(LONG)
3307 PVOID pData) // in: presparam data (e.g. a PLONG to a color)
3308{
3309 BOOL brc = FALSE;
3310 if (pppp)
3311 {
3312 ULONG cbOld = 0,
3313 cbNew;
3314 PBYTE pbTemp = 0;
3315 PPRESPARAMS pppTemp = 0;
3316 PPARAM pppCopyTo = 0;
3317
3318 if (*pppp != NULL)
3319 // subsequent calls:
3320 cbOld = (**pppp).cb;
3321
3322 cbNew = sizeof(ULONG) // PRESPARAMS.cb
3323 + cbOld // old count, which does not include PRESPARAMS.cb
3324 + sizeof(ULONG) // PRESPARAMS.aparam[0].id
3325 + sizeof(ULONG) // PRESPARAMS.aparam[0].cb
3326 + cbData; // PRESPARAMS.aparam[0].ab[]
3327
3328 pbTemp = (PBYTE)malloc(cbNew);
3329 if (pbTemp)
3330 {
3331 pppTemp = (PPRESPARAMS)pbTemp;
3332
3333 if (*pppp != NULL)
3334 {
3335 // copy old data
3336 memcpy(pbTemp, *pppp, cbOld + sizeof(ULONG)); // including PRESPARAMS.cb
3337 pppCopyTo = (PPARAM)(pbTemp // new buffer
3338 + sizeof(ULONG) // skipping PRESPARAMS.cb
3339 + cbOld); // old PARAM array
3340 }
3341 else
3342 // first call:
3343 pppCopyTo = pppTemp->aparam;
3344
3345 pppTemp->cb = cbNew - sizeof(ULONG); // excluding PRESPARAMS.cb
3346 pppCopyTo->id = ulAttrType;
3347 pppCopyTo->cb = cbData; // byte count of PARAM.ab[]
3348 memcpy(pppCopyTo->ab, pData, cbData);
3349
3350 free(*pppp);
3351 *pppp = pppTemp;
3352
3353 brc = TRUE;
3354 }
3355 }
3356
3357 return brc;
3358}
3359
3360/*
3361 *@@ winhCreateDefaultPresparams:
3362 *
3363 * Caller must free() the return value.
3364 *
3365 *@@added V1.0.1 (2002-11-30) [umoeller]
3366 */
3367
3368PPRESPARAMS winhCreateDefaultPresparams(VOID)
3369{
3370 PPRESPARAMS ppp = NULL;
3371
3372 PCSZ pcszFont = winhQueryDefaultFont();
3373 LONG lColor;
3374
3375 winhStorePresParam(&ppp,
3376 PP_FONTNAMESIZE,
3377 strlen(pcszFont) + 1,
3378 (PVOID)pcszFont);
3379
3380 lColor = WinQuerySysColor(HWND_DESKTOP,
3381 SYSCLR_DIALOGBACKGROUND,
3382 0);
3383 winhStorePresParam(&ppp,
3384 PP_BACKGROUNDCOLOR,
3385 sizeof(lColor),
3386 &lColor);
3387
3388 lColor = RGBCOL_BLACK;
3389 winhStorePresParam(&ppp,
3390 PP_FOREGROUNDCOLOR,
3391 sizeof(lColor),
3392 &lColor);
3393
3394 return ppp;
3395}
3396
3397/*
3398 *@@ winhQueryPresColor2:
3399 * returns the specified color. This is queried in the
3400 * following order:
3401 *
3402 * 1) hwnd's pres params are searched for ulPP
3403 * (which should be a PP_* index);
3404 * 2) if (fInherit == TRUE), the parent windows
3405 * are searched also;
3406 * 3) if this fails or (fInherit == FALSE), WinQuerySysColor
3407 * is called to get lSysColor (which should be a SYSCLR_*
3408 * index), if lSysColor != -1;
3409 * 4) if (lSysColor == -1), -1 is returned.
3410 *
3411 * The return value is always an RGB LONG, _not_ a color index.
3412 * This is even true for the returned system colors, which are
3413 * converted to RGB.
3414 *
3415 * If you do any painting with this value, you should switch
3416 * the HPS you're using to RGB mode (use gpihSwitchToRGB for that).
3417 *
3418 * Some useful ulPP / lSysColor pairs
3419 * (default values as in PMREF):
3420 *
3421 + -- PP_FOREGROUNDCOLOR SYSCLR_WINDOWTEXT (for most controls also)
3422 + SYSCLR_WINDOWSTATICTEXT (for static controls)
3423 + Foreground color (default: black)
3424 + -- PP_BACKGROUNDCOLOR SYSCLR_BACKGROUND
3425 + SYSCLR_DIALOGBACKGROUND
3426 + SYSCLR_FIELDBACKGROUND (for disabled scrollbars)
3427 + SYSCLR_WINDOW (application surface -- empty clients)
3428 + Background color (default: light gray)
3429 + -- PP_ACTIVETEXTFGNDCOLOR
3430 + -- PP_HILITEFOREGROUNDCOLOR SYSCLR_HILITEFOREGROUND
3431 + Highlighted foreground color, for example for selected menu
3432 + (def.: white)
3433 + -- PP_ACTIVETEXTBGNDCOLOR
3434 + -- PP_HILITEBACKGROUNDCOLOR SYSCLR_HILITEBACKGROUND
3435 + Highlighted background color (def.: dark gray)
3436 + -- PP_INACTIVETEXTFGNDCOLOR
3437 + -- PP_DISABLEDFOREGROUNDCOLOR SYSCLR_MENUDISABLEDTEXT
3438 + Disabled foreground color (dark gray)
3439 + -- PP_INACTIVETEXTBGNDCOLOR
3440 + -- PP_DISABLEDBACKGROUNDCOLOR
3441 + Disabled background color
3442 + -- PP_BORDERCOLOR SYSCLR_WINDOWFRAME
3443 + SYSCLR_INACTIVEBORDER
3444 + Border color (around pushbuttons, in addition to
3445 + the 3D colors)
3446 + -- PP_ACTIVECOLOR SYSCLR_ACTIVETITLE
3447 + Active color
3448 + -- PP_INACTIVECOLOR SYSCLR_INACTIVETITLE
3449 + Inactive color
3450 *
3451 * For menus:
3452 + -- PP_MENUBACKGROUNDCOLOR SYSCLR_MENU
3453 + -- PP_MENUFOREGROUNDCOLOR SYSCLR_MENUTEXT
3454 + -- PP_MENUHILITEBGNDCOLOR SYSCLR_MENUHILITEBGND
3455 + -- PP_MENUHILITEFGNDCOLOR SYSCLR_MENUHILITE
3456 + -- ?? SYSCLR_MENUDISABLEDTEXT
3457 +
3458 * For containers (according to the API ref. at EDM/2):
3459 + -- PP_FOREGROUNDCOLOR SYSCLR_WINDOWTEXT
3460 + -- PP_BACKGROUNDCOLOR SYSCLR_WINDOW
3461 + -- PP_HILITEFOREGROUNDCOLOR SYSCLR_HILITEFOREGROUND
3462 + -- PP_HILITEBACKGROUNDCOLOR SYSCLR_HILITEBACKGROUND
3463 + -- PP_BORDERCOLOR
3464 + (used for separator lines, eg. in Details view)
3465 + -- PP_ICONTEXTBACKGROUNDCOLOR
3466 + (column titles in Details view?!?)
3467 +
3468 * For listboxes / entryfields / MLE's:
3469 + -- PP_BACKGROUNDCOLOR SYSCLR_ENTRYFIELD
3470 *
3471 * PMREF has more of these.
3472 *
3473 *@@changed V0.9.0 [umoeller]: removed INI key query, using SYSCLR_* instead; function prototype changed
3474 *@@changed V0.9.0 [umoeller]: added fInherit parameter
3475 *@@changed V0.9.7 (2000-12-02) [umoeller]: added lSysColor == -1 support
3476 *@@changed V0.9.20 (2002-08-04) [umoeller]: added ulPPIndex, renamed func
3477 */
3478
3479LONG winhQueryPresColor2(HWND hwnd, // in: window to query
3480 ULONG ulppRGB, // in: PP_* index for RGB color
3481 ULONG ulppIndex, // in: PP_* index for color _index_ (can be null)
3482 BOOL fInherit, // in: search parent windows too?
3483 LONG lSysColor) // in: SYSCLR_* index or -1
3484{
3485 ULONG ul,
3486 attrFound;
3487 LONG lColorFound;
3488
3489 if (ulppRGB != (ULONG)-1)
3490 {
3491 ULONG fl = 0;
3492 if (!fInherit)
3493 fl = QPF_NOINHERIT;
3494 if (ulppIndex)
3495 fl |= QPF_ID2COLORINDEX; // convert indexed color 2 to RGB V0.9.20 (2002-08-04) [umoeller]
3496
3497 if (ul = WinQueryPresParam(hwnd,
3498 ulppRGB,
3499 ulppIndex,
3500 &attrFound,
3501 sizeof(lColorFound),
3502 &lColorFound,
3503 fl))
3504 return lColorFound;
3505 }
3506
3507 // not found: get system color
3508 if (lSysColor != -1)
3509 return WinQuerySysColor(HWND_DESKTOP, lSysColor, 0);
3510
3511 return -1;
3512}
3513
3514/*
3515 *@@ winhQueryPresColor:
3516 * compatibility function because this one was
3517 * exported.
3518 *
3519 *@@added V0.9.20 (2002-08-04) [umoeller]
3520 */
3521
3522LONG XWPENTRY winhQueryPresColor(HWND hwnd,
3523 ULONG ulPP,
3524 BOOL fInherit,
3525 LONG lSysColor)
3526{
3527 return winhQueryPresColor2(hwnd,
3528 ulPP,
3529 0,
3530 fInherit,
3531 lSysColor);
3532}
3533
3534/*
3535 *@@ winhSetPresColor:
3536 * sets a color presparam. ulIndex specifies
3537 * the presparam to be set and would normally
3538 * be either PP_BACKGROUNDCOLOR or PP_FOREGROUNDCOLOR.
3539 *
3540 *@@added V0.9.16 (2001-10-15) [umoeller]
3541 */
3542
3543BOOL winhSetPresColor(HWND hwnd,
3544 ULONG ulIndex,
3545 LONG lColor)
3546{
3547 return WinSetPresParam(hwnd,
3548 ulIndex,
3549 sizeof(LONG),
3550 &lColor);
3551}
3552
3553/*
3554 *@@category: Helpers\PM helpers\Help (IPF)
3555 */
3556
3557/* ******************************************************************
3558 *
3559 * Help instance helpers
3560 *
3561 ********************************************************************/
3562
3563/*
3564 *@@ winhCreateHelp:
3565 * creates a help instance and connects it with the
3566 * given frame window.
3567 *
3568 * If (pszFileName == NULL), we'll retrieve the
3569 * executable's fully qualified file name and
3570 * replace the extension with .HLP simply. This
3571 * avoids the typical "Help not found" errors if
3572 * the program isn't started in its own directory.
3573 *
3574 * If you have created a help table in memory, specify it
3575 * with pHelpTable. To load a help table from the resources,
3576 * specify hmod (or NULLHANDLE) and set pHelpTable to the
3577 * following:
3578 +
3579 + (PHELPTABLE)MAKELONG(usTableID, 0xffff)
3580 *
3581 * Returns the help window handle or NULLHANDLE on errors.
3582 *
3583 * Based on an EDM/2 code snippet.
3584 *
3585 *@@added V0.9.4 (2000-07-03) [umoeller]
3586 */
3587
3588HWND winhCreateHelp(HWND hwndFrame, // in: app's frame window handle; can be NULLHANDLE
3589 const char *pcszFileName, // in: help file name or NULL
3590 HMODULE hmod, // in: module with help table or NULLHANDLE (current)
3591 PHELPTABLE pHelpTable, // in: help table or resource ID
3592 const char *pcszWindowTitle) // in: help window title or NULL
3593{
3594 HELPINIT hi;
3595 PSZ pszExt;
3596 CHAR szName[CCHMAXPATH];
3597 HWND hwndHelp;
3598
3599 if (pcszFileName == NULL)
3600 {
3601 PPIB ppib;
3602 PTIB ptib;
3603 DosGetInfoBlocks(&ptib, &ppib);
3604 DosQueryModuleName(ppib->pib_hmte, sizeof(szName), szName);
3605
3606 pszExt = strrchr(szName, '.');
3607 if (pszExt)
3608 strcpy(pszExt, ".hlp");
3609 else
3610 strcat(szName, ".hlp");
3611
3612 pcszFileName = szName;
3613 }
3614
3615 hi.cb = sizeof(HELPINIT);
3616 hi.ulReturnCode = 0;
3617 hi.pszTutorialName = NULL;
3618 hi.phtHelpTable = pHelpTable;
3619 hi.hmodHelpTableModule = hmod;
3620 hi.hmodAccelActionBarModule = NULLHANDLE;
3621 hi.idAccelTable = 0;
3622 hi.idActionBar = 0;
3623 hi.pszHelpWindowTitle = (PSZ)pcszWindowTitle;
3624 hi.fShowPanelId = CMIC_HIDE_PANEL_ID;
3625 hi.pszHelpLibraryName = (PSZ)pcszFileName;
3626
3627 hwndHelp = WinCreateHelpInstance(WinQueryAnchorBlock(hwndFrame),
3628 &hi);
3629 if ((hwndFrame) && (hwndHelp))
3630 {
3631 WinAssociateHelpInstance(hwndHelp, hwndFrame);
3632 }
3633
3634 return hwndHelp;
3635}
3636
3637/*
3638 *@@ winhDisplayHelpPanel:
3639 * displays the specified help panel ID.
3640 *
3641 * If (ulHelpPanel == 0), this displays the
3642 * standard OS/2 "Using help" panel.
3643 *
3644 * Returns zero on success or one of the
3645 * help manager error codes on failure.
3646 * See HM_ERROR for those.
3647 *
3648 *@@added V0.9.7 (2001-01-21) [umoeller]
3649 */
3650
3651ULONG winhDisplayHelpPanel(HWND hwndHelpInstance, // in: from winhCreateHelp
3652 ULONG ulHelpPanel) // in: help panel ID
3653{
3654 return (ULONG)WinSendMsg(hwndHelpInstance,
3655 HM_DISPLAY_HELP,
3656 (MPARAM)ulHelpPanel,
3657 (MPARAM)( (ulHelpPanel != 0)
3658 ? HM_RESOURCEID
3659 : 0));
3660}
3661
3662/*
3663 *@@ winhDestroyHelp:
3664 * destroys the help instance created by winhCreateHelp.
3665 *
3666 * Based on an EDM/2 code snippet.
3667 *
3668 *@@added V0.9.4 (2000-07-03) [umoeller]
3669 */
3670
3671void winhDestroyHelp(HWND hwndHelp,
3672 HWND hwndFrame) // can be NULLHANDLE if not used with winhCreateHelp
3673{
3674 if (hwndHelp)
3675 {
3676 if (hwndFrame)
3677 WinAssociateHelpInstance(NULLHANDLE, hwndFrame);
3678 WinDestroyHelpInstance(hwndHelp);
3679 }
3680}
3681
3682/*
3683 *@@category: Helpers\PM helpers\Application control
3684 */
3685
3686/* ******************************************************************
3687 *
3688 * Application control
3689 *
3690 ********************************************************************/
3691
3692/*
3693 *@@ winhAnotherInstance:
3694 * this tests whether another instance of the same
3695 * application is already running.
3696 *
3697 * To identify instances of the same application, the
3698 * application must call this function during startup
3699 * with the unique name of an OS/2 semaphore. As with
3700 * all OS/2 semaphores, the semaphore name must begin
3701 * with "\\SEM32\\". The semaphore isn't really used
3702 * except for testing for its existence, since that
3703 * name is unique among all processes.
3704 *
3705 * If another instance is found, TRUE is returned. If
3706 * (fSwitch == TRUE), that instance is switched to,
3707 * using the tasklist.
3708 *
3709 * If no other instance is found, FALSE is returned only.
3710 *
3711 * Based on an EDM/2 code snippet.
3712 *
3713 *@@added V0.9.0 (99-10-22) [umoeller]
3714 */
3715
3716BOOL winhAnotherInstance(const char *pcszSemName, // in: semaphore ID
3717 BOOL fSwitch) // in: if TRUE, switch to first instance if running
3718{
3719 HMTX hmtx;
3720
3721 if (DosCreateMutexSem((PSZ)pcszSemName,
3722 &hmtx,
3723 DC_SEM_SHARED,
3724 TRUE)
3725 == NO_ERROR)
3726 // semapore created: this doesn't happen if the semaphore
3727 // exists already, so no other instance is running
3728 return FALSE;
3729
3730 // else: instance running
3731 hmtx = NULLHANDLE;
3732
3733 // switch to other instance?
3734 if (fSwitch)
3735 {
3736 // yes: query mutex creator
3737 if (DosOpenMutexSem((PSZ)pcszSemName,
3738 &hmtx)
3739 == NO_ERROR)
3740 {
3741 PID pid = 0;
3742 TID tid = 0; // unused
3743 ULONG ulCount; // unused
3744
3745 if (DosQueryMutexSem(hmtx, &pid, &tid, &ulCount) == NO_ERROR)
3746 {
3747 HSWITCH hswitch = WinQuerySwitchHandle(NULLHANDLE, pid);
3748 if (hswitch != NULLHANDLE)
3749 WinSwitchToProgram(hswitch);
3750 }
3751
3752 DosCloseMutexSem(hmtx);
3753 }
3754 }
3755
3756 return TRUE; // another instance exists
3757}
3758
3759/*
3760 *@@ winhAddToTasklist:
3761 * this adds the specified window to the tasklist
3762 * with hIcon as its program icon (which is also
3763 * set for the main window). This is useful for
3764 * the old "dialog as main window" trick.
3765 *
3766 * Returns the HSWITCH of the added entry.
3767 */
3768
3769HSWITCH winhAddToTasklist(HWND hwnd, // in: window to add
3770 HPOINTER hIcon) // in: icon for main window
3771{
3772 SWCNTRL swctl;
3773 HSWITCH hswitch = 0;
3774 swctl.hwnd = hwnd; // window handle
3775 swctl.hwndIcon = hIcon; // icon handle
3776 swctl.hprog = NULLHANDLE; // program handle (use default)
3777 WinQueryWindowProcess(hwnd, &(swctl.idProcess), NULL);
3778 // process identifier
3779 swctl.idSession = 0; // session identifier ?
3780 swctl.uchVisibility = SWL_VISIBLE; // visibility
3781 swctl.fbJump = SWL_JUMPABLE; // jump indicator
3782 // get window title from window titlebar
3783 if (hwnd)
3784 WinQueryWindowText(hwnd, sizeof(swctl.szSwtitle), swctl.szSwtitle);
3785 swctl.bProgType = PROG_DEFAULT; // program type
3786 hswitch = WinAddSwitchEntry(&swctl);
3787
3788 // give the main window the icon
3789 if ((hwnd) && (hIcon))
3790 WinSendMsg(hwnd,
3791 WM_SETICON,
3792 (MPARAM)hIcon,
3793 NULL);
3794
3795 return hswitch;
3796}
3797
3798/*
3799 *@@ winhUpdateTasklist:
3800 * refreshes the task list entry for the given
3801 * window with a new title text.
3802 *
3803 *@@added V1.0.1 (2003-01-25) [umoeller]
3804 */
3805
3806BOOL winhUpdateTasklist(HWND hwnd,
3807 PCSZ pcszNewTitle)
3808{
3809 HSWITCH hsw;
3810 if (hsw = WinQuerySwitchHandle(hwnd, 0))
3811 {
3812 SWCNTRL swc;
3813 WinQuerySwitchEntry(hsw, &swc);
3814 strhncpy0(swc.szSwtitle,
3815 pcszNewTitle,
3816 sizeof(swc.szSwtitle));
3817 return !WinChangeSwitchEntry(hsw, &swc);
3818 }
3819
3820 return FALSE;
3821}
3822
3823/*
3824 *@@category: Helpers\PM helpers\Miscellaneous
3825 */
3826
3827/* ******************************************************************
3828 *
3829 * Miscellaneous
3830 *
3831 ********************************************************************/
3832
3833/*
3834 *@@ winhFree:
3835 * frees a block of memory allocated by the
3836 * winh* functions.
3837 *
3838 * Since the winh* functions use malloc(),
3839 * you can also use free() directly on such
3840 * blocks. However, you must use winhFree
3841 * if the winh* functions are in a module
3842 * with a different C runtime.
3843 *
3844 *@@added V0.9.7 (2000-12-06) [umoeller]
3845 */
3846
3847VOID winhFree(PVOID p)
3848{
3849 if (p)
3850 free(p);
3851}
3852
3853/*
3854 *@@ winhSleep:
3855 * sleeps at least the specified amount of time,
3856 * without blocking the message queue.
3857 *
3858 * NOTE: This function is a bit expensive because
3859 * it creates a temporary object window. If you
3860 * need to sleep several times, you should rather
3861 * use a private timer.
3862 *
3863 *@@added V0.9.4 (2000-07-11) [umoeller]
3864 *@@changed V0.9.9 (2001-03-11) [umoeller]: rewritten
3865 */
3866
3867VOID winhSleep(ULONG ulSleep) // in: sleep time in milliseconds
3868{
3869 HWND hwnd;
3870
3871 if (hwnd = winhCreateObjectWindow(WC_STATIC, NULL))
3872 {
3873 QMSG qmsg;
3874 HAB hab;
3875
3876 if ( (hab = WinQueryAnchorBlock(hwnd))
3877 && (WinStartTimer(hab,
3878 hwnd,
3879 1,
3880 ulSleep))
3881 )
3882 {
3883 while (WinGetMsg(hab, &qmsg, NULLHANDLE, 0, 0))
3884 {
3885 if ( (qmsg.hwnd == hwnd)
3886 && (qmsg.msg == WM_TIMER)
3887 && (qmsg.mp1 == (MPARAM)1) // timer ID
3888 )
3889 break;
3890
3891 WinDispatchMsg(hab, &qmsg);
3892 }
3893 WinStopTimer(hab,
3894 hwnd,
3895 1);
3896 }
3897 else
3898 // timer creation failed:
3899 DosSleep(ulSleep);
3900
3901 WinDestroyWindow(hwnd);
3902 }
3903 else
3904 DosSleep(ulSleep);
3905}
3906
3907/*
3908 *@@ winhFileDlg:
3909 * one-short function for opening an "Open" file
3910 * dialog.
3911 *
3912 * On input, pszFile specifies the directory and
3913 * file specification (e.g. "F:\*.txt").
3914 *
3915 * Returns TRUE if the user pressed OK. In that
3916 * case, the fully qualified filename is written
3917 * into pszFile again.
3918 *
3919 * Returns FALSE if the user pressed Cancel.
3920 *
3921 * Notes about flFlags:
3922 *
3923 * -- WINH_FOD_SAVEDLG: display a "Save As" dialog.
3924 * Otherwise an "Open" dialog is displayed.
3925 *
3926 * -- WINH_FOD_INILOADDIR: load a directory from the
3927 * specified INI key and switch the dlg to it.
3928 * In that case, on input, pszFile must only
3929 * contain the file filter without any path
3930 * specification, because that is loaded from
3931 * the INI key. If the INI key does not exist,
3932 * the current process directory will be used.
3933 *
3934 * -- WINH_FOD_INISAVEDIR: if the user presses OK,
3935 * the directory of the selected file is written
3936 * to the specified INI key so that it can be
3937 * reused later. This flag is independent of
3938 * WINH_FOD_INISAVEDIR: you can specify none,
3939 * one, or both of them.
3940 *
3941 *@@added V0.9.3 (2000-04-29) [umoeller]
3942 *@@changed V0.9.12 (2001-05-21) [umoeller]: this failed if INI data had root dir, fixed
3943 */
3944
3945BOOL winhFileDlg(HWND hwndOwner, // in: owner for file dlg
3946 PSZ pszFile, // in: file mask; out: fully q'd filename
3947 // (should be CCHMAXPATH in size)
3948 ULONG flFlags, // in: any combination of the following:
3949 // -- WINH_FOD_SAVEDLG: save dlg; else open dlg
3950 // -- WINH_FOD_INILOADDIR: load FOD path from INI
3951 // -- WINH_FOD_INISAVEDIR: store FOD path to INI on OK
3952 HINI hini, // in: INI file to load/store last path from (can be HINI_USER)
3953 const char *pcszApplication, // in: INI application to load/store last path from
3954 const char *pcszKey) // in: INI key to load/store last path from
3955{
3956 FILEDLG fd;
3957 FILESTATUS3 fs3;
3958
3959 memset(&fd, 0, sizeof(FILEDLG));
3960 fd.cbSize = sizeof(FILEDLG);
3961 fd.fl = FDS_CENTER;
3962
3963 if (flFlags & WINH_FOD_SAVEDLG)
3964 fd.fl |= FDS_SAVEAS_DIALOG;
3965 else
3966 fd.fl |= FDS_OPEN_DIALOG;
3967
3968 if ( (hini)
3969 && (flFlags & WINH_FOD_INILOADDIR)
3970 && (PrfQueryProfileString(hini,
3971 (PSZ)pcszApplication,
3972 (PSZ)pcszKey,
3973 "", // default string V0.9.9 (2001-02-10) [umoeller]
3974 fd.szFullFile,
3975 sizeof(fd.szFullFile)-10)
3976 > 2)
3977 // added these checks V0.9.12 (2001-05-21) [umoeller]
3978 && (!DosQueryPathInfo(fd.szFullFile,
3979 FIL_STANDARD,
3980 &fs3,
3981 sizeof(fs3)))
3982 && (fs3.attrFile & FILE_DIRECTORY)
3983 )
3984 {
3985 // found: append "\*"
3986 strcat(fd.szFullFile, "\\");
3987 strcat(fd.szFullFile, pszFile);
3988 }
3989 else
3990 // default: copy pszFile
3991 strcpy(fd.szFullFile, pszFile);
3992 // fixed V0.9.12 (2001-05-21) [umoeller]
3993
3994 if ( WinFileDlg(HWND_DESKTOP, // parent
3995 hwndOwner, // owner
3996 &fd)
3997 && (fd.lReturn == DID_OK)
3998 )
3999 {
4000 // save path back?
4001 if ( (hini)
4002 && (flFlags & WINH_FOD_INISAVEDIR)
4003 )
4004 {
4005 // get the directory that was used
4006 PSZ p = strrchr(fd.szFullFile, '\\');
4007 if (p)
4008 {
4009 // contains directory:
4010 // copy to OS2.INI
4011 PSZ pszDir = strhSubstr(fd.szFullFile, p);
4012 if (pszDir)
4013 {
4014 PrfWriteProfileString(hini,
4015 (PSZ)pcszApplication,
4016 (PSZ)pcszKey,
4017 pszDir);
4018 free(pszDir);
4019 }
4020 }
4021 }
4022
4023 strcpy(pszFile, fd.szFullFile);
4024
4025 return TRUE;
4026 }
4027
4028 return FALSE;
4029}
4030
4031/*
4032 *@@ winhQueryWaitPointer:
4033 * shortcut for getting the system "wait" pointer.
4034 *
4035 *@@added V1.0.1 (2002-11-30) [umoeller]
4036 */
4037
4038HPOINTER winhQueryWaitPointer(VOID)
4039{
4040 return WinQuerySysPointer(HWND_DESKTOP,
4041 SPTR_WAIT,
4042 FALSE); // no copy
4043}
4044
4045/*
4046 *@@ winhSetWaitPointer:
4047 * this sets the mouse pointer to "Wait".
4048 * Returns the previous pointer (HPOINTER),
4049 * which should be stored somewhere to be
4050 * restored later. Example:
4051 + HPOINTER hptrOld = winhSetWaitPointer();
4052 + ...
4053 + WinSetPointer(HWND_DESKTOP, hptrOld);
4054 */
4055
4056HPOINTER winhSetWaitPointer(VOID)
4057{
4058 HPOINTER hptr = WinQueryPointer(HWND_DESKTOP);
4059 WinSetPointer(HWND_DESKTOP,
4060 winhQueryWaitPointer());
4061 return hptr;
4062}
4063
4064/*
4065 *@@ winhQueryWindowText2:
4066 * this returns the window text of the specified
4067 * HWND in a newly allocated buffer.
4068 *
4069 * If pulExtra is specified, *pulExtra bytes will
4070 * be allocated in addition to the window text
4071 * length. Useful if you plan to append something
4072 * to the string. On output, *pulExtra receives
4073 * the string length excluding the extra bytes
4074 * and the terminating null byte.
4075 *
4076 * Returns NULL on error. Use free()
4077 * to free the return value.
4078 *
4079 *@@added V1.0.1 (2003-01-05) [umoeller]
4080 */
4081
4082PSZ winhQueryWindowText2(HWND hwnd, // in: window whose text to query
4083 PULONG pulExtra) // in: extra bytes to allocate or NULL,
4084 // out: size of allocated buffer (including null byte)
4085{
4086 PSZ pszText = NULL;
4087 ULONG cbText;
4088 if (cbText = WinQueryWindowTextLength(hwnd))
4089 {
4090 ULONG cbExtra = 1; // additional null character
4091 if (pulExtra)
4092 cbExtra += *pulExtra;
4093
4094 if (pszText = (PSZ)malloc(cbText + cbExtra))
4095 {
4096 WinQueryWindowText(hwnd,
4097 cbText + 1,
4098 pszText);
4099 if (pulExtra)
4100 *pulExtra = cbText;
4101 }
4102 }
4103
4104 return pszText;
4105}
4106
4107/*
4108 *@@ winhQueryWindowText:
4109 * this returns the window text of the specified
4110 * HWND in a newly allocated buffer.
4111 *
4112 * Returns NULL on error. Use free()
4113 * to free the return value.
4114 *
4115 *@@changed V1.0.1 (2003-01-05) [umoeller]: now using winhQueryWindowText2
4116 */
4117
4118PSZ winhQueryWindowText(HWND hwnd)
4119{
4120 return winhQueryWindowText2(hwnd, NULL); // V1.0.1 (2003-01-05) [umoeller]
4121}
4122
4123/*
4124 *@@ winhQueryDlgItemText2:
4125 * shortcut around winhQueryWindowText2 to allow for
4126 * specifying a dialog item ID instead.
4127 *
4128 *@@added V1.0.1 (2003-01-05) [umoeller]
4129 */
4130
4131PSZ winhQueryDlgItemText2(HWND hwnd,
4132 USHORT usItemID,
4133 PULONG pulExtra)
4134{
4135 return winhQueryWindowText2(WinWindowFromID(hwnd, usItemID),
4136 pulExtra);
4137}
4138
4139/*
4140 *@@ winhSetWindowText:
4141 * like WinSetWindowText, but this one accepts
4142 * printf-like arguments.
4143 *
4144 * Note that the total string is limited to
4145 * 1000 characters.
4146 *
4147 *@@added V0.9.16 (2001-10-08) [umoeller]
4148 */
4149
4150BOOL winhSetWindowText(HWND hwnd,
4151 const char *pcszFormat,
4152 ...)
4153{
4154 CHAR szBuf[1000];
4155 va_list args;
4156 int i;
4157 va_start(args, pcszFormat);
4158 i = vsprintf(szBuf, pcszFormat, args);
4159 va_end(args);
4160
4161 return WinSetWindowText(hwnd,
4162 szBuf);
4163}
4164
4165/*
4166 *@@ winhAppendWindowEllipseText:
4167 * appends three dots ("...") to the title
4168 * of the given window.
4169 *
4170 *@@added V1.0.1 (2003-01-05) [umoeller]
4171 */
4172
4173BOOL winhAppendWindowEllipseText(HWND hwnd)
4174{
4175 ULONG cbExtra = 3;
4176 PSZ psz;
4177 BOOL brc = FALSE;
4178 if (psz = winhQueryWindowText2(hwnd, &cbExtra))
4179 {
4180 memcpy(psz + cbExtra, "...", 4);
4181 brc = WinSetWindowText(hwnd, psz);
4182 free(psz);
4183 }
4184
4185 return brc;
4186}
4187
4188/*
4189 *@@ winhAppendDlgItemEllipseText:
4190 *
4191 *@@added V1.0.1 (2003-01-05) [umoeller]
4192 */
4193
4194BOOL winhAppendDlgItemEllipseText(HWND hwnd,
4195 USHORT usItemID)
4196{
4197 return winhAppendWindowEllipseText(WinWindowFromID(hwnd, usItemID));
4198}
4199
4200/*
4201 *@@ winhReplaceWindowText:
4202 * this is a combination of winhQueryWindowText
4203 * and strhFindReplace to replace substrings in a window.
4204 *
4205 * This is useful for filling in placeholders
4206 * a la "%1" in control windows, e.g. static
4207 * texts.
4208 *
4209 * This replaces only the first occurence of
4210 * pszSearch.
4211 *
4212 * Returns TRUE only if the window exists and
4213 * the search string was replaced.
4214 *
4215 *@@added V0.9.0 [umoeller]
4216 */
4217
4218BOOL winhReplaceWindowText(HWND hwnd, // in: window whose text is to be modified
4219 const char *pcszSearch, // in: search string (e.g. "%1")
4220 const char *pcszReplaceWith) // in: replacement string for pszSearch
4221{
4222 BOOL brc = FALSE;
4223 PSZ pszText;
4224
4225 if (pszText = winhQueryWindowText(hwnd))
4226 {
4227 ULONG ulOfs = 0;
4228 if (strhFindReplace(&pszText, &ulOfs, pcszSearch, pcszReplaceWith) > 0)
4229 {
4230 WinSetWindowText(hwnd, pszText);
4231 brc = TRUE;
4232 }
4233
4234 free(pszText);
4235 }
4236
4237 return brc;
4238}
4239
4240/*
4241 *@@ winhEnableDlgItems:
4242 * this enables/disables a whole range of controls
4243 * in a window by enumerating the child windows
4244 * until usIDFirst is found. If so, that subwindow
4245 * is enabled/disabled and all the following windows
4246 * in the enumeration also, until usIDLast is found.
4247 *
4248 * Note that this affects _all_ controls following
4249 * the usIDFirst window, no matter what ID they have
4250 * (even if "-1"), until usIDLast is found.
4251 *
4252 * Returns the no. of controls which were enabled/disabled
4253 * (null if none).
4254 *
4255 *@@added V0.9.0 [umoeller]
4256 *@@changed V0.9.1 (99-12-20) [umoeller]: renamed from winhEnableDlgItems
4257 */
4258
4259ULONG winhEnableControls(HWND hwndDlg, // in: dialog window
4260 USHORT usIDFirst, // in: first affected control ID
4261 USHORT usIDLast, // in: last affected control ID (inclusive)
4262 BOOL fEnable) // in: enable or disable?
4263{
4264 HENUM henum1 = NULLHANDLE;
4265 HWND hwndThis = NULLHANDLE;
4266 ULONG ulCount = 0;
4267
4268 henum1 = WinBeginEnumWindows(hwndDlg);
4269 while ((hwndThis = WinGetNextWindow(henum1)) != NULLHANDLE)
4270 {
4271 USHORT usIDCheckFirst = WinQueryWindowUShort(hwndThis, QWS_ID),
4272 usIDCheckLast;
4273 if (usIDCheckFirst == usIDFirst)
4274 {
4275 WinEnableWindow(hwndThis, fEnable);
4276 ulCount++;
4277
4278 while ((hwndThis = WinGetNextWindow(henum1)) != NULLHANDLE)
4279 {
4280 WinEnableWindow(hwndThis, fEnable);
4281 ulCount++;
4282 usIDCheckLast = WinQueryWindowUShort(hwndThis, QWS_ID);
4283 if (usIDCheckLast == usIDLast)
4284 break;
4285 }
4286
4287 break; // outer loop
4288 }
4289 }
4290 WinEndEnumWindows(henum1);
4291
4292 return ulCount;
4293}
4294
4295/*
4296 *@@ winhEnableControls2:
4297 * like winhEnableControls, but instead this
4298 * takes an array of ULONGs as input, which
4299 * is assumed to contain the dialog IDs of
4300 * the controls to be enabled/disabled.
4301 *
4302 *@@added V0.9.19 (2002-05-28) [umoeller]
4303 */
4304
4305ULONG winhEnableControls2(HWND hwndDlg, // in: dialog window
4306 const ULONG *paulIDs, // in: array of dialog IDs
4307 ULONG cIDs, // in: array item count (NOT array size)
4308 BOOL fEnable) // in: enable or disable?
4309{
4310 ULONG ul,
4311 ulrc = 0;
4312 for (ul = 0;
4313 ul < cIDs;
4314 ++ul)
4315 {
4316 if (WinEnableControl(hwndDlg, paulIDs[ul], fEnable))
4317 ++ulrc;
4318 }
4319
4320 return ulrc;
4321}
4322
4323/*
4324 *@@ winhCreateStdWindow:
4325 * much like WinCreateStdWindow, but this one
4326 * allows you to have the standard window
4327 * positioned automatically, using a given
4328 * SWP structure (*pswpFrame).
4329 *
4330 * The frame is created with the specified parent
4331 * (usually HWND_DESKTOP), but no owner.
4332 *
4333 * The client window is created with the frame as
4334 * its parent and owner and gets an ID of FID_CLIENT.
4335 *
4336 * Alternatively, you can set pswpFrame to NULL
4337 * and specify FCF_SHELLPOSITION with flFrameCreateFlags.
4338 * If you want the window to be shown, specify
4339 * SWP_SHOW (and maybe SWP_ACTIVATE) in *pswpFrame.
4340 *
4341 *@@added V0.9.0 [umoeller]
4342 *@@changed V0.9.5 (2000-08-13) [umoeller]: flStyleClient never worked, fixed
4343 *@@changed V0.9.7 (2000-12-08) [umoeller]: fixed client calc for invisible window
4344 *@@changed V1.0.1 (2002-11-30) [umoeller]: added support for NULL pcszClassClient
4345 */
4346
4347HWND winhCreateStdWindow(HWND hwndFrameParent, // in: normally HWND_DESKTOP
4348 PSWP pswpFrame, // in: frame wnd pos (ptr can be NULL)
4349 ULONG flFrameCreateFlags, // in: FCF_* flags
4350 ULONG ulFrameStyle, // in: WS_* flags (e.g. WS_VISIBLE, WS_ANIMATE)
4351 const char *pcszFrameTitle, // in: frame title (title bar)
4352 ULONG ulResourcesID, // in: according to FCF_* flags
4353 const char *pcszClassClient, // in: client class name (can be NULL for no client)
4354 ULONG flStyleClient, // in: client style
4355 ULONG ulID, // in: frame window ID
4356 PVOID pClientCtlData, // in: pCtlData structure pointer for client
4357 PHWND phwndClient) // out: created client wnd (required)
4358{
4359 FRAMECDATA fcdata;
4360 HWND hwndFrame;
4361 RECTL rclClient;
4362
4363 fcdata.cb = sizeof(FRAMECDATA);
4364 fcdata.flCreateFlags = flFrameCreateFlags;
4365 fcdata.hmodResources = (HMODULE)NULL;
4366 fcdata.idResources = ulResourcesID;
4367
4368 // create the frame and client windows
4369 if (hwndFrame = WinCreateWindow(hwndFrameParent,
4370 WC_FRAME,
4371 (PSZ)pcszFrameTitle,
4372 ulFrameStyle,
4373 0,0,0,0,
4374 NULLHANDLE,
4375 HWND_TOP,
4376 ulID,
4377 &fcdata,
4378 NULL))
4379 {
4380 if ( (!pcszClassClient) // V1.0.1 (2002-11-30) [umoeller]
4381 || (*phwndClient = WinCreateWindow(hwndFrame, // parent
4382 (PSZ)pcszClassClient, // class
4383 NULL, // no title
4384 flStyleClient, // style
4385 0,0,0,0, // size and position = 0
4386 hwndFrame, // owner
4387 HWND_BOTTOM, // bottom z-order
4388 FID_CLIENT, // frame window ID
4389 pClientCtlData, // class data
4390 NULL)) // no presparams
4391 )
4392 {
4393 if (pswpFrame)
4394 {
4395 // position frame
4396 WinSetWindowPos(hwndFrame,
4397 pswpFrame->hwndInsertBehind,
4398 pswpFrame->x,
4399 pswpFrame->y,
4400 pswpFrame->cx,
4401 pswpFrame->cy,
4402 pswpFrame->fl);
4403
4404 if (!pcszClassClient)
4405 *phwndClient = NULLHANDLE;
4406 else
4407 {
4408 // position client
4409 // WinQueryWindowRect(hwndFrame, &rclClient);
4410 // doesn't work because it might be invisible V0.9.7 (2000-12-08) [umoeller]
4411 rclClient.xLeft = 0;
4412 rclClient.yBottom = 0;
4413 rclClient.xRight = pswpFrame->cx;
4414 rclClient.yTop = pswpFrame->cy;
4415 WinCalcFrameRect(hwndFrame,
4416 &rclClient,
4417 TRUE); // calc client from frame
4418 WinSetWindowPos(*phwndClient,
4419 HWND_TOP,
4420 rclClient.xLeft,
4421 rclClient.yBottom,
4422 rclClient.xRight - rclClient.xLeft,
4423 rclClient.yTop - rclClient.yBottom,
4424 SWP_MOVE | SWP_SIZE | SWP_SHOW);
4425 }
4426 }
4427 }
4428 }
4429
4430 return hwndFrame;
4431}
4432
4433/*
4434 *@@ winhCreateObjectWindow:
4435 * creates an object window of the specified
4436 * window class, which you should have registered
4437 * before calling this. pvCreateParam will be
4438 * given to the window on WM_CREATE.
4439 *
4440 * Returns the HWND of the object window or
4441 * NULLHANDLE on errors.
4442 *
4443 *@@added V0.9.3 (2000-04-17) [umoeller]
4444 *@@changed V0.9.7 (2001-01-17) [umoeller]: made this a function from a macro
4445 */
4446
4447HWND winhCreateObjectWindow(const char *pcszWindowClass, // in: PM window class name
4448 PVOID pvCreateParam) // in: create param
4449{
4450 return WinCreateWindow(HWND_OBJECT,
4451 (PSZ)pcszWindowClass,
4452 (PSZ)"",
4453 0,
4454 0,0,0,0,
4455 0,
4456 HWND_BOTTOM,
4457 0,
4458 pvCreateParam,
4459 NULL);
4460}
4461
4462/*
4463 *@@ winhCreateControl:
4464 * creates a control with a size and position of 0.
4465 *
4466 *@@added V0.9.9 (2001-03-13) [umoeller]
4467 *@@changed V1.0.0 (2002-08-26) [umoeller]: added separate hwndOwner
4468 */
4469
4470HWND winhCreateControl(HWND hwndParent, // in: parent window
4471 HWND hwndOwner, // in: owner window
4472 const char *pcszClass, // in: window class (e.g. WC_BUTTON)
4473 const char *pcszText, // in: window title
4474 ULONG ulStyle, // in: control style
4475 ULONG ulID) // in: control ID
4476{
4477 return WinCreateWindow(hwndParent,
4478 (PSZ)pcszClass,
4479 (PSZ)pcszText,
4480 ulStyle,
4481 0, 0, 0, 0,
4482 hwndOwner,
4483 HWND_TOP,
4484 ulID,
4485 NULL,
4486 NULL);
4487}
4488
4489/*
4490 *@@ winhSetParentAndOwner:
4491 * switches owner _and_ parent of the given window.
4492 *
4493 *@@added V1.0.1 (2003-01-17) [umoeller]
4494 */
4495
4496BOOL winhSetParentAndOwner(HWND hwnd, // in: window whose parent and owner to change
4497 HWND hwndNewParent, // in: new parent and owner
4498 BOOL fRedraw)
4499{
4500 return ( WinSetParent(hwnd, hwndNewParent, fRedraw)
4501 && WinSetOwner(hwnd, hwndNewParent)
4502 );
4503}
4504
4505/*
4506 *@@ winhRepaintWindows:
4507 * this repaints all children of hwndParent.
4508 * If this is passed as HWND_DESKTOP, the
4509 * whole screen is repainted.
4510 *
4511 *@@changed V0.9.7 (2000-12-13) [umoeller]: hwndParent was never respected, fixed
4512 */
4513
4514VOID winhRepaintWindows(HWND hwndParent)
4515{
4516 HWND hwndTop;
4517 HENUM henum = WinBeginEnumWindows(hwndParent);
4518 while ((hwndTop = WinGetNextWindow(henum)))
4519 if (WinIsWindowShowing(hwndTop))
4520 WinInvalidateRect(hwndTop, NULL, TRUE);
4521 WinEndEnumWindows(henum);
4522}
4523
4524/*
4525 *@@ winhFindMsgQueue:
4526 * returns the message queue which matches
4527 * the given process and thread IDs. Since,
4528 * per IBM definition, every thread may only
4529 * have one MQ, this should be unique.
4530 *
4531 *@@added V0.9.2 (2000-03-08) [umoeller]
4532 */
4533
4534HMQ winhFindMsgQueue(PID pid, // in: process ID
4535 TID tid, // in: thread ID
4536 HAB* phab) // out: anchor block
4537{
4538 HWND hwndThis = 0,
4539 rc = 0;
4540 HENUM henum = WinBeginEnumWindows(HWND_OBJECT);
4541 while ((hwndThis = WinGetNextWindow(henum)))
4542 {
4543 CHAR szClass[200];
4544 if (WinQueryClassName(hwndThis, sizeof(szClass), szClass))
4545 {
4546 if (!strcmp(szClass, "#32767"))
4547 {
4548 // message queue window:
4549 PID pidWin = 0;
4550 TID tidWin = 0;
4551 WinQueryWindowProcess(hwndThis,
4552 &pidWin,
4553 &tidWin);
4554 if ( (pidWin == pid)
4555 && (tidWin == tid)
4556 )
4557 {
4558 // get HMQ from window words
4559 if (rc = WinQueryWindowULong(hwndThis, QWL_HMQ))
4560 if (phab)
4561 *phab = WinQueryAnchorBlock(hwndThis);
4562
4563 break;
4564 }
4565 }
4566 }
4567 }
4568 WinEndEnumWindows(henum);
4569
4570 return rc;
4571}
4572
4573/*
4574 *@@ winhFindHardErrorWindow:
4575 * this searches all children of HWND_OBJECT
4576 * for the PM hard error windows, which are
4577 * invisible most of the time. When a hard
4578 * error occurs, that window is made a child
4579 * of HWND_DESKTOP instead.
4580 *
4581 * Stolen from ProgramCommander/2 (C) Roman Stangl.
4582 *
4583 *@@added V0.9.3 (2000-04-27) [umoeller]
4584 */
4585
4586VOID winhFindPMErrorWindows(HWND *phwndHardError, // out: hard error window
4587 HWND *phwndSysError) // out: system error window
4588{
4589 PID pidObject; // HWND_OBJECT's process and thread id
4590 TID tidObject;
4591 PID pidObjectChild; // HWND_OBJECT's child window process and thread id
4592 TID tidObjectChild;
4593 HENUM henumObject; // HWND_OBJECT enumeration handle
4594 HWND hwndObjectChild; // Window handle of current HWND_OBJECT child
4595 UCHAR ucClassName[32]; // Window class e.g. #1 for WC_FRAME
4596 CLASSINFO classinfoWindow; // Class info of current HWND_OBJECT child
4597
4598 *phwndHardError = NULLHANDLE;
4599 *phwndSysError = NULLHANDLE;
4600
4601 // query HWND_OBJECT's window process
4602 WinQueryWindowProcess(WinQueryObjectWindow(HWND_DESKTOP), &pidObject, &tidObject);
4603 // enumerate all child windows of HWND_OBJECT
4604 henumObject = WinBeginEnumWindows(HWND_OBJECT);
4605 while ((hwndObjectChild = WinGetNextWindow(henumObject)))
4606 {
4607 // see if the current HWND_OBJECT child window runs in the
4608 // process of HWND_OBJECT (PM)
4609 WinQueryWindowProcess(hwndObjectChild, &pidObjectChild, &tidObjectChild);
4610 if (pidObject == pidObjectChild)
4611 {
4612 // get the child window's data
4613 WinQueryClassName(hwndObjectChild,
4614 sizeof(ucClassName),
4615 (PCH)ucClassName);
4616 WinQueryClassInfo(WinQueryAnchorBlock(hwndObjectChild),
4617 (PSZ)ucClassName,
4618 &classinfoWindow);
4619 if ( (!strcmp((PSZ)ucClassName, "#1")
4620 || (classinfoWindow.flClassStyle & CS_FRAME))
4621 )
4622 {
4623 // if the child window is a frame window and running in
4624 // HWND_OBJECT's (PM's) window process, it must be the
4625 // PM Hard Error or System Error window
4626 WinQueryClassName(WinWindowFromID(hwndObjectChild,
4627 FID_CLIENT),
4628 sizeof(ucClassName),
4629 (PSZ)ucClassName);
4630 if (!strcmp((PSZ)ucClassName, "PM Hard Error"))
4631 {
4632 *phwndHardError = hwndObjectChild;
4633 if (*phwndSysError)
4634 // we found the other one already:
4635 // stop searching, we got both
4636 break;
4637 }
4638 else
4639 {
4640 printf("Utility: Found System Error %08X\n", (int)hwndObjectChild);
4641 *phwndSysError = hwndObjectChild;
4642 if (*phwndHardError)
4643 // we found the other one already:
4644 // stop searching, we got both
4645 break;
4646 }
4647 }
4648 } // end if (pidObject == pidObjectChild)
4649 } // end while ((hwndObjectChild = WinGetNextWindow(henumObject)) != NULLHANDLE)
4650 WinEndEnumWindows(henumObject);
4651}
4652
4653/*
4654 *@@ winhCreateFakeDesktop:
4655 * this routine creates and displays a frameless window over
4656 * the whole screen in the color of PM's Desktop to fool the
4657 * user that all windows have been closed (which in fact might
4658 * not be the case).
4659 *
4660 * This window's background color is set to the Desktop's
4661 * (PM's one, not the WPS's one).
4662 *
4663 * Returns the HWND of this window.
4664 */
4665
4666HWND winhCreateFakeDesktop(HWND hwndSibling)
4667{
4668 // presparam for background
4669 typedef struct _BACKGROUND
4670 {
4671 ULONG cb; // length of the aparam parameter, in bytes
4672 ULONG id; // attribute type identity
4673 ULONG cb2; // byte count of the ab parameter
4674 RGB rgb; // attribute value
4675 } BACKGROUND;
4676
4677 BACKGROUND background;
4678 LONG lDesktopColor;
4679
4680 // create fake desktop window = empty window with
4681 // the size of full screen
4682 lDesktopColor = WinQuerySysColor(HWND_DESKTOP,
4683 SYSCLR_BACKGROUND,
4684 0);
4685 background.cb = sizeof(background.id)
4686 + sizeof(background.cb)
4687 + sizeof(background.rgb);
4688 background.id = PP_BACKGROUNDCOLOR;
4689 background.cb2 = sizeof(RGB);
4690 background.rgb.bBlue = (CHAR1FROMMP(lDesktopColor));
4691 background.rgb.bGreen= (CHAR2FROMMP(lDesktopColor));
4692 background.rgb.bRed = (CHAR3FROMMP(lDesktopColor));
4693
4694 return WinCreateWindow(HWND_DESKTOP, // parent window
4695 WC_FRAME, // class name
4696 "", // window text
4697 WS_VISIBLE, // window style
4698 0, 0, // position and size
4699 G_cxScreen,
4700 G_cyScreen,
4701 NULLHANDLE, // owner window
4702 hwndSibling, // sibling window
4703 1, // window id
4704 NULL, // control data
4705 &background); // presentation parms
4706}
4707
4708/*
4709 *@@ winhAssertWarp4Notebook:
4710 * this takes hwndDlg as a notebook dialog page and
4711 * goes thru all its controls. If a control with an
4712 * ID <= udIdThreshold is found, this is assumed to
4713 * be a button which is to be given the BS_NOTEBOOKBUTTON
4714 * style. You should therefore give all your button
4715 * controls which should be moved such an ID.
4716 *
4717 * Note that this function will now automatically
4718 * find out the lowest y coordinate that was used
4719 * for a non-notebook button and move all controls
4720 * down accordingly. As a result, ulDownUnit must
4721 * no longer be specified (V0.9.19).
4722 *
4723 * This function is useful if you wish to create
4724 * notebook pages using dlgedit.exe which are compatible
4725 * with both Warp 3 and Warp 4. This should be executed
4726 * in WM_INITDLG of the notebook dlg function if the app
4727 * has determined that it is running on Warp 4.
4728 *
4729 *@@changed V0.9.16 (2002-02-02) [umoeller]: fixed entry fields
4730 *@@changed V0.9.19 (2002-04-24) [umoeller]: removed ulDownUnits
4731 *@@changed V0.9.19 (2002-05-02) [umoeller]: fix for combobox
4732 */
4733
4734BOOL winhAssertWarp4Notebook(HWND hwndDlg,
4735 USHORT usIdThreshold) // in: ID threshold
4736{
4737 BOOL brc = FALSE;
4738
4739 if (doshIsWarp4())
4740 {
4741 LONG yLowest = 10000;
4742 HWND hwndItem;
4743 HENUM henum = 0;
4744 PSWP paswp,
4745 pswpThis;
4746 ULONG cWindows = 0,
4747 ul;
4748
4749 BOOL fIsVisible;
4750
4751 if (fIsVisible = WinIsWindowVisible(hwndDlg))
4752 // avoid flicker
4753 WinEnableWindowUpdate(hwndDlg, FALSE);
4754
4755 if (paswp = (PSWP)malloc(sizeof(SWP) * 100))
4756 {
4757 pswpThis = paswp;
4758
4759 // loop 1: set notebook buttons, find lowest y used
4760 henum = WinBeginEnumWindows(hwndDlg);
4761 while ((hwndItem = WinGetNextWindow(henum)))
4762 {
4763 USHORT usId = WinQueryWindowUShort(hwndItem, QWS_ID);
4764 // _Pmpf(("hwndItem: 0x%lX, ID: 0x%lX", hwndItem, usId));
4765 if (usId <= usIdThreshold)
4766 {
4767 // pushbutton to change:
4768 WinSetWindowBits(hwndItem,
4769 QWL_STYLE,
4770 BS_NOTEBOOKBUTTON, BS_NOTEBOOKBUTTON);
4771 brc = TRUE;
4772 }
4773 else
4774 {
4775 // no pushbutton to change:
4776 CHAR szClass[10];
4777
4778 WinQueryWindowPos(hwndItem, pswpThis);
4779
4780 // special handling for entry fields
4781 // V0.9.16 (2002-02-02) [umoeller]
4782 WinQueryClassName(hwndItem, sizeof(szClass), szClass);
4783 if (!strcmp(szClass, "#6"))
4784 {
4785 pswpThis->x += 3 * WinQuerySysValue(HWND_DESKTOP, SV_CXBORDER);
4786 pswpThis->y += 3 * WinQuerySysValue(HWND_DESKTOP, SV_CYBORDER);
4787 }
4788
4789 // check lowest y
4790 if ( (pswpThis->y < yLowest)
4791 // ignore combobox, this will distort everything
4792 // AGAIN ... sigh V0.9.19 (2002-05-02) [umoeller]
4793 && (strcmp(szClass, "#2"))
4794 )
4795 yLowest = pswpThis->y ;
4796
4797 ++pswpThis;
4798 if (++cWindows == 100)
4799 break;
4800 }
4801 } // end while ((hwndItem = WinGetNextWindow(henum)))
4802 WinEndEnumWindows(henum);
4803
4804 // now adjust window positions
4805 pswpThis = paswp;
4806 for (ul = 0;
4807 ul < cWindows;
4808 ++ul, ++pswpThis)
4809 {
4810 pswpThis->y -= (yLowest - 8);
4811 // 8 is magic to match the lower border of the
4812 // standard WPS notebook pages V0.9.19 (2002-04-24) [umoeller]
4813 pswpThis->fl = SWP_MOVE;
4814 }
4815
4816 WinSetMultWindowPos(WinQueryAnchorBlock(hwndDlg),
4817 paswp,
4818 cWindows);
4819
4820 free(paswp);
4821 }
4822
4823 if (fIsVisible)
4824 WinShowWindow(hwndDlg, TRUE);
4825 }
4826
4827 return brc;
4828}
4829
4830/*
4831 *@@ winhDrawFormattedText:
4832 * this func takes a rectangle and draws pszText into
4833 * it, breaking the words as neccessary. The line spacing
4834 * is determined from the font currently selected in hps.
4835 *
4836 * As opposed to WinDrawText, this can draw several lines
4837 * at once, and format the _complete_ text according to the
4838 * flCmd parameter, which is like with WinDrawText.
4839 *
4840 * After this function returns, *prcl is modified like this:
4841 *
4842 * -- yTop and yBottom contain the upper and lower boundaries
4843 * which were needed to draw the text. This depends on
4844 * whether DT_TOP etc. were specified.
4845 * To get the height of the rectangle used, calculate the
4846 * delta between yTop and yBottom.
4847 *
4848 * -- xLeft and xRight are modified to contain the outmost
4849 * left and right coordinates which were needed to draw
4850 * the text. This will be set to the longest line which
4851 * was encountered.
4852 *
4853 * The following DT_* flags are supported:
4854 *
4855 * -- DT_LEFT, DT_CENTER, DT_RIGHT all work.
4856 *
4857 * -- Vertically however only DT_TOP is supported.
4858 *
4859 * -- You can specify DT_QUERYEXTENT to only have
4860 * these text boundaries calculated without actually
4861 * drawing.
4862 *
4863 * Note that DT_TEXTATTRS will always be added, so you
4864 * will want to call GpiSetColor before this.
4865 *
4866 * This returns the number of lines drawn.
4867 *
4868 *@@changed V0.9.0 [umoeller]: prcl.xLeft and xRight are now updated too upon return
4869 */
4870
4871ULONG winhDrawFormattedText(HPS hps, // in: presentation space; its settings
4872 // are used, but not altered
4873 PRECTL prcl, // in/out: rectangle to use for drawing
4874 // (modified)
4875 const char *pcszText, // in: text to draw (zero-terminated)
4876 ULONG flCmd) // in: DT_* flags like in WinDrawText; see remarks
4877{
4878 PSZ p = (PSZ)pcszText;
4879 LONG lDrawn = 1,
4880 lTotalDrawn = 0,
4881 lLineCount = 0,
4882 lOrigYTop = prcl->yTop;
4883 ULONG ulTextLen = strlen(pcszText),
4884 ulCharHeight,
4885 flCmd2,
4886 xLeftmost = prcl->xRight,
4887 xRightmost = prcl->xLeft;
4888 RECTL rcl2;
4889
4890 flCmd2 = flCmd | DT_WORDBREAK | DT_TEXTATTRS;
4891
4892 ulCharHeight = gpihQueryLineSpacing(hps);
4893
4894 while ( (lDrawn)
4895 && (lTotalDrawn < ulTextLen)
4896 )
4897 {
4898 memcpy(&rcl2, prcl, sizeof(rcl2));
4899 lDrawn = WinDrawText(hps,
4900 ulTextLen - lTotalDrawn,
4901 p,
4902 &rcl2,
4903 0,
4904 0, // colors
4905 flCmd2);
4906
4907 // update char counters
4908 p += lDrawn;
4909 lTotalDrawn += lDrawn;
4910
4911 // update x extents
4912 if (rcl2.xLeft < xLeftmost)
4913 xLeftmost = rcl2.xLeft;
4914 if (rcl2.xRight > xRightmost)
4915 xRightmost = rcl2.xRight;
4916
4917 // update y for next line
4918 prcl->yTop -= ulCharHeight;
4919
4920 // increase line count
4921 lLineCount++;
4922 }
4923
4924 prcl->xLeft = xLeftmost;
4925 prcl->xRight = xRightmost;
4926 prcl->yBottom = prcl->yTop;
4927 prcl->yTop = lOrigYTop;
4928
4929 return lLineCount;
4930}
4931
4932/*
4933 *@@ winhQuerySwitchList:
4934 * returns the switch list in a newly
4935 * allocated buffer. This does the
4936 * regular double WinQuerySwitchList
4937 * call to first get the no. of items
4938 * and then get the items.
4939 *
4940 * The no. of items can be found in
4941 * the returned SWBLOCK.cwsentry.
4942 *
4943 * Returns NULL on errors. Use
4944 * free() to free the return value.
4945 *
4946 *@@added V0.9.7 (2000-12-06) [umoeller]
4947 */
4948
4949PSWBLOCK winhQuerySwitchList(HAB hab)
4950{
4951 ULONG cItems = WinQuerySwitchList(hab, NULL, 0);
4952 ULONG ulBufSize = (cItems * sizeof(SWENTRY)) + sizeof(HSWITCH);
4953 PSWBLOCK pSwBlock;
4954 if (pSwBlock = (PSWBLOCK)malloc(ulBufSize))
4955 {
4956 if (!(cItems = WinQuerySwitchList(hab, pSwBlock, ulBufSize)))
4957 {
4958 free(pSwBlock);
4959 pSwBlock = NULL;
4960 }
4961 }
4962
4963 return pSwBlock;
4964}
4965
4966typedef HSWITCH APIENTRY WINHSWITCHFROMHAPP(HAPP happ);
4967
4968/*
4969 *@@ winhHSWITCHfromHAPP:
4970 * resolves and calls the undocumented
4971 * WinHSWITCHfromHAPP API.
4972 *
4973 *@@added V0.9.19 (2002-04-17) [umoeller]
4974 */
4975
4976HSWITCH winhHSWITCHfromHAPP(HAPP happ)
4977{
4978 static WINHSWITCHFROMHAPP *pWinHSWITCHfromHAPP = NULL;
4979
4980 if (!pWinHSWITCHfromHAPP)
4981 // first call: import WinHSWITCHfromHAPP
4982 // WinHSWITCHfromHAPP PMMERGE.5199
4983 doshQueryProcAddr("PMMERGE",
4984 5199,
4985 (PFN*)&pWinHSWITCHfromHAPP);
4986
4987 if (pWinHSWITCHfromHAPP)
4988 return pWinHSWITCHfromHAPP(happ);
4989
4990 return NULLHANDLE;
4991}
4992
4993/*
4994 *@@ winhQueryTasklistWindow:
4995 * returns the window handle of the PM task list.
4996 *
4997 *@@added V0.9.7 (2000-12-07) [umoeller]
4998 */
4999
5000HWND winhQueryTasklistWindow(VOID)
5001{
5002 SWBLOCK swblock;
5003
5004 // the tasklist has entry #0 in the SWBLOCK
5005 WinQuerySwitchList(NULLHANDLE, &swblock, sizeof(SWBLOCK));
5006
5007 return swblock.aswentry[0].swctl.hwnd;
5008}
5009
5010/*
5011 *@@ winhKillTasklist:
5012 * this will destroy the Tasklist (window list) window.
5013 * Note: you will only be able to get it back after a
5014 * reboot, not a WPS restart. Only for use at shutdown and such.
5015 * This trick by Uri J. Stern at
5016 * http://zebra.asta.fh-weingarten.de/os2/Snippets/Howt8881.HTML
5017 */
5018
5019VOID winhKillTasklist(VOID)
5020{
5021 HWND hwndTasklist = winhQueryTasklistWindow();
5022 WinPostMsg(hwndTasklist,
5023 0x0454, // undocumented msg for killing tasklist
5024 NULL, NULL);
5025}
5026
5027// the following must be added for EMX (99-10-22) [umoeller]
5028#ifndef NERR_BufTooSmall
5029 #define NERR_BASE 2100
5030 #define NERR_BufTooSmall (NERR_BASE+23)
5031 // the API return buffer is too small
5032#endif
5033
5034/*
5035 *@@ winhQueryPendingSpoolJobs:
5036 * returns the number of pending print jobs in the spooler
5037 * or 0 if none. Useful for testing before shutdown.
5038 */
5039
5040ULONG winhQueryPendingSpoolJobs(VOID)
5041{
5042 // BOOL rcPending = FALSE;
5043 ULONG ulTotalJobCount = 0;
5044
5045 SPLERR splerr;
5046 USHORT jobCount;
5047 ULONG cbBuf;
5048 ULONG cTotal;
5049 ULONG cReturned;
5050 ULONG cbNeeded;
5051 ULONG ulLevel;
5052 ULONG i,j;
5053 PSZ pszComputerName;
5054 PBYTE pBuf = NULL;
5055 PPRQINFO3 prq;
5056 PPRJINFO2 prj2;
5057
5058 ulLevel = 4L;
5059 pszComputerName = (PSZ)NULL;
5060 splerr = SplEnumQueue(pszComputerName, ulLevel, pBuf, 0L, // cbBuf
5061 &cReturned, &cTotal,
5062 &cbNeeded, NULL);
5063 if ( (splerr == ERROR_MORE_DATA)
5064 || (splerr == NERR_BufTooSmall)
5065 )
5066 {
5067 if (!DosAllocMem((PPVOID)&pBuf,
5068 cbNeeded,
5069 PAG_READ | PAG_WRITE | PAG_COMMIT))
5070 {
5071 cbBuf = cbNeeded;
5072 splerr = SplEnumQueue(pszComputerName, ulLevel, pBuf, cbBuf,
5073 &cReturned, &cTotal,
5074 &cbNeeded, NULL);
5075 if (splerr == NO_ERROR)
5076 {
5077 // set pointer to point to the beginning of the buffer
5078 prq = (PPRQINFO3)pBuf;
5079
5080 // cReturned has the count of the number of PRQINFO3 structures
5081 for (i = 0;
5082 i < cReturned;
5083 i++)
5084 {
5085 // save the count of jobs; there are this many PRJINFO2
5086 // structures following the PRQINFO3 structure
5087 jobCount = prq->cJobs;
5088 // _Pmpf(( "Job count in this queue is %d",jobCount ));
5089
5090 // increment the pointer past the PRQINFO3 structure
5091 prq++;
5092
5093 // set a pointer to point to the first PRJINFO2 structure
5094 prj2=(PPRJINFO2)prq;
5095 for (j = 0;
5096 j < jobCount;
5097 j++)
5098 {
5099 // increment the pointer to point to the next structure
5100 prj2++;
5101 // increase the job count, which we'll return
5102 ulTotalJobCount++;
5103
5104 } // endfor jobCount
5105
5106 // after doing all the job structures, prj2 points to the next
5107 // queue structure; set the pointer for a PRQINFO3 structure
5108 prq = (PPRQINFO3)prj2;
5109 } //endfor cReturned
5110 } // endif NO_ERROR
5111 DosFreeMem(pBuf);
5112 }
5113 } // end if Q level given
5114
5115 return ulTotalJobCount;
5116}
5117
5118/*
5119 *@@ winhSetNumLock:
5120 * this sets the NumLock key on or off, depending
5121 * on fState.
5122 *
5123 * Based on code from WarpEnhancer, (C) Achim Hasenmller.
5124 *
5125 *@@added V0.9.1 (99-12-18) [umoeller]
5126 *@@changed V1.0.6 (2006-09-30) [pr]: Set Keyboard LEDs to match @@fixes 831
5127 */
5128
5129VOID winhSetNumLock(BOOL fState)
5130{
5131 // BOOL fRestoreKBD = FALSE; // Assume we're not going to close Kbd
5132 BYTE KeyStateTable[256];
5133 ULONG ulActionTaken; // Used by DosOpen
5134 HFILE hKbd;
5135
5136 // read keyboard state table
5137 if (WinSetKeyboardStateTable(HWND_DESKTOP, &KeyStateTable[0],
5138 FALSE))
5139 {
5140 // first set the PM state
5141 if (fState)
5142 KeyStateTable[VK_NUMLOCK] |= 0x01; // Turn numlock on
5143 else
5144 KeyStateTable[VK_NUMLOCK] &= 0xFE; // Turn numlock off
5145
5146 // set keyboard state table with new state values
5147 WinSetKeyboardStateTable(HWND_DESKTOP, &KeyStateTable[0], TRUE);
5148 }
5149
5150 // now set the OS/2 keyboard state
5151
5152 // try to open OS/2 keyboard driver
5153 if (!DosOpen("KBD$",
5154 &hKbd, &ulActionTaken,
5155 0, // cbFile
5156 FILE_NORMAL,
5157 OPEN_ACTION_FAIL_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS,
5158 OPEN_SHARE_DENYNONE | OPEN_ACCESS_READWRITE,
5159 NULL))
5160 {
5161 SHIFTSTATE ShiftState;
5162 USHORT usLEDState;
5163 ULONG DataLen = sizeof(SHIFTSTATE);
5164
5165 memset(&ShiftState, '\0', DataLen);
5166 DosDevIOCtl(hKbd, IOCTL_KEYBOARD, KBD_GETSHIFTSTATE,
5167 NULL, 0L, NULL,
5168 &ShiftState, DataLen, &DataLen);
5169
5170 if (fState)
5171 ShiftState.fsState |= 0x0020; // turn NumLock on
5172 else
5173 ShiftState.fsState &= 0xFFDF; // turn NumLock off
5174
5175 DosDevIOCtl(hKbd, IOCTL_KEYBOARD, KBD_SETSHIFTSTATE,
5176 &ShiftState, DataLen, &DataLen,
5177 NULL, 0L, NULL);
5178
5179 // XWP V1.0.6 (2006-09-30) [pr]: Set Keyboard LEDs to match @@fixes 831
5180 usLEDState = (ShiftState.fsState & (SCROLLLOCK_ON | NUMLOCK_ON | CAPSLOCK_ON)) >> 4;
5181 DataLen = sizeof(usLEDState);
5182 DosDevIOCtl(hKbd, IOCTL_KEYBOARD, KBD_ALTERKBDLED,
5183 &usLEDState, DataLen, &DataLen,
5184 NULL, 0L, NULL);
5185
5186 // now close OS/2 keyboard driver
5187 DosClose(hKbd);
5188 }
5189}
5190
5191/*
5192 *@@ winhSetClipboardText:
5193 * sets the clipboard data to the given text,
5194 * replacing the current clipboard contents.
5195 *
5196 *@@added V1.0.0 (2002-08-28) [umoeller]
5197 */
5198
5199BOOL winhSetClipboardText(HAB hab,
5200 PCSZ pcsz,
5201 ULONG cbSize) // in: size of buffer INCLUDING null byte
5202{
5203 BOOL fSuccess = FALSE;
5204
5205 if (WinOpenClipbrd(hab))
5206 {
5207 PSZ pszDest;
5208 if (!DosAllocSharedMem((PVOID*)&pszDest,
5209 NULL,
5210 cbSize,
5211 PAG_WRITE | PAG_COMMIT | OBJ_GIVEABLE))
5212 {
5213 memcpy(pszDest,
5214 pcsz,
5215 cbSize);
5216
5217 WinEmptyClipbrd(hab);
5218
5219 fSuccess = WinSetClipbrdData(hab, // anchor-block handle
5220 (ULONG)pszDest, // pointer to text data
5221 CF_TEXT, // data is in text format
5222 CFI_POINTER); // passing a pointer
5223
5224 // PMREF says (implicitly) it is not necessary to call
5225 // DosFreeMem. I hope that is correct.
5226 // V0.9.19 (2002-06-02) [umoeller]
5227 }
5228
5229 WinCloseClipbrd(hab);
5230 }
5231
5232 return fSuccess;
5233}
5234
5235/*
5236 *@@category: Helpers\PM helpers\Workplace Shell\WPS class list
5237 */
5238
5239/* ******************************************************************
5240 *
5241 * WPS Class List helpers
5242 *
5243 ********************************************************************/
5244
5245/*
5246 *@@ winhQueryWPSClassList:
5247 * this returns the WPS class list in a newly
5248 * allocated buffer. This is just a shortcut to
5249 * the usual double WinEnumObjectClasses call.
5250 *
5251 * The return value is actually of the POBJCLASS type,
5252 * so you better cast this manually. We declare this
5253 * this as PBYTE though because POBJCLASS requires
5254 * INCL_WINWORKPLACE.
5255 * See WinEnumObjectClasses() for details.
5256 *
5257 * Returns NULL on error. Use free()
5258 * to free the return value.
5259 *
5260 *@@added V0.9.0 [umoeller]
5261 */
5262
5263PBYTE winhQueryWPSClassList(VOID)
5264{
5265 ULONG ulSize;
5266 POBJCLASS pObjClass = 0;
5267
5268 // get WPS class list size
5269 if (WinEnumObjectClasses(NULL, &ulSize))
5270 {
5271 // allocate buffer
5272 pObjClass = (POBJCLASS)malloc(ulSize + 1);
5273 // and load the classes into it
5274 WinEnumObjectClasses(pObjClass, &ulSize);
5275 }
5276
5277 return (PBYTE)pObjClass;
5278}
5279
5280/*
5281 *@@ winhQueryWPSClass:
5282 * this returns the POBJCLASS item if pszClass is registered
5283 * with the WPS or NULL if the class could not be found.
5284 *
5285 * The return value is actually of the POBJCLASS type,
5286 * so you better cast this manually. We declare this
5287 * this as PBYTE though because POBJCLASS requires
5288 * INCL_WINWORKPLACE.
5289 *
5290 * This takes as input the return value of winhQueryWPSClassList,
5291 * which you must call first.
5292 *
5293 * <B>Usage:</B>
5294 + PBYTE pClassList = winhQueryWPSClassList(),
5295 + pWPFolder;
5296 + if (pClassList)
5297 + {
5298 + if (pWPFolder = winhQueryWPSClass(pClassList, "WPFolder"))
5299 + ...
5300 + free(pClassList);
5301 + }
5302 *
5303 *@@added V0.9.0 [umoeller]
5304 */
5305
5306PBYTE winhQueryWPSClass(PBYTE pObjClass, // in: buffer returned by
5307 // winhQueryWPSClassList
5308 const char *pszClass) // in: class name to query
5309{
5310 PBYTE pbReturn = 0;
5311
5312 POBJCLASS pocThis = (POBJCLASS)pObjClass;
5313 // now go thru the WPS class list
5314 while (pocThis)
5315 {
5316 if (!strcmp(pocThis->pszClassName, pszClass))
5317 {
5318 pbReturn = (PBYTE)pocThis;
5319 break;
5320 }
5321
5322 // next class
5323 pocThis = pocThis->pNext;
5324 } // end while (pocThis)
5325
5326 return pbReturn;
5327}
5328
5329/*
5330 *@@ winhRegisterClass:
5331 * this works just like WinRegisterObjectClass,
5332 * except that it returns a more meaningful
5333 * error code than just FALSE in case registering
5334 * fails.
5335 *
5336 * This returns NO_ERROR if the class was successfully
5337 * registered (WinRegisterObjectClass returned TRUE).
5338 *
5339 * Otherwise, we do a DosLoadModule if maybe the DLL
5340 * couldn't be loaded in the first place. If DosLoadModule
5341 * did not return NO_ERROR, this function returns that
5342 * return code, which can be:
5343 *
5344 * -- 2 ERROR_FILE_NOT_FOUND: pcszModule does not exist
5345 * -- 2 ERROR_FILE_NOT_FOUND
5346 * -- 3 ERROR_PATH_NOT_FOUND
5347 * -- 4 ERROR_TOO_MANY_OPEN_FILES
5348 * -- 5 ERROR_ACCESS_DENIED
5349 * -- 8 ERROR_NOT_ENOUGH_MEMORY
5350 * -- 11 ERROR_BAD_FORMAT
5351 * -- 26 ERROR_NOT_DOS_DISK (unknown media type)
5352 * -- 32 ERROR_SHARING_VIOLATION
5353 * -- 33 ERROR_LOCK_VIOLATION
5354 * -- 36 ERROR_SHARING_BUFFER_EXCEEDED
5355 * -- 95 ERROR_INTERRUPT (interrupted system call)
5356 * -- 108 ERROR_DRIVE_LOCKED (by another process)
5357 * -- 123 ERROR_INVALID_NAME (illegal character or FS name not valid)
5358 * -- 127 ERROR_PROC_NOT_FOUND (DosQueryProcAddr error)
5359 * -- 180 ERROR_INVALID_SEGMENT_NUMBER
5360 * -- 182 ERROR_INVALID_ORDINAL
5361 * -- 190 ERROR_INVALID_MODULETYPE (probably an application)
5362 * -- 191 ERROR_INVALID_EXE_SIGNATURE (probably not LX DLL)
5363 * -- 192 ERROR_EXE_MARKED_INVALID (by linker)
5364 * -- 194 ERROR_ITERATED_DATA_EXCEEDS_64K (in a DLL segment)
5365 * -- 195 ERROR_INVALID_MINALLOCSIZE
5366 * -- 196 ERROR_DYNLINK_FROM_INVALID_RING
5367 * -- 198 ERROR_INVALID_SEGDPL
5368 * -- 199 ERROR_AUTODATASEG_EXCEEDS_64K
5369 * -- 201 ERROR_RELOCSRC_CHAIN_EXCEEDS_SEGLIMIT
5370 * -- 206 ERROR_FILENAME_EXCED_RANGE (not matching 8+3 spec)
5371 * -- 295 ERROR_INIT_ROUTINE_FAILED (DLL init routine failed)
5372 *
5373 * In all these cases, pszBuf may contain a meaningful
5374 * error message from DosLoadModule, especially if an import
5375 * could not be resolved.
5376 *
5377 * Still worse, if DosLoadModule returned NO_ERROR, we
5378 * probably have some SOM internal error. A probable
5379 * reason is that the parent class of pcszClassName
5380 * is not installed, but that's WPS/SOM internal
5381 * and cannot be queried from outside the WPS context.
5382 *
5383 * In that case, ERROR_OPEN_FAILED (110) is returned.
5384 * That one sounded good to me. ;-)
5385 */
5386
5387APIRET winhRegisterClass(const char* pcszClassName, // in: e.g. "XFolder"
5388 const char* pcszModule, // in: e.g. "C:\XFOLDER\XFLDR.DLL"
5389 PSZ pszBuf, // out: error message from DosLoadModule
5390 ULONG cbBuf) // in: sizeof(*pszBuf), passed to DosLoadModule
5391{
5392 APIRET arc = NO_ERROR;
5393
5394 if (!WinRegisterObjectClass((PSZ)pcszClassName, (PSZ)pcszModule))
5395 {
5396 // failed: do more error checking then, try DosLoadModule
5397 HMODULE hmod = NULLHANDLE;
5398 if (!(arc = DosLoadModule(pszBuf, cbBuf,
5399 (PSZ)pcszModule,
5400 &hmod)))
5401 {
5402 // DosLoadModule succeeded:
5403 // some SOM error then
5404 DosFreeModule(hmod);
5405 arc = ERROR_OPEN_FAILED;
5406 }
5407 }
5408 // else: ulrc still 0 (== no error)
5409
5410 return arc;
5411}
5412
5413/*
5414 *@@ winhIsClassRegistered:
5415 * quick one-shot function which checks if
5416 * a class is currently registered. Calls
5417 * winhQueryWPSClassList and winhQueryWPSClass
5418 * in turn.
5419 *
5420 *@@added V0.9.2 (2000-02-26) [umoeller]
5421 */
5422
5423BOOL winhIsClassRegistered(const char *pcszClass)
5424{
5425 BOOL brc = FALSE;
5426 PBYTE pClassList;
5427
5428 if (pClassList = winhQueryWPSClassList())
5429 {
5430 if (winhQueryWPSClass(pClassList, pcszClass))
5431 brc = TRUE;
5432
5433 free(pClassList);
5434 }
5435
5436 return brc;
5437}
5438
5439/*
5440 *@@category: Helpers\PM helpers\Workplace Shell
5441 */
5442
5443/*
5444 *@@ winhResetWPS:
5445 * restarts the WPS using PrfReset. Returns
5446 * one of the following:
5447 *
5448 * -- 0: no error.
5449 * -- 1: PrfReset failed.
5450 * -- 2 or 4: PrfQueryProfile failed.
5451 * -- 3: malloc() failed.
5452 *
5453 *@@added V0.9.4 (2000-07-01) [umoeller]
5454 *@@changed V1.0.5 (2005-02-17) [pr]: replaced this with something less brutal
5455 */
5456
5457ULONG winhResetWPS(HAB hab)
5458{
5459 ULONG ulrc = 0;
5460
5461#if 1
5462 WinRestartWorkplace();
5463#else
5464 // find out current profile names
5465 PRFPROFILE Profiles;
5466 Profiles.cchUserName = Profiles.cchSysName = 0;
5467 // first query their file name lengths
5468 if (PrfQueryProfile(hab, &Profiles))
5469 {
5470 // allocate memory for filenames
5471 Profiles.pszUserName = (PSZ)malloc(Profiles.cchUserName);
5472 Profiles.pszSysName = (PSZ)malloc(Profiles.cchSysName);
5473
5474 if (Profiles.pszSysName)
5475 {
5476 // get filenames
5477 if (PrfQueryProfile(hab, &Profiles))
5478 {
5479
5480 // "change" INIs to these filenames:
5481 // THIS WILL RESET THE WPS
5482 if (PrfReset(hab, &Profiles) == FALSE)
5483 ulrc = 1;
5484 free(Profiles.pszSysName);
5485 free(Profiles.pszUserName);
5486 }
5487 else
5488 ulrc = 2;
5489 }
5490 else
5491 ulrc = 3;
5492 }
5493 else
5494 ulrc = 4;
5495#endif
5496
5497 return ulrc;
5498}
Note: See TracBrowser for help on using the repository browser.