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

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

Misc. changes.

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