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

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

Tons of changes from the last weeks.

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