1  /*
     2   Copyright aswing.org, see the LICENCE.txt.
     3  */
     4  
     5  import org.aswing.*;
     6  import org.aswing.geom.*;
     7  
     8  /**
     9   * A flow layout arranges components in a left-to-right flow, much
    10   * like lines of text in a paragraph. Flow layouts are typically used
    11   * to arrange buttons in a panel. It will arrange
    12   * buttons left to right until no more buttons fit on the same line.
    13   * Each line is centered.
    14   * <p>
    15   * For example, the following picture shows an applet using the flow
    16   * layout manager (its default layout manager) to position three buttons:
    17   * <p>
    18   * A flow layout lets each component assume its natural (preferred) size.
    19   *
    20   * @author 	iiley
    21   */
    22  class org.aswing.FlowLayout extends EmptyLayout{
    23  
    24      /**
    25       * This value indicates that each row of components
    26       * should be left-justified.
    27       */
    28      public static var LEFT:Number 	= 0;
    29  
    30      /**
    31       * This value indicates that each row of components
    32       * should be centered.
    33       */
    34      public static var CENTER:Number 	= 1;
    35  
    36      /**
    37       * This value indicates that each row of components
    38       * should be right-justified.
    39       */
    40      public static var RIGHT:Number 	= 2;
    41  
    42      /**
    43       * <code>align</code> is the property that determines
    44       * how each row distributes empty space.
    45       * It can be one of the following values:
    46       * <ul>
    47       * <code>LEFT</code>
    48       * <code>RIGHT</code>
    49       * <code>CENTER</code>
    50       * </ul>
    51       *
    52       * @see #getAlignment
    53       * @see #setAlignment
    54       */
    55      private var align:Number;
    56  
    57      /**
    58       * The flow layout manager allows a seperation of
    59       * components with gaps.  The horizontal gap will
    60       * specify the space between components.
    61       *
    62       * @see #getHgap()
    63       * @see #setHgap(int)
    64       */
    65      private var hgap:Number;
    66  
    67      /**
    68       * The flow layout manager allows a seperation of
    69       * components with gaps.  The vertical gap will
    70       * specify the space between rows.
    71       *
    72       * @see #getHgap()
    73       * @see #setHgap(int)
    74       */
    75      private var vgap:Number;
    76  
    77      /**
    78       * Creates a new flow layout manager with the indicated alignment
    79       * and the indicated horizontal and vertical gaps.
    80       * <p>
    81       * The value of the alignment argument must be one of
    82       * <code>FlowLayout.LEFT</code>, <code>FlowLayout.RIGHT</code>,
    83       * or <code>FlowLayout.CENTER</code>.
    84       * @param      align   the alignment value, default is LEFT
    85       * @param      hgap    the horizontal gap between components, default 5
    86       * @param      vgap    the vertical gap between components, default 5
    87       */
    88      public function FlowLayout(align:Number, hgap:Number, vgap:Number) {
    89      	if(align == undefined) align = LEFT;
    90      	if(hgap == undefined) hgap = 5;
    91      	if(vgap == undefined) vgap = 5;
    92  		this.hgap = hgap;
    93  		this.vgap = vgap;
    94          setAlignment(align);
    95      }
    96  
    97      /**
    98       * Gets the alignment for this layout.
    99       * Possible values are <code>FlowLayout.LEFT</code>,
   100       * <code>FlowLayout.RIGHT</code>, <code>FlowLayout.CENTER</code>,
   101       * @return     the alignment value for this layout
   102       * @see        #setAlignment
   103       */
   104      public function getAlignment():Number {
   105  		return align;
   106      }
   107  
   108      /**
   109       * Sets the alignment for this layout.
   110       * Possible values are
   111       * <ul>
   112       * <li><code>FlowLayout.LEFT</code>
   113       * <li><code>FlowLayout.RIGHT</code>
   114       * <li><code>FlowLayout.CENTER</code>
   115       * </ul>
   116       * @param      align one of the alignment values shown above
   117       * @see        #getAlignment()
   118       */
   119      public function setAlignment(align:Number):Void {
   120      	//Flashout.log("set align : " + align)
   121          this.align = align;
   122      }
   123  
   124      /**
   125       * Gets the horizontal gap between components.
   126       * @return     the horizontal gap between components
   127       * @see        #setHgap()
   128       */
   129      public function getHgap():Number {
   130  		return hgap;
   131      }
   132  
   133      /**
   134       * Sets the horizontal gap between components.
   135       * @param hgap the horizontal gap between components
   136       * @see        #getHgap()
   137       */
   138      public function setHgap(hgap:Number):Void {
   139  		this.hgap = hgap;
   140      }
   141  
   142      /**
   143       * Gets the vertical gap between components.
   144       * @return     the vertical gap between components
   145       * @see        #setVgap()
   146       */
   147      public function getVgap():Number {
   148  		return vgap;
   149      }
   150  
   151      /**
   152       * Sets the vertical gap between components.
   153       * @param vgap the vertical gap between components
   154       * @see        #getVgap()
   155       */
   156      public function setVgap(vgap:Number):Void {
   157  		this.vgap = vgap;
   158      }
   159  
   160      /**
   161       * Returns the preferred dimensions for this layout given the 
   162       * <i>visible</i> components in the specified target container.
   163       * @param target the component which needs to be laid out
   164       * @return    the preferred dimensions to lay out the
   165       *            subcomponents of the specified container
   166       * @see Container
   167       * @see #doLayout()
   168       */
   169      public function preferredLayoutSize(target:Container):Dimension {
   170  		var dim:Dimension = new Dimension(0, 0);
   171  		var nmembers:Number = target.getComponentCount();
   172  
   173  		var counted:Number = 0;
   174  		for (var i:Number = 0 ; i < nmembers ; i++) {
   175  	    	var m:Component = target.getComponent(i);
   176  	    	if (m.isVisible()) {
   177  				var d:Dimension = m.getPreferredSize();
   178  				dim.height = Math.max(dim.height, d.height);
   179                  if (counted > 0) {
   180                      dim.width += hgap;
   181                  }
   182  				dim.width += d.width;
   183  				counted ++;
   184  	    	}
   185  		}
   186  		var insets:Insets = target.getInsets();
   187  		dim.width += insets.left + insets.right + hgap*2;
   188  		dim.height += insets.top + insets.bottom + vgap*2;
   189  		return dim;
   190      }
   191  
   192      /**
   193       * Returns the minimum dimensions needed to layout the <i>visible</i>
   194       * components contained in the specified target container.
   195       * @param target the component which needs to be laid out
   196       * @return    the minimum dimensions to lay out the
   197       *            subcomponents of the specified container
   198       * @see #preferredLayoutSize()
   199       * @see Container
   200       * @see Container#doLayout()
   201       */
   202      public function minimumLayoutSize(target:Container):Dimension {
   203  		var dim:Dimension = new Dimension(0, 0);
   204  		var nmembers:Number = target.getComponentCount();
   205  		var counted:Number = 0;
   206  		for (var i:Number = 0 ; i < nmembers ; i++) {
   207  	    	var m:Component = target.getComponent(i);
   208  	    	if (m.isVisible()) {
   209  				var d:Dimension = m.getMinimumSize();
   210  				dim.height = Math.max(dim.height, d.height);
   211  				if (counted > 0) {
   212  		   			dim.width += hgap;
   213  				}
   214  				dim.width += d.width;
   215  				counted ++;
   216  	   	 	}
   217  		}
   218  		var insets:Insets = target.getInsets();
   219  		dim.width += insets.left + insets.right + hgap*2;
   220  		dim.height += insets.top + insets.bottom + vgap*2;
   221  		return dim;
   222      }
   223      
   224      /**
   225       * Centers the elements in the specified row, if there is any slack.
   226       * @param target the component which needs to be moved
   227       * @param x the x coordinate
   228       * @param y the y coordinate
   229       * @param width the width dimensions
   230       * @param height the height dimensions
   231       * @param rowStart the beginning of the row
   232       * @param rowEnd the the ending of the row
   233       */
   234      private function moveComponents(target:Container, x:Number, y:Number, width:Number, height:Number,
   235                                  rowStart:Number, rowEnd:Number, ltr:Boolean):Void {
   236  		switch (align) {
   237  			case LEFT:
   238  	    		x += ltr ? 0 : width;
   239  	    		break;
   240  			case CENTER:
   241  	    		x += width / 2;
   242  	   			break;
   243  			case RIGHT:
   244  	    		x += ltr ? width : 0;
   245  	    		break;
   246  		}
   247  		for (var i:Number = rowStart ; i < rowEnd ; i++) {
   248  	    	var m:Component = target.getComponent(i);
   249  	    	var d:Dimension = m.getSize();
   250  	    	var td:Dimension = target.getSize();
   251  	    	if (m.isVisible()) {
   252  	        	if (ltr) {
   253          	    	m.setLocation(x, y + (height - d.height) / 2);
   254  	        	} else {
   255  	            	m.setLocation(td.width - x - d.width, y + (height - d.height) / 2);
   256                  }
   257                  x += d.width + hgap;
   258  	    	}
   259  		}
   260      }
   261  
   262      /**
   263       * Lays out the container. This method lets each component take
   264       * its preferred size by reshaping the components in the
   265       * target container in order to satisfy the alignment of
   266       * this <code>FlowLayout</code> object.
   267       * @param target the specified component being laid out
   268       * @see Container
   269       * @see Container#doLayout
   270       */
   271      public function layoutContainer(target:Container):Void {
   272  		var insets:Insets = target.getInsets();
   273  	    var td:Dimension = target.getSize();
   274  		var maxwidth:Number = td.width - (insets.left + insets.right + hgap*2);
   275  		var nmembers:Number = target.getComponentCount();
   276  		var x:Number = 0;
   277  		var y:Number = insets.top + vgap;
   278  		var rowh:Number = 0;
   279  		var start:Number = 0;
   280  
   281          var ltr:Boolean = true;
   282  
   283  		for (var i:Number = 0 ; i < nmembers ; i++) {
   284  	    	var m:Component = target.getComponent(i);
   285  	    	if (m.isVisible()) {
   286  				var d:Dimension = m.getPreferredSize();
   287  				m.setSize(d.width, d.height);
   288  
   289  				if ((x == 0) || ((x + d.width) <= maxwidth)) {
   290  		    		if (x > 0) {
   291  						x += hgap;
   292  		    		}
   293  		    		x += d.width;
   294  		    		rowh = Math.max(rowh, d.height);
   295  				} else {
   296  		    		moveComponents(target, insets.left + hgap, y, maxwidth - x, rowh, start, i, ltr);
   297  		    		x = d.width;
   298  		    		y += vgap + rowh;
   299  		    		rowh = d.height;
   300  		    		start = i;
   301  				}
   302  	    	}
   303  		}
   304  		moveComponents(target, insets.left + hgap, y, maxwidth - x, rowh, start, nmembers, ltr);
   305      }
   306      
   307      /**
   308       * Returns a string representation of this <code>FlowLayout</code>
   309       * object and its values.
   310       * @return     a string representation of this layout
   311       */
   312      public function toString():String {
   313  		var str:String = "";
   314  		switch (align) {
   315  	 	 	case LEFT:        str = ",align=left"; break;
   316  	 		case CENTER:      str = ",align=center"; break;
   317  	  		case RIGHT:       str = ",align=right"; break;
   318  		}
   319  		return "FlowLayout[hgap=" + hgap + ",vgap=" + vgap + str + "]";
   320      }
   321  }
   322