source: trunk/src/helpers/cctl_chart.c@ 9

Last change on this file since 9 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: 48.8 KB
Line 
1
2/*
3 *@@sourcefile cctl_chart.c:
4 * implementation for the "chart" common control.
5 * See comctl.c for an overview.
6 *
7 * This has been extracted from comctl.c with V0.9.3 (2000-05-21) [umoeller].
8 *
9 * Note: Version numbering in this file relates to XWorkplace version
10 * numbering.
11 *
12 *@@header "helpers\comctl.h"
13 *@@added V0.9.3 (2000-05-21) [umoeller].
14 */
15
16/*
17 * Copyright (C) 1997-2000 Ulrich M”ller.
18 * This file is part of the XWorkplace source package.
19 * XWorkplace is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published
21 * by the Free Software Foundation, in version 2 as it comes in the
22 * "COPYING" file of the XWorkplace main distribution.
23 * This program is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
27 */
28
29#define OS2EMX_PLAIN_CHAR
30 // this is needed for "os2emx.h"; if this is defined,
31 // emx will define PSZ as _signed_ char, otherwise
32 // as unsigned char
33
34#define INCL_DOSEXCEPTIONS
35#define INCL_DOSPROCESS
36#define INCL_DOSSEMAPHORES
37#define INCL_DOSERRORS
38
39#define INCL_WINWINDOWMGR
40#define INCL_WINFRAMEMGR
41#define INCL_WINMESSAGEMGR
42#define INCL_WININPUT
43#define INCL_WINPOINTERS
44#define INCL_WINTRACKRECT
45#define INCL_WINTIMER
46#define INCL_WINSYS
47
48#define INCL_WINRECTANGLES /// xxx temporary
49
50#define INCL_WINMENUS
51#define INCL_WINSTATICS
52#define INCL_WINBUTTONS
53#define INCL_WINSTDCNR
54
55#define INCL_GPIPRIMITIVES
56#define INCL_GPILOGCOLORTABLE
57#define INCL_GPILCIDS
58#define INCL_GPIPATHS
59#define INCL_GPIREGIONS
60#define INCL_GPIBITMAPS // added V0.9.1 (2000-01-04) [umoeller]: needed for EMX headers
61#include <os2.h>
62
63#include <stdlib.h>
64#include <stdio.h>
65#include <string.h>
66#include <setjmp.h> // needed for except.h
67#include <assert.h> // needed for except.h
68
69#include "setup.h" // code generation and debugging options
70
71#include "helpers\cnrh.h"
72#include "helpers\except.h" // exception handling
73#include "helpers\gpih.h"
74#include "helpers\linklist.h"
75#include "helpers\winh.h"
76
77#include "helpers\comctl.h"
78
79#pragma hdrstop
80
81/*
82 *@@category: Helpers\PM helpers\Window classes\Chart control
83 */
84
85/* ******************************************************************
86 * *
87 * Chart Control *
88 * *
89 ********************************************************************/
90
91/*
92 *@@ ctlCreateChartBitmap:
93 * this creates a new bitmap and paints the
94 * chart into it. This bitmap will be used
95 * in WM_PAINT of ctl_fnwpChart to quickly paint
96 * the chart image.
97 *
98 * The bitmap will be created with the specified
99 * size. The chart will consume all available
100 * space in the bitmap. The returned bitmap is
101 * not selected into any presentation space.
102 *
103 * Note: Description text will be drawn with the
104 * current font specified for the memory PS, and
105 * with the current character cell box.
106 *
107 * This function gets called automatically from
108 * WM_PAINT if we determine that the bitmap for
109 * painting has not been created yet or has been
110 * invalidated (because data or styles changed).
111 *
112 * However, you can also use this function
113 * independently to have a chart bitmap created,
114 * wherever you might need it.
115 * You will then have to fill in the CHARTDATA
116 * and CHARTSTYLE structures before calling
117 * this function.
118 *
119 * If (paRegions != NULL), this function will
120 * create GPI regions for data item. Each GPI
121 * region will then contain the outline of the
122 * corresponding pie chart slice. This allows
123 * you to easily relate coordinates to pie slieces,
124 * for example for mouse clicks.
125 *
126 * In that case, paRegions must point to an array
127 * of HRGN items, each of which will contain a
128 * region handle later. It is the responsibility
129 * of the caller to free the regions later, using
130 * GpiDestroyRegion on each region handle (with
131 * the hpsMem specified with this function).
132 *
133 * If you're not interested in the regions, set
134 * paRegions to NULL.
135 *
136 * This returns NULLHANDLE if an error occured.
137 * This can mean the following:
138 * -- The data is invalid, because the total is 0.
139 * -- The bitmap could not be created (memory?).
140 */
141
142HBITMAP ctlCreateChartBitmap(HPS hpsMem, // in: memory PS to use for bitmap creation
143 LONG lcx, // in: width of bitmap
144 LONG lcy, // in: height of bitmap
145 PCHARTDATA pChartData, // in: chart data
146 PCHARTSTYLE pChartStyle, // in: chart style
147 LONG lBackgroundColor, // in: color around the chart (RGB)
148 LONG lTextColor, // in: description text color (RGB)
149 HRGN* paRegions) // out: GPI regions for each data item
150{
151 HBITMAP hbmReturn = NULLHANDLE;
152
153 // sum up the values to get the total
154 ULONG ul = 0;
155 double dTotal = 0;
156 double* pdThis = pChartData->padValues;
157 for (ul = 0; ul < pChartData->cValues; ul++)
158 {
159 dTotal += *pdThis;
160 pdThis++;
161 }
162
163 // avoid division by zero
164 if (dTotal > 0)
165 {
166 RECTL rclWholeStatic;
167 ULONG ulYBottomNow = 0;
168
169 // get window rectangle (bottom left is 0, 0)
170 rclWholeStatic.xLeft = 0;
171 rclWholeStatic.yBottom = 0;
172 rclWholeStatic.xRight = lcx;
173 rclWholeStatic.yTop = lcy;
174
175 // create bitmap of that size
176 if ((hbmReturn = gpihCreateBitmap(hpsMem,
177 rclWholeStatic.xRight,
178 rclWholeStatic.yTop)))
179 {
180 // successfully created:
181
182 // allocate array for storing text positions later
183 PPOINTL paptlDescriptions = (PPOINTL)malloc(sizeof(POINTL)
184 * pChartData->cValues);
185 POINTL ptlCenter;
186
187 FIXED fxPieSize = (LONG)(pChartStyle->dPieSize * 65536),
188 fxDescriptions = (LONG)(pChartStyle->dDescriptions * 65536);
189
190 // associate bitmap with memory PS
191 GpiSetBitmap(hpsMem, hbmReturn);
192
193 // switch the HPS to RGB mode
194 gpihSwitchToRGB(hpsMem);
195
196 // fill bitmap with static's background color
197 gpihBox(hpsMem,
198 DRO_FILL,
199 &rclWholeStatic,
200 lBackgroundColor);
201
202 // We'll paint into the bitmap in two loops:
203 // +-- The outer "3D" loop is executed
204 // | pChartStyle->ulThickness-fold, if
205 // | CHS_3Dxxx has been enabled; otherwise
206 // | just once.
207 // |
208 // | +-- The inner "slice" loop goes thru the
209 // | data fields in pChartData and draws
210 // | the pies accordingly.
211 // |
212 // +-- We then increase the base Y point (ulYBottomNow)
213 // by one and draw again, thereby getting the 3D
214 // effect.
215
216 // 1) outer 3D loop
217 do // while ( (pChartStyle->ulStyle & CHS_3D_BRIGHT)...
218 {
219 RECTL rclArc;
220 PLONG plColorThis;
221 PSZ *ppszDescriptionThis = NULL;
222 PPOINTL pptlDescriptionThis;
223 HRGN* phRegionThis;
224
225 ARCPARAMS ap;
226 AREABUNDLE ab;
227
228 double dStartAngle = pChartData->usStartAngle,
229 dSweepAngle = 0;
230
231 // this is only TRUE for the last loop
232 BOOL fNowDrawingSurface =
233 (
234 ((pChartStyle->ulStyle & CHS_3D_BRIGHT) == 0)
235 ||
236 (ulYBottomNow == pChartStyle->ulThickness - 1)
237 );
238
239 // // _Pmpf(("Looping, ulYBottomNow: %d", ulYBottomNow));
240
241 // calculate pie rectangle for this loop;
242 // this is the size of the static control
243 // minus the "3D thickness", if enabled
244 memcpy(&rclArc, &rclWholeStatic, sizeof(RECTL));
245 if (pChartStyle->ulStyle & CHS_3D_BRIGHT)
246 // this includes CHS_3D_DARKEN
247 {
248 rclArc.yBottom = rclWholeStatic.yBottom
249 + ulYBottomNow;
250 rclArc.yTop = rclWholeStatic.yTop
251 - pChartStyle->ulThickness
252 + ulYBottomNow;
253 }
254
255 // calculate center point
256 ptlCenter.x = rclArc.xRight / 2;
257 ptlCenter.y = ((rclArc.yTop - rclArc.yBottom) / 2) + ulYBottomNow;
258
259 // Now, the "arc" APIs really suck. The following
260 // has cost me hours of testing to find out:
261
262 // a) The arc functions expect some kind of
263 // "default arc" to be defined, which they
264 // refer to. We define the arc as elliptical;
265 // this will be used as the "current arc"
266 // for subsequent arc calls.
267 // (P, S) and (R, Q) define the end points
268 // of the major axes of the ellipse.
269 // The center of the arc will later be
270 // specified with GpiPartialArc (while GpiFullArc
271 // uses the current pen position...
272 // Who created these APIs?!? This might be a most
273 // flexible way to do things, but where's the
274 // simple stuff?!?)
275 ap.lP = ptlCenter.x; // X-axis X
276 ap.lS = 0; // X-axis Y
277 ap.lR = 0; // Y-axis X
278 ap.lQ = ((rclArc.yTop - rclArc.yBottom) / 2);
279 // Y-axis Y
280 GpiSetArcParams(hpsMem, &ap);
281
282 // b) The line primitives determine lines
283 // to be drawn around the pie slices.
284 // We don't want any.
285 GpiSetLineType(hpsMem, LINETYPE_INVISIBLE);
286
287 // c) Strangely, GpiSetPattern does work,
288 // while GpiSetColor doesn't (see below).
289 GpiSetPattern(hpsMem, PATSYM_SOLID);
290
291 // initialize per-item data pointers for
292 // loop below
293 pdThis = pChartData->padValues;
294 plColorThis = pChartData->palColors;
295 ppszDescriptionThis = pChartData->papszDescriptions;
296 pptlDescriptionThis = paptlDescriptions;
297 phRegionThis = paRegions;
298
299 // 2) inner "pie slice loop":
300 // this loop goes over the data pointers
301 // and paints accordingly. At the end of
302 // the loop, we'll advance all those pointers.
303 for (ul = 0; ul < pChartData->cValues; ul++)
304 {
305 HRGN hrgnThis;
306 SHORT sSweepAngle,
307 sStartAngle;
308
309 // calculate the angle to sweep to:
310 // a simple rule of three
311 dSweepAngle = *pdThis // current data pointer
312 * pChartData->usSweepAngle
313 // maximum angle
314 / dTotal; // total data sum
315
316 // d) And now comes the real fun part.
317 // GpiPartialArc is too dumb to draw
318 // anything on its own, it must _always_
319 // appear within an area or path definition.
320 // Unfortunately, this isn't really said
321 // clearly anywhere.
322 // Even worse, in order to set the color
323 // with which the slice is to be drawn,
324 // one has to define an AREABUNDLE because
325 // the regular GpiSetColor functions don't
326 // seem to work here. Or maybe it's my fault,
327 // but with this awful documentation, who knows.
328 // We use the current color defined in the
329 // pie chart data (this pointer was set above
330 // and will be advanced for the next slice).
331 ab.lColor = *plColorThis;
332
333 // "3D mode" enabled with darkened socket?
334 if ( (pChartStyle->ulStyle & CHS_3D_DARKEN)
335 // not last loop?
336 && (!fNowDrawingSurface)
337 )
338 // darken the current fill color
339 // by halving each color component
340 gpihManipulateRGB(&ab.lColor,
341 1, // multiplier
342 2); // divisor
343
344 // set the area (fill) color
345 GpiSetAttrs(hpsMem,
346 PRIM_AREA,
347 ABB_COLOR,
348 0,
349 (PBUNDLE)&ab);
350
351 GpiSetCurrentPosition(hpsMem, &ptlCenter);
352
353 // round the angle values properly
354 sStartAngle = (SHORT)(dStartAngle + .5);
355 sSweepAngle = (SHORT)(dSweepAngle + .5);
356
357 // now draw the pie slice;
358 // we could use an area, but since we need
359 // to remember the coordinates of the slice
360 // for mouse click handling, we require a
361 // region. But areas cannot be converted
362 // to regions, so we use a path instead.
363 GpiBeginPath(hpsMem,
364 1); // path ID, must be 1
365 GpiPartialArc(hpsMem,
366 &ptlCenter,
367 fxPieSize, // calculated from CHARTSTYLE
368 MAKEFIXED(sStartAngle, 0),
369 MAKEFIXED(sSweepAngle, 0));
370 // this moves the current position to the outer
371 // point on the ellipse which corresponds to
372 // sSweepAngle
373 GpiEndPath(hpsMem);
374
375 // convert the path to a region;
376 // we'll need the region for mouse hit testing later
377 hrgnThis = GpiPathToRegion(hpsMem, 1,
378 FPATH_ALTERNATE);
379 // after this, the path is deleted
380 GpiPaintRegion(hpsMem, hrgnThis);
381 if (phRegionThis)
382 // region output requested by caller:
383 // store region, the caller will clean this up
384 *phRegionThis = hrgnThis;
385 else
386 // drop region
387 GpiDestroyRegion(hpsMem, hrgnThis);
388
389 // descriptions enabled and last run?
390 if ( (ppszDescriptionThis)
391 && (fNowDrawingSurface)
392 )
393 {
394 if (*ppszDescriptionThis)
395 {
396 // yes: calculate position to paint
397 // text at later (we can't do this now,
398 // because it might be overpainted by
399 // the next arc again)
400
401 GpiSetCurrentPosition(hpsMem, &ptlCenter);
402 // move the current position to
403 // the center outer point on the ellipse
404 // (in between sStartAngle and sSweepAngle);
405 // since we're outside an area, this will not
406 // paint anything
407 GpiPartialArc(hpsMem,
408 &ptlCenter,
409 fxDescriptions, // calculated from CHARTSTYLE
410 MAKEFIXED(sStartAngle, 0),
411 // only half the sweep now:
412 MAKEFIXED(sSweepAngle / 2, 0));
413
414 // store this outer point in the array
415 // of description coordinates for later
416 GpiQueryCurrentPosition(hpsMem, pptlDescriptionThis);
417 }
418 }
419
420 // increase the start angle by the sweep angle for next loop
421 dStartAngle += dSweepAngle;
422
423 // advance the data pointers
424 pdThis++;
425 plColorThis++;
426 if (ppszDescriptionThis)
427 ppszDescriptionThis++;
428 pptlDescriptionThis++;
429 if (phRegionThis)
430 phRegionThis++;
431 } // end for (ul...)
432
433 // go for next "3D thickness" iteration
434 ulYBottomNow++;
435 } while ( (pChartStyle->ulStyle & CHS_3D_BRIGHT)
436 && (ulYBottomNow < pChartStyle->ulThickness)
437 );
438
439 // now paint descriptions
440 if (pChartStyle->ulStyle & CHS_DESCRIPTIONS)
441 {
442 // we use two pointers during the iteration,
443 // which point to the item corresponding
444 // to the current data item:
445 // 1) pointer to center point on outer border
446 // of partial arc
447 // (calculated above)
448 PPOINTL pptlDescriptionThis = paptlDescriptions;
449 // 2) pointer to current description string
450 PSZ* ppszDescriptionThis = pChartData->papszDescriptions;
451
452 // description strings valid?
453 if (ppszDescriptionThis)
454 {
455 // set presentation color
456 GpiSetColor(hpsMem, lTextColor);
457
458 // set text aligment to centered
459 // both horizontally and vertically;
460 // this affects subsequent GpiCharStringAt
461 // calls in that the output text will
462 // be centered around the specified
463 // point
464 GpiSetTextAlignment(hpsMem,
465 TA_CENTER, // horizontally
466 TA_HALF); // center vertically
467
468 // loop thru data items
469 for (ul = 0;
470 ul < pChartData->cValues;
471 ul++)
472 {
473 POINTL ptlMiddlePoint;
474
475 // when drawing the arcs above, we have,
476 // for each pie slice, stored the middle
477 // point on the outer edge of the ellipse
478 // in the paptlDescriptions POINTL array:
479
480 // ++++
481 // + +
482 // + +
483 // ptlCenter\ + +
484 // \ + + <-- current partial arc
485 // \+ +
486 // +++++++++++X +
487 // + +
488 // + +
489 // + XX <-- point calculated above
490 // + +
491 // + +
492 // ++++++
493
494 // now calculate a middle point between
495 // that outer point on the ellipse and
496 // the center of the ellipse, which will
497 // be the center point for the text
498
499 // ++++
500 // + +
501 // + +
502 // ptlCenter\ + +
503 // \ + + <-- current partial arc
504 // \+ +
505 // ++++++++++++ +
506 // + XX + <-- new middle point
507 // + +
508 // + XX <-- point calculated above
509 // + +
510 // + +
511 // ++++++
512
513 ptlMiddlePoint.x =
514 ptlCenter.x
515 + ((pptlDescriptionThis->x - ptlCenter.x) * 2 / 3);
516 ptlMiddlePoint.y =
517 ptlCenter.y
518 - (ptlCenter.y - pptlDescriptionThis->y) * 2 / 3;
519
520 // FINALLY, draw the description
521 // at this point; since we have used
522 // GpiSetTextAlignment above, the
523 // text will be centered on exactly
524 // that point
525 GpiCharStringAt(hpsMem,
526 &ptlMiddlePoint,
527 strlen(*ppszDescriptionThis),
528 *ppszDescriptionThis);
529
530 pptlDescriptionThis++;
531 ppszDescriptionThis++;
532 } // end for (ul = 0; ul < pChartData->cValues; ul++)
533 } // end if (ppszDescriptionThis)
534 } // end if (pChartStyle->ulStyle & CHS_DESCRIPTIONS)
535
536 // cleanup
537 free(paptlDescriptions);
538
539 // deselect (free) bitmap
540 GpiSetBitmap(hpsMem, NULLHANDLE);
541 } // end if (pChtCData->hbmChart ...)
542 } // end if (dTotal > 0)
543
544 return (hbmReturn);
545}
546
547/*
548 *@@ CleanupBitmap:
549 * this frees the resources associated with
550 * the chart bitmap.
551 *
552 *@@changed V0.9.2 (2000-02-29) [umoeller]: fixed maaajor memory leak
553 */
554
555VOID CleanupBitmap(PCHARTCDATA pChtCData)
556{
557 if (pChtCData)
558 {
559 // bitmap already created?
560 if (pChtCData->hbmChart)
561 {
562 // free current bitmap
563 // GpiSetBitmap(pChtCData->hpsMem, NULLHANDLE);
564 // delete bitmap; fails if not freed!
565 GpiDeleteBitmap(pChtCData->hbmChart);
566 pChtCData->hbmChart = NULLHANDLE;
567 }
568
569 // destroy regions, but not the array itself
570 // (this is done in CleanupData)
571 if (pChtCData->paRegions)
572 {
573 ULONG ul;
574 HRGN *phRegionThis = pChtCData->paRegions;
575 for (ul = 0;
576 ul < pChtCData->cd.cValues;
577 ul++)
578 {
579 if (*phRegionThis)
580 {
581 GpiDestroyRegion(pChtCData->hpsMem, *phRegionThis);
582 *phRegionThis = NULLHANDLE;
583 }
584 phRegionThis++;
585 }
586 }
587 }
588}
589
590/*
591 *@@ CleanupData:
592 * this frees all allocated resources of the chart
593 * control, except the bitmap (use CleanupBitmap
594 * for that) and the CHARTCDATA itself.
595 *
596 * Note: CleanupBitmap must be called _before_ this
597 * function.
598 */
599
600VOID CleanupData(PCHARTCDATA pChtCData)
601{
602 if (pChtCData)
603 if (pChtCData->cd.cValues)
604 {
605 // _Pmpf(("Cleaning up data"));
606 // values array
607 if (pChtCData->cd.padValues)
608 free(pChtCData->cd.padValues);
609
610 // colors array
611 if (pChtCData->cd.palColors)
612 free(pChtCData->cd.palColors);
613
614 // strings array
615 if (pChtCData->cd.papszDescriptions)
616 {
617 ULONG ul;
618 PSZ *ppszDescriptionThis = pChtCData->cd.papszDescriptions;
619 for (ul = 0;
620 ul < pChtCData->cd.cValues;
621 ul++)
622 {
623 if (*ppszDescriptionThis)
624 free(*ppszDescriptionThis);
625
626 ppszDescriptionThis++;
627 }
628
629 free(pChtCData->cd.papszDescriptions);
630 }
631
632 pChtCData->cd.cValues = 0;
633
634 // GPI regions array
635 if (pChtCData->paRegions)
636 {
637 free(pChtCData->paRegions);
638 pChtCData->paRegions = NULL;
639 }
640 }
641}
642
643/*
644 *@@ SetChartData:
645 * implementation for CHTM_SETCHARTDATA
646 * in ctl_fnwpChart.
647 *
648 *@@added V0.9.2 (2000-02-29) [umoeller]
649 */
650
651VOID SetChartData(HWND hwndChart,
652 PCHARTCDATA pChtCData,
653 PCHARTDATA pcdNew)
654{
655 ULONG ul = 0;
656 PSZ *ppszDescriptionSource,
657 *ppszDescriptionTarget;
658
659 // free previous values, if set
660 if (pChtCData->hbmChart)
661 CleanupBitmap(pChtCData);
662
663 CleanupData(pChtCData);
664
665 // _Pmpf(("Setting up data"));
666
667 if (pChtCData->hpsMem == NULLHANDLE)
668 {
669 // first call:
670 // create a memory PS for the bitmap
671 SIZEL szlPage = {0, 0};
672 gpihCreateMemPS(WinQueryAnchorBlock(hwndChart),
673 &szlPage,
674 &pChtCData->hdcMem,
675 &pChtCData->hpsMem);
676 // _Pmpf(("Created HPS 0x%lX", pChtCData->hpsMem));
677 // _Pmpf(("Created HDC 0x%lX", pChtCData->hdcMem));
678 }
679
680 pChtCData->cd.usStartAngle = pcdNew->usStartAngle;
681 pChtCData->cd.usSweepAngle = pcdNew->usSweepAngle;
682 pChtCData->cd.cValues = pcdNew->cValues;
683
684 // copy values
685 pChtCData->cd.padValues = (double*)malloc(sizeof(double) * pcdNew->cValues);
686 memcpy(pChtCData->cd.padValues,
687 pcdNew->padValues,
688 sizeof(double) * pcdNew->cValues);
689
690 // copy colors
691 pChtCData->cd.palColors = (LONG*)malloc(sizeof(LONG) * pcdNew->cValues);
692 memcpy(pChtCData->cd.palColors,
693 pcdNew->palColors,
694 sizeof(LONG) * pcdNew->cValues);
695
696 // copy strings
697 pChtCData->cd.papszDescriptions = (PSZ*)malloc(sizeof(PSZ) * pcdNew->cValues);
698 ppszDescriptionSource = pcdNew->papszDescriptions;
699 ppszDescriptionTarget = pChtCData->cd.papszDescriptions;
700 for (ul = 0;
701 ul < pcdNew->cValues;
702 ul++)
703 {
704 if (*ppszDescriptionSource)
705 *ppszDescriptionTarget = strdup(*ppszDescriptionSource);
706 else
707 *ppszDescriptionTarget = NULL;
708 ppszDescriptionSource++;
709 ppszDescriptionTarget++;
710 }
711
712 // create an array of GPI region handles
713 pChtCData->paRegions = (HRGN*)malloc(sizeof(HRGN) * pcdNew->cValues);
714 // initialize all regions to null
715 memset(pChtCData->paRegions, 0, sizeof(HRGN) * pcdNew->cValues);
716
717 pChtCData->lSelected = -1; // none selected
718
719 WinInvalidateRect(hwndChart, NULL, FALSE);
720}
721
722/*
723 *@@ PaintChart:
724 * implementation for WM_PAINT
725 * in ctl_fnwpChart.
726 *
727 *@@added V0.9.2 (2000-02-29) [umoeller]
728 */
729
730VOID PaintChart(HWND hwndChart,
731 PCHARTCDATA pChtCData,
732 HPS hps,
733 PRECTL prclPaint)
734{
735 RECTL rclStatic;
736 WinQueryWindowRect(hwndChart, &rclStatic);
737
738 // _Pmpf(("ctl_fnwpChart: WM_PAINT, cValues: %d", pChtCData->cd.cValues));
739
740 // do we have any values yet?
741 if (pChtCData->cd.cValues == 0)
742 {
743 CHAR szDebug[200];
744 sprintf(szDebug, "Error, no values set");
745 WinFillRect(hps,
746 &rclStatic,
747 CLR_WHITE);
748 WinDrawText(hps,
749 strlen(szDebug),
750 szDebug,
751 &rclStatic,
752 CLR_BLACK,
753 CLR_WHITE,
754 DT_LEFT | DT_TOP);
755 }
756 else
757 {
758 // valid values, apparently:
759 LONG lForegroundColor = winhQueryPresColor(hwndChart,
760 PP_FOREGROUNDCOLOR,
761 TRUE, // inherit presparams
762 SYSCLR_WINDOWTEXT);
763
764 // yes: check if we created the bitmap
765 // already
766 if (pChtCData->hbmChart == NULLHANDLE)
767 {
768 // no: do it now
769 HPOINTER hptrOld = winhSetWaitPointer();
770
771 // get presentation font
772 FONTMETRICS FontMetrics;
773 LONG lPointSize;
774 LONG lLCIDSet = gpihFindPresFont(hwndChart,
775 TRUE, // inherit PP
776 pChtCData->hpsMem,
777 "8.Helv",
778 &FontMetrics,
779 &lPointSize);
780 // set presentation font
781 if (lLCIDSet)
782 {
783 GpiSetCharSet(pChtCData->hpsMem, lLCIDSet);
784 if (FontMetrics.fsDefn & FM_DEFN_OUTLINE)
785 gpihSetPointSize(pChtCData->hpsMem, lPointSize);
786 }
787
788
789 gpihSwitchToRGB(hps);
790
791 pChtCData->hbmChart = ctlCreateChartBitmap(
792 pChtCData->hpsMem, // mem PS
793 rclStatic.xRight, // cx
794 rclStatic.yTop, // cy
795 &pChtCData->cd, // data
796 &pChtCData->cs, // style
797 // background color:
798 winhQueryPresColor(hwndChart,
799 PP_BACKGROUNDCOLOR,
800 TRUE, // inherit presparams
801 SYSCLR_DIALOGBACKGROUND),
802 // description text color:
803 lForegroundColor,
804 pChtCData->paRegions);
805 // out: regions array
806 // _Pmpf(("Created bitmap 0x%lX", pChtCData->hbmChart));
807
808 // unset and delete font
809 GpiSetCharSet(pChtCData->hpsMem, LCID_DEFAULT);
810 if (lLCIDSet)
811 GpiDeleteSetId(pChtCData->hpsMem, lLCIDSet);
812
813 WinSetPointer(HWND_DESKTOP, hptrOld);
814 }
815
816 if (pChtCData->hbmChart)
817 {
818 POINTL ptlDest = { 0, 0 };
819 WinDrawBitmap(hps,
820 pChtCData->hbmChart,
821 NULL, // draw whole bitmap
822 &ptlDest,
823 0, 0, // colors (don't care)
824 DBM_NORMAL);
825
826 // do we have the focus?
827 if (pChtCData->fHasFocus)
828 // something selected?
829 if (pChtCData->lSelected != -1)
830 if (pChtCData->paRegions)
831 {
832 HRGN* pRegionThis = pChtCData->paRegions; // first region
833 pRegionThis += pChtCData->lSelected; // array item
834
835 if (*pRegionThis)
836 {
837 SIZEL sl = {2, 2};
838 GpiSetColor(hps, lForegroundColor);
839 GpiFrameRegion(hps,
840 *pRegionThis,
841 &sl);
842 }
843 }
844 }
845 }
846}
847
848/*
849 *@@ ctl_fnwpChart:
850 * window procedure for the "chart" control.
851 *
852 * This is not a stand-alone window procedure, but must only
853 * be used with static controls subclassed by ctlChartFromStatic.
854 *
855 *@@added V0.9.0 [umoeller]
856 *@@changed V0.9.2 (2000-02-29) [umoeller]: added resize support
857 *@@changed V0.9.2 (2000-02-29) [umoeller]: fixed baaad PM resource leaks, the bitmap was never freed
858 */
859
860MRESULT EXPENTRY ctl_fnwpChart(HWND hwndChart, ULONG msg, MPARAM mp1, MPARAM mp2)
861{
862 MRESULT mrc = 0;
863 PCHARTCDATA pChtCData = (PCHARTCDATA)WinQueryWindowULong(hwndChart, QWL_USER);
864
865 if (pChtCData)
866 {
867 PFNWP OldStaticProc = pChtCData->OldStaticProc;
868
869 switch (msg)
870 {
871 /*
872 *@@ CHTM_SETCHARTDATA:
873 * user msg to set the pie chart data,
874 * which is copied to the control's
875 * memory.
876 * This can be sent several times to
877 * have the chart refreshed with new
878 * values.
879 *
880 * Parameters:
881 * -- PCHARTDATA mp1: new values.
882 * These will be copied to the chart's internal data.
883 * -- mp2: unused.
884 */
885
886 case CHTM_SETCHARTDATA:
887 // _Pmpf(("CHTM_SETCHARTDATA, mp1: 0x%lX", mp1));
888 if (mp1)
889 {
890 PCHARTDATA pcdNew = (PCHARTDATA)mp1;
891 SetChartData(hwndChart, pChtCData, pcdNew);
892 }
893 break;
894
895 /*
896 *@@ CHTM_SETCHARTSTYLE:
897 * user msg to set the chart style,
898 * which is copied to the control's
899 * memory.
900 * This can be sent several times to
901 * have the chart refreshed with new
902 * styles.
903 *
904 * Parameters:
905 * -- PCHARTSTYLE mp1: new style data.
906 * This will be copied to the chart's internal data.
907 * -- mp2: unused.
908 */
909
910 case CHTM_SETCHARTSTYLE:
911 // _Pmpf(("CHTM_SETCHARTSTYLE, mp1: 0x%lX", mp1));
912 memcpy(&(pChtCData->cs), mp1, sizeof(CHARTSTYLE));
913 if (pChtCData->hbmChart)
914 {
915 CleanupBitmap(pChtCData);
916 WinInvalidateRect(hwndChart, NULL, FALSE);
917 }
918 break;
919
920 /*
921 *@@ CHTM_ITEMFROMPOINT:
922 * this can be _sent_ to the chart control
923 * to query the chart item which surrounds
924 * the given point. Specify the coordinates
925 * in two SHORT's in mp1 (as with WM_BUTTON1*
926 * messages), in window coordinates.
927 *
928 * Parameters:
929 * -- SHORT SHORT1FROMMP(mp1): x
930 * -- SHORT SHORT1FROMMP(mp1): y
931 * -- mp2: unused.
932 *
933 * Returns:
934 * -- LONG: data item index (counting from
935 * 0) or -1 if none, e.g. if no
936 * data has been set yet or if
937 * the point is outside the pie
938 * slices.
939 *
940 *@@added V0.9.2 (2000-02-29) [umoeller]
941 */
942
943 case CHTM_ITEMFROMPOINT:
944 {
945 LONG lRegionFound = -1; // none
946
947 // get mouse coordinates
948 POINTL ptlMouse;
949 ptlMouse.x = SHORT1FROMMP(mp1);
950 ptlMouse.y = SHORT2FROMMP(mp1);
951
952 // data set?
953 if (pChtCData->cd.cValues)
954 {
955 // regions defined?
956 if (pChtCData->paRegions)
957 {
958 ULONG ul;
959 HRGN* phRegionThis = pChtCData->paRegions;
960 for (ul = 0;
961 ul < pChtCData->cd.cValues;
962 ul++)
963 {
964 if (*phRegionThis)
965 {
966 if (GpiPtInRegion(pChtCData->hpsMem,
967 *phRegionThis,
968 &ptlMouse)
969 == PRGN_INSIDE)
970 {
971 // _Pmpf(("Clicked in region %d", ul));
972 lRegionFound = ul;
973 break;
974 }
975 }
976 phRegionThis++;
977 }
978
979 }
980 }
981
982 mrc = (MPARAM)lRegionFound;
983 break; }
984
985 /*
986 * WM_BUTTON1DOWN:
987 *
988 */
989
990 case WM_BUTTON1DOWN:
991 if (pChtCData->cs.ulStyle & CHS_SELECTIONS)
992 {
993 LONG lRegionFound = (LONG)WinSendMsg(hwndChart,
994 CHTM_ITEMFROMPOINT,
995 mp1,
996 NULL);
997
998 // selections allowed:
999 // we then accept WM_CHAR msgs, so
1000 // we need the focus
1001 WinSetFocus(HWND_DESKTOP, hwndChart);
1002 // this invalidates the window
1003 if (pChtCData->lSelected != lRegionFound)
1004 {
1005 // selection changed:
1006 pChtCData->lSelected = lRegionFound;
1007 // repaint
1008 WinInvalidateRect(hwndChart, NULL, FALSE);
1009 }
1010 }
1011 else
1012 // no selections allowed:
1013 // just activate the window
1014 WinSetActiveWindow(HWND_DESKTOP, hwndChart);
1015 // the parent frame gets activated this way
1016 mrc = (MPARAM)TRUE;
1017 break;
1018
1019 /*
1020 * WM_SETFOCUS:
1021 * we might need to redraw the selection.
1022 */
1023
1024 case WM_SETFOCUS:
1025 if (pChtCData->cs.ulStyle & CHS_SELECTIONS)
1026 {
1027 // selections allowed:
1028 pChtCData->fHasFocus = (BOOL)mp2;
1029 if (pChtCData->lSelected != -1)
1030 WinInvalidateRect(hwndChart, NULL, FALSE);
1031 }
1032 break;
1033
1034 /*
1035 * WM_WINDOWPOSCHANGED:
1036 *
1037 */
1038
1039 case WM_WINDOWPOSCHANGED:
1040 {
1041 // this msg is passed two SWP structs:
1042 // one for the old, one for the new data
1043 // (from PM docs)
1044 PSWP pswpNew = (PSWP)mp1;
1045 // PSWP pswpOld = pswpNew + 1;
1046
1047 // resizing?
1048 if (pswpNew->fl & SWP_SIZE)
1049 if (pChtCData->hbmChart)
1050 {
1051 // invalidate bitmap so that
1052 // it will be recreated with new size
1053 CleanupBitmap(pChtCData);
1054 WinInvalidateRect(hwndChart, NULL, FALSE);
1055 }
1056
1057 // return default NULL
1058 break; }
1059
1060 /*
1061 * WM_PRESPARAMCHANGED:
1062 *
1063 */
1064
1065 case WM_PRESPARAMCHANGED:
1066 if (pChtCData->hbmChart)
1067 {
1068 // invalidate bitmap so that
1069 // it will be recreated with new
1070 // fonts and colors
1071 CleanupBitmap(pChtCData);
1072 WinInvalidateRect(hwndChart, NULL, FALSE);
1073 }
1074 break;
1075
1076 /*
1077 * WM_PAINT:
1078 * paint the chart bitmap, which is created
1079 * if necessary (calling ctlCreateChartBitmap).
1080 */
1081
1082 case WM_PAINT:
1083 {
1084 RECTL rclPaint;
1085 HPS hps = WinBeginPaint(hwndChart,
1086 NULLHANDLE, // obtain cached micro-PS
1087 &rclPaint);
1088
1089 PaintChart(hwndChart,
1090 pChtCData,
1091 hps,
1092 &rclPaint);
1093
1094 WinEndPaint(hps);
1095 mrc = 0;
1096 break; }
1097
1098 /*
1099 * WM_DESTROY:
1100 * clean up resources
1101 */
1102
1103 case WM_DESTROY:
1104 CleanupBitmap(pChtCData);
1105 CleanupData(pChtCData);
1106
1107 // _Pmpf(("Destroying HPS 0x%lX", pChtCData->hpsMem));
1108 if (!GpiDestroyPS(pChtCData->hpsMem));
1109 // _Pmpf((" Error!"));
1110 // _Pmpf(("Destroying HDC 0x%lX", pChtCData->hdcMem));
1111 if (!DevCloseDC(pChtCData->hdcMem));
1112 // _Pmpf((" Error!"));
1113 free(pChtCData);
1114
1115 mrc = (*OldStaticProc)(hwndChart, msg, mp1, mp2);
1116 break;
1117
1118 default:
1119 mrc = (*OldStaticProc)(hwndChart, msg, mp1, mp2);
1120 }
1121 }
1122
1123 return (mrc);
1124}
1125
1126/*
1127 *@@ ctlChartFromStatic:
1128 * this function turns an existing static text control into
1129 * a chart control (for visualizing data) by subclassing its
1130 * window procedure with ctl_fnwpChart.
1131 *
1132 * This way you can easily create a chart control as a static
1133 * control in any Dialog Editor;
1134 * after loading the dlg template, simply call this function
1135 * with the hwnd of the static control to make it a chart.
1136 *
1137 * The pie chart consumes all available space in the static control.
1138 *
1139 * In XWorkplace, this is used for the pie chart on the new
1140 * XFldDisk "Details" settings page to display the free space
1141 * on a certain drive.
1142 *
1143 * Note: even though you can use _any_ type of static control
1144 * with this function, you should use a static _text_ control,
1145 * because not all types of static controls react to fonts and
1146 * colors dragged upon them. The static _text_ control does.
1147 *
1148 * <B>Chart data:</B>
1149 *
1150 * The pie chart control operates on an array of "double" values.
1151 * Each value in that array corresponds to a color in a second
1152 * array of (LONG) RGB values and, if description texts are
1153 * enabled, to a third array of PSZ's.
1154 *
1155 * The data on which the pie chart operates is initialized to
1156 * be void, so that the pie chart will not paint anything
1157 * initially. In order to have the pie chart display something,
1158 * post or send a CHTM_SETCHARTDATA message (comctl.h) to the static
1159 * control after it has been subclassed.
1160 *
1161 * CHTM_SETCHARTDATA takes a CHARTDATA structure (comctl.h) in mp1,
1162 * which must contain the chart data and corresponding colors to be
1163 * displayed.
1164 *
1165 * The total sum of the "double" values will represent the angle in
1166 * CHARTDATA.usSweepAngle.
1167 *
1168 * For example, if two values of 50 and 100 are passed to the
1169 * control and usSweepAngle is 270 (i.e. a three-quarter pie),
1170 * the chart control will calculate the following:
1171 *
1172 * 1) The sum of the data is 150.
1173 *
1174 * 2) The first sub-arc will span an angle of 270 * (50/150)
1175 * = 90 degrees.
1176 *
1177 * 3) The second sub-arc will span an angle of 270 * (100/150)
1178 * = 180 degrees.
1179 *
1180 * You can also have descriptions displayed along the different
1181 * chart items by specifying CHARTDATA.papszDescriptions and
1182 * setting the CHS_DESCRIPTIONS flag (below).
1183 *
1184 * <B>Chart styles:</B>
1185 *
1186 * Use CHTM_SETCHARTSTYLE with a PCHARTSTYLE (comctl.h) in mp1.
1187 * This can be sent to the chart control several times.
1188 *
1189 * Presently, only pie charts are implemented. However, we do
1190 * have several "sub-styles" for pie charts:
1191 * -- CHS_3D_BRIGHT: paint a "3D" socket below the actual chart.
1192 * -- CHS_3D_DARKEN: like CHS_3D_BRIGHT, but the socket will be made
1193 * darker compared to the surface.
1194 *
1195 * General styles:
1196 * -- CHS_DESCRIPTIONS: show descriptions on the chart
1197 * (CHARTDATA.papszDescriptions data).
1198 * -- CHS_SELECTIONS: allow pie chart slices to be selectable,
1199 * using the mouse and the keyboard.
1200 *
1201 * <B>Display:</B>
1202 *
1203 * The chart control creates an internal bitmap for the display
1204 * only once (ctlCreateChartBitmap). This bitmap is refreshed if
1205 * neccessary, e.g. because chart data or styles have changed.
1206 *
1207 * The chart control uses presentation parameters, as listed below.
1208 * Presentation parameters are inherited from the parent window.
1209 * If a presparam is not set, the corresponding system color is
1210 * used. The following color pairs are recognized:
1211 *
1212 * -- PP_BACKGROUNDCOLOR / SYSCLR_DIALOGBACKGROUND:
1213 * background of the chart control (outside the chart).
1214 * -- PP_FOREGROUNDCOLOR / SYSCLR_WINDOWTEXT:
1215 * text color, if description texts are enabled and valid.
1216 * -- PP_FONTNAMESIZE:
1217 * text font, if description texts are enabled and valid.
1218 * If this presparam is not set, the system font is used.
1219 *
1220 * The control reacts to fonts and colors dropped upon it, if
1221 * it has been subclassed from a static _text_ control (see above).
1222 * It also recalculates the bitmap when it's resized.
1223 *
1224 * <B>Example usage:</B>
1225 *
1226 + // get static control:
1227 + HWND hwndChart = WinWindowFromID(hwndDialog, ID_...);
1228 + CHARTSTYLE cs;
1229 + CHARTDATA cd;
1230 + // define data:
1231 + double adData[3] = { 100, 200, 300 };
1232 + // define corresponding colors:
1233 + LONG alColors[3] = { 0x800000, 0x008000, 0x000080 };
1234 + // define correspdonding descriptions:
1235 + PSZ apszDescriptions[3] = { "item 1", "item 3", "item 3" };
1236 +
1237 + ctlChartFromStatic(hwndChart); // create chart
1238 +
1239 + cs.ulStyle = CHS_3D_DARKEN | CHS_DESCRIPTIONS;
1240 + cs.ulThickness = 20;
1241 + WinSendMsg(hwndChart, CHTM_SETCHARTSTYLE, &cs, NULL);
1242 +
1243 + cd.usStartAngle = 15; // start at 15ø from right
1244 + cd.usSweepAngle = 270; // three-quarter pie (for the sum of the
1245 + // above values: 100+200+300 = 600)
1246 + cd.cValues = 3; // array count
1247 + cd.padValues = &adData[0];
1248 + cd.palColors = &alColors[0];
1249 + cd.papszDescriptions = &apszDescriptions[0];
1250 + WinSendMsg(hwndChart, CHTM_SETCHARTDATA, &cd, NULL);
1251 *
1252 *@@added V0.9.0 [umoeller]
1253 */
1254
1255BOOL ctlChartFromStatic(HWND hwndChart) // in: static control to subclass
1256{
1257 if (hwndChart)
1258 {
1259 PFNWP OldStaticProc = WinSubclassWindow(hwndChart, ctl_fnwpChart);
1260
1261 if (OldStaticProc)
1262 {
1263 PCHARTCDATA pChtCData = (PCHARTCDATA)malloc(sizeof(CHARTCDATA));
1264 memset(pChtCData, 0, sizeof(CHARTCDATA));
1265 pChtCData->OldStaticProc = OldStaticProc;
1266 pChtCData->fHasFocus = FALSE;
1267 WinSetWindowPtr(hwndChart, QWL_USER, pChtCData);
1268 return (TRUE);
1269 }
1270 }
1271
1272 return (FALSE);
1273}
1274
1275
Note: See TracBrowser for help on using the repository browser.