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

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

Mistc. updates for WarpIN, new features.

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