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

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

Misc fixes.

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