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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * ident "%Z%%M% %I% %E% SMI" 24 * 25 * Copyright (c) 1996-2001 by Sun Microsystems, Inc. 26 * All rights reserved. 27 */ 28 package com.sun.dhcpmgr.ui; 29 30 import java.awt.*; 31 import java.util.*; 32 33 /** 34 * <CODE>FieldLayout</CODE> treats components as a list of 35 * labeled fields, where each label is placed on the left 36 * edge of the container with its associated field to its 37 * right.<P> 38 * 39 * Two kinds of components may be added: "Label" and "Field." 40 * Labels and Fields must be added in pairs, because there is 41 * a one-to-one correspondence between them.<P> 42 * 43 * When a <CODE>Field</CODE> is added, it is associated with the 44 * last <CODE>Label</CODE> added.<P> 45 */ 46 public class FieldLayout implements LayoutManager { 47 public static final String LABEL = "Label"; 48 public static final String LABELTOP = "LabelTop"; 49 public static final String FIELD = "Field"; 50 51 class Row { 52 Component label; 53 Component field; 54 double yRatio; 55 boolean center; 56 57 public Row() { 58 label = null; 59 field = null; 60 yRatio = 1; 61 center = true; 62 } 63 } 64 65 Vector rows; 66 int hgap; 67 int vgap; 68 69 /** 70 * Constructs a new <CODE>FieldLayout</CODE> with a centered alignment. 71 */ 72 public FieldLayout() { 73 this(5, 5); 74 } 75 76 /** 77 * Constructs a new <CODE>FieldLayout</CODE> with the specified gap values. 78 * @param <VAR>hgap</VAR> The horizontal gap variable. 79 * @param <VAR>vgap</VAR> The vertical gap variable. 80 */ 81 public FieldLayout(int hgap, int vgap) { 82 this.hgap = hgap; 83 this.vgap = vgap; 84 rows = new Vector(); 85 } 86 87 /** 88 * Adds the specified component to the layout. 89 * @param <VAR>name</VAR> The name of the component. 90 * @param <VAR>comp</VAR> The component to be added. 91 */ 92 public void addLayoutComponent(String name, Component comp) { 93 if (LABEL.equals(name)) { 94 Row r = new Row(); 95 r.label = comp; 96 r.center = true; 97 rows.addElement(r); 98 } else if (LABELTOP.equals(name)) { 99 Row r = new Row(); 100 r.label = comp; 101 r.center = false; 102 rows.addElement(r); 103 } else if (FIELD.equals(name)) { 104 ((Row)rows.lastElement()).field = comp; 105 } 106 } 107 108 /** 109 * Removes the specified component from the layout. 110 * @param <VAR>comp</VAR> The component to remove. 111 */ 112 public void removeLayoutComponent(Component comp) { 113 Enumeration en = rows.elements(); 114 while (en.hasMoreElements()) { 115 Row r = (Row)en.nextElement(); 116 if (comp == r.label || comp == r.field) { 117 rows.removeElement(r); 118 return; 119 } 120 } 121 } 122 123 /** 124 * Returns the preferred dimensions for this layout given the components 125 * in the specified target container. 126 * @param <VAR>target</VAR> The component that needs to be laid out. 127 * @see java.awt.Container 128 * @see #minimumLayoutSize 129 */ 130 public Dimension preferredLayoutSize(Container target) { 131 Dimension dim = new Dimension(0, 0); 132 int widestLabel = 0, widestField = 0; 133 134 Enumeration en = rows.elements(); 135 while (en.hasMoreElements()) { 136 Row r = (Row)en.nextElement(); 137 if (!r.label.isVisible() || !r.field.isVisible()) { 138 continue; 139 } 140 Dimension ld = r.label.getPreferredSize(); 141 widestLabel = Math.max(widestLabel, ld.width); 142 Dimension fd = r.field.getPreferredSize(); 143 widestField = Math.max(widestField, fd.width); 144 dim.height += Math.max(ld.height, fd.height) + vgap; 145 } 146 dim.width = widestLabel + hgap + widestField; 147 Insets insets = target.getInsets(); 148 dim.width += insets.left + insets.right + hgap*2; 149 dim.height += insets.top + insets.bottom + vgap; 150 return dim; 151 } 152 153 /** 154 * Returns the minimum dimensions needed to layout the components 155 * contained in the specified target container. 156 * @param <VAR>target</VAR> The component that needs to be laid out. 157 * @see #preferredLayoutSize 158 */ 159 public Dimension minimumLayoutSize(Container target) { 160 Dimension dim = new Dimension(0, 0); 161 int widestLabel = 0, widestField = 0; 162 163 Enumeration en = rows.elements(); 164 while (en.hasMoreElements()) { 165 Row r = (Row)en.nextElement(); 166 if (!r.label.isVisible() || !r.field.isVisible()) { 167 continue; 168 } 169 Dimension ld = r.label.getMinimumSize(); 170 widestLabel = Math.max(widestLabel, ld.width); 171 Dimension fd = r.field.getMinimumSize(); 172 widestField = Math.max(widestField, fd.width); 173 dim.height += Math.max(ld.height, fd.height) + vgap; 174 } 175 dim.width = widestLabel + hgap + widestField; 176 Insets insets = target.getInsets(); 177 dim.width += insets.left + insets.right + hgap*2; 178 dim.height += insets.top + insets.bottom + vgap; 179 return dim; 180 } 181 182 /** 183 * Performs the layout of the container. Components are treated 184 * either as labels or fields. Labels go on the left (right-aligned), 185 * with their associated fields placed immediately to their right. 186 * @param <VAR>target</VAR> The specified component being laid out. 187 * @see java.awt.Container 188 */ 189 public void layoutContainer(Container target) { 190 Insets insets = target.getInsets(); 191 Dimension dim = target.getSize(); 192 int x = 0, y = insets.top, offset = 0; 193 int widestLabel = 0; 194 int ySlop = 0; 195 196 // Compute whether preferred sizes will fit 197 Dimension pDim = preferredLayoutSize(target); 198 boolean usingPreferred = true; 199 if ((pDim.height > (dim.height - insets.top - insets.bottom)) || 200 (pDim.width > (dim.width - insets.left - insets.right))) { 201 usingPreferred = false; 202 // Compute leftover vertical space 203 pDim = minimumLayoutSize(target); 204 ySlop = dim.height - insets.top - insets.bottom - pDim.height; 205 if (ySlop < 0) { 206 ySlop = 0; 207 } 208 } 209 210 /* 211 * Find widest label. Our policy on horizontal space is that labels 212 * are fully satisfied and fields get whatever's left. 213 * For vertical space, if there's any leftovers then allocate it 214 * in proportion to demand, which we'll define as the ratio between 215 * preferred size and minimum size. 216 */ 217 double sumRatios = 0; 218 Enumeration en = rows.elements(); 219 while (en.hasMoreElements()) { 220 Row r = (Row)en.nextElement(); 221 if (r.label.isVisible() && r.field.isVisible()) { 222 Dimension d = usingPreferred ? r.label.getPreferredSize() : 223 r.label.getMinimumSize(); 224 widestLabel = Math.max(widestLabel, d.width); 225 if (!usingPreferred) { 226 double lRatio = r.label.getPreferredSize().getHeight() / 227 r.label.getMinimumSize().getHeight(); 228 double fRatio = r.field.getPreferredSize().getHeight() / 229 r.field.getMinimumSize().getHeight(); 230 r.yRatio = Math.max(lRatio, fRatio); 231 } 232 // If there is no demand, then adjust ratio to zero 233 if (r.yRatio == 1.0) { 234 r.yRatio = 0; 235 } 236 sumRatios += r.yRatio; 237 } 238 } 239 240 // lay out rows, right-aligning labels 241 en = rows.elements(); 242 while (en.hasMoreElements()) { 243 Row r = (Row)en.nextElement(); 244 Component l = r.label; 245 Component f = r.field; 246 // Skip the row if both aren't visible 247 if (!l.isVisible() || !f.isVisible()) 248 continue; 249 Dimension ld = usingPreferred ? l.getPreferredSize() : 250 l.getMinimumSize(); 251 Dimension fd = usingPreferred ? f.getPreferredSize() : 252 f.getMinimumSize(); 253 254 int rowHeight = Math.max(ld.height, fd.height) + 255 (int)(ySlop * r.yRatio / sumRatios); 256 257 x = insets.left; 258 /* 259 * If the field is visible, move it right to line up with 260 * the widest line. 261 */ 262 x += Math.max(widestLabel - ld.width, 0); 263 offset = 0; 264 if (r.center) { 265 // center label on field 266 offset = Math.max(0, (rowHeight-ld.height)/2); 267 } 268 int labelHeight = rowHeight; 269 /* 270 * If label doesn't look like it wants extra space, don't give it; 271 * otherwise, JLabels will get drawn centered even if user 272 * specified it as a top alignment when doing the layout. 273 */ 274 if (l.getPreferredSize().height == l.getMinimumSize().height) { 275 labelHeight = ld.height; 276 } 277 l.setBounds(x, y+offset, ld.width, labelHeight); 278 x = insets.left + widestLabel + hgap; 279 int w = dim.width-x-hgap; 280 f.setBounds(x, y, w, rowHeight); 281 y += rowHeight + vgap; 282 } 283 284 } 285 286 /** 287 * Returns the <CODE>String</CODE> representation of this 288 * <CODE>FieldLayout</CODE>'s values. 289 */ 290 public String toString() { 291 return getClass().getName() + "[hgap=" + hgap + ",vgap=" + vgap + "]"; 292 } 293 }