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

Last change on this file since 385 was 384, checked in by pr, 15 years ago

Fix variable init. bugs.

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