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

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

Misc changes

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