diff options
author | Henrik Paul <henrik@vaadin.com> | 2015-03-31 14:44:23 +0300 |
---|---|---|
committer | Henrik Paul <henrik@vaadin.com> | 2015-03-31 14:44:23 +0300 |
commit | 844b2c6c41d57d4db1238eb6096f225c9fdb8314 (patch) | |
tree | ffdd57cd35da2df500fbfae5981de381439d1f5c | |
parent | 2080f86e03552c56d52f488e4dcd72282cd64f62 (diff) | |
parent | 3ab82ace45827365e87f9540fad3dffaed0679b5 (diff) | |
download | vaadin-framework-844b2c6c41d57d4db1238eb6096f225c9fdb8314.tar.gz vaadin-framework-844b2c6c41d57d4db1238eb6096f225c9fdb8314.zip |
Merge remote-tracking branch 'origin/master' into grid-7.5
Change-Id: Ife8c6d2a5f6c134a6e28e862f524b6e687199cc8
84 files changed, 3728 insertions, 492 deletions
diff --git a/WebContent/WEB-INF/web.xml b/WebContent/WEB-INF/web.xml index c1102f4682..ef60364202 100644 --- a/WebContent/WEB-INF/web.xml +++ b/WebContent/WEB-INF/web.xml @@ -87,9 +87,9 @@ </servlet> <servlet> - <!-- This servlet is a separate instance for the sole purpose of testing - #12446 (com.vaadin.tests.components.ui.TimeoutRedirectResetsOnActivity) because - it modifies the VaadinService timeout parameters --> + <!-- This servlet is a separate instance for the sole purpose of + testing #12446 (com.vaadin.tests.components.ui.TimeoutRedirectResetsOnActivity) + because it modifies the VaadinService timeout parameters --> <servlet-name>VaadinApplicationRunnerWithTimeoutRedirect</servlet-name> <servlet-class>com.vaadin.launcher.ApplicationRunnerServlet</servlet-class> </servlet> @@ -115,17 +115,6 @@ <async-supported>true</async-supported> </servlet> - <!-- For testing custom push path with, for example, Weblogic 12.1.2 --> - <servlet> - <servlet-name>VaadinApplicationRunnerWithPushPathTest</servlet-name> - <servlet-class>com.vaadin.launcher.ApplicationRunnerServlet</servlet-class> - <init-param> - <param-name>pushPath</param-name> - <param-value>ws</param-value> - </init-param> - <async-supported>true</async-supported> - </servlet> - <servlet-mapping> <servlet-name>Embed App 1</servlet-name> <url-pattern>/embed1/*</url-pattern> @@ -160,11 +149,6 @@ </servlet-mapping> <servlet-mapping> - <servlet-name>VaadinApplicationRunnerWithPushPathTest</servlet-name> - <url-pattern>/run-pushpath/*</url-pattern> - </servlet-mapping> - - <servlet-mapping> <servlet-name>IntegrationTest</servlet-name> <url-pattern>/integration/*</url-pattern> </servlet-mapping> diff --git a/WebContent/statictestfiles/TableInIframeRowClickScrollJumpTest.html b/WebContent/statictestfiles/TableInIframeRowClickScrollJumpTest.html new file mode 100644 index 0000000000..dfddbdb7e5 --- /dev/null +++ b/WebContent/statictestfiles/TableInIframeRowClickScrollJumpTest.html @@ -0,0 +1,12 @@ +<html>
+<head>
+<title>IEJumpTest</title>
+</head>
+<!-- This is for testing Vaadin UI in iFrame (test class TableInIframeRowClickScrollJumpTest, for ticket #15294 -->
+<body>
+ <div style="font-size: 4px">test div</div>
+ <iframe id="test-iframe-0" src="/run/com.vaadin.tests.components.table.TableBlurFocus" width="100%" height="2000px" style="visibility: visible;">
+ </iframe>
+</body>
+
+</html>
\ No newline at end of file diff --git a/all/build.xml b/all/build.xml index 37f728e529..c3672fc828 100644 --- a/all/build.xml +++ b/all/build.xml @@ -50,7 +50,7 @@ </then> <else> <!-- Unpack all source files to javadoc.temp.dir --> - <antcontrib:foreach list="${modules.to.publish.to.maven}" + <antcontrib:foreach list="${modules.producing.jar}" target="unzip.to.javadoctemp" param="module" /> <property name="javadoc.dir" location="${result.dir}/javadoc" /> @@ -88,7 +88,7 @@ <target name="copy-jars"> <delete dir="${temp.dir}" /> - <antcontrib:foreach list="${modules.to.publish.to.maven}" + <antcontrib:foreach list="${modules.producing.jar}" target="fetch.module.and.dependencies" param="module" /> <!-- All jars are now in temp.dir. Still need to separate vaadin and deps --> diff --git a/bom/build.xml b/bom/build.xml new file mode 100644 index 0000000000..42d7303b7b --- /dev/null +++ b/bom/build.xml @@ -0,0 +1,46 @@ +<?xml version="1.0"?> + +<project name="vaadin-bom" basedir="." default="publish-local" + xmlns:artifact="antlib:org.apache.maven.artifact.ant" xmlns:antcontrib="antlib:net.sf.antcontrib"> + <description> + Compiles a BOM (Bill of Materials) Maven artifact + </description> + <include file="../common.xml" as="common" /> + <include file="../build.xml" as="vaadin" /> + + <!-- global properties --> + <property name="module.name" value="vaadin-bom" /> + <property name="result.dir" value="result" /> + + <target name="bom" description="Generates a BOM (Bill of Materials) pom.xml. Either for a snapshot or a release version"> + <fail unless="result.dir" message="No result.dir parameter given" /> + <condition property="vaadin.maven.version" value="${vaadin.version}"> + <isset property="build.release" /> + </condition> + <!-- if this wasn't already set by the condition, this is a snapshot --> + <property name="vaadin.maven.version" value="${vaadin.version.major}.${vaadin.version.minor}-SNAPSHOT" /> + + <property name="bom.xml" location="${result.dir}/lib/vaadin-bom-${vaadin.version}.pom" /> + + <copy file="vaadin-bom.pom" tofile="${bom.xml}" overwrite="true"> + <filterchain> + <replacestring from="@vaadin.version@" to="${vaadin.maven.version}" /> + </filterchain> + </copy> + </target> + + <target name="publish-local" depends="bom"> + <antcall target="common.publish-local" /> + </target> + + <target name="clean"> + <antcall target="common.clean" /> + </target> + <target name="checkstyle"> + <!-- No code in this module --> + </target> + <target name="test" depends="checkstyle"> + <!-- No tests for this BOM.. --> + </target> + +</project> diff --git a/bom/ivy.xml b/bom/ivy.xml new file mode 100644 index 0000000000..39abb30d80 --- /dev/null +++ b/bom/ivy.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ivy-module version="2.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="http://ant.apache.org/ivy/schemas/ivy.xsd" + xmlns:m="http://ant.apache.org/ivy/maven"> + + <info organisation="com.vaadin" module="vaadin-bom" revision="${vaadin.version}" /> + + <configurations> + <conf name="build" /> + </configurations> + <publications> + <artifact type="pom" ext="pom" /> + </publications> + <dependencies defaultconf="build"> + </dependencies> + +</ivy-module> diff --git a/bom/vaadin-bom.pom b/bom/vaadin-bom.pom new file mode 100644 index 0000000000..f2b482ad9b --- /dev/null +++ b/bom/vaadin-bom.pom @@ -0,0 +1,103 @@ +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>com.vaadin</groupId> + <artifactId>vaadin-parent</artifactId> + <version>1.0.0</version> + </parent> + <groupId>com.vaadin</groupId> + <artifactId>vaadin-bom</artifactId> + <version>@vaadin.version@</version> + <packaging>pom</packaging> + <name>Vaadin Framework (Bill of Materials)</name> + <description>Vaadin Framework (Bill of Materials)</description> + <organization> + <name>Vaadin Ltd</name> + <url>http://vaadin.com</url> + </organization> + <url>http://vaadin.com</url> + <licenses> + <license> + <name>Apache License Version 2.0</name> + <distribution>repo</distribution> + <url>http://www.apache.org/licenses/LICENSE-2.0</url> + </license> + </licenses> + <scm> + <connection>scm:https:https://github.com/vaadin/vaadin.git</connection> + <developerConnection>scm:https:https://github.com/vaadin/vaadin.git</developerConnection> + <url>https://github.com/vaadin/vaadin</url> + </scm> + <developers> + <developer> + <id>artur</id> + <name>Artur Signell</name> + <email>artur.signell@vaadin.com</email> + <url>http://vaadin.com/web/artur</url> + <organization>Vaadin Ltd</organization> + <organizationUrl>http://vaadin.com/</organizationUrl> + <roles> + <role>architect</role> + <role>developer</role> + <role>team manager</role> + </roles> + <timezone>2</timezone> + </developer> + <developer> + <id>hesara</id> + <name>Henri Sara</name> + <email>hesara@vaadin.com</email> + <organization>Vaadin Ltd</organization> + <organizationUrl>http://vaadin.com/</organizationUrl> + <roles> + <role>developer</role> + </roles> + <timezone>2</timezone> + </developer> + </developers> + <dependencyManagement> + <dependencies> + <dependency> + <groupId>com.vaadin</groupId> + <artifactId>vaadin-shared</artifactId> + <version>@vaadin.version@</version> + </dependency> + <dependency> + <groupId>com.vaadin</groupId> + <artifactId>vaadin-server</artifactId> + <version>@vaadin.version@</version> + </dependency> + <dependency> + <groupId>com.vaadin</groupId> + <artifactId>vaadin-push</artifactId> + <version>@vaadin.version@</version> + </dependency> + <dependency> + <groupId>com.vaadin</groupId> + <artifactId>vaadin-widgets</artifactId> + <version>@vaadin.version@</version> + </dependency> + <dependency> + <groupId>com.vaadin</groupId> + <artifactId>vaadin-client</artifactId> + <version>@vaadin.version@</version> + </dependency> + <dependency> + <groupId>com.vaadin</groupId> + <artifactId>vaadin-client-compiler</artifactId> + <version>@vaadin.version@</version> + </dependency> + <dependency> + <groupId>com.vaadin</groupId> + <artifactId>vaadin-client-compiled</artifactId> + <version>@vaadin.version@</version> + </dependency> + <dependency> + <groupId>com.vaadin</groupId> + <artifactId>vaadin-themes</artifactId> + <version>@vaadin.version@</version> + </dependency> + </dependencies> + </dependencyManagement> +</project> @@ -21,11 +21,13 @@ <exclude name="build/**" /> <exclude name="bin/**" /> <exclude name="buildhelpers/**" /> + <exclude name="all/**" /> </fileset> </ivy:buildlist> <path id="build-path"> <path location="buildhelpers/build.xml" /> <path refid="ivy.build.path" /> + <path location="all/build.xml" /> </path> </target> <target name="clean" depends="buildorder"> diff --git a/client/src/com/vaadin/client/BrowserInfo.java b/client/src/com/vaadin/client/BrowserInfo.java index 5ca79cb121..3bc75a9a9b 100644 --- a/client/src/com/vaadin/client/BrowserInfo.java +++ b/client/src/com/vaadin/client/BrowserInfo.java @@ -411,6 +411,11 @@ public class BrowserInfo { if (isIOS() && isWebkit() && getOperatingSystemMajorVersion() >= 6) { return false; } + + if (isIE()) { + return false; + } + return true; } diff --git a/client/src/com/vaadin/client/communication/AtmospherePushConnection.java b/client/src/com/vaadin/client/communication/AtmospherePushConnection.java index 787da9bc6f..e544c91d0f 100644 --- a/client/src/com/vaadin/client/communication/AtmospherePushConnection.java +++ b/client/src/com/vaadin/client/communication/AtmospherePushConnection.java @@ -43,7 +43,7 @@ import elemental.json.JsonObject; /** * The default {@link PushConnection} implementation that uses Atmosphere for * handling the communication channel. - * + * * @author Vaadin Ltd * @since 7.1 */ @@ -135,8 +135,6 @@ public class AtmospherePushConnection implements PushConnection { */ private Command pendingDisconnectCommand; - private String pushPath; - public AtmospherePushConnection() { } @@ -183,9 +181,6 @@ public class AtmospherePushConnection implements PushConnection { pushConfiguration.parameters.get(param)); } - pushPath = pushConfiguration.pushPath; - assert pushPath != null; - runWhenAtmosphereLoaded(new Command() { @Override public void execute() { @@ -202,7 +197,7 @@ public class AtmospherePushConnection implements PushConnection { private void connect() { String baseUrl = connection .translateVaadinUri(ApplicationConstants.APP_PROTOCOL_PREFIX - + pushPath + '/'); + + ApplicationConstants.PUSH_PATH + '/'); String extraParams = UIConstants.UI_ID_PARAMETER + "=" + connection.getConfiguration().getUIId(); @@ -277,9 +272,9 @@ public class AtmospherePushConnection implements PushConnection { /** * Called whenever a server push connection is established (or * re-established). - * + * * @param response - * + * * @since 7.2 */ protected void onConnect(AtmosphereResponse response) { @@ -360,7 +355,7 @@ public class AtmospherePushConnection implements PushConnection { /** * Called if the push connection fails. Atmosphere will automatically retry * the connection until successful. - * + * */ protected void onError(AtmosphereResponse response) { state = State.DISCONNECTED; diff --git a/client/src/com/vaadin/client/connectors/GridConnector.java b/client/src/com/vaadin/client/connectors/GridConnector.java index d17f378611..0e2ee0046b 100644 --- a/client/src/com/vaadin/client/connectors/GridConnector.java +++ b/client/src/com/vaadin/client/connectors/GridConnector.java @@ -176,6 +176,18 @@ public class GridConnector extends AbstractHasComponentsConnector implements this.id = id; } + /** + * Sets a new renderer for this column object + * + * @param rendererConnector + * a renderer connector object + */ + public void setRenderer( + AbstractRendererConnector<Object> rendererConnector) { + setRenderer(rendererConnector.getRenderer()); + this.rendererConnector = rendererConnector; + } + @Override public Object getValue(final JsonObject obj) { final JsonObject rowData = obj.getObject(GridState.JSONKEY_DATA); @@ -189,16 +201,6 @@ public class GridConnector extends AbstractHasComponentsConnector implements return null; } - /* - * Only used to check that the renderer connector will not change during - * the column lifetime. - * - * TODO remove once support for changing renderers is implemented - */ - private AbstractRendererConnector<Object> getRendererConnector() { - return rendererConnector; - } - private AbstractFieldConnector getEditorConnector() { return editorConnector; } @@ -1137,11 +1139,6 @@ public class GridConnector extends AbstractHasComponentsConnector implements columnsUpdatedFromState = true; updateColumnFromState(column, columnState); columnsUpdatedFromState = false; - - if (columnState.rendererConnector != column.getRendererConnector()) { - throw new UnsupportedOperationException( - "Changing column renderer after initialization is currently unsupported"); - } } /** @@ -1185,6 +1182,7 @@ public class GridConnector extends AbstractHasComponentsConnector implements * @param state * The state to get the data from */ + @SuppressWarnings("unchecked") private static void updateColumnFromState(CustomGridColumn column, GridColumnState state) { column.setWidth(state.width); @@ -1192,6 +1190,9 @@ public class GridConnector extends AbstractHasComponentsConnector implements column.setMaximumWidth(state.maxWidth); column.setExpandRatio(state.expandRatio); + assert state.rendererConnector instanceof AbstractRendererConnector : "GridColumnState.rendererConnector is invalid (not subclass of AbstractRendererConnector)"; + column.setRenderer((AbstractRendererConnector<Object>) state.rendererConnector); + column.setSortable(state.sortable); column.setHidden(state.hidden); diff --git a/client/src/com/vaadin/client/ui/FocusableScrollPanel.java b/client/src/com/vaadin/client/ui/FocusableScrollPanel.java index 475c8f8074..9dd9c17675 100644 --- a/client/src/com/vaadin/client/ui/FocusableScrollPanel.java +++ b/client/src/com/vaadin/client/ui/FocusableScrollPanel.java @@ -48,9 +48,11 @@ public class FocusableScrollPanel extends SimpleFocusablePanel implements Style style = getElement().getStyle(); style.setProperty("zoom", "1"); style.setPosition(Position.RELATIVE); + browserInfo = BrowserInfo.get(); } private DivElement focusElement; + private BrowserInfo browserInfo; public FocusableScrollPanel(boolean useFakeFocusElement) { this(); @@ -72,6 +74,12 @@ public class FocusableScrollPanel extends SimpleFocusablePanel implements style.setPosition(Position.FIXED); style.setTop(0, Unit.PX); style.setLeft(0, Unit.PX); + if (browserInfo.isIE()) { + // for #15294: artificially hide little bit more the + // focusElement, otherwise IE will make the window to scroll + // into it when focused + style.setLeft(-999, Unit.PX); + } getElement().appendChild(focusElement); /* Sink from focusElemet too as focusa and blur don't bubble */ DOM.sinkEvents(focusElement, Event.FOCUSEVENTS); diff --git a/client/src/com/vaadin/client/ui/VAccordion.java b/client/src/com/vaadin/client/ui/VAccordion.java index 06eaecaf70..fc328dd56a 100644 --- a/client/src/com/vaadin/client/ui/VAccordion.java +++ b/client/src/com/vaadin/client/ui/VAccordion.java @@ -29,8 +29,8 @@ import com.google.gwt.user.client.Event; import com.google.gwt.user.client.ui.ComplexPanel; import com.google.gwt.user.client.ui.Widget; import com.vaadin.client.ComponentConnector; -import com.vaadin.client.WidgetUtil; import com.vaadin.client.VCaption; +import com.vaadin.client.WidgetUtil; import com.vaadin.client.ui.TouchScrollDelegate.TouchScrollHandler; import com.vaadin.shared.ComponentConstants; import com.vaadin.shared.ui.accordion.AccordionState; @@ -417,6 +417,9 @@ public class VAccordion extends VTabsheetBase { public void removeTab(int index) { StackItem item = getStackItem(index); remove(item); + if (selectedItemIndex == index) { + selectedItemIndex = -1; + } touchScrollHandler.removeElement(item.getContainerElement()); } diff --git a/client/src/com/vaadin/client/ui/VScrollTable.java b/client/src/com/vaadin/client/ui/VScrollTable.java index 12de2724fc..13561dcd0f 100644 --- a/client/src/com/vaadin/client/ui/VScrollTable.java +++ b/client/src/com/vaadin/client/ui/VScrollTable.java @@ -2774,7 +2774,9 @@ public class VScrollTable extends FlowPanel implements HasWidgets, private boolean sortable = false; private final String cid; + private boolean dragging; + private Integer currentDragX = null; // is used to resolve #14796 private int dragStartX; private int colIndex; @@ -3146,6 +3148,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets, event.stopPropagation(); } dragging = true; + currentDragX = WidgetUtil.getTouchOrMouseClientX(event); moved = false; colIndex = getColIndexByKey(cid); DOM.setCapture(getElement()); @@ -3160,6 +3163,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets, if (columnReordering && WidgetUtil.isTouchEventOrLeftMouseButton(event)) { dragging = false; + currentDragX = null; DOM.releaseCapture(getElement()); if (WidgetUtil.isTouchEvent(event)) { @@ -3227,47 +3231,57 @@ public class VScrollTable extends FlowPanel implements HasWidgets, break; case Event.ONTOUCHMOVE: case Event.ONMOUSEMOVE: - if (dragging && WidgetUtil.isTouchEventOrLeftMouseButton(event)) { - if (event.getTypeInt() == Event.ONTOUCHMOVE) { - /* - * prevent using this event in e.g. scrolling - */ - event.stopPropagation(); - } - if (!moved) { - createFloatingCopy(); - moved = true; - } + // only start the drag if the mouse / touch has moved a minimum + // distance in x-axis (the same idea as in #13381) + int currentX = WidgetUtil.getTouchOrMouseClientX(event); - final int clientX = WidgetUtil - .getTouchOrMouseClientX(event); - final int x = clientX + tHead.hTableWrapper.getScrollLeft(); - int slotX = headerX; - closestSlot = colIndex; - int closestDistance = -1; - int start = 0; - if (showRowHeaders) { - start++; - } - final int visibleCellCount = tHead.getVisibleCellCount(); - for (int i = start; i <= visibleCellCount; i++) { - if (i > 0) { - final String colKey = getColKeyByIndex(i - 1); - // getColWidth only returns the internal width - // without padding, not the offset width of the - // whole td (#10890) - slotX += getColWidth(colKey) - + scrollBody.getCellExtraWidth(); + if (currentDragX == null + || Math.abs(currentDragX - currentX) > VDragAndDropManager.MINIMUM_DISTANCE_TO_START_DRAG) { + if (dragging + && WidgetUtil.isTouchEventOrLeftMouseButton(event)) { + if (event.getTypeInt() == Event.ONTOUCHMOVE) { + /* + * prevent using this event in e.g. scrolling + */ + event.stopPropagation(); } - final int dist = Math.abs(x - slotX); - if (closestDistance == -1 || dist < closestDistance) { - closestDistance = dist; - closestSlot = i; + if (!moved) { + createFloatingCopy(); + moved = true; } - } - tHead.focusSlot(closestSlot); - updateFloatingCopysPosition(clientX, -1); + final int clientX = WidgetUtil + .getTouchOrMouseClientX(event); + final int x = clientX + + tHead.hTableWrapper.getScrollLeft(); + int slotX = headerX; + closestSlot = colIndex; + int closestDistance = -1; + int start = 0; + if (showRowHeaders) { + start++; + } + final int visibleCellCount = tHead + .getVisibleCellCount(); + for (int i = start; i <= visibleCellCount; i++) { + if (i > 0) { + final String colKey = getColKeyByIndex(i - 1); + // getColWidth only returns the internal width + // without padding, not the offset width of the + // whole td (#10890) + slotX += getColWidth(colKey) + + scrollBody.getCellExtraWidth(); + } + final int dist = Math.abs(x - slotX); + if (closestDistance == -1 || dist < closestDistance) { + closestDistance = dist; + closestSlot = i; + } + } + tHead.focusSlot(closestSlot); + + updateFloatingCopysPosition(clientX, -1); + } } break; default: diff --git a/client/src/com/vaadin/client/ui/customlayout/CustomLayoutConnector.java b/client/src/com/vaadin/client/ui/customlayout/CustomLayoutConnector.java index 80979587b9..cde1f1af0f 100644 --- a/client/src/com/vaadin/client/ui/customlayout/CustomLayoutConnector.java +++ b/client/src/com/vaadin/client/ui/customlayout/CustomLayoutConnector.java @@ -77,22 +77,24 @@ public class CustomLayoutConnector extends AbstractLayoutConnector implements // (even though both can never be given at the same time) templateContents = getConnection().getResource( "layouts/" + templateName + ".html"); - if (templateContents == null) { - // Template missing -> show debug notice and render components - // in order. - getWidget() - .getElement() - .setInnerHTML( - "<em>Layout file layouts/" - + templateName - + ".html is missing. Components will be drawn for debug purposes.</em>"); - } } if (templateContents != null) { // Template ok -> initialize. getWidget().initializeHTML(templateContents, getConnection().getThemeUri()); + } else { + // Template missing -> show debug notice and render components in + // order. + String warning = templateName != null ? "Layout file layouts/" + + templateName + ".html is missing." + : "Layout file not specified."; + getWidget() + .getElement() + .setInnerHTML( + "<em>" + + warning + + " Components will be drawn for debug purposes.</em>"); } templateUpdated = true; } diff --git a/client/src/com/vaadin/client/ui/dd/VDragAndDropManager.java b/client/src/com/vaadin/client/ui/dd/VDragAndDropManager.java index 844f4c1b9c..47f8eb1b66 100644 --- a/client/src/com/vaadin/client/ui/dd/VDragAndDropManager.java +++ b/client/src/com/vaadin/client/ui/dd/VDragAndDropManager.java @@ -38,9 +38,9 @@ import com.vaadin.client.ComponentConnector; import com.vaadin.client.MouseEventDetailsBuilder; import com.vaadin.client.Profiler; import com.vaadin.client.UIDL; -import com.vaadin.client.WidgetUtil; import com.vaadin.client.VConsole; import com.vaadin.client.ValueMap; +import com.vaadin.client.WidgetUtil; import com.vaadin.client.ui.VOverlay; import com.vaadin.shared.ApplicationConstants; import com.vaadin.shared.MouseEventDetails; @@ -240,6 +240,12 @@ public class VDragAndDropManager { } + /* + * #13381, #14796. The drag only actually starts when the mouse move or + * touch move event is more than 3 pixel away. + */ + public static final int MINIMUM_DISTANCE_TO_START_DRAG = 3; + private static VDragAndDropManager instance; private HandlerRegistration handlerRegistration; private VDragEvent currentDrag; @@ -425,25 +431,16 @@ public class VDragAndDropManager { int currentY = WidgetUtil .getTouchOrMouseClientY(event .getNativeEvent()); - if (Math.abs(startX - currentX) > 3 - || Math.abs(startY - currentY) > 3) { - if (deferredStartRegistration != null) { - deferredStartRegistration - .removeHandler(); - deferredStartRegistration = null; - } + if (Math.abs(startX - currentX) > MINIMUM_DISTANCE_TO_START_DRAG + || Math.abs(startY - currentY) > MINIMUM_DISTANCE_TO_START_DRAG) { + ensureDeferredRegistrationCleanup(); currentDrag.setCurrentGwtEvent(event .getNativeEvent()); startDrag.execute(); } break; default: - // on any other events, clean up the - // deferred drag start - if (deferredStartRegistration != null) { - deferredStartRegistration.removeHandler(); - deferredStartRegistration = null; - } + ensureDeferredRegistrationCleanup(); currentDrag = null; clearDragElement(); break; @@ -534,10 +531,10 @@ public class VDragAndDropManager { } private void endDrag(boolean doDrop) { - if (handlerRegistration != null) { - handlerRegistration.removeHandler(); - handlerRegistration = null; - } + + ensureDeferredRegistrationCleanup(); + ensureHandlerRegistrationCleanup(); + boolean sendTransferableToServer = false; if (currentDropHandler != null) { if (doDrop) { @@ -595,6 +592,20 @@ public class VDragAndDropManager { } + private void ensureHandlerRegistrationCleanup() { + if (handlerRegistration != null) { + handlerRegistration.removeHandler(); + handlerRegistration = null; + } + } + + private void ensureDeferredRegistrationCleanup() { + if (deferredStartRegistration != null) { + deferredStartRegistration.removeHandler(); + deferredStartRegistration = null; + } + } + private void removeActiveDragSourceStyleName(ComponentConnector dragSource) { dragSource.getWidget().removeStyleName(ACTIVE_DRAG_SOURCE_STYLENAME); } diff --git a/client/src/com/vaadin/client/widget/grid/selection/MultiSelectionRenderer.java b/client/src/com/vaadin/client/widget/grid/selection/MultiSelectionRenderer.java index 5024c8bffa..ddbf690970 100644 --- a/client/src/com/vaadin/client/widget/grid/selection/MultiSelectionRenderer.java +++ b/client/src/com/vaadin/client/widget/grid/selection/MultiSelectionRenderer.java @@ -569,6 +569,7 @@ public class MultiSelectionRenderer<T> extends ComplexRenderer<Boolean> { InputElement checkbox = InputElement.as(cell.getElement() .getFirstChildElement()); checkbox.setChecked(data.booleanValue()); + checkbox.setDisabled(grid.isEditorActive()); checkbox.setPropertyInt(LOGICAL_ROW_PROPERTY_INT, cell.getRowIndex()); } diff --git a/client/src/com/vaadin/client/widgets/Escalator.java b/client/src/com/vaadin/client/widgets/Escalator.java index 6d9b8ee70a..88ed9295e4 100644 --- a/client/src/com/vaadin/client/widgets/Escalator.java +++ b/client/src/com/vaadin/client/widgets/Escalator.java @@ -2031,9 +2031,8 @@ public class Escalator extends Widget implements RequiresResize, return new Cell(domRowIndex, domColumnIndex, cellElement); } - double getMaxCellWidth(int colIndex) throws IllegalArgumentException { - double maxCellWidth = -1; - + void createAutoSizeElements(int colIndex, + Collection<TableCellElement> elements) { assert isAttached() : "Can't measure max width of cell, since Escalator is not attached to the DOM."; NodeList<TableRowElement> rows = root.getRows(); @@ -2062,24 +2061,9 @@ public class Escalator extends Widget implements RequiresResize, cellClone.getStyle().clearWidth(); rowElement.insertBefore(cellClone, cellOriginal); - double requiredWidth = WidgetUtil - .getRequiredWidthBoundingClientRectDouble(cellClone); - - if (BrowserInfo.get().isIE()) { - /* - * IE browsers have some issues with subpixels. Occasionally - * content is overflown even if not necessary. Increase the - * counted required size by 0.01 just to be on the safe - * side. - */ - requiredWidth += 0.01; - } - maxCellWidth = Math.max(requiredWidth, maxCellWidth); - cellClone.removeFromParent(); + elements.add(cellClone); } - - return maxCellWidth; } private boolean cellIsPartOfSpan(TableCellElement cell) { @@ -3906,7 +3890,8 @@ public class Escalator extends Widget implements RequiresResize, if (px < 0) { if (isAttached()) { - calculateWidth(); + autosizeColumns(Collections.singletonList(columns + .indexOf(this))); } else { /* * the column's width is calculated at Escalator.onLoad @@ -3960,10 +3945,6 @@ public class Escalator extends Widget implements RequiresResize, } return false; } - - private void calculateWidth() { - calculatedWidth = getMaxCellWidth(columns.indexOf(this)); - } } private final List<Column> columns = new ArrayList<Column>(); @@ -4268,6 +4249,7 @@ public class Escalator extends Widget implements RequiresResize, return; } + List<Integer> autosizeColumns = new ArrayList<Integer>(); for (Entry<Integer, Double> entry : indexWidthMap.entrySet()) { int index = entry.getKey().intValue(); double width = entry.getValue().doubleValue(); @@ -4277,10 +4259,15 @@ public class Escalator extends Widget implements RequiresResize, } checkValidColumnIndex(index); - columns.get(index).setWidth(width); - + if (width >= 0) { + columns.get(index).setWidth(width); + } else { + autosizeColumns.add(index); + } } + autosizeColumns(autosizeColumns); + widthsArray = null; header.reapplyColumnWidths(); body.reapplyColumnWidths(); @@ -4291,6 +4278,64 @@ public class Escalator extends Widget implements RequiresResize, recalculateElementSizes(); } + private void autosizeColumns(List<Integer> columns) { + if (columns.isEmpty()) { + return; + } + + // Must process columns in index order + Collections.sort(columns); + + Map<Integer, List<TableCellElement>> autoSizeElements = new HashMap<Integer, List<TableCellElement>>(); + try { + // Set up the entire DOM at once + for (int i = columns.size() - 1; i >= 0; i--) { + // Iterate backwards to not mess with the indexing + Integer colIndex = columns.get(i); + + ArrayList<TableCellElement> elements = new ArrayList<TableCellElement>(); + autoSizeElements.put(colIndex, elements); + + header.createAutoSizeElements(colIndex, elements); + body.createAutoSizeElements(colIndex, elements); + footer.createAutoSizeElements(colIndex, elements); + } + + // Extract all measurements & update values + for (Integer colIndex : columns) { + double maxWidth = Double.NEGATIVE_INFINITY; + List<TableCellElement> elements = autoSizeElements + .get(colIndex); + for (TableCellElement element : elements) { + + double cellWidth = WidgetUtil + .getRequiredWidthBoundingClientRectDouble(element); + + maxWidth = Math.max(maxWidth, cellWidth); + } + assert maxWidth >= 0 : "Got a negative max width for a column, which should be impossible."; + + if (BrowserInfo.get().isIE()) { + /* + * IE browsers have some issues with subpixels. + * Occasionally content is overflown even if not + * necessary. Increase the counted required size by 0.01 + * just to be on the safe side. + */ + maxWidth += 0.01; + } + + this.columns.get(colIndex).calculatedWidth = maxWidth; + } + } finally { + for (List<TableCellElement> list : autoSizeElements.values()) { + for (TableCellElement element : list) { + element.removeFromParent(); + } + } + } + } + private void checkValidColumnIndex(int index) throws IllegalArgumentException { if (!Range.withLength(0, getColumnCount()).contains(index)) { @@ -4310,18 +4355,6 @@ public class Escalator extends Widget implements RequiresResize, return columns.get(index).getCalculatedWidth(); } - private double getMaxCellWidth(int colIndex) - throws IllegalArgumentException { - double headerWidth = header.getMaxCellWidth(colIndex); - double bodyWidth = body.getMaxCellWidth(colIndex); - double footerWidth = footer.getMaxCellWidth(colIndex); - - double maxWidth = Math.max(headerWidth, - Math.max(bodyWidth, footerWidth)); - assert maxWidth >= 0 : "Got a negative max width for a column, which should be impossible."; - return maxWidth; - } - /** * Calculates the width of the columns in a given range. * diff --git a/client/src/com/vaadin/client/widgets/Grid.java b/client/src/com/vaadin/client/widgets/Grid.java index dad6f0fcfc..20b8844623 100644 --- a/client/src/com/vaadin/client/widgets/Grid.java +++ b/client/src/com/vaadin/client/widgets/Grid.java @@ -1190,6 +1190,7 @@ public class Grid<T> extends ResizeComposite implements + "Grid editor"); grid.getEscalator().setScrollLocked(Direction.VERTICAL, false); + updateSelectionCheckboxesAsNeeded(true); } } }; @@ -1289,6 +1290,16 @@ public class Grid<T> extends ResizeComposite implements null); handler.cancel(request); state = State.INACTIVE; + updateSelectionCheckboxesAsNeeded(true); + } + + private void updateSelectionCheckboxesAsNeeded(boolean isEnabled) { + if (grid.getSelectionModel() instanceof Multi) { + grid.refreshBody(); + CheckBox checkBox = (CheckBox) grid.getDefaultHeaderRow() + .getCell(grid.selectionColumn).getWidget(); + checkBox.setEnabled(isEnabled); + } } /** @@ -1315,6 +1326,7 @@ public class Grid<T> extends ResizeComposite implements EditorRequest<T> request = new EditorRequestImpl<T>(grid, rowIndex, saveRequestCallback); handler.save(request); + updateSelectionCheckboxesAsNeeded(true); } /** @@ -1379,6 +1391,7 @@ public class Grid<T> extends ResizeComposite implements rowIndex, bindRequestCallback); handler.bind(request); grid.getEscalator().setScrollLocked(Direction.VERTICAL, true); + updateSelectionCheckboxesAsNeeded(false); } } @@ -2577,7 +2590,7 @@ public class Grid<T> extends ResizeComposite implements final double widthFixed = Math.max(widthAsIs, column.getMinimumWidth()); defaultExpandRatios = defaultExpandRatios - && column.getExpandRatio() == -1; + && (column.getExpandRatio() == -1 || column == selectionColumn); if (isFixedWidth) { columnSizes.put(visibleColumns.indexOf(column), widthFixed); @@ -2595,7 +2608,8 @@ public class Grid<T> extends ResizeComposite implements .getExpandRatio()); final double newWidth = column.getWidthActual(); final double maxWidth = getMaxWidth(column); - boolean shouldExpand = newWidth < maxWidth && expandRatio > 0; + boolean shouldExpand = newWidth < maxWidth && expandRatio > 0 + && column != selectionColumn; if (shouldExpand) { totalRatios += expandRatio; columnsToExpand.add(column); @@ -3923,12 +3937,14 @@ public class Grid<T> extends ResizeComposite implements if (renderer == null) { throw new IllegalArgumentException("Renderer cannot be null."); } - bodyRenderer = renderer; - if (grid != null) { - grid.refreshBody(); - } + if (renderer != bodyRenderer) { + bodyRenderer = renderer; + if (grid != null) { + grid.refreshBody(); + } + } return this; } @@ -5635,7 +5651,6 @@ public class Grid<T> extends ResizeComposite implements escalator.getColumnConfiguration() .setFrozenColumnCount(numberOfColumns); - } /** @@ -6975,6 +6990,9 @@ public class Grid<T> extends ResizeComposite implements // Do ComplexRenderer.init and render new content conf.insertColumns(0, visibleColumns.size()); + // Number of frozen columns should be kept same #16901 + updateFrozenColumns(); + // Update column widths. for (Column<?, T> column : columns) { column.reapplyWidth(); diff --git a/common.xml b/common.xml index 9487560051..075044bb5b 100644 --- a/common.xml +++ b/common.xml @@ -9,7 +9,8 @@ <property name="gwt.basedir" location="${vaadin.basedir}/../gwt" /> <property file="${vaadin.basedir}/build.properties" /> - <property name="modules.to.publish.to.maven" value="shared,server,client,client-compiler,client-compiled,themes,push,widgets" /> + <property name="modules.producing.jar" value="shared,server,client,client-compiler,client-compiled,themes,push,widgets" /> + <property name="modules.to.publish.to.maven" value="${modules.producing.jar},bom" /> <property name="modules.to.publish.to.download" value="${modules.to.publish.to.maven},all" /> <ivy:settings file="${vaadin.basedir}/ivysettings.xml" /> diff --git a/ivysettings.xml b/ivysettings.xml index f1fc4d1c63..c97b6a3bfb 100644 --- a/ivysettings.xml +++ b/ivysettings.xml @@ -49,6 +49,8 @@ resolver="build-temp" /> <module organisation="com.vaadin" name="vaadin-widgets" resolver="build-temp" /> + <module organisation="com.vaadin" name="vaadin-bom" + resolver="build-temp" /> <module organisation="com.vaadin" name="vaadin-liferay" resolver="build-temp" /> </modules> diff --git a/pom-template.xml b/pom-template.xml index 4f02482ceb..46b0a518ed 100644 --- a/pom-template.xml +++ b/pom-template.xml @@ -1,6 +1,11 @@ <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>com.vaadin</groupId> + <artifactId>vaadin-parent</artifactId> + <version>1.0.0</version> + </parent> <groupId>com.vaadin</groupId> <artifactId>${module.name}</artifactId> <version>${vaadin.maven.version}</version> @@ -62,18 +67,6 @@ <timezone>2</timezone> </developer> </developers> - <distributionManagement> - <repository> - <id>vaadin-releases</id> - <name>Vaadin release repository</name> - <url>https://oss.sonatype.org/content/repositories/vaadin-releases/</url> - </repository> - <snapshotRepository> - <id>vaadin-snapshots</id> - <name>Vaadin snapshot repository</name> - <url>https://oss.sonatype.org/content/repositories/vaadin-snapshots/</url> - </snapshotRepository> - </distributionManagement> <repositories> <repository> <id>vaadin-snapshots</id> diff --git a/publish.xml b/publish.xml index 2fafb68246..3c14e86774 100644 --- a/publish.xml +++ b/publish.xml @@ -57,15 +57,33 @@ <property name="jar.file" location="result/artifacts/${vaadin.version}/vaadin-${module}/vaadin-${module}-${vaadin.version}.jar" /> <property name="pom.file" location="result/artifacts/${vaadin.version}/vaadin-${module}/vaadin-${module}-${vaadin.version}.pom" /> - <artifact:mvn failonerror="true"> - <arg value="gpg:sign-and-deploy-file" /> - <sysproperty key="file" value="${jar.file}" /> - <sysproperty key="pomFile" value="${pom.file}" /> - <sysproperty key="repositoryId" value="${maven.snapshot.repository.id}" /> - <sysproperty key="url" value="${maven.snapshot.repository.url}" /> - <sysproperty key="gpg.passphrase" value="${gpg.passphrase}" /> - <sysproperty key="retryFailedDeploymentCount" value="10" /> - </artifact:mvn> + <antcontrib:if> + <available file="${jar.file}"/> + <then> + <artifact:mvn failonerror="true"> + <arg value="gpg:sign-and-deploy-file" /> + <sysproperty key="file" value="${jar.file}" /> + <sysproperty key="pomFile" value="${pom.file}" /> + <sysproperty key="repositoryId" value="${maven.snapshot.repository.id}" /> + <sysproperty key="url" value="${maven.snapshot.repository.url}" /> + <sysproperty key="gpg.passphrase" value="${gpg.passphrase}" /> + <sysproperty key="retryFailedDeploymentCount" value="10" /> + </artifact:mvn> + </then> + <else> + <artifact:mvn failonerror="true"> + <arg value="gpg:sign-and-deploy-file" /> + <sysproperty key="file" value="${pom.file}" /> + <sysproperty key="pomFile" value="${pom.file}" /> + <sysproperty key="generatePom" value="false" /> + <sysproperty key="packaging" value="pom" /> + <sysproperty key="repositoryId" value="${maven.snapshot.repository.id}" /> + <sysproperty key="url" value="${maven.snapshot.repository.url}" /> + <sysproperty key="gpg.passphrase" value="${gpg.passphrase}" /> + <sysproperty key="retryFailedDeploymentCount" value="10" /> + </artifact:mvn> + </else> + </antcontrib:if> </target> <!-- Use this to publish to local Maven repo --> @@ -87,12 +105,26 @@ <property name="sources.file" location="result/artifacts/${vaadin.version}/vaadin-${module}/vaadin-${module}-${vaadin.version}-sources.jar" /> <property name="pom.file" location="result/artifacts/${vaadin.version}/vaadin-${module}/vaadin-${module}-${vaadin.version}.pom" /> - <artifact:mvn failonerror="true"> - <arg value="install:install-file" /> - <sysproperty key="file" value="${jar.file}" /> - <sysproperty key="pomFile" value="${pom.file}" /> - <sysproperty key="javadoc" value="${javadoc.file}" /> - <sysproperty key="sources" value="${sources.file}" /> - </artifact:mvn> + <antcontrib:if> + <available file="${jar.file}"/> + <then> + <artifact:mvn failonerror="true"> + <arg value="install:install-file" /> + <sysproperty key="file" value="${jar.file}" /> + <sysproperty key="pomFile" value="${pom.file}" /> + <sysproperty key="javadoc" value="${javadoc.file}" /> + <sysproperty key="sources" value="${sources.file}" /> + </artifact:mvn> + </then> + <else> + <artifact:mvn failonerror="true"> + <arg value="install:install-file" /> + <sysproperty key="file" value="${pom.file}" /> + <sysproperty key="pomFile" value="${pom.file}" /> + <sysproperty key="generatePom" value="false" /> + <sysproperty key="packaging" value="pom" /> + </artifact:mvn> + </else> + </antcontrib:if> </target> </project> diff --git a/server/src/com/vaadin/server/Constants.java b/server/src/com/vaadin/server/Constants.java index b7c2a1ff3e..f3cdd48d58 100644 --- a/server/src/com/vaadin/server/Constants.java +++ b/server/src/com/vaadin/server/Constants.java @@ -132,12 +132,11 @@ public interface Constants { static final String SERVLET_PARAMETER_RESOURCE_CACHE_TIME = "resourceCacheTime"; static final String SERVLET_PARAMETER_HEARTBEAT_INTERVAL = "heartbeatInterval"; static final String SERVLET_PARAMETER_CLOSE_IDLE_SESSIONS = "closeIdleSessions"; + static final String SERVLET_PARAMETER_PUSH_MODE = "pushMode"; static final String SERVLET_PARAMETER_UI_PROVIDER = "UIProvider"; static final String SERVLET_PARAMETER_LEGACY_PROPERTY_TOSTRING = "legacyPropertyToString"; static final String SERVLET_PARAMETER_SYNC_ID_CHECK = "syncIdCheck"; static final String SERVLET_PARAMETER_SENDURLSASPARAMETERS = "sendUrlsAsParameters"; - static final String SERVLET_PARAMETER_PUSH_MODE = "pushMode"; - static final String SERVLET_PARAMETER_PUSH_PATH = "pushPath"; // Configurable parameter names static final String PARAMETER_VAADIN_RESOURCES = "Resources"; diff --git a/server/src/com/vaadin/server/DefaultDeploymentConfiguration.java b/server/src/com/vaadin/server/DefaultDeploymentConfiguration.java index 5402979be8..b26e048431 100644 --- a/server/src/com/vaadin/server/DefaultDeploymentConfiguration.java +++ b/server/src/com/vaadin/server/DefaultDeploymentConfiguration.java @@ -61,13 +61,6 @@ public class DefaultDeploymentConfiguration extends public static final boolean DEFAULT_SEND_URLS_AS_PARAMETERS = true; - /** - * Default value for {@link #getPushPath()} = {@value} . - * - * @since 7.4.1 - */ - public static final String DEFAULT_PUSH_PATH = "PUSH"; - private final Properties initParameters; private boolean productionMode; private boolean xsrfProtectionEnabled; @@ -292,18 +285,6 @@ public class DefaultDeploymentConfiguration extends } /** - * {@inheritDoc} - * <p> - * The default path {@link DEFAULT_PUSH_PATH} can be changed by using init - * parameter {@link Constants.SERVLET_PARAMETER_PUSH_PATH}. - */ - @Override - public String getPushPath() { - return getApplicationOrSystemProperty( - Constants.SERVLET_PARAMETER_PUSH_PATH, DEFAULT_PUSH_PATH); - } - - /** * Log a warning if Vaadin is not running in production mode. */ private void checkProductionMode() { diff --git a/server/src/com/vaadin/server/DeploymentConfiguration.java b/server/src/com/vaadin/server/DeploymentConfiguration.java index 06556e28a7..968ec7c0c3 100644 --- a/server/src/com/vaadin/server/DeploymentConfiguration.java +++ b/server/src/com/vaadin/server/DeploymentConfiguration.java @@ -195,7 +195,7 @@ public interface DeploymentConfiguration extends Serializable { * * @since 7.4 * - * @return the name of the widgetset + * @return UI class name */ public String getWidgetset(String defaultValue); @@ -214,14 +214,6 @@ public interface DeploymentConfiguration extends Serializable { public String getClassLoaderName(); /** - * Returns the push path configuration option value. Should never be null. - * - * @since 7.4.1 - * @return the path used with server push - */ - public String getPushPath(); - - /** * Returns to legacy Property.toString() mode used. See * {@link AbstractProperty#isLegacyToStringEnabled()} for more information. * diff --git a/server/src/com/vaadin/server/ExternalResource.java b/server/src/com/vaadin/server/ExternalResource.java index 0c724ae19f..e3b026dde8 100644 --- a/server/src/com/vaadin/server/ExternalResource.java +++ b/server/src/com/vaadin/server/ExternalResource.java @@ -124,5 +124,4 @@ public class ExternalResource implements Resource, Serializable { public void setMIMEType(String mimeType) { this.mimeType = mimeType; } - } diff --git a/server/src/com/vaadin/server/FileResource.java b/server/src/com/vaadin/server/FileResource.java index b32905f972..28de124fe9 100644 --- a/server/src/com/vaadin/server/FileResource.java +++ b/server/src/com/vaadin/server/FileResource.java @@ -156,5 +156,4 @@ public class FileResource implements ConnectorResource { public void setBufferSize(int bufferSize) { this.bufferSize = bufferSize; } - } diff --git a/server/src/com/vaadin/server/ServletPortletHelper.java b/server/src/com/vaadin/server/ServletPortletHelper.java index 1f0c7f02b9..197d9fe416 100644 --- a/server/src/com/vaadin/server/ServletPortletHelper.java +++ b/server/src/com/vaadin/server/ServletPortletHelper.java @@ -124,8 +124,7 @@ public class ServletPortletHelper implements Serializable { } public static boolean isPushRequest(VaadinRequest request) { - return hasPathPrefix(request, request.getService() - .getDeploymentConfiguration().getPushPath() + '/'); + return hasPathPrefix(request, ApplicationConstants.PUSH_PATH + '/'); } public static void initDefaultUIProvider(VaadinSession session, diff --git a/server/src/com/vaadin/server/communication/UIInitHandler.java b/server/src/com/vaadin/server/communication/UIInitHandler.java index 02b4e64159..3a6dc1e55f 100644 --- a/server/src/com/vaadin/server/communication/UIInitHandler.java +++ b/server/src/com/vaadin/server/communication/UIInitHandler.java @@ -198,11 +198,10 @@ public abstract class UIInitHandler extends SynchronizedRequestHandler { PushMode pushMode = provider.getPushMode(event); if (pushMode == null) { - pushMode = session.getConfiguration().getPushMode(); + pushMode = session.getService().getDeploymentConfiguration() + .getPushMode(); } ui.getPushConfiguration().setPushMode(pushMode); - ui.getPushConfiguration().setPushPath( - session.getConfiguration().getPushPath()); Transport transport = provider.getPushTransport(event); if (transport != null) { diff --git a/server/src/com/vaadin/ui/AbstractMedia.java b/server/src/com/vaadin/ui/AbstractMedia.java index 0bd8c3ea77..a0344624d7 100644 --- a/server/src/com/vaadin/ui/AbstractMedia.java +++ b/server/src/com/vaadin/ui/AbstractMedia.java @@ -18,12 +18,17 @@ package com.vaadin.ui; import java.io.IOException; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.jsoup.nodes.Attributes; +import org.jsoup.nodes.Element; +import org.jsoup.nodes.Node; + import com.vaadin.server.ConnectorResource; import com.vaadin.server.DownloadStream; import com.vaadin.server.Resource; @@ -34,6 +39,8 @@ import com.vaadin.server.VaadinSession; import com.vaadin.shared.communication.URLReference; import com.vaadin.shared.ui.AbstractMediaState; import com.vaadin.shared.ui.MediaControl; +import com.vaadin.ui.declarative.DesignAttributeHandler; +import com.vaadin.ui.declarative.DesignContext; /** * Abstract base class for the HTML5 media components. @@ -256,4 +263,49 @@ public abstract class AbstractMedia extends AbstractComponent { getRpcProxy(MediaControl.class).play(); } + @Override + public void writeDesign(Element design, DesignContext designContext) { + super.writeDesign(design, designContext); + + String altText = getAltText(); + if (altText != null && !altText.isEmpty()) { + design.append(altText); + } + + for (Resource r : getSources()) { + Attributes attr = design.appendElement("source").attributes(); + DesignAttributeHandler.writeAttribute("href", attr, r, null, + Resource.class); + } + } + + @Override + public void readDesign(Element design, DesignContext designContext) { + super.readDesign(design, designContext); + + String altText = ""; + for (Node child : design.childNodes()) { + if (child instanceof Element + && ((Element) child).tagName().equals("source") + && child.hasAttr("href")) { + addSource(DesignAttributeHandler.readAttribute("href", + child.attributes(), Resource.class)); + } else { + altText += child.toString(); + } + } + + altText = altText.trim(); + if (!altText.isEmpty()) { + setAltText(altText); + } + } + + @Override + protected Collection<String> getCustomAttributes() { + Collection<String> result = super.getCustomAttributes(); + result.add("alt-text"); + return result; + } + } diff --git a/server/src/com/vaadin/ui/AbstractSplitPanel.java b/server/src/com/vaadin/ui/AbstractSplitPanel.java index af73fca6a8..8a7b9086c2 100644 --- a/server/src/com/vaadin/ui/AbstractSplitPanel.java +++ b/server/src/com/vaadin/ui/AbstractSplitPanel.java @@ -64,6 +64,8 @@ public abstract class AbstractSplitPanel extends AbstractComponentContainer { @Override public void setSplitterPosition(float position) { getSplitterState().position = position; + fireEvent(new SplitPositionChangeEvent(AbstractSplitPanel.this, + position, getSplitPositionUnit())); } }; @@ -331,6 +333,8 @@ public abstract class AbstractSplitPanel extends AbstractComponentContainer { splitterState.positionUnit = unit.getSymbol(); splitterState.positionReversed = reverse; posUnit = unit; + fireEvent(new SplitPositionChangeEvent(AbstractSplitPanel.this, pos, + posUnit)); } /** @@ -520,6 +524,54 @@ public abstract class AbstractSplitPanel extends AbstractComponentContainer { } + /** + * Interface for listening for {@link SplitPositionChangeEvent}s fired by a + * SplitPanel. + * + * @since + */ + public interface SplitPositionChangeListener extends ConnectorEventListener { + + public static final Method moveMethod = ReflectTools.findMethod( + SplitPositionChangeListener.class, "onSplitPositionChanged", + SplitPositionChangeEvent.class); + + /** + * SplitPanel splitter position has been changed. + * + * @param event + * SplitPositionChangeEvent event. + */ + public void onSplitPositionChanged(SplitPositionChangeEvent event); + } + + /** + * Event that indicates a change in SplitPanel's splitter position. + * + * @since + */ + public static class SplitPositionChangeEvent extends Component.Event { + + private final float position; + private final Unit unit; + + public SplitPositionChangeEvent(final Component source, + final float position, final Unit unit) { + super(source); + this.position = position; + this.unit = unit; + } + + public float getSplitPosition() { + return position; + } + + public Unit getSplitPositionUnit() { + return unit; + } + + } + public void addSplitterClickListener(SplitterClickListener listener) { addListener(EventId.CLICK_EVENT_IDENTIFIER, SplitterClickEvent.class, listener, SplitterClickListener.clickMethod); @@ -548,6 +600,31 @@ public abstract class AbstractSplitPanel extends AbstractComponentContainer { removeSplitterClickListener(listener); } + /** + * Register a listener to handle {@link SplitPositionChangeEvent}s. + * + * @since + * @param listener + * {@link SplitPositionChangeListener} to be registered. + */ + public void addSplitPositionChangeListener( + SplitPositionChangeListener listener) { + addListener(SplitPositionChangeEvent.class, listener, + SplitPositionChangeListener.moveMethod); + } + + /** + * Removes a {@link SplitPositionChangeListener}. + * + * @since + * @param listener + * SplitPositionChangeListener to be removed. + */ + public void removeSplitPositionChangeListener( + SplitPositionChangeListener listener) { + removeListener(SplitPositionChangeEvent.class, listener); + } + @Override protected AbstractSplitPanelState getState() { return (AbstractSplitPanelState) super.getState(); diff --git a/server/src/com/vaadin/ui/Calendar.java b/server/src/com/vaadin/ui/Calendar.java index 48c024026e..acddbe308b 100644 --- a/server/src/com/vaadin/ui/Calendar.java +++ b/server/src/com/vaadin/ui/Calendar.java @@ -22,6 +22,7 @@ import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Date; import java.util.EventListener; import java.util.GregorianCalendar; @@ -37,6 +38,9 @@ import java.util.TimeZone; import java.util.logging.Level; import java.util.logging.Logger; +import org.jsoup.nodes.Attributes; +import org.jsoup.nodes.Element; + import com.vaadin.data.Container; import com.vaadin.data.util.BeanItemContainer; import com.vaadin.event.Action; @@ -84,6 +88,8 @@ import com.vaadin.ui.components.calendar.handler.BasicEventMoveHandler; import com.vaadin.ui.components.calendar.handler.BasicEventResizeHandler; import com.vaadin.ui.components.calendar.handler.BasicForwardHandler; import com.vaadin.ui.components.calendar.handler.BasicWeekClickHandler; +import com.vaadin.ui.declarative.DesignAttributeHandler; +import com.vaadin.ui.declarative.DesignContext; /** * <p> @@ -334,6 +340,10 @@ public class Calendar extends AbstractComponent implements */ public Date getStartDate() { if (startDate == null) { + currentCalendar.set(java.util.Calendar.MILLISECOND, 0); + currentCalendar.set(java.util.Calendar.SECOND, 0); + currentCalendar.set(java.util.Calendar.MINUTE, 0); + currentCalendar.set(java.util.Calendar.HOUR_OF_DAY, 0); currentCalendar.set(java.util.Calendar.DAY_OF_WEEK, currentCalendar.getFirstDayOfWeek()); return currentCalendar.getTime(); @@ -363,6 +373,10 @@ public class Calendar extends AbstractComponent implements */ public Date getEndDate() { if (endDate == null) { + currentCalendar.set(java.util.Calendar.MILLISECOND, 0); + currentCalendar.set(java.util.Calendar.SECOND, 59); + currentCalendar.set(java.util.Calendar.MINUTE, 59); + currentCalendar.set(java.util.Calendar.HOUR_OF_DAY, 23); currentCalendar.set(java.util.Calendar.DAY_OF_WEEK, currentCalendar.getFirstDayOfWeek() + 6); return currentCalendar.getTime(); @@ -655,8 +669,14 @@ public class Calendar extends AbstractComponent implements */ public TimeFormat getTimeFormat() { if (currentTimeFormat == null) { - SimpleDateFormat f = (SimpleDateFormat) SimpleDateFormat - .getTimeInstance(SimpleDateFormat.SHORT, getLocale()); + SimpleDateFormat f; + if (getLocale() == null) { + f = (SimpleDateFormat) SimpleDateFormat + .getTimeInstance(SimpleDateFormat.SHORT); + } else { + f = (SimpleDateFormat) SimpleDateFormat.getTimeInstance( + SimpleDateFormat.SHORT, getLocale()); + } String p = f.toPattern(); if (p.indexOf("HH") != -1 || p.indexOf("H") != -1) { return TimeFormat.Format24H; @@ -1925,4 +1945,51 @@ public class Calendar extends AbstractComponent implements return getState(false).eventCaptionAsHtml; } + @Override + public void readDesign(Element design, DesignContext designContext) { + super.readDesign(design, designContext); + + Attributes attr = design.attributes(); + if (design.hasAttr("time-format")) { + setTimeFormat(TimeFormat.valueOf("Format" + + design.attr("time-format").toUpperCase())); + } + + if (design.hasAttr("start-date")) { + setStartDate(DesignAttributeHandler.readAttribute("start-date", + attr, Date.class)); + } + if (design.hasAttr("end-date")) { + setEndDate(DesignAttributeHandler.readAttribute("end-date", attr, + Date.class)); + } + }; + + @Override + public void writeDesign(Element design, DesignContext designContext) { + super.writeDesign(design, designContext); + + if (currentTimeFormat != null) { + design.attr("time-format", + (currentTimeFormat == TimeFormat.Format12H ? "12h" : "24h")); + } + if (startDate != null) { + design.attr("start-date", df_date.format(getStartDate())); + } + if (endDate != null) { + design.attr("end-date", df_date.format(getEndDate())); + } + if (!getTimeZone().equals(TimeZone.getDefault())) { + design.attr("time-zone", getTimeZone().getID()); + } + } + + @Override + protected Collection<String> getCustomAttributes() { + Collection<String> customAttributes = super.getCustomAttributes(); + customAttributes.add("time-format"); + customAttributes.add("start-date"); + customAttributes.add("end-date"); + return customAttributes; + } } diff --git a/server/src/com/vaadin/ui/CustomLayout.java b/server/src/com/vaadin/ui/CustomLayout.java index a9c266b0b9..ceb47e1e7a 100644 --- a/server/src/com/vaadin/ui/CustomLayout.java +++ b/server/src/com/vaadin/ui/CustomLayout.java @@ -23,12 +23,16 @@ import java.io.InputStreamReader; import java.util.HashMap; import java.util.Iterator; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; +import org.jsoup.nodes.Element; + import com.vaadin.server.JsonPaintTarget; import com.vaadin.server.PaintException; import com.vaadin.server.PaintTarget; import com.vaadin.shared.ui.customlayout.CustomLayoutState; +import com.vaadin.ui.declarative.DesignContext; /** * <p> @@ -71,8 +75,8 @@ public class CustomLayout extends AbstractLayout implements LegacyComponent { * {@link #setTemplateName(String)}, that makes layout fetch the template * from theme, or {@link #setTemplateContents(String)}. */ - protected CustomLayout() { - setWidth(100, UNITS_PERCENTAGE); + public CustomLayout() { + setWidth(100, Unit.PERCENTAGE); } /** @@ -305,4 +309,32 @@ public class CustomLayout extends AbstractLayout implements LegacyComponent { } } + @Override + public void readDesign(Element design, DesignContext designContext) { + super.readDesign(design, designContext); + + for (Element child : design.children()) { + + Component childComponent = designContext.readDesign(child); + + if (child.hasAttr(":location")) { + addComponent(childComponent, child.attr(":location")); + } else { + addComponent(childComponent); + } + } + } + + @Override + public void writeDesign(Element design, DesignContext designContext) { + super.writeDesign(design, designContext); + + for (Entry<String, Component> slot : slots.entrySet()) { + Element child = designContext.createElement(slot.getValue()); + if (slots.size() > 1 || !"".equals(slot.getKey())) { + child.attr(":location", slot.getKey()); + } + design.appendChild(child); + } + } } diff --git a/server/src/com/vaadin/ui/DragAndDropWrapper.java b/server/src/com/vaadin/ui/DragAndDropWrapper.java index 6e4ec903d2..b813973861 100644 --- a/server/src/com/vaadin/ui/DragAndDropWrapper.java +++ b/server/src/com/vaadin/ui/DragAndDropWrapper.java @@ -24,6 +24,8 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import org.jsoup.nodes.Element; + import com.vaadin.event.Transferable; import com.vaadin.event.TransferableImpl; import com.vaadin.event.dd.DragSource; @@ -38,6 +40,7 @@ import com.vaadin.shared.MouseEventDetails; import com.vaadin.shared.ui.dd.HorizontalDropLocation; import com.vaadin.shared.ui.dd.VerticalDropLocation; import com.vaadin.shared.ui.draganddropwrapper.DragAndDropWrapperConstants; +import com.vaadin.ui.declarative.DesignContext; @SuppressWarnings("serial") public class DragAndDropWrapper extends CustomComponent implements DropTarget, @@ -185,7 +188,12 @@ public class DragAndDropWrapper extends CustomComponent implements DropTarget, private Set<String> sentIds = new HashSet<String>(); - private DragAndDropWrapper() { + /** + * This is an internal constructor. Use + * {@link DragAndDropWrapper#DragAndDropWrapper(Component)} instead. + */ + @Deprecated + public DragAndDropWrapper() { super(); } @@ -458,4 +466,31 @@ public class DragAndDropWrapper extends CustomComponent implements DropTarget, } + @Override + public void readDesign(Element design, DesignContext designContext) { + super.readDesign(design, designContext); + + for (Element child : design.children()) { + Component component = designContext.readDesign(child); + if (getDragStartMode() == DragStartMode.COMPONENT_OTHER + && child.hasAttr(":drag-image")) { + setDragImageComponent(component); + } else if (getCompositionRoot() == null) { + setCompositionRoot(component); + } + } + } + + @Override + public void writeDesign(Element design, DesignContext designContext) { + super.writeDesign(design, designContext); + + design.appendChild(designContext.createElement(getCompositionRoot())); + if (getDragStartMode() == DragStartMode.COMPONENT_OTHER) { + Element child = designContext + .createElement(getDragImageComponent()); + child.attr(":drag-image", ""); + design.appendChild(child); + } + } } diff --git a/server/src/com/vaadin/ui/MenuBar.java b/server/src/com/vaadin/ui/MenuBar.java index 6b6555c0a2..747ce42727 100644 --- a/server/src/com/vaadin/ui/MenuBar.java +++ b/server/src/com/vaadin/ui/MenuBar.java @@ -17,17 +17,25 @@ package com.vaadin.ui; import java.io.Serializable; import java.util.ArrayList; +import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Stack; +import org.jsoup.nodes.Attributes; +import org.jsoup.nodes.Element; +import org.jsoup.nodes.Node; +import org.jsoup.parser.Tag; + import com.vaadin.server.PaintException; import com.vaadin.server.PaintTarget; import com.vaadin.server.Resource; import com.vaadin.shared.ui.menubar.MenuBarConstants; import com.vaadin.shared.ui.menubar.MenuBarState; import com.vaadin.ui.Component.Focusable; +import com.vaadin.ui.declarative.DesignAttributeHandler; +import com.vaadin.ui.declarative.DesignContext; /** * <p> @@ -932,7 +940,150 @@ public class MenuBar extends AbstractComponent implements LegacyComponent, this.checked = checked; markAsDirty(); } - }// class MenuItem + @Override + public void writeDesign(Element design, DesignContext designContext) { + super.writeDesign(design, designContext); + for (MenuItem item : getItems()) { + design.appendChild(createMenuElement(item)); + } + + // in many cases there seems to be an empty more menu item + if (getMoreMenuItem() != null && !getMoreMenuItem().getText().isEmpty()) { + Element moreMenu = createMenuElement(getMoreMenuItem()); + moreMenu.attr("more", ""); + design.appendChild(moreMenu); + } + + if (!htmlContentAllowed) { + design.attr(DESIGN_ATTR_PLAIN_TEXT, ""); + } + } + + protected Element createMenuElement(MenuItem item) { + Element menuElement = new Element(Tag.valueOf("menu"), ""); + // Defaults + MenuItem def = new MenuItem("", null, null); + + Attributes attr = menuElement.attributes(); + DesignAttributeHandler.writeAttribute("icon", attr, item.getIcon(), + def.getIcon(), Resource.class); + DesignAttributeHandler.writeAttribute("disabled", attr, + !item.isEnabled(), !def.isEnabled(), boolean.class); + DesignAttributeHandler.writeAttribute("visible", attr, + item.isVisible(), def.isVisible(), boolean.class); + DesignAttributeHandler.writeAttribute("separator", attr, + item.isSeparator(), def.isSeparator(), boolean.class); + DesignAttributeHandler.writeAttribute("checkable", attr, + item.isCheckable(), def.isCheckable(), boolean.class); + DesignAttributeHandler.writeAttribute("checked", attr, + item.isChecked(), def.isChecked(), boolean.class); + DesignAttributeHandler.writeAttribute("description", attr, + item.getDescription(), def.getDescription(), String.class); + DesignAttributeHandler.writeAttribute("style-name", attr, + item.getStyleName(), def.getStyleName(), String.class); + + menuElement.append(item.getText()); + + if (item.hasChildren()) { + for (MenuItem subMenu : item.getChildren()) { + menuElement.appendChild(createMenuElement(subMenu)); + } + } + + return menuElement; + } + + protected MenuItem readMenuElement(Element menuElement) { + Resource icon = null; + if (menuElement.hasAttr("icon")) { + icon = DesignAttributeHandler.getFormatter().parse( + menuElement.attr("icon"), Resource.class); + } + + String caption = ""; + List<Element> subMenus = new ArrayList<Element>(); + for (Node node : menuElement.childNodes()) { + if (node instanceof Element + && ((Element) node).tagName().equals("menu")) { + subMenus.add((Element) node); + } + caption += node.toString(); + } + MenuItem menu = new MenuItem(caption.trim(), icon, null); + + Attributes attr = menuElement.attributes(); + if (menuElement.hasAttr("icon")) { + menu.setIcon(DesignAttributeHandler.readAttribute("icon", attr, + Resource.class)); + } + if (menuElement.hasAttr("disabled")) { + menu.setEnabled(!DesignAttributeHandler.readAttribute("disabled", + attr, boolean.class)); + } + if (menuElement.hasAttr("visible")) { + menu.setVisible(DesignAttributeHandler.readAttribute("visible", + attr, boolean.class)); + } + if (menuElement.hasAttr("separator")) { + menu.setSeparator(DesignAttributeHandler.readAttribute("separator", + attr, boolean.class)); + } + if (menuElement.hasAttr("checkable")) { + menu.setCheckable(DesignAttributeHandler.readAttribute("checkable", + attr, boolean.class)); + } + if (menuElement.hasAttr("checked")) { + menu.setChecked(DesignAttributeHandler.readAttribute("checked", + attr, boolean.class)); + } + if (menuElement.hasAttr("description")) { + menu.setDescription(DesignAttributeHandler.readAttribute( + "description", attr, String.class)); + } + if (menuElement.hasAttr("style-name")) { + menu.setStyleName(DesignAttributeHandler.readAttribute( + "style-name", attr, String.class)); + } + + if (!subMenus.isEmpty()) { + menu.itsChildren = new ArrayList<MenuItem>(); + } + + for (Element subMenu : subMenus) { + MenuItem newItem = readMenuElement(subMenu); + + newItem.setParent(menu); + menu.itsChildren.add(newItem); + } + + return menu; + } + + @Override + public void readDesign(Element design, DesignContext designContext) { + super.readDesign(design, designContext); + + for (Element itemElement : design.children()) { + if (itemElement.tagName().equals("menu")) { + MenuItem menuItem = readMenuElement(itemElement); + if (itemElement.hasAttr("more")) { + setMoreMenuItem(menuItem); + } else { + menuItems.add(menuItem); + } + } + } + + setHtmlContentAllowed(!design.hasAttr(DESIGN_ATTR_PLAIN_TEXT)); + } + + @Override + protected Collection<String> getCustomAttributes() { + Collection<String> result = super.getCustomAttributes(); + result.add(DESIGN_ATTR_PLAIN_TEXT); + result.add("html-content-allowed"); + return result; + } }// class MenuBar diff --git a/server/src/com/vaadin/ui/PushConfiguration.java b/server/src/com/vaadin/ui/PushConfiguration.java index d5e89b4b14..90ad28542c 100644 --- a/server/src/com/vaadin/ui/PushConfiguration.java +++ b/server/src/com/vaadin/ui/PushConfiguration.java @@ -105,26 +105,6 @@ public interface PushConfiguration extends Serializable { public void setFallbackTransport(Transport fallbackTransport); /** - * Sets the path that is used with push. - * - * @since 7.4.1 - * @param pushPath - * The path to be used with push - * - * @throws IllegalArgumentException - * if the argument is null or empty. - */ - public void setPushPath(String pushPath); - - /** - * Returns the path used with push. - * - * @since 7.4.1 - * @return The path that is used with push - */ - public String getPushPath(); - - /** * Returns the given parameter, if set. * <p> * This method provides low level access to push parameters and is typically @@ -278,32 +258,6 @@ class PushConfigurationImpl implements PushConfiguration { /* * (non-Javadoc) * - * @see com.vaadin.ui.PushConfiguration#setPushPath(java.lang.String) - */ - @Override - public void setPushPath(String pushPath) { - if (pushPath != null && !pushPath.isEmpty()) { - getState().pushPath = pushPath; - } else { - throw new IllegalArgumentException( - "Push path can't be empty or null"); - } - - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.ui.PushConfiguration#getPushPath() - */ - @Override - public String getPushPath() { - return getState(false).pushPath; - } - - /* - * (non-Javadoc) - * * @see com.vaadin.ui.PushConfiguration#getParameter(java.lang.String) */ @Override @@ -336,4 +290,5 @@ class PushConfigurationImpl implements PushConfiguration { return Collections.unmodifiableCollection(getState(false).parameters .keySet()); } + } diff --git a/server/src/com/vaadin/ui/Slider.java b/server/src/com/vaadin/ui/Slider.java index 99e1e8d5e9..40a4047d53 100644 --- a/server/src/com/vaadin/ui/Slider.java +++ b/server/src/com/vaadin/ui/Slider.java @@ -16,9 +16,16 @@ package com.vaadin.ui; +import java.util.Collection; + +import org.jsoup.nodes.Attributes; +import org.jsoup.nodes.Element; + import com.vaadin.shared.ui.slider.SliderOrientation; import com.vaadin.shared.ui.slider.SliderServerRpc; import com.vaadin.shared.ui.slider.SliderState; +import com.vaadin.ui.declarative.DesignAttributeHandler; +import com.vaadin.ui.declarative.DesignContext; /** * A component for selecting a numerical value within a range. @@ -353,4 +360,35 @@ public class Slider extends AbstractField<Double> { // Slider is never really "empty" return false; } + + @Override + public void readDesign(Element design, DesignContext context) { + super.readDesign(design, context); + Attributes attr = design.attributes(); + if (attr.hasKey("vertical")) { + setOrientation(SliderOrientation.VERTICAL); + } + if (!attr.get("value").isEmpty()) { + setValue(DesignAttributeHandler.readAttribute("value", attr, + Double.class)); + } + } + + @Override + public void writeDesign(Element design, DesignContext context) { + super.writeDesign(design, context); + if (getOrientation() == SliderOrientation.VERTICAL) { + design.attr("vertical", ""); + } + Slider defaultSlider = context.getDefaultInstance(this); + DesignAttributeHandler.writeAttribute(this, "value", + design.attributes(), defaultSlider); + } + + @Override + protected Collection<String> getCustomAttributes() { + Collection<String> result = super.getCustomAttributes(); + result.add("orientation"); + return result; + } } diff --git a/server/src/com/vaadin/ui/Video.java b/server/src/com/vaadin/ui/Video.java index e690218e6f..46a4293b36 100644 --- a/server/src/com/vaadin/ui/Video.java +++ b/server/src/com/vaadin/ui/Video.java @@ -16,9 +16,17 @@ package com.vaadin.ui; +import java.util.Collection; + +import org.jsoup.nodes.Attributes; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; + import com.vaadin.server.Resource; import com.vaadin.shared.ui.video.VideoConstants; import com.vaadin.shared.ui.video.VideoState; +import com.vaadin.ui.declarative.DesignAttributeHandler; +import com.vaadin.ui.declarative.DesignContext; /** * The Video component translates into an HTML5 <video> element and as @@ -89,4 +97,35 @@ public class Video extends AbstractMedia { return getResource(VideoConstants.POSTER_RESOURCE); } + @Override + public void readDesign(Element design, DesignContext designContext) { + Elements elems = design.getElementsByTag("poster"); + for (Element poster : elems) { + if (getPoster() == null && poster.hasAttr("href")) { + setPoster(DesignAttributeHandler.readAttribute("href", + poster.attributes(), Resource.class)); + } + poster.remove(); + } + + // Poster is extracted so AbstractMedia does not include it in alt text + super.readDesign(design, designContext); + } + + @Override + public void writeDesign(Element design, DesignContext designContext) { + super.writeDesign(design, designContext); + if (getPoster() != null) { + Attributes attr = design.appendElement("poster").attributes(); + DesignAttributeHandler.writeAttribute("href", attr, getPoster(), + null, Resource.class); + } + } + + @Override + protected Collection<String> getCustomAttributes() { + Collection<String> result = super.getCustomAttributes(); + result.add("poster"); + return result; + } } diff --git a/server/src/com/vaadin/ui/declarative/Design.java b/server/src/com/vaadin/ui/declarative/Design.java index 1b8585e6f6..6634e5248a 100644 --- a/server/src/com/vaadin/ui/declarative/Design.java +++ b/server/src/com/vaadin/ui/declarative/Design.java @@ -33,6 +33,7 @@ import org.jsoup.parser.Parser; import org.jsoup.select.Elements; import com.vaadin.annotations.DesignRoot; +import com.vaadin.shared.util.SharedUtil; import com.vaadin.ui.Component; import com.vaadin.ui.declarative.DesignContext.ComponentCreatedEvent; import com.vaadin.ui.declarative.DesignContext.ComponentCreationListener; @@ -68,7 +69,6 @@ public class Design implements Serializable { * Use {@link Design#setComponentFactory(ComponentFactory)} to configure * Vaadin to use a custom component factory. * - * * @since 7.4.1 */ public interface ComponentFactory extends Serializable { @@ -88,6 +88,50 @@ public class Design implements Serializable { } /** + * Delegate for handling the mapping between tag names and component + * instances. + * <p> + * Use {@link Design#setComponentMapper(ComponentMapper)} to configure + * Vaadin to use a custom component mapper. + * + * @since + * @author Vaadin Ltd + */ + public interface ComponentMapper extends Serializable { + /** + * Resolves and creates a component using the provided component factory + * based on a tag name. + * <p> + * This method should be in sync with + * {@link #componentToTag(Component, DesignContext)} so that the + * resolved tag for a created component is the same as the tag for which + * the component was created. + * + * @param tag + * the tag name to create a component for + * @param componentFactory + * the component factory that actually creates a component + * based on a fully qualified class name + * @param context + * the design context for which the component is created + * @return a newly created component + */ + public Component tagToComponent(String tag, + ComponentFactory componentFactory, DesignContext context); + + /** + * Resolves a tag name from a component. + * + * @param component + * the component to get a tag name for + * @param context + * the design context for which the tag name is needed + * @return the tag name corresponding to the component + */ + public String componentToTag(Component component, DesignContext context); + } + + /** * Default implementation of {@link ComponentFactory}, using * <code>Class.forName(className).newInstance()</code> for finding the * component class and creating a component instance. @@ -135,7 +179,100 @@ public class Design implements Serializable { } + /** + * Default implementation of {@link ComponentMapper}, + * + * @since + */ + public static class DefaultComponentMapper implements ComponentMapper { + + @Override + public Component tagToComponent(String tagName, + ComponentFactory componentFactory, DesignContext context) { + // Extract the package and class names. + // Otherwise, get the full class name using the prefix to package + // mapping. Example: "v-vertical-layout" -> + // "com.vaadin.ui.VerticalLayout" + String[] parts = tagName.split("-", 2); + if (parts.length < 2) { + throw new DesignException("The tagname '" + tagName + + "' is invalid: missing prefix."); + } + String prefixName = parts[0]; + String packageName = context.getPackage(prefixName); + if (packageName == null) { + throw new DesignException("Unknown tag: " + tagName); + } + String[] classNameParts = parts[1].split("-"); + String className = ""; + for (String classNamePart : classNameParts) { + // Split will ignore trailing and multiple dashes but that + // should be + // ok + // <v-button--> will be resolved to <v-button> + // <v--button> will be resolved to <v-button> + className += SharedUtil.capitalize(classNamePart); + } + String qualifiedClassName = packageName + "." + className; + + Component component = componentFactory.createComponent( + qualifiedClassName, context); + + if (component == null) { + throw new DesignException("Got unexpected null component from " + + componentFactory.getClass().getName() + " for class " + + qualifiedClassName); + } + + return component; + } + + @Override + public String componentToTag(Component component, DesignContext context) { + Class<?> componentClass = component.getClass(); + String packageName = componentClass.getPackage().getName(); + String prefix = context.getPackagePrefix(packageName); + if (prefix == null) { + prefix = packageName.replace('.', '_'); + context.addPackagePrefix(prefix, packageName); + } + prefix = prefix + "-"; + String className = classNameToElementName(componentClass + .getSimpleName()); + String tagName = prefix + className; + + return tagName; + } + + /** + * Creates the name of the html tag corresponding to the given class + * name. The name is derived by converting each uppercase letter to + * lowercase and inserting a dash before the letter. No dash is inserted + * before the first letter of the class name. + * + * @param className + * the name of the class without a package name + * @return the html tag name corresponding to className + */ + private String classNameToElementName(String className) { + StringBuilder result = new StringBuilder(); + for (int i = 0; i < className.length(); i++) { + Character c = className.charAt(i); + if (Character.isUpperCase(c)) { + if (i > 0) { + result.append("-"); + } + result.append(Character.toLowerCase(c)); + } else { + result.append(c); + } + } + return result.toString(); + } + } + private static volatile ComponentFactory componentFactory = new DefaultComponentFactory(); + private static volatile ComponentMapper componentMapper = new DefaultComponentMapper(); /** * Sets the component factory that is used for creating component instances @@ -171,6 +308,39 @@ public class Design implements Serializable { } /** + * Sets the component mapper that is used for resolving between tag names + * and component instances. + * <p> + * Please note that this setting is global, so care should be taken to avoid + * conflicting changes. + * + * @param componentMapper + * the component mapper to set; not <code>null</code> + * + * @since + */ + public static void setComponentMapper(ComponentMapper componentMapper) { + if (componentMapper == null) { + throw new IllegalArgumentException( + "Cannot set null component mapper"); + } + Design.componentMapper = componentMapper; + } + + /** + * Gets the currently used component mapper. + * + * @see #setComponentMapper(ComponentMapper) + * + * @return the component mapper + * + * @since + */ + public static ComponentMapper getComponentMapper() { + return componentMapper; + } + + /** * Parses the given input stream into a jsoup document * * @param html diff --git a/server/src/com/vaadin/ui/declarative/DesignAttributeHandler.java b/server/src/com/vaadin/ui/declarative/DesignAttributeHandler.java index 215afd5041..2b446bda0e 100644 --- a/server/src/com/vaadin/ui/declarative/DesignAttributeHandler.java +++ b/server/src/com/vaadin/ui/declarative/DesignAttributeHandler.java @@ -38,7 +38,6 @@ import org.jsoup.nodes.Node; import com.vaadin.data.util.converter.Converter; import com.vaadin.shared.util.SharedUtil; -import com.vaadin.ui.Component; /** * Default attribute handler implementation used when parsing designs to @@ -192,8 +191,8 @@ public class DesignAttributeHandler implements Serializable { * @param defaultInstance * the default instance for comparing default values */ - public static void writeAttribute(Component component, String attribute, - Attributes attr, Component defaultInstance) { + public static void writeAttribute(Object component, String attribute, + Attributes attr, Object defaultInstance) { Method getter = findGetterForAttribute(component.getClass(), attribute); if (getter == null) { getLogger().warning( diff --git a/server/src/com/vaadin/ui/declarative/DesignContext.java b/server/src/com/vaadin/ui/declarative/DesignContext.java index 218774c72d..f991b3013a 100644 --- a/server/src/com/vaadin/ui/declarative/DesignContext.java +++ b/server/src/com/vaadin/ui/declarative/DesignContext.java @@ -17,6 +17,8 @@ package com.vaadin.ui.declarative; import java.io.Serializable; import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -28,10 +30,10 @@ import org.jsoup.nodes.Element; import org.jsoup.nodes.Node; import com.vaadin.annotations.DesignRoot; -import com.vaadin.shared.util.SharedUtil; import com.vaadin.ui.Component; import com.vaadin.ui.HasComponents; import com.vaadin.ui.declarative.Design.ComponentFactory; +import com.vaadin.ui.declarative.Design.ComponentMapper; /** * This class contains contextual information that is collected when a component @@ -79,7 +81,7 @@ public class DesignContext implements Serializable { defaultPrefixes.put("v", "com.vaadin.ui"); for (String prefix : defaultPrefixes.keySet()) { String packageName = defaultPrefixes.get(prefix); - mapPrefixToPackage(prefix, packageName); + addPackagePrefix(prefix, packageName); } } @@ -236,20 +238,65 @@ public class DesignContext implements Serializable { } /** - * Creates a two-way mapping between a prefix and a package name. Return - * true if prefix was already mapped to some package name or packageName to - * some prefix. + * Creates a two-way mapping between a prefix and a package name. * * @param prefix * the prefix name without an ending dash (for instance, "v" is - * always used for "com.vaadin.ui") + * by default used for "com.vaadin.ui") * @param packageName * the name of the package corresponding to prefix - * @return whether there was a mapping from prefix to some package name or - * from packageName to some prefix. + * + * @see #getPackagePrefixes() + * @see #getPackagePrefix(String) + * @see #getPackage(String) + * @since + */ + public void addPackagePrefix(String prefix, String packageName) { + twoWayMap(prefix, packageName, prefixToPackage, packageToPrefix); + } + + /** + * Gets the prefix mapping for a given package, or <code>null</code> if + * there is no mapping for the package. + * + * @see #addPackagePrefix(String, String) + * @see #getPackagePrefixes() + * + * @since + * @param packageName + * the package name to get a prefix for + * @return the prefix for the package, or <code>null</code> if no prefix is + * registered + */ + public String getPackagePrefix(String packageName) { + return packageToPrefix.get(packageName); + } + + /** + * Gets all registered package prefixes. + * + * + * @since + * @see #getPackage(String) + * @return a collection of package prefixes */ - private boolean mapPrefixToPackage(String prefix, String packageName) { - return twoWayMap(prefix, packageName, prefixToPackage, packageToPrefix); + public Collection<String> getPackagePrefixes() { + return Collections.unmodifiableCollection(prefixToPackage.keySet()); + } + + /** + * Gets the package corresponding to the give prefix, or <code>null</code> + * no package has been registered for the prefix + * + * @since + * @see #addPackagePrefix(String, String) + * @param prefix + * the prefix to find a package for + * @return the package prefix, or <code>null</code> if no package is + * registered for the provided prefix + */ + public String getPackage(String prefix) { + return prefixToPackage.get(prefix); } /** @@ -309,8 +356,7 @@ public class DesignContext implements Serializable { } String prefixName = parts[0]; String packageName = parts[1]; - twoWayMap(prefixName, packageName, prefixToPackage, - packageToPrefix); + addPackagePrefix(prefixName, packageName); } } } @@ -328,14 +374,13 @@ public class DesignContext implements Serializable { */ public void writePackageMappings(Document doc) { Element head = doc.head(); - for (String prefix : prefixToPackage.keySet()) { + for (String prefix : getPackagePrefixes()) { // Only store the prefix-name mapping if it is not a default mapping // (such as "v" -> "com.vaadin.ui") if (defaultPrefixes.get(prefix) == null) { Node newNode = doc.createElement("meta"); newNode.attr("name", "package-mapping"); - String prefixToPackageName = prefix + ":" - + prefixToPackage.get(prefix); + String prefixToPackageName = prefix + ":" + getPackage(prefix); newNode.attr("content", prefixToPackageName); head.appendChild(newNode); } @@ -355,17 +400,11 @@ public class DesignContext implements Serializable { * childComponent. */ public Element createElement(Component childComponent) { - Class<?> componentClass = childComponent.getClass(); - String packageName = componentClass.getPackage().getName(); - String prefix = packageToPrefix.get(packageName); - if (prefix == null) { - prefix = packageName.replace('.', '_'); - twoWayMap(prefix, packageName, prefixToPackage, packageToPrefix); - } - prefix = prefix + "-"; - String className = classNameToElementName(componentClass - .getSimpleName()); - Element newElement = doc.createElement(prefix + className); + ComponentMapper componentMapper = Design.getComponentMapper(); + + String tagName = componentMapper.componentToTag(childComponent, this); + + Element newElement = doc.createElement(tagName); childComponent.writeDesign(newElement, this); // Handle the local id. Global id and caption should have been taken // care of by writeDesign. @@ -377,32 +416,6 @@ public class DesignContext implements Serializable { } /** - * Creates the name of the html tag corresponding to the given class name. - * The name is derived by converting each uppercase letter to lowercase and - * inserting a dash before the letter. No dash is inserted before the first - * letter of the class name. - * - * @param className - * the name of the class without a package name - * @return the html tag name corresponding to className - */ - private String classNameToElementName(String className) { - StringBuilder result = new StringBuilder(); - for (int i = 0; i < className.length(); i++) { - Character c = className.charAt(i); - if (Character.isUpperCase(c)) { - if (i > 0) { - result.append("-"); - } - result.append(Character.toLowerCase(c)); - } else { - result.append(c); - } - } - return result.toString(); - } - - /** * Reads the given design node and creates the corresponding component tree * * @param componentDesign @@ -473,15 +486,22 @@ public class DesignContext implements Serializable { * @return a Component corresponding to node, with no attributes set. */ private Component instantiateComponent(Node node) { - // Extract the package and class names. - String qualifiedClassName = tagNameToClassName(node); + String tag = node.nodeName(); + + ComponentMapper componentMapper = Design.getComponentMapper(); + Component component = componentMapper.tagToComponent(tag, + Design.getComponentFactory(), this); - return instantiateClass(qualifiedClassName); + assert tag.equals(componentMapper.componentToTag(component, this)); + + return component; } /** * Instantiates given class via ComponentFactory. - * @param qualifiedClassName class name to instantiate + * + * @param qualifiedClassName + * class name to instantiate * @return instance of a given class */ private Component instantiateClass(String qualifiedClassName) { @@ -498,44 +518,6 @@ public class DesignContext implements Serializable { } /** - * Returns the qualified class name corresponding to the given html tree - * node. The class name is extracted from the tag name of node. - * - * @param node - * an html tree node - * @return The qualified class name corresponding to the given node. - */ - private String tagNameToClassName(Node node) { - String tagName = node.nodeName(); - if (tagName.equals("v-addon")) { - return node.attr("class"); - } - // Otherwise, get the full class name using the prefix to package - // mapping. Example: "v-vertical-layout" -> - // "com.vaadin.ui.VerticalLayout" - String[] parts = tagName.split("-", 2); - if (parts.length < 2) { - throw new DesignException("The tagname '" + tagName - + "' is invalid: missing prefix."); - } - String prefixName = parts[0]; - String packageName = prefixToPackage.get(prefixName); - if (packageName == null) { - throw new DesignException("Unknown tag: " + tagName); - } - String[] classNameParts = parts[1].split("-"); - String className = ""; - for (String classNamePart : classNameParts) { - // Split will ignore trailing and multiple dashes but that should be - // ok - // <v-button--> will be resolved to <v-button> - // <v--button> will be resolved to <v-button> - className += SharedUtil.capitalize(classNamePart); - } - return packageName + "." + className; - } - - /** * Returns the root component of a created component hierarchy. * * @return the root component of the hierarchy diff --git a/server/src/com/vaadin/ui/declarative/converters/DesignResourceConverter.java b/server/src/com/vaadin/ui/declarative/converters/DesignResourceConverter.java index 70e46b8e7f..21f20e6403 100644 --- a/server/src/com/vaadin/ui/declarative/converters/DesignResourceConverter.java +++ b/server/src/com/vaadin/ui/declarative/converters/DesignResourceConverter.java @@ -39,7 +39,8 @@ public class DesignResourceConverter implements Converter<String, Resource> { public Resource convertToModel(String value, Class<? extends Resource> targetType, Locale locale) throws Converter.ConversionException { - if (value.startsWith("http://")) { + if (value.startsWith("http://") || value.startsWith("https://") + || value.startsWith("ftp://") || value.startsWith("ftps://")) { return new ExternalResource(value); } else if (value.startsWith("theme://")) { return new ThemeResource(value.substring(8)); diff --git a/server/tests/src/com/vaadin/server/AbstractDeploymentConfigurationTest.java b/server/tests/src/com/vaadin/server/AbstractDeploymentConfigurationTest.java index ccdbfea150..0518bea650 100644 --- a/server/tests/src/com/vaadin/server/AbstractDeploymentConfigurationTest.java +++ b/server/tests/src/com/vaadin/server/AbstractDeploymentConfigurationTest.java @@ -158,9 +158,5 @@ public class AbstractDeploymentConfigurationTest { return DefaultDeploymentConfiguration.DEFAULT_SEND_URLS_AS_PARAMETERS; } - @Override - public String getPushPath() { - return null; - } } } diff --git a/server/tests/src/com/vaadin/tests/components/draganddropwrapper/DragAndDropWrapperDeclarativeTest.java b/server/tests/src/com/vaadin/tests/components/draganddropwrapper/DragAndDropWrapperDeclarativeTest.java new file mode 100644 index 0000000000..735216c474 --- /dev/null +++ b/server/tests/src/com/vaadin/tests/components/draganddropwrapper/DragAndDropWrapperDeclarativeTest.java @@ -0,0 +1,66 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.components.draganddropwrapper; + +import org.junit.Test; + +import com.vaadin.tests.design.DeclarativeTestBase; +import com.vaadin.ui.Button; +import com.vaadin.ui.DragAndDropWrapper; +import com.vaadin.ui.DragAndDropWrapper.DragStartMode; +import com.vaadin.ui.declarative.DesignContext; + +public class DragAndDropWrapperDeclarativeTest extends + DeclarativeTestBase<DragAndDropWrapper> { + + @Test + public void testDefaultDnDWrapper() { + Button okButton = new Button("OK"); + String input = "<v-drag-and-drop-wrapper>" + + new DesignContext().createElement(okButton) + + "</v-drag-and-drop-wrapper>"; + DragAndDropWrapper wrapper = new DragAndDropWrapper(okButton); + testWrite(input, wrapper); + testRead(input, wrapper); + } + + @Test + public void testNoDragImage() { + Button okButton = new Button("OK"); + String input = "<v-drag-and-drop-wrapper drag-start-mode='wrapper'>" + + new DesignContext().createElement(okButton) + + "</v-drag-and-drop-wrapper>"; + DragAndDropWrapper wrapper = new DragAndDropWrapper(okButton); + wrapper.setDragStartMode(DragStartMode.WRAPPER); + testWrite(input, wrapper); + testRead(input, wrapper); + } + + @Test + public void testWithDragImage() { + Button dragImage = new Button("Cancel"); + Button okButton = new Button("OK"); + String input = "<v-drag-and-drop-wrapper drag-start-mode='component_other'>" + + new DesignContext().createElement(okButton) + + new DesignContext().createElement(dragImage).attr( + ":drag-image", "") + "</v-drag-and-drop-wrapper>"; + DragAndDropWrapper wrapper = new DragAndDropWrapper(okButton); + wrapper.setDragStartMode(DragStartMode.COMPONENT_OTHER); + wrapper.setDragImageComponent(dragImage); + testWrite(input, wrapper); + testRead(input, wrapper); + } +} diff --git a/server/tests/src/com/vaadin/tests/components/menubar/MenuBarDeclarativeTest.java b/server/tests/src/com/vaadin/tests/components/menubar/MenuBarDeclarativeTest.java new file mode 100644 index 0000000000..e6dee44812 --- /dev/null +++ b/server/tests/src/com/vaadin/tests/components/menubar/MenuBarDeclarativeTest.java @@ -0,0 +1,139 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.components.menubar; + +import java.io.IOException; + +import org.junit.Test; + +import com.vaadin.server.ExternalResource; +import com.vaadin.server.ThemeResource; +import com.vaadin.tests.design.DeclarativeTestBase; +import com.vaadin.ui.MenuBar; +import com.vaadin.ui.MenuBar.MenuItem; + +/** + * Tests declarative support for menu bars. + * + * @since 7.4 + * @author Vaadin Ltd + */ +public class MenuBarDeclarativeTest extends DeclarativeTestBase<MenuBar> { + + @Test + // #16328 + public void testReadWrite() throws IOException { + String design = "<v-menu-bar auto-open='true' tabindex=5>" + + "<menu checkable='true'>Save</menu>" + + "<menu description='Open a file'>Open</menu>" + + "<menu disabled='true'>Close</menu>" + + "<menu icon='http://foo.bar/ico.png'>Help</menu>" + + "<menu visible='false'>About</menu>" + + "<menu>Sub<menu>Item</menu></menu>" + + "<menu more>WTF?!</menu>" + "</v-menu-bar>"; + MenuBar bar = new MenuBar(); + bar.setAutoOpen(true); + bar.setHtmlContentAllowed(true); + bar.setTabIndex(5); + + bar.addItem("Save", null).setCheckable(true); + bar.addItem("Open", null).setDescription("Open a file"); + bar.addItem("Close", null).setEnabled(false); + bar.addItem("Help", null).setIcon( + new ExternalResource("http://foo.bar/ico.png")); + bar.addItem("About", null).setVisible(false); + + bar.addItem("Sub", null).addItem("Item", null); + + bar.setMoreMenuItem(bar.new MenuItem("WTF?!", null, null)); + + testWrite(design, bar); + testRead(design, bar); + } + + @Test + // #16328 + public void testTicketSpec1() throws IOException { + String design = "<v-menu-bar auto-open='true' plain-text tabindex=5> " + + "<menu>File" + + "<menu>Save</menu>" + + "<menu icon=\"theme://../runo/icons/16/folder.png\">Open</menu>" + + "<menu separator='true' />" + + "<menu disabled='true'>Exit</menu>" + + "<menu visible='false'>Not for everybody</menu>" + + "</menu>" + + "<menu description=\"This contains many items in sub menus\">Other" + + "<menu style-name=\"fancy\">Sub" + + "<menu checkable='true' checked='true'>Option 1 - no <b>html</b></menu>" + + "<menu checkable='true'>Option 2</menu>" + + "<menu checkable='true'>Option 3</menu>" // + + "</menu>" // + + "</menu>" // + + "<menu more icon=\"theme://icon.png\">foo</menu>" + + "</v-menu-bar>"; + // for one reason or another, no component has a correct .equals + // implementation, which makes tests a bit annoying + MenuBar menuBar = new MenuBar(); + menuBar.setHtmlContentAllowed(false); + menuBar.setTabIndex(5); + menuBar.setAutoOpen(true); + // File menu + MenuItem fileMenu = menuBar.addItem("File", null); + fileMenu.addItem("Save", null); + fileMenu.addItem("Open", new ThemeResource( + "../runo/icons/16/folder.png"), null); + fileMenu.addSeparator(); + fileMenu.addItem("Exit", null).setEnabled(false); + fileMenu.addItem("Not for everybody", null).setVisible(false); + MenuItem otherMenu = menuBar.addItem("Other", null); + otherMenu.setDescription("This contains many items in sub menus"); + MenuItem subMenu = otherMenu.addItem("Sub", null); + subMenu.setStyleName("fancy"); + MenuItem option1 = subMenu.addItem("Option 1 - no <b>html</b>", null); + option1.setCheckable(true); + option1.setChecked(true); + subMenu.addItem("Option 2", null).setCheckable(true); + subMenu.addItem("Option 3", null).setCheckable(true); + menuBar.setMoreMenuItem(null); + MenuItem moreMenu = menuBar.getMoreMenuItem(); + moreMenu.setIcon(new ThemeResource("icon.png")); + moreMenu.setText("foo"); + testRead(design, menuBar); + testWrite(design, menuBar); + } + + @Test + // #16328 + public void testTicketSpec2() throws IOException { + String design = "<v-menu-bar>" + + "<menu><b>File</b>" + + "<menu><font style=\"color: red\">Save</font></menu>" + + "<menu icon=\"theme://../runo/icons/16/folder.png\">Open</menu>" + + "<menu separator='true' />" + + "<menu disabled='true'>Exit</menu>" // + + "</menu></v-menu-bar>"; + MenuBar menuBar = new MenuBar(); + menuBar.setHtmlContentAllowed(true); + MenuItem fileMenu = menuBar.addItem("<b>File</b>", null); + fileMenu.addItem("<font style='color: red'>Save</font>", null); + fileMenu.addItem("Open", new ThemeResource( + "../runo/icons/16/folder.png"), null); + fileMenu.addSeparator(); + fileMenu.addItem("Exit", null).setEnabled(false); + testRead(design, menuBar); + testWrite(design, menuBar); + } +}
\ No newline at end of file diff --git a/server/tests/src/com/vaadin/tests/design/ComponentMapperTest.java b/server/tests/src/com/vaadin/tests/design/ComponentMapperTest.java new file mode 100644 index 0000000000..c6e8c15109 --- /dev/null +++ b/server/tests/src/com/vaadin/tests/design/ComponentMapperTest.java @@ -0,0 +1,129 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.design; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import org.junit.Assert; +import org.junit.Test; + +import com.vaadin.ui.Component; +import com.vaadin.ui.Label; +import com.vaadin.ui.declarative.Design; +import com.vaadin.ui.declarative.Design.ComponentFactory; +import com.vaadin.ui.declarative.Design.ComponentMapper; +import com.vaadin.ui.declarative.DesignContext; + +public class ComponentMapperTest { + private static final ComponentMapper defaultMapper = Design + .getComponentMapper(); + + private static final ThreadLocal<ComponentMapper> currentMapper = new ThreadLocal<ComponentMapper>(); + + static { + Design.setComponentMapper(new ComponentMapper() { + @Override + public Component tagToComponent(String tag, + ComponentFactory componentFactory, DesignContext context) { + return getActualMapper().tagToComponent(tag, componentFactory, + context); + } + + @Override + public String componentToTag(Component component, + DesignContext context) { + return getActualMapper().componentToTag(component, context); + } + + private ComponentMapper getActualMapper() { + ComponentMapper mapper = currentMapper.get(); + if (mapper == null) { + mapper = defaultMapper; + } + return mapper; + } + }); + } + + private final class CustomComponentMapper extends + Design.DefaultComponentMapper { + @Override + public Component tagToComponent(String tag, + ComponentFactory componentFactory, DesignContext context) { + if (tag.startsWith("custom-")) { + ComponentWithCustomTagName component = (ComponentWithCustomTagName) componentFactory + .createComponent( + ComponentWithCustomTagName.class.getName(), + context); + component.tagName = tag; + return component; + } else { + return super.tagToComponent(tag, componentFactory, context); + } + } + + @Override + public String componentToTag(Component component, DesignContext context) { + if (component instanceof ComponentWithCustomTagName) { + ComponentWithCustomTagName withCustomTagName = (ComponentWithCustomTagName) component; + return withCustomTagName.tagName; + } else { + return super.componentToTag(component, context); + } + } + } + + public static class ComponentWithCustomTagName extends Label { + private String tagName; + } + + @Test + public void testCustomComponentMapperRead() { + currentMapper.set(new CustomComponentMapper()); + + Component component = Design.read(new ByteArrayInputStream( + "<custom-foobar />".getBytes())); + + Assert.assertTrue("<custom-foobar> should resolve " + + ComponentWithCustomTagName.class.getSimpleName(), + component instanceof ComponentWithCustomTagName); + Assert.assertEquals("custom-foobar", + ((ComponentWithCustomTagName) component).tagName); + } + + @Test + public void testCustomComponentMapperWrite() throws IOException { + currentMapper.set(new CustomComponentMapper()); + + ComponentWithCustomTagName component = new ComponentWithCustomTagName(); + component.tagName = "custom-special"; + + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + Design.write(component, bos); + String writtenDesign = new String(bos.toByteArray()); + + Assert.assertTrue( + "Written design should contain \"<custom-special\", but instead got " + + writtenDesign, + writtenDesign.contains("<custom-special")); + } + + public void cleanup() { + currentMapper.remove(); + } +} diff --git a/server/tests/src/com/vaadin/tests/server/component/audio/AudioDeclarativeTest.java b/server/tests/src/com/vaadin/tests/server/component/audio/AudioDeclarativeTest.java new file mode 100644 index 0000000000..4390499c4e --- /dev/null +++ b/server/tests/src/com/vaadin/tests/server/component/audio/AudioDeclarativeTest.java @@ -0,0 +1,63 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.server.component.audio; + +import java.io.File; + +import org.junit.Test; + +import com.vaadin.server.ExternalResource; +import com.vaadin.server.FileResource; +import com.vaadin.tests.design.DeclarativeTestBase; +import com.vaadin.ui.Audio; + +/** + * Tests specs of declarative support for abstract media and its + * implementations. + * + * @since 7.4 + * @author Vaadin Ltd + */ +public class AudioDeclarativeTest extends DeclarativeTestBase<Audio> { + + @Test + public void testEmptyAudio() { + String design = "<v-audio />"; + Audio audio = new Audio(); + testRead(design, audio); + testWrite(design, audio); + } + + @Test + public void testAudioMultipleSources() { + String design = "<v-audio muted='true' show-controls='false'>" + + "some <b>text</b>" // + + "<source href='http://foo.pl' />" + + "<source href='https://bar.pl' />" // + + "<source href='ohai' />" // + + "</v-audio>"; + Audio audio = new Audio(); + audio.setAltText("some <b>text</b>"); + audio.setAutoplay(false); + audio.setMuted(true); + audio.setShowControls(false); + audio.setSources(new ExternalResource("http://foo.pl"), + new ExternalResource("https://bar.pl"), new FileResource( + new File("ohai"))); + testRead(design, audio); + testWrite(design, audio); + } +} diff --git a/server/tests/src/com/vaadin/tests/server/component/audio/VideoDeclarativeTest.java b/server/tests/src/com/vaadin/tests/server/component/audio/VideoDeclarativeTest.java new file mode 100644 index 0000000000..fc0b3d9512 --- /dev/null +++ b/server/tests/src/com/vaadin/tests/server/component/audio/VideoDeclarativeTest.java @@ -0,0 +1,59 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.server.component.audio; + +import java.io.File; + +import org.junit.Test; + +import com.vaadin.server.ExternalResource; +import com.vaadin.server.FileResource; +import com.vaadin.tests.design.DeclarativeTestBase; +import com.vaadin.ui.Video; + +public class VideoDeclarativeTest extends DeclarativeTestBase<Video> { + + @Test + public void testEmptyVideo() { + String design = "<v-video />"; + Video audio = new Video(); + testRead(design, audio); + testWrite(design, audio); + } + + @Test + public void testVideoMultipleSources() { + String design = "<v-video muted='true' show-controls='false'>" + + "some <b>text</b>" // + + "<source href='http://foo.pl' />" + + "<source href='https://bar.pl' />" // + + "<source href='ohai' />" // + + "<poster href='http://foo.pl/poster' />" // + + "</v-video>"; + Video video = new Video(); + video.setAltText("some <b>text</b>"); + video.setAutoplay(false); + video.setMuted(true); + video.setShowControls(false); + video.setSources(new ExternalResource("http://foo.pl"), + new ExternalResource("https://bar.pl"), new FileResource( + new File("ohai"))); + video.setPoster(new ExternalResource("http://foo.pl/poster")); + testRead(design, video); + testWrite(design, video); + } + +} diff --git a/server/tests/src/com/vaadin/tests/server/component/calendar/CalendarDeclarativeTest.java b/server/tests/src/com/vaadin/tests/server/component/calendar/CalendarDeclarativeTest.java new file mode 100644 index 0000000000..d866878fc7 --- /dev/null +++ b/server/tests/src/com/vaadin/tests/server/component/calendar/CalendarDeclarativeTest.java @@ -0,0 +1,61 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.server.component.calendar; + +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.TimeZone; + +import org.junit.Test; + +import com.vaadin.tests.design.DeclarativeTestBase; +import com.vaadin.ui.Calendar; +import com.vaadin.ui.Calendar.TimeFormat; + +public class CalendarDeclarativeTest extends DeclarativeTestBase<Calendar> { + + @Test + public void testEmpty() { + verifyDeclarativeDesign("<v-calendar></v-calendar>", new Calendar()); + } + + @Test + public void testCalendarAllFeatures() throws ParseException { + String design = "<v-calendar start-date='2014-11-17' end-date='2014-11-23' " + + "first-visible-day-of-week=2 last-visible-day-of-week=5 " + + "time-zone='EST' time-format='12h' first-visible-hour-of-day=8 " + + "last-visible-hour-of-day=18 weekly-caption-format='mmm MM/dd' />"; + + DateFormat format = new SimpleDateFormat("yyyy-MM-dd"); + Calendar calendar = new Calendar(); + calendar.setStartDate(format.parse("2014-11-17")); + calendar.setEndDate(format.parse("2014-11-23")); + calendar.setFirstVisibleDayOfWeek(2); + calendar.setLastVisibleDayOfWeek(5); + calendar.setTimeZone(TimeZone.getTimeZone("EST")); + calendar.setTimeFormat(TimeFormat.Format12H); + calendar.setFirstVisibleHourOfDay(8); + calendar.setLastVisibleHourOfDay(18); + calendar.setWeeklyCaptionFormat("mmm MM/dd"); + verifyDeclarativeDesign(design, calendar); + } + + protected void verifyDeclarativeDesign(String design, Calendar expected) { + testRead(design, expected); + testWrite(design, expected); + } +} diff --git a/server/tests/src/com/vaadin/tests/server/component/customlayout/CustomLayoutDeclarativeTest.java b/server/tests/src/com/vaadin/tests/server/component/customlayout/CustomLayoutDeclarativeTest.java new file mode 100644 index 0000000000..44261a61dc --- /dev/null +++ b/server/tests/src/com/vaadin/tests/server/component/customlayout/CustomLayoutDeclarativeTest.java @@ -0,0 +1,96 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.server.component.customlayout; + +import org.junit.Test; + +import com.vaadin.tests.design.DeclarativeTestBase; +import com.vaadin.ui.Button; +import com.vaadin.ui.CustomLayout; +import com.vaadin.ui.Label; + +/** + * Tests declarative support for {@link CustomLayout}. + * + * @since + * @author Vaadin Ltd + */ +public class CustomLayoutDeclarativeTest extends + DeclarativeTestBase<CustomLayout> { + + @Test + public void testEmpty() { + String design = "<v-custom-layout>"; + CustomLayout expected = new CustomLayout(); + test(design, expected); + } + + @Test + public void testWithChildren() { + String design = "<v-custom-layout>" + // + "<v-button plain-text :location='b'></v-button>" + // + "<v-label plain-text :location='l'></v-label>" + // + "</v-custom-layout>"; + + CustomLayout expected = new CustomLayout(); + expected.addComponent(new Button(), "b"); + expected.addComponent(new Label(), "l"); + + test(design, expected); + } + + @Test + public void testWithOneChild() { + String design = "<v-custom-layout><v-button plain-text></v-button></v-custom-layout>"; + + CustomLayout expected = new CustomLayout(); + expected.addComponent(new Button()); + + test(design, expected); + } + + @Test + public void testWithTemplate() { + String design = "<v-custom-layout template-name='template.html'></v-custom-layout>"; + CustomLayout expected = new CustomLayout("template.html"); + test(design, expected); + } + + @Test + public void testWithDuplicateLocations() { + String design = "<v-custom-layout>" + // + "<v-button plain-text :location='foo'></v-button>" + // + "<v-label plain-text :location='foo'></v-label>" + // + "</v-custom-layout>"; + + CustomLayout expected = new CustomLayout(); + expected.addComponent(new Button(), "foo"); + expected.addComponent(new Label(), "foo"); + + testRead(design, expected); + + String written = "<v-custom-layout>" + // + "<v-label plain-text :location='foo'></v-label>" + // + "</v-custom-layout>"; + + testWrite(written, expected); + } + + protected void test(String design, CustomLayout expected) { + testRead(design, expected); + testWrite(design, expected); + } +} diff --git a/server/tests/src/com/vaadin/tests/server/component/slider/SliderDeclarativeTest.java b/server/tests/src/com/vaadin/tests/server/component/slider/SliderDeclarativeTest.java new file mode 100644 index 0000000000..9ef28afb82 --- /dev/null +++ b/server/tests/src/com/vaadin/tests/server/component/slider/SliderDeclarativeTest.java @@ -0,0 +1,66 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.server.component.slider; + +import org.junit.Test; + +import com.vaadin.shared.ui.slider.SliderOrientation; +import com.vaadin.tests.design.DeclarativeTestBase; +import com.vaadin.ui.Slider; + +/** + * Tests declarative support for implementations of {@link Slider}. + * + * @since + * @author Vaadin Ltd + */ +public class SliderDeclarativeTest extends DeclarativeTestBase<Slider> { + + @Test + public void testDefault() { + String design = "<v-slider>"; + + Slider expected = new Slider(); + + testRead(design, expected); + testWrite(design, expected); + } + + @Test + public void testHorizontal() { + String design = "<v-slider min=10 max=20 resolution=1 value=12.3>"; + + Slider expected = new Slider(); + expected.setMin(10.0); + expected.setMax(20.0); + expected.setResolution(1); + expected.setValue(12.3); + + testRead(design, expected); + testWrite(design, expected); + } + + @Test + public void testVertical() { + String design = "<v-slider vertical>"; + + Slider expected = new Slider(); + expected.setOrientation(SliderOrientation.VERTICAL); + + testRead(design, expected); + testWrite(design, expected); + } +} diff --git a/server/tests/src/com/vaadin/tests/util/MockDeploymentConfiguration.java b/server/tests/src/com/vaadin/tests/util/MockDeploymentConfiguration.java index 175dcb2b94..ddee23a9ec 100644 --- a/server/tests/src/com/vaadin/tests/util/MockDeploymentConfiguration.java +++ b/server/tests/src/com/vaadin/tests/util/MockDeploymentConfiguration.java @@ -22,7 +22,6 @@ public class MockDeploymentConfiguration extends private LegacyProperyToStringMode legacyPropertyToStringMode = LegacyProperyToStringMode.DISABLED; private boolean syncIdCheckEnabled = true; private boolean sendUrlsAsParameters = true; - private String pushPath = "PUSH"; @Override public boolean isProductionMode() { @@ -126,9 +125,4 @@ public class MockDeploymentConfiguration extends return sendUrlsAsParameters; } - @Override - public String getPushPath() { - return pushPath; - } - } diff --git a/server/tests/src/com/vaadin/ui/SplitPositionChangeListenerTest.java b/server/tests/src/com/vaadin/ui/SplitPositionChangeListenerTest.java new file mode 100644 index 0000000000..02dc412cd9 --- /dev/null +++ b/server/tests/src/com/vaadin/ui/SplitPositionChangeListenerTest.java @@ -0,0 +1,44 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.ui; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import org.junit.Test; + +import com.vaadin.server.Sizeable.Unit; +import com.vaadin.ui.AbstractSplitPanel.SplitPositionChangeEvent; +import com.vaadin.ui.AbstractSplitPanel.SplitPositionChangeListener; + +/** + * Test for {@link SplitPositionChangeListener} + * + * @author Vaadin Ltd + */ +public class SplitPositionChangeListenerTest { + + @Test + public void testSplitPositionListenerIsTriggered() throws Exception { + final HorizontalSplitPanel splitPanel = new HorizontalSplitPanel(); + SplitPositionChangeListener splitPositionChangeListener = mock(SplitPositionChangeListener.class); + splitPanel.addSplitPositionChangeListener(splitPositionChangeListener); + splitPanel.setSplitPosition(50, Unit.PERCENTAGE); + verify(splitPositionChangeListener).onSplitPositionChanged( + any(SplitPositionChangeEvent.class)); + } +} diff --git a/shared/src/com/vaadin/shared/ApplicationConstants.java b/shared/src/com/vaadin/shared/ApplicationConstants.java index 990564a6b8..d7aaee6267 100644 --- a/shared/src/com/vaadin/shared/ApplicationConstants.java +++ b/shared/src/com/vaadin/shared/ApplicationConstants.java @@ -28,6 +28,8 @@ public class ApplicationConstants implements Serializable { public static final String HEARTBEAT_PATH = "HEARTBEAT"; + public static final String PUSH_PATH = "PUSH"; + public static final String PUBLISHED_FILE_PATH = APP_PATH + '/' + "PUBLISHED"; @@ -74,7 +76,7 @@ public class ApplicationConstants implements Serializable { /** * The name of the javascript containing the bootstrap code. The file is * located in the VAADIN directory. - * + * * @since 7.3 */ public static final String VAADIN_BOOTSTRAP_JS = "vaadinBootstrap.js"; @@ -88,7 +90,7 @@ public class ApplicationConstants implements Serializable { /** * The name of the debug version of the javascript containing push support. * The file is located in the VAADIN directory. - * + * * @since 7.1.6 */ public static final String VAADIN_PUSH_DEBUG_JS = "vaadinPush.debug.js"; @@ -100,14 +102,14 @@ public class ApplicationConstants implements Serializable { /** * The name of the parameter used to transmit RPC invocations - * + * * @since 7.2 */ public static final String RPC_INVOCATIONS = "rpc"; /** * The name of the parameter used to transmit the CSRF token - * + * * @since 7.2 */ public static final String CSRF_TOKEN = "csrfToken"; @@ -116,7 +118,7 @@ public class ApplicationConstants implements Serializable { * The name of the parameter used to transmit the sync id. The value can be * set to -1 e.g. when testing with pre-recorded requests to make the * framework ignore the sync id. - * + * * @see com.vaadin.ui.ConnectorTracker#getCurrentSyncId() * @since 7.2 */ diff --git a/shared/src/com/vaadin/shared/ui/ui/UIState.java b/shared/src/com/vaadin/shared/ui/ui/UIState.java index 04e182c5d4..2f51fef6ee 100644 --- a/shared/src/com/vaadin/shared/ui/ui/UIState.java +++ b/shared/src/com/vaadin/shared/ui/ui/UIState.java @@ -110,7 +110,6 @@ public class UIState extends TabIndexState { public static final String FALLBACK_TRANSPORT_PARAM = "fallbackTransport"; public PushMode mode = PushMode.DISABLED; - public String pushPath; public Map<String, String> parameters = new HashMap<String, String>(); { parameters diff --git a/uitest/src/com/vaadin/launcher/ApplicationRunnerServlet.java b/uitest/src/com/vaadin/launcher/ApplicationRunnerServlet.java index 5c2e58d3a2..e2b93ab7d2 100644 --- a/uitest/src/com/vaadin/launcher/ApplicationRunnerServlet.java +++ b/uitest/src/com/vaadin/launcher/ApplicationRunnerServlet.java @@ -402,12 +402,9 @@ public class ApplicationRunnerServlet extends LegacyVaadinServlet { try { VaadinServletService service = (VaadinServletService) VaadinService .getCurrent(); - if (service != null) { - session = service - .findVaadinSession(new VaadinServletRequest( - currentRequest, service)); - } - + session = service + .findVaadinSession(new VaadinServletRequest( + currentRequest, service)); } finally { /* * Clear some state set by findVaadinSession to diff --git a/uitest/src/com/vaadin/tests/VerifyBrowserVersionTest.java b/uitest/src/com/vaadin/tests/VerifyBrowserVersionTest.java index 5b4f4df788..8d0530204a 100644 --- a/uitest/src/com/vaadin/tests/VerifyBrowserVersionTest.java +++ b/uitest/src/com/vaadin/tests/VerifyBrowserVersionTest.java @@ -33,9 +33,20 @@ public class VerifyBrowserVersionTest extends MultiBrowserTest { DesiredCapabilities desiredCapabilities = getDesiredCapabilities(); - assertThat(vaadinElementById("userAgent").getText(), - containsString(getExpectedUserAgentString(desiredCapabilities) - + desiredCapabilities.getVersion())); + String userAgent = vaadinElementById("userAgent").getText(); + String browserIdentifier; + + if (BrowserUtil.isChrome(getDesiredCapabilities())) { + // Chrome version does not necessarily match the desired version + // because of auto updates... + browserIdentifier = getExpectedUserAgentString(getDesiredCapabilities()) + + "41"; + } else { + browserIdentifier = getExpectedUserAgentString(desiredCapabilities) + + desiredCapabilities.getVersion(); + } + + assertThat(userAgent, containsString(browserIdentifier)); assertThat(vaadinElementById("touchDevice").getText(), is("Touch device? No")); diff --git a/uitest/src/com/vaadin/tests/components/accordion/AccordionRemoveComponent.java b/uitest/src/com/vaadin/tests/components/accordion/AccordionRemoveComponent.java new file mode 100644 index 0000000000..80e0929f8d --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/accordion/AccordionRemoveComponent.java @@ -0,0 +1,55 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.components.accordion; + +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.Accordion; +import com.vaadin.ui.Button; +import com.vaadin.ui.Button.ClickEvent; + +/** + * Test for removing component from Accordion. + * + * @author Vaadin Ltd + */ +public class AccordionRemoveComponent extends AbstractTestUI { + + @Override + protected void setup(VaadinRequest request) { + final Accordion accordion = new Accordion(); + Button button = new Button("remove"); + button.addClickListener(new Button.ClickListener() { + + @Override + public void buttonClick(ClickEvent event) { + accordion.removeComponent(event.getButton()); + } + }); + accordion.addComponent(button); + addComponent(accordion); + } + + @Override + protected String getTestDescription() { + return "Reset selected index when tab is removed"; + } + + @Override + protected Integer getTicketNumber() { + return 17248; + } +} diff --git a/uitest/src/com/vaadin/tests/push/PushPathTest.java b/uitest/src/com/vaadin/tests/components/accordion/AccordionRemoveComponentTest.java index 0af9c8d3a7..84f1734897 100644 --- a/uitest/src/com/vaadin/tests/push/PushPathTest.java +++ b/uitest/src/com/vaadin/tests/components/accordion/AccordionRemoveComponentTest.java @@ -13,27 +13,31 @@ * License for the specific language governing permissions and limitations under * the License. */ -package com.vaadin.tests.push; +package com.vaadin.tests.components.accordion; import org.junit.Assert; import org.junit.Test; +import org.openqa.selenium.By; -import com.vaadin.tests.tb3.WebsocketTest; +import com.vaadin.testbench.elements.ButtonElement; +import com.vaadin.tests.tb3.MultiBrowserTest; -public class PushPathTest extends WebsocketTest { - - private static final int TEN_SECONDS_IN_MS = 10 * 1000; +/** + * Test for removing component from Accordion + * + * @author Vaadin Ltd + */ +public class AccordionRemoveComponentTest extends MultiBrowserTest { @Test - public void testCustomPushPath() throws InterruptedException { + public void removeComponent_noClientSideException() { + setDebug(true); openTestURL(); - sleep(TEN_SECONDS_IN_MS); - Assert.assertEquals(vaadinElementById(PushPath.PUSH_PATH_LABEL_ID) - .getText(), PushPath.PUSH_PATH_LABEL_TEXT); - } - @Override - protected String getDeploymentPath(Class<?> uiClass) { - return "/run-pushpath/" + uiClass.getCanonicalName(); + $(ButtonElement.class).first().click(); + + Assert.assertFalse( + "Error notification with client side exception is shown", + isElementPresent(By.className("v-Notification-error"))); } } diff --git a/uitest/src/com/vaadin/tests/components/customlayout/CustomLayoutWithNullTemplate.java b/uitest/src/com/vaadin/tests/components/customlayout/CustomLayoutWithNullTemplate.java new file mode 100644 index 0000000000..de1caf86df --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/customlayout/CustomLayoutWithNullTemplate.java @@ -0,0 +1,44 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.components.customlayout; + +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.Button; +import com.vaadin.ui.CustomLayout; +import com.vaadin.ui.Label; + +public class CustomLayoutWithNullTemplate extends AbstractTestUI { + + @Override + protected void setup(VaadinRequest request) { + CustomLayout cl = new CustomLayout(); + cl.addComponent(new Label("This Label should be visible."), "foo"); + cl.addComponent(new Button("This Button too."), "bar"); + + addComponent(cl); + } + + @Override + protected String getTestDescription() { + return "Verify that a default-constructed CustomLayout renders child components"; + } + + @Override + protected Integer getTicketNumber() { + return 17210; + } +} diff --git a/uitest/src/com/vaadin/tests/components/customlayout/CustomLayoutWithNullTemplateTest.java b/uitest/src/com/vaadin/tests/components/customlayout/CustomLayoutWithNullTemplateTest.java new file mode 100644 index 0000000000..668d43f18f --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/customlayout/CustomLayoutWithNullTemplateTest.java @@ -0,0 +1,43 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.components.customlayout; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import com.vaadin.testbench.ElementQuery; +import com.vaadin.testbench.elements.ButtonElement; +import com.vaadin.testbench.elements.CustomLayoutElement; +import com.vaadin.testbench.elements.LabelElement; +import com.vaadin.tests.tb3.SingleBrowserTest; + +public class CustomLayoutWithNullTemplateTest extends SingleBrowserTest { + + @Test + public void testChildComponents() { + openTestURL(); + + ElementQuery<CustomLayoutElement> customLayout = $(CustomLayoutElement.class); + + // Verify the Button and Label are rendered inside the CustomLayout. + assertTrue("Button was not rendered.", + customLayout.$(ButtonElement.class).exists()); + assertTrue("Label was not rendered.", customLayout + .$(LabelElement.class).exists()); + } + +} diff --git a/uitest/src/com/vaadin/tests/components/grid/GridColumnWidthsWithoutData.java b/uitest/src/com/vaadin/tests/components/grid/GridColumnWidthsWithoutData.java index 1dfc7bcf11..b04745f17a 100644 --- a/uitest/src/com/vaadin/tests/components/grid/GridColumnWidthsWithoutData.java +++ b/uitest/src/com/vaadin/tests/components/grid/GridColumnWidthsWithoutData.java @@ -15,21 +15,41 @@ */ package com.vaadin.tests.components.grid; +import java.util.EnumSet; + +import com.vaadin.data.Property.ValueChangeEvent; +import com.vaadin.data.Property.ValueChangeListener; import com.vaadin.server.VaadinRequest; import com.vaadin.tests.components.AbstractTestUI; import com.vaadin.ui.Button; import com.vaadin.ui.Button.ClickEvent; import com.vaadin.ui.Grid; +import com.vaadin.ui.Grid.SelectionMode; +import com.vaadin.ui.NativeSelect; import com.vaadin.ui.VerticalLayout; public class GridColumnWidthsWithoutData extends AbstractTestUI { + private SelectionMode selectionMode = SelectionMode.NONE; private Grid grid = createGrid(true); @Override protected void setup(VaadinRequest request) { addComponent(grid); + NativeSelect selectionModeSelector = new NativeSelect("Selection mode", + EnumSet.allOf(SelectionMode.class)); + selectionModeSelector.setValue(selectionMode); + selectionModeSelector.setNullSelectionAllowed(false); + selectionModeSelector.addValueChangeListener(new ValueChangeListener() { + @Override + public void valueChange(ValueChangeEvent event) { + selectionMode = (SelectionMode) event.getProperty().getValue(); + grid.setSelectionMode(selectionMode); + } + }); + addComponent(selectionModeSelector); + addComponent(new Button("Recreate without data", new Button.ClickListener() { @Override @@ -72,6 +92,7 @@ public class GridColumnWidthsWithoutData extends AbstractTestUI { grid.addColumn("foo"); grid.addColumn("bar"); grid.setWidth("300px"); + grid.setSelectionMode(selectionMode); if (withData) { addDataToGrid(grid); diff --git a/uitest/src/com/vaadin/tests/components/grid/GridColumnWidthsWithoutDataTest.java b/uitest/src/com/vaadin/tests/components/grid/GridColumnWidthsWithoutDataTest.java index dd63d6abc9..c215fa0a59 100644 --- a/uitest/src/com/vaadin/tests/components/grid/GridColumnWidthsWithoutDataTest.java +++ b/uitest/src/com/vaadin/tests/components/grid/GridColumnWidthsWithoutDataTest.java @@ -23,6 +23,7 @@ import org.junit.Test; import com.vaadin.testbench.elements.ButtonElement; import com.vaadin.testbench.elements.GridElement; import com.vaadin.testbench.elements.GridElement.GridCellElement; +import com.vaadin.testbench.elements.NativeSelectElement; import com.vaadin.testbench.elements.NotificationElement; import com.vaadin.tests.tb3.SingleBrowserTest; @@ -49,7 +50,7 @@ public class GridColumnWidthsWithoutDataTest extends SingleBrowserTest { } @Test - public void restWidthsWhenInitiallyEmpty() { + public void testWidthsWhenInitiallyEmpty() { setDebug(true); openTestURL(); $(ButtonElement.class).caption("Recreate without data").first().click(); @@ -74,6 +75,37 @@ public class GridColumnWidthsWithoutDataTest extends SingleBrowserTest { isElementPresent(NotificationElement.class)); } + @Test + public void testMultiSelectWidths() { + setDebug(true); + openTestURL(); + $(NativeSelectElement.class).caption("Selection mode").first() + .selectByText("Multi"); + + GridElement grid = $(GridElement.class).first(); + + int sum = sumUsedWidths(grid); + + // 295 instead of 300 to avoid rounding issues + Assert.assertTrue("Only " + sum + " out of 300px was used", sum > 295); + + $(ButtonElement.class).caption("Recreate without data").first().click(); + + grid = $(GridElement.class).first(); + sum = sumUsedWidths(grid); + + // 295 instead of 300 to avoid rounding issues + Assert.assertTrue("Only " + sum + " out of 300px was used", sum > 295); + } + + private int sumUsedWidths(GridElement grid) { + int sum = 0; + for (int i : getColWidths(grid)) { + sum += i; + } + return sum; + } + private static void assertSameWidths(int[] expected, int[] actual) { Assert.assertEquals("Arrays have differing lengths", expected.length, actual.length); diff --git a/uitest/src/com/vaadin/tests/components/grid/GridEditorMultiselect.java b/uitest/src/com/vaadin/tests/components/grid/GridEditorMultiselect.java new file mode 100644 index 0000000000..b80a9d1153 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/grid/GridEditorMultiselect.java @@ -0,0 +1,35 @@ +package com.vaadin.tests.components.grid; + +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.Grid; + +public class GridEditorMultiselect extends AbstractTestUI { + + @Override + protected void setup(VaadinRequest request) { + Grid grid = new Grid(); + + grid.addColumn("name"); + grid.addColumn("age", Integer.class); + + for (int i = 0; i < 30; i++) { + grid.addRow("name " + i, i); + } + + grid.setEditorEnabled(true); + grid.setSelectionMode(Grid.SelectionMode.MULTI); + + addComponent(grid); + } + + @Override + protected Integer getTicketNumber() { + return 17132; + } + + @Override + public String getDescription() { + return "Grid Multiselect: Edit mode allows invalid selection"; + } +} diff --git a/uitest/src/com/vaadin/tests/components/grid/GridEditorMultiselectTest.java b/uitest/src/com/vaadin/tests/components/grid/GridEditorMultiselectTest.java new file mode 100644 index 0000000000..ba689fb5e1 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/grid/GridEditorMultiselectTest.java @@ -0,0 +1,55 @@ +package com.vaadin.tests.components.grid; + +import java.util.List; + +import org.junit.Assert; +import org.junit.Test; +import org.openqa.selenium.WebElement; + +import com.vaadin.testbench.By; +import com.vaadin.testbench.elements.GridElement; +import com.vaadin.tests.tb3.MultiBrowserTest; + +public class GridEditorMultiselectTest extends MultiBrowserTest { + + @Test + public void testSelectCheckboxesDisabled() { + openTestURL(); + GridElement grid = openEditor(); + assertCheckboxesEnabled(grid, false); + } + + @Test + public void testSelectCheckboxesEnabledBackOnSave() { + openTestURL(); + GridElement grid = openEditor(); + grid.getEditor().save(); + assertCheckboxesEnabled(grid, true); + } + + @Test + public void testSelectCheckboxesEnabledBackOnCancel() { + openTestURL(); + GridElement grid = openEditor(); + grid.getEditor().cancel(); + assertCheckboxesEnabled(grid, true); + } + + private GridElement openEditor() { + GridElement grid = $(GridElement.class).first(); + grid.getRow(0).doubleClick(); + Assert.assertTrue("Grid editor should be displayed.", grid.getEditor() + .isDisplayed()); + return grid; + } + + private void assertCheckboxesEnabled(GridElement grid, boolean isEnabled) { + List<WebElement> checkboxes = grid.findElements(By + .xpath("//input[@type='checkbox']")); + for (WebElement checkbox : checkboxes) { + Assert.assertEquals("Select checkboxes should be " + + (isEnabled ? "enabled" : "disabled"), isEnabled, + checkbox.isEnabled()); + } + } +} diff --git a/uitest/src/com/vaadin/tests/components/grid/GridResizeTerror.java b/uitest/src/com/vaadin/tests/components/grid/GridResizeTerror.java new file mode 100644 index 0000000000..365461caa9 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/grid/GridResizeTerror.java @@ -0,0 +1,45 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.components.grid; + +import com.vaadin.annotations.Widgetset; +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.util.ResizeTerrorizer; +import com.vaadin.tests.widgetset.TestingWidgetSet; +import com.vaadin.ui.Grid; +import com.vaadin.ui.UI; + +@Widgetset(TestingWidgetSet.NAME) +public class GridResizeTerror extends UI { + @Override + protected void init(VaadinRequest request) { + Grid grid = new Grid(); + + int cols = 10; + Object[] data = new Object[cols]; + + for (int i = 0; i < cols; i++) { + grid.addColumn("Col " + i); + data[i] = "Data " + i; + } + + for (int i = 0; i < 500; i++) { + grid.addRow(data); + } + + setContent(new ResizeTerrorizer(grid)); + } +} diff --git a/uitest/src/com/vaadin/tests/components/grid/GridSwitchRenderers.java b/uitest/src/com/vaadin/tests/components/grid/GridSwitchRenderers.java new file mode 100644 index 0000000000..e233edc5d0 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/grid/GridSwitchRenderers.java @@ -0,0 +1,103 @@ +package com.vaadin.tests.components.grid; + +import java.util.List; +import java.util.Random; + +import com.vaadin.annotations.Theme; +import com.vaadin.data.Item; +import com.vaadin.data.Property.ValueChangeEvent; +import com.vaadin.data.Property.ValueChangeListener; +import com.vaadin.data.util.IndexedContainer; +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUIWithLog; +import com.vaadin.ui.CheckBox; +import com.vaadin.ui.Grid; +import com.vaadin.ui.Grid.Column; +import com.vaadin.ui.Grid.SelectionMode; +import com.vaadin.ui.renderers.HtmlRenderer; +import com.vaadin.ui.renderers.TextRenderer; + +@SuppressWarnings("serial") +@Theme("valo") +public class GridSwitchRenderers extends AbstractTestUIWithLog { + private static final int MANUALLY_FORMATTED_COLUMNS = 1; + private static final int COLUMNS = 3; + private static final int ROWS = 1000; + private static final String EXPANSION_COLUMN_ID = "Column 0"; + + private IndexedContainer ds; + + @Override + protected void setup(VaadinRequest request) { + ds = new IndexedContainer() { + @Override + public List<Object> getItemIds(int startIndex, int numberOfIds) { + log("Requested items " + startIndex + " - " + + (startIndex + numberOfIds)); + return super.getItemIds(startIndex, numberOfIds); + } + }; + + { + ds.addContainerProperty(EXPANSION_COLUMN_ID, String.class, ""); + + int col = MANUALLY_FORMATTED_COLUMNS; + for (; col < COLUMNS; col++) { + ds.addContainerProperty(getColumnProperty(col), String.class, + ""); + } + } + + Random rand = new Random(); + rand.setSeed(13334); + for (int row = 0; row < ROWS; row++) { + Item item = ds.addItem(Integer.valueOf(row)); + fillRow("" + row, item); + item.getItemProperty(getColumnProperty(1)).setReadOnly(true); + } + + final Grid grid = new Grid(ds); + grid.setWidth("100%"); + + grid.getColumn(EXPANSION_COLUMN_ID).setWidth(50); + for (int col = MANUALLY_FORMATTED_COLUMNS; col < COLUMNS; col++) { + grid.getColumn(getColumnProperty(col)).setWidth(300); + grid.getColumn(getColumnProperty(col)).setRenderer( + new TextRenderer()); + } + + grid.setSelectionMode(SelectionMode.NONE); + addComponent(grid); + + final CheckBox changeRenderer = new CheckBox( + "SetHtmlRenderer for Column 2", false); + changeRenderer.addValueChangeListener(new ValueChangeListener() { + @Override + public void valueChange(ValueChangeEvent event) { + Column column = grid.getColumn(getColumnProperty(1)); + if (changeRenderer.getValue()) { + column.setRenderer(new HtmlRenderer()); + } else { + column.setRenderer(new TextRenderer()); + } + grid.markAsDirty(); + } + }); + addComponent(changeRenderer); + } + + @SuppressWarnings("unchecked") + private void fillRow(String content, Item item) { + int col = MANUALLY_FORMATTED_COLUMNS; + + for (; col < COLUMNS; col++) { + item.getItemProperty(getColumnProperty(col)).setValue( + "<b>(" + content + ", " + col + ")</b>"); + } + } + + private static String getColumnProperty(int c) { + return "Column " + c; + } + +}
\ No newline at end of file diff --git a/uitest/src/com/vaadin/tests/components/grid/GridSwitchRenderersTest.java b/uitest/src/com/vaadin/tests/components/grid/GridSwitchRenderersTest.java new file mode 100644 index 0000000000..e3bab865c0 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/grid/GridSwitchRenderersTest.java @@ -0,0 +1,84 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.components.grid; + +import org.junit.Assert; +import org.junit.Test; + +import com.vaadin.testbench.elements.CheckBoxElement; +import com.vaadin.testbench.elements.GridElement; +import com.vaadin.tests.tb3.MultiBrowserTest; + +public class GridSwitchRenderersTest extends MultiBrowserTest { + + @Test + public void testRendererSwitch() { + + // The UI should start with TEXT rendering in the second column + // Clicking the checkbox will toggle rendering to HTML mode + // Clicking it again should return TEXT rendering mode. + + openTestURL(); + + GridElement grid = $(GridElement.class).first(); + + Assert.assertTrue( + "Initial rendering of column 1 is not unformatted text", + cellTextIsUnformatted(grid.getCell(0, 1).getText())); + + // NOTE: must click at 5,5 because of Valo and rendering in Chrome + // This is a TestBench bug that may be fixed sometime in the future + CheckBoxElement cb = $(CheckBoxElement.class).first(); + cb.click(5, 5); + + Assert.assertTrue( + "Column 1 data has not been rendered with HTMLRenderer after renderer swap", + cellTextIsHTMLFormatted(grid.getCell(0, 1).getText())); + cb.click(5, 5); + + Assert.assertTrue( + "Column 1 data has not been re-rendered as text after renderer swap", + cellTextIsUnformatted(grid.getCell(0, 1).getText())); + } + + /** + * Attempts to match a string to a string like {@code <b>(4, 1)</b>}. + * + * @param cellText + * input string + * @return true if input string is formatted like a raw HTML string + */ + private boolean cellTextIsUnformatted(String cellText) { + String regex = "<b>\\(\\d+, \\d+\\)</b>"; + return cellText.matches(regex); + } + + /** + * Attempts to match a string to a string like {@code (4, 1)}, i.e. the HTML + * formatted version of the above (the bold tags should be consumed by the + * renderer). + * + * @param cellText + * input string + * @return true if input string is formatted like plain text (i.e. HTML bits + * have been consumed by renderer) + */ + private boolean cellTextIsHTMLFormatted(String cellText) { + String regex = "\\(\\d+, \\d+\\)"; + return cellText.matches(regex); + } + +} diff --git a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/client/GridClientColumnPropertiesTest.java b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/client/GridClientColumnPropertiesTest.java index 9e7256e0d3..2f5d0e7291 100644 --- a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/client/GridClientColumnPropertiesTest.java +++ b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/client/GridClientColumnPropertiesTest.java @@ -94,6 +94,23 @@ public class GridClientColumnPropertiesTest extends GridBasicClientFeaturesTest } @Test + public void testFrozenColumns_columnsReordered_frozenColumnsKept() { + openTestURL(); + + selectMenuPath("Component", "State", "Frozen column count", "2 columns"); + + assertTrue(cellIsFrozen(1, 0)); + assertTrue(cellIsFrozen(1, 1)); + assertFalse(cellIsFrozen(1, 2)); + + selectMenuPath("Component", "State", "Reverse grid columns"); + + assertTrue(cellIsFrozen(1, 0)); + assertTrue(cellIsFrozen(1, 1)); + assertFalse(cellIsFrozen(1, 2)); + } + + @Test public void testBrokenRenderer() { setDebug(true); openTestURL(); diff --git a/uitest/src/com/vaadin/tests/components/splitpanel/SplitPositionChange.java b/uitest/src/com/vaadin/tests/components/splitpanel/SplitPositionChange.java new file mode 100644 index 0000000000..81c449731c --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/splitpanel/SplitPositionChange.java @@ -0,0 +1,87 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.components.splitpanel; + +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUIWithLog; +import com.vaadin.ui.AbstractSplitPanel; +import com.vaadin.ui.HorizontalSplitPanel; +import com.vaadin.ui.Label; +import com.vaadin.ui.Panel; +import com.vaadin.ui.VerticalLayout; +import com.vaadin.ui.VerticalSplitPanel; + +/** + * Test for {@link SplitPositionChangeListeners}. + * + * @author Vaadin Ltd + */ +public class SplitPositionChange extends AbstractTestUIWithLog { + + @Override + protected void setup(VaadinRequest request) { + addSplitPanel(true, "Left", "Right"); + addSplitPanel(false, "Top", "Bottom"); + } + + private void addSplitPanel(final boolean horizontal, String firstCaption, + String secondCaption) { + AbstractSplitPanel splitPanel; + if (horizontal) { + splitPanel = new HorizontalSplitPanel(); + } else { + splitPanel = new VerticalSplitPanel(); + } + splitPanel.setWidth("200px"); + splitPanel.setHeight("200px"); + splitPanel.addComponent(buildPanel(firstCaption)); + splitPanel.addComponent(buildPanel(secondCaption)); + splitPanel + .addSplitPositionChangeListener(new AbstractSplitPanel.SplitPositionChangeListener() { + + @Override + public void onSplitPositionChanged( + AbstractSplitPanel.SplitPositionChangeEvent event) { + log(String.format( + "Split position changed: %s, position: %s %s", + (horizontal ? "horizontal" : "vertical"), + event.getSplitPosition(), + event.getSplitPositionUnit())); + } + }); + addComponent(splitPanel); + } + + private Panel buildPanel(String caption) { + VerticalLayout pl = new VerticalLayout(); + pl.setMargin(true); + pl.addComponent(new Label("content")); + Panel panel = new Panel(caption, pl); + panel.setSizeFull(); + return panel; + } + + @Override + protected String getTestDescription() { + return "SplitPanel should have an event for the splitter being moved"; + } + + @Override + protected Integer getTicketNumber() { + return 3855; + } + +} diff --git a/uitest/src/com/vaadin/tests/components/splitpanel/SplitPositionChangeTest.java b/uitest/src/com/vaadin/tests/components/splitpanel/SplitPositionChangeTest.java new file mode 100644 index 0000000000..43edb56f3a --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/splitpanel/SplitPositionChangeTest.java @@ -0,0 +1,82 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.components.splitpanel; + +import org.junit.Assert; +import org.junit.Test; +import org.openqa.selenium.By; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.interactions.Actions; + +import com.vaadin.testbench.elements.HorizontalSplitPanelElement; +import com.vaadin.testbench.elements.VerticalSplitPanelElement; +import com.vaadin.tests.tb3.MultiBrowserTest; + +/** + * Test for {@link SplitPositionChangeListeners}. + * + * @author Vaadin Ltd + */ +public class SplitPositionChangeTest extends MultiBrowserTest { + + @Override + public void setup() throws Exception { + super.setup(); + openTestURL(); + waitForElementPresent(By.className("v-splitpanel-horizontal")); + } + + @Test + public void testHorizontalSplit() { + HorizontalSplitPanelElement split = $(HorizontalSplitPanelElement.class) + .first(); + WebElement splitter = split.findElement(By + .className("v-splitpanel-hsplitter")); + int position = splitter.getLocation().getX(); + Actions actions = new Actions(driver); + actions.clickAndHold(splitter).moveByOffset(50, 0).release().perform(); + assertPosition(position, splitter.getLocation().getX()); + assertLogText(true); + } + + @Test + public void testVerticalSplit() { + VerticalSplitPanelElement split = $(VerticalSplitPanelElement.class) + .first(); + WebElement splitter = split.findElement(By + .className("v-splitpanel-vsplitter")); + int position = splitter.getLocation().getY(); + Actions actions = new Actions(driver); + actions.clickAndHold(splitter).moveByOffset(0, 50).release().perform(); + assertPosition(position, splitter.getLocation().getY()); + assertLogText(false); + } + + private void assertPosition(int original, int current) { + Assert.assertFalse("Position didn't change", original == current); + } + + private void assertLogText(boolean horizontal) { + String expected = String.format( + "1. Split position changed: %s, position: .*", + horizontal ? "horizontal" : "vertical"); + String actual = getLogRow(0); + Assert.assertTrue( + String.format( + "Log content didn't match the expected format.\nexpected: '%s'\nwas: '%s'", + expected, actual), actual.matches(expected)); + } +} diff --git a/uitest/src/com/vaadin/tests/components/table/TableBlurFocus.java b/uitest/src/com/vaadin/tests/components/table/TableBlurFocus.java new file mode 100644 index 0000000000..d95b406c1a --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/table/TableBlurFocus.java @@ -0,0 +1,153 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.components.table; + +import java.util.Map; + +import com.vaadin.event.FieldEvents.BlurEvent; +import com.vaadin.event.FieldEvents.BlurListener; +import com.vaadin.event.FieldEvents.FocusEvent; +import com.vaadin.event.FieldEvents.FocusListener; +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUIWithLog; +import com.vaadin.ui.Button; +import com.vaadin.ui.Button.ClickEvent; +import com.vaadin.ui.Button.ClickListener; +import com.vaadin.ui.Label; +import com.vaadin.ui.Notification; +import com.vaadin.ui.Table; + +/** + * Tests that previously focused component's blur event happens before any + * variable changes in the focused Table. + * + * @author Vaadin Ltd + */ +public class TableBlurFocus extends AbstractTestUIWithLog { + + enum Columns { + COLUMN1, COLUMN2, COLUMN3, COLUMN4, COLUMN5 + } + + private int count = 0; + private Button focusButton; + + @Override + protected void setup(VaadinRequest request) { + System.out + .println("TableBlurFocus/TableInIframeRowClickScrollJumpTest"); + Button button = new Button("click to focus"); + button.addFocusListener(new FocusListener() { + + @Override + public void focus(FocusEvent event) { + log("focus"); + } + }); + button.addBlurListener(new BlurListener() { + + @Override + public void blur(BlurEvent event) { + log("blur"); + } + }); + final Button scrollButton = new Button( + "focus lowest button to scroll down"); + scrollButton.setId("scroll-button"); + scrollButton.addClickListener(new ClickListener() { + @Override + public void buttonClick(ClickEvent event) { + focusButton.focus(); + } + }); + + Label spacerLabel = new Label("spacer"); + spacerLabel.setHeight("300px"); + + addComponent(button); + addComponent(scrollButton); + addComponent(createTable()); + addComponent(spacerLabel); + addComponent(focusButton = new Button("for focus")); + focusButton.setId("focus-button"); + focusButton.addFocusListener(new FocusListener() { + @Override + public void focus(FocusEvent event) { + focusButton.setCaption("focused"); + } + }); + } + + private Table createTable() { + Table table = new Table() { + @Override + public void changeVariables(Object source, + Map<String, Object> variables) { + log("variable change"); + super.changeVariables(source, variables); + } + }; + table.setSelectable(true); + table.setImmediate(true); + + table.addContainerProperty(Columns.COLUMN1, String.class, " "); + table.addContainerProperty(Columns.COLUMN2, Label.class, null); + table.addContainerProperty(Columns.COLUMN3, Button.class, null); + table.addContainerProperty(Columns.COLUMN4, String.class, " "); + table.setColumnCollapsingAllowed(true); + table.setColumnCollapsible(Columns.COLUMN4, true); + table.setColumnCollapsed(Columns.COLUMN4, true); + table.setSortEnabled(true); + table.setFooterVisible(true); + table.setPageLength(14); + table.addGeneratedColumn(Columns.COLUMN5, new Table.ColumnGenerator() { + + @Override + public Object generateCell(Table source, Object itemId, + Object columnId) { + return "Generated"; + } + }); + + table.setColumnHeader(Columns.COLUMN1, "Column"); + for (int x = 0; x < 120; x++) { + final Label buttonLabel = new Label("Not clicked"); + Button button = new Button("Click me?", new Button.ClickListener() { + + @Override + public void buttonClick(Button.ClickEvent event) { + ++count; + buttonLabel.setValue("Clicked " + count + " times"); + Notification.show("Clicked!"); + } + }); + table.addItem(new Object[] { "entryString" + x, buttonLabel, + button, " " }, "entryID" + x); + } + return table; + } + + @Override + protected String getTestDescription() { + return "Click button to focus, then click Table header. Blur event should arrive before the next variable change."; + } + + @Override + protected Integer getTicketNumber() { + return 15294; + } + +} diff --git a/uitest/src/com/vaadin/tests/components/table/TableBlurFocusTest.java b/uitest/src/com/vaadin/tests/components/table/TableBlurFocusTest.java new file mode 100644 index 0000000000..e5d07abc22 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/table/TableBlurFocusTest.java @@ -0,0 +1,64 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.components.table; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertEquals; + +import java.util.Arrays; + +import org.junit.Test; +import org.openqa.selenium.By; + +import com.vaadin.testbench.elements.ButtonElement; +import com.vaadin.testbench.elements.TableElement; +import com.vaadin.tests.tb3.MultiBrowserTest; + +public class TableBlurFocusTest extends MultiBrowserTest { + + @Test + public void testBlurAndFocus() throws InterruptedException { + openTestURL(); + waitForElementPresent(By.className("v-button")); + + assertAnyLogText("1. variable change"); + assertEquals("Unexpected column header,", "COLUMN2", + $(TableElement.class).first().getHeaderCell(1).getCaption()); + assertEquals("Unexpected button caption,", "click to focus", + $(ButtonElement.class).first().getCaption()); + + $(ButtonElement.class).first().click(); + assertAnyLogText("2. focus", "3. focus"); + + $(TableElement.class).first().getHeaderCell(1).click(); + assertAnyLogText("3. blur", "4. blur"); + } + + private void assertAnyLogText(String... texts) { + assertThat(String.format( + "Correct log text was not found, expected any of %s", + Arrays.asList(texts)), logContainsAnyText(texts)); + } + + private boolean logContainsAnyText(String... texts) { + for (String text : texts) { + if (logContainsText(text)) { + return true; + } + } + return false; + } +} diff --git a/uitest/src/com/vaadin/tests/components/table/TableInIframeRowClickScrollJumpTest.java b/uitest/src/com/vaadin/tests/components/table/TableInIframeRowClickScrollJumpTest.java new file mode 100644 index 0000000000..074958d671 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/table/TableInIframeRowClickScrollJumpTest.java @@ -0,0 +1,127 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.components.table; + +import static org.hamcrest.MatcherAssert.assertThat; + +import java.util.List; + +import org.junit.Test; +import org.openqa.selenium.JavascriptExecutor; +import org.openqa.selenium.WebElement; + +import com.vaadin.testbench.By; +import com.vaadin.testbench.elements.ButtonElement; +import com.vaadin.testbench.elements.TableElement; +import com.vaadin.tests.tb3.MultiBrowserTest; + +/** + * For testing that UI scroll does not jump back to up when: 1. UI is in iframe + * 2. the window scrolled down 3. and table is clicked + * + * @since + * @author Vaadin Ltd + */ +public class TableInIframeRowClickScrollJumpTest extends MultiBrowserTest { + + private static final String TEST_URL = "statictestfiles/TableInIframeRowClickScrollJumpTest.html"; + + @Test + public void testRowClicking_WhenScrolledDown_shouldMaintainScrollPosition() + throws InterruptedException { + System.out.println(">>>" + getBaseURL() + TEST_URL); + + driver.get(getUrl()); + + // using non-standard way because of iframe + sleep(4000); + + // make sure we are in the "main content" + driver.switchTo().defaultContent(); + sleep(2000); + switchIntoIframe(); + + // using non-standard way because of iframe + waitForElementVisible(By.id("scroll-button")); + + ButtonElement scrollbutton = $(ButtonElement.class).id("scroll-button"); + scrollbutton.click(); + + // using non-standard way because of iframe + sleep(1000); + + Long scrollPosition = getWindowsScrollPosition(); + + assertThat("Scroll position should be greater than 100 (it was " + + scrollPosition + ")", scrollPosition > 100); + + TableElement table = $(TableElement.class).first(); + table.getRow(13).getCell(0).click(); + + // using non-standard way because of iframe + sleep(1000); + + Long scrollPosition2 = getWindowsScrollPosition(); + + assertThat("Scroll position should stay about the same. Old was " + + scrollPosition + " and new one " + scrollPosition2, + Math.abs(scrollPosition - scrollPosition2) < 10); + } + + private String getUrl() { + String url; + // using non-standard way because of iframe + if (getBaseURL().charAt(getBaseURL().length() - 1) == '/') { + url = getBaseURL() + TEST_URL; + } else { + // this one is for gerrit's teamcity :( + url = getBaseURL() + '/' + TEST_URL; + } + return url; + } + + public void switchIntoIframe() { + List<WebElement> frames = driver.findElements(By.tagName("iframe")); + assertThat("No frames was found", frames.size() > 0); + driver.switchTo().frame(frames.get(0)); + } + + private Long getWindowsScrollPosition() { + // measure scroll pos in the main window + driver.switchTo().defaultContent(); + + JavascriptExecutor executor = (JavascriptExecutor) driver; + Long value = (Long) executor + .executeScript("if (window.pageYOffset) return window.pageYOffset;else if (window.document.documentElement.scrollTop) return window.document.documentElement.scrollTop;else return window.document.body.scrollTop;"); + + // back to the iframe + switchIntoIframe(); + + return value; + } + + @Override + // using non-standard way because of iframe + protected void closeApplication() { + if (driver != null) { + try { + driver.get(getUrl() + "?closeApplication"); + } catch (Exception e) { + e.printStackTrace(); + } + } + } +} diff --git a/uitest/src/com/vaadin/tests/components/table/TableSortingStopsWorkingOnChrome.java b/uitest/src/com/vaadin/tests/components/table/TableSortingStopsWorkingOnChrome.java new file mode 100644 index 0000000000..23d0581e5a --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/table/TableSortingStopsWorkingOnChrome.java @@ -0,0 +1,150 @@ +package com.vaadin.tests.components.table; + +import java.io.Serializable; + +import com.vaadin.annotations.Theme; +import com.vaadin.data.util.BeanItemContainer; +import com.vaadin.event.dd.DragAndDropEvent; +import com.vaadin.event.dd.DropHandler; +import com.vaadin.event.dd.acceptcriteria.AcceptAll; +import com.vaadin.event.dd.acceptcriteria.AcceptCriterion; +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.Table; +import com.vaadin.ui.Table.ColumnReorderEvent; +import com.vaadin.ui.Table.ColumnReorderListener; +import com.vaadin.ui.Table.HeaderClickEvent; +import com.vaadin.ui.Table.HeaderClickListener; +import com.vaadin.ui.VerticalLayout; + +@Theme("valo") +@SuppressWarnings("serial") +public class TableSortingStopsWorkingOnChrome extends AbstractTestUI { + + protected static final int ROW_COUNT = 100; + + @Override + protected void setup(VaadinRequest request) { + VerticalLayout layout = new VerticalLayout(); + layout.setSizeFull(); + + final Table table = new Table(); + table.setColumnReorderingAllowed(true); + table.setSizeFull(); + + BeanItemContainer<TestItem> cont = new BeanItemContainer<TestItem>( + TestItem.class); + + for (int i = 0; i < ROW_COUNT; i++) { + TestItem ti = new TestItem(); + ti.setValue1("Value1_" + i); + ti.setValue2("Value2_" + (ROW_COUNT - i)); + ti.setValue3("Value3_" + i); + ti.setValue4("Value4_" + (ROW_COUNT - i)); + ti.setValue5("Value5_" + i); + cont.addBean(ti); + } + + table.setContainerDataSource(cont); + table.setImmediate(true); + table.setSelectable(true); + table.setMultiSelect(false); + + table.setPageLength(10); + table.setDragMode(Table.TableDragMode.ROW); + + table.setDropHandler(new DropHandler() { + @Override + public void drop(DragAndDropEvent dragAndDropEvent) { + + } + + @Override + public AcceptCriterion getAcceptCriterion() { + return AcceptAll.get(); + } + }); + + table.addColumnReorderListener(new ColumnReorderListener() { + + @Override + public void columnReorder(ColumnReorderEvent event) { + System.out.println("columnReorder"); + } + }); + + table.addHeaderClickListener(new HeaderClickListener() { + + @Override + public void headerClick(HeaderClickEvent event) { + System.out.println("Header was clicked"); + } + }); + + layout.addComponent(table); + + addComponent(layout); + } + + public class TestItem implements Serializable { + private static final long serialVersionUID = -745849615488792221L; + + private String value1; + private String value2; + private String value3; + private String value4; + private String value5; + + public String getValue1() { + return value1; + } + + public void setValue1(String value1) { + this.value1 = value1; + } + + public String getValue2() { + return value2; + } + + public void setValue2(String value2) { + this.value2 = value2; + } + + public String getValue3() { + return value3; + } + + public void setValue3(String value3) { + this.value3 = value3; + } + + public String getValue4() { + return value4; + } + + public void setValue4(String value4) { + this.value4 = value4; + } + + public String getValue5() { + return value5; + } + + public void setValue5(String value5) { + this.value5 = value5; + } + + } + + @Override + protected String getTestDescription() { + return "After an indeterminate period of time sorting tables via clicking on the column header should not stop working on Chrome"; + } + + @Override + protected Integer getTicketNumber() { + return 14796; + } + +} diff --git a/uitest/src/com/vaadin/tests/dd/DDInterrupt.java b/uitest/src/com/vaadin/tests/dd/DDInterrupt.java new file mode 100644 index 0000000000..1939bbbce5 --- /dev/null +++ b/uitest/src/com/vaadin/tests/dd/DDInterrupt.java @@ -0,0 +1,87 @@ +package com.vaadin.tests.dd; + +import com.vaadin.annotations.Widgetset; +import com.vaadin.event.dd.DragAndDropEvent; +import com.vaadin.event.dd.DropHandler; +import com.vaadin.event.dd.TargetDetailsImpl; +import com.vaadin.event.dd.acceptcriteria.AcceptAll; +import com.vaadin.event.dd.acceptcriteria.AcceptCriterion; +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.tests.widgetset.TestingWidgetSet; +import com.vaadin.ui.Button; +import com.vaadin.ui.Button.ClickEvent; +import com.vaadin.ui.CssLayout; +import com.vaadin.ui.DragAndDropWrapper; +import com.vaadin.ui.DragAndDropWrapper.DragStartMode; +import com.vaadin.ui.Label; + +/** + * Test for interrupting drag-and-drop. + * + * @author Vaadin Ltd + */ +@Widgetset(TestingWidgetSet.NAME) +public class DDInterrupt extends AbstractTestUI { + + private SpacebarPanner sp; + + @Override + protected void setup(VaadinRequest vaadinRequest) { + final CssLayout layout = new CssLayout(); + layout.setWidth("500px"); + layout.setHeight("500px"); + + addButton("Click to interrupt next drag.", new Button.ClickListener() { + + @Override + public void buttonClick(ClickEvent event) { + sp.interruptNext(); + } + }); + + final Label draggableLabel = new Label("Drag me"); + draggableLabel.setId("draggable"); + + DragAndDropWrapper dndLayoutWrapper = new DragAndDropWrapper(layout); + addComponent(dndLayoutWrapper); + + DragAndDropWrapper labelWrapper = new DragAndDropWrapper(draggableLabel); + draggableLabel.setSizeUndefined(); + labelWrapper.setDragStartMode(DragStartMode.COMPONENT); + labelWrapper.setSizeUndefined(); + + layout.addComponent(labelWrapper); + + dndLayoutWrapper.setDropHandler(new DropHandler() { + + @Override + public AcceptCriterion getAcceptCriterion() { + return AcceptAll.get(); + } + + @Override + public void drop(DragAndDropEvent event) { + TargetDetailsImpl targetDetails = (TargetDetailsImpl) event + .getTargetDetails(); + int x = targetDetails.getMouseEvent().getRelativeX(); + int y = targetDetails.getMouseEvent().getRelativeY(); + + draggableLabel.setWidth(x, Unit.PIXELS); + draggableLabel.setHeight(y, Unit.PIXELS); + } + }); + + sp = SpacebarPanner.wrap(this); + } + + @Override + protected Integer getTicketNumber() { + return 17163; + } + + @Override + protected String getTestDescription() { + return "Hold space while clicking and dragging the label, or click the button before draggin the label. There should be no client-side exception."; + } +} diff --git a/uitest/src/com/vaadin/tests/dd/DDInterruptTest.java b/uitest/src/com/vaadin/tests/dd/DDInterruptTest.java new file mode 100644 index 0000000000..e078bc4665 --- /dev/null +++ b/uitest/src/com/vaadin/tests/dd/DDInterruptTest.java @@ -0,0 +1,97 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.dd; + +import org.junit.Assert; +import org.junit.Test; +import org.openqa.selenium.By; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.interactions.Actions; +import org.openqa.selenium.support.ui.ExpectedCondition; + +import com.vaadin.testbench.elements.ButtonElement; +import com.vaadin.testbench.elements.LabelElement; +import com.vaadin.testbench.elements.UIElement; +import com.vaadin.tests.tb3.MultiBrowserTest; + +/** + * Test for interrupting drag-and-drop. + * + * @author Vaadin Ltd + */ +public class DDInterruptTest extends MultiBrowserTest { + + private UIElement ui; + + @Override + public void setup() throws Exception { + super.setup(); + openTestURL("debug"); + ui = $(UIElement.class).first(); + } + + @Test + public void testRegularDragging() { + dragElement(); + assertNoNotifications(); + assertDragged(true); + } + + @Test + public void testTriggeredDragging() { + $(ButtonElement.class).first().click(); + waitUntilTriggered(true); + + dragElement(); + + waitUntilTriggered(false); + assertNoNotifications(); + assertDragged(false); + } + + private void dragElement() { + LabelElement label = $(LabelElement.class).id("draggable"); + new Actions(driver).moveToElement(label).clickAndHold() + .moveByOffset(400, 400).perform(); + } + + private void assertNoNotifications() { + Assert.assertEquals( + "Notification found when there should have been none,", 0, + findElements(By.className("v-Notification")).size()); + } + + private void assertDragged(boolean dragged) { + Assert.assertEquals("Unexpected drag state,", dragged ? 1 : 0, + findElements(By.className("v-ddwrapper-over-bottom")).size()); + } + + private void waitUntilTriggered(final boolean triggered) { + waitUntil(new ExpectedCondition<Boolean>() { + + @Override + public Boolean apply(WebDriver arg0) { + return triggered == hasCssClass(ui, "triggered"); + } + + @Override + public String toString() { + return String.format("UI to %s stylename 'triggered'", + (triggered ? "get" : "lose")); + } + }); + } +} diff --git a/uitest/src/com/vaadin/tests/dd/SpacebarPanner.java b/uitest/src/com/vaadin/tests/dd/SpacebarPanner.java new file mode 100644 index 0000000000..d6179066fb --- /dev/null +++ b/uitest/src/com/vaadin/tests/dd/SpacebarPanner.java @@ -0,0 +1,20 @@ +package com.vaadin.tests.dd; + +import com.vaadin.server.AbstractExtension; +import com.vaadin.ui.UI; + +public class SpacebarPanner extends AbstractExtension { + + private static final long serialVersionUID = -7712258690917457123L; + + public static SpacebarPanner wrap(UI ui) { + SpacebarPanner panner = new SpacebarPanner(); + panner.extend(ui); + return panner; + } + + public void interruptNext() { + getState().enabled = !getState().enabled; + } + +} diff --git a/uitest/src/com/vaadin/tests/declarative/DeclarativeEditor.java b/uitest/src/com/vaadin/tests/declarative/DeclarativeEditor.java index 17942ee201..d62f71b513 100644 --- a/uitest/src/com/vaadin/tests/declarative/DeclarativeEditor.java +++ b/uitest/src/com/vaadin/tests/declarative/DeclarativeEditor.java @@ -15,11 +15,11 @@ */ package com.vaadin.tests.declarative; +import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import org.apache.commons.io.IOUtils; -import org.apache.tools.ant.filters.StringInputStream; import com.vaadin.annotations.Theme; import com.vaadin.data.Property.ReadOnlyException; @@ -87,7 +87,7 @@ public class DeclarativeEditor extends UI { return; } - dc = Design.read(new StringInputStream(string), null); + dc = Design.read(new ByteArrayInputStream(string.getBytes()), null); treeHolder.removeAllComponents(); treeHolder.addComponent(dc.getRootComponent()); diff --git a/uitest/src/com/vaadin/tests/push/PushPath.java b/uitest/src/com/vaadin/tests/push/PushPath.java deleted file mode 100644 index 20771bd84f..0000000000 --- a/uitest/src/com/vaadin/tests/push/PushPath.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2000-2014 Vaadin Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package com.vaadin.tests.push; - -import com.vaadin.annotations.Push; -import com.vaadin.server.VaadinRequest; -import com.vaadin.shared.ui.ui.Transport; -import com.vaadin.tests.components.AbstractTestUI; -import com.vaadin.ui.Label; - -@Push(transport = Transport.WEBSOCKET) -public class PushPath extends AbstractTestUI { - - public static final String PUSH_PATH_LABEL_ID = "push-path-label-id"; - public static final String PUSH_PATH_LABEL_TEXT = "Label by push"; - - @Override - protected void setup(VaadinRequest request) { - // use only websockets - getPushConfiguration().setFallbackTransport(Transport.WEBSOCKET); - - String pushPath = request.getService().getDeploymentConfiguration() - .getPushPath(); - String transport = getPushConfiguration().getTransport().name(); - Label pushPathLabel = new Label(String.format( - "Waiting for push from path '%s' using %s in 3 seconds.", - pushPath, transport)); - addComponent(pushPathLabel); - - new PushThread().start(); - } - - public class PushThread extends Thread { - - @Override - public void run() { - try { - Thread.sleep(3000); - } catch (InterruptedException e) { - } - access(new Runnable() { - - @Override - public void run() { - Label pushLabel = new Label(PUSH_PATH_LABEL_TEXT); - pushLabel.setId(PUSH_PATH_LABEL_ID); - addComponent(pushLabel); - } - }); - - } - } - - @Override - public Integer getTicketNumber() { - return 14432; - } - - @Override - public String getDescription() { - return "Push path should be configurable since some servers can't serve both websockets and long polling from same URL."; - } - -} diff --git a/uitest/src/com/vaadin/tests/util/ResizeTerrorizer.java b/uitest/src/com/vaadin/tests/util/ResizeTerrorizer.java new file mode 100644 index 0000000000..f124305d8a --- /dev/null +++ b/uitest/src/com/vaadin/tests/util/ResizeTerrorizer.java @@ -0,0 +1,51 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.util; + +import com.vaadin.tests.widgetset.client.ResizeTerrorizerControlConnector.ResizeTerorrizerState; +import com.vaadin.ui.AbstractComponent; +import com.vaadin.ui.Component; +import com.vaadin.ui.VerticalLayout; + +public class ResizeTerrorizer extends VerticalLayout { + private final ResizeTerrorizerControl control; + + public class ResizeTerrorizerControl extends AbstractComponent implements + Component { + + public ResizeTerrorizerControl(Component target) { + getState().target = target; + } + + @Override + protected ResizeTerorrizerState getState() { + return (ResizeTerorrizerState) super.getState(); + } + } + + public ResizeTerrorizer(Component target) { + target.setWidth("700px"); + setSizeFull(); + addComponent(target); + setExpandRatio(target, 1); + control = new ResizeTerrorizerControl(target); + addComponent(control); + } + + public void setDefaultWidthOffset(int px) { + control.getState().defaultWidthOffset = px; + } +} diff --git a/uitest/src/com/vaadin/tests/widgetset/client/ResizeTerrorizerControlConnector.java b/uitest/src/com/vaadin/tests/widgetset/client/ResizeTerrorizerControlConnector.java new file mode 100644 index 0000000000..9fe037706b --- /dev/null +++ b/uitest/src/com/vaadin/tests/widgetset/client/ResizeTerrorizerControlConnector.java @@ -0,0 +1,157 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.widgetset.client; + +import com.google.gwt.animation.client.AnimationScheduler; +import com.google.gwt.animation.client.AnimationScheduler.AnimationCallback; +import com.google.gwt.event.dom.client.ClickEvent; +import com.google.gwt.event.dom.client.ClickHandler; +import com.google.gwt.user.client.ui.Button; +import com.google.gwt.user.client.ui.FlowPanel; +import com.google.gwt.user.client.ui.IntegerBox; +import com.google.gwt.user.client.ui.Label; +import com.google.gwt.user.client.ui.RequiresResize; +import com.vaadin.client.ui.AbstractComponentConnector; +import com.vaadin.client.ui.PostLayoutListener; +import com.vaadin.shared.AbstractComponentState; +import com.vaadin.shared.Connector; +import com.vaadin.shared.ui.Connect; +import com.vaadin.tests.util.ResizeTerrorizer; + +@Connect(ResizeTerrorizer.ResizeTerrorizerControl.class) +public class ResizeTerrorizerControlConnector extends + AbstractComponentConnector implements PostLayoutListener { + + public static class ResizeTerorrizerState extends AbstractComponentState { + public Connector target; + public int defaultWidthOffset = 200; + } + + public class ResizeTerrorizerControlPanel extends FlowPanel { + private Label results = new Label("Results"); + private IntegerBox startWidth = new IntegerBox(); + private IntegerBox endWidth = new IntegerBox(); + private final Button terrorizeButton = new Button("Terrorize", + new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + terrorize(startWidth.getValue(), endWidth.getValue(), + 1000); + } + }); + + public ResizeTerrorizerControlPanel() { + add(new Label("Start width")); + add(startWidth); + + add(new Label("End width")); + add(endWidth); + + add(terrorizeButton); + add(results); + + startWidth.getElement().setId("terror-start-width"); + endWidth.getElement().setId("terror-end-width"); + terrorizeButton.getElement().setId("terror-button"); + results.getElement().setId("terror-results"); + } + + private void showResults(String results) { + Integer temp = startWidth.getValue(); + startWidth.setValue(endWidth.getValue()); + endWidth.setValue(temp); + + this.results.setText(results); + } + } + + private void terrorize(final double startWidth, final double endWidth, + final double duration) { + final AbstractComponentConnector target = getTarget(); + + final AnimationScheduler scheduler = AnimationScheduler.get(); + AnimationCallback callback = new AnimationCallback() { + double startTime = -1; + int frameCount = 0; + + @Override + public void execute(double timestamp) { + frameCount++; + + boolean done = false; + if (startTime == -1) { + startTime = timestamp; + } + + double time = timestamp - startTime; + if (time > duration) { + time = duration; + done = true; + } + + double progress = time / duration; + double widthToSet = startWidth + (endWidth - startWidth) + * progress; + + // TODO Optionally inform LayoutManager as well + target.getWidget().setWidth(widthToSet + "px"); + if (target.getWidget() instanceof RequiresResize) { + ((RequiresResize) target.getWidget()).onResize(); + } + + if (!done) { + scheduler.requestAnimationFrame(this); + } else { + double fps = Math.round(frameCount / (duration / 1000)); + String results = frameCount + " frames, " + fps + " fps"; + + getWidget().showResults(results); + } + } + }; + scheduler.requestAnimationFrame(callback); + } + + private AbstractComponentConnector getTarget() { + return (AbstractComponentConnector) getState().target; + } + + @Override + public ResizeTerorrizerState getState() { + return (ResizeTerorrizerState) super.getState(); + } + + @Override + public ResizeTerrorizerControlPanel getWidget() { + return (ResizeTerrorizerControlPanel) super.getWidget(); + } + + @Override + protected ResizeTerrorizerControlPanel createWidget() { + return new ResizeTerrorizerControlPanel(); + } + + @Override + public void postLayout() { + if (getWidget().startWidth.getValue() == null) { + int width = getTarget().getWidget().getElement().getOffsetWidth(); + getWidget().startWidth.setValue(width); + getWidget().endWidth + .setValue(width + getState().defaultWidthOffset); + } + } + +} diff --git a/uitest/src/com/vaadin/tests/widgetset/client/dd/SpacebarPannerConnector.java b/uitest/src/com/vaadin/tests/widgetset/client/dd/SpacebarPannerConnector.java new file mode 100644 index 0000000000..e13dd5030b --- /dev/null +++ b/uitest/src/com/vaadin/tests/widgetset/client/dd/SpacebarPannerConnector.java @@ -0,0 +1,125 @@ +package com.vaadin.tests.widgetset.client.dd; + +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.google.gwt.dom.client.Element; +import com.google.gwt.dom.client.NativeEvent; +import com.google.gwt.event.dom.client.KeyCodes; +import com.google.gwt.user.client.Event; +import com.google.gwt.user.client.Event.NativePreviewEvent; +import com.google.gwt.user.client.Event.NativePreviewHandler; +import com.vaadin.client.ServerConnector; +import com.vaadin.client.annotations.OnStateChange; +import com.vaadin.client.extensions.AbstractExtensionConnector; +import com.vaadin.client.ui.VUI; +import com.vaadin.client.ui.dd.VDragAndDropManager; +import com.vaadin.client.ui.ui.UIConnector; +import com.vaadin.shared.ui.Connect; + +@Connect(com.vaadin.tests.dd.SpacebarPanner.class) +public class SpacebarPannerConnector extends AbstractExtensionConnector { + + Logger logger = Logger.getLogger(SpacebarPannerConnector.class + .getSimpleName()); + + private boolean trigger = false; + private VUI vui; + private boolean first = true; + + @Override + protected void extend(ServerConnector target) { + UIConnector uic = (UIConnector) target; + vui = uic.getWidget(); + Event.sinkEvents(vui.getElement(), Event.MOUSEEVENTS | Event.KEYEVENTS); + Event.addNativePreviewHandler(createNativePreviewHandler()); + } + + @OnStateChange("enabled") + private void trigger() { + if (first) { + // ignore initial state change + first = false; + } else { + trigger = true; + vui.addStyleName("triggered"); + } + } + + private NativePreviewHandler createNativePreviewHandler() { + return new NativePreviewHandler() { + + private boolean spacebarDown = false; + private boolean shouldPan = false; + private boolean mouseDown = false; + + private int lastMouseX; + private int lastMouseY; + + @Override + public void onPreviewNativeEvent(NativePreviewEvent event) { + NativeEvent ne = event.getNativeEvent(); + int type = event.getTypeInt(); + + switch (type) { + case Event.ONKEYDOWN: + if (ne.getKeyCode() == KeyCodes.KEY_SPACE) { + event.cancel(); + ne.preventDefault(); + ne.stopPropagation(); + spacebarDown = true; + break; + } + case Event.ONKEYUP: + if (ne.getKeyCode() == KeyCodes.KEY_SPACE) { + spacebarDown = false; + } + break; + case Event.ONMOUSEDOWN: + logger.log(Level.INFO, "Drag started"); + lastMouseX = ne.getClientX(); + lastMouseY = ne.getClientY(); + + shouldPan = spacebarDown || trigger; + mouseDown = true; + break; + + case Event.ONMOUSEUP: + shouldPan = false || trigger; + mouseDown = false; + break; + + case Event.ONMOUSEMOVE: + if (mouseDown && shouldPan) { + logger.log(Level.INFO, "In mousemove: mouseDown:" + + mouseDown + ", shouldPan: " + shouldPan); + trigger = false; + vui.removeStyleName("triggered"); + + logger.log(Level.INFO, "Panning!"); + int currentClientX = ne.getClientX(); + int currentClientY = ne.getClientY(); + + int deltaX = lastMouseX - currentClientX; + int deltaY = lastMouseY - currentClientY; + lastMouseX = currentClientX; + lastMouseY = currentClientY; + + // this causes #17163 + VDragAndDropManager.get().interruptDrag(); + + Element uiElement = vui.getElement(); + int top = uiElement.getScrollTop(); + int left = uiElement.getScrollLeft(); + + uiElement.setScrollTop(top + deltaY); + uiElement.setScrollLeft(left + deltaX); + } + + default: + break; + } + } + }; + } +} diff --git a/uitest/src/com/vaadin/tests/widgetset/rebind/TestWidgetRegistryGenerator.java b/uitest/src/com/vaadin/tests/widgetset/rebind/TestWidgetRegistryGenerator.java index 1bdbba2c36..c7b29e271b 100644 --- a/uitest/src/com/vaadin/tests/widgetset/rebind/TestWidgetRegistryGenerator.java +++ b/uitest/src/com/vaadin/tests/widgetset/rebind/TestWidgetRegistryGenerator.java @@ -136,6 +136,9 @@ public class TestWidgetRegistryGenerator extends Generator { } else if (!widgetType.getPackage().getName() .startsWith(TestWidgetConnector.class.getPackage().getName())) { return false; + } else if (widgetType.getEnclosingType() != null + && !widgetType.isStatic()) { + return false; } return true; |