1//////////////////////////////////////////////////////////////////////////////// 2// 3// ADOBE SYSTEMS INCORPORATED 4// Copyright 2009 Adobe Systems Incorporated 5// All Rights Reserved. 6// 7// NOTICE: Adobe permits you to use, modify, and distribute this file 8// in accordance with the terms of the license agreement accompanying it. 9// 10//////////////////////////////////////////////////////////////////////////////// 11 12package mx.charts 13{ 14 15import flash.display.DisplayObject; 16import flash.filters.DropShadowFilter; 17import flash.system.ApplicationDomain; 18 19import mx.charts.chartClasses.CartesianChart; 20import mx.charts.chartClasses.CartesianTransform; 21import mx.charts.chartClasses.DataTip; 22import mx.charts.chartClasses.DataTransform; 23import mx.charts.chartClasses.IChartElement; 24import mx.charts.chartClasses.IStackable; 25import mx.charts.chartClasses.NumericAxis; 26import mx.charts.chartClasses.Series; 27import mx.charts.series.ColumnSeries; 28import mx.charts.series.ColumnSet; 29import mx.charts.series.items.ColumnSeriesItem; 30import mx.charts.styles.HaloDefaults; 31import mx.core.IFlexModuleFactory; 32import mx.core.mx_internal; 33import mx.graphics.SolidColor; 34import mx.graphics.SolidColorStroke; 35import mx.graphics.Stroke; 36import mx.styles.CSSStyleDeclaration; 37 38use namespace mx_internal; 39 40//-------------------------------------- 41// Styles 42//-------------------------------------- 43 44/** 45 * Specifies a ratio of wide to draw the columns relative to 46 * the category width, as a percentage in the range of 0 to 1. 47 * A value of 1 uses the entire space, while a value of 0.6 48 * uses 60% of the column's available space. 49 * The actual column width used is the smaller of the 50 * <code>columnWidthRatio</code> property and the 51 * <code>maxColumnWidth</code> property. 52 * Clustered columns divide this space proportionally 53 * among the columns in each cluster. 54 * The default value is 0.65. 55 * 56 * @langversion 3.0 57 * @playerversion Flash 9 58 * @playerversion AIR 1.1 59 * @productversion Flex 3 60 */ 61[Style(name="columnWidthRatio", type="Number", inherit="no")] 62 63/** 64 * Specifies how wide to draw the columns, in pixels. 65 * The actual column width used is the smaller of this property 66 * and the <code>columnWidthRatio</code> property. 67 * Clustered columns divide this space proportionally 68 * among the columns in each cluster. 69 * 70 * @langversion 3.0 71 * @playerversion Flash 9 72 * @playerversion AIR 1.1 73 * @productversion Flex 3 74 */ 75[Style(name="maxColumnWidth", type="Number", format="Length", inherit="no")] 76 77/** 78 * The class that is used by this component to render labels. 79 * 80 * <p>It can be set to either the mx.controls.Label class 81 * or the spark.components.Label class.</p> 82 * 83 * @default spark.components.Label 84 * 85 * @langversion 3.0 86 * @playerversion Flash 10.2 87 * @playerversion AIR 2.0 88 * @productversion Flex 4 89 */ 90[Style(name="labelClass", type="Class", inherit="no")] 91 92//-------------------------------------- 93// Other metadata 94//-------------------------------------- 95 96[DefaultBindingProperty(destination="dataProvider")] 97 98[DefaultTriggerEvent("itemClick")] 99 100[IconFile("ColumnChart.png")] 101 102/** 103 * The ColumnChart control represents data as a series of vertical columns 104 * whose height is determined by values in the data. 105 * You can use the ColumnChart to represent a variety of different charts 106 * including simple columns, clustered columns, stacked, 107 * 100% stacked, and high/low. 108 * 109 * <p>A ColumnChart control expects its <code>series</code> property 110 * to contain an array of ColumnSeries objects.</p> 111 * 112 * <p>Stacked and 100% column charts override the <code>minField</code> 113 * property of their ColumnSeries objects.</p> 114 * 115 * @mxml 116 * 117 * <p>The <code><mx:ColumnChart></code> tag inherits all the properties 118 * of its parent classes and adds the following properties:</p> 119 * 120 * <pre> 121 * <mx:ColumnChart 122 * <strong>Properties</strong> 123 * extendLabelToEnd="false|true" 124 * maxLabelWidth="<i>50</i>" 125 * showLabelVertically="false|true" 126 * type="<i>clustered|overlaid|stacked|100%</i>" 127 * 128 * <strong>Styles</strong> 129 * columnWidthRatio=".65" 130 * maxColumnWidth="<i>No default</i>" 131 * /> 132 * </pre> 133 * 134 * @includeExample examples/Column_BarChartExample.mxml 135 * 136 * @see mx.charts.series.ColumnSeries 137 * 138 * @langversion 3.0 139 * @playerversion Flash 9 140 * @playerversion AIR 1.1 141 * @productversion Flex 3 142 */ 143public class ColumnChart extends CartesianChart 144{ 145 include "../core/Version.as"; 146 147 //-------------------------------------------------------------------------- 148 // 149 // Class initialization 150 // 151 //-------------------------------------------------------------------------- 152 153 //-------------------------------------------------------------------------- 154 // 155 // Class constants 156 // 157 //-------------------------------------------------------------------------- 158 159 /** 160 * @private 161 */ 162 private static var INVALIDATING_STYLES:Object = 163 { 164 columnWidthRatio: 1, 165 maxColumnWidth: 1 166 } 167 168 //-------------------------------------------------------------------------- 169 // 170 // Constructor 171 // 172 //-------------------------------------------------------------------------- 173 174 /** 175 * Constructor. 176 * 177 * @langversion 3.0 178 * @playerversion Flash 9 179 * @playerversion AIR 1.1 180 * @productversion Flex 3 181 */ 182 public function ColumnChart() 183 { 184 super(); 185 186 LinearAxis(horizontalAxis).autoAdjust = false; 187 188 dataTipMode = "single"; 189 190 seriesFilters = [ new DropShadowFilter(2, 45, 0.2 * 0xFFFFFF)]; 191 } 192 193 //-------------------------------------------------------------------------- 194 // 195 // Variables 196 // 197 //-------------------------------------------------------------------------- 198 199 /** 200 * @private 201 */ 202 private var _moduleFactoryInitialized:Boolean = false; 203 204 205 /** 206 * @private 207 */ 208 private var _perSeriescolumnWidthRatio:Number; 209 210 /** 211 * @private 212 */ 213 private var _perSeriesMaxColumnWidth:Number; 214 215 /** 216 * @private 217 */ 218 private var _leftOffset:Number; 219 220 /** 221 * @private 222 */ 223 mx_internal var allLabelsMeasured: Boolean = false; 224 225 /** 226 * @private 227 */ 228 private var _allItems:Array /* of ChartItem */ = []; 229 230 /** 231 * @private 232 */ 233 private var _needLabels:Boolean = false; 234 235 /** 236 * @private 237 */ 238 private var _tempField:Object; 239 //-------------------------------------------------------------------------- 240 // 241 // Properties 242 // 243 //-------------------------------------------------------------------------- 244 245 //--------------------------------- 246 // extendLabelToEnd 247 //--------------------------------- 248 /** 249 * @private 250 * Storage for extendLabelToEnd property 251 */ 252 private var _extendLabelToEnd:Boolean = false; 253 254 [Inspectable(category="General")] 255 256 /** 257 * Determines whether or not data labels can extend to the end of the chart. 258 * If you set this to true, labels can use the whole space between the item 259 * and the boundary of the chart to show its label. Otherwise, data labels are 260 * restricted to the area defined by their chart item. 261 * 262 * @default false 263 * 264 * @langversion 3.0 265 * @playerversion Flash 9 266 * @playerversion AIR 1.1 267 * @productversion Flex 3 268 */ 269 public function get extendLabelToEnd():Boolean 270 { 271 return _extendLabelToEnd; 272 } 273 274 /** 275 * @private 276 */ 277 public function set extendLabelToEnd(value:Boolean):void 278 { 279 _extendLabelToEnd = value; 280 measureLabels(); 281 } 282 283 //--------------------------------- 284 // maxLabelWidth 285 //--------------------------------- 286 287 /** 288 * @private 289 * Storage for maxLabelWidth property 290 */ 291 private var _maxLabelWidth:int = 50; 292 293 [Inspectable(category="General")] 294 295 /** 296 * Determines maximum width in pixels of label of items. 297 * 298 * @default 50 299 * 300 * @langversion 3.0 301 * @playerversion Flash 9 302 * @playerversion AIR 1.1 303 * @productversion Flex 3 304 */ 305 public function get maxLabelWidth():int 306 { 307 return _maxLabelWidth; 308 } 309 310 /** 311 * @private 312 */ 313 public function set maxLabelWidth(value:int):void 314 { 315 _maxLabelWidth = value; 316 measureLabels(); 317 } 318 319 //--------------------------------- 320 // showLabelVertically 321 //--------------------------------- 322 /** 323 * @private 324 * Storage for showLabelVertically property 325 */ 326 private var _showLabelVertically:Boolean = false; 327 328 [Inspectable(category="General")] 329 330 /** 331 * Determines whether or not the data labels can be shown vertically. 332 * If you set this to true and if embedded fonts are used, labels will be shown vertically 333 * if they cannot be fit horizontally within the column width. 334 * 335 * @default false 336 * 337 * @langversion 3.0 338 * @playerversion Flash 9 339 * @playerversion AIR 1.1 340 * @productversion Flex 3 341 */ 342 public function get showLabelVertically():Boolean 343 { 344 return _showLabelVertically; 345 } 346 347 /** 348 * @private 349 */ 350 public function set showLabelVertically(value:Boolean):void 351 { 352 _showLabelVertically = value; 353 measureLabels(); 354 } 355 356 //---------------------------------- 357 // type 358 //---------------------------------- 359 360 /** 361 * @private 362 * Storage for the type property. 363 */ 364 private var _type:String = "clustered"; 365 366 [Inspectable(category="General", enumeration="stacked,100%,clustered,overlaid", defaultValue="clustered")] 367 368 /** 369 * The type of the column chart. 370 * 371 * <p>Possible values are:</p> 372 * <ul> 373 * <li><code>"clustered"</code>: 374 * Values from different series are grouped by category. 375 * This is the default type.</li> 376 * <li><code>"overlaid"</code>: 377 * Multiple values are rendered on top of one another by category, 378 * with the last series on top. </li> 379 * <li><code>"stacked"</code>: 380 * Columns are stacked on top of each other and grouped by category. 381 * Each column represents the cumulative value 382 * of the columns beneath it. </li> 383 * <li><code>"100%"</code>: 384 * Columns are stacked on top of each other, adding up to 100%. 385 * Each column represents the percent that it contributes 386 * to the sum of the values for that category.</li> 387 * </ul> 388 * 389 * @langversion 3.0 390 * @playerversion Flash 9 391 * @playerversion AIR 1.1 392 * @productversion Flex 3 393 */ 394 public function get type():String 395 { 396 return _type; 397 } 398 399 /** 400 * @private 401 */ 402 public function set type(value:String):void 403 { 404 if (_type == value) 405 return; 406 407 _type = value; 408 409 invalidateSeries(); 410 invalidateData(); 411 } 412 413 //-------------------------------------------------------------------------- 414 // 415 // Overridden methods: UIComponent 416 // 417 //-------------------------------------------------------------------------- 418 419 /** 420 * @private 421 */ 422 private function initStyles():Boolean 423 { 424 HaloDefaults.init(styleManager); 425 426 var columnChartStyle:CSSStyleDeclaration = styleManager.getStyleDeclaration("mx.charts.ColumnChart"); 427 columnChartStyle.setStyle("chartSeriesStyles", HaloDefaults.chartBaseChartSeriesStyles); 428 columnChartStyle.setStyle("fill", new SolidColor(0xFFFFFF, 0)); 429 columnChartStyle.setStyle("calloutStroke", new SolidColorStroke(0x888888,2)); 430 columnChartStyle.setStyle("horizontalAxisStyleNames", ["blockCategoryAxis"]); 431 columnChartStyle.setStyle("verticalAxisStyleNames", ["blockNumericAxis"]); 432 433 return true; 434 } 435 436 437 /** 438 * A module factory is used as context for using embedded fonts and for finding the style manager that controls the styles for this component. 439 * 440 * @langversion 3.0 441 * @playerversion Flash 9 442 * @playerversion AIR 1.1 443 * @productversion Flex 3 444 */ 445 override public function set moduleFactory(factory:IFlexModuleFactory):void 446 { 447 super.moduleFactory = factory; 448 449 if (_moduleFactoryInitialized) 450 return; 451 452 _moduleFactoryInitialized = true; 453 454 // our style settings 455 initStyles(); 456 } 457 458 /** 459 * @private 460 */ 461 override protected function createChildren():void 462 { 463 super.createChildren(); 464 var labelClass:Class = getLabelClass(); 465 _tempField = new labelClass(); 466 _tempField.visible = false; 467 _tempField.text = "W..."; 468 _tempField.toolTip = ""; 469 addChild(_tempField as DisplayObject); 470 _tempField.validateNow(); 471 } 472 473 private function getLabelClass():Class 474 { 475 var labelClass:Class = getStyle("labelClass"); 476 if(labelClass == null) 477 { 478 try{ 479 labelClass = Class(ApplicationDomain.currentDomain. 480 getDefinition("spark.components::Label")); 481 } 482 catch(e:Error) 483 { 484 labelClass = Class(ApplicationDomain.currentDomain. 485 getDefinition("mx.controls::Label")); 486 } 487 } 488 return labelClass; 489 } 490 491 /** 492 * @private 493 */ 494 override public function styleChanged(styleProp:String):void 495 { 496 if (styleProp == null || INVALIDATING_STYLES[styleProp] != undefined) 497 invalidateSeries(); 498 499 super.styleChanged(styleProp); 500 } 501 502 //-------------------------------------------------------------------------- 503 // 504 // Overridden methods: ChartBase 505 // 506 //-------------------------------------------------------------------------- 507 508 /** 509 * @private 510 */ 511 override protected function customizeSeries(seriesGlyph:Series, i:uint):void 512 { 513 if (seriesGlyph is ColumnSeries || seriesGlyph is ColumnSet) 514 { 515 var series:Object = seriesGlyph; 516 517 if (!isNaN(_perSeriescolumnWidthRatio)) 518 series.columnWidthRatio = _perSeriescolumnWidthRatio; 519 520 if (!isNaN(_perSeriesMaxColumnWidth)) 521 series.maxColumnWidth = _perSeriesMaxColumnWidth; 522 523 if (_type == "overlaid") 524 series.offset = 0; 525 else 526 series.offset = _leftOffset + i * _perSeriescolumnWidthRatio; 527 528 if (series is IStackable) 529 { 530 var stackSeries:IStackable = IStackable(series); 531 stackSeries.stacker = null; 532 stackSeries.stackTotals = null; 533 } 534 } 535 } 536 537 /** 538 * @private 539 */ 540 override protected function applySeriesSet(seriesSet:Array /* of Series */, 541 transform:DataTransform):Array /* of Series */ 542 { 543 var columnWidthRatio:Number = getStyle("columnWidthRatio"); 544 var maxColumnWidth:Number = getStyle("maxColumnWidth"); 545 var g:IChartElement; 546 547 switch (_type) 548 { 549 case "stacked": 550 case "100%": 551 { 552 var n:int = seriesSet.length; 553 var i:int; 554 for (i = 0; i < n; i++) 555 { 556 seriesSet[i].offset = 0; 557 } 558 559 var newSeriesGlyph:ColumnSet = new ColumnSet(); 560 newSeriesGlyph.owner = this; 561 newSeriesGlyph.series = seriesSet; 562 for (i = 0; i < n; i++) 563 { 564 g = seriesSet[i] as IChartElement; 565 newSeriesGlyph.series[i].owner = newSeriesGlyph; 566 if (!g) 567 continue; 568 if (g.labelContainer) 569 newSeriesGlyph.labelContainer.addChild(seriesSet[i].labelContainer); 570 } 571 572 if (!isNaN(columnWidthRatio)) 573 newSeriesGlyph.columnWidthRatio = _perSeriescolumnWidthRatio; 574 575 if (!isNaN(maxColumnWidth)) 576 newSeriesGlyph.maxColumnWidth = _perSeriesMaxColumnWidth; 577 578 newSeriesGlyph.type = _type; 579 580 invalidateData(); 581 582 return [ newSeriesGlyph ]; 583 } 584 585 case "clustered": 586 default: 587 { 588 589 var columnSeriesCount:int = 0; 590 for each(var series:Series in seriesSet) { 591 if(series is ColumnSet || series is ColumnSeries) 592 columnSeriesCount++; 593 } 594 595 _perSeriescolumnWidthRatio = columnWidthRatio / columnSeriesCount; 596 _perSeriesMaxColumnWidth = maxColumnWidth / columnSeriesCount; 597 598 _leftOffset = (1 - columnWidthRatio) / 2 + 599 _perSeriescolumnWidthRatio / 2 - 0.5; 600 601 n = seriesSet.length; 602 var count:int = 0; 603 for (i = 0; i < n; i++) 604 { 605 var newSeries:IChartElement = seriesSet[i]; 606 if (newSeries is ColumnSeries || newSeries is ColumnSet) 607 { 608 customizeSeries(Series(seriesSet[i]), count); 609 count++; 610 } 611 } 612 613 return seriesSet; 614 } 615 616 case "overlaid": 617 { 618 _perSeriescolumnWidthRatio = columnWidthRatio; 619 _perSeriesMaxColumnWidth = maxColumnWidth; 620 621 _leftOffset = 0; 622 return super.applySeriesSet(seriesSet, transform); 623 } 624 } 625 } 626 627 /** 628 * Determines positions and dimensions of labels for all series in the chart 629 * 630 * @langversion 3.0 631 * @playerversion Flash 9 632 * @playerversion AIR 1.1 633 * @productversion Flex 3 634 */ 635 override mx_internal function measureLabels():Object 636 { 637 getSeriesLabelPosSet(); 638 if (!_needLabels) 639 return null; 640 var len:int = series.length; 641 var n:int; 642 var allSeriesTransform:Boolean = true; 643 644 if (type == "stacked" || type == "overlaid" || type == "100%") 645 allLabelsMeasured = false; 646 647 _allItems = []; 648 n = len; 649 for (var i:int = 0; i < n; i++) 650 { 651 findChartItems(series[i]); 652 } 653 654 _allItems.sort(sortOnX); //sort all items with respect to their position along X-axis 655 656 var itemsLen:Number = _allItems.length; 657 n = itemsLen; 658 for (i = 0; i < n; i++) 659 { 660 var v:ColumnSeriesItem = _allItems[i]; 661 var columnSeries:ColumnSeries = ColumnSeries(ColumnSeriesItem(_allItems[i]).element); 662 var labelPosition:String = columnSeries.labelPos; 663 664 if (labelPosition == "inside" || labelPosition == "outside") 665 { 666 var base:Number = columnSeries.seriesRenderData.renderedBase; 667 var size:Number = columnSeries.getStyle('fontSize'); 668 if (columnSeries.labelFunction != null) 669 columnSeries.measuringField.text = v.labelText = columnSeries.labelFunction(v, columnSeries); 670 else if (columnSeries.labelField != null) 671 columnSeries.measuringField.text = v.labelText = v.item[columnSeries.labelField]; 672 else if (columnSeries.dataFunction != null) 673 columnSeries.measuringField.text = v.labelText = columnSeries.dataFunction(columnSeries, v.item, 'yNumber'); 674 else 675 columnSeries.measuringField.text = v.labelText = v.item[columnSeries.yField]; 676 677 columnSeries.measuringField.validateNow(); 678 679 var labelRotation:Number = columnSeries.labelAngle; 680 var labelSizeLimit:Number = columnSeries.maxLabelSize; 681 682 if (labelPosition == "outside" && (type == "overlaid" || type == "stacked" || type == "100%")) 683 { 684 columnSeries.labelPos = 'inside'; 685 labelPosition = "inside"; 686 // today, labelPosition = inside is only supported for 100%, stacked and overlaid charts 687 } 688 689 690 if (labelPosition == 'outside') 691 { 692 if (!showLabelVertically) 693 { 694 v.labelIsHorizontal = true; 695 if (extendLabelToEnd) 696 { 697 v.labelWidth = Math.abs((this.dataRegion.right - 698 (v.x + columnSeries.seriesRenderData.renderedXOffset - 699 columnSeries.seriesRenderData.renderedHalfWidth))/ Math.cos(0)); 700 } 701 else 702 { 703 v.labelWidth = Math.min(Math.abs((this.dataRegion.right - 704 (v.x + columnSeries.seriesRenderData.renderedXOffset - 705 columnSeries.seriesRenderData.renderedHalfWidth))/ Math.cos(0)),maxLabelWidth); 706 } 707 v.labelHeight = columnSeries.measuringField.textHeight + 2; 708 } 709 else 710 { //check whether labels can be rendered horizontally 711 v.labelIsHorizontal = true; 712 v.labelWidth = 2 * columnSeries.seriesRenderData.renderedHalfWidth; 713 v.labelHeight = columnSeries.measuringField.textHeight + 2; 714 715 if (columnSeries.measuringField.textWidth + 5 > 2 * columnSeries.seriesRenderData.renderedHalfWidth && 716 columnSeries.measuringField.embedFonts) 717 { 718 v.labelIsHorizontal = false; 719 v.labelHeight = 2 * columnSeries.seriesRenderData.renderedHalfWidth; 720 if (!(isNaN(labelRotation)) && labelRotation!=-90) 721 { 722 var r:Number = -labelRotation / Math.PI * 180; 723 //for future enhancement 724 //check for type of chart if we need to support labelPosition = outside for all types of charts 725 if (type == "clustered") 726 { 727 if (i < itemsLen - len) 728 { 729 v.labelWidth = Math.abs((_allItems[i + len - i%len].x + ColumnSeries(_allItems[i + len - i%len].element).seriesRenderData.renderedXOffset 730 - ColumnSeries(_allItems[i + len - i%len].element).seriesRenderData.renderedHalfWidth - 731 (v.x + columnSeries.seriesRenderData.renderedXOffset - 732 columnSeries.seriesRenderData.renderedHalfWidth))/ Math.cos(r)); 733 } 734 else 735 v.labelWidth = Math.abs((_allItems[itemsLen -1].x + ColumnSeries(_allItems[itemsLen - 1].element).seriesRenderData.renderedXOffset 736 + ColumnSeries(_allItems[itemsLen - 1].element).seriesRenderData.renderedHalfWidth - 737 (v.x + columnSeries.seriesRenderData.renderedXOffset - 738 columnSeries.seriesRenderData.renderedHalfWidth))/ Math.cos(r)); 739 } 740 } 741 else 742 { 743 if (v.y < (isNaN(v.min) ? base : v.min)) 744 { 745 v.labelWidth = columnSeries.dataToLocal(0,v.yNumber).y - columnSeries.dataToLocal(0,NumericAxis(columnSeries.dataTransform.getAxis(CartesianTransform.VERTICAL_AXIS)).computedMaximum).y; 746 v.labelY = v.y; 747 if (v.labelY < this.dataRegion.top) 748 { 749 v.labelWidth = Math.abs(v.y -(isNaN(v.min) ? base : v.min)); 750 v.labelY = isNaN(v.min) ? base : v.min; 751 } 752 } 753 else 754 { 755 v.labelWidth = columnSeries.dataToLocal(0,NumericAxis(columnSeries.dataTransform.getAxis(CartesianTransform.VERTICAL_AXIS)).computedMinimum).y - columnSeries.dataToLocal(0,v.yNumber).y; 756 v.labelY = v.y + v.labelWidth; 757 if (v.labelY > this.dataRegion.bottom) 758 { 759 v.labelWidth = Math.abs(v.y -(isNaN(v.min) ? base : v.min)); 760 v.labelY = v.y; 761 } 762 } 763 } 764 765 v.labelX = v.x - columnSeries.seriesRenderData.renderedHalfWidth + columnSeries.seriesRenderData.renderedXOffset; 766 } 767 } 768 769 var labelScale:Number = 1; 770 if (columnSeries.measuringField.textWidth + 5 > v.labelWidth || (columnSeries.measuringField.textHeight) > v.labelHeight) 771 { 772 labelScale = v.labelWidth / (columnSeries.measuringField.textWidth + 5); 773 labelScale = Math.min(labelScale, v.labelHeight / (columnSeries.measuringField.textHeight)); 774 if (size * labelScale > labelSizeLimit) 775 { 776 columnSeries.seriesRenderData.labelScale = Math.min(labelScale,columnSeries.seriesRenderData.labelScale); 777 /* if (v.labelIsHorizontal) 778 { 779 _tempField.setStyle('fontSize',size * columnSeries.seriesRenderData.labelScale); 780 v.labelHeight = _tempField.textHeight + 2; 781 } */ 782 } 783 else 784 { 785 _tempField.setStyle('fontSize',size); 786 _tempField.validateNow(); 787 if (_tempField.measuredWidth > v.labelWidth || columnSeries.measuringField.textHeight > v.labelHeight) 788 { 789 labelScale = v.labelWidth / _tempField.measuredWidth; 790 labelScale = Math.min(labelScale, v.labelHeight / (columnSeries.measuringField.measuredHeight)); 791 if (size * labelScale > labelSizeLimit) 792 { 793 columnSeries.seriesRenderData.labelScale = Math.min(labelScale,columnSeries.seriesRenderData.labelScale); 794 /* if (v.labelIsHorizontal) 795 { 796 _tempField.setStyle('fontSize',size * columnSeries.seriesRenderData.labelScale); 797 v.labelHeight = _tempField.textHeight + 2; 798 } */ 799 } 800 else 801 { 802 v.labelText = ""; 803 v.labelWidth = 1; 804 v.labelHeight = 0; 805 } 806 } 807 808 } 809 } 810 if (!isNaN(labelRotation) && labelRotation!=-90 && columnSeries.measuringField.embedFonts) 811 { 812 v.labelHeight = columnSeries.measuringField.textHeight + 2; 813 if (v.y < (isNaN(v.min) ? base : v.min)) 814 v.labelY = v.y - v.labelHeight; 815 else 816 v.labelY = v.y + v.labelHeight; 817 } 818 if (v.labelIsHorizontal) 819 { 820 if (v.y < (isNaN(v.min) ? base : v.min)) 821 { 822 v.labelY = v.y - v.labelHeight; 823 if (v.labelY < this.dataRegion.top) 824 v.labelY = v.y; 825 } 826 827 else 828 { 829 v.labelY = v.y; 830 if (v.labelY > this.dataRegion.bottom) 831 v.labelY = v.y - v.labelHeight; 832 833 } 834 v.labelX = v.x - columnSeries.seriesRenderData.renderedHalfWidth +columnSeries.seriesRenderData.renderedXOffset; 835 } 836 v.labelTextWidth = columnSeries.measuringField.textWidth; 837 var j:int = 0; 838 if (!extendLabelToEnd) 839 { 840 //maxLabelWidth 841 for (j = (i-1); j >= 1;) 842 { 843 if (_allItems[j].x >= v.x - maxLabelWidth) 844 j--; 845 else 846 break; 847 } 848 if (j < 0) 849 j = i; 850 } 851 852 for (var k:int = j; k < i; k++) 853 { 854 var tempItem:ColumnSeriesItem = _allItems[k]; 855 //test for overlaps with previous labels 856 if (tempItem.labelIsHorizontal) 857 { 858 if (v.labelX + 0.0001 < Math.min(tempItem.labelX + tempItem.labelWidth , tempItem.labelX + tempItem.labelTextWidth)) 859 { 860 if (v.labelIsHorizontal) 861 { 862 if ((((v.labelY + v.labelHeight) > tempItem.labelY) && ((v.labelY + v.labelHeight) < (tempItem.labelY + tempItem.labelHeight))) || 863 ((v.labelY > tempItem.labelY) && (v.labelY < (tempItem.labelY + tempItem.labelHeight))) || 864 ((v.labelY == tempItem.labelY) && ((tempItem.labelY + tempItem.labelHeight) == (v.labelY + v.labelHeight)) && (v.labelX > tempItem.labelX) && (v.labelX < (tempItem.labelX + tempItem.labelWidth))) || 865 ((v.labelY <= tempItem.labelY) && ((v.labelY + v.labelHeight) > (tempItem.labelY + tempItem.labelHeight))) || 866 ((v.labelY < tempItem.labelY) && ((v.labelY + v.labelHeight) >= (tempItem.labelY + tempItem.labelHeight)))) 867 { 868 if (v.y < (isNaN(v.min) ? base : v.min)) 869 v.labelY = tempItem.labelY - v.labelHeight; 870 else 871 v.labelY = tempItem.labelY + tempItem.labelHeight; 872 } 873 } 874 else 875 { 876 if (((v.labelY > tempItem.labelY) && ((v.labelY - v.labelWidth) < (tempItem.labelY))) || 877 (((v.labelY - v.labelWidth) >= tempItem.labelY) && (v.labelY > (tempItem.labelY - tempItem.labelHeight)))) 878 { 879 var prevY:Number = v.labelY; 880 if (v.y < (isNaN(v.min) ? base : v.min)) 881 { 882 v.labelY = tempItem.labelY - tempItem.labelHeight; 883 v.labelWidth = v.labelWidth - (prevY - v.labelY); 884 } 885 else 886 { 887 v.labelY = tempItem.labelY + tempItem.labelHeight; 888 v.labelWidth = v.labelWidth - (v.labelY - prevY); 889 } 890 } 891 } 892 } 893 } 894 } 895 } 896 else if (labelPosition == "inside") 897 { 898 v.labelIsHorizontal = true; 899 v.labelWidth = 2 * columnSeries.seriesRenderData.renderedHalfWidth; 900 v.labelHeight = columnSeries.measuringField.textHeight + 2; 901 902 if (columnSeries.measuringField.textWidth + 5 > 2 * columnSeries.seriesRenderData.renderedHalfWidth && 903 columnSeries.measuringField.embedFonts) 904 { 905 v.labelIsHorizontal = false; 906 v.labelWidth = Math.abs(v.y -(isNaN(v.min) ? base : v.min)); 907 v.labelHeight = 2 * columnSeries.seriesRenderData.renderedHalfWidth; 908 if (v.y < (isNaN(v.min) ? base : v.min)) 909 v.labelY = isNaN(v.min) ? base : v.min; 910 else 911 v.labelY = v.y; 912 913 v.labelX = v.x - v.labelHeight/2 + columnSeries.seriesRenderData.renderedXOffset; 914 } 915 916 labelScale = 1; 917 if (columnSeries.measuringField.textWidth + 5 > v.labelWidth || columnSeries.measuringField.textHeight > v.labelHeight) 918 { 919 labelScale = v.labelWidth / (columnSeries.measuringField.textWidth + 5); 920 labelScale = Math.min(labelScale, v.labelHeight / columnSeries.measuringField.textHeight); 921 if (size * labelScale > labelSizeLimit) 922 { 923 columnSeries.seriesRenderData.labelScale = Math.min(labelScale,columnSeries.seriesRenderData.labelScale); 924 /* if (v.labelIsHorizontal) 925 { 926 _tempField.setStyle('fontSize',size * columnSeries.seriesRenderData.labelScale); 927 v.labelHeight = _tempField.textHeight + 2; 928 } */ 929 } 930 else 931 { 932 _tempField.setStyle('fontSize',size); 933 _tempField.validateNow(); 934 if (_tempField.measuredWidth > v.labelWidth || columnSeries.measuringField.textHeight > v.labelHeight) 935 { 936 labelScale = v.labelWidth / _tempField.measuredWidth; 937 labelScale = Math.min(labelScale, v.labelHeight / (columnSeries.measuringField.textHeight)); 938 if (size * labelScale > labelSizeLimit) 939 { 940 columnSeries.seriesRenderData.labelScale = Math.min(labelScale,columnSeries.seriesRenderData.labelScale); 941 /* if (v.labelIsHorizontal) 942 { 943 _tempField.setStyle('fontSize',size * columnSeries.seriesRenderData.labelScale); 944 v.labelHeight = _tempField.textHeight + 2; 945 } */ 946 } 947 else 948 { 949 v.labelText = ""; 950 v.labelWidth = 1; 951 v.labelHeight = 0; 952 } 953 } 954 } 955 } 956 if (v.labelIsHorizontal) 957 { 958 var align:String = columnSeries.getStyle('labelAlign'); 959 if (align == "top") 960 { 961 if (v.y < (isNaN(v.min) ? base : v.min)) 962 v.labelY = v.y; 963 else 964 v.labelY = v.y - v.labelHeight; 965 } 966 else if (align == "bottom") 967 { 968 if (v.y < (isNaN(v.min) ? base : v.min)) 969 v.labelY = (isNaN(v.min) ? base : v.min) - v.labelHeight; 970 else 971 v.labelY = isNaN(v.min) ? base : v.min; 972 } 973 else 974 { 975 if (v.y < (isNaN(v.min) ? base : v.min)) 976 v.labelY = v.y + Math.abs(v.y - (isNaN(v.min) ? base : v.min))/2 - v.labelHeight/2; 977 else 978 v.labelY = v.y - Math.abs(v.y - (isNaN(v.min) ? base : v.min))/2 + v.labelHeight/2; 979 } 980 v.labelX = v.x - columnSeries.seriesRenderData.renderedHalfWidth + columnSeries.seriesRenderData.renderedXOffset; 981 } 982 v.labelTextWidth = columnSeries.measuringField.textWidth; 983 j = 0; 984 if (!extendLabelToEnd) 985 { 986 //maxLabelWidth 987 for (j = (i-1); j >= 1;) 988 { 989 if (_allItems[j].x >= v.x - maxLabelWidth) 990 j--; 991 else 992 break; 993 } 994 if (j < 0) 995 j = i; 996 } 997 998 for (k = j; k < i; k++) 999 { 1000 tempItem = _allItems[k]; 1001 if (tempItem.labelIsHorizontal) 1002 { 1003 if (v.labelX + 0.0001 < Math.min(tempItem.labelX + tempItem.labelWidth , tempItem.labelX + tempItem.labelTextWidth)) 1004 { 1005 if (v.labelIsHorizontal) 1006 { 1007 if ((((v.labelY + v.labelHeight) > tempItem.labelY) && ((v.labelY + v.labelHeight) < (tempItem.labelY + tempItem.labelHeight))) || 1008 ((v.labelY > tempItem.labelY) && (v.labelY < (tempItem.labelY + tempItem.labelHeight))) || 1009 ((v.labelY == tempItem.labelY) && ((tempItem.labelY + tempItem.labelHeight) == (v.labelY + v.labelHeight)) && (v.labelX > tempItem.labelX) && (v.labelX < (tempItem.labelX + tempItem.labelWidth))) || 1010 ((v.labelY <= tempItem.labelY) && ((v.labelY + v.labelHeight) > (tempItem.labelY + tempItem.labelHeight))) || 1011 ((v.labelY < tempItem.labelY) && ((v.labelY + v.labelHeight) >= (tempItem.labelY + tempItem.labelHeight)))) 1012 { 1013 if (v.y < (isNaN(v.min) ? base : v.min)) 1014 v.labelY = tempItem.labelY - v.labelHeight; 1015 else 1016 v.labelY = tempItem.labelY + tempItem.labelHeight; 1017 } 1018 else if (((v.labelY < tempItem.labelY) && ((v.labelY + v.labelHeight) > tempItem.labelY)) || 1019 ((v.labelY > tempItem.labelY) && (v.labelY < (tempItem.labelY + tempItem.labelHeight))) || 1020 (v.labelY == tempItem.labelY)) 1021 { 1022 if (v.yNumber >= tempItem.yNumber) 1023 { 1024 tempItem.labelText = ""; 1025 tempItem.labelWidth = 1; 1026 } 1027 else 1028 { 1029 v.labelText = ""; 1030 v.labelWidth = 1; 1031 } 1032 } 1033 } 1034 else 1035 { 1036 if (((v.labelY > tempItem.labelY) && ((v.labelY - Math.min(v.labelWidth,v.labelTextWidth)) < (tempItem.labelY))) || 1037 (((v.labelY - v.labelWidth) >= tempItem.labelY) && (v.labelY > (tempItem.labelY - tempItem.labelHeight)))) 1038 { 1039 prevY = v.labelY; 1040 if (v.y < (isNaN(v.min) ? base : v.min)) 1041 { 1042 v.labelY = tempItem.labelY - tempItem.labelHeight; 1043 v.labelWidth = v.labelWidth - (prevY - v.labelY); 1044 } 1045 else 1046 { 1047 v.labelY = tempItem.labelY + tempItem.labelHeight; 1048 v.labelWidth = v.labelWidth - (v.labelY - prevY); 1049 } 1050 } 1051 } 1052 } 1053 } 1054 else 1055 { 1056 if (v.labelX == tempItem.labelX) 1057 { 1058 if (((v.labelY <= tempItem.labelY) && (v.labelY > (tempItem.labelY - Math.min(tempItem.labelWidth, tempItem.labelTextWidth) + 0.0001))) || 1059 ((v.labelY >= tempItem.labelY) && ((v.labelY - Math.min(v.labelWidth, v.labelTextWidth) + 0.0001) < tempItem.labelY))) 1060 { 1061 if (v.yNumber < tempItem.yNumber) 1062 { 1063 v.labelText = ""; 1064 v.labelWidth = 1; 1065 } 1066 else 1067 { 1068 tempItem.labelText = ""; 1069 tempItem.labelWidth = 1; 1070 } 1071 } 1072 } 1073 } 1074 } 1075 } 1076 } 1077 else 1078 { 1079 columnSeries.seriesRenderData.labelData = null; 1080 columnSeries.labelCache.count = 0; 1081 } 1082 } 1083 allLabelsMeasured = true; 1084 n = len; 1085 for (i = 0; i < n; i++) 1086 { 1087 invalidateDisplay(series[i]); 1088 } 1089 return null; 1090 } 1091 1092 1093 //--------------------------------------------------------------------------------- 1094 // 1095 // Methods 1096 // 1097 //--------------------------------------------------------------------------------- 1098 mx_internal function getSeriesLabelPos(series:Series):void 1099 { 1100 if (series is ColumnSeries) 1101 { 1102 var columnSeries:ColumnSeries = ColumnSeries(series); 1103 var position:String = columnSeries.labelPos; 1104 if (position == "inside" || position == "outside" || position == "none") 1105 _needLabels = true; 1106 } 1107 else if (series is ColumnSet) 1108 { 1109 var setSeries:Array /* of Series */ = ColumnSet(series).series; 1110 var n:int = setSeries.length; 1111 for (var i:int = 0; i < n; i++) 1112 { 1113 getSeriesLabelPos(setSeries[i]); 1114 } 1115 } 1116 } 1117 1118 mx_internal function getSeriesLabelPosSet():void 1119 { 1120 var n:int = series.length; 1121 _needLabels = false; 1122 for (var i:int = 0; i < n; i++) 1123 { 1124 if (_needLabels) 1125 return; 1126 getSeriesLabelPos(series[i]); 1127 } 1128 } 1129 1130 /** 1131 * @private 1132 */ 1133 private function sortOnX(a:ColumnSeriesItem,b:ColumnSeriesItem):int 1134 { 1135 var offset1:Number = ColumnSeries(a.element).seriesRenderData.renderedXOffset; 1136 var offset2:Number = ColumnSeries(b.element).seriesRenderData.renderedXOffset; 1137 if (a.x + offset1 > b.x + offset2) 1138 return 1; 1139 else if (a.x + offset1 < b.x + offset2) 1140 return -1; 1141 else 1142 { 1143 if (a.yNumber > b.yNumber) 1144 return 1; 1145 else if (a.yNumber < b.yNumber) 1146 return -1; 1147 else 1148 return 0; 1149 } 1150 1151 } 1152 1153 /** 1154 * @private 1155 */ 1156 private function findChartItems(series:Series):void 1157 { 1158 var n:int; 1159 var i:int; 1160 if (series is ColumnSeries) 1161 { 1162 var columnSeries:ColumnSeries = ColumnSeries(series); 1163 var seriesItems:Array /* of ColumnSeriesItem */ = columnSeries.seriesRenderData.filteredCache; 1164 n = seriesItems.length; 1165 columnSeries.labelCache.count = n; 1166 columnSeries.seriesRenderData.labelData = columnSeries.labelContainer; 1167 columnSeries.seriesRenderData.labelScale = 1; 1168 for (i = 0; i < n; i++) 1169 { 1170 _allItems.push(ColumnSeriesItem(seriesItems[i])); 1171 } 1172 } 1173 else if (series is ColumnSet) 1174 { 1175 var setSeries:Array /* of Series */ = ColumnSet(series).series; 1176 n = setSeries.length; 1177 for (i = 0; i < n; i++) 1178 { 1179 findChartItems(setSeries[i]); 1180 } 1181 } 1182 } 1183 /** 1184 * @private 1185 */ 1186 private function invalidateDisplay(series:Series):void 1187 { 1188 if (series is ColumnSeries) 1189 ColumnSeries(series).updateLabels(); 1190 else if (series is ColumnSet) 1191 { 1192 var setSeries:Array /* of Series */ = ColumnSet(series).series; 1193 var n:int = setSeries.length; 1194 for (var i:int = 0; i < n; i++) 1195 { 1196 invalidateDisplay(setSeries[i]); 1197 } 1198 } 1199 } 1200} 1201 1202} 1203