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

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

Misc. changes for V0.9.7.

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