1  /*
     2   Copyright aswing.org, see the LICENCE.txt.
     3  */
     4  
     5  import org.aswing.ASColor;
     6  import org.aswing.ASFont;
     7  import org.aswing.ASTextExtent;
     8  import org.aswing.ASTextFormat;
     9  import org.aswing.ASWingConstants;
    10  import org.aswing.Component;
    11  import org.aswing.Container;
    12  import org.aswing.geom.Rectangle;
    13  import org.aswing.Icon;
    14  import org.aswing.JPanel;
    15  import org.aswing.JWindow;
    16  import org.aswing.LayoutManager;
    17  import org.aswing.MCPanel;
    18  
    19  class org.aswing.ASWingUtils{
    20  	/**
    21  	 * A fast access to ASWingConstants Constant
    22  	 * @see org.aswing.ASWingConstants
    23  	 */
    24  	public static var CENTER:Number  = ASWingConstants.CENTER;
    25  	/**
    26  	 * A fast access to ASWingConstants Constant
    27  	 * @see org.aswing.ASWingConstants
    28  	 */
    29  	public static var TOP:Number     = ASWingConstants.TOP;
    30  	/**
    31  	 * A fast access to ASWingConstants Constant
    32  	 * @see org.aswing.ASWingConstants
    33  	 */
    34      public static var LEFT:Number    = ASWingConstants.LEFT;
    35  	/**
    36  	 * A fast access to ASWingConstants Constant
    37  	 * @see org.aswing.ASWingConstants
    38  	 */
    39      public static var BOTTOM:Number  = ASWingConstants.BOTTOM;
    40   	/**
    41  	 * A fast access to ASWingConstants Constant
    42  	 * @see org.aswing.ASWingConstants
    43  	 */
    44      public static var RIGHT:Number   = ASWingConstants.RIGHT;
    45  	/**
    46  	 * A fast access to ASWingConstants Constant
    47  	 * @see org.aswing.ASWingConstants
    48  	 */        
    49  	public static var HORIZONTAL:Number = ASWingConstants.HORIZONTAL;
    50  	/**
    51  	 * A fast access to ASWingConstants Constant
    52  	 * @see org.aswing.ASWingConstants
    53  	 */
    54  	public static var VERTICAL:Number   = ASWingConstants.VERTICAL;
    55  	
    56  	
    57  
    58  
    59  	private static var ROOT:MovieClip;
    60  	
    61  	private static var initialStageWidth:Number = Stage.width;
    62  	private static var initialStageHeight:Number = Stage.height;
    63  	
    64  	
    65  	
    66  	/**
    67  	 * getVisibleMaximizedBounds(mc:MovieClip)<br>
    68  	 * getVisibleMaximizedBounds() default to return the _root's visible bounds
    69  	 * Returns the currently visible maximized bounds at the stage of a MovieClip.
    70  	 */
    71  	public static function getVisibleMaximizedBounds(mc:MovieClip):Rectangle{
    72  		var sw:Number = Stage.width;
    73  		var sh:Number = Stage.height;
    74  		var sa:String = Stage.align;
    75  		
    76  		var dw:Number = sw - initialStageWidth;
    77  		var dh:Number = sh - initialStageHeight;
    78  		
    79  		var b:Rectangle = new Rectangle(0, 0, sw, sh);
    80  		if(mc._parent != null){
    81  			mc._parent.globalToLocal(b);
    82  		}
    83  		switch(sa){
    84  			case "T":
    85  				b.x -= dw/2;
    86  				break;
    87  			case "B":
    88  				b.x -= dw/2;
    89  				b.y -= dh;
    90  				break;
    91  			case "L":
    92  				b.y -= dh/2;
    93  				break;
    94  			case "R":
    95  				b.x -= dw;
    96  				b.y -= dh/2;
    97  				break;
    98  			case "TL":
    99  				break;
   100  			case "TR":
   101  				b.x -= dw;
   102  				break;
   103  			case "BL":
   104  				b.y -= dh;
   105  				break;
   106  			case "BR":
   107  				b.x -= dw;
   108  				b.y -= dh;
   109  				break;
   110  			default:
   111  				b.x -= dw/2;
   112  				b.y -= dh/2;
   113  				break;
   114  		}
   115  		return b;
   116  	}
   117  	
   118  	
   119  	
   120  	/**
   121  	 * Sets the root movieclip for components base on. 
   122  	 * Default is _root.
   123  	 */
   124  	public static function setRootMovieClip(root:MovieClip):Void{
   125  		ROOT = root;
   126  	} 
   127  	
   128  	/**
   129  	 * Returns the root movieclip which components base on.
   130  	 * @see #setRootMovieClip()
   131  	 */	
   132  	public static function getRootMovieClip():MovieClip{
   133  		if(ROOT == undefined){
   134  			ROOT = _root;
   135  		}
   136  		return ROOT;
   137  	} 	
   138  	
   139  	/**
   140  	 * Creates and return a pane to hold the component with specified layout manager and constraints.
   141  	 */
   142  	public static function createPaneToHold(com:Component, layout:LayoutManager, constraints:Object):Container{
   143  		var p:JPanel = new JPanel(layout);
   144  		p.setOpaque(false);
   145  		p.append(com, constraints);
   146  		return p;
   147  	}
   148  	
   149  	/**
   150  	 * @return the first Window ancestor of c, or null if component is not contained inside a window
   151  	 */
   152  	public static function getWindowAncestor(c:Component):JWindow{
   153  		while(c != null){
   154  			if(c instanceof JWindow){
   155  				return JWindow(c);
   156  			}
   157  			c = c.getParent();
   158  		}
   159  		return null;
   160  	}
   161  	
   162  
   163  	
   164  	/**
   165  	 * When call <code>setLookAndFeel</code> it will not change the UIs at created components.
   166  	 * Call this method to update all existing component's UIs.
   167  	 * @see #updateComponentTreeUI()
   168  	 * @see org.aswing.Component#updateUI()
   169  	 */
   170  	public static function updateAllComponentUI():Void{
   171  		var mcps:Array = MCPanel.getMCPanels();
   172  		var n:Number = mcps.length;
   173  		for(var i:Number=0; i<n; i++){
   174  			updateComponentTreeUI(MCPanel(mcps[i]));
   175  		}
   176  	}
   177  	
   178  	/**
   179  	 * A simple minded look and feel change: ask each node in the tree to updateUI() -- that is, 
   180  	 * to initialize its UI property with the current look and feel. 
   181  	 * @see org.aswing.Component#updateUI()
   182  	 */
   183  	public static function updateComponentTreeUI(com:Component):Void{
   184  		var rootc:Component = getRoot(com);
   185  		updateChildrenUI(rootc);
   186  	}
   187  	
   188  	private static function updateChildrenUI(c:Component):Void{
   189  		c.updateUI();
   190  		//trace("UI updated : " + c);
   191  		if(c instanceof Container){
   192  			var con:Container = Container(c);
   193  			for(var i:Number = con.getComponentCount()-1; i>=0; i--){
   194  				updateChildrenUI(con.getComponent(i));
   195  			}
   196  		}
   197  	}
   198  	
   199  	/**
   200  	 * Returns the root component for the current component tree.
   201  	 * @return the first ancestor of c that's ordinaryly a MCPanel or a JWindow(or JFrame).
   202  	 */
   203  	public static function getRoot(c:Component):Component{
   204  		var maxLoop:Number = 10000; //max search depth 1000
   205  		while(c.getParent() != null && maxLoop>0){
   206  			c = c.getParent();
   207  			maxLoop--;
   208  		}
   209  		return c;
   210  	}
   211  	
   212  	//Don't call this method currently, it has some problem cause text shake
   213  	public static function boundsTextField(tf:TextField, r:Rectangle):Void{
   214  		tf._x = r.x;
   215  		tf._y = r.y;
   216  		tf._width = r.width;
   217  		tf._height = r.height;
   218  	}
   219  	
   220  	/**
   221  	 * Apply the font and color to the textfield.
   222  	 * @param text
   223  	 * @param font
   224  	 * @param color
   225  	 */
   226  	public static function applyTextFontAndColor(text:TextField, font:ASFont, color:ASColor):Void{
   227  		var tf:ASTextFormat = font.getASTextFormat();
   228  		applyTextFormatAndColor(text, tf, color);
   229  	}
   230  	
   231  	/**
   232  	 * Apply the textformat and color to the textfield.
   233  	 * @param text
   234  	 * @param textFormat
   235  	 * @param color
   236  	 */
   237  	public static function applyTextFormatAndColor(text:TextField, textFormat:ASTextFormat, color:ASColor):Void{
   238  		textFormat.applyToText(text);
   239  		textFormat.applyToTextForNew(text);
   240  		text.textColor = color.getRGB();
   241  		text._alpha = color.getAlpha();
   242  	}
   243  	
   244      /**
   245       * Compute and return the location of the icons origin, the
   246       * location of origin of the text baseline, and a possibly clipped
   247       * version of the compound labels string.  Locations are computed
   248       * relative to the viewR rectangle.
   249       */
   250      public static function layoutCompoundLabel(
   251  		f:ASFont,
   252          text:String,
   253          icon:Icon,
   254          verticalAlignment:Number,
   255          horizontalAlignment:Number,
   256          verticalTextPosition:Number,
   257          horizontalTextPosition:Number,
   258          viewR:Rectangle,
   259          iconR:Rectangle,
   260          textR:Rectangle,
   261          textIconGap:Number):String
   262      {
   263      	
   264      	if (icon != null) {
   265              iconR.width = icon.getIconWidth();
   266              iconR.height = icon.getIconHeight();
   267          }else {
   268              iconR.width = iconR.height = 0;
   269          }
   270          
   271          var tf:ASTextFormat = f.getASTextFormat();
   272          
   273          var textIsEmpty:Boolean = (text==null || text=="");
   274          if(textIsEmpty){
   275          	textR.width = textR.height = 0;
   276          }else{
   277          	var ts:ASTextExtent = tf.getTextExtent(text);
   278          	textR.width = ts.getTextFieldWidth();
   279          	textR.height = ts.getTextFieldHeight();
   280          }
   281          
   282           /* Unless both text and icon are non-null, we effectively ignore
   283           * the value of textIconGap.  The code that follows uses the
   284           * value of gap instead of textIconGap.
   285           */
   286  
   287          var gap:Number = (textIsEmpty || (icon == null)) ? 0 : textIconGap;
   288          
   289          if(!textIsEmpty){
   290          	
   291              /* If the label text string is too wide to fit within the available
   292               * space "..." and as many characters as will fit will be
   293               * displayed instead.
   294               */
   295  
   296              var availTextWidth:Number;
   297  
   298              if (horizontalTextPosition == CENTER) {
   299                  availTextWidth = viewR.width;
   300              }else {
   301                  availTextWidth = viewR.width - (iconR.width + gap);
   302              }
   303              
   304              if (textR.width > availTextWidth) {
   305              	text = layoutTextWidth(text, textR, availTextWidth, tf);
   306          	}
   307          }
   308  
   309          /* Compute textR.x,y given the verticalTextPosition and
   310           * horizontalTextPosition properties
   311           */
   312  
   313          if (verticalTextPosition == TOP) {
   314              if (horizontalTextPosition != CENTER) {
   315                  textR.y = 0;
   316              }else {
   317                  textR.y = -(textR.height + gap);
   318              }
   319          }else if (verticalTextPosition == CENTER) {
   320              textR.y = (iconR.height / 2) - (textR.height / 2);
   321          }else { // (verticalTextPosition == BOTTOM)
   322              if (horizontalTextPosition != CENTER) {
   323                  textR.y = iconR.height - textR.height;
   324              }else {
   325                  textR.y = (iconR.height + gap);
   326              }
   327          }
   328  
   329          if (horizontalTextPosition == LEFT) {
   330              textR.x = -(textR.width + gap);
   331          }else if (horizontalTextPosition == CENTER) {
   332              textR.x = (iconR.width / 2) - (textR.width / 2);
   333          }else { // (horizontalTextPosition == RIGHT)
   334              textR.x = (iconR.width + gap);
   335          }
   336          
   337  
   338  		//trace("textR : " + textR);
   339  		//trace("iconR : " + iconR);	
   340  		//trace("viewR : " + viewR);	
   341  
   342          /* labelR is the rectangle that contains iconR and textR.
   343           * Move it to its proper position given the labelAlignment
   344           * properties.
   345           *
   346           * To avoid actually allocating a Rectangle, Rectangle.union
   347           * has been inlined below.
   348           */
   349          var labelR_x:Number = Math.min(iconR.x, textR.x);
   350          var labelR_width:Number = Math.max(iconR.x + iconR.width, textR.x + textR.width) - labelR_x;
   351          var labelR_y:Number = Math.min(iconR.y, textR.y);
   352          var labelR_height:Number = Math.max(iconR.y + iconR.height, textR.y + textR.height) - labelR_y;
   353          
   354  		//trace("labelR_x : " + labelR_x);
   355  		//trace("labelR_width : " + labelR_width);	
   356  		//trace("labelR_y : " + labelR_y);
   357  		//trace("labelR_height : " + labelR_height);		
   358  		
   359          var dx:Number = 0;
   360          var dy:Number = 0;
   361  
   362          if (verticalAlignment == TOP) {
   363              dy = viewR.y - labelR_y;
   364          }
   365          else if (verticalAlignment == CENTER) {
   366              dy = (viewR.y + (viewR.height/2)) - (labelR_y + (labelR_height/2));
   367          }
   368          else { // (verticalAlignment == BOTTOM)
   369              dy = (viewR.y + viewR.height) - (labelR_y + labelR_height);
   370          }
   371  
   372          if (horizontalAlignment == LEFT) {
   373              dx = viewR.x - labelR_x;
   374          }
   375          else if (horizontalAlignment == RIGHT) {
   376              dx = (viewR.x + viewR.width) - (labelR_x + labelR_width);
   377          }
   378          else { // (horizontalAlignment == CENTER)
   379              dx = (viewR.x + (viewR.width/2)) - (labelR_x + (labelR_width/2));
   380          }
   381  
   382          /* Translate textR and glypyR by dx,dy.
   383           */
   384  
   385  		//trace("dx : " + dx);
   386  		//trace("dy : " + dy);	
   387  		
   388          textR.x += dx;
   389          textR.y += dy;
   390  
   391          iconR.x += dx;
   392          iconR.y += dy;
   393  		
   394  		//trace("tf = " + tf);
   395  
   396  		//trace("textR : " + textR);
   397  		//trace("iconR : " + iconR);		
   398  		
   399          return text;
   400      }
   401      
   402     	private static function charWidth(atf:ASTextFormat, ch:String):Number{
   403     		return atf.getTextExtent(ch).getWidth();
   404     	}
   405      
   406      public static function computeStringWidth(atf:ASTextFormat, str:String):Number{
   407      	return atf.getTextExtent(str).getTextFieldWidth();
   408      }
   409      
   410      /**
   411       * before call this method textR.width must be filled with correct value of whole text.
   412       */
   413      public static function layoutTextWidth(text:String, textR:Rectangle, availTextWidth:Number, tf:ASTextFormat):String{
   414      	if (textR.width <= availTextWidth) {
   415      		return text;
   416          }
   417  		var clipString:String = "...";
   418  		var totalWidth:Number = computeStringWidth(tf, clipString);
   419      	if(totalWidth > availTextWidth){
   420      		totalWidth = computeStringWidth(tf, "..");
   421      		if(totalWidth > availTextWidth){
   422          		text = ".";
   423          		textR.width = computeStringWidth(tf, ".");
   424          		if(textR.width > availTextWidth){
   425          			textR.width = 0;
   426          			text = "";
   427          		}
   428      		}else{
   429      			text = "..";
   430      			textR.width = totalWidth;
   431      		}
   432      		return text;
   433      	}else{
   434      		var nChars:Number;
   435      		var lastWidth:Number = totalWidth;
   436      		
   437      		
   438      		//begin binary search
   439      		var num:Number = text.length;
   440      		var li:Number = 0; //binary search of left index 
   441      		var ri:Number = num; //binary search of right index
   442      		
   443      		while(li<ri){
   444      			var i:Number = li + Math.floor((ri - li)/2);
   445      			var subText:String = text.substring(0, i);
   446      			var length:Number = lastWidth + computeStringWidth(tf, subText);
   447      			
   448      			if((li == i - 1) && li>0){
   449      				if(length > availTextWidth){
   450      					subText = text.substring(0, li);
   451      					textR.width = lastWidth + computeStringWidth(tf, text.substring(0, li));
   452      				}else{
   453      					textR.width = length;
   454      				}
   455      				return subText + clipString;
   456      			}else if(i <= 1){
   457      				if(length <= availTextWidth){
   458      					textR.width = length;
   459      					return subText + clipString;
   460      				}else{
   461      					textR.width = lastWidth;
   462      					return clipString;
   463      				}
   464      			}
   465      			
   466      			if(length < availTextWidth){
   467      				li = i;
   468      			}else if(length > availTextWidth){
   469      				ri = i;
   470      			}else{
   471      				text = subText + clipString;
   472      				textR.width = length;
   473      				return text;
   474      			}
   475      		}
   476      		//end binary search
   477      		textR.width = lastWidth;
   478      		return "";
   479      		
   480      		/*
   481      		//the old arithmetic
   482      		var mCharWidth:Number = charWidth(tf, "i");
   483     			for(nChars = 0; nChars < text.length; nChars++) {
   484     				lastWidth = totalWidth;
   485  				totalWidth += charWidth(tf, text.charAt(nChars));
   486  				if (totalWidth > availTextWidth) {
   487  	   	 			break;
   488  				}
   489      		}
   490      		if(nChars > 0){
   491      			text = text.substring(0, nChars) + clipString;
   492      		}else{
   493      			text = clipString;
   494      		}
   495      		textR.width = lastWidth;
   496      		return text;
   497      		*/
   498      	}
   499      }
   500      
   501      /**
   502       * Compute and return the location of origin of the text baseline, and a possibly clipped
   503       * version of the text string.  Locations are computed
   504       * relative to the viewR rectangle.
   505       */
   506      public static function layoutText(
   507  		f:ASFont,
   508          text:String,
   509          verticalAlignment:Number,
   510          horizontalAlignment:Number,
   511          viewR:Rectangle,
   512          textR:Rectangle):String
   513      {        
   514      	var tf:ASTextFormat = f.getASTextFormat();
   515          
   516          var textIsEmpty:Boolean = (text==null || text=="");
   517          if(textIsEmpty){
   518          	textR.width = textR.height = 0;
   519          }else{
   520          	var ts:ASTextExtent = tf.getTextExtent(text);
   521          	textR.width = ts.getTextFieldWidth();
   522          	textR.height = ts.getTextFieldHeight();
   523          }        
   524          
   525          if(!textIsEmpty){
   526          	
   527              /* If the label text string is too wide to fit within the available
   528               * space "..." and as many characters as will fit will be
   529               * displayed instead.
   530               */
   531  
   532              var availTextWidth:Number = viewR.width;
   533              if (textR.width > availTextWidth) {
   534              	text = layoutTextWidth(text, textR, availTextWidth, tf);
   535          	}
   536          }
   537          if(horizontalAlignment == CENTER){
   538          	textR.x = viewR.x + (viewR.width - textR.width)/2;
   539          }else if(horizontalAlignment == RIGHT){
   540          	textR.x = viewR.x + (viewR.width - textR.width);
   541          }else{
   542          	textR.x = viewR.x;
   543          }
   544          if(verticalAlignment == CENTER){
   545          	textR.y = viewR.y + (viewR.height - textR.height)/2;
   546          }else if(verticalAlignment == BOTTOM){
   547          	textR.y = viewR.y + (viewR.height - textR.height);
   548          }else{
   549          	textR.y = viewR.y;
   550          }
   551      	return text;
   552      }
   553  }
   554