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

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

misc changes

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