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

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

Buncha fixes, and fixes for fixes.

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