/*
* Copyright (c) 1998, 2018, 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.
/**
* 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.
Params: - elem – the element that this view is responsible for
/**
* 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.
/**
* 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.
/**
* 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).trim());
relativeTotals += relativeChildren[i];
} else {
relativeChildren[i] = 1;
relativeTotals += 1;
}
} else if (children[i].indexOf('%') != -1) {
percentChildren[i] = parseDigits(children[i]);
percentTotals += percentChildren[i];
} else {
String value = children[i].toLowerCase();
if (value.endsWith("px")) {
value = value.substring(0, value.length()-2).trim();
}
absoluteChildren[i] = Integer.parseInt(value);
}
}
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.
Params: - targetSpan – the total span given to the view, which
would be used to layout the children
- axis – the axis being layed out
- 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
- spans – the span of each child view; this is a return
value and is filled in by the implementation of this method
Returns: the offset and span for each child view in the
offsets and spans parameters
/**
* 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
* would 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.
/**
* 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 similar 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;
}
}