1 /* 2 * Copyright (c) 1998, 2014, 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 @SuppressWarnings("deprecation") 133 void updateYAlign(Font font) { 134 Container c = getContainer(); 135 FontMetrics fm = (c != null) ? c.getFontMetrics(font) : 136 Toolkit.getDefaultToolkit().getFontMetrics(font); 137 float h = fm.getHeight(); 138 float d = fm.getDescent(); 139 yAlign = (h > 0) ? (h - d) / h : 0; 140 } 141 142 void resetBorder() { 143 Component comp = getComponent(); 144 145 if (comp != null) { 146 if (isEndTag()) { 147 ((JPanel)comp).setBorder(EndBorder); 148 } 149 else { 150 ((JPanel)comp).setBorder(StartBorder); 151 } 152 } 153 } 154 155 /** 156 * This resets the text on the text component we created to match 157 * that of the AttributeSet for the Element we represent. 158 * <p>If this is invoked on the event dispatching thread, this 159 * directly invokes <code>_setTextFromModel</code>, otherwise 160 * <code>SwingUtilities.invokeLater</code> is used to schedule execution 161 * of <code>_setTextFromModel</code>. 162 */ 163 void setTextFromModel() { 164 if (SwingUtilities.isEventDispatchThread()) { 165 _setTextFromModel(); 166 } 167 else { 168 SwingUtilities.invokeLater(new Runnable() { 169 public void run() { 170 _setTextFromModel(); 171 } 172 }); 173 } 174 } 175 176 /** 177 * This resets the text on the text component we created to match 178 * that of the AttributeSet for the Element we represent. 179 */ 180 void _setTextFromModel() { 181 Document doc = getDocument(); 182 try { 183 isSettingAttributes = true; 184 if (doc instanceof AbstractDocument) { 185 ((AbstractDocument)doc).readLock(); 186 } 187 JTextComponent text = getTextComponent(); 188 if (text != null) { 189 text.setText(getRepresentedText()); 190 resetBorder(); 191 Container host = getContainer(); 192 if (host != null) { 193 preferenceChanged(this, true, true); 194 host.repaint(); 195 } 196 } 197 } 198 finally { 199 isSettingAttributes = false; 200 if (doc instanceof AbstractDocument) { 201 ((AbstractDocument)doc).readUnlock(); 202 } 203 } 204 } 205 206 /** 207 * This copies the text from the text component we've created 208 * to the Element's AttributeSet we represent. 209 * <p>If this is invoked on the event dispatching thread, this 210 * directly invokes <code>_updateModelFromText</code>, otherwise 211 * <code>SwingUtilities.invokeLater</code> is used to schedule execution 212 * of <code>_updateModelFromText</code>. 213 */ 214 void updateModelFromText() { 215 if (!isSettingAttributes) { 216 if (SwingUtilities.isEventDispatchThread()) { 217 _updateModelFromText(); 218 } 219 else { 220 SwingUtilities.invokeLater(new Runnable() { 221 public void run() { 222 _updateModelFromText(); 223 } 224 }); 225 } 226 } 227 } 228 229 /** 230 * This copies the text from the text component we've created 231 * to the Element's AttributeSet we represent. 232 */ 233 void _updateModelFromText() { 234 Document doc = getDocument(); 235 Object name = getElement().getAttributes().getAttribute 236 (StyleConstants.NameAttribute); 237 if ((name instanceof HTML.UnknownTag) && 238 (doc instanceof StyledDocument)) { 239 SimpleAttributeSet sas = new SimpleAttributeSet(); 240 JTextComponent textComponent = getTextComponent(); 241 if (textComponent != null) { 242 String text = textComponent.getText(); 243 isSettingAttributes = true; 244 try { 245 sas.addAttribute(StyleConstants.NameAttribute, 246 new HTML.UnknownTag(text)); 247 ((StyledDocument)doc).setCharacterAttributes 248 (getStartOffset(), getEndOffset() - 249 getStartOffset(), sas, false); 250 } 251 finally { 252 isSettingAttributes = false; 253 } 254 } 255 } 256 } 257 258 JTextComponent getTextComponent() { 259 Component comp = getComponent(); 260 261 return (comp == null) ? null : (JTextComponent)((Container)comp). 262 getComponent(0); 263 } 264 265 String getRepresentedText() { 266 String retValue = getElement().getName(); 267 return (retValue == null) ? "" : retValue; 268 } 269 270 boolean isEndTag() { 271 AttributeSet as = getElement().getAttributes(); 272 if (as != null) { 273 Object end = as.getAttribute(HTML.Attribute.ENDTAG); 274 if (end != null && (end instanceof String) && 275 ((String)end).equals("true")) { 276 return true; 277 } 278 } 279 return false; 280 } 281 282 /** Alignment along the y axis, based on the font of the textfield. */ 283 float yAlign; 284 /** Set to true when setting attributes. */ 285 boolean isSettingAttributes; 286 287 288 // Following are for Borders that used for Unknown tags and comments. 289 // 290 // Border defines 291 static final int circleR = 3; 292 static final int circleD = circleR * 2; 293 static final int tagSize = 6; 294 static final int padding = 3; 295 static final Color UnknownTagBorderColor = Color.black; 296 static final Border StartBorder = new StartTagBorder(); 297 static final Border EndBorder = new EndTagBorder(); 298 299 @SuppressWarnings("serial") // Same-version serialization only 300 static class StartTagBorder implements Border, Serializable { 301 public void paintBorder(Component c, Graphics g, int x, int y, 302 int width, int height) { 303 g.setColor(UnknownTagBorderColor); 304 x += padding; 305 width -= (padding * 2); 306 g.drawLine(x, y + circleR, 307 x, y + height - circleR); 308 g.drawArc(x, y + height - circleD - 1, 309 circleD, circleD, 180, 90); 310 g.drawArc(x, y, circleD, circleD, 90, 90); 311 g.drawLine(x + circleR, y, x + width - tagSize, y); 312 g.drawLine(x + circleR, y + height - 1, 313 x + width - tagSize, y + height - 1); 314 315 g.drawLine(x + width - tagSize, y, 316 x + width - 1, y + height / 2); 317 g.drawLine(x + width - tagSize, y + height, 318 x + width - 1, y + height / 2); 319 } 320 321 public Insets getBorderInsets(Component c) { 322 return new Insets(2, 2 + padding, 2, tagSize + 2 + padding); 323 } 324 325 public boolean isBorderOpaque() { 326 return false; 327 } 328 } // End of class HiddenTagView.StartTagBorder 329 330 @SuppressWarnings("serial") // Same-version serialization only 331 static class EndTagBorder implements Border, Serializable { 332 public void paintBorder(Component c, Graphics g, int x, int y, 333 int width, int height) { 334 g.setColor(UnknownTagBorderColor); 335 x += padding; 336 width -= (padding * 2); 337 g.drawLine(x + width - 1, y + circleR, 338 x + width - 1, y + height - circleR); 339 g.drawArc(x + width - circleD - 1, y + height - circleD - 1, 340 circleD, circleD, 270, 90); 341 g.drawArc(x + width - circleD - 1, y, circleD, circleD, 0, 90); 342 g.drawLine(x + tagSize, y, x + width - circleR, y); 343 g.drawLine(x + tagSize, y + height - 1, 344 x + width - circleR, y + height - 1); 345 346 g.drawLine(x + tagSize, y, 347 x, y + height / 2); 348 g.drawLine(x + tagSize, y + height, 349 x, y + height / 2); 350 } 351 352 public Insets getBorderInsets(Component c) { 353 return new Insets(2, tagSize + 2 + padding, 2, 2 + padding); 354 } 355 356 public boolean isBorderOpaque() { 357 return false; 358 } 359 } // End of class HiddenTagView.EndTagBorder 360 361 362 } // End of HiddenTagView