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

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

Dialog mgr.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 44.3 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-2000 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
92#define INCL_GPIPRIMITIVES
93#define INCL_GPILOGCOLORTABLE
94#define INCL_GPILCIDS
95#define INCL_GPIPATHS
96#define INCL_GPIREGIONS
97#define INCL_GPIBITMAPS // added V0.9.1 (2000-01-04) [umoeller]: needed for EMX headers
98#include <os2.h>
99
100#include <stdlib.h>
101#include <stdio.h>
102#include <string.h>
103#include <setjmp.h> // needed for except.h
104#include <assert.h> // needed for except.h
105
106#include "setup.h" // code generation and debugging options
107
108#include "helpers\cnrh.h"
109#include "helpers\except.h" // exception handling
110#include "helpers\gpih.h"
111#include "helpers\linklist.h"
112#include "helpers\winh.h"
113
114#include "helpers\comctl.h"
115
116#pragma hdrstop
117
118/* ******************************************************************
119 *
120 * Global variables
121 *
122 ********************************************************************/
123
124/*
125 *@@category: Helpers\PM helpers\Window classes\Menu buttons
126 * See comctl.c and ctlMakeMenuButton.
127 */
128
129/* ******************************************************************
130 *
131 * "Menu button" control
132 *
133 ********************************************************************/
134
135/*
136 *@@ MENUBUTTONDATA:
137 * internal data for "menu button"
138 *
139 *@@added V0.9.0 [umoeller]
140 */
141
142typedef struct _MENUBUTTONDATA
143{
144 PFNWP pfnwpButtonOriginal;
145 HMODULE hmodMenu;
146 ULONG idMenu;
147 BOOL fMouseCaptured, // TRUE if WinSetCapture
148 fMouseButton1Down, // TRUE in between WM_BUTTON1DOWN and WM_BUTTON1UP
149 fButtonSunk; // toggle state of the button
150 HWND hwndMenu;
151} MENUBUTTONDATA, *PMENUBUTTONDATA;
152
153/*
154 *@@ ctlDisplayButtonMenu:
155 * displays the specified menu above the button.
156 *
157 *@@added V0.9.7 (2000-11-29) [umoeller]
158 */
159
160VOID ctlDisplayButtonMenu(HWND hwndButton,
161 HWND hwndMenu)
162{
163 SWP swpButton;
164 POINTL ptlMenu;
165 WinQueryWindowPos(hwndButton, &swpButton);
166 ptlMenu.x = swpButton.x;
167 ptlMenu.y = swpButton.y;
168
169 // ptlMenu now has button coordinates
170 // relative to the button's parent;
171 // convert this to screen coordinates:
172 WinMapWindowPoints(WinQueryWindow(hwndButton, QW_PARENT),
173 HWND_DESKTOP,
174 &ptlMenu,
175 1);
176
177 // now show the menu on top of the button
178 WinPopupMenu(HWND_DESKTOP, // menu parent
179 hwndButton, // owner
180 hwndMenu,
181 (SHORT)(ptlMenu.x),
182 (SHORT)(ptlMenu.y + swpButton.cy - 1),
183 0, // ID
184 PU_NONE
185 | PU_MOUSEBUTTON1
186 | PU_KEYBOARD
187 | PU_HCONSTRAIN
188 | PU_VCONSTRAIN);
189}
190
191/*
192 *@@ ctl_fnwpSubclassedMenuButton:
193 * subclassed window proc for "menu button".
194 * See ctlMakeMenuButton for details.
195 *
196 *@@added V0.9.0 [umoeller]
197 *@@changed V0.9.2 (2000-02-28) [umoeller]: menu was displayed even if button was disabled; fixed
198 */
199
200MRESULT EXPENTRY ctl_fnwpSubclassedMenuButton(HWND hwndButton, ULONG msg, MPARAM mp1, MPARAM mp2)
201{
202 MRESULT mrc = 0;
203 PMENUBUTTONDATA pmbd = (PMENUBUTTONDATA)WinQueryWindowULong(hwndButton, QWL_USER);
204
205 switch (msg)
206 {
207 /*
208 * WM_BUTTON1DOWN:
209 * WM_BUTTON1UP:
210 * these show/hide the menu.
211 *
212 * Showing the menu follows these steps:
213 * a) first WM_BUTTON1DOWN hilites the button;
214 * b) first WM_BUTTON1UP shows the menu.
215 *
216 * When the button is pressed again, the open
217 * menu loses focus, which results in WM_MENUEND
218 * and destroys the window automatically.
219 */
220
221 case WM_BUTTON1DOWN:
222 case WM_BUTTON1DBLCLK:
223 // only do this if the button is enabled
224 // V0.9.2 (2000-02-28) [umoeller]
225 if (WinIsWindowEnabled(hwndButton))
226 {
227 // _Pmpf(("WM_BUTTON1DOWN"));
228 // since we're not passing the message
229 // to WinDefWndProc, we need to give
230 // ourselves the focus
231 WinSetFocus(HWND_DESKTOP, hwndButton);
232
233 if (!pmbd->fMouseCaptured)
234 {
235 // capture mouse events while the
236 // mouse button is down
237 WinSetCapture(HWND_DESKTOP, hwndButton);
238 pmbd->fMouseCaptured = TRUE;
239 }
240
241 pmbd->fMouseButton1Down = TRUE;
242
243 if (!pmbd->fButtonSunk)
244 {
245 // button not hilited yet (first click):
246 // do it now
247 // _Pmpf((" Sinking menu WM_BUTTON1DOWN "));
248 pmbd->fButtonSunk = TRUE;
249 WinSendMsg(hwndButton,
250 BM_SETHILITE,
251 (MPARAM)TRUE,
252 (MPARAM)0);
253 }
254
255 // else: the menu has just been destroyed
256 // (WM_MENUEND below)
257 }
258
259 mrc = (MPARAM)TRUE; // message processed
260 break;
261
262 /*
263 * WM_BUTTON1UP:
264 *
265 */
266
267 case WM_BUTTON1UP:
268 // only do this if the button is enabled
269 // V0.9.2 (2000-02-28) [umoeller]
270 if (WinIsWindowEnabled(hwndButton))
271 {
272 // _Pmpf(("WM_BUTTON1UP sunk %d hwndMenu 0x%lX", pmbd->fButtonSunk, pmbd->hwndMenu));
273
274 // un-capture the mouse first
275 if (pmbd->fMouseCaptured)
276 {
277 WinSetCapture(HWND_DESKTOP, NULLHANDLE);
278 pmbd->fMouseCaptured = FALSE;
279 }
280
281 pmbd->fMouseButton1Down = FALSE;
282
283 if ( (pmbd->fButtonSunk) // set by WM_BUTTON1DOWN above
284 && (pmbd->hwndMenu) // menu currently showing
285 )
286 {
287 // button currently depressed:
288 // un-hilite button
289 // _Pmpf((" Unsinking menu WM_BUTTON1UP 1"));
290 pmbd->fButtonSunk = FALSE;
291 WinSendMsg(hwndButton,
292 BM_SETHILITE,
293 (MPARAM)FALSE,
294 (MPARAM)0);
295 }
296 else
297 {
298 // first button up:
299 // show menu
300
301 if (pmbd->idMenu)
302 {
303 // _Pmpf((" Loading menu hmod %lX id %lX", pmbd->hmodMenu, pmbd->idMenu));
304 // menu specified: load from
305 // specified resources
306 pmbd->hwndMenu = WinLoadMenu(hwndButton,
307 pmbd->hmodMenu,
308 pmbd->idMenu);
309 }
310 else
311 {
312 HWND hwndOwner = WinQueryWindow(hwndButton, QW_OWNER);
313 // _Pmpf((" Querying menu WM_COMMAND from owner 0x%lX", hwndOwner));
314 // send WM_COMMAND to owner
315 pmbd->hwndMenu
316 = (HWND)WinSendMsg(hwndOwner,
317 WM_COMMAND,
318 (MPARAM)(ULONG)WinQueryWindowUShort(hwndButton,
319 QWS_ID),
320 (MPARAM)0);
321 }
322
323 // _Pmpf((" Loaded menu, hwnd: 0x%lX", pmbd->hwndMenu));
324
325 if (pmbd->hwndMenu)
326 {
327 // menu successfully loaded:
328 // find out where to put it
329 ctlDisplayButtonMenu(hwndButton,
330 pmbd->hwndMenu);
331 } // end if (pmbd->hwndMenu)
332 else
333 {
334 // menu not loaded:
335 // _Pmpf((" Unsinking menu WM_BUTTON1UP 2"));
336 pmbd->fButtonSunk = FALSE;
337 WinSendMsg(hwndButton,
338 BM_SETHILITE,
339 (MPARAM)FALSE,
340 (MPARAM)0);
341 }
342 }
343 }
344
345 mrc = (MPARAM)TRUE; // message processed
346 break;
347
348 /*
349 * WM_BUTTON1CLICK:
350 * swallow this
351 */
352
353 case WM_BUTTON1CLICK:
354 mrc = (MPARAM)TRUE;
355 break;
356
357 /*
358 * WM_SETFOCUS:
359 * swallow this, the button keeps painting
360 * itself otherwise
361 */
362
363 case WM_SETFOCUS:
364 break;
365
366 /*
367 * WM_MENUEND:
368 * menu is destroyed; we get this
369 * because we're the owner
370 */
371
372 case WM_MENUEND:
373 {
374 BOOL fUnHilite = TRUE;
375 // _Pmpf(("WM_MENUEND"));
376 // At this point, the menu has been
377 // destroyed already.
378 // Since WM_BUTTON1UP handles the
379 // default case that the user presses
380 // the menu button a second time while
381 // the menu is open, we only need
382 // to handle the case that the user
383 // c) presses some key on the menu (ESC, menu selection) or
384 // a) selects a menu item (which
385 // dismisses the menu) or
386 // b) clicks anywhere else.
387
388 // Case a) if mouse button 1 is not currently down
389 if ((WinGetKeyState(HWND_DESKTOP, VK_BUTTON1) & 0x8000) == 0)
390 // button 1 _not_ down:
391 // must be keyboard
392 fUnHilite = TRUE;
393 else
394 // button 1 _is_ down:
395 // query window under mouse pointer
396 ;
397
398 if (fUnHilite)
399 {
400 // _Pmpf((" Unsinking menu WM_MENUEND"));
401 pmbd->fButtonSunk = FALSE;
402 WinSendMsg(hwndButton,
403 BM_SETHILITE,
404 (MPARAM)FALSE,
405 (MPARAM)0);
406 }
407 pmbd->hwndMenu = NULLHANDLE;
408 break; }
409
410 /*
411 * WM_COMMAND:
412 * this must be from the menu, so
413 * forward this to the button's owner
414 */
415
416 case WM_COMMAND:
417 WinPostMsg(WinQueryWindow(hwndButton, QW_OWNER),
418 msg,
419 mp1,
420 mp2);
421 break;
422
423 /*
424 * WM_DESTROY:
425 * clean up allocated data
426 */
427
428 case WM_DESTROY:
429 mrc = (*(pmbd->pfnwpButtonOriginal))(hwndButton, msg, mp1, mp2);
430 free(pmbd);
431 break;
432
433 default:
434 mrc = (*(pmbd->pfnwpButtonOriginal))(hwndButton, msg, mp1, mp2);
435 }
436
437 return (mrc);
438}
439
440/*
441 *@@ ctlMakeMenuButton:
442 * this turns the specified button into a "menu button",
443 * which shows a popup menu when the button is depressed.
444 * This is done by subclassing the menu button with
445 * ctl_fnwpSubclassedMenuButton.
446 *
447 * Simply call this function upon any button, and it'll
448 * turn in to a menu button.
449 *
450 * When the user presses the button, the specified menu
451 * is loaded from the resources. The button will then
452 * be set as the owner, but it will forward all WM_COMMAND
453 * messages from the menu to its own owner (probably your
454 * dialog), so you can handle WM_COMMAND messages just as
455 * if the menu was owned by your dialog.
456 *
457 * Alternatively, if you don't want to load a menu from
458 * the resources, you can specify idMenu == 0. In that case,
459 * when the menu button is pressed, it sends (!) a WM_COMMAND
460 * message to its owner with its ID in mp1 (as usual). The
461 * difference is that the return value from that message will
462 * be interpreted as a menu handle (HWND) to use for the button
463 * menu.
464 *
465 * The subclassed button also handles menu destruction etc.
466 * by itself.
467 *
468 *@@added V0.9.0 [umoeller]
469 */
470
471BOOL ctlMakeMenuButton(HWND hwndButton, // in: button to subclass
472 HMODULE hmodMenu, // in: resource module (can be NULLHANDLE for
473 // current EXE)
474 ULONG idMenu) // in: resource menu ID (or 0)
475{
476 BOOL brc = FALSE;
477 PMENUBUTTONDATA pmbd = (PMENUBUTTONDATA)malloc(sizeof(MENUBUTTONDATA));
478 if (pmbd)
479 {
480 memset(pmbd, 0, sizeof(MENUBUTTONDATA));
481 pmbd->pfnwpButtonOriginal = WinSubclassWindow(hwndButton,
482 ctl_fnwpSubclassedMenuButton);
483 if (pmbd->pfnwpButtonOriginal)
484 {
485 pmbd->hmodMenu = hmodMenu;
486 pmbd->idMenu = idMenu;
487 WinSetWindowULong(hwndButton, QWL_USER, (ULONG)pmbd);
488 brc = TRUE;
489 }
490 else
491 free(pmbd);
492 }
493 return (brc);
494}
495
496/*
497 *@@category: Helpers\PM helpers\Window classes\Static bitmaps
498 * See comctl.c and ctl_fnwpBitmapStatic.
499 */
500
501/* ******************************************************************
502 *
503 * Subclassed Static Bitmap Control
504 *
505 ********************************************************************/
506
507/*
508 *@@ ctl_fnwpBitmapStatic:
509 * subclassed wnd proc for both static controls which
510 * should display icons and stretched bitmaps.
511 *
512 * This is not a stand-alone window procedure, but must only
513 * be used with static controls subclassed by the functions
514 * listed below.
515 *
516 * This is now (V0.9.0) used in two contexts:
517 * -- when subclassed from ctlPrepareStaticIcon,
518 * this displays transparent icons properly
519 * (for regular icons or icon animations);
520 * -- when subclassed from ctlPrepareStretchedBitmap,
521 * this displays a stretched bitmap.
522 *
523 * The behavior depends on ANIMATIONDATA.ulFlags,
524 * which is set by both of these functions (either to
525 * ANF_ICON or ANF_BITMAP).
526 *
527 * Only one msg is of interest to the "user" application
528 * of this control, which is SM_SETHANDLE,
529 * the normal message sent to a static icon control to change
530 * its icon or bitmap.
531 * The new handle (HPOINTER or HBITMAP) is
532 * in mp1; mp2 must be null.
533 *
534 * Depending on ANIMATIONDATA.ulFlags, we do the following:
535 *
536 * -- ANF_ICON: Unfortunately, the
537 * standard static control paints garbage if
538 * the icons contain transparent areas, and the
539 * display often flickers too.
540 * We improve this by creating one bitmap from
541 * the icon that we were given which we can then
542 * simply copy to the screen in one step in
543 * WM_PAINT.
544 *
545 * -- ANF_BITMAP: we create a bitmap which is
546 * stretched to the size of the static control,
547 * using gpihStretchBitmap.
548 *
549 * In any case, a new bitmap is produced internally and
550 * stored so that it can easily be painted when WM_PAINT
551 * comes in. The source icon/bitmap can therefore
552 * be safely destroyed by the caller after sending
553 * SM_SETHANDLE. This bitmap is automatically deleted when
554 * the window is destroyed.
555 *
556 *@@changed V0.9.0 [umoeller]: function renamed
557 *@@changed V0.9.0 [umoeller]: added support for stretched bitmaps
558 *@@changed V0.9.0 [umoeller]: added halftoned display for WS_DISABLED
559 *@@changed V0.9.0 [umoeller]: exported gpihIcon2Bitmap function to gpih.c
560 *@@changed V0.9.0 [umoeller]: fixed paint errors when SM_SETHANDLE had NULL argument in mp1
561 */
562
563MRESULT EXPENTRY ctl_fnwpBitmapStatic(HWND hwndStatic, ULONG msg, MPARAM mp1, MPARAM mp2)
564{
565 PANIMATIONDATA pa = (PANIMATIONDATA)WinQueryWindowULong(hwndStatic, QWL_USER);
566 // animation data which was stored in window words
567 PFNWP OldStaticProc = NULL;
568 MRESULT mrc = NULL;
569
570 if (pa)
571 {
572 OldStaticProc = pa->OldStaticProc;
573
574 switch(msg)
575 {
576 /*
577 * WM_TIMER:
578 * this timer is started by the ctl* funcs
579 * below. Proceed to the next animation step.
580 *
581 * Note: this timer is only used with
582 * ANF_ICON, and even then, it
583 * is not necessarily running.
584 */
585
586 case WM_TIMER:
587 {
588 pa->usAniCurrent++;
589 if (pa->usAniCurrent >= pa->usAniCount)
590 pa->usAniCurrent = 0;
591
592 WinSendMsg(hwndStatic,
593 SM_SETHANDLE,
594 (MPARAM)pa->ahptrAniIcons[pa->usAniCurrent],
595 (MPARAM)NULL);
596 break; }
597
598 /*
599 * SM_SETHANDLE:
600 *
601 */
602
603 case SM_SETHANDLE:
604 {
605 HDC hdcMem;
606 HPS hpsMem;
607 SIZEL szlPage;
608
609 LONG lBkgndColor
610 = winhQueryPresColor(WinQueryWindow(hwndStatic, QW_PARENT),
611 PP_BACKGROUNDCOLOR,
612 FALSE,
613 SYSCLR_DIALOGBACKGROUND);
614
615 HPS hps = WinGetPS(hwndStatic);
616
617 // if we have created bitmaps previously, delete them
618 if (pa->hbm)
619 {
620 GpiDeleteBitmap(pa->hbm);
621 pa->hbm = NULLHANDLE;
622 }
623 if (pa->hbmHalftoned)
624 {
625 GpiDeleteBitmap(pa->hbmHalftoned);
626 pa->hbmHalftoned = NULLHANDLE;
627 }
628
629 // switch to RGB mode
630 gpihSwitchToRGB(hps);
631
632 // create a memory PS
633 szlPage.cx = pa->rclIcon.xRight - pa->rclIcon.xLeft;
634 szlPage.cy = pa->rclIcon.yTop - pa->rclIcon.yBottom;
635 if (gpihCreateMemPS(pa->hab,
636 &szlPage,
637 &hdcMem,
638 &hpsMem))
639 {
640 // switch the memory PS to RGB mode too
641 gpihSwitchToRGB(hpsMem);
642
643 // create a suitable bitmap w/ the size of the
644 // static control
645 if ((pa->hbm = gpihCreateBitmap(hpsMem,
646 szlPage.cx,
647 szlPage.cy)))
648 {
649 // associate the bit map with the memory PS
650 if (GpiSetBitmap(hpsMem, pa->hbm) != HBM_ERROR)
651 {
652 // fill the bitmap with the current static
653 // background color
654 POINTL ptl = {0, 0};
655 GpiMove(hpsMem, &ptl);
656 ptl.x = pa->rclIcon.xRight;
657 ptl.y = pa->rclIcon.yTop;
658 GpiSetColor(hpsMem,
659 lBkgndColor);
660 GpiBox(hpsMem,
661 DRO_FILL, // interior only
662 &ptl,
663 0, 0); // no corner rounding
664
665 /*
666 * ANF_ICON:
667 *
668 */
669
670 if (pa->ulFlags & ANF_ICON)
671 {
672 // store new icon in our own structure
673 pa->hptr = (HPOINTER)mp1;
674
675 if (pa->hptr)
676 // paint icon into bitmap
677 gpihIcon2Bitmap(hpsMem,
678 pa->hptr,
679 lBkgndColor,
680 WinQuerySysValue(HWND_DESKTOP, SV_CXICON));
681
682 } // end if (pa->ulFlags & ANF_ICON)
683
684 /*
685 * ANF_BITMAP:
686 *
687 */
688
689 else if (pa->ulFlags & ANF_BITMAP)
690 {
691 // store passed bitmap
692 HBITMAP hbmSource = (HBITMAP)mp1;
693
694 if (hbmSource)
695 gpihStretchBitmap(hpsMem, // target
696 hbmSource, // source
697 NULL, // use size of bitmap
698 &pa->rclIcon,
699 ((pa->ulFlags & ANF_PROPORTIONAL)
700 != 0));
701
702 } // end if (pa->ulFlags & ANF_BITMAP)
703
704 } // end if (GpiSetBitmap(...
705 } // if (pa->hbm = gpihCreateBitmap(hpsMem, &(pa->rclIcon)))
706
707 // in any case, clean up now
708 GpiDestroyPS(hpsMem);
709 DevCloseDC(hdcMem);
710 } // end if (gpihCreateMemPS(hab, &hdcMem, &hpsMem))
711
712 WinReleasePS(hps);
713
714 // enforce WM_PAINT
715 WinInvalidateRect(hwndStatic, NULL, FALSE);
716
717 break; }
718
719 /*
720 * WM_PAINT:
721 * "normal" paint; this only arrives here if the
722 * icon needs to be repainted. We simply paint
723 * the bitmap we created in WM_SETHANDLE.
724 */
725
726 case WM_PAINT:
727 {
728 RECTL rcl;
729 HPS hps = WinBeginPaint(hwndStatic, NULLHANDLE, &rcl);
730 POINTL ptl = {0, 0};
731
732 if (pa->hbm)
733 {
734 if (WinQueryWindowULong(hwndStatic, QWL_STYLE)
735 & WS_DISABLED)
736 {
737 // if the control is currently disabled,
738 // draw in half-toned (grayed)
739
740 LONG lBkgndColor = winhQueryPresColor(
741 WinQueryWindow(hwndStatic, QW_PARENT),
742 PP_BACKGROUNDCOLOR,
743 FALSE,
744 SYSCLR_DIALOGBACKGROUND);
745
746 // 1) check if we created the half-tone
747 // bitmap already (WinDrawBitmap doesn't
748 // work with half-toned color bitmaps, so
749 // here's yet another thing we have to do
750 // all alone)
751 if (pa->hbmHalftoned == NULLHANDLE)
752 pa->hbmHalftoned = gpihCreateHalftonedBitmap(pa->hab,
753 pa->hbm,
754 lBkgndColor);
755
756 if (pa->hbmHalftoned)
757 WinDrawBitmap(hps,
758 pa->hbmHalftoned,
759 NULL, // whole bmp
760 &ptl, // lower left corner
761 0, 0, // no colors
762 DBM_NORMAL);
763 }
764 else
765 {
766 // not disabled: draw regular bitmap
767 // we created previously
768 // draw the bitmap that we created previously
769 WinDrawBitmap(hps,
770 pa->hbm,
771 NULL, // whole bmp
772 &ptl, // lower left corner
773 0, 0, // no colors
774 DBM_NORMAL);
775 }
776 }
777
778 WinEndPaint(hps);
779 break; }
780
781 /*
782 * WM_DESTROY:
783 * clean up.
784 */
785
786 case WM_DESTROY:
787 {
788 // undo subclassing in case more WM_TIMERs come in
789 WinSubclassWindow(hwndStatic, OldStaticProc);
790
791 if (pa->hbm)
792 GpiDeleteBitmap(pa->hbm);
793 if (pa->hbmHalftoned)
794 GpiDeleteBitmap(pa->hbm);
795
796 // clean up ANIMATIONDATA struct
797 WinSetWindowULong(hwndStatic, QWL_USER, (ULONG)NULL);
798 free(pa);
799
800 mrc = OldStaticProc(hwndStatic, msg, mp1, mp2);
801 break; }
802
803 default:
804 mrc = OldStaticProc(hwndStatic, msg, mp1, mp2);
805 }
806 }
807 return (mrc);
808}
809
810/* ******************************************************************
811 *
812 * Icon animation
813 *
814 ********************************************************************/
815
816/*
817 *@@ ctlPrepareStaticIcon:
818 * turns a static control into one which properly
819 * displays transparent icons when given a
820 * SM_SETHANDLE msg.
821 * This is done by subclassing the static control
822 * with ctl_fnwpBitmapStatic.
823 *
824 * This function gets called automatically by
825 * ctlPrepareAnimation, so you need not call it
826 * independently for animations. See the notes there
827 * how this can be done.
828 *
829 * You can, however, call this function if you
830 * have some static control with transparent icons
831 * which is not animated but changes sometimes
832 * (because you have the same repaint problems there).
833 *
834 * This func does _not_ start an animation yet.
835 *
836 * To change the icon being displayed, send the control
837 * a SM_SETHANDLE msg with the icon handle in (HPOINTER)mp1.
838 *
839 * Returns a PANIMATIONDATA if subclassing succeeded or
840 * NULL upon errors. If you only call this function, you
841 * do not need this structure; it is needed by
842 * ctlPrepareAnimation though.
843 *
844 * The subclassed static icon func above automatically
845 * cleans up resources, so you don't have to worry about
846 * that either.
847 *
848 *@@changed V0.9.0 [umoeller]: adjusted for ctl_fnwpBitmapStatic changes
849 */
850
851PANIMATIONDATA ctlPrepareStaticIcon(HWND hwndStatic,
852 USHORT usAnimCount) // needed for allocating extra memory,
853 // this must be at least 1
854{
855 PANIMATIONDATA pa = NULL;
856 PFNWP OldStaticProc = WinSubclassWindow(hwndStatic, ctl_fnwpBitmapStatic);
857 if (OldStaticProc)
858 {
859 // create the ANIMATIONDATA structure,
860 // initialize some fields,
861 // and store it in QWL_USER of the static control
862 ULONG cbStruct = sizeof(ANIMATIONDATA)
863 + ((usAnimCount-1) * sizeof(HPOINTER));
864 pa = (PANIMATIONDATA)malloc(cbStruct);
865 memset(pa, 0, cbStruct);
866
867 // switch static to icon mode
868 pa->ulFlags = ANF_ICON;
869 pa->OldStaticProc = OldStaticProc;
870 WinQueryWindowRect(hwndStatic, &(pa->rclIcon));
871 pa->hab = WinQueryAnchorBlock(hwndStatic);
872
873 WinSetWindowULong(hwndStatic, QWL_USER, (ULONG)pa);
874 }
875 return (pa);
876}
877
878/*
879 *@@ ctlPrepareAnimation:
880 * this function turns a regular static icon
881 * control an animated one. This is the one-shot
882 * function for animating static icon controls.
883 *
884 * It calls ctlPrepareStaticIcon first, then uses
885 * the passed parameters to prepare an animation:
886 *
887 * pahptr must point to an array of HPOINTERs
888 * which must contain usAnimCount icon handles.
889 * If (fStartAnimation == TRUE), the animation
890 * is started already. Otherwise you'll have
891 * to call ctlStartAnimation yourself.
892 *
893 * To create an animated icon, simply create a static
894 * control with the dialog editor (can be any static,
895 * e.g. a text control). Then do the following in your code:
896 *
897 * 1) load the dlg box template (using WinLoadDlg);
898 *
899 * 2) load all the icons for the animation in an array of
900 * HPOINTERs (check xsdLoadAnimation in shutdown.c for an
901 * example);
902 *
903 * 3) call ctlPrepareAnimation, e.g.:
904 + ctlPrepareAnimation(WinWindowFromID(hwndDlg, ID_ICON), // get icon hwnd
905 + 8, // no. of icons for the anim
906 + &ahptr[0], // ptr to first icon in the array
907 + 150, // delay
908 + TRUE); // start animation now
909 *
910 * 4) call WinProcessDlg(hwndDlg);
911 *
912 * 5) stop the animation, e.g.:
913 + ctlStopAnimation(WinWindowFromID(hwndDlg, ID_ICON));
914 *
915 * 6) destroy the dlg window;
916 *
917 * 7) free all the HPOINTERS loaded above (check xsdFreeAnimation
918 * in shutdown.c for an example).
919 *
920 * When the icon control is destroyed, the
921 * subclassed window proc (ctl_fnwpBitmapStatic)
922 * automatically cleans up resources. However,
923 * the icons in *pahptr are not freed.
924 */
925
926BOOL ctlPrepareAnimation(HWND hwndStatic, // icon hwnd
927 USHORT usAnimCount, // no. of anim steps
928 HPOINTER *pahptr, // array of HPOINTERs
929 ULONG ulDelay, // delay per anim step (in ms)
930 BOOL fStartAnimation) // TRUE: start animation now
931{
932 PANIMATIONDATA paNew = ctlPrepareStaticIcon(hwndStatic, usAnimCount);
933 if (paNew)
934 {
935 paNew->ulDelay = ulDelay;
936 // paNew->OldStaticProc already set
937 paNew->hptr = NULLHANDLE;
938 // paNew->rclIcon already set
939 paNew->usAniCurrent = 0;
940 paNew->usAniCount = usAnimCount;
941 memcpy(&(paNew->ahptrAniIcons), pahptr,
942 (usAnimCount * sizeof(HPOINTER)));
943
944 if (fStartAnimation) {
945 WinStartTimer(WinQueryAnchorBlock(hwndStatic), hwndStatic,
946 1, ulDelay);
947 WinPostMsg(hwndStatic, WM_TIMER, NULL, NULL);
948 }
949 }
950 return (paNew != NULL);
951}
952
953/*
954 *@@ ctlStartAnimation:
955 * starts an animation that not currently running. You
956 * must prepare the animation by calling ctlPrepareAnimation
957 * first.
958 */
959
960BOOL ctlStartAnimation(HWND hwndStatic)
961{
962 BOOL brc = FALSE;
963 PANIMATIONDATA pa = (PANIMATIONDATA)WinQueryWindowULong(hwndStatic, QWL_USER);
964 if (pa)
965 if (WinStartTimer(WinQueryAnchorBlock(hwndStatic), hwndStatic,
966 1, pa->ulDelay))
967 {
968 brc = TRUE;
969 WinPostMsg(hwndStatic, WM_TIMER, NULL, NULL);
970 }
971 return (brc);
972}
973
974/*
975 *@@ ctlStopAnimation:
976 * stops an animation that is currently running.
977 * This does not free any resources.
978 */
979
980BOOL ctlStopAnimation(HWND hwndStatic)
981{
982 return (WinStopTimer(WinQueryAnchorBlock(hwndStatic), hwndStatic, 1));
983}
984
985/*
986 *@@category: Helpers\PM helpers\Window classes\Stretched bitmaps
987 * See comctl.c and ctlPrepareStretchedBitmap.
988 */
989
990/* ******************************************************************
991 *
992 * Bitmap functions
993 *
994 ********************************************************************/
995
996/*
997 *@@ ctlPrepareStretchedBitmap:
998 * turns a static control into a bitmap control
999 * which stretches the bitmap to the size of the
1000 * control when given an SM_SETHANDLE msg.
1001 *
1002 * If (fPreserveProportions == TRUE), the control
1003 * will size the bitmap proportionally only. Otherwise,
1004 * it will always stretch the bitmap to the whole
1005 * size of the static control (possibly distorting
1006 * the proportions). See gpihStretchBitmap for
1007 * details.
1008 *
1009 * This function subclasses the static control
1010 * with ctl_fnwpBitmapStatic, setting the flags as
1011 * necessary. However, this does not set the bitmap
1012 * to display yet.
1013 * To have a bitmap displayed, send the control
1014 * a SM_SETHANDLE message with the bitmap to be
1015 * displayed in (HBITMAP)mp1 after having used this
1016 * function.
1017 *
1018 * ctl_fnwpBitmapStatic will then create a private
1019 * copy of that bitmap, stretched to its own size.
1020 * You can therefore delete the "source" bitmap
1021 * after sending SM_SETHANDLE.
1022 *
1023 * You can also send SM_SETHANDLE several times.
1024 * The control will then readjust itself and delete
1025 * its old bitmap.
1026 * But note that GpiWCBitBlt is invoked on that new
1027 * bitmap with every new message, so this is not
1028 * terribly fast.
1029 *
1030 * Also note that you should not resize the static
1031 * control after creation, because the bitmap will
1032 * _not_ be adjusted.
1033 *
1034 * This returns a PANIMATIONDATA if subclassing succeeded or
1035 * NULL upon errors. You do not need to save this structure;
1036 * it is cleaned up automatically when the control is destroyed.
1037 *
1038 *@@added V0.9.0 [umoeller]
1039 */
1040
1041PANIMATIONDATA ctlPrepareStretchedBitmap(HWND hwndStatic,
1042 BOOL fPreserveProportions)
1043{
1044 PANIMATIONDATA pa = NULL;
1045 PFNWP OldStaticProc = WinSubclassWindow(hwndStatic, ctl_fnwpBitmapStatic);
1046 if (OldStaticProc)
1047 {
1048 // create the ANIMATIONDATA structure,
1049 // initialize some fields,
1050 // and store it in QWL_USER of the static control
1051 ULONG cbStruct = sizeof(ANIMATIONDATA);
1052 pa = (PANIMATIONDATA)malloc(cbStruct);
1053 memset(pa, 0, cbStruct);
1054
1055 // switch static to bitmap mode
1056 pa->ulFlags = ANF_BITMAP;
1057 if (fPreserveProportions)
1058 pa->ulFlags |= ANF_PROPORTIONAL;
1059
1060 pa->OldStaticProc = OldStaticProc;
1061 WinQueryWindowRect(hwndStatic, &(pa->rclIcon));
1062 pa->hab = WinQueryAnchorBlock(hwndStatic);
1063
1064 WinSetWindowULong(hwndStatic, QWL_USER, (ULONG)pa);
1065 }
1066 return (pa);
1067}
1068
1069/*
1070 *@@category: Helpers\PM helpers\Window classes\Hotkey entry field
1071 * See comctl.c and ctl_fnwpObjectHotkeyEntryField.
1072 */
1073
1074/* ******************************************************************
1075 *
1076 * Hotkey entry field
1077 *
1078 ********************************************************************/
1079
1080/*
1081 *@@ ctl_fnwpHotkeyEntryField:
1082 * this is the window proc for a subclassed entry
1083 * field which displays a description of a keyboard
1084 * combination upon receiving WM_CHAR.
1085 *
1086 * Use ctlMakeHotkeyEntryField to turn any existing
1087 * entry field into such an "hotkey entry field".
1088 *
1089 * The entry field is deleted on every WM_CHAR that
1090 * comes in.
1091 *
1092 * For this to work, this window proc needs the
1093 * cooperation of its owner. Whenever a WM_CHAR
1094 * message is received by the entry field which
1095 * looks like a meaningful key or key combination
1096 * (some filtering is done by this func), then
1097 * the owner of the entry field receives a WM_CONTROL
1098 * message with the new EN_HOTKEY notification code
1099 * (see comctl.h for details).
1100 *
1101 * It is then the responsibility of the owner to
1102 * check whether the HOTKEYNOTIFY structure pointed
1103 * to by WM_CONTROL's mp2 is a meaningful keyboard
1104 * combination.
1105 *
1106 * If not, the owner must return FALSE, and the
1107 * entry field's contents are deleted.
1108 *
1109 * If yes however, the owner must fill the szDescription
1110 * field with a description of the keyboard combination
1111 * (e.g. "Shift+Alt+C") and return TRUE. The entry field
1112 * will then be set to that description.
1113 *
1114 *@@added V0.9.1 (99-12-19) [umoeller]
1115 */
1116
1117MRESULT EXPENTRY ctl_fnwpObjectHotkeyEntryField(HWND hwndEdit, ULONG msg, MPARAM mp1, MPARAM mp2)
1118{
1119 MRESULT mrc = (MPARAM)FALSE; // WM_CHAR not-processed flag
1120
1121 // get object window data; this was stored in QWL_USER by
1122 // WM_INITDLG of obj_fnwpSettingsObjDetails
1123 // PXFOBJWINDATA pWinData = (PXFOBJWINDATA)WinQueryWindowULong(hwndEdit, QWL_USER);
1124 PFNWP pfnwpOrig = (PFNWP)WinQueryWindowULong(hwndEdit, QWL_USER);
1125
1126 switch (msg)
1127 {
1128 case WM_CHAR:
1129 {
1130 /*
1131 #define KC_CHAR 0x0001
1132 #define KC_VIRTUALKEY 0x0002
1133 #define KC_SCANCODE 0x0004
1134 #define KC_SHIFT 0x0008
1135 #define KC_CTRL 0x0010
1136 #define KC_ALT 0x0020
1137 #define KC_KEYUP 0x0040
1138 #define KC_PREVDOWN 0x0080
1139 #define KC_LONEKEY 0x0100
1140 #define KC_DEADKEY 0x0200
1141 #define KC_COMPOSITE 0x0400
1142 #define KC_INVALIDCOMP 0x0800
1143 */
1144
1145 /*
1146 Examples: usFlags usKeyCode
1147 F3 02 22
1148 Ctrl+F4 12 23
1149 Ctrl+Shift+F4 1a 23
1150 Ctrl 12 0a
1151 Alt 22 0b
1152 Shift 0a 09
1153 */
1154
1155 // USHORT usCommand;
1156 // USHORT usKeyCode;
1157 USHORT usFlags = SHORT1FROMMP(mp1);
1158 // UCHAR ucRepeat = CHAR3FROMMP(mp1);
1159 UCHAR ucScanCode = CHAR4FROMMP(mp1);
1160 USHORT usch = SHORT1FROMMP(mp2);
1161 USHORT usvk = SHORT2FROMMP(mp2);
1162
1163 if (
1164 // process only key-down messages
1165 ((usFlags & KC_KEYUP) == 0)
1166 // check flags:
1167 // filter out those ugly composite key (accents etc.)
1168 && ((usFlags & (KC_DEADKEY | KC_COMPOSITE | KC_INVALIDCOMP)) == 0)
1169 )
1170 {
1171 HOTKEYNOTIFY hkn;
1172 ULONG flReturned;
1173 HWND hwndOwner = WinQueryWindow(hwndEdit, QW_OWNER);
1174
1175 usFlags &= (KC_VIRTUALKEY | KC_CTRL | KC_ALT | KC_SHIFT);
1176
1177 hkn.usFlags = usFlags;
1178 hkn.usvk = usvk;
1179 hkn.usch = usch;
1180 hkn.ucScanCode = ucScanCode;
1181
1182 if (usFlags & KC_VIRTUALKEY)
1183 hkn.usKeyCode = usvk;
1184 else
1185 hkn.usKeyCode = usch;
1186
1187 hkn.szDescription[0] = 0;
1188
1189 flReturned = (ULONG)WinSendMsg(hwndOwner,
1190 WM_CONTROL,
1191 MPFROM2SHORT(WinQueryWindowUShort(hwndEdit,
1192 QWS_ID),
1193 EN_HOTKEY), // new notification code
1194 (MPARAM)&hkn);
1195 if (flReturned & HEFL_SETTEXT)
1196 WinSetWindowText(hwndEdit, hkn.szDescription);
1197 else
1198 WinSetWindowText(hwndEdit, "");
1199
1200 if (flReturned & HEFL_FORWARD2OWNER)
1201 WinPostMsg(hwndOwner,
1202 WM_CHAR,
1203 mp1, mp2);
1204
1205 mrc = (MPARAM)TRUE; // WM_CHAR processed flag;
1206 }
1207 break; }
1208
1209 default:
1210 mrc = pfnwpOrig(hwndEdit, msg, mp1, mp2);
1211 }
1212 return (mrc);
1213}
1214
1215/*
1216 *@@ ctlMakeHotkeyEntryField:
1217 * this turns any entry field into a "hotkey entry field"
1218 * by subclassing it with ctl_fnwpObjectHotkeyEntryField.
1219 * See remarks there for details.
1220 *
1221 * Returns FALSE upon errors, TRUE otherwise.
1222 *
1223 *@@added V0.9.1 (99-12-19) [umoeller]
1224 */
1225
1226BOOL ctlMakeHotkeyEntryField(HWND hwndHotkeyEntryField)
1227{
1228 PFNWP pfnwpOrig = WinSubclassWindow(hwndHotkeyEntryField,
1229 ctl_fnwpObjectHotkeyEntryField);
1230 if (pfnwpOrig)
1231 {
1232 WinSetWindowPtr(hwndHotkeyEntryField, QWL_USER, (PVOID)pfnwpOrig);
1233 return (TRUE);
1234 }
1235
1236 return (FALSE);
1237}
1238
Note: See TracBrowser for help on using the repository browser.