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

Last change on this file since 85 was 85, checked in by umoeller, 24 years ago

Misc changes.

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