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

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

Misc changes.

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