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

Last change on this file since 196 was 196, checked in by umoeller, 23 years ago

Misc fixes.

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