Ignore:
Timestamp:
May 15, 2001, 6:15:18 PM (24 years ago)
Author:
umoeller
Message:

Lotsa fixes from the last two weeks.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/helpers/cctl_chart.c

    r63 r68  
    22/*
    33 *@@sourcefile cctl_chart.c:
    4  *      implementation for the "chart" common control.
    5  *      See comctl.c for an overview.
     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.
    68 *
    79 *      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) == (sizeSlize / 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);
    8159 *
    9160 *      Note: Version numbering in this file relates to XWorkplace version
     
    86237/* ******************************************************************
    87238 *
     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 *
    88273 *   Chart Control
    89274 *
    90275 ********************************************************************/
     276
     277/*
     278 *@@ DrawCenteredText:
     279 *
     280 *@@added V0.9.12 (2001-05-03) [umoeller]
     281 */
     282
     283VOID 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
     356VOID 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
     559VOID 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?!? This might be a most
     654        //    flexible way to do things, but where's the
     655        //    simple stuff?!?)
     656        ap.lP = ptlCenter.x;        // X-axis X
     657        ap.lS = 0;                  // X-axis Y
     658        ap.lR = 0;                  // Y-axis X
     659        ap.lQ = ((rclArc.yTop - rclArc.yBottom) / 2);
     660                                    // Y-axis Y
     661        GpiSetArcParams(hpsMem, &ap);
     662
     663        // b)  The line primitives determine lines
     664        //     to be drawn around the pie slices.
     665        //     We don't want any.
     666        GpiSetLineType(hpsMem, LINETYPE_INVISIBLE);
     667
     668        // c)  Strangely, GpiSetPattern does work,
     669        //     while GpiSetColor doesn't (see below).
     670        GpiSetPattern(hpsMem, PATSYM_SOLID);
     671
     672        // initialize per-item data pointers for
     673        // loop below
     674        pdValueThis = pChartData->padValues;
     675        plColorThis = pChartData->palColors;
     676        ppszDescriptionThis = pChartData->papszDescriptions;
     677        pptlDescriptionThis = paptlDescriptions;
     678        phRegionThis = paRegions;
     679
     680        // 2) inner "pie slice loop":
     681        // this loop goes over the data pointers
     682        // and paints accordingly. At the end of
     683        // the loop, we'll advance all those pointers.
     684        for (ul = 0;
     685             ul < pChartData->cValues;
     686             ul++)
     687        {
     688            HRGN        hrgnThis;
     689            double      dSweepAngleRounded,
     690                        dStartAngleRounded;
     691
     692            // calculate the angle to sweep to:
     693            // a simple rule of three
     694            dSweepAngle = *pdValueThis      // current data pointer
     695                          * (double)pChartData->usSweepAngle
     696                                            // maximum angle
     697                          / dTotal;         // total data sum
     698
     699            // d)  And now comes the real fun part.
     700            //     GpiPartialArc is too dumb to draw
     701            //     anything on its own, it must _always_
     702            //     appear within an area or path definition.
     703            //     Unfortunately, this isn't really said
     704            //     clearly anywhere.
     705            //     Even worse, in order to set the color
     706            //     with which the slice is to be drawn,
     707            //     one has to define an AREABUNDLE because
     708            //     the regular GpiSetColor functions don't
     709            //     seem to work here. Or maybe it's my fault,
     710            //     but with this awful documentation, who knows.
     711
     712            //     We set the current color in the AREABUNDLE
     713            //     from the color which was defined in the
     714            //     pie chart data (this pointer was set above
     715            //     and will be advanced for the next slice).
     716            ab.lColor = *plColorThis;
     717
     718            // "3D mode" enabled with darkened socket?
     719            if (    (pChartStyle->ulStyle & CHS_3D_DARKEN)
     720                    // not last loop?
     721                 && (!fNowDrawingSurface)
     722               )
     723                // darken the current fill color
     724                // by halving each color component
     725                gpihManipulateRGB(&ab.lColor,
     726                                  .5);       // factor
     727
     728            // set the area (fill) color
     729            GpiSetAttrs(hpsMem,
     730                        PRIM_AREA,
     731                        ABB_COLOR,
     732                        0,
     733                        (PBUNDLE)&ab);
     734
     735            GpiSetCurrentPosition(hpsMem, &ptlCenter);
     736
     737            // round the angle values properly
     738            dStartAngleRounded = (dStartAngle + .5);
     739            dSweepAngleRounded = (dSweepAngle + .5);
     740
     741            // now draw the pie slice;
     742            // we could use an area, but since we need
     743            // to remember the coordinates of the slice
     744            // for mouse click handling, we require a
     745            // region. But areas cannot be converted
     746            // to regions, so we use a path instead.
     747            GpiBeginPath(hpsMem,
     748                         1);    // path ID, must be 1
     749
     750            // note that the arc functions use the FIXED type
     751            // for representing fractions... GPI was written back
     752            // at the days when coprocessors were a luxury, so
     753            // a floating point value is represented by a LONG
     754            // with the hiword containing the rounded integer
     755            // and the loword the remaining fraction. So we
     756            // can simply multiply the "double" by 65536, and
     757            // we get the FIXED value V0.9.12 (2001-05-03) [umoeller]
     758            GpiPartialArc(hpsMem,
     759                          &ptlCenter,
     760                          fxPieSize,    // calculated from CHARTSTYLE
     761                          (FIXED)(dStartAngleRounded * (double)65536),
     762                          (FIXED)(dSweepAngleRounded * (double)65536));
     763                // this moves the current position to the outer
     764                // point on the ellipse which corresponds to
     765                // sSweepAngle
     766            GpiEndPath(hpsMem);
     767
     768            // convert the path to a region;
     769            // we'll need the region for mouse hit testing later
     770            hrgnThis = GpiPathToRegion(hpsMem, 1,
     771                                       FPATH_ALTERNATE);
     772                // after this, the path is deleted
     773
     774            // last run (are we painting the top now)?
     775            if (    (fNowDrawingSurface)
     776                    // descriptions enabled?
     777                 && (ppszDescriptionThis)
     778                 && (*ppszDescriptionThis)
     779               )
     780            {
     781                // yes: calculate position to paint
     782                // text at later (we can't do this now,
     783                // because it might be overpainted by
     784                // the next arc again)
     785
     786                GpiSetCurrentPosition(hpsMem, &ptlCenter);
     787                // move the current position to
     788                // the center outer point on the ellipse
     789                // (in between sStartAngle and sSweepAngle);
     790                // since we're outside an area, this will not
     791                // paint anything
     792                GpiPartialArc(hpsMem,
     793                              &ptlCenter,
     794                              fxDescriptions, // calculated from CHARTSTYLE
     795                              (FIXED)(dStartAngleRounded * (double)65536),
     796                              // only half the sweep now:
     797                              (FIXED)((dSweepAngleRounded / 2) * (double)65536));
     798
     799                // store this outer point in the array
     800                // of description coordinates for later
     801                GpiQueryCurrentPosition(hpsMem, pptlDescriptionThis);
     802
     803                _Pmpf(("pptlDescriptionThis = %d, %d",
     804                            pptlDescriptionThis->x,
     805                            pptlDescriptionThis->y));
     806            }
     807
     808            // now, this FINALLY paints the arc
     809            GpiPaintRegion(hpsMem, hrgnThis);
     810
     811            // last run (are we painting the top now)?
     812            if (    (fNowDrawingSurface)
     813                    // if separator lines are enabled,
     814                    // draw a frame around the thing
     815                 && (pChartStyle->ulStyle & CHS_DRAWLINES)
     816               )
     817            {
     818                GpiSetColor(hpsMem, lTextColor);
     819                GpiFrameRegion(hpsMem,
     820                               hrgnThis,
     821                               (PSIZEL)&cszlDrawLines);      // always (1, 1)
     822            }
     823
     824            if (    phRegionThis
     825                 && fNowDrawingSurface      // this was missing
     826                                            // V0.9.12 (2001-05-03) [umoeller]
     827                                            // this created tons of regions which
     828                                            // were never deleted
     829               )
     830                // region output requested by caller:
     831                // store region, the caller will clean this up
     832                *phRegionThis = hrgnThis;
     833            else
     834                // no region output requested: destroy region
     835                GpiDestroyRegion(hpsMem, hrgnThis);
     836
     837            // increase the start angle by the sweep angle for next loop
     838            dStartAngle += dSweepAngle;
     839
     840            // advance the data pointers
     841            pdValueThis++;
     842            plColorThis++;
     843            if (ppszDescriptionThis)
     844                ppszDescriptionThis++;
     845            pptlDescriptionThis++;
     846            if (phRegionThis)
     847                phRegionThis++;
     848        } // end for (ul...)
     849
     850        // go for next "3D thickness" iteration
     851        ulYBottomNow++;
     852    } while (   (pChartStyle->ulStyle & CHS_3D_BRIGHT)
     853             && (ulYBottomNow < pChartStyle->ulThickness)
     854            );
     855
     856    // now paint descriptions
     857    if (pChartStyle->ulStyle & CHS_DESCRIPTIONS)
     858    {
     859        // we use two pointers during the iteration,
     860        // which point to the item corresponding
     861        // to the current data item:
     862        // 1)  pointer to center point on outer border
     863        //     of partial arc
     864        //     (calculated above)
     865        PPOINTL     pptlDescriptionThis = paptlDescriptions;
     866        // 2)  pointer to current description string
     867        PSZ*        ppszDescriptionThis = pChartData->papszDescriptions;
     868
     869        // description strings valid?
     870        if (ppszDescriptionThis)
     871        {
     872            // set presentation color
     873            GpiSetColor(hpsMem, lTextColor);
     874
     875            // set text aligment to centered
     876            // both horizontally and vertically;
     877            // this affects subsequent GpiCharStringAt
     878            // calls in that the output text will
     879            // be centered around the specified
     880            // point
     881            GpiSetTextAlignment(hpsMem,
     882                                TA_CENTER,      // horizontally
     883                                TA_HALF);       // center vertically
     884
     885            // loop thru data items
     886            for (ul = 0;
     887                 ul < pChartData->cValues;
     888                 ul++)
     889            {
     890                POINTL  ptlMiddlePoint;
     891
     892                // when drawing the arcs above, we have,
     893                // for each pie slice, stored the middle
     894                // point on the outer edge of the ellipse
     895                // in the paptlDescriptions POINTL array:
     896
     897                //                ++++
     898                //                +    +
     899                //                +      +
     900                //    ptlCenter\  +       +
     901                //              \ +        + <-- current partial arc
     902                //               \+        +
     903                //     +++++++++++X        +
     904                //     +                   +
     905                //      +                  +
     906                //       +               XX  <-- point calculated above
     907                //        +             +
     908                //           +        +
     909                //             ++++++
     910
     911                // now calculate a middle point between
     912                // that outer point on the ellipse and
     913                // the center of the ellipse, which will
     914                // be the center point for the text
     915
     916                //                ++++
     917                //                +    +
     918                //                +      +
     919                //    ptlCenter\  +       +
     920                //              \ +        + <-- current partial arc
     921                //               \+        +
     922                //     ++++++++++++        +
     923                //     +             XX    + <-- new middle point
     924                //      +                  +
     925                //       +               XX  <-- point calculated above
     926                //        +             +
     927                //           +        +
     928                //             ++++++
     929
     930                ptlMiddlePoint.x =
     931                        ptlCenter.x
     932                        + ((pptlDescriptionThis->x - ptlCenter.x) * 2 / 3);
     933                ptlMiddlePoint.y =
     934                        ptlCenter.y
     935                        - (ptlCenter.y - pptlDescriptionThis->y) * 2 / 3;
     936
     937                // FINALLY, draw the description
     938                // at this point; since we have used
     939                // GpiSetTextAlignment above, the
     940                // text will be centered on exactly
     941                // that point
     942                DrawCenteredText(hpsMem,
     943                                 &ptlMiddlePoint,
     944                                 *ppszDescriptionThis);
     945
     946                pptlDescriptionThis++;
     947                ppszDescriptionThis++;
     948            } // end for (ul = 0; ul < pChartData->cValues; ul++)
     949        } // end if (ppszDescriptionThis)
     950    } // end if (pChartStyle->ulStyle & CHS_DESCRIPTIONS)
     951
     952    // cleanup
     953    free(paptlDescriptions);
     954}
    91955
    92956/*
     
    119983 *
    120984 *      If (paRegions != NULL), this function will
    121  *      create GPI regions for data item. Each GPI
     985 *      create a GPI region for each data item. Each GPI
    122986 *      region will then contain the outline of the
    123987 *      corresponding pie chart slice. This allows
     
    1371001 *      This returns NULLHANDLE if an error occured.
    1381002 *      This can mean the following:
     1003 *
    1391004 *      --  The data is invalid, because the total is 0.
     1005 *
    1401006 *      --  The bitmap could not be created (memory?).
     1007 *
     1008 *@@changed V0.9.12 (2001-05-03) [umoeller]: extracted PaintPieChart
     1009 *@@changed V0.9.12 (2001-05-03) [umoeller]: added bar chart style
    1411010 */
    1421011
     
    1581027    for (ul = 0; ul < pChartData->cValues; ul++)
    1591028    {
     1029        _Pmpf(("    dThis is %d", (ULONG)(*pdThis))); // printf
     1030
    1601031        dTotal += *pdThis;
    1611032        pdThis++;
    1621033    }
     1034
     1035    _Pmpf((__FUNCTION__ ": dTotal is %d", (ULONG)dTotal)); // printf
    1631036
    1641037    // avoid division by zero
     
    1661039    {
    1671040        RECTL       rclWholeStatic;
    168         ULONG       ulYBottomNow = 0;
    169 
    1701041        // get window rectangle (bottom left is 0, 0)
    1711042        rclWholeStatic.xLeft = 0;
     
    1811052            // successfully created:
    1821053
    183             // allocate array for storing text positions later
    184             PPOINTL     paptlDescriptions = (PPOINTL)malloc(sizeof(POINTL)
    185                                                             * pChartData->cValues);
    186             POINTL      ptlCenter;
    187 
    188             FIXED       fxPieSize = (LONG)(pChartStyle->dPieSize * 65536),
    189                         fxDescriptions = (LONG)(pChartStyle->dDescriptions * 65536);
    190 
    1911054            // associate bitmap with memory PS
    1921055            GpiSetBitmap(hpsMem, hbmReturn);
     
    2011064                    &rclWholeStatic);
    2021065
    203             // We'll paint into the bitmap in two loops:
    204             // +--  The outer "3D" loop is executed
    205             // |    pChartStyle->ulThickness-fold, if
    206             // |    CHS_3Dxxx has been enabled; otherwise
    207             // |    just once.
    208             // |
    209             // |    +-- The inner "slice" loop goes thru the
    210             // |        data fields in pChartData and draws
    211             // |        the pies accordingly.
    212             // |
    213             // +--  We then increase the base Y point (ulYBottomNow)
    214             //      by one and draw again, thereby getting the 3D
    215             //      effect.
    216 
    217             // 1) outer 3D loop
    218             do // while (   (pChartStyle->ulStyle & CHS_3D_BRIGHT)...
    219             {
    220                 RECTL       rclArc;
    221                 PLONG       plColorThis;
    222                 PSZ         *ppszDescriptionThis = NULL;
    223                 PPOINTL     pptlDescriptionThis;
    224                 HRGN*       phRegionThis;
    225 
    226                 ARCPARAMS   ap;
    227                 AREABUNDLE  ab;
    228 
    229                 double      dStartAngle = pChartData->usStartAngle,
    230                             dSweepAngle = 0;
    231 
    232                 // this is only TRUE for the last loop
    233                 BOOL        fNowDrawingSurface =
    234                               (
    235                                    ((pChartStyle->ulStyle & CHS_3D_BRIGHT) == 0)
    236                                 ||
    237                                    (ulYBottomNow == pChartStyle->ulThickness - 1)
    238                               );
    239 
    240                 // // _Pmpf(("Looping, ulYBottomNow: %d", ulYBottomNow));
    241 
    242                 // calculate pie rectangle for this loop;
    243                 // this is the size of the static control
    244                 // minus the "3D thickness", if enabled
    245                 memcpy(&rclArc, &rclWholeStatic, sizeof(RECTL));
    246                 if (pChartStyle->ulStyle & CHS_3D_BRIGHT)
    247                         // this includes CHS_3D_DARKEN
    248                 {
    249                     rclArc.yBottom = rclWholeStatic.yBottom
    250                                                 + ulYBottomNow;
    251                     rclArc.yTop = rclWholeStatic.yTop
    252                                                 - pChartStyle->ulThickness
    253                                                 + ulYBottomNow;
    254                 }
    255 
    256                 // calculate center point
    257                 ptlCenter.x = rclArc.xRight / 2;
    258                 ptlCenter.y = ((rclArc.yTop - rclArc.yBottom) / 2) + ulYBottomNow;
    259 
    260                 // Now, the "arc" APIs really suck. The following
    261                 // has cost me hours of testing to find out:
    262 
    263                 // a) The arc functions expect some kind of
    264                 //    "default arc" to be defined, which they
    265                 //    refer to. We define the arc as elliptical;
    266                 //    this will be used as the "current arc"
    267                 //    for subsequent arc calls.
    268                 //    (P, S) and (R, Q) define the end points
    269                 //    of the major axes of the ellipse.
    270                 //    The center of the arc will later be
    271                 //    specified with GpiPartialArc (while GpiFullArc
    272                 //    uses the current pen position...
    273                 //    Who created these APIs?!? This might be a most
    274                 //    flexible way to do things, but where's the
    275                 //    simple stuff?!?)
    276                 ap.lP = ptlCenter.x;        // X-axis X
    277                 ap.lS = 0;                  // X-axis Y
    278                 ap.lR = 0;                  // Y-axis X
    279                 ap.lQ = ((rclArc.yTop - rclArc.yBottom) / 2);
    280                                             // Y-axis Y
    281                 GpiSetArcParams(hpsMem, &ap);
    282 
    283                 // b)  The line primitives determine lines
    284                 //     to be drawn around the pie slices.
    285                 //     We don't want any.
    286                 GpiSetLineType(hpsMem, LINETYPE_INVISIBLE);
    287 
    288                 // c)  Strangely, GpiSetPattern does work,
    289                 //     while GpiSetColor doesn't (see below).
    290                 GpiSetPattern(hpsMem, PATSYM_SOLID);
    291 
    292                 // initialize per-item data pointers for
    293                 // loop below
    294                 pdThis = pChartData->padValues;
    295                 plColorThis = pChartData->palColors;
    296                 ppszDescriptionThis = pChartData->papszDescriptions;
    297                 pptlDescriptionThis = paptlDescriptions;
    298                 phRegionThis = paRegions;
    299 
    300                 // 2) inner "pie slice loop":
    301                 // this loop goes over the data pointers
    302                 // and paints accordingly. At the end of
    303                 // the loop, we'll advance all those pointers.
    304                 for (ul = 0; ul < pChartData->cValues; ul++)
    305                 {
    306                     HRGN        hrgnThis;
    307                     SHORT       sSweepAngle,
    308                                 sStartAngle;
    309 
    310                     // calculate the angle to sweep to:
    311                     // a simple rule of three
    312                     dSweepAngle = *pdThis           // current data pointer
    313                                   * pChartData->usSweepAngle
    314                                                     // maximum angle
    315                                   / dTotal;         // total data sum
    316 
    317                     // d)  And now comes the real fun part.
    318                     //     GpiPartialArc is too dumb to draw
    319                     //     anything on its own, it must _always_
    320                     //     appear within an area or path definition.
    321                     //     Unfortunately, this isn't really said
    322                     //     clearly anywhere.
    323                     //     Even worse, in order to set the color
    324                     //     with which the slice is to be drawn,
    325                     //     one has to define an AREABUNDLE because
    326                     //     the regular GpiSetColor functions don't
    327                     //     seem to work here. Or maybe it's my fault,
    328                     //     but with this awful documentation, who knows.
    329                     //     We use the current color defined in the
    330                     //     pie chart data (this pointer was set above
    331                     //     and will be advanced for the next slice).
    332                     ab.lColor = *plColorThis;
    333 
    334                     // "3D mode" enabled with darkened socket?
    335                     if (    (pChartStyle->ulStyle & CHS_3D_DARKEN)
    336                             // not last loop?
    337                          && (!fNowDrawingSurface)
    338                        )
    339                         // darken the current fill color
    340                         // by halving each color component
    341                         gpihManipulateRGB(&ab.lColor,
    342                                           .5);       // factor
    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);
     1066            if (pChartStyle->ulStyle & CHS_BARCHART)
     1067                PaintBarChart(hpsMem,
     1068                              &rclWholeStatic,
     1069                              pChartData,
     1070                              pChartStyle,
     1071                              dTotal,
     1072                              lTextColor,
     1073                              paRegions);
     1074            else
     1075                PaintPieChart(hpsMem,
     1076                              &rclWholeStatic,
     1077                              pChartData,
     1078                              pChartStyle,
     1079                              dTotal,
     1080                              lTextColor,
     1081                              paRegions);
    5381082
    5391083            // deselect (free) bitmap
     
    5511095 *
    5521096 *@@changed V0.9.2 (2000-02-29) [umoeller]: fixed maaajor memory leak
     1097 *@@changed V0.9.12 (2001-05-03) [umoeller]: fixed major PM resource leaks
    5531098 */
    5541099
     
    5571102    if (pChtCData)
    5581103    {
     1104        // destroy regions, but not the array itself
     1105        // (this is done in CleanupData)
     1106        if (pChtCData->hpsMem && pChtCData->paRegions)
     1107        {
     1108            ULONG   ul;
     1109            for (ul = 0;
     1110                 ul < pChtCData->cd.cValues;
     1111                 ul++)
     1112            {
     1113                if (pChtCData->paRegions[ul])
     1114                {
     1115                    GpiDestroyRegion(pChtCData->hpsMem, pChtCData->paRegions[ul]);
     1116                    pChtCData->paRegions[ul] = NULLHANDLE;
     1117                }
     1118            }
     1119        }
     1120
    5591121        // bitmap already created?
    5601122        if (pChtCData->hbmChart)
    5611123        {
    5621124            // free current bitmap
    563             // GpiSetBitmap(pChtCData->hpsMem, NULLHANDLE);
     1125            GpiSetBitmap(pChtCData->hpsMem, NULLHANDLE);
    5641126            // delete bitmap; fails if not freed!
    565             GpiDeleteBitmap(pChtCData->hbmChart);
     1127            if (!GpiDeleteBitmap(pChtCData->hbmChart))
     1128                _Pmpf((__FUNCTION__ ": GpiDeleteBitmap failed."));
    5661129            pChtCData->hbmChart = NULLHANDLE;
    5671130        }
    5681131
    569         // destroy regions, but not the array itself
    570         // (this is done in CleanupData)
    571         if (pChtCData->paRegions)
     1132        if (pChtCData->hpsMem)
    5721133        {
    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             }
     1134            GpiDestroyPS(pChtCData->hpsMem);
     1135            pChtCData->hpsMem = NULLHANDLE;
     1136        }
     1137        if (pChtCData->hdcMem)
     1138        {
     1139            DevCloseDC(pChtCData->hdcMem);
     1140            pChtCData->hdcMem = NULLHANDLE;
    5861141        }
    5871142    }
     
    6471202 *
    6481203 *@@added V0.9.2 (2000-02-29) [umoeller]
     1204 *@@changed V0.9.12 (2001-05-03) [umoeller]: fixed trap if ptr to descriptions was null
    6491205 */
    6501206
     
    6631219    CleanupData(pChtCData);
    6641220
    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 
    6801221    pChtCData->cd.usStartAngle = pcdNew->usStartAngle;
    6811222    pChtCData->cd.usSweepAngle = pcdNew->usSweepAngle;
     
    6951236
    6961237    // 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++)
     1238    if (!pcdNew->papszDescriptions)
     1239        pChtCData->cd.papszDescriptions = NULL;
     1240    else
    7031241    {
    704         if (*ppszDescriptionSource)
    705             *ppszDescriptionTarget = strdup(*ppszDescriptionSource);
    706         else
    707             *ppszDescriptionTarget = NULL;
    708         ppszDescriptionSource++;
    709         ppszDescriptionTarget++;
     1242        pChtCData->cd.papszDescriptions = (PSZ*)malloc(sizeof(PSZ) * pcdNew->cValues);
     1243        ppszDescriptionSource = pcdNew->papszDescriptions;
     1244        ppszDescriptionTarget = pChtCData->cd.papszDescriptions;
     1245        for (ul = 0;
     1246             ul < pcdNew->cValues;
     1247             ul++)
     1248        {
     1249            if (*ppszDescriptionSource)
     1250                *ppszDescriptionTarget = strdup(*ppszDescriptionSource);
     1251            else
     1252                *ppszDescriptionTarget = NULL;
     1253            ppszDescriptionSource++;
     1254            ppszDescriptionTarget++;
     1255        }
    7101256    }
    7111257
     
    7161262
    7171263    pChtCData->lSelected = -1;     // none selected
     1264    pChtCData->lSourceEmphasis = -1;     // none selected
    7181265
    7191266    WinInvalidateRect(hwndChart, NULL, FALSE);
     
    7261273 *
    7271274 *@@added V0.9.2 (2000-02-29) [umoeller]
     1275 *@@changed V0.9.12 (2001-05-03) [umoeller]: fixed major PM resource leaks
    7281276 */
    7291277
     
    7351283    RECTL   rclStatic;
    7361284    WinQueryWindowRect(hwndChart, &rclStatic);
    737 
    738     // _Pmpf(("ctl_fnwpChart: WM_PAINT, cValues: %d", pChtCData->cd.cValues));
    7391285
    7401286    // do we have any values yet?
     
    7621308                                                   SYSCLR_WINDOWTEXT);
    7631309
    764         // yes: check if we created the bitmap
    765         // already
     1310        gpihSwitchToRGB(hps);
     1311
     1312        // check if the bitmap needs to be (re)created
    7661313        if (pChtCData->hbmChart == NULLHANDLE)
    7671314        {
    7681315            // no: do it now
    7691316            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)
     1317            LONG        lLCIDSet = 0;
     1318
     1319            if (pChtCData->hpsMem == NULLHANDLE)
    7821320            {
    783                 GpiSetCharSet(pChtCData->hpsMem, lLCIDSet);
    784                 if (FontMetrics.fsDefn & FM_DEFN_OUTLINE)
    785                     gpihSetPointSize(pChtCData->hpsMem, lPointSize);
     1321                // first call:
     1322                FONTMETRICS FontMetrics;
     1323                LONG        lPointSize;
     1324
     1325                // create a memory PS for the bitmap
     1326                SIZEL szlPage = {rclStatic.xRight,
     1327                                 rclStatic.yTop};
     1328                gpihCreateMemPS(WinQueryAnchorBlock(hwndChart),
     1329                                &szlPage,
     1330                                &pChtCData->hdcMem,
     1331                                &pChtCData->hpsMem);
     1332
     1333                // get presentation font
     1334                lLCIDSet = gpihFindPresFont(hwndChart,
     1335                                            TRUE,      // inherit PP
     1336                                            pChtCData->hpsMem,
     1337                                            "8.Helv",
     1338                                            &FontMetrics,
     1339                                            &lPointSize);
     1340                // set presentation font
     1341                if (lLCIDSet)
     1342                {
     1343                    GpiSetCharSet(pChtCData->hpsMem, lLCIDSet);
     1344                    if (FontMetrics.fsDefn & FM_DEFN_OUTLINE)
     1345                        gpihSetPointSize(pChtCData->hpsMem, lPointSize);
     1346                }
    7861347            }
    787 
    788 
    789             gpihSwitchToRGB(hps);
    7901348
    7911349            pChtCData->hbmChart = ctlCreateChartBitmap(
     
    8041362                      pChtCData->paRegions);
    8051363                              // out: regions array
    806             // _Pmpf(("Created bitmap 0x%lX", pChtCData->hbmChart));
    8071364
    8081365            // unset and delete font
    809             GpiSetCharSet(pChtCData->hpsMem, LCID_DEFAULT);
    8101366            if (lLCIDSet)
     1367            {
     1368                GpiSetCharSet(pChtCData->hpsMem, LCID_DEFAULT);
    8111369                GpiDeleteSetId(pChtCData->hpsMem, lLCIDSet);
     1370            }
    8121371
    8131372            WinSetPointer(HWND_DESKTOP, hptrOld);
     
    8161375        if (pChtCData->hbmChart)
    8171376        {
     1377            // draw the chart bitmap
    8181378            POINTL  ptlDest = { 0, 0 };
    8191379            WinDrawBitmap(hps,
     
    8241384                          DBM_NORMAL);
    8251385
    826             // do we have the focus?
    827             if (pChtCData->fHasFocus)
    828                 // something selected?
    829                 if (pChtCData->lSelected != -1)
    830                     if (pChtCData->paRegions)
     1386            // now draw emphasis, if we have regions
     1387            if (pChtCData->paRegions)
     1388            {
     1389                AREABUNDLE ab;
     1390                SIZEL   sl = {2, 2};
     1391
     1392                // 1) source emphasis (even if we don't have focus)
     1393                if (pChtCData->lSourceEmphasis != -1)
     1394                {
     1395                    if (pChtCData->paRegions[pChtCData->lSourceEmphasis])
    8311396                    {
    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                         }
     1397                        // GpiFrameRegion uses the current pattern
     1398                        // attributes, so we must set the correct
     1399                        // color in there (GpiSetColor doesn't work)
     1400                        // V0.9.12 (2001-05-03) [umoeller]
     1401
     1402                        ab.lColor = lForegroundColor;
     1403                        GpiSetAttrs(hps,
     1404                                    PRIM_AREA,
     1405                                    ABB_COLOR,
     1406                                    0,
     1407                                    (PBUNDLE)&ab);
     1408                        GpiSetPattern(hps,
     1409                                      PATSYM_DIAG1);
     1410                        GpiPaintRegion(hps,
     1411                                       pChtCData->paRegions[pChtCData->lSourceEmphasis]);
    8431412                    }
     1413                }
     1414
     1415                // 2) selection
     1416
     1417                // do we have the focus?
     1418                if (    (pChtCData->fHasFocus)
     1419                        // something selected?
     1420                     && (pChtCData->lSelected != -1)
     1421                   )
     1422                {
     1423                    if (pChtCData->paRegions[pChtCData->lSelected])
     1424                    {
     1425                        // GpiFrameRegion uses the current pattern
     1426                        // attributes, so we must set the correct
     1427                        // color in there (GpiSetColor doesn't work)
     1428                        // V0.9.12 (2001-05-03) [umoeller]
     1429
     1430                        ab.lColor = RGBCOL_RED; // lForegroundColor;
     1431                        GpiSetAttrs(hps,
     1432                                    PRIM_AREA,
     1433                                    ABB_COLOR,
     1434                                    0,
     1435                                    (PBUNDLE)&ab);
     1436                        GpiFrameRegion(hps,
     1437                                       pChtCData->paRegions[pChtCData->lSelected],
     1438                                       &sl);
     1439                    }
     1440                }
     1441            }
    8441442        }
    8451443    }
     
    8471445
    8481446/*
     1447 *@@ FindItemFromPoint:
     1448 *      returns the index of the slice under the
     1449 *      given window coordinates, or -1 if there's
     1450 *      none.
     1451 *
     1452 *@@added V0.9.12 (2001-05-03) [umoeller]
     1453 */
     1454
     1455LONG FindItemFromPoint(PCHARTCDATA pChtCData,
     1456                       LONG lx,
     1457                       LONG ly)
     1458{
     1459    LONG lRegionFound = -1; // none
     1460
     1461    POINTL ptlMouse = {lx, ly};
     1462
     1463    // data set?
     1464    if (    (pChtCData->cd.cValues)
     1465         && (pChtCData->paRegions)
     1466       )
     1467    {
     1468        ULONG   ul;
     1469        for (ul = 0;
     1470             ul < pChtCData->cd.cValues;
     1471             ul++)
     1472        {
     1473            HRGN hRgnThis = pChtCData->paRegions[ul];
     1474            if (hRgnThis)
     1475            {
     1476                if (GpiPtInRegion(pChtCData->hpsMem,
     1477                                  hRgnThis,
     1478                                  &ptlMouse)
     1479                     == PRGN_INSIDE)
     1480                {
     1481                    return (ul);
     1482                }
     1483            }
     1484        }
     1485    }
     1486
     1487    return (-1);
     1488}
     1489
     1490/*
     1491 *@@ SendWMControl:
     1492 *
     1493 *@@added V0.9.12 (2001-05-03) [umoeller]
     1494 */
     1495
     1496VOID SendWMControl(HWND hwndChart,
     1497                   MPARAM mp1Mouse,
     1498                   USHORT usNotify,
     1499                   ULONG ulEmphasis,            // 0 or 1
     1500                   LONG lIndex)
     1501{
     1502    HWND hwndOwner;
     1503
     1504    if (hwndOwner = WinQueryWindow(hwndChart, QW_OWNER))
     1505    {
     1506        EMPHASISNOTIFY en;
     1507        en.hwndSource = hwndChart;
     1508        en.lIndex = lIndex;        // can be -1
     1509        en.ulEmphasis = ulEmphasis;
     1510        en.ptl.x = SHORT1FROMMP(mp1Mouse);
     1511        en.ptl.y = SHORT2FROMMP(mp1Mouse);
     1512
     1513        WinSendMsg(hwndOwner,
     1514                   WM_CONTROL,
     1515                   MPFROM2SHORT(WinQueryWindowUShort(hwndChart,
     1516                                                     QWS_ID),
     1517                                usNotify),
     1518                   &en);
     1519    }
     1520}
     1521
     1522/*
     1523 *@@ SetEmphasis:
     1524 *
     1525 *@@added V0.9.12 (2001-05-03) [umoeller]
     1526 */
     1527
     1528BOOL SetEmphasis(HWND hwndChart,
     1529                 PCHARTCDATA pChtCData,
     1530                 ULONG ulEmphasis,      // in: 0 == selection, 1 == source emphasis
     1531                 MPARAM mp1,            // in: mp1 with mouse values or -1 if none
     1532                 LONG lIndex,
     1533                 BOOL fIsContextMenu)
     1534{
     1535    BOOL brc = FALSE;
     1536
     1537    PLONG plOld;
     1538
     1539    if (ulEmphasis == 0)
     1540        plOld = &pChtCData->lSelected;
     1541    else if (ulEmphasis == 1)
     1542        plOld = &pChtCData->lSourceEmphasis;
     1543
     1544    if (plOld)
     1545    {
     1546        if (    (*plOld != lIndex)
     1547             || (fIsContextMenu)
     1548           )
     1549        {
     1550            // selection changed:
     1551            *plOld = lIndex;
     1552
     1553            brc = TRUE;
     1554
     1555            // repaint
     1556            WinInvalidateRect(hwndChart, NULL, FALSE);
     1557
     1558            // send notification to owner
     1559            // V0.9.12 (2001-05-03) [umoeller]
     1560            SendWMControl(hwndChart,
     1561                          mp1,
     1562                          CHTN_EMPHASISCHANGED,
     1563                          ulEmphasis,
     1564                          lIndex);
     1565        }
     1566    }
     1567
     1568    return (brc);
     1569}
     1570
     1571/*
    8491572 *@@ ctl_fnwpChart:
    8501573 *      window procedure for the "chart" control.
     
    8561579 *@@changed V0.9.2 (2000-02-29) [umoeller]: added resize support
    8571580 *@@changed V0.9.2 (2000-02-29) [umoeller]: fixed baaad PM resource leaks, the bitmap was never freed
     1581 *@@changed V0.9.12 (2001-05-03) [umoeller]: added WM_CONTEXTMENU support
    8581582 */
    8591583
     
    8871611                // _Pmpf(("CHTM_SETCHARTDATA, mp1: 0x%lX", mp1));
    8881612                if (mp1)
    889                 {
    890                     PCHARTDATA   pcdNew = (PCHARTDATA)mp1;
    891                     SetChartData(hwndChart, pChtCData, pcdNew);
    892                 }
     1613                    SetChartData(hwndChart,
     1614                                 pChtCData,
     1615                                 (PCHARTDATA)mp1);
    8931616            break;
    8941617
     
    9431666            case CHTM_ITEMFROMPOINT:
    9441667            {
    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)
     1668                mrc = (MPARAM)FindItemFromPoint(pChtCData,
     1669                                                SHORT1FROMMP(mp1),
     1670                                                SHORT2FROMMP(mp1));
     1671            break; }
     1672
     1673            /*
     1674             *@@ CHTM_SETEMPHASIS:
     1675             *      sets emphasis on a chart slice.
     1676             *
     1677             *      Parameters:
     1678             *
     1679             *      LONG mp1: emphasis to set. Currently
     1680             *              defined values:
     1681             *
     1682             *              -- 0: set selection.
     1683             *
     1684             *              -- 1: set source emphasis.
     1685             *
     1686             *      LONG mp2: zero-based index of slice to
     1687             *              set emphasis for, or -1 to
     1688             *              remove that emphasis.
     1689             *
     1690             *      Returns: TRUE if emphasis was changed.
     1691             *
     1692             *@@added V0.9.12 (2001-05-03) [umoeller]
     1693             */
     1694
     1695            case CHTM_SETEMPHASIS:
     1696            {
     1697                LONG lEmph = (LONG)mp1;
     1698                LONG lIndex = (LONG)mp2;
     1699                if (    lEmph > 0
     1700                     && lEmph < 2
     1701                     && (    lIndex >= -1
     1702                          || (    lIndex > 0
     1703                               && lIndex < pChtCData->cd.cValues
     1704                             )
     1705                        )
     1706                   )
    9541707                {
    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                     }
     1708                    mrc = (MPARAM)SetEmphasis(hwndChart,
     1709                                              pChtCData,
     1710                                              lEmph,
     1711                                              (MPARAM)-1,
     1712                                              lIndex,
     1713                                              // force flag:
     1714                                              TRUE);
    9801715                }
    981 
    982                 mrc = (MPARAM)lRegionFound;
    983             break; }
     1716            }
     1717            break;
    9841718
    9851719            /*
    986              * WM_BUTTON1DOWN:
    987              *
     1720             * WM_BUTTON1CLICK:
     1721             * WM_CONTEXTMENU: V0.9.12 (2001-05-03) [umoeller]
    9881722             */
    9891723
    990             case WM_BUTTON1DOWN:
     1724            case WM_BUTTON1CLICK:
     1725            case WM_BUTTON1DBLCLK:
     1726            case WM_CONTEXTMENU:
    9911727                if (pChtCData->cs.ulStyle & CHS_SELECTIONS)
    9921728                {
    993                     LONG lRegionFound = (LONG)WinSendMsg(hwndChart,
    994                                                          CHTM_ITEMFROMPOINT,
    995                                                          mp1,
    996                                                          NULL);
     1729                    LONG lRegionFound = FindItemFromPoint(pChtCData,
     1730                                                SHORT1FROMMP(mp1),
     1731                                                SHORT2FROMMP(mp1));
    9971732
    9981733                    // selections allowed:
     
    10011736                    WinSetFocus(HWND_DESKTOP, hwndChart);
    10021737                        // this invalidates the window
    1003                     if (pChtCData->lSelected != lRegionFound)
     1738
     1739                    if (msg == WM_CONTEXTMENU)
     1740                    {
     1741                        // context menu:
     1742                        SendWMControl(hwndChart,
     1743                                      mp1,
     1744                                      CHTN_CONTEXTMENU,
     1745                                      -1,
     1746                                      lRegionFound);
     1747                    }
     1748                    else
    10041749                    {
    10051750                        // selection changed:
    1006                         pChtCData->lSelected = lRegionFound;
    1007                         // repaint
    1008                         WinInvalidateRect(hwndChart, NULL, FALSE);
     1751                        SetEmphasis(hwndChart,
     1752                                    pChtCData,
     1753                                    0,    // set selection; caller must set source emphasis
     1754                                    mp1,
     1755                                    lRegionFound,
     1756                                    // force flag:
     1757                                    FALSE);
     1758
     1759                        if (msg == WM_BUTTON1DBLCLK)
     1760                            SendWMControl(hwndChart,
     1761                                          mp1,
     1762                                          CHTN_ENTER,
     1763                                          0,
     1764                                          lRegionFound);
    10091765                    }
    10101766                }
     
    10471803                // resizing?
    10481804                if (pswpNew->fl & SWP_SIZE)
     1805                {
    10491806                    if (pChtCData->hbmChart)
    1050                     {
    10511807                        // invalidate bitmap so that
    10521808                        // it will be recreated with new size
    10531809                        CleanupBitmap(pChtCData);
    1054                         WinInvalidateRect(hwndChart, NULL, FALSE);
    1055                     }
    1056 
     1810
     1811                    WinInvalidateRect(hwndChart, NULL, FALSE);
     1812                }
    10571813                // return default NULL
    10581814            break; }
     
    11031859            case WM_DESTROY:
    11041860                CleanupBitmap(pChtCData);
     1861
    11051862                CleanupData(pChtCData);
    11061863
    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!"));
    11131864                free(pChtCData);
    11141865
     
    11301881 *      window procedure with ctl_fnwpChart.
    11311882 *
    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);
     1883 *      See cctl_chart.c for an overview and usage instructions.
    12511884 *
    12521885 *@@added V0.9.0 [umoeller]
Note: See TracChangeset for help on using the changeset viewer.