1 /* 2 * Copyright (c) 1998, 2006, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package javax.swing.text.html; 26 27 import java.awt.*; 28 import java.awt.event.*; 29 import java.io.*; 30 import java.net.MalformedURLException; 31 import java.net.URL; 32 import javax.swing.text.*; 33 import javax.swing.*; 34 import javax.swing.border.*; 35 import javax.swing.event.*; 36 import java.util.*; 37 38 /** 39 * HiddenTagView subclasses EditableView to contain a JTextField showing 40 * the element name. When the textfield is edited the element name is 41 * reset. As this inherits from EditableView if the JTextComponent is 42 * not editable, the textfield will not be visible. 43 * 44 * @author Scott Violet 45 */ 46 class HiddenTagView extends EditableView implements DocumentListener { 47 HiddenTagView(Element e) { 48 super(e); 49 yAlign = 1; 50 } 51 52 protected Component createComponent() { 53 JTextField tf = new JTextField(getElement().getName()); 54 Document doc = getDocument(); 55 Font font; 56 if (doc instanceof StyledDocument) { 57 font = ((StyledDocument)doc).getFont(getAttributes()); 58 tf.setFont(font); 59 } 60 else { 61 font = tf.getFont(); 62 } 63 tf.getDocument().addDocumentListener(this); 64 updateYAlign(font); 65 66 // Create a panel to wrap the textfield so that the textfields 67 // laf border shows through. 68 JPanel panel = new JPanel(new BorderLayout()); 69 panel.setBackground(null); 70 if (isEndTag()) { 71 panel.setBorder(EndBorder); 72 } 73 else { 74 panel.setBorder(StartBorder); 75 } 76 panel.add(tf); 77 return panel; 78 } 79 80 public float getAlignment(int axis) { 81 if (axis == View.Y_AXIS) { 82 return yAlign; 83 } 84 return 0.5f; 85 } 86 87 public float getMinimumSpan(int axis) { 88 if (axis == View.X_AXIS && isVisible()) { 89 // Default to preferred. 90 return Math.max(30, super.getPreferredSpan(axis)); 91 } 92 return super.getMinimumSpan(axis); 93 } 94 95 public float getPreferredSpan(int axis) { 96 if (axis == View.X_AXIS && isVisible()) { 97 return Math.max(30, super.getPreferredSpan(axis)); 98 } 99 return super.getPreferredSpan(axis); 100 } 101 102 public float getMaximumSpan(int axis) { 103 if (axis == View.X_AXIS && isVisible()) { 104 // Default to preferred. 105 return Math.max(30, super.getMaximumSpan(axis)); 106 } 107 return super.getMaximumSpan(axis); 108 } 109 110 // DocumentListener methods 111 public void insertUpdate(DocumentEvent e) { 112 updateModelFromText(); 113 } 114 115 public void removeUpdate(DocumentEvent e) { 116 updateModelFromText(); 117 } 118 119 public void changedUpdate(DocumentEvent e) { 120 updateModelFromText(); 121 } 122 123 // View method 124 public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f) { 125 if (!isSettingAttributes) { 126 setTextFromModel(); 127 } 128 } 129 130 // local methods 131 132 void updateYAlign(Font font) { 133 Container c = getContainer(); 134 FontMetrics fm = (c != null) ? c.getFontMetrics(font) : 135 Toolkit.getDefaultToolkit().getFontMetrics(font); 136 float h = fm.getHeight(); 137 float d = fm.getDescent(); 138 yAlign = (h > 0) ? (h - d) / h : 0; 139 } 140 141 void resetBorder() { 142 Component comp = getComponent(); 143 144 if (comp != null) { 145 if (isEndTag()) { 146 ((JPanel)comp).setBorder(EndBorder); 147 } 148 else { 149 ((JPanel)comp).setBorder(StartBorder); 150 } 151 } 152 } 153 154 /** 155 * This resets the text on the text component we created to match 156 * that of the AttributeSet for the Element we represent. 157 * <p>If this is invoked on the event dispatching thread, this 158 * directly invokes <code>_setTextFromModel</code>, otherwise 159 * <code>SwingUtilities.invokeLater</code> is used to schedule execution 160 * of <code>_setTextFromModel</code>. 161 */ 162 void setTextFromModel() { 163 if (SwingUtilities.isEventDispatchThread()) { 164 _setTextFromModel(); 165 } 166 else { 167 SwingUtilities.invokeLater(new Runnable() { 168 public void run() { 169 _setTextFromModel(); 170 } 171 }); 172 } 173 } 174 175 /** 176 * This resets the text on the text component we created to match 177 * that of the AttributeSet for the Element we represent. 178 */ 179 void _setTextFromModel() { 180 Document doc = getDocument(); 181 try { 182 isSettingAttributes = true; 183 if (doc instanceof AbstractDocument) { 184 ((AbstractDocument)doc).readLock(); 185 } 186 JTextComponent text = getTextComponent(); 187 if (text != null) { 188 text.setText(getRepresentedText()); 189 resetBorder(); 190 Container host = getContainer(); 191 if (host != null) { 192 preferenceChanged(this, true, true); 193 host.repaint(); 194 } 195 } 196 } 197 finally { 198 isSettingAttributes = false; 199 if (doc instanceof AbstractDocument) { 200 ((AbstractDocument)doc).readUnlock(); 201 } 202 } 203 } 204 205 /** 206 * This copies the text from the text component we've created 207 * to the Element's AttributeSet we represent. 208 * <p>If this is invoked on the event dispatching thread, this 209 * directly invokes <code>_updateModelFromText</code>, otherwise 210 * <code>SwingUtilities.invokeLater</code> is used to schedule execution 211 * of <code>_updateModelFromText</code>. 212 */ 213 void updateModelFromText() { 214 if (!isSettingAttributes) { 215 if (SwingUtilities.isEventDispatchThread()) { 216 _updateModelFromText(); 217 } 218 else { 219 SwingUtilities.invokeLater(new Runnable() { 220 public void run() { 221 _updateModelFromText(); 222 } 223 }); 224 } 225 } 226 } 227 228 /** 229 * This copies the text from the text component we've created 230 * to the Element's AttributeSet we represent. 231 */ 232 void _updateModelFromText() { 233 Document doc = getDocument(); 234 Object name = getElement().getAttributes().getAttribute 235 (StyleConstants.NameAttribute); 236 if ((name instanceof HTML.UnknownTag) && 237 (doc instanceof StyledDocument)) { 238 SimpleAttributeSet sas = new SimpleAttributeSet(); 239 JTextComponent textComponent = getTextComponent(); 240 if (textComponent != null) { 241 String text = textComponent.getText(); 242 isSettingAttributes = true; 243 try { 244 sas.addAttribute(StyleConstants.NameAttribute, 245 new HTML.UnknownTag(text)); 246 ((StyledDocument)doc).setCharacterAttributes 247 (getStartOffset(), getEndOffset() - 248 getStartOffset(), sas, false); 249 } 250 finally { 251 isSettingAttributes = false; 252 } 253 } 254 } 255 } 256 257 JTextComponent getTextComponent() { 258 Component comp = getComponent(); 259 260 return (comp == null) ? null : (JTextComponent)((Container)comp). 261 getComponent(0); 262 } 263 264 String getRepresentedText() { 265 String retValue = getElement().getName(); 266 return (retValue == null) ? "" : retValue; 267 } 268 269 boolean isEndTag() { 270 AttributeSet as = getElement().getAttributes(); 271 if (as != null) { 272 Object end = as.getAttribute(HTML.Attribute.ENDTAG); 273 if (end != null && (end instanceof String) && 274 ((String)end).equals("true")) { 275 return true; 276 } 277 } 278 return false; 279 } 280 281 /** Alignment along the y axis, based on the font of the textfield. */ 282 float yAlign; 283 /** Set to true when setting attributes. */ 284 boolean isSettingAttributes; 285 286 287 // Following are for Borders that used for Unknown tags and comments. 288 // 289 // Border defines 290 static final int circleR = 3; 291 static final int circleD = circleR * 2; 292 static final int tagSize = 6; 293 static final int padding = 3; 294 static final Color UnknownTagBorderColor = Color.black; 295 static final Border StartBorder = new StartTagBorder(); 296 static final Border EndBorder = new EndTagBorder(); 297 298 299 static class StartTagBorder implements Border, Serializable { 300 public void paintBorder(Component c, Graphics g, int x, int y, 301 int width, int height) { 302 g.setColor(UnknownTagBorderColor); 303 x += padding; 304 width -= (padding * 2); 305 g.drawLine(x, y + circleR, 306 x, y + height - circleR); 307 g.drawArc(x, y + height - circleD - 1, 308 circleD, circleD, 180, 90); 309 g.drawArc(x, y, circleD, circleD, 90, 90); 310 g.drawLine(x + circleR, y, x + width - tagSize, y); 311 g.drawLine(x + circleR, y + height - 1, 312 x + width - tagSize, y + height - 1); 313 314 g.drawLine(x + width - tagSize, y, 315 x + width - 1, y + height / 2); 316 g.drawLine(x + width - tagSize, y + height, 317 x + width - 1, y + height / 2); 318 } 319 320 public Insets getBorderInsets(Component c) { 321 return new Insets(2, 2 + padding, 2, tagSize + 2 + padding); 322 } 323 324 public boolean isBorderOpaque() { 325 return false; 326 } 327 } // End of class HiddenTagView.StartTagBorder 328 329 330 static class EndTagBorder implements Border, Serializable { 331 public void paintBorder(Component c, Graphics g, int x, int y, 332 int width, int height) { 333 g.setColor(UnknownTagBorderColor); 334 x += padding; 335 width -= (padding * 2); 336 g.drawLine(x + width - 1, y + circleR, 337 x + width - 1, y + height - circleR); 338 g.drawArc(x + width - circleD - 1, y + height - circleD - 1, 339 circleD, circleD, 270, 90); 340 g.drawArc(x + width - circleD - 1, y, circleD, circleD, 0, 90); 341 g.drawLine(x + tagSize, y, x + width - circleR, y); 342 g.drawLine(x + tagSize, y + height - 1, 343 x + width - circleR, y + height - 1); 344 345 g.drawLine(x + tagSize, y, 346 x, y + height / 2); 347 g.drawLine(x + tagSize, y + height, 348 x, y + height / 2); 349 } 350 351 public Insets getBorderInsets(Component c) { 352 return new Insets(2, tagSize + 2 + padding, 2, 2 + padding); 353 } 354 355 public boolean isBorderOpaque() { 356 return false; 357 } 358 } // End of class HiddenTagView.EndTagBorder 359 360 361 } // End of HiddenTagView