components/visual-panels/core/src/java/util/com/oracle/solaris/vp/util/swing/layout/AbstractLayout.java
changeset 3553 f1d133b09a8c
parent 3552 077ebe3d0d24
child 3554 ef58713bafc4
equal deleted inserted replaced
3552:077ebe3d0d24 3553:f1d133b09a8c
     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.ArrayList;
       
    30 
       
    31 public abstract class AbstractLayout implements LayoutManager {
       
    32     //
       
    33     // Inner classes
       
    34     //
       
    35 
       
    36     public interface SizedSet {
       
    37 	/**
       
    38 	 * Gets the number of elements in the set.
       
    39 	 */
       
    40 	int getCount();
       
    41 
       
    42 	/**
       
    43 	 * Gets the size of the {@code i}th element.
       
    44 	 */
       
    45 	int getSize(int i);
       
    46 
       
    47 	/**
       
    48 	 * Gets the weight of the {@code i}th element.
       
    49 	 */
       
    50 	float getWeight(int i);
       
    51 
       
    52 	/**
       
    53 	 * Sets the size of the {@code i}th element.
       
    54 	 */
       
    55 	void setSize(int i, int size);
       
    56 
       
    57 	/**
       
    58 	 * Sets the weight of the {@code i}th element.
       
    59 	 */
       
    60 	void setWeight(int i, float weight);
       
    61     }
       
    62 
       
    63     protected static class ArrayBasedSizedSet implements SizedSet {
       
    64 	//
       
    65 	// Instance data
       
    66 	//
       
    67 
       
    68 	private int[] sizes;
       
    69 	private float[] weights;
       
    70 
       
    71 	//
       
    72 	// Constructors
       
    73 	//
       
    74 
       
    75 	public ArrayBasedSizedSet(int[] sizes, float[] weights) {
       
    76 	    if (weights == null) {
       
    77 		weights = new float[sizes.length];
       
    78 	    } else
       
    79 
       
    80 	    // Check for differing array lengths
       
    81 	    if (sizes.length != weights.length) {
       
    82 		throw new IllegalArgumentException(String.format(
       
    83 		    "sizes (%d) and weights (%d) arrays differ in length",
       
    84 		    sizes.length, weights.length));
       
    85 	    }
       
    86 
       
    87 	    this.sizes = sizes;
       
    88 	    this.weights = weights;
       
    89 	}
       
    90 
       
    91 	//
       
    92 	// SizedSet methods
       
    93 	//
       
    94 
       
    95 	@Override
       
    96 	public int getCount() {
       
    97 	    return sizes.length;
       
    98 	}
       
    99 
       
   100 	@Override
       
   101 	public int getSize(int i) {
       
   102 	    return sizes[i];
       
   103 	}
       
   104 
       
   105 	@Override
       
   106 	public float getWeight(int i) {
       
   107 	    return weights[i];
       
   108 	}
       
   109 
       
   110 	@Override
       
   111 	public void setSize(int i, int size) {
       
   112 	    sizes[i] = size;
       
   113 	}
       
   114 
       
   115 	@Override
       
   116 	public void setWeight(int i, float weight) {
       
   117 	    weights[i] = weight;
       
   118 	}
       
   119     }
       
   120 
       
   121     //
       
   122     // LayoutManager methods
       
   123     //
       
   124 
       
   125     @Override
       
   126     public void addLayoutComponent(String name, Component comp) {
       
   127     }
       
   128 
       
   129     @Override
       
   130     public Dimension minimumLayoutSize(Container container) {
       
   131 	return getLayoutSize(container, false);
       
   132     }
       
   133 
       
   134     @Override
       
   135     public Dimension preferredLayoutSize(Container container) {
       
   136 	return getLayoutSize(container, true);
       
   137     }
       
   138 
       
   139     @Override
       
   140     public void removeLayoutComponent(Component comp) {
       
   141     }
       
   142 
       
   143     //
       
   144     // AbstractLayout methods
       
   145     //
       
   146 
       
   147     /**
       
   148      * Distribute the given extra space among the given sizes, subject to the
       
   149      * given weights.
       
   150      *
       
   151      * @param	    space
       
   152      *		    the excess space (may be negative) to distribute
       
   153      *
       
   154      * @param	    set
       
   155      *		    encapsulates the sizes/weights of the elements to distribute
       
   156      *		    {@code space} to
       
   157      *
       
   158      * @return	    the amount of space that could not be distributed
       
   159      */
       
   160     protected int distributeSpace(int space, SizedSet set) {
       
   161 	int length = set.getCount();
       
   162 
       
   163 	// Check for negative weights
       
   164 	for (int i = 0; i < length; i++) {
       
   165 	    float weight = set.getWeight(i);
       
   166 	    if (weight < 0) {
       
   167 		throw new IllegalArgumentException(
       
   168 		    "negative weight: " + weight);
       
   169 	    }
       
   170 	}
       
   171 
       
   172 	// Check for negative sizes
       
   173 	for (int i = 0; i < length; i++) {
       
   174 	    int size = set.getSize(i);
       
   175 	    if (size < 0) {
       
   176 		space -= size;
       
   177 		set.setSize(i, 0);
       
   178 	    }
       
   179 	}
       
   180 
       
   181 	if (space == 0) {
       
   182 	    return space;
       
   183 	}
       
   184 
       
   185 	// If weights are all zeros, weight each size equally
       
   186 	for (int i = 0; i < length; i++) {
       
   187 	    if (set.getWeight(i) != 0) {
       
   188 		break;
       
   189 	    }
       
   190 
       
   191 	    if (i == length - 1) {
       
   192 		for (int j = 0; j < length; j++) {
       
   193 		    set.setWeight(j, 1);
       
   194 		}
       
   195 	    }
       
   196 	}
       
   197 
       
   198 	// Verify that no non-zero weight is associated with a zero size
       
   199 	boolean zeroedWeight = false;
       
   200 	for (int i = 0; i < length; i++) {
       
   201 	    if (set.getWeight(i) != 0 && set.getSize(i) == 0) {
       
   202 		if (space <= 0) {
       
   203 		    // Zero out weights corresponding to zero sizes (resulting
       
   204 		    // sizes should not be negative)
       
   205 		    set.setWeight(i, 0);
       
   206 		    zeroedWeight = true;
       
   207 		} else {
       
   208 		    set.setSize(i, 1);
       
   209 		    space--;
       
   210 		}
       
   211 	    }
       
   212 	}
       
   213 
       
   214 	// If we zeroed out a weight...
       
   215 	if (zeroedWeight) {
       
   216 	    for (int i = 0; i < length; i++) {
       
   217 		if (set.getWeight(i) != 0) {
       
   218 		    break;
       
   219 		}
       
   220 
       
   221 		// If all weights are now zeros
       
   222 		if (i == length - 1) {
       
   223 		    // Weight each non-zero size equally
       
   224 		    for (int k = 0; k < length; k++) {
       
   225 			if (set.getSize(k) != 0) {
       
   226 			    set.setWeight(k, 1);
       
   227 			}
       
   228 		    }
       
   229 		}
       
   230 	    }
       
   231 	}
       
   232 
       
   233 	float sum = 0;
       
   234 	for (int i = 0; i < length; i++) {
       
   235 	    float weight = set.getWeight(i);
       
   236 	    weight *= (float)set.getSize(i);
       
   237 	    set.setWeight(i, weight);
       
   238 	    sum += weight;
       
   239 	}
       
   240 
       
   241 	int notDistributed = 0;
       
   242 
       
   243 	// Avoid divide-by-zero
       
   244 	if (sum != 0) {
       
   245 	    float remainder = 0;
       
   246 	    int total = 0;
       
   247 
       
   248 	    for (int i = 0; i < length; i++) {
       
   249 		float weight = set.getWeight(i);
       
   250 		weight /= sum;
       
   251 		set.setWeight(i, weight);
       
   252 
       
   253 		float delta = weight * (float)space;
       
   254 		int trunc = (int)delta;
       
   255 
       
   256 		// Keep track of truncated portion
       
   257 		remainder += delta - trunc;
       
   258 
       
   259 		int round = Math.round(remainder);
       
   260 		if (round != 0) {
       
   261 		    trunc += round;
       
   262 		    remainder -= round;
       
   263 		}
       
   264 
       
   265 		total += trunc;
       
   266 
       
   267 		int newSize = set.getSize(i) + trunc;
       
   268 		if (newSize < 0) {
       
   269 		    notDistributed += newSize;
       
   270 		    newSize = 0;
       
   271 		}
       
   272 
       
   273 		set.setSize(i, newSize);
       
   274 	    }
       
   275 	}
       
   276 
       
   277 	if (notDistributed != 0) {
       
   278 	    for (int i = 0; i < length; i++) {
       
   279 		if (set.getSize(i) != 0) {
       
   280 		    notDistributed = distributeSpace(notDistributed, set);
       
   281 		    break;
       
   282 		}
       
   283 	    }
       
   284 	}
       
   285 
       
   286 	return notDistributed;
       
   287     }
       
   288 
       
   289     /**
       
   290      * Distribute the given extra space among the given sizes, subject to the
       
   291      * given weights.
       
   292      *
       
   293      * @param	    space
       
   294      *		    the excess space (may be negative) to distribute
       
   295      *
       
   296      * @param	    sizes
       
   297      *		    the sizes to modify -- if any are negative, they will be
       
   298      *		    initially set to zero and the excess will be added to the
       
   299      *		    {@code space}
       
   300      *
       
   301      * @param	    weights
       
   302      *		    the weighting of each size, or {@code null} to apply
       
   303      *		    weightings equally across all sizes
       
   304      *
       
   305      * @exception   IllegalArgumentException
       
   306      *		    if the lengths of the two arrays are not equal, or a weight
       
   307      *		    is negative
       
   308      *
       
   309      * @return	    the amount of space that could not be distributed
       
   310      */
       
   311     protected int distributeSpace(int space, int[] sizes, float[] weights) {
       
   312 	SizedSet set = new ArrayBasedSizedSet(sizes, weights);
       
   313 	return distributeSpace(space, set);
       
   314     }
       
   315 
       
   316     public Component[] getLayoutComponents(Component[] comps) {
       
   317 	// Weed out Components that don't need to be layed out
       
   318 	ArrayList<Component> list = new ArrayList<Component>();
       
   319 	for (Component comp : comps) {
       
   320 	    if (needsLayout(comp)) {
       
   321 		list.add(comp);
       
   322 	    }
       
   323 	}
       
   324 	return list.toArray(new Component[list.size()]);
       
   325     }
       
   326 
       
   327     /**
       
   328      * Convenience method called by {@link
       
   329      * #minimumLayoutSize(java.awt.Container)} and {@link
       
   330      * #preferredLayoutSize(java.awt.Container)}.  This default implementation
       
   331      * returns {@code null}, in case subclasses choose to override those methods
       
   332      * directly.
       
   333      */
       
   334     protected Dimension getLayoutSize(Container container, boolean preferred) {
       
   335 	return null;
       
   336     }
       
   337 
       
   338     /**
       
   339      * Called by {@code #getLayoutComponents(Component[])}, determines whether
       
   340      * the given {@code Component} should be layed out.  This default
       
   341      * implmentation returns {@code true} iff the {@code Component} is
       
   342      * non-{@code null}.
       
   343      */
       
   344     protected boolean needsLayout(Component c) {
       
   345 	return c != null;
       
   346     }
       
   347 }