1 /*
2 * Copyright 1998-2006 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25 package javax.swing.text.html;
26
27 import java.awt;
28 import java.util;
29 import java.net;
30 import java.io;
31 import javax.swing;
32 import javax.swing.text;
33 import javax.swing.event;
34
35 import sun.swing.text.html.FrameEditorPaneTag;
36
37 /**
38 * Implements a FrameView, intended to support the HTML
39 * <FRAME> tag. Supports the frameborder, scrolling,
40 * marginwidth and marginheight attributes.
41 *
42 * @author Sunita Mani
43 */
44
45 class FrameView extends ComponentView implements HyperlinkListener {
46
47
48 JEditorPane htmlPane;
49 JScrollPane scroller;
50 boolean editable;
51 float width;
52 float height;
53 URL src;
54 /** Set to true when the component has been created. */
55 private boolean createdComponent;
56
57 /**
58 * Creates a new Frame.
59 *
60 * @param elem the element to represent.
61 */
62 public FrameView(Element elem) {
63 super(elem);
64 }
65
66 protected Component createComponent() {
67
68 Element elem = getElement();
69 AttributeSet attributes = elem.getAttributes();
70 String srcAtt = (String)attributes.getAttribute(HTML.Attribute.SRC);
71
72 if ((srcAtt != null) && (!srcAtt.equals(""))) {
73 try {
74 URL base = ((HTMLDocument)elem.getDocument()).getBase();
75 src = new URL(base, srcAtt);
76 htmlPane = new FrameEditorPane();
77 htmlPane.addHyperlinkListener(this);
78 JEditorPane host = getHostPane();
79 boolean isAutoFormSubmission = true;
80 if (host != null) {
81 htmlPane.setEditable(host.isEditable());
82 String charset = (String) host.getClientProperty("charset");
83 if (charset != null) {
84 htmlPane.putClientProperty("charset", charset);
85 }
86 HTMLEditorKit hostKit = (HTMLEditorKit)host.getEditorKit();
87 if (hostKit != null) {
88 isAutoFormSubmission = hostKit.isAutoFormSubmission();
89 }
90 }
91 htmlPane.setPage(src);
92 HTMLEditorKit kit = (HTMLEditorKit)htmlPane.getEditorKit();
93 if (kit != null) {
94 kit.setAutoFormSubmission(isAutoFormSubmission);
95 }
96
97 Document doc = htmlPane.getDocument();
98 if (doc instanceof HTMLDocument) {
99 ((HTMLDocument)doc).setFrameDocumentState(true);
100 }
101 setMargin();
102 createScrollPane();
103 setBorder();
104 } catch (MalformedURLException e) {
105 e.printStackTrace();
106 } catch (IOException e1) {
107 e1.printStackTrace();
108 }
109 }
110 createdComponent = true;
111 return scroller;
112 }
113
114 JEditorPane getHostPane() {
115 Container c = getContainer();
116 while ((c != null) && ! (c instanceof JEditorPane)) {
117 c = c.getParent();
118 }
119 return (JEditorPane) c;
120 }
121
122
123 /**
124 * Sets the parent view for the FrameView.
125 * Also determines if the FrameView should be editable
126 * or not based on whether the JTextComponent that
127 * contains it is editable.
128 *
129 * @param parent View
130 */
131 public void setParent(View parent) {
132 if (parent != null) {
133 JTextComponent t = (JTextComponent)parent.getContainer();
134 editable = t.isEditable();
135 }
136 super.setParent(parent);
137 }
138
139
140 /**
141 * Also determines if the FrameView should be editable
142 * or not based on whether the JTextComponent that
143 * contains it is editable. And then proceeds to call
144 * the superclass to do the paint().
145 *
146 * @param parent View
147 * @see text.ComponentView#paint
148 */
149 public void paint(Graphics g, Shape allocation) {
150
151 Container host = getContainer();
152 if (host != null && htmlPane != null &&
153 htmlPane.isEditable() != ((JTextComponent)host).isEditable()) {
154 editable = ((JTextComponent)host).isEditable();
155 htmlPane.setEditable(editable);
156 }
157 super.paint(g, allocation);
158 }
159
160
161 /**
162 * If the marginwidth or marginheight attributes have been specified,
163 * then the JEditorPane's margin's are set to the new values.
164 */
165 private void setMargin() {
166 int margin = 0;
167 Insets in = htmlPane.getMargin();
168 Insets newInsets;
169 boolean modified = false;
170 AttributeSet attributes = getElement().getAttributes();
171 String marginStr = (String)attributes.getAttribute(HTML.Attribute.MARGINWIDTH);
172 if ( in != null) {
173 newInsets = new Insets(in.top, in.left, in.right, in.bottom);
174 } else {
175 newInsets = new Insets(0,0,0,0);
176 }
177 if (marginStr != null) {
178 margin = Integer.parseInt(marginStr);
179 if (margin > 0) {
180 newInsets.left = margin;
181 newInsets.right = margin;
182 modified = true;
183 }
184 }
185 marginStr = (String)attributes.getAttribute(HTML.Attribute.MARGINHEIGHT);
186 if (marginStr != null) {
187 margin = Integer.parseInt(marginStr);
188 if (margin > 0) {
189 newInsets.top = margin;
190 newInsets.bottom = margin;
191 modified = true;
192 }
193 }
194 if (modified) {
195 htmlPane.setMargin(newInsets);
196 }
197 }
198
199 /**
200 * If the frameborder attribute has been specified, either in the frame,
201 * or by the frames enclosing frameset, the JScrollPane's setBorder()
202 * method is invoked to achieve the desired look.
203 */
204 private void setBorder() {
205
206 AttributeSet attributes = getElement().getAttributes();
207 String frameBorder = (String)attributes.getAttribute(HTML.Attribute.FRAMEBORDER);
208 if ((frameBorder != null) &&
209 (frameBorder.equals("no") || frameBorder.equals("0"))) {
210 // make invisible borders.
211 scroller.setBorder(null);
212 }
213 }
214
215
216 /**
217 * This method creates the JScrollPane. The scrollbar policy is determined by
218 * the scrolling attribute. If not defined, the default is "auto" which
219 * maps to the scrollbar's being displayed as needed.
220 */
221 private void createScrollPane() {
222 AttributeSet attributes = getElement().getAttributes();
223 String scrolling = (String)attributes.getAttribute(HTML.Attribute.SCROLLING);
224 if (scrolling == null) {
225 scrolling = "auto";
226 }
227
228 if (!scrolling.equals("no")) {
229 if (scrolling.equals("yes")) {
230 scroller = new JScrollPane(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
231 JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
232 } else {
233 // scrollbars will be displayed if needed
234 //
235 scroller = new JScrollPane();
236 }
237 } else {
238 scroller = new JScrollPane(JScrollPane.VERTICAL_SCROLLBAR_NEVER,
239 JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
240 }
241
242 JViewport vp = scroller.getViewport();
243 vp.add(htmlPane);
244 vp.setBackingStoreEnabled(true);
245 scroller.setMinimumSize(new Dimension(5,5));
246 scroller.setMaximumSize(new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE));
247 }
248
249
250 /**
251 * Finds the outermost FrameSetView. It then
252 * returns that FrameSetView's container.
253 */
254 JEditorPane getOutermostJEditorPane() {
255
256 View parent = getParent();
257 FrameSetView frameSetView = null;
258 while (parent != null) {
259 if (parent instanceof FrameSetView) {
260 frameSetView = (FrameSetView)parent;
261 }
262 parent = parent.getParent();
263 }
264 if (frameSetView != null) {
265 return (JEditorPane)frameSetView.getContainer();
266 }
267 return null;
268 }
269
270
271 /**
272 * Returns true if this frame is contained within
273 * a nested frameset.
274 */
275 private boolean inNestedFrameSet() {
276 FrameSetView parent = (FrameSetView)getParent();
277 return (parent.getParent() instanceof FrameSetView);
278 }
279
280
281 /**
282 * Notification of a change relative to a
283 * hyperlink. This method searches for the outermost
284 * JEditorPane, and then fires an HTMLFrameHyperlinkEvent
285 * to that frame. In addition, if the target is _parent,
286 * and there is not nested framesets then the target is
287 * reset to _top. If the target is _top, in addition to
288 * firing the event to the outermost JEditorPane, this
289 * method also invokes the setPage() method and explicitly
290 * replaces the current document with the destination url.
291 *
292 * @param HyperlinkEvent
293 */
294 public void hyperlinkUpdate(HyperlinkEvent evt) {
295
296 JEditorPane c = getOutermostJEditorPane();
297 if (c == null) {
298 return;
299 }
300
301 if (!(evt instanceof HTMLFrameHyperlinkEvent)) {
302 c.fireHyperlinkUpdate(evt);
303 return;
304 }
305
306 HTMLFrameHyperlinkEvent e = (HTMLFrameHyperlinkEvent)evt;
307
308 if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
309 String target = e.getTarget();
310 String postTarget = target;
311
312 if (target.equals("_parent") && !inNestedFrameSet()){
313 target = "_top";
314 }
315
316 if (evt instanceof FormSubmitEvent) {
317 HTMLEditorKit kit = (HTMLEditorKit)c.getEditorKit();
318 if (kit != null && kit.isAutoFormSubmission()) {
319 if (target.equals("_top")) {
320 try {
321 movePostData(c, postTarget);
322 c.setPage(e.getURL());
323 } catch (IOException ex) {
324 // Need a way to handle exceptions
325 }
326 } else {
327 HTMLDocument doc = (HTMLDocument)c.getDocument();
328 doc.processHTMLFrameHyperlinkEvent(e);
329 }
330 } else {
331 c.fireHyperlinkUpdate(evt);
332 }
333 return;
334 }
335
336 if (target.equals("_top")) {
337 try {
338 c.setPage(e.getURL());
339 } catch (IOException ex) {
340 // Need a way to handle exceptions
341 // ex.printStackTrace();
342 }
343 }
344 if (!c.isEditable()) {
345 c.fireHyperlinkUpdate(new HTMLFrameHyperlinkEvent(c,
346 e.getEventType(),
347 e.getURL(),
348 e.getDescription(),
349 getElement(),
350 e.getInputEvent(),
351 target));
352 }
353 }
354 }
355
356 /**
357 * Gives notification from the document that attributes were changed
358 * in a location that this view is responsible for. Currently this view
359 * handles changes to its SRC attribute.
360 *
361 * @param e the change information from the associated document
362 * @param a the current allocation of the view
363 * @param f the factory to use to rebuild if the view has children
364 *
365 */
366 public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f) {
367
368 Element elem = getElement();
369 AttributeSet attributes = elem.getAttributes();
370
371 URL oldPage = src;
372
373 String srcAtt = (String)attributes.getAttribute(HTML.Attribute.SRC);
374 URL base = ((HTMLDocument)elem.getDocument()).getBase();
375 try {
376 if (!createdComponent) {
377 return;
378 }
379
380 Object postData = movePostData(htmlPane, null);
381 src = new URL(base, srcAtt);
382 if (oldPage.equals(src) && (src.getRef() == null) && (postData == null)) {
383 return;
384 }
385
386 htmlPane.setPage(src);
387 Document newDoc = htmlPane.getDocument();
388 if (newDoc instanceof HTMLDocument) {
389 ((HTMLDocument)newDoc).setFrameDocumentState(true);
390 }
391 } catch (MalformedURLException e1) {
392 // Need a way to handle exceptions
393 //e1.printStackTrace();
394 } catch (IOException e2) {
395 // Need a way to handle exceptions
396 //e2.printStackTrace();
397 }
398 }
399
400 /**
401 * Move POST data from temporary storage into the target document property.
402 *
403 * @return the POST data or null if no data found
404 */
405 private Object movePostData(JEditorPane targetPane, String frameName) {
406 Object postData = null;
407 JEditorPane p = getOutermostJEditorPane();
408 if (p != null) {
409 if (frameName == null) {
410 frameName = (String) getElement().getAttributes().getAttribute(
411 HTML.Attribute.NAME);
412 }
413 if (frameName != null) {
414 String propName = FormView.PostDataProperty + "." + frameName;
415 Document d = p.getDocument();
416 postData = d.getProperty(propName);
417 if (postData != null) {
418 targetPane.getDocument().putProperty(
419 FormView.PostDataProperty, postData);
420 d.putProperty(propName, null);
421 }
422 }
423 }
424
425 return postData;
426 }
427
428 /**
429 * Determines the minimum span for this view along an
430 * axis.
431 *
432 * @param axis may be either <code>View.X_AXIS</code> or
433 * <code>View.Y_AXIS</code>
434 * @return the preferred span; given that we do not
435 * support resizing of frames, the minimum span returned
436 * is the same as the preferred span
437 *
438 */
439 public float getMinimumSpan(int axis) {
440 return 5;
441 }
442
443 /**
444 * Determines the maximum span for this view along an
445 * axis.
446 *
447 * @param axis may be either <code>View.X_AXIS</code> or
448 * <code>View.Y_AXIS</code>
449 * @return the preferred span; given that we do not
450 * support resizing of frames, the maximum span returned
451 * is the same as the preferred span
452 *
453 */
454 public float getMaximumSpan(int axis) {
455 return Integer.MAX_VALUE;
456 }
457
458 /** Editor pane rendering frame of HTML document
459 * It uses the same editor kits classes as outermost JEditorPane
460 */
461 class FrameEditorPane extends JEditorPane implements FrameEditorPaneTag {
462 public EditorKit getEditorKitForContentType(String type) {
463 EditorKit editorKit = super.getEditorKitForContentType(type);
464 JEditorPane outerMostJEditorPane = null;
465 if ((outerMostJEditorPane = getOutermostJEditorPane()) != null) {
466 EditorKit inheritedEditorKit = outerMostJEditorPane.getEditorKitForContentType(type);
467 if (! editorKit.getClass().equals(inheritedEditorKit.getClass())) {
468 editorKit = (EditorKit) inheritedEditorKit.clone();
469 setEditorKitForContentType(type, editorKit);
470 }
471 }
472 return editorKit;
473 }
474
475 FrameView getFrameView() {
476 return FrameView.this;
477 }
478 }
479 }