org.cobraparser.html.gui.FrameSetPanel Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of Cobra Show documentation
Show all versions of Cobra Show documentation
Cobra is the rendering engine designed for LoboBrowser
/*
GNU LESSER GENERAL PUBLIC LICENSE
Copyright (C) 2006 The Lobo Project
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Contact info: [email protected]
*/
/*
* Created on Jan 29, 2006
*/
package org.cobraparser.html.gui;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Insets;
import java.util.ArrayList;
import java.util.StringTokenizer;
import java.util.logging.Logger;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JSplitPane;
import org.cobraparser.html.BrowserFrame;
import org.cobraparser.html.HtmlRendererContext;
import org.cobraparser.html.domimpl.FrameNode;
import org.cobraparser.html.domimpl.HTMLElementImpl;
import org.cobraparser.html.domimpl.NodeImpl;
import org.cobraparser.html.renderer.NodeRenderer;
import org.cobraparser.html.style.HtmlLength;
import org.cobraparser.util.gui.WrapperLayout;
/**
* A Swing panel used to render FRAMESETs only. It is used by {@link HtmlPanel}
* when a document is determined to be a FRAMESET.
*
* @see HtmlPanel
* @see HtmlBlockPanel
*/
public class FrameSetPanel extends JComponent implements NodeRenderer {
private static final long serialVersionUID = 5048031593959987324L;
private static final Logger logger = Logger.getLogger(FrameSetPanel.class.getName());
public FrameSetPanel() {
super();
this.setLayout(WrapperLayout.getInstance());
// TODO: This should be a temporary preferred size
this.setPreferredSize(new Dimension(600, 400));
}
private static HtmlLength[] getLengths(final String spec) {
if (spec == null) {
return new HtmlLength[] { new HtmlLength("1*") };
}
final StringTokenizer tok = new StringTokenizer(spec, ",");
final ArrayList lengths = new ArrayList<>();
while (tok.hasMoreTokens()) {
final String token = tok.nextToken().trim();
try {
lengths.add(new HtmlLength(token));
} catch (final Exception err) {
logger.warning("Frame rows or cols value [" + spec + "] is invalid.");
}
}
return lengths.toArray(HtmlLength.EMPTY_ARRAY);
}
private static HTMLElementImpl[] getSubFrames(final HTMLElementImpl parent) {
final NodeImpl[] children = parent.getChildrenArray();
final ArrayList subFrames = new ArrayList<>();
for (final NodeImpl child : children) {
if (child instanceof HTMLElementImpl) {
final String nodeName = child.getNodeName();
if ("FRAME".equalsIgnoreCase(nodeName) || "FRAMESET".equalsIgnoreCase(nodeName)) {
subFrames.add(child);
}
}
}
return subFrames.toArray(new HTMLElementImpl[0]);
}
private HTMLElementImpl rootNode;
/**
* Sets the FRAMESET node and invalidates the component so it can be rendered
* immediately in the GUI thread.
*/
public void setRootNode(final NodeImpl node) {
// Method expected to be called in the GUI thread.
if (!(node instanceof HTMLElementImpl)) {
throw new IllegalArgumentException("node=" + node);
}
final HTMLElementImpl element = (HTMLElementImpl) node;
this.rootNode = element;
final HtmlRendererContext context = element.getHtmlRendererContext();
this.htmlContext = context;
this.domInvalid = true;
this.invalidate();
this.validateAll();
this.repaint();
}
protected void validateAll() {
Component toValidate = this;
for (;;) {
final Container parent = toValidate.getParent();
if ((parent == null) || parent.isValid()) {
break;
}
toValidate = parent;
}
toValidate.validate();
}
public final void processDocumentNotifications(final DocumentNotification[] notifications) {
// Called in the GUI thread.
if (notifications.length > 0) {
// Not very efficient, but it will do.
this.domInvalid = true;
this.invalidate();
if (this.isVisible()) {
this.validate();
this.repaint();
}
}
}
private HtmlRendererContext htmlContext;
private Component[] frameComponents;
private boolean domInvalid = true;
@Override
public void setBounds(final int x, final int y, final int w, final int h) {
super.setBounds(x, y, w, h);
}
/**
* This method is invoked by AWT in the GUI thread to lay out the component.
* This implementation is an override.
*/
@Override
public void doLayout() {
if (this.domInvalid) {
this.domInvalid = false;
this.removeAll();
final HtmlRendererContext context = this.htmlContext;
if (context != null) {
final HTMLElementImpl element = this.rootNode;
final String rows = element.getAttribute("rows");
final String cols = element.getAttribute("cols");
final HtmlLength[] rowLengths = FrameSetPanel.getLengths(rows);
final HtmlLength[] colLengths = FrameSetPanel.getLengths(cols);
final HTMLElementImpl[] subframes = FrameSetPanel.getSubFrames(element);
final Component[] frameComponents = new Component[subframes.length];
this.frameComponents = frameComponents;
for (int i = 0; i < subframes.length; i++) {
final HTMLElementImpl frameElement = subframes[i];
if ((frameElement != null) && "FRAMESET".equalsIgnoreCase(frameElement.getTagName())) {
final FrameSetPanel fsp = new FrameSetPanel();
fsp.setRootNode(frameElement);
frameComponents[i] = fsp;
} else {
if (frameElement instanceof FrameNode) {
final FrameNode frameNode = (FrameNode) frameElement;
if (frameNode.getBrowserFrame() == null) {
final BrowserFrame frame = context.createBrowserFrame();
frameNode.setBrowserFrame(frame);
frameComponents[i] = frame.getComponent();
} else {
frameComponents[i] = frameNode.getBrowserFrame().getComponent();
}
} else {
frameComponents[i] = new JPanel();
}
}
}
final HtmlLength[] rhl = rowLengths;
final HtmlLength[] chl = colLengths;
final Component[] fc = this.frameComponents;
if ((rhl != null) && (chl != null) && (fc != null)) {
final Dimension size = this.getSize();
final Insets insets = this.getInsets();
final int width = size.width - insets.left - insets.right;
final int height = size.height - insets.left - insets.right;
final int[] absColLengths = getAbsoluteLengths(chl, width);
final int[] absRowLengths = getAbsoluteLengths(rhl, height);
this.add(this.getSplitPane(this.htmlContext, absColLengths, 0, absColLengths.length, absRowLengths, 0, absRowLengths.length, fc));
}
}
}
super.doLayout();
}
private static int[] getAbsoluteLengths(final HtmlLength[] htmlLengths, final int totalSize) {
final int[] absLengths = new int[htmlLengths.length];
int totalSizeNonMulti = 0;
int sumMulti = 0;
for (int i = 0; i < htmlLengths.length; i++) {
final HtmlLength htmlLength = htmlLengths[i];
final int lengthType = htmlLength.getLengthType();
if (lengthType == HtmlLength.PIXELS) {
final int absLength = htmlLength.getRawValue();
totalSizeNonMulti += absLength;
absLengths[i] = absLength;
} else if (lengthType == HtmlLength.LENGTH) {
final int absLength = htmlLength.getLength(totalSize);
totalSizeNonMulti += absLength;
absLengths[i] = absLength;
} else {
sumMulti += htmlLength.getRawValue();
}
}
final int remaining = totalSize - totalSizeNonMulti;
if ((remaining > 0) && (sumMulti > 0)) {
for (int i = 0; i < htmlLengths.length; i++) {
final HtmlLength htmlLength = htmlLengths[i];
if (htmlLength.getLengthType() == HtmlLength.MULTI_LENGTH) {
final int absLength = (remaining * htmlLength.getRawValue()) / sumMulti;
absLengths[i] = absLength;
}
}
}
return absLengths;
}
private Component getSplitPane(final HtmlRendererContext context, final int[] colLengths, final int firstCol, final int numCols,
final int[] rowLengths, final int firstRow,
final int numRows, final Component[] frameComponents) {
if (numCols == 1) {
final int frameindex = (colLengths.length * firstRow) + firstCol;
final Component topComponent = frameindex < frameComponents.length ? frameComponents[frameindex] : null;
if (numRows == 1) {
return topComponent;
} else {
final Component bottomComponent = this.getSplitPane(context, colLengths, firstCol, numCols, rowLengths, firstRow + 1, numRows - 1,
frameComponents);
final JSplitPane sp = new JSplitPane(JSplitPane.VERTICAL_SPLIT, topComponent, bottomComponent);
sp.setDividerLocation(rowLengths[firstRow]);
return sp;
}
} else {
final Component rightComponent = this.getSplitPane(context, colLengths, firstCol + 1, numCols - 1, rowLengths, firstRow, numRows,
frameComponents);
final Component leftComponent = this.getSplitPane(context, colLengths, firstCol, 1, rowLengths, firstRow, numRows, frameComponents);
final JSplitPane sp = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leftComponent, rightComponent);
sp.setDividerLocation(colLengths[firstCol]);
return sp;
}
}
}