diff options
Diffstat (limited to 'src/com/vaadin/ui')
30 files changed, 1636 insertions, 1862 deletions
diff --git a/src/com/vaadin/ui/AbsoluteLayout.java b/src/com/vaadin/ui/AbsoluteLayout.java index 9ba005f75a..7d8c402fc9 100644 --- a/src/com/vaadin/ui/AbsoluteLayout.java +++ b/src/com/vaadin/ui/AbsoluteLayout.java @@ -161,7 +161,8 @@ public class AbsoluteLayout extends AbstractLayout implements // connectorId unless the component is attached to the application so // the String->String map cannot be populated in internal* either. Map<String, String> connectorToPosition = new HashMap<String, String>(); - for (Component c : this) { + for (Iterator<Component> ci = getComponentIterator(); ci.hasNext();) { + Component c = ci.next(); connectorToPosition.put(c.getConnectorId(), getPosition(c) .getCSSString()); } diff --git a/src/com/vaadin/ui/AbstractComponent.java b/src/com/vaadin/ui/AbstractComponent.java index 554d7806f9..ba0e5db89c 100644 --- a/src/com/vaadin/ui/AbstractComponent.java +++ b/src/com/vaadin/ui/AbstractComponent.java @@ -5,20 +5,13 @@ package com.vaadin.ui; import java.io.Serializable; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; -import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.HashMap; import java.util.Iterator; -import java.util.LinkedList; import java.util.List; import java.util.Locale; -import java.util.Map; -import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -27,18 +20,14 @@ import com.vaadin.event.ActionManager; import com.vaadin.event.EventRouter; import com.vaadin.event.MethodEventSource; import com.vaadin.event.ShortcutListener; +import com.vaadin.terminal.AbstractClientConnector; import com.vaadin.terminal.ErrorMessage; import com.vaadin.terminal.Resource; import com.vaadin.terminal.Terminal; import com.vaadin.terminal.gwt.client.ComponentState; -import com.vaadin.terminal.gwt.client.communication.ClientRpc; -import com.vaadin.terminal.gwt.client.communication.ServerRpc; -import com.vaadin.terminal.gwt.server.ClientMethodInvocation; +import com.vaadin.terminal.gwt.server.ClientConnector; import com.vaadin.terminal.gwt.server.ComponentSizeValidator; import com.vaadin.terminal.gwt.server.ResourceReference; -import com.vaadin.terminal.gwt.server.RpcManager; -import com.vaadin.terminal.gwt.server.RpcTarget; -import com.vaadin.terminal.gwt.server.ServerRpcManager; import com.vaadin.tools.ReflectTools; /** @@ -53,7 +42,8 @@ import com.vaadin.tools.ReflectTools; * @since 3.0 */ @SuppressWarnings("serial") -public abstract class AbstractComponent implements Component, MethodEventSource { +public abstract class AbstractComponent extends AbstractClientConnector + implements Component, MethodEventSource { /* Private members */ @@ -64,11 +54,6 @@ public abstract class AbstractComponent implements Component, MethodEventSource private Object applicationData; /** - * The container this component resides in. - */ - private HasComponents parent = null; - - /** * The EventRouter used for the event model. */ private EventRouter eventRouter = null; @@ -88,11 +73,6 @@ public abstract class AbstractComponent implements Component, MethodEventSource */ private boolean delayedFocus; - /** - * List of repaint request listeners or null if not listened at all. - */ - private LinkedList<RepaintRequestListener> repaintRequestListeners = null; - /* Sizeable fields */ private float width = SIZE_UNDEFINED; @@ -110,31 +90,6 @@ public abstract class AbstractComponent implements Component, MethodEventSource */ private ActionManager actionManager; - /** - * A map from client to server RPC interface class to the RPC call manager - * that handles incoming RPC calls for that interface. - */ - private Map<Class<?>, RpcManager> rpcManagerMap = new HashMap<Class<?>, RpcManager>(); - - /** - * A map from server to client RPC interface class to the RPC proxy that - * sends ourgoing RPC calls for that interface. - */ - private Map<Class<?>, ClientRpc> rpcProxyMap = new HashMap<Class<?>, ClientRpc>(); - - /** - * Shared state object to be communicated from the server to the client when - * modified. - */ - private ComponentState sharedState; - - /** - * Pending RPC method invocations to be sent. - */ - private ArrayList<ClientMethodInvocation> pendingInvocations = new ArrayList<ClientMethodInvocation>(); - - private String connectorId; - /* Constructor */ /** @@ -287,6 +242,7 @@ public abstract class AbstractComponent implements Component, MethodEventSource if (locale != null) { return locale; } + HasComponents parent = getParent(); if (parent != null) { return parent.getLocale(); } @@ -378,21 +334,18 @@ public abstract class AbstractComponent implements Component, MethodEventSource * * @see com.vaadin.terminal.gwt.client.Connector#isConnectorEnabled() */ + @Override public boolean isConnectorEnabled() { - if (getParent() == null) { - // No parent -> the component cannot receive updates from the client + if (!isVisible()) { + return false; + } else if (!isEnabled()) { + return false; + } else if (!super.isConnectorEnabled()) { + return false; + } else if (!getParent().isComponentVisible(this)) { return false; } else { - boolean thisEnabledAndVisible = isEnabled() && isVisible(); - if (!thisEnabledAndVisible) { - return false; - } - - if (!getParent().isConnectorEnabled()) { - return false; - } - - return getParent().isComponentVisible(this); + return true; } } @@ -529,8 +482,20 @@ public abstract class AbstractComponent implements Component, MethodEventSource * Gets the component's parent component. Don't add a JavaDoc comment here, * we use the default documentation from implemented interface. */ + @Override public HasComponents getParent() { - return parent; + return (HasComponents) super.getParent(); + } + + @Override + public void setParent(ClientConnector parent) { + if (parent == null || parent instanceof HasComponents) { + super.setParent(parent); + } else { + throw new IllegalArgumentException( + "The parent of a Component must implement HasComponents, which " + + parent.getClass() + " doesn't do."); + } } /** @@ -558,36 +523,6 @@ public abstract class AbstractComponent implements Component, MethodEventSource return null; } - /* - * Sets the parent component. Don't add a JavaDoc comment here, we use the - * default documentation from implemented interface. - */ - public void setParent(HasComponents parent) { - - // If the parent is not changed, don't do anything - if (parent == this.parent) { - return; - } - - if (parent != null && this.parent != null) { - throw new IllegalStateException(getClass().getName() - + " already has a parent."); - } - - // Send detach event if the component have been connected to a window - if (getApplication() != null) { - detach(); - } - - // Connect to new parent - this.parent = parent; - - // Send attach event if connected to a window - if (getApplication() != null) { - attach(); - } - } - /** * Gets the error message for this component. * @@ -648,12 +583,10 @@ public abstract class AbstractComponent implements Component, MethodEventSource * Gets the parent window of the component. Don't add a JavaDoc comment * here, we use the default documentation from implemented interface. */ + @Override public Root getRoot() { - if (parent == null) { - return null; - } else { - return parent.getRoot(); - } + // Just make method from implemented Component interface public + return super.getRoot(); } /* @@ -661,9 +594,9 @@ public abstract class AbstractComponent implements Component, MethodEventSource * comment here, we use the default documentation from implemented * interface. */ + @Override public void attach() { - getRoot().componentAttached(this); - requestRepaint(); + super.attach(); if (delayedFocus) { focus(); } @@ -674,13 +607,14 @@ public abstract class AbstractComponent implements Component, MethodEventSource * Detach the component from application. Don't add a JavaDoc comment here, * we use the default documentation from implemented interface. */ + @Override public void detach() { + super.detach(); if (actionManager != null) { // Remove any existing viewer. Root cast is just to make the // compiler happy actionManager.setViewer((Root) null); } - getRoot().componentDetached(this); } /** @@ -717,12 +651,10 @@ public abstract class AbstractComponent implements Component, MethodEventSource * @return the parent application of the component or <code>null</code>. * @see #attach() */ + @Override public Application getApplication() { - if (parent == null) { - return null; - } else { - return parent.getApplication(); - } + // Just make method inherited from Component interface public + return super.getApplication(); } /** @@ -762,11 +694,9 @@ public abstract class AbstractComponent implements Component, MethodEventSource * * @return updated component shared state */ + @Override public ComponentState getState() { - if (null == sharedState) { - sharedState = createState(); - } - return sharedState; + return (ComponentState) super.getState(); } /* @@ -801,95 +731,15 @@ public abstract class AbstractComponent implements Component, MethodEventSource } } - /** - * Creates the shared state bean to be used in server to client - * communication. - * <p> - * By default a state object of the defined return type of - * {@link #getState()} is created. Subclasses can override this method and - * return a new instance of the correct state class but this should rarely - * be necessary. - * </p> - * <p> - * No configuration of the values of the state should be performed in - * {@link #createState()}. - * - * @since 7.0 - * - * @return new shared state object - */ - protected ComponentState createState() { - try { - return getStateType().newInstance(); - } catch (Exception e) { - throw new RuntimeException( - "Error creating state of type " + getStateType().getName() - + " for " + getClass().getName(), e); - } - } - - /* (non-Javadoc) - * @see com.vaadin.terminal.gwt.server.ClientConnector#getStateType() - */ - public Class<? extends ComponentState> getStateType() { - try { - Method m = getClass().getMethod("getState", (Class[]) null); - Class<? extends ComponentState> type = (Class<? extends ComponentState>) m - .getReturnType(); - return type; - } catch (Exception e) { - throw new RuntimeException("Error finding state type for " - + getClass().getName(), e); - } - } - /* Documentation copied from interface */ + @Override public void requestRepaint() { // Invisible components (by flag in this particular component) do not // need repaints if (!getState().isVisible()) { return; } - - fireRequestRepaintEvent(); - } - - /** - * Fires the repaint request event. - * - * @param alreadyNotified - */ - // Notify listeners only once - private void fireRequestRepaintEvent() { - // Notify the listeners - if (repaintRequestListeners != null - && !repaintRequestListeners.isEmpty()) { - final Object[] listeners = repaintRequestListeners.toArray(); - final RepaintRequestEvent event = new RepaintRequestEvent(this); - for (int i = 0; i < listeners.length; i++) { - ((RepaintRequestListener) listeners[i]).repaintRequested(event); - } - } - } - - /* Documentation copied from interface */ - public void addListener(RepaintRequestListener listener) { - if (repaintRequestListeners == null) { - repaintRequestListeners = new LinkedList<RepaintRequestListener>(); - } - if (!repaintRequestListeners.contains(listener)) { - repaintRequestListeners.add(listener); - } - } - - /* Documentation copied from interface */ - public void removeListener(RepaintRequestListener listener) { - if (repaintRequestListeners != null) { - repaintRequestListeners.remove(listener); - if (repaintRequestListeners.isEmpty()) { - repaintRequestListeners = null; - } - } + super.requestRepaint(); } /* General event framework */ @@ -1155,15 +1005,6 @@ public abstract class AbstractComponent implements Component, MethodEventSource * are found. */ public Collection<?> getListeners(Class<?> eventType) { - if (eventType.isAssignableFrom(RepaintRequestEvent.class)) { - // RepaintRequestListeners are not stored in eventRouter - if (repaintRequestListeners == null) { - return Collections.EMPTY_LIST; - } else { - return Collections - .unmodifiableCollection(repaintRequestListeners); - } - } if (eventRouter == null) { return Collections.EMPTY_LIST; } @@ -1512,159 +1353,4 @@ public abstract class AbstractComponent implements Component, MethodEventSource actionManager.removeAction(shortcut); } } - - /** - * Registers an RPC interface implementation for this component. - * - * A component can listen to multiple RPC interfaces, and subclasses can - * register additional implementations. - * - * @since 7.0 - * - * @param implementation - * RPC interface implementation - * @param rpcInterfaceType - * RPC interface class for which the implementation should be - * registered - */ - protected <T> void registerRpc(T implementation, Class<T> rpcInterfaceType) { - rpcManagerMap.put(rpcInterfaceType, new ServerRpcManager<T>( - implementation, rpcInterfaceType)); - } - - /** - * Registers an RPC interface implementation for this component. - * - * A component can listen to multiple RPC interfaces, and subclasses can - * register additional implementations. - * - * @since 7.0 - * - * @param implementation - * RPC interface implementation. Also used to deduce the type. - */ - protected <T extends ServerRpc> void registerRpc(T implementation) { - Class<?> cls = implementation.getClass(); - Class<?>[] interfaces = cls.getInterfaces(); - while (interfaces.length == 0) { - // Search upwards until an interface is found. It must be found as T - // extends ServerRpc - cls = cls.getSuperclass(); - interfaces = cls.getInterfaces(); - } - if (interfaces.length != 1 - || !(ServerRpc.class.isAssignableFrom(interfaces[0]))) { - throw new RuntimeException( - "Use registerRpc(T implementation, Class<T> rpcInterfaceType) if the Rpc implementation implements more than one interface"); - } - Class<T> type = (Class<T>) interfaces[0]; - registerRpc(implementation, type); - } - - /** - * Returns an RPC proxy for a given server to client RPC interface for this - * component. - * - * TODO more javadoc, subclasses, ... - * - * @param rpcInterface - * RPC interface type - * - * @since 7.0 - */ - public <T extends ClientRpc> T getRpcProxy(final Class<T> rpcInterface) { - // create, initialize and return a dynamic proxy for RPC - try { - if (!rpcProxyMap.containsKey(rpcInterface)) { - Class<T> proxyClass = (Class) Proxy.getProxyClass( - rpcInterface.getClassLoader(), rpcInterface); - Constructor<T> constructor = proxyClass - .getConstructor(InvocationHandler.class); - T rpcProxy = constructor.newInstance(new RpcInvoicationHandler( - rpcInterface)); - // cache the proxy - rpcProxyMap.put(rpcInterface, rpcProxy); - } - return (T) rpcProxyMap.get(rpcInterface); - } catch (Exception e) { - // TODO exception handling? - throw new RuntimeException(e); - } - } - - private class RpcInvoicationHandler implements InvocationHandler, - Serializable { - - private String rpcInterfaceName; - - public RpcInvoicationHandler(Class<?> rpcInterface) { - rpcInterfaceName = rpcInterface.getName().replaceAll("\\$", "."); - } - - public Object invoke(Object proxy, Method method, Object[] args) - throws Throwable { - addMethodInvocationToQueue(rpcInterfaceName, method, args); - // TODO no need to do full repaint if only RPC calls - requestRepaint(); - return null; - } - - } - - /** - * For internal use: adds a method invocation to the pending RPC call queue. - * - * @param interfaceName - * RPC interface name - * @param methodName - * RPC method name - * @param parameters - * RPC vall parameters - * - * @since 7.0 - */ - protected void addMethodInvocationToQueue(String interfaceName, - Method method, Object[] parameters) { - // add to queue - pendingInvocations.add(new ClientMethodInvocation(this, interfaceName, - method, parameters)); - } - - /** - * @see RpcTarget#getRpcManager(Class) - * - * @param rpcInterface - * RPC interface for which a call was made - * @return RPC Manager handling calls for the interface - * - * @since 7.0 - */ - public RpcManager getRpcManager(Class<?> rpcInterface) { - return rpcManagerMap.get(rpcInterface); - } - - public List<ClientMethodInvocation> retrievePendingRpcCalls() { - if (pendingInvocations.isEmpty()) { - return Collections.emptyList(); - } else { - List<ClientMethodInvocation> result = pendingInvocations; - pendingInvocations = new ArrayList<ClientMethodInvocation>(); - return Collections.unmodifiableList(result); - } - } - - public String getConnectorId() { - if (connectorId == null) { - if (getApplication() == null) { - throw new RuntimeException( - "Component must be attached to an application when getConnectorId() is called for the first time"); - } - connectorId = getApplication().createConnectorId(this); - } - return connectorId; - } - - private Logger getLogger() { - return Logger.getLogger(AbstractComponent.class.getName()); - } } diff --git a/src/com/vaadin/ui/AbstractComponentContainer.java b/src/com/vaadin/ui/AbstractComponentContainer.java index 1c857a03cd..8ef458b704 100644 --- a/src/com/vaadin/ui/AbstractComponentContainer.java +++ b/src/com/vaadin/ui/AbstractComponentContainer.java @@ -71,36 +71,6 @@ public abstract class AbstractComponentContainer extends AbstractComponent } } - /** - * Notifies all contained components that the container is attached to a - * window. - * - * @see com.vaadin.ui.Component#attach() - */ - @Override - public void attach() { - super.attach(); - - for (final Iterator<Component> i = getComponentIterator(); i.hasNext();) { - (i.next()).attach(); - } - } - - /** - * Notifies all contained components that the container is detached from a - * window. - * - * @see com.vaadin.ui.Component#detach() - */ - @Override - public void detach() { - super.detach(); - - for (final Iterator<Component> i = getComponentIterator(); i.hasNext();) { - (i.next()).detach(); - } - } - /* Events */ private static final Method COMPONENT_ATTACHED_METHOD; @@ -355,46 +325,6 @@ public abstract class AbstractComponentContainer extends AbstractComponent true); } - public void requestRepaintAll() { - requestRepaintAll(this); - } - - /** - * Helper that implements the logic needed by requestRepaintAll. Calls - * requestRepaintAll/requestRepaint for the component container and all its - * children recursively. - * - * @param container - */ - public static void requestRepaintAll(HasComponents container) { - container.requestRepaint(); - if (container instanceof Panel) { - Panel p = (Panel) container; - // #2924 Panel is invalid, really invalid. - // Panel.getComponentIterator returns the children of content, not - // of Panel... - if (p.getContent() != null) { - p.getContent().requestRepaint(); - } - } - for (Iterator<Component> childIterator = container - .getComponentIterator(); childIterator.hasNext();) { - Component c = childIterator.next(); - if (c instanceof HasComponents) { - requestRepaintAll((HasComponents) c); - } else { - c.requestRepaint(); - } - } - } - - /** - * Returns an iterator for the child components. - * - * @return An iterator for the child components. - * @see #getComponentIterator() - * @since 7.0.0 - */ public Iterator<Component> iterator() { return getComponentIterator(); } diff --git a/src/com/vaadin/ui/AbstractField.java b/src/com/vaadin/ui/AbstractField.java index 4efed11e2c..ce62fb43ed 100644 --- a/src/com/vaadin/ui/AbstractField.java +++ b/src/com/vaadin/ui/AbstractField.java @@ -14,14 +14,14 @@ import java.util.LinkedList; import java.util.List; import java.util.logging.Logger; -import com.vaadin.Application; import com.vaadin.data.Buffered; import com.vaadin.data.Property; import com.vaadin.data.Validatable; import com.vaadin.data.Validator; import com.vaadin.data.Validator.InvalidValueException; import com.vaadin.data.util.converter.Converter; -import com.vaadin.data.util.converter.ConverterFactory; +import com.vaadin.data.util.converter.Converter.ConversionException; +import com.vaadin.data.util.converter.ConverterUtil; import com.vaadin.event.Action; import com.vaadin.event.ShortcutAction; import com.vaadin.event.ShortcutListener; @@ -591,7 +591,7 @@ public abstract class AbstractField<T> extends AbstractComponent implements // Commits the value to datasource committingValueToDataSource = true; getPropertyDataSource().setValue( - convertToDataSource(newFieldValue)); + convertToModel(newFieldValue)); // The buffer is now unmodified setModified(false); @@ -701,8 +701,8 @@ public abstract class AbstractField<T> extends AbstractComponent implements // Check if the current converter is compatible. if (newDataSource != null - && (getConverter() == null || !newDataSource.getType() - .isAssignableFrom(getConverter().getModelType()))) { + && !ConverterUtil.canConverterHandle(getConverter(), getType(), + newDataSource.getType())) { // Changing from e.g. Number -> Double should set a new converter, // changing from Double -> Number can keep the old one (Property // accepts Number) @@ -760,15 +760,9 @@ public abstract class AbstractField<T> extends AbstractComponent implements * from */ public void setConverter(Class<?> datamodelType) { - Converter<T, ?> converter = null; - - Application app = Application.getCurrentApplication(); - if (app != null) { - ConverterFactory factory = app.getConverterFactory(); - converter = (Converter<T, ?>) factory.createConverter(getType(), - datamodelType); - } - setConverter(converter); + Converter<T, ?> c = (Converter<T, ?>) ConverterUtil.getConverter( + getType(), datamodelType, getApplication()); + setConverter(c); } /** @@ -782,26 +776,9 @@ public abstract class AbstractField<T> extends AbstractComponent implements * if there is no converter and the type is not compatible with * the data source type. */ - @SuppressWarnings("unchecked") - private T convertFromDataSource(Object newValue) - throws Converter.ConversionException { - if (converter != null) { - return converter.convertToPresentation(newValue, getLocale()); - } - if (newValue == null) { - return null; - } - - if (getType().isAssignableFrom(newValue.getClass())) { - return (T) newValue; - } else { - throw new Converter.ConversionException( - "Unable to convert value of type " - + newValue.getClass().getName() - + " to " - + getType() - + ". No converter is set and the types are not compatible."); - } + private T convertFromDataSource(Object newValue) { + return ConverterUtil.convertFromModel(newValue, getType(), + getConverter(), getLocale()); } /** @@ -815,40 +792,21 @@ public abstract class AbstractField<T> extends AbstractComponent implements * if there is no converter and the type is not compatible with * the data source type. */ - private Object convertToDataSource(T fieldValue) + private Object convertToModel(T fieldValue) throws Converter.ConversionException { - if (converter != null) { - /* - * If there is a converter, always use it. It must convert or throw - * an exception. - */ - try { - return converter.convertToModel(fieldValue, getLocale()); - } catch (com.vaadin.data.util.converter.Converter.ConversionException e) { - throw new Converter.ConversionException( - getConversionError(converter.getModelType()), e); + try { + Class<?> modelType = null; + Property pd = getPropertyDataSource(); + if (pd != null) { + modelType = pd.getType(); + } else if (getConverter() != null) { + modelType = getConverter().getModelType(); } - } - - if (fieldValue == null) { - // Null should always be passed through the converter but if there - // is no converter we can safely return null - return null; - } - - // check that the value class is compatible with the data source type - // (if data source set) or field type - Class<?> type; - if (getPropertyDataSource() != null) { - type = getPropertyDataSource().getType(); - } else { - type = getType(); - } - - if (type.isAssignableFrom(fieldValue.getClass())) { - return fieldValue; - } else { - throw new Converter.ConversionException(getConversionError(type)); + return ConverterUtil.convertToModel(fieldValue, + (Class<Object>) modelType, getConverter(), getLocale()); + } catch (ConversionException e) { + throw new ConversionException( + getConversionError(converter.getModelType()), e); } } @@ -880,7 +838,7 @@ public abstract class AbstractField<T> extends AbstractComponent implements * @return The converted value that is compatible with the data source type */ public Object getConvertedValue() { - return convertToDataSource(getFieldValue()); + return convertToModel(getFieldValue()); } /** diff --git a/src/com/vaadin/ui/AbstractJavaScriptComponent.java b/src/com/vaadin/ui/AbstractJavaScriptComponent.java new file mode 100644 index 0000000000..95c45f55f9 --- /dev/null +++ b/src/com/vaadin/ui/AbstractJavaScriptComponent.java @@ -0,0 +1,161 @@ +/* +@VaadinApache2LicenseForJavaFiles@ + */ +package com.vaadin.ui; + +import com.vaadin.terminal.JavaScriptCallbackHelper; +import com.vaadin.terminal.gwt.client.ui.JavaScriptComponentState; +import com.vaadin.terminal.gwt.client.ui.JavaScriptWidget; + +/** + * Base class for Components with all client-side logic implemented using + * JavaScript. + * <p> + * When a new JavaScript component is initialized in the browser, the framework + * will look for a globally defined JavaScript function that will initialize the + * component. The name of the initialization function is formed by replacing . + * with _ in the name of the server-side class. If no such function is defined, + * each super class is used in turn until a match is found. The framework will + * thus first attempt with <code>com_example_MyComponent</code> for the + * server-side + * <code>com.example.MyComponent extends AbstractJavaScriptComponent</code> + * class. If MyComponent instead extends <code>com.example.SuperComponent</code> + * , then <code>com_example_SuperComponent</code> will also be attempted if + * <code>com_example_MyComponent</code> has not been defined. + * <p> + * JavaScript components have a very simple GWT widget ({@link JavaScriptWidget} + * ) just consisting of a <code>div</code> element to which the JavaScript code + * should initialize its own user interface. + * <p> + * The initialization function will be called with <code>this</code> pointing to + * a connector wrapper object providing integration to Vaadin with the following + * functions: + * <ul> + * <li><code>getConnectorId()</code> - returns a string with the id of the + * connector.</li> + * <li><code>getParentId([connectorId])</code> - returns a string with the id of + * the connector's parent. If <code>connectorId</code> is provided, the id of + * the parent of the corresponding connector with the passed id is returned + * instead.</li> + * <li><code>getWidgetElement([connectorId])</code> - returns the DOM Element + * that is the root of a connector's widget. <code>null</code> is returned if + * the connector can not be found or if the connector doesn't have a widget. If + * <code>connectorId</code> is not provided, the connector id of the current + * connector will be used.</li> + * <li><code>getState()</code> - returns an object corresponding to the shared + * state defined on the server. The scheme for conversion between Java and + * JavaScript types is described bellow.</li> + * <li><code>registerRpc([name, ] rpcObject)</code> - registers the + * <code>rpcObject</code> as a RPC handler. <code>rpcObject</code> should be an + * object with field containing functions for all eligible RPC functions. If + * <code>name</code> is provided, the RPC handler will only used for RPC calls + * for the RPC interface with the same fully qualified Java name. If no + * <code>name</code> is provided, the RPC handler will be used for all incoming + * RPC invocations where the RPC method name is defined as a function field in + * the handler. The scheme for conversion between Java types in the RPC + * interface definition and the JavaScript values passed as arguments to the + * handler functions is described bellow.</li> + * <li><code>getRpcProxy([name])</code> - returns an RPC proxy object. If + * <code>name</code> is provided, the proxy object will contain functions for + * all methods in the RPC interface with the same fully qualified name, provided + * a RPC handler has been registered by the server-side code. If no + * <code>name</code> is provided, the returned RPC proxy object will contain + * functions for all methods in all RPC interfaces registered for the connector + * on the server. If the same method name is present in multiple registered RPC + * interfaces, the corresponding function in the RPC proxy object will throw an + * exception when called. The scheme for conversion between Java types in the + * RPC interface and the JavaScript values that should be passed to the + * functions is described bellow.</li> + * </ul> + * The connector wrapper also supports these special functions: + * <ul> + * <li><code>onStateChange</code> - If the JavaScript code assigns a function to + * the field, that function is called whenever the contents of the shared state + * is changed.</li> + * <li>Any field name corresponding to a call to + * {@link #registerCallback(String, JavaScriptCallback)} on the server will + * automatically be present as a function that triggers the registered callback + * on the server.</li> + * <li>Any field name referred to using + * {@link #invokeCallback(String, Object...)} on the server will be called if a + * function has been assigned to the field.</li> + * </ul> + * <p> + * + * Values in the Shared State and in RPC calls are converted between Java and + * JavaScript using the following conventions: + * <ul> + * <li>Primitive Java numbers (byte, char, int, long, float, double) and their + * boxed types (Byte, Character, Integer, Long, Float, Double) are represented + * by JavaScript numbers.</li> + * <li>The primitive Java boolean and the boxed Boolean are represented by + * JavaScript booleans.</li> + * <li>Java Strings are represented by JavaScript strings.</li> + * <li>List, Set and all arrays in Java are represented by JavaScript arrays.</li> + * <li>Map<String, ?> in Java is represented by JavaScript object with fields + * corresponding to the map keys.</li> + * <li>Any other Java Map is represented by a JavaScript array containing two + * arrays, the first contains the keys and the second contains the values in the + * same order.</li> + * <li>A Java Bean is represented by a JavaScript object with fields + * corresponding to the bean's properties.</li> + * <li>A Java Connector is represented by a JavaScript string containing the + * connector's id.</li> + * <li>A pluggable serialization mechanism is provided for types not described + * here. Please refer to the documentation for specific types for serialization + * information.</li> + * </ul> + * + * @author Vaadin Ltd + * @version @VERSION@ + * @since 7.0.0 + */ +public class AbstractJavaScriptComponent extends AbstractComponent { + private JavaScriptCallbackHelper callbackHelper = new JavaScriptCallbackHelper( + this); + + @Override + protected <T> void registerRpc(T implementation, Class<T> rpcInterfaceType) { + super.registerRpc(implementation, rpcInterfaceType); + callbackHelper.registerRpc(rpcInterfaceType); + } + + /** + * Register a {@link JavaScriptCallback} that can be called from the + * JavaScript using the provided name. A JavaScript function with the + * provided name will be added to the connector wrapper object (initially + * available as <code>this</code>). Calling that JavaScript function will + * cause the call method in the registered {@link JavaScriptCallback} to be + * invoked with the same arguments. + * + * @param functionName + * the name that should be used for client-side callback + * @param javaScriptCallback + * the callback object that will be invoked when the JavaScript + * function is called + */ + protected void registerCallback(String functionName, + JavaScriptCallback javaScriptCallback) { + callbackHelper.registerCallback(functionName, javaScriptCallback); + } + + /** + * Invoke a named function that the connector JavaScript has added to the + * JavaScript connector wrapper object. The arguments should only contain + * data types that can be represented in JavaScript, including primitive + * boxing types, arrays, String, List, Set, Map, Connector and JavaBeans. + * + * @param name + * the name of the function + * @param arguments + * function arguments + */ + protected void invokeCallback(String name, Object... arguments) { + callbackHelper.invokeCallback(name, arguments); + } + + @Override + public JavaScriptComponentState getState() { + return (JavaScriptComponentState) super.getState(); + } +} diff --git a/src/com/vaadin/ui/AbstractSelect.java b/src/com/vaadin/ui/AbstractSelect.java index e586810b2d..6a927251af 100644 --- a/src/com/vaadin/ui/AbstractSelect.java +++ b/src/com/vaadin/ui/AbstractSelect.java @@ -24,7 +24,6 @@ import com.vaadin.event.Transferable; import com.vaadin.event.dd.DragAndDropEvent; import com.vaadin.event.dd.DropTarget; import com.vaadin.event.dd.TargetDetailsImpl; -import com.vaadin.event.dd.acceptcriteria.ClientCriterion; import com.vaadin.event.dd.acceptcriteria.ClientSideCriterion; import com.vaadin.event.dd.acceptcriteria.ContainsDataFlavor; import com.vaadin.event.dd.acceptcriteria.TargetDetailIs; @@ -33,8 +32,6 @@ import com.vaadin.terminal.PaintException; import com.vaadin.terminal.PaintTarget; import com.vaadin.terminal.Resource; import com.vaadin.terminal.Vaadin6Component; -import com.vaadin.terminal.gwt.client.ui.dd.VIsOverId; -import com.vaadin.terminal.gwt.client.ui.dd.VItemIdIs; import com.vaadin.terminal.gwt.client.ui.dd.VerticalDropLocation; import com.vaadin.ui.AbstractSelect.ItemCaptionMode; @@ -1800,7 +1797,6 @@ public abstract class AbstractSelect extends AbstractField<Object> implements * * @since 6.3 */ - @ClientCriterion(VIsOverId.class) public static class TargetItemIs extends AbstractItemSetCriterion { /** @@ -1867,7 +1863,6 @@ public abstract class AbstractSelect extends AbstractField<Object> implements * * @since 6.3 */ - @ClientCriterion(VItemIdIs.class) public static class AcceptItem extends AbstractItemSetCriterion { /** diff --git a/src/com/vaadin/ui/AbstractSplitPanel.java b/src/com/vaadin/ui/AbstractSplitPanel.java index 5205952621..876d39f2ae 100644 --- a/src/com/vaadin/ui/AbstractSplitPanel.java +++ b/src/com/vaadin/ui/AbstractSplitPanel.java @@ -22,7 +22,7 @@ import com.vaadin.tools.ReflectTools; * AbstractSplitPanel. * * <code>AbstractSplitPanel</code> is base class for a component container that - * can contain two components. The comopnents are split by a divider element. + * can contain two components. The components are split by a divider element. * * @author Vaadin Ltd. * @version @@ -31,7 +31,10 @@ import com.vaadin.tools.ReflectTools; */ public abstract class AbstractSplitPanel extends AbstractComponentContainer { + // TODO use Unit in AbstractSplitPanelState and remove these private Unit posUnit; + private Unit posMinUnit; + private Unit posMaxUnit; private AbstractSplitPanelRpc rpc = new AbstractSplitPanelRpc() { @@ -41,13 +44,14 @@ public abstract class AbstractSplitPanel extends AbstractComponentContainer { } public void setSplitterPosition(float position) { - getState().getSplitterState().setPosition(position); + getSplitterState().setPosition(position); } }; public AbstractSplitPanel() { registerRpc(rpc); setSplitPosition(50, Unit.PERCENTAGE, false); + setSplitPositionLimits(0, Unit.PERCENTAGE, 100, Unit.PERCENTAGE); } /** @@ -101,6 +105,7 @@ public abstract class AbstractSplitPanel extends AbstractComponentContainer { * @param c * the component to be added. */ + @Override public void addComponent(Component c) { if (getFirstComponent() == null) { @@ -188,6 +193,7 @@ public abstract class AbstractSplitPanel extends AbstractComponentContainer { * @param c * the component to be removed. */ + @Override public void removeComponent(Component c) { super.removeComponent(c); @@ -204,6 +210,7 @@ public abstract class AbstractSplitPanel extends AbstractComponentContainer { * * @see com.vaadin.ui.ComponentContainer#getComponentIterator() */ + public Iterator<Component> getComponentIterator() { return new ComponentIterator(); } @@ -214,6 +221,7 @@ public abstract class AbstractSplitPanel extends AbstractComponentContainer { * * @return the number of contained components (zero, one or two) */ + public int getComponentCount() { int count = 0; if (getFirstComponent() != null) { @@ -226,6 +234,7 @@ public abstract class AbstractSplitPanel extends AbstractComponentContainer { } /* Documented in superclass */ + public void replaceComponent(Component oldComponent, Component newComponent) { if (oldComponent == getFirstComponent()) { setFirstComponent(newComponent); @@ -297,7 +306,7 @@ public abstract class AbstractSplitPanel extends AbstractComponentContainer { if (unit != Unit.PERCENTAGE) { pos = Math.round(pos); } - SplitterState splitterState = getState().getSplitterState(); + SplitterState splitterState = getSplitterState(); splitterState.setPosition(pos); splitterState.setPositionUnit(unit.getSymbol()); splitterState.setPositionReversed(reverse); @@ -313,7 +322,7 @@ public abstract class AbstractSplitPanel extends AbstractComponentContainer { * @return position of the splitter */ public float getSplitPosition() { - return getState().getSplitterState().getPosition(); + return getSplitterState().getPosition(); } /** @@ -326,6 +335,110 @@ public abstract class AbstractSplitPanel extends AbstractComponentContainer { } /** + * Sets the minimum split position to the given position and unit. If the + * split position is reversed, maximum and minimum are also reversed. + * + * @param pos + * the minimum position of the split + * @param unit + * the unit (from {@link Sizeable}) in which the size is given. + * Allowed units are UNITS_PERCENTAGE and UNITS_PIXELS + */ + public void setMinSplitPosition(int pos, Unit unit) { + setSplitPositionLimits(pos, unit, getSplitterState().getMaxPosition(), + posMaxUnit); + } + + /** + * Returns the current minimum position of the splitter, in + * {@link #getMinSplitPositionUnit()} units. + * + * @return the minimum position of the splitter + */ + public float getMinSplitPosition() { + return getSplitterState().getMinPosition(); + } + + /** + * Returns the unit of the minimum position of the splitter. + * + * @return the unit of the minimum position of the splitter + */ + public Unit getMinSplitPositionUnit() { + return posMinUnit; + } + + /** + * Sets the maximum split position to the given position and unit. If the + * split position is reversed, maximum and minimum are also reversed. + * + * @param pos + * the maximum position of the split + * @param unit + * the unit (from {@link Sizeable}) in which the size is given. + * Allowed units are UNITS_PERCENTAGE and UNITS_PIXELS + */ + public void setMaxSplitPosition(float pos, Unit unit) { + setSplitPositionLimits(getSplitterState().getMinPosition(), posMinUnit, + pos, unit); + } + + /** + * Returns the current maximum position of the splitter, in + * {@link #getMaxSplitPositionUnit()} units. + * + * @return the maximum position of the splitter + */ + public float getMaxSplitPosition() { + return getSplitterState().getMaxPosition(); + } + + /** + * Returns the unit of the maximum position of the splitter + * + * @return the unit of the maximum position of the splitter + */ + public Unit getMaxSplitPositionUnit() { + return posMaxUnit; + } + + /** + * Sets the maximum and minimum position of the splitter. If the split + * position is reversed, maximum and minimum are also reversed. + * + * @param minPos + * the new minimum position + * @param minPosUnit + * the unit (from {@link Sizeable}) in which the minimum position + * is given. + * @param maxPos + * the new maximum position + * @param maxPosUnit + * the unit (from {@link Sizeable}) in which the maximum position + * is given. + */ + private void setSplitPositionLimits(float minPos, Unit minPosUnit, + float maxPos, Unit maxPosUnit) { + if ((minPosUnit != Unit.PERCENTAGE && minPosUnit != Unit.PIXELS) + || (maxPosUnit != Unit.PERCENTAGE && maxPosUnit != Unit.PIXELS)) { + throw new IllegalArgumentException( + "Only percentage and pixel units are allowed"); + } + + SplitterState state = getSplitterState(); + + state.setMinPosition(minPos); + state.setMinPositionUnit(minPosUnit.getSymbol()); + posMinUnit = minPosUnit; + + state.setMaxPosition(maxPos); + state.setMaxPositionUnit(maxPosUnit.getSymbol()); + posMaxUnit = maxPosUnit; + + requestRepaint(); + } + + /** * Lock the SplitPanels position, disabling the user from dragging the split * handle. * @@ -333,7 +446,7 @@ public abstract class AbstractSplitPanel extends AbstractComponentContainer { * Set <code>true</code> if locked, <code>false</code> otherwise. */ public void setLocked(boolean locked) { - getState().getSplitterState().setLocked(locked); + getSplitterState().setLocked(locked); requestRepaint(); } @@ -344,7 +457,7 @@ public abstract class AbstractSplitPanel extends AbstractComponentContainer { * @return <code>true</code> if locked, <code>false</code> otherwise. */ public boolean isLocked() { - return getState().getSplitterState().isLocked(); + return getSplitterState().isLocked(); } /** @@ -394,4 +507,7 @@ public abstract class AbstractSplitPanel extends AbstractComponentContainer { return (AbstractSplitPanelState) super.getState(); } + private SplitterState getSplitterState() { + return getState().getSplitterState(); + } } diff --git a/src/com/vaadin/ui/Button.java b/src/com/vaadin/ui/Button.java index 876fe593e2..0abc50f26f 100644 --- a/src/com/vaadin/ui/Button.java +++ b/src/com/vaadin/ui/Button.java @@ -38,6 +38,7 @@ public class Button extends AbstractComponent implements Action.ShortcutNotifier { private ButtonServerRpc rpc = new ButtonServerRpc() { + public void click(MouseEventDetails mouseEventDetails) { fireClick(mouseEventDetails); } @@ -50,6 +51,7 @@ public class Button extends AbstractComponent implements }; FocusAndBlurServerRpcImpl focusBlurRpc = new FocusAndBlurServerRpcImpl(this) { + @Override protected void fireEvent(Event event) { Button.this.fireEvent(event); @@ -355,8 +357,6 @@ public class Button extends AbstractComponent implements protected ClickShortcut clickShortcut; - private int tabIndex = 0; - /** * Makes it possible to invoke a click on this button by pressing the given * {@link KeyCode} and (optional) {@link ModifierKey}s.<br/> @@ -469,13 +469,23 @@ public class Button extends AbstractComponent implements requestRepaint(); } + /* + * (non-Javadoc) + * + * @see com.vaadin.ui.Component.Focusable#getTabIndex() + */ public int getTabIndex() { - return tabIndex; + return getState().getTabIndex(); } + /* + * (non-Javadoc) + * + * @see com.vaadin.ui.Component.Focusable#setTabIndex(int) + */ public void setTabIndex(int tabIndex) { - this.tabIndex = tabIndex; - + getState().setTabIndex(tabIndex); + requestRepaint(); } @Override diff --git a/src/com/vaadin/ui/Component.java b/src/com/vaadin/ui/Component.java index 3632c4ca5e..81e0319880 100644 --- a/src/com/vaadin/ui/Component.java +++ b/src/com/vaadin/ui/Component.java @@ -304,41 +304,10 @@ public interface Component extends ClientConnector, Sizeable, Serializable { * </p> * * @return the parent component - * @see #setParent(Component) */ public HasComponents getParent(); /** - * Sets the parent component of the component. - * - * <p> - * This method automatically calls {@link #attach()} if the parent becomes - * attached to the application, regardless of whether it was attached - * previously. Conversely, if the parent is {@code null} and the component - * is attached to the application, {@link #detach()} is called for the - * component. - * </p> - * <p> - * This method is rarely called directly. The - * {@link ComponentContainer#addComponent(Component)} method is normally - * used for adding components to a container and it will call this method - * implicitly. - * </p> - * - * <p> - * It is not possible to change the parent without first setting the parent - * to {@code null}. - * </p> - * - * @param parent - * the parent component - * @throws IllegalStateException - * if a parent is given even though the component already has a - * parent - */ - public void setParent(HasComponents parent); - - /** * Tests whether the component is in the read-only mode. The user can not * change the value of a read-only component. As only {@link Field} * components normally have a value that can be input or changed by the @@ -528,21 +497,15 @@ public interface Component extends ClientConnector, Sizeable, Serializable { public void setIcon(Resource icon); /** - * Gets the parent window of the component. + * Gets the Root the component is attached to. * * <p> - * If the component is not attached to a window through a component + * If the component is not attached to a Root through a component * containment hierarchy, <code>null</code> is returned. * </p> * - * <p> - * The window can be either an application-level window or a sub-window. If - * the component is itself a window, it returns a reference to itself, not - * to its containing window (of a sub-window). - * </p> - * - * @return the parent window of the component or <code>null</code> if it is - * not attached to a window or is itself a window + * @return the Root of the component or <code>null</code> if it is not + * attached to a Root */ public Root getRoot(); @@ -551,11 +514,16 @@ public interface Component extends ClientConnector, Sizeable, Serializable { * * <p> * The method will return {@code null} if the component is not currently - * attached to an application. This is often a problem in constructors of - * regular components and in the initializers of custom composite - * components. A standard workaround is to move the problematic - * initialization to {@link #attach()}, as described in the documentation of - * the method. + * attached to an application. + * </p> + * + * <p> + * Getting a null value is often a problem in constructors of regular + * components and in the initializers of custom composite components. A + * standard workaround is to use {@link Application#getCurrent()} + * to retrieve the application instance that the current request relates to. + * Another way is to move the problematic initialization to + * {@link #attach()}, as described in the documentation of the method. * </p> * * @return the parent application of the component or <code>null</code>. @@ -564,15 +532,7 @@ public interface Component extends ClientConnector, Sizeable, Serializable { public Application getApplication(); /** - * Notifies the component that it is connected to an application. - * - * <p> - * The caller of this method is {@link #setParent(Component)} if the parent - * is itself already attached to the application. If not, the parent will - * call the {@link #attach()} for all its children when it is attached to - * the application. This method is always called before the component is - * painted for the first time. - * </p> + * {@inheritDoc} * * <p> * Reimplementing the {@code attach()} method is useful for tasks that need @@ -624,38 +584,10 @@ public interface Component extends ClientConnector, Sizeable, Serializable { * } * } * </pre> - * - * <p> - * The attachment logic is implemented in {@link AbstractComponent}. - * </p> - * - * @see #getApplication() */ public void attach(); /** - * Notifies the component that it is detached from the application. - * - * <p> - * The {@link #getApplication()} and {@link #getRoot()} methods might return - * <code>null</code> after this method is called. - * </p> - * - * <p> - * This method must call {@link Root#componentDetached(Component)} to let - * the Root know that a new Component has been attached. - * </p> - * * - * <p> - * The caller of this method is {@link #setParent(Component)} if the parent - * is in the application. When the parent is detached from the application - * it is its response to call {@link #detach()} for all the children and to - * detach itself from the terminal. - * </p> - */ - public void detach(); - - /** * Gets the locale of the component. * * <p> @@ -719,75 +651,6 @@ public interface Component extends ClientConnector, Sizeable, Serializable { */ public String getDebugId(); - /** - * Requests that the component should be repainted as soon as possible. - */ - public void requestRepaint(); - - /** - * Repaint request event is thrown when the connector needs to be repainted. - * This is typically done when the <code>paint</code> method would return - * dissimilar UIDL from the previous call of the method. - */ - @SuppressWarnings("serial") - public static class RepaintRequestEvent extends EventObject { - - /** - * Constructs a new event. - * - * @param source - * the paintable needing repaint. - */ - public RepaintRequestEvent(ClientConnector source) { - super(source); - } - - /** - * Gets the connector needing repainting. - * - * @return Paintable for which the <code>paint</code> method will return - * dissimilar UIDL from the previous call of the method. - */ - public ClientConnector getConnector() { - return (ClientConnector) getSource(); - } - } - - /** - * Listens repaint requests. The <code>repaintRequested</code> method is - * called when the paintable needs to be repainted. This is typically done - * when the <code>paint</code> method would return dissimilar UIDL from the - * previous call of the method. - */ - public interface RepaintRequestListener extends Serializable { - - /** - * Receives repaint request events. - * - * @param event - * the repaint request event specifying the paintable source. - */ - public void repaintRequested(RepaintRequestEvent event); - } - - /** - * Adds repaint request listener. In order to assure that no repaint - * requests are missed, the new repaint listener should paint the paintable - * right after adding itself as listener. - * - * @param listener - * the listener to be added. - */ - public void addListener(RepaintRequestListener listener); - - /** - * Removes repaint request listener. - * - * @param listener - * the listener to be removed. - */ - public void removeListener(RepaintRequestListener listener); - /* Component event framework */ /** diff --git a/src/com/vaadin/ui/ConnectorTracker.java b/src/com/vaadin/ui/ConnectorTracker.java new file mode 100644 index 0000000000..75a75ad22a --- /dev/null +++ b/src/com/vaadin/ui/ConnectorTracker.java @@ -0,0 +1,229 @@ +/* +@VaadinApache2LicenseForJavaFiles@ + */ +package com.vaadin.ui; + +import java.io.Serializable; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.vaadin.terminal.AbstractClientConnector; +import com.vaadin.terminal.gwt.client.ServerConnector; +import com.vaadin.terminal.gwt.server.ClientConnector; + +/** + * A class which takes care of book keeping of {@link ClientConnector}s for one + * Root. + * <p> + * Provides {@link #getConnector(String)} which can be used to lookup a + * connector from its id. This is for framework use only and should not be + * needed in applications. + * </p> + * <p> + * Tracks which {@link ClientConnector}s are dirty so they can be updated to the + * client when the following response is sent. A connector is dirty when an + * operation has been performed on it on the server and as a result of this + * operation new information needs to be sent to its {@link ServerConnector}. + * </p> + * + * @author Vaadin Ltd + * @version @VERSION@ + * @since 7.0.0 + * + */ +public class ConnectorTracker implements Serializable { + + private final HashMap<String, ClientConnector> connectorIdToConnector = new HashMap<String, ClientConnector>(); + private Set<ClientConnector> dirtyConnectors = new HashSet<ClientConnector>(); + + private Root root; + + /** + * Gets a logger for this class + * + * @return A logger instance for logging within this class + * + */ + public static Logger getLogger() { + return Logger.getLogger(ConnectorTracker.class.getName()); + } + + public ConnectorTracker(Root root) { + this.root = root; + } + + /** + * Register the given connector. + * <p> + * The lookup method {@link #getConnector(String)} only returns registered + * connectors. + * </p> + * + * @param connector + * The connector to register. + */ + public void registerConnector(ClientConnector connector) { + String connectorId = connector.getConnectorId(); + ClientConnector previouslyRegistered = connectorIdToConnector + .get(connectorId); + if (previouslyRegistered == null) { + connectorIdToConnector.put(connectorId, connector); + getLogger().fine( + "Registered " + connector.getClass().getSimpleName() + " (" + + connectorId + ")"); + } else if (previouslyRegistered != connector) { + throw new RuntimeException("A connector with id " + connectorId + + " is already registered!"); + } else { + getLogger().warning( + "An already registered connector was registered again: " + + connector.getClass().getSimpleName() + " (" + + connectorId + ")"); + } + + } + + /** + * Unregister the given connector. + * + * <p> + * The lookup method {@link #getConnector(String)} only returns registered + * connectors. + * </p> + * + * @param connector + * The connector to unregister + */ + public void unregisterConnector(ClientConnector connector) { + String connectorId = connector.getConnectorId(); + if (!connectorIdToConnector.containsKey(connectorId)) { + getLogger().warning( + "Tried to unregister " + + connector.getClass().getSimpleName() + " (" + + connectorId + ") which is not registered"); + return; + } + if (connectorIdToConnector.get(connectorId) != connector) { + throw new RuntimeException("The given connector with id " + + connectorId + + " is not the one that was registered for that id"); + } + + getLogger().fine( + "Unregistered " + connector.getClass().getSimpleName() + " (" + + connectorId + ")"); + connectorIdToConnector.remove(connectorId); + } + + /** + * Gets a connector by its id. + * + * @param connectorId + * The connector id to look for + * @return The connector with the given id or null if no connector has the + * given id + */ + public ClientConnector getConnector(String connectorId) { + return connectorIdToConnector.get(connectorId); + } + + /** + * Cleans the connector map from all connectors that are no longer attached + * to the application. This should only be called by the framework. + */ + public void cleanConnectorMap() { + // remove detached components from paintableIdMap so they + // can be GC'ed + Iterator<String> iterator = connectorIdToConnector.keySet().iterator(); + + while (iterator.hasNext()) { + String connectorId = iterator.next(); + ClientConnector connector = connectorIdToConnector.get(connectorId); + if (connector instanceof Component) { + Component component = (Component) connector; + if (component.getRoot() != root) { + // If component is no longer part of this application, + // remove it from the map. If it is re-attached to the + // application at some point it will be re-added through + // registerConnector(connector) + iterator.remove(); + } + } + } + + } + + public void markDirty(ClientConnector connector) { + if (getLogger().isLoggable(Level.FINE)) { + if (!dirtyConnectors.contains(connector)) { + getLogger() + .fine(getDebugInfo(connector) + " " + "is now dirty"); + } + } + + dirtyConnectors.add(connector); + } + + public void markClean(ClientConnector connector) { + if (getLogger().isLoggable(Level.FINE)) { + if (dirtyConnectors.contains(connector)) { + getLogger().fine( + getDebugInfo(connector) + " " + "is no longer dirty"); + } + } + + dirtyConnectors.remove(connector); + } + + private String getDebugInfo(ClientConnector connector) { + String message = getObjectString(connector); + if (connector.getParent() != null) { + message += " (parent: " + getObjectString(connector.getParent()) + + ")"; + } + return message; + } + + private String getObjectString(Object connector) { + return connector.getClass().getName() + "@" + + Integer.toHexString(connector.hashCode()); + } + + public void markAllConnectorsDirty() { + markConnectorsDirtyRecursively(root); + getLogger().fine("All connectors are now dirty"); + } + + public void markAllConnectorsClean() { + dirtyConnectors.clear(); + getLogger().fine("All connectors are now clean"); + } + + /** + * Marks all visible connectors dirty, starting from the given connector and + * going downwards in the hierarchy. + * + * @param c + * The component to start iterating downwards from + */ + private void markConnectorsDirtyRecursively(ClientConnector c) { + if (c instanceof Component && !((Component) c).isVisible()) { + return; + } + markDirty(c); + for (ClientConnector child : AbstractClientConnector + .getAllChildrenIterable(c)) { + markConnectorsDirtyRecursively(child); + } + } + + public Collection<ClientConnector> getDirtyConnectors() { + return dirtyConnectors; + } + +} diff --git a/src/com/vaadin/ui/CssLayout.java b/src/com/vaadin/ui/CssLayout.java index 0a2656af31..e8ec6bd041 100644 --- a/src/com/vaadin/ui/CssLayout.java +++ b/src/com/vaadin/ui/CssLayout.java @@ -11,9 +11,9 @@ import com.vaadin.event.LayoutEvents.LayoutClickListener; import com.vaadin.event.LayoutEvents.LayoutClickNotifier; import com.vaadin.terminal.gwt.client.Connector; import com.vaadin.terminal.gwt.client.MouseEventDetails; +import com.vaadin.terminal.gwt.client.ui.LayoutClickEventHandler; import com.vaadin.terminal.gwt.client.ui.csslayout.CssLayoutServerRpc; import com.vaadin.terminal.gwt.client.ui.csslayout.CssLayoutState; -import com.vaadin.terminal.gwt.client.ui.LayoutClickEventHandler; /** * CssLayout is a layout component that can be used in browser environment only. @@ -185,7 +185,8 @@ public class CssLayout extends AbstractLayout implements LayoutClickNotifier { public void updateState() { super.updateState(); getState().getChildCss().clear(); - for (Component child : this) { + for (Iterator<Component> ci = getComponentIterator(); ci.hasNext();) { + Component child = ci.next(); String componentCssString = getCss(child); if (componentCssString != null) { getState().getChildCss().put(child, componentCssString); diff --git a/src/com/vaadin/ui/CustomField.java b/src/com/vaadin/ui/CustomField.java index 806ee91335..0998c11612 100644 --- a/src/com/vaadin/ui/CustomField.java +++ b/src/com/vaadin/ui/CustomField.java @@ -62,24 +62,18 @@ public abstract class CustomField<T> extends AbstractField<T> implements */ @Override public void attach() { - root = getContent(); + // First call super attach to notify all children (none if content has + // not yet been created) super.attach(); - getContent().setParent(this); - getContent().attach(); - fireComponentAttachEvent(getContent()); - } - - /** - * Notifies the content that the {@link CustomField} is detached from a - * window. - * - * @see com.vaadin.ui.Component#detach() - */ - @Override - public void detach() { - super.detach(); - getContent().detach(); + // If the content has not yet been created, we create and attach it at + // this point. + if (root == null) { + // Ensure content is created and its parent is set. + // The getContent() call creates the content and attaches the + // content + fireComponentAttachEvent(getContent()); + } } /** @@ -90,6 +84,7 @@ public abstract class CustomField<T> extends AbstractField<T> implements protected Component getContent() { if (null == root) { root = initContent(); + root.setParent(this); } return root; } @@ -154,10 +149,6 @@ public abstract class CustomField<T> extends AbstractField<T> implements return (null != getContent()) ? 1 : 0; } - public void requestRepaintAll() { - AbstractComponentContainer.requestRepaintAll(this); - } - /** * Fires the component attached event. This should be called by the * addComponent methods after the component have been added to this diff --git a/src/com/vaadin/ui/DirtyConnectorTracker.java b/src/com/vaadin/ui/DirtyConnectorTracker.java deleted file mode 100644 index 84df7e7c7c..0000000000 --- a/src/com/vaadin/ui/DirtyConnectorTracker.java +++ /dev/null @@ -1,132 +0,0 @@ -/* -@VaadinApache2LicenseForJavaFiles@ - */ -package com.vaadin.ui; - -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.vaadin.terminal.gwt.server.AbstractCommunicationManager; -import com.vaadin.terminal.gwt.server.ClientConnector; -import com.vaadin.ui.Component.RepaintRequestEvent; -import com.vaadin.ui.Component.RepaintRequestListener; - -/** - * A class that tracks dirty {@link ClientConnector}s. A {@link ClientConnector} - * is dirty when an operation has been performed on it on the server and as a - * result of this operation new information needs to be sent to its client side - * counterpart. - * - * @author Vaadin Ltd - * @version @VERSION@ - * @since 7.0.0 - * - */ -public class DirtyConnectorTracker implements RepaintRequestListener { - private Set<Component> dirtyComponents = new HashSet<Component>(); - private Root root; - - /** - * Gets a logger for this class - * - * @return A logger instance for logging within this class - * - */ - public static Logger getLogger() { - return Logger.getLogger(DirtyConnectorTracker.class.getName()); - } - - public DirtyConnectorTracker(Root root) { - this.root = root; - } - - public void repaintRequested(RepaintRequestEvent event) { - markDirty((Component) event.getConnector()); - } - - public void componentAttached(Component component) { - component.addListener(this); - markDirty(component); - } - - private void markDirty(Component component) { - if (getLogger().isLoggable(Level.FINE)) { - if (!dirtyComponents.contains(component)) { - getLogger() - .fine(getDebugInfo(component) + " " + "is now dirty"); - } - } - - dirtyComponents.add(component); - } - - private void markClean(Component component) { - if (getLogger().isLoggable(Level.FINE)) { - if (dirtyComponents.contains(component)) { - getLogger().fine( - getDebugInfo(component) + " " + "is no longer dirty"); - } - } - - dirtyComponents.remove(component); - } - - private String getDebugInfo(Component component) { - String message = getObjectString(component); - if (component.getParent() != null) { - message += " (parent: " + getObjectString(component.getParent()) - + ")"; - } - return message; - } - - private String getObjectString(Object component) { - return component.getClass().getName() + "@" - + Integer.toHexString(component.hashCode()); - } - - public void componentDetached(Component component) { - component.removeListener(this); - markClean(component); - } - - public void markAllComponentsDirty() { - markComponentsDirtyRecursively(root); - getLogger().fine("All components are now dirty"); - } - - public void markAllComponentsClean() { - dirtyComponents.clear(); - getLogger().fine("All components are now clean"); - } - - /** - * Marks all visible components dirty, starting from the given component and - * going downwards in the hierarchy. - * - * @param c - * The component to start iterating downwards from - */ - private void markComponentsDirtyRecursively(Component c) { - if (!c.isVisible()) { - return; - } - markDirty(c); - if (c instanceof HasComponents) { - HasComponents container = (HasComponents) c; - for (Component child : AbstractCommunicationManager - .getChildComponents(container)) { - markComponentsDirtyRecursively(child); - } - } - - } - - public Collection<Component> getDirtyComponents() { - return dirtyComponents; - } - -} diff --git a/src/com/vaadin/ui/Form.java b/src/com/vaadin/ui/Form.java index 5f5516b21f..796ad17448 100644 --- a/src/com/vaadin/ui/Form.java +++ b/src/com/vaadin/ui/Form.java @@ -968,34 +968,6 @@ public class Form extends AbstractField<Object> implements Item.Editor, } /** - * Notifies the component that it is connected to an application - * - * @see com.vaadin.ui.Component#attach() - */ - @Override - public void attach() { - super.attach(); - getLayout().attach(); - if (getFooter() != null) { - getFooter().attach(); - } - } - - /** - * Notifies the component that it is detached from the application. - * - * @see com.vaadin.ui.Component#detach() - */ - @Override - public void detach() { - super.detach(); - getLayout().detach(); - if (getFooter() != null) { - getFooter().detach(); - } - } - - /** * Checks the validity of the Form and all of its fields. * * @see com.vaadin.data.Validatable#validate() @@ -1411,10 +1383,6 @@ public class Form extends AbstractField<Object> implements Item.Editor, return new ComponentIterator(); } - public void requestRepaintAll() { - AbstractComponentContainer.requestRepaintAll(this); - } - public int getComponentCount() { int count = 0; if (getLayout() != null) { diff --git a/src/com/vaadin/ui/HasComponents.java b/src/com/vaadin/ui/HasComponents.java index eca89ddcd2..3ebd63bff2 100644 --- a/src/com/vaadin/ui/HasComponents.java +++ b/src/com/vaadin/ui/HasComponents.java @@ -21,7 +21,10 @@ public interface HasComponents extends Component, Iterable<Component> { * container. * * @return the component iterator. + * + * @deprecated Use {@link #iterator()} instead. */ + @Deprecated public Iterator<Component> getComponentIterator(); /** @@ -43,12 +46,4 @@ public interface HasComponents extends Component, Iterable<Component> { */ public boolean isComponentVisible(Component childComponent); - /** - * Causes a repaint of this component, and all components below it. - * - * This should only be used in special cases, e.g when the state of a - * descendant depends on the state of a ancestor. - */ - public void requestRepaintAll(); - } diff --git a/src/com/vaadin/ui/HelloWorldExtension.java b/src/com/vaadin/ui/HelloWorldExtension.java new file mode 100644 index 0000000000..e705954f2e --- /dev/null +++ b/src/com/vaadin/ui/HelloWorldExtension.java @@ -0,0 +1,38 @@ +/* +@VaadinApache2LicenseForJavaFiles@ + */ +package com.vaadin.ui; + +import com.vaadin.terminal.AbstractExtension; +import com.vaadin.terminal.gwt.client.ui.helloworldfeature.GreetAgainRpc; +import com.vaadin.terminal.gwt.client.ui.helloworldfeature.HelloWorldRpc; +import com.vaadin.terminal.gwt.client.ui.helloworldfeature.HelloWorldState; + +public class HelloWorldExtension extends AbstractExtension { + + public HelloWorldExtension() { + registerRpc(new HelloWorldRpc() { + public void onMessageSent(String message) { + Notification.show(message); + } + }); + } + + @Override + public HelloWorldState getState() { + return (HelloWorldState) super.getState(); + } + + public void setGreeting(String greeting) { + getState().setGreeting(greeting); + requestRepaint(); + } + + public String getGreeting() { + return getState().getGreeting(); + } + + public void greetAgain() { + getRpcProxy(GreetAgainRpc.class).greetAgain(); + } +} diff --git a/src/com/vaadin/ui/JavaScript.java b/src/com/vaadin/ui/JavaScript.java new file mode 100644 index 0000000000..d256717711 --- /dev/null +++ b/src/com/vaadin/ui/JavaScript.java @@ -0,0 +1,146 @@ +/* +@VaadinApache2LicenseForJavaFiles@ + */ + +package com.vaadin.ui; + +import java.util.HashMap; +import java.util.Map; + +import com.vaadin.external.json.JSONArray; +import com.vaadin.external.json.JSONException; +import com.vaadin.terminal.AbstractExtension; +import com.vaadin.terminal.Page; +import com.vaadin.terminal.gwt.client.communication.ServerRpc; +import com.vaadin.terminal.gwt.client.extensions.javascriptmanager.ExecuteJavaScriptRpc; +import com.vaadin.terminal.gwt.client.extensions.javascriptmanager.JavaScriptManagerState; + +/** + * Provides access to JavaScript functionality in the web browser. To get an + * instance of JavaScript, either use Page.getJavaScript() or + * JavaScript.getCurrent() as a shorthand for getting the JavaScript object + * corresponding to the current Page. + * + * @author Vaadin Ltd + * @version @VERSION@ + * @since 7.0.0 + */ +public class JavaScript extends AbstractExtension { + private Map<String, JavaScriptCallback> callbacks = new HashMap<String, JavaScriptCallback>(); + + // Can not be defined in client package as this JSONArray is not available + // in GWT + public interface JavaScriptCallbackRpc extends ServerRpc { + public void call(String name, JSONArray arguments); + } + + /** + * Creates a new JavaScript object. You should typically not this, but + * instead use the JavaScript object already associated with your Page + * object. + */ + public JavaScript() { + registerRpc(new JavaScriptCallbackRpc() { + public void call(String name, JSONArray arguments) { + JavaScriptCallback callback = callbacks.get(name); + // TODO handle situation if name is not registered + try { + callback.call(arguments); + } catch (JSONException e) { + throw new IllegalArgumentException(e); + } + } + }); + } + + @Override + public JavaScriptManagerState getState() { + return (JavaScriptManagerState) super.getState(); + } + + /** + * Add a new function to the global JavaScript namespace (i.e. the window + * object). The <code>call</code> method in the passed + * {@link JavaScriptCallback} object will be invoked with the same + * parameters whenever the JavaScript function is called in the browser. + * + * A callback added with the name <code>"myCallback"</code> can thus be + * invoked with the following JavaScript code: + * <code>window.myCallback(argument1, argument2)</code>. + * + * If the name parameter contains dots, simple objects are created on demand + * to allow calling the function using the same name (e.g. + * <code>window.myObject.myFunction</code>). + * + * @param name + * the name that the callback function should get in the global + * JavaScript namespace. + * @param callback + * the JavaScriptCallback that will be invoked if the JavaScript + * function is called. + */ + public void addCallback(String name, JavaScriptCallback callback) { + callbacks.put(name, callback); + if (getState().getNames().add(name)) { + requestRepaint(); + } + } + + /** + * Removes a JavaScripCallback from the browser's global JavaScript + * namespace. + * + * If the name contains dots and intermediate were created by + * {@link #addCallback(String, JavaScriptCallback)}addCallback, these + * objects will not be removed when the callback is removed. + * + * @param name + * the name of the callback to remove + */ + public void removeCallback(String name) { + callbacks.remove(name); + if (getState().getNames().remove(name)) { + requestRepaint(); + } + } + + /** + * Executes the given JavaScript code in the browser. + * + * @param script + * The JavaScript code to run. + */ + public void execute(String script) { + getRpcProxy(ExecuteJavaScriptRpc.class).executeJavaScript(script); + } + + /** + * Get the JavaScript object for the current Page, or null if there is no + * current page. + * + * @see Page#getCurrent() + * + * @return the JavaScript object corresponding to the current Page, or + * <code>null</code> if there is no current page. + */ + public static JavaScript getCurrent() { + Page page = Page.getCurrent(); + if (page == null) { + return null; + } + return page.getJavaScript(); + } + + /** + * JavaScript is not designed to be removed. + * + * @throws UnsupportedOperationException + * when invoked + */ + @Override + public void removeFromTarget() { + throw new UnsupportedOperationException( + "JavaScript is not designed to be removed."); + } + +} diff --git a/src/com/vaadin/ui/JavaScriptCallback.java b/src/com/vaadin/ui/JavaScriptCallback.java new file mode 100644 index 0000000000..49f7695e89 --- /dev/null +++ b/src/com/vaadin/ui/JavaScriptCallback.java @@ -0,0 +1,41 @@ +/* +@VaadinApache2LicenseForJavaFiles@ + */ + +package com.vaadin.ui; + +import java.io.Serializable; + +import com.vaadin.external.json.JSONArray; +import com.vaadin.external.json.JSONException; +import com.vaadin.terminal.AbstractJavaScriptExtension; + +/** + * Defines a method that is called by a client-side JavaScript function. When + * the corresponding JavaScript function is called, the {@link #call(JSONArray)} + * method is invoked. + * + * @see JavaScript#addCallback(String, JavaScriptCallback) + * @see AbstractJavaScriptComponent#registerCallback(String, JavaScriptCallback) + * @see AbstractJavaScriptExtension#registerCallback(String, JavaScriptCallback) + * + * @author Vaadin Ltd + * @version @VERSION@ + * @since 7.0.0 + */ +public interface JavaScriptCallback extends Serializable { + /** + * Invoked whenever the corresponding JavaScript function is called in the + * browser. + * <p> + * Because of the asynchronous nature of the communication between client + * and server, no return value can be sent back to the browser. + * + * @param arguments + * an array with JSON representations of the arguments with which + * the JavaScript function was called. + * @throws JSONException + * if the arguments can not be interpreted + */ + public void call(JSONArray arguments) throws JSONException; +} diff --git a/src/com/vaadin/ui/Label.java b/src/com/vaadin/ui/Label.java index 99a0f89e5c..e1c64605d7 100644 --- a/src/com/vaadin/ui/Label.java +++ b/src/com/vaadin/ui/Label.java @@ -7,7 +7,8 @@ package com.vaadin.ui; import java.lang.reflect.Method; import com.vaadin.data.Property; -import com.vaadin.data.util.ObjectProperty; +import com.vaadin.data.util.converter.Converter; +import com.vaadin.data.util.converter.ConverterUtil; import com.vaadin.terminal.gwt.client.ui.label.ContentMode; import com.vaadin.terminal.gwt.client.ui.label.LabelState; @@ -36,10 +37,9 @@ import com.vaadin.terminal.gwt.client.ui.label.LabelState; * @since 3.0 */ @SuppressWarnings("serial") -// TODO generics for interface Property -public class Label extends AbstractComponent implements Property, +public class Label extends AbstractComponent implements Property<String>, Property.Viewer, Property.ValueChangeListener, - Property.ValueChangeNotifier, Comparable<Object> { + Property.ValueChangeNotifier, Comparable<Label> { /** * @deprecated From 7.0, use {@link ContentMode#TEXT} instead @@ -77,9 +77,13 @@ public class Label extends AbstractComponent implements Property, @Deprecated public static final ContentMode CONTENT_DEFAULT = ContentMode.TEXT; - private static final String DATASOURCE_MUST_BE_SET = "Datasource must be set"; + /** + * A converter used to convert from the data model type to the field type + * and vice versa. Label type is always String. + */ + private Converter<String, Object> converter = null; - private Property dataSource; + private Property<String> dataSource = null; /** * Creates an empty Label. @@ -114,7 +118,9 @@ public class Label extends AbstractComponent implements Property, * @param contentMode */ public Label(String content, ContentMode contentMode) { - this(new ObjectProperty<String>(content, String.class), contentMode); + setValue(content); + setContentMode(contentMode); + setWidth(100, Unit.PERCENTAGE); } /** @@ -127,15 +133,7 @@ public class Label extends AbstractComponent implements Property, public Label(Property contentSource, ContentMode contentMode) { setPropertyDataSource(contentSource); setContentMode(contentMode); - setWidth(100, UNITS_PERCENTAGE); - } - - @Override - public void updateState() { - super.updateState(); - // We don't know when the text is updated so update it here before - // sending the state to the client - getState().setText(getStringValue()); + setWidth(100, Unit.PERCENTAGE); } @Override @@ -149,25 +147,35 @@ public class Label extends AbstractComponent implements Property, * * @return the Value of the label. */ - public Object getValue() { - if (dataSource == null) { - throw new IllegalStateException(DATASOURCE_MUST_BE_SET); + public String getValue() { + if (getPropertyDataSource() == null) { + // Use internal value if we are running without a data source + return getState().getText(); } - return dataSource.getValue(); + return ConverterUtil.convertFromModel(getPropertyDataSource() + .getValue(), String.class, getConverter(), getLocale()); } /** * Set the value of the label. Value of the label is the XML contents of the * label. * - * @param newValue + * @param newStringValue * the New value of the label. */ - public void setValue(Object newValue) { - if (dataSource == null) { - throw new IllegalStateException(DATASOURCE_MUST_BE_SET); + public void setValue(Object newStringValue) { + if (newStringValue != null && newStringValue.getClass() != String.class) { + throw new Converter.ConversionException("Value of type " + + newStringValue.getClass() + " cannot be assigned to " + + String.class.getName()); + } + if (getPropertyDataSource() == null) { + getState().setText((String) newStringValue); + requestRepaint(); + } else { + throw new IllegalStateException( + "Label is only a Property.Viewer and cannot update its data source"); } - dataSource.setValue(newValue); } /** @@ -179,26 +187,7 @@ public class Label extends AbstractComponent implements Property, @Override public String toString() { throw new UnsupportedOperationException( - "Use Property.getValue() instead of Label.toString()"); - } - - /** - * Returns the value of the <code>Property</code> in human readable textual - * format. - * - * This method exists to help migration from previous Vaadin versions by - * providing a simple replacement for {@link #toString()}. However, it is - * normally better to use the value of the label directly. - * - * @return String representation of the value stored in the Property - * @since 7.0 - */ - public String getStringValue() { - if (dataSource == null) { - throw new IllegalStateException(DATASOURCE_MUST_BE_SET); - } - Object value = dataSource.getValue(); - return (null != value) ? value.toString() : null; + "Use getValue() instead of Label.toString()"); } /** @@ -206,11 +195,8 @@ public class Label extends AbstractComponent implements Property, * * @see com.vaadin.data.Property#getType() */ - public Class getType() { - if (dataSource == null) { - throw new IllegalStateException(DATASOURCE_MUST_BE_SET); - } - return dataSource.getType(); + public Class<String> getType() { + return String.class; } /** @@ -238,7 +224,13 @@ public class Label extends AbstractComponent implements Property, ((Property.ValueChangeNotifier) dataSource).removeListener(this); } - // Sets the new data source + if (!ConverterUtil.canConverterHandle(getConverter(), String.class, + newDataSource.getType())) { + // Try to find a converter + Converter<String, ?> c = ConverterUtil.getConverter(String.class, + newDataSource.getType(), getApplication()); + setConverter(c); + } dataSource = newDataSource; // Listens the new data source if possible @@ -354,7 +346,6 @@ public class Label extends AbstractComponent implements Property, protected void fireValueChange() { // Set the error message fireEvent(new Label.ValueChangeEvent(this)); - requestRepaint(); } /** @@ -363,9 +354,28 @@ public class Label extends AbstractComponent implements Property, * @see com.vaadin.data.Property.ValueChangeListener#valueChange(Property.ValueChangeEvent) */ public void valueChange(Property.ValueChangeEvent event) { + // Update the internal value from the data source + getState().setText(getValue()); + requestRepaint(); + fireValueChange(); } + private String getComparableValue() { + String stringValue = getValue(); + if (stringValue == null) { + stringValue = ""; + } + + if (getContentMode() == ContentMode.XHTML + || getContentMode() == ContentMode.XML) { + return stripTags(stringValue); + } else { + return stringValue; + } + + } + /** * Compares the Label to other objects. * @@ -387,27 +397,10 @@ public class Label extends AbstractComponent implements Property, * less than, equal to, or greater than the specified object. * @see java.lang.Comparable#compareTo(java.lang.Object) */ - public int compareTo(Object other) { - - String thisValue; - String otherValue; + public int compareTo(Label other) { - if (getContentMode() == ContentMode.XML - || getContentMode() == ContentMode.XHTML) { - thisValue = stripTags(getStringValue()); - } else { - thisValue = getStringValue(); - } - - if (other instanceof Label - && (((Label) other).getContentMode() == ContentMode.XML || ((Label) other) - .getContentMode() == ContentMode.XHTML)) { - otherValue = stripTags(((Label) other).getStringValue()); - } else { - // TODO not a good idea - and might assume that Field.toString() - // returns a string representation of the value - otherValue = other.toString(); - } + String thisValue = getComparableValue(); + String otherValue = other.getComparableValue(); return thisValue.compareTo(otherValue); } @@ -443,4 +436,26 @@ public class Label extends AbstractComponent implements Property, return res.toString(); } + /** + * Gets the converter used to convert the property data source value to the + * label value. + * + * @return The converter or null if none is set. + */ + public Converter<String, Object> getConverter() { + return converter; + } + + /** + * Sets the converter used to convert the label value to the property data + * source type. The converter must have a presentation type of String. + * + * @param converter + * The new converter to use. + */ + public void setConverter(Converter<String, ?> converter) { + this.converter = (Converter<String, Object>) converter; + requestRepaint(); + } + } diff --git a/src/com/vaadin/ui/Link.java b/src/com/vaadin/ui/Link.java index ed5ffbba3a..db0dc58e6b 100644 --- a/src/com/vaadin/ui/Link.java +++ b/src/com/vaadin/ui/Link.java @@ -6,6 +6,7 @@ package com.vaadin.ui; import java.util.Map; +import com.vaadin.terminal.Page; import com.vaadin.terminal.PaintException; import com.vaadin.terminal.PaintTarget; import com.vaadin.terminal.Resource; @@ -23,13 +24,13 @@ import com.vaadin.terminal.Vaadin6Component; public class Link extends AbstractComponent implements Vaadin6Component { /* Target window border type constant: No window border */ - public static final int TARGET_BORDER_NONE = Root.BORDER_NONE; + public static final int TARGET_BORDER_NONE = Page.BORDER_NONE; /* Target window border type constant: Minimal window border */ - public static final int TARGET_BORDER_MINIMAL = Root.BORDER_MINIMAL; + public static final int TARGET_BORDER_MINIMAL = Page.BORDER_MINIMAL; /* Target window border type constant: Default window border */ - public static final int TARGET_BORDER_DEFAULT = Root.BORDER_DEFAULT; + public static final int TARGET_BORDER_DEFAULT = Page.BORDER_DEFAULT; private Resource resource = null; diff --git a/src/com/vaadin/ui/Notification.java b/src/com/vaadin/ui/Notification.java index bb1f874635..0358283cb4 100644 --- a/src/com/vaadin/ui/Notification.java +++ b/src/com/vaadin/ui/Notification.java @@ -6,6 +6,7 @@ package com.vaadin.ui; import java.io.Serializable; +import com.vaadin.terminal.Page; import com.vaadin.terminal.Resource; /** @@ -318,4 +319,53 @@ public class Notification implements Serializable { public boolean isHtmlContentAllowed() { return htmlContentAllowed; } + + /** + * Shows this notification on a Page. + * + * @param page + * The page on which the notification should be shown + */ + public void show(Page page) { + // TODO Can avoid deprecated API when Notification extends Extension + page.showNotification(this); + } + + /** + * Shows a notification message on the middle of the current page. The + * message automatically disappears ("humanized message"). + * + * Care should be taken to to avoid XSS vulnerabilities as the caption is + * rendered as html. + * + * @see #Notification(String) + * @see #show(Page) + * + * @param caption + * The message + */ + public static void show(String caption) { + new Notification(caption).show(Page.getCurrent()); + } + + /** + * Shows a notification message the current page. The position and behavior + * of the message depends on the type, which is one of the basic types + * defined in {@link Notification}, for instance + * Notification.TYPE_WARNING_MESSAGE. + * + * Care should be taken to to avoid XSS vulnerabilities as the caption is + * rendered as html. + * + * @see #Notification(String, int) + * @see #show(Page) + * + * @param caption + * The message + * @param type + * The message type + */ + public static void show(String caption, int type) { + new Notification(caption, type).show(Page.getCurrent()); + } }
\ No newline at end of file diff --git a/src/com/vaadin/ui/Panel.java b/src/com/vaadin/ui/Panel.java index b2916f78c7..c339100cda 100644 --- a/src/com/vaadin/ui/Panel.java +++ b/src/com/vaadin/ui/Panel.java @@ -4,6 +4,7 @@ package com.vaadin.ui; +import java.util.Collections; import java.util.Iterator; import java.util.Map; @@ -164,6 +165,7 @@ public class Panel extends AbstractComponentContainer implements Scrollable, .addListener((ComponentContainer.ComponentDetachListener) this); content = newContent; + requestRepaint(); } /** @@ -192,15 +194,6 @@ public class Panel extends AbstractComponentContainer implements Scrollable, } } - @Override - public void requestRepaintAll() { - // Panel has odd structure, delegate to layout - requestRepaint(); - if (getContent() != null) { - getContent().requestRepaintAll(); - } - } - /** * Adds the component into this container. * @@ -237,7 +230,7 @@ public class Panel extends AbstractComponentContainer implements Scrollable, * @see com.vaadin.ui.ComponentContainer#getComponentIterator() */ public Iterator<Component> getComponentIterator() { - return content.getComponentIterator(); + return Collections.singleton((Component) content).iterator(); } /** @@ -354,35 +347,6 @@ public class Panel extends AbstractComponentContainer implements Scrollable, } /** - * Notifies the component that it is connected to an application. - * - * @see com.vaadin.ui.Component#attach() - */ - @Override - public void attach() { - getRoot().componentAttached(this); - // can't call parent here as this is Panels hierarchy is a hack - requestRepaint(); - if (content != null) { - content.attach(); - } - } - - /** - * Notifies the component that it is detached from the application. - * - * @see com.vaadin.ui.Component#detach() - */ - @Override - public void detach() { - // can't call parent here as this is Panels hierarchy is a hack - if (content != null) { - content.detach(); - } - getRoot().componentDetached(this); - } - - /** * Removes all components from this container. * * @see com.vaadin.ui.ComponentContainer#removeAllComponents() diff --git a/src/com/vaadin/ui/Root.java b/src/com/vaadin/ui/Root.java index 93b98693c2..7ae687be79 100644 --- a/src/com/vaadin/ui/Root.java +++ b/src/com/vaadin/ui/Root.java @@ -4,8 +4,6 @@ package com.vaadin.ui; -import java.io.Serializable; -import java.lang.reflect.Method; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; @@ -13,8 +11,6 @@ import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.LinkedHashSet; -import java.util.LinkedList; -import java.util.List; import java.util.Map; import com.vaadin.Application; @@ -24,6 +20,9 @@ import com.vaadin.event.Action.Handler; import com.vaadin.event.ActionManager; import com.vaadin.event.MouseEvents.ClickEvent; import com.vaadin.event.MouseEvents.ClickListener; +import com.vaadin.terminal.Page; +import com.vaadin.terminal.Page.BrowserWindowResizeEvent; +import com.vaadin.terminal.Page.BrowserWindowResizeListener; import com.vaadin.terminal.PaintException; import com.vaadin.terminal.PaintTarget; import com.vaadin.terminal.Resource; @@ -31,11 +30,9 @@ import com.vaadin.terminal.Vaadin6Component; import com.vaadin.terminal.WrappedRequest; import com.vaadin.terminal.WrappedRequest.BrowserDetails; import com.vaadin.terminal.gwt.client.MouseEventDetails; -import com.vaadin.terminal.gwt.client.ui.notification.VNotification; import com.vaadin.terminal.gwt.client.ui.root.RootServerRpc; import com.vaadin.terminal.gwt.client.ui.root.RootState; import com.vaadin.terminal.gwt.client.ui.root.VRoot; -import com.vaadin.tools.ReflectTools; import com.vaadin.ui.Window.CloseListener; /** @@ -79,121 +76,6 @@ public abstract class Root extends AbstractComponentContainer implements Action.Container, Action.Notifier, Vaadin6Component { /** - * Listener that gets notified when the size of the browser window - * containing the root has changed. - * - * @see Root#addListener(BrowserWindowResizeListener) - */ - public interface BrowserWindowResizeListener extends Serializable { - /** - * Invoked when the browser window containing a Root has been resized. - * - * @param event - * a browser window resize event - */ - public void browserWindowResized(BrowserWindowResizeEvent event); - } - - /** - * Event that is fired when a browser window containing a root is resized. - */ - public class BrowserWindowResizeEvent extends Component.Event { - - private final int width; - private final int height; - - /** - * Creates a new event - * - * @param source - * the root for which the browser window has been resized - * @param width - * the new width of the browser window - * @param height - * the new height of the browser window - */ - public BrowserWindowResizeEvent(Root source, int width, int height) { - super(source); - this.width = width; - this.height = height; - } - - @Override - public Root getSource() { - return (Root) super.getSource(); - } - - /** - * Gets the new browser window height - * - * @return an integer with the new pixel height of the browser window - */ - public int getHeight() { - return height; - } - - /** - * Gets the new browser window width - * - * @return an integer with the new pixel width of the browser window - */ - public int getWidth() { - return width; - } - } - - private static final Method BROWSWER_RESIZE_METHOD = ReflectTools - .findMethod(BrowserWindowResizeListener.class, - "browserWindowResized", BrowserWindowResizeEvent.class); - - /** - * Listener that listens changes in URI fragment. - */ - public interface FragmentChangedListener extends Serializable { - public void fragmentChanged(FragmentChangedEvent event); - } - - /** - * Event fired when uri fragment changes. - */ - public class FragmentChangedEvent extends Component.Event { - - /** - * The new uri fragment - */ - private final String fragment; - - /** - * Creates a new instance of UriFragmentReader change event. - * - * @param source - * the Source of the event. - */ - public FragmentChangedEvent(Root source, String fragment) { - super(source); - this.fragment = fragment; - } - - /** - * Gets the root in which the fragment has changed. - * - * @return the root in which the fragment has changed - */ - public Root getRoot() { - return (Root) getComponent(); - } - - /** - * Get the new fragment - * - * @return the new fragment - */ - public String getFragment() { - return fragment; - } - } - - /** * Helper class to emulate the main window from Vaadin 6 using roots. This * class should be used in the same way as Window used as a browser level * window in Vaadin 6 with {@link com.vaadin.Application.LegacyApplication} @@ -312,55 +194,191 @@ public abstract class Root extends AbstractComponentContainer implements "Internal problem getting window URL, please report"); } } - } - private static final Method FRAGMENT_CHANGED_METHOD; - - static { - try { - FRAGMENT_CHANGED_METHOD = FragmentChangedListener.class - .getDeclaredMethod("fragmentChanged", - new Class[] { FragmentChangedEvent.class }); - } catch (final java.lang.NoSuchMethodException e) { - // This should never happen - throw new java.lang.RuntimeException( - "Internal error finding methods in FragmentChangedListener"); + /** + * Opens the given resource in this root. The contents of this Root is + * replaced by the {@code Resource}. + * + * @param resource + * the resource to show in this root + * + * @deprecated As of 7.0, use getPage().open instead + */ + @Deprecated + public void open(Resource resource) { + getPage().open(resource); + } + + /* ********************************************************************* */ + + /** + * Opens the given resource in a window with the given name. + * <p> + * The supplied {@code windowName} is used as the target name in a + * window.open call in the client. This means that special values such + * as "_blank", "_self", "_top", "_parent" have special meaning. An + * empty or <code>null</code> window name is also a special case. + * </p> + * <p> + * "", null and "_self" as {@code windowName} all causes the resource to + * be opened in the current window, replacing any old contents. For + * downloadable content you should avoid "_self" as "_self" causes the + * client to skip rendering of any other changes as it considers them + * irrelevant (the page will be replaced by the resource). This can + * speed up the opening of a resource, but it might also put the client + * side into an inconsistent state if the window content is not + * completely replaced e.g., if the resource is downloaded instead of + * displayed in the browser. + * </p> + * <p> + * "_blank" as {@code windowName} causes the resource to always be + * opened in a new window or tab (depends on the browser and browser + * settings). + * </p> + * <p> + * "_top" and "_parent" as {@code windowName} works as specified by the + * HTML standard. + * </p> + * <p> + * Any other {@code windowName} will open the resource in a window with + * that name, either by opening a new window/tab in the browser or by + * replacing the contents of an existing window with that name. + * </p> + * + * @param resource + * the resource. + * @param windowName + * the name of the window. + * @deprecated As of 7.0, use getPage().open instead + */ + @Deprecated + public void open(Resource resource, String windowName) { + getPage().open(resource, windowName); } - } - /** - * A border style used for opening resources in a window without a border. - */ - public static final int BORDER_NONE = 0; + /** + * Opens the given resource in a window with the given size, border and + * name. For more information on the meaning of {@code windowName}, see + * {@link #open(Resource, String)}. + * + * @param resource + * the resource. + * @param windowName + * the name of the window. + * @param width + * the width of the window in pixels + * @param height + * the height of the window in pixels + * @param border + * the border style of the window. See {@link #BORDER_NONE + * Window.BORDER_* constants} + * @deprecated As of 7.0, use getPage().open instead + */ + @Deprecated + public void open(Resource resource, String windowName, int width, + int height, int border) { + getPage().open(resource, windowName, width, height, border); + } - /** - * A border style used for opening resources in a window with a minimal - * border. - */ - public static final int BORDER_MINIMAL = 1; + /** + * Adds a new {@link BrowserWindowResizeListener} to this root. The + * listener will be notified whenever the browser window within which + * this root resides is resized. + * + * @param resizeListener + * the listener to add + * + * @see BrowserWindowResizeListener#browserWindowResized(BrowserWindowResizeEvent) + * @see #setResizeLazy(boolean) + * + * @deprecated As of 7.0, use the similarly named api in Page instead + */ + @Deprecated + public void addListener(BrowserWindowResizeListener resizeListener) { + getPage().addListener(resizeListener); + } - /** - * A border style that indicates that the default border style should be - * used when opening resources. - */ - public static final int BORDER_DEFAULT = 2; + /** + * Removes a {@link BrowserWindowResizeListener} from this root. The + * listener will no longer be notified when the browser window is + * resized. + * + * @param resizeListener + * the listener to remove + * @deprecated As of 7.0, use the similarly named api in Page instead + */ + @Deprecated + public void removeListener(BrowserWindowResizeListener resizeListener) { + getPage().removeListener(resizeListener); + } - /** - * The application to which this root belongs - */ - private Application application; + /** + * Gets the last known height of the browser window in which this root + * resides. + * + * @return the browser window height in pixels + * @deprecated As of 7.0, use the similarly named api in Page instead + */ + @Deprecated + public int getBrowserWindowHeight() { + return getPage().getBrowserWindowHeight(); + } - /** - * A list of notifications that are waiting to be sent to the client. - * Cleared (set to null) when the notifications have been sent. - */ - private List<Notification> notifications; + /** + * Gets the last known width of the browser window in which this root + * resides. + * + * @return the browser window width in pixels + * + * @deprecated As of 7.0, use the similarly named api in Page instead + */ + @Deprecated + public int getBrowserWindowWidth() { + return getPage().getBrowserWindowWidth(); + } + + /** + * Executes JavaScript in this window. + * + * <p> + * This method allows one to inject javascript from the server to + * client. A client implementation is not required to implement this + * functionality, but currently all web-based clients do implement this. + * </p> + * + * <p> + * Executing javascript this way often leads to cross-browser + * compatibility issues and regressions that are hard to resolve. Use of + * this method should be avoided and instead it is recommended to create + * new widgets with GWT. For more info on creating own, reusable + * client-side widgets in Java, read the corresponding chapter in Book + * of Vaadin. + * </p> + * + * @param script + * JavaScript snippet that will be executed. + * + * @deprecated as of 7.0, use JavaScript.getCurrent().execute(String) + * instead + */ + @Deprecated + public void executeJavaScript(String script) { + getPage().getJavaScript().execute(script); + } + + @Override + public void setCaption(String caption) { + // Override to provide backwards compatibility + getState().setCaption(caption); + getPage().setTitle(caption); + } + + } /** - * A list of javascript commands that are waiting to be sent to the client. - * Cleared (set to null) when the commands have been sent. + * The application to which this root belongs */ - private List<String> jsExecQueue = null; + private Application application; /** * List of windows in this root. @@ -368,12 +386,6 @@ public abstract class Root extends AbstractComponentContainer implements private final LinkedHashSet<Window> windows = new LinkedHashSet<Window>(); /** - * Resources to be opened automatically on next repaint. The list is - * automatically cleared when it has been sent to the client. - */ - private final LinkedList<OpenResource> openList = new LinkedList<OpenResource>(); - - /** * The component that should be scrolled into view after the next repaint. * Null if nothing should be scrolled into view. */ @@ -399,14 +411,12 @@ public abstract class Root extends AbstractComponentContainer implements */ private static final ThreadLocal<Root> currentRoot = new ThreadLocal<Root>(); - private int browserWindowWidth = -1; - private int browserWindowHeight = -1; - /** Identifies the click event */ private static final String CLICK_EVENT_ID = VRoot.CLICK_EVENT_ID; - private DirtyConnectorTracker dirtyConnectorTracker = new DirtyConnectorTracker( - this); + private ConnectorTracker connectorTracker = new ConnectorTracker(this); + + private Page page = new Page(this); private RootServerRpc rpc = new RootServerRpc() { public void click(MouseEventDetails mouseDetails) { @@ -502,68 +512,7 @@ public abstract class Root extends AbstractComponentContainer implements } public void paintContent(PaintTarget target) throws PaintException { - // Open requested resource - synchronized (openList) { - if (!openList.isEmpty()) { - for (final Iterator<OpenResource> i = openList.iterator(); i - .hasNext();) { - (i.next()).paintContent(target); - } - openList.clear(); - } - } - - // Paint notifications - if (notifications != null) { - target.startTag("notifications"); - for (final Iterator<Notification> it = notifications.iterator(); it - .hasNext();) { - final Notification n = it.next(); - target.startTag("notification"); - if (n.getCaption() != null) { - target.addAttribute( - VNotification.ATTRIBUTE_NOTIFICATION_CAPTION, - n.getCaption()); - } - if (n.getDescription() != null) { - target.addAttribute( - VNotification.ATTRIBUTE_NOTIFICATION_MESSAGE, - n.getDescription()); - } - if (n.getIcon() != null) { - target.addAttribute( - VNotification.ATTRIBUTE_NOTIFICATION_ICON, - n.getIcon()); - } - if (!n.isHtmlContentAllowed()) { - target.addAttribute( - VRoot.NOTIFICATION_HTML_CONTENT_NOT_ALLOWED, true); - } - target.addAttribute( - VNotification.ATTRIBUTE_NOTIFICATION_POSITION, - n.getPosition()); - target.addAttribute(VNotification.ATTRIBUTE_NOTIFICATION_DELAY, - n.getDelayMsec()); - if (n.getStyleName() != null) { - target.addAttribute( - VNotification.ATTRIBUTE_NOTIFICATION_STYLE, - n.getStyleName()); - } - target.endTag("notification"); - } - target.endTag("notifications"); - notifications = null; - } - - // Add executable javascripts if needed - if (jsExecQueue != null) { - for (String script : jsExecQueue) { - target.startTag("execJS"); - target.addAttribute("script", script); - target.endTag("execJS"); - } - jsExecQueue = null; - } + page.paintContent(target); if (scrollIntoView != null) { target.addAttribute("scrollTo", scrollIntoView); @@ -584,10 +533,6 @@ public abstract class Root extends AbstractComponentContainer implements actionManager.paintActions(null, target); } - if (fragment != null) { - target.addAttribute(VRoot.FRAGMENT_VARIABLE, fragment); - } - if (isResizeLazy()) { target.addAttribute(VRoot.RESIZE_LAZY, true); } @@ -618,23 +563,14 @@ public abstract class Root extends AbstractComponentContainer implements if (variables.containsKey(VRoot.FRAGMENT_VARIABLE)) { String fragment = (String) variables.get(VRoot.FRAGMENT_VARIABLE); - setFragment(fragment, true); + getPage().setFragment(fragment, true); } - boolean sendResizeEvent = false; - if (variables.containsKey("height")) { - browserWindowHeight = ((Integer) variables.get("height")) - .intValue(); - sendResizeEvent = true; - } - if (variables.containsKey("width")) { - browserWindowWidth = ((Integer) variables.get("width")).intValue(); - sendResizeEvent = true; - } - if (sendResizeEvent) { - fireEvent(new BrowserWindowResizeEvent(this, browserWindowWidth, - browserWindowHeight)); + if (variables.containsKey("height") || variables.containsKey("width")) { + getPage().setBrowserWindowSize((Integer) variables.get("width"), + (Integer) variables.get("height")); } + } /* @@ -643,11 +579,17 @@ public abstract class Root extends AbstractComponentContainer implements * @see com.vaadin.ui.ComponentContainer#getComponentIterator() */ public Iterator<Component> getComponentIterator() { - if (getContent() == null) { - return Collections.EMPTY_LIST.iterator(); + // TODO could directly create some kind of combined iterator instead of + // creating a new ArrayList + ArrayList<Component> components = new ArrayList<Component>(); + + if (getContent() != null) { + components.add(getContent()); } - return Collections.singleton((Component) getContent()).iterator(); + components.addAll(windows); + + return components.iterator(); } /* @@ -656,7 +598,7 @@ public abstract class Root extends AbstractComponentContainer implements * @see com.vaadin.ui.ComponentContainer#getComponentCount() */ public int getComponentCount() { - return getContent() == null ? 0 : 1; + return windows.size() + (getContent() == null ? 0 : 1); } /** @@ -809,11 +751,6 @@ public abstract class Root extends AbstractComponentContainer implements */ private Focusable pendingFocus; - /** - * The current URI fragment. - */ - private String fragment; - private boolean resizeLazy = false; /** @@ -835,175 +772,6 @@ public abstract class Root extends AbstractComponentContainer implements } /** - * Shows a notification message on the middle of the root. The message - * automatically disappears ("humanized message"). - * - * Care should be taken to to avoid XSS vulnerabilities as the caption is - * rendered as html. - * - * @see #showNotification(Notification) - * @see Notification - * - * @param caption - * The message - */ - public void showNotification(String caption) { - addNotification(new Notification(caption)); - } - - /** - * Shows a notification message the root. The position and behavior of the - * message depends on the type, which is one of the basic types defined in - * {@link Notification}, for instance Notification.TYPE_WARNING_MESSAGE. - * - * Care should be taken to to avoid XSS vulnerabilities as the caption is - * rendered as html. - * - * @see #showNotification(Notification) - * @see Notification - * - * @param caption - * The message - * @param type - * The message type - */ - public void showNotification(String caption, int type) { - addNotification(new Notification(caption, type)); - } - - /** - * Shows a notification consisting of a bigger caption and a smaller - * description on the middle of the root. The message automatically - * disappears ("humanized message"). - * - * Care should be taken to to avoid XSS vulnerabilities as the caption and - * description are rendered as html. - * - * @see #showNotification(Notification) - * @see Notification - * - * @param caption - * The caption of the message - * @param description - * The message description - * - */ - public void showNotification(String caption, String description) { - addNotification(new Notification(caption, description)); - } - - /** - * Shows a notification consisting of a bigger caption and a smaller - * description. The position and behavior of the message depends on the - * type, which is one of the basic types defined in {@link Notification}, - * for instance Notification.TYPE_WARNING_MESSAGE. - * - * Care should be taken to to avoid XSS vulnerabilities as the caption and - * description are rendered as html. - * - * @see #showNotification(Notification) - * @see Notification - * - * @param caption - * The caption of the message - * @param description - * The message description - * @param type - * The message type - */ - public void showNotification(String caption, String description, int type) { - addNotification(new Notification(caption, description, type)); - } - - /** - * Shows a notification consisting of a bigger caption and a smaller - * description. The position and behavior of the message depends on the - * type, which is one of the basic types defined in {@link Notification}, - * for instance Notification.TYPE_WARNING_MESSAGE. - * - * Care should be taken to avoid XSS vulnerabilities if html content is - * allowed. - * - * @see #showNotification(Notification) - * @see Notification - * - * @param caption - * The message caption - * @param description - * The message description - * @param type - * The type of message - * @param htmlContentAllowed - * Whether html in the caption and description should be - * displayed as html or as plain text - */ - public void showNotification(String caption, String description, int type, - boolean htmlContentAllowed) { - addNotification(new Notification(caption, description, type, - htmlContentAllowed)); - } - - /** - * Shows a notification message. - * - * @see Notification - * @see #showNotification(String) - * @see #showNotification(String, int) - * @see #showNotification(String, String) - * @see #showNotification(String, String, int) - * - * @param notification - * The notification message to show - */ - public void showNotification(Notification notification) { - addNotification(notification); - } - - /** - * Internal helper method to actually add a notification. - * - * @param notification - * the notification to add - */ - private void addNotification(Notification notification) { - if (notifications == null) { - notifications = new LinkedList<Notification>(); - } - notifications.add(notification); - requestRepaint(); - } - - /** - * Executes JavaScript in this root. - * - * <p> - * This method allows one to inject javascript from the server to client. A - * client implementation is not required to implement this functionality, - * but currently all web-based clients do implement this. - * </p> - * - * <p> - * Executing javascript this way often leads to cross-browser compatibility - * issues and regressions that are hard to resolve. Use of this method - * should be avoided and instead it is recommended to create new widgets - * with GWT. For more info on creating own, reusable client-side widgets in - * Java, read the corresponding chapter in Book of Vaadin. - * </p> - * - * @param script - * JavaScript snippet that will be executed. - */ - public void executeJavaScript(String script) { - if (jsExecQueue == null) { - jsExecQueue = new ArrayList<String>(); - } - - jsExecQueue.add(script); - - requestRepaint(); - } - - /** * Scrolls any component between the component and root to a suitable * position so the component is visible to the user. The given component * must belong to this root. @@ -1072,6 +840,8 @@ public abstract class Root extends AbstractComponentContainer implements if (content != null) { super.addComponent(content); } + + requestRepaint(); } /** @@ -1115,10 +885,7 @@ public abstract class Root extends AbstractComponentContainer implements * the initialization request */ public void doInit(WrappedRequest request) { - BrowserDetails browserDetails = request.getBrowserDetails(); - if (browserDetails != null) { - fragment = browserDetails.getUriFragment(); - } + getPage().init(request); // Call the init overridden by the application developer init(request); @@ -1156,10 +923,10 @@ public abstract class Root extends AbstractComponentContainer implements * @param root * the root to register as the current root * - * @see #getCurrentRoot() + * @see #getCurrent() * @see ThreadLocal */ - public static void setCurrentRoot(Root root) { + public static void setCurrent(Root root) { currentRoot.set(root); } @@ -1171,192 +938,12 @@ public abstract class Root extends AbstractComponentContainer implements * @return the current root instance if available, otherwise * <code>null</code> * - * @see #setCurrentRoot(Root) + * @see #setCurrent(Root) */ - public static Root getCurrentRoot() { + public static Root getCurrent() { return currentRoot.get(); } - /** - * Opens the given resource in this root. The contents of this Root is - * replaced by the {@code Resource}. - * - * @param resource - * the resource to show in this root - */ - public void open(Resource resource) { - synchronized (openList) { - if (!openList.contains(resource)) { - openList.add(new OpenResource(resource, null, -1, -1, - BORDER_DEFAULT)); - } - } - requestRepaint(); - } - - /* ********************************************************************* */ - - /** - * Opens the given resource in a window with the given name. - * <p> - * The supplied {@code windowName} is used as the target name in a - * window.open call in the client. This means that special values such as - * "_blank", "_self", "_top", "_parent" have special meaning. An empty or - * <code>null</code> window name is also a special case. - * </p> - * <p> - * "", null and "_self" as {@code windowName} all causes the resource to be - * opened in the current window, replacing any old contents. For - * downloadable content you should avoid "_self" as "_self" causes the - * client to skip rendering of any other changes as it considers them - * irrelevant (the page will be replaced by the resource). This can speed up - * the opening of a resource, but it might also put the client side into an - * inconsistent state if the window content is not completely replaced e.g., - * if the resource is downloaded instead of displayed in the browser. - * </p> - * <p> - * "_blank" as {@code windowName} causes the resource to always be opened in - * a new window or tab (depends on the browser and browser settings). - * </p> - * <p> - * "_top" and "_parent" as {@code windowName} works as specified by the HTML - * standard. - * </p> - * <p> - * Any other {@code windowName} will open the resource in a window with that - * name, either by opening a new window/tab in the browser or by replacing - * the contents of an existing window with that name. - * </p> - * - * @param resource - * the resource. - * @param windowName - * the name of the window. - */ - public void open(Resource resource, String windowName) { - synchronized (openList) { - if (!openList.contains(resource)) { - openList.add(new OpenResource(resource, windowName, -1, -1, - BORDER_DEFAULT)); - } - } - requestRepaint(); - } - - /** - * Opens the given resource in a window with the given size, border and - * name. For more information on the meaning of {@code windowName}, see - * {@link #open(Resource, String)}. - * - * @param resource - * the resource. - * @param windowName - * the name of the window. - * @param width - * the width of the window in pixels - * @param height - * the height of the window in pixels - * @param border - * the border style of the window. See {@link #BORDER_NONE - * Window.BORDER_* constants} - */ - public void open(Resource resource, String windowName, int width, - int height, int border) { - synchronized (openList) { - if (!openList.contains(resource)) { - openList.add(new OpenResource(resource, windowName, width, - height, border)); - } - } - requestRepaint(); - } - - /** - * Private class for storing properties related to opening resources. - */ - private class OpenResource implements Serializable { - - /** - * The resource to open - */ - private final Resource resource; - - /** - * The name of the target window - */ - private final String name; - - /** - * The width of the target window - */ - private final int width; - - /** - * The height of the target window - */ - private final int height; - - /** - * The border style of the target window - */ - private final int border; - - /** - * Creates a new open resource. - * - * @param resource - * The resource to open - * @param name - * The name of the target window - * @param width - * The width of the target window - * @param height - * The height of the target window - * @param border - * The border style of the target window - */ - private OpenResource(Resource resource, String name, int width, - int height, int border) { - this.resource = resource; - this.name = name; - this.width = width; - this.height = height; - this.border = border; - } - - /** - * Paints the open request. Should be painted inside the window. - * - * @param target - * the paint target - * @throws PaintException - * if the paint operation fails - */ - private void paintContent(PaintTarget target) throws PaintException { - target.startTag("open"); - target.addAttribute("src", resource); - if (name != null && name.length() > 0) { - target.addAttribute("name", name); - } - if (width >= 0) { - target.addAttribute("width", width); - } - if (height >= 0) { - target.addAttribute("height", height); - } - switch (border) { - case BORDER_MINIMAL: - target.addAttribute("border", "minimal"); - break; - case BORDER_NONE: - target.addAttribute("border", "none"); - break; - } - - target.endTag("open"); - } - } - public void setScrollTop(int scrollTop) { throw new RuntimeException("Not yet implemented"); } @@ -1444,150 +1031,176 @@ public abstract class Root extends AbstractComponentContainer implements removeListener(CLICK_EVENT_ID, ClickEvent.class, listener); } - public void addListener(FragmentChangedListener listener) { - addListener(FragmentChangedEvent.class, listener, - FRAGMENT_CHANGED_METHOD); + @Override + public boolean isConnectorEnabled() { + // TODO How can a Root be invisible? What does it mean? + return isVisible() && isEnabled(); } - public void removeListener(FragmentChangedListener listener) { - removeListener(FragmentChangedEvent.class, listener, - FRAGMENT_CHANGED_METHOD); + public ConnectorTracker getConnectorTracker() { + return connectorTracker; } - /** - * Sets URI fragment. Optionally fires a {@link FragmentChangedEvent} - * - * @param newFragment - * id of the new fragment - * @param fireEvent - * true to fire event - * @see FragmentChangedEvent - * @see FragmentChangedListener - */ - public void setFragment(String newFragment, boolean fireEvents) { - if (newFragment == null) { - throw new NullPointerException("The fragment may not be null"); - } - if (!newFragment.equals(fragment)) { - fragment = newFragment; - if (fireEvents) { - fireEvent(new FragmentChangedEvent(this, newFragment)); - } - requestRepaint(); - } + public Page getPage() { + return page; } /** - * Sets URI fragment. This method fires a {@link FragmentChangedEvent} + * Setting the caption of a Root is not supported. To set the title of the + * HTML page, use Page.setTitle * - * @param newFragment - * id of the new fragment - * @see FragmentChangedEvent - * @see FragmentChangedListener + * @deprecated as of 7.0.0, use {@link Page#setTitle(String)} */ - public void setFragment(String newFragment) { - setFragment(newFragment, true); + @Override + @Deprecated + public void setCaption(String caption) { + throw new IllegalStateException( + "You can not set the title of a Root. To set the title of the HTML page, use Page.setTitle"); } /** - * Gets currently set URI fragment. - * <p> - * To listen changes in fragment, hook a {@link FragmentChangedListener}. + * Shows a notification message on the middle of the root. The message + * automatically disappears ("humanized message"). * - * @return the current fragment in browser uri or null if not known - */ - public String getFragment() { - return fragment; - } - - /** - * Adds a new {@link BrowserWindowResizeListener} to this root. The listener - * will be notified whenever the browser window within which this root - * resides is resized. + * Care should be taken to to avoid XSS vulnerabilities as the caption is + * rendered as html. + * + * @see #showNotification(Notification) + * @see Notification * - * @param resizeListener - * the listener to add + * @param caption + * The message * - * @see BrowserWindowResizeListener#browserWindowResized(BrowserWindowResizeEvent) - * @see #setResizeLazy(boolean) + * @deprecated As of 7.0, use Notification.show instead */ - public void addListener(BrowserWindowResizeListener resizeListener) { - addListener(BrowserWindowResizeEvent.class, resizeListener, - BROWSWER_RESIZE_METHOD); + @Deprecated + public void showNotification(String caption) { + getPage().showNotification(new Notification(caption)); } /** - * Removes a {@link BrowserWindowResizeListener} from this root. The - * listener will no longer be notified when the browser window is resized. + * Shows a notification message the root. The position and behavior of the + * message depends on the type, which is one of the basic types defined in + * {@link Notification}, for instance Notification.TYPE_WARNING_MESSAGE. + * + * Care should be taken to to avoid XSS vulnerabilities as the caption is + * rendered as html. * - * @param resizeListener - * the listener to remove + * @see #showNotification(Notification) + * @see Notification + * + * @param caption + * The message + * @param type + * The message type + * + * @deprecated As of 7.0, use Notification.show instead */ - public void removeListener(BrowserWindowResizeListener resizeListener) { - removeListener(BrowserWindowResizeEvent.class, resizeListener, - BROWSWER_RESIZE_METHOD); + @Deprecated + public void showNotification(String caption, int type) { + getPage().showNotification(new Notification(caption, type)); } /** - * Gets the last known height of the browser window in which this root - * resides. + * Shows a notification consisting of a bigger caption and a smaller + * description on the middle of the root. The message automatically + * disappears ("humanized message"). + * + * Care should be taken to to avoid XSS vulnerabilities as the caption and + * description are rendered as html. * - * @return the browser window height in pixels + * @see #showNotification(Notification) + * @see Notification + * + * @param caption + * The caption of the message + * @param description + * The message description + * + * @deprecated As of 7.0, use Notification.show instead */ - public int getBrowserWindowHeight() { - return browserWindowHeight; + @Deprecated + public void showNotification(String caption, String description) { + getPage().showNotification(new Notification(caption, description)); } /** - * Gets the last known width of the browser window in which this root - * resides. + * Shows a notification consisting of a bigger caption and a smaller + * description. The position and behavior of the message depends on the + * type, which is one of the basic types defined in {@link Notification} , + * for instance Notification.TYPE_WARNING_MESSAGE. + * + * Care should be taken to to avoid XSS vulnerabilities as the caption and + * description are rendered as html. * - * @return the browser window width in pixels + * @see #showNotification(Notification) + * @see Notification + * + * @param caption + * The caption of the message + * @param description + * The message description + * @param type + * The message type + * + * @deprecated As of 7.0, use Notification.show instead */ - public int getBrowserWindowWidth() { - return browserWindowWidth; + @Deprecated + public void showNotification(String caption, String description, int type) { + getPage() + .showNotification(new Notification(caption, description, type)); } /** - * Notifies the child components and windows that the root is attached to - * the application. + * Shows a notification consisting of a bigger caption and a smaller + * description. The position and behavior of the message depends on the + * type, which is one of the basic types defined in {@link Notification} , + * for instance Notification.TYPE_WARNING_MESSAGE. + * + * Care should be taken to avoid XSS vulnerabilities if html content is + * allowed. + * + * @see #showNotification(Notification) + * @see Notification + * + * @param caption + * The message caption + * @param description + * The message description + * @param type + * The type of message + * @param htmlContentAllowed + * Whether html in the caption and description should be + * displayed as html or as plain text + * + * @deprecated As of 7.0, use Notification.show instead */ - @Override - public void attach() { - super.attach(); - for (Window w : windows) { - w.attach(); - } + @Deprecated + public void showNotification(String caption, String description, int type, + boolean htmlContentAllowed) { + getPage() + .showNotification( + new Notification(caption, description, type, + htmlContentAllowed)); } /** - * Notifies the child components and windows that the root is detached from - * the application. + * Shows a notification message. + * + * @see Notification + * @see #showNotification(String) + * @see #showNotification(String, int) + * @see #showNotification(String, String) + * @see #showNotification(String, String, int) + * + * @param notification + * The notification message to show + * + * @deprecated As of 7.0, use Notification.show instead */ - @Override - public void detach() { - super.detach(); - for (Window w : windows) { - w.detach(); - } - } - - @Override - public boolean isConnectorEnabled() { - // TODO How can a Root be invisible? What does it mean? - return isVisible() && isEnabled(); - } - - public DirtyConnectorTracker getDirtyConnectorTracker() { - return dirtyConnectorTracker; - } - - public void componentAttached(Component component) { - getDirtyConnectorTracker().componentAttached(component); - } - - public void componentDetached(Component component) { - getDirtyConnectorTracker().componentDetached(component); + @Deprecated + public void showNotification(Notification notification) { + getPage().showNotification(notification); } } diff --git a/src/com/vaadin/ui/TabSheet.java b/src/com/vaadin/ui/TabSheet.java index 23dee15359..061809de67 100644 --- a/src/com/vaadin/ui/TabSheet.java +++ b/src/com/vaadin/ui/TabSheet.java @@ -108,6 +108,7 @@ public class TabSheet extends AbstractComponentContainer implements Focusable, setWidth(100, UNITS_PERCENTAGE); setImmediate(true); setCloseHandler(new CloseHandler() { + public void onTabClose(TabSheet tabsheet, Component c) { tabsheet.removeComponent(c); } @@ -120,6 +121,7 @@ public class TabSheet extends AbstractComponentContainer implements Focusable, * * @return the unmodifiable Iterator of the tab content components */ + public Iterator<Component> getComponentIterator() { return Collections.unmodifiableList(components).iterator(); } @@ -130,6 +132,7 @@ public class TabSheet extends AbstractComponentContainer implements Focusable, * * @return the number of contained components */ + public int getComponentCount() { return components.size(); } @@ -143,6 +146,7 @@ public class TabSheet extends AbstractComponentContainer implements Focusable, * @param c * the component to be removed. */ + @Override public void removeComponent(Component c) { if (c != null && components.contains(c)) { @@ -193,6 +197,7 @@ public class TabSheet extends AbstractComponentContainer implements Focusable, * @param c * the component to be added. */ + @Override public void addComponent(Component c) { addTab(c); @@ -334,6 +339,7 @@ public class TabSheet extends AbstractComponentContainer implements Focusable, * @param source * the container components are removed from. */ + @Override public void moveComponentsFrom(ComponentContainer source) { for (final Iterator<Component> i = source.getComponentIterator(); i @@ -359,6 +365,7 @@ public class TabSheet extends AbstractComponentContainer implements Focusable, * @throws PaintException * if the paint operation failed. */ + public void paintContent(PaintTarget target) throws PaintException { if (areTabsHidden()) { @@ -683,6 +690,7 @@ public class TabSheet extends AbstractComponentContainer implements Focusable, } // inherits javadoc + public void changeVariables(Object source, Map<String, Object> variables) { if (variables.containsKey("selected")) { setSelectedTab(keyMapper.get((String) variables.get("selected"))); @@ -719,6 +727,7 @@ public class TabSheet extends AbstractComponentContainer implements Focusable, * * {@inheritDoc} */ + public void replaceComponent(Component oldComponent, Component newComponent) { if (selected == oldComponent) { @@ -729,25 +738,6 @@ public class TabSheet extends AbstractComponentContainer implements Focusable, Tab newTab = tabs.get(newComponent); Tab oldTab = tabs.get(oldComponent); - // Gets the captions - String oldCaption = null; - Resource oldIcon = null; - String newCaption = null; - Resource newIcon = null; - - if (oldTab != null) { - oldCaption = oldTab.getCaption(); - oldIcon = oldTab.getIcon(); - } - - if (newTab != null) { - newCaption = newTab.getCaption(); - newIcon = newTab.getIcon(); - } else { - newCaption = newComponent.getCaption(); - newIcon = newComponent.getIcon(); - } - // Gets the locations int oldLocation = -1; int newLocation = -1; @@ -769,35 +759,21 @@ public class TabSheet extends AbstractComponentContainer implements Focusable, addComponent(newComponent); } else if (newLocation == -1) { removeComponent(oldComponent); - keyMapper.remove(oldComponent); - newTab = addTab(newComponent); - components.remove(newComponent); - components.add(oldLocation, newComponent); - newTab.setCaption(oldCaption); - newTab.setIcon(oldIcon); + newTab = addTab(newComponent, oldLocation); + // Copy all relevant metadata to the new tab (#8793) + // TODO Should reuse the old tab instance instead? + copyTabMetadata(oldTab, newTab); } else { - if (oldLocation > newLocation) { - components.remove(oldComponent); - components.add(newLocation, oldComponent); - components.remove(newComponent); - components.add(oldLocation, newComponent); - } else { - components.remove(newComponent); - components.add(oldLocation, newComponent); - components.remove(oldComponent); - components.add(newLocation, oldComponent); - } + components.set(oldLocation, newComponent); + components.set(newLocation, oldComponent); - if (newTab != null) { - // This should always be true - newTab.setCaption(oldCaption); - newTab.setIcon(oldIcon); - } - if (oldTab != null) { - // This should always be true - oldTab.setCaption(newCaption); - oldTab.setIcon(newIcon); - } + // Tab associations are not changed, but metadata is swapped between + // the instances + // TODO Should reassociate the instances instead? + Tab tmp = new TabSheetTabImpl(null, null); + copyTabMetadata(newTab, tmp); + copyTabMetadata(oldTab, newTab); + copyTabMetadata(tmp, oldTab); requestRepaint(); } @@ -1106,6 +1082,7 @@ public class TabSheet extends AbstractComponentContainer implements Focusable, /** * Returns the tab caption. Can never be null. */ + public String getCaption() { return caption; } @@ -1300,4 +1277,23 @@ public class TabSheet extends AbstractComponentContainer implements Focusable, public boolean isComponentVisible(Component childComponent) { return childComponent == getSelectedTab(); } + + /** + * Copies properties from one Tab to another. + * + * @param from + * The tab whose data to copy. + * @param to + * The tab to which copy the data. + */ + private static void copyTabMetadata(Tab from, Tab to) { + to.setCaption(from.getCaption()); + to.setIcon(from.getIcon()); + to.setDescription(from.getDescription()); + to.setVisible(from.isVisible()); + to.setEnabled(from.isEnabled()); + to.setClosable(from.isClosable()); + to.setStyleName(from.getStyleName()); + to.setComponentError(from.getComponentError()); + } } diff --git a/src/com/vaadin/ui/Table.java b/src/com/vaadin/ui/Table.java index ba4c6529c5..a1cc4f95fe 100644 --- a/src/com/vaadin/ui/Table.java +++ b/src/com/vaadin/ui/Table.java @@ -21,13 +21,13 @@ import java.util.StringTokenizer; import java.util.logging.Level; import java.util.logging.Logger; -import com.vaadin.Application; import com.vaadin.data.Container; import com.vaadin.data.Item; import com.vaadin.data.Property; import com.vaadin.data.util.ContainerOrderedWrapper; import com.vaadin.data.util.IndexedContainer; import com.vaadin.data.util.converter.Converter; +import com.vaadin.data.util.converter.ConverterUtil; import com.vaadin.event.Action; import com.vaadin.event.Action.Handler; import com.vaadin.event.DataBoundTransferable; @@ -39,7 +39,6 @@ import com.vaadin.event.dd.DragAndDropEvent; import com.vaadin.event.dd.DragSource; import com.vaadin.event.dd.DropHandler; import com.vaadin.event.dd.DropTarget; -import com.vaadin.event.dd.acceptcriteria.ClientCriterion; import com.vaadin.event.dd.acceptcriteria.ServerSideCriterion; import com.vaadin.terminal.KeyMapper; import com.vaadin.terminal.LegacyPaint; @@ -47,7 +46,6 @@ import com.vaadin.terminal.PaintException; import com.vaadin.terminal.PaintTarget; import com.vaadin.terminal.Resource; import com.vaadin.terminal.gwt.client.MouseEventDetails; -import com.vaadin.terminal.gwt.client.ui.dd.VLazyInitItemIdentifiers; import com.vaadin.terminal.gwt.client.ui.table.VScrollTable; /** @@ -78,8 +76,7 @@ public class Table extends AbstractSelect implements Action.Container, Container.Ordered, Container.Sortable, ItemClickNotifier, DragSource, DropTarget, HasComponents { - private static final Logger logger = Logger - .getLogger(Table.class.getName()); + private transient Logger logger = null; /** * Modes that Table support as drag sourse. @@ -331,7 +328,8 @@ public class Table extends AbstractSelect implements Action.Container, private static final double CACHE_RATE_DEFAULT = 2; private static final String ROW_HEADER_COLUMN_KEY = "0"; - private static final Object ROW_HEADER_FAKE_PROPERTY_ID = new Object(); + private static final Object ROW_HEADER_FAKE_PROPERTY_ID = new UniqueSerializable() { + }; /* Private table extensions to Select */ @@ -356,6 +354,11 @@ public class Table extends AbstractSelect implements Action.Container, private LinkedList<Object> visibleColumns = new LinkedList<Object>(); /** + * Holds noncollapsible columns. + */ + private HashSet<Object> noncollapsibleColumns = new HashSet<Object>(); + + /** * Holds propertyIds of currently collapsed columns. */ private final HashSet<Object> collapsedColumns = new HashSet<Object>(); @@ -473,10 +476,9 @@ public class Table extends AbstractSelect implements Action.Container, private Object sortContainerPropertyId = null; /** - * Is table sorting disabled alltogether; even if some of the properties - * would be sortable. + * Is table sorting by the user enabled. */ - private boolean sortDisabled = false; + private boolean sortEnabled = true; /** * Number of rows explicitly requested by the client to be painted on next @@ -1248,6 +1250,9 @@ public class Table extends AbstractSelect implements Action.Container, if (!isColumnCollapsingAllowed()) { throw new IllegalStateException("Column collapsing not allowed!"); } + if (collapsed && noncollapsibleColumns.contains(propertyId)) { + throw new IllegalStateException("The column is noncollapsible!"); + } if (collapsed) { collapsedColumns.add(propertyId); @@ -1287,6 +1292,41 @@ public class Table extends AbstractSelect implements Action.Container, } /** + * Sets whether the given column is collapsible. Note that collapsible + * columns can only be actually collapsed (via UI or with + * {@link #setColumnCollapsed(Object, boolean) setColumnCollapsed()}) if + * {@link #isColumnCollapsingAllowed()} is true. By default all columns are + * collapsible. + * + * @param propertyId + * the propertyID identifying the column. + * @param collapsible + * true if the column should be collapsible, false otherwise. + */ + public void setColumnCollapsible(Object propertyId, boolean collapsible) { + if (collapsible) { + noncollapsibleColumns.remove(propertyId); + } else { + noncollapsibleColumns.add(propertyId); + collapsedColumns.remove(propertyId); + } + refreshRowCache(); + } + + /** + * Checks if the given column is collapsible. Note that even if this method + * returns <code>true</code>, the column can only be actually collapsed (via + * UI or with {@link #setColumnCollapsed(Object, boolean) + * setColumnCollapsed()}) if {@link #isColumnCollapsingAllowed()} is also + * true. + * + * @return true if the column can be collapsed; false otherwise. + */ + public boolean isColumnCollapsible(Object propertyId) { + return !noncollapsibleColumns.contains(propertyId); + } + + /** * Checks if column reordering is allowed. * * @return true if columns can be reordered; false otherwise. @@ -1569,6 +1609,13 @@ public class Table extends AbstractSelect implements Action.Container, } } else { // initial load + + // #8805 send one extra row in the beginning in case a partial + // row is shown on the UI + if (firstIndex > 0) { + firstIndex = firstIndex - 1; + rows = rows + 1; + } firstToBeRenderedInClient = firstIndex; } if (totalRows > 0) { @@ -1597,12 +1644,21 @@ public class Table extends AbstractSelect implements Action.Container, * this method has been called. See {@link #refreshRowCache()} for forcing * an update of the contents. */ + @Override public void requestRepaint() { // Overridden only for javadoc super.requestRepaint(); } + @Override + public void requestRepaintAll() { + super.requestRepaintAll(); + + // Avoid sending a partial repaint (#8714) + refreshRowCache(); + } + private void removeRowsFromCacheAndFillBottom(int firstIndex, int rows) { int totalCachedRows = pageBuffer[CELL_ITEMID].length; int totalRows = size(); @@ -1706,8 +1762,9 @@ public class Table extends AbstractSelect implements Action.Container, * @return */ private Object[][] getVisibleCellsInsertIntoCache(int firstIndex, int rows) { - logger.finest("Insert " + rows + " rows at index " + firstIndex - + " to existing page buffer requested"); + getLogger().finest( + "Insert " + rows + " rows at index " + firstIndex + + " to existing page buffer requested"); // Page buffer must not become larger than pageLength*cacheRate before // or after the current page @@ -1810,11 +1867,14 @@ public class Table extends AbstractSelect implements Action.Container, } } pageBuffer = newPageBuffer; - logger.finest("Page Buffer now contains " - + pageBuffer[CELL_ITEMID].length + " rows (" - + pageBufferFirstIndex + "-" - + (pageBufferFirstIndex + pageBuffer[CELL_ITEMID].length - 1) - + ")"); + getLogger().finest( + "Page Buffer now contains " + + pageBuffer[CELL_ITEMID].length + + " rows (" + + pageBufferFirstIndex + + "-" + + (pageBufferFirstIndex + + pageBuffer[CELL_ITEMID].length - 1) + ")"); return cells; } @@ -1831,8 +1891,9 @@ public class Table extends AbstractSelect implements Action.Container, */ private Object[][] getVisibleCellsNoCache(int firstIndex, int rows, boolean replaceListeners) { - logger.finest("Render visible cells for rows " + firstIndex + "-" - + (firstIndex + rows - 1)); + getLogger().finest( + "Render visible cells for rows " + firstIndex + "-" + + (firstIndex + rows - 1)); final Object[] colids = getVisibleColumns(); final int cols = colids.length; @@ -2014,8 +2075,9 @@ public class Table extends AbstractSelect implements Action.Container, } protected void registerComponent(Component component) { - logger.finest("Registered " + component.getClass().getSimpleName() - + ": " + component.getCaption()); + getLogger().finest( + "Registered " + component.getClass().getSimpleName() + ": " + + component.getCaption()); if (component.getParent() != this) { component.setParent(this); } @@ -2046,8 +2108,9 @@ public class Table extends AbstractSelect implements Action.Container, * @param count */ private void unregisterComponentsAndPropertiesInRows(int firstIx, int count) { - logger.finest("Unregistering components in rows " + firstIx + "-" - + (firstIx + count - 1)); + getLogger().finest( + "Unregistering components in rows " + firstIx + "-" + + (firstIx + count - 1)); Object[] colids = getVisibleColumns(); if (pageBuffer != null && pageBuffer[CELL_ITEMID].length > 0) { int bufSize = pageBuffer[CELL_ITEMID].length; @@ -2127,8 +2190,9 @@ public class Table extends AbstractSelect implements Action.Container, * a set of components that should be unregistered. */ protected void unregisterComponent(Component component) { - logger.finest("Unregistered " + component.getClass().getSimpleName() - + ": " + component.getCaption()); + getLogger().finest( + "Unregistered " + component.getClass().getSimpleName() + ": " + + component.getCaption()); component.setParent(null); /* * Also remove property data sources to unregister listeners keeping the @@ -2438,6 +2502,7 @@ public class Table extends AbstractSelect implements Action.Container, * @see com.vaadin.ui.Select#changeVariables(java.lang.Object, * java.util.Map) */ + @Override public void changeVariables(Object source, Map<String, Object> variables) { @@ -2497,7 +2562,7 @@ public class Table extends AbstractSelect implements Action.Container, .get("lastToBeRendered")).intValue(); } catch (Exception e) { // FIXME: Handle exception - logger.log(Level.FINER, + getLogger().log(Level.FINER, "Could not parse the first and/or last rows.", e); } @@ -2517,12 +2582,13 @@ public class Table extends AbstractSelect implements Action.Container, } } } - logger.finest("Client wants rows " + reqFirstRowToPaint + "-" - + (reqFirstRowToPaint + reqRowsToPaint - 1)); + getLogger().finest( + "Client wants rows " + reqFirstRowToPaint + "-" + + (reqFirstRowToPaint + reqRowsToPaint - 1)); clientNeedsContentRefresh = true; } - if (!sortDisabled) { + if (isSortEnabled()) { // Sorting boolean doSort = false; if (variables.containsKey("sortcolumn")) { @@ -2564,7 +2630,7 @@ public class Table extends AbstractSelect implements Action.Container, } } catch (final Exception e) { // FIXME: Handle exception - logger.log(Level.FINER, + getLogger().log(Level.FINER, "Could not determine column collapsing state", e); } clientNeedsContentRefresh = true; @@ -2586,7 +2652,7 @@ public class Table extends AbstractSelect implements Action.Container, } } catch (final Exception e) { // FIXME: Handle exception - logger.log(Level.FINER, + getLogger().log(Level.FINER, "Could not determine column reordering state", e); } clientNeedsContentRefresh = true; @@ -2759,6 +2825,7 @@ public class Table extends AbstractSelect implements Action.Container, * @see com.vaadin.ui.AbstractSelect#paintContent(com.vaadin. * terminal.PaintTarget) */ + @Override public void paintContent(PaintTarget target) throws PaintException { /* @@ -2876,8 +2943,9 @@ public class Table extends AbstractSelect implements Action.Container, target.startTag("prows"); if (!shouldHideAddedRows()) { - logger.finest("Paint rows for add. Index: " + firstIx + ", count: " - + count + "."); + getLogger().finest( + "Paint rows for add. Index: " + firstIx + ", count: " + + count + "."); // Partial row additions bypass the normal caching mechanism. Object[][] cells = getVisibleCellsInsertIntoCache(firstIx, count); @@ -2900,8 +2968,9 @@ public class Table extends AbstractSelect implements Action.Container, indexInRowbuffer, itemId); } } else { - logger.finest("Paint rows for remove. Index: " + firstIx - + ", count: " + count + "."); + getLogger().finest( + "Paint rows for remove. Index: " + firstIx + ", count: " + + count + "."); removeRowsFromCacheAndFillBottom(firstIx, count); target.addAttribute("hide", true); } @@ -3100,7 +3169,17 @@ public class Table extends AbstractSelect implements Action.Container, } } target.addVariable(this, "collapsedcolumns", collapsedKeys); + + final String[] noncollapsibleKeys = new String[noncollapsibleColumns + .size()]; + nextColumn = 0; + for (Object colId : noncollapsibleColumns) { + noncollapsibleKeys[nextColumn++] = columnIdMap.key(colId); + } + target.addVariable(this, "noncollapsiblecolumns", + noncollapsibleKeys); } + } private void paintActions(PaintTarget target, final Set<Action> actionSet) @@ -3584,12 +3663,8 @@ public class Table extends AbstractSelect implements Action.Container, if (hasConverter(colId)) { converter = getConverter(colId); } else { - Application app = Application.getCurrentApplication(); - if (app != null) { - converter = (Converter<String, Object>) app - .getConverterFactory().createConverter(String.class, - property.getType()); - } + ConverterUtil.getConverter(String.class, property.getType(), + getApplication()); } Object value = property.getValue(); if (converter != null) { @@ -3605,6 +3680,7 @@ public class Table extends AbstractSelect implements Action.Container, * * @see com.vaadin.event.Action.Container#addActionHandler(Action.Handler) */ + public void addActionHandler(Action.Handler actionHandler) { if (actionHandler != null) { @@ -3631,6 +3707,7 @@ public class Table extends AbstractSelect implements Action.Container, * * @see com.vaadin.event.Action.Container#removeActionHandler(Action.Handler) */ + public void removeActionHandler(Action.Handler actionHandler) { if (actionHandlers != null && actionHandlers.contains(actionHandler)) { @@ -3670,6 +3747,7 @@ public class Table extends AbstractSelect implements Action.Container, * * @see com.vaadin.data.Property.ValueChangeListener#valueChange(Property.ValueChangeEvent) */ + @Override public void valueChange(Property.ValueChangeEvent event) { if (event.getProperty() == this @@ -3700,18 +3778,12 @@ public class Table extends AbstractSelect implements Action.Container, * * @see com.vaadin.ui.Component#attach() */ + @Override public void attach() { super.attach(); refreshRenderedCells(); - - if (visibleComponents != null) { - for (final Iterator<Component> i = visibleComponents.iterator(); i - .hasNext();) { - i.next().attach(); - } - } } /** @@ -3719,16 +3791,10 @@ public class Table extends AbstractSelect implements Action.Container, * * @see com.vaadin.ui.Component#detach() */ + @Override public void detach() { super.detach(); - - if (visibleComponents != null) { - for (final Iterator<Component> i = visibleComponents.iterator(); i - .hasNext();) { - i.next().detach(); - } - } } /** @@ -3736,6 +3802,7 @@ public class Table extends AbstractSelect implements Action.Container, * * @see com.vaadin.data.Container#removeAllItems() */ + @Override public boolean removeAllItems() { currentPageFirstItemId = null; @@ -3748,6 +3815,7 @@ public class Table extends AbstractSelect implements Action.Container, * * @see com.vaadin.data.Container#removeItem(Object) */ + @Override public boolean removeItem(Object itemId) { final Object nextItemId = nextItemId(itemId); @@ -3766,6 +3834,7 @@ public class Table extends AbstractSelect implements Action.Container, * * @see com.vaadin.data.Container#removeContainerProperty(Object) */ + @Override public boolean removeContainerProperty(Object propertyId) throws UnsupportedOperationException { @@ -3792,6 +3861,7 @@ public class Table extends AbstractSelect implements Action.Container, * @see com.vaadin.data.Container#addContainerProperty(Object, Class, * Object) */ + @Override public boolean addContainerProperty(Object propertyId, Class<?> type, Object defaultValue) throws UnsupportedOperationException { @@ -3947,6 +4017,7 @@ public class Table extends AbstractSelect implements Action.Container, * * @see com.vaadin.ui.Select#getVisibleItemIds() */ + @Override public Collection<?> getVisibleItemIds() { @@ -3970,6 +4041,7 @@ public class Table extends AbstractSelect implements Action.Container, * * @see com.vaadin.data.Container.ItemSetChangeListener#containerItemSetChange(com.vaadin.data.Container.ItemSetChangeEvent) */ + @Override public void containerItemSetChange(Container.ItemSetChangeEvent event) { super.containerItemSetChange(event); @@ -3986,6 +4058,7 @@ public class Table extends AbstractSelect implements Action.Container, * * @see com.vaadin.data.Container.PropertySetChangeListener#containerPropertySetChange(com.vaadin.data.Container.PropertySetChangeEvent) */ + @Override public void containerPropertySetChange( Container.PropertySetChangeEvent event) { @@ -4029,6 +4102,7 @@ public class Table extends AbstractSelect implements Action.Container, * if set to true. * @see com.vaadin.ui.Select#setNewItemsAllowed(boolean) */ + @Override public void setNewItemsAllowed(boolean allowNewOptions) throws UnsupportedOperationException { @@ -4042,6 +4116,7 @@ public class Table extends AbstractSelect implements Action.Container, * * @see com.vaadin.data.Container.Ordered#nextItemId(java.lang.Object) */ + public Object nextItemId(Object itemId) { return ((Container.Ordered) items).nextItemId(itemId); } @@ -4052,6 +4127,7 @@ public class Table extends AbstractSelect implements Action.Container, * * @see com.vaadin.data.Container.Ordered#prevItemId(java.lang.Object) */ + public Object prevItemId(Object itemId) { return ((Container.Ordered) items).prevItemId(itemId); } @@ -4061,6 +4137,7 @@ public class Table extends AbstractSelect implements Action.Container, * * @see com.vaadin.data.Container.Ordered#firstItemId() */ + public Object firstItemId() { return ((Container.Ordered) items).firstItemId(); } @@ -4070,6 +4147,7 @@ public class Table extends AbstractSelect implements Action.Container, * * @see com.vaadin.data.Container.Ordered#lastItemId() */ + public Object lastItemId() { return ((Container.Ordered) items).lastItemId(); } @@ -4080,6 +4158,7 @@ public class Table extends AbstractSelect implements Action.Container, * * @see com.vaadin.data.Container.Ordered#isFirstId(java.lang.Object) */ + public boolean isFirstId(Object itemId) { return ((Container.Ordered) items).isFirstId(itemId); } @@ -4090,6 +4169,7 @@ public class Table extends AbstractSelect implements Action.Container, * * @see com.vaadin.data.Container.Ordered#isLastId(java.lang.Object) */ + public boolean isLastId(Object itemId) { return ((Container.Ordered) items).isLastId(itemId); } @@ -4099,6 +4179,7 @@ public class Table extends AbstractSelect implements Action.Container, * * @see com.vaadin.data.Container.Ordered#addItemAfter(java.lang.Object) */ + public Object addItemAfter(Object previousItemId) throws UnsupportedOperationException { Object itemId = ((Container.Ordered) items) @@ -4115,6 +4196,7 @@ public class Table extends AbstractSelect implements Action.Container, * @see com.vaadin.data.Container.Ordered#addItemAfter(java.lang.Object, * java.lang.Object) */ + public Item addItemAfter(Object previousItemId, Object newItemId) throws UnsupportedOperationException { Item item = ((Container.Ordered) items).addItemAfter(previousItemId, @@ -4207,6 +4289,7 @@ public class Table extends AbstractSelect implements Action.Container, * boolean[]) * */ + public void sort(Object[] propertyId, boolean[] ascending) throws UnsupportedOperationException { final Container c = getContainerDataSource(); @@ -4239,15 +4322,21 @@ public class Table extends AbstractSelect implements Action.Container, /** * Gets the container property IDs, which can be used to sort the item. + * <p> + * Note that the {@link #isSortEnabled()} state affects what this method + * returns. Disabling sorting causes this method to always return an empty + * collection. + * </p> * * @see com.vaadin.data.Container.Sortable#getSortableContainerPropertyIds() */ + public Collection<?> getSortableContainerPropertyIds() { final Container c = getContainerDataSource(); - if (c instanceof Container.Sortable && !isSortDisabled()) { + if (c instanceof Container.Sortable && isSortEnabled()) { return ((Container.Sortable) c).getSortableContainerPropertyIds(); } else { - return new LinkedList<Object>(); + return Collections.EMPTY_LIST; } } @@ -4338,37 +4427,48 @@ public class Table extends AbstractSelect implements Action.Container, * would support this. * * @return True iff sorting is disabled. + * @deprecated Use {@link #isSortEnabled()} instead */ + @Deprecated public boolean isSortDisabled() { - return sortDisabled; + return !isSortEnabled(); } /** - * Disables the sorting altogether. + * Checks if sorting is enabled. * - * To disable sorting altogether, set to true. In this case no sortable - * columns are given even in the case where datasource would support this. + * @return true if sorting by the user is allowed, false otherwise + */ + public boolean isSortEnabled() { + return sortEnabled; + } + + /** + * Disables the sorting by the user altogether. * * @param sortDisabled * True iff sorting is disabled. + * @deprecated Use {@link #setSortEnabled(boolean)} instead */ + @Deprecated public void setSortDisabled(boolean sortDisabled) { - if (this.sortDisabled != sortDisabled) { - this.sortDisabled = sortDisabled; - requestRepaint(); - } + setSortEnabled(!sortDisabled); } /** - * Table does not support lazy options loading mode. Setting this true will - * throw UnsupportedOperationException. + * Enables or disables sorting. + * <p> + * Setting this to false disallows sorting by the user. It is still possible + * to call {@link #sort()}. + * </p> * - * @see com.vaadin.ui.Select#setLazyLoading(boolean) + * @param sortEnabled + * true to allow the user to sort the table, false to disallow it */ - public void setLazyLoading(boolean useLazyLoading) { - if (useLazyLoading) { - throw new UnsupportedOperationException( - "Lazy options loading is not supported by Table."); + public void setSortEnabled(boolean sortEnabled) { + if (this.sortEnabled != sortEnabled) { + this.sortEnabled = sortEnabled; + requestRepaint(); } } @@ -4455,6 +4555,7 @@ public class Table extends AbstractSelect implements Action.Container, } // Identical to AbstractCompoenentContainer.setEnabled(); + @Override public void setEnabled(boolean enabled) { super.setEnabled(enabled); @@ -4466,10 +4567,6 @@ public class Table extends AbstractSelect implements Action.Container, } } - public void requestRepaintAll() { - AbstractComponentContainer.requestRepaintAll(this); - } - /** * Sets the drag start mode of the Table. Drag start mode controls how Table * behaves as a drag source. @@ -4583,7 +4680,6 @@ public class Table extends AbstractSelect implements Action.Container, * initialized from server and no subsequent requests requests are needed * during that drag and drop operation. */ - @ClientCriterion(VLazyInitItemIdentifiers.class) public static abstract class TableDropCriterion extends ServerSideCriterion { private Table table; @@ -4597,6 +4693,7 @@ public class Table extends AbstractSelect implements Action.Container, * com.vaadin.event.dd.acceptcriteria.ServerSideCriterion#getIdentifier * () */ + @Override protected String getIdentifier() { return TableDropCriterion.class.getCanonicalName(); @@ -4628,6 +4725,7 @@ public class Table extends AbstractSelect implements Action.Container, * com.vaadin.event.dd.acceptcriteria.AcceptCriterion#paintResponse( * com.vaadin.terminal.PaintTarget) */ + @Override public void paintResponse(PaintTarget target) throws PaintException { /* @@ -5299,4 +5397,11 @@ public class Table extends AbstractSelect implements Action.Container, public boolean isComponentVisible(Component childComponent) { return true; } + + private final Logger getLogger() { + if (logger == null) { + logger = Logger.getLogger(Table.class.getName()); + } + return logger; + } } diff --git a/src/com/vaadin/ui/Tree.java b/src/com/vaadin/ui/Tree.java index db738fee58..dacb3a2027 100644 --- a/src/com/vaadin/ui/Tree.java +++ b/src/com/vaadin/ui/Tree.java @@ -34,7 +34,6 @@ import com.vaadin.event.dd.DragSource; import com.vaadin.event.dd.DropHandler; import com.vaadin.event.dd.DropTarget; import com.vaadin.event.dd.TargetDetails; -import com.vaadin.event.dd.acceptcriteria.ClientCriterion; import com.vaadin.event.dd.acceptcriteria.ClientSideCriterion; import com.vaadin.event.dd.acceptcriteria.ServerSideCriterion; import com.vaadin.event.dd.acceptcriteria.TargetDetailIs; @@ -43,8 +42,6 @@ import com.vaadin.terminal.PaintException; import com.vaadin.terminal.PaintTarget; import com.vaadin.terminal.Resource; import com.vaadin.terminal.gwt.client.MouseEventDetails; -import com.vaadin.terminal.gwt.client.ui.dd.VLazyInitItemIdentifiers; -import com.vaadin.terminal.gwt.client.ui.dd.VTargetInSubtree; import com.vaadin.terminal.gwt.client.ui.dd.VerticalDropLocation; import com.vaadin.terminal.gwt.client.ui.tree.TreeConnector; import com.vaadin.terminal.gwt.client.ui.tree.VTree; @@ -1396,7 +1393,6 @@ public class Tree extends AbstractSelect implements Container.Hierarchical, * initialized from server and no subsequent requests requests are needed * during that drag and drop operation. */ - @ClientCriterion(VLazyInitItemIdentifiers.class) public static abstract class TreeDropCriterion extends ServerSideCriterion { private Tree tree; @@ -1513,7 +1509,6 @@ public class Tree extends AbstractSelect implements Container.Hierarchical, * <p> * The root items is also consider to be valid target. */ - @ClientCriterion(VTargetInSubtree.class) public class TargetInSubtree extends ClientSideCriterion { private Object rootId; diff --git a/src/com/vaadin/ui/TreeTable.java b/src/com/vaadin/ui/TreeTable.java index 9607add2c9..3294f6fab0 100644 --- a/src/com/vaadin/ui/TreeTable.java +++ b/src/com/vaadin/ui/TreeTable.java @@ -49,9 +49,6 @@ import com.vaadin.ui.Tree.ExpandListener; @SuppressWarnings({ "serial" }) public class TreeTable extends Table implements Hierarchical { - private static final Logger logger = Logger.getLogger(TreeTable.class - .getName()); - private interface ContainerStrategy extends Serializable { public int size(); @@ -84,6 +81,7 @@ public class TreeTable extends Table implements Hierarchical { * Consider adding getDepth to {@link Collapsible}, might help * scalability with some container implementations. */ + public int getDepth(Object itemId) { int depth = 0; Hierarchical hierarchicalContainer = getContainerDataSource(); @@ -222,9 +220,9 @@ public class TreeTable extends Table implements Hierarchical { boolean removed = openItems.remove(itemId); if (!removed) { openItems.add(itemId); - logger.finest("Item " + itemId + " is now expanded"); + getLogger().finest("Item " + itemId + " is now expanded"); } else { - logger.finest("Item " + itemId + " is now collapsed"); + getLogger().finest("Item " + itemId + " is now collapsed"); } clearPreorderCache(); } @@ -789,4 +787,8 @@ public class TreeTable extends Table implements Hierarchical { requestRepaint(); } + private static final Logger getLogger() { + return Logger.getLogger(TreeTable.class.getName()); + } + } diff --git a/src/com/vaadin/ui/UniqueSerializable.java b/src/com/vaadin/ui/UniqueSerializable.java new file mode 100644 index 0000000000..828b285538 --- /dev/null +++ b/src/com/vaadin/ui/UniqueSerializable.java @@ -0,0 +1,30 @@ +/* +@VaadinApache2LicenseForJavaFiles@ + */ +package com.vaadin.ui; + +import java.io.Serializable; + +/** + * A base class for generating an unique object that is serializable. + * <p> + * This class is abstract but has no abstract methods to force users to create + * an anonymous inner class. Otherwise each instance will not be unique. + * + * @author Vaadin Ltd + * @version @VERSION@ + * @since 7.0 + * + */ +public abstract class UniqueSerializable implements Serializable { + + @Override + public int hashCode() { + return getClass().hashCode(); + } + + @Override + public boolean equals(Object obj) { + return getClass() == obj.getClass(); + } +} diff --git a/src/com/vaadin/ui/Window.java b/src/com/vaadin/ui/Window.java index 3c17baf414..8866362587 100644 --- a/src/com/vaadin/ui/Window.java +++ b/src/com/vaadin/ui/Window.java @@ -8,7 +8,6 @@ import java.io.Serializable; import java.lang.reflect.Method; import java.util.Map; -import com.vaadin.Application; import com.vaadin.event.FieldEvents.BlurEvent; import com.vaadin.event.FieldEvents.BlurListener; import com.vaadin.event.FieldEvents.BlurNotifier; @@ -24,47 +23,26 @@ import com.vaadin.terminal.PaintException; import com.vaadin.terminal.PaintTarget; import com.vaadin.terminal.Vaadin6Component; import com.vaadin.terminal.gwt.client.MouseEventDetails; +import com.vaadin.terminal.gwt.client.ui.root.VRoot; import com.vaadin.terminal.gwt.client.ui.window.WindowServerRpc; import com.vaadin.terminal.gwt.client.ui.window.WindowState; /** - * A component that represents an application (browser native) window or a sub - * window. + * A component that represents a floating popup window that can be added to a + * {@link Root}. A window is added to a {@code Root} using + * {@link Root#addWindow(Window)}. </p> * <p> - * If the window is a application window or a sub window depends on how it is - * added to the application. Adding a {@code Window} to a {@code Window} using - * {@link Window#addWindow(Window)} makes it a sub window and adding a - * {@code Window} to the {@code Application} using - * {@link Application#addWindow(Window)} makes it an application window. + * The contents of a window is set using {@link #setContent(ComponentContainer)} + * or by using the {@link #Window(String, ComponentContainer)} constructor. The + * contents can in turn contain other components. By default, a + * {@link VerticalLayout} is used as content. * </p> * <p> - * An application window is the base of any view in a Vaadin application. All - * applications contain a main application window (set using - * {@link Application#setMainWindow(Window)} which is what is initially shown to - * the user. The contents of a window is set using - * {@link #setContent(ComponentContainer)}. The contents can in turn contain - * other components. For multi-tab applications there is one window instance per - * opened tab. + * A window can be positioned on the screen using absolute coordinates (pixels) + * or set to be centered using {@link #center()} * </p> * <p> - * A sub window is floating popup style window that can be added to an - * application window. Like the application window its content is set using - * {@link #setContent(ComponentContainer)}. A sub window can be positioned on - * the screen using absolute coordinates (pixels). The default content of the - * Window is set to be suitable for application windows. For sub windows it - * might be necessary to set the size of the content to work as expected. - * </p> - * <p> - * Window caption is displayed in the browser title bar for application level - * windows and in the window header for sub windows. - * </p> - * <p> - * Certain methods in this class are only meaningful for sub windows and other - * parts only for application windows. These are marked using <b>Sub window - * only</b> and <b>Application window only</b> respectively in the javadoc. - * </p> - * <p> - * Sub window is to be split into a separate component in Vaadin 7. + * The caption is displayed in the window header. * </p> * * @author Vaadin Ltd. @@ -83,6 +61,10 @@ public class Window extends Panel implements FocusNotifier, BlurNotifier, } }; + private int browserWindowWidth = -1; + + private int browserWindowHeight = -1; + /** * Creates a new unnamed window with a default layout. */ @@ -119,6 +101,7 @@ public class Window extends Panel implements FocusNotifier, BlurNotifier, * * @see com.vaadin.ui.Panel#addComponent(com.vaadin.ui.Component) */ + @Override public void addComponent(Component c) { if (c instanceof Window) { @@ -136,6 +119,7 @@ public class Window extends Panel implements FocusNotifier, BlurNotifier, * * @see com.vaadin.ui.Panel#paintContent(com.vaadin.terminal.PaintTarget) */ + @Override public synchronized void paintContent(PaintTarget target) throws PaintException { @@ -153,6 +137,7 @@ public class Window extends Panel implements FocusNotifier, BlurNotifier, * * @see com.vaadin.ui.Panel#changeVariables(java.lang.Object, java.util.Map) */ + @Override public void changeVariables(Object source, Map<String, Object> variables) { @@ -161,15 +146,29 @@ public class Window extends Panel implements FocusNotifier, BlurNotifier, // size is handled in super class, but resize events only in windows -> // so detect if size change occurs before super.changeVariables() if (variables.containsKey("height") - && (getHeightUnits() != UNITS_PIXELS || (Integer) variables + && (getHeightUnits() != Unit.PIXELS || (Integer) variables .get("height") != getHeight())) { sizeHasChanged = true; } if (variables.containsKey("width") - && (getWidthUnits() != UNITS_PIXELS || (Integer) variables + && (getWidthUnits() != Unit.PIXELS || (Integer) variables .get("width") != getWidth())) { sizeHasChanged = true; } + Integer browserHeightVar = (Integer) variables + .get(VRoot.BROWSER_HEIGHT_VAR); + if (browserHeightVar != null + && browserHeightVar.intValue() != browserWindowHeight) { + browserWindowHeight = browserHeightVar.intValue(); + sizeHasChanged = true; + } + Integer browserWidthVar = (Integer) variables + .get(VRoot.BROWSER_WIDTH_VAR); + if (browserWidthVar != null + && browserWidthVar.intValue() != browserWindowWidth) { + browserWindowWidth = browserWidthVar.intValue(); + sizeHasChanged = true; + } super.changeVariables(source, variables); @@ -604,8 +603,13 @@ public class Window extends Panel implements FocusNotifier, BlurNotifier, } /** - * Request to center this window on the screen. <b>Note:</b> affects - * sub-windows only. + * Sets this window to be centered relative to its parent window. Affects + * sub-windows only. If the window is resized as a result of the size of its + * content changing, it will keep itself centered as long as its position is + * not explicitly changed programmatically or by the user. + * <p> + * <b>NOTE:</b> This method has several issues as currently implemented. + * Please refer to http://dev.vaadin.com/ticket/8971 for details. */ public void center() { getState().setCentered(true); @@ -788,6 +792,7 @@ public class Window extends Panel implements FocusNotifier, BlurNotifier, * * @see com.vaadin.event.FieldEvents.FocusNotifier#addListener(com.vaadin.event.FieldEvents.FocusListener) */ + public void addListener(FocusListener listener) { addListener(FocusEvent.EVENT_ID, FocusEvent.class, listener, FocusListener.focusMethod); @@ -804,6 +809,7 @@ public class Window extends Panel implements FocusNotifier, BlurNotifier, * * @see com.vaadin.event.FieldEvents.BlurNotifier#addListener(com.vaadin.event.FieldEvents.BlurListener) */ + public void addListener(BlurListener listener) { addListener(BlurEvent.EVENT_ID, BlurEvent.class, listener, BlurListener.blurMethod); @@ -819,6 +825,7 @@ public class Window extends Panel implements FocusNotifier, BlurNotifier, * If the window is a sub-window focusing will cause the sub-window to be * brought on top of other sub-windows on gain keyboard focus. */ + @Override public void focus() { /* @@ -834,5 +841,4 @@ public class Window extends Panel implements FocusNotifier, BlurNotifier, public WindowState getState() { return (WindowState) super.getState(); } - } diff --git a/src/com/vaadin/ui/themes/ChameleonTheme.java b/src/com/vaadin/ui/themes/ChameleonTheme.java index bfb9686018..5ae8cd4e57 100644 --- a/src/com/vaadin/ui/themes/ChameleonTheme.java +++ b/src/com/vaadin/ui/themes/ChameleonTheme.java @@ -5,7 +5,7 @@ package com.vaadin.ui.themes; public class ChameleonTheme extends BaseTheme { - public static final String THEME_NAME = "Chameleon"; + public static final String THEME_NAME = "chameleon"; /*************************************************************************** * Label styles |