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

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

Initial checkin of helpers code which used to be in WarpIN.

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