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

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

Final changes for 0.9.7, i hope...

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 44.2 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 pa->usAniCurrent++;
588 if (pa->usAniCurrent >= pa->usAniCount)
589 pa->usAniCurrent = 0;
590
591 WinSendMsg(hwndStatic,
592 SM_SETHANDLE,
593 (MPARAM)pa->ahptrAniIcons[pa->usAniCurrent],
594 (MPARAM)NULL);
595 break; }
596
597 /*
598 * SM_SETHANDLE:
599 *
600 */
601
602 case SM_SETHANDLE:
603 {
604 HDC hdcMem;
605 HPS hpsMem;
606 SIZEL szlPage;
607
608 LONG lBkgndColor = winhQueryPresColor(
609 WinQueryWindow(hwndStatic, QW_PARENT),
610 PP_BACKGROUNDCOLOR,
611 FALSE,
612 SYSCLR_DIALOGBACKGROUND);
613
614 HPS hps = WinGetPS(hwndStatic);
615
616 // if we have created bitmaps previously, delete them
617 if (pa->hbm)
618 {
619 GpiDeleteBitmap(pa->hbm);
620 pa->hbm = NULLHANDLE;
621 }
622 if (pa->hbmHalftoned)
623 {
624 GpiDeleteBitmap(pa->hbmHalftoned);
625 pa->hbmHalftoned = NULLHANDLE;
626 }
627
628 // switch to RGB mode
629 gpihSwitchToRGB(hps);
630
631 // create a memory PS
632 szlPage.cx = pa->rclIcon.xRight - pa->rclIcon.xLeft;
633 szlPage.cy = pa->rclIcon.yTop - pa->rclIcon.yBottom;
634 if (gpihCreateMemPS(pa->hab, &szlPage, &hdcMem, &hpsMem))
635 {
636 // switch the memory PS to RGB mode too
637 gpihSwitchToRGB(hpsMem);
638
639 // create a suitable bitmap w/ the size of the
640 // static control
641 if ((pa->hbm = gpihCreateBitmap(hpsMem,
642 szlPage.cx,
643 szlPage.cy)))
644 {
645 // associate the bit map with the memory PS
646 if (GpiSetBitmap(hpsMem, pa->hbm) != HBM_ERROR)
647 {
648 // fill the bitmap with the current static
649 // background color
650 POINTL ptl = {0, 0};
651 GpiMove(hpsMem, &ptl);
652 ptl.x = pa->rclIcon.xRight;
653 ptl.y = pa->rclIcon.yTop;
654 GpiSetColor(hpsMem,
655 lBkgndColor);
656 GpiBox(hpsMem,
657 DRO_FILL, // interior only
658 &ptl,
659 0, 0); // no corner rounding
660
661 /*
662 * ANF_ICON:
663 *
664 */
665
666 if (pa->ulFlags & ANF_ICON)
667 {
668 // store new icon in our own structure
669 pa->hptr = (HPOINTER)mp1;
670
671 if (pa->hptr)
672 // paint icon into bitmap
673 gpihIcon2Bitmap(hpsMem,
674 pa->hptr,
675 lBkgndColor,
676 WinQuerySysValue(HWND_DESKTOP, SV_CXICON));
677
678 } // end if (pa->ulFlags & ANF_ICON)
679
680 /*
681 * ANF_BITMAP:
682 *
683 */
684
685 else if (pa->ulFlags & ANF_BITMAP)
686 {
687 // store passed bitmap
688 HBITMAP hbmSource = (HBITMAP)mp1;
689
690 if (hbmSource)
691 gpihStretchBitmap(hpsMem,
692 hbmSource,
693 NULL, // use size of bitmap
694 &(pa->rclIcon),
695 ((pa->ulFlags & ANF_PROPORTIONAL)
696 != 0));
697
698 } // end if (pa->ulFlags & ANF_BITMAP)
699
700 } // end if (GpiSetBitmap(...
701 } // if (pa->hbm = gpihCreateBitmap(hpsMem, &(pa->rclIcon)))
702
703 // in any case, clean up now
704 GpiDestroyPS(hpsMem);
705 DevCloseDC(hdcMem);
706 } // end if (gpihCreateMemPS(hab, &hdcMem, &hpsMem))
707
708 WinReleasePS(hps);
709
710 // enforce WM_PAINT
711 WinInvalidateRect(hwndStatic, NULL, FALSE);
712
713 break; }
714
715 /*
716 * WM_PAINT:
717 * "normal" paint; this only arrives here if the
718 * icon needs to be repainted. We simply paint
719 * the bitmap we created in WM_SETHANDLE.
720 */
721
722 case WM_PAINT: {
723 RECTL rcl;
724 HPS hps = WinBeginPaint(hwndStatic, NULLHANDLE, &rcl);
725 POINTL ptl = {0, 0};
726
727 if (pa->hbm)
728 {
729 if (WinQueryWindowULong(hwndStatic, QWL_STYLE)
730 & WS_DISABLED)
731 {
732 // if the control is currently disabled,
733 // draw in half-toned (grayed)
734
735 LONG lBkgndColor = winhQueryPresColor(
736 WinQueryWindow(hwndStatic, QW_PARENT),
737 PP_BACKGROUNDCOLOR,
738 FALSE,
739 SYSCLR_DIALOGBACKGROUND);
740
741 // 1) check if we created the half-tone
742 // bitmap already (WinDrawBitmap doesn't
743 // work with half-toned color bitmaps, so
744 // here's yet another thing we have to do
745 // all alone)
746 if (pa->hbmHalftoned == NULLHANDLE)
747 pa->hbmHalftoned = gpihCreateHalftonedBitmap(pa->hab,
748 pa->hbm,
749 lBkgndColor);
750
751 if (pa->hbmHalftoned)
752 WinDrawBitmap(hps,
753 pa->hbmHalftoned,
754 NULL, // whole bmp
755 &ptl, // lower left corner
756 0, 0, // no colors
757 DBM_NORMAL);
758 }
759 else
760 {
761 // not disabled: draw regular bitmap
762 // we created previously
763 // draw the bitmap that we created previously
764 WinDrawBitmap(hps,
765 pa->hbm,
766 NULL, // whole bmp
767 &ptl, // lower left corner
768 0, 0, // no colors
769 DBM_NORMAL);
770 }
771 }
772
773 WinEndPaint(hps);
774 break; }
775
776 /*
777 * WM_DESTROY:
778 * clean up.
779 */
780
781 case WM_DESTROY:
782 {
783 // undo subclassing in case more WM_TIMERs come in
784 WinSubclassWindow(hwndStatic, OldStaticProc);
785
786 if (pa->hbm)
787 GpiDeleteBitmap(pa->hbm);
788 if (pa->hbmHalftoned)
789 GpiDeleteBitmap(pa->hbm);
790
791 // clean up ANIMATIONDATA struct
792 WinSetWindowULong(hwndStatic, QWL_USER, (ULONG)NULL);
793 free(pa);
794
795 mrc = OldStaticProc(hwndStatic, msg, mp1, mp2);
796 break; }
797
798 default:
799 mrc = OldStaticProc(hwndStatic, msg, mp1, mp2);
800 }
801 }
802 return (mrc);
803}
804
805/* ******************************************************************
806 *
807 * Icon animation
808 *
809 ********************************************************************/
810
811/*
812 *@@ ctlPrepareStaticIcon:
813 * turns a static control into one which properly
814 * displays transparent icons when given a
815 * SM_SETHANDLE msg.
816 * This is done by subclassing the static control
817 * with ctl_fnwpBitmapStatic.
818 *
819 * This function gets called automatically by
820 * ctlPrepareAnimation, so you need not call it
821 * independently for animations. See the notes there
822 * how this can be done.
823 *
824 * You can, however, call this function if you
825 * have some static control with transparent icons
826 * which is not animated but changes sometimes
827 * (because you have the same repaint problems there).
828 *
829 * This func does _not_ start an animation yet.
830 *
831 * To change the icon being displayed, send the control
832 * a SM_SETHANDLE msg with the icon handle in (HPOINTER)mp1.
833 *
834 * Returns a PANIMATIONDATA if subclassing succeeded or
835 * NULL upon errors. If you only call this function, you
836 * do not need this structure; it is needed by
837 * ctlPrepareAnimation though.
838 *
839 * The subclassed static icon func above automatically
840 * cleans up resources, so you don't have to worry about
841 * that either.
842 *
843 *@@changed V0.9.0 [umoeller]: adjusted for ctl_fnwpBitmapStatic changes
844 */
845
846PANIMATIONDATA ctlPrepareStaticIcon(HWND hwndStatic,
847 USHORT usAnimCount) // needed for allocating extra memory,
848 // this must be at least 1
849{
850 PANIMATIONDATA pa = NULL;
851 PFNWP OldStaticProc = WinSubclassWindow(hwndStatic, ctl_fnwpBitmapStatic);
852 if (OldStaticProc)
853 {
854 // create the ANIMATIONDATA structure,
855 // initialize some fields,
856 // and store it in QWL_USER of the static control
857 ULONG cbStruct = sizeof(ANIMATIONDATA)
858 + ((usAnimCount-1) * sizeof(HPOINTER));
859 pa = (PANIMATIONDATA)malloc(cbStruct);
860 memset(pa, 0, cbStruct);
861
862 // switch static to icon mode
863 pa->ulFlags = ANF_ICON;
864 pa->OldStaticProc = OldStaticProc;
865 WinQueryWindowRect(hwndStatic, &(pa->rclIcon));
866 pa->hab = WinQueryAnchorBlock(hwndStatic);
867
868 WinSetWindowULong(hwndStatic, QWL_USER, (ULONG)pa);
869 }
870 return (pa);
871}
872
873/*
874 *@@ ctlPrepareAnimation:
875 * this function turns a regular static icon
876 * control an animated one. This is the one-shot
877 * function for animating static icon controls.
878 *
879 * It calls ctlPrepareStaticIcon first, then uses
880 * the passed parameters to prepare an animation:
881 *
882 * pahptr must point to an array of HPOINTERs
883 * which must contain usAnimCount icon handles.
884 * If (fStartAnimation == TRUE), the animation
885 * is started already. Otherwise you'll have
886 * to call ctlStartAnimation yourself.
887 *
888 * To create an animated icon, simply create a static
889 * control with the dialog editor (can be any static,
890 * e.g. a text control). Then do the following in your code:
891 *
892 * 1) load the dlg box template (using WinLoadDlg);
893 *
894 * 2) load all the icons for the animation in an array of
895 * HPOINTERs (check xsdLoadAnimation in shutdown.c for an
896 * example);
897 *
898 * 3) call ctlPrepareAnimation, e.g.:
899 + ctlPrepareAnimation(WinWindowFromID(hwndDlg, ID_ICON), // get icon hwnd
900 + 8, // no. of icons for the anim
901 + &ahptr[0], // ptr to first icon in the array
902 + 150, // delay
903 + TRUE); // start animation now
904 *
905 * 4) call WinProcessDlg(hwndDlg);
906 *
907 * 5) stop the animation, e.g.:
908 + ctlStopAnimation(WinWindowFromID(hwndDlg, ID_ICON));
909 *
910 * 6) destroy the dlg window;
911 *
912 * 7) free all the HPOINTERS loaded above (check xsdFreeAnimation
913 * in shutdown.c for an example).
914 *
915 * When the icon control is destroyed, the
916 * subclassed window proc (ctl_fnwpBitmapStatic)
917 * automatically cleans up resources. However,
918 * the icons in *pahptr are not freed.
919 */
920
921BOOL ctlPrepareAnimation(HWND hwndStatic, // icon hwnd
922 USHORT usAnimCount, // no. of anim steps
923 HPOINTER *pahptr, // array of HPOINTERs
924 ULONG ulDelay, // delay per anim step (in ms)
925 BOOL fStartAnimation) // TRUE: start animation now
926{
927 PANIMATIONDATA paNew = ctlPrepareStaticIcon(hwndStatic, usAnimCount);
928 if (paNew)
929 {
930 paNew->ulDelay = ulDelay;
931 // paNew->OldStaticProc already set
932 paNew->hptr = NULLHANDLE;
933 // paNew->rclIcon already set
934 paNew->usAniCurrent = 0;
935 paNew->usAniCount = usAnimCount;
936 memcpy(&(paNew->ahptrAniIcons), pahptr,
937 (usAnimCount * sizeof(HPOINTER)));
938
939 if (fStartAnimation) {
940 WinStartTimer(WinQueryAnchorBlock(hwndStatic), hwndStatic,
941 1, ulDelay);
942 WinPostMsg(hwndStatic, WM_TIMER, NULL, NULL);
943 }
944 }
945 return (paNew != NULL);
946}
947
948/*
949 *@@ ctlStartAnimation:
950 * starts an animation that not currently running. You
951 * must prepare the animation by calling ctlPrepareAnimation
952 * first.
953 */
954
955BOOL ctlStartAnimation(HWND hwndStatic)
956{
957 BOOL brc = FALSE;
958 PANIMATIONDATA pa = (PANIMATIONDATA)WinQueryWindowULong(hwndStatic, QWL_USER);
959 if (pa)
960 if (WinStartTimer(WinQueryAnchorBlock(hwndStatic), hwndStatic,
961 1, pa->ulDelay))
962 {
963 brc = TRUE;
964 WinPostMsg(hwndStatic, WM_TIMER, NULL, NULL);
965 }
966 return (brc);
967}
968
969/*
970 *@@ ctlStopAnimation:
971 * stops an animation that is currently running.
972 * This does not free any resources.
973 */
974
975BOOL ctlStopAnimation(HWND hwndStatic)
976{
977 return (WinStopTimer(WinQueryAnchorBlock(hwndStatic), hwndStatic, 1));
978}
979
980/*
981 *@@category: Helpers\PM helpers\Window classes\Stretched bitmaps
982 * See comctl.c and ctlPrepareStretchedBitmap.
983 */
984
985/* ******************************************************************
986 *
987 * Bitmap functions
988 *
989 ********************************************************************/
990
991/*
992 *@@ ctlPrepareStretchedBitmap:
993 * turns a static control into a bitmap control
994 * which stretches the bitmap to the size of the
995 * control when given an SM_SETHANDLE msg.
996 *
997 * If (fPreserveProportions == TRUE), the control
998 * will size the bitmap proportionally only. Otherwise,
999 * it will always stretch the bitmap to the whole
1000 * size of the static control (possibly distorting
1001 * the proportions). See gpihStretchBitmap for
1002 * details.
1003 *
1004 * This function subclasses the static control
1005 * with ctl_fnwpBitmapStatic, setting the flags as
1006 * necessary. However, this does not set the bitmap
1007 * to display yet.
1008 * To have a bitmap displayed, send the control
1009 * a SM_SETHANDLE message with the bitmap to be
1010 * displayed in (HBITMAP)mp1 after having used this
1011 * function.
1012 *
1013 * ctl_fnwpBitmapStatic will then create a private
1014 * copy of that bitmap, stretched to its own size.
1015 * You can therefore delete the "source" bitmap
1016 * after sending SM_SETHANDLE.
1017 *
1018 * You can also send SM_SETHANDLE several times.
1019 * The control will then readjust itself and delete
1020 * its old bitmap.
1021 * But note that GpiWCBitBlt is invoked on that new
1022 * bitmap with every new message, so this is not
1023 * terribly fast.
1024 *
1025 * Also note that you should not resize the static
1026 * control after creation, because the bitmap will
1027 * _not_ be adjusted.
1028 *
1029 * This returns a PANIMATIONDATA if subclassing succeeded or
1030 * NULL upon errors. You do not need to save this structure;
1031 * it is cleaned up automatically when the control is destroyed.
1032 *
1033 *@@added V0.9.0 [umoeller]
1034 */
1035
1036PANIMATIONDATA ctlPrepareStretchedBitmap(HWND hwndStatic,
1037 BOOL fPreserveProportions)
1038{
1039 PANIMATIONDATA pa = NULL;
1040 PFNWP OldStaticProc = WinSubclassWindow(hwndStatic, ctl_fnwpBitmapStatic);
1041 if (OldStaticProc)
1042 {
1043 // create the ANIMATIONDATA structure,
1044 // initialize some fields,
1045 // and store it in QWL_USER of the static control
1046 ULONG cbStruct = sizeof(ANIMATIONDATA);
1047 pa = (PANIMATIONDATA)malloc(cbStruct);
1048 memset(pa, 0, cbStruct);
1049
1050 // switch static to bitmap mode
1051 pa->ulFlags = ANF_BITMAP;
1052 if (fPreserveProportions)
1053 pa->ulFlags |= ANF_PROPORTIONAL;
1054
1055 pa->OldStaticProc = OldStaticProc;
1056 WinQueryWindowRect(hwndStatic, &(pa->rclIcon));
1057 pa->hab = WinQueryAnchorBlock(hwndStatic);
1058
1059 WinSetWindowULong(hwndStatic, QWL_USER, (ULONG)pa);
1060 }
1061 return (pa);
1062}
1063
1064/*
1065 *@@category: Helpers\PM helpers\Window classes\Hotkey entry field
1066 * See comctl.c and ctl_fnwpObjectHotkeyEntryField.
1067 */
1068
1069/* ******************************************************************
1070 *
1071 * Hotkey entry field
1072 *
1073 ********************************************************************/
1074
1075/*
1076 *@@ ctl_fnwpHotkeyEntryField:
1077 * this is the window proc for a subclassed entry
1078 * field which displays a description of a keyboard
1079 * combination upon receiving WM_CHAR.
1080 *
1081 * Use ctlMakeHotkeyEntryField to turn any existing
1082 * entry field into such an "hotkey entry field".
1083 *
1084 * The entry field is deleted on every WM_CHAR that
1085 * comes in.
1086 *
1087 * For this to work, this window proc needs the
1088 * cooperation of its owner. Whenever a WM_CHAR
1089 * message is received by the entry field which
1090 * looks like a meaningful key or key combination
1091 * (some filtering is done by this func), then
1092 * the owner of the entry field receives a WM_CONTROL
1093 * message with the new EN_HOTKEY notification code
1094 * (see comctl.h for details).
1095 *
1096 * It is then the responsibility of the owner to
1097 * check whether the HOTKEYNOTIFY structure pointed
1098 * to by WM_CONTROL's mp2 is a meaningful keyboard
1099 * combination.
1100 *
1101 * If not, the owner must return FALSE, and the
1102 * entry field's contents are deleted.
1103 *
1104 * If yes however, the owner must fill the szDescription
1105 * field with a description of the keyboard combination
1106 * (e.g. "Shift+Alt+C") and return TRUE. The entry field
1107 * will then be set to that description.
1108 *
1109 *@@added V0.9.1 (99-12-19) [umoeller]
1110 */
1111
1112MRESULT EXPENTRY ctl_fnwpObjectHotkeyEntryField(HWND hwndEdit, ULONG msg, MPARAM mp1, MPARAM mp2)
1113{
1114 MRESULT mrc = (MPARAM)FALSE; // WM_CHAR not-processed flag
1115
1116 // get object window data; this was stored in QWL_USER by
1117 // WM_INITDLG of obj_fnwpSettingsObjDetails
1118 // PXFOBJWINDATA pWinData = (PXFOBJWINDATA)WinQueryWindowULong(hwndEdit, QWL_USER);
1119 PFNWP pfnwpOrig = (PFNWP)WinQueryWindowULong(hwndEdit, QWL_USER);
1120
1121 switch (msg)
1122 {
1123 case WM_CHAR:
1124 {
1125 /*
1126 #define KC_CHAR 0x0001
1127 #define KC_VIRTUALKEY 0x0002
1128 #define KC_SCANCODE 0x0004
1129 #define KC_SHIFT 0x0008
1130 #define KC_CTRL 0x0010
1131 #define KC_ALT 0x0020
1132 #define KC_KEYUP 0x0040
1133 #define KC_PREVDOWN 0x0080
1134 #define KC_LONEKEY 0x0100
1135 #define KC_DEADKEY 0x0200
1136 #define KC_COMPOSITE 0x0400
1137 #define KC_INVALIDCOMP 0x0800
1138 */
1139
1140 /*
1141 Examples: usFlags usKeyCode
1142 F3 02 22
1143 Ctrl+F4 12 23
1144 Ctrl+Shift+F4 1a 23
1145 Ctrl 12 0a
1146 Alt 22 0b
1147 Shift 0a 09
1148 */
1149
1150 // USHORT usCommand;
1151 // USHORT usKeyCode;
1152 USHORT usFlags = SHORT1FROMMP(mp1);
1153 // UCHAR ucRepeat = CHAR3FROMMP(mp1);
1154 UCHAR ucScanCode = CHAR4FROMMP(mp1);
1155 USHORT usch = SHORT1FROMMP(mp2);
1156 USHORT usvk = SHORT2FROMMP(mp2);
1157
1158 if (
1159 // process only key-down messages
1160 ((usFlags & KC_KEYUP) == 0)
1161 // check flags:
1162 // filter out those ugly composite key (accents etc.)
1163 && ((usFlags & (KC_DEADKEY | KC_COMPOSITE | KC_INVALIDCOMP)) == 0)
1164 )
1165 {
1166 HOTKEYNOTIFY hkn;
1167 ULONG flReturned;
1168 HWND hwndOwner = WinQueryWindow(hwndEdit, QW_OWNER);
1169
1170 usFlags &= (KC_VIRTUALKEY | KC_CTRL | KC_ALT | KC_SHIFT);
1171
1172 hkn.usFlags = usFlags;
1173 hkn.usvk = usvk;
1174 hkn.usch = usch;
1175 hkn.ucScanCode = ucScanCode;
1176
1177 if (usFlags & KC_VIRTUALKEY)
1178 hkn.usKeyCode = usvk;
1179 else
1180 hkn.usKeyCode = usch;
1181
1182 hkn.szDescription[0] = 0;
1183
1184 flReturned = (ULONG)WinSendMsg(hwndOwner,
1185 WM_CONTROL,
1186 MPFROM2SHORT(WinQueryWindowUShort(hwndEdit,
1187 QWS_ID),
1188 EN_HOTKEY), // new notification code
1189 (MPARAM)&hkn);
1190 if (flReturned & HEFL_SETTEXT)
1191 WinSetWindowText(hwndEdit, hkn.szDescription);
1192 else
1193 WinSetWindowText(hwndEdit, "");
1194
1195 if (flReturned & HEFL_FORWARD2OWNER)
1196 WinPostMsg(hwndOwner,
1197 WM_CHAR,
1198 mp1, mp2);
1199
1200 mrc = (MPARAM)TRUE; // WM_CHAR processed flag;
1201 }
1202 break; }
1203
1204 default:
1205 mrc = pfnwpOrig(hwndEdit, msg, mp1, mp2);
1206 }
1207 return (mrc);
1208}
1209
1210/*
1211 *@@ ctlMakeHotkeyEntryField:
1212 * this turns any entry field into a "hotkey entry field"
1213 * by subclassing it with ctl_fnwpObjectHotkeyEntryField.
1214 * See remarks there for details.
1215 *
1216 * Returns FALSE upon errors, TRUE otherwise.
1217 *
1218 *@@added V0.9.1 (99-12-19) [umoeller]
1219 */
1220
1221BOOL ctlMakeHotkeyEntryField(HWND hwndHotkeyEntryField)
1222{
1223 PFNWP pfnwpOrig = WinSubclassWindow(hwndHotkeyEntryField,
1224 ctl_fnwpObjectHotkeyEntryField);
1225 if (pfnwpOrig)
1226 {
1227 WinSetWindowPtr(hwndHotkeyEntryField, QWL_USER, (PVOID)pfnwpOrig);
1228 return (TRUE);
1229 }
1230
1231 return (FALSE);
1232}
1233
Note: See TracBrowser for help on using the repository browser.