1  import org.aswing.ASColor;
     2  import org.aswing.ASWingUtils;
     3  import org.aswing.border.LineBorder;
     4  import org.aswing.Component;
     5  import org.aswing.Container;
     6  import org.aswing.ElementCreater;
     7  import org.aswing.geom.Dimension;
     8  import org.aswing.geom.Point;
     9  import org.aswing.geom.Rectangle;
    10  import org.aswing.graphices.Graphics;
    11  import org.aswing.graphices.SolidBrush;
    12  import org.aswing.Insets;
    13  import org.aswing.JButton;
    14  import org.aswing.JComboBox;
    15  import org.aswing.JList;
    16  import org.aswing.JScrollPane;
    17  import org.aswing.LayoutManager;
    18  import org.aswing.LookAndFeel;
    19  import org.aswing.MCPanel;
    20  import org.aswing.plaf.basic.icon.ArrowIcon;
    21  import org.aswing.plaf.ComboBoxUI;
    22  import org.aswing.UIManager;
    23  import org.aswing.utils.Delegate;
    24  import org.aswing.utils.Timer;
    25  
    26  /**
    27   * @author iiley
    28   */
    29  class org.aswing.plaf.basic.BasicComboBoxUI extends ComboBoxUI implements LayoutManager{
    30  		
    31  	private var dropDownButton:Component;
    32  	private var box:JComboBox;
    33  	private var mcPane:MCPanel;
    34  	private var maskMC:MovieClip;
    35  	private var scollPane:JScrollPane;
    36  	private var mouseListener:Object;
    37  	private var listListener:Object;
    38  	private var boxListener:Object;
    39  	
    40  	private var popupTimer:Timer;
    41  	private var popupDestinationY:Number;
    42  	private var moveDir:Number;
    43  	
    44  	private var shadowColor:ASColor;
    45  	
    46  	public function BasicComboBoxUI() {
    47  		super();
    48  	}
    49  	
    50      public function installUI(c:Component):Void{
    51      	box = JComboBox(c);
    52  		installDefaults();
    53  		installComponents();
    54  		installListeners();
    55      }
    56      
    57  	public function uninstallUI(c:Component):Void{
    58      	box = JComboBox(c);
    59  		uninstallDefaults();
    60  		uninstallComponents();
    61  		uninstallListeners();
    62      }
    63  	
    64  	private function installDefaults():Void{
    65  		var pp:String = "ComboBox.";
    66          LookAndFeel.installBorder(box, pp + "border");
    67          LookAndFeel.installColorsAndFont(box, pp + "background", pp + "foreground", pp + "font");
    68          LookAndFeel.installBasicProperties(box, pp);
    69          box.setLayout(this);
    70          shadowColor = UIManager.getColor("ComboBox.shadow");
    71  	}
    72      
    73      private function uninstallDefaults():Void{
    74      	LookAndFeel.uninstallBorder(box);
    75      }
    76      
    77  	private function installComponents():Void{
    78  		dropDownButton = createDropDownButton();
    79  		box.append(dropDownButton);
    80      }
    81  	private function uninstallComponents():Void{
    82  		box.remove(dropDownButton);
    83      }
    84  	
    85  	private var dropDownListener:Object;
    86  	private function installListeners():Void{
    87  		dropDownListener = dropDownButton.addEventListener(Component.ON_PRESS, __onDropDownPressed, this);
    88  		
    89  		mouseListener = {onMouseDown:Delegate.create(this, __onMouseDown)};
    90  		
    91  		listListener = new Object();
    92  		listListener[JList.ON_ITEM_RELEASE] = listListener[JList.ON_ITEM_RELEASEOUTSIDE] = Delegate.create(this, __listItemReleased);
    93  		getPopupList().addEventListener(listListener);
    94  		
    95  		boxListener = box.addEventListener(Component.ON_PRESS, __onDropDownPressed, this);
    96  		
    97  		popupTimer = new Timer(40);
    98  		popupTimer.addActionListener(__movePopup, this);
    99  	}
   100      
   101      private function uninstallListeners():Void{
   102      	dropDownButton.removeEventListener(dropDownListener);
   103      	getPopupList().removeEventListener(listListener);
   104      	box.removeEventListener(boxListener);
   105      	popupTimer.stop();
   106      	popupTimer = null;
   107      }
   108          
   109      private function paintBackGround(c:Component, g:Graphics, b:Rectangle):Void{
   110      	if(c.isOpaque()){
   111  	 		var bgColor:ASColor;
   112  	 		if(box.isEditable()){
   113  	 			bgColor = (c.getBackground() == null ? ASColor.WHITE : c.getBackground());
   114  	 		}else{
   115  	 			bgColor = shadowColor;
   116  	 		}
   117  			g.fillRectangle(new SolidBrush(bgColor), b.x, b.y, b.width, b.height);
   118      	}
   119      }
   120      
   121      /**
   122       * Just override this method if you want other LAF drop down buttons.
   123       */
   124      private function createDropDownButton():Component{
   125      	var btn:JButton = new JButton(new ArrowIcon(
   126      				Math.PI/2, 5,
   127  				    new ASColor(0x000000,100),
   128  				   new ASColor(0x666666,100),
   129  				   new ASColor(0x666666,100),
   130  				    new ASColor(0xFF0000,100)
   131      	));
   132      	btn.setPreferredSize(16, 16);
   133      	return btn;
   134      }
   135      
   136      private function getMCPane():MCPanel{
   137      	if(mcPane == null){
   138      		mcPane = new MCPanel(ASWingUtils.getRootMovieClip(), 10000, 10000);
   139      	}
   140      	return mcPane;
   141      }
   142      private function getScollPane():JScrollPane{
   143      	if(scollPane == null){
   144      		scollPane = new JScrollPane(getPopupList());
   145      		scollPane.setBorder(new LineBorder());
   146      		scollPane.setOpaque(true);
   147      	}
   148      	return scollPane;
   149      }
   150      private function getPopupList():JList{
   151      	return box.getPopupList();
   152      }
   153      private function viewPopup():Void{
   154      	var popupPane:JScrollPane = getScollPane();
   155      	var mcPanel:MCPanel = getMCPane();
   156      	if(!mcPanel.contains(popupPane)){
   157      		mcPanel.append(popupPane);
   158  			var paneMC:MovieClip = mcPanel.getPanelMC();
   159  			var width:Number = box.getWidth();
   160  			var cellHeight:Number;
   161  			if(box.getListCellFactory().isAllCellHasSameHeight()){
   162  				cellHeight = box.getListCellFactory().getCellHeight();
   163  			}else{
   164  				cellHeight = box.getPreferredSize().height;
   165  			}
   166  			var height:Number = Math.min(box.getItemCount(), box.getMaximumRowCount())*cellHeight;
   167  			var i:Insets = getScollPane().getInsets();
   168  			height += i.top + i.bottom;
   169  			i = getPopupList().getInsets();
   170  			height += i.top + i.bottom;
   171  			popupPane.setSize(width, height);
   172  			
   173  			var p:Point = startMoveToView(height);
   174  						
   175  			Mouse.addListener(mouseListener);
   176  			dropDownButton.setTriggerEnabled(false);
   177  			box.setTriggerEnabled(false);
   178  			
   179  			var popupPaneMC:MovieClip = MovieClip(paneMC.getInstanceAtDepth(paneMC.getNextHighestDepth()-1));
   180  			maskMC = ElementCreater.getInstance().createMC(paneMC, "comb_pop_mask");
   181  			if(popupPaneMC != undefined){
   182  				var g:Graphics = new Graphics(maskMC);
   183  				g.fillRectangle(new SolidBrush(0), p.x, p.y, width, height);
   184  				popupPaneMC.setMask(maskMC);
   185  			}else{
   186  				trace("Mask popup list failed!");
   187  				maskMC.removeMovieClip();
   188  				maskMC = null;
   189  			}
   190      	}
   191      }
   192      private function hidePopup():Void{
   193      	getMCPane().remove(getScollPane());
   194      	maskMC.unloadMovie();
   195      	maskMC.removeMovieClip();
   196      	maskMC = null;
   197  		Mouse.removeListener(mouseListener);
   198  		dropDownButton.setTriggerEnabled(true);
   199  		box.setTriggerEnabled(true);
   200  		popupTimer.stop();
   201      }
   202      //return the destination pos
   203      private function startMoveToView(height:Number):Point{
   204      	var popupPane:JScrollPane = getScollPane();
   205      	var popupPaneHeight:Number = height;
   206      	var downDest:Point = box.componentToGlobal(new Point(0, box.getHeight()));
   207      	var upDest:Point = new Point(downDest.x, downDest.y - box.getHeight() - popupPaneHeight);
   208      	var visibleBounds:Rectangle = ASWingUtils.getVisibleMaximizedBounds();
   209      	
   210      	var distToBottom:Number = visibleBounds.y + visibleBounds.height - downDest.y - popupPaneHeight;
   211      	var distToTop:Number = upDest.y - visibleBounds.y;
   212      	
   213      	var dest:Point;
   214      	if(distToBottom > 0 || (distToBottom < 0 && distToTop < 0 && distToBottom > distToTop)){
   215      		moveDir = 1;
   216  			popupDestinationY = downDest.y;
   217  			popupPane.setGlobalLocation(downDest.x, popupDestinationY - popupPaneHeight);
   218  			dest = downDest;
   219      	}else{
   220      		moveDir = -1;
   221  			popupDestinationY = upDest.y;
   222  			popupPane.setGlobalLocation(upDest.x, popupDestinationY + popupPaneHeight);
   223  			dest = upDest;
   224      	}
   225      	
   226  		popupTimer.restart();
   227  		
   228  		return new Point(dest);
   229      }
   230      //-----------------------------
   231      private function __movePopup():Void{
   232      	var popupPane:JScrollPane = getScollPane();
   233      	var popupPaneHeight:Number = popupPane.getHeight();
   234      	var maxTime:Number = 10;
   235      	var minTime:Number = 3;
   236      	var speed:Number = 50;
   237      	if(popupPaneHeight < speed*minTime){
   238      		speed = Math.ceil(popupPaneHeight/minTime);
   239      	}else if(popupPaneHeight > speed*maxTime){
   240      		speed = Math.ceil(popupPaneHeight/maxTime);
   241      	}
   242  		var p:Point = popupPane.getGlobalLocation();
   243  		if(Math.abs(popupDestinationY - p.y) < speed){
   244  			p.y = popupDestinationY;
   245  			popupPane.setLocation(p);
   246  			popupTimer.stop();
   247  		}else if(moveDir > 0){
   248  			p.y += speed;
   249  			popupPane.setLocation(p);
   250  		}else{
   251  			p.y -= speed;
   252  			popupPane.setLocation(p);
   253  		}
   254  		popupPane.revalidate();
   255  		updateAfterEvent();
   256      }
   257      private function __listItemReleased():Void{
   258      	hidePopup();
   259  		var selectedValue:Object = getPopupList().getSelectedValue();
   260  		box.setSelectedItem(selectedValue);
   261      }
   262      private function __onDropDownPressed():Void{
   263      	if(!isPopupVisible(box)){
   264      		setPopupVisible(box, true);
   265      	}
   266      }
   267      private function __onMouseDown():Void{
   268      	if(!getScollPane().hitTest(_root._xmouse, _root._ymouse)){
   269      		hidePopup();
   270      	}
   271      }
   272      
   273  	/**
   274       * Set the visiblity of the popup
   275       */
   276  	public function setPopupVisible(c:JComboBox, v:Boolean):Void{
   277  		if(v){
   278  			viewPopup();
   279  		}else{
   280  			hidePopup();
   281  		}
   282  	}
   283  	
   284  	/** 
   285       * Determine the visibility of the popup
   286       */
   287  	public function isPopupVisible(c:JComboBox):Boolean{
   288  		return getScollPane().isDisplayable() && getScollPane().isVisible();
   289  	}
   290  	
   291  	/** 
   292       * Determine whether or not the combo box itself is traversable 
   293       */
   294  	public function isFocusTraversable(c:JComboBox):Boolean{
   295  		return false;
   296  	}
   297  	
   298  	//---------------------Layout Implementation---------------------------
   299  
   300      /**
   301       * may need override in subclass
   302       */
   303      public function addLayoutComponent(comp:Component, constraints:Object):Void{
   304      }
   305  
   306      /**
   307       * may need override in subclass
   308       */
   309      public function removeLayoutComponent(comp:Component):Void{
   310      }
   311  	
   312      public function preferredLayoutSize(target:Container):Dimension{
   313      	var insets:Insets = box.getInsets();
   314      	var listPreferSize:Dimension = getPopupList().getPreferredSize();
   315      	var ew:Number = listPreferSize.width;
   316      	var wh:Number = box.getEditor().getEditorComponent().getPreferredSize().height;
   317      	var buttonSize:Dimension = dropDownButton.getPreferredSize(); 
   318      	buttonSize.width += ew;
   319      	if(wh > buttonSize.height){
   320      		buttonSize.height = wh;
   321      	}
   322      	return insets.roundsSize(buttonSize);
   323      }
   324  
   325      public function minimumLayoutSize(target:Container):Dimension{
   326      	return box.getInsets().roundsSize(dropDownButton.getPreferredSize());
   327      }
   328  	
   329  	/**
   330       * may need override in subclass
   331  	 */
   332      public function maximumLayoutSize(target:Container):Dimension{
   333      	return new Dimension(Number.MAX_VALUE, Number.MAX_VALUE);
   334      }
   335      
   336      /**
   337       * may need override in subclass
   338       */
   339      public function layoutContainer(target:Container):Void{
   340      	var td:Dimension = target.getSize();
   341  		var insets:Insets = target.getInsets();
   342  		var top:Number = insets.top;
   343  		var bottom:Number = td.height - insets.bottom;
   344  		var left:Number = insets.left;
   345  		var right:Number = td.width - insets.right;
   346  		
   347  		var height:Number = td.height - insets.top - insets.bottom;
   348      	var buttonSize:Dimension = dropDownButton.getPreferredSize(); 
   349      	dropDownButton.setSize(buttonSize.width, height);
   350      	dropDownButton.setLocation(right - buttonSize.width, top);
   351      	box.getEditor().getEditorComponent().setLocation(left, top);
   352      	box.getEditor().getEditorComponent().setSize(td.width-insets.left-insets.right- buttonSize.width, height);
   353      }
   354      
   355  	/**
   356       * may need override in subclass
   357  	 */
   358      public function getLayoutAlignmentX(target:Container):Number{
   359      	return 0.5;
   360      }
   361  
   362  	/**
   363       * may need override in subclass
   364  	 */
   365      public function getLayoutAlignmentY(target:Container):Number{
   366      	return 0.5;
   367      }
   368  
   369      /**
   370       * may need override in subclass
   371       */
   372      public function invalidateLayout(target:Container):Void{
   373      }	
   374  }