0001: /*
0002: * Copyright 2000,2005 wingS development team.
0003: *
0004: * This file is part of wingS (http://wingsframework.org).
0005: *
0006: * wingS is free software; you can redistribute it and/or modify
0007: * it under the terms of the GNU Lesser General Public License
0008: * as published by the Free Software Foundation; either version 2.1
0009: * of the License, or (at your option) any later version.
0010: *
0011: * Please see COPYING for the complete licence.
0012: */
0013: package org.wings;
0014:
0015: import org.apache.commons.logging.Log;
0016: import org.apache.commons.logging.LogFactory;
0017: import org.wings.border.SBorder;
0018: import org.wings.event.SComponentEvent;
0019: import org.wings.event.SComponentListener;
0020: import org.wings.event.SParentFrameEvent;
0021: import org.wings.event.SParentFrameListener;
0022: import org.wings.event.SRenderEvent;
0023: import org.wings.event.SRenderListener;
0024: import org.wings.io.Device;
0025: import org.wings.plaf.ComponentCG;
0026: import org.wings.plaf.Update;
0027: import org.wings.script.JavaScriptListener;
0028: import org.wings.script.ScriptListener;
0029: import org.wings.session.Session;
0030: import org.wings.session.SessionManager;
0031: import org.wings.style.CSSAttributeSet;
0032: import org.wings.style.CSSProperty;
0033: import org.wings.style.CSSStyle;
0034: import org.wings.style.CSSStyleSheet;
0035: import org.wings.style.Selector;
0036: import org.wings.style.Style;
0037: import org.wings.util.ComponentVisitor;
0038: import org.wings.util.SStringBuilder;
0039:
0040: import javax.swing.Action;
0041: import javax.swing.ActionMap;
0042: import javax.swing.InputMap;
0043: import javax.swing.JComponent;
0044: import javax.swing.event.EventListenerList;
0045: import java.awt.Color;
0046: import java.awt.Rectangle;
0047: import java.awt.event.ActionEvent;
0048: import java.beans.BeanInfo;
0049: import java.beans.Introspector;
0050: import java.beans.PropertyDescriptor;
0051: import java.io.IOException;
0052: import java.io.ObjectInputStream;
0053: import java.io.ObjectOutputStream;
0054: import java.io.Serializable;
0055: import java.lang.reflect.Array;
0056: import java.lang.reflect.Method;
0057: import java.util.Arrays;
0058: import java.util.Collection;
0059: import java.util.Collections;
0060: import java.util.EventListener;
0061: import java.util.HashMap;
0062: import java.util.LinkedList;
0063: import java.util.List;
0064: import java.util.Map;
0065:
0066: /**
0067: * Object having a graphical representation that can be displayed on the
0068: * screen and that can interact with the user.
0069: *
0070: * @author <a href="mailto:haaf@mercatis.de">Armin Haaf</a>
0071: */
0072: public abstract class SComponent implements Cloneable, Serializable,
0073: Renderable {
0074:
0075: private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
0076:
0077: private static final Log log = LogFactory.getLog(SComponent.class);
0078:
0079: private static final int ACTION_CONDITIONS_AMOUNT = 2;
0080:
0081: /* Components unique name. */
0082: private String name;
0083:
0084: /**
0085: * the session
0086: */
0087: private transient Session session;
0088:
0089: /**
0090: * The code generation delegate, which is responsible for
0091: * the visual representation of this component.
0092: */
0093: private transient ComponentCG cg;
0094:
0095: /**
0096: * Vertical alignment
0097: */
0098: private int verticalAlignment = SConstants.NO_ALIGN;
0099:
0100: /**
0101: * Horizontal alignment
0102: */
0103: private int horizontalAlignment = SConstants.NO_ALIGN;
0104:
0105: /**
0106: * The name of the style class
0107: */
0108: private String style;
0109:
0110: /**
0111: * Map of {@link Selector} to CSS {@link Style}s currently assigned to this component.
0112: */
0113: protected Map<Selector, Style> dynamicStyles;
0114:
0115: /**
0116: * Visibility of the component.
0117: */
0118: protected boolean visible = true;
0119:
0120: /**
0121: * Enabled / disabled.
0122: */
0123: protected boolean enabled = true;
0124:
0125: /**
0126: * The container, this component resides in.
0127: */
0128: private SContainer parent;
0129:
0130: /**
0131: * The frame in which this component resides.
0132: */
0133: private SFrame parentFrame;
0134:
0135: /**
0136: * The border for the component.
0137: */
0138: private SBorder border;
0139:
0140: /**
0141: * The tooltip for this component.
0142: */
0143: private String tooltip;
0144:
0145: /**
0146: * The focus traversal Index
0147: */
0148: private int focusTraversalIndex = -1;
0149:
0150: /**
0151: * Preferred size of component in pixel.
0152: */
0153: private SDimension preferredSize;
0154:
0155: /**
0156: * This is for performance optimizations. With this flag is set, property change
0157: * events are generated and so every property setter method has to test if a property
0158: * has changed and temporarily store the old value to generate the property
0159: * change event
0160: */
0161: private boolean fireComponentChangeEvents = false;
0162:
0163: /**
0164: * Generate and fire {@link SParentFrameEvent}s. Performace optimitation
0165: */
0166: private boolean fireParentFrameChangeEvents = false;
0167:
0168: /**
0169: * Flag indiccating if {@link SRenderEvent}s should be fired. Used for performace reasons
0170: */
0171: private boolean fireRenderEvents = false;
0172:
0173: /**
0174: * All event listeners of this component
0175: */
0176: private EventListenerList listeners;
0177:
0178: private boolean showAsFormComponent = true;
0179:
0180: private boolean reloadForced = false;
0181:
0182: private SPopupMenu popupMenu;
0183:
0184: /*private boolean inheritsPopupMenu;*/
0185:
0186: private InputMap[] inputMaps;
0187:
0188: /**
0189: * Contains all script listeners of the component.
0190: */
0191: private List<ScriptListener> scriptListenerList;
0192:
0193: private ActionMap actionMap;
0194:
0195: private Map<Action, ActionEvent> actionEvents;
0196:
0197: private transient SRenderEvent renderEvent;
0198:
0199: private SParentFrameListener globalInputMapListener;
0200:
0201: private Map<Object, Object> clientProperties;
0202:
0203: /**
0204: * Internal constants for {@link #fireRenderEvent(int)}
0205: */
0206: public static final int START_RENDERING = 1;
0207:
0208: /**
0209: * Internal constants for {@link #fireRenderEvent(int)}
0210: */
0211: public static final int DONE_RENDERING = 2;
0212:
0213: /**
0214: * Constants for conditions on which actions are triggered. Mainly two
0215: * cases: the focus has either to be at the component (or at a child)
0216: * or somewhere in the parent frame.
0217: *
0218: * @see #setInputMap(int, javax.swing.InputMap)
0219: */
0220: public static final int WHEN_FOCUSED_OR_ANCESTOR_OF_FOCUSED_COMPONENT = 0;
0221:
0222: /**
0223: * Constants for conditions on which actions are triggered. Mainly two
0224: * cases: the focus has either to be at the component (or at a child)
0225: * or somewhere in the parent frame.
0226: *
0227: * @see #setInputMap(int, javax.swing.InputMap)
0228: */
0229: public static final int WHEN_IN_FOCUSED_FRAME = 1;
0230:
0231: /**
0232: * Global CSS selector
0233: */
0234: public static final Selector SELECTOR_ALL = new Selector(
0235: "everything");
0236:
0237: /**
0238: * Performance improvement constant
0239: */
0240: private static final ScriptListener[] EMPTY_SCRIPTLISTENERLIST = new ScriptListener[0];
0241:
0242: /**
0243: * Default empty constructor.
0244: * The method updateCG is called during construction time to get a cg delegate installed (renderer).
0245: */
0246: public SComponent() {
0247: updateCG();
0248: }
0249:
0250: /**
0251: * Returns the border of this component or null if no border has been set.
0252: *
0253: * @return the border object
0254: * @see #setBorder(SBorder)
0255: */
0256: public SBorder getBorder() {
0257: return border;
0258: }
0259:
0260: /**
0261: * Sets the border for this component.
0262: *
0263: * @param border the border to be set for the component
0264: */
0265: public void setBorder(SBorder border) {
0266: reloadIfChange(this .border, border);
0267: if (this .border != null)
0268: this .border.setComponent(null);
0269: this .border = border;
0270: if (this .border != null)
0271: this .border.setComponent(this );
0272: }
0273:
0274: /**
0275: * Return the parent container.
0276: *
0277: * @return the container this component resides in
0278: */
0279: public final SContainer getParent() {
0280: return parent;
0281: }
0282:
0283: /**
0284: * Sets the parent container. Also gets the parent frame from the parent.
0285: *
0286: * @param parent the container
0287: */
0288: public void setParent(SContainer parent) {
0289: this .parent = parent;
0290: if (parent != null)
0291: setParentFrame(parent.getParentFrame());
0292: else
0293: setParentFrame(null);
0294: }
0295:
0296: /**
0297: * Sets the parent frame.
0298: *
0299: * @param parentFrame the frame
0300: */
0301: protected void setParentFrame(SFrame parentFrame) {
0302: if (this .parentFrame == parentFrame) {
0303: return;
0304: }
0305:
0306: if (this .parentFrame != null) {
0307: unregister();
0308: fireParentFrameEvent(new SParentFrameEvent(this ,
0309: SParentFrameEvent.PARENTFRAME_REMOVED,
0310: this .parentFrame));
0311: }
0312:
0313: this .parentFrame = parentFrame;
0314:
0315: if (this .parentFrame != null) {
0316: register();
0317: // notify the listeners...
0318: fireParentFrameEvent(new SParentFrameEvent(this ,
0319: SParentFrameEvent.PARENTFRAME_ADDED,
0320: this .parentFrame));
0321: }
0322:
0323: if (this .popupMenu != null) {
0324: popupMenu.setParentFrame(parentFrame);
0325: }
0326:
0327: //reload();
0328: if (getScriptListeners().length > 0)
0329: reload();
0330: if (dynamicStyles != null && dynamicStyles.size() > 0)
0331: reload();
0332: }
0333:
0334: /*public void setInheritsPopupMenu(boolean inheritsPopupMenu) {
0335: reloadIfChange(this.inheritsPopupMenu, inheritsPopupMenu);
0336: this.inheritsPopupMenu = inheritsPopupMenu;
0337: }
0338:
0339: public boolean getInheritsPopupMenu() {
0340: return inheritsPopupMenu;
0341: }*/
0342:
0343: public void setComponentPopupMenu(SPopupMenu popupMenu) {
0344: reloadIfChange(this .popupMenu, popupMenu);
0345: if (this .popupMenu != null) {
0346: getSession().getMenuManager().deregisterMenuLink(
0347: this .popupMenu, this );
0348: this .popupMenu.setParentFrame(null);
0349: }
0350: this .popupMenu = popupMenu;
0351: if (this .popupMenu != null) {
0352: getSession().getMenuManager().registerMenuLink(
0353: this .popupMenu, this );
0354: this .popupMenu.setParentFrame(getParentFrame());
0355: }
0356: }
0357:
0358: public SPopupMenu getComponentPopupMenu() {
0359: /* (OL) we probably don't need the recursive stuff here... */
0360: // if (!getInheritsPopupMenu())
0361: // return popupMenu;
0362: //
0363: // if (popupMenu == null) {
0364: // // Search parents for its popup
0365: // SContainer parent = getParent();
0366: // while (parent != null) {
0367: // if (parent instanceof SComponent) {
0368: // return ((SComponent) parent).getComponentPopupMenu();
0369: // }
0370: // if (parent instanceof SFrame)
0371: // break;
0372: //
0373: // parent = parent.getParent();
0374: // }
0375: // return null;
0376: // }
0377: return popupMenu;
0378: }
0379:
0380: /* No reason -- even JComponent does not define such a method.
0381: Undo change also in DynamicScriptResource.visit().
0382: public boolean hasComponentPopupMenu() {
0383: return popupMenu != null;
0384: } */
0385:
0386: /**
0387: * The URL under which this component is accessible for the browser.
0388: * This is equivalent to the URL of the component's root frame, as this is the
0389: * node externalized to the browser via the {@link org.wings.resource.ReloadResource}
0390: * externalizer.
0391: *
0392: * @return The HTTP URL where this component can be accessed.
0393: */
0394: public RequestURL getRequestURL() {
0395: SFrame p = getParentFrame();
0396: if (p == null)
0397: throw new IllegalStateException("no parent frame");
0398:
0399: return p.getRequestURL();
0400: }
0401:
0402: /**
0403: * Set the preferred size of the receiving component in pixel.
0404: * It is not guaranteed that the component accepts this property because of
0405: * missing implementations in the component cg or html properties.
0406: * If <i>width</i> or <i>height</i> is zero, it is ignored and the browser
0407: * defines the size.
0408: *
0409: * @see org.wings.SComponent#getPreferredSize
0410: */
0411: public void setPreferredSize(SDimension preferredSize) {
0412: reloadIfChange(this .preferredSize, preferredSize);
0413: this .preferredSize = preferredSize;
0414: }
0415:
0416: /**
0417: * Get the preferred size of this component.
0418: *
0419: * @see SComponent#setPreferredSize
0420: */
0421: public SDimension getPreferredSize() {
0422: return preferredSize;
0423: }
0424:
0425: /**
0426: * Adds the specified component listener to receive component events from
0427: * this component.
0428: * If l is null, no exception is thrown and no action is performed.
0429: *
0430: * @param l the component listener.
0431: * @see org.wings.event.SComponentEvent
0432: * @see org.wings.event.SComponentListener
0433: * @see org.wings.SComponent#removeComponentListener
0434: */
0435: public final void addComponentListener(SComponentListener l) {
0436: addEventListener(SComponentListener.class, l);
0437: fireComponentChangeEvents = true;
0438: }
0439:
0440: /**
0441: * Removes the specified component listener so that it no longer
0442: * receives component events from this component. This method performs
0443: * no function, nor does it throw an exception, if the listener
0444: * specified by the argument was not previously added to this component.
0445: * If l is null, no exception is thrown and no action is performed.
0446: *
0447: * @param l the component listener.
0448: * @see org.wings.event.SComponentEvent
0449: * @see org.wings.event.SComponentListener
0450: * @see org.wings.SComponent#addComponentListener
0451: */
0452: public final void removeComponentListener(SComponentListener l) {
0453: removeEventListener(SComponentListener.class, l);
0454: }
0455:
0456: /**
0457: * Registers a "parent frame listener" to receive events from
0458: * this component when the parent frame chanegs i.e. via {@link #setParentFrame(SFrame)}.
0459: * If l is null, no exception is thrown and no action is performed.
0460: *
0461: * @param l the parent frame listener. May be <code>null</code>.
0462: * @see org.wings.event.SParentFrameEvent
0463: * @see org.wings.event.SParentFrameListener
0464: * @see org.wings.SComponent#removeParentFrameListener
0465: * @see SComponent#removeNotify()
0466: * @see SComponent#addNotify()
0467: */
0468: public final void addParentFrameListener(SParentFrameListener l) {
0469: addEventListener(SParentFrameListener.class, l);
0470: fireParentFrameChangeEvents = true;
0471: }
0472:
0473: /**
0474: * Removes the specified parent frame listener so that it no longer
0475: * receives events from this component. This method performs
0476: * no function, nor does it throw an exception, if the listener
0477: * specified by the argument was not previously added to this component.
0478: * If l is null, no exception is thrown and no action is performed.
0479: *
0480: * @param l the parent frame listener.
0481: * @see org.wings.event.SParentFrameEvent
0482: * @see org.wings.event.SParentFrameListener
0483: * @see org.wings.SComponent#addParentFrameListener
0484: * @see SComponent#removeNotify()
0485: * @see SComponent#addNotify()
0486: */
0487: public final void removeParentFrameListener(SParentFrameListener l) {
0488: removeEventListener(SParentFrameListener.class, l);
0489: }
0490:
0491: /**
0492: * Reports a component change.
0493: *
0494: * @param aEvent report this event to all listeners
0495: * @see org.wings.event.SComponentListener
0496: */
0497: protected void fireComponentChangeEvent(SComponentEvent aEvent) {
0498: // maybe the better way to do this is to user the getListenerList
0499: // and iterate through all listeners, this saves the creation of
0500: // an array but it must cast to the apropriate listener
0501: Object[] listeners = getListenerList();
0502: for (int i = listeners.length - 2; i >= 0; i -= 2) {
0503: if (listeners[i] == SComponentListener.class) {
0504: // Lazily create the event:
0505: processComponentEvent(
0506: (SComponentListener) listeners[i + 1], aEvent);
0507: }
0508: }
0509:
0510: }
0511:
0512: /**
0513: * Reports a parent frame change.
0514: *
0515: * @param aEvent report this event to all listeners
0516: * @see org.wings.event.SParentFrameListener
0517: */
0518: private void fireParentFrameEvent(SParentFrameEvent aEvent) {
0519: // are listeners registered?
0520: if (fireParentFrameChangeEvents) {
0521: // maybe the better way to do this is to user the getListenerList
0522: // and iterate through all listeners, this saves the creation of
0523: // an array but it must cast to the apropriate listener
0524: Object[] listeners = getListenerList();
0525: for (int i = listeners.length - 2; i >= 0; i -= 2) {
0526: if (listeners[i] == SParentFrameListener.class) {
0527: // Lazily create the event:
0528: processParentFrameEvent(
0529: (SParentFrameListener) listeners[i + 1],
0530: aEvent);
0531: }
0532: }
0533: }
0534:
0535: }
0536:
0537: /**
0538: * Processes parent frame events occurring on this component by
0539: * dispatching them to any registered
0540: * <code>SParentFrameListener</code> objects.
0541: * <p/>
0542: */
0543: private void processParentFrameEvent(SParentFrameListener listener,
0544: SParentFrameEvent event) {
0545: int id = event.getID();
0546: switch (id) {
0547: case SParentFrameEvent.PARENTFRAME_ADDED:
0548: listener.parentFrameAdded(event);
0549: break;
0550: case SParentFrameEvent.PARENTFRAME_REMOVED:
0551: listener.parentFrameRemoved(event);
0552: break;
0553: }
0554: }
0555:
0556: /**
0557: * Processes component events occurring on this component by
0558: * dispatching them to any registered
0559: * <code>SComponentListener</code> objects.
0560: * <p/>
0561: * This method is not called unless component events are
0562: * enabled for this component. Component events are enabled
0563: * when one of the following occurs:
0564: * <p><ul>
0565: * <li>A <code>SComponentListener</code> object is registered
0566: * via <code>addComponentListener</code>.
0567: * </ul>
0568: *
0569: * @param e the component event.
0570: * @see org.wings.event.SComponentEvent
0571: * @see org.wings.event.SComponentListener
0572: * @see org.wings.SComponent#addComponentListener
0573: */
0574: protected void processComponentEvent(SComponentListener listener,
0575: SComponentEvent e) {
0576: int id = e.getID();
0577: switch (id) {
0578: case SComponentEvent.COMPONENT_RESIZED:
0579: listener.componentResized(e);
0580: break;
0581: case SComponentEvent.COMPONENT_MOVED:
0582: listener.componentMoved(e);
0583: break;
0584: case SComponentEvent.COMPONENT_SHOWN:
0585: listener.componentShown(e);
0586: break;
0587: case SComponentEvent.COMPONENT_HIDDEN:
0588: listener.componentHidden(e);
0589: break;
0590: }
0591: }
0592:
0593: /**
0594: * Adds the specified component listener to receive component events from
0595: * this component.
0596: * If l is null, no exception is thrown and no action is performed.
0597: * If there is already a ScriptListener which is equal, the new one is not
0598: * added.
0599: *
0600: * @param listener the component listener.
0601: * @see org.wings.event.SComponentEvent
0602: * @see org.wings.event.SComponentListener
0603: * @see org.wings.SComponent#removeComponentListener
0604: */
0605: public final void addScriptListener(ScriptListener listener) {
0606: if (scriptListenerList != null
0607: && scriptListenerList.contains(listener))
0608: return;
0609:
0610: if (scriptListenerList == null) {
0611: scriptListenerList = new LinkedList<ScriptListener>();
0612: }
0613:
0614: int placingPosition = -1;
0615: for (int i = 0; i < scriptListenerList.size()
0616: && placingPosition < 0; i++) {
0617: ScriptListener existingListener = scriptListenerList.get(i);
0618: if (existingListener.getPriority() < listener.getPriority())
0619: placingPosition = i;
0620: }
0621: reload();
0622: if (placingPosition >= 0)
0623: scriptListenerList.add(placingPosition, listener);
0624: else
0625: scriptListenerList.add(listener);
0626: }
0627:
0628: /**
0629: * Removes the specified component listener so that it no longer
0630: * receives component events from this component. This method performs
0631: * no function, nor does it throw an exception, if the listener
0632: * specified by the argument was not previously added to this component.
0633: * If l is null, no exception is thrown and no action is performed.
0634: *
0635: * @param listener the component listener.
0636: * @see org.wings.event.SComponentEvent
0637: * @see org.wings.event.SComponentListener
0638: * @see org.wings.SComponent#addComponentListener
0639: */
0640: public final void removeScriptListener(ScriptListener listener) {
0641: if (scriptListenerList != null) {
0642: scriptListenerList.remove(listener);
0643: reload();
0644: }
0645: }
0646:
0647: /**
0648: * returns the script listeners of this component
0649: *
0650: * @return the ScriptListener Array.
0651: */
0652: public ScriptListener[] getScriptListeners() {
0653: if (scriptListenerList != null) {
0654: return scriptListenerList
0655: .toArray(new ScriptListener[scriptListenerList
0656: .size()]);
0657: } else {
0658: return EMPTY_SCRIPTLISTENERLIST;
0659: }
0660: }
0661:
0662: /**
0663: * Returns the script listeners of this component.
0664: *
0665: * @return The <code>ScriptListener</code>s in a <code>List</code>.
0666: */
0667: public List<ScriptListener> getScriptListenerList() {
0668: if (scriptListenerList != null) {
0669: return Collections.unmodifiableList(scriptListenerList);
0670: } else {
0671: return Collections.emptyList();
0672: }
0673: }
0674:
0675: /**
0676: * Sets the name property of a component which must be <b>unique</b>!
0677: * <br/>Assigning the same name multiple times will cause strange results!
0678: * <p/>
0679: * <p>Valid names must begin with a letter ([A-Za-z]), underscores ("_") or dollars ("$") and may be followed by any number of
0680: * letters, digits ([0-9]), underscores ("_") and dollars ("$")
0681: * <p/>
0682: * <p>If no name is set, it is generated when necessary.
0683: * <p/>
0684: * <p><i>Explanation:</i> This property is an identifier which is used inside the generated HTML as an element identifier (id="")
0685: * and sometimes as a javascript function name.
0686: *
0687: * @param uniqueName A <b>unique</b> name to set. <b>Only valid identifier as described are allowed!</b>
0688: * @see Character
0689: */
0690: public void setName(String uniqueName) {
0691: if (uniqueName != null) {
0692: char ch = uniqueName.charAt(0);
0693: if (uniqueName.length() == 0
0694: || !(Character.isLetter(ch) || ch == '_' || ch == '$'))
0695: throw new IllegalArgumentException(uniqueName
0696: + " is not a valid identifier");
0697: for (int i = 1; i < uniqueName.length(); i++) {
0698: ch = uniqueName.charAt(i);
0699: if (!(Character.isLetter(ch) || Character.isDigit(ch)
0700: || ch == '_' || ch == '$'))
0701: throw new IllegalArgumentException(uniqueName
0702: + " is not a valid identifier");
0703: }
0704: }
0705: setNameRaw(uniqueName);
0706: }
0707:
0708: /**
0709: * <b>Direct setter for name. Do not use unless you explicitly know what you're doing!</b>
0710: * (Former package) protected raw setter for component name to avoid sanity check.
0711: *
0712: * @param uncheckedName String to use as componentn name/identifier.
0713: */
0714: public void setNameRaw(String uncheckedName) {
0715: reloadIfChange(this .name, uncheckedName);
0716: this .name = uncheckedName;
0717: }
0718:
0719: /**
0720: * Gets the name property of a component. This property is an identifier,so it should be always unique.
0721: * For details refer to {@link #setName(String)}
0722: *
0723: * @return The name of the component.
0724: */
0725: public final String getName() {
0726: if (name == null)
0727: name = getSession().createUniqueId();
0728: return name;
0729: }
0730:
0731: /**
0732: * Return the session this component belongs to.
0733: *
0734: * @return the session
0735: */
0736: public final Session getSession() {
0737: if (session == null) {
0738: session = SessionManager.getSession();
0739: }
0740:
0741: return session;
0742: }
0743:
0744: /*
0745: * If a subclass implements the {@link LowLevelEventListener} interface,
0746: * it will be unregistered at the associated dispatcher.
0747: */
0748: final void unregister() {
0749: if (getSession().getDispatcher() != null
0750: && this instanceof LowLevelEventListener) {
0751: getSession().getDispatcher().unregister(
0752: (LowLevelEventListener) this );
0753: }
0754: }
0755:
0756: /*
0757: * If a subclass implements the {@link LowLevelEventListener} interface,
0758: * it will be registered at the associated dispatcher.
0759: */
0760: final void register() {
0761: if (getSession().getDispatcher() != null
0762: && this instanceof LowLevelEventListener) {
0763: getSession().getDispatcher().register(
0764: (LowLevelEventListener) this );
0765: }
0766: }
0767:
0768: /**
0769: * Set an CSS class name provided by the laf-provided Cascading Style Sheet (CSS), which should
0770: * be applied to this component.
0771: * <p>
0772: * <b>Note:</b> Probably the {@link #addStyle(String)} method is more what you want.
0773: * <p>By <b>default</b> this is set to the wingS component class name (i.e. "SLabel").
0774: * <p>The PLAFs render the value of this String to an <code>class="<i>cssClassName</i>"</code> attribute
0775: * inside the generated HTML code of this component.
0776: * <p>The default wingS plaf initializes this by default to the wingS component class name
0777: * (i.e. <code>SButton</code> for button instances). <br/>Please be aware if you <b>replace</b> this
0778: * default value, the default wingS style will no longer take effect, as they operate on these
0779: * default styles. To avoid this you should append your CSS styles via spaces i.e.<br>
0780: * <code>c.setStyle(c.getStyle + "myStyle");</code>
0781: *
0782: * @param cssClassName The new CSS name value for this component
0783: * @see #addStyle(String)
0784: * @see #removeStyle(String)
0785: */
0786: // <p>Please consider using {@link #addStyle(String)} to avoid disabling of default wingS stylesheet set.
0787: public void setStyle(String cssClassName) {
0788: reloadIfChange(style, cssClassName);
0789: this .style = cssClassName;
0790: }
0791:
0792: /**
0793: * Append a style class name to the style string. Use this method if you want to append a specific CSS
0794: * class to a componentn without loosing the other CSS styles assigned to the component (i.e. the wingS
0795: * default styles.)
0796: * @param additionalCssClassName The style class to remove (if existing).
0797: * @see #removeStyle(String)
0798: */
0799: public void addStyle(String additionalCssClassName) {
0800: if (this .style == null || this .style.length() == 0) {
0801: setStyle(additionalCssClassName); // trivial case
0802: } else {
0803: if (this .style.indexOf(additionalCssClassName) < 0) {
0804: setStyle(this .style + " " + additionalCssClassName);
0805: }
0806: }
0807: }
0808:
0809: /**
0810: * Remove a style class definiton from this component.
0811: * @param cssStyleClassName The style class to remove (if existing).
0812: */
0813: public void removeStyle(String cssStyleClassName) {
0814: if (this .style != null && cssStyleClassName != null
0815: && this .style.indexOf(cssStyleClassName) >= 0) {
0816: if (this .style.length() == cssStyleClassName.length())
0817: setStyle(null); // trivial case
0818: else
0819: setStyle(this .style.replaceAll(
0820: "\\b" + cssStyleClassName + "\\b", "")
0821: .replaceAll(" ", " ").trim());
0822: }
0823: }
0824:
0825: /**
0826: * @return The current CSS style class name. Defaults to the unqualified wingS component class name.
0827: * @see #setStyle(String)
0828: */
0829: public String getStyle() {
0830: return style;
0831: }
0832:
0833: /**
0834: * Register a new CSS style on this component for a specfic CSS selector.
0835: * <p>Typically you will want to use the method {@link #setAttribute(org.wings.style.CSSProperty, String)}
0836: * to specify a single CSS property/value pair on this component.
0837: *
0838: * @param style A Style instance.
0839: */
0840: public void addDynamicStyle(Style style) {
0841: if (dynamicStyles == null)
0842: dynamicStyles = new HashMap<Selector, Style>(4);
0843: dynamicStyles.put(style.getSelector(), style);
0844: reload();
0845: }
0846:
0847: /**
0848: * Remove all CSS style definitions defined for the passed CSS selector.
0849: *
0850: * @param selector The selector. The default selector for most CSS attributes is {@link #SELECTOR_ALL}.
0851: */
0852: public void removeDynamicStyle(Selector selector) {
0853: if (dynamicStyles == null)
0854: return;
0855: dynamicStyles.remove(selector);
0856: reload();
0857: }
0858:
0859: /**
0860: * Returns the style defined for the passed CSS selector.
0861: *
0862: * @param selector The CSS selector the style to retrieve. See {@link org.wings.style.Style#getSelector()}.
0863: * The default selector for most CSS styles is {@link #SELECTOR_ALL}.
0864: * @return A style (collection of css property/value pairs) or <code>null</code>
0865: */
0866: public Style getDynamicStyle(Selector selector) {
0867: if (dynamicStyles == null)
0868: return null;
0869: return dynamicStyles.get(selector);
0870: }
0871:
0872: /**
0873: * Adds the passed collection of {@link Style} definitions. Existing Styles for the same CSS selectors
0874: * (see {@link org.wings.style.Style#getSelector()}) are overwritten.
0875: *
0876: * @param dynamicStyles A collection collection of {@link Style} definitions.
0877: */
0878: public void setDynamicStyles(Collection dynamicStyles) {
0879: if (dynamicStyles == null)
0880: return;
0881: if (this .dynamicStyles == null)
0882: this .dynamicStyles = new HashMap<Selector, Style>(4);
0883: for (Object dynamicStyle : dynamicStyles) {
0884: Style style = (Style) dynamicStyle;
0885: this .dynamicStyles.put(style.getSelector(), style);
0886: }
0887: reload();
0888: }
0889:
0890: /**
0891: * Returns the collection of currently defined CSS styles on this component.
0892: *
0893: * @return A unmodifyable collection of {@link Style} instances.
0894: */
0895: public Collection getDynamicStyles() {
0896: if (dynamicStyles == null || dynamicStyles.size() == 0)
0897: return null;
0898: return Collections.unmodifiableCollection(dynamicStyles
0899: .values());
0900: }
0901:
0902: /**
0903: * Defines a free text css property / value pair to this component.
0904: * The CSS property will appear as an inline style in the generated HTML code.
0905: */
0906: public void setAttribute(String cssPropertyName, String value) {
0907: final CSSProperty property = CSSProperty
0908: .valueOf(cssPropertyName);
0909: if (CSSProperty.BORDER_PROPERTIES.contains(property))
0910: throw new IllegalArgumentException(
0911: "Border properties have to be applied to the border!");
0912: setAttribute(SELECTOR_ALL, property, value);
0913: }
0914:
0915: /**
0916: * Assign or overwrite a CSS property/value pair on this component. This CSS property definition will
0917: * use a CSS selector which adresses this component as whole as CSS selector (<code>new CSSProperty(this)</code>).
0918: * The CSS property will appear as an inline style in the generated HTML code.
0919: *
0920: * @param property The CSS property (i.e. {@link CSSProperty#BACKGROUND}).
0921: * @param propertyValue A valid string value for this CSS property (i.e. <code>red</code> or <code>#fff</code> in our example).
0922: */
0923: public void setAttribute(CSSProperty property, String propertyValue) {
0924: if (CSSProperty.BORDER_PROPERTIES.contains(property))
0925: throw new IllegalArgumentException(
0926: "Border properties have to be applied to the border!");
0927: setAttribute(SELECTOR_ALL, property, propertyValue);
0928: }
0929:
0930: /**
0931: * Assign or overwrite a CSS property/value pair at this component. This CSS property definition will
0932: * use the CSS selector you passed, so in the most exotic case it could affect a totally different
0933: * component or component area. Typically you use this method to assign CSS property values to
0934: * pseudo CSS selectors {@link Selector}. This are selector affecting only a part of a component
0935: * and not the component at all..
0936: * The CSS property will appear as an inline style in the generated HTML code.
0937: *
0938: * @param selector A valid CSS selector. Typically values are i.e. the {@link #SELECTOR_ALL}
0939: * or other <code>SELECTOR_xxx</code> value instances declared in the component.
0940: * (look ie. at {@link STabbedPane#SELECTOR_CONTENT}) or manually constructed instances of
0941: * <code>Selector</code>. In most case {@link #setAttribute(org.wings.style.CSSProperty, String)} will be your
0942: * choice.
0943: * @param property The css property you want to define a value for
0944: * @param propertyValue A valid string value for this property.
0945: */
0946: public void setAttribute(Selector selector, CSSProperty property,
0947: String propertyValue) {
0948: Style style = getDynamicStyle(selector);
0949: if (style == null) {
0950: addDynamicStyle(new CSSStyle(selector, property,
0951: propertyValue));
0952: } else {
0953: String old = style.put(property, propertyValue);
0954: reloadIfChange(old, propertyValue);
0955: }
0956: }
0957:
0958: /**
0959: * Convenience variant of {@link #setAttribute(org.wings.style.Selector, org.wings.style.CSSProperty, String)}.
0960: * Converts the passed icon into a URL and applies the according CSS style.
0961: *
0962: * @param selector A valid CSS selector. Typically values are i.e. the {@link #SELECTOR_ALL}
0963: * or other <code>SELECTOR_xxx</code> value instances declared in the component.
0964: * (look ie. at {@link STabbedPane#SELECTOR_CONTENT}) or manually constructed instances of
0965: * <code>Selector</code>. In most case {@link #setAttribute(org.wings.style.CSSProperty, String)} will be your
0966: * choice.
0967: * @param property The css property you want to define a value for (in this case
0968: * mostly something like {@link CSSProperty#BACKGROUND_IMAGE}.
0969: * @param icon The icon you want to assign.
0970: */
0971: public void setAttribute(Selector selector, CSSProperty property,
0972: SIcon icon) {
0973: setAttribute(selector, property, icon != null ? "url('"
0974: + icon.getURL().toString() + "')" : "none");
0975: }
0976:
0977: /**
0978: * Convenience variant of {@link #setAttribute(org.wings.style.Selector, org.wings.style.CSSProperty, String)}.
0979: * Converts the passed color into according color string.
0980: *
0981: * @param selector A valid CSS selector. Typically values are i.e. the {@link #SELECTOR_ALL}
0982: * or other <code>SELECTOR_xxx</code> value instances declared in the component.
0983: * (look ie. at {@link STabbedPane#SELECTOR_CONTENT}) or manually constructed instances of
0984: * <code>Selector</code>. In most case {@link #setAttribute(org.wings.style.CSSProperty, String)} will be your
0985: * choice.
0986: * @param property The css property you want to define a value for (in this case
0987: * mostly something like {@link CSSProperty#BACKGROUND_IMAGE}.
0988: * @param color The color value you want to assign.
0989: */
0990: public void setAttribute(Selector selector, CSSProperty property,
0991: Color color) {
0992: setAttribute(selector, property, CSSStyleSheet
0993: .getAttribute(color));
0994: }
0995:
0996: public void setAttributes(Selector selector,
0997: CSSAttributeSet attributes) {
0998: Style style = getDynamicStyle(selector);
0999: if (style == null) {
1000: addDynamicStyle(new CSSStyle(selector, attributes));
1001: } else {
1002: boolean changed = style.putAll(attributes);
1003: if (changed)
1004: reload();
1005: }
1006: }
1007:
1008: /**
1009: * Returns the current background color of this component.
1010: *
1011: * @return The current background color or <code>null</code>
1012: */
1013: public Color getBackground() {
1014: return dynamicStyles == null
1015: || dynamicStyles.get(SELECTOR_ALL) == null ? null
1016: : CSSStyleSheet
1017: .getBackground((CSSAttributeSet) dynamicStyles
1018: .get(SELECTOR_ALL));
1019: }
1020:
1021: /**
1022: * Set the components foreground color.
1023: *
1024: * @param color the new foreground color or <code>null</code>
1025: */
1026: public void setBackground(Color color) {
1027: setAttribute(SELECTOR_ALL, CSSProperty.BACKGROUND_COLOR,
1028: CSSStyleSheet.getAttribute(color));
1029: }
1030:
1031: /**
1032: * Return the components foreground color.
1033: *
1034: * @return the foreground color or <code>null</code>
1035: */
1036: public Color getForeground() {
1037: return dynamicStyles == null
1038: || dynamicStyles.get(SELECTOR_ALL) == null ? null
1039: : CSSStyleSheet
1040: .getForeground((CSSAttributeSet) dynamicStyles
1041: .get(SELECTOR_ALL));
1042: }
1043:
1044: /**
1045: * Set the foreground color.
1046: *
1047: * @param color the new foreground color or <code>null</code>
1048: */
1049: public void setForeground(Color color) {
1050: setAttribute(SELECTOR_ALL, CSSProperty.COLOR, CSSStyleSheet
1051: .getAttribute(color));
1052: }
1053:
1054: /**
1055: * Set the font.
1056: *
1057: * @param font the new font
1058: */
1059: public void setFont(SFont font) {
1060: CSSAttributeSet attributes = CSSStyleSheet.getAttributes(font);
1061: Style style = getDynamicStyle(SELECTOR_ALL);
1062: if (style == null) {
1063: addDynamicStyle(new CSSStyle(SELECTOR_ALL, attributes));
1064: } else {
1065: style.remove(CSSProperty.FONT);
1066: style.remove(CSSProperty.FONT_FAMILY);
1067: style.remove(CSSProperty.FONT_SIZE);
1068: style.remove(CSSProperty.FONT_STYLE);
1069: style.remove(CSSProperty.FONT_WEIGHT);
1070: style.putAll(attributes);
1071: reload();
1072: }
1073: }
1074:
1075: /**
1076: * Return the font used inside this component.
1077: *
1078: * @return The current font declaration or <code>null</code>
1079: */
1080: public SFont getFont() {
1081: return dynamicStyles == null
1082: || dynamicStyles.get(SELECTOR_ALL) == null ? null
1083: : CSSStyleSheet.getFont((CSSAttributeSet) dynamicStyles
1084: .get(SELECTOR_ALL));
1085: }
1086:
1087: /**
1088: * Set the visibility.
1089: *
1090: * @param visible wether this component will show or not
1091: */
1092: public void setVisible(boolean visible) {
1093: boolean old = this .visible;
1094: this .visible = visible;
1095: if (visible != old) {
1096: if (fireComponentChangeEvents) {
1097: fireComponentChangeEvent(new SComponentEvent(this ,
1098: visible ? SComponentEvent.COMPONENT_SHOWN
1099: : SComponentEvent.COMPONENT_HIDDEN));
1100: }
1101:
1102: if (parent != null) {
1103: parent.reload();
1104: } else {
1105: reload();
1106: }
1107: }
1108: }
1109:
1110: /**
1111: * Return the <b>local</b> visibility. If set to <code>true</code> this ccmponent
1112: * should be visible if all parent components are visible, too.
1113: *
1114: * @return <code>true</code> If the component and it's children should show, <code>false</code> otherwise
1115: * @see #isRecursivelyVisible()
1116: */
1117: public boolean isVisible() {
1118: return visible;
1119: }
1120:
1121: /**
1122: * Return the visibility. If the Component itself or any of it's parent is invisible,
1123: * this method will return <code>false</code>.
1124: *
1125: * @return <code>true</code> if this component and all it's ancestors are visible, <code>false</code> otherwise.
1126: */
1127: public boolean isRecursivelyVisible() {
1128: return visible
1129: && (parent == null || (parent.isShowingChildren() && parent
1130: .isRecursivelyVisible()));
1131: }
1132:
1133: /**
1134: * Set wether this component should be enabled.
1135: *
1136: * @param enabled true if the component is enabled, false otherwise
1137: */
1138: public void setEnabled(boolean enabled) {
1139: reloadIfChange(this .enabled, enabled);
1140: this .enabled = enabled;
1141: }
1142:
1143: /**
1144: * Return true if this component is enabled.
1145: *
1146: * @return true if component is enabled
1147: */
1148: public boolean isEnabled() {
1149: return enabled;
1150: }
1151:
1152: /**
1153: * Marks this component as subject to reload.
1154: * The component will be registered with the ReloadManager.
1155: */
1156: public void reload() {
1157: getSession().getReloadManager().reload(this );
1158: }
1159:
1160: /**
1161: * Hands the given update to the Reload Manager.
1162: * @param update the update for this component
1163: */
1164: public void update(Update update) {
1165: getSession().getReloadManager().addUpdate(this , update);
1166: }
1167:
1168: protected boolean isUpdatePossible() {
1169: return getSession().getReloadManager().isUpdateMode();
1170: }
1171:
1172: public boolean isReloadForced() {
1173: return reloadForced;
1174: }
1175:
1176: public void setReloadForced(boolean forced) {
1177: if (reloadForced != forced) {
1178: Object clientProperty = getClientProperty("onChangeSubmitListener");
1179: if (clientProperty != null
1180: && clientProperty instanceof JavaScriptListener) {
1181: removeScriptListener((JavaScriptListener) clientProperty);
1182: putClientProperty("onChangeSubmitListener", null);
1183: }
1184: reloadForced = forced;
1185: reload();
1186: }
1187: }
1188:
1189: /**
1190: * Mark this component as subject to reload if the property,
1191: * that is given in its old and new fashion, changed.
1192: *
1193: * @param oldVal the old value of some property
1194: * @param newVal the new value of some property
1195: */
1196: protected final void reloadIfChange(Object oldVal, Object newVal) {
1197: if (isDifferent(oldVal, newVal))
1198: reload();
1199: }
1200:
1201: /**
1202: * Mark this component as subject to reload if the property,
1203: * that is given in its old and new fashion, changed.
1204: *
1205: * @param oldVal the old value of some property
1206: * @param newVal the new value of some property
1207: */
1208: protected final void reloadIfChange(int oldVal, int newVal) {
1209: if (oldVal != newVal)
1210: reload();
1211: }
1212:
1213: /**
1214: * Mark this component as subject to reload if the property,
1215: * that is given in its old and new fashion, changed.
1216: *
1217: * @param oldVal the old value of some property
1218: * @param newVal the new value of some property
1219: */
1220: protected final void reloadIfChange(boolean oldVal, boolean newVal) {
1221: if (oldVal != newVal)
1222: reload();
1223: }
1224:
1225: /**
1226: * Mark this component as subject to reload if the property,
1227: * that is given in its old and new fashion, changed.
1228: *
1229: * @param oldVal the old value of some property
1230: * @param newVal the new value of some property
1231: */
1232: protected final void reloadIfChange(byte oldVal, byte newVal) {
1233: if (oldVal != newVal)
1234: reload();
1235: }
1236:
1237: /**
1238: * Mark this component as subject to reload if the property,
1239: * that is given in its old and new fashion, changed.
1240: *
1241: * @param oldVal the old value of some property
1242: * @param newVal the new value of some property
1243: */
1244: protected final void reloadIfChange(short oldVal, short newVal) {
1245: if (oldVal != newVal)
1246: reload();
1247: }
1248:
1249: /**
1250: * Mark this component as subject to reload if the property,
1251: * that is given in its old and new fashion, changed.
1252: *
1253: * @param oldVal the old value of some property
1254: * @param newVal the new value of some property
1255: */
1256: protected final void reloadIfChange(long oldVal, long newVal) {
1257: if (oldVal != newVal)
1258: reload();
1259: }
1260:
1261: /**
1262: * Mark this component as subject to reload if the property,
1263: * that is given in its old and new fashion, changed.
1264: *
1265: * @param oldVal the old value of some property
1266: * @param newVal the new value of some property
1267: */
1268: protected final void reloadIfChange(float oldVal, float newVal) {
1269: if (oldVal != newVal)
1270: reload();
1271: }
1272:
1273: /**
1274: * Mark this component as subject to reload if the property,
1275: * that is given in its old and new fashion, changed.
1276: *
1277: * @param oldVal the old value of some property
1278: * @param newVal the new value of some property
1279: */
1280: protected final void reloadIfChange(double oldVal, double newVal) {
1281: if (oldVal != newVal)
1282: reload();
1283: }
1284:
1285: /**
1286: * Mark this component as subject to reload if the property,
1287: * that is given in its old and new fashion, changed.
1288: *
1289: * @param oldVal the old value of some property
1290: * @param newVal the new value of some property
1291: */
1292: protected final void reloadIfChange(char oldVal, char newVal) {
1293: if (oldVal != newVal)
1294: reload();
1295: }
1296:
1297: /**
1298: * Mark this component as subject to reload if the property,
1299: * that is given in its old and new fashion, changed.
1300: */
1301: @SuppressWarnings({"unchecked"})
1302: public void write(Device s) throws IOException {
1303: try {
1304: if (visible) {
1305: cg.write(s, this );
1306: }
1307: } catch (IOException se) {
1308: // Typical double-clicks. Not severe
1309: log
1310: .debug("Not Severe: Socket exception during code generation for "
1311: + getClass().getName() + se);
1312: } catch (Throwable t) {
1313: // should we warn here? or maybe throw an error...
1314: log.error("Exception during code generation for "
1315: + getClass().getName(), t);
1316: }
1317: }
1318:
1319: /**
1320: * A string representation of this component. Uses the {@link #paramString()} methods
1321: *
1322: * @return string representation of this component with all properties.
1323: */
1324: @Override
1325: public String toString() {
1326: return getClass().getName() + "[" + getName() + "]";
1327: }
1328:
1329: /**
1330: * Generates a string describing this <code>SComponent</code>.
1331: * This method is mainly for debugging purposes.
1332: *
1333: * @return a string containing all properties
1334: */
1335: protected String paramString() {
1336: SStringBuilder buffer = new SStringBuilder(getClass().getName());
1337: buffer.append("[");
1338:
1339: try {
1340: BeanInfo info = Introspector.getBeanInfo(getClass());
1341: PropertyDescriptor[] descriptors = info
1342: .getPropertyDescriptors();
1343:
1344: boolean first = true;
1345: for (PropertyDescriptor descriptor : descriptors) {
1346: try {
1347: Method getter = descriptor.getReadMethod();
1348: if (getter == null
1349: || getter.getName().startsWith("getParent")) {
1350: continue;
1351: }
1352: // System.out.println("invoking " + this.getClass().getDescription()+"."+getter.getDescription());
1353: Object value = getter.invoke(this );
1354: if (first) {
1355: first = false;
1356: } else {
1357: buffer.append(",");
1358: }
1359: buffer.append(descriptor.getName() + "=" + value);
1360: } catch (Exception e) {
1361: log.debug("Exception during paramString()" + e);
1362: }
1363: }
1364: } catch (Exception e) {
1365: log.debug("Exception during paramString()" + e);
1366: }
1367:
1368: buffer.append("]");
1369: return buffer.toString();
1370: }
1371:
1372: /**
1373: * Default implementation of the method in
1374: * {@link LowLevelEventListener}.
1375: */
1376: public String getLowLevelEventId() {
1377: return getName();
1378: }
1379:
1380: /**
1381: * Return the parent frame.
1382: * <p><b>NOTE:</b> You will receive <code>null</code> if you call this i.e. during
1383: * component creation time, as the parent frame is set when you add it to a visible {@link SContainer}.
1384: * Use {@link #addParentFrameListener(org.wings.event.SParentFrameListener)} in this case.
1385: *
1386: * @return the parent frame
1387: * @see #addParentFrameListener(org.wings.event.SParentFrameListener)
1388: */
1389: public SFrame getParentFrame() {
1390: return parentFrame;
1391: }
1392:
1393: /**
1394: * Return true, if this component is contained in a form.
1395: *
1396: * @return true, if this component resides in a form, false otherwise
1397: */
1398: public boolean getResidesInForm() {
1399: SComponent parent = getParent();
1400:
1401: boolean actuallyDoes = parent instanceof SForm;
1402: while (parent != null && !actuallyDoes) {
1403: parent = parent.getParent();
1404: actuallyDoes = parent instanceof SForm;
1405: }
1406:
1407: return actuallyDoes;
1408: }
1409:
1410: /**
1411: * Set the tooltip text. To style use HTML tags.
1412: *
1413: * @param t the new tooltip text
1414: */
1415: public void setToolTipText(String t) {
1416: reloadIfChange(this .tooltip, t);
1417: this .tooltip = t;
1418: }
1419:
1420: /**
1421: * Return the tooltip text.
1422: *
1423: * @return the tooltip text
1424: */
1425: public String getToolTipText() {
1426: return tooltip;
1427: }
1428:
1429: /**
1430: * The index in which the focus is traversed using Tab. This is
1431: * a very simplified notion of traversing the focus, but that is,
1432: * what browser like interfaces currently offer. This has a bit rough
1433: * edge, since you have to make sure, that the index is unique within
1434: * the whole frame. You probably don't want to change this
1435: * programmatically, but this is set usually by the template property
1436: * manager.
1437: *
1438: * @param index the focus traversal index. Pressing the focus traversal
1439: * key (usually TAB) in the browser jumps to the next index.
1440: * Must not be zero.
1441: */
1442: public void setFocusTraversalIndex(int index) {
1443: reloadIfChange(this .focusTraversalIndex, index);
1444: focusTraversalIndex = index;
1445: }
1446:
1447: /**
1448: * returns the focus traversal index.
1449: *
1450: * @see #setFocusTraversalIndex(int)
1451: */
1452: public int getFocusTraversalIndex() {
1453: return focusTraversalIndex;
1454: }
1455:
1456: /**
1457: * Clone this component.
1458: *
1459: * @return a clone of this component
1460: */
1461: @Override
1462: public Object clone() {
1463: try {
1464: return super .clone();
1465: } catch (Exception e) {
1466: log.error("Unable to clone component", e);
1467: return null;
1468: }
1469: }
1470:
1471: /**
1472: * Return the value of the horizontal alignment property.
1473: *
1474: * @return the horizontal alignment
1475: * @see SConstants
1476: */
1477: public int getHorizontalAlignment() {
1478: return horizontalAlignment;
1479: }
1480:
1481: /**
1482: * Set the horizontal alignment.
1483: *
1484: * @param alignment new value for the horizontal alignment
1485: * @see SConstants
1486: */
1487: public void setHorizontalAlignment(int alignment) {
1488: reloadIfChange(this .horizontalAlignment, alignment);
1489: horizontalAlignment = alignment;
1490: }
1491:
1492: /**
1493: * Set the vertical alignment.
1494: *
1495: * @param alignment new value for the vertical alignment
1496: * @see SConstants
1497: */
1498: public void setVerticalAlignment(int alignment) {
1499: reloadIfChange(this .verticalAlignment, alignment);
1500: verticalAlignment = alignment;
1501: }
1502:
1503: /**
1504: * Return the value of the vertical alignment property.
1505: *
1506: * @return the vertical alignment
1507: * @see SConstants
1508: */
1509: public int getVerticalAlignment() {
1510: return verticalAlignment;
1511: }
1512:
1513: /**
1514: * @return a small HashMap
1515: * @see #putClientProperty
1516: * @see #getClientProperty
1517: */
1518: private Map<Object, Object> getClientProperties() {
1519: if (clientProperties == null) {
1520: clientProperties = new HashMap<Object, Object>(2);
1521: }
1522: return clientProperties;
1523: }
1524:
1525: /**
1526: * Returns the value of the property with the specified key. Only
1527: * properties added with <code>putClientProperty</code> will return
1528: * a non-null value.
1529: *
1530: * @return the value of this property or null
1531: * @see #putClientProperty
1532: */
1533: public final Object getClientProperty(Object key) {
1534: if (clientProperties == null) {
1535: return null;
1536: } else {
1537: return getClientProperties().get(key);
1538: }
1539: }
1540:
1541: /**
1542: * Add an arbitrary key/value "client property" to this component.
1543: * <p/>
1544: * The <code>get/putClientProperty<code> methods provide access to
1545: * a small per-instance hashtable. Callers can use get/putClientProperty
1546: * to annotate components that were created by another module, e.g. a
1547: * layout manager might store per child constraints this way. For example:
1548: * <pre>
1549: * componentA.putClientProperty("to the left of", componentB);
1550: * </pre>
1551: * <p/>
1552: * If value is null this method will remove the property.
1553: * Changes to client properties are reported with PropertyChange
1554: * events. The name of the property (for the sake of PropertyChange
1555: * events) is <code>key.toString()</code>.
1556: * <p/>
1557: * The clientProperty dictionary is not intended to support large
1558: * scale extensions to SComponent nor should be it considered an
1559: * alternative to subclassing when designing a new component.
1560: *
1561: * @see #getClientProperty
1562: */
1563: public final void putClientProperty(Object key, Object value) {
1564: if (value != null) {
1565: getClientProperties().put(key, value);
1566: } else {
1567: getClientProperties().remove(key);
1568: }
1569: }
1570:
1571: /**
1572: * Set the look and feel delegate for this component.
1573: * SComponent subclasses generally override this method
1574: * to narrow the argument type, e.g. in STextField:
1575: * <pre>
1576: * public void setCG(TextFieldCG newCG) {
1577: * super.setCG(newCG);
1578: * }
1579: * </pre>
1580: *
1581: * @see #updateCG
1582: * @see org.wings.plaf.CGManager#getLookAndFeel
1583: * @see org.wings.plaf.CGManager#getCG
1584: */
1585: @SuppressWarnings({"unchecked"})
1586: public void setCG(ComponentCG newCG) {
1587: /* We do not check that the CG instance is different
1588: * before allowing the switch in order to enable the
1589: * same CG instance *with different default settings*
1590: * to be installed.
1591: */
1592: if (cg != null) {
1593: cg.uninstallCG(this );
1594: }
1595: ComponentCG oldCG = cg;
1596: cg = newCG;
1597: if (cg != null) {
1598: cg.installCG(this );
1599: }
1600: reloadIfChange(cg, oldCG);
1601: }
1602:
1603: /**
1604: * Return the look and feel delegate.
1605: *
1606: * @return the componet's cg
1607: */
1608: public ComponentCG getCG() {
1609: return cg;
1610: }
1611:
1612: /**
1613: * Notification from the CGFactory that the L&F has changed.
1614: *
1615: * @see SComponent#updateCG
1616: */
1617: public void updateCG() {
1618: if (getSession() == null) {
1619: log.warn("no session yet.");
1620: } else if (getSession().getCGManager() == null) {
1621: log.warn("no CGManager");
1622: } else {
1623: setCG(getSession().getCGManager().getCG(this ));
1624: }
1625: }
1626:
1627: /**
1628: * Invite a ComponentVisitor.
1629: * Invokes visit(SComponent) on the ComponentVisitor.
1630: *
1631: * @param visitor the visitor to be invited
1632: */
1633: public void invite(ComponentVisitor visitor) throws Exception {
1634: visitor.visit(this );
1635: }
1636:
1637: /**
1638: * use this method for changing a variable. if a new value is different
1639: * from the old value set the new one and notify e.g. the reloadmanager...
1640: */
1641: protected static boolean isDifferent(Object oldObject,
1642: Object newObject) {
1643: if (oldObject == newObject)
1644: return false;
1645:
1646: if (oldObject == null)
1647: return true;
1648:
1649: return !oldObject.equals(newObject);
1650: }
1651:
1652: /**
1653: * Adds an event listener for the given event class
1654: *
1655: * @param type The class/type of events to listen to.
1656: * @param listener The listener itself.
1657: */
1658: protected final <T extends EventListener> void addEventListener(
1659: Class<T> type, T listener) {
1660: if (listeners == null) {
1661: listeners = new EventListenerList();
1662: }
1663: listeners.add(type, listener);
1664: }
1665:
1666: /**
1667: * Removed named event listener.
1668: *
1669: * @param type The class/type of events to listen to.
1670: * @param listener The listener itself.
1671: */
1672: protected final <T extends EventListener> void removeEventListener(
1673: Class<T> type, T listener) {
1674: if (listeners != null) {
1675: listeners.remove(type, listener);
1676: }
1677: }
1678:
1679: /**
1680: * Returns the number of listeners of the specified type for this component.
1681: *
1682: * @param type The type of listeners
1683: * @return The number of listeners
1684: * @see EventListenerList
1685: */
1686: protected final int getListenerCount(Class type) {
1687: if (listeners != null) {
1688: return listeners.getListenerCount(type);
1689: } else {
1690: return 0;
1691: }
1692: }
1693:
1694: /**
1695: * Returns all the listeners of this component. For performance reasons, this is the actual data
1696: * structure and so no modification of this array should be made.
1697: *
1698: * @return All listeners of this component. The result array has a pair structure,
1699: * the first element of each pair is the listener type, the second the listener
1700: * itself. It is guaranteed that this returns a non-null array.
1701: * @see EventListenerList
1702: */
1703: protected final Object[] getListenerList() {
1704: if (listeners == null) {
1705: return EMPTY_OBJECT_ARRAY;
1706: } else {
1707: return listeners.getListenerList();
1708: } // end of else
1709: }
1710:
1711: /**
1712: * Creates an typed array of all listeners of the specified type
1713: *
1714: * @param type All listeners of this type are added to the result array
1715: * @return an array of the specified type with all listeners of the specified type
1716: * @see EventListenerList
1717: */
1718: public final EventListener[] getListeners(
1719: Class<? extends EventListener> type) {
1720: if (listeners != null) {
1721: return listeners.getListeners(type);
1722: } else {
1723: return (EventListener[]) Array.newInstance(type, 0);
1724: }
1725: }
1726:
1727: /**
1728: * Adds a listenere to this component which wil get notified when the rendering of
1729: * this components starts. (This happens after all events / request has been processed and wingS
1730: * starts to build the response).
1731: *
1732: * @param renderListener
1733: * @see SRenderListener
1734: */
1735: public final void addRenderListener(SRenderListener renderListener) {
1736: addEventListener(SRenderListener.class, renderListener);
1737: fireRenderEvents = true;
1738: }
1739:
1740: /**
1741: * Removes the named render listener.
1742: *
1743: * @param renderListener Render listener to remove
1744: * @see #addRenderListener(org.wings.event.SRenderListener)
1745: * @see SRenderListener
1746: */
1747: public final void removeRenderListener(
1748: SRenderListener renderListener) {
1749: removeEventListener(SRenderListener.class, renderListener);
1750: }
1751:
1752: /**
1753: * <b>Internal method</b> called by the CGs to indicate different states of the rendering process.
1754: *
1755: * @param type Either {@link #DONE_RENDERING} or {@link #START_RENDERING}.
1756: */
1757: public final void fireRenderEvent(int type) {
1758: if (fireRenderEvents) {
1759: // maybe the better way to do this is to user the getListenerList
1760: // and iterate through all listeners, this saves the creation of
1761: // an array but it must cast to the apropriate listener
1762: Object[] listeners = getListenerList();
1763: for (int i = listeners.length - 2; i >= 0; i -= 2) {
1764: if (listeners[i] == SRenderListener.class) {
1765: // Lazily create the event:
1766: if (renderEvent == null) {
1767: renderEvent = new SRenderEvent(this );
1768: } // end of if ()
1769:
1770: switch (type) {
1771: case START_RENDERING:
1772: ((SRenderListener) listeners[i + 1])
1773: .startRendering(renderEvent);
1774: break;
1775: case DONE_RENDERING:
1776: ((SRenderListener) listeners[i + 1])
1777: .doneRendering(renderEvent);
1778: break;
1779: }
1780: }
1781: }
1782: }
1783: }
1784:
1785: /**
1786: * Forwards the scrollRectToVisible() message to the SComponent's
1787: * parent. Components that can service the request, such as
1788: * SScrollPane, override this method and perform the scrolling.
1789: *
1790: * @param aRect the visible Rectangle
1791: * @see SScrollPane
1792: */
1793: public void scrollRectToVisible(Rectangle aRect) {
1794: if (parent != null) {
1795: parent.scrollRectToVisible(aRect);
1796: }
1797: }
1798:
1799: /**
1800: * Requests the edit focus for this component for the following renderings
1801: * by calling {@link SFrame#setFocus(SComponent)}.
1802: */
1803: public void requestFocus() {
1804: if (getParentFrame() != null) {
1805: getParentFrame().setFocus(this );
1806: }
1807: }
1808:
1809: /**
1810: * Returns <code>true</code> if this <code>SComponent</code> is owning the edit focus.
1811: *
1812: * @return <code>true</code> if this <code>SComponent</code> is owning the edit focus otherwise <code>false</code>
1813: * @see #requestFocus()
1814: */
1815: public boolean isFocusOwner() {
1816: if (getParentFrame() != null)
1817: return this == getParentFrame().getFocus();
1818: return false;
1819: }
1820:
1821: /**
1822: * Set display mode (href or form-component).
1823: * An AbstractButton can appear as HTML-Form-Button or as
1824: * HTML-HREF. If button is inside a {@link SForm} the default
1825: * is displaying it as html form button.
1826: * Setting <i>showAsFormComponent</i> to <i>false</i> will
1827: * force displaying as href even if button is inside
1828: * a form.
1829: *
1830: * @param showAsFormComponent if true, display as link, if false as html form component.
1831: */
1832: public void setShowAsFormComponent(boolean showAsFormComponent) {
1833: if (this .showAsFormComponent != showAsFormComponent) {
1834: this .showAsFormComponent = showAsFormComponent;
1835: reload();
1836: }
1837: }
1838:
1839: /**
1840: * Test, what display method is set.
1841: *
1842: * @return true, if displayed as link, false when displayed as html form component.
1843: * @see #setShowAsFormComponent(boolean)
1844: */
1845: public boolean getShowAsFormComponent() {
1846: return showAsFormComponent && getResidesInForm();
1847: }
1848:
1849: /**
1850: * Binds action names to {@link Action}s. Use for key binding feature.
1851: *
1852: * @param actionMap The new action map.
1853: * @see #setInputMap(javax.swing.InputMap)
1854: * @see ActionMap
1855: * @see InputMap
1856: */
1857: public void setActionMap(ActionMap actionMap) {
1858: this .actionMap = actionMap;
1859: }
1860:
1861: /**
1862: * Action map for key binding feature
1863: *
1864: * @return The current action map
1865: * @see #setActionMap(javax.swing.ActionMap)
1866: */
1867: public ActionMap getActionMap() {
1868: if (actionMap == null)
1869: actionMap = new ActionMap();
1870: return actionMap;
1871: }
1872:
1873: /**
1874: * Map for key binding feature. (?) Binds input keystrokes to action names (?).
1875: *
1876: * @param inputMap The current input map.
1877: * @see InputMap
1878: * @see ActionMap
1879: * @see #setActionMap(javax.swing.ActionMap)
1880: * @see JComponent#setInputMap(int, javax.swing.InputMap)
1881: */
1882: public void setInputMap(InputMap inputMap) {
1883: setInputMap(WHEN_FOCUSED_OR_ANCESTOR_OF_FOCUSED_COMPONENT,
1884: inputMap);
1885: }
1886:
1887: /**
1888: * Sets The current input map.
1889: *
1890: * @param inputMap The new input map
1891: * @param condition Either {@link #WHEN_FOCUSED_OR_ANCESTOR_OF_FOCUSED_COMPONENT} or {@link #WHEN_IN_FOCUSED_FRAME}
1892: * @see JComponent#setInputMap(int, javax.swing.InputMap)
1893: */
1894: public void setInputMap(int condition, InputMap inputMap) {
1895: initInputMaps();
1896: this .inputMaps[condition] = inputMap;
1897: registerGlobalInputMapWithFrame();
1898: }
1899:
1900: /**
1901: * @return The input map for the condition {@link #WHEN_FOCUSED_OR_ANCESTOR_OF_FOCUSED_COMPONENT}
1902: * @see #setInputMap(javax.swing.InputMap)
1903: */
1904: public InputMap getInputMap() {
1905: return getInputMap(WHEN_FOCUSED_OR_ANCESTOR_OF_FOCUSED_COMPONENT);
1906: }
1907:
1908: /**
1909: * @param condition Either {@link #WHEN_FOCUSED_OR_ANCESTOR_OF_FOCUSED_COMPONENT} or {@link #WHEN_IN_FOCUSED_FRAME}
1910: * @return The input map for the given condition.
1911: * @see #setInputMap(int, javax.swing.InputMap)
1912: */
1913: public InputMap getInputMap(int condition) {
1914: initInputMaps();
1915: InputMap result = inputMaps[condition];
1916: if (result == null) {
1917: inputMaps[condition] = new InputMap();
1918: result = inputMaps[condition];
1919: }
1920: registerGlobalInputMapWithFrame();
1921: return result;
1922: }
1923:
1924: private void registerGlobalInputMapWithFrame() {
1925: final SFrame parentFrame = getParentFrame();
1926: if (parentFrame != null)
1927: parentFrame.registerGlobalInputMapComponent(this );
1928:
1929: if (globalInputMapListener == null) {
1930: globalInputMapListener = new GlobalInputMapParentFrameListener(
1931: this );
1932: addParentFrameListener(globalInputMapListener);
1933: }
1934: }
1935:
1936: private void initInputMaps() {
1937: if (inputMaps == null) {
1938: inputMaps = new InputMap[ACTION_CONDITIONS_AMOUNT];
1939: }
1940: }
1941:
1942: protected void processLowLevelEvent(String name, String[] values) {
1943: }
1944:
1945: protected boolean processKeyEvents(String[] values) {
1946: if (actionMap == null)
1947: return false;
1948:
1949: if (log.isDebugEnabled())
1950: log.debug("processKeyEvents " + Arrays.asList(values));
1951:
1952: boolean arm = false;
1953: for (String value : values) {
1954: final Action action = actionMap.get(value);
1955: if (action != null) {
1956: if (actionEvents == null)
1957: actionEvents = new HashMap<Action, ActionEvent>();
1958:
1959: actionEvents.put(action,
1960: new ActionEvent(this , 0, value));
1961: arm = true;
1962: }
1963: }
1964: if (arm)
1965: SForm.addArmedComponent((LowLevelEventListener) this );
1966:
1967: return arm;
1968: }
1969:
1970: /**
1971: * Internal event trigger used by CGs.
1972: * This Method is called internal and should not be called directly
1973: */
1974: public void fireFinalEvents() {
1975: fireKeyEvents();
1976: }
1977:
1978: /**
1979: * Internal method to trigger firing of key events.
1980: */
1981: protected void fireKeyEvents() {
1982: if (actionEvents != null) {
1983: for (Map.Entry<Action, ActionEvent> entry : actionEvents
1984: .entrySet()) {
1985: Action action = entry.getKey();
1986: ActionEvent event = entry.getValue();
1987: action.actionPerformed(event);
1988: }
1989: actionEvents.clear();
1990: }
1991: }
1992:
1993: /**
1994: * Method called to notify this <code>SComponent</code> that it has no longer a parent component.
1995: * This Method is called internal and should not be called directly, but can be overerloaded
1996: * to react on this event.
1997: */
1998: public void removeNotify() {
1999: /* currently nothing to do, but great to overwrite for some dangling eventListener */
2000: }
2001:
2002: /**
2003: * Method called to notify this <code>SComponent</code> that it has a new parent component.
2004: * This Method is called internal and should not be called directly, but can be overerloaded
2005: * to react on this event.
2006: */
2007: public void addNotify() {
2008: /* currently nothing to do */
2009: }
2010:
2011: // Nice: undocumented and useless
2012: /*public ArrayList getMenus() {
2013: ArrayList menus = new ArrayList();
2014: if (isVisible()) {
2015: SPopupMenu pmenu = getComponentPopupMenu();
2016: if (pmenu != null) {
2017: menus.add(pmenu);
2018: }
2019: }
2020: return menus;
2021: } */
2022:
2023: private void readObject(ObjectInputStream in) throws IOException,
2024: ClassNotFoundException {
2025: // preprocessing, e. g. serialize static vars or transient variables as cipher text
2026: in.defaultReadObject(); // default serialization
2027: // do postprocessing here
2028: }
2029:
2030: private void writeObject(ObjectOutputStream out) throws IOException {
2031: try {
2032: // preprocessing
2033: out.defaultWriteObject(); // default
2034: // postprocessing
2035: } catch (IOException e) {
2036: log.warn("Unexpected Exception", e);
2037: throw e;
2038: }
2039: }
2040:
2041: /**
2042: * Listener registering/deregistering this component on the parent frame.
2043: */
2044: private final static class GlobalInputMapParentFrameListener
2045: implements SParentFrameListener, Serializable {
2046: private final SComponent me;
2047:
2048: public GlobalInputMapParentFrameListener(SComponent me) {
2049: this .me = me;
2050: }
2051:
2052: public void parentFrameAdded(SParentFrameEvent e) {
2053: if (e.getParentFrame() != null)
2054: e.getParentFrame().registerGlobalInputMapComponent(me);
2055: }
2056:
2057: public void parentFrameRemoved(SParentFrameEvent e) {
2058: if (e.getParentFrame() != null)
2059: e.getParentFrame()
2060: .deregisterGlobalInputMapComponent(me);
2061: }
2062: }
2063: }
|