All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.wings.SComponent Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2000,2005 wingS development team.
 *
 * This file is part of wingS (http://wingsframework.org).
 *
 * wingS is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2.1
 * of the License, or (at your option) any later version.
 *
 * Please see COPYING for the complete licence.
 */
package org.wings;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.wings.border.SBorder;
import org.wings.event.*;
import org.wings.io.Device;
import org.wings.plaf.ComponentCG;
import org.wings.plaf.Update;
import org.wings.script.JavaScriptListener;
import org.wings.script.ScriptListener;
import org.wings.session.Session;
import org.wings.session.SessionManager;
import org.wings.style.*;
import org.wings.util.ComponentVisitor;

import javax.swing.*;
import javax.swing.event.EventListenerList;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.beans.*;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.util.*;
import java.util.List;

/**
 * Object having a graphical representation that can be displayed on the
 * screen and that can interact with the user.
 *
 * @author Armin Haaf
 */
public abstract class SComponent implements Cloneable, Serializable, Renderable {

    private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];

    private static final Logger log = LoggerFactory.getLogger(SComponent.class);

    private static final int ACTION_CONDITIONS_AMOUNT = 2;

    /* Components unique name. */
    private String name;

    /**
     * the session
     */
    private transient Session session;

    /**
     * The code generation delegate, which is responsible for
     * the visual representation of this component.
     */
    private transient ComponentCG cg;

    /**
     * Vertical alignment
     */
    private int verticalAlignment = SConstants.NO_ALIGN;

    /**
     * Horizontal alignment
     */
    private int horizontalAlignment = SConstants.NO_ALIGN;

    /**
     * The name of the style class
     */
    private String style;

    private HashMap styles;

    /**
     * Map of {@link Selector} to CSS {@link Style}s currently assigned to this component.
     */
    protected Map dynamicStyles;

    /**
     * Visibility of the component.
     */
    protected boolean visible = true;

    /**
     * Visibility of the component.
     */
    protected boolean recursivelyVisible = false;

    /**
     * Enabled / disabled.
     */
    protected boolean enabled = true;

    /**
     * The container, this component resides in.
     */
    private SContainer parent;

    /**
     * The frame in which this component resides.
     */
    private SFrame parentFrame;

    /**
     * The border for the component.
     */
    private SBorder border;

    /**
     * The tooltip for this component.
     */
    private String tooltip;

    /**
     * The focus traversal Index
     */
    private int focusTraversalIndex = -1;

    /**
     * Preferred size of component in pixel.
     */
    private SDimension preferredSize;

    /**
     * This is for performance optimizations. With this flag is set, property change
     * events are generated and so every property setter method has to test if a property
     * has changed and temporarily store the old value to generate the property
     * change event
     */
    private boolean fireComponentChangeEvents = false;

    /**
     * Generate and fire {@link SParentFrameEvent}s. Performace optimitation
     */
    private boolean fireParentFrameChangeEvents = false;

    /**
     * Flag indiccating if {@link SRenderEvent}s should be fired. Used for performace reasons
     */
    private boolean fireRenderEvents = false;

    /**
     * All event listeners of this component
     */
    private EventListenerList listeners;

    private boolean showAsFormComponent = true;

    private boolean reloadForced = false;

    private SPopupMenu popupMenu;

    /*private boolean inheritsPopupMenu;*/

    private InputMap[] inputMaps;

    /**
     * Contains all script listeners of the component.
     */
    private List scriptListenerList;

    private ActionMap actionMap;

    private Map actionEvents;

    private transient SRenderEvent renderEvent;

    private SParentFrameListener globalInputMapListener;

    private Map clientProperties;

    /**
     * Internal constants for {@link #fireRenderEvent(int)}
     */
    public static final int START_RENDERING = 1;

    /**
     * Internal constants for {@link #fireRenderEvent(int)}
     */
    public static final int DONE_RENDERING = 2;

    /**
     * Constants for conditions on which actions are triggered. Mainly two
     * cases: the focus has either to be at the component (or at a child)
     * or somewhere in the parent frame.
     *
     * @see #setInputMap(int, javax.swing.InputMap)
     */
    public static final int WHEN_FOCUSED_OR_ANCESTOR_OF_FOCUSED_COMPONENT = 0;

    /**
     * Constants for conditions on which actions are triggered. Mainly two
     * cases: the focus has either to be at the component (or at a child)
     * or somewhere in the parent frame.
     *
     * @see #setInputMap(int, javax.swing.InputMap)
     */
    public static final int WHEN_IN_FOCUSED_FRAME = 1;

    /**
     * Global CSS selector 
     */
    public static final Selector SELECTOR_ALL = new Selector("everything");

    /**
     * Performance improvement constant
     */
    private static final ScriptListener[] EMPTY_SCRIPTLISTENERLIST = new ScriptListener[0];

    /**
     *  PropertyChangeSupport
     */
    protected final PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);


    private STransferHandler transferHandler = null;


    /**
     * Default empty constructor.
     * The method updateCG is called during construction time to get a cg delegate installed (renderer).
     */
    public SComponent() {
        updateCG();
    }

    /**
     * Returns the border of this component or null if no border has been set.
     *
     * @return the border object
     * @see #setBorder(SBorder)
     */
    public SBorder getBorder() {
        return border;
    }

    /**
     * Sets the border for this component.
     *
     * @param border the border to be set for the component
     */
    public void setBorder(SBorder border) {
        SBorder oldVal = this.border;
        reloadIfChange(this.border, border);
        if (this.border != null)
            this.border.setComponent(null);
        this.border = border;
        if (this.border != null)
            this.border.setComponent(this);
        propertyChangeSupport.firePropertyChange("border", oldVal, this.border);
    }

    /**
     * Return the parent container.
     *
     * @return the container this component resides in
     */
    public final SContainer getParent() {
        return parent;
    }

    /**
     * Sets the parent container. Also gets the parent frame from the parent.
     *
     * @param parent the container
     */
    public void setParent(SContainer parent) {
        SContainer oldVal = this.parent;
        this.parent = parent;
        if (parent != null)
            setParentFrame(parent.getParentFrame());
        else
            setParentFrame(null);
        propertyChangeSupport.firePropertyChange("parent", oldVal, this.parent);

        setRecursivelyVisible(parent != null && parent.isRecursivelyVisible());
    }

    /**
     * Sets the parent frame.
     *
     * @param parentFrame the frame
     */
    protected void setParentFrame(SFrame parentFrame) {
        SFrame oldVal = this.parentFrame;
        if (this.parentFrame == parentFrame) {
            return;
        }

        if (this.parentFrame != null) {
            unregister();
            fireParentFrameEvent(new SParentFrameEvent(this, SParentFrameEvent.PARENTFRAME_REMOVED, this.parentFrame));
        }

        this.parentFrame = parentFrame;

        if (this.parentFrame != null) {
            register();
            // notify the listeners...
            fireParentFrameEvent(new SParentFrameEvent(this, SParentFrameEvent.PARENTFRAME_ADDED, this.parentFrame));
        }

        if (this.popupMenu != null) {
            popupMenu.setParentFrame(parentFrame);
        }

        //reload();
        if (getScriptListeners().length > 0)
            reload();
        if (dynamicStyles != null && dynamicStyles.size() > 0)
            reload();

        propertyChangeSupport.firePropertyChange("parentFrame", oldVal, this.parentFrame);
    }

    public void setComponentPopupMenu(SPopupMenu popupMenu) {
        reloadIfChange(this.popupMenu, popupMenu);
        if (this.popupMenu != null) {
            getSession().getMenuManager().deregisterMenuLink(this.popupMenu, this);
            this.popupMenu.setParentFrame(null);
        }
        SPopupMenu oldVal = this.popupMenu;
        this.popupMenu = popupMenu;
        if (this.popupMenu != null) {
            getSession().getMenuManager().registerMenuLink(this.popupMenu, this);
            this.popupMenu.setParentFrame(getParentFrame());
        }
        propertyChangeSupport.firePropertyChange("componentPopupMenu", oldVal, this.popupMenu);
    }

    public SPopupMenu getComponentPopupMenu() {
        return popupMenu;
    }

    /**
     * The URL under which this component is accessible for the browser.
     * This is equivalent to the URL of the component's root frame, as this is the
     * node externalized to the browser via the {@link org.wings.resource.ReloadResource}
     * externalizer.
     *
     * @return The HTTP URL where this component can be accessed.
     */
    public RequestURL getRequestURL() {
        SFrame p = getParentFrame();
        if (p == null)
            throw new IllegalStateException("no parent frame");

        return p.getRequestURL();
    }

    /**
     * Set the preferred size of the receiving component in pixel.
     * It is not guaranteed that the component accepts this property because of
     * missing implementations in the component cg or html properties.
     * If width or height is zero, it is ignored and the browser
     * defines the size.
     *
     * @see org.wings.SComponent#getPreferredSize
     */
    public void setPreferredSize(SDimension preferredSize) {
        SDimension oldVal = this.preferredSize;
        reloadIfChange(this.preferredSize, preferredSize);
        this.preferredSize = preferredSize;
        propertyChangeSupport.firePropertyChange("preferredSize", oldVal, this.preferredSize);
    }

    /**
     * Get the preferred size of this component.
     *
     * @see SComponent#setPreferredSize
     */
    public SDimension getPreferredSize() {
        return preferredSize;
    }


    /**
     * Adds the specified component listener to receive component events from
     * this component.
     * If l is null, no exception is thrown and no action is performed.
     *
     * @param l the component listener.
     * @see org.wings.event.SComponentEvent
     * @see org.wings.event.SComponentListener
     * @see org.wings.SComponent#removeComponentListener
     */
    public final void addComponentListener(SComponentListener l) {
        addEventListener(SComponentListener.class, l);
        fireComponentChangeEvents = true;
    }

    /**
     * Removes the specified component listener so that it no longer
     * receives component events from this component. This method performs
     * no function, nor does it throw an exception, if the listener
     * specified by the argument was not previously added to this component.
     * If l is null, no exception is thrown and no action is performed.
     *
     * @param l the component listener.
     * @see org.wings.event.SComponentEvent
     * @see org.wings.event.SComponentListener
     * @see org.wings.SComponent#addComponentListener
     */
    public final void removeComponentListener(SComponentListener l) {
        removeEventListener(SComponentListener.class, l);
    }

    /**
     * Registers a "parent frame listener" to receive events from
     * this component when the parent frame chanegs i.e. via {@link #setParentFrame(SFrame)}.
     * If l is null, no exception is thrown and no action is performed.
     *
     * @param l the parent frame listener. May be null.
     * @see org.wings.event.SParentFrameEvent
     * @see org.wings.event.SParentFrameListener
     * @see org.wings.SComponent#removeParentFrameListener
     * @see SComponent#removeNotify()
     * @see SComponent#addNotify()
     */
    public final void addParentFrameListener(SParentFrameListener l) {
        addEventListener(SParentFrameListener.class, l);
        fireParentFrameChangeEvents = true;
    }

    /**
     * Removes the specified parent frame listener so that it no longer
     * receives events from this component. This method performs
     * no function, nor does it throw an exception, if the listener
     * specified by the argument was not previously added to this component.
     * If l is null, no exception is thrown and no action is performed.
     *
     * @param l the parent frame listener.
     * @see org.wings.event.SParentFrameEvent
     * @see org.wings.event.SParentFrameListener
     * @see org.wings.SComponent#addParentFrameListener
     * @see SComponent#removeNotify()
     * @see SComponent#addNotify()
     */
    public final void removeParentFrameListener(SParentFrameListener l) {
        removeEventListener(SParentFrameListener.class, l);
    }

    /**
     * Reports a component change.
     *
     * @param aEvent report this event to all listeners
     * @see org.wings.event.SComponentListener
     */
    protected void fireComponentChangeEvent(SComponentEvent aEvent) {
        // maybe the better way to do this is to user the getListenerList
        // and iterate through all listeners, this saves the creation of
        // an array but it must cast to the apropriate listener
        Object[] listeners = getListenerList();
        for (int i = listeners.length - 2; i >= 0; i -= 2) {
            if (listeners[i] == SComponentListener.class) {
                // Lazily create the event:
                processComponentEvent((SComponentListener) listeners[i + 1],
                        aEvent);
            }
        }

    }

    /**
     * Reports a parent frame change.
     *
     * @param aEvent report this event to all listeners
     * @see org.wings.event.SParentFrameListener
     */
    private void fireParentFrameEvent(SParentFrameEvent aEvent) {
        // are listeners registered?
        if (fireParentFrameChangeEvents) {
            // maybe the better way to do this is to user the getListenerList
            // and iterate through all listeners, this saves the creation of
            // an array but it must cast to the apropriate listener
            Object[] listeners = getListenerList();
            for (int i = listeners.length - 2; i >= 0; i -= 2) {
                if (listeners[i] == SParentFrameListener.class) {
                    // Lazily create the event:
                    processParentFrameEvent((SParentFrameListener) listeners[i + 1],
                            aEvent);
                }
            }
        }

    }

    /**
     * Processes parent frame events occurring on this component by
     * dispatching them to any registered
     * SParentFrameListener objects.
     * 

*/ private static void processParentFrameEvent(SParentFrameListener listener, SParentFrameEvent event) { int id = event.getID(); switch (id) { case SParentFrameEvent.PARENTFRAME_ADDED: listener.parentFrameAdded(event); break; case SParentFrameEvent.PARENTFRAME_REMOVED: listener.parentFrameRemoved(event); break; } } /** * Processes component events occurring on this component by * dispatching them to any registered * SComponentListener objects. *

* This method is not called unless component events are * enabled for this component. Component events are enabled * when one of the following occurs: *

    *
  • A SComponentListener object is registered * via addComponentListener. *
* * @param e the component event. * @see org.wings.event.SComponentEvent * @see org.wings.event.SComponentListener * @see org.wings.SComponent#addComponentListener */ protected static void processComponentEvent(SComponentListener listener, SComponentEvent e) { int id = e.getID(); switch (id) { case SComponentEvent.COMPONENT_RESIZED: listener.componentResized(e); break; case SComponentEvent.COMPONENT_MOVED: listener.componentMoved(e); break; case SComponentEvent.COMPONENT_SHOWN: listener.componentShown(e); break; case SComponentEvent.COMPONENT_HIDDEN: listener.componentHidden(e); break; } } /** * Adds the specified component listener to receive component events from * this component. * If l is null, no exception is thrown and no action is performed. * If there is already a ScriptListener which is equal, the new one is not * added. * * @param listener the component listener. * @see org.wings.event.SComponentEvent * @see org.wings.event.SComponentListener * @see org.wings.SComponent#removeComponentListener */ public final void addScriptListener(ScriptListener listener) { if (scriptListenerList != null && scriptListenerList.contains(listener)) return; if (scriptListenerList == null) { scriptListenerList = new LinkedList<>(); } int placingPosition = -1; for (int i = 0; i < scriptListenerList.size() && placingPosition < 0; i++) { ScriptListener existingListener = scriptListenerList.get(i); if (existingListener.getPriority() < listener.getPriority()) placingPosition = i; } reload(); if (placingPosition >= 0) scriptListenerList.add(placingPosition, listener); else scriptListenerList.add(listener); } public final void addScriptListenerWithoutReload(ScriptListener listener) { if (scriptListenerList != null && scriptListenerList.contains(listener)) return; if (scriptListenerList == null) { scriptListenerList = new LinkedList<>(); } int placingPosition = -1; for (int i = 0; i < scriptListenerList.size() && placingPosition < 0; i++) { ScriptListener existingListener = scriptListenerList.get(i); if (existingListener.getPriority() < listener.getPriority()) placingPosition = i; } if (placingPosition >= 0) scriptListenerList.add(placingPosition, listener); else scriptListenerList.add(listener); } /** * Removes the specified component listener so that it no longer * receives component events from this component. This method performs * no function, nor does it throw an exception, if the listener * specified by the argument was not previously added to this component. * If l is null, no exception is thrown and no action is performed. * * @param listener the component listener. * @see org.wings.event.SComponentEvent * @see org.wings.event.SComponentListener * @see org.wings.SComponent#addComponentListener */ public final void removeScriptListener(ScriptListener listener) { if (scriptListenerList != null) { scriptListenerList.remove(listener); reload(); } } public final void removeScriptListenerWithoutReload(ScriptListener listener) { if(scriptListenerList != null) { scriptListenerList.remove(listener); } } /** * returns the script listeners of this component * * @return the ScriptListener Array. */ public ScriptListener[] getScriptListeners() { if (scriptListenerList != null) { return scriptListenerList.toArray(new ScriptListener[scriptListenerList.size()]); } else { return EMPTY_SCRIPTLISTENERLIST; } } /** * Returns the script listeners of this component. * * @return The ScriptListeners in a List. */ public List getScriptListenerList() { if (scriptListenerList != null) { return Collections.unmodifiableList(scriptListenerList); } else { return Collections.emptyList(); } } /** * Sets the name property of a component which must be unique! *
Assigning the same name multiple times will cause strange results! *

*

Valid names must begin with a letter ([A-Za-z]), underscores ("_") or dollars ("$") and may be followed by any number of * letters, digits ([0-9]), underscores ("_") and dollars ("$") *

*

If no name is set, it is generated when necessary. *

*

Explanation: This property is an identifier which is used inside the generated HTML as an element identifier (id="") * and sometimes as a javascript function name. * * @param uniqueName A unique name to set. Only valid identifier as described are allowed! * @see Character */ public void setName(String uniqueName) { if (uniqueName != null) { char ch = uniqueName.charAt(0); if (uniqueName.length() == 0 || !(Character.isLetter(ch) || ch == '_' || ch == '$')) throw new IllegalArgumentException(uniqueName + " is not a valid identifier"); for (int i = 1; i < uniqueName.length(); i++) { ch = uniqueName.charAt(i); if (!(Character.isLetter(ch) || Character.isDigit(ch) || ch == '_' || ch == '$')) throw new IllegalArgumentException(uniqueName + " is not a valid identifier"); } } setNameRaw(uniqueName); } /** * Direct setter for name. Do not use unless you explicitly know what you're doing! * (Former package) protected raw setter for component name to avoid sanity check. * * @param uncheckedName String to use as componentn name/identifier. */ public void setNameRaw(String uncheckedName) { String oldVal = this.name; reloadIfChange(this.name, uncheckedName); this.name = uncheckedName; unregister(); register(); propertyChangeSupport.firePropertyChange("name", oldVal, this.name); } /** * Gets the name property of a component. This property is an identifier,so it should be always unique. * For details refer to {@link #setName(String)} * * @return The name of the component. */ public final String getName() { if (name == null) name = getSession().createUniqueId(); return name; } /** * Return the session this component belongs to. * * @return the session */ public final Session getSession() { if (session == null) { session = SessionManager.getSession(); } return session; } /* * If a subclass implements the {@link LowLevelEventListener} interface, * it will be unregistered at the associated dispatcher. */ final void unregister() { if (getSession().getDispatcher() != null && this instanceof LowLevelEventListener) { getSession().getDispatcher().unregister((LowLevelEventListener) this); } } /* * If a subclass implements the {@link LowLevelEventListener} interface, * it will be registered at the associated dispatcher. */ final void register() { if (getSession().getDispatcher() != null && this instanceof LowLevelEventListener) { getSession().getDispatcher().register((LowLevelEventListener) this); } } /** * Set an CSS class name provided by the laf-provided Cascading Style Sheet (CSS), which should * be applied to this component. *

* Note: Probably the {@link #addStyle(String)} method is more what you want. *

By default this is set to the wingS component class name (i.e. "SLabel"). *

The PLAFs render the value of this String to an class="cssClassName" attribute * inside the generated HTML code of this component. *

The default wingS plaf initializes this by default to the wingS component class name * (i.e. SButton for button instances).
Please be aware if you replace this * default value, the default wingS style will no longer take effect, as they operate on these * default styles. To avoid this you should append your CSS styles via spaces i.e.
* c.setStyle(c.getStyle + "myStyle"); *

Please consider using {@link #addStyle(String)} to avoid disabling of default wingS stylesheet set. * * @param cssClassName The new CSS name value for this component * @see #addStyle(String) * @see #removeStyle(String) */ public void setStyle(String cssClassName) { String oldVal = this.style; reloadIfChange(style, cssClassName); this.style = cssClassName; propertyChangeSupport.firePropertyChange("style", oldVal, this.style); } /** * Append a style class name to the style string. Use this method if you want to append a specific CSS * class to a componentn without loosing the other CSS styles assigned to the component (i.e. the wingS * default styles.) * @param additionalCssClassName The style class to remove (if existing). * @see #removeStyle(String) */ public void addStyle(String additionalCssClassName) { if (this.style == null || this.style.length() == 0) { setStyle(additionalCssClassName); // trivial case } else { if (!this.style.contains(additionalCssClassName)) { setStyle(this.style+ ' ' +additionalCssClassName); } } } /** * Remove a style class definiton from this component. * @param cssStyleClassName The style class to remove (if existing). */ public void removeStyle(String cssStyleClassName) { if (this.style != null && cssStyleClassName != null && this.style.contains(cssStyleClassName)) { if (this.style.length() == cssStyleClassName.length()) setStyle(null); // trivial case else setStyle(this.style.replaceAll("\\b"+cssStyleClassName+"\\b","").replaceAll(" "," ").trim()); } } /** * @return The current CSS style class name. Defaults to the unqualified wingS component class name. * @see #setStyle(String) */ public String getStyle() { return style; } /** * Set an CSS class name for a certain area of the component which is addressed by the pseudo selector. * * @param selector The pseudo selector addressing an area of the component * @param cssClassName The new CSS name value for this component */ public void setStyle(Selector selector, String cssClassName) { if (styles == null) styles = new HashMap<>(4); if (cssClassName == null) styles.remove(selector); else styles.put(selector, cssClassName); reload(); } public String getStyle(Selector selector) { return styles != null ? styles.get(selector) : null; } /** * Register a new CSS style on this component for a specfic CSS selector. *

Typically you will want to use the method {@link #setAttribute(org.wings.style.CSSProperty, String)} * to specify a single CSS property/value pair on this component. * * @param style A Style instance. */ public void addDynamicStyle(Style style) { if (dynamicStyles == null) dynamicStyles = new HashMap<>(4); dynamicStyles.put(style.getSelector(), style); reload(); } /** * Remove all CSS style definitions defined for the passed CSS selector. * * @param selector The selector. The default selector for most CSS attributes is {@link #SELECTOR_ALL}. */ public void removeDynamicStyle(Selector selector) { if (dynamicStyles == null) return; dynamicStyles.remove(selector); reload(); } /** * Returns the style defined for the passed CSS selector. * * @param selector The CSS selector the style to retrieve. See {@link org.wings.style.Style#getSelector()}. * The default selector for most CSS styles is {@link #SELECTOR_ALL}. * @return A style (collection of css property/value pairs) or null */ public Style getDynamicStyle(Selector selector) { if (dynamicStyles == null) return null; return dynamicStyles.get(selector); } /** * Adds the passed collection of {@link Style} definitions. Existing Styles for the same CSS selectors * (see {@link org.wings.style.Style#getSelector()}) are overwritten. * * @param dynamicStyles A collection collection of {@link Style} definitions. */ public void setDynamicStyles(Collection dynamicStyles) { Map oldVal = this.dynamicStyles; if (dynamicStyles == null) return; if (this.dynamicStyles == null) this.dynamicStyles = new HashMap<>(4); for (Object dynamicStyle : dynamicStyles) { Style style = (Style) dynamicStyle; this.dynamicStyles.put(style.getSelector(), style); } reload(); propertyChangeSupport.firePropertyChange("dynamicStyles", oldVal, this.dynamicStyles); } /** * Returns the collection of currently defined CSS styles on this component. * * @return A unmodifyable collection of {@link Style} instances. */ public Collection getDynamicStyles() { if (dynamicStyles == null || dynamicStyles.size() == 0) return null; return Collections.unmodifiableCollection(dynamicStyles.values()); } /** * Defines a free text css property / value pair to this component. * The CSS property will appear as an inline style in the generated HTML code. */ public void setAttribute(String cssPropertyName, String value) { final CSSProperty property = CSSProperty.valueOf(cssPropertyName); if (CSSProperty.BORDER_PROPERTIES.contains(property)) throw new IllegalArgumentException("Border properties have to be applied to the border!"); setAttribute(SELECTOR_ALL, property, value); } /** * Assign or overwrite a CSS property/value pair on this component. This CSS property definition will * use a CSS selector which adresses this component as whole as CSS selector (new CSSProperty(this)). * The CSS property will appear as an inline style in the generated HTML code. * * @param property The CSS property (i.e. {@link CSSProperty#BACKGROUND}). * @param propertyValue A valid string value for this CSS property (i.e. red or #fff in our example). */ public void setAttribute(CSSProperty property, String propertyValue) { if (CSSProperty.BORDER_PROPERTIES.contains(property)) throw new IllegalArgumentException("Border properties have to be applied to the border!"); setAttribute(SELECTOR_ALL, property, propertyValue); } /** * Assign or overwrite a CSS property/value pair at this component. This CSS property definition will * use the CSS selector you passed, so in the most exotic case it could affect a totally different * component or component area. Typically you use this method to assign CSS property values to * pseudo CSS selectors {@link Selector}. This are selector affecting only a part of a component * and not the component at all.. * The CSS property will appear as an inline style in the generated HTML code. * * @param selector A valid CSS selector. Typically values are i.e. the {@link #SELECTOR_ALL} * or other SELECTOR_xxx value instances declared in the component. * (look ie. at {@link STabbedPane#SELECTOR_CONTENT}) or manually constructed instances of * Selector. In most case {@link #setAttribute(org.wings.style.CSSProperty, String)} will be your * choice. * @param property The css property you want to define a value for * @param propertyValue A valid string value for this property. */ public void setAttribute(Selector selector, CSSProperty property, String propertyValue) { Style style = getDynamicStyle(selector); if (style == null) { addDynamicStyle(new CSSStyle(selector, property, propertyValue)); } else { Map oldStyle = style.properties(); String oldVal = oldStyle.get(property); String old = style.put(property, propertyValue); reloadIfChange(old, propertyValue); propertyChangeSupport.firePropertyChange("attribute", oldVal, propertyValue); } } /** * Convenience variant of {@link #setAttribute(org.wings.style.Selector, org.wings.style.CSSProperty, String)}. * Converts the passed icon into a URL and applies the according CSS style. * * @param selector A valid CSS selector. Typically values are i.e. the {@link #SELECTOR_ALL} * or other SELECTOR_xxx value instances declared in the component. * (look ie. at {@link STabbedPane#SELECTOR_CONTENT}) or manually constructed instances of * Selector. In most case {@link #setAttribute(org.wings.style.CSSProperty, String)} will be your * choice. * @param property The css property you want to define a value for (in this case * mostly something like {@link CSSProperty#BACKGROUND_IMAGE}. * @param icon The icon you want to assign. */ public void setAttribute(Selector selector, CSSProperty property, SIcon icon) { setAttribute(selector, property, icon != null ? "url('" + icon.getURL().toString() + "')" : "none"); } /** * Convenience variant of {@link #setAttribute(org.wings.style.Selector, org.wings.style.CSSProperty, String)}. * Converts the passed color into according color string. * * @param selector A valid CSS selector. Typically values are i.e. the {@link #SELECTOR_ALL} * or other SELECTOR_xxx value instances declared in the component. * (look ie. at {@link STabbedPane#SELECTOR_CONTENT}) or manually constructed instances of * Selector. In most case {@link #setAttribute(org.wings.style.CSSProperty, String)} will be your * choice. * @param property The css property you want to define a value for (in this case * mostly something like {@link CSSProperty#BACKGROUND_IMAGE}. * @param color The color value you want to assign. */ public void setAttribute(Selector selector, CSSProperty property, Color color) { setAttribute(selector, property, CSSStyleSheet.getAttribute(color)); } public void setAttributes(Selector selector, CSSAttributeSet attributes) { Style style = getDynamicStyle(selector); if (style == null) { addDynamicStyle(new CSSStyle(selector, attributes)); } else { Map oldStyleProperties = style.properties(); boolean changed = style.putAll(attributes); if (changed) reload(); propertyChangeSupport.firePropertyChange("attributes", oldStyleProperties, style.properties()); } } /** * Returns the current background color of this component. * * @return The current background color or null */ public Color getBackground() { return dynamicStyles == null || dynamicStyles.get(SELECTOR_ALL) == null ? null : CSSStyleSheet.getBackground((CSSAttributeSet) dynamicStyles.get(SELECTOR_ALL)); } /** * Set the components background color. * * @param color the new background color or null */ public void setBackground(Color color) { Color oldVal = this.getBackground(); setAttribute(SELECTOR_ALL, CSSProperty.BACKGROUND_COLOR, CSSStyleSheet.getAttribute(color)); propertyChangeSupport.firePropertyChange("background", oldVal, this.getBackground()); } /** * Return the components foreground color. * * @return the foreground color or null */ public Color getForeground() { return dynamicStyles == null || dynamicStyles.get(SELECTOR_ALL) == null ? null : CSSStyleSheet.getForeground((CSSAttributeSet) dynamicStyles.get(SELECTOR_ALL)); } /** * Set the foreground color. * * @param color the new foreground color or null */ public void setForeground(Color color) { Color oldVal = this.getForeground(); setAttribute(SELECTOR_ALL, CSSProperty.COLOR, CSSStyleSheet.getAttribute(color)); propertyChangeSupport.firePropertyChange("foreground", oldVal, this.getForeground()); } /** * Set the font. * * @param font the new font */ //TODO: firePropertyChange() in IF public void setFont(SFont font) { SFont oldVal = this.getFont(); CSSAttributeSet attributes = CSSStyleSheet.getAttributes(font); Style style = getDynamicStyle(SELECTOR_ALL); if (style == null) { addDynamicStyle(new CSSStyle(SELECTOR_ALL, attributes)); } else { style.remove(CSSProperty.FONT); style.remove(CSSProperty.FONT_FAMILY); style.remove(CSSProperty.FONT_SIZE); style.remove(CSSProperty.FONT_STYLE); style.remove(CSSProperty.FONT_WEIGHT); style.putAll(attributes); reload(); propertyChangeSupport.firePropertyChange("font", oldVal, this.getFont()); } } /** * Return the font used inside this component. * * @return The current font declaration or null */ public SFont getFont() { return dynamicStyles == null || dynamicStyles.get(SELECTOR_ALL) == null ? null : CSSStyleSheet.getFont((CSSAttributeSet) dynamicStyles.get(SELECTOR_ALL)); } /** * Set the visibility. * * @param visible wether this component will show or not */ public void setVisible(boolean visible) { boolean old = this.visible; this.visible = visible; if (visible != old) { if (parent != null) { parent.reload(); } else { reload(); } propertyChangeSupport.firePropertyChange("visible", old, this.visible); setRecursivelyVisible(isRecursivelyVisible()); } } /** * Return the local visibility. If set to true this ccmponent * should be visible if all parent components are visible, too. * * @return true If the component and it's children should show, false otherwise * @see #isRecursivelyVisible() */ public boolean isVisible() { return visible; } /** * Return the visibility. If the Component itself or any of it's parent is invisible, * this method will return false. * * @return true if this component and all it's ancestors are visible, false otherwise. */ public boolean isRecursivelyVisible() { return recursivelyVisible; } protected void setRecursivelyVisible(boolean recursivelyVisible) { if (isRecursivelyVisible() != recursivelyVisible) { this.recursivelyVisible = recursivelyVisible; if (fireComponentChangeEvents) fireComponentChangeEvent(new SComponentEvent(this, isRecursivelyVisible() ? SComponentEvent.COMPONENT_SHOWN : SComponentEvent.COMPONENT_HIDDEN)); if (popupMenu != null) if (recursivelyVisible) getSession().getMenuManager().registerMenuLink(this.popupMenu, this); else getSession().getMenuManager().deregisterMenuLink(this.popupMenu, this); } } /** * Set wether this component should be enabled. * * @param enabled true if the component is enabled, false otherwise */ public void setEnabled(boolean enabled) { boolean oldVal = this.enabled; reloadIfChange(this.enabled, enabled); this.enabled = enabled; propertyChangeSupport.firePropertyChange("enabled", oldVal, this.enabled); } /** * Return true if this component is enabled. * * @return true if component is enabled */ public boolean isEnabled() { return enabled; } /** * Marks this component as subject to reload. * The component will be registered with the ReloadManager. */ public void reload() { getSession().getReloadManager().reload(this); } /** * Hands the given update to the Reload Manager. * @param update the update for this component */ public void update(Update update) { getSession().getReloadManager().addUpdate(this, update); } protected boolean isUpdatePossible() { return getSession().getReloadManager().isUpdateMode(); } public boolean isReloadForced() { return reloadForced; } public void setReloadForced(boolean forced) { if (reloadForced != forced) { boolean oldVal = this.reloadForced; Object clientProperty = getClientProperty("onChangeSubmitListener"); if (clientProperty instanceof JavaScriptListener) { removeScriptListener((JavaScriptListener) clientProperty); putClientProperty("onChangeSubmitListener", null); } reloadForced = forced; reload(); propertyChangeSupport.firePropertyChange("reloadForced", oldVal, this.reloadForced); } } /** * Mark this component as subject to reload if the property, * that is given in its old and new fashion, changed. * * Fires an PropertyChangeEvent with the property name, the old value and the new value * * @param oldVal the old value of some property * @param newVal the new value of some property */ protected final void reloadIfChange(Object oldVal, Object newVal) { if (isDifferent(oldVal, newVal)) reload(); } /** * Mark this component as subject to reload if the property, * that is given in its old and new fashion, changed. * * @param oldVal the old value of some property * @param newVal the new value of some property */ protected final void reloadIfChange(int oldVal, int newVal) { if (oldVal != newVal) reload(); } /** * Mark this component as subject to reload if the property, * that is given in its old and new fashion, changed. * * @param oldVal the old value of some property * @param newVal the new value of some property */ protected final void reloadIfChange(boolean oldVal, boolean newVal) { if (oldVal != newVal) reload(); } /** * Mark this component as subject to reload if the property, * that is given in its old and new fashion, changed. * * @param oldVal the old value of some property * @param newVal the new value of some property */ protected final void reloadIfChange(byte oldVal, byte newVal) { if (oldVal != newVal) reload(); } /** * Mark this component as subject to reload if the property, * that is given in its old and new fashion, changed. * * @param oldVal the old value of some property * @param newVal the new value of some property */ protected final void reloadIfChange(short oldVal, short newVal) { if (oldVal != newVal) reload(); } /** * Mark this component as subject to reload if the property, * that is given in its old and new fashion, changed. * * @param oldVal the old value of some property * @param newVal the new value of some property */ protected final void reloadIfChange(long oldVal, long newVal) { if (oldVal != newVal) reload(); } /** * Mark this component as subject to reload if the property, * that is given in its old and new fashion, changed. * * @param oldVal the old value of some property * @param newVal the new value of some property */ protected final void reloadIfChange(float oldVal, float newVal) { if (oldVal != newVal) reload(); } /** * Mark this component as subject to reload if the property, * that is given in its old and new fashion, changed. * * @param oldVal the old value of some property * @param newVal the new value of some property */ protected final void reloadIfChange(double oldVal, double newVal) { if (oldVal != newVal) reload(); } /** * Mark this component as subject to reload if the property, * that is given in its old and new fashion, changed. * * @param oldVal the old value of some property * @param newVal the new value of some property */ protected final void reloadIfChange(char oldVal, char newVal) { if (oldVal != newVal) reload(); } /** * Mark this component as subject to reload if the property, * that is given in its old and new fashion, changed. */ @Override @SuppressWarnings({"unchecked"}) public void write(Device s) throws IOException { try { if (visible) { cg.write(s, this); } } catch (IOException se) { // Typical double-clicks. Not severe log.debug("Not Severe: Socket exception during code generation for " + getClass().getName() + se); } catch (Throwable t) { // should we warn here? or maybe throw an error... log.error("Exception during code generation for " + getClass().getName(), t); } } /** * A string representation of this component. Uses the {@link #paramString()} methods * * @return string representation of this component with all properties. */ @Override public String toString() { return getClass().getName() + '[' + getName() + ']'; } /** * Generates a string describing this SComponent. * This method is mainly for debugging purposes. * * @return a string containing all properties */ protected String paramString() { StringBuilder buffer = new StringBuilder(getClass().getName()); buffer.append('['); try { BeanInfo info = Introspector.getBeanInfo(getClass()); PropertyDescriptor[] descriptors = info.getPropertyDescriptors(); boolean first = true; for (PropertyDescriptor descriptor : descriptors) { try { Method getter = descriptor.getReadMethod(); if (getter == null || getter.getName().startsWith("getParent")) { continue; } // System.out.println("invoking " + this.getClass().getDescription()+"."+getter.getDescription()); Object value = getter.invoke(this); if (first) { first = false; } else { buffer.append(','); } buffer.append(descriptor.getName()).append('=').append(value); } catch (Exception e) { log.debug("Exception during paramString()" +e ); } } } catch (Exception e) { log.debug("Exception during paramString()" +e ); } buffer.append(']'); return buffer.toString(); } /** * Default implementation of the method in * {@link LowLevelEventListener}. */ public String getLowLevelEventId() { return getName(); } /** * Return the parent frame. *

NOTE: You will receive null if you call this i.e. during * component creation time, as the parent frame is set when you add it to a visible {@link SContainer}. * Use {@link #addParentFrameListener(org.wings.event.SParentFrameListener)} in this case. * * @return the parent frame * @see #addParentFrameListener(org.wings.event.SParentFrameListener) */ public SFrame getParentFrame() { return parentFrame; } /** * Return true, if this component is contained in a form. * * @return true, if this component resides in a form, false otherwise */ public boolean getResidesInForm() { SComponent parent = this.parent; boolean actuallyDoes = parent instanceof SForm; while (parent != null && !actuallyDoes) { parent = parent.parent; actuallyDoes = parent instanceof SForm; } return actuallyDoes; } /** * Set the tooltip text. To style use HTML tags. * * @param t the new tooltip text */ public void setToolTipText(String t) { String oldVal = this.tooltip; reloadIfChange(this.tooltip, t); this.tooltip = t; propertyChangeSupport.firePropertyChange("toolTipText", oldVal, this.tooltip); } /** * Return the tooltip text. * * @return the tooltip text */ public String getToolTipText() { return tooltip; } /** * The index in which the focus is traversed using Tab. This is * a very simplified notion of traversing the focus, but that is, * what browser like interfaces currently offer. This has a bit rough * edge, since you have to make sure, that the index is unique within * the whole frame. You probably don't want to change this * programmatically, but this is set usually by the template property * manager. * * @param index the focus traversal index. Pressing the focus traversal * key (usually TAB) in the browser jumps to the next index. * Must not be zero. */ public void setFocusTraversalIndex(int index) { int oldVal = this.focusTraversalIndex; reloadIfChange(this.focusTraversalIndex, index); focusTraversalIndex = index; propertyChangeSupport.firePropertyChange("focusTraversalIndex", oldVal, this.focusTraversalIndex); } /** * returns the focus traversal index. * * @see #setFocusTraversalIndex(int) */ public int getFocusTraversalIndex() { return focusTraversalIndex; } /** * Clone this component. * * @return a clone of this component */ @Override public Object clone() { try { return super.clone(); } catch (Exception e) { log.error("Unable to clone component", e); return null; } } /** * Return the value of the horizontal alignment property. * * @return the horizontal alignment * @see SConstants */ public int getHorizontalAlignment() { return horizontalAlignment; } /** * Set the horizontal alignment. * * @param alignment new value for the horizontal alignment * @see SConstants */ public void setHorizontalAlignment(int alignment) { int oldVal = this.horizontalAlignment; reloadIfChange(this.horizontalAlignment, alignment); horizontalAlignment = alignment; propertyChangeSupport.firePropertyChange("horizontalAlignment", oldVal, this.horizontalAlignment); } /** * Set the vertical alignment. * * @param alignment new value for the vertical alignment * @see SConstants */ public void setVerticalAlignment(int alignment) { int oldVal = this.verticalAlignment; reloadIfChange(this.verticalAlignment, alignment); verticalAlignment = alignment; propertyChangeSupport.firePropertyChange("verticalAlignment", oldVal, this.verticalAlignment); } /** * Return the value of the vertical alignment property. * * @return the vertical alignment * @see SConstants */ public int getVerticalAlignment() { return verticalAlignment; } /** * @return a small HashMap * @see #putClientProperty * @see #getClientProperty */ private Map getClientProperties() { if (clientProperties == null) { clientProperties = new HashMap<>(2); } return clientProperties; } /** * Returns the value of the property with the specified key. Only * properties added with putClientProperty will return * a non-null value. * * @return the value of this property or null * @see #putClientProperty */ public final Object getClientProperty(Object key) { if (clientProperties == null) { return null; } else { return getClientProperties().get(key); } } /** * Add an arbitrary key/value "client property" to this component. *

* The get/putClientProperty methods provide access to * a small per-instance hashtable. Callers can use get/putClientProperty * to annotate components that were created by another module, e.g. a * layout manager might store per child constraints this way. For example: *

     * componentA.putClientProperty("to the left of", componentB);
     * 
*

* If value is null this method will remove the property. * Changes to client properties are reported with PropertyChange * events. The name of the property (for the sake of PropertyChange * events) is key.toString(). *

* The clientProperty dictionary is not intended to support large * scale extensions to SComponent nor should be it considered an * alternative to subclassing when designing a new component. * * @see #getClientProperty */ public final void putClientProperty(Object key, Object value) { if (value != null) { getClientProperties().put(key, value); } else { getClientProperties().remove(key); } } /** * Set the look and feel delegate for this component. * SComponent subclasses generally override this method * to narrow the argument type, e.g. in STextField: *

     * public void setCG(TextFieldCG newCG) {
     *     super.setCG(newCG);
     * }
     * 
* * @see #updateCG * @see org.wings.plaf.CGManager#getLookAndFeel * @see org.wings.plaf.CGManager#getCG */ @SuppressWarnings({"unchecked"}) public void setCG(ComponentCG newCG) { /* We do not check that the CG instance is different * before allowing the switch in order to enable the * same CG instance *with different default settings* * to be installed. */ if (cg != null) { cg.uninstallCG(this); } ComponentCG oldCG = cg; cg = newCG; if (cg != null) { cg.installCG(this); } reloadIfChange(cg, oldCG); } /** * Return the look and feel delegate. * * @return the componet's cg */ public ComponentCG getCG() { return cg; } /** * Notification from the CGFactory that the L&F has changed. * * @see SComponent#updateCG */ public void updateCG() { if (getSession() == null) { log.warn("no session yet."); } else if (getSession().getCGManager() == null) { log.warn("no CGManager"); } else { setCG(getSession().getCGManager().getCG(this)); } } /** * Invite a ComponentVisitor. * Invokes visit(SComponent) on the ComponentVisitor. * * @param visitor the visitor to be invited */ public void invite(ComponentVisitor visitor) throws Exception { visitor.visit(this); } /** * use this method for changing a variable. if a new value is different * from the old value set the new one and notify e.g. the reloadmanager... */ protected static boolean isDifferent(Object oldObject, Object newObject) { if (oldObject == newObject) return false; if (oldObject == null) return true; return !oldObject.equals(newObject); } /** * Adds an event listener for the given event class * * @param type The class/type of events to listen to. * @param listener The listener itself. */ protected final void addEventListener(Class type, T listener) { if (listeners == null) { listeners = new EventListenerList(); } listeners.add(type, listener); } /** * Removed named event listener. * * @param type The class/type of events to listen to. * @param listener The listener itself. */ protected final void removeEventListener(Class type, T listener) { if (listeners != null) { listeners.remove(type, listener); } } /** * Returns the number of listeners of the specified type for this component. * * @param type The type of listeners * @return The number of listeners * @see EventListenerList */ protected final int getListenerCount(Class type) { if (listeners != null) { return listeners.getListenerCount(type); } else { return 0; } } /** * Returns all the listeners of this component. For performance reasons, this is the actual data * structure and so no modification of this array should be made. * * @return All listeners of this component. The result array has a pair structure, * the first element of each pair is the listener type, the second the listener * itself. It is guaranteed that this returns a non-null array. * @see EventListenerList */ protected final Object[] getListenerList() { if (listeners == null) { return EMPTY_OBJECT_ARRAY; } else { return listeners.getListenerList(); } // end of else } /** * Creates an typed array of all listeners of the specified type * * @param type All listeners of this type are added to the result array * @return an array of the specified type with all listeners of the specified type * @see EventListenerList */ public final EventListener[] getListeners(Class type) { if (listeners != null) { return listeners.getListeners(type); } else { return (EventListener[]) Array.newInstance(type, 0); } } /** * Adds a listenere to this component which wil get notified when the rendering of * this components starts. (This happens after all events / request has been processed and wingS * starts to build the response). * * @param renderListener * @see SRenderListener */ public final void addRenderListener(SRenderListener renderListener) { addEventListener(SRenderListener.class, renderListener); fireRenderEvents = true; } /** * Removes the named render listener. * * @param renderListener Render listener to remove * @see #addRenderListener(org.wings.event.SRenderListener) * @see SRenderListener */ public final void removeRenderListener(SRenderListener renderListener) { removeEventListener(SRenderListener.class, renderListener); } /** * Internal method called by the CGs to indicate different states of the rendering process. * * @param type Either {@link #DONE_RENDERING} or {@link #START_RENDERING}. */ public final void fireRenderEvent(int type) { if (fireRenderEvents) { // maybe the better way to do this is to user the getListenerList // and iterate through all listeners, this saves the creation of // an array but it must cast to the apropriate listener Object[] listeners = getListenerList(); for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == SRenderListener.class) { // Lazily create the event: if (renderEvent == null) { renderEvent = new SRenderEvent(this); } // end of if () switch (type) { case START_RENDERING: ((SRenderListener) listeners[i + 1]).startRendering(renderEvent); break; case DONE_RENDERING: ((SRenderListener) listeners[i + 1]).doneRendering(renderEvent); break; } } } } } /** * Forwards the scrollRectToVisible() message to the SComponent's * parent. Components that can service the request, such as * SScrollPane, override this method and perform the scrolling. * * @param aRect the visible Rectangle * @see SScrollPane */ public void scrollRectToVisible(Rectangle aRect) { if (parent != null) { parent.scrollRectToVisible(aRect); } } /** * Requests the edit focus for this component for the following renderings * by calling {@link SFrame#setFocus(SComponent)}. */ public void requestFocus() { if (getParentFrame() != null) { getParentFrame().setFocus(this); } } /** * Returns true if this SComponent is owning the edit focus. * * @return true if this SComponent is owning the edit focus otherwise false * @see #requestFocus() */ public boolean isFocusOwner() { if (getParentFrame() != null) return this == getParentFrame().getFocus(); return false; } /** * Set display mode (href or form-component). * An AbstractButton can appear as HTML-Form-Button or as * HTML-HREF. If button is inside a {@link SForm} the default * is displaying it as html form button. * Setting showAsFormComponent to false will * force displaying as href even if button is inside * a form. Beware that form buttons submit the whole form while * href buttons send a get request! * * @param showAsFormComponent if true, display as link, if false as html form component. */ public void setShowAsFormComponent(boolean showAsFormComponent) { if (this.showAsFormComponent != showAsFormComponent) { boolean oldVal = this.showAsFormComponent; this.showAsFormComponent = showAsFormComponent; reload(); propertyChangeSupport.firePropertyChange("showAsFormComponent", oldVal, this.showAsFormComponent); } } /** * Test, what display method is set. * * @return true, if displayed as link, false when displayed as html form component. * @see #setShowAsFormComponent(boolean) */ public boolean getShowAsFormComponent() { return showAsFormComponent && getResidesInForm(); } /** * Binds action names to {@link Action}s. Use for key binding feature. * * @param actionMap The new action map. * @see #setInputMap(javax.swing.InputMap) * @see ActionMap * @see InputMap */ public void setActionMap(ActionMap actionMap) { ActionMap oldVal = this.actionMap; this.actionMap = actionMap; propertyChangeSupport.firePropertyChange("actionMap", oldVal, this.actionMap); } /** * Action map for key binding feature * * @return The current action map * @see #setActionMap(javax.swing.ActionMap) */ public ActionMap getActionMap() { if (actionMap == null) actionMap = new ActionMap(); return actionMap; } /** * Map for key binding feature. (?) Binds input keystrokes to action names (?). * * @param inputMap The current input map. * @see InputMap * @see ActionMap * @see #setActionMap(javax.swing.ActionMap) * @see JComponent#setInputMap(int, javax.swing.InputMap) */ public void setInputMap(InputMap inputMap) { setInputMap(WHEN_FOCUSED_OR_ANCESTOR_OF_FOCUSED_COMPONENT, inputMap); } /** * Sets The current input map. * * @param inputMap The new input map * @param condition Either {@link #WHEN_FOCUSED_OR_ANCESTOR_OF_FOCUSED_COMPONENT} or {@link #WHEN_IN_FOCUSED_FRAME} * @see JComponent#setInputMap(int, javax.swing.InputMap) */ public void setInputMap(int condition, InputMap inputMap) { initInputMaps(); InputMap oldVal = inputMaps[condition]; this.inputMaps[condition] = inputMap; registerGlobalInputMapWithFrame(); propertyChangeSupport.firePropertyChange("inputMap", oldVal, this.inputMaps[condition]); } /** * @return The input map for the condition {@link #WHEN_FOCUSED_OR_ANCESTOR_OF_FOCUSED_COMPONENT} * @see #setInputMap(javax.swing.InputMap) */ public InputMap getInputMap() { return getInputMap(WHEN_FOCUSED_OR_ANCESTOR_OF_FOCUSED_COMPONENT); } /** * @param condition Either {@link #WHEN_FOCUSED_OR_ANCESTOR_OF_FOCUSED_COMPONENT} or {@link #WHEN_IN_FOCUSED_FRAME} * @return The input map for the given condition. * @see #setInputMap(int, javax.swing.InputMap) */ public InputMap getInputMap(int condition) { initInputMaps(); InputMap result = inputMaps[condition]; if (result == null) { inputMaps[condition] = new InputMap(); result = inputMaps[condition]; } registerGlobalInputMapWithFrame(); return result; } private void registerGlobalInputMapWithFrame() { final SFrame parentFrame = getParentFrame(); if (parentFrame != null) parentFrame.registerGlobalInputMapComponent(this); if (globalInputMapListener == null) { globalInputMapListener = new GlobalInputMapParentFrameListener(this); addParentFrameListener(globalInputMapListener); } } private void initInputMaps() { if (inputMaps == null) { inputMaps = new InputMap[ACTION_CONDITIONS_AMOUNT]; } } protected void processLowLevelEvent(String name, String... values) { } protected boolean processKeyEvents(String... values) { if (actionMap == null) return false; if (log.isDebugEnabled()) log.debug("processKeyEvents " + Arrays.asList(values)); boolean arm = false; for (String value : values) { final Action action = actionMap.get(value); if (action != null) { if (actionEvents == null) actionEvents = new HashMap<>(); actionEvents.put(action, new ActionEvent(this, 0, value)); arm = true; } } if (arm) SForm.addArmedComponent((LowLevelEventListener) this); return arm; } /** * Internal event trigger used by CGs. * This Method is called internal and should not be called directly */ public void fireFinalEvents() { fireKeyEvents(); } /** * Internal method to trigger firing of key events. */ protected void fireKeyEvents() { if (actionEvents != null) { for (Map.Entry entry : actionEvents.entrySet()) { Action action = entry.getKey(); ActionEvent event = entry.getValue(); action.actionPerformed(event); } actionEvents.clear(); } } /** * Method called to notify this SComponent that it has no longer a parent component. * This Method is called internal and should not be called directly, but can be overerloaded * to react on this event. */ public void removeNotify() { /* currently nothing to do, but great to overwrite for some dangling eventListener */ } /** * Method called to notify this SComponent that it has a new parent component. * This Method is called internal and should not be called directly, but can be overerloaded * to react on this event. */ public void addNotify() { /* currently nothing to do */ } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { // preprocessing, e. g. serialize static vars or transient variables as cipher text in.defaultReadObject(); // default serialization // do postprocessing here } private void writeObject(ObjectOutputStream out) throws IOException { try { // preprocessing out.defaultWriteObject(); // default // postprocessing } catch (IOException e) { log.warn("Unexpected Exception", e); throw e; } } public void setTransferHandler(STransferHandler newHandler) { STransferHandler oldHandler = this.transferHandler; this.transferHandler = newHandler; propertyChangeSupport.firePropertyChange("transferHandler", oldHandler, this.transferHandler); } public STransferHandler getTransferHandler() { return this.transferHandler; } protected STransferHandler.DropLocation dropLocationForPoint(SPoint p) { return null; } /** * Listener registering/deregistering this component on the parent frame. */ private final static class GlobalInputMapParentFrameListener implements SParentFrameListener, Serializable { private final SComponent me; public GlobalInputMapParentFrameListener(SComponent me) { this.me = me; } @Override public void parentFrameAdded(SParentFrameEvent e) { if (e.getParentFrame() != null) e.getParentFrame().registerGlobalInputMapComponent(me); } @Override public void parentFrameRemoved(SParentFrameEvent e) { if (e.getParentFrame() != null) e.getParentFrame().deregisterGlobalInputMapComponent(me); } } /** * Add a PropertyChangeListener to the listener list. The listener is registered for all properties. * */ public void addPropertyChangeListener(PropertyChangeListener listener) { if(listener == null) return; propertyChangeSupport.addPropertyChangeListener(listener); } /** * Remove a PropertyChangeListener from the listener list. * */ public void removePropertyChangeListener(PropertyChangeListener listener) { if(propertyChangeSupport != null) propertyChangeSupport.removePropertyChangeListener(listener); } /** * Add a PropertyChangeListener for a specific property. */ public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) { if(listener == null || propertyName == null) return; propertyChangeSupport.addPropertyChangeListener(propertyName, listener); } /** * Remove a PropertyChangeListener for a specific property. */ public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) { if(propertyChangeSupport != null || propertyName != null) propertyChangeSupport.removePropertyChangeListener(propertyName, listener); } }