1 /*
2 * Copyright 1998-2006 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any 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