blob: b653d32eec4e1a9e17ded1825d5360cb5725670a [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1998-2003 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25package javax.swing.text.html;
26
27import java.awt.*;
28import java.util.*;
29import javax.swing.*;
30import javax.swing.text.*;
31import 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
46class 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));
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 absoluteChildren[i] = Integer.parseInt(children[i]);
140 }
141 }
142 if (percentTotals > 100) {
143 for (int i = 0; i < percentChildren.length; i++) {
144 if (percentChildren[i] > 0) {
145 percentChildren[i] =
146 (percentChildren[i] * 100) / percentTotals;
147 }
148 }
149 percentTotals = 100;
150 }
151 }
152
153 /**
154 * Perform layout for the major axis of the box (i.e. the
155 * axis that it represents). The results of the layout should
156 * be placed in the given arrays which represent the allocations
157 * to the children along the major axis.
158 *
159 * @param targetSpan the total span given to the view, which
160 * whould be used to layout the children
161 * @param axis the axis being layed out
162 * @param offsets the offsets from the origin of the view for
163 * each of the child views; this is a return value and is
164 * filled in by the implementation of this method
165 * @param spans the span of each child view; this is a return
166 * value and is filled in by the implementation of this method
167 * @return the offset and span for each child view in the
168 * offsets and spans parameters
169 */
170 protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets,
171 int[] spans) {
172 if (children == null) {
173 init();
174 }
175 SizeRequirements.calculateTiledPositions(targetSpan, null,
176 getChildRequests(targetSpan,
177 axis),
178 offsets, spans);
179 }
180
181 protected SizeRequirements[] getChildRequests(int targetSpan, int axis) {
182
183 int span[] = new int[children.length];
184
185 spread(targetSpan, span);
186 int n = getViewCount();
187 SizeRequirements[] reqs = new SizeRequirements[n];
188 for (int i = 0, sIndex = 0; i < n; i++) {
189 View v = getView(i);
190 if ((v instanceof FrameView) || (v instanceof FrameSetView)) {
191 reqs[i] = new SizeRequirements((int) v.getMinimumSpan(axis),
192 span[sIndex],
193 (int) v.getMaximumSpan(axis),
194 0.5f);
195 sIndex++;
196 } else {
197 int min = (int) v.getMinimumSpan(axis);
198 int pref = (int) v.getPreferredSpan(axis);
199 int max = (int) v.getMaximumSpan(axis);
200 float a = v.getAlignment(axis);
201 reqs[i] = new SizeRequirements(min, pref, max, a);
202 }
203 }
204 return reqs;
205 }
206
207
208 /**
209 * This method is responsible for returning in span[] the
210 * span for each child view along the major axis. it
211 * computes this based on the information that extracted
212 * from the value of the ROW/COL attribute.
213 */
214 private void spread(int targetSpan, int span[]) {
215
216 if (targetSpan == 0) {
217 return;
218 }
219
220 int tempSpace = 0;
221 int remainingSpace = targetSpan;
222
223 // allocate the absolute's first, they have
224 // precedence
225 //
226 for (int i = 0; i < span.length; i++) {
227 if (absoluteChildren[i] > 0) {
228 span[i] = absoluteChildren[i];
229 remainingSpace -= span[i];
230 }
231 }
232
233 // then deal with percents.
234 //
235 tempSpace = remainingSpace;
236 for (int i = 0; i < span.length; i++) {
237 if (percentChildren[i] > 0 && tempSpace > 0) {
238 span[i] = (percentChildren[i] * tempSpace) / 100;
239 remainingSpace -= span[i];
240 } else if (percentChildren[i] > 0 && tempSpace <= 0) {
241 span[i] = targetSpan / span.length;
242 remainingSpace -= span[i];
243 }
244 }
245
246 // allocate remainingSpace to relative
247 if (remainingSpace > 0 && relativeTotals > 0) {
248 for (int i = 0; i < span.length; i++) {
249 if (relativeChildren[i] > 0) {
250 span[i] = (remainingSpace *
251 relativeChildren[i]) / relativeTotals;
252 }
253 }
254 } else if (remainingSpace > 0) {
255 // There are no relative columns and the space has been
256 // under- or overallocated. In this case, turn all the
257 // percentage and pixel specified columns to percentage
258 // columns based on the ratio of their pixel count to the
259 // total "virtual" size. (In the case of percentage columns,
260 // the pixel count would equal the specified percentage
261 // of the screen size.
262
263 // This action is in accordance with the HTML
264 // 4.0 spec (see section 8.3, the end of the discussion of
265 // the FRAMESET tag). The precedence of percentage and pixel
266 // specified columns is unclear (spec seems to indicate that
267 // they share priority, however, unspecified what happens when
268 // overallocation occurs.)
269
270 // addendum is that we behave similiar to netscape in that specified
271 // widths have precedance over percentage widths...
272
273 float vTotal = (float)(targetSpan - remainingSpace);
274 float[] tempPercents = new float[span.length];
275 remainingSpace = targetSpan;
276 for (int i = 0; i < span.length; i++) {
277 // ok we know what our total space is, and we know how large each
278 // column should be relative to each other... therefore we can use
279 // that relative information to deduce their percentages of a whole
280 // and then scale them appropriately for the correct size
281 tempPercents[i] = ((float)span[i] / vTotal) * 100.00f;
282 span[i] = (int) ( ((float)targetSpan * tempPercents[i]) / 100.00f);
283 remainingSpace -= span[i];
284 }
285
286
287 // this is for just in case there is something left over.. if there is we just
288 // add it one pixel at a time to the frames in order.. We shouldn't really ever get
289 // here and if we do it shouldn't be with more than 1 pixel, maybe two.
290 int i = 0;
291 while (remainingSpace != 0) {
292 if (remainingSpace < 0) {
293 span[i++]--;
294 remainingSpace++;
295 }
296 else {
297 span[i++]++;
298 remainingSpace--;
299 }
300
301 // just in case there are more pixels than frames...should never happen..
302 if (i == span.length)i = 0;
303 }
304 }
305 }
306
307 /*
308 * Users have been known to type things like "%25" and "25 %". Deal
309 * with it.
310 */
311 private int parseDigits(String mixedStr) {
312 int result = 0;
313 for (int i = 0; i < mixedStr.length(); i++) {
314 char ch = mixedStr.charAt(i);
315 if (Character.isDigit(ch)) {
316 result = (result * 10) + Character.digit(ch, 10);
317 }
318 }
319 return result;
320 }
321
322}