001 /*
002 * Copyright 1998-2006 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025 package javax.swing.text.html;
026
027 import java.awt.*;
028 import java.awt.event.*;
029 import java.io.*;
030 import java.net.MalformedURLException;
031 import java.net.URL;
032 import javax.swing.text.*;
033 import javax.swing.*;
034 import javax.swing.border.*;
035 import javax.swing.event.*;
036 import java.util.*;
037
038 /**
039 * HiddenTagView subclasses EditableView to contain a JTextField showing
040 * the element name. When the textfield is edited the element name is
041 * reset. As this inherits from EditableView if the JTextComponent is
042 * not editable, the textfield will not be visible.
043 *
044 * @author Scott Violet
045 * @version 1.23, 05/05/07
046 */
047 class HiddenTagView extends EditableView implements DocumentListener {
048 HiddenTagView(Element e) {
049 super (e);
050 yAlign = 1;
051 }
052
053 protected Component createComponent() {
054 JTextField tf = new JTextField(getElement().getName());
055 Document doc = getDocument();
056 Font font;
057 if (doc instanceof StyledDocument) {
058 font = ((StyledDocument) doc).getFont(getAttributes());
059 tf.setFont(font);
060 } else {
061 font = tf.getFont();
062 }
063 tf.getDocument().addDocumentListener(this );
064 updateYAlign(font);
065
066 // Create a panel to wrap the textfield so that the textfields
067 // laf border shows through.
068 JPanel panel = new JPanel(new BorderLayout());
069 panel.setBackground(null);
070 if (isEndTag()) {
071 panel.setBorder(EndBorder);
072 } else {
073 panel.setBorder(StartBorder);
074 }
075 panel.add(tf);
076 return panel;
077 }
078
079 public float getAlignment(int axis) {
080 if (axis == View.Y_AXIS) {
081 return yAlign;
082 }
083 return 0.5f;
084 }
085
086 public float getMinimumSpan(int axis) {
087 if (axis == View.X_AXIS && isVisible()) {
088 // Default to preferred.
089 return Math.max(30, super .getPreferredSpan(axis));
090 }
091 return super .getMinimumSpan(axis);
092 }
093
094 public float getPreferredSpan(int axis) {
095 if (axis == View.X_AXIS && isVisible()) {
096 return Math.max(30, super .getPreferredSpan(axis));
097 }
098 return super .getPreferredSpan(axis);
099 }
100
101 public float getMaximumSpan(int axis) {
102 if (axis == View.X_AXIS && isVisible()) {
103 // Default to preferred.
104 return Math.max(30, super .getMaximumSpan(axis));
105 }
106 return super .getMaximumSpan(axis);
107 }
108
109 // DocumentListener methods
110 public void insertUpdate(DocumentEvent e) {
111 updateModelFromText();
112 }
113
114 public void removeUpdate(DocumentEvent e) {
115 updateModelFromText();
116 }
117
118 public void changedUpdate(DocumentEvent e) {
119 updateModelFromText();
120 }
121
122 // View method
123 public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f) {
124 if (!isSettingAttributes) {
125 setTextFromModel();
126 }
127 }
128
129 // local methods
130
131 void updateYAlign(Font font) {
132 Container c = getContainer();
133 FontMetrics fm = (c != null) ? c.getFontMetrics(font) : Toolkit
134 .getDefaultToolkit().getFontMetrics(font);
135 float h = fm.getHeight();
136 float d = fm.getDescent();
137 yAlign = (h > 0) ? (h - d) / h : 0;
138 }
139
140 void resetBorder() {
141 Component comp = getComponent();
142
143 if (comp != null) {
144 if (isEndTag()) {
145 ((JPanel) comp).setBorder(EndBorder);
146 } else {
147 ((JPanel) comp).setBorder(StartBorder);
148 }
149 }
150 }
151
152 /**
153 * This resets the text on the text component we created to match
154 * that of the AttributeSet for the Element we represent.
155 * <p>If this is invoked on the event dispatching thread, this
156 * directly invokes <code>_setTextFromModel</code>, otherwise
157 * <code>SwingUtilities.invokeLater</code> is used to schedule execution
158 * of <code>_setTextFromModel</code>.
159 */
160 void setTextFromModel() {
161 if (SwingUtilities.isEventDispatchThread()) {
162 _setTextFromModel();
163 } else {
164 SwingUtilities.invokeLater(new Runnable() {
165 public void run() {
166 _setTextFromModel();
167 }
168 });
169 }
170 }
171
172 /**
173 * This resets the text on the text component we created to match
174 * that of the AttributeSet for the Element we represent.
175 */
176 void _setTextFromModel() {
177 Document doc = getDocument();
178 try {
179 isSettingAttributes = true;
180 if (doc instanceof AbstractDocument) {
181 ((AbstractDocument) doc).readLock();
182 }
183 JTextComponent text = getTextComponent();
184 if (text != null) {
185 text.setText(getRepresentedText());
186 resetBorder();
187 Container host = getContainer();
188 if (host != null) {
189 preferenceChanged(this , true, true);
190 host.repaint();
191 }
192 }
193 } finally {
194 isSettingAttributes = false;
195 if (doc instanceof AbstractDocument) {
196 ((AbstractDocument) doc).readUnlock();
197 }
198 }
199 }
200
201 /**
202 * This copies the text from the text component we've created
203 * to the Element's AttributeSet we represent.
204 * <p>If this is invoked on the event dispatching thread, this
205 * directly invokes <code>_updateModelFromText</code>, otherwise
206 * <code>SwingUtilities.invokeLater</code> is used to schedule execution
207 * of <code>_updateModelFromText</code>.
208 */
209 void updateModelFromText() {
210 if (!isSettingAttributes) {
211 if (SwingUtilities.isEventDispatchThread()) {
212 _updateModelFromText();
213 } else {
214 SwingUtilities.invokeLater(new Runnable() {
215 public void run() {
216 _updateModelFromText();
217 }
218 });
219 }
220 }
221 }
222
223 /**
224 * This copies the text from the text component we've created
225 * to the Element's AttributeSet we represent.
226 */
227 void _updateModelFromText() {
228 Document doc = getDocument();
229 Object name = getElement().getAttributes().getAttribute(
230 StyleConstants.NameAttribute);
231 if ((name instanceof HTML.UnknownTag)
232 && (doc instanceof StyledDocument)) {
233 SimpleAttributeSet sas = new SimpleAttributeSet();
234 JTextComponent textComponent = getTextComponent();
235 if (textComponent != null) {
236 String text = textComponent.getText();
237 isSettingAttributes = true;
238 try {
239 sas.addAttribute(StyleConstants.NameAttribute,
240 new HTML.UnknownTag(text));
241 ((StyledDocument) doc).setCharacterAttributes(
242 getStartOffset(), getEndOffset()
243 - getStartOffset(), sas, false);
244 } finally {
245 isSettingAttributes = false;
246 }
247 }
248 }
249 }
250
251 JTextComponent getTextComponent() {
252 Component comp = getComponent();
253
254 return (comp == null) ? null
255 : (JTextComponent) ((Container) comp).getComponent(0);
256 }
257
258 String getRepresentedText() {
259 String retValue = getElement().getName();
260 return (retValue == null) ? "" : retValue;
261 }
262
263 boolean isEndTag() {
264 AttributeSet as = getElement().getAttributes();
265 if (as != null) {
266 Object end = as.getAttribute(HTML.Attribute.ENDTAG);
267 if (end != null && (end instanceof String)
268 && ((String) end).equals("true")) {
269 return true;
270 }
271 }
272 return false;
273 }
274
275 /** Alignment along the y axis, based on the font of the textfield. */
276 float yAlign;
277 /** Set to true when setting attributes. */
278 boolean isSettingAttributes;
279
280 // Following are for Borders that used for Unknown tags and comments.
281 //
282 // Border defines
283 static final int circleR = 3;
284 static final int circleD = circleR * 2;
285 static final int tagSize = 6;
286 static final int padding = 3;
287 static final Color UnknownTagBorderColor = Color.black;
288 static final Border StartBorder = new StartTagBorder();
289 static final Border EndBorder = new EndTagBorder();
290
291 static class StartTagBorder implements Border, Serializable {
292 public void paintBorder(Component c, Graphics g, int x, int y,
293 int width, int height) {
294 g.setColor(UnknownTagBorderColor);
295 x += padding;
296 width -= (padding * 2);
297 g.drawLine(x, y + circleR, x, y + height - circleR);
298 g.drawArc(x, y + height - circleD - 1, circleD, circleD,
299 180, 90);
300 g.drawArc(x, y, circleD, circleD, 90, 90);
301 g.drawLine(x + circleR, y, x + width - tagSize, y);
302 g.drawLine(x + circleR, y + height - 1,
303 x + width - tagSize, y + height - 1);
304
305 g.drawLine(x + width - tagSize, y, x + width - 1, y
306 + height / 2);
307 g.drawLine(x + width - tagSize, y + height, x + width - 1,
308 y + height / 2);
309 }
310
311 public Insets getBorderInsets(Component c) {
312 return new Insets(2, 2 + padding, 2, tagSize + 2 + padding);
313 }
314
315 public boolean isBorderOpaque() {
316 return false;
317 }
318 } // End of class HiddenTagView.StartTagBorder
319
320 static class EndTagBorder implements Border, Serializable {
321 public void paintBorder(Component c, Graphics g, int x, int y,
322 int width, int height) {
323 g.setColor(UnknownTagBorderColor);
324 x += padding;
325 width -= (padding * 2);
326 g.drawLine(x + width - 1, y + circleR, x + width - 1, y
327 + height - circleR);
328 g
329 .drawArc(x + width - circleD - 1, y + height
330 - circleD - 1, circleD, circleD, 270, 90);
331 g.drawArc(x + width - circleD - 1, y, circleD, circleD, 0,
332 90);
333 g.drawLine(x + tagSize, y, x + width - circleR, y);
334 g.drawLine(x + tagSize, y + height - 1,
335 x + width - circleR, y + height - 1);
336
337 g.drawLine(x + tagSize, y, x, y + height / 2);
338 g.drawLine(x + tagSize, y + height, x, y + height / 2);
339 }
340
341 public Insets getBorderInsets(Component c) {
342 return new Insets(2, tagSize + 2 + padding, 2, 2 + padding);
343 }
344
345 public boolean isBorderOpaque() {
346 return false;
347 }
348 } // End of class HiddenTagView.EndTagBorder
349
350 } // End of HiddenTagView
|