components/visual-panels/core/src/java/util/com/oracle/solaris/vp/util/swing/layout/AbstractTableLayout.java
changeset 827 0944d8c0158b
equal deleted inserted replaced
826:c6aad84d2493 827:0944d8c0158b
       
     1 /*
       
     2  * CDDL HEADER START
       
     3  *
       
     4  * The contents of this file are subject to the terms of the
       
     5  * Common Development and Distribution License (the "License").
       
     6  * You may not use this file except in compliance with the License.
       
     7  *
       
     8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
       
     9  * or http://www.opensolaris.org/os/licensing.
       
    10  * See the License for the specific language governing permissions
       
    11  * and limitations under the License.
       
    12  *
       
    13  * When distributing Covered Code, include this CDDL HEADER in each
       
    14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
       
    15  * If applicable, add the following below this CDDL HEADER, with the
       
    16  * fields enclosed by brackets "[]" replaced with your own identifying
       
    17  * information: Portions Copyright [yyyy] [name of copyright owner]
       
    18  *
       
    19  * CDDL HEADER END
       
    20  */
       
    21 
       
    22 /*
       
    23  * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved.
       
    24  */
       
    25 
       
    26 package com.oracle.solaris.vp.util.swing.layout;
       
    27 
       
    28 import java.awt.*;
       
    29 import java.util.*;
       
    30 import java.util.List;
       
    31 import com.oracle.solaris.vp.util.swing.layout.AbstractLayout.SizedSet;
       
    32 import com.oracle.solaris.vp.util.swing.layout.Anchor.AnchorType;
       
    33 
       
    34 public abstract class AbstractTableLayout<C extends HasAnchors>
       
    35     extends ConstrainedLayout<C> implements HasAnchors {
       
    36 
       
    37     //
       
    38     // Inner classes
       
    39     //
       
    40 
       
    41     protected static class CellInfo {
       
    42 	//
       
    43 	// Instance data
       
    44 	//
       
    45 
       
    46 	private Component component;
       
    47 	private Dimension size;
       
    48 	private boolean preferred;
       
    49 	private List<RowOrCol> parents = new ArrayList<RowOrCol>();
       
    50 
       
    51 	//
       
    52 	// Constructors
       
    53 	//
       
    54 
       
    55 	public CellInfo(Component component, boolean preferred) {
       
    56 	    this.component = component;
       
    57 	    this.preferred = preferred;
       
    58 	}
       
    59 
       
    60 	//
       
    61 	// CellInfo methods
       
    62 	//
       
    63 
       
    64 	public void add(RowOrCol parent) {
       
    65 	    parents.add(parent);
       
    66 	}
       
    67 
       
    68 	public Component getComponent() {
       
    69 	    return component;
       
    70 	}
       
    71 
       
    72 	public Dimension getSize() {
       
    73 	    // Deferred fetch
       
    74 	    if (size == null) {
       
    75 		size = preferred ?
       
    76 		    component.getPreferredSize() : component.getMinimumSize();
       
    77 	    }
       
    78 	    return size;
       
    79 	}
       
    80 
       
    81 	public void remove() {
       
    82 	    for (int i = parents.size() - 1; i >= 0; i--) {
       
    83 		parents.get(i).childRemoved(this);
       
    84 		parents.remove(i);
       
    85 	    }
       
    86 	}
       
    87     }
       
    88 
       
    89     protected static class RowOrCol {
       
    90 	//
       
    91 	// Instance data
       
    92 	//
       
    93 
       
    94 	private AbstractTableConstraint constraint;
       
    95 	private boolean isRow;
       
    96 	private int size = -1;
       
    97 	private float weight = -1f;
       
    98 	private List<CellInfo> children = new ArrayList<CellInfo>();
       
    99 	private List<CellInfo> roChildren =
       
   100 	    Collections.unmodifiableList(children);
       
   101 
       
   102 	//
       
   103 	// Constructors
       
   104 	//
       
   105 
       
   106 	public RowOrCol(AbstractTableConstraint constraint, boolean isRow) {
       
   107 	    this.constraint = constraint;
       
   108 	    this.isRow = isRow;
       
   109 	}
       
   110 
       
   111 	//
       
   112 	// Instance data
       
   113 	//
       
   114 
       
   115 	public void add(CellInfo child) {
       
   116 	    children.add(child);
       
   117 	    child.add(this);
       
   118 	}
       
   119 
       
   120 	/**
       
   121 	 * Called by the child when it is removed from layout.
       
   122 	 */
       
   123 	public boolean childRemoved(CellInfo child) {
       
   124 	    boolean ret = children.remove(child);
       
   125 	    if (ret) {
       
   126 		size = -1;
       
   127 	    }
       
   128 	    return ret;
       
   129 	}
       
   130 
       
   131 	public AbstractTableConstraint getConstraint() {
       
   132 	    return constraint;
       
   133 	}
       
   134 
       
   135 	public List<CellInfo> getChildren() {
       
   136 	    return roChildren;
       
   137 	}
       
   138 
       
   139 	/**
       
   140 	 * Get the maximum size (row height or column width) of every child
       
   141 	 * {@code Component}.
       
   142 	 */
       
   143 	public int getSize() {
       
   144 	    // Deferred fetch
       
   145 	    if (size == -1) {
       
   146 		for (CellInfo child : children) {
       
   147 		    Dimension d = child.getSize();
       
   148 		    int s = isRow ? d.height : d.width;
       
   149 		    if (s > size) {
       
   150 			size = s;
       
   151 		    }
       
   152 		}
       
   153 	    }
       
   154 	    return size;
       
   155 	}
       
   156 
       
   157 	public float getWeight() {
       
   158 	    // Deferred fetch
       
   159 	    if (weight == -1) {
       
   160 		weight = constraint.getWeight();
       
   161 	    }
       
   162 	    return weight;
       
   163 	}
       
   164 
       
   165 	/**
       
   166 	 * Remove all children in this row/column from layout (in any
       
   167 	 * row/column).
       
   168 	 */
       
   169 	public void remove() {
       
   170 	    for (int i = children.size() - 1; i >= 0; i--) {
       
   171 		remove(children.get(i));
       
   172 	    }
       
   173 	}
       
   174 
       
   175 	/**
       
   176 	 * Remove the given child in this row/column from layout (in any
       
   177 	 * row/column).
       
   178 	 */
       
   179 	public boolean remove(CellInfo child) {
       
   180 	    if (children.contains(child)) {
       
   181 		child.remove();
       
   182 		return true;
       
   183 	    }
       
   184 	    return false;
       
   185 	}
       
   186 
       
   187 	public void setSize(int size) {
       
   188 	    this.size = size;
       
   189 	}
       
   190 
       
   191 	public void setWeight(float weight) {
       
   192 	    this.weight = weight;
       
   193 	}
       
   194     }
       
   195 
       
   196     protected static class RowOrColSet implements SizedSet {
       
   197 	//
       
   198 	// Instance data
       
   199 	//
       
   200 
       
   201 	private int tSizes = -1;
       
   202 	private int tGaps = -1;
       
   203 	private List<RowOrCol> elements = new ArrayList<RowOrCol>();
       
   204 	private List<RowOrCol> roElements =
       
   205 	    Collections.unmodifiableList(elements);
       
   206 
       
   207 	//
       
   208 	// SizedSet methods
       
   209 	//
       
   210 
       
   211 	@Override
       
   212 	public int getCount() {
       
   213 	    return elements.size();
       
   214 	}
       
   215 
       
   216 	@Override
       
   217 	public int getSize(int i) {
       
   218 	    return elements.get(i).getSize();
       
   219 	}
       
   220 
       
   221 	@Override
       
   222 	public float getWeight(int i) {
       
   223 	    return elements.get(i).getWeight();
       
   224 	}
       
   225 
       
   226 	@Override
       
   227 	public void setSize(int i, int size) {
       
   228 	    elements.get(i).setSize(size);
       
   229 	}
       
   230 
       
   231 	@Override
       
   232 	public void setWeight(int i, float weight) {
       
   233 	    elements.get(i).setWeight(weight);
       
   234 	}
       
   235 
       
   236 	//
       
   237 	// RowOrColSet methods
       
   238 	//
       
   239 
       
   240 	public void add(RowOrCol element) {
       
   241 	    elements.add(element);
       
   242 	}
       
   243 
       
   244 	public void add(int i, RowOrCol element) {
       
   245 	    elements.add(i, element);
       
   246 	}
       
   247 
       
   248 	public List<RowOrCol> getElements() {
       
   249 	    return roElements;
       
   250 	}
       
   251 
       
   252 	public int getGap(int i) {
       
   253 	    AbstractTableConstraint constraint =
       
   254 		elements.get(i).getConstraint();
       
   255 
       
   256 	    return (i == 0 && constraint.getIgnoreFirstGap()) ?
       
   257 		0 : constraint.getGap();
       
   258 	}
       
   259 
       
   260 	public int getGapTotal() {
       
   261 	    // Deferred fetch
       
   262 	    if (tGaps == -1) {
       
   263 		tGaps = 0;
       
   264 		for (int i = 0, n = getCount(); i < n; i++) {
       
   265 		    tGaps += getGap(i);
       
   266 		}
       
   267 	    }
       
   268 	    return tGaps;
       
   269 	}
       
   270 
       
   271 	public int getSizeTotal() {
       
   272 	    // Deferred fetch
       
   273 	    if (tSizes == -1) {
       
   274 		tSizes = 0;
       
   275 		for (RowOrCol element : elements) {
       
   276 		    tSizes += element.getSize();
       
   277 		}
       
   278 	    }
       
   279 	    return tSizes;
       
   280 	}
       
   281 
       
   282 	public void remove(int i) {
       
   283 	    RowOrCol element = elements.remove(i);
       
   284 	    if (element != null) {
       
   285 		element.remove();
       
   286 		tSizes = -1;
       
   287 		tGaps = -1;
       
   288 	    }
       
   289 	}
       
   290     }
       
   291 
       
   292     protected static class TableInfo {
       
   293 	//
       
   294 	// Instance data
       
   295 	//
       
   296 
       
   297 	private RowOrColSet cols;
       
   298 	private RowOrColSet rows;
       
   299 
       
   300 	//
       
   301 	// Constructors
       
   302 	//
       
   303 
       
   304 	public TableInfo(RowOrColSet cols, RowOrColSet rows) {
       
   305 	    this.cols = cols;
       
   306 	    this.rows = rows;
       
   307 
       
   308 	    // Remove rows/columns from layout if a) directed by constraints b)
       
   309 	    // they contain only invisible Components
       
   310 	    for (RowOrColSet set : new RowOrColSet[] {rows, cols}) {
       
   311 		List<RowOrCol> elements = set.getElements();
       
   312 		OUTER: for (int i = elements.size() - 1; i >= 0; i--) {
       
   313 		    RowOrCol element = elements.get(i);
       
   314 		    if (!element.getConstraint().getLayoutIfInvisible()) {
       
   315 			for (CellInfo comp : element.getChildren()) {
       
   316 			    if (comp.getComponent().isVisible()) {
       
   317 				continue OUTER;
       
   318 			    }
       
   319 			}
       
   320 
       
   321 			// Remove this row/column from layout
       
   322 			set.remove(i);
       
   323 		    }
       
   324 		}
       
   325 	    }
       
   326 	}
       
   327 
       
   328 	//
       
   329 	// TableInfo methods
       
   330 	//
       
   331 
       
   332 	public RowOrColSet getColumns() {
       
   333 	    return cols;
       
   334 	}
       
   335 
       
   336 	public RowOrColSet getRows() {
       
   337 	    return rows;
       
   338 	}
       
   339     }
       
   340 
       
   341     //
       
   342     // Instance data
       
   343     //
       
   344 
       
   345     public HorizontalAnchor hAnchor;
       
   346     public VerticalAnchor vAnchor;
       
   347 
       
   348     //
       
   349     // Constructors
       
   350     //
       
   351 
       
   352     public AbstractTableLayout(HorizontalAnchor hAnchor,
       
   353 	VerticalAnchor vAnchor, C defaultElementConstraint) {
       
   354 
       
   355 	super(defaultElementConstraint);
       
   356 	this.hAnchor = hAnchor;
       
   357 	this.vAnchor = vAnchor;
       
   358     }
       
   359 
       
   360     //
       
   361     // HasAnchors methods
       
   362     //
       
   363 
       
   364     @Override
       
   365     public HorizontalAnchor getHorizontalAnchor() {
       
   366 	return hAnchor;
       
   367     }
       
   368 
       
   369     @Override
       
   370     public VerticalAnchor getVerticalAnchor() {
       
   371 	return vAnchor;
       
   372     }
       
   373 
       
   374     //
       
   375     // LayoutManager methods
       
   376     //
       
   377 
       
   378     /**
       
   379      * Lay out Components in the given Container.
       
   380      */
       
   381     @Override
       
   382     public void layoutContainer(Container container) {
       
   383 	TableInfo info = getTableInfo(container, true);
       
   384 
       
   385 	RowOrColSet rows = info.getRows();
       
   386 	int nRows = rows.getCount();
       
   387 	if (nRows == 0) {
       
   388 	    return;
       
   389 	}
       
   390 
       
   391 	RowOrColSet cols = info.getColumns();
       
   392 	int nCols = cols.getCount();
       
   393 
       
   394 	Insets insets = container.getInsets();
       
   395 	Dimension aSize = container.getSize();
       
   396 
       
   397 	// The amount of space we have to work with
       
   398 	int cWidth = aSize.width - insets.left - insets.right;
       
   399 	int cHeight = aSize.height - insets.top - insets.bottom;
       
   400 
       
   401 	// Where to start drawing the first Component
       
   402 	int top = insets.top;
       
   403 	int tableLeft = insets.left;
       
   404 
       
   405 	// First set heights of rows
       
   406 	top += fitToSize(cHeight, rows, getVerticalAnchor());
       
   407 
       
   408 	// Then set widths of columns
       
   409 	tableLeft += fitToSize(cWidth, cols, getHorizontalAnchor());
       
   410 
       
   411 	// Lay out components
       
   412 	for (int r = 0; r < nRows; r++) {
       
   413 
       
   414 	    RowOrCol row = rows.getElements().get(r);
       
   415 	    int rSize = row.getSize();
       
   416 	    top += rows.getGap(r);
       
   417 
       
   418 	    List<CellInfo> children = row.getChildren();
       
   419 	    int left = tableLeft;
       
   420 	    for (int c = 0; c < nCols; c++) {
       
   421 
       
   422 		RowOrCol col = cols.getElements().get(c);
       
   423 		int cSize = col.getSize();
       
   424 		left += cols.getGap(c);
       
   425 
       
   426 		if (c < children.size()) {
       
   427 		    CellInfo cInfo = children.get(c);
       
   428 		    Component comp = cInfo.getComponent();
       
   429 		    Dimension pSize = cInfo.getSize();
       
   430 
       
   431 		    HasAnchors hasAnchors = compToConst.get(comp);
       
   432 
       
   433 		    int[] yAndHeight = getOffsetAndSize(rSize,
       
   434 			pSize.height, hasAnchors.getVerticalAnchor());
       
   435 
       
   436 		    int[] xAndWidth = getOffsetAndSize(cSize,
       
   437 			pSize.width, hasAnchors.getHorizontalAnchor());
       
   438 
       
   439 		    comp.setBounds(left + xAndWidth[0], top + yAndHeight[0],
       
   440 			xAndWidth[1], yAndHeight[1]);
       
   441 		}
       
   442 
       
   443 		left += cSize;
       
   444 	    }
       
   445 
       
   446 	    top += rSize;
       
   447 	}
       
   448     }
       
   449 
       
   450     //
       
   451     // AbstractLayout methods
       
   452     //
       
   453 
       
   454     @Override
       
   455     protected Dimension getLayoutSize(Container container, boolean preferred) {
       
   456 	TableInfo info = getTableInfo(container, preferred);
       
   457 
       
   458 	RowOrColSet cols = info.getColumns();
       
   459 	RowOrColSet rows = info.getRows();
       
   460 
       
   461 	int width = cols.getSizeTotal() + cols.getGapTotal();
       
   462 	int height = rows.getSizeTotal() + rows.getGapTotal();
       
   463 
       
   464 	Insets insets = container.getInsets();
       
   465 	width += insets.left + insets.right;
       
   466 	height += insets.top + insets.bottom;
       
   467 
       
   468 	return new Dimension(width, height);
       
   469     }
       
   470 
       
   471     protected TableInfo getTableInfo(Container container, boolean preferred) {
       
   472 	Component[] comps = getLayoutComponents(container.getComponents());
       
   473 
       
   474 	int nCols = getColumnCount(container);
       
   475 	int nRows = 0;
       
   476 	if (nCols != 0) {
       
   477 	    nRows = comps.length / nCols;
       
   478 	    if (comps.length % nCols > 0) {
       
   479 		nRows++;
       
   480 	    }
       
   481 	}
       
   482 
       
   483 	// Build cells
       
   484 	CellInfo[] cInfo = new CellInfo[comps.length];
       
   485 	for (int i = 0; i < comps.length; i++) {
       
   486 	    cInfo[i] = new CellInfo(comps[i], preferred);
       
   487 	}
       
   488 
       
   489 	// Build columns
       
   490 	RowOrColSet cols = new RowOrColSet();
       
   491 	for (int c = 0; c < nCols; c++) {
       
   492 	    AbstractTableConstraint constraint =
       
   493 		getColumnConstraint(container, c);
       
   494 
       
   495 	    RowOrCol col = new RowOrCol(constraint, false);
       
   496 
       
   497 	    for (int r = 0; r < nRows; r++) {
       
   498 		int index = r * nCols + c;
       
   499 		if (index < cInfo.length) {
       
   500 		    col.add(cInfo[index]);
       
   501 		}
       
   502 	    }
       
   503 
       
   504 	    cols.add(col);
       
   505 	}
       
   506 
       
   507 	// Build rows
       
   508 	RowOrColSet rows = new RowOrColSet();
       
   509 	for (int r = 0; r < nRows; r++) {
       
   510 	    AbstractTableConstraint constraint =
       
   511 		getRowConstraint(container, r);
       
   512 
       
   513 	    RowOrCol row = new RowOrCol(constraint, true);
       
   514 
       
   515 	    for (int c = 0; c < nCols; c++) {
       
   516 		int index = r * nCols + c;
       
   517 		if (index < cInfo.length) {
       
   518 		    row.add(cInfo[index]);
       
   519 		}
       
   520 	    }
       
   521 
       
   522 	    rows.add(row);
       
   523 	}
       
   524 
       
   525 	return new TableInfo(cols, rows);
       
   526     }
       
   527 
       
   528     //
       
   529     // AbstractTableLayout methods
       
   530     //
       
   531 
       
   532     protected abstract AbstractTableConstraint getColumnConstraint(
       
   533 	Container container, int col);
       
   534 
       
   535     protected abstract AbstractTableConstraint getRowConstraint(
       
   536 	Container container, int row);
       
   537 
       
   538     /**
       
   539      * Gets the number of columns in this {@code AbstractTableLayout}.
       
   540      *
       
   541      * @param	    container
       
   542      *		    the {@code Container} being layed out
       
   543      */
       
   544     protected abstract int getColumnCount(Container container);
       
   545 
       
   546     public void setHorizontalAnchor(HorizontalAnchor hAnchor) {
       
   547 	this.hAnchor = hAnchor;
       
   548     }
       
   549 
       
   550     public void setVerticalAnchor(VerticalAnchor vAnchor) {
       
   551 	this.vAnchor = vAnchor;
       
   552     }
       
   553 
       
   554     //
       
   555     // Private methods
       
   556     //
       
   557 
       
   558     private int fitToSize(int aSize, RowOrColSet set, Anchor anchor) {
       
   559 	int top = 0;
       
   560 	int tSizes = set.getSizeTotal();
       
   561 	int tGaps = set.getGapTotal();
       
   562 
       
   563 	// Excess/insufficient space
       
   564 	int extraSize = aSize - tSizes - tGaps;
       
   565 
       
   566 	if (extraSize != 0) {
       
   567 
       
   568 	    if (extraSize < 0 ||
       
   569 		anchor.getAnchorType() == Anchor.AnchorType.FILL) {
       
   570 
       
   571 		distributeSpace(extraSize, set);
       
   572 	    } else {
       
   573 
       
   574 		switch (anchor.getAnchorType()) {
       
   575 		    case RIGHT_BOTTOM:
       
   576 			top += (aSize - tSizes - tGaps);
       
   577 			break;
       
   578 
       
   579 		    case CENTER:
       
   580 			top += (aSize - tSizes - tGaps) / 2;
       
   581 			break;
       
   582 		}
       
   583 	    }
       
   584 	}
       
   585 
       
   586 	return top;
       
   587     }
       
   588 
       
   589     /**
       
   590      * Returns a two-element array consisting of the offset and size of a
       
   591      * resized {@code Component}.
       
   592      *
       
   593      * @param	    aSize
       
   594      *		    the available size in which to fit the {@code Component}
       
   595      *
       
   596      * @param	    pSize
       
   597      *		    the preferred size in which to fit the {@code Component}
       
   598      *
       
   599      * @param	    anchor
       
   600      *		    the Anchor of the {@code Component}
       
   601      */
       
   602     private int[] getOffsetAndSize(int aSize, int pSize, Anchor anchor) {
       
   603 	// Excess/insufficient space
       
   604 	int extra = aSize - pSize;
       
   605 
       
   606 	int offset = 0;
       
   607 	int size = pSize;
       
   608 	Anchor.AnchorType type = anchor.getAnchorType();
       
   609 
       
   610 	if (extra != 0) {
       
   611 
       
   612 	    if (extra < 0 || type == Anchor.AnchorType.FILL) {
       
   613 		size = aSize;
       
   614 	    } else {
       
   615 
       
   616 		switch (type) {
       
   617 		    case RIGHT_BOTTOM:
       
   618 			offset = (aSize - pSize);
       
   619 			break;
       
   620 
       
   621 		    case CENTER:
       
   622 			offset = (aSize - pSize) / 2;
       
   623 			break;
       
   624 		}
       
   625 	    }
       
   626 	}
       
   627 
       
   628 	return new int[] {offset, size};
       
   629     }
       
   630 }