Changeset 68 for trunk/src/helpers/cctl_chart.c
- Timestamp:
- May 15, 2001, 6:15:18 PM (24 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/helpers/cctl_chart.c
r63 r68 2 2 /* 3 3 *@@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. 6 8 * 7 9 * This has been extracted from comctl.c with V0.9.3 (2000-05-21) [umoeller]. 10 * 11 * The "chart" common control presently is a subclassed static 12 * text control. This way you can easily create a chart control 13 * as a static control in any Dialog Editor; after loading the 14 * dlg template, simply call ctlChartFromStatic with the hwnd 15 * of the static control to make it a chart. 16 * 17 * Note: even though you can use _any_ type of static control 18 * with this function, you should use a static _text_ control, 19 * because not all types of static controls react to fonts and 20 * colors dragged upon them. The static _text_ control does. 21 * 22 * The pie chart consumes all available space in the static control. 23 * 24 * In XWorkplace, this is used for the pie chart on the new 25 * XFldDisk "Details" settings page to display the free space 26 * on a certain drive. 27 * 28 * <B>Chart data:</B> 29 * 30 * The pie chart control operates on an array of "double" values. 31 * Each value in that array corresponds to a color in a second 32 * array of (LONG) RGB values and, if description texts are 33 * enabled, to a third array of PSZ's with description texts. 34 * In each of the three arrays, the array item count must be 35 * the same, naturally. 36 * 37 * The data on which the pie chart operates is initialized to 38 * be void, so that the pie chart will not paint anything 39 * initially. In order to have the pie chart display something, 40 * send a CHTM_SETCHARTDATA message (comctl.h) to the static 41 * control after it has been subclassed. 42 * 43 * CHTM_SETCHARTDATA takes a CHARTDATA structure (comctl.h) in mp1, 44 * which must contain the three arrays with the chart data, colors, 45 * and descriptive texts to be displayed. 46 * 47 * The chart data will automatically compute the sum of all values 48 * so that each data item will be displayed as a fraction of the 49 * total chart control size, proportionally to the item's value 50 * against the sum of all values. In other words, 51 * 52 + (dValue / dTotal) == (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); 8 159 * 9 160 * Note: Version numbering in this file relates to XWorkplace version … … 86 237 /* ****************************************************************** 87 238 * 239 * Private declarations 240 * 241 ********************************************************************/ 242 243 /* 244 *@@ CHARTCDATA: 245 * pie chart control data. Composed from the various 246 * chart initialization data. 247 * Stored in QWL_USER of the subclassed static control. 248 * Not available to the application. 249 */ 250 251 typedef 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 * 88 273 * Chart Control 89 274 * 90 275 ********************************************************************/ 276 277 /* 278 *@@ DrawCenteredText: 279 * 280 *@@added V0.9.12 (2001-05-03) [umoeller] 281 */ 282 283 VOID DrawCenteredText(HPS hpsMem, 284 PPOINTL pptlMiddlePoint, 285 const char *pcsz) 286 { 287 if (pcsz) 288 { 289 PSZ psz = strdup(pcsz), 290 p2 = psz; 291 if (psz) 292 { 293 ULONG cLineBreaks = 0; 294 LONG lLineSpacing = 0; 295 296 POINTL ptl; 297 memcpy(&ptl, pptlMiddlePoint, sizeof(ptl)); 298 299 // count how many lines we have 300 while (p2 = strchr(p2, '\n')) 301 { 302 cLineBreaks++; 303 p2++; 304 } 305 306 if (cLineBreaks) 307 { 308 // if we have more than one line: 309 lLineSpacing = gpihQueryLineSpacing(hpsMem); 310 311 // center vertically 312 ptl.y += (lLineSpacing * cLineBreaks) / 2; 313 } 314 315 p2 = psz; 316 while (TRUE) 317 { 318 PSZ pNext = strchr(p2, '\n'); 319 if (pNext) 320 { 321 *pNext = '\0'; 322 pNext++; 323 } 324 325 GpiCharStringAt(hpsMem, 326 &ptl, 327 strlen(p2), 328 (PSZ)p2); 329 330 if (pNext) 331 { 332 p2 = pNext; 333 ptl.y -= lLineSpacing; 334 } 335 else 336 break; 337 } 338 339 free(psz); 340 } 341 } 342 } 343 344 /* 345 *@@ PaintBarChart: 346 * paint implementation for the "bar chart". 347 * 348 * This gets called from ctlCreateChartBitmap if the 349 * current style for the chart control is the "bar 350 * chart" style. 351 * 352 *@@added V0.9.12 (2001-05-03) [umoeller] 353 *@@changed V0.9.12 (2001-05-03) [umoeller]: fixed another maaajor PM resource leak with regions 354 */ 355 356 VOID PaintBarChart(HPS hpsMem, 357 PRECTL prclWholeStatic, // in: rectl to paint into 358 PCHARTDATA pChartData, // in: chart data 359 PCHARTSTYLE pChartStyle, // in: chart style 360 double dTotal, // in: sum of all values in pChartData 361 LONG lTextColor, // in: description text color (RGB) 362 HRGN* paRegions) // out: GPI regions for each data item 363 { 364 ULONG ulYBottomNow = 0; 365 ULONG ul; 366 367 PLONG plColorThis; 368 PSZ *ppszDescriptionThis = NULL; 369 HRGN *phRegionThis; 370 double *pdValueThis; 371 372 // allocate array for storing text positions later 373 PPOINTL paptlDescriptions = (PPOINTL)malloc(sizeof(POINTL) 374 * pChartData->cValues); 375 376 RECTL rclPaint; 377 378 POINTL ptlLowerLeft, 379 ptlUpperRight; 380 381 // thickness of line separators for CHS_DRAWLINES 382 // V0.9.12 (2001-05-03) [umoeller] 383 const SIZEL cszlDrawLines = {1, 1}; 384 385 // calculate rectangle; 386 // this is the size of the static control 387 // minus the "3D thickness", if enabled 388 memcpy(&rclPaint, prclWholeStatic, sizeof(RECTL)); 389 if (pChartStyle->ulStyle & CHS_3D_BRIGHT) 390 // this includes CHS_3D_DARKEN 391 { 392 rclPaint.yBottom = prclWholeStatic->yBottom 393 + ulYBottomNow; 394 rclPaint.yTop = prclWholeStatic->yTop 395 - pChartStyle->ulThickness 396 + ulYBottomNow; 397 } 398 399 // c) Strangely, GpiSetPattern does work, 400 // while GpiSetColor doesn't (see below). 401 GpiSetPattern(hpsMem, PATSYM_SOLID); 402 403 // initialize per-item data pointers for 404 // loop below 405 pdValueThis = pChartData->padValues; 406 plColorThis = pChartData->palColors; 407 ppszDescriptionThis = pChartData->papszDescriptions; 408 phRegionThis = paRegions; 409 410 // initialize corners 411 ptlLowerLeft.x = 0; 412 ptlLowerLeft.y = 0; 413 ptlUpperRight.x = 0; 414 ptlUpperRight.y = rclPaint.yTop; 415 416 // inner "slice loop": 417 // this loop goes over the data pointers 418 // and paints accordingly. At the end of 419 // the loop, we'll advance all those pointers. 420 for (ul = 0; 421 ul < pChartData->cValues; 422 ul++) 423 { 424 HRGN hrgnThis; 425 SHORT sSweepAngle, 426 sStartAngle; 427 428 GpiSetCurrentPosition(hpsMem, &ptlLowerLeft); 429 430 _Pmpf((__FUNCTION__ ": ptlLowerLeft.x = %d", ptlLowerLeft.x)); 431 432 // calc upper right from data values 433 ptlUpperRight.x += (ULONG)( (double)rclPaint.xRight 434 * (*pdValueThis) 435 / dTotal); 436 437 // set the area (fill) color 438 GpiSetColor(hpsMem, 439 *plColorThis); 440 441 // now draw the slice; 442 // we could use an area, but since we need 443 // to remember the coordinates of the slice 444 // for mouse click handling, we require a 445 // region. But areas cannot be converted 446 // to regions, so we use a path instead. 447 GpiBeginPath(hpsMem, 448 1); // path ID, must be 1 449 450 GpiBox(hpsMem, 451 DRO_OUTLINE, 452 &ptlUpperRight, 453 0, 0); // no rounding 454 455 GpiEndPath(hpsMem); 456 457 // convert the path to a region; 458 // we'll need the region for mouse hit testing later 459 hrgnThis = GpiPathToRegion(hpsMem, 460 1, 461 FPATH_ALTERNATE); 462 // after this, the path is deleted 463 464 // now, this FINALLY paints the slice 465 GpiPaintRegion(hpsMem, hrgnThis); 466 467 // now paint descriptions 468 if (pChartStyle->ulStyle & CHS_DESCRIPTIONS) 469 { 470 // description strings valid? 471 if (ppszDescriptionThis) 472 { 473 POINTL ptlMiddlePoint; 474 475 // set presentation color 476 GpiSetColor(hpsMem, lTextColor); 477 478 // set text aligment to centered 479 // both horizontally and vertically; 480 // this affects subsequent GpiCharStringAt 481 // calls in that the output text will 482 // be centered around the specified 483 // point 484 GpiSetTextAlignment(hpsMem, 485 TA_CENTER, // horizontally 486 TA_HALF); // center vertically 487 488 489 490 // center the text in the box 491 ptlMiddlePoint.x = ptlLowerLeft.x 492 + (ptlUpperRight.x - ptlLowerLeft.x) / 2; 493 ptlMiddlePoint.y = ptlLowerLeft.y 494 + (ptlUpperRight.y - ptlLowerLeft.y) / 2; 495 496 // FINALLY, draw the description 497 // at this point; since we have used 498 // GpiSetTextAlignment above, the 499 // text will be centered on exactly 500 // that point 501 DrawCenteredText(hpsMem, 502 &ptlMiddlePoint, 503 *ppszDescriptionThis); 504 505 } // end if (ppszDescriptionThis) 506 } // end if (pChartStyle->ulStyle & CHS_DESCRIPTIONS) 507 508 // last run (are we painting the top now)? 509 if ( // if separator lines are enabled, 510 // draw a frame around the thing 511 (pChartStyle->ulStyle & CHS_DRAWLINES) 512 ) 513 { 514 GpiSetColor(hpsMem, lTextColor); 515 GpiFrameRegion(hpsMem, 516 hrgnThis, 517 (PSIZEL)&cszlDrawLines); // always (1, 1) 518 } 519 520 if ( phRegionThis 521 ) 522 // region output requested by caller: 523 // store region, the caller will clean this up 524 *phRegionThis = hrgnThis; 525 else 526 // no region output requested: destroy region 527 GpiDestroyRegion(hpsMem, hrgnThis); 528 529 // use the "right" xpos we had now as the "left" 530 // xpos for the next loop 531 ptlLowerLeft.x = ptlUpperRight.x; 532 533 // advance the data pointers 534 pdValueThis++; 535 plColorThis++; 536 if (ppszDescriptionThis) 537 ppszDescriptionThis++; 538 if (phRegionThis) 539 phRegionThis++; 540 } // end for (ul...) 541 542 // cleanup 543 free(paptlDescriptions); 544 } 545 546 /* 547 *@@ PaintPieChart: 548 * paint implementation for the "pie chart". 549 * 550 * This gets called from ctlCreateChartBitmap if the 551 * current style for the chart control is the "pie 552 * chart" style. 553 * 554 *@@added V0.9.12 (2001-05-03) [umoeller] 555 *@@changed V0.9.12 (2001-05-03) [umoeller]: fixed another maaajor PM resource leak with regions 556 *@@changed V0.9.12 (2001-05-03) [umoeller]: fixed rounding errors 557 */ 558 559 VOID PaintPieChart(HPS hpsMem, 560 PRECTL prclWholeStatic, // in: rectl to paint into 561 PCHARTDATA pChartData, // in: chart data 562 PCHARTSTYLE pChartStyle, // in: chart style 563 double dTotal, // in: sum of all values in pChartData 564 LONG lTextColor, // in: description text color (RGB) 565 HRGN* paRegions) // out: GPI regions for each data item 566 { 567 ULONG ulYBottomNow = 0; 568 ULONG ul; 569 570 // allocate array for storing text positions later 571 PPOINTL paptlDescriptions = (PPOINTL)malloc(sizeof(POINTL) 572 * pChartData->cValues); 573 POINTL ptlCenter; 574 575 FIXED fxPieSize = (LONG)(pChartStyle->dPieSize * 65536), 576 fxDescriptions = (LONG)(pChartStyle->dDescriptions * 65536); 577 578 // We'll paint into the bitmap in two loops: 579 // +-- The outer "3D" loop is executed 580 // | pChartStyle->ulThickness-fold, if 581 // | CHS_3Dxxx has been enabled; otherwise 582 // | just once. 583 // | 584 // | +-- The inner "slice" loop goes thru the 585 // | data fields in pChartData and draws 586 // | the pies accordingly. 587 // | 588 // +-- We then increase the base Y point (ulYBottomNow) 589 // by one and draw again, thereby getting the 3D 590 // effect. 591 592 // 1) outer 3D loop 593 do // while ( (pChartStyle->ulStyle & CHS_3D_BRIGHT)... 594 { 595 RECTL rclArc; 596 PLONG plColorThis; 597 PSZ *ppszDescriptionThis = NULL; 598 PPOINTL pptlDescriptionThis; 599 HRGN *phRegionThis; 600 double *pdValueThis; 601 602 ARCPARAMS ap; 603 AREABUNDLE ab; 604 605 double dStartAngle = pChartData->usStartAngle, 606 dSweepAngle = 0; 607 608 // thickness of line separators for CHS_DRAWLINES 609 // V0.9.12 (2001-05-03) [umoeller] 610 const SIZEL cszlDrawLines = {1, 1}; 611 612 // this is only TRUE for the last loop 613 BOOL fNowDrawingSurface = 614 ( 615 ((pChartStyle->ulStyle & CHS_3D_BRIGHT) == 0) 616 || 617 (ulYBottomNow == pChartStyle->ulThickness - 1) 618 ); 619 620 // // _Pmpf(("Looping, ulYBottomNow: %d", ulYBottomNow)); 621 622 // calculate pie rectangle for this loop; 623 // this is the size of the static control 624 // minus the "3D thickness", if enabled 625 memcpy(&rclArc, prclWholeStatic, sizeof(RECTL)); 626 if (pChartStyle->ulStyle & CHS_3D_BRIGHT) 627 // this includes CHS_3D_DARKEN 628 { 629 rclArc.yBottom = prclWholeStatic->yBottom 630 + ulYBottomNow; 631 rclArc.yTop = prclWholeStatic->yTop 632 - pChartStyle->ulThickness 633 + ulYBottomNow; 634 } 635 636 // calculate center point 637 ptlCenter.x = rclArc.xRight / 2; 638 ptlCenter.y = ((rclArc.yTop - rclArc.yBottom) / 2) + ulYBottomNow; 639 640 // Now, the "arc" APIs really suck. The following 641 // has cost me hours of testing to find out: 642 643 // a) The arc functions expect some kind of 644 // "default arc" to be defined, which they 645 // refer to. We define the arc as elliptical; 646 // this will be used as the "current arc" 647 // for subsequent arc calls. 648 // (P, S) and (R, Q) define the end points 649 // of the major axes of the ellipse. 650 // The center of the arc will later be 651 // specified with GpiPartialArc (while GpiFullArc 652 // uses the current pen position... 653 // Who created these APIs?!? 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 } 91 955 92 956 /* … … 119 983 * 120 984 * If (paRegions != NULL), this function will 121 * create GPI regions fordata item. Each GPI985 * create a GPI region for each data item. Each GPI 122 986 * region will then contain the outline of the 123 987 * corresponding pie chart slice. This allows … … 137 1001 * This returns NULLHANDLE if an error occured. 138 1002 * This can mean the following: 1003 * 139 1004 * -- The data is invalid, because the total is 0. 1005 * 140 1006 * -- 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 141 1010 */ 142 1011 … … 158 1027 for (ul = 0; ul < pChartData->cValues; ul++) 159 1028 { 1029 _Pmpf((" dThis is %d", (ULONG)(*pdThis))); // printf 1030 160 1031 dTotal += *pdThis; 161 1032 pdThis++; 162 1033 } 1034 1035 _Pmpf((__FUNCTION__ ": dTotal is %d", (ULONG)dTotal)); // printf 163 1036 164 1037 // avoid division by zero … … 166 1039 { 167 1040 RECTL rclWholeStatic; 168 ULONG ulYBottomNow = 0;169 170 1041 // get window rectangle (bottom left is 0, 0) 171 1042 rclWholeStatic.xLeft = 0; … … 181 1052 // successfully created: 182 1053 183 // allocate array for storing text positions later184 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 191 1054 // associate bitmap with memory PS 192 1055 GpiSetBitmap(hpsMem, hbmReturn); … … 201 1064 &rclWholeStatic); 202 1065 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); 538 1082 539 1083 // deselect (free) bitmap … … 551 1095 * 552 1096 *@@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 553 1098 */ 554 1099 … … 557 1102 if (pChtCData) 558 1103 { 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 559 1121 // bitmap already created? 560 1122 if (pChtCData->hbmChart) 561 1123 { 562 1124 // free current bitmap 563 //GpiSetBitmap(pChtCData->hpsMem, NULLHANDLE);1125 GpiSetBitmap(pChtCData->hpsMem, NULLHANDLE); 564 1126 // delete bitmap; fails if not freed! 565 GpiDeleteBitmap(pChtCData->hbmChart); 1127 if (!GpiDeleteBitmap(pChtCData->hbmChart)) 1128 _Pmpf((__FUNCTION__ ": GpiDeleteBitmap failed.")); 566 1129 pChtCData->hbmChart = NULLHANDLE; 567 1130 } 568 1131 569 // destroy regions, but not the array itself 570 // (this is done in CleanupData) 571 if (pChtCData->paRegions) 1132 if (pChtCData->hpsMem) 572 1133 { 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; 586 1141 } 587 1142 } … … 647 1202 * 648 1203 *@@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 649 1205 */ 650 1206 … … 663 1219 CleanupData(pChtCData); 664 1220 665 // _Pmpf(("Setting up data"));666 667 if (pChtCData->hpsMem == NULLHANDLE)668 {669 // first call:670 // create a memory PS for the bitmap671 SIZEL szlPage = {0, 0};672 gpihCreateMemPS(WinQueryAnchorBlock(hwndChart),673 &szlPage,674 &pChtCData->hdcMem,675 &pChtCData->hpsMem);676 // _Pmpf(("Created HPS 0x%lX", pChtCData->hpsMem));677 // _Pmpf(("Created HDC 0x%lX", pChtCData->hdcMem));678 }679 680 1221 pChtCData->cd.usStartAngle = pcdNew->usStartAngle; 681 1222 pChtCData->cd.usSweepAngle = pcdNew->usSweepAngle; … … 695 1236 696 1237 // 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 703 1241 { 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 } 710 1256 } 711 1257 … … 716 1262 717 1263 pChtCData->lSelected = -1; // none selected 1264 pChtCData->lSourceEmphasis = -1; // none selected 718 1265 719 1266 WinInvalidateRect(hwndChart, NULL, FALSE); … … 726 1273 * 727 1274 *@@added V0.9.2 (2000-02-29) [umoeller] 1275 *@@changed V0.9.12 (2001-05-03) [umoeller]: fixed major PM resource leaks 728 1276 */ 729 1277 … … 735 1283 RECTL rclStatic; 736 1284 WinQueryWindowRect(hwndChart, &rclStatic); 737 738 // _Pmpf(("ctl_fnwpChart: WM_PAINT, cValues: %d", pChtCData->cd.cValues));739 1285 740 1286 // do we have any values yet? … … 762 1308 SYSCLR_WINDOWTEXT); 763 1309 764 // yes: check if we created the bitmap 765 // already 1310 gpihSwitchToRGB(hps); 1311 1312 // check if the bitmap needs to be (re)created 766 1313 if (pChtCData->hbmChart == NULLHANDLE) 767 1314 { 768 1315 // no: do it now 769 1316 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) 782 1320 { 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 } 786 1347 } 787 788 789 gpihSwitchToRGB(hps);790 1348 791 1349 pChtCData->hbmChart = ctlCreateChartBitmap( … … 804 1362 pChtCData->paRegions); 805 1363 // out: regions array 806 // _Pmpf(("Created bitmap 0x%lX", pChtCData->hbmChart));807 1364 808 1365 // unset and delete font 809 GpiSetCharSet(pChtCData->hpsMem, LCID_DEFAULT);810 1366 if (lLCIDSet) 1367 { 1368 GpiSetCharSet(pChtCData->hpsMem, LCID_DEFAULT); 811 1369 GpiDeleteSetId(pChtCData->hpsMem, lLCIDSet); 1370 } 812 1371 813 1372 WinSetPointer(HWND_DESKTOP, hptrOld); … … 816 1375 if (pChtCData->hbmChart) 817 1376 { 1377 // draw the chart bitmap 818 1378 POINTL ptlDest = { 0, 0 }; 819 1379 WinDrawBitmap(hps, … … 824 1384 DBM_NORMAL); 825 1385 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]) 831 1396 { 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]); 843 1412 } 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 } 844 1442 } 845 1443 } … … 847 1445 848 1446 /* 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 1455 LONG 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 1496 VOID 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 1528 BOOL 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 /* 849 1572 *@@ ctl_fnwpChart: 850 1573 * window procedure for the "chart" control. … … 856 1579 *@@changed V0.9.2 (2000-02-29) [umoeller]: added resize support 857 1580 *@@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 858 1582 */ 859 1583 … … 887 1611 // _Pmpf(("CHTM_SETCHARTDATA, mp1: 0x%lX", mp1)); 888 1612 if (mp1) 889 { 890 PCHARTDATA pcdNew = (PCHARTDATA)mp1; 891 SetChartData(hwndChart, pChtCData, pcdNew); 892 } 1613 SetChartData(hwndChart, 1614 pChtCData, 1615 (PCHARTDATA)mp1); 893 1616 break; 894 1617 … … 943 1666 case CHTM_ITEMFROMPOINT: 944 1667 { 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 ) 954 1707 { 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); 980 1715 } 981 982 mrc = (MPARAM)lRegionFound; 983 break; } 1716 } 1717 break; 984 1718 985 1719 /* 986 * WM_BUTTON1 DOWN:987 * 1720 * WM_BUTTON1CLICK: 1721 * WM_CONTEXTMENU: V0.9.12 (2001-05-03) [umoeller] 988 1722 */ 989 1723 990 case WM_BUTTON1DOWN: 1724 case WM_BUTTON1CLICK: 1725 case WM_BUTTON1DBLCLK: 1726 case WM_CONTEXTMENU: 991 1727 if (pChtCData->cs.ulStyle & CHS_SELECTIONS) 992 1728 { 993 LONG lRegionFound = (LONG)WinSendMsg(hwndChart, 994 CHTM_ITEMFROMPOINT, 995 mp1, 996 NULL); 1729 LONG lRegionFound = FindItemFromPoint(pChtCData, 1730 SHORT1FROMMP(mp1), 1731 SHORT2FROMMP(mp1)); 997 1732 998 1733 // selections allowed: … … 1001 1736 WinSetFocus(HWND_DESKTOP, hwndChart); 1002 1737 // 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 1004 1749 { 1005 1750 // 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); 1009 1765 } 1010 1766 } … … 1047 1803 // resizing? 1048 1804 if (pswpNew->fl & SWP_SIZE) 1805 { 1049 1806 if (pChtCData->hbmChart) 1050 {1051 1807 // invalidate bitmap so that 1052 1808 // it will be recreated with new size 1053 1809 CleanupBitmap(pChtCData); 1054 WinInvalidateRect(hwndChart, NULL, FALSE); 1055 }1056 1810 1811 WinInvalidateRect(hwndChart, NULL, FALSE); 1812 } 1057 1813 // return default NULL 1058 1814 break; } … … 1103 1859 case WM_DESTROY: 1104 1860 CleanupBitmap(pChtCData); 1861 1105 1862 CleanupData(pChtCData); 1106 1863 1107 // _Pmpf(("Destroying HPS 0x%lX", pChtCData->hpsMem));1108 if (!GpiDestroyPS(pChtCData->hpsMem));1109 // _Pmpf((" Error!"));1110 // _Pmpf(("Destroying HDC 0x%lX", pChtCData->hdcMem));1111 if (!DevCloseDC(pChtCData->hdcMem));1112 // _Pmpf((" Error!"));1113 1864 free(pChtCData); 1114 1865 … … 1130 1881 * window procedure with ctl_fnwpChart. 1131 1882 * 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. 1251 1884 * 1252 1885 *@@added V0.9.0 [umoeller]
Note:
See TracChangeset
for help on using the changeset viewer.