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

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

Release 0.9.8 plus some patches.

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