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