1. /*
  2. * @(#)HiddenTagView.java 1.15 03/12/19
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.swing.text.html;
  8. import java.awt.*;
  9. import java.awt.event.*;
  10. import java.io.*;
  11. import java.net.MalformedURLException;
  12. import java.net.URL;
  13. import javax.swing.text.*;
  14. import javax.swing.*;
  15. import javax.swing.border.*;
  16. import javax.swing.event.*;
  17. import java.util.*;
  18. /**
  19. * HiddenTagView subclasses EditableView to contain a JTextField showing
  20. * the element name. When the textfield is edited the element name is
  21. * reset. As this inherits from EditableView if the JTextComponent is
  22. * not editable, the textfield will not be visible.
  23. *
  24. * @author Scott Violet
  25. * @version 1.15, 12/19/03
  26. */
  27. class HiddenTagView extends EditableView implements DocumentListener {
  28. HiddenTagView(Element e) {
  29. super(e);
  30. yAlign = 1;
  31. }
  32. protected Component createComponent() {
  33. JTextField tf = new JTextField(getElement().getName());
  34. Document doc = getDocument();
  35. Font font;
  36. if (doc instanceof StyledDocument) {
  37. font = ((StyledDocument)doc).getFont(getAttributes());
  38. tf.setFont(font);
  39. }
  40. else {
  41. font = tf.getFont();
  42. }
  43. tf.getDocument().addDocumentListener(this);
  44. updateYAlign(font);
  45. // Create a panel to wrap the textfield so that the textfields
  46. // laf border shows through.
  47. JPanel panel = new JPanel(new BorderLayout());
  48. panel.setBackground(null);
  49. if (isEndTag()) {
  50. panel.setBorder(EndBorder);
  51. }
  52. else {
  53. panel.setBorder(StartBorder);
  54. }
  55. panel.add(tf);
  56. return panel;
  57. }
  58. public float getAlignment(int axis) {
  59. if (axis == View.Y_AXIS) {
  60. return yAlign;
  61. }
  62. return 0.5f;
  63. }
  64. public float getMinimumSpan(int axis) {
  65. if (axis == View.X_AXIS && isVisible()) {
  66. // Default to preferred.
  67. return Math.max(30, super.getPreferredSpan(axis));
  68. }
  69. return super.getMinimumSpan(axis);
  70. }
  71. public float getPreferredSpan(int axis) {
  72. if (axis == View.X_AXIS && isVisible()) {
  73. return Math.max(30, super.getPreferredSpan(axis));
  74. }
  75. return super.getPreferredSpan(axis);
  76. }
  77. public float getMaximumSpan(int axis) {
  78. if (axis == View.X_AXIS && isVisible()) {
  79. // Default to preferred.
  80. return Math.max(30, super.getMaximumSpan(axis));
  81. }
  82. return super.getMaximumSpan(axis);
  83. }
  84. // DocumentListener methods
  85. public void insertUpdate(DocumentEvent e) {
  86. updateModelFromText();
  87. }
  88. public void removeUpdate(DocumentEvent e) {
  89. updateModelFromText();
  90. }
  91. public void changedUpdate(DocumentEvent e) {
  92. updateModelFromText();
  93. }
  94. // View method
  95. public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f) {
  96. if (!isSettingAttributes) {
  97. setTextFromModel();
  98. }
  99. }
  100. // local methods
  101. void updateYAlign(Font font) {
  102. Container c = getContainer();
  103. FontMetrics fm = (c != null) ? c.getFontMetrics(font) :
  104. Toolkit.getDefaultToolkit().getFontMetrics(font);
  105. float h = fm.getHeight();
  106. float d = fm.getDescent();
  107. yAlign = (h - d) / h;
  108. }
  109. void resetBorder() {
  110. Component comp = getComponent();
  111. if (comp != null) {
  112. if (isEndTag()) {
  113. ((JPanel)comp).setBorder(EndBorder);
  114. }
  115. else {
  116. ((JPanel)comp).setBorder(StartBorder);
  117. }
  118. }
  119. }
  120. /**
  121. * This resets the text on the text component we created to match
  122. * that of the AttributeSet for the Element we represent.
  123. * <p>If this is invoked on the event dispatching thread, this
  124. * directly invokes <code>_setTextFromModel</code>, otherwise
  125. * <code>SwingUtilities.invokeLater</code> is used to schedule execution
  126. * of <code>_setTextFromModel</code>.
  127. */
  128. void setTextFromModel() {
  129. if (SwingUtilities.isEventDispatchThread()) {
  130. _setTextFromModel();
  131. }
  132. else {
  133. SwingUtilities.invokeLater(new Runnable() {
  134. public void run() {
  135. _setTextFromModel();
  136. }
  137. });
  138. }
  139. }
  140. /**
  141. * This resets the text on the text component we created to match
  142. * that of the AttributeSet for the Element we represent.
  143. */
  144. void _setTextFromModel() {
  145. Document doc = getDocument();
  146. try {
  147. isSettingAttributes = true;
  148. if (doc instanceof AbstractDocument) {
  149. ((AbstractDocument)doc).readLock();
  150. }
  151. JTextComponent text = getTextComponent();
  152. if (text != null) {
  153. text.setText(getRepresentedText());
  154. resetBorder();
  155. Container host = getContainer();
  156. if (host != null) {
  157. preferenceChanged(this, true, true);
  158. host.repaint();
  159. }
  160. }
  161. }
  162. finally {
  163. isSettingAttributes = false;
  164. if (doc instanceof AbstractDocument) {
  165. ((AbstractDocument)doc).readUnlock();
  166. }
  167. }
  168. }
  169. /**
  170. * This copies the text from the text component we've created
  171. * to the Element's AttributeSet we represent.
  172. * <p>If this is invoked on the event dispatching thread, this
  173. * directly invokes <code>_updateModelFromText</code>, otherwise
  174. * <code>SwingUtilities.invokeLater</code> is used to schedule execution
  175. * of <code>_updateModelFromText</code>.
  176. */
  177. void updateModelFromText() {
  178. if (!isSettingAttributes) {
  179. if (SwingUtilities.isEventDispatchThread()) {
  180. _updateModelFromText();
  181. }
  182. else {
  183. SwingUtilities.invokeLater(new Runnable() {
  184. public void run() {
  185. _updateModelFromText();
  186. }
  187. });
  188. }
  189. }
  190. }
  191. /**
  192. * This copies the text from the text component we've created
  193. * to the Element's AttributeSet we represent.
  194. */
  195. void _updateModelFromText() {
  196. Document doc = getDocument();
  197. Object name = getElement().getAttributes().getAttribute
  198. (StyleConstants.NameAttribute);
  199. if ((name instanceof HTML.UnknownTag) &&
  200. (doc instanceof StyledDocument)) {
  201. SimpleAttributeSet sas = new SimpleAttributeSet();
  202. JTextComponent textComponent = getTextComponent();
  203. if (textComponent != null) {
  204. String text = textComponent.getText();
  205. isSettingAttributes = true;
  206. try {
  207. sas.addAttribute(StyleConstants.NameAttribute,
  208. new HTML.UnknownTag(text));
  209. ((StyledDocument)doc).setCharacterAttributes
  210. (getStartOffset(), getEndOffset() -
  211. getStartOffset(), sas, false);
  212. }
  213. finally {
  214. isSettingAttributes = false;
  215. }
  216. }
  217. }
  218. }
  219. JTextComponent getTextComponent() {
  220. Component comp = getComponent();
  221. return (comp == null) ? null : (JTextComponent)((Container)comp).
  222. getComponent(0);
  223. }
  224. String getRepresentedText() {
  225. String retValue = getElement().getName();
  226. return (retValue == null) ? "" : retValue;
  227. }
  228. boolean isEndTag() {
  229. AttributeSet as = getElement().getAttributes();
  230. if (as != null) {
  231. Object end = as.getAttribute(HTML.Attribute.ENDTAG);
  232. if (end != null && (end instanceof String) &&
  233. ((String)end).equals("true")) {
  234. return true;
  235. }
  236. }
  237. return false;
  238. }
  239. /** Alignment along the y axis, based on the font of the textfield. */
  240. float yAlign;
  241. /** Set to true when setting attributes. */
  242. boolean isSettingAttributes;
  243. // Following are for Borders that used for Unknown tags and comments.
  244. //
  245. // Border defines
  246. static final int circleR = 3;
  247. static final int circleD = circleR * 2;
  248. static final int tagSize = 6;
  249. static final int padding = 3;
  250. static final Color UnknownTagBorderColor = Color.black;
  251. static final Border StartBorder = new StartTagBorder();
  252. static final Border EndBorder = new EndTagBorder();
  253. static class StartTagBorder implements Border, Serializable {
  254. public void paintBorder(Component c, Graphics g, int x, int y,
  255. int width, int height) {
  256. g.setColor(UnknownTagBorderColor);
  257. x += padding;
  258. width -= (padding * 2);
  259. g.drawLine(x, y + circleR,
  260. x, y + height - circleR);
  261. g.drawArc(x, y + height - circleD - 1,
  262. circleD, circleD, 180, 90);
  263. g.drawArc(x, y, circleD, circleD, 90, 90);
  264. g.drawLine(x + circleR, y, x + width - tagSize, y);
  265. g.drawLine(x + circleR, y + height - 1,
  266. x + width - tagSize, y + height - 1);
  267. g.drawLine(x + width - tagSize, y,
  268. x + width - 1, y + height / 2);
  269. g.drawLine(x + width - tagSize, y + height,
  270. x + width - 1, y + height / 2);
  271. }
  272. public Insets getBorderInsets(Component c) {
  273. return new Insets(2, 2 + padding, 2, tagSize + 2 + padding);
  274. }
  275. public boolean isBorderOpaque() {
  276. return false;
  277. }
  278. } // End of class HiddenTagView.StartTagBorder
  279. static class EndTagBorder implements Border, Serializable {
  280. public void paintBorder(Component c, Graphics g, int x, int y,
  281. int width, int height) {
  282. g.setColor(UnknownTagBorderColor);
  283. x += padding;
  284. width -= (padding * 2);
  285. g.drawLine(x + width - 1, y + circleR,
  286. x + width - 1, y + height - circleR);
  287. g.drawArc(x + width - circleD - 1, y + height - circleD - 1,
  288. circleD, circleD, 270, 90);
  289. g.drawArc(x + width - circleD - 1, y, circleD, circleD, 0, 90);
  290. g.drawLine(x + tagSize, y, x + width - circleR, y);
  291. g.drawLine(x + tagSize, y + height - 1,
  292. x + width - circleR, y + height - 1);
  293. g.drawLine(x + tagSize, y,
  294. x, y + height / 2);
  295. g.drawLine(x + tagSize, y + height,
  296. x, y + height / 2);
  297. }
  298. public Insets getBorderInsets(Component c) {
  299. return new Insets(2, tagSize + 2 + padding, 2, 2 + padding);
  300. }
  301. public boolean isBorderOpaque() {
  302. return false;
  303. }
  304. } // End of class HiddenTagView.EndTagBorder
  305. } // End of HiddenTagView