//**************************************************************************** //Copyright (C) 2003 Macromedia, Inc. All Rights Reserved. //The following is Sample Code and is subject to all restrictions on //such code as contained in the End User License Agreement accompanying //this product. //**************************************************************************** import mx.styles.StyleManager; import mx.styles.CSSStyleDeclaration; import mx.skins.SkinElement; /** * @tiptext resize event * @helpid 3972 */ [Event("resize")] /** * @tiptext move event * @helpid 3969 */ [Event("move")] /** * @tiptext draw event * @helpid 3963 */ [Event("draw")] /** * @tiptext load event * @helpid 3968 */ [Event("load")] /** * @tiptext unload event * @helpid 3980 */ [Event("unload")] /** * The base class for all components and graphical objects. * UIObjects support events and styles and resize by scaling. * * @helpid 3285 * @tiptext Base class for all components and graphical objects. Extends MovieClip */ class mx.core.UIObject extends MovieClip { /** * @private * SymbolName for object */ static var symbolName:String = "UIObject"; /** * @private * Class used in createClassObject */ static var symbolOwner:Object = UIObject; // Version string #include "../core/ComponentVersion.as" /** * @private * standard color list for text objects */ static var textColorList = { color: 1, disabledColor: 1 }; // whether the component needs to be drawn private var invalidateFlag:Boolean = false; // line width and color for drawing API private var lineWidth:Number = 1; private var lineColor:Number = 0; // black // UIObjects are not supposed to receive focus var tabEnabled:Boolean = false; // sometimes we inherit from something, but don't want to // inherit its class styles. var ignoreClassStyleDeclaration:Object; // sometimes properties get set before the children components // have been created. This flag can be used to guard against // early property setting. var childrenCreated:Boolean; /** * @private * @see mx.events.EventDispatcher */ var createEvent:Function; /** * @private * @see mx.events.EventDispatcher */ var dispatchEvent:Function; /** * @see mx.events.EventDispatcher * @tiptext Adds a listener for an event * @helpid 3958 */ var addEventListener:Function; /** * @see mx.events.EventDispatcher * @tiptext Handles all events * @helpid 3032 */ var handleEvent:Function; /** * @see mx.events.EventDispatcher */ var removeEventListener:Function; // see mx.managers.DepthManager var buildDepthTable:Function; var findNextAvailableDepth:Function; var createChildAtDepth:Function; var createClassChildAtDepth:Function /** * @see mx.accessibility.accImpl */ var createAccessibilityImplementation:Function; // internal ID name for this instance private var _id:String; /** * @private * Initialization property that forces immediate drawing after creation */ var validateNow:Boolean; // foreground text color var color:Number; // mixed from styles var fontSize:Number; var fontWeight:String; var fontFamily:String; var fontStyle:String; var textAlign:String; var textDecoration:String; var textIndent:Number; var marginLeft:Number; var marginRight:Number; var embedFonts:Boolean; var changeTextStyleInChildren:Function; var changeColorStyleInChildren:Function; var notifyStyleChangeInChildren:Function; // mixed from DepthManager; var _topmost:Boolean; /** * @private * The color style used by this component. If more than one * color style, use an object containing the colors as properties * on the object */ var _color; /** * @private * CSSStyleDeclaration or pointer to parent to be used by component in * calculating style values. */ var styleName:String; /** * * Name of component class. This is also used in calculating style values. * If _global.styles[className] exists, it set defaults for a component. */ var className:String; // cache of cascading styles var stylecache:Object; // list of functions used by doLater() var methodTable:Array; // array of skin instance names used by setSkin var idNames:Array; /** * set a style property. Causes lots of processing so use sparingly. * actual implementation is in mx.styles.CSSSetStyle.as * * @param String prop name of style property * @param Variant value new value for style * * @tiptext Sets a style value for the specified style property * @helpid 3978 */ var setStyle : Function; /** * @private * list of clip parameters to check at init * only getter/setter properties go in this list */ var clipParameters:Object = {visible: 1, minHeight: 1, minWidth: 1, maxHeight: 1, maxWidth: 1, preferredHeight: 1, preferredWidth: 1}; /** * @private * method used to init objects w/ getter-setters */ var initProperties:Function; // these hold the actual values for the getter-setters var __width:Number; var __height:Number; private var _minHeight:Number; private var _minWidth:Number; private var _maxHeight:Number; private var _maxWidth:Number; private var _preferredHeight:Number; private var _preferredWidth:Number; // local copy of textformat private var _tf:TextFormat; // list of textfield children private var tfList:Object; // place to store the hooked copy of onUnload private var __onUnload:Function; // place to hook in other init-time functions private var _endInit:Function; function UIObject() { constructObject(); } /** * width of object * Read-Only: use setSize() to change. * @helpid 3982 */ function get width():Number { return _width; } /** * height of object * Read-Only: use setSize() to change. * @helpid 3964 */ function get height():Number { return _height; } /** * left of object * Read-Only: use move() to change. * @helpid 3967 */ function get left():Number { return _x; } /** * x = left of object * Read-Only: use move() to change. * @helpid 3983 */ function get x():Number { return _x; } /** * top of object * Read-Only: use move() to change. * @helpid 3979 */ function get top():Number { return _y; } /** * y = top of object * Read-Only: use move() to change. * @helpid 3984 */ function get y():Number { return _y; } /** * right of object relative to its parent's right edge. * Read-Only: use setSize() to change. * @helpid 3973 */ function get right():Number { return _parent.width - (_x + width); } /** * bottom of object relative to its parent's bottom * Read-Only: use setSize() to change. * @helpid 3959 */ function get bottom():Number { return _parent.height - (_y + height); } /** * @private * override this instead of adding your own getter-setter for minHeight */ function getMinHeight(Void):Number { return _minHeight; } /** * @private * override this instead of adding your own getter-setter for minHeight */ function setMinHeight(h:Number):Void { _minHeight = h; } /** * minimum height of object */ [Inspectable(defaultValue=0, verbose=1, category="Size")] function get minHeight():Number { return getMinHeight(); } function set minHeight(h:Number):Void { setMinHeight(h); } /** * @private * override this instead of adding your own getter-setter for minWidth */ function getMinWidth(Void):Number { return _minWidth; } /** * @private * override this instead of adding your own getter-setter for minWidth */ function setMinWidth(w:Number):Void { _minWidth = w; } /** * minimum width of object */ [Inspectable(defaultValue=0, verbose=1, category="Size")] function get minWidth():Number { return getMinWidth(); } function set minWidth(w:Number):Void { setMinWidth(w); } /** * @private * override this instead of adding your own getter-setter for visible */ function setVisible(x:Boolean, noEvent:Boolean):Void { if (x != this._visible) { _visible = x; if (noEvent != true) { dispatchEvent({type: x ? "reveal" : "hide"}); } } } /** * True if object is visible * @helpid 3981 */ [Inspectable(defaultValue=true, verbose=1, category="Other")] function get visible():Boolean { return _visible; } function set visible(x:Boolean):Void { setVisible(x, false); } /** * 100 is standard scale * @tiptext Specifies the horizontal scale factor * @helpid 3974 */ function get scaleX():Number { return _xscale; } function set scaleX(x:Number):Void { _xscale = x; //__width = _width; } /** * 100 is standard scale * @tiptext Specifies the vertical scale factor * @helpid 3975 */ function get scaleY():Number { return _yscale; } function set scaleY(y:Number):Void { _yscale = y; //__height = _height; } /** * Queues a function to be called later * * @param obj Object that contains the function * @param fn Name of function on Object */ function doLater(obj:Object, fn:String):Void { if (methodTable == undefined) { methodTable = new Array(); } methodTable.push({obj:obj, fn:fn}); onEnterFrame = doLaterDispatcher; } // callback that then calls queued functions function doLaterDispatcher(Void):Void { delete onEnterFrame; // invalidation comes first if (invalidateFlag) { redraw(); } // make a copy of the methodtable so methods called can requeue themselves w/o putting // us in an infinite loop var __methodTable:Array = methodTable; // new doLater calls will be pushed here methodTable = new Array(); // now do everything else if (__methodTable.length > 0) { var m:Object; while((m = __methodTable.shift()) != undefined) { m.obj[m.fn](); } } } /** * cancel all queued functions */ function cancelAllDoLaters(Void):Void { delete this.onEnterFrame; this.methodTable = new Array(); } /** * mark component so it will get drawn later * @tiptext Marks an object to be redrawn on the next frame interval * @helpid 3966 */ function invalidate(Void):Void { invalidateFlag = true; onEnterFrame = doLaterDispatcher; } /** * called if just styles are changing so subclasses don't have to redraw everything */ function invalidateStyle(Void):Void { invalidate(); } /** * redraws object if you couldn't wait for invalidation to do it * * @param bAlways if False, doesn't redraw if not invalidated * @tiptext Redraws an object immediately * @helpid 3971 */ function redraw(bAlways:Boolean):Void { if (invalidateFlag || bAlways) { invalidateFlag = false; // all textfields are hung off this method so we can call them as well since they don't // have their own enterFrame event var i:String; for (i in tfList) { tfList[i].draw(); } draw(); dispatchEvent({ type:"draw"}); } } /** * @private * draw the object. Called by redraw() which is called explicitly or * by the system if the object is invalidated. * Each component should implement this method and make subobjects visible and lay them out. * Most components do the layout by calling their size() method. */ function draw(Void):Void { } /** * move the object * * @param x left position of the object * @param y top position of the object * @param noEvent if true, doesn't broadcast "move" event * * @tiptext Moves the object to the specified location * @helpid 3970 */ function move(x:Number, y:Number, noEvent:Boolean):Void { // trace("UIObject.move " + this + " -> (" + x + ", " + y + ")"); var oldX:Number = _x; var oldY:Number = _y; _x = x; _y = y; if (noEvent != true) { dispatchEvent({type:"move", oldX:oldX, oldY:oldY}); } } /** * size the object * * @param w width of the object * @param h height of the object * @param noEvent if true, doesn't broadcast "resize" event * * @tiptext Resizes the object to the specified size * @helpid 3976 */ function setSize(w:Number, h:Number, noEvent:Boolean):Void { // trace("UIObject.setSize " + this + " -> (" + w + ", " + h + ")"); var oldWidth:Number = __width; var oldHeight:Number = __height; __width = w; __height = h; size(); if (noEvent != true) { dispatchEvent({type:"resize", oldWidth:oldWidth, oldHeight:oldHeight}); } } /** * @private * size the object. called by setSize(). Components should implement this method * to layout their contents. The width and height properties are set to the * new desired size by the time size() is called. */ // default is to scale object. override to do something more intelligent function size(Void):Void { _width = __width; _height = __height; } /** * draw unfilled rectangle on the screen * * @param x1 (x1, y1) is one corner of rectangle * @param y1 (x1, y1) is one corner of rectangle * @param x2 (x2, y2) is other corner of rectangle * @param y2 (x2, y2) is other corner of rectangle * @param r (r) is corner radius of rectangle or object containing radii for each corner */ function drawRect(x1:Number, y1:Number, x2:Number, y2:Number):Void { moveTo(x1,y1); lineTo(x2,y1); lineTo(x2,y2); lineTo(x1,y2); lineTo(x1,y1); } /** * @private * create a text label subobject. Used by most components to get a lightweight * text object to display text in the component. * * @param name instance name of text object * @param depth z order of object * @param text text of object * @return reference to text object */ function createLabel(name:String, depth:Number, text):TextField { //createTextField(name, depth, x, y, w, h); createTextField(name, depth, 0, 0, 0, 0); var o:TextField = this[name]; o._color = UIObject.textColorList; o._visible = false; // defer the style lookup to the textfield's draw routine o.__text = text; // @@ this needs improvement, since margin will vary with the font and size // var margin = 4; // o.setSize(o.textWidth + margin, o.textHeight + margin); if (tfList == undefined) tfList = new Object(); tfList[name] = o; o.invalidateStyle(); invalidate(); // force redraw call o.styleName = this; // labels always inherit styles of parent unless set otherwise return o; } /** * create a subobject from its symbol name * * @param symbol symbol name of object * @param id instance name of object * @param depth z order of object * @param initObj object containing initialization properties * @return reference to object * * @tiptext Creates a sub-object using its symbol name * @helpid 3960 */ function createObject(linkageName:String, id:String, depth:Number, initobj:Object):MovieClip { // trace("UIObject createObject: " + linkageName); return attachMovie(linkageName, id, depth, initobj); } /** * create a subobject from its class definition * * @param class reference to class of object * @param id instance name of object * @param depth z order of object * @param initObj object containing initialization properties * @return reference to object * * @tiptext Creates a sub-object using its class name * @helpid 3961 */ function createClassObject(className:Function, id:String, depth:Number, initobj:Object):UIObject { var bSubClass:Boolean = (className.symbolName == undefined); if (bSubClass) { Object.registerClass(className.symbolOwner.symbolName, className); } var o:UIObject = UIObject(createObject(className.symbolOwner.symbolName, id, depth, initobj)); if (bSubClass) { Object.registerClass(className.symbolOwner.symbolName, className.symbolOwner); } return o; } /** * create a blank or empty subobject * * @param id instance name of object * @param depth z order of object * @return reference to object */ function createEmptyObject(id:String, depth:Number):UIObject { return createClassObject(UIObject, id, depth); } /** * destroy the subobject * * @param id instance name of object * * @tiptext Destroys the specified object * @helpid 3962 */ function destroyObject(id:String):Void { var o:MovieClip = this[id]; if (o.getDepth() < 0) { var dt:Array = buildDepthTable(); var i:Number = findNextAvailableDepth(0, dt, "up"); var temp = i; // COMPILER WORKAROUND o.swapDepths(temp); } o.removeMovieClip(); delete this[id]; } /** * @private * lookup the instance name from the skin ID number. Uses the idNames * array to map to an instance name * * @param tag id number in idNames */ function getSkinIDName(tag:Number):String { return idNames[tag]; } /** * @private * create a skin element. Recommended way of adding graphical * objects to a component. * * @param tag id number of skin * @param name symbol name of object * @param initObj object containing initialization properties * @return reference to object * @helpid 3977 */ function setSkin(tag:Number, linkageName:String, initObj:Object):MovieClip { if (_global.skinRegistry[linkageName] == undefined) { SkinElement.registerElement(linkageName, SkinElement); } return createObject(linkageName, getSkinIDName(tag), tag, initObj); } /** * @private * create a blank or empty skin element. Rarely used. Recommended way is to * load a skin containing graphics or drawing code * * @param tag id number of skin * @return reference to object */ function createSkin(tag:Number):UIObject { var id:String = getSkinIDName(tag); createEmptyObject(id, tag); return this[id]; } /** * @private * create children objects. Components implement this method to create the * subobjects in the component. Recommended way is to make text objects * invisible and make them visible when the draw() method is called to * avoid flicker on the screen. * */ function createChildren(Void):Void { } // call create children and set flag. function _createChildren(Void):Void { createChildren(); childrenCreated = true; } // sets up the order of construction of a component function constructObject(Void):Void { // this gets called when being defined as the prototype // don't do anything, just return. if (_name == undefined) { return; } // initialize variables and the like; init(); // create child objects _createChildren(); // create accessibility if needed createAccessibilityImplementation(); // hook extension _endInit(); // draw it now if (validateNow) { redraw(true); } else // or draw it later { invalidate(); } } // process all the clipParameters in the list so the setters get fired. function initFromClipParameters(Void):Void { var bFound:Boolean = false; var i:String; for (i in clipParameters) { if (this.hasOwnProperty(i)) { bFound = true; this["def_" + i] = this[i]; delete this[i]; } } if (bFound) { for (i in clipParameters) { var v = this["def_" + i]; if (v != undefined) this[i] = v; } } } /** * @private * init variables. Components should implement this method and call super.init() to * ensure this method gets called. The width, height and clip parameters will not * be properly set until after this is called. * */ function init(Void):Void { __width = _width; __height = _height; if (initProperties == undefined) { initFromClipParameters(); } else { initProperties(); // defined in onClipEvent(initialize) for each instance. } if (_global.cascadingStyles == true) { stylecache = new Object(); } } // find the class style sheet for this instance. Equivalent // to type selectors in CSS, except that it also uses // inheritance as valid types function getClassStyleDeclaration(Void):CSSStyleDeclaration { var o:Object = this; var c:String = className; while (c != undefined) { if (ignoreClassStyleDeclaration[c] == undefined) { if (_global.styles[c] != undefined) { return _global.styles[c]; } } o = o.__proto__; c = o.className; } } /** * @private * set color on object. This method gets called if the value of a * color style gets changed and that color style is listed * as the _color property of the component. * */ function setColor(color:Number):Void { // standard UIObject doesn't do any coloring } // called recursively to fill out a textFormat object by calling each of its parents function __getTextFormat(tf:TextFormat, bAll:Boolean):Boolean { // see if we have a cached text format. Note that this should never be true on the first // call since this does not get called if the object has a cached text format. This is only to // see if a parent has a text format. var o:TextFormat = stylecache.tf; if (o != undefined) { // for each field in the mapping var j:String; for (j in StyleManager.TextFormatStyleProps) { if (bAll || StyleManager.TextFormatStyleProps[j]) if (tf[j] == undefined) tf[j] = o[j]; } return false; } var bUndefined:Boolean = false; // for each field in the mapping var j:String; for (j in StyleManager.TextFormatStyleProps) { if (bAll || StyleManager.TextFormatStyleProps[j]) { if (tf[j] == undefined) { // get the value from the textFormat var v = _tf[j]; // store it in the tf if not defined if (v != undefined) { tf[j] = v; } else { // nsaxena: for cascading styles, if _parent is a movie clip // since mx.styles.CSSTextStyles.addTextStyles(mc) is not called // no better way to check than reverse mapping if(j == "font" && this["fontFamily"] != undefined) { tf[j] = this["fontFamily"]; } else if(j == "size" && this["fontSize"] != undefined) { tf[j] = this["fontSize"]; } else if(j == "color" && this["color"] != undefined) { tf[j] = this["color"]; } else if(j == "leftMargin" && this["marginLeft"] != undefined) { tf[j] = this["marginLeft"]; } else if(j == "rightMargin" && this["marginRight"] != undefined) { tf[j] = this["marginRight"]; } else if(j == "italic" && this["fontStyle"] != undefined) { tf[j] = (this["fontStyle"] == j); } else if(j == "bold" && this["fontWeight"] != undefined) { tf[j] = (this["fontWeight"] == j); } else if(j == "align" && this["textAlign"] != undefined) { tf[j] = this["textAlign"]; } else if(j == "indent" && this["textIndent"] != undefined) { tf[j] = this["textIndent"]; } else if(j == "underline" && this["textDecoration"] != undefined) { tf[j] = (this["textDecoration"] == j); } else if(j == "embedFonts" && this["embedFonts"] != undefined) { tf[j] = this["embedFonts"]; } else { bUndefined = true; } } } } } // if some fields are still undefined use css rules to ask the next person if (bUndefined) { // check our style sheet var name = styleName; if (name != undefined) { if (typeof (name) != "string") { bUndefined = name.__getTextFormat(tf, true, this); } else { if (_global.styles[name] != undefined) { bUndefined = _global.styles[name].__getTextFormat(tf, true, this); } } } } if (bUndefined) { var ss = getClassStyleDeclaration(); if (ss != undefined) bUndefined = ss.__getTextFormat(tf, true, this); } if (bUndefined) { if (_global.cascadingStyles) { if (_parent != undefined) bUndefined = _parent.__getTextFormat(tf, false); } } if (bUndefined) bUndefined = _global.style.__getTextFormat(tf, true, this); return bUndefined; } // text objects call this to find out their styles function _getTextFormat(Void):TextFormat { var tf:TextFormat = stylecache.tf; if (tf != undefined) return tf; tf = new TextFormat(); __getTextFormat(tf, true); stylecache.tf = tf; if (enabled == false) { var c:Number = getStyle("disabledColor"); tf.color = c; } return tf; } // Used to see if a styleDeclaration change might apply to // this instance. function getStyleName(Void):String { var name = styleName; // can be object or string if (name != undefined) { if (typeof (name) != "string") { return name.getStyleName(); } else return name; } if (_parent != undefined) return _parent.getStyleName(); else return undefined; } /** * get a style property * * @param String prop name of style property * @return Variant the style value * * @tiptext Gets the style value associated with the style property * @helpid 3965 */ function getStyle(styleProp:String) { var v = undefined; _global.getStyleCounter ++; // see if it is in-line if (this[styleProp] != undefined) { return this[styleProp]; } // check our style sheet var name = styleName; if (name != undefined) { if (typeof (name) != "string") { // went back to Slightly slower for inheriting styles, but required for non-inheriting v = name.getStyle(styleProp); } else { var ss = _global.styles[name]; v = ss.getStyle(styleProp); // if (ss[styleProp] != undefined) // v = ss[styleProp]; } } if (v != undefined) return v; var ss = getClassStyleDeclaration(); if (ss != undefined) v = ss[styleProp]; if (v != undefined) return v; if (_global.cascadingStyles) { if (StyleManager.isInheritingStyle(styleProp) || StyleManager.isColorStyle(styleProp)) { // see if there is a style cache var b:Object = stylecache; if (b != undefined) { // see if the value is on the style cache if (b[styleProp] != undefined) return b[styleProp]; } // if we have a parent get it from the parent if (_parent != undefined) v = _parent.getStyle(styleProp); else { v = _global.style[styleProp]; } if (b != undefined) b[styleProp] = v; return v; } } if (v == undefined) v = _global.style[styleProp]; return v; } /** * @private * Used by component developers to create the list of clip parameters. */ static function mergeClipParameters(o, p):Boolean { for (var i in p) { o[i] = p[i]; } return true; } }