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

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

Lafaix and Ratcliffe updates.

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