source: branches/branch-1-0/src/helpers/winh.c@ 290

Last change on this file since 290 was 290, checked in by pr, 20 years ago

Bart's fix for bug 655

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