001: /*
002: * Copyright 2000,2005 wingS development team.
003: *
004: * This file is part of wingS (http://wingsframework.org).
005: *
006: * wingS is free software; you can redistribute it and/or modify
007: * it under the terms of the GNU Lesser General Public License
008: * as published by the Free Software Foundation; either version 2.1
009: * of the License, or (at your option) any later version.
010: *
011: * Please see COPYING for the complete licence.
012: */
013: package org.wings;
014:
015: import org.apache.commons.logging.Log;
016: import org.apache.commons.logging.LogFactory;
017: import org.wings.event.*;
018: import org.wings.header.SessionHeaders;
019: import org.wings.io.Device;
020: import org.wings.plaf.FrameCG;
021: import org.wings.resource.DynamicResource;
022: import org.wings.resource.ReloadResource;
023: import org.wings.session.SessionManager;
024: import org.wings.style.StyleSheet;
025: import org.wings.util.ComponentVisitor;
026: import org.wings.util.StringUtil;
027:
028: import javax.swing.*;
029: import java.beans.PropertyChangeEvent;
030: import java.beans.PropertyChangeListener;
031: import java.io.IOException;
032: import java.util.*;
033:
034: /**
035: * The root component of every component hierarchy.
036: * <p/>
037: * A SessionServlet requires an instance of SFrame to render the page.
038: * SFrame consists of some header informaton (meta, link, script)
039: * and a stack of components. The bottommost component of the stack is always
040: * the contentPane. When dialogs are to be shown, they are stacked on top of
041: * it.
042: *
043: * @author Holger Engels,
044: * <a href="mailto:haaf@mercatis.de">Armin Haaf</a>
045: */
046: public class SFrame extends SRootContainer implements
047: PropertyChangeListener, LowLevelEventListener {
048: private final transient static Log log = LogFactory
049: .getLog(SFrame.class);
050:
051: /**
052: * The Title of the Frame.
053: */
054: protected String title = "Untitled";
055:
056: /**
057: * A list of all header used by this frame.
058: */
059: protected List headers = new ArrayList();
060:
061: /**
062: * the style sheet used in certain look and feels.
063: */
064: protected StyleSheet styleSheet; // IMPORTANT: initialization with null causes errors;
065: // These: all properties, that are installed by the plaf, are installed during the initialization of
066: // SComponent. The null initializations happen afterwards and overwrite the plaf installed values.
067: // However: null is the default initialization value, so this is not a problem!
068: // The same applies to all descendants of SComponent!
069:
070: protected String statusLine;
071:
072: /**
073: * The event epoch of this frame which is incremented with each invalidation.
074: */
075: private int eventEpoch = 0;
076:
077: /**
078: * The event epoch of this frame represented as a (preferably short) string.
079: */
080: private String epochCache = "W"
081: + StringUtil.toShortestAlphaNumericString(eventEpoch);
082:
083: private RequestURL requestURL = null;
084:
085: private String targetResource;
086:
087: private HashMap dynamicResources;
088:
089: private boolean updateEnabled;
090:
091: private Map<String, Object> updateCursor;
092:
093: private Map<String, Object> autoAdjustLayout;
094:
095: private SComponent focusComponent = null; // Component which requests the focus
096:
097: /**
098: * @see #setBackButton(SButton)
099: */
100: private SButton backButton;
101:
102: /*
103: * see fireDefaultBackButton()
104: */
105: private LastRequestListener backbuttonRequestListener;
106:
107: /**
108: * @see #setNoCaching(boolean)
109: */
110: private boolean noCaching = true;
111:
112: /**
113: * For performance reasons.
114: *
115: * @see #fireInvalidLowLevelEventListener
116: */
117: private boolean fireInvalidLowLevelEvents = false;
118:
119: /**
120: * we need to overwrite the inherited input map from SComponent.
121: */
122: private InputMap myInputMap;
123:
124: /**
125: * Global input maps
126: */
127: private HashSet<SComponent> globalInputMapComponents = new HashSet<SComponent>();
128:
129: /**
130: * Should we send JS Headers in debug mode?
131: */
132: private String logLevel = "off";
133:
134: /**
135: * Creates a new SFrame
136: */
137: public SFrame() {
138: getSession().addPropertyChangeListener("lookAndFeel", this );
139: getSession().addPropertyChangeListener("request.url", this );
140: this .visible = false; // Frames are invisible originally.
141:
142: setUpdateEnabled(true);
143:
144: Map<String, Object> updateCursor = new HashMap<String, Object>();
145: SIcon cursorImage = new SResourceIcon(
146: "org/wings/icons/AjaxActivityCursor.gif");
147: updateCursor.put("enabled", true);
148: updateCursor.put("image", cursorImage.getURL().toString());
149: updateCursor.put("width", cursorImage.getIconWidth());
150: updateCursor.put("height", cursorImage.getIconHeight());
151: updateCursor.put("dx", 15);
152: updateCursor.put("dy", 0);
153: setUpdateCursor(updateCursor);
154:
155: Map<String, Object> autoAdjustLayout = new HashMap<String, Object>();
156: autoAdjustLayout.put("enabled", true);
157: autoAdjustLayout.put("delay", 250);
158: setAutoAdjustLayout(autoAdjustLayout);
159:
160: addStyle("yui-skin-sam");
161: }
162:
163: /**
164: * Creates a new SFrame
165: *
166: * @param title Title of this frame, rendered in browser window title
167: */
168: public SFrame(String title) {
169: this ();
170: setTitle(title);
171: }
172:
173: /**
174: * Adds a dynamic ressoure.
175: *
176: * @see #getDynamicResource(Class)
177: */
178: public void addDynamicResource(DynamicResource d) {
179: if (dynamicResources == null) {
180: dynamicResources = new HashMap();
181: }
182: dynamicResources.put(d.getClass(), d);
183: }
184:
185: /**
186: * Removes the instance of the dynamic ressource of the given class.
187: *
188: * @param dynamicResourceClass Class of dynamic ressource to remove
189: * @see #getDynamicResource(Class)
190: */
191: public void removeDynamicResource(Class dynamicResourceClass) {
192: if (dynamicResources != null) {
193: dynamicResources.remove(dynamicResourceClass);
194: }
195: }
196:
197: /**
198: * Severeral Dynamic code Ressources are attached to a <code>SFrame</code>.
199: * <br>See <code>Frame.plaf</code> for details, but in general you wil find attached
200: * to every <code>SFrame</code> a
201: * <ul><li>A {@link ReloadResource} rendering the HTML-Code of all SComponents inside this frame.
202: * </ul>
203: */
204: public DynamicResource getDynamicResource(Class c) {
205: if (dynamicResources == null) {
206: dynamicResources = new HashMap();
207: }
208: return (DynamicResource) dynamicResources.get(c);
209: }
210:
211: /**
212: * @return all dynamic script resources
213: */
214: public Collection getDynamicResources() {
215: if (dynamicResources == null) {
216: dynamicResources = new HashMap();
217: }
218: return dynamicResources.values();
219: }
220:
221: /**
222: * Return <code>this</code>.
223: *
224: * @return this.
225: */
226: public SFrame getParentFrame() {
227: return this ;
228: }
229:
230: /**
231: * Invalidate this frame by incrementing its event epoch. This method is called
232: * whenever a change took place inside this frame - that is whenever one of its
233: * child components has become dirty. In consequence each dynamic resource which
234: * is attached to this frame has to be externalized with a new "version-number".
235: */
236: public final void invalidate() {
237: epochCache = "W" + (++eventEpoch);
238: if (isUpdatePossible()
239: && SFrame.class.isAssignableFrom(getClass()))
240: update(((FrameCG) getCG()).getEpochUpdate(this , epochCache));
241:
242: if (log.isDebugEnabled()) {
243: log.debug("Event epoch of " + this
244: + " has been invalidated: " + epochCache);
245: }
246: }
247:
248: public final String getEventEpoch() {
249: return epochCache;
250: }
251:
252: /**
253: * Set server address.
254: */
255: public final void setRequestURL(RequestURL requestURL) {
256: this .requestURL = requestURL;
257: }
258:
259: /**
260: * Returns the base URL for a request to the WingsServlet. This URL
261: * is used to assemble an URL that trigger events. In order to be used
262: * for this purpose, you've to add your parameters here.
263: */
264: public final RequestURL getRequestURL() {
265: RequestURL result = null;
266: // first time we are called, and we didn't get any change yet
267: if (requestURL == null) {
268: requestURL = (RequestURL) getSession().getProperty(
269: "request.url");
270: }
271: if (requestURL != null) {
272: result = (RequestURL) requestURL.clone();
273: result.setEventEpoch(getEventEpoch());
274: result.setResource(getTargetResource());
275: }
276: return result;
277: }
278:
279: /**
280: * Set the target resource
281: */
282: public void setTargetResource(String targetResource) {
283: this .targetResource = targetResource;
284: }
285:
286: /**
287: * Every externalized ressource has an id. A frame is a <code>ReloadResource</code>.
288: *
289: * @return The id of this <code>ReloadResource</code>
290: */
291: public String getTargetResource() {
292: if (targetResource == null) {
293: targetResource = getDynamicResource(ReloadResource.class)
294: .getId();
295: }
296: return targetResource;
297: }
298:
299: /**
300: * Add an {@link Renderable} into the header of the HTML page
301: *
302: * @param headerElement is typically a {@link org.wings.header.Link} or {@link DynamicResource}.
303: * @see org.wings.header.Link
304: */
305: public void addHeader(Object headerElement) {
306: if (!headers.contains(headerElement) && headerElement != null) {
307: headers.add(headerElement);
308: if (isUpdatePossible()
309: && SFrame.class.isAssignableFrom(getClass()))
310: update(((FrameCG) getCG()).getAddHeaderUpdate(this ,
311: headerElement));
312: else
313: reload();
314: }
315: }
316:
317: /**
318: * Add an {@link Renderable} into the header of the HTML page at the desired index position
319: *
320: * @param headerElement is typically a {@link org.wings.header.Link} or {@link DynamicResource}.
321: * @param index index in header list to add this item
322: * @see org.wings.header.Link
323: * @see #getHeaders()
324: */
325: public void addHeader(int index, Object headerElement) {
326: if (!headers.contains(headerElement) && headerElement != null) {
327: headers.add(index, headerElement);
328: if (isUpdatePossible()
329: && SFrame.class.isAssignableFrom(getClass()))
330: update(((FrameCG) getCG()).getAddHeaderUpdate(this ,
331: index, headerElement));
332: else
333: reload();
334: }
335: }
336:
337: /**
338: * @see #addHeader(Object)
339: * @return <tt>true</tt> if this frame contained the specified header element.
340: */
341: public boolean removeHeader(Object headerElement) {
342: boolean deleted = headers.remove(headerElement);
343: if (deleted) {
344: if (isUpdatePossible()
345: && SFrame.class.isAssignableFrom(getClass()))
346: update(((FrameCG) getCG()).getRemoveHeaderUpdate(this ,
347: headerElement));
348: else
349: reload();
350: }
351: return deleted;
352: }
353:
354: /**
355: * Removes all headers. Be carful about what you do!
356: *
357: * @see #addHeader(Object)
358: */
359: public void clearHeaders() {
360: headers.clear();
361: reload();
362: }
363:
364: /**
365: * @see #addHeader(Object)
366: * @deprecated Use {@link #getHeaders()} instead
367: */
368: public List headers() {
369: return getHeaders();
370: }
371:
372: /**
373: * @see #addHeader(Object)
374: */
375: public List getHeaders() {
376: return Collections.unmodifiableList(headers);
377: }
378:
379: /**
380: * Sets the title of this HTML page. Typically shown in the browsers window title.
381: *
382: * @param title The window title.
383: */
384: public void setTitle(String title) {
385: if (title == null) {
386: title = "";
387: }
388: if (!title.equals(this .title)) {
389: this .title = title;
390: reload();
391: }
392: }
393:
394: /**
395: * Title of this HTML page. Typically shown in the browsers window title.
396: *
397: * @return the current page title or an empty string if this page doesn't have a title.
398: */
399: public String getTitle() {
400: return title;
401: }
402:
403: public void setStatusLine(String s) {
404: statusLine = s;
405: }
406:
407: /**
408: * @return <code>true</code> if the generated HTML code of this frame/page should
409: * not be cached by browser, <code>false</code> if no according HTTP/HTML headers
410: * should be rendered
411: * @see #setNoCaching(boolean)
412: */
413: public boolean isNoCaching() {
414: return noCaching;
415: }
416:
417: public void write(Device s) throws IOException {
418: if (isNoCaching()) {
419: reload(); // invalidate frame on each rendering!
420: }
421: super .write(s);
422: }
423:
424: /**
425: * Typically you don't want any wings application to operate on old 'views' meaning
426: * old pages. Hence all generated HTML pages (<code>SFrame</code> objects
427: * rendered through {@link ReloadResource} are marked as <b>do not cache</b>
428: * inside the HTTP response header and the generated HTML frame code.
429: * <p>If for any purpose (i.e. you a writing a read only application) you want
430: * th user to be able to work on old views then set this to <code>false</code>
431: * and Mark the according <code>SComponent</code>s to be not epoch checked
432: * (i.e. {@link SAbstractButton#setEpochCheckEnabled(boolean)})
433: *
434: * @param noCaching The noCaching to set.
435: * @see LowLevelEventListener#isEpochCheckEnabled()
436: * @see org.wings.session.LowLevelEventDispatcher
437: */
438: public void setNoCaching(boolean noCaching) {
439: this .noCaching = noCaching;
440: }
441:
442: /**
443: * Shows this frame. This means it gets registered at the session.
444: *
445: * @see org.wings.session.Session#getFrames()
446: */
447: public void show() {
448: setVisible(true);
449: }
450:
451: /**
452: * Hides this frame. This means it gets removed at the session.
453: *
454: * @see org.wings.session.Session#getFrames()
455: */
456: public void hide() {
457: setVisible(false);
458: }
459:
460: /**
461: * Shows or hide this frame. This means it gets (un)registered at the session.
462: *
463: * @see org.wings.session.Session#getFrames()
464: */
465: public void setVisible(boolean visible) {
466: if (visible != isVisible()) {
467: if (visible) {
468: List newHeaders = new ArrayList(SessionHeaders
469: .getInstance().getHeaders());
470: for (Iterator i = headers.iterator(); i.hasNext();) {
471: Object oldHeaders = i.next();
472: if (!newHeaders.contains(oldHeaders)) {
473: newHeaders.add(oldHeaders);
474: }
475: }
476: headers = newHeaders;
477: getSession().addFrame(this );
478: register();
479: } else {
480: getSession().removeFrame(this );
481: unregister();
482: }
483: super .setVisible(visible);
484: }
485: }
486:
487: public void propertyChange(PropertyChangeEvent pe) {
488: if ("lookAndFeel".equals(pe.getPropertyName())) {
489: updateComponentTreeCG(getContentPane());
490: }
491: if ("request.url".equals(pe.getPropertyName())) {
492: setRequestURL((RequestURL) pe.getNewValue());
493: }
494: }
495:
496: private void updateComponentTreeCG(SComponent c) {
497: c.updateCG();
498: if (c instanceof SContainer) {
499: SComponent[] children = ((SContainer) c).getComponents();
500: for (int i = 0; i < children.length; i++) {
501: updateComponentTreeCG(children[i]);
502: }
503: }
504: updateCG();
505: }
506:
507: public void setCG(FrameCG cg) {
508: super .setCG(cg);
509: }
510:
511: public void invite(ComponentVisitor visitor) throws Exception {
512: visitor.visit(this );
513: }
514:
515: /**
516: * Choose which component rendered inside this frame should gain the edit focus on next rendering
517: * This function is called by {@link SComponent#requestFocus()}
518: *
519: * @param focusOnComponent the component which requests the focus.
520: */
521: public void setFocus(SComponent focusOnComponent) {
522: focusComponent = focusOnComponent;
523: if (focusComponent != null && isUpdatePossible())
524: update(((FrameCG) getCG()).getFocusUpdate(this ,
525: focusComponent));
526: }
527:
528: /**
529: * @see #setFocus(SComponent)
530: */
531: public SComponent getFocus() {
532: return focusComponent;
533: }
534:
535: public void processLowLevelEvent(String name, String[] values) {
536: focusComponent = null;
537: if (values.length == 1 && name.endsWith("_focus")) {
538: String eventId = values[0];
539: List listeners = getSession().getDispatcher()
540: .getLowLevelEventListener(eventId);
541: for (int i = 0; i < listeners.size()
542: && focusComponent == null; i++) {
543: Object listener = listeners.get(i);
544: if (listener instanceof SComponent) {
545: this .focusComponent = (SComponent) listener;
546: }
547: }
548: }
549: /*
550: * When there is a debug Cookie,
551: * change the debug headers in the CG according to the value of the
552: * cookie.
553: */
554: if (name.endsWith("_debug")) {
555: log.info("input " + name + values);
556: String newLogLevel = (values.length == 1) ? values[0] != null ? values[0]
557: : "off"
558: : "off";
559: logLevel = newLogLevel;
560: }
561: }
562:
563: /**
564: * Registers an {@link SInvalidLowLevelEventListener} in this frame.
565: *
566: * @param l The listener to notify about outdated reqests
567: * @see org.wings.event.InvalidLowLevelEvent
568: */
569: public final void addInvalidLowLevelEventListener(
570: SInvalidLowLevelEventListener l) {
571: addEventListener(SInvalidLowLevelEventListener.class, l);
572: fireInvalidLowLevelEvents = true;
573: }
574:
575: /**
576: * Removes the passed {@link SInvalidLowLevelEventListener} from this frame.
577: *
578: * @param l The listener to remove
579: * @see org.wings.event.InvalidLowLevelEvent
580: */
581:
582: public final void removeDispatchListener(
583: SInvalidLowLevelEventListener l) {
584: removeEventListener(SInvalidLowLevelEventListener.class, l);
585: }
586:
587: /**
588: * Notify all {@link SInvalidLowLevelEventListener} about an outdated request
589: * on the passed component
590: *
591: * @param source The <code>SComponent</code> received an outdated event
592: * @see org.wings.session.LowLevelEventDispatcher
593: */
594: public final void fireInvalidLowLevelEventListener(
595: LowLevelEventListener source) {
596: if (fireInvalidLowLevelEvents) {
597: Object[] listeners = getListenerList();
598: InvalidLowLevelEvent e = null;
599: for (int i = listeners.length - 2; i >= 0; i -= 2) {
600: if (listeners[i] == SInvalidLowLevelEventListener.class) {
601: // Lazily create the event:
602: if (e == null) {
603: e = new InvalidLowLevelEvent(source);
604: }
605: ((SInvalidLowLevelEventListener) listeners[i + 1])
606: .invalidLowLevelEvent(e);
607: }
608: }
609: }
610: fireDefaultBackButton();
611: }
612:
613: /**
614: * A button activated on detected browser back clicks.
615: *
616: * @return Returns the backButton.
617: * @see #setBackButton(SButton)
618: */
619: public SButton getBackButton() {
620: return backButton;
621: }
622:
623: /**
624: * This button allows you to programattically react on Back buttons pressed in the browser itselfs.
625: * This is a convenience method in contrast to {@link #addInvalidLowLevelEventListener(SInvalidLowLevelEventListener)}.
626: * While the listener throws an event on every detected component receiving an invalid
627: * request, this button is only activated if
628: * <ul>
629: * <li>Maximum once per request
630: * <li>Only if some time passed by to avoid double-clicks to be recognized as back button clicks.
631: * </ul>
632: * <b>Note:</b> To work correctly you should set use GET posting
633: * {@link SForm#setPostMethod(boolean)} and use {@link SFrame#setNoCaching(boolean)} for
634: * no caching. This will advise the browser to reload every back page.
635: *
636: * @param defaultBackButton A button to trigger upon detected invalid epochs.
637: */
638: public void setBackButton(SButton defaultBackButton) {
639: if (backbuttonRequestListener == null) {
640: backbuttonRequestListener = new LastRequestListener();
641: getSession().addRequestListener(backbuttonRequestListener);
642: }
643: this .backButton = defaultBackButton;
644: }
645:
646: /**
647: * Fire back button only once and if some time already passed by to avoid double clicks.
648: */
649: private void fireDefaultBackButton() {
650: final int DISPATCHINGFINISH_BACKBUTTON_DEADTIME = 500;
651: if (this .backButton != null
652: && this .backbuttonRequestListener != null) {
653: long currentTime = System.currentTimeMillis();
654: if (currentTime
655: - backbuttonRequestListener.lastDispatchingFinished > DISPATCHINGFINISH_BACKBUTTON_DEADTIME) {
656: // Simulate a button press
657: backButton.processLowLevelEvent(null,
658: new String[] { "1" });
659: }
660: }
661: }
662:
663: public void fireIntermediateEvents() {
664: }
665:
666: /**
667: * @see LowLevelEventListener#isEpochCheckEnabled()
668: */
669: private boolean epochCheckEnabled = true;
670:
671: /**
672: * @see LowLevelEventListener#isEpochCheckEnabled()
673: */
674: public boolean isEpochCheckEnabled() {
675: return epochCheckEnabled;
676: }
677:
678: /**
679: * @see LowLevelEventListener#isEpochCheckEnabled()
680: */
681: public void setEpochCheckEnabled(boolean epochCheckEnabled) {
682: this .epochCheckEnabled = epochCheckEnabled;
683: }
684:
685: /**
686: * custom error handling. If you want to catch application errors,
687: * return true here.
688: * @param e The throwable causing this.
689: * @return does this frame handle errors...
690: */
691: public boolean handleError(Throwable e) {
692: return false;
693: }
694:
695: public InputMap getInputMap(int condition) {
696: // SFrame has only one inputMap
697: if (myInputMap == null) {
698: myInputMap = new InputMap();
699: }
700: return myInputMap;
701: }
702:
703: public void setInputMap(int condition, InputMap inputMap) {
704: this .myInputMap = inputMap;
705: }
706:
707: public void registerGlobalInputMapComponent(SComponent comp) {
708: if (!globalInputMapComponents.contains(comp)) {
709: // not yet registered
710: globalInputMapComponents.add(comp);
711: }
712: }
713:
714: public void deregisterGlobalInputMapComponent(SComponent comp) {
715: if (globalInputMapComponents != null) {
716: globalInputMapComponents.remove(comp);
717: }
718: }
719:
720: public Set<SComponent> getGlobalInputMapComponents() {
721: return globalInputMapComponents;
722: }
723:
724: /**
725: * Private helper class to remember the end of the last dispatch.
726: * @see org.wings.SFrame#fireDefaultBackButton()
727: */
728: private static class LastRequestListener implements
729: SRequestListener {
730: private long lastDispatchingFinished = -1;
731:
732: public void processRequest(SRequestEvent e) {
733: if (e.getType() == SRequestEvent.DISPATCH_DONE)
734: lastDispatchingFinished = System.currentTimeMillis();
735: }
736: }
737:
738: public boolean isUpdateEnabled() {
739: return updateEnabled;
740: }
741:
742: public void setUpdateEnabled(boolean enabled) {
743: if (updateEnabled != enabled) {
744: if (isUpdatePossible()
745: && SFrame.class.isAssignableFrom(getClass()))
746: update(((FrameCG) getCG()).getUpdateEnabledUpdate(this ,
747: enabled));
748: else
749: reload();
750: updateEnabled = enabled;
751: }
752: }
753:
754: public Map<String, Object> getUpdateCursor() {
755: return updateCursor;
756: }
757:
758: public void setUpdateCursor(Map<String, Object> updateCursor) {
759: if (isDifferent(this .updateCursor, updateCursor)) {
760: this .updateCursor = updateCursor;
761: reload();
762: }
763: }
764:
765: public Map<String, Object> getAutoAdjustLayout() {
766: return autoAdjustLayout;
767: }
768:
769: public void setAutoAdjustLayout(Map<String, Object> autoAdjustLayout) {
770: if (isDifferent(this .autoAdjustLayout, autoAdjustLayout)) {
771: this .autoAdjustLayout = autoAdjustLayout;
772: reload();
773: }
774: }
775:
776: protected void initializeContentPane() {
777: setContentPane(new SForm(new SBorderLayout()));
778: }
779:
780: /**
781: * Tell wether the contentPane is an SForm.
782: * @return <code>true</code> if the contentPane is an SForm, <code>false</code> otherwise
783: */
784: public boolean isFormContentPane() {
785: return contentPane instanceof SForm;
786: }
787:
788: /**
789: * Determine wether the contentPane shall be an SForm. The property is true by default.
790: * @param contentPaneForm <code>true</code> if the contentPane shall be an SForm, <code>false</code> otherwise
791: */
792: public void setFormContentPane(boolean contentPaneForm) {
793: if (contentPane instanceof SForm && !contentPaneForm) {
794: SPanel newPanel = new SPanel();
795: rebuildPanel(contentPane, newPanel);
796: setContentPane(newPanel);
797: } else if (!(contentPane instanceof SForm) && contentPaneForm) {
798: SForm newPanel = new SForm();
799: rebuildPanel(contentPane, newPanel);
800: setContentPane(newPanel);
801: }
802: }
803:
804: private void rebuildPanel(SContainer oldPanel, SContainer newPanel) {
805: SLayoutManager layoutManager = oldPanel.getLayout();
806: SComponent[] components = oldPanel.getComponents();
807: ArrayList constraints = oldPanel.getConstraintList();
808:
809: oldPanel.removeAll();
810: oldPanel.setLayout(null);
811:
812: newPanel.setLayout(layoutManager);
813: for (int i = 0; i < components.length; i++) {
814: SComponent component = components[i];
815: Object constraint = constraints.get(i);
816: newPanel.add(component, constraint);
817: }
818: }
819:
820: /**
821: * @return
822: */
823: public String getLogLevel() {
824: String[] debugSettings = (String[]) SessionManager.getSession()
825: .getProperty("debug.cookie");
826: if (debugSettings != null) {
827: for (int i = 0; i < debugSettings.length; i++) {
828: if (debugSettings[i] != null
829: && debugSettings[i].startsWith("loglevel=")) {
830: return debugSettings[i].substring(9);
831: }
832: }
833: }
834: return null;
835: }
836:
837: public boolean isDebugJs() {
838: String[] debugSettings = (String[]) SessionManager.getSession()
839: .getProperty("debug.cookie");
840: if (debugSettings != null) {
841: for (int i = 0; i < debugSettings.length; i++) {
842: if ("javascript".equals(debugSettings[i])) {
843: return true;
844: }
845: }
846: }
847: return false;
848: }
849: }
|