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

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

Minor changes.

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