1 /* 2 * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package javax.swing.text.html; 26 27 import java.awt.*; 28 import java.util.*; 29 import javax.swing.*; 30 import javax.swing.text.*; 31 import javax.swing.event.*; 32 33 /** 34 * Implements a FrameSetView, intended to support the HTML 35 * <FRAMESET> tag. Supports the ROWS and COLS attributes. 36 * 37 * @author Sunita Mani 38 * 39 * Credit also to the hotjava browser engineers that 40 * worked on making the allocation of space algorithms 41 * conform to the HTML 4.0 standard and also be netscape 42 * compatible. 43 * 44 */ 45 46 class FrameSetView extends javax.swing.text.BoxView { 47 48 String[] children; 49 int[] percentChildren; 50 int[] absoluteChildren; 51 int[] relativeChildren; 52 int percentTotals; 53 int absoluteTotals; 54 int relativeTotals; 55 56 /** 57 * Constructs a FrameSetView for the given element. 58 * 59 * @param elem the element that this view is responsible for 60 */ 61 public FrameSetView(Element elem, int axis) { 62 super(elem, axis); 63 children = null; 64 } 65 66 /** 67 * Parses the ROW or COL attributes and returns 68 * an array of strings that represent the space 69 * distribution. 70 * 71 */ 72 private String[] parseRowColSpec(HTML.Attribute key) { 73 74 AttributeSet attributes = getElement().getAttributes(); 75 String spec = "*"; 76 if (attributes != null) { 77 if (attributes.getAttribute(key) != null) { 78 spec = (String)attributes.getAttribute(key); 79 } 80 } 81 82 StringTokenizer tokenizer = new StringTokenizer(spec, ","); 83 int nTokens = tokenizer.countTokens(); 84 int n = getViewCount(); 85 String[] items = new String[Math.max(nTokens, n)]; 86 int i = 0; 87 for (; i < nTokens; i++) { 88 items[i] = tokenizer.nextToken().trim(); 89 // As per the spec, 100% is the same as * 90 // hence the mapping. 91 // 92 if (items[i].equals("100%")) { 93 items[i] = "*"; 94 } 95 } 96 // extend spec if we have more children than specified 97 // in ROWS or COLS attribute 98 for (; i < items.length; i++) { 99 items[i] = "*"; 100 } 101 return items; 102 } 103 104 105 /** 106 * Initializes a number of internal state variables 107 * that store information about space allocation 108 * for the frames contained within the frameset. 109 */ 110 private void init() { 111 if (getAxis() == View.Y_AXIS) { 112 children = parseRowColSpec(HTML.Attribute.ROWS); 113 } else { 114 children = parseRowColSpec(HTML.Attribute.COLS); 115 } 116 percentChildren = new int[children.length]; 117 relativeChildren = new int[children.length]; 118 absoluteChildren = new int[children.length]; 119 120 for (int i = 0; i < children.length; i++) { 121 percentChildren[i] = -1; 122 relativeChildren[i] = -1; 123 absoluteChildren[i] = -1; 124 125 if (children[i].endsWith("*")) { 126 if (children[i].length() > 1) { 127 relativeChildren[i] = 128 Integer.parseInt(children[i].substring( 129 0, children[i].length()-1).trim()); 130 relativeTotals += relativeChildren[i]; 131 } else { 132 relativeChildren[i] = 1; 133 relativeTotals += 1; 134 } 135 } else if (children[i].indexOf('%') != -1) { 136 percentChildren[i] = parseDigits(children[i]); 137 percentTotals += percentChildren[i]; 138 } else { 139 String value = children[i].toLowerCase(); 140 if (value.endsWith("px")) { 141 value = value.substring(0, value.length()-2).trim(); 142 } 143 absoluteChildren[i] = Integer.parseInt(value); 144 } 145 } 146 if (percentTotals > 100) { 147 for (int i = 0; i < percentChildren.length; i++) { 148 if (percentChildren[i] > 0) { 149 percentChildren[i] = 150 (percentChildren[i] * 100) / percentTotals; 151 } 152 } 153 percentTotals = 100; 154 } 155 } 156 157 /** 158 * Perform layout for the major axis of the box (i.e. the 159 * axis that it represents). The results of the layout should 160 * be placed in the given arrays which represent the allocations 161 * to the children along the major axis. 162 * 163 * @param targetSpan the total span given to the view, which 164 * would be used to layout the children 165 * @param axis the axis being layed out 166 * @param offsets the offsets from the origin of the view for 167 * each of the child views; this is a return value and is 168 * filled in by the implementation of this method 169 * @param spans the span of each child view; this is a return 170 * value and is filled in by the implementation of this method 171 * @return the offset and span for each child view in the 172 * offsets and spans parameters 173 */ 174 protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets, 175 int[] spans) { 176 if (children == null) { 177 init(); 178 } 179 SizeRequirements.calculateTiledPositions(targetSpan, null, 180 getChildRequests(targetSpan, 181 axis), 182 offsets, spans); 183 } 184 185 protected SizeRequirements[] getChildRequests(int targetSpan, int axis) { 186 187 int span[] = new int[children.length]; 188 189 spread(targetSpan, span); 190 int n = getViewCount(); 191 SizeRequirements[] reqs = new SizeRequirements[n]; 192 for (int i = 0, sIndex = 0; i < n; i++) { 193 View v = getView(i); 194 if ((v instanceof FrameView) || (v instanceof FrameSetView)) { 195 reqs[i] = new SizeRequirements((int) v.getMinimumSpan(axis), 196 span[sIndex], 197 (int) v.getMaximumSpan(axis), 198 0.5f); 199 sIndex++; 200 } else { 201 int min = (int) v.getMinimumSpan(axis); 202 int pref = (int) v.getPreferredSpan(axis); 203 int max = (int) v.getMaximumSpan(axis); 204 float a = v.getAlignment(axis); 205 reqs[i] = new SizeRequirements(min, pref, max, a); 206 } 207 } 208 return reqs; 209 } 210 211 212 /** 213 * This method is responsible for returning in span[] the 214 * span for each child view along the major axis. it 215 * computes this based on the information that extracted 216 * from the value of the ROW/COL attribute. 217 */ 218 private void spread(int targetSpan, int span[]) { 219 220 if (targetSpan == 0) { 221 return; 222 } 223 224 int tempSpace = 0; 225 int remainingSpace = targetSpan; 226 227 // allocate the absolute's first, they have 228 // precedence 229 // 230 for (int i = 0; i < span.length; i++) { 231 if (absoluteChildren[i] > 0) { 232 span[i] = absoluteChildren[i]; 233 remainingSpace -= span[i]; 234 } 235 } 236 237 // then deal with percents. 238 // 239 tempSpace = remainingSpace; 240 for (int i = 0; i < span.length; i++) { 241 if (percentChildren[i] > 0 && tempSpace > 0) { 242 span[i] = (percentChildren[i] * tempSpace) / 100; 243 remainingSpace -= span[i]; 244 } else if (percentChildren[i] > 0 && tempSpace <= 0) { 245 span[i] = targetSpan / span.length; 246 remainingSpace -= span[i]; 247 } 248 } 249 250 // allocate remainingSpace to relative 251 if (remainingSpace > 0 && relativeTotals > 0) { 252 for (int i = 0; i < span.length; i++) { 253 if (relativeChildren[i] > 0) { 254 span[i] = (remainingSpace * 255 relativeChildren[i]) / relativeTotals; 256 } 257 } 258 } else if (remainingSpace > 0) { 259 // There are no relative columns and the space has been 260 // under- or overallocated. In this case, turn all the 261 // percentage and pixel specified columns to percentage 262 // columns based on the ratio of their pixel count to the 263 // total "virtual" size. (In the case of percentage columns, 264 // the pixel count would equal the specified percentage 265 // of the screen size. 266 267 // This action is in accordance with the HTML 268 // 4.0 spec (see section 8.3, the end of the discussion of 269 // the FRAMESET tag). The precedence of percentage and pixel 270 // specified columns is unclear (spec seems to indicate that 271 // they share priority, however, unspecified what happens when 272 // overallocation occurs.) 273 274 // addendum is that we behave similar to netscape in that specified 275 // widths have precedance over percentage widths... 276 277 float vTotal = (float)(targetSpan - remainingSpace); 278 float[] tempPercents = new float[span.length]; 279 remainingSpace = targetSpan; 280 for (int i = 0; i < span.length; i++) { 281 // ok we know what our total space is, and we know how large each 282 // column should be relative to each other... therefore we can use 283 // that relative information to deduce their percentages of a whole 284 // and then scale them appropriately for the correct size 285 tempPercents[i] = ((float)span[i] / vTotal) * 100.00f; 286 span[i] = (int) ( ((float)targetSpan * tempPercents[i]) / 100.00f); 287 remainingSpace -= span[i]; 288 } 289 290 291 // this is for just in case there is something left over.. if there is we just 292 // add it one pixel at a time to the frames in order.. We shouldn't really ever get 293 // here and if we do it shouldn't be with more than 1 pixel, maybe two. 294 int i = 0; 295 while (remainingSpace != 0) { 296 if (remainingSpace < 0) { 297 span[i++]--; 298 remainingSpace++; 299 } 300 else { 301 span[i++]++; 302 remainingSpace--; 303 } 304 305 // just in case there are more pixels than frames...should never happen.. 306 if (i == span.length)i = 0; 307 } 308 } 309 } 310 311 /* 312 * Users have been known to type things like "%25" and "25 %". Deal 313 * with it. 314 */ 315 private int parseDigits(String mixedStr) { 316 int result = 0; 317 for (int i = 0; i < mixedStr.length(); i++) { 318 char ch = mixedStr.charAt(i); 319 if (Character.isDigit(ch)) { 320 result = (result * 10) + Character.digit(ch, 10); 321 } 322 } 323 return result; 324 } 325 326 }