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

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

More fixes.

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