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

Last change on this file since 374 was 374, checked in by pr, 17 years ago

Fix scroll bar bugs in textview controls. Bug 1086.

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