KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > wings > SFrame


1 /*
2  * $Id: SFrame.java,v 1.14 2005/05/07 17:54:57 oliverscheck Exp $
3  * Copyright 2000,2005 wingS development team.
4  *
5  * This file is part of wingS (http://www.j-wings.org).
6  *
7  * wingS is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU Lesser General Public License
9  * as published by the Free Software Foundation; either version 2.1
10  * of the License, or (at your option) any later version.
11  *
12  * Please see COPYING for the complete licence.
13  */

14 package org.wings;
15
16 import org.apache.commons.logging.Log;
17 import org.apache.commons.logging.LogFactory;
18 import org.wings.event.InvalidLowLevelEvent;
19 import org.wings.event.SInvalidLowLevelEventListener;
20 import org.wings.event.SRenderListener;
21 import org.wings.io.Device;
22 import org.wings.plaf.FrameCG;
23 import org.wings.resource.DynamicCodeResource;
24 import org.wings.resource.DynamicResource;
25 import org.wings.session.SessionManager;
26 import org.wings.style.StyleSheet;
27 import org.wings.util.ComponentVisitor;
28
29 import java.beans.PropertyChangeEvent JavaDoc;
30 import java.beans.PropertyChangeListener JavaDoc;
31 import java.io.IOException JavaDoc;
32 import java.util.ArrayList JavaDoc;
33 import java.util.HashMap JavaDoc;
34 import java.util.List JavaDoc;
35
36 /**
37  * The frame is the root component in every component hierarchie.
38  * A SessionServlet requires an instance of SFrame to render the page.
39  * SFrame consists of some header informaton (meta, link, script)
40  * and a stack of components. The bottommost component of the stack is always
41  * the contentPane. When dialogs are to be shown, they are stacked on top of
42  * it.
43  *
44  * @author <a HREF="mailto:hengels@mercatis.de">Holger Engels</a>,
45  * <a HREF="mailto:haaf@mercatis.de">Armin Haaf</a>
46  * @version $Revision: 1.14 $
47  */

