diff options
Diffstat (limited to 'server/src/com/vaadin')
19 files changed, 367 insertions, 160 deletions
diff --git a/server/src/com/vaadin/data/util/sqlcontainer/query/FreeformQuery.java b/server/src/com/vaadin/data/util/sqlcontainer/query/FreeformQuery.java index 79a5b6c067..6b800cb965 100644 --- a/server/src/com/vaadin/data/util/sqlcontainer/query/FreeformQuery.java +++ b/server/src/com/vaadin/data/util/sqlcontainer/query/FreeformQuery.java @@ -155,8 +155,13 @@ public class FreeformQuery extends AbstractTransactionalQuery implements pstmt = c.prepareStatement(sh.getQueryString()); sh.setParameterValuesToStatement(pstmt); rs = pstmt.executeQuery(); - rs.next(); - count = rs.getInt(1); + if (rs.next()) { + count = rs.getInt(1); + } else { + // The result can be empty when using group by and there + // are no matches (#18043) + count = 0; + } } finally { releaseConnection(c, pstmt, rs); } @@ -175,8 +180,13 @@ public class FreeformQuery extends AbstractTransactionalQuery implements try { statement = conn.createStatement(); rs = statement.executeQuery(countQuery); - rs.next(); - count = rs.getInt(1); + if (rs.next()) { + count = rs.getInt(1); + } else { + // The result can be empty when using group by and there + // are no matches (#18043) + count = 0; + } return count; } finally { releaseConnection(conn, statement, rs); diff --git a/server/src/com/vaadin/navigator/Navigator.java b/server/src/com/vaadin/navigator/Navigator.java index 65b3fec488..bd2b5711f8 100644 --- a/server/src/com/vaadin/navigator/Navigator.java +++ b/server/src/com/vaadin/navigator/Navigator.java @@ -745,9 +745,15 @@ public class Navigator implements Serializable { * the requested view name is found. * * @param provider - * provider to register + * provider to register, not <code>null</code> + * @throws IllegalArgumentException + * if the provided view provider is <code>null</code> */ public void addProvider(ViewProvider provider) { + if (provider == null) { + throw new IllegalArgumentException( + "Cannot add a null view provider"); + } providers.add(provider); } diff --git a/server/src/com/vaadin/server/ComponentSizeValidator.java b/server/src/com/vaadin/server/ComponentSizeValidator.java index 2d88ae3b53..1fbd840932 100644 --- a/server/src/com/vaadin/server/ComponentSizeValidator.java +++ b/server/src/com/vaadin/server/ComponentSizeValidator.java @@ -415,7 +415,7 @@ public class ComponentSizeValidator implements Serializable { // main window, valid situation return true; } - if (parent.getHeight() < 0) { + if (isEffectiveUndefinedHeight(component)) { // Undefined height if (parent instanceof Window) { // Sub window with undefined size has a min-height @@ -513,10 +513,7 @@ public class ComponentSizeValidator implements Serializable { // Sub window with undefined size has a min-width return true; } - - if (parent.getWidth() < 0) { - // Undefined width - + if (isEffectiveUndefinedWidth(parent)) { if (parent instanceof AbstractOrderedLayout) { AbstractOrderedLayout ol = (AbstractOrderedLayout) parent; boolean horizontal = true; @@ -591,6 +588,40 @@ public class ComponentSizeValidator implements Serializable { } + /** + * Checks if this component will be rendered with undefined width, either + * because it has been set to undefined wide or because the parent forces it + * to be (100% inside undefined) + * + */ + private static boolean isEffectiveUndefinedWidth(Component parent) { + if (parent == null) { + return false; + } else if (parent.getWidth() < 0) { + return true; + } else if (parent.getWidthUnits() == Unit.PERCENTAGE) { + return isEffectiveUndefinedWidth(parent.getParent()); + } + return false; + } + + /** + * Checks if this component will be rendered with undefined Height, either + * because it has been set to undefined wide or because the parent forces it + * to be (100% inside undefined) + * + */ + private static boolean isEffectiveUndefinedHeight(Component parent) { + if (parent == null) { + return false; + } else if (parent.getHeight() < 0) { + return true; + } else if (parent.getHeightUnits() == Unit.PERCENTAGE) { + return isEffectiveUndefinedHeight(parent.getParent()); + } + return false; + } + private static boolean hasNonRelativeWidthComponent(Form form) { Layout layout = form.getLayout(); Layout footer = form.getFooter(); diff --git a/server/src/com/vaadin/server/ConnectorResourceHandler.java b/server/src/com/vaadin/server/ConnectorResourceHandler.java index 6c486a2d65..8715134773 100644 --- a/server/src/com/vaadin/server/ConnectorResourceHandler.java +++ b/server/src/com/vaadin/server/ConnectorResourceHandler.java @@ -30,10 +30,11 @@ import com.vaadin.util.CurrentInstance; public class ConnectorResourceHandler implements RequestHandler { // APP/connector/[uiid]/[cid]/[filename.xyz] + private static final String CONNECTOR_RESOURCE_PREFIX = "/" + + ApplicationConstants.APP_PATH + "/" + + ConnectorResource.CONNECTOR_PATH + "/"; private static final Pattern CONNECTOR_RESOURCE_PATTERN = Pattern - .compile("^/?" + ApplicationConstants.APP_PATH + '/' - + ConnectorResource.CONNECTOR_PATH + '/' - + "(\\d+)/(\\d+)/(.*)"); + .compile("^" + CONNECTOR_RESOURCE_PREFIX + "(\\d+)/(\\d+)/(.*)"); private static Logger getLogger() { return Logger.getLogger(ConnectorResourceHandler.class.getName()); @@ -44,12 +45,18 @@ public class ConnectorResourceHandler implements RequestHandler { public boolean handleRequest(VaadinSession session, VaadinRequest request, VaadinResponse response) throws IOException { String requestPath = request.getPathInfo(); - if (requestPath == null) { + if (requestPath == null + || !requestPath.startsWith(CONNECTOR_RESOURCE_PREFIX)) { return false; } Matcher matcher = CONNECTOR_RESOURCE_PATTERN.matcher(requestPath); if (!matcher.matches()) { - return false; + // This is a connector resource request based on the prefix but the + // pattern did not match + warnAboutInvalidURLEncoding(requestPath); + response.sendError(HttpServletResponse.SC_NOT_FOUND, + "Connector resource not found"); + return true; } String uiId = matcher.group(1); String cid = matcher.group(2); @@ -102,6 +109,25 @@ public class ConnectorResourceHandler implements RequestHandler { return true; } + private boolean loggedDecodingWarning = false; + + private void warnAboutInvalidURLEncoding(String requestPath) { + if (requestPath.contains("\n") || requestPath.indexOf(0x85) != -1) { + // What, path info should not contain a new line or UTF-8 Next Line + // (NEL) character, but it does in + // Tomcat 7 with default configuration in some cases (URL is encoded + // by the browser as UTF-8 and decoded as ISO-8859-1 by Tomcat) + + if (!loggedDecodingWarning) { + loggedDecodingWarning = true; + getLogger() + .warning( + "Request path contains a new line character. This typically means that the server is incorrectly configured to use something else than UTF-8 for URL decoding (requestPath: " + + requestPath + ")"); + } + } + } + private static boolean error(VaadinRequest request, VaadinResponse response, String logMessage) throws IOException { getLogger().log(Level.WARNING, logMessage); diff --git a/server/src/com/vaadin/server/Constants.java b/server/src/com/vaadin/server/Constants.java index 122cce2e74..5a0d852299 100644 --- a/server/src/com/vaadin/server/Constants.java +++ b/server/src/com/vaadin/server/Constants.java @@ -67,7 +67,7 @@ public interface Constants { // Keep the version number in sync with push/build.xml and other locations // listed in that file - static final String REQUIRED_ATMOSPHERE_RUNTIME_VERSION = "2.2.4.vaadin8"; + static final String REQUIRED_ATMOSPHERE_RUNTIME_VERSION = "2.2.7.vaadin1"; static final String INVALID_ATMOSPHERE_VERSION_WARNING = "\n" + "=================================================================\n" diff --git a/server/src/com/vaadin/server/DownloadStream.java b/server/src/com/vaadin/server/DownloadStream.java index 681c438967..65ef560974 100644 --- a/server/src/com/vaadin/server/DownloadStream.java +++ b/server/src/com/vaadin/server/DownloadStream.java @@ -20,6 +20,8 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.Serializable; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; import java.util.HashMap; import java.util.Iterator; import java.util.Map; @@ -40,6 +42,8 @@ import javax.servlet.http.HttpServletResponse; @SuppressWarnings("serial") public class DownloadStream implements Serializable { + public static final String CONTENT_DISPOSITION = "Content-Disposition"; + /** * Maximum cache time. */ @@ -280,17 +284,14 @@ public class DownloadStream implements Serializable { } } - // suggest local filename from DownloadStream if - // Content-Disposition - // not explicitly set - String contentDispositionValue = getParameter("Content-Disposition"); - if (contentDispositionValue == null) { - contentDispositionValue = "filename=\"" + getFileName() - + "\""; - response.setHeader("Content-Disposition", - contentDispositionValue); + // Content-Disposition: attachment generally forces download + String contentDisposition = getParameter(CONTENT_DISPOSITION); + if (contentDisposition == null) { + contentDisposition = getContentDispositionFilename(getFileName()); } + response.setHeader(CONTENT_DISPOSITION, contentDisposition); + int bufferSize = getBufferSize(); if (bufferSize <= 0 || bufferSize > Constants.MAX_BUFFER_SIZE) { bufferSize = Constants.DEFAULT_BUFFER_SIZE; @@ -318,6 +319,25 @@ public class DownloadStream implements Serializable { } /** + * Returns the filename formatted for inclusion in a Content-Disposition + * header. Includes both a plain version of the name and a UTF-8 version + * + * @since 7.4.8 + * @param filename + * The filename to include + * @return A value for inclusion in a Content-Disposition header + */ + public static String getContentDispositionFilename(String filename) { + try { + String encodedFilename = URLEncoder.encode(filename, "UTF-8"); + return String.format("filename=\"%s\"; filename*=utf-8''%s", + encodedFilename, encodedFilename); + } catch (UnsupportedEncodingException e) { + return null; + } + } + + /** * Helper method that tries to close an output stream and ignores any * exceptions. * diff --git a/server/src/com/vaadin/server/FileDownloader.java b/server/src/com/vaadin/server/FileDownloader.java index 42c2f76e1a..b0c3bb1120 100644 --- a/server/src/com/vaadin/server/FileDownloader.java +++ b/server/src/com/vaadin/server/FileDownloader.java @@ -141,12 +141,17 @@ public class FileDownloader extends AbstractExtension { } stream = ((ConnectorResource) resource).getStream(); - if (stream.getParameter("Content-Disposition") == null) { - // Content-Disposition: attachment generally forces download - stream.setParameter("Content-Disposition", - "attachment; filename=\"" + stream.getFileName() + "\""); + String contentDisposition = stream + .getParameter(DownloadStream.CONTENT_DISPOSITION); + if (contentDisposition == null) { + contentDisposition = "attachment; " + + DownloadStream.getContentDispositionFilename(stream + .getFileName()); } + stream.setParameter(DownloadStream.CONTENT_DISPOSITION, + contentDisposition); + // Content-Type to block eager browser plug-ins from hijacking // the file if (isOverrideContentType()) { @@ -158,4 +163,5 @@ public class FileDownloader extends AbstractExtension { stream.writeResponse(request, response); return true; } + } diff --git a/server/src/com/vaadin/server/GAEVaadinServlet.java b/server/src/com/vaadin/server/GAEVaadinServlet.java index df7cd0a66e..6f5c15ebdd 100644 --- a/server/src/com/vaadin/server/GAEVaadinServlet.java +++ b/server/src/com/vaadin/server/GAEVaadinServlet.java @@ -57,7 +57,7 @@ import com.google.apphosting.api.DeadlineExceededException; * <servlet-name>HelloWorld</servlet-name> * <servlet-class>com.vaadin.server.GAEApplicationServlet</servlet-class> * <init-param> - * <param-name>application</param-name> + * <param-name>UI</param-name> * <param-value>com.vaadin.demo.HelloWorld</param-value> * </init-param> * </servlet> diff --git a/server/src/com/vaadin/server/ServerRpcManager.java b/server/src/com/vaadin/server/ServerRpcManager.java index 3a2cb3a32c..ae99622a4a 100644 --- a/server/src/com/vaadin/server/ServerRpcManager.java +++ b/server/src/com/vaadin/server/ServerRpcManager.java @@ -153,19 +153,9 @@ public class ServerRpcManager<T extends ServerRpc> implements Serializable { public void applyInvocation(ServerRpcMethodInvocation invocation) throws RpcInvocationException { Method method = invocation.getMethod(); - Class<?>[] parameterTypes = method.getParameterTypes(); - Object[] args = new Object[parameterTypes.length]; Object[] arguments = invocation.getParameters(); - for (int i = 0; i < args.length; i++) { - // no conversion needed for basic cases - // Class<?> type = parameterTypes[i]; - // if (type.isPrimitive()) { - // type = boxedTypes.get(type); - // } - args[i] = arguments[i]; - } try { - method.invoke(implementation, args); + method.invoke(implementation, arguments); } catch (Exception e) { throw new RpcInvocationException("Unable to invoke method " + invocation.getMethodName() + " in " diff --git a/server/src/com/vaadin/ui/AbstractComponent.java b/server/src/com/vaadin/ui/AbstractComponent.java index 27d97d5e03..18c3509af7 100644 --- a/server/src/com/vaadin/ui/AbstractComponent.java +++ b/server/src/com/vaadin/ui/AbstractComponent.java @@ -243,6 +243,34 @@ public abstract class AbstractComponent extends AbstractClientConnector } } + /** + * Adds or removes a style name. Multiple styles can be specified as a + * space-separated list of style names. + * + * If the {@code add} parameter is true, the style name is added to the + * component. If the {@code add} parameter is false, the style name is + * removed from the component. + * <p> + * Functionally this is equivalent to using {@link #addStyleName(String)} or + * {@link #removeStyleName(String)} + * + * @since 7.5 + * @param style + * the style name to be added or removed + * @param add + * <code>true</code> to add the given style, <code>false</code> + * to remove it + * @see #addStyleName(String) + * @see #removeStyleName(String) + */ + public void setStyleName(String style, boolean add) { + if (add) { + addStyleName(style); + } else { + removeStyleName(style); + } + } + /* * Get's the component's caption. Don't add a JavaDoc comment here, we use * the default documentation from implemented interface. diff --git a/server/src/com/vaadin/ui/AbstractLayout.java b/server/src/com/vaadin/ui/AbstractLayout.java index 9cdb0a326a..0770670680 100644 --- a/server/src/com/vaadin/ui/AbstractLayout.java +++ b/server/src/com/vaadin/ui/AbstractLayout.java @@ -16,7 +16,12 @@ package com.vaadin.ui; +import org.jsoup.nodes.Element; + import com.vaadin.shared.ui.AbstractLayoutState; +import com.vaadin.shared.ui.MarginInfo; +import com.vaadin.ui.declarative.DesignAttributeHandler; +import com.vaadin.ui.declarative.DesignContext; /** * An abstract class that defines default implementation for the {@link Layout} @@ -33,4 +38,90 @@ public abstract class AbstractLayout extends AbstractComponentContainer return (AbstractLayoutState) super.getState(); } + /** + * Reads margin attributes from a design into a MarginInfo object. This + * helper method should be called from the + * {@link #readDesign(Element, DesignContext) readDesign} method of layouts + * that implement {@link MarginHandler}. + * + * @since 7.5 + * + * @param design + * the design from which to read + * @param defMargin + * the default margin state for edges that are not set in the + * design + * @param context + * the DesignContext instance used for parsing the design + * @return the margin info + */ + protected MarginInfo readMargin(Element design, MarginInfo defMargin, + DesignContext context) { + + if (design.hasAttr("margin")) { + boolean margin = DesignAttributeHandler.readAttribute("margin", + design.attributes(), boolean.class); + return new MarginInfo(margin); + } else { + boolean left = DesignAttributeHandler.readAttribute("margin-left", + design.attributes(), defMargin.hasLeft(), boolean.class); + + boolean right = DesignAttributeHandler.readAttribute( + "margin-right", design.attributes(), defMargin.hasRight(), + boolean.class); + + boolean top = DesignAttributeHandler.readAttribute("margin-top", + design.attributes(), defMargin.hasTop(), boolean.class); + + boolean bottom = DesignAttributeHandler.readAttribute( + "margin-bottom", design.attributes(), + defMargin.hasBottom(), boolean.class); + + return new MarginInfo(top, right, bottom, left); + } + } + + /** + * Writes margin attributes from a MarginInfo object to a design. This + * helper method should be called from the + * {@link #readDesign(Element, DesignContext) writeDesign} method of layouts + * that implement {@link MarginHandler}. + * + * + * @since 7.5 + * + * @param design + * the design to write to + * @param margin + * the margin state to write + * @param defMargin + * the default margin state to compare against + * @param context + * the DesignContext instance used for parsing the design + */ + protected void writeMargin(Element design, MarginInfo margin, + MarginInfo defMargin, DesignContext context) { + if (margin.hasAll()) { + DesignAttributeHandler.writeAttribute("margin", + design.attributes(), margin.hasAll(), defMargin.hasAll(), + boolean.class); + } else { + + DesignAttributeHandler.writeAttribute("margin-left", + design.attributes(), margin.hasLeft(), defMargin.hasLeft(), + boolean.class); + + DesignAttributeHandler.writeAttribute("margin-right", + design.attributes(), margin.hasRight(), + defMargin.hasRight(), boolean.class); + + DesignAttributeHandler.writeAttribute("margin-top", + design.attributes(), margin.hasTop(), defMargin.hasTop(), + boolean.class); + + DesignAttributeHandler.writeAttribute("margin-bottom", + design.attributes(), margin.hasBottom(), + defMargin.hasBottom(), boolean.class); + } + } } diff --git a/server/src/com/vaadin/ui/AbstractOrderedLayout.java b/server/src/com/vaadin/ui/AbstractOrderedLayout.java index 0214ff4be1..afe4717212 100644 --- a/server/src/com/vaadin/ui/AbstractOrderedLayout.java +++ b/server/src/com/vaadin/ui/AbstractOrderedLayout.java @@ -478,30 +478,7 @@ public abstract class AbstractOrderedLayout extends AbstractLayout implements // process default attributes super.readDesign(design, designContext); - // handle margins - if (design.hasAttr("margin")) { - setMargin(DesignAttributeHandler.readAttribute("margin", - design.attributes(), Boolean.class)); - } else { - boolean marginLeft = DesignAttributeHandler.readAttribute( - "margin-left", design.attributes(), getMargin().hasLeft(), - Boolean.class); - - boolean marginRight = DesignAttributeHandler.readAttribute( - "margin-right", design.attributes(), - getMargin().hasRight(), Boolean.class); - - boolean marginTop = DesignAttributeHandler.readAttribute( - "margin-top", design.attributes(), getMargin().hasTop(), - Boolean.class); - - boolean marginBottom = DesignAttributeHandler.readAttribute( - "margin-bottom", design.attributes(), getMargin() - .hasBottom(), Boolean.class); - - setMargin(new MarginInfo(marginTop, marginBottom, marginLeft, - marginRight)); - } + setMargin(readMargin(design, getMargin(), designContext)); // handle children for (Element childComponent : design.children()) { @@ -557,31 +534,7 @@ public abstract class AbstractOrderedLayout extends AbstractLayout implements AbstractOrderedLayout def = (AbstractOrderedLayout) designContext .getDefaultInstance(this); - // handle margin - MarginInfo marginInfo = getMargin(); - - if (marginInfo.hasAll()) { - DesignAttributeHandler.writeAttribute("margin", - design.attributes(), marginInfo.hasAll(), def.getMargin() - .hasAll(), Boolean.class); - } else { - - DesignAttributeHandler.writeAttribute("margin-left", design - .attributes(), marginInfo.hasLeft(), def.getMargin() - .hasLeft(), Boolean.class); - - DesignAttributeHandler.writeAttribute("margin-right", design - .attributes(), marginInfo.hasRight(), def.getMargin() - .hasRight(), Boolean.class); - - DesignAttributeHandler.writeAttribute("margin-top", design - .attributes(), marginInfo.hasTop(), def.getMargin() - .hasTop(), Boolean.class); - - DesignAttributeHandler.writeAttribute("margin-bottom", design - .attributes(), marginInfo.hasBottom(), def.getMargin() - .hasBottom(), Boolean.class); - } + writeMargin(design, getMargin(), def.getMargin(), designContext); // handle children if (!designContext.shouldWriteChildren(this, def)) { diff --git a/server/src/com/vaadin/ui/AbstractTextField.java b/server/src/com/vaadin/ui/AbstractTextField.java index 93025ac0fd..14c135228c 100644 --- a/server/src/com/vaadin/ui/AbstractTextField.java +++ b/server/src/com/vaadin/ui/AbstractTextField.java @@ -126,25 +126,22 @@ public abstract class AbstractTextField extends AbstractField<String> implements selectionPosition = -1; } - if (hasListeners(TextChangeEvent.class)) { - target.addAttribute(TextFieldConstants.ATTR_TEXTCHANGE_EVENTMODE, - getTextChangeEventMode().toString()); - target.addAttribute(TextFieldConstants.ATTR_TEXTCHANGE_TIMEOUT, - getTextChangeTimeout()); - if (lastKnownTextContent != null) { - /* - * The field has be repainted for some reason (e.g. caption, - * size, stylename), but the value has not been changed since - * the last text change event. Let the client side know about - * the value the server side knows. Client side may then ignore - * the actual value, depending on its state. - */ - target.addAttribute( - TextFieldConstants.ATTR_NO_VALUE_CHANGE_BETWEEN_PAINTS, - true); - } + target.addAttribute(TextFieldConstants.ATTR_TEXTCHANGE_EVENTMODE, + getTextChangeEventMode().toString()); + target.addAttribute(TextFieldConstants.ATTR_TEXTCHANGE_TIMEOUT, + getTextChangeTimeout()); + if (lastKnownTextContent != null) { + /* + * The field has be repainted for some reason (e.g. caption, size, + * stylename), but the value has not been changed since the last + * text change event. Let the client side know about the value the + * server side knows. Client side may then ignore the actual value, + * depending on its state. + */ + target.addAttribute( + TextFieldConstants.ATTR_NO_VALUE_CHANGE_BETWEEN_PAINTS, + true); } - } @Override diff --git a/server/src/com/vaadin/ui/ComboBox.java b/server/src/com/vaadin/ui/ComboBox.java index 4af93113f9..033ec3cd14 100644 --- a/server/src/com/vaadin/ui/ComboBox.java +++ b/server/src/com/vaadin/ui/ComboBox.java @@ -288,6 +288,13 @@ public class ComboBox extends AbstractSelect implements // Paint variables target.addVariable(this, "selected", selectedKeys); + if (getValue() != null && selectedKeys[0] == null) { + // not always available, e.g. scrollToSelectedIndex=false + // Give the caption for selected item still, not to make it look + // like there is no selection at all + target.addAttribute("selectedCaption", + getItemCaption(getValue())); + } if (isNewItemsAllowed()) { target.addVariable(this, "newitem", ""); } diff --git a/server/src/com/vaadin/ui/Grid.java b/server/src/com/vaadin/ui/Grid.java index 68676e5435..b0542352b9 100644 --- a/server/src/com/vaadin/ui/Grid.java +++ b/server/src/com/vaadin/ui/Grid.java @@ -57,7 +57,6 @@ import com.vaadin.data.RpcDataProviderExtension.DetailComponentManager; import com.vaadin.data.Validator.InvalidValueException; import com.vaadin.data.fieldgroup.DefaultFieldGroupFieldFactory; import com.vaadin.data.fieldgroup.FieldGroup; -import com.vaadin.data.fieldgroup.FieldGroup.BindException; import com.vaadin.data.fieldgroup.FieldGroup.CommitException; import com.vaadin.data.fieldgroup.FieldGroupFieldFactory; import com.vaadin.data.sort.Sort; @@ -2536,9 +2535,10 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, private Converter<?, Object> converter; /** - * A check for allowing the {@link #Column(Grid, GridColumnState, Object) - * constructor} to call {@link #setConverter(Converter)} with a - * <code>null</code>, even if model and renderer aren't compatible. + * A check for allowing the + * {@link #Column(Grid, GridColumnState, Object) constructor} to call + * {@link #setConverter(Converter)} with a <code>null</code>, even if + * model and renderer aren't compatible. */ private boolean isFirstConverterAssignment = true; @@ -2598,7 +2598,9 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, } /** - * Sets the caption of the header. + * Sets the caption of the header. This caption is also used as the + * hiding toggle caption, unless it is explicitly set via + * {@link #setHidingToggleCaption(String)}. * * @param caption * the text to show in the caption @@ -2610,6 +2612,9 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, public Column setHeaderCaption(String caption) throws IllegalStateException { checkColumnIsAttached(); + + state.headerCaption = caption; + HeaderRow row = grid.getHeader().getDefaultRow(); if (row != null) { row.getCell(grid.getPropertyIdByColumnId(state.id)).setText( @@ -2637,11 +2642,11 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, * toggle for this column in the grid's sidebar when the column is * {@link #isHidable() hidable}. * <p> - * By default, before triggering this setter, a user friendly version of - * the column's {@link #getPropertyId() property id} is used. + * The default value is <code>null</code>, and in that case the column's + * {@link #getHeaderCaption() header caption} is used. * <p> - * <em>NOTE:</em> setting this to <code>null</code> or empty string - * might cause the hiding toggle to not render correctly. + * <em>NOTE:</em> setting this to empty string might cause the hiding + * toggle to not render correctly. * * @since 7.5.0 * @param hidingToggleCaption @@ -3302,9 +3307,7 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, DesignAttributeHandler.writeAttribute("hidden", attributes, isHidden(), def.hidden, boolean.class); DesignAttributeHandler.writeAttribute("hiding-toggle-caption", - attributes, getHidingToggleCaption(), - SharedUtil.propertyIdToHumanFriendly(getPropertyId()), - String.class); + attributes, getHidingToggleCaption(), null, String.class); DesignAttributeHandler.writeAttribute("property-id", attributes, getPropertyId(), null, Object.class); } @@ -3372,7 +3375,7 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, * currently extends the AbstractExtension superclass, but this fact should * be regarded as an implementation detail and subject to change in a future * major or minor Vaadin revision. - * + * * @param <T> * the type this renderer knows how to present */ @@ -3445,7 +3448,7 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, * is desired. For instance, a {@code Renderer<Date>} could first turn a * date value into a formatted string and return * {@code encode(dateString, String.class)}. - * + * * @param value * the value to be encoded * @param type @@ -3460,7 +3463,7 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, /** * An abstract base class for server-side Grid extensions. - * + * * @since 7.5 */ public static abstract class AbstractGridExtension extends @@ -3475,7 +3478,7 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, /** * Constructs a new Grid extension and extends given Grid. - * + * * @param grid * a grid instance */ @@ -3977,7 +3980,7 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, @Override public void bind(int rowIndex) { - boolean success = false; + Exception exception = null; try { Object id = getContainerDataSource().getIdByIndex(rowIndex); if (!isEditorBuffered() || editedItemId == null) { @@ -3986,12 +3989,19 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, if (editedItemId.equals(id)) { doEditItem(); - success = true; } } catch (Exception e) { - handleError(e); + exception = e; + } + + if (exception != null) { + handleError(exception); + doCancelEditor(); + getEditorRpc().confirmBind(false); + } else { + doEditItem(); + getEditorRpc().confirmBind(true); } - getEditorRpc().confirmBind(success); } @Override @@ -4248,8 +4258,18 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, if (datasource.getContainerPropertyIds().contains(propertyId) && !columns.containsKey(propertyId)) { appendColumn(propertyId); - } else { + } else if (defaultContainer) { addColumnProperty(propertyId, String.class, ""); + } else { + if (columns.containsKey(propertyId)) { + throw new IllegalStateException("A column for property id '" + + propertyId.toString() + + "' already exists in this grid"); + } else { + throw new IllegalStateException("Property id '" + + propertyId.toString() + + "' does not exist in the container"); + } } // Inform the data provider of this new column. @@ -4418,7 +4438,6 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, String humanFriendlyPropertyId = SharedUtil .propertyIdToHumanFriendly(String.valueOf(datasourcePropertyId)); column.setHeaderCaption(humanFriendlyPropertyId); - column.setHidingToggleCaption(humanFriendlyPropertyId); if (datasource instanceof Sortable && ((Sortable) datasource).getSortableContainerPropertyIds() @@ -4628,8 +4647,7 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, * @throws IllegalArgumentException * if {@code rows} is zero or less * @throws IllegalArgumentException - * if {@code rows} is {@link Double#isInfinite(double) - * infinite} + * if {@code rows} is {@link Double#isInfinite(double) infinite} * @throws IllegalArgumentException * if {@code rows} is {@link Double#isNaN(double) NaN} */ @@ -5803,13 +5821,20 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, } Field<?> editor = editorFieldGroup.getField(propertyId); - if (editor == null) { - editor = editorFieldGroup.buildAndBind(propertyId); - } - if (editor.getParent() != Grid.this) { - assert editor.getParent() == null; - editor.setParent(this); + try { + if (editor == null) { + editor = editorFieldGroup.buildAndBind(propertyId); + } + } finally { + if (editor == null) { + editor = editorFieldGroup.getField(propertyId); + } + + if (editor != null && editor.getParent() != Grid.this) { + assert editor.getParent() == null; + editor.setParent(this); + } } return editor; } @@ -5905,6 +5930,7 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, protected void doCancelEditor() { editedItemId = null; editorFieldGroup.discard(); + editorFieldGroup.setItemDataSource(null); } void resetEditor() { diff --git a/server/src/com/vaadin/ui/GridLayout.java b/server/src/com/vaadin/ui/GridLayout.java index 35110b39ab..6ccb272704 100644 --- a/server/src/com/vaadin/ui/GridLayout.java +++ b/server/src/com/vaadin/ui/GridLayout.java @@ -782,7 +782,14 @@ public class GridLayout extends AbstractLayout implements } } } - // TODO forget expands for removed columns + + // Forget expands for removed columns + if (columns < getColumns()) { + for (int i = columns - 1; i < getColumns(); i++) { + columnExpandRatio.remove(i); + getState().explicitColRatios.remove(i); + } + } getState().columns = columns; } @@ -826,7 +833,13 @@ public class GridLayout extends AbstractLayout implements } } } - // TODO forget expands for removed rows + // Forget expands for removed rows + if (rows < getRows()) { + for (int i = rows - 1; i < getRows(); i++) { + rowExpandRatio.remove(i); + getState().explicitRowRatios.remove(i); + } + } getState().rows = rows; } @@ -1304,6 +1317,8 @@ public class GridLayout extends AbstractLayout implements public void readDesign(Element design, DesignContext designContext) { super.readDesign(design, designContext); + setMargin(readMargin(design, getMargin(), designContext)); + // Prepare a 2D map for reading column contents Elements rowElements = design.getElementsByTag("row"); List<Map<Integer, Component>> rows = new ArrayList<Map<Integer, Component>>(); @@ -1434,6 +1449,9 @@ public class GridLayout extends AbstractLayout implements super.writeDesign(design, designContext); GridLayout def = designContext.getDefaultInstance(this); + + writeMargin(design, getMargin(), def.getMargin(), designContext); + if (components.isEmpty() || !designContext.shouldWriteChildren(this, def)) { return; diff --git a/server/src/com/vaadin/ui/Table.java b/server/src/com/vaadin/ui/Table.java index cb61fa31ec..42c4beab6c 100644 --- a/server/src/com/vaadin/ui/Table.java +++ b/server/src/com/vaadin/ui/Table.java @@ -676,26 +676,6 @@ public class Table extends AbstractSelect implements Action.Container, } } - // Removes alignments, icons and headers from hidden columns - if (this.visibleColumns != null) { - boolean disabledHere = disableContentRefreshing(); - try { - for (final Iterator<Object> i = this.visibleColumns.iterator(); i - .hasNext();) { - final Object col = i.next(); - if (!newVC.contains(col)) { - setColumnHeader(col, null); - setColumnAlignment(col, (Align) null); - setColumnIcon(col, null); - } - } - } finally { - if (disabledHere) { - enableContentRefreshing(false); - } - } - } - this.visibleColumns = newVC; // Assures visual refresh diff --git a/server/src/com/vaadin/ui/UI.java b/server/src/com/vaadin/ui/UI.java index b16d7e32d3..2129db614b 100644 --- a/server/src/com/vaadin/ui/UI.java +++ b/server/src/com/vaadin/ui/UI.java @@ -718,6 +718,9 @@ public abstract class UI extends AbstractSingleComponentContainer implements page.init(request); + // Reset heartbeat timeout to avoid surprise if it's almost expired + setLastHeartbeatTimestamp(System.currentTimeMillis()); + refresh(request); URI newLocation = page.getLocation(); diff --git a/server/src/com/vaadin/ui/Window.java b/server/src/com/vaadin/ui/Window.java index 61664fc95d..61ba5826b8 100644 --- a/server/src/com/vaadin/ui/Window.java +++ b/server/src/com/vaadin/ui/Window.java @@ -268,6 +268,21 @@ public class Window extends Panel implements FocusNotifier, BlurNotifier, } /** + * Sets the position of the window on the screen using + * {@link #setPositionX(int)} and {@link #setPositionY(int)} + * + * @since 7.5 + * @param x + * The new x coordinate for the window + * @param y + * The new y coordinate for the window + */ + public void setPosition(int x, int y) { + setPositionX(x); + setPositionY(y); + } + + /** * Sets the distance of Window left border in pixels from left border of the * containing (main window). Has effect only if in {@link WindowMode#NORMAL} * mode. |