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

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

Major work on textview control and dialogs.

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