Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.wings.SComponent Maven / Gradle / Ivy
/*
* 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 ScriptListener
s 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);
}
}