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

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

Many misc updates.

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