1. /*
  2. * @(#)FrameView.java 1.25 04/01/13
  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.util.*;
  10. import java.net.*;
  11. import java.io.*;
  12. import javax.swing.*;
  13. import javax.swing.text.*;
  14. import javax.swing.event.*;
  15. /**
  16. * Implements a FrameView, intended to support the HTML
  17. * <FRAME> tag. Supports the frameborder, scrolling,
  18. * marginwidth and marginheight attributes.
  19. *
  20. * @author Sunita Mani
  21. * @version 1.25, 01/13/04
  22. */
  23. class FrameView extends ComponentView implements HyperlinkListener {
  24. JEditorPane htmlPane;
  25. JScrollPane scroller;
  26. boolean editable;
  27. float width;
  28. float height;
  29. URL src;
  30. /** Set to true when the component has been created. */
  31. private boolean createdComponent;
  32. /**
  33. * Creates a new Frame.
  34. *
  35. * @param elem the element to represent.
  36. */
  37. public FrameView(Element elem) {
  38. super(elem);
  39. }
  40. protected Component createComponent() {
  41. Element elem = getElement();
  42. AttributeSet attributes = elem.getAttributes();
  43. String srcAtt = (String)attributes.getAttribute(HTML.Attribute.SRC);
  44. if ((srcAtt != null) && (!srcAtt.equals(""))) {
  45. try {
  46. URL base = ((HTMLDocument)elem.getDocument()).getBase();
  47. src = new URL(base, srcAtt);
  48. htmlPane = new FrameEditorPane();
  49. htmlPane.addHyperlinkListener(this);
  50. JEditorPane host = getHostPane();
  51. if (host != null) {
  52. htmlPane.setEditable(host.isEditable());
  53. String charset = (String) host.getClientProperty("charset");
  54. if (charset != null) {
  55. htmlPane.putClientProperty("charset", charset);
  56. }
  57. }
  58. htmlPane.setPage(src);
  59. Document doc = htmlPane.getDocument();
  60. if (doc instanceof HTMLDocument) {
  61. ((HTMLDocument)doc).setFrameDocumentState(true);
  62. }
  63. setMargin();
  64. createScrollPane();
  65. setBorder();
  66. } catch (MalformedURLException e) {
  67. e.printStackTrace();
  68. } catch (IOException e1) {
  69. e1.printStackTrace();
  70. }
  71. }
  72. createdComponent = true;
  73. return scroller;
  74. }
  75. JEditorPane getHostPane() {
  76. Container c = getContainer();
  77. while ((c != null) && ! (c instanceof JEditorPane)) {
  78. c = c.getParent();
  79. }
  80. return (JEditorPane) c;
  81. }
  82. /**
  83. * Sets the parent view for the FrameView.
  84. * Also determines if the FrameView should be editable
  85. * or not based on whether the JTextComponent that
  86. * contains it is editable.
  87. *
  88. * @param parent View
  89. */
  90. public void setParent(View parent) {
  91. if (parent != null) {
  92. JTextComponent t = (JTextComponent)parent.getContainer();
  93. editable = t.isEditable();
  94. }
  95. super.setParent(parent);
  96. }
  97. /**
  98. * Also determines if the FrameView should be editable
  99. * or not based on whether the JTextComponent that
  100. * contains it is editable. And then proceeds to call
  101. * the superclass to do the paint().
  102. *
  103. * @param parent View
  104. * @see text.ComponentView#paint
  105. */
  106. public void paint(Graphics g, Shape allocation) {
  107. Container host = getContainer();
  108. if (host != null && htmlPane != null &&
  109. htmlPane.isEditable() != ((JTextComponent)host).isEditable()) {
  110. editable = ((JTextComponent)host).isEditable();
  111. htmlPane.setEditable(editable);
  112. }
  113. super.paint(g, allocation);
  114. }
  115. /**
  116. * If the marginwidth or marginheight attributes have been specified,
  117. * then the JEditorPane's margin's are set to the new values.
  118. */
  119. private void setMargin() {
  120. int margin = 0;
  121. Insets in = htmlPane.getMargin();
  122. Insets newInsets;
  123. boolean modified = false;
  124. AttributeSet attributes = getElement().getAttributes();
  125. String marginStr = (String)attributes.getAttribute(HTML.Attribute.MARGINWIDTH);
  126. if ( in != null) {
  127. newInsets = new Insets(in.top, in.left, in.right, in.bottom);
  128. } else {
  129. newInsets = new Insets(0,0,0,0);
  130. }
  131. if (marginStr != null) {
  132. margin = Integer.parseInt(marginStr);
  133. if (margin > 0) {
  134. newInsets.left = margin;
  135. newInsets.right = margin;
  136. modified = true;
  137. }
  138. }
  139. marginStr = (String)attributes.getAttribute(HTML.Attribute.MARGINHEIGHT);
  140. if (marginStr != null) {
  141. margin = Integer.parseInt(marginStr);
  142. if (margin > 0) {
  143. newInsets.top = margin;
  144. newInsets.bottom = margin;
  145. modified = true;
  146. }
  147. }
  148. if (modified) {
  149. htmlPane.setMargin(newInsets);
  150. }
  151. }
  152. /**
  153. * If the frameborder attribute has been specified, either in the frame,
  154. * or by the frames enclosing frameset, the JScrollPane's setBorder()
  155. * method is invoked to achieve the desired look.
  156. */
  157. private void setBorder() {
  158. AttributeSet attributes = getElement().getAttributes();
  159. String frameBorder = (String)attributes.getAttribute(HTML.Attribute.FRAMEBORDER);
  160. if ((frameBorder != null) &&
  161. (frameBorder.equals("no") || frameBorder.equals("0"))) {
  162. // make invisible borders.
  163. scroller.setBorder(null);
  164. }
  165. }
  166. /**
  167. * This method creates the JScrollPane. The scrollbar policy is determined by
  168. * the scrolling attribute. If not defined, the default is "auto" which
  169. * maps to the scrollbar's being displayed as needed.
  170. */
  171. private void createScrollPane() {
  172. AttributeSet attributes = getElement().getAttributes();
  173. String scrolling = (String)attributes.getAttribute(HTML.Attribute.SCROLLING);
  174. if (scrolling == null) {
  175. scrolling = "auto";
  176. }
  177. if (!scrolling.equals("no")) {
  178. if (scrolling.equals("yes")) {
  179. scroller = new JScrollPane(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
  180. JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
  181. } else {
  182. // scrollbars will be displayed if needed
  183. //
  184. scroller = new JScrollPane();
  185. }
  186. } else {
  187. scroller = new JScrollPane(JScrollPane.VERTICAL_SCROLLBAR_NEVER,
  188. JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
  189. }
  190. JViewport vp = scroller.getViewport();
  191. vp.add(htmlPane);
  192. vp.setBackingStoreEnabled(true);
  193. scroller.setMinimumSize(new Dimension(5,5));
  194. scroller.setMaximumSize(new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE));
  195. }
  196. /**
  197. * Finds the outermost FrameSetView. It then
  198. * returns that FrameSetView's container.
  199. */
  200. private JEditorPane getOutermostJEditorPane() {
  201. View parent = getParent();
  202. FrameSetView frameSetView = null;
  203. while (parent != null) {
  204. if (parent instanceof FrameSetView) {
  205. frameSetView = (FrameSetView)parent;
  206. }
  207. parent = parent.getParent();
  208. }
  209. if (frameSetView != null) {
  210. return (JEditorPane)frameSetView.getContainer();
  211. }
  212. return null;
  213. }
  214. /**
  215. * Returns true if this frame is contained within
  216. * a nested frameset.
  217. */
  218. private boolean inNestedFrameSet() {
  219. FrameSetView parent = (FrameSetView)getParent();
  220. return (parent.getParent() instanceof FrameSetView);
  221. }
  222. /**
  223. * Notification of a change relative to a
  224. * hyperlink. This method searches for the outermost
  225. * JEditorPane, and then fires an HTMLFrameHyperlinkEvent
  226. * to that frame. In addition, if the target is _parent,
  227. * and there is not nested framesets then the target is
  228. * reset to _top. If the target is _top, in addition to
  229. * firing the event to the outermost JEditorPane, this
  230. * method also invokes the setPage() method and explicitly
  231. * replaces the current document with the destination url.
  232. *
  233. * @param HyperlinkEvent
  234. */
  235. public void hyperlinkUpdate(HyperlinkEvent evt) {
  236. if (!(evt instanceof HTMLFrameHyperlinkEvent)) {
  237. return;
  238. }
  239. HTMLFrameHyperlinkEvent e = (HTMLFrameHyperlinkEvent)evt;
  240. if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
  241. JEditorPane c = getOutermostJEditorPane();
  242. String target = e.getTarget();
  243. if (e.getTarget().equals("_parent") && !inNestedFrameSet()){
  244. target = "_top";
  245. }
  246. if (c != null && !c.isEditable()) {
  247. c.fireHyperlinkUpdate(new HTMLFrameHyperlinkEvent(c,
  248. e.getEventType(),
  249. e.getURL(),
  250. e.getDescription(),
  251. getElement(),
  252. target));
  253. if (target.equals("_top")) {
  254. try {
  255. c.setPage(e.getURL());
  256. } catch (IOException ex) {
  257. // Need a way to handle exceptions
  258. // ex.printStackTrace();
  259. }
  260. }
  261. }
  262. }
  263. }
  264. /**
  265. * Gives notification from the document that attributes were changed
  266. * in a location that this view is responsible for. Currently this view
  267. * handles changes to its SRC attribute.
  268. *
  269. * @param e the change information from the associated document
  270. * @param a the current allocation of the view
  271. * @param f the factory to use to rebuild if the view has children
  272. *
  273. */
  274. public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f) {
  275. Element elem = getElement();
  276. AttributeSet attributes = elem.getAttributes();
  277. URL oldPage = src;
  278. String srcAtt = (String)attributes.getAttribute(HTML.Attribute.SRC);
  279. URL base = ((HTMLDocument)elem.getDocument()).getBase();
  280. try {
  281. src = new URL(base, srcAtt);
  282. if (!createdComponent) {
  283. return;
  284. }
  285. if (oldPage.equals(src)) {
  286. return;
  287. }
  288. htmlPane.setPage(src);
  289. Document newDoc = htmlPane.getDocument();
  290. if (newDoc instanceof HTMLDocument) {
  291. ((HTMLDocument)newDoc).setFrameDocumentState(true);
  292. }
  293. } catch (MalformedURLException e1) {
  294. // Need a way to handle exceptions
  295. //e1.printStackTrace();
  296. } catch (IOException e2) {
  297. // Need a way to handle exceptions
  298. //e2.printStackTrace();
  299. }
  300. }
  301. /**
  302. * Determines the minimum span for this view along an
  303. * axis.
  304. *
  305. * @param axis may be either <code>View.X_AXIS</code> or
  306. * <code>View.Y_AXIS</code>
  307. * @return the preferred span; given that we do not
  308. * support resizing of frames, the minimum span returned
  309. * is the same as the preferred span
  310. *
  311. */
  312. public float getMinimumSpan(int axis) {
  313. return 5;
  314. }
  315. /**
  316. * Determines the maximum span for this view along an
  317. * axis.
  318. *
  319. * @param axis may be either <code>View.X_AXIS</code> or
  320. * <code>View.Y_AXIS</code>
  321. * @return the preferred span; given that we do not
  322. * support resizing of frames, the maximum span returned
  323. * is the same as the preferred span
  324. *
  325. */
  326. public float getMaximumSpan(int axis) {
  327. return Integer.MAX_VALUE;
  328. }
  329. /** Editor pane rendering frame of HTML document
  330. * It uses the same editor kits classes as outermost JEditorPane
  331. */
  332. private class FrameEditorPane extends JEditorPane {
  333. public EditorKit getEditorKitForContentType(String type) {
  334. EditorKit editorKit = super.getEditorKitForContentType(type);
  335. JEditorPane outerMostJEditorPane = null;
  336. if ((outerMostJEditorPane = getOutermostJEditorPane()) != null) {
  337. EditorKit inheritedEditorKit = outerMostJEditorPane.getEditorKitForContentType(type);
  338. if (! editorKit.getClass().equals(inheritedEditorKit.getClass())) {
  339. editorKit = (EditorKit) inheritedEditorKit.clone();
  340. setEditorKitForContentType(type, editorKit);
  341. }
  342. }
  343. return editorKit;
  344. }
  345. }
  346. }