source: trunk/src/helpers/comctl.c@ 365

Last change on this file since 365 was 243, checked in by umoeller, 23 years ago

New build system, multimedia, other misc fixes.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 61.4 KB
Line 
1
2/*
3 *@@sourcefile comctl.c:
4 * contains various window procedures for implementing
5 * common controls. The source file name has nothing to do
6 * with the Windoze DLL of the same name.
7 *
8 * Usage: All PM programs.
9 *
10 * Function prefixes (new with V0.81):
11 * -- ctl* common control helper functions
12 *
13 * The functionality of this code is accessed with the use
14 * of a special helper function. Sometimes that function
15 * registers a new window class, sometimes a static control
16 * needs to be subclassed. See the respective functions for
17 * details.
18 *
19 * In detail, we have:
20 *
21 * -- a "menu button" control, which displays a menu when
22 * pressed (see ctlMakeMenuButton for details);
23 *
24 * -- progress bar support (see ctlProgressBarFromStatic for
25 * details);
26 *
27 * -- a "chart" control for displaying pie charts (all new
28 * with V0.9.0; see ctlChartFromStatic for details);
29 *
30 * -- split windows support (all new with V0.9.0; see
31 * ctlCreateSplitWindow for details);
32 *
33 * -- a subclassed static control for enhanced bitmap and
34 * and icon display (see ctl_fnwpBitmapStatic for details).
35 * This used to be in animate.c and has been enhanced
36 * with V0.9.0;
37 *
38 * -- a "tooltip" control, which shows fly-over ("bubble")
39 * help over any window, including controls. This is
40 * largely API-compatible with the Win95 tooltip control.
41 * See ctl_fnwpTooltip for details;
42 *
43 * -- a "checkbox container" control, which is a subclassed
44 * container which uses checkboxes as record icons.
45 * See ctlMakeCheckboxContainer for details.
46 *
47 * Note: Version numbering in this file relates to XWorkplace version
48 * numbering.
49 *
50 *@@header "helpers\comctl.h"
51 */
52
53/*
54 * Copyright (C) 1997-2002 Ulrich M”ller.
55 * This file is part of the "XWorkplace helpers" source package.
56 * This is free software; you can redistribute it and/or modify
57 * it under the terms of the GNU General Public License as published
58 * by the Free Software Foundation, in version 2 as it comes in the
59 * "COPYING" file of the XWorkplace main distribution.
60 * This program is distributed in the hope that it will be useful,
61 * but WITHOUT ANY WARRANTY; without even the implied warranty of
62 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
63 * GNU General Public License for more details.
64 */
65
66#define OS2EMX_PLAIN_CHAR
67 // this is needed for "os2emx.h"; if this is defined,
68 // emx will define PSZ as _signed_ char, otherwise
69 // as unsigned char
70
71#define INCL_DOSEXCEPTIONS
72#define INCL_DOSPROCESS
73#define INCL_DOSSEMAPHORES
74#define INCL_DOSERRORS
75
76#define INCL_WINWINDOWMGR
77#define INCL_WINFRAMEMGR
78#define INCL_WINMESSAGEMGR
79#define INCL_WININPUT
80#define INCL_WINPOINTERS
81#define INCL_WINTRACKRECT
82#define INCL_WINTIMER
83#define INCL_WINSYS
84
85#define INCL_WINRECTANGLES /// xxx temporary
86
87#define INCL_WINMENUS
88#define INCL_WINSTATICS
89#define INCL_WINBUTTONS
90#define INCL_WINSTDCNR
91#define INCL_WINENTRYFIELDS
92#define INCL_WINSHELLDATA
93
94#define INCL_GPIPRIMITIVES
95#define INCL_GPILOGCOLORTABLE
96#define INCL_GPILCIDS
97#define INCL_GPIPATHS
98#define INCL_GPIREGIONS
99#define INCL_GPIBITMAPS // added V0.9.1 (2000-01-04) [umoeller]: needed for EMX headers
100#include <os2.h>
101
102#include <stdlib.h>
103#include <stdio.h>
104#include <string.h>
105#include <setjmp.h> // needed for except.h
106#include <assert.h> // needed for except.h
107
108#include "setup.h" // code generation and debugging options
109
110#include "helpers\cnrh.h"
111#include "helpers\except.h" // exception handling
112#include "helpers\gpih.h"
113#include "helpers\linklist.h"
114#include "helpers\winh.h"
115#include "helpers\standards.h"
116
117#include "helpers\comctl.h"
118
119#pragma hdrstop
120
121/* ******************************************************************
122 *
123 * Shared stuff
124 *
125 ********************************************************************/
126
127/*
128 *@@ ctlSendWmControl:
129 * little helper that post a WM_CONTROL message to
130 * a control's owner.
131 *
132 *@@added V1.0.1 (2002-11-30) [umoeller]
133 */
134
135MRESULT ctlSendWmControl(HWND hwndControl, // in: control who's posting
136 USHORT usCode, // in: code for SHORT2FROMMP(mp1)
137 MPARAM mp2) // in: mp2 from WM_CONTROL
138{
139 HWND hwndOwner;
140
141 if (hwndOwner = WinQueryWindow(hwndControl, QW_OWNER))
142 return WinSendMsg(hwndOwner,
143 WM_CONTROL,
144 MPFROM2SHORT(WinQueryWindowUShort(hwndControl, QWS_ID),
145 usCode),
146 mp2);
147
148 return NULL;
149}
150
151/*
152 *@@ ctlPostWmControl:
153 * little helper that post a WM_CONTROL message to
154 * a control's owner.
155 *
156 *@@added V1.0.1 (2002-11-30) [umoeller]
157 */
158
159BOOL ctlPostWmControl(HWND hwndControl, // in: control who's posting
160 USHORT usCode, // in: code for SHORT2FROMMP(mp1)
161 MPARAM mp2) // in: mp2 from WM_CONTROL
162{
163 HWND hwndOwner;
164
165 if (hwndOwner = WinQueryWindow(hwndControl, QW_OWNER))
166 return WinPostMsg(hwndOwner,
167 WM_CONTROL,
168 MPFROM2SHORT(WinQueryWindowUShort(hwndControl, QWS_ID),
169 usCode),
170 mp2);
171
172 return FALSE;
173}
174
175/*
176 *@@ ctlInitDWD:
177 * ininitializes the DEFWINDATA struct for the
178 * given window. This must be called in WM_CREATE
179 * of a window proc if it intends to use
180 * ctlDefWindowProc as its default window procedure
181 * for generic message handling.
182 *
183 * Parameter remarks:
184 *
185 * -- For hwnd and mp2, pass in what you get in WM_CREATE.
186 *
187 * -- pdwd must point to a static DEFWINDATA which will
188 * maintain instance data for this control. It is
189 * recommended to have a DEFWINDATA struct in your
190 * window's instance data, which you must allocate
191 * in WM_CREATE anyway. So just pass the address of
192 * that struct within your instance buffer.
193 *
194 * -- pDefWindowProc is called by ctlDefWindowProc
195 * after its own message processing. In most cases,
196 * pass in WinDefWindowProc, unless you're superclassing
197 * a standard PM control.
198 *
199 * -- flCtl fine-tunes the behavior of ctlDefWindowProc:
200 *
201 * -- If CCS_NOSENDCTLPTR, we do not send WM_CONTROLPOINTER
202 * on every mouse move, but force the system "arrow"
203 * pointer over the control.
204 *
205 * -- paCtlColors must point to a CCTLCOLOR array. This
206 * enables automatic presparams and syscolor caching
207 * within ctlDefWindowProc so your window gets invalidated
208 * automatically. If you always use ctlQueryColor() in your
209 * WM_PAINT handler to determine the colors to be used for
210 * painting, you have full presparams support.
211 *
212 *@@added V1.0.1 (2002-11-30) [umoeller]
213 */
214
215VOID ctlInitDWD(HWND hwnd,
216 MPARAM mp2, // in: mp2 of WM_CREATE
217 PDEFWINDATA pdwd,
218 PFNWP pDefWindowProc, // in: parent window proc
219 ULONG flCtl, // in: CTL_* flags
220 const CCTLCOLOR *paCtlColors,
221 ULONG cCtlColors)
222{
223 pdwd->hwnd = hwnd;
224 pdwd->hab = WinQueryAnchorBlock(hwnd);
225 pdwd->pDefWindowProc = pDefWindowProc;
226 pdwd->flCtl = flCtl;
227 pdwd->szlWin.cx = ((PCREATESTRUCT)mp2)->cx;
228 pdwd->szlWin.cy = ((PCREATESTRUCT)mp2)->cy;
229 pdwd->paCtlColors = paCtlColors;
230 pdwd->cCtlColors = cCtlColors;
231
232 pdwd->palColorValues = (PLONG)malloc(sizeof(LONG) * cCtlColors);
233
234 ctlRefreshColors(pdwd);
235}
236
237/*
238 *@@ ctlRefreshColors:
239 *
240 *@@added V1.0.1 (2002-11-30) [umoeller]
241 */
242
243VOID ctlRefreshColors(PDEFWINDATA pdwd)
244{
245 ULONG ul;
246 for (ul = 0;
247 ul < pdwd->cCtlColors;
248 ++ul)
249 {
250 pdwd->palColorValues[ul] = winhQueryPresColor2(pdwd->hwnd,
251 pdwd->paCtlColors[ul].ulPP,
252 0,
253 pdwd->paCtlColors[ul].fInheritPP,
254 pdwd->paCtlColors[ul].ulSysColor);
255 }
256}
257
258/*
259 *@@ ctlQueryColor:
260 * returns the color value corresponding to the given
261 * color index. The color index is interpreted according
262 * to the CCTLCOLOR array that was given to ctlInitDWD
263 * and must not be larger than the number of colors in
264 * that array minus 1.
265 *
266 *@@added V1.0.1 (2003-01-22) [umoeller]
267 */
268
269LONG ctlQueryColor(PDEFWINDATA pdwd, ULONG ulIndex)
270{
271 if (ulIndex < pdwd->cCtlColors)
272 return pdwd->palColorValues[ulIndex];
273
274 return 0;
275}
276
277/*
278 *@@ ctlDefWindowProc:
279 * replacement default window procedure for controls that
280 * have a custom window class and do not inherit from
281 * standard OS/2 controls.
282 *
283 * If a window proc wishes to use this, it must allocate
284 * its own private window data in WM_CREATE (preferrably in
285 * QWL_USER + 1) and have room for a DEFWINDATA struct in
286 * there. It must call ctlInitDWD in WM_CREATE also which
287 * initializes that structure. It can then safely pass
288 * messages to this function.
289 *
290 *@@added V1.0.1 (2002-11-30) [umoeller]
291 */
292
293MRESULT ctlDefWindowProc(PDEFWINDATA pdwd, ULONG msg, MPARAM mp1, MPARAM mp2)
294{
295 MRESULT mrc = 0;
296 BOOL fCallDefault = TRUE;
297 HWND hwndOwner;
298
299 switch (msg)
300 {
301 case WM_MOUSEMOVE:
302 if ( (!(pdwd->flCtl & CCS_NOSENDCTLPTR))
303 && (hwndOwner = WinQueryWindow(pdwd->hwnd, QW_OWNER))
304 )
305 {
306 HPOINTER hptrArrow = WinQuerySysPointer(HWND_DESKTOP, SPTR_ARROW, FALSE);
307 WinSetPointer(HWND_DESKTOP,
308 (HPOINTER)WinSendMsg(hwndOwner,
309 WM_CONTROLPOINTER,
310 (MPARAM)WinQueryWindowUShort(pdwd->hwnd, QWS_ID),
311 (MPARAM)hptrArrow));
312 fCallDefault = FALSE;
313 }
314 break;
315
316 case WM_SYSCOLORCHANGE:
317 case WM_PRESPARAMCHANGED:
318 ctlRefreshColors(pdwd);
319 break;
320
321 case WM_ENABLE:
322 WinInvalidateRect(pdwd->hwnd, NULL, TRUE);
323 break;
324
325 case WM_WINDOWPOSCHANGED:
326 if (((PSWP)mp1)->fl & SWP_SIZE)
327 {
328 pdwd->szlWin.cx = ((PSWP)mp1)->cx;
329 pdwd->szlWin.cy = ((PSWP)mp1)->cy;
330 }
331 break;
332
333 case WM_DESTROY:
334 FREE(pdwd->palColorValues);
335 break;
336 }
337
338 if (fCallDefault)
339 mrc = pdwd->pDefWindowProc(pdwd->hwnd, msg, mp1, mp2);
340
341 return mrc;
342}
343
344/* ******************************************************************
345 *
346 * "Separator line" control
347 *
348 ********************************************************************/
349
350PFNWP G_pfnwpSepStatic = NULL;
351
352/*
353 *@@ fnwpSeparatorLine:
354 * window proc for the subclassed static control that makes
355 * the "separator line" control.
356 *
357 *@@added V0.9.20 (2002-08-10) [umoeller]
358 *@@changed V1.0.1 (2002-11-30) [umoeller]: added SEPS_VERTICAL
359 *@@changed V1.0.1 (2002-11-30) [umoeller]: fixed default background color
360 */
361
362STATIC MRESULT EXPENTRY fnwpSeparatorLine(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
363{
364 MRESULT mrc = 0;
365
366 switch (msg)
367 {
368 case WM_PAINT:
369 {
370 RECTL rcl;
371 HPS hps;
372 WinQueryWindowRect(hwnd, &rcl);
373 if (hps = WinBeginPaint(hwnd, NULLHANDLE, NULL))
374 {
375 POINTL ptl;
376
377 gpihSwitchToRGB(hps);
378
379 WinFillRect(hps,
380 &rcl,
381 winhQueryPresColor2(hwnd,
382 PP_BACKGROUNDCOLOR,
383 PP_BACKGROUNDCOLORINDEX,
384 TRUE,
385 SYSCLR_DIALOGBACKGROUND)); // SYSCLR_WINDOW));
386 // fixed V1.0.1 (2002-11-30) [umoeller]
387
388 if (WinQueryWindowULong(hwnd, QWL_STYLE) & SEPS_VERTICAL)
389 {
390 GpiSetColor(hps, G_lcol3DDark);
391
392 ptl.x = (rcl.xRight - rcl.xLeft) / 2 - 1;
393 ptl.y = rcl.yBottom;
394 GpiMove(hps, &ptl);
395 ptl.y = rcl.yTop;
396 GpiLine(hps, &ptl);
397
398 GpiSetColor(hps, G_lcol3DLight);
399
400 ptl.y = rcl.yBottom;
401 ++ptl.x;
402 GpiMove(hps, &ptl);
403 ptl.y = rcl.yTop;
404 GpiLine(hps, &ptl);
405 }
406 else
407 {
408 GpiSetColor(hps, G_lcol3DLight);
409
410 ptl.x = rcl.xLeft;
411 ptl.y = (rcl.yTop - rcl.yBottom) / 2 - 1;
412 GpiMove(hps, &ptl);
413 ptl.x = rcl.xRight;
414 GpiLine(hps, &ptl);
415
416 GpiSetColor(hps, G_lcol3DDark);
417
418 ptl.x = rcl.xLeft;
419 ++ptl.y;
420 GpiMove(hps, &ptl);
421 ptl.x = rcl.xRight;
422 GpiLine(hps, &ptl);
423 }
424
425 WinEndPaint(hps);
426 }
427 }
428 break;
429
430 default:
431 mrc = G_pfnwpSepStatic(hwnd, msg, mp1, mp2);
432 }
433
434 return mrc;
435}
436
437/*
438 *@@ ctlRegisterSeparatorLine:
439 * registers the separator line control, which is a dull
440 * static displaying a 3D line for use as a separator
441 * in dialogs.
442 *
443 * In addition to the standard WS_* styles, the control
444 * supports the SEPS_VERTICAL style bit. If set, the
445 * separator is vertical; if not, it is horizontal.
446 *
447 *@@added V1.0.0 (2002-08-12) [umoeller]
448 */
449
450BOOL ctlRegisterSeparatorLine(HAB hab)
451{
452 CLASSINFO ciStatic;
453 if (WinQueryClassInfo(hab,
454 WC_STATIC,
455 &ciStatic))
456 {
457 G_pfnwpSepStatic = ciStatic.pfnWindowProc;
458
459 return WinRegisterClass(hab,
460 WC_CCTL_SEPARATOR,
461 fnwpSeparatorLine,
462 (ciStatic.flClassStyle & ~CS_PUBLIC),
463 ciStatic.cbWindowData);
464
465 }
466
467 return FALSE;
468}
469
470/*
471 *@@category: Helpers\PM helpers\Window classes\Menu buttons
472 * See comctl.c and ctlMakeMenuButton.
473 */
474
475/* ******************************************************************
476 *
477 * "Menu button" control
478 *
479 ********************************************************************/
480
481/*
482 *@@ MENUBUTTONDATA:
483 * internal data for "menu button"
484 *
485 *@@added V0.9.0 [umoeller]
486 */
487
488typedef struct _MENUBUTTONDATA
489{
490 PFNWP pfnwpButtonOriginal;
491 HMODULE hmodMenu;
492 ULONG idMenu;
493 BOOL fMouseCaptured, // TRUE if WinSetCapture
494 fMouseButton1Down, // TRUE in between WM_BUTTON1DOWN and WM_BUTTON1UP
495 fButtonSunk; // toggle state of the button
496 HWND hwndMenu;
497} MENUBUTTONDATA, *PMENUBUTTONDATA;
498
499/*
500 *@@ ctlDisplayButtonMenu:
501 * displays the specified menu above the button.
502 *
503 *@@added V0.9.7 (2000-11-29) [umoeller]
504 */
505
506VOID ctlDisplayButtonMenu(HWND hwndButton,
507 HWND hwndMenu)
508{
509 SWP swpButton;
510 POINTL ptlMenu;
511 WinQueryWindowPos(hwndButton, &swpButton);
512 ptlMenu.x = swpButton.x;
513 ptlMenu.y = swpButton.y;
514
515 // ptlMenu now has button coordinates
516 // relative to the button's parent;
517 // convert this to screen coordinates:
518 WinMapWindowPoints(WinQueryWindow(hwndButton, QW_PARENT),
519 HWND_DESKTOP,
520 &ptlMenu,
521 1);
522
523 // now show the menu on top of the button
524 WinPopupMenu(HWND_DESKTOP, // menu parent
525 hwndButton, // owner
526 hwndMenu,
527 (SHORT)(ptlMenu.x),
528 (SHORT)(ptlMenu.y + swpButton.cy - 1),
529 0, // ID
530 PU_NONE
531 | PU_MOUSEBUTTON1
532 | PU_KEYBOARD
533 | PU_HCONSTRAIN
534 | PU_VCONSTRAIN);
535}
536
537/*
538 *@@ ctl_fnwpSubclassedMenuButton:
539 * subclassed window proc for "menu button".
540 * See ctlMakeMenuButton for details.
541 *
542 *@@added V0.9.0 [umoeller]
543 *@@changed V0.9.2 (2000-02-28) [umoeller]: menu was displayed even if button was disabled; fixed
544 *@@changed V0.9.14 (2001-07-31) [umoeller]: fixed WM_MENUEND submenu quirk
545 */
546
547MRESULT EXPENTRY ctl_fnwpSubclassedMenuButton(HWND hwndButton, ULONG msg, MPARAM mp1, MPARAM mp2)
548{
549 MRESULT mrc = 0;
550 PMENUBUTTONDATA pmbd = (PMENUBUTTONDATA)WinQueryWindowULong(hwndButton, QWL_USER);
551
552 switch (msg)
553 {
554 /*
555 * WM_BUTTON1DOWN:
556 * WM_BUTTON1UP:
557 * these show/hide the menu.
558 *
559 * Showing the menu follows these steps:
560 * a) first WM_BUTTON1DOWN hilites the button;
561 * b) first WM_BUTTON1UP shows the menu.
562 *
563 * When the button is pressed again, the open
564 * menu loses focus, which results in WM_MENUEND
565 * and destroys the window automatically.
566 */
567
568 case WM_BUTTON1DOWN:
569 case WM_BUTTON1DBLCLK:
570 // only do this if the button is enabled
571 // V0.9.2 (2000-02-28) [umoeller]
572 if (WinIsWindowEnabled(hwndButton))
573 {
574 // _Pmpf(("WM_BUTTON1DOWN"));
575 // since we're not passing the message
576 // to WinDefWndProc, we need to give
577 // ourselves the focus
578 WinSetFocus(HWND_DESKTOP, hwndButton);
579
580 if (!pmbd->fMouseCaptured)
581 {
582 // capture mouse events while the
583 // mouse button is down
584 WinSetCapture(HWND_DESKTOP, hwndButton);
585 pmbd->fMouseCaptured = TRUE;
586 }
587
588 pmbd->fMouseButton1Down = TRUE;
589
590 if (!pmbd->fButtonSunk)
591 {
592 // button not hilited yet (first click):
593 // do it now
594 // _Pmpf((" Sinking menu WM_BUTTON1DOWN "));
595 pmbd->fButtonSunk = TRUE;
596 WinSendMsg(hwndButton,
597 BM_SETHILITE,
598 (MPARAM)TRUE,
599 (MPARAM)0);
600 }
601
602 // else: the menu has just been destroyed
603 // (WM_MENUEND below)
604 }
605
606 mrc = (MPARAM)TRUE; // message processed
607 break;
608
609 /*
610 * WM_BUTTON1UP:
611 *
612 */
613
614 case WM_BUTTON1UP:
615 // only do this if the button is enabled
616 // V0.9.2 (2000-02-28) [umoeller]
617 if (WinIsWindowEnabled(hwndButton))
618 {
619 // _Pmpf(("WM_BUTTON1UP sunk %d hwndMenu 0x%lX", pmbd->fButtonSunk, pmbd->hwndMenu));
620
621 // un-capture the mouse first
622 if (pmbd->fMouseCaptured)
623 {
624 WinSetCapture(HWND_DESKTOP, NULLHANDLE);
625 pmbd->fMouseCaptured = FALSE;
626 }
627
628 pmbd->fMouseButton1Down = FALSE;
629
630 if ( (pmbd->fButtonSunk) // set by WM_BUTTON1DOWN above
631 && (pmbd->hwndMenu) // menu currently showing
632 )
633 {
634 // button currently depressed:
635 // un-hilite button
636 // _Pmpf((" Unsinking menu WM_BUTTON1UP 1"));
637 pmbd->fButtonSunk = FALSE;
638 WinSendMsg(hwndButton,
639 BM_SETHILITE,
640 (MPARAM)FALSE,
641 (MPARAM)0);
642 }
643 else
644 {
645 // first button up:
646 // show menu
647
648 if (pmbd->idMenu)
649 {
650 // _Pmpf((" Loading menu hmod %lX id %lX", pmbd->hmodMenu, pmbd->idMenu));
651 // menu specified: load from
652 // specified resources
653 pmbd->hwndMenu = WinLoadMenu(hwndButton,
654 pmbd->hmodMenu,
655 pmbd->idMenu);
656 }
657 else
658 {
659 HWND hwndOwner = WinQueryWindow(hwndButton, QW_OWNER);
660 // _Pmpf((" Querying menu WM_COMMAND from owner 0x%lX", hwndOwner));
661 // send WM_COMMAND to owner
662 pmbd->hwndMenu
663 = (HWND)WinSendMsg(hwndOwner,
664 WM_COMMAND,
665 (MPARAM)(ULONG)WinQueryWindowUShort(hwndButton,
666 QWS_ID),
667 (MPARAM)0);
668 }
669
670 // _Pmpf((" Loaded menu, hwnd: 0x%lX", pmbd->hwndMenu));
671
672 if (pmbd->hwndMenu)
673 {
674 // menu successfully loaded:
675 // find out where to put it
676 ctlDisplayButtonMenu(hwndButton,
677 pmbd->hwndMenu);
678 } // end if (pmbd->hwndMenu)
679 else
680 {
681 // menu not loaded:
682 // _Pmpf((" Unsinking menu WM_BUTTON1UP 2"));
683 pmbd->fButtonSunk = FALSE;
684 WinSendMsg(hwndButton,
685 BM_SETHILITE,
686 (MPARAM)FALSE,
687 (MPARAM)0);
688 }
689 }
690 }
691
692 mrc = (MPARAM)TRUE; // message processed
693 break;
694
695 /*
696 * WM_BUTTON1CLICK:
697 * swallow this
698 */
699
700 case WM_BUTTON1CLICK:
701 mrc = (MPARAM)TRUE;
702 break;
703
704 /*
705 * WM_SETFOCUS:
706 * swallow this, the button keeps painting
707 * itself otherwise
708 */
709
710 case WM_SETFOCUS:
711 break;
712
713 /*
714 * WM_MENUEND:
715 * menu is destroyed; we get this
716 * because we're the owner
717 */
718
719 case WM_MENUEND:
720 if ((HWND)mp2 == pmbd->hwndMenu) // V0.9.14 (2001-07-31) [umoeller]
721 {
722 BOOL fUnHilite = TRUE;
723 // _Pmpf(("WM_MENUEND"));
724 // At this point, the menu has been
725 // destroyed already.
726 // Since WM_BUTTON1UP handles the
727 // default case that the user presses
728 // the menu button a second time while
729 // the menu is open, we only need
730 // to handle the case that the user
731 // c) presses some key on the menu (ESC, menu selection) or
732 // a) selects a menu item (which
733 // dismisses the menu) or
734 // b) clicks anywhere else.
735
736 // Case a) if mouse button 1 is not currently down
737 if ((WinGetKeyState(HWND_DESKTOP, VK_BUTTON1) & 0x8000) == 0)
738 // button 1 _not_ down:
739 // must be keyboard
740 fUnHilite = TRUE;
741 else
742 // button 1 _is_ down:
743 // query window under mouse pointer
744 ;
745
746 if (fUnHilite)
747 {
748 // _Pmpf((" Unsinking menu WM_MENUEND"));
749 pmbd->fButtonSunk = FALSE;
750 WinSendMsg(hwndButton,
751 BM_SETHILITE,
752 (MPARAM)FALSE,
753 (MPARAM)0);
754 }
755 pmbd->hwndMenu = NULLHANDLE;
756 } // end if ((HWND)mp1 == pmbd->pmbd->hwndMenu) // V0.9.14 (2001-07-31) [umoeller]
757 break;
758
759 /*
760 * WM_COMMAND:
761 * this must be from the menu, so
762 * forward this to the button's owner
763 */
764
765 case WM_COMMAND:
766 WinPostMsg(WinQueryWindow(hwndButton, QW_OWNER),
767 msg,
768 mp1,
769 mp2);
770 break;
771
772 /*
773 * WM_DESTROY:
774 * clean up allocated data
775 */
776
777 case WM_DESTROY:
778 mrc = pmbd->pfnwpButtonOriginal(hwndButton, msg, mp1, mp2);
779 free(pmbd);
780 break;
781
782 default:
783 mrc = pmbd->pfnwpButtonOriginal(hwndButton, msg, mp1, mp2);
784 }
785
786 return mrc;
787}
788
789/*
790 *@@ ctlMakeMenuButton:
791 * this turns the specified button into a "menu button",
792 * which shows a popup menu when the button is depressed.
793 * This is done by subclassing the menu button with
794 * ctl_fnwpSubclassedMenuButton.
795 *
796 * Simply call this function upon any button, and it'll
797 * turn in to a menu button.
798 *
799 * When the user presses the button, the specified menu
800 * is loaded from the resources. The button will then
801 * be set as the owner, but it will forward all WM_COMMAND
802 * messages from the menu to its own owner (probably your
803 * dialog), so you can handle WM_COMMAND messages just as
804 * if the menu was owned by your dialog.
805 *
806 * Alternatively, if you don't want to load a menu from
807 * the resources, you can specify idMenu == 0. In that case,
808 * when the menu button is pressed, it sends (!) a WM_COMMAND
809 * message to its owner with its ID in mp1 (as usual). The
810 * difference is that the return value from that message will
811 * be interpreted as a menu handle (HWND) to use for the button
812 * menu.
813 *
814 * The subclassed button also handles menu destruction etc.
815 * by itself.
816 *
817 *@@added V0.9.0 [umoeller]
818 */
819
820BOOL ctlMakeMenuButton(HWND hwndButton, // in: button to subclass
821 HMODULE hmodMenu, // in: resource module (can be NULLHANDLE for
822 // current EXE)
823 ULONG idMenu) // in: resource menu ID (or 0)
824{
825 BOOL brc = FALSE;
826 PMENUBUTTONDATA pmbd;
827 if (pmbd = (PMENUBUTTONDATA)malloc(sizeof(MENUBUTTONDATA)))
828 {
829 memset(pmbd, 0, sizeof(MENUBUTTONDATA));
830 if (pmbd->pfnwpButtonOriginal = WinSubclassWindow(hwndButton,
831 ctl_fnwpSubclassedMenuButton))
832 {
833 pmbd->hmodMenu = hmodMenu;
834 pmbd->idMenu = idMenu;
835 WinSetWindowULong(hwndButton, QWL_USER, (ULONG)pmbd);
836 brc = TRUE;
837 }
838 else
839 free(pmbd);
840 }
841
842 return brc;
843}
844
845/*
846 *@@category: Helpers\PM helpers\Window classes\Static bitmaps
847 * See comctl.c and ctl_fnwpBitmapStatic.
848 */
849
850/* ******************************************************************
851 *
852 * Subclassed Static Bitmap Control
853 *
854 ********************************************************************/
855
856/*
857 *@@ ctl_fnwpBitmapStatic:
858 * subclassed wnd proc for both static controls which
859 * should display icons and stretched bitmaps.
860 *
861 * This is not a stand-alone window procedure, but must only
862 * be used with static controls subclassed by the functions
863 * listed below.
864 *
865 * This is now (V0.9.0) used in two contexts:
866 * -- when subclassed from ctlPrepareStaticIcon,
867 * this displays transparent icons properly
868 * (for regular icons or icon animations);
869 * -- when subclassed from ctlPrepareStretchedBitmap,
870 * this displays a stretched bitmap.
871 *
872 * The behavior depends on ANIMATIONDATA.ulFlags,
873 * which is set by both of these functions (either to
874 * ANF_ICON or ANF_BITMAP).
875 *
876 * Only one msg is of interest to the "user" application
877 * of this control, which is SM_SETHANDLE,
878 * the normal message sent to a static icon control to change
879 * its icon or bitmap.
880 * The new handle (HPOINTER or HBITMAP) is
881 * in mp1; mp2 must be null.
882 *
883 * Depending on ANIMATIONDATA.ulFlags, we do the following:
884 *
885 * -- ANF_ICON: Unfortunately, the
886 * standard static control paints garbage if
887 * the icons contain transparent areas, and the
888 * display often flickers too.
889 * We improve this by creating one bitmap from
890 * the icon that we were given which we can then
891 * simply copy to the screen in one step in
892 * WM_PAINT.
893 *
894 * -- ANF_BITMAP: we create a bitmap which is
895 * stretched to the size of the static control,
896 * using gpihStretchBitmap.
897 *
898 * In any case, a new bitmap is produced internally and
899 * stored so that it can easily be painted when WM_PAINT
900 * comes in. The source icon/bitmap can therefore
901 * be safely destroyed by the caller after sending
902 * SM_SETHANDLE. This bitmap is automatically deleted when
903 * the window is destroyed.
904 *
905 *@@changed V0.9.0 [umoeller]: function renamed
906 *@@changed V0.9.0 [umoeller]: added support for stretched bitmaps
907 *@@changed V0.9.0 [umoeller]: added halftoned display for WS_DISABLED
908 *@@changed V0.9.0 [umoeller]: exported gpihIcon2Bitmap function to gpih.c
909 *@@changed V0.9.0 [umoeller]: fixed paint errors when SM_SETHANDLE had NULL argument in mp1
910 *@@changed V0.9.16 (2001-10-15) [umoeller]: now centering icon in static properly
911 *@@changed V0.9.16 (2001-10-15) [umoeller]: this always used the presparam colors of the parent instead of its own ones
912 */
913
914MRESULT EXPENTRY ctl_fnwpBitmapStatic(HWND hwndStatic, ULONG msg, MPARAM mp1, MPARAM mp2)
915{
916 PANIMATIONDATA pa = (PANIMATIONDATA)WinQueryWindowULong(hwndStatic, QWL_USER);
917 // animation data which was stored in window words
918
919 PFNWP OldStaticProc = NULL;
920 MRESULT mrc = NULL;
921
922 if (pa)
923 {
924 OldStaticProc = pa->OldStaticProc;
925
926 switch(msg)
927 {
928 /*
929 * WM_TIMER:
930 * this timer is started by the ctl* funcs
931 * below. Proceed to the next animation step.
932 *
933 * Note: this timer is only used with
934 * ANF_ICON, and even then, it
935 * is not necessarily running.
936 */
937
938 case WM_TIMER:
939 pa->usAniCurrent++;
940 if (pa->usAniCurrent >= pa->usAniCount)
941 pa->usAniCurrent = 0;
942
943 WinSendMsg(hwndStatic,
944 SM_SETHANDLE,
945 (MPARAM)pa->ahptrAniIcons[pa->usAniCurrent],
946 (MPARAM)NULL);
947 break;
948
949 /*
950 * SM_SETHANDLE:
951 *
952 */
953
954 case SM_SETHANDLE:
955 {
956 HDC hdcMem;
957 HPS hpsMem;
958 SIZEL szlPage;
959
960 LONG lBkgndColor
961 /* = winhQueryPresColor(WinQueryWindow(hwndStatic, QW_PARENT),
962 PP_BACKGROUNDCOLOR,
963 FALSE,
964 SYSCLR_DIALOGBACKGROUND); */
965 // fixed this... V0.9.16 (2001-10-15) [umoeller]
966 = winhQueryPresColor(hwndStatic,
967 PP_BACKGROUNDCOLOR,
968 TRUE,
969 SYSCLR_DIALOGBACKGROUND);
970
971 HPS hps = WinGetPS(hwndStatic);
972
973 // if we have created bitmaps previously, delete them
974 if (pa->hbm)
975 {
976 GpiDeleteBitmap(pa->hbm);
977 pa->hbm = NULLHANDLE;
978 }
979 if (pa->hbmHalftoned)
980 {
981 GpiDeleteBitmap(pa->hbmHalftoned);
982 pa->hbmHalftoned = NULLHANDLE;
983 }
984
985 // switch to RGB mode
986 gpihSwitchToRGB(hps);
987
988 // create a memory PS
989 szlPage.cx = pa->rclIcon.xRight - pa->rclIcon.xLeft;
990 szlPage.cy = pa->rclIcon.yTop - pa->rclIcon.yBottom;
991 if (gpihCreateMemPS(pa->hab,
992 &szlPage,
993 &hdcMem,
994 &hpsMem))
995 {
996 // switch the memory PS to RGB mode too
997 gpihSwitchToRGB(hpsMem);
998
999 // create a suitable bitmap w/ the size of the
1000 // static control
1001 if ( ((pa->hbm = gpihCreateBitmap(hpsMem,
1002 szlPage.cx,
1003 szlPage.cy)))
1004 // associate the bit map with the memory PS
1005 && (GpiSetBitmap(hpsMem, pa->hbm) != HBM_ERROR)
1006 )
1007 {
1008 // fill the bitmap with the current static
1009 // background color
1010 POINTL ptl = {0, 0};
1011 GpiMove(hpsMem, &ptl);
1012 ptl.x = pa->rclIcon.xRight;
1013 ptl.y = pa->rclIcon.yTop;
1014 GpiSetColor(hpsMem,
1015 lBkgndColor);
1016 GpiBox(hpsMem,
1017 DRO_FILL, // interior only
1018 &ptl,
1019 0, 0); // no corner rounding
1020
1021 /*
1022 * ANF_ICON:
1023 *
1024 */
1025
1026 if (pa->ulFlags & ANF_ICON)
1027 {
1028 // store new icon in our own structure
1029 if (pa->hptr = (HPOINTER)mp1)
1030 {
1031 // center the icon in the bitmap
1032 // V0.9.16 (2001-10-15) [umoeller]
1033
1034 // replaced call V0.9.19 (2002-06-18) [umoeller]
1035 gpihDrawPointer(hpsMem,
1036 ( (pa->rclIcon.xRight - pa->rclIcon.xLeft)
1037 - pa->szlIcon.cx
1038 ) / 2,
1039 ( (pa->rclIcon.yTop - pa->rclIcon.yBottom)
1040 - pa->szlIcon.cy
1041 ) / 2,
1042 pa->hptr,
1043 &pa->szlIcon,
1044 NULL, // no clipping
1045 0); // no mini
1046 }
1047
1048 } // end if (pa->ulFlags & ANF_ICON)
1049
1050 /*
1051 * ANF_BITMAP:
1052 *
1053 */
1054
1055 else if (pa->ulFlags & ANF_BITMAP)
1056 {
1057 // store passed bitmap
1058 HBITMAP hbmSource;
1059 if (hbmSource = (HBITMAP)mp1)
1060 gpihStretchBitmap(hpsMem, // target
1061 hbmSource, // source
1062 NULL, // use size of bitmap
1063 &pa->rclIcon,
1064 ((pa->ulFlags & ANF_PROPORTIONAL)
1065 != 0));
1066
1067 } // end if (pa->ulFlags & ANF_BITMAP)
1068
1069 } // end if (GpiSetBitmap(...
1070 // && (pa->hbm = gpihCreateBitmap(hpsMem, &(pa->rclIcon)))
1071
1072 // in any case, clean up now
1073 GpiDestroyPS(hpsMem);
1074 DevCloseDC(hdcMem);
1075 } // end if (gpihCreateMemPS(hab, &hdcMem, &hpsMem))
1076
1077 WinReleasePS(hps);
1078
1079 // enforce WM_PAINT
1080 WinInvalidateRect(hwndStatic, NULL, FALSE);
1081 }
1082 break;
1083
1084 /*
1085 * WM_PAINT:
1086 * "normal" paint; this only arrives here if the
1087 * icon needs to be repainted. We simply paint
1088 * the bitmap we created in WM_SETHANDLE.
1089 */
1090
1091 case WM_PAINT:
1092 {
1093 RECTL rcl;
1094 HPS hps = WinBeginPaint(hwndStatic, NULLHANDLE, &rcl);
1095 POINTL ptl = {0, 0};
1096
1097 if (pa->hbm)
1098 {
1099 if (WinQueryWindowULong(hwndStatic, QWL_STYLE)
1100 & WS_DISABLED)
1101 {
1102 // if the control is currently disabled,
1103 // draw in half-toned (grayed)
1104
1105 LONG lBkgndColor = winhQueryPresColor(
1106 WinQueryWindow(hwndStatic, QW_PARENT),
1107 PP_BACKGROUNDCOLOR,
1108 FALSE,
1109 SYSCLR_DIALOGBACKGROUND);
1110
1111 // 1) check if we created the half-tone
1112 // bitmap already (WinDrawBitmap doesn't
1113 // work with half-toned color bitmaps, so
1114 // here's yet another thing we have to do
1115 // all alone)
1116 if (pa->hbmHalftoned == NULLHANDLE)
1117 pa->hbmHalftoned = gpihCreateHalftonedBitmap(pa->hab,
1118 pa->hbm,
1119 lBkgndColor);
1120
1121 if (pa->hbmHalftoned)
1122 WinDrawBitmap(hps,
1123 pa->hbmHalftoned,
1124 NULL, // whole bmp
1125 &ptl, // lower left corner
1126 0, 0, // no colors
1127 DBM_NORMAL);
1128 }
1129 else
1130 {
1131 // not disabled: draw regular bitmap
1132 // we created previously
1133 // draw the bitmap that we created previously
1134 WinDrawBitmap(hps,
1135 pa->hbm,
1136 NULL, // whole bmp
1137 &ptl, // lower left corner
1138 0, 0, // no colors
1139 DBM_NORMAL);
1140 }
1141 }
1142
1143 WinEndPaint(hps);
1144 }
1145 break;
1146
1147 /*
1148 * WM_DESTROY:
1149 * clean up.
1150 */
1151
1152 case WM_DESTROY:
1153 // undo subclassing in case more WM_TIMERs come in
1154 WinSubclassWindow(hwndStatic, OldStaticProc);
1155
1156 if (pa->hbm)
1157 GpiDeleteBitmap(pa->hbm);
1158 if (pa->hbmHalftoned)
1159 GpiDeleteBitmap(pa->hbm);
1160
1161 // clean up ANIMATIONDATA struct
1162 WinSetWindowULong(hwndStatic, QWL_USER, (ULONG)NULL);
1163 free(pa);
1164
1165 mrc = OldStaticProc(hwndStatic, msg, mp1, mp2);
1166 break;
1167
1168 default:
1169 mrc = OldStaticProc(hwndStatic, msg, mp1, mp2);
1170 }
1171 }
1172 return mrc;
1173}
1174
1175/* ******************************************************************
1176 *
1177 * Icon animation
1178 *
1179 ********************************************************************/
1180
1181/*
1182 *@@ CreateAnimationData:
1183 *
1184 *@@added V0.9.16 (2001-10-15) [umoeller]
1185 */
1186
1187STATIC PANIMATIONDATA CreateAnimationData(HAB hab,
1188 HWND hwndStatic,
1189 USHORT cAnimations)
1190{
1191 PANIMATIONDATA pa = NULL;
1192
1193 if (cAnimations >= 1)
1194 {
1195 // create the ANIMATIONDATA structure,
1196 // initialize some fields,
1197 // and store it in QWL_USER of the static control
1198 ULONG cbStruct = sizeof(ANIMATIONDATA)
1199 + ((cAnimations - 1) * sizeof(HPOINTER));
1200
1201 if (pa = (PANIMATIONDATA)malloc(cbStruct))
1202 {
1203 memset(pa, 0, cbStruct);
1204
1205 WinSetWindowULong(hwndStatic, QWL_USER, (ULONG)pa);
1206
1207 pa->hab = hab;
1208 WinQueryWindowRect(hwndStatic, &pa->rclIcon);
1209 pa->OldStaticProc = WinSubclassWindow(hwndStatic, ctl_fnwpBitmapStatic);
1210 pa->szlIcon.cx = G_cxIcon;
1211 pa->szlIcon.cy = G_cyIcon;
1212 }
1213 }
1214
1215 return pa;
1216}
1217
1218/*
1219 *@@ ctlPrepareStaticIcon:
1220 * turns a static control into one which properly
1221 * displays transparent icons when given a
1222 * SM_SETHANDLE msg.
1223 * This is done by subclassing the static control
1224 * with ctl_fnwpBitmapStatic.
1225 *
1226 * This function gets called automatically by
1227 * ctlPrepareAnimation, so you need not call it
1228 * independently for animations. See the notes there
1229 * how this can be done.
1230 *
1231 * You can, however, call this function if you
1232 * have some static control with transparent icons
1233 * which is not animated but changes sometimes
1234 * (because you have the same repaint problems there).
1235 *
1236 * This func does _not_ start an animation yet.
1237 *
1238 * To change the icon being displayed, send the control
1239 * a SM_SETHANDLE msg with the icon handle in (HPOINTER)mp1.
1240 *
1241 * Returns a PANIMATIONDATA if subclassing succeeded or
1242 * NULL upon errors. If you only call this function, you
1243 * do not need this structure; it is needed by
1244 * ctlPrepareAnimation though.
1245 *
1246 * The subclassed static icon func above automatically
1247 * cleans up resources, so you don't have to worry about
1248 * that either.
1249 *
1250 *@@changed V0.9.0 [umoeller]: adjusted for ctl_fnwpBitmapStatic changes
1251 */
1252
1253PANIMATIONDATA ctlPrepareStaticIcon(HWND hwndStatic,
1254 USHORT usAnimCount) // needed for allocating extra memory,
1255 // this must be at least 1
1256{
1257 PANIMATIONDATA pa;
1258 HAB hab;
1259 if ( (hwndStatic)
1260 && (hab = WinQueryAnchorBlock(hwndStatic))
1261 && (WinIsWindow(hab, hwndStatic))
1262 && (pa = CreateAnimationData(hab,
1263 hwndStatic,
1264 usAnimCount))
1265 )
1266 {
1267 // switch static to icon mode
1268 pa->ulFlags = ANF_ICON;
1269 return pa;
1270 }
1271
1272 return NULL;
1273}
1274
1275/*
1276 *@@ ctlPrepareAnimation:
1277 * this function turns a regular static icon
1278 * control an animated one. This is the one-shot
1279 * function for animating static icon controls.
1280 *
1281 * It calls ctlPrepareStaticIcon first, then uses
1282 * the passed parameters to prepare an animation:
1283 *
1284 * pahptr must point to an array of HPOINTERs
1285 * which must contain usAnimCount icon handles.
1286 * If (fStartAnimation == TRUE), the animation
1287 * is started already. Otherwise you'll have
1288 * to call ctlStartAnimation yourself.
1289 *
1290 * To create an animated icon, simply create a static
1291 * control with the dialog editor (can be any static,
1292 * e.g. a text control). Then do the following in your code:
1293 *
1294 * 1) load the dlg box template (using WinLoadDlg);
1295 *
1296 * 2) load all the icons for the animation in an array of
1297 * HPOINTERs (check xsdLoadAnimation in shutdown.c for an
1298 * example);
1299 *
1300 * 3) call ctlPrepareAnimation, e.g.:
1301 + ctlPrepareAnimation(WinWindowFromID(hwndDlg, ID_ICON), // get icon hwnd
1302 + 8, // no. of icons for the anim
1303 + &ahptr[0], // ptr to first icon in the array
1304 + 150, // delay
1305 + TRUE); // start animation now
1306 *
1307 * 4) call WinProcessDlg(hwndDlg);
1308 *
1309 * 5) stop the animation, e.g.:
1310 + ctlStopAnimation(WinWindowFromID(hwndDlg, ID_ICON));
1311 *
1312 * 6) destroy the dlg window;
1313 *
1314 * 7) free all the HPOINTERS loaded above (check xsdFreeAnimation
1315 * in shutdown.c for an example).
1316 *
1317 * When the icon control is destroyed, the
1318 * subclassed window proc (ctl_fnwpBitmapStatic)
1319 * automatically cleans up resources. However,
1320 * the icons in *pahptr are not freed.
1321 */
1322
1323BOOL ctlPrepareAnimation(HWND hwndStatic, // icon hwnd
1324 USHORT usAnimCount, // no. of anim steps
1325 HPOINTER *pahptr, // array of HPOINTERs
1326 ULONG ulDelay, // delay per anim step (in ms)
1327 BOOL fStartAnimation) // TRUE: start animation now
1328{
1329 PANIMATIONDATA paNew;
1330 if (paNew = ctlPrepareStaticIcon(hwndStatic, usAnimCount))
1331 {
1332 paNew->ulDelay = ulDelay;
1333 // paNew->OldStaticProc already set
1334 paNew->hptr = NULLHANDLE;
1335 // paNew->rclIcon already set
1336 paNew->usAniCurrent = 0;
1337 paNew->usAniCount = usAnimCount;
1338 memcpy(&paNew->ahptrAniIcons,
1339 pahptr,
1340 (usAnimCount * sizeof(HPOINTER)));
1341
1342 if (fStartAnimation)
1343 {
1344 WinStartTimer(paNew->hab,
1345 hwndStatic,
1346 1,
1347 ulDelay);
1348 WinPostMsg(hwndStatic, WM_TIMER, (MPARAM)1, NULL);
1349 }
1350 }
1351
1352 return (paNew != NULL);
1353}
1354
1355/*
1356 *@@ ctlStartAnimation:
1357 * starts an animation that not currently running. You
1358 * must prepare the animation by calling ctlPrepareAnimation
1359 * first.
1360 */
1361
1362BOOL ctlStartAnimation(HWND hwndStatic)
1363{
1364 BOOL brc = FALSE;
1365 PANIMATIONDATA pa;
1366 if ( (pa = (PANIMATIONDATA)WinQueryWindowULong(hwndStatic, QWL_USER))
1367 && (WinStartTimer(pa->hab,
1368 hwndStatic,
1369 1,
1370 pa->ulDelay))
1371 )
1372 {
1373 brc = TRUE;
1374 WinPostMsg(hwndStatic, WM_TIMER, (MPARAM)1, NULL);
1375 }
1376
1377 return brc;
1378}
1379
1380/*
1381 *@@ ctlStopAnimation:
1382 * stops an animation that is currently running.
1383 * This does not free any resources.
1384 */
1385
1386BOOL ctlStopAnimation(HWND hwndStatic)
1387{
1388 return WinStopTimer(WinQueryAnchorBlock(hwndStatic), hwndStatic, 1);
1389}
1390
1391/*
1392 *@@category: Helpers\PM helpers\Window classes\Stretched bitmaps
1393 * See comctl.c and ctlPrepareStretchedBitmap.
1394 */
1395
1396/* ******************************************************************
1397 *
1398 * Bitmap functions
1399 *
1400 ********************************************************************/
1401
1402/*
1403 *@@ ctlPrepareStretchedBitmap:
1404 * turns a static control into a bitmap control
1405 * which stretches the bitmap to the size of the
1406 * control when given an SM_SETHANDLE msg.
1407 *
1408 * If (fPreserveProportions == TRUE), the control
1409 * will size the bitmap proportionally only. Otherwise,
1410 * it will always stretch the bitmap to the whole
1411 * size of the static control (possibly distorting
1412 * the proportions). See gpihStretchBitmap for
1413 * details.
1414 *
1415 * This function subclasses the static control
1416 * with ctl_fnwpBitmapStatic, setting the flags as
1417 * necessary. However, this does not set the bitmap
1418 * to display yet.
1419 * To have a bitmap displayed, send the control
1420 * a SM_SETHANDLE message with the bitmap to be
1421 * displayed in (HBITMAP)mp1 after having used this
1422 * function.
1423 *
1424 * ctl_fnwpBitmapStatic will then create a private
1425 * copy of that bitmap, stretched to its own size.
1426 * You can therefore delete the "source" bitmap
1427 * after sending SM_SETHANDLE.
1428 *
1429 * You can also send SM_SETHANDLE several times.
1430 * The control will then readjust itself and delete
1431 * its old bitmap.
1432 * But note that GpiWCBitBlt is invoked on that new
1433 * bitmap with every new message, so this is not
1434 * terribly fast.
1435 *
1436 * Also note that you should not resize the static
1437 * control after creation, because the bitmap will
1438 * _not_ be adjusted.
1439 *
1440 * This returns a PANIMATIONDATA if subclassing succeeded or
1441 * NULL upon errors. You do not need to save this structure;
1442 * it is cleaned up automatically when the control is destroyed.
1443 *
1444 *@@added V0.9.0 [umoeller]
1445 *@@changed V0.9.16 (2001-10-15) [umoeller]: some cleanup
1446 */
1447
1448PANIMATIONDATA ctlPrepareStretchedBitmap(HWND hwndStatic,
1449 BOOL fPreserveProportions)
1450{
1451 PANIMATIONDATA pa;
1452 HAB hab;
1453 if ( (hwndStatic)
1454 && (hab = WinQueryAnchorBlock(hwndStatic))
1455 && (WinIsWindow(hab, hwndStatic))
1456 && (pa = CreateAnimationData(hab, hwndStatic, 1))
1457 )
1458 {
1459 // switch static to bitmap mode
1460 pa->ulFlags = ANF_BITMAP;
1461 if (fPreserveProportions)
1462 pa->ulFlags |= ANF_PROPORTIONAL;
1463 return pa;
1464 }
1465
1466 return NULL;
1467}
1468
1469/*
1470 *@@category: Helpers\PM helpers\Window classes\Hotkey entry field
1471 * See comctl.c and ctl_fnwpObjectHotkeyEntryField.
1472 */
1473
1474/* ******************************************************************
1475 *
1476 * Hotkey entry field
1477 *
1478 ********************************************************************/
1479
1480/*
1481 *@@ ctl_fnwpHotkeyEntryField:
1482 * this is the window proc for a subclassed entry
1483 * field which displays a description of a keyboard
1484 * combination upon receiving WM_CHAR.
1485 *
1486 * Use ctlMakeHotkeyEntryField to turn any existing
1487 * entry field into such an "hotkey entry field".
1488 *
1489 * The entry field is deleted on every WM_CHAR that
1490 * comes in.
1491 *
1492 * For this to work, this window proc needs the
1493 * cooperation of its owner. Whenever a WM_CHAR
1494 * message is received by the entry field which
1495 * looks like a meaningful key or key combination
1496 * (some filtering is done by this func), then
1497 * the owner of the entry field receives a WM_CONTROL
1498 * message with the new EN_HOTKEY notification code
1499 * (see comctl.h for details).
1500 *
1501 * It is then the responsibility of the owner to
1502 * check whether the HOTKEYNOTIFY structure pointed
1503 * to by WM_CONTROL's mp2 is a meaningful keyboard
1504 * combination.
1505 *
1506 * If not, the owner must return FALSE, and the
1507 * entry field's contents are deleted.
1508 *
1509 * If yes however, the owner must fill the szDescription
1510 * field with a description of the keyboard combination
1511 * (e.g. "Shift+Alt+C") and return TRUE. The entry field
1512 * will then be set to that description.
1513 *
1514 *@@added V0.9.1 (99-12-19) [umoeller]
1515 *@@changed V0.9.16 (2001-12-08) [umoeller]: fixed empty entry field on tab key
1516 */
1517
1518MRESULT EXPENTRY ctl_fnwpObjectHotkeyEntryField(HWND hwndEdit, ULONG msg, MPARAM mp1, MPARAM mp2)
1519{
1520 MRESULT mrc = (MPARAM)FALSE; // WM_CHAR not-processed flag
1521
1522 // get object window data; this was stored in QWL_USER by
1523 // WM_INITDLG of obj_fnwpSettingsObjDetails
1524 // PXFOBJWINDATA pWinData = (PXFOBJWINDATA)WinQueryWindowULong(hwndEdit, QWL_USER);
1525 PFNWP pfnwpOrig = (PFNWP)WinQueryWindowULong(hwndEdit, QWL_USER);
1526
1527 switch (msg)
1528 {
1529 case WM_CHAR:
1530 {
1531 /*
1532 #define KC_CHAR 0x0001
1533 #define KC_VIRTUALKEY 0x0002
1534 #define KC_SCANCODE 0x0004
1535 #define KC_SHIFT 0x0008
1536 #define KC_CTRL 0x0010
1537 #define KC_ALT 0x0020
1538 #define KC_KEYUP 0x0040
1539 #define KC_PREVDOWN 0x0080
1540 #define KC_LONEKEY 0x0100
1541 #define KC_DEADKEY 0x0200
1542 #define KC_COMPOSITE 0x0400
1543 #define KC_INVALIDCOMP 0x0800
1544 */
1545
1546 /*
1547 Examples: usFlags usKeyCode
1548 F3 02 22
1549 Ctrl+F4 12 23
1550 Ctrl+Shift+F4 1a 23
1551 Ctrl 12 0a
1552 Alt 22 0b
1553 Shift 0a 09
1554 */
1555
1556 // USHORT usCommand;
1557 // USHORT usKeyCode;
1558 USHORT usFlags = SHORT1FROMMP(mp1);
1559 // UCHAR ucRepeat = CHAR3FROMMP(mp1);
1560 UCHAR ucScanCode = CHAR4FROMMP(mp1);
1561 USHORT usch = SHORT1FROMMP(mp2);
1562 USHORT usvk = SHORT2FROMMP(mp2);
1563
1564 if (
1565 // process only key-down messages
1566 ((usFlags & KC_KEYUP) == 0)
1567 // check flags:
1568 // filter out those ugly composite key (accents etc.)
1569 && ((usFlags & (KC_DEADKEY | KC_COMPOSITE | KC_INVALIDCOMP)) == 0)
1570 )
1571 {
1572 HOTKEYNOTIFY hkn;
1573 ULONG flReturned;
1574 HWND hwndOwner = WinQueryWindow(hwndEdit, QW_OWNER);
1575
1576 usFlags &= (KC_VIRTUALKEY | KC_CTRL | KC_ALT | KC_SHIFT);
1577
1578 hkn.usFlags = usFlags;
1579 hkn.usvk = usvk;
1580 hkn.usch = usch;
1581 hkn.ucScanCode = ucScanCode;
1582
1583 if (usFlags & KC_VIRTUALKEY)
1584 hkn.usKeyCode = usvk;
1585 else
1586 hkn.usKeyCode = usch;
1587
1588 hkn.szDescription[0] = 0;
1589
1590 flReturned = (ULONG)WinSendMsg(hwndOwner,
1591 WM_CONTROL,
1592 MPFROM2SHORT(WinQueryWindowUShort(hwndEdit,
1593 QWS_ID),
1594 EN_HOTKEY), // new notification code
1595 (MPARAM)&hkn);
1596 if (flReturned & HEFL_SETTEXT)
1597 WinSetWindowText(hwndEdit, hkn.szDescription);
1598 else if (flReturned & HEFL_FORWARD2OWNER)
1599 WinPostMsg(hwndOwner,
1600 WM_CHAR,
1601 mp1, mp2);
1602 else
1603 // fixed V0.9.16 (2001-12-06) [umoeller]:
1604 // do not clear the entry field if we had HEFL_FORWARD2OWNER
1605 WinSetWindowText(hwndEdit, "");
1606
1607 mrc = (MPARAM)TRUE; // WM_CHAR processed flag;
1608 }
1609 }
1610 break;
1611
1612 default:
1613 mrc = pfnwpOrig(hwndEdit, msg, mp1, mp2);
1614 }
1615 return mrc;
1616}
1617
1618/*
1619 *@@ ctlMakeHotkeyEntryField:
1620 * this turns any entry field into a "hotkey entry field"
1621 * by subclassing it with ctl_fnwpObjectHotkeyEntryField.
1622 * See remarks there for details.
1623 *
1624 * Returns FALSE upon errors, TRUE otherwise.
1625 *
1626 *@@added V0.9.1 (99-12-19) [umoeller]
1627 */
1628
1629BOOL ctlMakeHotkeyEntryField(HWND hwndHotkeyEntryField)
1630{
1631 PFNWP pfnwpOrig;
1632 if (pfnwpOrig = WinSubclassWindow(hwndHotkeyEntryField,
1633 ctl_fnwpObjectHotkeyEntryField))
1634 {
1635 WinSetWindowPtr(hwndHotkeyEntryField, QWL_USER, (PVOID)pfnwpOrig);
1636 return TRUE;
1637 }
1638
1639 return FALSE;
1640}
1641
1642/* ******************************************************************
1643 *
1644 * Color rectangle
1645 *
1646 ********************************************************************/
1647
1648STATIC PFNWP G_pfnwpColorRectOrigStatic = NULL;
1649
1650/*
1651 *@@ ctl_fnwpSubclassedColorRect:
1652 * window procedure for subclassed static frames representing
1653 * a color.
1654 *
1655 * The control simply paints itself as a rectangle with the
1656 * color specified in its PP_BACKGROUNDCOLOR presentation
1657 * parameter. If a window text is set for the control, it is
1658 * painted with the PP_FOREGROUNDCOLOR color.
1659 *
1660 * If the user drags a color onto the control, it notifies
1661 * its owner with the WM_CONTROL message and the EN_CHANGE
1662 * notification code, as with any entry field (since the
1663 * static control knows no notifications, we use that code
1664 * instead).
1665 *
1666 *@@added V0.9.16 (2002-01-05) [umoeller]
1667 *@@changed V0.9.19 (2002-06-02) [umoeller]: moved this here from w_pulse.c
1668 */
1669
1670STATIC MRESULT EXPENTRY ctl_fnwpSubclassedColorRect(HWND hwndStatic, ULONG msg, MPARAM mp1, MPARAM mp2)
1671{
1672 MRESULT mrc = 0;
1673
1674 switch (msg)
1675 {
1676 case WM_PAINT:
1677 {
1678 LONG lColor;
1679 RECTL rclPaint;
1680 PSZ pszText;
1681
1682 HPS hps = WinBeginPaint(hwndStatic,
1683 NULLHANDLE, // HPS
1684 NULL); // PRECTL
1685 gpihSwitchToRGB(hps);
1686 WinQueryWindowRect(hwndStatic,
1687 &rclPaint); // exclusive
1688 lColor = winhQueryPresColor(hwndStatic,
1689 PP_BACKGROUNDCOLOR,
1690 FALSE, // no inherit
1691 SYSCLR_DIALOGBACKGROUND);
1692
1693 // make rect inclusive
1694 rclPaint.xRight--;
1695 rclPaint.yTop--;
1696
1697 // draw interior
1698 GpiSetColor(hps, lColor);
1699 gpihBox(hps,
1700 DRO_FILL,
1701 &rclPaint);
1702
1703 // draw frame
1704 GpiSetColor(hps, RGBCOL_BLACK);
1705 gpihBox(hps,
1706 DRO_OUTLINE,
1707 &rclPaint);
1708
1709 if (pszText = winhQueryWindowText(hwndStatic))
1710 {
1711 GpiSetColor(hps,
1712 winhQueryPresColor(hwndStatic,
1713 PP_FOREGROUNDCOLOR,
1714 FALSE,
1715 -1));
1716 WinDrawText(hps,
1717 strlen(pszText),
1718 pszText,
1719 &rclPaint,
1720 0,
1721 0,
1722 DT_CENTER | DT_VCENTER | DT_TEXTATTRS);
1723
1724 free(pszText);
1725 }
1726
1727 WinEndPaint(hps);
1728 }
1729 break;
1730
1731 case WM_PRESPARAMCHANGED:
1732 switch ((ULONG)mp1)
1733 {
1734 case PP_BACKGROUNDCOLOR:
1735 WinInvalidateRect(hwndStatic,
1736 NULL,
1737 FALSE);
1738 // notify owner; since the static control
1739 // doesn't send any notifications, we
1740 // use EN_CHANGED
1741 ctlSendWmControl(hwndStatic,
1742 EN_CHANGE,
1743 (MPARAM)hwndStatic);
1744 break;
1745 }
1746 break;
1747
1748 default:
1749 mrc = G_pfnwpColorRectOrigStatic(hwndStatic, msg, mp1, mp2);
1750 break;
1751 }
1752
1753 return mrc;
1754}
1755
1756/*
1757 *@@ ctlMakeColorRect:
1758 * turns a static rectangle into a color rectangle.
1759 *
1760 *@@added V0.9.19 (2002-06-02) [umoeller]
1761 */
1762
1763BOOL ctlMakeColorRect(HWND hwndStatic)
1764{
1765 PFNWP pfnwp;
1766
1767 if (pfnwp = WinSubclassWindow(hwndStatic,
1768 ctl_fnwpSubclassedColorRect))
1769 {
1770 G_pfnwpColorRectOrigStatic = pfnwp;
1771 return TRUE;
1772 }
1773
1774 return FALSE;
1775}
1776
1777
Note: See TracBrowser for help on using the repository browser.