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.Graphics;
16import flash.geom.Rectangle;
17
18import mx.charts.chartClasses.CartesianChart;
19import mx.charts.chartClasses.ChartElement;
20import mx.charts.chartClasses.ChartState;
21import mx.charts.chartClasses.GraphicsUtilities;
22import mx.charts.chartClasses.IAxisRenderer;
23import mx.charts.styles.HaloDefaults;
24import mx.core.IFlexModuleFactory;
25import mx.core.mx_internal;
26import mx.graphics.IFill;
27import mx.graphics.IStroke;
28import mx.graphics.SolidColorStroke;
29import mx.graphics.Stroke;
30import mx.styles.CSSStyleDeclaration;
31
32use namespace mx_internal;
33
34//--------------------------------------
35//  Styles
36//--------------------------------------
37
38/**
39 *  Specifies the direction of the grid lines.
40 *  Allowable values are <code>horizontal</code>,
41 *  <code>vertical</code>, or <code>both</code>.
42 *  The default value is <code>horizontal</code>.
43 *
44 *  @langversion 3.0
45 *  @playerversion Flash 9
46 *  @playerversion AIR 1.1
47 *  @productversion Flex 3
48 */
49[Style(name="gridDirection", type="String", enumeration="horizontal,vertical,both", inherit="no")]
50
51/**
52 *  Specifies the fill pattern for alternating horizontal bands
53 *  not defined by the <code>fill</code> property.
54 *  Use the IFill class to define the properties of the fill
55 *  as a child tag in MXML, or create an IFill object in ActionScript.
56 *  Set to <code>null</code> to not fill the bands.
57 *  The default value is <code>null</code>.
58 *
59 *  @langversion 3.0
60 *  @playerversion Flash 9
61 *  @playerversion AIR 1.1
62 *  @productversion Flex 3
63 */
64[Style(name="horizontalAlternateFill", type="mx.graphics.IFill", inherit="no")]
65
66/**
67 *  Specifies the number of tick marks between horizontal grid lines.
68 *  Set the <code>horizontalChangeCount</code> property to 3
69 *  to draw a grid line at every third tick mark along the axis.
70 *  The fill style alternates at each grid line, so a larger
71 *  <code>horizontalChangeCount</code> value results
72 *  in large alternating bands.
73 *  The defaults value is <code>1</code>.
74 *
75 *  @langversion 3.0
76 *  @playerversion Flash 9
77 *  @playerversion AIR 1.1
78 *  @productversion Flex 3
79 */
80[Style(name="horizontalChangeCount", type="int", inherit="no")]
81
82/**
83 *  Specifies the fill pattern for every other horizontal band
84 *  created by the grid lines.
85 *  Use the IFill class to define the  properties of the fill
86 *  as a child tag in MXML, or create a IFill object in ActionScript.
87 *  Set to <code>null</code> to not fill the bands.
88 *  The default value is <code>null</code>.
89 *
90 *  @langversion 3.0
91 *  @playerversion Flash 9
92 *  @playerversion AIR 1.1
93 *  @productversion Flex 3
94 */
95[Style(name="horizontalFill", type="mx.graphics.IFill", inherit="no")]
96
97/**
98 *  Specifies the line style for the horizontal origin.
99 *  Use the Stroke class to define the properties as a child tag in MXML,
100 *  or create a Stroke object in ActionScript.
101 *
102 *  @langversion 3.0
103 *  @playerversion Flash 9
104 *  @playerversion AIR 1.1
105 *  @productversion Flex 3
106 */
107[Style(name="horizontalOriginStroke", type="mx.graphics.IStroke", inherit="no")]
108
109/**
110 *  Determines whether to draw the horizontal origin.
111 *  If <code>true</code>, and the origin falls within the chart bounds,
112 *  the grid lines draw it using the <code>horizontalOriginStroke</code> style.
113 *  For ColumnChart, LineChart, PlotChart, BubbleChart, and AreaChart
114 *  controls, the default value is <code>true</code>.
115 *  For BarChart controls, the default value is <code>false</code>.
116 *  This property does not apply to PieChart controls.
117 *
118 *  @langversion 3.0
119 *  @playerversion Flash 9
120 *  @playerversion AIR 1.1
121 *  @productversion Flex 3
122 */
123[Style(name="horizontalShowOrigin", type="Boolean", inherit="no")]
124
125/**
126 *  Specifies the line style for horizontal grid lines.
127 *  Use the Stroke class to define the properties as a child tag in MXML,
128 *  or create a Stroke object in ActionScript.
129 *
130 *  @langversion 3.0
131 *  @playerversion Flash 9
132 *  @playerversion AIR 1.1
133 *  @productversion Flex 3
134 */
135[Style(name="horizontalStroke", type="mx.graphics.IStroke", inherit="no")]
136
137/**
138 *  Determines whether to align horizontal grid lines with tick marks.
139 *  If <code>true</code>, horizontal grid lines are drawn aligned
140 *  with the tick marks.
141 *  If <code>false</code>, Flex draws them between tick marks.
142 *  The default value is <code>true</code>.
143 *
144 *  @langversion 3.0
145 *  @playerversion Flash 9
146 *  @playerversion AIR 1.1
147 *  @productversion Flex 3
148 */
149[Style(name="horizontalTickAligned", type="Boolean", inherit="no")]
150
151/**
152 *  Specifies the fill pattern for alternating vertical bands
153 *  not defined by the fill property.
154 *  Use the IFill class to define the properties of the fill
155 *  as a child tag in MXML, or create an IFill object in ActionScript.
156 *  Set to <code>null</code> to not fill the bands.
157 *  The default value is <code>null</code>.
158 *
159 *  @langversion 3.0
160 *  @playerversion Flash 9
161 *  @playerversion AIR 1.1
162 *  @productversion Flex 3
163 */
164[Style(name="verticalAlternateFill", type="mx.graphics.IFill", inherit="no")]
165
166/**
167 *  Specifies the number of tick marks between vertical grid lines.
168 *  Set <code>verticalChangeCount</code> to <code>3</code>
169 *  to draw a grid line at every third tick mark along the axis.
170 *  The fill style alternates at each grid line, so a larger
171 *  <code>verticalChangeCount</code> value results in large alternating bands.
172 *  The default value is <code>1</code>.
173 *
174 *  @langversion 3.0
175 *  @playerversion Flash 9
176 *  @playerversion AIR 1.1
177 *  @productversion Flex 3
178 */
179[Style(name="verticalChangeCount", type="int", inherit="no")]
180
181/**
182 *  Specifies the fill pattern for alternating vertical bands
183 *  created by the grid lines.
184 *  Use the IFill class to define the properties of the fill
185 *  as a child tag in MXML, or create a IFill object in ActionScript.
186 *  Set to <code>null</code> to not fill the bands.
187 *  The default value is <code>null</code>.
188 *
189 *  @langversion 3.0
190 *  @playerversion Flash 9
191 *  @playerversion AIR 1.1
192 *  @productversion Flex 3
193 */
194[Style(name="verticalFill", type="mx.graphics.IFill", inherit="no")]
195
196/**
197 *  Specifies the line style for the vertical origin.
198 *  Use the Stroke class to define the properties as a child tag in MXML,
199 *  or create a Stroke object in ActionScript.
200 *
201 *  @langversion 3.0
202 *  @playerversion Flash 9
203 *  @playerversion AIR 1.1
204 *  @productversion Flex 3
205 */
206[Style(name="verticalOriginStroke", type="mx.graphics.IStroke", inherit="no")]
207
208/**
209 *  Determines whether to draw the vertical Origin.
210 *  If <code>true</code>, and the origin falls within the chart bounds,
211 *  Flex draws it using the <code>verticalOriginStroke</code> style.
212 *  For ColumnChart, LineChart, and AreaChart controls,
213 *  the default value is <code>false</code>.
214 *  For PlotChart, BubbleChart, and BarChart controls,
215 *  the default value is <code>true</code>.
216 *  This property does not apply to PieChart controls.
217 *
218 *  @langversion 3.0
219 *  @playerversion Flash 9
220 *  @playerversion AIR 1.1
221 *  @productversion Flex 3
222 */
223[Style(name="verticalShowOrigin", type="Boolean", inherit="no")]
224
225/**
226 *  Specifies the line style for vertical grid lines.
227 *  Use the Stroke class to define the properties as a child tag in MXML,
228 *  or create a Stroke object in ActionScript.
229 *
230 *  @langversion 3.0
231 *  @playerversion Flash 9
232 *  @playerversion AIR 1.1
233 *  @productversion Flex 3
234 */
235[Style(name="verticalStroke", type="mx.graphics.IStroke", inherit="no")]
236
237/**
238 *  Determines whether to align vertical grid lines with tick marks.
239 *  If <code>true</code>, Flex draws vertical grid lines aligned
240 *  with the tick marks.
241 *  If <code>false</code>, Flex draws them between tick marks.
242 *  The default value is <code>true</code>.
243 *
244 *  @langversion 3.0
245 *  @playerversion Flash 9
246 *  @playerversion AIR 1.1
247 *  @productversion Flex 3
248 */
249[Style(name="verticalTickAligned", type="Boolean", inherit="no")]
250
251/**
252 *  The GridLines class draws grid lines inside the data area of the chart.
253 *  Flex can draw lines horizontally, vertically, or both.
254 *
255 *  <p>Flex draws grid lines aligned to the tick marks of the parent chart.
256 *  By default, Flex draws one line for every tick mark
257 *  along the appropriate axis.</p>
258 *
259 *  <p>You typically use the GridLines class as a child tag
260 *  of a chart control's <code>backgroundElements</code> property
261 *  or <code>annotationElements</code> property.</p>
262 *
263 *  @mxml
264 *
265 *  <p>The <code>&lt;mx:GridLines&gt;</code> tag inherits all the properties
266 *  of its parent classes and adds the following properties:</p>
267 *
268 *  <pre>
269 *  &lt;mx:GridLines
270 *    <strong>Styles</strong>
271 *     gridDirection="horizontal|vertical|both"
272 *     horizontalAlternateFill="null"
273 *     horizontalChangeCount="1"
274 *     horizontalFill="null"
275 *     horizontalOriginStroke="<i>IStroke; No default</i>"
276 *     horizontalShowOrigin="<i>Default depends on type of chart</i>"
277 *     horizontalStroke="<i>IStroke; No default</i>"
278 *     horizontalTickAligned="true|false"
279 *     verticalAlternateFill="null"
280 *     verticalChangeCount="1"
281 *     verticalFill="null"
282 *     verticalOriginStroke="<i>IStroke; No default</i>"
283 *     verticalShowOrigin="<i>Default depends on type of chart</i>"
284 *     verticalStroke="<i>IStroke; No default</i>"
285 *     verticalTickAligned="true|false"
286 *  /&gt;
287 *  </pre>
288 *
289 *  @includeExample examples/GridLinesExample.mxml
290 *
291 *  @langversion 3.0
292 *  @playerversion Flash 9
293 *  @playerversion AIR 1.1
294 *  @productversion Flex 3
295 */
296public class GridLines extends ChartElement
297{
298    include "../core/Version.as";
299
300	//--------------------------------------------------------------------------
301	//
302	//  Class initialization
303	//
304	//--------------------------------------------------------------------------
305
306	//--------------------------------------------------------------------------
307	//
308	//  Constructor
309	//
310	//--------------------------------------------------------------------------
311
312	/**
313	 *  Constructor.
314	 *
315	 *  @langversion 3.0
316	 *  @playerversion Flash 9
317	 *  @playerversion AIR 1.1
318	 *  @productversion Flex 3
319	 */
320	public function GridLines()
321	{
322		super();
323	}
324
325	//--------------------------------------------------------------------------
326	//
327	//  Variables
328	//
329	//--------------------------------------------------------------------------
330
331	/**
332	 *  @private
333	 */
334	private var _moduleFactoryInitialized:Boolean = false;
335
336	//--------------------------------------------------------------------------
337	//
338	//  Overridden methods: UIComponent
339	//
340	//--------------------------------------------------------------------------
341
342	/**
343	 *  @private
344	 */
345	private function initStyles():Boolean
346	{
347		HaloDefaults.init(styleManager);
348
349		var gridLinesStyle:CSSStyleDeclaration = styleManager.getStyleDeclaration("mx.charts.GridLines");
350		gridLinesStyle.setStyle("horizontalOriginStroke", new SolidColorStroke(0xB0C1D0, 1));
351		gridLinesStyle.setStyle("horizontalStroke", new SolidColorStroke(0xEEEEEE, 0));
352		gridLinesStyle.setStyle("verticalOriginStroke", new SolidColorStroke(0xB0C1D0, 1));
353		gridLinesStyle.setStyle("verticalStroke", new SolidColorStroke(0xEEEEEE, 0));
354
355		var hgridLinesStyle:CSSStyleDeclaration = styleManager.getStyleDeclaration(".horizontalGridLines");
356		hgridLinesStyle.setStyle("horizontalFill", null);
357		hgridLinesStyle.setStyle("verticalFill", null);
358		return true;
359	}
360
361	/**
362	 *  @inheritDoc
363	 *
364	 *  @langversion 3.0
365	 *  @playerversion Flash 9
366	 *  @playerversion AIR 1.1
367	 *  @productversion Flex 3
368	 */
369	override public function set moduleFactory(factory:IFlexModuleFactory):void
370	{
371		super.moduleFactory = factory;
372
373		if (_moduleFactoryInitialized)
374			return;
375
376		_moduleFactoryInitialized = true;
377
378		// our style settings
379		initStyles();
380	}
381
382	/**
383	 *  @private
384	 */
385	override protected function updateDisplayList(unscaledWidth:Number,
386												  unscaledHeight:Number):void
387	{
388		super.updateDisplayList(unscaledWidth, unscaledHeight);
389
390		var len:int;
391		var c:Object;
392		var stroke:IStroke;
393		var changeCount:int;
394		var ticks:Array /* of Number */;
395		var spacing:Array /* of Number */;
396		var axisLength:Number;
397		var colors:Array /* of IFill */;
398		var rc:Rectangle;
399		var originStroke:IStroke;
400		var addedFirstLine:Boolean;
401		var addedLastLine:Boolean;
402		var n:int;
403
404		if (!chart||
405			chart.chartState == ChartState.PREPARING_TO_HIDE_DATA ||
406			chart.chartState == ChartState.HIDING_DATA)
407		{
408			return;
409		}
410
411		var g:Graphics = graphics;
412		g.clear();
413
414		var gridDirection:String = getStyle("gridDirection");
415		if (gridDirection == "horizontal" || gridDirection == "both")
416		{
417			stroke = getStyle("horizontalStroke");
418
419			changeCount = Math.max(1, getStyle("horizontalChangeCount"));
420			if ((changeCount * 0 != 0) || changeCount <= 1)
421				changeCount = 1;
422
423			var verticalAxisRenderer:IAxisRenderer;
424
425			if (!(CartesianChart(chart).verticalAxisRenderer))
426			{
427				verticalAxisRenderer = CartesianChart(chart).getLeftMostRenderer();
428				if (!verticalAxisRenderer)
429					verticalAxisRenderer = CartesianChart(chart).getRightMostRenderer();
430			}
431			else
432			    verticalAxisRenderer = CartesianChart(chart).verticalAxisRenderer;
433
434			ticks = verticalAxisRenderer.ticks;
435
436			if (getStyle("horizontalTickAligned") == false)
437			{
438				len = ticks.length;
439				spacing = [];
440				n = len;
441				for (var i:int = 1; i < n; i++)
442				{
443					spacing[i - 1] = (ticks[i] + ticks[i - 1]) / 2;
444				}
445			}
446			else
447			{
448				spacing = ticks;
449			}
450
451			addedFirstLine = false;
452			addedLastLine = false;
453
454			if (spacing[0] != 0)
455			{
456				addedFirstLine = true;
457				spacing.unshift(0);
458			}
459
460			if (spacing[spacing.length - 1] != 1)
461			{
462				addedLastLine = true;
463				spacing.push(1);
464			}
465
466			axisLength = unscaledHeight;
467
468			colors = [ getStyle("horizontalFill"),
469					   getStyle("horizontalAlternateFill") ];
470
471			len = spacing.length;
472
473			if (spacing[len - 1] < 1)
474			{
475				c = colors[1];
476				if (c != null)
477				{
478					g.lineStyle(0, 0, 0);
479					GraphicsUtilities.fillRect(g, 0,
480						spacing[len - 1] * axisLength, unscaledWidth,
481						unscaledHeight, c);
482				}
483			}
484
485			n = spacing.length;
486			for (i = 0; i < n; i += changeCount)
487			{
488				var idx:int = len - 1 - i;
489				c = colors[(i / changeCount) % 2];
490				var bottom:Number = spacing[idx] * axisLength;
491				var top:Number =
492					spacing[Math.max(0, idx - changeCount)] * axisLength;
493				rc = new Rectangle(0, top, unscaledWidth, bottom-top);
494
495				if (c != null)
496				{
497					g.lineStyle(0, 0, 0);
498					GraphicsUtilities.fillRect(g, rc.left, rc.top,
499											   rc.right, rc.bottom, c);
500				}
501
502				if (stroke && rc.bottom >= -1) //round off errors
503				{
504					if (addedFirstLine && idx == 0)
505						continue;
506					if (addedLastLine && idx == (spacing.length-1))
507						continue;
508
509					stroke.apply(g,null,null);
510					g.moveTo(rc.left, rc.bottom);
511					g.lineTo(rc.right, rc.bottom);
512
513				}
514			}
515		}
516
517		if (gridDirection == "vertical" || gridDirection == "both")
518		{
519
520			stroke = getStyle("verticalStroke");
521			changeCount = Math.max(1,getStyle("verticalChangeCount"));
522
523			if (isNaN(changeCount) || changeCount <= 1)
524				changeCount = 1;
525
526			var horizontalAxisRenderer:IAxisRenderer;
527
528			if (!(CartesianChart(chart).horizontalAxisRenderer))
529			{
530				horizontalAxisRenderer = CartesianChart(chart).getBottomMostRenderer();
531				if (!horizontalAxisRenderer)
532					horizontalAxisRenderer = CartesianChart(chart).getTopMostRenderer();
533			}
534			else
535			    horizontalAxisRenderer = CartesianChart(chart).horizontalAxisRenderer;
536
537			ticks = horizontalAxisRenderer.ticks.concat();
538
539			if (getStyle("verticalTickAligned") == false)
540			{
541				len = ticks.length;
542				spacing = [];
543				n = len;
544				for (i = 1; i < n; i++)
545				{
546					spacing[i - 1] = (ticks[i] + ticks[i - 1]) / 2;
547				}
548			}
549			else
550			{
551				spacing = ticks;
552			}
553
554			addedFirstLine = false;
555			addedLastLine = false;
556
557			if (spacing[0] != 0)
558			{
559				addedFirstLine = true;
560				spacing.unshift(0);
561			}
562
563			if (spacing[spacing.length - 1] != 1)
564			{
565				addedLastLine = true;
566				spacing.push(1);
567			}
568
569			axisLength = unscaledWidth;
570
571			colors = [ getStyle("verticalFill"),
572					   getStyle("verticalAlternateFill") ];
573
574			n = spacing.length;
575			for (i = 0; i < n; i += changeCount)
576			{
577				c = colors[(i / changeCount) % 2];
578				var left:Number = spacing[i] * axisLength;
579				var right:Number =
580					spacing[Math.min(spacing.length - 1,
581									 i + changeCount)] * axisLength;
582				rc = new Rectangle(left, 0, right - left, unscaledHeight);
583				if (c != null)
584				{
585					g.lineStyle(0, 0, 0);
586					GraphicsUtilities.fillRect(g, rc.left, rc.top,
587											   rc.right, rc.bottom, c);
588				}
589
590				if (stroke) // round off errors
591				{
592					if (addedFirstLine && i == 0)
593						continue;
594					if (addedLastLine && i == spacing.length-1)
595						continue;
596
597					stroke.apply(g,null,null);
598					g.moveTo(rc.left, rc.top);
599					g.lineTo(rc.left, rc.bottom);
600				}
601			}
602		}
603
604		var horizontalShowOrigin:Object = getStyle("horizontalShowOrigin");
605		var verticalShowOrigin:Object = getStyle("verticalShowOrigin");
606
607		if (verticalShowOrigin || horizontalShowOrigin)
608		{
609			var cache:Array /* of Object */ = [ { xOrigin: 0, yOrigin: 0 } ];
610			var sWidth:Number = 0.5;
611
612			dataTransform.transformCache(cache, "xOrigin", "x", "yOrigin", "y");
613
614			if (horizontalShowOrigin &&
615				cache[0].y > 0 && cache[0].y < unscaledHeight)
616			{
617				originStroke = getStyle("horizontalOriginStroke");
618				originStroke.apply(g,null,null);
619				g.moveTo(0, cache[0].y - sWidth / 2);
620				g.lineTo($width, cache[0].y - sWidth / 2);
621			}
622
623			if (verticalShowOrigin &&
624				cache[0].x > 0 && cache[0].x < unscaledWidth)
625			{
626				originStroke = getStyle("verticalOriginStroke");
627				originStroke.apply(g,null,null);
628				g.moveTo(cache[0].x - sWidth / 2, 0);
629				g.lineTo(cache[0].x - sWidth / 2, $height);
630			}
631		}
632	}
633
634	//--------------------------------------------------------------------------
635	//
636	//  Overridden methods: ChartElement
637	//
638	//--------------------------------------------------------------------------
639
640	/**
641	 *  @private
642	 */
643	override public function mappingChanged():void
644	{
645		invalidateDisplayList();
646	}
647
648	/**
649	 *  @private
650	 */
651	override public function chartStateChanged(oldState:uint,
652											   newState:uint):void
653	{
654		invalidateDisplayList();
655	}
656}
657
658}
659