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 {
HiddenTagView(Element e)47     HiddenTagView(Element e) {
48         super(e);
49         yAlign = 1;
50     }
51 
createComponent()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 
getAlignment(int axis)80     public float getAlignment(int axis) {
81         if (axis == View.Y_AXIS) {
82             return yAlign;
83         }
84         return 0.5f;
85     }
86 
getMinimumSpan(int axis)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 
getPreferredSpan(int axis)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 
getMaximumSpan(int axis)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
insertUpdate(DocumentEvent e)111     public void insertUpdate(DocumentEvent e) {
112         updateModelFromText();
113     }
114 
removeUpdate(DocumentEvent e)115     public void removeUpdate(DocumentEvent e) {
116         updateModelFromText();
117     }
118 
changedUpdate(DocumentEvent e)119     public void changedUpdate(DocumentEvent e) {
120         updateModelFromText();
121     }
122 
123     // View method
changedUpdate(DocumentEvent e, Shape a, ViewFactory f)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")
updateYAlign(Font font)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 
resetBorder()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      */
setTextFromModel()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      */
_setTextFromModel()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      */
updateModelFromText()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      */
_updateModelFromText()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 
getTextComponent()258     JTextComponent getTextComponent() {
259         Component comp = getComponent();
260 
261         return (comp == null) ? null : (JTextComponent)((Container)comp).
262                                        getComponent(0);
263     }
264 
getRepresentedText()265     String getRepresentedText() {
266         String retValue = getElement().getName();
267         return (retValue == null) ? "" : retValue;
268     }
269 
isEndTag()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 {
paintBorder(Component c, Graphics g, int x, int y, int width, int height)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 
getBorderInsets(Component c)321         public Insets getBorderInsets(Component c) {
322             return new Insets(2, 2 + padding, 2, tagSize + 2 + padding);
323         }
324 
isBorderOpaque()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 {
paintBorder(Component c, Graphics g, int x, int y, int width, int height)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 
getBorderInsets(Component c)352         public Insets getBorderInsets(Component c) {
353             return new Insets(2, tagSize + 2 + padding, 2, 2 + padding);
354         }
355 
isBorderOpaque()356         public boolean isBorderOpaque() {
357             return false;
358         }
359     } // End of class HiddenTagView.EndTagBorder
360 
361 
362 } // End of HiddenTagView
363