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

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

Misc speed improvements.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 169.0 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
3953BOOL winhAssertWarp4Notebook(HWND hwndDlg,
3954 USHORT usIdThreshold, // in: ID threshold
3955 ULONG ulDownUnits) // in: dialog units or 0
3956{
3957 BOOL brc = FALSE;
3958
3959 if (doshIsWarp4())
3960 {
3961 POINTL ptl;
3962 HWND hwndItem;
3963 HENUM henum = 0;
3964
3965 BOOL fIsVisible = WinIsWindowVisible(hwndDlg);
3966 if (ulDownUnits)
3967 {
3968 ptl.x = 0;
3969 ptl.y = ulDownUnits;
3970 WinMapDlgPoints(hwndDlg, &ptl, 1, TRUE);
3971 }
3972
3973 if (fIsVisible)
3974 WinEnableWindowUpdate(hwndDlg, FALSE);
3975
3976 henum = WinBeginEnumWindows(hwndDlg);
3977 while ((hwndItem = WinGetNextWindow(henum)))
3978 {
3979 USHORT usId = WinQueryWindowUShort(hwndItem, QWS_ID);
3980 // _Pmpf(("hwndItem: 0x%lX, ID: 0x%lX", hwndItem, usId));
3981 if (usId <= usIdThreshold)
3982 {
3983 // pushbutton to change:
3984 // _Pmpf((" Setting bit"));
3985 WinSetWindowBits(hwndItem,
3986 QWL_STYLE,
3987 BS_NOTEBOOKBUTTON, BS_NOTEBOOKBUTTON);
3988 brc = TRUE;
3989 }
3990 else
3991 // no pushbutton to change: move downwards
3992 // if desired
3993 if (ulDownUnits)
3994 {
3995 SWP swp;
3996 // _Pmpf(("Moving downwards %d pixels", ptl.y));
3997 WinQueryWindowPos(hwndItem, &swp);
3998 WinSetWindowPos(hwndItem, 0,
3999 swp.x,
4000 swp.y - ptl.y,
4001 0, 0,
4002 SWP_MOVE);
4003 }
4004 }
4005 WinEndEnumWindows(henum);
4006
4007 if (fIsVisible)
4008 WinShowWindow(hwndDlg, TRUE);
4009 }
4010
4011 return (brc);
4012}
4013
4014/*
4015 *@@ winhDrawFormattedText:
4016 * this func takes a rectangle and draws pszText into
4017 * it, breaking the words as neccessary. The line spacing
4018 * is determined from the font currently selected in hps.
4019 *
4020 * As opposed to WinDrawText, this can draw several lines
4021 * at once, and format the _complete_ text according to the
4022 * flCmd parameter, which is like with WinDrawText.
4023 *
4024 * After this function returns, *prcl is modified like this:
4025 *
4026 * -- yTop and yBottom contain the upper and lower boundaries
4027 * which were needed to draw the text. This depends on
4028 * whether DT_TOP etc. were specified.
4029 * To get the height of the rectangle used, calculate the
4030 * delta between yTop and yBottom.
4031 *
4032 * -- xLeft and xRight are modified to contain the outmost
4033 * left and right coordinates which were needed to draw
4034 * the text. This will be set to the longest line which
4035 * was encountered.
4036 *
4037 * You can specify DT_QUERYEXTENT with flDraw to only have
4038 * these text boundaries calculated without actually drawing.
4039 *
4040 * This returns the number of lines drawn.
4041 *
4042 * Note that this calls WinDrawText with DT_TEXTATTRS set,
4043 * that is, the current text primitive attributes will be
4044 * used (fonts and colors).
4045 *
4046 *@@changed V0.9.0 [umoeller]: prcl.xLeft and xRight are now updated too upon return
4047 */
4048
4049ULONG winhDrawFormattedText(HPS hps, // in: presentation space; its settings
4050 // are used, but not altered
4051 PRECTL prcl, // in/out: rectangle to use for drawing
4052 // (modified)
4053 const char *pcszText, // in: text to draw (zero-terminated)
4054 ULONG flCmd) // in: flags like in WinDrawText; I have
4055 // only tested DT_TOP and DT_LEFT though.
4056 // DT_WORDBREAK | DT_TEXTATTRS are always
4057 // set.
4058 // You can specify DT_QUERYEXTENT to only
4059 // have prcl calculated without drawing.
4060{
4061 PSZ p = (PSZ)pcszText;
4062 LONG lDrawn = 1,
4063 lTotalDrawn = 0,
4064 lLineCount = 0,
4065 lOrigYTop = prcl->yTop;
4066 ULONG ulTextLen = strlen(pcszText),
4067 ulCharHeight,
4068 flCmd2,
4069 xLeftmost = prcl->xRight,
4070 xRightmost = prcl->xLeft;
4071 RECTL rcl2;
4072
4073 flCmd2 = flCmd | DT_WORDBREAK | DT_TEXTATTRS;
4074
4075 ulCharHeight = gpihQueryLineSpacing(hps);
4076
4077 while ( (lDrawn)
4078 && (lTotalDrawn < ulTextLen)
4079 )
4080 {
4081 memcpy(&rcl2, prcl, sizeof(rcl2));
4082 lDrawn = WinDrawText(hps,
4083 ulTextLen-lTotalDrawn,
4084 p,
4085 &rcl2,
4086 0, 0, // colors
4087 flCmd2);
4088
4089 // update char counters
4090 p += lDrawn;
4091 lTotalDrawn += lDrawn;
4092
4093 // update x extents
4094 if (rcl2.xLeft < xLeftmost)
4095 xLeftmost = rcl2.xLeft;
4096 if (rcl2.xRight > xRightmost)
4097 xRightmost = rcl2.xRight;
4098
4099 // update y for next line
4100 prcl->yTop -= ulCharHeight;
4101
4102 // increase line count
4103 lLineCount++;
4104 }
4105 prcl->xLeft = xLeftmost;
4106 prcl->xRight = xRightmost;
4107 prcl->yBottom = prcl->yTop;
4108 prcl->yTop = lOrigYTop;
4109
4110 return (lLineCount);
4111}
4112
4113/*
4114 *@@ winhQuerySwitchList:
4115 * returns the switch list in a newly
4116 * allocated buffer. This does the
4117 * regular double WinQuerySwitchList
4118 * call to first get the no. of items
4119 * and then get the items.
4120 *
4121 * The no. of items can be found in
4122 * the returned SWBLOCK.cwsentry.
4123 *
4124 * Returns NULL on errors. Use
4125 * free() to free the return value.
4126 *
4127 *@@added V0.9.7 (2000-12-06) [umoeller]
4128 */
4129
4130PSWBLOCK winhQuerySwitchList(HAB hab)
4131{
4132 ULONG cItems = WinQuerySwitchList(hab, NULL, 0);
4133 ULONG ulBufSize = (cItems * sizeof(SWENTRY)) + sizeof(HSWITCH);
4134 PSWBLOCK pSwBlock = (PSWBLOCK)malloc(ulBufSize);
4135 if (pSwBlock)
4136 {
4137 cItems = WinQuerySwitchList(hab, pSwBlock, ulBufSize);
4138 if (!cItems)
4139 {
4140 free(pSwBlock);
4141 pSwBlock = NULL;
4142 }
4143 }
4144
4145 return (pSwBlock);
4146}
4147
4148/*
4149 *@@ winhQueryTasklistWindow:
4150 * returns the window handle of the PM task list.
4151 *
4152 *@@added V0.9.7 (2000-12-07) [umoeller]
4153 */
4154
4155HWND winhQueryTasklistWindow(VOID)
4156{
4157 SWBLOCK swblock;
4158 // HWND hwndTasklist = winhQueryTasklistWindow();
4159 // the tasklist has entry #0 in the SWBLOCK
4160 WinQuerySwitchList(NULLHANDLE, &swblock, sizeof(SWBLOCK));
4161 return (swblock.aswentry[0].swctl.hwnd);
4162}
4163
4164/*
4165 *@@ winhKillTasklist:
4166 * this will destroy the Tasklist (window list) window.
4167 * Note: you will only be able to get it back after a
4168 * reboot, not a WPS restart. Only for use at shutdown and such.
4169 * This trick by Uri J. Stern at
4170 * http://zebra.asta.fh-weingarten.de/os2/Snippets/Howt8881.HTML
4171 */
4172
4173VOID winhKillTasklist(VOID)
4174{
4175 HWND hwndTasklist = winhQueryTasklistWindow();
4176 WinPostMsg(hwndTasklist,
4177 0x0454, // undocumented msg for killing tasklist
4178 NULL, NULL);
4179}
4180
4181// the following must be added for EMX (99-10-22) [umoeller]
4182#ifndef NERR_BufTooSmall
4183 #define NERR_BASE 2100
4184 #define NERR_BufTooSmall (NERR_BASE+23)
4185 // the API return buffer is too small
4186#endif
4187
4188/*
4189 *@@ winhQueryPendingSpoolJobs:
4190 * returns the number of pending print jobs in the spooler
4191 * or 0 if none. Useful for testing before shutdown.
4192 */
4193
4194ULONG winhQueryPendingSpoolJobs(VOID)
4195{
4196 // BOOL rcPending = FALSE;
4197 ULONG ulTotalJobCount = 0;
4198
4199 SPLERR splerr;
4200 USHORT jobCount;
4201 ULONG cbBuf;
4202 ULONG cTotal;
4203 ULONG cReturned;
4204 ULONG cbNeeded;
4205 ULONG ulLevel;
4206 ULONG i,j;
4207 PSZ pszComputerName;
4208 PBYTE pBuf = NULL;
4209 PPRQINFO3 prq;
4210 PPRJINFO2 prj2;
4211
4212 ulLevel = 4L;
4213 pszComputerName = (PSZ)NULL;
4214 splerr = SplEnumQueue(pszComputerName, ulLevel, pBuf, 0L, // cbBuf
4215 &cReturned, &cTotal,
4216 &cbNeeded, NULL);
4217 if ( (splerr == ERROR_MORE_DATA)
4218 || (splerr == NERR_BufTooSmall)
4219 )
4220 {
4221 if (!DosAllocMem((PPVOID)&pBuf,
4222 cbNeeded,
4223 PAG_READ | PAG_WRITE | PAG_COMMIT))
4224 {
4225 cbBuf = cbNeeded;
4226 splerr = SplEnumQueue(pszComputerName, ulLevel, pBuf, cbBuf,
4227 &cReturned, &cTotal,
4228 &cbNeeded, NULL);
4229 if (splerr == NO_ERROR)
4230 {
4231 // set pointer to point to the beginning of the buffer
4232 prq = (PPRQINFO3)pBuf;
4233
4234 // cReturned has the count of the number of PRQINFO3 structures
4235 for (i = 0;
4236 i < cReturned;
4237 i++)
4238 {
4239 // save the count of jobs; there are this many PRJINFO2
4240 // structures following the PRQINFO3 structure
4241 jobCount = prq->cJobs;
4242 // _Pmpf(( "Job count in this queue is %d",jobCount ));
4243
4244 // increment the pointer past the PRQINFO3 structure
4245 prq++;
4246
4247 // set a pointer to point to the first PRJINFO2 structure
4248 prj2=(PPRJINFO2)prq;
4249 for (j = 0;
4250 j < jobCount;
4251 j++)
4252 {
4253 // increment the pointer to point to the next structure
4254 prj2++;
4255 // increase the job count, which we'll return
4256 ulTotalJobCount++;
4257
4258 } // endfor jobCount
4259
4260 // after doing all the job structures, prj2 points to the next
4261 // queue structure; set the pointer for a PRQINFO3 structure
4262 prq = (PPRQINFO3)prj2;
4263 } //endfor cReturned
4264 } // endif NO_ERROR
4265 DosFreeMem(pBuf);
4266 }
4267 } // end if Q level given
4268
4269 return (ulTotalJobCount);
4270}
4271
4272/*
4273 *@@ winhSetNumLock:
4274 * this sets the NumLock key on or off, depending
4275 * on fState.
4276 *
4277 * Based on code from WarpEnhancer, (C) Achim Hasenmller.
4278 *
4279 *@@added V0.9.1 (99-12-18) [umoeller]
4280 */
4281
4282VOID winhSetNumLock(BOOL fState)
4283{
4284 // BOOL fRestoreKBD = FALSE; // Assume we're not going to close Kbd
4285 BYTE KeyStateTable[256];
4286 ULONG ulActionTaken; // Used by DosOpen
4287 HFILE hKbd;
4288
4289 // read keyboard state table
4290 if (WinSetKeyboardStateTable(HWND_DESKTOP, &KeyStateTable[0],
4291 FALSE))
4292 {
4293 // first set the PM state
4294 if (fState)
4295 KeyStateTable[VK_NUMLOCK] |= 0x01; // Turn numlock on
4296 else
4297 KeyStateTable[VK_NUMLOCK] &= 0xFE; // Turn numlock off
4298
4299 // set keyboard state table with new state values
4300 WinSetKeyboardStateTable(HWND_DESKTOP, &KeyStateTable[0], TRUE);
4301 }
4302
4303 // now set the OS/2 keyboard state
4304
4305 // try to open OS/2 keyboard driver
4306 if (!DosOpen("KBD$",
4307 &hKbd, &ulActionTaken,
4308 0, // cbFile
4309 FILE_NORMAL,
4310 OPEN_ACTION_FAIL_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS,
4311 OPEN_SHARE_DENYNONE | OPEN_ACCESS_READWRITE,
4312 NULL))
4313 {
4314 SHIFTSTATE ShiftState;
4315 ULONG DataLen = sizeof(SHIFTSTATE);
4316
4317 memset(&ShiftState, '\0', DataLen);
4318 DosDevIOCtl(hKbd, IOCTL_KEYBOARD, KBD_GETSHIFTSTATE,
4319 NULL, 0L, NULL,
4320 &ShiftState, DataLen, &DataLen);
4321
4322 if (fState)
4323 ShiftState.fsState |= 0x0020; // turn NumLock on
4324 else
4325 ShiftState.fsState &= 0xFFDF; // turn NumLock off
4326
4327 DosDevIOCtl(hKbd, IOCTL_KEYBOARD, KBD_SETSHIFTSTATE,
4328 &ShiftState, DataLen, &DataLen,
4329 NULL, 0L, NULL);
4330 // now close OS/2 keyboard driver
4331 DosClose(hKbd);
4332 }
4333 return;
4334}
4335
4336/*
4337 *@@category: Helpers\PM helpers\Extended frame windows
4338 */
4339
4340/* ******************************************************************
4341 *
4342 * Extended frame
4343 *
4344 ********************************************************************/
4345
4346/*
4347 *@@ fnwpSubclExtFrame:
4348 * subclassed frame window proc.
4349 *
4350 *@@added V0.9.16 (2001-09-29) [umoeller]
4351 */
4352
4353MRESULT EXPENTRY fnwpSubclExtFrame(HWND hwndFrame, ULONG msg, MPARAM mp1, MPARAM mp2)
4354{
4355 MRESULT mrc = 0;
4356
4357 PEXTFRAMEDATA pData = (PEXTFRAMEDATA)WinQueryWindowPtr(hwndFrame, QWL_USER);
4358
4359 switch (msg)
4360 {
4361 case WM_QUERYFRAMECTLCOUNT:
4362 {
4363 // query the standard frame controls count
4364 ULONG ulrc = (ULONG)pData->pfnwpOrig(hwndFrame, msg, mp1, mp2);
4365
4366 // if we have a status bar, increment the count
4367 ulrc++;
4368
4369 mrc = (MPARAM)ulrc;
4370 }
4371 break;
4372
4373 case WM_FORMATFRAME:
4374 {
4375 // query the number of standard frame controls
4376 ULONG ulCount = (ULONG)pData->pfnwpOrig(hwndFrame, msg, mp1, mp2);
4377
4378 // we have a status bar:
4379 // format the frame
4380 ULONG ul;
4381 PSWP swpArr = (PSWP)mp1;
4382
4383 for (ul = 0; ul < ulCount; ul++)
4384 {
4385 if (WinQueryWindowUShort( swpArr[ul].hwnd, QWS_ID ) == 0x8008 )
4386 // FID_CLIENT
4387 {
4388 POINTL ptlBorderSizes;
4389 ULONG ulStatusBarHeight = 20;
4390 WinSendMsg(hwndFrame,
4391 WM_QUERYBORDERSIZE,
4392 (MPARAM)&ptlBorderSizes,
4393 0);
4394
4395 // first initialize the _new_ SWP for the status bar.
4396 // Since the SWP array for the std frame controls is
4397 // zero-based, and the standard frame controls occupy
4398 // indices 0 thru ulCount-1 (where ulCount is the total
4399 // count), we use ulCount for our static text control.
4400 swpArr[ulCount].fl = SWP_MOVE | SWP_SIZE | SWP_NOADJUST | SWP_ZORDER;
4401 swpArr[ulCount].x = ptlBorderSizes.x;
4402 swpArr[ulCount].y = ptlBorderSizes.y;
4403 swpArr[ulCount].cx = swpArr[ul].cx; // same as cnr's width
4404 swpArr[ulCount].cy = ulStatusBarHeight;
4405 swpArr[ulCount].hwndInsertBehind = HWND_BOTTOM; // HWND_TOP;
4406 swpArr[ulCount].hwnd = WinWindowFromID(hwndFrame, FID_STATUSBAR);
4407
4408 // adjust the origin and height of the container to
4409 // accomodate our static text control
4410 swpArr[ul].y += swpArr[ulCount].cy;
4411 swpArr[ul].cy -= swpArr[ulCount].cy;
4412 }
4413 }
4414
4415 // increment the number of frame controls
4416 // to include our status bar
4417 mrc = (MRESULT)(ulCount + 1);
4418 }
4419 break;
4420
4421 case WM_CALCFRAMERECT:
4422 {
4423 mrc = pData->pfnwpOrig(hwndFrame, msg, mp1, mp2);
4424
4425 // we have a status bar: calculate its rectangle
4426 // CalcFrameRect(mp1, mp2);
4427 }
4428 break;
4429
4430 case WM_DESTROY:
4431 WinSubclassWindow(hwndFrame, pData->pfnwpOrig);
4432 free(pData);
4433 WinSetWindowPtr(hwndFrame, QWL_USER, NULL);
4434 break;
4435
4436 default:
4437 mrc = pData->pfnwpOrig(hwndFrame, msg, mp1, mp2);
4438 }
4439
4440 return (mrc);
4441}
4442
4443/*
4444 *@@ winhCreateStatusBar:
4445 * creates a status bar for a frame window.
4446 *
4447 * Normally there's no need to call this manually,
4448 * this gets called by winhCreateExtStdWindow
4449 * automatically.
4450 *
4451 *@@added V0.9.16 (2001-09-29) [umoeller]
4452 */
4453
4454HWND winhCreateStatusBar(HWND hwndFrame,
4455 HWND hwndOwner,
4456 const char *pcszText, // in: initial status bar text
4457 const char *pcszFont, // in: font to use for status bar
4458 LONG lColor) // in: foreground color for status bar
4459{
4460 // create status bar
4461 HWND hwndReturn = NULLHANDLE;
4462 PPRESPARAMS ppp = NULL;
4463 winhStorePresParam(&ppp, PP_FONTNAMESIZE, strlen(pcszFont)+1, (PVOID)pcszFont);
4464 lColor = WinQuerySysColor(HWND_DESKTOP, SYSCLR_DIALOGBACKGROUND, 0);
4465 winhStorePresParam(&ppp, PP_BACKGROUNDCOLOR, sizeof(lColor), &lColor);
4466 lColor = CLR_BLACK;
4467 winhStorePresParam(&ppp, PP_FOREGROUNDCOLOR, sizeof(lColor), &lColor);
4468 hwndReturn = WinCreateWindow(hwndFrame,
4469 WC_STATIC,
4470 (PSZ)pcszText,
4471 SS_TEXT | DT_VCENTER | WS_VISIBLE,
4472 0, 0, 0, 0,
4473 hwndOwner,
4474 HWND_TOP,
4475 FID_STATUSBAR,
4476 NULL,
4477 ppp);
4478 free(ppp);
4479 return (hwndReturn);
4480}
4481
4482/*
4483 *@@ winhCreateExtStdWindow:
4484 * creates an extended frame window.
4485 *
4486 * pData must point to an EXTFRAMECDATA structure
4487 * which contains a copy of the parameters to be
4488 * passed to winhCreateStdWindow. In addition,
4489 * this contains the flExtFlags field, which allows
4490 * you to automatically create a status bar for
4491 * the window.
4492 *
4493 * Note that we subclass the frame here and require
4494 * QWL_USER for that. The frame's QWL_USER points
4495 * to an EXTFRAMEDATA structure whose pUser parameter
4496 * you may use for additional data, if you want to
4497 * do further subclassing.
4498 *
4499 *@@added V0.9.16 (2001-09-29) [umoeller]
4500 */
4501
4502HWND winhCreateExtStdWindow(PEXTFRAMECDATA pData, // in: extended frame data
4503 PHWND phwndClient) // out: created client wnd
4504{
4505 HWND hwndFrame;
4506
4507 if (hwndFrame = winhCreateStdWindow(HWND_DESKTOP,
4508 pData->pswpFrame,
4509 pData->flFrameCreateFlags,
4510 pData->ulFrameStyle,
4511 pData->pcszFrameTitle,
4512 pData->ulResourcesID,
4513 pData->pcszClassClient,
4514 pData->flStyleClient,
4515 pData->ulID,
4516 pData->pClientCtlData,
4517 phwndClient))
4518 {
4519 if (pData->flExtFlags & XFCF_STATUSBAR)
4520 {
4521 // create status bar as child of the frame
4522 HWND hwndStatusBar = winhCreateStatusBar(hwndFrame,
4523 hwndFrame,
4524 "",
4525 "9.WarpSans",
4526 CLR_BLACK);
4527
4528 // subclass frame for supporting status bar and msgs
4529 PEXTFRAMEDATA pFrameData;
4530 if (pFrameData = NEW(EXTFRAMEDATA))
4531 {
4532 ZERO(pFrameData),
4533 memcpy(&pFrameData->CData, pData, sizeof(pFrameData->CData));
4534 if (pFrameData->pfnwpOrig = WinSubclassWindow(hwndFrame,
4535 fnwpSubclExtFrame))
4536 {
4537 WinSetWindowPtr(hwndFrame, QWL_USER, pFrameData);
4538 }
4539 else
4540 free(pFrameData);
4541 }
4542 }
4543 }
4544
4545 return (hwndFrame);
4546}
4547
4548/*
4549 *@@category: Helpers\PM helpers\Workplace Shell\WPS class list
4550 */
4551
4552/* ******************************************************************
4553 *
4554 * WPS Class List helpers
4555 *
4556 ********************************************************************/
4557
4558/*
4559 *@@ winhQueryWPSClassList:
4560 * this returns the WPS class list in a newly
4561 * allocated buffer. This is just a shortcut to
4562 * the usual double WinEnumObjectClasses call.
4563 *
4564 * The return value is actually of the POBJCLASS type,
4565 * so you better cast this manually. We declare this
4566 * this as PBYTE though because POBJCLASS requires
4567 * INCL_WINWORKPLACE.
4568 * See WinEnumObjectClasses() for details.
4569 *
4570 * Returns NULL on error. Use free()
4571 * to free the return value.
4572 *
4573 *@@added V0.9.0 [umoeller]
4574 */
4575
4576PBYTE winhQueryWPSClassList(VOID)
4577{
4578 ULONG ulSize;
4579 POBJCLASS pObjClass = 0;
4580
4581 // get WPS class list size
4582 if (WinEnumObjectClasses(NULL, &ulSize))
4583 {
4584 // allocate buffer
4585 pObjClass = (POBJCLASS)malloc(ulSize+1);
4586 // and load the classes into it
4587 WinEnumObjectClasses(pObjClass, &ulSize);
4588 }
4589
4590 return ((PBYTE)pObjClass);
4591}
4592
4593/*
4594 *@@ winhQueryWPSClass:
4595 * this returns the POBJCLASS item if pszClass is registered
4596 * with the WPS or NULL if the class could not be found.
4597 *
4598 * The return value is actually of the POBJCLASS type,
4599 * so you better cast this manually. We declare this
4600 * this as PBYTE though because POBJCLASS requires
4601 * INCL_WINWORKPLACE.
4602 *
4603 * This takes as input the return value of winhQueryWPSClassList,
4604 * which you must call first.
4605 *
4606 * <B>Usage:</B>
4607 + PBYTE pClassList = winhQueryWPSClassList(),
4608 + pWPFolder;
4609 + if (pClassList)
4610 + {
4611 + if (pWPFolder = winhQueryWPSClass(pClassList, "WPFolder"))
4612 + ...
4613 + free(pClassList);
4614 + }
4615 *
4616 *@@added V0.9.0 [umoeller]
4617 */
4618
4619PBYTE winhQueryWPSClass(PBYTE pObjClass, // in: buffer returned by
4620 // winhQueryWPSClassList
4621 const char *pszClass) // in: class name to query
4622{
4623 PBYTE pbReturn = 0;
4624
4625 POBJCLASS pocThis = (POBJCLASS)pObjClass;
4626 // now go thru the WPS class list
4627 while (pocThis)
4628 {
4629 if (strcmp(pocThis->pszClassName, pszClass) == 0)
4630 {
4631 pbReturn = (PBYTE)pocThis;
4632 break;
4633 }
4634 // next class
4635 pocThis = pocThis->pNext;
4636 } // end while (pocThis)
4637
4638 return (pbReturn);
4639}
4640
4641/*
4642 *@@ winhRegisterClass:
4643 * this works just like WinRegisterObjectClass,
4644 * except that it returns a more meaningful
4645 * error code than just FALSE in case registering
4646 * fails.
4647 *
4648 * This returns NO_ERROR if the class was successfully
4649 * registered (WinRegisterObjectClass returned TRUE).
4650 *
4651 * Otherwise, we do a DosLoadModule if maybe the DLL
4652 * couldn't be loaded in the first place. If DosLoadModule
4653 * did not return NO_ERROR, this function returns that
4654 * return code, which can be:
4655 *
4656 * -- 2 ERROR_FILE_NOT_FOUND: pcszModule does not exist
4657 * -- 2 ERROR_FILE_NOT_FOUND
4658 * -- 3 ERROR_PATH_NOT_FOUND
4659 * -- 4 ERROR_TOO_MANY_OPEN_FILES
4660 * -- 5 ERROR_ACCESS_DENIED
4661 * -- 8 ERROR_NOT_ENOUGH_MEMORY
4662 * -- 11 ERROR_BAD_FORMAT
4663 * -- 26 ERROR_NOT_DOS_DISK (unknown media type)
4664 * -- 32 ERROR_SHARING_VIOLATION
4665 * -- 33 ERROR_LOCK_VIOLATION
4666 * -- 36 ERROR_SHARING_BUFFER_EXCEEDED
4667 * -- 95 ERROR_INTERRUPT (interrupted system call)
4668 * -- 108 ERROR_DRIVE_LOCKED (by another process)
4669 * -- 123 ERROR_INVALID_NAME (illegal character or FS name not valid)
4670 * -- 127 ERROR_PROC_NOT_FOUND (DosQueryProcAddr error)
4671 * -- 180 ERROR_INVALID_SEGMENT_NUMBER
4672 * -- 182 ERROR_INVALID_ORDINAL
4673 * -- 190 ERROR_INVALID_MODULETYPE (probably an application)
4674 * -- 191 ERROR_INVALID_EXE_SIGNATURE (probably not LX DLL)
4675 * -- 192 ERROR_EXE_MARKED_INVALID (by linker)
4676 * -- 194 ERROR_ITERATED_DATA_EXCEEDS_64K (in a DLL segment)
4677 * -- 195 ERROR_INVALID_MINALLOCSIZE
4678 * -- 196 ERROR_DYNLINK_FROM_INVALID_RING
4679 * -- 198 ERROR_INVALID_SEGDPL
4680 * -- 199 ERROR_AUTODATASEG_EXCEEDS_64K
4681 * -- 201 ERROR_RELOCSRC_CHAIN_EXCEEDS_SEGLIMIT
4682 * -- 206 ERROR_FILENAME_EXCED_RANGE (not matching 8+3 spec)
4683 * -- 295 ERROR_INIT_ROUTINE_FAILED (DLL init routine failed)
4684 *
4685 * In all these cases, pszBuf may contain a meaningful
4686 * error message from DosLoadModule, especially if an import
4687 * could not be resolved.
4688 *
4689 * Still worse, if DosLoadModule returned NO_ERROR, we
4690 * probably have some SOM internal error. A probable
4691 * reason is that the parent class of pcszClassName
4692 * is not installed, but that's WPS/SOM internal
4693 * and cannot be queried from outside the WPS context.
4694 *
4695 * In that case, ERROR_OPEN_FAILED (110) is returned.
4696 * That one sounded good to me. ;-)
4697 */
4698
4699APIRET winhRegisterClass(const char* pcszClassName, // in: e.g. "XFolder"
4700 const char* pcszModule, // in: e.g. "C:\XFOLDER\XFLDR.DLL"
4701 PSZ pszBuf, // out: error message from DosLoadModule
4702 ULONG cbBuf) // in: sizeof(*pszBuf), passed to DosLoadModule
4703{
4704 APIRET arc = NO_ERROR;
4705
4706 if (!WinRegisterObjectClass((PSZ)pcszClassName, (PSZ)pcszModule))
4707 {
4708 // failed: do more error checking then, try DosLoadModule
4709 HMODULE hmod = NULLHANDLE;
4710 arc = DosLoadModule(pszBuf, cbBuf,
4711 (PSZ)pcszModule,
4712 &hmod);
4713 if (arc == NO_ERROR)
4714 {
4715 // DosLoadModule succeeded:
4716 // some SOM error then
4717 DosFreeModule(hmod);
4718 arc = ERROR_OPEN_FAILED;
4719 }
4720 }
4721 // else: ulrc still 0 (== no error)
4722
4723 return (arc);
4724}
4725
4726/*
4727 *@@ winhIsClassRegistered:
4728 * quick one-shot function which checks if
4729 * a class is currently registered. Calls
4730 * winhQueryWPSClassList and winhQueryWPSClass
4731 * in turn.
4732 *
4733 *@@added V0.9.2 (2000-02-26) [umoeller]
4734 */
4735
4736BOOL winhIsClassRegistered(const char *pcszClass)
4737{
4738 BOOL brc = FALSE;
4739 PBYTE pClassList = winhQueryWPSClassList();
4740 if (pClassList)
4741 {
4742 if (winhQueryWPSClass(pClassList, pcszClass))
4743 brc = TRUE;
4744 free(pClassList);
4745 }
4746
4747 return (brc);
4748}
4749
4750/*
4751 *@@category: Helpers\PM helpers\Workplace Shell
4752 */
4753
4754/*
4755 *@@ winhResetWPS:
4756 * restarts the WPS using PrfReset. Returns
4757 * one of the following:
4758 *
4759 * -- 0: no error.
4760 * -- 1: PrfReset failed.
4761 * -- 2 or 4: PrfQueryProfile failed.
4762 * -- 3: malloc() failed.
4763 *
4764 *@@added V0.9.4 (2000-07-01) [umoeller]
4765 */
4766
4767ULONG winhResetWPS(HAB hab)
4768{
4769 ULONG ulrc = 0;
4770 // find out current profile names
4771 PRFPROFILE Profiles;
4772 Profiles.cchUserName = Profiles.cchSysName = 0;
4773 // first query their file name lengths
4774 if (PrfQueryProfile(hab, &Profiles))
4775 {
4776 // allocate memory for filenames
4777 Profiles.pszUserName = (PSZ)malloc(Profiles.cchUserName);
4778 Profiles.pszSysName = (PSZ)malloc(Profiles.cchSysName);
4779
4780 if (Profiles.pszSysName)
4781 {
4782 // get filenames
4783 if (PrfQueryProfile(hab, &Profiles))
4784 {
4785
4786 // "change" INIs to these filenames:
4787 // THIS WILL RESET THE WPS
4788 if (PrfReset(hab, &Profiles) == FALSE)
4789 ulrc = 1;
4790 free(Profiles.pszSysName);
4791 free(Profiles.pszUserName);
4792 }
4793 else
4794 ulrc = 2;
4795 }
4796 else
4797 ulrc = 3;
4798 }
4799 else
4800 ulrc = 4;
4801
4802 return (ulrc);
4803}
Note: See TracBrowser for help on using the repository browser.