1////////////////////////////////////////////////////////////////////////////////
2//
3//  ADOBE SYSTEMS INCORPORATED
4//  Copyright 2003-2007 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.skins.halo
13{
14
15import flash.display.GradientType;
16import flash.display.Graphics;
17import mx.core.IContainer;
18import mx.core.EdgeMetrics;
19import mx.core.FlexVersion;
20import mx.core.IUIComponent;
21import mx.core.mx_internal;
22import mx.graphics.RectangularDropShadow;
23import mx.skins.RectangularBorder;
24import mx.styles.IStyleClient;
25import mx.styles.StyleManager;
26import mx.utils.ColorUtil;
27import mx.graphics.GradientEntry;
28
29use namespace mx_internal;
30
31/**
32 *  Defines the appearance of the default border for the Halo theme.
33 */
34public class HaloBorder extends RectangularBorder
35{
36	include "../../core/Version.as";
37
38	//--------------------------------------------------------------------------
39	//
40	//  Class constants
41	//
42	//--------------------------------------------------------------------------
43
44	/**
45	 *  @private
46	 *  A look up table for the offsets.
47	 */
48	private static var BORDER_WIDTHS:Object =
49	{
50		none: 0,
51		solid: 1,
52		inset: 2,
53		outset: 2,
54		alert: 3,
55		dropdown: 2,
56		menuBorder: 1,
57		comboNonEdit: 2
58	};
59
60	//--------------------------------------------------------------------------
61	//
62	//  Constructor
63	//
64	//--------------------------------------------------------------------------
65
66	/**
67	 *  Constructor.
68	 */
69	public function HaloBorder()
70	{
71		super();
72
73		// 'default' is a keyword; setting it this way avoids a compiler error
74		BORDER_WIDTHS["default"] = 3;
75	}
76
77	//--------------------------------------------------------------------------
78	//
79	//  Fields
80	//
81	//--------------------------------------------------------------------------
82
83	/**
84	 *  @private
85	 *  A reference to the object used to cast a drop shadow.
86	 *  See the drawDropShadow() method for details.
87	 */
88	private var dropShadow:RectangularDropShadow;
89
90	mx_internal var backgroundColor:Object;
91	mx_internal var backgroundAlphaName:String;
92	mx_internal var backgroundHole:Object;
93	mx_internal var bRoundedCorners:Boolean;
94	mx_internal var radius:Number;
95	mx_internal var radiusObj:Object;
96
97	//--------------------------------------------------------------------------
98	//
99	//  Overridden properties
100	//
101	//--------------------------------------------------------------------------
102
103	//----------------------------------
104	//  borderMetrics
105	//----------------------------------
106
107	/**
108	 *  @private
109	 *  Internal object that contains the thickness of each edge
110	 *  of the border
111	 */
112	protected var _borderMetrics:EdgeMetrics;
113
114	/**
115	 *  @private
116	 *  Return the thickness of the border edges.
117	 *
118	 *  @return Object	top, bottom, left, right thickness in pixels
119	 */
120	override public function get borderMetrics():EdgeMetrics
121	{
122		if (_borderMetrics)
123			return _borderMetrics;
124
125		var borderThickness:Number;
126
127		// Add support for "custom" style type here when we support it.
128		var borderStyle:String = getStyle("borderStyle");
129
130 		if (borderStyle == "default" ||
131			borderStyle == "alert")
132 		{
133 			if (FlexVersion.compatibilityVersion < FlexVersion.VERSION_3_0)
134	        	_borderMetrics = new EdgeMetrics(0, 0, 0, 0);
135	        else
136	        	return EdgeMetrics.EMPTY;
137 		}
138
139		else if (borderStyle == "controlBar" ||
140				 borderStyle == "applicationControlBar")
141		{
142			_borderMetrics = new EdgeMetrics(1, 1, 1, 1);
143		}
144
145		else if (borderStyle == "solid")
146		{
147			borderThickness = getStyle("borderThickness");
148			if (isNaN(borderThickness))
149				borderThickness = 0;
150
151			_borderMetrics = new EdgeMetrics(borderThickness,
152											  borderThickness,
153											  borderThickness,
154											  borderThickness);
155
156			var borderSides:String = getStyle("borderSides");
157			if (borderSides != "left top right bottom")
158			{
159				// Adjust metrics based on which sides we have
160				if (borderSides.indexOf("left") == -1)
161					_borderMetrics.left = 0;
162
163				if (borderSides.indexOf("top") == -1)
164					_borderMetrics.top = 0;
165
166				if (borderSides.indexOf("right") == -1)
167					_borderMetrics.right = 0;
168
169				if (borderSides.indexOf("bottom") == -1)
170					_borderMetrics.bottom = 0;
171			}
172		}
173
174		else
175		{
176			borderThickness = BORDER_WIDTHS[borderStyle];
177			if (isNaN(borderThickness))
178				borderThickness = 0;
179
180			_borderMetrics = new EdgeMetrics(borderThickness,
181											 borderThickness,
182											 borderThickness,
183											 borderThickness);
184		}
185
186		return _borderMetrics;
187	}
188
189	//--------------------------------------------------------------------------
190	//
191	//  Overridden methods
192	//
193	//--------------------------------------------------------------------------
194
195	/**
196	 *  @private
197	 *  If borderStyle may have changed, clear the cached border metrics.
198	 */
199	override public function styleChanged(styleProp:String):void
200	{
201		if (styleProp == null ||
202			styleProp == "styleName" ||
203			styleProp == "borderStyle" ||
204			styleProp == "borderThickness" ||
205			styleProp == "borderSides")
206		{
207			_borderMetrics = null;
208		}
209
210		invalidateDisplayList();
211	}
212
213	/**
214	 *  @private
215	 *  Draw the border, either 3D or 2D or nothing at all.
216	 */
217	override protected function updateDisplayList(w:Number, h:Number):void
218	{
219		if (isNaN(w) || isNaN(h))
220			return;
221
222		super.updateDisplayList(w, h);
223
224		// Store background color in an object,
225		// so that null is distinct from black.
226		backgroundColor = getBackgroundColor();
227		bRoundedCorners = false;
228		backgroundAlphaName = "backgroundAlpha";
229		backgroundHole = null;
230		radius = 0;
231		radiusObj = null;
232
233		drawBorder(w,h);
234		drawBackground(w,h);
235	}
236
237	//--------------------------------------------------------------------------
238	//
239	//  Methods
240	//
241	//--------------------------------------------------------------------------
242
243	/**
244	 *  @private
245	 */
246	mx_internal function drawBorder(w:Number, h:Number):void
247	{
248		var borderStyle:String = getStyle("borderStyle");
249
250		// Other styles that we may fetch.
251		var highlightAlphas:Array = getStyle("highlightAlphas");
252
253		var backgroundAlpha:Number;
254
255		var borderCapColor:uint;
256		var borderColor:uint;
257		var borderSides:String;
258		var borderThickness:Number;
259		var buttonColor:uint;
260		var docked:Boolean;
261		var dropdownBorderColor:uint;
262		var fillColors:Array;
263		var footerColors:Array;
264		var highlightColor:uint;
265		var shadowCapColor:uint;
266		var shadowColor:uint;
267		var themeColor:uint;
268		var translucent:Boolean;
269
270		var hole:Object;
271		var drawTopHighlight:Boolean = false;
272
273		var borderColorDrk1:Number
274		var borderColorDrk2:Number
275		var borderColorLt1:Number
276
277		var borderInnerColor:Object;
278
279		var g:Graphics = graphics;
280		g.clear();
281
282		if (borderStyle)
283		{
284			switch (borderStyle)
285			{
286				case "none":
287				{
288					break;
289				}
290
291				case "inset": // used for text input & numeric stepper
292				{
293					borderColor = getStyle("borderColor");
294					borderColorDrk1 =
295						ColorUtil.adjustBrightness2(borderColor, -40);
296					borderColorDrk2 =
297						ColorUtil.adjustBrightness2(borderColor, +25);
298					borderColorLt1 =
299						ColorUtil.adjustBrightness2(borderColor, +40);
300
301					borderInnerColor = backgroundColor;
302					if (borderInnerColor === null ||
303						borderInnerColor === "")
304					{
305						borderInnerColor = borderColor;
306					}
307
308					draw3dBorder(borderColorDrk2, borderColorDrk1, borderColorLt1,
309								 Number(borderInnerColor),
310								 Number(borderInnerColor),
311								 Number(borderInnerColor));
312					break;
313				}
314
315				case "outset":
316				{
317					borderColor = getStyle("borderColor");
318					borderColorDrk1 =
319						ColorUtil.adjustBrightness2(borderColor, -40);
320					borderColorDrk2 =
321						ColorUtil.adjustBrightness2(borderColor, -25);
322					borderColorLt1 =
323						ColorUtil.adjustBrightness2(borderColor, +40);
324
325					borderInnerColor = backgroundColor;
326					if (borderInnerColor === null ||
327						borderInnerColor === "")
328					{
329						borderInnerColor = borderColor;
330					}
331
332					draw3dBorder(borderColorDrk2, borderColorLt1, borderColorDrk1,
333								 Number(borderInnerColor),
334								 Number(borderInnerColor),
335								 Number(borderInnerColor));
336					break;
337				}
338
339				case "alert":
340				case "default":
341				{
342					if (FlexVersion.compatibilityVersion < FlexVersion.VERSION_3_0)
343					{
344						// For Panel/Alert, "borderAlpha" is the alpha for the
345						// title/control/gutter area and "backgroundAlpha"
346						// is the alpha for the content area.
347						// We flip-flop the variables here so the "borderAlpha"
348						// is applied by the background drawing code at the bottom.
349						var contentAlpha:Number = getStyle("backgroundAlpha");
350						backgroundAlpha = getStyle("borderAlpha");
351						backgroundAlphaName = "borderAlpha";
352
353						radius = getStyle("cornerRadius");
354						bRoundedCorners =
355							getStyle("roundedBottomCorners").toString().toLowerCase() == "true";
356						var br:Number = bRoundedCorners ? radius : 0;
357
358						drawDropShadow(0, 0, w, h, radius, radius, br, br);
359
360						// If we don't have rounded corners we need to initialize
361						// the complex radius object so the background fill code
362						// below works correctly.
363						if (!bRoundedCorners)
364							radiusObj = {};
365
366						var parentContainer:IContainer = parent as IContainer;
367
368						if (parentContainer)
369						{
370							var vm:EdgeMetrics = parentContainer.viewMetrics;
371
372							// The backgroundHole is the content area
373							backgroundHole = {x:vm.left, y:vm.top,
374											  w: Math.max(0, w - vm.left - vm.right),
375											  h: Math.max(0, h - vm.top - vm.bottom),
376											  r:0};
377
378							if (backgroundHole.w > 0 && backgroundHole.h > 0)
379							{
380								// Draw a shadow around the content
381								// if the content and panel alpha are different.
382								// This could be a style property if needed
383								if (contentAlpha != backgroundAlpha)
384								{
385									drawDropShadow(backgroundHole.x, backgroundHole.y,
386											backgroundHole.w, backgroundHole.h,
387											0, 0, 0, 0);
388								}
389
390								// Fill in the content area
391								g.beginFill(Number(backgroundColor), contentAlpha);
392								g.drawRect(backgroundHole.x, backgroundHole.y,
393										backgroundHole.w, backgroundHole.h);
394								g.endFill();
395							}
396						}
397
398						// When the content and panel alpha are different, the border
399						// of the panel is drawn using borderColor. We've already
400						// drawn the content background so we set backgroundColor to
401						// borderColor here so the drawing code below is done with the
402						// border color.
403						backgroundColor = getStyle("borderColor");
404					}
405
406					break;
407				}
408
409				case "dropdown": // never used
410				{
411					// The dropdownBorderColor is currently only used
412					// when displaying an error state.
413					dropdownBorderColor = getStyle("dropdownBorderColor");
414
415					drawDropShadow(0, 0, w, h, 4, 0, 0, 4);
416
417					// frame
418					drawRoundRect(
419						0, 0, w, h,
420						{ tl: 4, tr: 0, br: 0, bl: 4 },
421						0x4D555E, 1);
422
423					// gradient
424					drawRoundRect(
425						0, 0, w, h,
426						{ tl: 4, tr: 0, br: 0, bl: 4},
427						[ 0xFFFFFF, 0xFFFFFF ], [ 0.7, 0 ],
428						verticalGradientMatrix(0, 0, w, h));
429
430					// button top higlight edge
431					drawRoundRect(
432						1, 1, w - 1, h - 2,
433						{ tl: 3, tr: 0, br: 0, bl: 3 },
434						0xFFFFFF, 1);
435
436					// button face
437					drawRoundRect(
438						1, 2, w - 1, h - 3,
439						{ tl: 3, tr: 0, br: 0, bl: 3 },
440						[ 0xEEEEEE, 0xFFFFFF ], 1,
441						verticalGradientMatrix(0, 0, w - 1, h - 3));
442
443					if (!isNaN(dropdownBorderColor))
444					{
445						// combo background in error state
446						drawRoundRect(
447							0, 0, w + 1, h,
448							{ tl: 4, tr: 0, br: 0, bl: 4 },
449							dropdownBorderColor, 0.5);
450
451						// button top higlight edge
452						drawRoundRect(
453							1, 1, w - 1, h - 2,
454							{ tl: 3, tr: 0, br: 0, bl: 3 },
455							0xFFFFFF, 1);
456
457						//button face
458						drawRoundRect(
459							1, 2, w - 1, h - 3,
460							{ tl: 3, tr: 0, br: 0, bl: 3 },
461							[ 0xEEEEEE, 0xFFFFFF ], 1,
462							verticalGradientMatrix(0, 0, w - 1, h - 3));
463					}
464
465					// Make sure the border isn't filled in down below.
466					backgroundColor = null;
467
468					break;
469				}
470
471				case "menuBorder":
472				{
473					borderColor = getStyle("borderColor");
474					drawRoundRect(
475						0, 0, w, h, 0,
476						borderColor, 1);
477					drawDropShadow(1, 1, w - 2, h - 2, 0, 0, 0, 0);
478					break;
479				}
480
481				case "comboNonEdit":
482				{
483					break;
484				}
485
486				case "controlBar":
487				{
488					if (w == 0 || h == 0)
489					{
490						// If the width or height is 0, don't draw anything.
491						backgroundColor = null;
492						break;
493					}
494
495					footerColors = getStyle("footerColors");
496					var showChrome:Boolean = footerColors != null;
497					var borderAlpha:Number = getStyle("borderAlpha");
498
499					if (showChrome)
500					{
501						g.lineStyle(0, footerColors.length > 0 ?
502									footerColors[1] : footerColors[0], borderAlpha);
503						g.moveTo(0, 0);
504						g.lineTo(w, 0);
505						g.lineStyle(0, 0, 0);
506
507						// cornerRadius is defined on our parent container. Reach up
508						// and grab it. Yes, this is cheating...
509						if (parent && parent.parent && parent.parent is IStyleClient)
510						{
511							radius =
512								IStyleClient(parent.parent).getStyle("cornerRadius");
513							borderAlpha =
514								IStyleClient(parent.parent).getStyle("borderAlpha");
515						}
516
517						if (isNaN(radius))
518							radius = 0;
519
520						// If our parent has square bottom corners,
521						// use square corners.
522						if (IStyleClient(parent.parent).
523							getStyle("roundedBottomCorners").toString().toLowerCase() != "true")
524						{
525							radius = 0;
526						}
527
528						drawRoundRect(
529							0, 1, w, h - 1,
530							{ tl: 0, tr: 0, bl:radius, br: radius },
531							footerColors, borderAlpha,
532							verticalGradientMatrix(0, 0, w, h));
533
534						if (footerColors.length > 1 &&
535							footerColors[0] != footerColors[1])
536						{
537							drawRoundRect(
538								0, 1, w, h - 1,
539								{ tl: 0, tr: 0, bl: radius, br: radius },
540								[ 0xFFFFFF, 0xFFFFFF ], highlightAlphas,
541								verticalGradientMatrix(0, 0, w, h));
542
543							drawRoundRect(
544								1, 2, w - 2, h - 3,
545								{ tl: 0, tr: 0, bl: radius - 1, br: radius - 1 },
546								footerColors, borderAlpha,
547								verticalGradientMatrix(0, 0, w, h));
548						}
549					}
550
551					// Don't draw the background color below.
552					// We've already handled it here.
553					backgroundColor = null;
554					break;
555				}
556
557				case "applicationControlBar":
558				{
559					fillColors = getStyle("fillColors");
560					backgroundAlpha = getStyle("backgroundAlpha");
561					highlightAlphas = getStyle("highlightAlphas");
562					var fillAlphas:Array = getStyle("fillAlphas");
563					docked = getStyle("docked");
564
565					// background color of the bar
566					var backgroundColorNum:uint = uint(backgroundColor);
567
568					radius = getStyle("cornerRadius");
569					if (!radius)
570						radius = 0;
571
572					drawDropShadow(0, 1, w, h - 1,
573								   radius, radius, radius, radius);
574
575					if (backgroundColor !== null && StyleManager.isValidStyleValue(backgroundColor))
576					{
577						drawRoundRect(
578							0, 1, w, h - 1, radius,
579							backgroundColorNum, backgroundAlpha,
580							verticalGradientMatrix(0, 0, w, h));
581					}
582
583					// surface
584					drawRoundRect(
585						0, 1, w, h - 1, radius,
586						fillColors, fillAlphas,
587						verticalGradientMatrix(0, 0, w, h));
588
589					// highlight
590					drawRoundRect(
591						0, 1, w, (h / 2) - 1,
592						{ tl: radius, tr: radius, bl: 0, br: 0 },
593						[ 0xFFFFFF, 0xFFFFFF ], highlightAlphas,
594						verticalGradientMatrix(0, 0, w, h / 2 - 1));
595
596					// edge
597					drawRoundRect(
598						0, 1, w, h - 1,
599						{ tl: radius, tr: radius, bl: 0, br: 0 },
600						0xFFFFFF, 0.3, null,
601						GradientType.LINEAR, null,
602						{ x: 0, y: 2, w: w, h: h - 2,
603						  r: { tl: radius, tr: radius, bl: 0, br: 0 } });
604
605					// Don't draw the background color below.
606					// We've already handled it here.
607					backgroundColor = null;
608
609					break;
610				}
611
612				default: // ((borderStyle == "solid") || (borderStyle == null))
613				{
614					borderColor = getStyle("borderColor");
615					borderThickness = getStyle("borderThickness");
616					borderSides = getStyle("borderSides");
617					var bHasAllSides:Boolean = true;
618					radius = getStyle("cornerRadius");
619
620					bRoundedCorners =
621						getStyle("roundedBottomCorners").toString().toLowerCase() == "true";
622
623					var holeRadius:Number =
624						Math.max(radius - borderThickness, 0);
625
626					hole = { x: borderThickness,
627							 y: borderThickness,
628							 w: w - borderThickness * 2,
629							 h: h - borderThickness * 2,
630							 r: holeRadius };
631
632					if (!bRoundedCorners)
633					{
634						radiusObj = {tl:radius, tr:radius, bl:0, br:0};
635						hole.r = {tl:holeRadius, tr:holeRadius, bl:0, br:0};
636					}
637
638					if (borderSides != "left top right bottom")
639					{
640						// Convert the radius values from a scalar to an object
641						// because we need to adjust individual radius values
642						// if we are missing any sides.
643						hole.r = { tl: holeRadius,
644								   tr: holeRadius,
645								   bl: bRoundedCorners ? holeRadius : 0,
646								   br: bRoundedCorners ? holeRadius : 0 };
647
648						radiusObj = { tl: radius,
649									  tr: radius,
650									  bl: bRoundedCorners ? radius : 0,
651									  br: bRoundedCorners ? radius : 0};
652
653						borderSides = borderSides.toLowerCase();
654
655						if (borderSides.indexOf("left") == -1)
656						{
657							hole.x = 0;
658							hole.w += borderThickness;
659							hole.r.tl = 0;
660							hole.r.bl = 0;
661							radiusObj.tl = 0;
662							radiusObj.bl = 0;
663							bHasAllSides = false;
664						}
665
666						if (borderSides.indexOf("top") == -1)
667						{
668							hole.y = 0;
669							hole.h += borderThickness;
670							hole.r.tl = 0;
671							hole.r.tr = 0;
672							radiusObj.tl = 0;
673							radiusObj.tr = 0;
674							bHasAllSides = false;
675						}
676
677						if (borderSides.indexOf("right") == -1)
678						{
679							hole.w += borderThickness;
680							hole.r.tr = 0;
681							hole.r.br = 0;
682							radiusObj.tr = 0;
683							radiusObj.br = 0;
684							bHasAllSides = false;
685						}
686
687						if (borderSides.indexOf("bottom") == -1)
688						{
689							hole.h += borderThickness;
690							hole.r.bl = 0;
691							hole.r.br = 0;
692							radiusObj.bl = 0;
693							radiusObj.br = 0;
694							bHasAllSides = false;
695						}
696					}
697
698					if (radius == 0 && bHasAllSides)
699					{
700						drawDropShadow(0, 0, w, h, 0, 0, 0, 0);
701
702						g.beginFill(borderColor);
703						g.drawRect(0, 0, w, h);
704						g.drawRect(borderThickness, borderThickness,
705								   w - 2 * borderThickness,
706								   h - 2 * borderThickness);
707						g.endFill();
708					}
709					else if (radiusObj)
710					{
711						drawDropShadow(0, 0, w, h,
712									   radiusObj.tl, radiusObj.tr,
713									   radiusObj.br, radiusObj.bl);
714
715						drawRoundRect(
716							0, 0, w, h, radiusObj,
717							borderColor, 1,
718							null, null, null, hole);
719
720						// Reset radius here so background drawing
721						// below is correct.
722						radiusObj.tl = Math.max(radius - borderThickness, 0);
723						radiusObj.tr = Math.max(radius - borderThickness, 0);
724						radiusObj.bl = bRoundedCorners ? Math.max(radius - borderThickness, 0) : 0;
725						radiusObj.br = bRoundedCorners ? Math.max(radius - borderThickness, 0) : 0;
726					}
727					else
728					{
729						drawDropShadow(0, 0, w, h,
730									   radius, radius, radius, radius);
731
732						drawRoundRect(
733							0, 0, w, h, radius,
734							borderColor, 1,
735							null, null, null, hole);
736
737						// Reset radius here so background drawing
738						// below is correct.
739						radius = Math.max(getStyle("cornerRadius") -
740								 borderThickness, 0);
741					}
742				}
743			} // switch
744		}
745	}
746
747
748	/**
749	 *  @private
750	 *  Draw a 3D border.
751	 */
752	mx_internal function draw3dBorder(c1:Number, c2:Number, c3:Number,
753								  c4:Number, c5:Number, c6:Number):void
754	{
755		var w:Number = width;
756		var h:Number = height;
757
758		/*
759		// temp color override to verify layout of lines
760		var c1:Number = 0x919999;
761		var c2:Number = 0x6F7777;
762		var c3:Number = 0xD5DDDD;
763		var c4:Number = 0xC4CCCC;
764		var c5:Number = 0xEEEEEE;
765		var c6:Number = 0xD5DDDD;
766		*/
767
768		drawDropShadow(0, 0, width, height, 0, 0, 0, 0);
769
770		var g:Graphics = graphics;
771
772		// outside sides
773		g.beginFill(c1);
774		g.drawRect(0, 0, w, h);
775		g.drawRect(1, 0, w - 2, h);
776		g.endFill();
777
778		// outside top
779		g.beginFill(c2);
780		g.drawRect(1, 0, w - 2, 1);
781		g.endFill();
782
783		// outside bottom
784		g.beginFill(c3);
785		g.drawRect(1, h - 1, w - 2, 1);
786		g.endFill();
787
788		// inside top
789		g.beginFill(c4);
790		g.drawRect(1, 1, w - 2, 1);
791		g.endFill();
792
793		// inside bottom
794		g.beginFill(c5);
795		g.drawRect(1, h - 2, w - 2, 1);
796		g.endFill();
797
798		// inside sides
799		g.beginFill(c6);
800		g.drawRect(1, 2, w - 2, h - 4);
801		g.drawRect(2, 2, w - 4, h - 4);
802		g.endFill();
803	}
804
805	/**
806	 *  @private
807	 */
808	mx_internal function drawBackground(w:Number, h:Number):void
809	{
810		// The behavior used to be that we always create a background
811		// regardless of whether we have a background color or not.
812		// Now we only create a background if we have a color
813		// or if the mouseShield or mouseShieldChildren styles are true.
814		// Look at Container.addEventListener and Container.isBorderNeeded
815		// for the mouseShield logic. JCS 6/24/05
816		if ((backgroundColor !== null &&
817		     backgroundColor !== "") ||
818			getStyle("mouseShield") ||
819			getStyle("mouseShieldChildren"))
820		{
821			var nd:Number = Number(backgroundColor);
822			var alpha:Number = 1.0;
823			var bm:EdgeMetrics = getBackgroundColorMetrics();
824			var g:Graphics = graphics;
825
826			if (isNaN(nd) ||
827				backgroundColor === "" ||
828				backgroundColor === null)
829			{
830				alpha = 0;
831				nd = 0xFFFFFF;
832			}
833			else
834			{
835				alpha = getStyle(backgroundAlphaName);
836			}
837
838			// If we have a non-zero radius, use drawRoundRect()
839			// to fill in the background.
840			if (radius != 0 || backgroundHole)
841			{
842				var bottom:Number = bm.bottom;
843
844				if (radiusObj)
845				{
846					var topRadius:Number =
847 						Math.max(radius - Math.max(bm.top, bm.left, bm.right), 0);
848 					var bottomRadius:Number = bRoundedCorners ?
849 						Math.max(radius - Math.max(bm.bottom, bm.left, bm.right), 0) : 0;
850
851					radiusObj = { tl: topRadius,
852							      tr: topRadius,
853							      bl: bottomRadius,
854							      br: bottomRadius };
855
856					drawRoundRect(
857						bm.left, bm.top,
858						width - (bm.left + bm.right),
859						height - (bm.top + bottom),
860						radiusObj,
861						nd, alpha, null,
862						GradientType.LINEAR, null,
863						backgroundHole);
864				}
865				else
866				{
867					drawRoundRect(
868						bm.left, bm.top,
869						width - (bm.left + bm.right),
870						height - (bm.top + bottom),
871						radius,
872						nd, alpha, null,
873						GradientType.LINEAR, null,
874						backgroundHole);
875				}
876			}
877			else
878			{
879				g.beginFill(nd, alpha);
880				g.drawRect(bm.left, bm.top,
881						   w - bm.right - bm.left, h - bm.bottom - bm.top);
882				g.endFill();
883			}
884		}
885
886		var borderStyle:String = getStyle("borderStyle");
887
888		if (FlexVersion.compatibilityVersion < FlexVersion.VERSION_3_0 && (borderStyle == "alert" || borderStyle == "default") && getStyle("headerColors") == null)
889 		{
890 			var highlightAlphas:Array = getStyle("highlightAlphas");
891 			var highlightAlpha:Number = highlightAlphas ? highlightAlphas[0] : 0.3;
892 			// edge
893 			drawRoundRect(
894 				0, 0, w, h,
895 				{ tl: radius, tr: radius, bl: 0, br: 0 },
896 				0xFFFFFF, highlightAlpha, null,
897 				GradientType.LINEAR, null,
898 				{ x: 0, y: 1, w: w, h: h - 1,
899 				  r: { tl: radius, tr: radius, bl: 0, br: 0 } });
900 		}
901	}
902
903	/**
904	 *  @private
905	 *  Apply a drop shadow using a bitmap filter.
906	 *
907	 *  Bitmap filters are slow, and their slowness is proportional
908	 *  to the number of pixels being filtered.
909	 *  For a large HaloBorder, it's wasteful to create a big shadow.
910	 *  Instead, we'll create the shadow offscreen
911	 *  and stretch it to fit the HaloBorder.
912	 */
913	mx_internal function drawDropShadow(x:Number, y:Number,
914									width:Number, height:Number,
915									tlRadius:Number, trRadius:Number,
916									brRadius:Number, blRadius:Number):void
917	{
918		// Do I need a drop shadow in the first place?  If not, return
919		// immediately.
920		if (getStyle("dropShadowEnabled") == false ||
921		    getStyle("dropShadowEnabled") == "false" ||
922			width == 0 ||
923			height == 0)
924		{
925			return;
926		}
927
928		// Calculate the angle and distance for the shadow
929		var distance:Number = getStyle("shadowDistance");
930		var direction:String = getStyle("shadowDirection");
931		var angle:Number;
932		if (getStyle("borderStyle") == "applicationControlBar")
933		{
934			var docked:Boolean = getStyle("docked");
935			angle = docked ? 90 : getDropShadowAngle(distance, direction);
936			distance = Math.abs(distance);
937		}
938		else
939		{
940			angle = getDropShadowAngle(distance, direction);
941			distance = Math.abs(distance) + 2;
942		}
943
944		// Create a RectangularDropShadow object, set its properties,
945		// and draw the shadow
946		if (!dropShadow)
947			dropShadow = new RectangularDropShadow();
948
949		dropShadow.distance = distance;
950		dropShadow.angle = angle;
951		dropShadow.color = getStyle("dropShadowColor");
952		dropShadow.alpha = 0.4;
953
954		dropShadow.tlRadius = tlRadius;
955		dropShadow.trRadius = trRadius;
956		dropShadow.blRadius = blRadius;
957		dropShadow.brRadius = brRadius;
958
959		dropShadow.drawShadow(graphics, x, y, width, height);
960	}
961
962	/**
963	 *  @private
964	 *  Convert the value of the shadowDirection property
965	 *  into a shadow angle.
966	 */
967	mx_internal function getDropShadowAngle(distance:Number,
968										direction:String):Number
969	{
970		if (direction == "left")
971			return distance >= 0 ? 135 : 225;
972
973		else if (direction == "right")
974			return distance >= 0 ? 45 : 315;
975
976		else // direction == "center"
977			return distance >= 0 ? 90 : 270;
978	}
979
980	/**
981	 *  @private
982	 */
983	mx_internal function getBackgroundColor():Object
984	{
985		var p:IUIComponent = parent as IUIComponent;
986		if (p && !p.enabled)
987		{
988			var color:Object = getStyle("backgroundDisabledColor");
989			if (color !== null && StyleManager.isValidStyleValue(color))
990				return color;
991		}
992
993		return getStyle("backgroundColor");
994	}
995
996	/**
997	 *  @private
998	 */
999	mx_internal function getBackgroundColorMetrics():EdgeMetrics
1000	{
1001		return borderMetrics;
1002	}
1003}
1004
1005}
1006