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

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

Updated string helpers.

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