48 public class SFrame
49         extends SRootContainer
50         implements PropertyChangeListener JavaDoc, LowLevelEventListener {
51
52     private final transient static Log log = LogFactory.getLog(SFrame.class);
53
54     /**
55      * The Title of the Frame.
56      */

57     protected String JavaDoc title;
58
59     /**
60      * A Set containing additional tags for the html header.
61      */

62     protected List JavaDoc headers;
63
64     /**
65      * the style sheet used in certain look and feels.
66      */

67     protected StyleSheet styleSheet; // IMPORTANT: initialization with null causes errors;
68
// These: all properties, that are installed by the plaf, are installed during the initialization of
69
// SComponent. The null initializations happen afterwards and overwrite the plaf installed values.
70
// However: null is the default initialization value, so this is not a problem!
71
// The same applies to all descendants of SComponent!
72

73     protected String JavaDoc statusLine;
74
75     private RequestURL requestURL = null;
76     private String JavaDoc targetResource;
77
78     private HashMap JavaDoc dynamicResources;
79
80     private SComponent focusComponent = null; //Component which requests the focus
81

82     /**
83      * @see #setBackButton(SButton)
84      */

85     private SButton backButton;
86
87     /*
88      * see fireDefaultBackButton()
89      */

90     private long defaultBackButtonLastPressedTime;
91
92     /**
93      * @see #setNoCaching(boolean)
94      */

95     private boolean noCaching = true;
96
97     /**
98      * For performance reasons.
99      *
100      * @see #fireInvalidLowLevelEventListener
101      */

102     private boolean fireInvalidLowLevelEvents = false;
103
104     /**
105      * Creates a new SFrame
106      */

107     public SFrame() {
108         getSession().addPropertyChangeListener("lookAndFeel", this);
109         getSession().addPropertyChangeListener("request.url", this);
110     }
111
112     /**
113      * Creates a new SFrame
114      *
115      * @param title Title of this frame, rendered in browser window title
116      */

117     public SFrame(String JavaDoc title) {
118         this();
119         setTitle(title);
120     }
121
122     /**
123      * Adds a dynamic ressoure.
124      *
125      * @see #getDynamicResource(Class)
126      */

127     public void addDynamicResource(DynamicResource d) {
128         if (dynamicResources == null) {
129             dynamicResources = new HashMap JavaDoc();
130         }
131         dynamicResources.put(d.getClass(), d);
132     }
133
134     /**
135      * Removes the instance of the dynamic ressource of the given class.
136      *
137      * @param dynamicResourceClass Class of dynamic ressource to remove
138      * @see #getDynamicResource(Class)
139      */

140     public void removeDynamicResource(Class JavaDoc dynamicResourceClass) {
141         if (dynamicResources != null) {
142             dynamicResources.remove(dynamicResourceClass);
143         }
144     }
145
146     /**
147      * Severeral Dynamic code Ressources are attached to a <code>SFrame</code>.
148      * <br>See <code>Frame.plaf</code> for details, but in general you wil find attached
149      * to every <code>SFrame</code> a
150      * <ul><li>A {@link DynamicCodeResource} rendering the HTML-Code of all SComponents inside this frame.
151      * <li>A {@link org.wings.script.DynamicScriptResource} rendering the attached (Java-)Scripts of all SComponents
152      * into an external file and including them by a link tag into the rendered frame.
153      * <li>A {@link org.wings.style.DynamicStyleSheetResource} rendering the CSS attributes
154      * of all SComponents inside this frame into an external file with CSS classes.
155      * </ul>
156      */

157     public DynamicResource getDynamicResource(Class JavaDoc c) {
158         if (dynamicResources == null) {
159             dynamicResources = new HashMap JavaDoc();
160         }
161         return (DynamicResource) dynamicResources.get(c);
162     }
163
164     /**
165      * Return <code>this</code>.
166      *
167      * @return this.
168      */

169     public SFrame getParentFrame() {
170         return this;
171     }
172
173     /**
174      * A String with the current epoch of this SFrame. Provided by the
175      * {@link DynamicCodeResource} rendering this frame.
176      *
177      * @return A String with current epoch. Increased on every invalidation.
178      */

179     public String JavaDoc getEventEpoch() {
180         return getDynamicResource(DynamicCodeResource.class).getEpoch();
181     }
182
183     /**
184      * Set server address.
185      */

186     public final void setRequestURL(RequestURL requestURL) {
187         this.requestURL = requestURL;
188     }
189
190     /**
191      * Returns the base URL for a request to the WingsServlet. This URL
192      * is used to assemble an URL that trigger events. In order to be used
193      * for this purpose, you've to add your parameters here.
194      */

195     public final RequestURL getRequestURL() {
196         RequestURL result = null;
197         // first time we are called, and we didn't get any change yet
198
if (requestURL == null) {
199             requestURL = (RequestURL) SessionManager.getSession().getProperty("request.url");
200         }
201         if (requestURL != null) {
202             result = (RequestURL) requestURL.clone();
203             result.setResource(getTargetResource());
204         }
205         return result;
206     }
207
208     /**
209      * Set the target resource
210      */

211     public void setTargetResource(String JavaDoc targetResource) {
212         this.targetResource = targetResource;
213     }
214
215     /**
216      * Every externalized ressource has an id. A frame is a <code>DynamicCodeResource</code>.
217      *
218      * @return The id of this <code>DynamicCodeResource</code>
219      */

220     public String JavaDoc getTargetResource() {
221         if (targetResource == null) {
222             targetResource = getDynamicResource(DynamicCodeResource.class).getId();
223         }
224         return targetResource;
225     }
226
227     /**
228      * Add an {@link Renderable} into the header of the HTML page
229      *
230      * @param m is typically a {@link org.wings.header.Link} or {@link DynamicResource}.
231      * @see org.wings.header.Link
232      * @see org.wings.script.DynamicScriptResource
233      * @see DynamicCodeResource
234      */

235     public void addHeader(Object JavaDoc m) {
236         if (!headers().contains(m))
237             headers.add(m);
238     }
239
240     /**
241      * @see #addHeader(Object)
242      */

243     public void removeHeader(Object JavaDoc m) {
244         headers.remove(m);
245     }
246
247     /**
248      * Removes all headers. Be carful about what you do!
249      *
250      * @see #addHeader(Object)
251      */

252     public void clearHeaders() {
253         headers().clear();
254     }
255
256     /**
257      * @see #addHeader(Object)
258      */

259     public List JavaDoc headers() {
260         if (headers == null)
261             headers = new ArrayList JavaDoc(2);
262         return headers;
263     }
264
265     /**
266      * Sets the title of this HTML page. Typically shown in the browsers window title.
267      *
268      * @param title The window title.
269      */

270     public void setTitle(String JavaDoc title) {
271         this.title = title;
272     }
273
274     /**
275      * Title of this HTML page. Typically shown in the browsers window title.
276      *
277      * @return Current page title
278      */

279     public String JavaDoc getTitle() {
280         return title;
281     }
282
283     public void setStatusLine(String JavaDoc s) {
284         statusLine = s;
285     }
286
287     /**
288      * @return <code>true</code> if the generated HTML code of this frame/page should
289      * not be cached by browser, <code>false</code> if no according HTTP/HTML headers
290      * should be rendered
291      * @see #setNoCaching(boolean)
292      */

293     public boolean isNoCaching() {
294         return noCaching;
295     }
296
297     public void write(Device s) throws IOException JavaDoc {
298         if (isNoCaching())
299             reload(); // invalidate frame on each rendering!
300
super.write(s);
301     }
302
303     /**
304      * Typically you don't want any wings application to operate on old 'views' meaning
305      * old pages. Hence all generated HTML pages (<code>SFrame</code> objects
306      * rendered through {@link DynamicCodeResource} are marked as <b>do not cache</b>
307      * inside the HTTP response header and the generated HTML frame code.
308      * <p>If for any purpose (i.e. you a writing a read only application) you want
309      * th user to be able to work on old views then set this to <code>false</code>
310      * and Mark the according <code>SComponent</code>s to be not epoch checked
311      * (i.e. {@link SAbstractButton#setEpochCheckEnabled(boolean)})
312      *
313      * @param noCaching The noCaching to set.
314      * @see LowLevelEventListener#isEpochCheckEnabled()
315      * @see org.wings.session.LowLevelEventDispatcher
316      */

317     public void setNoCaching(boolean noCaching) {
318         this.noCaching = noCaching;
319     }
320
321     /**
322      * Shows this frame. This means it gets registered at the session.
323      *
324      * @see org.wings.session.Session#getFrames()
325      */

326     public void show() {
327         setVisible(true);
328     }
329
330     /**
331      * Hides this frame. This means it gets removed at the session.
332      *
333      * @see org.wings.session.Session#getFrames()
334      */

335     public void hide() {
336         setVisible(false);
337     }
338
339     /**
340      * Shows or hide this frame. This means it gets (un)registered at the session.
341      *
342      * @see org.wings.session.Session#getFrames()
343      */

344     public void setVisible(boolean b) {
345         if (b) {
346             getSession().addFrame(this);
347         } else {
348             getSession().removeFrame(this);
349         }
350         super.setVisible(b);
351     }
352
353     public void propertyChange(PropertyChangeEvent JavaDoc pe) {
354         if ("lookAndFeel".equals(pe.getPropertyName())) {
355             updateComponentTreeCG(getContentPane());
356         }
357         if ("request.url".equals(pe.getPropertyName())) {
358             setRequestURL((RequestURL) pe.getNewValue());
359         }
360     }
361
362     private void updateComponentTreeCG(SComponent c) {
363         c.updateCG();
364         if (c instanceof SContainer) {
365             SComponent[] children = ((SContainer) c).getComponents();
366             for (int i = 0; i < children.length; i++) {
367                 updateComponentTreeCG(children[i]);
368             }
369         }
370         updateCG();
371     }
372
373     public void setCG(FrameCG cg) {
374         super.setCG(cg);
375     }
376
377     public void invite(ComponentVisitor visitor)
378             throws Exception JavaDoc {
379         visitor.visit(this);
380     }
381
382     /*
383      * This function is called by SComponent.requestFocus().
384      * @param c the component which requests the focus.
385      */

386     public void setFocus(SComponent c) {
387         focusComponent = c;
388     }
389
390     public SComponent getFocus() {
391         return focusComponent;
392     }
393
394     public void processLowLevelEvent(String JavaDoc name, String JavaDoc[] values) {
395         if (values.length == 1) {
396             String JavaDoc eventId = values[0];
397             eventId = eventId.substring("focus_".length());
398             SComponent component = (SComponent) getDispatcher().getLowLevelEventListener(eventId);
399             component.requestFocus();
400         }
401     }
402
403     /**
404      * Registers an {@link SInvalidLowLevelEventListener} in this frame.
405      *
406      * @param l The listener to notify about outdated reqests
407      * @see org.wings.event.InvalidLowLevelEvent
408      */

409     public final void addInvalidLowLevelEventListener(SInvalidLowLevelEventListener l) {
410         addEventListener(SInvalidLowLevelEventListener.class, l);
411         fireInvalidLowLevelEvents = true;
412     }
413
414     /**
415      * Removes the passed {@link SInvalidLowLevelEventListener} from this frame.
416      *
417      * @param l The listener to remove
418      * @see org.wings.event.InvalidLowLevelEvent
419      */

420
421     public final void removeDispatchListener(SInvalidLowLevelEventListener l) {
422         removeEventListener(SRenderListener.class, l);
423     }
424
425     /**
426      * Notify all {@link SInvalidLowLevelEventListener} about an outdated request
427      * on the passed component
428      *
429      * @param source The <code>SComponent</code> received an outdated event
430      * @see org.wings.session.LowLevelEventDispatcher
431      */

432     public final void fireInvalidLowLevelEventListener(LowLevelEventListener source) {
433         if (fireInvalidLowLevelEvents) {
434             Object JavaDoc[] listeners = getListenerList();
435             InvalidLowLevelEvent e = null;
436             for (int i = listeners.length - 2; i >= 0; i -= 2) {
437                 if (listeners[i] == SInvalidLowLevelEventListener.class) {
438                     // Lazily create the event:
439
if (e == null)
440                         e = new InvalidLowLevelEvent(source);
441                     ((SInvalidLowLevelEventListener) listeners[i + 1]).invalidLowLevelEvent(e);
442                 }
443             }
444         }
445         fireDefaultBackButton();
446     }
447
448
449     /**
450      * A button activated on detected browser back clicks.
451      *
452      * @return Returns the backButton.
453      * @see #setBackButton(SButton)
454      */

455     public SButton getBackButton() {
456         return backButton;
457     }
458
459     /**
460      * This button allows you to programattically react on Back buttons pressed in the browser itselfs.
461      * This is a convenience method in contrast to {@link #addInvalidLowLevelEventListener(SInvalidLowLevelEventListener)}.
462      * While the listener throws an event on every detected component receiving an invalid
463      * request, this button is only activated if
464      * <ul>
465      * <li>Maximum once per request
466      * <li>Only if some time passed by to avoid double-clicks to be recognized as back button clicks.
467      * </ul>
468      * <b>Note:</b> To work correctly you should set use GET posting
469      * {@link SForm#setPostMethod(boolean)} and use {@link SFrame#setNoCaching(boolean)} for
470      * no caching. This will advise the browser to reload every back page.
471      *
472      * @param defaultBackButton A button to trigger upon detected invalid epochs.
473      */

474     public void setBackButton(SButton defaultBackButton) {
475         this.backButton = defaultBackButton;
476     }
477
478     /**
479      * Fire back button only once and if some time already passed by to avoid double clicks.
480      */

481     private void fireDefaultBackButton() {
482         final int TIME_TO_ASSUME_DOUBLECLICKS_MS = 750;
483         if (this.backButton != null) {
484             long currentTime = System.currentTimeMillis();
485             if (currentTime - defaultBackButtonLastPressedTime > TIME_TO_ASSUME_DOUBLECLICKS_MS) {
486                 // Simulate a button press
487
backButton.processLowLevelEvent(null, new String JavaDoc[]{"1"});
488             }
489             defaultBackButtonLastPressedTime = currentTime;
490         }
491     }
492
493     public void fireIntermediateEvents() {
494     }
495
496     /**
497      * @see LowLevelEventListener#isEpochCheckEnabled()
498      */

499     private boolean epochCheckEnabled = true;
500
501     /**
502      * @see LowLevelEventListener#isEpochCheckEnabled()
503      */

504     public boolean isEpochCheckEnabled() {
505         return epochCheckEnabled;
506     }
507
508     /**
509      * @see LowLevelEventListener#isEpochCheckEnabled()
510      */

511     public void setEpochCheckEnabled(boolean epochCheckEnabled) {
512         this.epochCheckEnabled = epochCheckEnabled;
513     }
514 }
515
Popular Tags