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

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

New build system, multimedia, other misc fixes.

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