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

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

Fixed build breaks.

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