/*
* Copyright (c) 1998, 2006, 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 java.net.*;
import java.io.*;
import javax.swing.*;
import javax.swing.text.*;
import javax.swing.event.*;
import sun.swing.text.html.FrameEditorPaneTag;
/**
* Implements a FrameView, intended to support the HTML
* <FRAME> tag. Supports the frameborder, scrolling,
* marginwidth and marginheight attributes.
*
* @author Sunita Mani
*/
class FrameView extends ComponentView implements HyperlinkListener {
JEditorPane htmlPane;
JScrollPane scroller;
boolean editable;
float width;
float height;
URL src;
/** Set to true when the component has been created. */
private boolean createdComponent;
/**
* Creates a new Frame.
*
* @param elem the element to represent.
*/
public FrameView(Element elem) {
super(elem);
}
protected Component createComponent() {
Element elem = getElement();
AttributeSet attributes = elem.getAttributes();
String srcAtt = (String)attributes.getAttribute(HTML.Attribute.SRC);
if ((srcAtt != null) && (!srcAtt.equals(""))) {
try {
URL base = ((HTMLDocument)elem.getDocument()).getBase();
src = new URL(base, srcAtt);
htmlPane = new FrameEditorPane();
htmlPane.addHyperlinkListener(this);
JEditorPane host = getHostPane();
boolean isAutoFormSubmission = true;
if (host != null) {
htmlPane.setEditable(host.isEditable());
String charset = (String) host.getClientProperty("charset");
if (charset != null) {
htmlPane.putClientProperty("charset", charset);
}
HTMLEditorKit hostKit = (HTMLEditorKit)host.getEditorKit();
if (hostKit != null) {
isAutoFormSubmission = hostKit.isAutoFormSubmission();
}
}
htmlPane.setPage(src);
HTMLEditorKit kit = (HTMLEditorKit)htmlPane.getEditorKit();
if (kit != null) {
kit.setAutoFormSubmission(isAutoFormSubmission);
}
Document doc = htmlPane.getDocument();
if (doc instanceof HTMLDocument) {
((HTMLDocument)doc).setFrameDocumentState(true);
}
setMargin();
createScrollPane();
setBorder();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
}
}
createdComponent = true;
return scroller;
}
JEditorPane getHostPane() {
Container c = getContainer();
while ((c != null) && ! (c instanceof JEditorPane)) {
c = c.getParent();
}
return (JEditorPane) c;
}
/**
* Sets the parent view for the FrameView.
* Also determines if the FrameView should be editable
* or not based on whether the JTextComponent that
* contains it is editable.
*
* @param parent View
*/
public void setParent(View parent) {
if (parent != null) {
JTextComponent t = (JTextComponent)parent.getContainer();
editable = t.isEditable();
}
super.setParent(parent);
}
/**
* Also determines if the FrameView should be editable
* or not based on whether the JTextComponent that
* contains it is editable. And then proceeds to call
* the superclass to do the paint().
*
* @param parent View
* @see text.ComponentView#paint
*/
public void paint(Graphics g, Shape allocation) {
Container host = getContainer();
if (host != null && htmlPane != null &&
htmlPane.isEditable() != ((JTextComponent)host).isEditable()) {
editable = ((JTextComponent)host).isEditable();
htmlPane.setEditable(editable);
}
super.paint(g, allocation);
}
/**
* If the marginwidth or marginheight attributes have been specified,
* then the JEditorPane's margin's are set to the new values.
*/
private void setMargin() {
int margin = 0;
Insets in = htmlPane.getMargin();
Insets newInsets;
boolean modified = false;
AttributeSet attributes = getElement().getAttributes();
String marginStr = (String)attributes.getAttribute(HTML.Attribute.MARGINWIDTH);
if ( in != null) {
newInsets = new Insets(in.top, in.left, in.right, in.bottom);
} else {
newInsets = new Insets(0,0,0,0);
}
if (marginStr != null) {
margin = Integer.parseInt(marginStr);
if (margin > 0) {
newInsets.left = margin;
newInsets.right = margin;
modified = true;
}
}
marginStr = (String)attributes.getAttribute(HTML.Attribute.MARGINHEIGHT);
if (marginStr != null) {
margin = Integer.parseInt(marginStr);
if (margin > 0) {
newInsets.top = margin;
newInsets.bottom = margin;
modified = true;
}
}
if (modified) {
htmlPane.setMargin(newInsets);
}
}
/**
* If the frameborder attribute has been specified, either in the frame,
* or by the frames enclosing frameset, the JScrollPane's setBorder()
* method is invoked to achieve the desired look.
*/
private void setBorder() {
AttributeSet attributes = getElement().getAttributes();
String frameBorder = (String)attributes.getAttribute(HTML.Attribute.FRAMEBORDER);
if ((frameBorder != null) &&
(frameBorder.equals("no") || frameBorder.equals("0"))) {
// make invisible borders.
scroller.setBorder(null);
}
}
/**
* This method creates the JScrollPane. The scrollbar policy is determined by
* the scrolling attribute. If not defined, the default is "auto" which
* maps to the scrollbar's being displayed as needed.
*/
private void createScrollPane() {
AttributeSet attributes = getElement().getAttributes();
String scrolling = (String)attributes.getAttribute(HTML.Attribute.SCROLLING);
if (scrolling == null) {
scrolling = "auto";
}
if (!scrolling.equals("no")) {
if (scrolling.equals("yes")) {
scroller = new JScrollPane(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
} else {
// scrollbars will be displayed if needed
//
scroller = new JScrollPane();
}
} else {
scroller = new JScrollPane(JScrollPane.VERTICAL_SCROLLBAR_NEVER,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
}
JViewport vp = scroller.getViewport();
vp.add(htmlPane);
vp.setBackingStoreEnabled(true);
scroller.setMinimumSize(new Dimension(5,5));
scroller.setMaximumSize(new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE));
}
/**
* Finds the outermost FrameSetView. It then
* returns that FrameSetView's container.
*/
JEditorPane getOutermostJEditorPane() {
View parent = getParent();
FrameSetView frameSetView = null;
while (parent != null) {
if (parent instanceof FrameSetView) {
frameSetView = (FrameSetView)parent;
}
parent = parent.getParent();
}
if (frameSetView != null) {
return (JEditorPane)frameSetView.getContainer();
}
return null;
}
/**
* Returns true if this frame is contained within
* a nested frameset.
*/
private boolean inNestedFrameSet() {
FrameSetView parent = (FrameSetView)getParent();
return (parent.getParent() instanceof FrameSetView);
}
/**
* Notification of a change relative to a
* hyperlink. This method searches for the outermost
* JEditorPane, and then fires an HTMLFrameHyperlinkEvent
* to that frame. In addition, if the target is _parent,
* and there is not nested framesets then the target is
* reset to _top. If the target is _top, in addition to
* firing the event to the outermost JEditorPane, this
* method also invokes the setPage() method and explicitly
* replaces the current document with the destination url.
*
* @param HyperlinkEvent
*/
public void hyperlinkUpdate(HyperlinkEvent evt) {
JEditorPane c = getOutermostJEditorPane();
if (c == null) {
return;
}
if (!(evt instanceof HTMLFrameHyperlinkEvent)) {
c.fireHyperlinkUpdate(evt);
return;
}
HTMLFrameHyperlinkEvent e = (HTMLFrameHyperlinkEvent)evt;
if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
String target = e.getTarget();
String postTarget = target;
if (target.equals("_parent") && !inNestedFrameSet()){
target = "_top";
}
if (evt instanceof FormSubmitEvent) {
HTMLEditorKit kit = (HTMLEditorKit)c.getEditorKit();
if (kit != null && kit.isAutoFormSubmission()) {
if (target.equals("_top")) {
try {
movePostData(c, postTarget);
c.setPage(e.getURL());
} catch (IOException ex) {
// Need a way to handle exceptions
}
} else {
HTMLDocument doc = (HTMLDocument)c.getDocument();
doc.processHTMLFrameHyperlinkEvent(e);
}
} else {
c.fireHyperlinkUpdate(evt);
}
return;
}
if (target.equals("_top")) {
try {
c.setPage(e.getURL());
} catch (IOException ex) {
// Need a way to handle exceptions
// ex.printStackTrace();
}
}
if (!c.isEditable()) {
c.fireHyperlinkUpdate(new HTMLFrameHyperlinkEvent(c,
e.getEventType(),
e.getURL(),
e.getDescription(),
getElement(),
e.getInputEvent(),
target));
}
}
}
/**
* Gives notification from the document that attributes were changed
* in a location that this view is responsible for. Currently this view
* handles changes to its SRC attribute.
*
* @param e the change information from the associated document
* @param a the current allocation of the view
* @param f the factory to use to rebuild if the view has children
*
*/
public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f) {
Element elem = getElement();
AttributeSet attributes = elem.getAttributes();
URL oldPage = src;
String srcAtt = (String)attributes.getAttribute(HTML.Attribute.SRC);
URL base = ((HTMLDocument)elem.getDocument()).getBase();
try {
if (!createdComponent) {
return;
}
Object postData = movePostData(htmlPane, null);
src = new URL(base, srcAtt);
if (oldPage.equals(src) && (src.getRef() == null) && (postData == null)) {
return;
}
htmlPane.setPage(src);
Document newDoc = htmlPane.getDocument();
if (newDoc instanceof HTMLDocument) {
((HTMLDocument)newDoc).setFrameDocumentState(true);
}
} catch (MalformedURLException e1) {
// Need a way to handle exceptions
//e1.printStackTrace();
} catch (IOException e2) {
// Need a way to handle exceptions
//e2.printStackTrace();
}
}
/**
* Move POST data from temporary storage into the target document property.
*
* @return the POST data or null if no data found
*/
private Object movePostData(JEditorPane targetPane, String frameName) {
Object postData = null;
JEditorPane p = getOutermostJEditorPane();
if (p != null) {
if (frameName == null) {
frameName = (String) getElement().getAttributes().getAttribute(
HTML.Attribute.NAME);
}
if (frameName != null) {
String propName = FormView.PostDataProperty + "." + frameName;
Document d = p.getDocument();
postData = d.getProperty(propName);
if (postData != null) {
targetPane.getDocument().putProperty(
FormView.PostDataProperty, postData);
d.putProperty(propName, null);
}
}
}
return postData;
}
/**
* Determines the minimum span for this view along an
* axis.
*
* @param axis may be either <code>View.X_AXIS</code> or
* <code>View.Y_AXIS</code>
* @return the preferred span; given that we do not
* support resizing of frames, the minimum span returned
* is the same as the preferred span
*
*/
public float getMinimumSpan(int axis) {
return 5;
}
/**
* Determines the maximum span for this view along an
* axis.
*
* @param axis may be either <code>View.X_AXIS</code> or
* <code>View.Y_AXIS</code>
* @return the preferred span; given that we do not
* support resizing of frames, the maximum span returned
* is the same as the preferred span
*
*/
public float getMaximumSpan(int axis) {
return Integer.MAX_VALUE;
}
/** Editor pane rendering frame of HTML document
* It uses the same editor kits classes as outermost JEditorPane
*/
class FrameEditorPane extends JEditorPane implements FrameEditorPaneTag {
public EditorKit getEditorKitForContentType(String type) {
EditorKit editorKit = super.getEditorKitForContentType(type);
JEditorPane outerMostJEditorPane = null;
if ((outerMostJEditorPane = getOutermostJEditorPane()) != null) {
EditorKit inheritedEditorKit = outerMostJEditorPane.getEditorKitForContentType(type);
if (! editorKit.getClass().equals(inheritedEditorKit.getClass())) {
editorKit = (EditorKit) inheritedEditorKit.clone();
setEditorKitForContentType(type, editorKit);
}
}
return editorKit;
}
FrameView getFrameView() {
return FrameView.this;
}
}
}