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

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

New toolbar control.

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