source: branches/branch-1-0/src/helpers/cctl_chart.c

Last change on this file was 264, checked in by pr, 21 years ago

Fix spelling errors.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 68.3 KB
Line 
1
2/*
3 *@@sourcefile cctl_chart.c:
4 * implementation for the "chart" common control, which can
5 * represent an array of "double" values as either a pie chart
6 * or a bar chart. Selections of chart slices and owner
7 * notifications are also supported.
8 *
9 * This has been extracted from comctl.c with V0.9.3 (2000-05-21) [umoeller].
10 *
11 * The "chart" common control presently is a subclassed static
12 * text control. This way you can easily create a chart control
13 * as a static control in any Dialog Editor; after loading the
14 * dlg template, simply call ctlChartFromStatic with the hwnd
15 * of the static control to make it a chart.
16 *
17 * Note: even though you can use _any_ type of static control
18 * with this function, you should use a static _text_ control,
19 * because not all types of static controls react to fonts and
20 * colors dragged upon them. The static _text_ control does.
21 *
22 * The pie chart consumes all available space in the static control.
23 *
24 * In XWorkplace, this is used for the pie chart on the new
25 * XFldDisk "Details" settings page to display the free space
26 * on a certain drive.
27 *
28 * <B>Chart data:</B>
29 *
30 * The pie chart control operates on an array of "double" values.
31 * Each value in that array corresponds to a color in a second
32 * array of (LONG) RGB values and, if description texts are
33 * enabled, to a third array of PSZ's with description texts.
34 * In each of the three arrays, the array item count must be
35 * the same, naturally.
36 *
37 * The data on which the pie chart operates is initialized to
38 * be void, so that the pie chart will not paint anything
39 * initially. In order to have the pie chart display something,
40 * send a CHTM_SETCHARTDATA message (comctl.h) to the static
41 * control after it has been subclassed.
42 *
43 * CHTM_SETCHARTDATA takes a CHARTDATA structure (comctl.h) in mp1,
44 * which must contain the three arrays with the chart data, colors,
45 * and descriptive texts to be displayed.
46 *
47 * The chart data will automatically compute the sum of all values
48 * so that each data item will be displayed as a fraction of the
49 * total chart control size, proportionally to the item's value
50 * against the sum of all values. In other words,
51 *
52 + (dValue / dTotal) == (sizeSlice / sizeTotal).
53 *
54 * The display depends on whether the chart control operates in
55 * "pie chart" or "bar chart" mode.
56 *
57 * -- In "pie chart" mode, data items are displayed as slices of
58 * a pie chart. The total sum of the "double" values will
59 * represent the angle in CHARTDATA.usSweepAngle.
60 *
61 * For example, if two values of 50 and 100 are passed to the
62 * control and usSweepAngle is 270 (i.e. a three-quarter pie),
63 * the chart control will calculate the following:
64 *
65 * 1) The sum of the data is 150.
66 *
67 * 2) The first sub-arc will span an angle of 270 * (50/150)
68 * = 90 degrees.
69 *
70 * 3) The second sub-arc will span an angle of 270 * (100/150)
71 * = 180 degrees.
72 *
73 * You can also have descriptions displayed along the different
74 * chart items by specifying CHARTDATA.papszDescriptions and
75 * setting the CHS_DESCRIPTIONS flag (below).
76 *
77 * -- In "bar chart" mode, the angle values make no sense and
78 * are therefore ignored. In "bar chart" mode, the chart will
79 * be drawn as a simple rectangle, with subrectangles representing
80 * the chart slices.
81 *
82 * <B>Chart styles:</B>
83 *
84 * Use CHTM_SETCHARTSTYLE with a PCHARTSTYLE (comctl.h) in mp1.
85 * This can be sent to the chart control several times.
86 *
87 * Set either CHS_PIECHART or CHS_BARCHART to switch the control
88 * to "pie chart" or "bar chart" mode, respectively.
89 *
90 * For pie charts, there are several "display sub-styles":
91 *
92 * -- CHS_3D_BRIGHT: paint a "3D" socket below the actual chart.
93 *
94 * -- CHS_3D_DARKEN: like CHS_3D_BRIGHT, but the socket will be made
95 * darker compared to the surface.
96 *
97 * General styles (for all modes):
98 *
99 * -- CHS_DESCRIPTIONS: show descriptions on the chart
100 * (CHARTDATA.papszDescriptions data).
101 *
102 * -- CHS_SELECTIONS: allow pie chart slices to be selectable,
103 * using the mouse and the keyboard (see below).
104 *
105 * <B>Display:</B>
106 *
107 * The chart control creates an internal bitmap for the display
108 * only once (ctlCreateChartBitmap). This bitmap is refreshed if
109 * neccessary, e.g. because chart data or styles have changed.
110 *
111 * The chart control uses presentation parameters, as listed below.
112 * Presentation parameters are inherited from the parent window.
113 * If a presparam is not set, the corresponding system color is
114 * used. The following color pairs are recognized:
115 *
116 * -- PP_BACKGROUNDCOLOR / SYSCLR_DIALOGBACKGROUND:
117 * background of the chart control (outside the chart).
118 *
119 * -- PP_FOREGROUNDCOLOR / SYSCLR_WINDOWTEXT:
120 * text color, if description texts are enabled and valid.
121 *
122 * -- PP_FONTNAMESIZE:
123 * text font, if description texts are enabled and valid.
124 * If this presparam is not set, the system font is used.
125 *
126 * The control reacts to fonts and colors dropped upon it, if
127 * it has been subclassed from a static _text_ control (see above).
128 * It also recalculates the bitmap when it's resized.
129 *
130 * <B>Example usage:</B>
131 *
132 + // get static control:
133 + HWND hwndChart = WinWindowFromID(hwndDialog, ID_...);
134 + CHARTSTYLE cs;
135 + CHARTDATA cd;
136 + // define data:
137 + double adData[3] = { 100, 200, 300 };
138 + // define corresponding colors:
139 + LONG alColors[3] = { 0x800000, 0x008000, 0x000080 };
140 + // define correspdonding descriptions:
141 + PSZ apszDescriptions[3] = { "item 1", "item 3", "item 3" };
142 +
143 + ctlChartFromStatic(hwndChart); // create chart
144 +
145 + cs.ulStyle = CHS_3D_DARKEN | CHS_DESCRIPTIONS;
146 + cs.ulThickness = 20;
147 + cs.dPieSize = .8; // use 80% of control size
148 + cs.dDescriptions = .5 // draw descriptions at 50% from center
149 + WinSendMsg(hwndChart, CHTM_SETCHARTSTYLE, &cs, NULL);
150 +
151 + cd.usStartAngle = 15; // start at 15ø from right
152 + cd.usSweepAngle = 270; // three-quarter pie (for the sum of the
153 + // above values: 100+200+300 = 600)
154 + cd.cValues = 3; // array count
155 + cd.padValues = &adData[0];
156 + cd.palColors = &alColors[0];
157 + cd.papszDescriptions = &apszDescriptions[0];
158 + WinSendMsg(hwndChart, CHTM_SETCHARTDATA, &cd, NULL);
159 *
160 * Note: Version numbering in this file relates to XWorkplace version
161 * numbering.
162 *
163 *@@header "helpers\comctl.h"
164 *@@added V0.9.3 (2000-05-21) [umoeller].
165 */
166
167/*
168 * Copyright (C) 1997-2002 Ulrich M”ller.
169 * This file is part of the "XWorkplace helpers" source package.
170 * This is free software; you can redistribute it and/or modify
171 * it under the terms of the GNU General Public License as published
172 * by the Free Software Foundation, in version 2 as it comes in the
173 * "COPYING" file of the XWorkplace main distribution.
174 * This program is distributed in the hope that it will be useful,
175 * but WITHOUT ANY WARRANTY; without even the implied warranty of
176 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
177 * GNU General Public License for more details.
178 */
179
180#define OS2EMX_PLAIN_CHAR
181 // this is needed for "os2emx.h"; if this is defined,
182 // emx will define PSZ as _signed_ char, otherwise
183 // as unsigned char
184
185#define INCL_DOSEXCEPTIONS
186#define INCL_DOSPROCESS
187#define INCL_DOSSEMAPHORES
188#define INCL_DOSERRORS
189
190#define INCL_WINWINDOWMGR
191#define INCL_WINFRAMEMGR
192#define INCL_WINMESSAGEMGR
193#define INCL_WININPUT
194#define INCL_WINPOINTERS
195#define INCL_WINTRACKRECT
196#define INCL_WINTIMER
197#define INCL_WINSYS
198
199#define INCL_WINRECTANGLES /// xxx temporary
200
201#define INCL_WINMENUS
202#define INCL_WINSTATICS
203#define INCL_WINBUTTONS
204#define INCL_WINSTDCNR
205
206#define INCL_GPIPRIMITIVES
207#define INCL_GPILOGCOLORTABLE
208#define INCL_GPILCIDS
209#define INCL_GPIPATHS
210#define INCL_GPIREGIONS
211#define INCL_GPIBITMAPS // added V0.9.1 (2000-01-04) [umoeller]: needed for EMX headers
212#include <os2.h>
213
214#include <stdlib.h>
215#include <stdio.h>
216#include <string.h>
217#include <setjmp.h> // needed for except.h
218#include <assert.h> // needed for except.h
219
220#include "setup.h" // code generation and debugging options
221
222#include "helpers\cnrh.h"
223#include "helpers\except.h" // exception handling
224#include "helpers\gpih.h"
225#include "helpers\linklist.h"
226#include "helpers\winh.h"
227
228#include "helpers\comctl.h"
229
230#pragma hdrstop
231
232/*
233 *@@category: Helpers\PM helpers\Window classes\Chart control
234 * See cctl_chart.c.
235 */
236
237/* ******************************************************************
238 *
239 * Private declarations
240 *
241 ********************************************************************/
242
243/*
244 *@@ CHARTCDATA:
245 * pie chart control data. Composed from the various
246 * chart initialization data.
247 * Stored in QWL_USER of the subclassed static control.
248 * Not available to the application.
249 */
250
251typedef struct _CHARTCDATA
252{
253 // data which is initialized upon creation:
254 PFNWP OldStaticProc; // old static window procedure (from WinSubclassWindow)
255
256 // data which is initialized upon CHTM_SETCHARTDATA/CHTM_SETCHARTSTYLE:
257 HDC hdcMem; // memory device context for bitmap
258 HPS hpsMem; // memory presentation space for bitmap
259 CHARTDATA cd; // chart data: initialized to null values
260 CHARTSTYLE cs; // chart style: initialized to null values
261
262 HBITMAP hbmChart; // chart bitmap (for quick painting)
263 HRGN* paRegions; // pointer to array of GPI regions for each data item
264
265 // user interaction data:
266 LONG lSelected; // zero-based index of selected chart item, or -1 if none
267 LONG lSourceEmphasis; // zero-based index of item with source emphysis, or -1 if none
268 BOOL fHasFocus;
269} CHARTCDATA, *PCHARTCDATA;
270
271/* ******************************************************************
272 *
273 * Chart Control
274 *
275 ********************************************************************/
276
277/*
278 *@@ DrawCenteredText:
279 *
280 *@@added V0.9.12 (2001-05-03) [umoeller]
281 */
282
283STATIC VOID DrawCenteredText(HPS hpsMem,
284 PPOINTL pptlMiddlePoint,
285 const char *pcsz)
286{
287 if (pcsz)
288 {
289 PSZ psz = strdup(pcsz),
290 p2 = psz;
291 if (psz)
292 {
293 ULONG cLineBreaks = 0;
294 LONG lLineSpacing = 0;
295
296 POINTL ptl;
297 memcpy(&ptl, pptlMiddlePoint, sizeof(ptl));
298
299 // count how many lines we have
300 while (p2 = strchr(p2, '\n'))
301 {
302 cLineBreaks++;
303 p2++;
304 }
305
306 if (cLineBreaks)
307 {
308 // if we have more than one line:
309 lLineSpacing = gpihQueryLineSpacing(hpsMem);
310
311 // center vertically
312 ptl.y += (lLineSpacing * cLineBreaks) / 2;
313 }
314
315 p2 = psz;
316 while (TRUE)
317 {
318 PSZ pNext = strchr(p2, '\n');
319 if (pNext)
320 {
321 *pNext = '\0';
322 pNext++;
323 }
324
325 GpiCharStringAt(hpsMem,
326 &ptl,
327 strlen(p2),
328 (PSZ)p2);
329
330 if (pNext)
331 {
332 p2 = pNext;
333 ptl.y -= lLineSpacing;
334 }
335 else
336 break;
337 }
338
339 free(psz);
340 }
341 }
342}
343
344/*
345 *@@ PaintBarChart:
346 * paint implementation for the "bar chart".
347 *
348 * This gets called from ctlCreateChartBitmap if the
349 * current style for the chart control is the "bar
350 * chart" style.
351 *
352 *@@added V0.9.12 (2001-05-03) [umoeller]
353 *@@changed V0.9.12 (2001-05-03) [umoeller]: fixed another maaajor PM resource leak with regions
354 */
355
356STATIC VOID PaintBarChart(HPS hpsMem,
357 PRECTL prclWholeStatic, // in: rectl to paint into
358 PCHARTDATA pChartData, // in: chart data
359 PCHARTSTYLE pChartStyle, // in: chart style
360 double dTotal, // in: sum of all values in pChartData
361 LONG lTextColor, // in: description text color (RGB)
362 HRGN* paRegions) // out: GPI regions for each data item
363{
364 ULONG ulYBottomNow = 0;
365 ULONG ul;
366
367 PLONG plColorThis;
368 PSZ *ppszDescriptionThis = NULL;
369 HRGN *phRegionThis;
370 double *pdValueThis;
371
372 // allocate array for storing text positions later
373 PPOINTL paptlDescriptions = (PPOINTL)malloc(sizeof(POINTL)
374 * pChartData->cValues);
375
376 RECTL rclPaint;
377
378 POINTL ptlLowerLeft,
379 ptlUpperRight;
380
381 // thickness of line separators for CHS_DRAWLINES
382 // V0.9.12 (2001-05-03) [umoeller]
383 const SIZEL cszlDrawLines = {1, 1};
384
385 // calculate rectangle;
386 // this is the size of the static control
387 // minus the "3D thickness", if enabled
388 memcpy(&rclPaint, prclWholeStatic, sizeof(RECTL));
389 if (pChartStyle->ulStyle & CHS_3D_BRIGHT)
390 // this includes CHS_3D_DARKEN
391 {
392 rclPaint.yBottom = prclWholeStatic->yBottom
393 + ulYBottomNow;
394 rclPaint.yTop = prclWholeStatic->yTop
395 - pChartStyle->ulThickness
396 + ulYBottomNow;
397 }
398
399 // c) Strangely, GpiSetPattern does work,
400 // while GpiSetColor doesn't (see below).
401 GpiSetPattern(hpsMem, PATSYM_SOLID);
402
403 // initialize per-item data pointers for
404 // loop below
405 pdValueThis = pChartData->padValues;
406 plColorThis = pChartData->palColors;
407 ppszDescriptionThis = pChartData->papszDescriptions;
408 phRegionThis = paRegions;
409
410 // initialize corners
411 ptlLowerLeft.x = 0;
412 ptlLowerLeft.y = 0;
413 ptlUpperRight.x = 0;
414 ptlUpperRight.y = rclPaint.yTop;
415
416 // inner "slice loop":
417 // this loop goes over the data pointers
418 // and paints accordingly. At the end of
419 // the loop, we'll advance all those pointers.
420 for (ul = 0;
421 ul < pChartData->cValues;
422 ul++)
423 {
424 HRGN hrgnThis;
425 /* SHORT sSweepAngle,
426 sStartAngle; */
427
428 GpiSetCurrentPosition(hpsMem, &ptlLowerLeft);
429
430 _Pmpf((__FUNCTION__ ": ptlLowerLeft.x = %d", ptlLowerLeft.x));
431
432 // calc upper right from data values
433 ptlUpperRight.x += (ULONG)( (double)rclPaint.xRight
434 * (*pdValueThis)
435 / dTotal);
436
437 // set the area (fill) color
438 GpiSetColor(hpsMem,
439 *plColorThis);
440
441 // now draw the slice;
442 // we could use an area, but since we need
443 // to remember the coordinates of the slice
444 // for mouse click handling, we require a
445 // region. But areas cannot be converted
446 // to regions, so we use a path instead.
447 GpiBeginPath(hpsMem,
448 1); // path ID, must be 1
449
450 GpiBox(hpsMem,
451 DRO_OUTLINE,
452 &ptlUpperRight,
453 0, 0); // no rounding
454
455 GpiEndPath(hpsMem);
456
457 // convert the path to a region;
458 // we'll need the region for mouse hit testing later
459 hrgnThis = GpiPathToRegion(hpsMem,
460 1,
461 FPATH_ALTERNATE);
462 // after this, the path is deleted
463
464 // now, this FINALLY paints the slice
465 GpiPaintRegion(hpsMem, hrgnThis);
466
467 // now paint descriptions
468 if (pChartStyle->ulStyle & CHS_DESCRIPTIONS)
469 {
470 // description strings valid?
471 if (ppszDescriptionThis)
472 {
473 POINTL ptlMiddlePoint;
474
475 // set presentation color
476 GpiSetColor(hpsMem, lTextColor);
477
478 // set text aligment to centered
479 // both horizontally and vertically;
480 // this affects subsequent GpiCharStringAt
481 // calls in that the output text will
482 // be centered around the specified
483 // point
484 GpiSetTextAlignment(hpsMem,
485 TA_CENTER, // horizontally
486 TA_HALF); // center vertically
487
488
489
490 // center the text in the box
491 ptlMiddlePoint.x = ptlLowerLeft.x
492 + (ptlUpperRight.x - ptlLowerLeft.x) / 2;
493 ptlMiddlePoint.y = ptlLowerLeft.y
494 + (ptlUpperRight.y - ptlLowerLeft.y) / 2;
495
496 // FINALLY, draw the description
497 // at this point; since we have used
498 // GpiSetTextAlignment above, the
499 // text will be centered on exactly
500 // that point
501 DrawCenteredText(hpsMem,
502 &ptlMiddlePoint,
503 *ppszDescriptionThis);
504
505 } // end if (ppszDescriptionThis)
506 } // end if (pChartStyle->ulStyle & CHS_DESCRIPTIONS)
507
508 // last run (are we painting the top now)?
509 if ( // if separator lines are enabled,
510 // draw a frame around the thing
511 (pChartStyle->ulStyle & CHS_DRAWLINES)
512 )
513 {
514 GpiSetColor(hpsMem, lTextColor);
515 GpiFrameRegion(hpsMem,
516 hrgnThis,
517 (PSIZEL)&cszlDrawLines); // always (1, 1)
518 }
519
520 if ( phRegionThis
521 )
522 // region output requested by caller:
523 // store region, the caller will clean this up
524 *phRegionThis = hrgnThis;
525 else
526 // no region output requested: destroy region
527 GpiDestroyRegion(hpsMem, hrgnThis);
528
529 // use the "right" xpos we had now as the "left"
530 // xpos for the next loop
531 ptlLowerLeft.x = ptlUpperRight.x;
532
533 // advance the data pointers
534 pdValueThis++;
535 plColorThis++;
536 if (ppszDescriptionThis)
537 ppszDescriptionThis++;
538 if (phRegionThis)
539 phRegionThis++;
540 } // end for (ul...)
541
542 // cleanup
543 free(paptlDescriptions);
544}
545
546/*
547 *@@ PaintPieChart:
548 * paint implementation for the "pie chart".
549 *
550 * This gets called from ctlCreateChartBitmap if the
551 * current style for the chart control is the "pie
552 * chart" style.
553 *
554 *@@added V0.9.12 (2001-05-03) [umoeller]
555 *@@changed V0.9.12 (2001-05-03) [umoeller]: fixed another maaajor PM resource leak with regions
556 *@@changed V0.9.12 (2001-05-03) [umoeller]: fixed rounding errors
557 */
558
559STATIC VOID PaintPieChart(HPS hpsMem,
560 PRECTL prclWholeStatic, // in: rectl to paint into
561 PCHARTDATA pChartData, // in: chart data
562 PCHARTSTYLE pChartStyle, // in: chart style
563 double dTotal, // in: sum of all values in pChartData
564 LONG lTextColor, // in: description text color (RGB)
565 HRGN* paRegions) // out: GPI regions for each data item
566{
567 ULONG ulYBottomNow = 0;
568 ULONG ul;
569
570 // allocate array for storing text positions later
571 PPOINTL paptlDescriptions = (PPOINTL)malloc(sizeof(POINTL)
572 * pChartData->cValues);
573 POINTL ptlCenter;
574
575 FIXED fxPieSize = (LONG)(pChartStyle->dPieSize * 65536),
576 fxDescriptions = (LONG)(pChartStyle->dDescriptions * 65536);
577
578 // We'll paint into the bitmap in two loops:
579 // +-- The outer "3D" loop is executed
580 // | pChartStyle->ulThickness-fold, if
581 // | CHS_3Dxxx has been enabled; otherwise
582 // | just once.
583 // |
584 // | +-- The inner "slice" loop goes thru the
585 // | data fields in pChartData and draws
586 // | the pies accordingly.
587 // |
588 // +-- We then increase the base Y point (ulYBottomNow)
589 // by one and draw again, thereby getting the 3D
590 // effect.
591
592 // 1) outer 3D loop
593 do // while ( (pChartStyle->ulStyle & CHS_3D_BRIGHT)...
594 {
595 RECTL rclArc;
596 PLONG plColorThis;
597 PSZ *ppszDescriptionThis = NULL;
598 PPOINTL pptlDescriptionThis;
599 HRGN *phRegionThis;
600 double *pdValueThis;
601
602 ARCPARAMS ap;
603 AREABUNDLE ab;
604
605 double dStartAngle = pChartData->usStartAngle,
606 dSweepAngle = 0;
607
608 // thickness of line separators for CHS_DRAWLINES
609 // V0.9.12 (2001-05-03) [umoeller]
610 const SIZEL cszlDrawLines = {1, 1};
611
612 // this is only TRUE for the last loop
613 BOOL fNowDrawingSurface =
614 (
615 ((pChartStyle->ulStyle & CHS_3D_BRIGHT) == 0)
616 ||
617 (ulYBottomNow == pChartStyle->ulThickness - 1)
618 );
619
620 // // _Pmpf(("Looping, ulYBottomNow: %d", ulYBottomNow));
621
622 // calculate pie rectangle for this loop;
623 // this is the size of the static control
624 // minus the "3D thickness", if enabled
625 memcpy(&rclArc, prclWholeStatic, sizeof(RECTL));
626 if (pChartStyle->ulStyle & CHS_3D_BRIGHT)
627 // this includes CHS_3D_DARKEN
628 {
629 rclArc.yBottom = prclWholeStatic->yBottom
630 + ulYBottomNow;
631 rclArc.yTop = prclWholeStatic->yTop
632 - pChartStyle->ulThickness
633 + ulYBottomNow;
634 }
635
636 // calculate center point
637 ptlCenter.x = rclArc.xRight / 2;
638 ptlCenter.y = ((rclArc.yTop - rclArc.yBottom) / 2) + ulYBottomNow;
639
640 // Now, the "arc" APIs really suck. The following
641 // has cost me hours of testing to find out:
642
643 // a) The arc functions expect some kind of
644 // "default arc" to be defined, which they
645 // refer to. We define the arc as elliptical;
646 // this will be used as the "current arc"
647 // for subsequent arc calls.
648 // (P, S) and (R, Q) define the end points
649 // of the major axes of the ellipse.
650 // The center of the arc will later be
651 // specified with GpiPartialArc (while GpiFullArc
652 // uses the current pen position...
653 // Who created these APIs?!?)
654 ap.lP = ptlCenter.x; // X-axis X
655 ap.lS = 0; // X-axis Y
656 ap.lR = 0; // Y-axis X
657 ap.lQ = ((rclArc.yTop - rclArc.yBottom) / 2);
658 // Y-axis Y
659 GpiSetArcParams(hpsMem, &ap);
660
661 // b) The line primitives determine lines
662 // to be drawn around the pie slices.
663 // We don't want any.
664 GpiSetLineType(hpsMem, LINETYPE_INVISIBLE);
665
666 // c) Strangely, GpiSetPattern does work,
667 // while GpiSetColor doesn't (see below).
668 GpiSetPattern(hpsMem, PATSYM_SOLID);
669
670 // initialize per-item data pointers for
671 // loop below
672 pdValueThis = pChartData->padValues;
673 plColorThis = pChartData->palColors;
674 ppszDescriptionThis = pChartData->papszDescriptions;
675 pptlDescriptionThis = paptlDescriptions;
676 phRegionThis = paRegions;
677
678 // 2) inner "pie slice loop":
679 // this loop goes over the data pointers
680 // and paints accordingly. At the end of
681 // the loop, we'll advance all those pointers.
682 for (ul = 0;
683 ul < pChartData->cValues;
684 ul++)
685 {
686 HRGN hrgnThis;
687 double dSweepAngleRounded,
688 dStartAngleRounded;
689
690 // calculate the angle to sweep to:
691 // a simple rule of three
692 dSweepAngle = *pdValueThis // current data pointer
693 * (double)pChartData->usSweepAngle
694 // maximum angle
695 / dTotal; // total data sum
696
697 // d) And now comes the real fun part.
698 // GpiPartialArc is too dumb to draw
699 // anything on its own, it must _always_
700 // appear within an area or path definition.
701 // Unfortunately, this isn't really said
702 // clearly anywhere.
703 // Even worse, in order to set the color
704 // with which the slice is to be drawn,
705 // one has to define an AREABUNDLE because
706 // the regular GpiSetColor functions don't
707 // seem to work here. Or maybe it's my fault,
708 // but with this awful documentation, who knows.
709
710 // We set the current color in the AREABUNDLE
711 // from the color which was defined in the
712 // pie chart data (this pointer was set above
713 // and will be advanced for the next slice).
714 ab.lColor = *plColorThis;
715
716 // "3D mode" enabled with darkened socket?
717 if ( (pChartStyle->ulStyle & CHS_3D_DARKEN)
718 // not last loop?
719 && (!fNowDrawingSurface)
720 )
721 // darken the current fill color
722 // by halving each color component
723 gpihManipulateRGB(&ab.lColor,
724 .5); // factor
725
726 // set the area (fill) color
727 GpiSetAttrs(hpsMem,
728 PRIM_AREA,
729 ABB_COLOR,
730 0,
731 (PBUNDLE)&ab);
732
733 GpiSetCurrentPosition(hpsMem, &ptlCenter);
734
735 // round the angle values properly
736 dStartAngleRounded = (dStartAngle + .5);
737 dSweepAngleRounded = (dSweepAngle + .5);
738
739 // now draw the pie slice;
740 // we could use an area, but since we need
741 // to remember the coordinates of the slice
742 // for mouse click handling, we require a
743 // region. But areas cannot be converted
744 // to regions, so we use a path instead.
745 GpiBeginPath(hpsMem,
746 1); // path ID, must be 1
747
748 // note that the arc functions use the FIXED type
749 // for representing fractions... GPI was written back
750 // at the days when coprocessors were a luxury, so
751 // a floating point value is represented by a LONG
752 // with the hiword containing the rounded integer
753 // and the loword the remaining fraction. So we
754 // can simply multiply the "double" by 65536, and
755 // we get the FIXED value V0.9.12 (2001-05-03) [umoeller]
756 GpiPartialArc(hpsMem,
757 &ptlCenter,
758 fxPieSize, // calculated from CHARTSTYLE
759 (FIXED)(dStartAngleRounded * (double)65536),
760 (FIXED)(dSweepAngleRounded * (double)65536));
761 // this moves the current position to the outer
762 // point on the ellipse which corresponds to
763 // sSweepAngle
764 GpiEndPath(hpsMem);
765
766 // convert the path to a region;
767 // we'll need the region for mouse hit testing later
768 hrgnThis = GpiPathToRegion(hpsMem, 1,
769 FPATH_ALTERNATE);
770 // after this, the path is deleted
771
772 // last run (are we painting the top now)?
773 if ( (fNowDrawingSurface)
774 // descriptions enabled?
775 && (ppszDescriptionThis)
776 && (*ppszDescriptionThis)
777 )
778 {
779 // yes: calculate position to paint
780 // text at later (we can't do this now,
781 // because it might be overpainted by
782 // the next arc again)
783
784 GpiSetCurrentPosition(hpsMem, &ptlCenter);
785 // move the current position to
786 // the center outer point on the ellipse
787 // (in between sStartAngle and sSweepAngle);
788 // since we're outside an area, this will not
789 // paint anything
790 GpiPartialArc(hpsMem,
791 &ptlCenter,
792 fxDescriptions, // calculated from CHARTSTYLE
793 (FIXED)(dStartAngleRounded * (double)65536),
794 // only half the sweep now:
795 (FIXED)((dSweepAngleRounded / 2) * (double)65536));
796
797 // store this outer point in the array
798 // of description coordinates for later
799 GpiQueryCurrentPosition(hpsMem, pptlDescriptionThis);
800
801 _Pmpf(("pptlDescriptionThis = %d, %d",
802 pptlDescriptionThis->x,
803 pptlDescriptionThis->y));
804 }
805
806 // now, this FINALLY paints the arc
807 GpiPaintRegion(hpsMem, hrgnThis);
808
809 // last run (are we painting the top now)?
810 if ( (fNowDrawingSurface)
811 // if separator lines are enabled,
812 // draw a frame around the thing
813 && (pChartStyle->ulStyle & CHS_DRAWLINES)
814 )
815 {
816 GpiSetColor(hpsMem, lTextColor);
817 GpiFrameRegion(hpsMem,
818 hrgnThis,
819 (PSIZEL)&cszlDrawLines); // always (1, 1)
820 }
821
822 if ( phRegionThis
823 && fNowDrawingSurface // this was missing
824 // V0.9.12 (2001-05-03) [umoeller]
825 // this created tons of regions which
826 // were never deleted
827 )
828 // region output requested by caller:
829 // store region, the caller will clean this up
830 *phRegionThis = hrgnThis;
831 else
832 // no region output requested: destroy region
833 GpiDestroyRegion(hpsMem, hrgnThis);
834
835 // increase the start angle by the sweep angle for next loop
836 dStartAngle += dSweepAngle;
837
838 // advance the data pointers
839 pdValueThis++;
840 plColorThis++;
841 if (ppszDescriptionThis)
842 ppszDescriptionThis++;
843 pptlDescriptionThis++;
844 if (phRegionThis)
845 phRegionThis++;
846 } // end for (ul...)
847
848 // go for next "3D thickness" iteration
849 ulYBottomNow++;
850 } while ( (pChartStyle->ulStyle & CHS_3D_BRIGHT)
851 && (ulYBottomNow < pChartStyle->ulThickness)
852 );
853
854 // now paint descriptions
855 if (pChartStyle->ulStyle & CHS_DESCRIPTIONS)
856 {
857 // we use two pointers during the iteration,
858 // which point to the item corresponding
859 // to the current data item:
860 // 1) pointer to center point on outer border
861 // of partial arc
862 // (calculated above)
863 PPOINTL pptlDescriptionThis = paptlDescriptions;
864 // 2) pointer to current description string
865 PSZ* ppszDescriptionThis = pChartData->papszDescriptions;
866
867 // description strings valid?
868 if (ppszDescriptionThis)
869 {
870 // set presentation color
871 GpiSetColor(hpsMem, lTextColor);
872
873 // set text aligment to centered
874 // both horizontally and vertically;
875 // this affects subsequent GpiCharStringAt
876 // calls in that the output text will
877 // be centered around the specified
878 // point
879 GpiSetTextAlignment(hpsMem,
880 TA_CENTER, // horizontally
881 TA_HALF); // center vertically
882
883 // loop thru data items
884 for (ul = 0;
885 ul < pChartData->cValues;
886 ul++)
887 {
888 POINTL ptlMiddlePoint;
889
890 // when drawing the arcs above, we have,
891 // for each pie slice, stored the middle
892 // point on the outer edge of the ellipse
893 // in the paptlDescriptions POINTL array:
894
895 // ++++
896 // + +
897 // + +
898 // ptlCenter\ + +
899 // \ + + <-- current partial arc
900 // \+ +
901 // +++++++++++X +
902 // + +
903 // + +
904 // + XX <-- point calculated above
905 // + +
906 // + +
907 // ++++++
908
909 // now calculate a middle point between
910 // that outer point on the ellipse and
911 // the center of the ellipse, which will
912 // be the center point for the text
913
914 // ++++
915 // + +
916 // + +
917 // ptlCenter\ + +
918 // \ + + <-- current partial arc
919 // \+ +
920 // ++++++++++++ +
921 // + XX + <-- new middle point
922 // + +
923 // + XX <-- point calculated above
924 // + +
925 // + +
926 // ++++++
927
928 ptlMiddlePoint.x =
929 ptlCenter.x
930 + ((pptlDescriptionThis->x - ptlCenter.x) * 2 / 3);
931 ptlMiddlePoint.y =
932 ptlCenter.y
933 - (ptlCenter.y - pptlDescriptionThis->y) * 2 / 3;
934
935 // FINALLY, draw the description
936 // at this point; since we have used
937 // GpiSetTextAlignment above, the
938 // text will be centered on exactly
939 // that point
940 DrawCenteredText(hpsMem,
941 &ptlMiddlePoint,
942 *ppszDescriptionThis);
943
944 pptlDescriptionThis++;
945 ppszDescriptionThis++;
946 } // end for (ul = 0; ul < pChartData->cValues; ul++)
947 } // end if (ppszDescriptionThis)
948 } // end if (pChartStyle->ulStyle & CHS_DESCRIPTIONS)
949
950 // cleanup
951 free(paptlDescriptions);
952}
953
954/*
955 *@@ ctlCreateChartBitmap:
956 * this creates a new bitmap and paints the
957 * chart into it. This bitmap will be used
958 * in WM_PAINT of ctl_fnwpChart to quickly paint
959 * the chart image.
960 *
961 * The bitmap will be created with the specified
962 * size. The chart will consume all available
963 * space in the bitmap. The returned bitmap is
964 * not selected into any presentation space.
965 *
966 * Note: Description text will be drawn with the
967 * current font specified for the memory PS, and
968 * with the current character cell box.
969 *
970 * This function gets called automatically from
971 * WM_PAINT if we determine that the bitmap for
972 * painting has not been created yet or has been
973 * invalidated (because data or styles changed).
974 *
975 * However, you can also use this function
976 * independently to have a chart bitmap created,
977 * wherever you might need it.
978 * You will then have to fill in the CHARTDATA
979 * and CHARTSTYLE structures before calling
980 * this function.
981 *
982 * If (paRegions != NULL), this function will
983 * create a GPI region for each data item. Each GPI
984 * region will then contain the outline of the
985 * corresponding pie chart slice. This allows
986 * you to easily relate coordinates to pie slieces,
987 * for example for mouse clicks.
988 *
989 * In that case, paRegions must point to an array
990 * of HRGN items, each of which will contain a
991 * region handle later. It is the responsibility
992 * of the caller to free the regions later, using
993 * GpiDestroyRegion on each region handle (with
994 * the hpsMem specified with this function).
995 *
996 * If you're not interested in the regions, set
997 * paRegions to NULL.
998 *
999 * This returns NULLHANDLE if an error occurred.
1000 * This can mean the following:
1001 *
1002 * -- The data is invalid, because the total is 0.
1003 *
1004 * -- The bitmap could not be created (memory?).
1005 *
1006 *@@changed V0.9.12 (2001-05-03) [umoeller]: extracted PaintPieChart
1007 *@@changed V0.9.12 (2001-05-03) [umoeller]: added bar chart style
1008 */
1009
1010HBITMAP ctlCreateChartBitmap(HPS hpsMem, // in: memory PS to use for bitmap creation
1011 LONG lcx, // in: width of bitmap
1012 LONG lcy, // in: height of bitmap
1013 PCHARTDATA pChartData, // in: chart data
1014 PCHARTSTYLE pChartStyle, // in: chart style
1015 LONG lBackgroundColor, // in: color around the chart (RGB)
1016 LONG lTextColor, // in: description text color (RGB)
1017 HRGN* paRegions) // out: GPI regions for each data item
1018{
1019 HBITMAP hbmReturn = NULLHANDLE;
1020
1021 // sum up the values to get the total
1022 ULONG ul = 0;
1023 double dTotal = 0;
1024 double* pdThis = pChartData->padValues;
1025 for (ul = 0; ul < pChartData->cValues; ul++)
1026 {
1027 _Pmpf((" dThis is %d", (ULONG)(*pdThis))); // printf
1028
1029 dTotal += *pdThis;
1030 pdThis++;
1031 }
1032
1033 _Pmpf((__FUNCTION__ ": dTotal is %d", (ULONG)dTotal)); // printf
1034
1035 // avoid division by zero
1036 if (dTotal > 0)
1037 {
1038 RECTL rclWholeStatic;
1039 // get window rectangle (bottom left is 0, 0)
1040 rclWholeStatic.xLeft = 0;
1041 rclWholeStatic.yBottom = 0;
1042 rclWholeStatic.xRight = lcx;
1043 rclWholeStatic.yTop = lcy;
1044
1045 // create bitmap of that size
1046 if ((hbmReturn = gpihCreateBitmap(hpsMem,
1047 rclWholeStatic.xRight,
1048 rclWholeStatic.yTop)))
1049 {
1050 // successfully created:
1051
1052 // associate bitmap with memory PS
1053 GpiSetBitmap(hpsMem, hbmReturn);
1054
1055 // switch the HPS to RGB mode
1056 gpihSwitchToRGB(hpsMem);
1057
1058 // fill bitmap with static's background color
1059 GpiSetColor(hpsMem, lBackgroundColor);
1060 gpihBox(hpsMem,
1061 DRO_FILL,
1062 &rclWholeStatic);
1063
1064 if (pChartStyle->ulStyle & CHS_BARCHART)
1065 PaintBarChart(hpsMem,
1066 &rclWholeStatic,
1067 pChartData,
1068 pChartStyle,
1069 dTotal,
1070 lTextColor,
1071 paRegions);
1072 else
1073 PaintPieChart(hpsMem,
1074 &rclWholeStatic,
1075 pChartData,
1076 pChartStyle,
1077 dTotal,
1078 lTextColor,
1079 paRegions);
1080
1081 // deselect (free) bitmap
1082 GpiSetBitmap(hpsMem, NULLHANDLE);
1083 } // end if (pChtCData->hbmChart ...)
1084 } // end if (dTotal > 0)
1085
1086 return hbmReturn;
1087}
1088
1089/*
1090 *@@ CleanupBitmap:
1091 * this frees the resources associated with
1092 * the chart bitmap.
1093 *
1094 *@@changed V0.9.2 (2000-02-29) [umoeller]: fixed maaajor memory leak
1095 *@@changed V0.9.12 (2001-05-03) [umoeller]: fixed major PM resource leaks
1096 */
1097
1098STATIC VOID CleanupBitmap(PCHARTCDATA pChtCData)
1099{
1100 if (pChtCData)
1101 {
1102 // destroy regions, but not the array itself
1103 // (this is done in CleanupData)
1104 if (pChtCData->hpsMem && pChtCData->paRegions)
1105 {
1106 ULONG ul;
1107 for (ul = 0;
1108 ul < pChtCData->cd.cValues;
1109 ul++)
1110 {
1111 if (pChtCData->paRegions[ul])
1112 {
1113 GpiDestroyRegion(pChtCData->hpsMem, pChtCData->paRegions[ul]);
1114 pChtCData->paRegions[ul] = NULLHANDLE;
1115 }
1116 }
1117 }
1118
1119 // bitmap already created?
1120 if (pChtCData->hbmChart)
1121 {
1122 // free current bitmap
1123 GpiSetBitmap(pChtCData->hpsMem, NULLHANDLE);
1124 // delete bitmap; fails if not freed!
1125 if (!GpiDeleteBitmap(pChtCData->hbmChart))
1126 _Pmpf((__FUNCTION__ ": GpiDeleteBitmap failed."));
1127 pChtCData->hbmChart = NULLHANDLE;
1128 }
1129
1130 if (pChtCData->hpsMem)
1131 {
1132 GpiDestroyPS(pChtCData->hpsMem);
1133 pChtCData->hpsMem = NULLHANDLE;
1134 }
1135 if (pChtCData->hdcMem)
1136 {
1137 DevCloseDC(pChtCData->hdcMem);
1138 pChtCData->hdcMem = NULLHANDLE;
1139 }
1140 }
1141}
1142
1143/*
1144 *@@ CleanupData:
1145 * this frees all allocated resources of the chart
1146 * control, except the bitmap (use CleanupBitmap
1147 * for that) and the CHARTCDATA itself.
1148 *
1149 * Note: CleanupBitmap must be called _before_ this
1150 * function.
1151 */
1152
1153STATIC VOID CleanupData(PCHARTCDATA pChtCData)
1154{
1155 if (pChtCData)
1156 if (pChtCData->cd.cValues)
1157 {
1158 // _Pmpf(("Cleaning up data"));
1159 // values array
1160 if (pChtCData->cd.padValues)
1161 free(pChtCData->cd.padValues);
1162
1163 // colors array
1164 if (pChtCData->cd.palColors)
1165 free(pChtCData->cd.palColors);
1166
1167 // strings array
1168 if (pChtCData->cd.papszDescriptions)
1169 {
1170 ULONG ul;
1171 PSZ *ppszDescriptionThis = pChtCData->cd.papszDescriptions;
1172 for (ul = 0;
1173 ul < pChtCData->cd.cValues;
1174 ul++)
1175 {
1176 if (*ppszDescriptionThis)
1177 free(*ppszDescriptionThis);
1178
1179 ppszDescriptionThis++;
1180 }
1181
1182 free(pChtCData->cd.papszDescriptions);
1183 }
1184
1185 pChtCData->cd.cValues = 0;
1186
1187 // GPI regions array
1188 if (pChtCData->paRegions)
1189 {
1190 free(pChtCData->paRegions);
1191 pChtCData->paRegions = NULL;
1192 }
1193 }
1194}
1195
1196/*
1197 *@@ SetChartData:
1198 * implementation for CHTM_SETCHARTDATA
1199 * in ctl_fnwpChart.
1200 *
1201 *@@added V0.9.2 (2000-02-29) [umoeller]
1202 *@@changed V0.9.12 (2001-05-03) [umoeller]: fixed trap if ptr to descriptions was null
1203 */
1204
1205STATIC VOID SetChartData(HWND hwndChart,
1206 PCHARTCDATA pChtCData,
1207 PCHARTDATA pcdNew)
1208{
1209 ULONG ul = 0;
1210 PSZ *ppszDescriptionSource,
1211 *ppszDescriptionTarget;
1212
1213 // free previous values, if set
1214 if (pChtCData->hbmChart)
1215 CleanupBitmap(pChtCData);
1216
1217 CleanupData(pChtCData);
1218
1219 pChtCData->cd.usStartAngle = pcdNew->usStartAngle;
1220 pChtCData->cd.usSweepAngle = pcdNew->usSweepAngle;
1221 pChtCData->cd.cValues = pcdNew->cValues;
1222
1223 // copy values
1224 pChtCData->cd.padValues = (double*)malloc(sizeof(double) * pcdNew->cValues);
1225 memcpy(pChtCData->cd.padValues,
1226 pcdNew->padValues,
1227 sizeof(double) * pcdNew->cValues);
1228
1229 // copy colors
1230 pChtCData->cd.palColors = (LONG*)malloc(sizeof(LONG) * pcdNew->cValues);
1231 memcpy(pChtCData->cd.palColors,
1232 pcdNew->palColors,
1233 sizeof(LONG) * pcdNew->cValues);
1234
1235 // copy strings
1236 if (!pcdNew->papszDescriptions)
1237 pChtCData->cd.papszDescriptions = NULL;
1238 else
1239 {
1240 pChtCData->cd.papszDescriptions = (PSZ*)malloc(sizeof(PSZ) * pcdNew->cValues);
1241 ppszDescriptionSource = pcdNew->papszDescriptions;
1242 ppszDescriptionTarget = pChtCData->cd.papszDescriptions;
1243 for (ul = 0;
1244 ul < pcdNew->cValues;
1245 ul++)
1246 {
1247 if (*ppszDescriptionSource)
1248 *ppszDescriptionTarget = strdup(*ppszDescriptionSource);
1249 else
1250 *ppszDescriptionTarget = NULL;
1251 ppszDescriptionSource++;
1252 ppszDescriptionTarget++;
1253 }
1254 }
1255
1256 // create an array of GPI region handles
1257 pChtCData->paRegions = (HRGN*)malloc(sizeof(HRGN) * pcdNew->cValues);
1258 // initialize all regions to null
1259 memset(pChtCData->paRegions, 0, sizeof(HRGN) * pcdNew->cValues);
1260
1261 pChtCData->lSelected = -1; // none selected
1262 pChtCData->lSourceEmphasis = -1; // none selected
1263
1264 WinInvalidateRect(hwndChart, NULL, FALSE);
1265}
1266
1267/*
1268 *@@ PaintChart:
1269 * implementation for WM_PAINT
1270 * in ctl_fnwpChart.
1271 *
1272 *@@added V0.9.2 (2000-02-29) [umoeller]
1273 *@@changed V0.9.12 (2001-05-03) [umoeller]: fixed major PM resource leaks
1274 */
1275
1276STATIC VOID PaintChart(HWND hwndChart,
1277 PCHARTCDATA pChtCData,
1278 HPS hps,
1279 PRECTL prclPaint)
1280{
1281 RECTL rclStatic;
1282 WinQueryWindowRect(hwndChart, &rclStatic);
1283
1284 // do we have any values yet?
1285 if (pChtCData->cd.cValues == 0)
1286 {
1287 CHAR szDebug[200];
1288 sprintf(szDebug, "Error, no values set");
1289 WinFillRect(hps,
1290 &rclStatic, // exclusive
1291 CLR_WHITE);
1292 WinDrawText(hps,
1293 strlen(szDebug),
1294 szDebug,
1295 &rclStatic,
1296 CLR_BLACK,
1297 CLR_WHITE,
1298 DT_LEFT | DT_TOP);
1299 }
1300 else
1301 {
1302 // valid values, apparently:
1303 LONG lForegroundColor = winhQueryPresColor(hwndChart,
1304 PP_FOREGROUNDCOLOR,
1305 TRUE, // inherit presparams
1306 SYSCLR_WINDOWTEXT);
1307
1308 gpihSwitchToRGB(hps);
1309
1310 // check if the bitmap needs to be (re)created
1311 if (pChtCData->hbmChart == NULLHANDLE)
1312 {
1313 // no: do it now
1314 HPOINTER hptrOld = winhSetWaitPointer();
1315 LONG lLCIDSet = 0;
1316
1317 if (pChtCData->hpsMem == NULLHANDLE)
1318 {
1319 // first call:
1320 FONTMETRICS FontMetrics;
1321 LONG lPointSize;
1322
1323 // create a memory PS for the bitmap
1324 SIZEL szlPage = {rclStatic.xRight,
1325 rclStatic.yTop};
1326 gpihCreateMemPS(WinQueryAnchorBlock(hwndChart),
1327 &szlPage,
1328 &pChtCData->hdcMem,
1329 &pChtCData->hpsMem);
1330
1331 // get presentation font
1332 lLCIDSet = gpihFindPresFont(hwndChart,
1333 TRUE, // inherit PP
1334 pChtCData->hpsMem,
1335 "8.Helv",
1336 &FontMetrics,
1337 &lPointSize);
1338 // set presentation font
1339 if (lLCIDSet)
1340 {
1341 GpiSetCharSet(pChtCData->hpsMem, lLCIDSet);
1342 if (FontMetrics.fsDefn & FM_DEFN_OUTLINE)
1343 gpihSetPointSize(pChtCData->hpsMem, lPointSize);
1344 }
1345 }
1346
1347 pChtCData->hbmChart = ctlCreateChartBitmap(
1348 pChtCData->hpsMem, // mem PS
1349 rclStatic.xRight, // cx
1350 rclStatic.yTop, // cy
1351 &pChtCData->cd, // data
1352 &pChtCData->cs, // style
1353 // background color:
1354 winhQueryPresColor(hwndChart,
1355 PP_BACKGROUNDCOLOR,
1356 TRUE, // inherit presparams
1357 SYSCLR_DIALOGBACKGROUND),
1358 // description text color:
1359 lForegroundColor,
1360 pChtCData->paRegions);
1361 // out: regions array
1362
1363 // unset and delete font
1364 if (lLCIDSet)
1365 {
1366 GpiSetCharSet(pChtCData->hpsMem, LCID_DEFAULT);
1367 GpiDeleteSetId(pChtCData->hpsMem, lLCIDSet);
1368 }
1369
1370 WinSetPointer(HWND_DESKTOP, hptrOld);
1371 }
1372
1373 if (pChtCData->hbmChart)
1374 {
1375 // draw the chart bitmap
1376 POINTL ptlDest = { 0, 0 };
1377 WinDrawBitmap(hps,
1378 pChtCData->hbmChart,
1379 NULL, // draw whole bitmap
1380 &ptlDest,
1381 0, 0, // colors (don't care)
1382 DBM_NORMAL);
1383
1384 // now draw emphasis, if we have regions
1385 if (pChtCData->paRegions)
1386 {
1387 AREABUNDLE ab;
1388 SIZEL sl = {2, 2};
1389
1390 // 1) source emphasis (even if we don't have focus)
1391 if (pChtCData->lSourceEmphasis != -1)
1392 {
1393 if (pChtCData->paRegions[pChtCData->lSourceEmphasis])
1394 {
1395 // GpiFrameRegion uses the current pattern
1396 // attributes, so we must set the correct
1397 // color in there (GpiSetColor doesn't work)
1398 // V0.9.12 (2001-05-03) [umoeller]
1399
1400 ab.lColor = lForegroundColor;
1401 GpiSetAttrs(hps,
1402 PRIM_AREA,
1403 ABB_COLOR,
1404 0,
1405 (PBUNDLE)&ab);
1406 GpiSetPattern(hps,
1407 PATSYM_DIAG1);
1408 GpiPaintRegion(hps,
1409 pChtCData->paRegions[pChtCData->lSourceEmphasis]);
1410 }
1411 }
1412
1413 // 2) selection
1414
1415 // do we have the focus?
1416 if ( (pChtCData->fHasFocus)
1417 // something selected?
1418 && (pChtCData->lSelected != -1)
1419 )
1420 {
1421 if (pChtCData->paRegions[pChtCData->lSelected])
1422 {
1423 // GpiFrameRegion uses the current pattern
1424 // attributes, so we must set the correct
1425 // color in there (GpiSetColor doesn't work)
1426 // V0.9.12 (2001-05-03) [umoeller]
1427
1428 ab.lColor = RGBCOL_RED; // lForegroundColor;
1429 GpiSetAttrs(hps,
1430 PRIM_AREA,
1431 ABB_COLOR,
1432 0,
1433 (PBUNDLE)&ab);
1434 GpiFrameRegion(hps,
1435 pChtCData->paRegions[pChtCData->lSelected],
1436 &sl);
1437 }
1438 }
1439 }
1440 }
1441 }
1442}
1443
1444/*
1445 *@@ FindItemFromPoint:
1446 * returns the index of the slice under the
1447 * given window coordinates, or -1 if there's
1448 * none.
1449 *
1450 *@@added V0.9.12 (2001-05-03) [umoeller]
1451 */
1452
1453STATIC LONG FindItemFromPoint(PCHARTCDATA pChtCData,
1454 LONG lx,
1455 LONG ly)
1456{
1457 POINTL ptlMouse = {lx, ly};
1458
1459 // data set?
1460 if ( (pChtCData->cd.cValues)
1461 && (pChtCData->paRegions)
1462 )
1463 {
1464 ULONG ul;
1465 for (ul = 0;
1466 ul < pChtCData->cd.cValues;
1467 ul++)
1468 {
1469 HRGN hRgnThis = pChtCData->paRegions[ul];
1470 if (hRgnThis)
1471 {
1472 if (GpiPtInRegion(pChtCData->hpsMem,
1473 hRgnThis,
1474 &ptlMouse)
1475 == PRGN_INSIDE)
1476 {
1477 return ul;
1478 }
1479 }
1480 }
1481 }
1482
1483 return -1;
1484}
1485
1486/*
1487 *@@ SendWMControl:
1488 *
1489 *@@added V0.9.12 (2001-05-03) [umoeller]
1490 */
1491
1492STATIC VOID SendWMControl(HWND hwndChart,
1493 MPARAM mp1Mouse,
1494 USHORT usNotify,
1495 ULONG ulEmphasis, // 0 or 1
1496 LONG lIndex)
1497{
1498 HWND hwndOwner;
1499
1500 if (hwndOwner = WinQueryWindow(hwndChart, QW_OWNER))
1501 {
1502 EMPHASISNOTIFY en;
1503 en.hwndSource = hwndChart;
1504 en.lIndex = lIndex; // can be -1
1505 en.ulEmphasis = ulEmphasis;
1506 en.ptl.x = SHORT1FROMMP(mp1Mouse);
1507 en.ptl.y = SHORT2FROMMP(mp1Mouse);
1508
1509 WinSendMsg(hwndOwner,
1510 WM_CONTROL,
1511 MPFROM2SHORT(WinQueryWindowUShort(hwndChart,
1512 QWS_ID),
1513 usNotify),
1514 &en);
1515 }
1516}
1517
1518/*
1519 *@@ SetEmphasis:
1520 *
1521 *@@added V0.9.12 (2001-05-03) [umoeller]
1522 */
1523
1524STATIC BOOL SetEmphasis(HWND hwndChart,
1525 PCHARTCDATA pChtCData,
1526 ULONG ulEmphasis, // in: 0 == selection, 1 == source emphasis
1527 MPARAM mp1, // in: mp1 with mouse values or -1 if none
1528 LONG lIndex,
1529 BOOL fIsContextMenu)
1530{
1531 BOOL brc = FALSE;
1532
1533 PLONG plOld;
1534
1535 if (ulEmphasis == 0)
1536 plOld = &pChtCData->lSelected;
1537 else if (ulEmphasis == 1)
1538 plOld = &pChtCData->lSourceEmphasis;
1539
1540 if (plOld)
1541 {
1542 if ( (*plOld != lIndex)
1543 || (fIsContextMenu)
1544 )
1545 {
1546 // selection changed:
1547 *plOld = lIndex;
1548
1549 brc = TRUE;
1550
1551 // repaint
1552 WinInvalidateRect(hwndChart, NULL, FALSE);
1553
1554 // send notification to owner
1555 // V0.9.12 (2001-05-03) [umoeller]
1556 SendWMControl(hwndChart,
1557 mp1,
1558 CHTN_EMPHASISCHANGED,
1559 ulEmphasis,
1560 lIndex);
1561 }
1562 }
1563
1564 return brc;
1565}
1566
1567/*
1568 *@@ ctl_fnwpChart:
1569 * window procedure for the "chart" control.
1570 *
1571 * This is not a stand-alone window procedure, but must only
1572 * be used with static controls subclassed by ctlChartFromStatic.
1573 *
1574 *@@added V0.9.0 [umoeller]
1575 *@@changed V0.9.2 (2000-02-29) [umoeller]: added resize support
1576 *@@changed V0.9.2 (2000-02-29) [umoeller]: fixed baaad PM resource leaks, the bitmap was never freed
1577 *@@changed V0.9.12 (2001-05-03) [umoeller]: added WM_CONTEXTMENU support
1578 *@@changed V0.9.20 (2002-07-17) [pr]: added CHTN_SETFOCUS and CHTN_KILLFOCUS support
1579 */
1580
1581MRESULT EXPENTRY ctl_fnwpChart(HWND hwndChart, ULONG msg, MPARAM mp1, MPARAM mp2)
1582{
1583 MRESULT mrc = 0;
1584 PCHARTCDATA pChtCData = (PCHARTCDATA)WinQueryWindowULong(hwndChart, QWL_USER);
1585
1586 if (pChtCData)
1587 {
1588 PFNWP OldStaticProc = pChtCData->OldStaticProc;
1589
1590 switch (msg)
1591 {
1592 /*
1593 *@@ CHTM_SETCHARTDATA:
1594 * user msg to set the pie chart data,
1595 * which is copied to the control's
1596 * memory.
1597 * This can be sent several times to
1598 * have the chart refreshed with new
1599 * values.
1600 *
1601 * Parameters:
1602 * -- PCHARTDATA mp1: new values.
1603 * These will be copied to the chart's internal data.
1604 * -- mp2: unused.
1605 */
1606
1607 case CHTM_SETCHARTDATA:
1608 // _Pmpf(("CHTM_SETCHARTDATA, mp1: 0x%lX", mp1));
1609 if (mp1)
1610 SetChartData(hwndChart,
1611 pChtCData,
1612 (PCHARTDATA)mp1);
1613 break;
1614
1615 /*
1616 *@@ CHTM_SETCHARTSTYLE:
1617 * user msg to set the chart style,
1618 * which is copied to the control's
1619 * memory.
1620 * This can be sent several times to
1621 * have the chart refreshed with new
1622 * styles.
1623 *
1624 * Parameters:
1625 * -- PCHARTSTYLE mp1: new style data.
1626 * This will be copied to the chart's internal data.
1627 * -- mp2: unused.
1628 */
1629
1630 case CHTM_SETCHARTSTYLE:
1631 // _Pmpf(("CHTM_SETCHARTSTYLE, mp1: 0x%lX", mp1));
1632 memcpy(&(pChtCData->cs), mp1, sizeof(CHARTSTYLE));
1633 if (pChtCData->hbmChart)
1634 {
1635 CleanupBitmap(pChtCData);
1636 WinInvalidateRect(hwndChart, NULL, FALSE);
1637 }
1638 break;
1639
1640 /*
1641 *@@ CHTM_ITEMFROMPOINT:
1642 * this can be _sent_ to the chart control
1643 * to query the chart item which surrounds
1644 * the given point. Specify the coordinates
1645 * in two SHORT's in mp1 (as with WM_BUTTON1*
1646 * messages), in window coordinates.
1647 *
1648 * Parameters:
1649 * -- SHORT SHORT1FROMMP(mp1): x
1650 * -- SHORT SHORT1FROMMP(mp1): y
1651 * -- mp2: unused.
1652 *
1653 * Returns:
1654 * -- LONG: data item index (counting from
1655 * 0) or -1 if none, e.g. if no
1656 * data has been set yet or if
1657 * the point is outside the pie
1658 * slices.
1659 *
1660 *@@added V0.9.2 (2000-02-29) [umoeller]
1661 */
1662
1663 case CHTM_ITEMFROMPOINT:
1664 {
1665 mrc = (MPARAM)FindItemFromPoint(pChtCData,
1666 SHORT1FROMMP(mp1),
1667 SHORT2FROMMP(mp1));
1668 break; }
1669
1670 /*
1671 *@@ CHTM_SETEMPHASIS:
1672 * sets emphasis on a chart slice.
1673 *
1674 * Parameters:
1675 *
1676 * LONG mp1: emphasis to set. Currently
1677 * defined values:
1678 *
1679 * -- 0: set selection.
1680 *
1681 * -- 1: set source emphasis.
1682 *
1683 * LONG mp2: zero-based index of slice to
1684 * set emphasis for, or -1 to
1685 * remove that emphasis.
1686 *
1687 * Returns: TRUE if emphasis was changed.
1688 *
1689 *@@added V0.9.12 (2001-05-03) [umoeller]
1690 */
1691
1692 case CHTM_SETEMPHASIS:
1693 {
1694 LONG lEmph = (LONG)mp1;
1695 LONG lIndex = (LONG)mp2;
1696 if ( lEmph > 0
1697 && lEmph < 2
1698 && ( lIndex >= -1
1699 || ( lIndex > 0
1700 && lIndex < pChtCData->cd.cValues
1701 )
1702 )
1703 )
1704 {
1705 mrc = (MPARAM)SetEmphasis(hwndChart,
1706 pChtCData,
1707 lEmph,
1708 (MPARAM)-1,
1709 lIndex,
1710 // force flag:
1711 TRUE);
1712 }
1713 }
1714 break;
1715
1716 /*
1717 * WM_BUTTON1CLICK:
1718 * WM_CONTEXTMENU: V0.9.12 (2001-05-03) [umoeller]
1719 */
1720
1721 case WM_BUTTON1CLICK:
1722 case WM_BUTTON1DBLCLK:
1723 case WM_CONTEXTMENU:
1724 if (pChtCData->cs.ulStyle & CHS_SELECTIONS)
1725 {
1726 LONG lRegionFound = FindItemFromPoint(pChtCData,
1727 SHORT1FROMMP(mp1),
1728 SHORT2FROMMP(mp1));
1729
1730 // selections allowed:
1731 // we then accept WM_CHAR msgs, so
1732 // we need the focus
1733 WinSetFocus(HWND_DESKTOP, hwndChart);
1734 // this invalidates the window
1735
1736 if (msg == WM_CONTEXTMENU)
1737 {
1738 // context menu:
1739 SendWMControl(hwndChart,
1740 mp1,
1741 CHTN_CONTEXTMENU,
1742 -1,
1743 lRegionFound);
1744 }
1745 else
1746 {
1747 // selection changed:
1748 SetEmphasis(hwndChart,
1749 pChtCData,
1750 0, // set selection; caller must set source emphasis
1751 mp1,
1752 lRegionFound,
1753 // force flag:
1754 FALSE);
1755
1756 if (msg == WM_BUTTON1DBLCLK)
1757 SendWMControl(hwndChart,
1758 mp1,
1759 CHTN_ENTER,
1760 0,
1761 lRegionFound);
1762 }
1763 }
1764 else
1765 // no selections allowed:
1766 // just activate the window
1767 WinSetActiveWindow(HWND_DESKTOP, hwndChart);
1768 // the parent frame gets activated this way
1769 mrc = (MPARAM)TRUE;
1770 break;
1771
1772 /*
1773 * WM_SETFOCUS:
1774 * we might need to redraw the selection.
1775 */
1776
1777 case WM_SETFOCUS:
1778 if (pChtCData->cs.ulStyle & CHS_SELECTIONS)
1779 {
1780 // selections allowed:
1781 pChtCData->fHasFocus = (BOOL)mp2;
1782 if (pChtCData->lSelected != -1)
1783 WinInvalidateRect(hwndChart, NULL, FALSE);
1784 }
1785
1786 // 0.9.20 (2002-07-17) [pr]
1787 SendWMControl(hwndChart,
1788 (MPARAM)-1,
1789 (BOOL)mp2 ? CHTN_SETFOCUS : CHTN_KILLFOCUS,
1790 pChtCData->lSourceEmphasis,
1791 pChtCData->lSelected);
1792
1793 break;
1794
1795 /*
1796 * WM_WINDOWPOSCHANGED:
1797 *
1798 */
1799
1800 case WM_WINDOWPOSCHANGED:
1801 {
1802 // this msg is passed two SWP structs:
1803 // one for the old, one for the new data
1804 // (from PM docs)
1805 PSWP pswpNew = (PSWP)mp1;
1806 // PSWP pswpOld = pswpNew + 1;
1807
1808 // resizing?
1809 if (pswpNew->fl & SWP_SIZE)
1810 {
1811 if (pChtCData->hbmChart)
1812 // invalidate bitmap so that
1813 // it will be recreated with new size
1814 CleanupBitmap(pChtCData);
1815
1816 WinInvalidateRect(hwndChart, NULL, FALSE);
1817 }
1818 // return default NULL
1819 break; }
1820
1821 /*
1822 * WM_PRESPARAMCHANGED:
1823 *
1824 */
1825
1826 case WM_PRESPARAMCHANGED:
1827 if (pChtCData->hbmChart)
1828 {
1829 // invalidate bitmap so that
1830 // it will be recreated with new
1831 // fonts and colors
1832 CleanupBitmap(pChtCData);
1833 WinInvalidateRect(hwndChart, NULL, FALSE);
1834 }
1835 break;
1836
1837 /*
1838 * WM_PAINT:
1839 * paint the chart bitmap, which is created
1840 * if necessary (calling ctlCreateChartBitmap).
1841 */
1842
1843 case WM_PAINT:
1844 {
1845 RECTL rclPaint;
1846 HPS hps = WinBeginPaint(hwndChart,
1847 NULLHANDLE, // obtain cached micro-PS
1848 &rclPaint);
1849
1850 PaintChart(hwndChart,
1851 pChtCData,
1852 hps,
1853 &rclPaint);
1854
1855 WinEndPaint(hps);
1856 mrc = 0;
1857 break; }
1858
1859 /*
1860 * WM_DESTROY:
1861 * clean up resources
1862 */
1863
1864 case WM_DESTROY:
1865 CleanupBitmap(pChtCData);
1866
1867 CleanupData(pChtCData);
1868
1869 free(pChtCData);
1870
1871 mrc = (*OldStaticProc)(hwndChart, msg, mp1, mp2);
1872 break;
1873
1874 default:
1875 mrc = (*OldStaticProc)(hwndChart, msg, mp1, mp2);
1876 }
1877 }
1878
1879 return mrc;
1880}
1881
1882/*
1883 *@@ ctlChartFromStatic:
1884 * this function turns an existing static text control into
1885 * a chart control (for visualizing data) by subclassing its
1886 * window procedure with ctl_fnwpChart.
1887 *
1888 * See cctl_chart.c for an overview and usage instructions.
1889 *
1890 *@@added V0.9.0 [umoeller]
1891 */
1892
1893BOOL ctlChartFromStatic(HWND hwndChart) // in: static control to subclass
1894{
1895 if (hwndChart)
1896 {
1897 PFNWP OldStaticProc = WinSubclassWindow(hwndChart, ctl_fnwpChart);
1898
1899 if (OldStaticProc)
1900 {
1901 PCHARTCDATA pChtCData = (PCHARTCDATA)malloc(sizeof(CHARTCDATA));
1902 memset(pChtCData, 0, sizeof(CHARTCDATA));
1903 pChtCData->OldStaticProc = OldStaticProc;
1904 pChtCData->fHasFocus = FALSE;
1905 WinSetWindowPtr(hwndChart, QWL_USER, pChtCData);
1906 return TRUE;
1907 }
1908 }
1909
1910 return FALSE;
1911}
1912
1913
Note: See TracBrowser for help on using the repository browser.