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

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