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

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

Misc changes

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