1  /*
     2   Copyright aswing.org, see the LICENCE.txt.
     3  */
     4  
     5  import org.aswing.Component;
     6  import org.aswing.EmptyLayout;
     7  import org.aswing.Event;
     8  import org.aswing.geom.Dimension;
     9  import org.aswing.LayoutManager;
    10  
    11  
    12  /**
    13   * Container can contain many component to be his child, all children are in its bounds,
    14   * and it moved, all children moved. It be removed(destroy) all children will be removed.
    15   * @author iiley
    16   */
    17  class org.aswing.Container extends Component{
    18  	/**
    19  	 * Event include source and the component just removed.
    20  	 * onComponentAdded Event{source:Component, com:Component}
    21  	 */	
    22  	public static var ON_COM_ADDED:String = "onComponentAdded";
    23  	
    24  	/**
    25  	 * Event include source and the component just added.
    26  	 * onComponentRemoved Event{source:Component, com:Component}
    27  	 */	
    28  	public static var ON_COM_REMOVED:String = "onComponentRemoved";
    29  	
    30  	private var children:Array;
    31  	private var layout:LayoutManager;
    32  	
    33  	
    34  	/**
    35  	 * Abstract Container
    36  	 * 
    37  	 * @see Component
    38  	 */
    39  	private function Container(){
    40  		super();
    41  		setName("Container");
    42  		children = new Array();
    43  		layout = new EmptyLayout();
    44  		
    45  		
    46  	}
    47  	
    48  	public function setLayout(layout:LayoutManager):Void{
    49  		this.layout = layout;
    50  		revalidate();
    51  	}
    52  	
    53  	public function getLayout():LayoutManager{
    54  		return layout;
    55  	}
    56  	
    57      /** 
    58       * Invalidates the container.  The container and all parents
    59       * above it are marked as needing to be laid out.  This method can
    60       * be called often, so it needs to execute quickly.
    61       * @see #validate()
    62       * @see #doLayout()
    63       * @see org.aswing.LayoutManager
    64       */
    65      public function invalidate():Void {
    66      	layout.invalidateLayout(this);
    67      	super.invalidate();
    68      }
    69  	
    70      /** 
    71       * Validates this container and all of its subcomponents.
    72       * <p>
    73       * The <code>validate</code> method is used to cause a container
    74       * to lay out its subcomponents again. It should be invoked when
    75       * this container's subcomponents are modified (added to or
    76       * removed from the container, or layout-related information
    77       * changed) after the container has been displayed.
    78       *
    79       * @see #append()
    80       * @see Component#invalidate()
    81       * @see org.aswing.Component#revalidate()
    82       */
    83      public function validate():Void {
    84      	if(!valid){
    85      		doLayout();
    86      		for(var i:Number=0; i<children.length; i++){
    87      			children[i].validate();
    88      		}
    89      		valid = true;
    90      	}
    91      }
    92      
    93  	/**
    94  	 * layout this container
    95  	 */
    96  	public function doLayout():Void{
    97  		super.doLayout();
    98  		if(displayable && isVisible())
    99  			layout.layoutContainer(this);
   100  	}
   101  	
   102  	/**
   103  	 * Create and add this component to a Container.
   104  	 * the method must only can call in a Container's method,
   105  	 * else the Container's layout maybe wrong and Container event will not be called
   106  	 */
   107  	public function addTo(parent:Container):Void{
   108  		super.addTo(parent);
   109  		if(displayable){
   110  			for(var i:Number=0; i<children.length; i++){
   111  				children[i].addTo(this);
   112  			}
   113  		}
   114  	}
   115  	
   116  	/**
   117  	 * Destroy a Container.
   118  	 * A Container was called destroy, it will call its children destroy first,
   119  	 * This can ensure that all its children as not displayable after it be removed.
   120  	 * @see org.aswing.Component#destroy()
   121  	 */
   122  	public function destroy():Void{
   123  		for(var i:Number=0; i<children.length; i++){
   124  			children[i].destroy();
   125  		}
   126  		super.destroy();
   127  	}
   128  		
   129  	public function createChildMC(nameStart:String):MovieClip{
   130  		return creater.createMC(target_mc, nameStart);
   131  	}
   132  	
   133  	
   134  	/**
   135  	 * On Component just can add to one Container.
   136  	 * So if the com has a parent, it will remove from its parent first, then add to 
   137  	 * this container. 
   138  	 * This method is shortcut of <code>insert(-1, com, constraints)</code>.
   139  	 * @param com the component to be added
   140  	 * @param constraints an object expressing layout contraints for this component
   141  	 * @see #insert()
   142  	 */
   143  	public function append(com:Component, constraints:Object):Void{
   144  	    insert(-1, com, constraints);
   145  	}
   146  	
   147  	/**
   148  	 * Add component to spesified index.
   149  	 * So if the com has a parent, it will remove from its parent first, then add to 
   150  	 * this container. 
   151  	 * @param i index the position at which to insert the component, or less than 0 value to append the component to the end 
   152  	 * @param com the component to be added
   153  	 * @param constraints an object expressing layout contraints for this component
   154  	 * @throws Error when index > children count
   155  	 * @throws Error when add container's parent(or itself) to itself
   156  	 * @see Component#removeFromContainer()
   157  	 * @see #append()
   158  	 */
   159  	public function insert(i:Number, com:Component, constraints:Object):Void{
   160  		if(i > getComponentCount()){
   161  			trace("illegal component position when insert comp to container");
   162  			throw new Error("illegal component position when insert comp to container");
   163  		}
   164  		if(com instanceof Container){
   165  			for(var cn:Container = this; cn != null; cn = cn.getParent()) {
   166                  if (cn == com) {
   167                  	trace("adding container's parent to itself");
   168                  	throw new Error("adding container's parent to itself");
   169                  }
   170              }
   171  		}
   172  		if(com.getParent() != null){
   173  			com.removeFromContainer();
   174  		}	
   175  		com.addTo(this);
   176  		if(i < 0){
   177  			children.push(com);
   178  		}else{
   179  			children.splice(i, 0, com);
   180  		}
   181  		layout.addLayoutComponent(com, constraints);
   182  		dispatchEvent(ON_COM_ADDED, createChildEvent(com, ON_COM_ADDED));	
   183  		
   184  		if (valid) {
   185  			invalidate();
   186  	    }			
   187  	}
   188  	
   189  	/**
   190  	 * Remove the specified child component.
   191  	 * @return the component just removed, null if the component is not in this container.
   192  	 */
   193  	public function remove(com:Component):Component{
   194  		var i:Number = index(com);
   195  		if(i >= 0){
   196  			return removeAt(i);
   197  		}
   198  		return null;
   199  	}
   200  	
   201  	/**
   202  	 * Remove the specified index child component.
   203  	 * @param i the index of component, less than 0 mean the component in the end of children list.
   204  	 * @return the component just removed. or null there is not component at this position.
   205  	 */	
   206  	public function removeAt(i:Number):Component{
   207  		if(i < 0){
   208  			i = children.length - 1;
   209  		}
   210  		if(i < 0){
   211  			return null;
   212  		}
   213  		var com:Component = children[i];
   214  		if(com != null){
   215  			layout.removeLayoutComponent(com);
   216  			children.splice(i, 1);
   217  			dispatchEvent(ON_COM_REMOVED, createChildEvent(com, ON_COM_ADDED));
   218  			com.destroy();
   219  			
   220  			if (valid) {
   221  				invalidate();
   222  		    }			
   223  		}
   224  		return com;
   225  	}
   226  	
   227  	private function createChildEvent(com:Component,type:String):Event{
   228  		var event:Event = createEventObj(type);
   229  		event.com = com;
   230  		return event;		
   231  	}	
   232  	
   233  	/**
   234  	 * Remove all child components.
   235  	 */
   236  	public function removeAll():Void{
   237  		while(children.length > 0){
   238  			removeAt(children.length - 1);
   239  		}
   240  	}
   241  	
   242  	public function contains(com:Component):Boolean{
   243  		return index(com) >= 0;
   244  	}
   245  	
   246  	public function getComponent(i:Number):Component{
   247  		return children[i];
   248  	}
   249  	
   250  	public function index(com:Component):Number{
   251  		for(var i:Number=0; i<children.length; i++){
   252  			if(com == children[i]){
   253  				return i;
   254  			}
   255  		}
   256  		return -1;
   257  	}
   258  	
   259  	public function getComponentCount():Number{
   260  		return children.length;
   261  	}
   262  		
   263  	/**
   264  	 * call the ui, if ui return null, ehn call layout to count.
   265  	 */
   266  	private function countMinimumSize():Dimension{
   267  		var size:Dimension = null;
   268  		if(ui != null){
   269  			size = ui.getMinimumSize(this);
   270  		}
   271  		if(size == null){
   272  			size = layout.minimumLayoutSize(this);
   273  		}
   274  		if(size == null){//this should never happen
   275  			size = super.countMinimumSize();
   276  		}
   277  		return size;
   278  	}
   279  	
   280  	/**
   281  	 * call the ui, if ui return null, ehn call layout to count.
   282  	 */
   283  	private function countMaximumSize():Dimension{
   284  		var size:Dimension = null;
   285  		if(ui != null){
   286  			size = ui.getMaximumSize(this);
   287  		}
   288  		if(size == null){
   289  			size = layout.maximumLayoutSize(this);
   290  		}
   291  		if(size == null){//this should never happen
   292  			size = super.countMaximumSize();
   293  		}
   294  		return size;
   295  	}
   296  	
   297  	/**
   298  	 * call the ui, if ui return null, ehn call layout to count.
   299  	 */
   300  	private function countPreferredSize():Dimension{
   301  		var size:Dimension = null;
   302  		if(ui != null){
   303  			size = ui.getPreferredSize(this);
   304  		}
   305  		if(size == null){
   306  			size = layout.preferredLayoutSize(this);
   307  		}
   308  		if(size == null){//this should never happen
   309  			size = super.countPreferredSize();
   310  		}
   311  		return size;
   312  	}
   313  
   314  
   315  
   316  	/**
   317  	 * When child component pressed.
   318  	 * @see org.aswing.JWindow#__onChildPressed()
   319  	 */
   320  	public function __onChildPressed(child:Component):Void{
   321  		parent.__onChildPressed(child);
   322  	}
   323  
   324  }
   325