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

Last change on this file since 238 was 238, 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: 176.9 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 *@@ winhSetWindowFont:
2972 * this sets a window's font by invoking
2973 * WinSetPresParam on it.
2974 *
2975 * If (pszFont == NULL), a default font will be set
2976 * (on Warp 4, "9.WarpSans", on Warp 3, "8.Helv").
2977 *
2978 * winh.h also defines the winhSetDlgItemFont macro.
2979 *
2980 * Returns TRUE if successful or FALSE otherwise.
2981 *
2982 *@@added V0.9.0 [umoeller]
2983 *@@changed V1.0.1 (2002-11-30) [umoeller]: optimized
2984 */
2985
2986BOOL winhSetWindowFont(HWND hwnd,
2987 const char *pcszFont)
2988{
2989 if (!pcszFont)
2990 pcszFont = winhQueryDefaultFont();
2991
2992 return WinSetPresParam(hwnd,
2993 PP_FONTNAMESIZE,
2994 strlen(pcszFont) + 1,
2995 (PSZ)pcszFont);
2996}
2997
2998/*
2999 *@@ winhSetControlsFont:
3000 * this sets the font for all the controls of hwndDlg
3001 * which have a control ID in the range of usIDMin to
3002 * usIDMax. "Unused" IDs (i.e. -1) will also be set.
3003 *
3004 * If (pszFont == NULL), a default font will be set
3005 * (on Warp 4, "9.WarpSans", on Warp 3, "8.Helv").
3006 *
3007 * Returns the no. of controls set.
3008 *
3009 *@@added V0.9.0 [umoeller]
3010 */
3011
3012ULONG winhSetControlsFont(HWND hwndDlg, // in: dlg to set
3013 SHORT usIDMin, // in: minimum control ID to be set (inclusive)
3014 SHORT usIDMax, // in: maximum control ID to be set (inclusive)
3015 const char *pcszFont) // in: font to use (e.g. "9.WarpSans") or NULL
3016{
3017 ULONG ulrc = 0;
3018 HENUM henum;
3019 HWND hwndItem;
3020 CHAR szFont[256];
3021 ULONG cbFont;
3022
3023 if (pcszFont == NULL)
3024 {
3025 if (doshIsWarp4())
3026 strhncpy0(szFont, "9.WarpSans", sizeof(szFont));
3027 else
3028 strhncpy0(szFont, "8.Helv", sizeof(szFont));
3029 }
3030 else
3031 strhncpy0(szFont, pcszFont, sizeof(szFont));
3032
3033 cbFont = strlen(szFont) + 1;
3034
3035 // set font for all the dialog controls
3036 henum = WinBeginEnumWindows(hwndDlg);
3037 while ((hwndItem = WinGetNextWindow(henum)))
3038 {
3039 SHORT sID = WinQueryWindowUShort(hwndItem, QWS_ID);
3040 if ( (sID == -1)
3041 || ((sID >= usIDMin) && (sID <= usIDMax))
3042 )
3043 if (WinSetPresParam(hwndItem,
3044 PP_FONTNAMESIZE,
3045 cbFont,
3046 szFont))
3047 // successful:
3048 ulrc++;
3049 }
3050 WinEndEnumWindows(henum);
3051
3052 return ulrc;
3053}
3054
3055/*
3056 *@@ winhStorePresParam:
3057 * this appends a new presentation parameter to an
3058 * array of presentation parameters which can be
3059 * passed to WinCreateWindow. This is preferred
3060 * over setting the presparams using WinSetPresParams,
3061 * because that call will cause a lot of messages.
3062 *
3063 * On the first call, pppp _must_ be NULL. This
3064 * will allocate memory for storing the given
3065 * data as necessary and modify *pppp to point
3066 * to the new array.
3067 *
3068 * On subsequent calls with the same pppp, memory
3069 * will be reallocated, the old data will be copied,
3070 * and the new given data will be appended.
3071 *
3072 * Use free() on your PPRESPARAMS pointer (whose
3073 * address was passed) after WinCreateWindow.
3074 *
3075 * See winhQueryPresColor for typical presparams
3076 * used in OS/2.
3077 *
3078 * Example:
3079 *
3080 + PPRESPARAMS ppp = NULL;
3081 + CHAR szFont[] = "9.WarpSans";
3082 + LONG lColor = CLR_WHITE;
3083 + winhStorePresParam(&ppp, PP_FONTNAMESIZE, sizeof(szFont), szFont);
3084 + winhStorePresParam(&ppp, PP_BACKGROUNDCOLOR, sizeof(lColor), &lColor);
3085 + WinCreateWindow(...., ppp);
3086 + free(ppp);
3087 *
3088 *@@added V0.9.0 [umoeller]
3089 */
3090
3091BOOL winhStorePresParam(PPRESPARAMS *pppp, // in: data pointer (modified)
3092 ULONG ulAttrType, // in: PP_* index
3093 ULONG cbData, // in: sizeof(*pData), e.g. sizeof(LONG)
3094 PVOID pData) // in: presparam data (e.g. a PLONG to a color)
3095{
3096 BOOL brc = FALSE;
3097 if (pppp)
3098 {
3099 ULONG cbOld = 0,
3100 cbNew;
3101 PBYTE pbTemp = 0;
3102 PPRESPARAMS pppTemp = 0;
3103 PPARAM pppCopyTo = 0;
3104
3105 if (*pppp != NULL)
3106 // subsequent calls:
3107 cbOld = (**pppp).cb;
3108
3109 cbNew = sizeof(ULONG) // PRESPARAMS.cb
3110 + cbOld // old count, which does not include PRESPARAMS.cb
3111 + sizeof(ULONG) // PRESPARAMS.aparam[0].id
3112 + sizeof(ULONG) // PRESPARAMS.aparam[0].cb
3113 + cbData; // PRESPARAMS.aparam[0].ab[]
3114
3115 pbTemp = (PBYTE)malloc(cbNew);
3116 if (pbTemp)
3117 {
3118 pppTemp = (PPRESPARAMS)pbTemp;
3119
3120 if (*pppp != NULL)
3121 {
3122 // copy old data
3123 memcpy(pbTemp, *pppp, cbOld + sizeof(ULONG)); // including PRESPARAMS.cb
3124 pppCopyTo = (PPARAM)(pbTemp // new buffer
3125 + sizeof(ULONG) // skipping PRESPARAMS.cb
3126 + cbOld); // old PARAM array
3127 }
3128 else
3129 // first call:
3130 pppCopyTo = pppTemp->aparam;
3131
3132 pppTemp->cb = cbNew - sizeof(ULONG); // excluding PRESPARAMS.cb
3133 pppCopyTo->id = ulAttrType;
3134 pppCopyTo->cb = cbData; // byte count of PARAM.ab[]
3135 memcpy(pppCopyTo->ab, pData, cbData);
3136
3137 free(*pppp);
3138 *pppp = pppTemp;
3139
3140 brc = TRUE;
3141 }
3142 }
3143
3144 return brc;
3145}
3146
3147/*
3148 *@@ winhCreateDefaultPresparams:
3149 *
3150 * Caller must free() the return value.
3151 *
3152 *@@added V1.0.1 (2002-11-30) [umoeller]
3153 */
3154
3155PPRESPARAMS winhCreateDefaultPresparams(VOID)
3156{
3157 PPRESPARAMS ppp = NULL;
3158
3159 PCSZ pcszFont = winhQueryDefaultFont();
3160 LONG lColor;
3161
3162 winhStorePresParam(&ppp,
3163 PP_FONTNAMESIZE,
3164 strlen(pcszFont) + 1,
3165 (PVOID)pcszFont);
3166
3167 lColor = WinQuerySysColor(HWND_DESKTOP,
3168 SYSCLR_DIALOGBACKGROUND,
3169 0);
3170 winhStorePresParam(&ppp,
3171 PP_BACKGROUNDCOLOR,
3172 sizeof(lColor),
3173 &lColor);
3174
3175 lColor = RGBCOL_BLACK;
3176 winhStorePresParam(&ppp,
3177 PP_FOREGROUNDCOLOR,
3178 sizeof(lColor),
3179 &lColor);
3180
3181 return ppp;
3182}
3183
3184/*
3185 *@@ winhQueryPresColor2:
3186 * returns the specified color. This is queried in the
3187 * following order:
3188 *
3189 * 1) hwnd's pres params are searched for ulPP
3190 * (which should be a PP_* index);
3191 * 2) if (fInherit == TRUE), the parent windows
3192 * are searched also;
3193 * 3) if this fails or (fInherit == FALSE), WinQuerySysColor
3194 * is called to get lSysColor (which should be a SYSCLR_*
3195 * index), if lSysColor != -1;
3196 * 4) if (lSysColor == -1), -1 is returned.
3197 *
3198 * The return value is always an RGB LONG, _not_ a color index.
3199 * This is even true for the returned system colors, which are
3200 * converted to RGB.
3201 *
3202 * If you do any painting with this value, you should switch
3203 * the HPS you're using to RGB mode (use gpihSwitchToRGB for that).
3204 *
3205 * Some useful ulPP / lSysColor pairs
3206 * (default values as in PMREF):
3207 *
3208 + -- PP_FOREGROUNDCOLOR SYSCLR_WINDOWTEXT (for most controls also)
3209 + SYSCLR_WINDOWSTATICTEXT (for static controls)
3210 + Foreground color (default: black)
3211 + -- PP_BACKGROUNDCOLOR SYSCLR_BACKGROUND
3212 + SYSCLR_DIALOGBACKGROUND
3213 + SYSCLR_FIELDBACKGROUND (for disabled scrollbars)
3214 + SYSCLR_WINDOW (application surface -- empty clients)
3215 + Background color (default: light gray)
3216 + -- PP_ACTIVETEXTFGNDCOLOR
3217 + -- PP_HILITEFOREGROUNDCOLOR SYSCLR_HILITEFOREGROUND
3218 + Highlighted foreground color, for example for selected menu
3219 + (def.: white)
3220 + -- PP_ACTIVETEXTBGNDCOLOR
3221 + -- PP_HILITEBACKGROUNDCOLOR SYSCLR_HILITEBACKGROUND
3222 + Highlighted background color (def.: dark gray)
3223 + -- PP_INACTIVETEXTFGNDCOLOR
3224 + -- PP_DISABLEDFOREGROUNDCOLOR SYSCLR_MENUDISABLEDTEXT
3225 + Disabled foreground color (dark gray)
3226 + -- PP_INACTIVETEXTBGNDCOLOR
3227 + -- PP_DISABLEDBACKGROUNDCOLOR
3228 + Disabled background color
3229 + -- PP_BORDERCOLOR SYSCLR_WINDOWFRAME
3230 + SYSCLR_INACTIVEBORDER
3231 + Border color (around pushbuttons, in addition to
3232 + the 3D colors)
3233 + -- PP_ACTIVECOLOR SYSCLR_ACTIVETITLE
3234 + Active color
3235 + -- PP_INACTIVECOLOR SYSCLR_INACTIVETITLE
3236 + Inactive color
3237 *
3238 * For menus:
3239 + -- PP_MENUBACKGROUNDCOLOR SYSCLR_MENU
3240 + -- PP_MENUFOREGROUNDCOLOR SYSCLR_MENUTEXT
3241 + -- PP_MENUHILITEBGNDCOLOR SYSCLR_MENUHILITEBGND
3242 + -- PP_MENUHILITEFGNDCOLOR SYSCLR_MENUHILITE
3243 + -- ?? SYSCLR_MENUDISABLEDTEXT
3244 +
3245 * For containers (according to the API ref. at EDM/2):
3246 + -- PP_FOREGROUNDCOLOR SYSCLR_WINDOWTEXT
3247 + -- PP_BACKGROUNDCOLOR SYSCLR_WINDOW
3248 + -- PP_HILITEFOREGROUNDCOLOR SYSCLR_HILITEFOREGROUND
3249 + -- PP_HILITEBACKGROUNDCOLOR SYSCLR_HILITEBACKGROUND
3250 + -- PP_BORDERCOLOR
3251 + (used for separator lines, eg. in Details view)
3252 + -- PP_ICONTEXTBACKGROUNDCOLOR
3253 + (column titles in Details view?!?)
3254 +
3255 * For listboxes / entryfields / MLE's:
3256 + -- PP_BACKGROUNDCOLOR SYSCLR_ENTRYFIELD
3257 *
3258 * PMREF has more of these.
3259 *
3260 *@@changed V0.9.0 [umoeller]: removed INI key query, using SYSCLR_* instead; function prototype changed
3261 *@@changed V0.9.0 [umoeller]: added fInherit parameter
3262 *@@changed V0.9.7 (2000-12-02) [umoeller]: added lSysColor == -1 support
3263 *@@changed V0.9.20 (2002-08-04) [umoeller]: added ulPPIndex, renamed func
3264 */
3265
3266LONG winhQueryPresColor2(HWND hwnd, // in: window to query
3267 ULONG ulppRGB, // in: PP_* index for RGB color
3268 ULONG ulppIndex, // in: PP_* index for color _index_ (can be null)
3269 BOOL fInherit, // in: search parent windows too?
3270 LONG lSysColor) // in: SYSCLR_* index or -1
3271{
3272 ULONG ul,
3273 attrFound;
3274 LONG lColorFound;
3275
3276 if (ulppRGB != (ULONG)-1)
3277 {
3278 ULONG fl = 0;
3279 if (!fInherit)
3280 fl = QPF_NOINHERIT;
3281 if (ulppIndex)
3282 fl |= QPF_ID2COLORINDEX; // convert indexed color 2 to RGB V0.9.20 (2002-08-04) [umoeller]
3283
3284 if ((ul = WinQueryPresParam(hwnd,
3285 ulppRGB,
3286 ulppIndex,
3287 &attrFound,
3288 sizeof(lColorFound),
3289 &lColorFound,
3290 fl)))
3291 return lColorFound;
3292 }
3293
3294 // not found: get system color
3295 if (lSysColor != -1)
3296 return WinQuerySysColor(HWND_DESKTOP, lSysColor, 0);
3297
3298 return -1;
3299}
3300
3301/*
3302 *@@ winhQueryPresColor:
3303 * compatibility function because this one was
3304 * exported.
3305 *
3306 *@@added V0.9.20 (2002-08-04) [umoeller]
3307 */
3308
3309LONG XWPENTRY winhQueryPresColor(HWND hwnd,
3310 ULONG ulPP,
3311 BOOL fInherit,
3312 LONG lSysColor)
3313{
3314 return winhQueryPresColor2(hwnd,
3315 ulPP,
3316 0,
3317 fInherit,
3318 lSysColor);
3319}
3320
3321/*
3322 *@@ winhSetPresColor:
3323 * sets a color presparam. ulIndex specifies
3324 * the presparam to be set and would normally
3325 * be either PP_BACKGROUNDCOLOR or PP_FOREGROUNDCOLOR.
3326 *
3327 *@@added V0.9.16 (2001-10-15) [umoeller]
3328 */
3329
3330BOOL winhSetPresColor(HWND hwnd,
3331 ULONG ulIndex,
3332 LONG lColor)
3333{
3334 return WinSetPresParam(hwnd,
3335 ulIndex,
3336 sizeof(LONG),
3337 &lColor);
3338}
3339
3340/*
3341 *@@category: Helpers\PM helpers\Help (IPF)
3342 */
3343
3344/* ******************************************************************
3345 *
3346 * Help instance helpers
3347 *
3348 ********************************************************************/
3349
3350/*
3351 *@@ winhCreateHelp:
3352 * creates a help instance and connects it with the
3353 * given frame window.
3354 *
3355 * If (pszFileName == NULL), we'll retrieve the
3356 * executable's fully qualified file name and
3357 * replace the extension with .HLP simply. This
3358 * avoids the typical "Help not found" errors if
3359 * the program isn't started in its own directory.
3360 *
3361 * If you have created a help table in memory, specify it
3362 * with pHelpTable. To load a help table from the resources,
3363 * specify hmod (or NULLHANDLE) and set pHelpTable to the
3364 * following:
3365 +
3366 + (PHELPTABLE)MAKELONG(usTableID, 0xffff)
3367 *
3368 * Returns the help window handle or NULLHANDLE on errors.
3369 *
3370 * Based on an EDM/2 code snippet.
3371 *
3372 *@@added V0.9.4 (2000-07-03) [umoeller]
3373 */
3374
3375HWND winhCreateHelp(HWND hwndFrame, // in: app's frame window handle; can be NULLHANDLE
3376 const char *pcszFileName, // in: help file name or NULL
3377 HMODULE hmod, // in: module with help table or NULLHANDLE (current)
3378 PHELPTABLE pHelpTable, // in: help table or resource ID
3379 const char *pcszWindowTitle) // in: help window title or NULL
3380{
3381 HELPINIT hi;
3382 PSZ pszExt;
3383 CHAR szName[CCHMAXPATH];
3384 HWND hwndHelp;
3385
3386 if (pcszFileName == NULL)
3387 {
3388 PPIB ppib;
3389 PTIB ptib;
3390 DosGetInfoBlocks(&ptib, &ppib);
3391 DosQueryModuleName(ppib->pib_hmte, sizeof(szName), szName);
3392
3393 pszExt = strrchr(szName, '.');
3394 if (pszExt)
3395 strcpy(pszExt, ".hlp");
3396 else
3397 strcat(szName, ".hlp");
3398
3399 pcszFileName = szName;
3400 }
3401
3402 hi.cb = sizeof(HELPINIT);
3403 hi.ulReturnCode = 0;
3404 hi.pszTutorialName = NULL;
3405 hi.phtHelpTable = pHelpTable;
3406 hi.hmodHelpTableModule = hmod;
3407 hi.hmodAccelActionBarModule = NULLHANDLE;
3408 hi.idAccelTable = 0;
3409 hi.idActionBar = 0;
3410 hi.pszHelpWindowTitle = (PSZ)pcszWindowTitle;
3411 hi.fShowPanelId = CMIC_HIDE_PANEL_ID;
3412 hi.pszHelpLibraryName = (PSZ)pcszFileName;
3413
3414 hwndHelp = WinCreateHelpInstance(WinQueryAnchorBlock(hwndFrame),
3415 &hi);
3416 if ((hwndFrame) && (hwndHelp))
3417 {
3418 WinAssociateHelpInstance(hwndHelp, hwndFrame);
3419 }
3420
3421 return hwndHelp;
3422}
3423
3424/*
3425 *@@ winhDisplayHelpPanel:
3426 * displays the specified help panel ID.
3427 *
3428 * If (ulHelpPanel == 0), this displays the
3429 * standard OS/2 "Using help" panel.
3430 *
3431 * Returns zero on success or one of the
3432 * help manager error codes on failure.
3433 * See HM_ERROR for those.
3434 *
3435 *@@added V0.9.7 (2001-01-21) [umoeller]
3436 */
3437
3438ULONG winhDisplayHelpPanel(HWND hwndHelpInstance, // in: from winhCreateHelp
3439 ULONG ulHelpPanel) // in: help panel ID
3440{
3441 return (ULONG)WinSendMsg(hwndHelpInstance,
3442 HM_DISPLAY_HELP,
3443 (MPARAM)ulHelpPanel,
3444 (MPARAM)( (ulHelpPanel != 0)
3445 ? HM_RESOURCEID
3446 : 0));
3447}
3448
3449/*
3450 *@@ winhDestroyHelp:
3451 * destroys the help instance created by winhCreateHelp.
3452 *
3453 * Based on an EDM/2 code snippet.
3454 *
3455 *@@added V0.9.4 (2000-07-03) [umoeller]
3456 */
3457
3458void winhDestroyHelp(HWND hwndHelp,
3459 HWND hwndFrame) // can be NULLHANDLE if not used with winhCreateHelp
3460{
3461 if (hwndHelp)
3462 {
3463 if (hwndFrame)
3464 WinAssociateHelpInstance(NULLHANDLE, hwndFrame);
3465 WinDestroyHelpInstance(hwndHelp);
3466 }
3467}
3468
3469/*
3470 *@@category: Helpers\PM helpers\Application control
3471 */
3472
3473/* ******************************************************************
3474 *
3475 * Application control
3476 *
3477 ********************************************************************/
3478
3479/*
3480 *@@ winhAnotherInstance:
3481 * this tests whether another instance of the same
3482 * application is already running.
3483 *
3484 * To identify instances of the same application, the
3485 * application must call this function during startup
3486 * with the unique name of an OS/2 semaphore. As with
3487 * all OS/2 semaphores, the semaphore name must begin
3488 * with "\\SEM32\\". The semaphore isn't really used
3489 * except for testing for its existence, since that
3490 * name is unique among all processes.
3491 *
3492 * If another instance is found, TRUE is returned. If
3493 * (fSwitch == TRUE), that instance is switched to,
3494 * using the tasklist.
3495 *
3496 * If no other instance is found, FALSE is returned only.
3497 *
3498 * Based on an EDM/2 code snippet.
3499 *
3500 *@@added V0.9.0 (99-10-22) [umoeller]
3501 */
3502
3503BOOL winhAnotherInstance(const char *pcszSemName, // in: semaphore ID
3504 BOOL fSwitch) // in: if TRUE, switch to first instance if running
3505{
3506 HMTX hmtx;
3507
3508 if (DosCreateMutexSem((PSZ)pcszSemName,
3509 &hmtx,
3510 DC_SEM_SHARED,
3511 TRUE)
3512 == NO_ERROR)
3513 // semapore created: this doesn't happen if the semaphore
3514 // exists already, so no other instance is running
3515 return FALSE;
3516
3517 // else: instance running
3518 hmtx = NULLHANDLE;
3519
3520 // switch to other instance?
3521 if (fSwitch)
3522 {
3523 // yes: query mutex creator
3524 if (DosOpenMutexSem((PSZ)pcszSemName,
3525 &hmtx)
3526 == NO_ERROR)
3527 {
3528 PID pid = 0;
3529 TID tid = 0; // unused
3530 ULONG ulCount; // unused
3531
3532 if (DosQueryMutexSem(hmtx, &pid, &tid, &ulCount) == NO_ERROR)
3533 {
3534 HSWITCH hswitch = WinQuerySwitchHandle(NULLHANDLE, pid);
3535 if (hswitch != NULLHANDLE)
3536 WinSwitchToProgram(hswitch);
3537 }
3538
3539 DosCloseMutexSem(hmtx);
3540 }
3541 }
3542
3543 return TRUE; // another instance exists
3544}
3545
3546/*
3547 *@@ winhAddToTasklist:
3548 * this adds the specified window to the tasklist
3549 * with hIcon as its program icon (which is also
3550 * set for the main window). This is useful for
3551 * the old "dialog as main window" trick.
3552 *
3553 * Returns the HSWITCH of the added entry.
3554 */
3555
3556HSWITCH winhAddToTasklist(HWND hwnd, // in: window to add
3557 HPOINTER hIcon) // in: icon for main window
3558{
3559 SWCNTRL swctl;
3560 HSWITCH hswitch = 0;
3561 swctl.hwnd = hwnd; // window handle
3562 swctl.hwndIcon = hIcon; // icon handle
3563 swctl.hprog = NULLHANDLE; // program handle (use default)
3564 WinQueryWindowProcess(hwnd, &(swctl.idProcess), NULL);
3565 // process identifier
3566 swctl.idSession = 0; // session identifier ?
3567 swctl.uchVisibility = SWL_VISIBLE; // visibility
3568 swctl.fbJump = SWL_JUMPABLE; // jump indicator
3569 // get window title from window titlebar
3570 if (hwnd)
3571 WinQueryWindowText(hwnd, sizeof(swctl.szSwtitle), swctl.szSwtitle);
3572 swctl.bProgType = PROG_DEFAULT; // program type
3573 hswitch = WinAddSwitchEntry(&swctl);
3574
3575 // give the main window the icon
3576 if ((hwnd) && (hIcon))
3577 WinSendMsg(hwnd,
3578 WM_SETICON,
3579 (MPARAM)hIcon,
3580 NULL);
3581
3582 return hswitch;
3583}
3584
3585/*
3586 *@@category: Helpers\PM helpers\Miscellaneous
3587 */
3588
3589/* ******************************************************************
3590 *
3591 * Miscellaneous
3592 *
3593 ********************************************************************/
3594
3595/*
3596 *@@ winhFree:
3597 * frees a block of memory allocated by the
3598 * winh* functions.
3599 *
3600 * Since the winh* functions use malloc(),
3601 * you can also use free() directly on such
3602 * blocks. However, you must use winhFree
3603 * if the winh* functions are in a module
3604 * with a different C runtime.
3605 *
3606 *@@added V0.9.7 (2000-12-06) [umoeller]
3607 */
3608
3609VOID winhFree(PVOID p)
3610{
3611 if (p)
3612 free(p);
3613}
3614
3615/*
3616 *@@ winhSleep:
3617 * sleeps at least the specified amount of time,
3618 * without blocking the message queue.
3619 *
3620 * NOTE: This function is a bit expensive because
3621 * it creates a temporary object window. If you
3622 * need to sleep several times, you should rather
3623 * use a private timer.
3624 *
3625 *@@added V0.9.4 (2000-07-11) [umoeller]
3626 *@@changed V0.9.9 (2001-03-11) [umoeller]: rewritten
3627 */
3628
3629VOID winhSleep(ULONG ulSleep) // in: sleep time in milliseconds
3630{
3631 HWND hwnd;
3632
3633 if (hwnd = winhCreateObjectWindow(WC_STATIC, NULL))
3634 {
3635 QMSG qmsg;
3636 HAB hab;
3637
3638 if ( (hab = WinQueryAnchorBlock(hwnd))
3639 && (WinStartTimer(hab,
3640 hwnd,
3641 1,
3642 ulSleep))
3643 )
3644 {
3645 while (WinGetMsg(hab, &qmsg, NULLHANDLE, 0, 0))
3646 {
3647 if ( (qmsg.hwnd == hwnd)
3648 && (qmsg.msg == WM_TIMER)
3649 && (qmsg.mp1 == (MPARAM)1) // timer ID
3650 )
3651 break;
3652
3653 WinDispatchMsg(hab, &qmsg);
3654 }
3655 WinStopTimer(hab,
3656 hwnd,
3657 1);
3658 }
3659 else
3660 // timer creation failed:
3661 DosSleep(ulSleep);
3662
3663 WinDestroyWindow(hwnd);
3664 }
3665 else
3666 DosSleep(ulSleep);
3667}
3668
3669/*
3670 *@@ winhFileDlg:
3671 * one-short function for opening an "Open" file
3672 * dialog.
3673 *
3674 * On input, pszFile specifies the directory and
3675 * file specification (e.g. "F:\*.txt").
3676 *
3677 * Returns TRUE if the user pressed OK. In that
3678 * case, the fully qualified filename is written
3679 * into pszFile again.
3680 *
3681 * Returns FALSE if the user pressed Cancel.
3682 *
3683 * Notes about flFlags:
3684 *
3685 * -- WINH_FOD_SAVEDLG: display a "Save As" dialog.
3686 * Otherwise an "Open" dialog is displayed.
3687 *
3688 * -- WINH_FOD_INILOADDIR: load a directory from the
3689 * specified INI key and switch the dlg to it.
3690 * In that case, on input, pszFile must only
3691 * contain the file filter without any path
3692 * specification, because that is loaded from
3693 * the INI key. If the INI key does not exist,
3694 * the current process directory will be used.
3695 *
3696 * -- WINH_FOD_INISAVEDIR: if the user presses OK,
3697 * the directory of the selected file is written
3698 * to the specified INI key so that it can be
3699 * reused later. This flag is independent of
3700 * WINH_FOD_INISAVEDIR: you can specify none,
3701 * one, or both of them.
3702 *
3703 *@@added V0.9.3 (2000-04-29) [umoeller]
3704 *@@changed V0.9.12 (2001-05-21) [umoeller]: this failed if INI data had root dir, fixed
3705 */
3706
3707BOOL winhFileDlg(HWND hwndOwner, // in: owner for file dlg
3708 PSZ pszFile, // in: file mask; out: fully q'd filename
3709 // (should be CCHMAXPATH in size)
3710 ULONG flFlags, // in: any combination of the following:
3711 // -- WINH_FOD_SAVEDLG: save dlg; else open dlg
3712 // -- WINH_FOD_INILOADDIR: load FOD path from INI
3713 // -- WINH_FOD_INISAVEDIR: store FOD path to INI on OK
3714 HINI hini, // in: INI file to load/store last path from (can be HINI_USER)
3715 const char *pcszApplication, // in: INI application to load/store last path from
3716 const char *pcszKey) // in: INI key to load/store last path from
3717{
3718 FILEDLG fd;
3719 FILESTATUS3 fs3;
3720
3721 memset(&fd, 0, sizeof(FILEDLG));
3722 fd.cbSize = sizeof(FILEDLG);
3723 fd.fl = FDS_CENTER;
3724
3725 if (flFlags & WINH_FOD_SAVEDLG)
3726 fd.fl |= FDS_SAVEAS_DIALOG;
3727 else
3728 fd.fl |= FDS_OPEN_DIALOG;
3729
3730 if ( (hini)
3731 && (flFlags & WINH_FOD_INILOADDIR)
3732 && (PrfQueryProfileString(hini,
3733 (PSZ)pcszApplication,
3734 (PSZ)pcszKey,
3735 "", // default string V0.9.9 (2001-02-10) [umoeller]
3736 fd.szFullFile,
3737 sizeof(fd.szFullFile)-10)
3738 > 2)
3739 // added these checks V0.9.12 (2001-05-21) [umoeller]
3740 && (!DosQueryPathInfo(fd.szFullFile,
3741 FIL_STANDARD,
3742 &fs3,
3743 sizeof(fs3)))
3744 && (fs3.attrFile & FILE_DIRECTORY)
3745 )
3746 {
3747 // found: append "\*"
3748 strcat(fd.szFullFile, "\\");
3749 strcat(fd.szFullFile, pszFile);
3750 }
3751 else
3752 // default: copy pszFile
3753 strcpy(fd.szFullFile, pszFile);
3754 // fixed V0.9.12 (2001-05-21) [umoeller]
3755
3756 if ( WinFileDlg(HWND_DESKTOP, // parent
3757 hwndOwner, // owner
3758 &fd)
3759 && (fd.lReturn == DID_OK)
3760 )
3761 {
3762 // save path back?
3763 if ( (hini)
3764 && (flFlags & WINH_FOD_INISAVEDIR)
3765 )
3766 {
3767 // get the directory that was used
3768 PSZ p = strrchr(fd.szFullFile, '\\');
3769 if (p)
3770 {
3771 // contains directory:
3772 // copy to OS2.INI
3773 PSZ pszDir = strhSubstr(fd.szFullFile, p);
3774 if (pszDir)
3775 {
3776 PrfWriteProfileString(hini,
3777 (PSZ)pcszApplication,
3778 (PSZ)pcszKey,
3779 pszDir);
3780 free(pszDir);
3781 }
3782 }
3783 }
3784
3785 strcpy(pszFile, fd.szFullFile);
3786
3787 return TRUE;
3788 }
3789
3790 return FALSE;
3791}
3792
3793/*
3794 *@@ winhQueryWaitPointer:
3795 * shortcut for getting the system "wait" pointer.
3796 *
3797 *@@added V1.0.1 (2002-11-30) [umoeller]
3798 */
3799
3800HPOINTER winhQueryWaitPointer(VOID)
3801{
3802 return WinQuerySysPointer(HWND_DESKTOP,
3803 SPTR_WAIT,
3804 FALSE); // no copy
3805}
3806
3807/*
3808 *@@ winhSetWaitPointer:
3809 * this sets the mouse pointer to "Wait".
3810 * Returns the previous pointer (HPOINTER),
3811 * which should be stored somewhere to be
3812 * restored later. Example:
3813 + HPOINTER hptrOld = winhSetWaitPointer();
3814 + ...
3815 + WinSetPointer(HWND_DESKTOP, hptrOld);
3816 */
3817
3818HPOINTER winhSetWaitPointer(VOID)
3819{
3820 HPOINTER hptr = WinQueryPointer(HWND_DESKTOP);
3821 WinSetPointer(HWND_DESKTOP,
3822 winhQueryWaitPointer());
3823 return hptr;
3824}
3825
3826/*
3827 *@@ winhQueryWindowText:
3828 * this returns the window text of the specified
3829 * HWND in a newly allocated buffer.
3830 *
3831 * Returns NULL on error. Use free()
3832 * to free the return value.
3833 */
3834
3835PSZ winhQueryWindowText(HWND hwnd)
3836{
3837 PSZ pszText = NULL;
3838 ULONG cbText;
3839 if (cbText = WinQueryWindowTextLength(hwnd))
3840 {
3841 if (pszText = (PSZ)malloc(cbText + 1)) // additional null character
3842 WinQueryWindowText(hwnd,
3843 cbText + 1,
3844 pszText);
3845 }
3846
3847 return pszText;
3848}
3849
3850/*
3851 *@@ winhSetWindowText:
3852 * like WinSetWindowText, but this one accepts
3853 * printf-like arguments.
3854 *
3855 * Note that the total string is limited to
3856 * 1000 characters.
3857 *
3858 *@@added V0.9.16 (2001-10-08) [umoeller]
3859 */
3860
3861BOOL winhSetWindowText(HWND hwnd,
3862 const char *pcszFormat,
3863 ...)
3864{
3865 CHAR szBuf[1000];
3866 va_list args;
3867 int i;
3868 va_start(args, pcszFormat);
3869 i = vsprintf(szBuf, pcszFormat, args);
3870 va_end(args);
3871
3872 return WinSetWindowText(hwnd,
3873 szBuf);
3874}
3875
3876/*
3877 *@@ winhReplaceWindowText:
3878 * this is a combination of winhQueryWindowText
3879 * and strhFindReplace to replace substrings in a window.
3880 *
3881 * This is useful for filling in placeholders
3882 * a la "%1" in control windows, e.g. static
3883 * texts.
3884 *
3885 * This replaces only the first occurence of
3886 * pszSearch.
3887 *
3888 * Returns TRUE only if the window exists and
3889 * the search string was replaced.
3890 *
3891 *@@added V0.9.0 [umoeller]
3892 */
3893
3894BOOL winhReplaceWindowText(HWND hwnd, // in: window whose text is to be modified
3895 const char *pcszSearch, // in: search string (e.g. "%1")
3896 const char *pcszReplaceWith) // in: replacement string for pszSearch
3897{
3898 BOOL brc = FALSE;
3899 PSZ pszText;
3900
3901 if (pszText = winhQueryWindowText(hwnd))
3902 {
3903 ULONG ulOfs = 0;
3904 if (strhFindReplace(&pszText, &ulOfs, pcszSearch, pcszReplaceWith) > 0)
3905 {
3906 WinSetWindowText(hwnd, pszText);
3907 brc = TRUE;
3908 }
3909
3910 free(pszText);
3911 }
3912
3913 return brc;
3914}
3915
3916/*
3917 *@@ winhEnableDlgItems:
3918 * this enables/disables a whole range of controls
3919 * in a window by enumerating the child windows
3920 * until usIDFirst is found. If so, that subwindow
3921 * is enabled/disabled and all the following windows
3922 * in the enumeration also, until usIDLast is found.
3923 *
3924 * Note that this affects _all_ controls following
3925 * the usIDFirst window, no matter what ID they have
3926 * (even if "-1"), until usIDLast is found.
3927 *
3928 * Returns the no. of controls which were enabled/disabled
3929 * (null if none).
3930 *
3931 *@@added V0.9.0 [umoeller]
3932 *@@changed V0.9.1 (99-12-20) [umoeller]: renamed from winhEnableDlgItems
3933 */
3934
3935ULONG winhEnableControls(HWND hwndDlg, // in: dialog window
3936 USHORT usIDFirst, // in: first affected control ID
3937 USHORT usIDLast, // in: last affected control ID (inclusive)
3938 BOOL fEnable) // in: enable or disable?
3939{
3940 HENUM henum1 = NULLHANDLE;
3941 HWND hwndThis = NULLHANDLE;
3942 ULONG ulCount = 0;
3943
3944 henum1 = WinBeginEnumWindows(hwndDlg);
3945 while ((hwndThis = WinGetNextWindow(henum1)) != NULLHANDLE)
3946 {
3947 USHORT usIDCheckFirst = WinQueryWindowUShort(hwndThis, QWS_ID),
3948 usIDCheckLast;
3949 if (usIDCheckFirst == usIDFirst)
3950 {
3951 WinEnableWindow(hwndThis, fEnable);
3952 ulCount++;
3953
3954 while ((hwndThis = WinGetNextWindow(henum1)) != NULLHANDLE)
3955 {
3956 WinEnableWindow(hwndThis, fEnable);
3957 ulCount++;
3958 usIDCheckLast = WinQueryWindowUShort(hwndThis, QWS_ID);
3959 if (usIDCheckLast == usIDLast)
3960 break;
3961 }
3962
3963 break; // outer loop
3964 }
3965 }
3966 WinEndEnumWindows(henum1);
3967
3968 return ulCount;
3969}
3970
3971/*
3972 *@@ winhEnableControls2:
3973 * like winhEnableControls, but instead this
3974 * takes an array of ULONGs as input, which
3975 * is assumed to contain the dialog IDs of
3976 * the controls to be enabled/disabled.
3977 *
3978 *@@added V0.9.19 (2002-05-28) [umoeller]
3979 */
3980
3981ULONG winhEnableControls2(HWND hwndDlg, // in: dialog window
3982 const ULONG *paulIDs, // in: array of dialog IDs
3983 ULONG cIDs, // in: array item count (NOT array size)
3984 BOOL fEnable) // in: enable or disable?
3985{
3986 ULONG ul,
3987 ulrc = 0;
3988 for (ul = 0;
3989 ul < cIDs;
3990 ++ul)
3991 {
3992 if (WinEnableControl(hwndDlg, paulIDs[ul], fEnable))
3993 ++ulrc;
3994 }
3995
3996 return ulrc;
3997}
3998
3999/*
4000 *@@ winhCreateStdWindow:
4001 * much like WinCreateStdWindow, but this one
4002 * allows you to have the standard window
4003 * positioned automatically, using a given
4004 * SWP structure (*pswpFrame).
4005 *
4006 * The frame is created with the specified parent
4007 * (usually HWND_DESKTOP), but no owner.
4008 *
4009 * The client window is created with the frame as
4010 * its parent and owner and gets an ID of FID_CLIENT.
4011 *
4012 * Alternatively, you can set pswpFrame to NULL
4013 * and specify FCF_SHELLPOSITION with flFrameCreateFlags.
4014 * If you want the window to be shown, specify
4015 * SWP_SHOW (and maybe SWP_ACTIVATE) in *pswpFrame.
4016 *
4017 *@@added V0.9.0 [umoeller]
4018 *@@changed V0.9.5 (2000-08-13) [umoeller]: flStyleClient never worked, fixed
4019 *@@changed V0.9.7 (2000-12-08) [umoeller]: fixed client calc for invisible window
4020 *@@changed V1.0.1 (2002-11-30) [umoeller]: added support for NULL pcszClassClient
4021 */
4022
4023HWND winhCreateStdWindow(HWND hwndFrameParent, // in: normally HWND_DESKTOP
4024 PSWP pswpFrame, // in: frame wnd pos (ptr can be NULL)
4025 ULONG flFrameCreateFlags, // in: FCF_* flags
4026 ULONG ulFrameStyle, // in: WS_* flags (e.g. WS_VISIBLE, WS_ANIMATE)
4027 const char *pcszFrameTitle, // in: frame title (title bar)
4028 ULONG ulResourcesID, // in: according to FCF_* flags
4029 const char *pcszClassClient, // in: client class name (can be NULL for no client)
4030 ULONG flStyleClient, // in: client style
4031 ULONG ulID, // in: frame window ID
4032 PVOID pClientCtlData, // in: pCtlData structure pointer for client
4033 PHWND phwndClient) // out: created client wnd (required)
4034{
4035 FRAMECDATA fcdata;
4036 HWND hwndFrame;
4037 RECTL rclClient;
4038
4039 fcdata.cb = sizeof(FRAMECDATA);
4040 fcdata.flCreateFlags = flFrameCreateFlags;
4041 fcdata.hmodResources = (HMODULE)NULL;
4042 fcdata.idResources = ulResourcesID;
4043
4044 // create the frame and client windows
4045 if (hwndFrame = WinCreateWindow(hwndFrameParent,
4046 WC_FRAME,
4047 (PSZ)pcszFrameTitle,
4048 ulFrameStyle,
4049 0,0,0,0,
4050 NULLHANDLE,
4051 HWND_TOP,
4052 ulID,
4053 &fcdata,
4054 NULL))
4055 {
4056 if ( (!pcszClassClient) // V1.0.1 (2002-11-30) [umoeller]
4057 || (*phwndClient = WinCreateWindow(hwndFrame, // parent
4058 (PSZ)pcszClassClient, // class
4059 NULL, // no title
4060 flStyleClient, // style
4061 0,0,0,0, // size and position = 0
4062 hwndFrame, // owner
4063 HWND_BOTTOM, // bottom z-order
4064 FID_CLIENT, // frame window ID
4065 pClientCtlData, // class data
4066 NULL)) // no presparams
4067 )
4068 {
4069 if (pswpFrame)
4070 {
4071 // position frame
4072 WinSetWindowPos(hwndFrame,
4073 pswpFrame->hwndInsertBehind,
4074 pswpFrame->x,
4075 pswpFrame->y,
4076 pswpFrame->cx,
4077 pswpFrame->cy,
4078 pswpFrame->fl);
4079
4080 if (!pcszClassClient)
4081 *phwndClient = NULLHANDLE;
4082 else
4083 {
4084 // position client
4085 // WinQueryWindowRect(hwndFrame, &rclClient);
4086 // doesn't work because it might be invisible V0.9.7 (2000-12-08) [umoeller]
4087 rclClient.xLeft = 0;
4088 rclClient.yBottom = 0;
4089 rclClient.xRight = pswpFrame->cx;
4090 rclClient.yTop = pswpFrame->cy;
4091 WinCalcFrameRect(hwndFrame,
4092 &rclClient,
4093 TRUE); // calc client from frame
4094 WinSetWindowPos(*phwndClient,
4095 HWND_TOP,
4096 rclClient.xLeft,
4097 rclClient.yBottom,
4098 rclClient.xRight - rclClient.xLeft,
4099 rclClient.yTop - rclClient.yBottom,
4100 SWP_MOVE | SWP_SIZE | SWP_SHOW);
4101 }
4102 }
4103 }
4104 }
4105
4106 return hwndFrame;
4107}
4108
4109/*
4110 *@@ winhCreateObjectWindow:
4111 * creates an object window of the specified
4112 * window class, which you should have registered
4113 * before calling this. pvCreateParam will be
4114 * given to the window on WM_CREATE.
4115 *
4116 * Returns the HWND of the object window or
4117 * NULLHANDLE on errors.
4118 *
4119 *@@added V0.9.3 (2000-04-17) [umoeller]
4120 *@@changed V0.9.7 (2001-01-17) [umoeller]: made this a function from a macro
4121 */
4122
4123HWND winhCreateObjectWindow(const char *pcszWindowClass, // in: PM window class name
4124 PVOID pvCreateParam) // in: create param
4125{
4126 return WinCreateWindow(HWND_OBJECT,
4127 (PSZ)pcszWindowClass,
4128 (PSZ)"",
4129 0,
4130 0,0,0,0,
4131 0,
4132 HWND_BOTTOM,
4133 0,
4134 pvCreateParam,
4135 NULL);
4136}
4137
4138/*
4139 *@@ winhCreateControl:
4140 * creates a control with a size and position of 0.
4141 *
4142 *@@added V0.9.9 (2001-03-13) [umoeller]
4143 *@@changed V1.0.0 (2002-08-26) [umoeller]: added separate hwndOwner
4144 */
4145
4146HWND winhCreateControl(HWND hwndParent, // in: parent window
4147 HWND hwndOwner, // in: owner window
4148 const char *pcszClass, // in: window class (e.g. WC_BUTTON)
4149 const char *pcszText, // in: window title
4150 ULONG ulStyle, // in: control style
4151 ULONG ulID) // in: control ID
4152{
4153 return WinCreateWindow(hwndParent,
4154 (PSZ)pcszClass,
4155 (PSZ)pcszText,
4156 ulStyle,
4157 0, 0, 0, 0,
4158 hwndOwner,
4159 HWND_TOP,
4160 ulID,
4161 NULL,
4162 NULL);
4163}
4164
4165/*
4166 *@@ winhRepaintWindows:
4167 * this repaints all children of hwndParent.
4168 * If this is passed as HWND_DESKTOP, the
4169 * whole screen is repainted.
4170 *
4171 *@@changed V0.9.7 (2000-12-13) [umoeller]: hwndParent was never respected, fixed
4172 */
4173
4174VOID winhRepaintWindows(HWND hwndParent)
4175{
4176 HWND hwndTop;
4177 HENUM henum = WinBeginEnumWindows(hwndParent);
4178 while ((hwndTop = WinGetNextWindow(henum)))
4179 if (WinIsWindowShowing(hwndTop))
4180 WinInvalidateRect(hwndTop, NULL, TRUE);
4181 WinEndEnumWindows(henum);
4182}
4183
4184/*
4185 *@@ winhFindMsgQueue:
4186 * returns the message queue which matches
4187 * the given process and thread IDs. Since,
4188 * per IBM definition, every thread may only
4189 * have one MQ, this should be unique.
4190 *
4191 *@@added V0.9.2 (2000-03-08) [umoeller]
4192 */
4193
4194HMQ winhFindMsgQueue(PID pid, // in: process ID
4195 TID tid, // in: thread ID
4196 HAB* phab) // out: anchor block
4197{
4198 HWND hwndThis = 0,
4199 rc = 0;
4200 HENUM henum = WinBeginEnumWindows(HWND_OBJECT);
4201 while ((hwndThis = WinGetNextWindow(henum)))
4202 {
4203 CHAR szClass[200];
4204 if (WinQueryClassName(hwndThis, sizeof(szClass), szClass))
4205 {
4206 if (!strcmp(szClass, "#32767"))
4207 {
4208 // message queue window:
4209 PID pidWin = 0;
4210 TID tidWin = 0;
4211 WinQueryWindowProcess(hwndThis,
4212 &pidWin,
4213 &tidWin);
4214 if ( (pidWin == pid)
4215 && (tidWin == tid)
4216 )
4217 {
4218 // get HMQ from window words
4219 if (rc = WinQueryWindowULong(hwndThis, QWL_HMQ))
4220 if (phab)
4221 *phab = WinQueryAnchorBlock(hwndThis);
4222
4223 break;
4224 }
4225 }
4226 }
4227 }
4228 WinEndEnumWindows(henum);
4229
4230 return rc;
4231}
4232
4233/*
4234 *@@ winhFindHardErrorWindow:
4235 * this searches all children of HWND_OBJECT
4236 * for the PM hard error windows, which are
4237 * invisible most of the time. When a hard
4238 * error occurs, that window is made a child
4239 * of HWND_DESKTOP instead.
4240 *
4241 * Stolen from ProgramCommander/2 (C) Roman Stangl.
4242 *
4243 *@@added V0.9.3 (2000-04-27) [umoeller]
4244 */
4245
4246VOID winhFindPMErrorWindows(HWND *phwndHardError, // out: hard error window
4247 HWND *phwndSysError) // out: system error window
4248{
4249 PID pidObject; // HWND_OBJECT's process and thread id
4250 TID tidObject;
4251 PID pidObjectChild; // HWND_OBJECT's child window process and thread id
4252 TID tidObjectChild;
4253 HENUM henumObject; // HWND_OBJECT enumeration handle
4254 HWND hwndObjectChild; // Window handle of current HWND_OBJECT child
4255 UCHAR ucClassName[32]; // Window class e.g. #1 for WC_FRAME
4256 CLASSINFO classinfoWindow; // Class info of current HWND_OBJECT child
4257
4258 *phwndHardError = NULLHANDLE;
4259 *phwndSysError = NULLHANDLE;
4260
4261 // query HWND_OBJECT's window process
4262 WinQueryWindowProcess(WinQueryObjectWindow(HWND_DESKTOP), &pidObject, &tidObject);
4263 // enumerate all child windows of HWND_OBJECT
4264 henumObject = WinBeginEnumWindows(HWND_OBJECT);
4265 while ((hwndObjectChild = WinGetNextWindow(henumObject)))
4266 {
4267 // see if the current HWND_OBJECT child window runs in the
4268 // process of HWND_OBJECT (PM)
4269 WinQueryWindowProcess(hwndObjectChild, &pidObjectChild, &tidObjectChild);
4270 if (pidObject == pidObjectChild)
4271 {
4272 // get the child window's data
4273 WinQueryClassName(hwndObjectChild,
4274 sizeof(ucClassName),
4275 (PCH)ucClassName);
4276 WinQueryClassInfo(WinQueryAnchorBlock(hwndObjectChild),
4277 (PSZ)ucClassName,
4278 &classinfoWindow);
4279 if ( (!strcmp((PSZ)ucClassName, "#1")
4280 || (classinfoWindow.flClassStyle & CS_FRAME))
4281 )
4282 {
4283 // if the child window is a frame window and running in
4284 // HWND_OBJECT's (PM's) window process, it must be the
4285 // PM Hard Error or System Error window
4286 WinQueryClassName(WinWindowFromID(hwndObjectChild,
4287 FID_CLIENT),
4288 sizeof(ucClassName),
4289 (PSZ)ucClassName);
4290 if (!strcmp((PSZ)ucClassName, "PM Hard Error"))
4291 {
4292 *phwndHardError = hwndObjectChild;
4293 if (*phwndSysError)
4294 // we found the other one already:
4295 // stop searching, we got both
4296 break;
4297 }
4298 else
4299 {
4300 printf("Utility: Found System Error %08X\n", (int)hwndObjectChild);
4301 *phwndSysError = hwndObjectChild;
4302 if (*phwndHardError)
4303 // we found the other one already:
4304 // stop searching, we got both
4305 break;
4306 }
4307 }
4308 } // end if (pidObject == pidObjectChild)
4309 } // end while ((hwndObjectChild = WinGetNextWindow(henumObject)) != NULLHANDLE)
4310 WinEndEnumWindows(henumObject);
4311}
4312
4313/*
4314 *@@ winhCreateFakeDesktop:
4315 * this routine creates and displays a frameless window over
4316 * the whole screen in the color of PM's Desktop to fool the
4317 * user that all windows have been closed (which in fact might
4318 * not be the case).
4319 *
4320 * This window's background color is set to the Desktop's
4321 * (PM's one, not the WPS's one).
4322 *
4323 * Returns the HWND of this window.
4324 */
4325
4326HWND winhCreateFakeDesktop(HWND hwndSibling)
4327{
4328 // presparam for background
4329 typedef struct _BACKGROUND
4330 {
4331 ULONG cb; // length of the aparam parameter, in bytes
4332 ULONG id; // attribute type identity
4333 ULONG cb2; // byte count of the ab parameter
4334 RGB rgb; // attribute value
4335 } BACKGROUND;
4336
4337 BACKGROUND background;
4338 LONG lDesktopColor;
4339
4340 // create fake desktop window = empty window with
4341 // the size of full screen
4342 lDesktopColor = WinQuerySysColor(HWND_DESKTOP,
4343 SYSCLR_BACKGROUND,
4344 0);
4345 background.cb = sizeof(background.id)
4346 + sizeof(background.cb)
4347 + sizeof(background.rgb);
4348 background.id = PP_BACKGROUNDCOLOR;
4349 background.cb2 = sizeof(RGB);
4350 background.rgb.bBlue = (CHAR1FROMMP(lDesktopColor));
4351 background.rgb.bGreen= (CHAR2FROMMP(lDesktopColor));
4352 background.rgb.bRed = (CHAR3FROMMP(lDesktopColor));
4353
4354 return WinCreateWindow(HWND_DESKTOP, // parent window
4355 WC_FRAME, // class name
4356 "", // window text
4357 WS_VISIBLE, // window style
4358 0, 0, // position and size
4359 G_cxScreen,
4360 G_cyScreen,
4361 NULLHANDLE, // owner window
4362 hwndSibling, // sibling window
4363 1, // window id
4364 NULL, // control data
4365 &background); // presentation parms
4366}
4367
4368/*
4369 *@@ winhAssertWarp4Notebook:
4370 * this takes hwndDlg as a notebook dialog page and
4371 * goes thru all its controls. If a control with an
4372 * ID <= udIdThreshold is found, this is assumed to
4373 * be a button which is to be given the BS_NOTEBOOKBUTTON
4374 * style. You should therefore give all your button
4375 * controls which should be moved such an ID.
4376 *
4377 * Note that this function will now automatically
4378 * find out the lowest y coordinate that was used
4379 * for a non-notebook button and move all controls
4380 * down accordingly. As a result, ulDownUnit must
4381 * no longer be specified (V0.9.19).
4382 *
4383 * This function is useful if you wish to create
4384 * notebook pages using dlgedit.exe which are compatible
4385 * with both Warp 3 and Warp 4. This should be executed
4386 * in WM_INITDLG of the notebook dlg function if the app
4387 * has determined that it is running on Warp 4.
4388 *
4389 *@@changed V0.9.16 (2002-02-02) [umoeller]: fixed entry fields
4390 *@@changed V0.9.19 (2002-04-24) [umoeller]: removed ulDownUnits
4391 *@@changed V0.9.19 (2002-05-02) [umoeller]: fix for combobox
4392 */
4393
4394BOOL winhAssertWarp4Notebook(HWND hwndDlg,
4395 USHORT usIdThreshold) // in: ID threshold
4396{
4397 BOOL brc = FALSE;
4398
4399 if (doshIsWarp4())
4400 {
4401 LONG yLowest = 10000;
4402 HWND hwndItem;
4403 HENUM henum = 0;
4404 PSWP paswp,
4405 pswpThis;
4406 ULONG cWindows = 0,
4407 ul;
4408
4409 BOOL fIsVisible;
4410
4411 if (fIsVisible = WinIsWindowVisible(hwndDlg))
4412 // avoid flicker
4413 WinEnableWindowUpdate(hwndDlg, FALSE);
4414
4415 if (paswp = (PSWP)malloc(sizeof(SWP) * 100))
4416 {
4417 pswpThis = paswp;
4418
4419 // loop 1: set notebook buttons, find lowest y used
4420 henum = WinBeginEnumWindows(hwndDlg);
4421 while ((hwndItem = WinGetNextWindow(henum)))
4422 {
4423 USHORT usId = WinQueryWindowUShort(hwndItem, QWS_ID);
4424 // _Pmpf(("hwndItem: 0x%lX, ID: 0x%lX", hwndItem, usId));
4425 if (usId <= usIdThreshold)
4426 {
4427 // pushbutton to change:
4428 WinSetWindowBits(hwndItem,
4429 QWL_STYLE,
4430 BS_NOTEBOOKBUTTON, BS_NOTEBOOKBUTTON);
4431 brc = TRUE;
4432 }
4433 else
4434 {
4435 // no pushbutton to change:
4436 CHAR szClass[10];
4437
4438 WinQueryWindowPos(hwndItem, pswpThis);
4439
4440 // special handling for entry fields
4441 // V0.9.16 (2002-02-02) [umoeller]
4442 WinQueryClassName(hwndItem, sizeof(szClass), szClass);
4443 if (!strcmp(szClass, "#6"))
4444 {
4445 pswpThis->x += 3 * WinQuerySysValue(HWND_DESKTOP, SV_CXBORDER);
4446 pswpThis->y += 3 * WinQuerySysValue(HWND_DESKTOP, SV_CYBORDER);
4447 }
4448
4449 // check lowest y
4450 if ( (pswpThis->y < yLowest)
4451 // ignore combobox, this will distort everything
4452 // AGAIN ... sigh V0.9.19 (2002-05-02) [umoeller]
4453 && (strcmp(szClass, "#2"))
4454 )
4455 yLowest = pswpThis->y ;
4456
4457 ++pswpThis;
4458 if (++cWindows == 100)
4459 break;
4460 }
4461 } // end while ((hwndItem = WinGetNextWindow(henum)))
4462 WinEndEnumWindows(henum);
4463
4464 // now adjust window positions
4465 pswpThis = paswp;
4466 for (ul = 0;
4467 ul < cWindows;
4468 ++ul, ++pswpThis)
4469 {
4470 pswpThis->y -= (yLowest - 8);
4471 // 8 is magic to match the lower border of the
4472 // standard WPS notebook pages V0.9.19 (2002-04-24) [umoeller]
4473 pswpThis->fl = SWP_MOVE;
4474 }
4475
4476 WinSetMultWindowPos(WinQueryAnchorBlock(hwndDlg),
4477 paswp,
4478 cWindows);
4479
4480 free(paswp);
4481 }
4482
4483 if (fIsVisible)
4484 WinShowWindow(hwndDlg, TRUE);
4485 }
4486
4487 return brc;
4488}
4489
4490/*
4491 *@@ winhDrawFormattedText:
4492 * this func takes a rectangle and draws pszText into
4493 * it, breaking the words as neccessary. The line spacing
4494 * is determined from the font currently selected in hps.
4495 *
4496 * As opposed to WinDrawText, this can draw several lines
4497 * at once, and format the _complete_ text according to the
4498 * flCmd parameter, which is like with WinDrawText.
4499 *
4500 * After this function returns, *prcl is modified like this:
4501 *
4502 * -- yTop and yBottom contain the upper and lower boundaries
4503 * which were needed to draw the text. This depends on
4504 * whether DT_TOP etc. were specified.
4505 * To get the height of the rectangle used, calculate the
4506 * delta between yTop and yBottom.
4507 *
4508 * -- xLeft and xRight are modified to contain the outmost
4509 * left and right coordinates which were needed to draw
4510 * the text. This will be set to the longest line which
4511 * was encountered.
4512 *
4513 * The following DT_* flags are supported:
4514 *
4515 * -- DT_LEFT, DT_CENTER, DT_RIGHT all work.
4516 *
4517 * -- Vertically however only DT_TOP is supported.
4518 *
4519 * -- You can specify DT_QUERYEXTENT to only have
4520 * these text boundaries calculated without actually
4521 * drawing.
4522 *
4523 * Note that DT_TEXTATTRS will always be added, so you
4524 * will want to call GpiSetColor before this.
4525 *
4526 * This returns the number of lines drawn.
4527 *
4528 *@@changed V0.9.0 [umoeller]: prcl.xLeft and xRight are now updated too upon return
4529 */
4530
4531ULONG winhDrawFormattedText(HPS hps, // in: presentation space; its settings
4532 // are used, but not altered
4533 PRECTL prcl, // in/out: rectangle to use for drawing
4534 // (modified)
4535 const char *pcszText, // in: text to draw (zero-terminated)
4536 ULONG flCmd) // in: DT_* flags like in WinDrawText; see remarks
4537{
4538 PSZ p = (PSZ)pcszText;
4539 LONG lDrawn = 1,
4540 lTotalDrawn = 0,
4541 lLineCount = 0,
4542 lOrigYTop = prcl->yTop;
4543 ULONG ulTextLen = strlen(pcszText),
4544 ulCharHeight,
4545 flCmd2,
4546 xLeftmost = prcl->xRight,
4547 xRightmost = prcl->xLeft;
4548 RECTL rcl2;
4549
4550 flCmd2 = flCmd | DT_WORDBREAK | DT_TEXTATTRS;
4551
4552 ulCharHeight = gpihQueryLineSpacing(hps);
4553
4554 while ( (lDrawn)
4555 && (lTotalDrawn < ulTextLen)
4556 )
4557 {
4558 memcpy(&rcl2, prcl, sizeof(rcl2));
4559 lDrawn = WinDrawText(hps,
4560 ulTextLen - lTotalDrawn,
4561 p,
4562 &rcl2,
4563 0,
4564 0, // colors
4565 flCmd2);
4566
4567 // update char counters
4568 p += lDrawn;
4569 lTotalDrawn += lDrawn;
4570
4571 // update x extents
4572 if (rcl2.xLeft < xLeftmost)
4573 xLeftmost = rcl2.xLeft;
4574 if (rcl2.xRight > xRightmost)
4575 xRightmost = rcl2.xRight;
4576
4577 // update y for next line
4578 prcl->yTop -= ulCharHeight;
4579
4580 // increase line count
4581 lLineCount++;
4582 }
4583
4584 prcl->xLeft = xLeftmost;
4585 prcl->xRight = xRightmost;
4586 prcl->yBottom = prcl->yTop;
4587 prcl->yTop = lOrigYTop;
4588
4589 return lLineCount;
4590}
4591
4592/*
4593 *@@ winhQuerySwitchList:
4594 * returns the switch list in a newly
4595 * allocated buffer. This does the
4596 * regular double WinQuerySwitchList
4597 * call to first get the no. of items
4598 * and then get the items.
4599 *
4600 * The no. of items can be found in
4601 * the returned SWBLOCK.cwsentry.
4602 *
4603 * Returns NULL on errors. Use
4604 * free() to free the return value.
4605 *
4606 *@@added V0.9.7 (2000-12-06) [umoeller]
4607 */
4608
4609PSWBLOCK winhQuerySwitchList(HAB hab)
4610{
4611 ULONG cItems = WinQuerySwitchList(hab, NULL, 0);
4612 ULONG ulBufSize = (cItems * sizeof(SWENTRY)) + sizeof(HSWITCH);
4613 PSWBLOCK pSwBlock;
4614 if (pSwBlock = (PSWBLOCK)malloc(ulBufSize))
4615 {
4616 if (!(cItems = WinQuerySwitchList(hab, pSwBlock, ulBufSize)))
4617 {
4618 free(pSwBlock);
4619 pSwBlock = NULL;
4620 }
4621 }
4622
4623 return pSwBlock;
4624}
4625
4626typedef HSWITCH APIENTRY WINHSWITCHFROMHAPP(HAPP happ);
4627
4628/*
4629 *@@ winhHSWITCHfromHAPP:
4630 * resolves and calls the undocumented
4631 * WinHSWITCHfromHAPP API.
4632 *
4633 *@@added V0.9.19 (2002-04-17) [umoeller]
4634 */
4635
4636HSWITCH winhHSWITCHfromHAPP(HAPP happ)
4637{
4638 static WINHSWITCHFROMHAPP *pWinHSWITCHfromHAPP = NULL;
4639
4640 if (!pWinHSWITCHfromHAPP)
4641 // first call: import WinHSWITCHfromHAPP
4642 // WinHSWITCHfromHAPP PMMERGE.5199
4643 doshQueryProcAddr("PMMERGE",
4644 5199,
4645 (PFN*)&pWinHSWITCHfromHAPP);
4646
4647 if (pWinHSWITCHfromHAPP)
4648 return pWinHSWITCHfromHAPP(happ);
4649
4650 return NULLHANDLE;
4651}
4652
4653/*
4654 *@@ winhQueryTasklistWindow:
4655 * returns the window handle of the PM task list.
4656 *
4657 *@@added V0.9.7 (2000-12-07) [umoeller]
4658 */
4659
4660HWND winhQueryTasklistWindow(VOID)
4661{
4662 SWBLOCK swblock;
4663
4664 // the tasklist has entry #0 in the SWBLOCK
4665 WinQuerySwitchList(NULLHANDLE, &swblock, sizeof(SWBLOCK));
4666
4667 return swblock.aswentry[0].swctl.hwnd;
4668}
4669
4670/*
4671 *@@ winhKillTasklist:
4672 * this will destroy the Tasklist (window list) window.
4673 * Note: you will only be able to get it back after a
4674 * reboot, not a WPS restart. Only for use at shutdown and such.
4675 * This trick by Uri J. Stern at
4676 * http://zebra.asta.fh-weingarten.de/os2/Snippets/Howt8881.HTML
4677 */
4678
4679VOID winhKillTasklist(VOID)
4680{
4681 HWND hwndTasklist = winhQueryTasklistWindow();
4682 WinPostMsg(hwndTasklist,
4683 0x0454, // undocumented msg for killing tasklist
4684 NULL, NULL);
4685}
4686
4687// the following must be added for EMX (99-10-22) [umoeller]
4688#ifndef NERR_BufTooSmall
4689 #define NERR_BASE 2100
4690 #define NERR_BufTooSmall (NERR_BASE+23)
4691 // the API return buffer is too small
4692#endif
4693
4694/*
4695 *@@ winhQueryPendingSpoolJobs:
4696 * returns the number of pending print jobs in the spooler
4697 * or 0 if none. Useful for testing before shutdown.
4698 */
4699
4700ULONG winhQueryPendingSpoolJobs(VOID)
4701{
4702 // BOOL rcPending = FALSE;
4703 ULONG ulTotalJobCount = 0;
4704
4705 SPLERR splerr;
4706 USHORT jobCount;
4707 ULONG cbBuf;
4708 ULONG cTotal;
4709 ULONG cReturned;
4710 ULONG cbNeeded;
4711 ULONG ulLevel;
4712 ULONG i,j;
4713 PSZ pszComputerName;
4714 PBYTE pBuf = NULL;
4715 PPRQINFO3 prq;
4716 PPRJINFO2 prj2;
4717
4718 ulLevel = 4L;
4719 pszComputerName = (PSZ)NULL;
4720 splerr = SplEnumQueue(pszComputerName, ulLevel, pBuf, 0L, // cbBuf
4721 &cReturned, &cTotal,
4722 &cbNeeded, NULL);
4723 if ( (splerr == ERROR_MORE_DATA)
4724 || (splerr == NERR_BufTooSmall)
4725 )
4726 {
4727 if (!DosAllocMem((PPVOID)&pBuf,
4728 cbNeeded,
4729 PAG_READ | PAG_WRITE | PAG_COMMIT))
4730 {
4731 cbBuf = cbNeeded;
4732 splerr = SplEnumQueue(pszComputerName, ulLevel, pBuf, cbBuf,
4733 &cReturned, &cTotal,
4734 &cbNeeded, NULL);
4735 if (splerr == NO_ERROR)
4736 {
4737 // set pointer to point to the beginning of the buffer
4738 prq = (PPRQINFO3)pBuf;
4739
4740 // cReturned has the count of the number of PRQINFO3 structures
4741 for (i = 0;
4742 i < cReturned;
4743 i++)
4744 {
4745 // save the count of jobs; there are this many PRJINFO2
4746 // structures following the PRQINFO3 structure
4747 jobCount = prq->cJobs;
4748 // _Pmpf(( "Job count in this queue is %d",jobCount ));
4749
4750 // increment the pointer past the PRQINFO3 structure
4751 prq++;
4752
4753 // set a pointer to point to the first PRJINFO2 structure
4754 prj2=(PPRJINFO2)prq;
4755 for (j = 0;
4756 j < jobCount;
4757 j++)
4758 {
4759 // increment the pointer to point to the next structure
4760 prj2++;
4761 // increase the job count, which we'll return
4762 ulTotalJobCount++;
4763
4764 } // endfor jobCount
4765
4766 // after doing all the job structures, prj2 points to the next
4767 // queue structure; set the pointer for a PRQINFO3 structure
4768 prq = (PPRQINFO3)prj2;
4769 } //endfor cReturned
4770 } // endif NO_ERROR
4771 DosFreeMem(pBuf);
4772 }
4773 } // end if Q level given
4774
4775 return ulTotalJobCount;
4776}
4777
4778/*
4779 *@@ winhSetNumLock:
4780 * this sets the NumLock key on or off, depending
4781 * on fState.
4782 *
4783 * Based on code from WarpEnhancer, (C) Achim Hasenmller.
4784 *
4785 *@@added V0.9.1 (99-12-18) [umoeller]
4786 */
4787
4788VOID winhSetNumLock(BOOL fState)
4789{
4790 // BOOL fRestoreKBD = FALSE; // Assume we're not going to close Kbd
4791 BYTE KeyStateTable[256];
4792 ULONG ulActionTaken; // Used by DosOpen
4793 HFILE hKbd;
4794
4795 // read keyboard state table
4796 if (WinSetKeyboardStateTable(HWND_DESKTOP, &KeyStateTable[0],
4797 FALSE))
4798 {
4799 // first set the PM state
4800 if (fState)
4801 KeyStateTable[VK_NUMLOCK] |= 0x01; // Turn numlock on
4802 else
4803 KeyStateTable[VK_NUMLOCK] &= 0xFE; // Turn numlock off
4804
4805 // set keyboard state table with new state values
4806 WinSetKeyboardStateTable(HWND_DESKTOP, &KeyStateTable[0], TRUE);
4807 }
4808
4809 // now set the OS/2 keyboard state
4810
4811 // try to open OS/2 keyboard driver
4812 if (!DosOpen("KBD$",
4813 &hKbd, &ulActionTaken,
4814 0, // cbFile
4815 FILE_NORMAL,
4816 OPEN_ACTION_FAIL_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS,
4817 OPEN_SHARE_DENYNONE | OPEN_ACCESS_READWRITE,
4818 NULL))
4819 {
4820 SHIFTSTATE ShiftState;
4821 ULONG DataLen = sizeof(SHIFTSTATE);
4822
4823 memset(&ShiftState, '\0', DataLen);
4824 DosDevIOCtl(hKbd, IOCTL_KEYBOARD, KBD_GETSHIFTSTATE,
4825 NULL, 0L, NULL,
4826 &ShiftState, DataLen, &DataLen);
4827
4828 if (fState)
4829 ShiftState.fsState |= 0x0020; // turn NumLock on
4830 else
4831 ShiftState.fsState &= 0xFFDF; // turn NumLock off
4832
4833 DosDevIOCtl(hKbd, IOCTL_KEYBOARD, KBD_SETSHIFTSTATE,
4834 &ShiftState, DataLen, &DataLen,
4835 NULL, 0L, NULL);
4836
4837 // now close OS/2 keyboard driver
4838 DosClose(hKbd);
4839 }
4840}
4841
4842/*
4843 *@@ winhSetClipboardText:
4844 * sets the clipboard data to the given text,
4845 * replacing the current clipboard contents.
4846 *
4847 *@@added V1.0.0 (2002-08-28) [umoeller]
4848 */
4849
4850BOOL winhSetClipboardText(HAB hab,
4851 PCSZ pcsz,
4852 ULONG cbSize) // in: size of buffer INCLUDING null byte
4853{
4854 BOOL fSuccess = FALSE;
4855
4856 if (WinOpenClipbrd(hab))
4857 {
4858 PSZ pszDest;
4859 if (!DosAllocSharedMem((PVOID*)&pszDest,
4860 NULL,
4861 cbSize,
4862 PAG_WRITE | PAG_COMMIT | OBJ_GIVEABLE))
4863 {
4864 memcpy(pszDest,
4865 pcsz,
4866 cbSize);
4867
4868 WinEmptyClipbrd(hab);
4869
4870 fSuccess = WinSetClipbrdData(hab, // anchor-block handle
4871 (ULONG)pszDest, // pointer to text data
4872 CF_TEXT, // data is in text format
4873 CFI_POINTER); // passing a pointer
4874
4875 // PMREF says (implicitly) it is not necessary to call
4876 // DosFreeMem. I hope that is correct.
4877 // V0.9.19 (2002-06-02) [umoeller]
4878 }
4879
4880 WinCloseClipbrd(hab);
4881 }
4882
4883 return fSuccess;
4884}
4885
4886/*
4887 *@@category: Helpers\PM helpers\Workplace Shell\WPS class list
4888 */
4889
4890/* ******************************************************************
4891 *
4892 * WPS Class List helpers
4893 *
4894 ********************************************************************/
4895
4896/*
4897 *@@ winhQueryWPSClassList:
4898 * this returns the WPS class list in a newly
4899 * allocated buffer. This is just a shortcut to
4900 * the usual double WinEnumObjectClasses call.
4901 *
4902 * The return value is actually of the POBJCLASS type,
4903 * so you better cast this manually. We declare this
4904 * this as PBYTE though because POBJCLASS requires
4905 * INCL_WINWORKPLACE.
4906 * See WinEnumObjectClasses() for details.
4907 *
4908 * Returns NULL on error. Use free()
4909 * to free the return value.
4910 *
4911 *@@added V0.9.0 [umoeller]
4912 */
4913
4914PBYTE winhQueryWPSClassList(VOID)
4915{
4916 ULONG ulSize;
4917 POBJCLASS pObjClass = 0;
4918
4919 // get WPS class list size
4920 if (WinEnumObjectClasses(NULL, &ulSize))
4921 {
4922 // allocate buffer
4923 pObjClass = (POBJCLASS)malloc(ulSize + 1);
4924 // and load the classes into it
4925 WinEnumObjectClasses(pObjClass, &ulSize);
4926 }
4927
4928 return (PBYTE)pObjClass;
4929}
4930
4931/*
4932 *@@ winhQueryWPSClass:
4933 * this returns the POBJCLASS item if pszClass is registered
4934 * with the WPS or NULL if the class could not be found.
4935 *
4936 * The return value is actually of the POBJCLASS type,
4937 * so you better cast this manually. We declare this
4938 * this as PBYTE though because POBJCLASS requires
4939 * INCL_WINWORKPLACE.
4940 *
4941 * This takes as input the return value of winhQueryWPSClassList,
4942 * which you must call first.
4943 *
4944 * <B>Usage:</B>
4945 + PBYTE pClassList = winhQueryWPSClassList(),
4946 + pWPFolder;
4947 + if (pClassList)
4948 + {
4949 + if (pWPFolder = winhQueryWPSClass(pClassList, "WPFolder"))
4950 + ...
4951 + free(pClassList);
4952 + }
4953 *
4954 *@@added V0.9.0 [umoeller]
4955 */
4956
4957PBYTE winhQueryWPSClass(PBYTE pObjClass, // in: buffer returned by
4958 // winhQueryWPSClassList
4959 const char *pszClass) // in: class name to query
4960{
4961 PBYTE pbReturn = 0;
4962
4963 POBJCLASS pocThis = (POBJCLASS)pObjClass;
4964 // now go thru the WPS class list
4965 while (pocThis)
4966 {
4967 if (!strcmp(pocThis->pszClassName, pszClass))
4968 {
4969 pbReturn = (PBYTE)pocThis;
4970 break;
4971 }
4972
4973 // next class
4974 pocThis = pocThis->pNext;
4975 } // end while (pocThis)
4976
4977 return pbReturn;
4978}
4979
4980/*
4981 *@@ winhRegisterClass:
4982 * this works just like WinRegisterObjectClass,
4983 * except that it returns a more meaningful
4984 * error code than just FALSE in case registering
4985 * fails.
4986 *
4987 * This returns NO_ERROR if the class was successfully
4988 * registered (WinRegisterObjectClass returned TRUE).
4989 *
4990 * Otherwise, we do a DosLoadModule if maybe the DLL
4991 * couldn't be loaded in the first place. If DosLoadModule
4992 * did not return NO_ERROR, this function returns that
4993 * return code, which can be:
4994 *
4995 * -- 2 ERROR_FILE_NOT_FOUND: pcszModule does not exist
4996 * -- 2 ERROR_FILE_NOT_FOUND
4997 * -- 3 ERROR_PATH_NOT_FOUND
4998 * -- 4 ERROR_TOO_MANY_OPEN_FILES
4999 * -- 5 ERROR_ACCESS_DENIED
5000 * -- 8 ERROR_NOT_ENOUGH_MEMORY
5001 * -- 11 ERROR_BAD_FORMAT
5002 * -- 26 ERROR_NOT_DOS_DISK (unknown media type)
5003 * -- 32 ERROR_SHARING_VIOLATION
5004 * -- 33 ERROR_LOCK_VIOLATION
5005 * -- 36 ERROR_SHARING_BUFFER_EXCEEDED
5006 * -- 95 ERROR_INTERRUPT (interrupted system call)
5007 * -- 108 ERROR_DRIVE_LOCKED (by another process)
5008 * -- 123 ERROR_INVALID_NAME (illegal character or FS name not valid)
5009 * -- 127 ERROR_PROC_NOT_FOUND (DosQueryProcAddr error)
5010 * -- 180 ERROR_INVALID_SEGMENT_NUMBER
5011 * -- 182 ERROR_INVALID_ORDINAL
5012 * -- 190 ERROR_INVALID_MODULETYPE (probably an application)
5013 * -- 191 ERROR_INVALID_EXE_SIGNATURE (probably not LX DLL)
5014 * -- 192 ERROR_EXE_MARKED_INVALID (by linker)
5015 * -- 194 ERROR_ITERATED_DATA_EXCEEDS_64K (in a DLL segment)
5016 * -- 195 ERROR_INVALID_MINALLOCSIZE
5017 * -- 196 ERROR_DYNLINK_FROM_INVALID_RING
5018 * -- 198 ERROR_INVALID_SEGDPL
5019 * -- 199 ERROR_AUTODATASEG_EXCEEDS_64K
5020 * -- 201 ERROR_RELOCSRC_CHAIN_EXCEEDS_SEGLIMIT
5021 * -- 206 ERROR_FILENAME_EXCED_RANGE (not matching 8+3 spec)
5022 * -- 295 ERROR_INIT_ROUTINE_FAILED (DLL init routine failed)
5023 *
5024 * In all these cases, pszBuf may contain a meaningful
5025 * error message from DosLoadModule, especially if an import
5026 * could not be resolved.
5027 *
5028 * Still worse, if DosLoadModule returned NO_ERROR, we
5029 * probably have some SOM internal error. A probable
5030 * reason is that the parent class of pcszClassName
5031 * is not installed, but that's WPS/SOM internal
5032 * and cannot be queried from outside the WPS context.
5033 *
5034 * In that case, ERROR_OPEN_FAILED (110) is returned.
5035 * That one sounded good to me. ;-)
5036 */
5037
5038APIRET winhRegisterClass(const char* pcszClassName, // in: e.g. "XFolder"
5039 const char* pcszModule, // in: e.g. "C:\XFOLDER\XFLDR.DLL"
5040 PSZ pszBuf, // out: error message from DosLoadModule
5041 ULONG cbBuf) // in: sizeof(*pszBuf), passed to DosLoadModule
5042{
5043 APIRET arc = NO_ERROR;
5044
5045 if (!WinRegisterObjectClass((PSZ)pcszClassName, (PSZ)pcszModule))
5046 {
5047 // failed: do more error checking then, try DosLoadModule
5048 HMODULE hmod = NULLHANDLE;
5049 if (!(arc = DosLoadModule(pszBuf, cbBuf,
5050 (PSZ)pcszModule,
5051 &hmod)))
5052 {
5053 // DosLoadModule succeeded:
5054 // some SOM error then
5055 DosFreeModule(hmod);
5056 arc = ERROR_OPEN_FAILED;
5057 }
5058 }
5059 // else: ulrc still 0 (== no error)
5060
5061 return arc;
5062}
5063
5064/*
5065 *@@ winhIsClassRegistered:
5066 * quick one-shot function which checks if
5067 * a class is currently registered. Calls
5068 * winhQueryWPSClassList and winhQueryWPSClass
5069 * in turn.
5070 *
5071 *@@added V0.9.2 (2000-02-26) [umoeller]
5072 */
5073
5074BOOL winhIsClassRegistered(const char *pcszClass)
5075{
5076 BOOL brc = FALSE;
5077 PBYTE pClassList;
5078
5079 if (pClassList = winhQueryWPSClassList())
5080 {
5081 if (winhQueryWPSClass(pClassList, pcszClass))
5082 brc = TRUE;
5083
5084 free(pClassList);
5085 }
5086
5087 return brc;
5088}
5089
5090/*
5091 *@@category: Helpers\PM helpers\Workplace Shell
5092 */
5093
5094/*
5095 *@@ winhResetWPS:
5096 * restarts the WPS using PrfReset. Returns
5097 * one of the following:
5098 *
5099 * -- 0: no error.
5100 * -- 1: PrfReset failed.
5101 * -- 2 or 4: PrfQueryProfile failed.
5102 * -- 3: malloc() failed.
5103 *
5104 *@@added V0.9.4 (2000-07-01) [umoeller]
5105 */
5106
5107ULONG winhResetWPS(HAB hab)
5108{
5109 ULONG ulrc = 0;
5110 // find out current profile names
5111 PRFPROFILE Profiles;
5112 Profiles.cchUserName = Profiles.cchSysName = 0;
5113 // first query their file name lengths
5114 if (PrfQueryProfile(hab, &Profiles))
5115 {
5116 // allocate memory for filenames
5117 Profiles.pszUserName = (PSZ)malloc(Profiles.cchUserName);
5118 Profiles.pszSysName = (PSZ)malloc(Profiles.cchSysName);
5119
5120 if (Profiles.pszSysName)
5121 {
5122 // get filenames
5123 if (PrfQueryProfile(hab, &Profiles))
5124 {
5125
5126 // "change" INIs to these filenames:
5127 // THIS WILL RESET THE WPS
5128 if (PrfReset(hab, &Profiles) == FALSE)
5129 ulrc = 1;
5130 free(Profiles.pszSysName);
5131 free(Profiles.pszUserName);
5132 }
5133 else
5134 ulrc = 2;
5135 }
5136 else
5137 ulrc = 3;
5138 }
5139 else
5140 ulrc = 4;
5141
5142 return ulrc;
5143}
5144
Note: See TracBrowser for help on using the repository browser.