1  /*
     2   Copyright aswing.org, see the LICENCE.txt.
     3  */
     4  import org.aswing.Component;
     5  import org.aswing.Container;
     6  import org.aswing.Icon;
     7  import org.aswing.LayoutManager;
     8  import org.aswing.plaf.AccordionUI;
     9  import org.aswing.UIManager;
    10  import org.aswing.utils.ArrayUtils;
    11  import org.aswing.utils.StringUtils;
    12  
    13  /**
    14   *
    15   * @author iiley
    16   */
    17  class org.aswing.JAccordion extends Container {
    18  	public static var ON_STATE_CHANGED:String = "onStateChanged";		
    19  		
    20      private var titles:Array;
    21      private var icons:Array;
    22      private var tips:Array;
    23      
    24      private var selectedIndex:Number;
    25      
    26      /**
    27       * JAccordion()
    28       * <p>
    29       */
    30  	public function JAccordion() {
    31  		super();
    32  		titles = new Array();
    33  		icons = new Array();
    34  		tips = new Array();
    35  		
    36  		selectedIndex = -1;
    37  		
    38  		updateUI();
    39  	}
    40  	
    41      public function updateUI():Void{
    42      	setUI(AccordionUI(UIManager.getUI(this)));
    43      	setLayout(getUI());
    44      }
    45      
    46      public function setUI(newUI:AccordionUI):Void{
    47      	super.setUI(newUI);
    48      }
    49      
    50      public function getUI():AccordionUI{
    51      	return AccordionUI(ui);
    52      }
    53  	
    54  	public function getUIClassID():String{
    55  		return "AccordionUI";
    56  	}
    57  	
    58  	/**
    59  	 * Generally you should not set layout to JAccordion.
    60  	 * @param layout layoutManager for JAccordion
    61  	 * @throws Error when you set a non-AccordionUI layout to JAccordion.
    62  	 */
    63  	public function setLayout(layout:LayoutManager):Void{
    64  		if(layout instanceof AccordionUI){
    65  			super.setLayout(layout);
    66  		}else{
    67  			trace("Cannot set non-AccordionUI layout to JAccordion!");
    68  			throw Error("Cannot set non-AccordionUI layout to JAccordion!");
    69  		}
    70  	}
    71  		
    72  	/**
    73  	 * Adds a component to the accordion. 
    74  	 * If constraints is a String or an Icon or an Object(object.toString() as a title), 
    75  	 * it will be used for the tab title, 
    76  	 * otherwise the component's name will be used as the tab title. 
    77  	 * Shortcut of <code>insert(-1, com, constraints)</code>. 
    78  	 * @param com  the component to be displayed when this tab is clicked
    79  	 * @param constraints  the object to be displayed in the tab
    80  	 * @see Container#append()
    81  	 * @see #insert()
    82  	 * @see #insertTab()
    83  	 */
    84  	public function append(com:Component, constraints:Object):Void{
    85  		insert(-1, com, constraints);
    86  	}
    87  	
    88  	/**
    89  	 * Adds a component to the accordion with spesified index.
    90  	 * If constraints is a String or an Icon or an Object(object.toString() as a title), 
    91  	 * it will be used for the tab title, 
    92  	 * otherwise the component's name will be used as the tab title. 
    93  	 * Cover method for insertTab. 
    94  	 * @param i index the position at which to insert the component, or less than 0 value to append the component to the end 
    95  	 * @param com the component to be added
    96  	 * @param constraints the object to be displayed in the tab
    97  	 * @see Container#insert()
    98  	 * @see #insertTab()
    99  	 */
   100  	public function insert(i:Number, com:Component, constraints:Object):Void{
   101  		var title:String = null;
   102  		var icon:Icon = null;
   103  		if(constraints == undefined){
   104  			title = com.getName();
   105  		}else if(StringUtils.isString(constraints)){
   106  			title = String(constraints);
   107  		}else if(constraints instanceof Icon){
   108  			icon = Icon(constraints);
   109  		}else{
   110  			title = constraints.toString();
   111  		}
   112  		insertTab(i, com, title, icon, null);
   113  	}
   114  	
   115  	/**
   116  	 * Adds a component and tip represented by a title and/or icon, either of which can be null.
   117  	 * Shortcut of <code>insertTab(-1, com, title, icon, tip)</code>
   118  	 * @param com The component to be displayed when this tab is clicked
   119  	 * @param title the title to be displayed in this tab
   120  	 * @param icon the icon to be displayed in this tab
   121  	 * @param tip the tooltip to be displayed for this tab, can be null means no tool tip.
   122  	 */
   123  	public function appendTab(com:Component, title:String, icon:Icon, tip:String):Void{
   124  		insertTab(-1, com, title, icon, tip);
   125  	}
   126  	
   127  	/**
   128  	 * Inserts a component, at index, represented by a title and/or icon, 
   129  	 * either of which may be null.
   130  	 * @param i the index position to insert this new tab
   131  	 * @param com The component to be displayed when this tab is clicked
   132  	 * @param title the title to be displayed in this tab
   133  	 * @param icon the icon to be displayed in this tab
   134  	 * @param tip the tooltip to be displayed for this tab, can be null means no tool tip.
   135  	 * @throws Error when index > children count
   136  	 */
   137  	public function insertTab(i:Number, com:Component, title:String, icon:Icon, tip:String):Void{
   138  		if(i > getComponentCount()){
   139  			trace("illegal component position when insert comp to container");
   140  			throw new Error("illegal component position when insert comp to container");
   141  		}
   142  		insertToArray(titles, i, title);
   143  		insertToArray(icons, i, icon);
   144  		insertToArray(tips, i, tip);
   145  		super.insert(i, com);
   146  		if(selectedIndex < 0){
   147  			setSelectedIndex(0);
   148  		}
   149  	}
   150  	
   151  	/**
   152  	 * Removes the specified child component.
   153  	 * After the component is removed, its visibility is reset to true to ensure it will be visible if added to other containers. 
   154  	 * @param i the index of component, less than 0 mean the component in the end of children list.
   155  	 * @return the component just removed, or null there is not component at this position.
   156  	 */
   157  	public function removeTabAt(i):Component{
   158  		removeFromArray(titles, i);
   159  		removeFromArray(icons, i);
   160  		removeFromArray(tips, i);
   161  		
   162  		var rc:Component = super.removeAt(i);
   163  		rc.setVisible(true);
   164  		
   165  		if(i < 0) i = getComponentCount();
   166  		if(getComponentCount() > 0){
   167  			setSelectedIndex(i);
   168  		}else{
   169  			selectedIndex = -1;
   170  		}
   171  		
   172  		return rc;
   173  	}
   174  	
   175  	/**
   176  	 * Removes the specified child component.
   177  	 * After the component is removed, its visibility is reset to true to ensure it will be visible if added to other containers. 
   178  	 * 
   179  	 * Cover method for removeTabAt. 
   180  	 * @see Container#remove()
   181  	 * @see #removeTabAt()
   182  	 */
   183  	public function remove(com:Component):Component{
   184  		var index:Number = index(com);
   185  		return removeAt(index);
   186  	}
   187  	
   188  	/**
   189  	 * Removes the specified index child component. 
   190  	 * After the component associated with index is removed, its visibility is reset to true to ensure it will be visible if added to other containers.
   191  	 * Cover method for removeTabAt. 
   192  	 * @see #removeTabAt() 
   193  	 * @see Container#removeAt()
   194  	 */	
   195  	public function removeAt(index:Number):Component{
   196  		return removeTabAt(index);
   197  	}
   198  	
   199  	/**
   200  	 * Remove all child components.
   201  	 * After the component is removed, its visibility is reset to true to ensure it will be visible if added to other containers. 
   202  	 * @see #removeAt()
   203  	 * @see #removeTabAt()
   204  	 * @see Container#removeAll()
   205  	 */
   206  	public function removeAll():Void{
   207  		while(children.length > 0){
   208  			removeAt(children.length - 1);
   209  		}
   210  	}
   211  	
   212  	/**
   213  	 * Returns the count of tabs.
   214  	 */
   215  	public function getTabCount():Number{
   216  		return getComponentCount();
   217  	}
   218  	
   219  	/**
   220  	 * Returns the tab title at specified index. 
   221  	 * @param i the index
   222  	 * @return the tab title
   223  	 */
   224  	public function getTitleAt(i:Number):String{
   225  		return titles[i];//StringUtils.castString(titles[i]);
   226  	}
   227  	
   228  	/**
   229  	 * Returns the tab icon at specified index. 
   230  	 * @param i the index
   231  	 * @return the tab icon
   232  	 */	
   233  	public function getIconAt(i:Number):Icon{
   234  		return Icon(icons[i]);
   235  	}
   236  	
   237  	/**
   238  	 * Returns the tab tool tip text at specified index. 
   239  	 * @param i the index
   240  	 * @return the tab tool tip text
   241  	 */	
   242  	public function getTipAt(i:Number):String{
   243  		return tips[i];//StringUtils.castString(tips[i]);
   244  	}
   245  	
   246  	/**
   247  	 * Returns the first tab index with a given title, or -1 if no tab has this title. 
   248  	 * @param title the title for the tab 
   249  	 * @return the first tab index which matches title, or -1 if no tab has this title
   250  	 */
   251  	public function indexOfTitle(title:String):Number{
   252  		return ArrayUtils.indexInArray(titles, title);
   253  	}
   254  	
   255  	/**
   256  	 * Returns the first tab index with a given icon, or -1 if no tab has this icon. 
   257  	 * @param title the title for the tab 
   258  	 * @return the first tab index which matches icon, or -1 if no tab has this icon
   259  	 */	
   260  	public function indexOfIcon(icon:Icon):Number{
   261  		return ArrayUtils.indexInArray(icons, icon);
   262  	}
   263  	
   264  	/**
   265  	 * Returns the first tab index with a given tip, or -1 if no tab has this tip. 
   266  	 * @param title the title for the tab 
   267  	 * @return the first tab index which matches tip, or -1 if no tab has this tip
   268  	 */		
   269  	public function indexOfTip(tip:String):Number{
   270  		return ArrayUtils.indexInArray(tips, tip);
   271  	}
   272  	
   273  	public function setSelectedIndex(i:Number):Void{
   274  		if(i>=0 && i<getComponentCount() && i != selectedIndex){
   275  			selectedIndex = i;
   276  		}
   277  		fireStateChanged();
   278  	}
   279  	
   280  	public function setSelectedComponent(com:Component):Void{
   281  		setSelectedIndex(index(com));
   282  	}
   283  	
   284  	public function getSelectedIndex():Number{
   285  		return selectedIndex;
   286  	}
   287  	
   288  	public function getSelectedComponent():Component{
   289  		if(selectedIndex >= 0){
   290  			return getComponent(selectedIndex);
   291  		}
   292  		return null;
   293  	}
   294      
   295      //----------------------------------------------------------------
   296      
   297  	private function insertToArray(arr:Array, i:Number, obj:Object):Void{
   298  		if(i < 0){
   299  			arr.push(obj);
   300  		}else{
   301  			arr.splice(i, 0, obj);
   302  		}
   303  	}	
   304  	
   305  	private function removeFromArray(arr:Array, i:Number):Void{
   306  		if(i < 0){
   307  			arr.pop();
   308  		}else{
   309  			arr.splice(i, 1);
   310  		}
   311  	}    
   312  }
   313