blob: 68ca3f6179db2e7a836e000511d0140d6140dba9 [file] [log] [blame]
/*
* Copyright (c) 1998, 2003, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.swing.text.html;
import java.awt.*;
import java.util.*;
import javax.swing.*;
import javax.swing.text.*;
import javax.swing.event.*;
/**
* Implements a FrameSetView, intended to support the HTML
* <FRAMESET> tag. Supports the ROWS and COLS attributes.
*
* @author Sunita Mani
*
* Credit also to the hotjava browser engineers that
* worked on making the allocation of space algorithms
* conform to the HTML 4.0 standard and also be netscape
* compatible.
*
*/
class FrameSetView extends javax.swing.text.BoxView {
String[] children;
int[] percentChildren;
int[] absoluteChildren;
int[] relativeChildren;
int percentTotals;
int absoluteTotals;
int relativeTotals;
/**
* Constructs a FrameSetView for the given element.
*
* @param elem the element that this view is responsible for
*/
public FrameSetView(Element elem, int axis) {
super(elem, axis);
children = null;
}
/**
* Parses the ROW or COL attributes and returns
* an array of strings that represent the space
* distribution.
*
*/
private String[] parseRowColSpec(HTML.Attribute key) {
AttributeSet attributes = getElement().getAttributes();
String spec = "*";
if (attributes != null) {
if (attributes.getAttribute(key) != null) {
spec = (String)attributes.getAttribute(key);
}
}
StringTokenizer tokenizer = new StringTokenizer(spec, ",");
int nTokens = tokenizer.countTokens();
int n = getViewCount();
String[] items = new String[Math.max(nTokens, n)];
int i = 0;
for (; i < nTokens; i++) {
items[i] = tokenizer.nextToken().trim();
// As per the spec, 100% is the same as *
// hence the mapping.
//
if (items[i].equals("100%")) {
items[i] = "*";
}
}
// extend spec if we have more children than specified
// in ROWS or COLS attribute
for (; i < items.length; i++) {
items[i] = "*";
}
return items;
}
/**
* Initializes a number of internal state variables
* that store information about space allocation
* for the frames contained within the frameset.
*/
private void init() {
if (getAxis() == View.Y_AXIS) {
children = parseRowColSpec(HTML.Attribute.ROWS);
} else {
children = parseRowColSpec(HTML.Attribute.COLS);
}
percentChildren = new int[children.length];
relativeChildren = new int[children.length];
absoluteChildren = new int[children.length];
for (int i = 0; i < children.length; i++) {
percentChildren[i] = -1;
relativeChildren[i] = -1;
absoluteChildren[i] = -1;
if (children[i].endsWith("*")) {
if (children[i].length() > 1) {
relativeChildren[i] =
Integer.parseInt(children[i].substring(
0, children[i].length()-1));
relativeTotals += relativeChildren[i];
} else {
relativeChildren[i] = 1;
relativeTotals += 1;
}
} else if (children[i].indexOf('%') != -1) {
percentChildren[i] = parseDigits(children[i]);
percentTotals += percentChildren[i];
} else {
absoluteChildren[i] = Integer.parseInt(children[i]);
}
}
if (percentTotals > 100) {
for (int i = 0; i < percentChildren.length; i++) {
if (percentChildren[i] > 0) {
percentChildren[i] =
(percentChildren[i] * 100) / percentTotals;
}
}
percentTotals = 100;
}
}
/**
* Perform layout for the major axis of the box (i.e. the
* axis that it represents). The results of the layout should
* be placed in the given arrays which represent the allocations
* to the children along the major axis.
*
* @param targetSpan the total span given to the view, which
* whould be used to layout the children
* @param axis the axis being layed out
* @param offsets the offsets from the origin of the view for
* each of the child views; this is a return value and is
* filled in by the implementation of this method
* @param spans the span of each child view; this is a return
* value and is filled in by the implementation of this method
* @return the offset and span for each child view in the
* offsets and spans parameters
*/
protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets,
int[] spans) {
if (children == null) {
init();
}
SizeRequirements.calculateTiledPositions(targetSpan, null,
getChildRequests(targetSpan,
axis),
offsets, spans);
}
protected SizeRequirements[] getChildRequests(int targetSpan, int axis) {
int span[] = new int[children.length];
spread(targetSpan, span);
int n = getViewCount();
SizeRequirements[] reqs = new SizeRequirements[n];
for (int i = 0, sIndex = 0; i < n; i++) {
View v = getView(i);
if ((v instanceof FrameView) || (v instanceof FrameSetView)) {
reqs[i] = new SizeRequirements((int) v.getMinimumSpan(axis),
span[sIndex],
(int) v.getMaximumSpan(axis),
0.5f);
sIndex++;
} else {
int min = (int) v.getMinimumSpan(axis);
int pref = (int) v.getPreferredSpan(axis);
int max = (int) v.getMaximumSpan(axis);
float a = v.getAlignment(axis);
reqs[i] = new SizeRequirements(min, pref, max, a);
}
}
return reqs;
}
/**
* This method is responsible for returning in span[] the
* span for each child view along the major axis. it
* computes this based on the information that extracted
* from the value of the ROW/COL attribute.
*/
private void spread(int targetSpan, int span[]) {
if (targetSpan == 0) {
return;
}
int tempSpace = 0;
int remainingSpace = targetSpan;
// allocate the absolute's first, they have
// precedence
//
for (int i = 0; i < span.length; i++) {
if (absoluteChildren[i] > 0) {
span[i] = absoluteChildren[i];
remainingSpace -= span[i];
}
}
// then deal with percents.
//
tempSpace = remainingSpace;
for (int i = 0; i < span.length; i++) {
if (percentChildren[i] > 0 && tempSpace > 0) {
span[i] = (percentChildren[i] * tempSpace) / 100;
remainingSpace -= span[i];
} else if (percentChildren[i] > 0 && tempSpace <= 0) {
span[i] = targetSpan / span.length;
remainingSpace -= span[i];
}
}
// allocate remainingSpace to relative
if (remainingSpace > 0 && relativeTotals > 0) {
for (int i = 0; i < span.length; i++) {
if (relativeChildren[i] > 0) {
span[i] = (remainingSpace *
relativeChildren[i]) / relativeTotals;
}
}
} else if (remainingSpace > 0) {
// There are no relative columns and the space has been
// under- or overallocated. In this case, turn all the
// percentage and pixel specified columns to percentage
// columns based on the ratio of their pixel count to the
// total "virtual" size. (In the case of percentage columns,
// the pixel count would equal the specified percentage
// of the screen size.
// This action is in accordance with the HTML
// 4.0 spec (see section 8.3, the end of the discussion of
// the FRAMESET tag). The precedence of percentage and pixel
// specified columns is unclear (spec seems to indicate that
// they share priority, however, unspecified what happens when
// overallocation occurs.)
// addendum is that we behave similiar to netscape in that specified
// widths have precedance over percentage widths...
float vTotal = (float)(targetSpan - remainingSpace);
float[] tempPercents = new float[span.length];
remainingSpace = targetSpan;
for (int i = 0; i < span.length; i++) {
// ok we know what our total space is, and we know how large each
// column should be relative to each other... therefore we can use
// that relative information to deduce their percentages of a whole
// and then scale them appropriately for the correct size
tempPercents[i] = ((float)span[i] / vTotal) * 100.00f;
span[i] = (int) ( ((float)targetSpan * tempPercents[i]) / 100.00f);
remainingSpace -= span[i];
}
// this is for just in case there is something left over.. if there is we just
// add it one pixel at a time to the frames in order.. We shouldn't really ever get
// here and if we do it shouldn't be with more than 1 pixel, maybe two.
int i = 0;
while (remainingSpace != 0) {
if (remainingSpace < 0) {
span[i++]--;
remainingSpace++;
}
else {
span[i++]++;
remainingSpace--;
}
// just in case there are more pixels than frames...should never happen..
if (i == span.length)i = 0;
}
}
}
/*
* Users have been known to type things like "%25" and "25 %". Deal
* with it.
*/
private int parseDigits(String mixedStr) {
int result = 0;
for (int i = 0; i < mixedStr.length(); i++) {
char ch = mixedStr.charAt(i);
if (Character.isDigit(ch)) {
result = (result * 10) + Character.digit(ch, 10);
}
}
return result;
}
}