blob: 698db97f3a1c8a95284fb5e1e7da23a04de0a24c [file] [log] [blame]
/*
* 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.awt.event.*;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import javax.swing.text.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.event.*;
import java.util.*;
/**
* HiddenTagView subclasses EditableView to contain a JTextField showing
* the element name. When the textfield is edited the element name is
* reset. As this inherits from EditableView if the JTextComponent is
* not editable, the textfield will not be visible.
*
* @author Scott Violet
*/
class HiddenTagView extends EditableView implements DocumentListener {
HiddenTagView(Element e) {
super(e);
yAlign = 1;
}
protected Component createComponent() {
JTextField tf = new JTextField(getElement().getName());
Document doc = getDocument();
Font font;
if (doc instanceof StyledDocument) {
font = ((StyledDocument)doc).getFont(getAttributes());
tf.setFont(font);
}
else {
font = tf.getFont();
}
tf.getDocument().addDocumentListener(this);
updateYAlign(font);
// Create a panel to wrap the textfield so that the textfields
// laf border shows through.
JPanel panel = new JPanel(new BorderLayout());
panel.setBackground(null);
if (isEndTag()) {
panel.setBorder(EndBorder);
}
else {
panel.setBorder(StartBorder);
}
panel.add(tf);
return panel;
}
public float getAlignment(int axis) {
if (axis == View.Y_AXIS) {
return yAlign;
}
return 0.5f;
}
public float getMinimumSpan(int axis) {
if (axis == View.X_AXIS && isVisible()) {
// Default to preferred.
return Math.max(30, super.getPreferredSpan(axis));
}
return super.getMinimumSpan(axis);
}
public float getPreferredSpan(int axis) {
if (axis == View.X_AXIS && isVisible()) {
return Math.max(30, super.getPreferredSpan(axis));
}
return super.getPreferredSpan(axis);
}
public float getMaximumSpan(int axis) {
if (axis == View.X_AXIS && isVisible()) {
// Default to preferred.
return Math.max(30, super.getMaximumSpan(axis));
}
return super.getMaximumSpan(axis);
}
// DocumentListener methods
public void insertUpdate(DocumentEvent e) {
updateModelFromText();
}
public void removeUpdate(DocumentEvent e) {
updateModelFromText();
}
public void changedUpdate(DocumentEvent e) {
updateModelFromText();
}
// View method
public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f) {
if (!isSettingAttributes) {
setTextFromModel();
}
}
// local methods
void updateYAlign(Font font) {
Container c = getContainer();
FontMetrics fm = (c != null) ? c.getFontMetrics(font) :
Toolkit.getDefaultToolkit().getFontMetrics(font);
float h = fm.getHeight();
float d = fm.getDescent();
yAlign = (h > 0) ? (h - d) / h : 0;
}
void resetBorder() {
Component comp = getComponent();
if (comp != null) {
if (isEndTag()) {
((JPanel)comp).setBorder(EndBorder);
}
else {
((JPanel)comp).setBorder(StartBorder);
}
}
}
/**
* This resets the text on the text component we created to match
* that of the AttributeSet for the Element we represent.
* <p>If this is invoked on the event dispatching thread, this
* directly invokes <code>_setTextFromModel</code>, otherwise
* <code>SwingUtilities.invokeLater</code> is used to schedule execution
* of <code>_setTextFromModel</code>.
*/
void setTextFromModel() {
if (SwingUtilities.isEventDispatchThread()) {
_setTextFromModel();
}
else {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
_setTextFromModel();
}
});
}
}
/**
* This resets the text on the text component we created to match
* that of the AttributeSet for the Element we represent.
*/
void _setTextFromModel() {
Document doc = getDocument();
try {
isSettingAttributes = true;
if (doc instanceof AbstractDocument) {
((AbstractDocument)doc).readLock();
}
JTextComponent text = getTextComponent();
if (text != null) {
text.setText(getRepresentedText());
resetBorder();
Container host = getContainer();
if (host != null) {
preferenceChanged(this, true, true);
host.repaint();
}
}
}
finally {
isSettingAttributes = false;
if (doc instanceof AbstractDocument) {
((AbstractDocument)doc).readUnlock();
}
}
}
/**
* This copies the text from the text component we've created
* to the Element's AttributeSet we represent.
* <p>If this is invoked on the event dispatching thread, this
* directly invokes <code>_updateModelFromText</code>, otherwise
* <code>SwingUtilities.invokeLater</code> is used to schedule execution
* of <code>_updateModelFromText</code>.
*/
void updateModelFromText() {
if (!isSettingAttributes) {
if (SwingUtilities.isEventDispatchThread()) {
_updateModelFromText();
}
else {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
_updateModelFromText();
}
});
}
}
}
/**
* This copies the text from the text component we've created
* to the Element's AttributeSet we represent.
*/
void _updateModelFromText() {
Document doc = getDocument();
Object name = getElement().getAttributes().getAttribute
(StyleConstants.NameAttribute);
if ((name instanceof HTML.UnknownTag) &&
(doc instanceof StyledDocument)) {
SimpleAttributeSet sas = new SimpleAttributeSet();
JTextComponent textComponent = getTextComponent();
if (textComponent != null) {
String text = textComponent.getText();
isSettingAttributes = true;
try {
sas.addAttribute(StyleConstants.NameAttribute,
new HTML.UnknownTag(text));
((StyledDocument)doc).setCharacterAttributes
(getStartOffset(), getEndOffset() -
getStartOffset(), sas, false);
}
finally {
isSettingAttributes = false;
}
}
}
}
JTextComponent getTextComponent() {
Component comp = getComponent();
return (comp == null) ? null : (JTextComponent)((Container)comp).
getComponent(0);
}
String getRepresentedText() {
String retValue = getElement().getName();
return (retValue == null) ? "" : retValue;
}
boolean isEndTag() {
AttributeSet as = getElement().getAttributes();
if (as != null) {
Object end = as.getAttribute(HTML.Attribute.ENDTAG);
if (end != null && (end instanceof String) &&
((String)end).equals("true")) {
return true;
}
}
return false;
}
/** Alignment along the y axis, based on the font of the textfield. */
float yAlign;
/** Set to true when setting attributes. */
boolean isSettingAttributes;
// Following are for Borders that used for Unknown tags and comments.
//
// Border defines
static final int circleR = 3;
static final int circleD = circleR * 2;
static final int tagSize = 6;
static final int padding = 3;
static final Color UnknownTagBorderColor = Color.black;
static final Border StartBorder = new StartTagBorder();
static final Border EndBorder = new EndTagBorder();
static class StartTagBorder implements Border, Serializable {
public void paintBorder(Component c, Graphics g, int x, int y,
int width, int height) {
g.setColor(UnknownTagBorderColor);
x += padding;
width -= (padding * 2);
g.drawLine(x, y + circleR,
x, y + height - circleR);
g.drawArc(x, y + height - circleD - 1,
circleD, circleD, 180, 90);
g.drawArc(x, y, circleD, circleD, 90, 90);
g.drawLine(x + circleR, y, x + width - tagSize, y);
g.drawLine(x + circleR, y + height - 1,
x + width - tagSize, y + height - 1);
g.drawLine(x + width - tagSize, y,
x + width - 1, y + height / 2);
g.drawLine(x + width - tagSize, y + height,
x + width - 1, y + height / 2);
}
public Insets getBorderInsets(Component c) {
return new Insets(2, 2 + padding, 2, tagSize + 2 + padding);
}
public boolean isBorderOpaque() {
return false;
}
} // End of class HiddenTagView.StartTagBorder
static class EndTagBorder implements Border, Serializable {
public void paintBorder(Component c, Graphics g, int x, int y,
int width, int height) {
g.setColor(UnknownTagBorderColor);
x += padding;
width -= (padding * 2);
g.drawLine(x + width - 1, y + circleR,
x + width - 1, y + height - circleR);
g.drawArc(x + width - circleD - 1, y + height - circleD - 1,
circleD, circleD, 270, 90);
g.drawArc(x + width - circleD - 1, y, circleD, circleD, 0, 90);
g.drawLine(x + tagSize, y, x + width - circleR, y);
g.drawLine(x + tagSize, y + height - 1,
x + width - circleR, y + height - 1);
g.drawLine(x + tagSize, y,
x, y + height / 2);
g.drawLine(x + tagSize, y + height,
x, y + height / 2);
}
public Insets getBorderInsets(Component c) {
return new Insets(2, tagSize + 2 + padding, 2, 2 + padding);
}
public boolean isBorderOpaque() {
return false;
}
} // End of class HiddenTagView.EndTagBorder
} // End of HiddenTagView