Browse Source

Merge branch 'master' into fix11576

fix11576
Tatu Lund 3 years ago
parent
commit
60d4551b4d
No account linked to committer's email address
100 changed files with 1760 additions and 520 deletions
  1. 186
    0
      README-DEV.md
  2. 12
    84
      README.md
  3. 1
    1
      all/pom.xml
  4. 6
    8
      all/src/main/templates/release-notes.html
  5. 1
    1
      bom/pom.xml
  6. 1
    1
      client-compiled/pom.xml
  7. 1
    1
      client-compiler/pom.xml
  8. 1
    1
      client/pom.xml
  9. 24
    7
      client/src/main/java/com/vaadin/client/Util.java
  10. 22
    0
      client/src/main/java/com/vaadin/client/WidgetUtil.java
  11. 1
    1
      client/src/main/java/com/vaadin/client/communication/DefaultConnectionStateHandler.java
  12. 56
    7
      client/src/main/java/com/vaadin/client/connectors/grid/GridConnector.java
  13. 39
    3
      client/src/main/java/com/vaadin/client/ui/ShortcutActionHandler.java
  14. 34
    10
      client/src/main/java/com/vaadin/client/ui/VAbstractCalendarPanel.java
  15. 3
    0
      client/src/main/java/com/vaadin/client/ui/VAbstractTextualDate.java
  16. 75
    27
      client/src/main/java/com/vaadin/client/ui/VComboBox.java
  17. 2
    0
      client/src/main/java/com/vaadin/client/ui/VContextMenu.java
  18. 2
    2
      client/src/main/java/com/vaadin/client/ui/VRichTextArea.java
  19. 65
    8
      client/src/main/java/com/vaadin/client/ui/VUpload.java
  20. 10
    4
      client/src/main/java/com/vaadin/client/ui/VWindow.java
  21. 6
    1
      client/src/main/java/com/vaadin/client/ui/optiongroup/RadioButtonGroupConnector.java
  22. 10
    0
      client/src/main/java/com/vaadin/client/ui/orderedlayout/AbstractOrderedLayoutConnector.java
  23. 25
    1
      client/src/main/java/com/vaadin/client/ui/richtextarea/VRichTextToolbar.java
  24. 24
    25
      client/src/main/java/com/vaadin/client/ui/upload/UploadConnector.java
  25. 8
    2
      client/src/main/java/com/vaadin/client/ui/window/WindowConnector.java
  26. 70
    11
      client/src/main/java/com/vaadin/client/widgets/Escalator.java
  27. 180
    84
      client/src/main/java/com/vaadin/client/widgets/Grid.java
  28. 1
    1
      compatibility-client-compiled/pom.xml
  29. 1
    1
      compatibility-client/pom.xml
  30. 2
    17
      compatibility-client/src/main/java/com/vaadin/v7/client/ui/VFilterSelect.java
  31. 14
    5
      compatibility-client/src/main/java/com/vaadin/v7/client/ui/VScrollTable.java
  32. 3
    0
      compatibility-client/src/main/java/com/vaadin/v7/client/ui/VTextualDate.java
  33. 1
    0
      compatibility-client/src/main/java/com/vaadin/v7/client/widgets/Escalator.java
  34. 46
    24
      compatibility-client/src/main/java/com/vaadin/v7/client/widgets/Grid.java
  35. 1
    1
      compatibility-server-gae/pom.xml
  36. 1
    1
      compatibility-server/pom.xml
  37. 3
    1
      compatibility-server/src/main/java/com/vaadin/v7/ui/ComboBox.java
  38. 2
    0
      compatibility-server/src/main/java/com/vaadin/v7/ui/Grid.java
  39. 35
    1
      compatibility-server/src/main/java/com/vaadin/v7/ui/Table.java
  40. 1
    1
      compatibility-shared/pom.xml
  41. 1
    1
      compatibility-themes/pom.xml
  42. 1
    0
      compatibility-themes/src/main/themes/VAADIN/themes/base/table/table.scss
  43. 1
    0
      documentation/articles/FindingTheCurrentUIAndPageAndVaadinSession.asciidoc
  44. 9
    2
      documentation/articles/LettingTheUserDownloadAFile.asciidoc
  45. 7
    0
      documentation/components/components-grid.asciidoc
  46. 13
    0
      documentation/datamodel/datamodel-forms.asciidoc
  47. 1
    1
      liferay-integration/pom.xml
  48. 1
    1
      liferay/pom.xml
  49. 1
    1
      osgi-integration/pom.xml
  50. 16
    2
      pom.xml
  51. 1
    1
      push/pom.xml
  52. 1
    1
      server/bnd.bnd
  53. 1
    1
      server/pom.xml
  54. 5
    2
      server/src/main/java/com/vaadin/data/BeanValidationBinder.java
  55. 135
    19
      server/src/main/java/com/vaadin/data/Binder.java
  56. 2
    1
      server/src/main/java/com/vaadin/data/RequiredFieldConfigurator.java
  57. 0
    5
      server/src/main/java/com/vaadin/data/ValueContext.java
  58. 2
    2
      server/src/main/java/com/vaadin/data/provider/HierarchicalDataCommunicator.java
  59. 13
    2
      server/src/main/java/com/vaadin/data/provider/TreeDataProvider.java
  60. 5
    0
      server/src/main/java/com/vaadin/navigator/Navigator.java
  61. 2
    4
      server/src/main/java/com/vaadin/server/BootstrapHandler.java
  62. 14
    0
      server/src/main/java/com/vaadin/server/Sizeable.java
  63. 18
    6
      server/src/main/java/com/vaadin/server/VaadinService.java
  64. 6
    7
      server/src/main/java/com/vaadin/server/communication/HeartbeatHandler.java
  65. 6
    9
      server/src/main/java/com/vaadin/server/communication/PushHandler.java
  66. 20
    2
      server/src/main/java/com/vaadin/ui/AbstractComponent.java
  67. 119
    21
      server/src/main/java/com/vaadin/ui/AbstractDateField.java
  68. 6
    4
      server/src/main/java/com/vaadin/ui/ConnectorTracker.java
  69. 9
    3
      server/src/main/java/com/vaadin/ui/Grid.java
  70. 12
    0
      server/src/main/java/com/vaadin/ui/Tree.java
  71. 5
    4
      server/src/main/java/com/vaadin/ui/UI.java
  72. 60
    35
      server/src/main/java/com/vaadin/ui/Upload.java
  73. 5
    1
      server/src/main/java/com/vaadin/ui/components/grid/GridRowDragger.java
  74. 2
    2
      server/src/main/java/com/vaadin/ui/declarative/Design.java
  75. 5
    0
      server/src/main/java/com/vaadin/ui/themes/ValoTheme.java
  76. 37
    5
      server/src/main/java/com/vaadin/util/TimeZoneUtil.java
  77. 77
    2
      server/src/test/java/com/vaadin/data/BinderTest.java
  78. 49
    4
      server/src/test/java/com/vaadin/data/provider/TreeDataProviderTest.java
  79. 7
    5
      server/src/test/java/com/vaadin/data/provider/hierarchical/HierarchyMapperWithDataTest.java
  80. 20
    0
      server/src/test/java/com/vaadin/tests/server/component/grid/GridRowDraggerOneGridTest.java
  81. 11
    2
      server/src/test/java/com/vaadin/ui/DateFieldTestCase.java
  82. 1
    1
      shared/pom.xml
  83. 44
    4
      shared/src/main/java/com/vaadin/shared/ui/dnd/criteria/Criterion.java
  84. 4
    1
      shared/src/main/java/com/vaadin/shared/ui/upload/UploadClientRpc.java
  85. 3
    0
      shared/src/main/java/com/vaadin/shared/ui/upload/UploadServerRpc.java
  86. 13
    0
      shared/src/main/java/com/vaadin/shared/ui/upload/UploadState.java
  87. 1
    1
      test/addon-using-init-param-widget-set/pom.xml
  88. 1
    1
      test/addon-using-no-defined-widget-set/pom.xml
  89. 1
    1
      test/addon-using-own-widget-set/pom.xml
  90. 1
    1
      test/bean-api-validation/pom.xml
  91. 1
    1
      test/bean-impl-validation/pom.xml
  92. 1
    1
      test/cdi/pom.xml
  93. 1
    1
      test/default-widget-set/pom.xml
  94. 1
    1
      test/dependency-rewrite-addon/pom.xml
  95. 1
    1
      test/dependency-rewrite/pom.xml
  96. 1
    1
      test/own-widget-set/pom.xml
  97. 1
    1
      test/pom.xml
  98. 1
    1
      test/servlet-containers/generic-tests/pom.xml
  99. 1
    1
      test/servlet-containers/generic-ui/pom.xml
  100. 0
    0
      test/servlet-containers/generic/pom.xml

+ 186
- 0
README-DEV.md View File

# Development Instructions

To contribute, first refer to [Contributing Code](https://github.com/vaadin/framework/blob/master/CONTRIBUTING.md)
for general instructions and requirements for contributing code to the Vaadin framework.

We appreciate all contributors and want to make submitting changes as easy as possible. If you find any mistakes, unclear parts or out-of-date instructions, please let us know by submitting an issue or a pull request.

#### Table of Contents
1. [Building a package](#building-a-package)
1. [About committing changes](#about-committing-changes)
1. [Eclipse quick setup](#eclipse-quick-setup)
1. [IntelliJ IDEA quick setup](#intellij-idea-quick-setup)

## Building a package

The distribution files can be built by running the standard Maven goal `mvn install` in the project root.

## About committing changes

Despite our best efforts the formatting options aren't always entirely consistent between different development environments, and sometimes we miss inconsistent formatting during code review. When you commit your changes for a pull request, try to make sure that the commit _only contains changes that are relevant to your patch,_ or at least closely affiliated with the relevant changes. Random formatting changes all over the changed file(s) make it difficult to grasp the main purpose of your patch.

## Eclipse quick setup

For IntelliJ IDEA users, see [IntelliJ IDEA Quick Setup](#intellij-idea-quick-setup).

1. Decide were you would like your Eclipse workspace to be located.
* This project contains multiple modules and uses configurations that might clash with your existing projects, using a separate workspace is recommended.
* Eclipse Oxygen is recommended, different versions may treat formatting rules differently.
* If you are using Windows, you may wish to keep the workspace path reasonably short (e.g. `C:\dev\<workspaceName>`) to avoid problems with too long file paths.
1. Start Eclipse with your chosen workspace and set up [workspace preferences](#workspace-preferences).
1. Clone the repository within your selected workspace using Eclipse's clone wizard, using your favorite Git tool, or in command-line running
<code>git clone https://github.com/vaadin/framework.git</code> command.
* Eclipse's clone wizard can be found within Git perspective (*Window* -> *Perspectives* -> *Open Perspective* -> *Git*). _Only_ clone the project at this stage, do not let the clone wizard import projects as well.
* If using Windows, you might also want to add these Git settings: `core.autocrlf=false` and `core.fileMode=false`. You can do this in Eclipse by right-clicking the repository in Git perspective, clicking *Properties*, then *Add Entry...* and using key `core.autocrlf` and value `false` etc.
* If too long file paths become a problem you may also need `core.longpaths=true`.
1. Import the project into Eclipse as a Maven project. Use *File* -> *Import* -> *Maven* -> *Existing Maven Projects*.
1. Select the *framework* folder (where you cloned the project).
* It is not necessary to import all the modules, but it is recommended to include at least the root module, `vaadin-uitest` module, and any modules you may wish to make changes to. You can import more modules when needed by repeating these last steps.
1. Click “Finish” to complete the import of Vaadin Framework.

### Workspace preferences

The following preferences need to be set to keep the project consistent. You need to do this especially to be able to contribute changes to the project.

#### General

1. Open *Window* -> *Preferences* (Windows) or *Eclipse* -> *Preferences* (Mac)
1. Go to *General* -> *Workspace*
- Set *Text file encoding* to *UTF-8*
- Set *New text file line delimiter* to *Unix*
1. Go to *XML* -> *XML Files* -> *Editor*
- Ensure the settings are follows:
- Line width: 72
- Format comments: true
- Join lines: true
- Insert whitespace before closing empty end-tags: true
- Indent-using spaces: true
- Indentation size: 4
1. Go to *Java* -> *Compiler* -> *Errors*
- Switch *Serializable class without serialVersionUID* to *Ignore*
1. Go to *Java* -> *Installed JREs*
- Select a Java 8 JDK as the default

#### Configuration files

1. Open *Window* -> *Preferences* (Windows) or *Eclipse* -> *Preferences* (Mac)
1. Go to *Java* -> *Code Style* -> *Clean Up*
- Import /eclipse/VaadinCleanup.xml
1. Go to *Java* -> *Code Style* -> *Formatter*
- Import /eclipse/VaadinJavaConventions.xml

#### Save actions

1. Open *Window* -> *Preferences* (Windows) or *Eclipse* -> *Preferences* (Mac)
1. Go to *Java* -> *Editor* -> *Save Actions*
- Check *Perform the selected actions on save*
- Check *Format source code*
- Select *Format edited lines*
- Check *Organize imports*
- Check *Additional actions*
- Click *Configure*
1. In tab *Code Organizing*
- Check *Remove trailing whitespace*
- Select *All lines*
- Uncheck everything else
1. In tab *Code Style*
- Check *Use blocks in if/while/for/do statements*
- Select *Always*
- Uncheck everything else
1. In tab *Member Accesses*
- Check *Use 'this' qualifier for field accesses*
- Select *Only if necessary*
- Check *Use 'this' qualifier for method accesses*
- Select *Only if necessary*
- Uncheck everything else
1. In tab *Missing Code*
- Check *Add missing Annotations*
- Check *'@Override'*
- Check *Implementations of interface methods (1.6 or higher)*
- Check *'@Deprecated'*
1. In tab *Unnecessary Code*
- Check *Remove unused imports* and *Remove unnecessary casts*, uncheck everything else.
1. Click *OK*

After that is done, you should have 9 of 28 save actions activated and listed as such:

* Remove 'this' qualifier for non static field accesses
* Remove 'this' qualifier for non static method accesses
* Convert control statement bodies to block
* Remove unused imports
* Add missing '@Override' annotations
* Add missing '@Override' annotations to implementations of interface methods
* Add missing '@Deprecated' annotations
* Remove unnecessary casts
* Remove trailing white spaces on all lines

### Getting started

Run <code>install</code> maven goal for the project root to get started.
In Eclipse this is done by right-clicking on the project root in Project Explorer and choosing *Run As* -> *Maven Build...*. If you choose to skip tests you may need to run the <code>install</code> maven goal for `vaadin-uitest` project separately.
* Note that the first compilation takes a while to finish as Maven downloads dependencies used in the projects.
* In some Windows environments the compilation doesn't respect the `core.autocrlf=false` and the workspace preferences listed in the previous section, and running <code>install</code> converts all line endings from six core projects (root, `vaadin-client`, `vaadin-server`, `vaadin-shared`, `vaadin-testbench-api`, `vaadin-uitest`) to Windows-style. As a quick-and-dirty workaround you can change the line endings back through *File* -> *Convert Line Delimiters To* -> *Unix* and comment out references to plugins `net.revelc.code.formatter` and `com.github.dantwining.whitespace-maven-plugin` from each affected module's pom.xml (root project references them twice) to prevent it from happening again. *Do not* include those changes or any files with Windows-style line endings in any pull request. Because you consequently lose the formatting benefits of those plugins, you also need to be more careful about not including irrelevant formatting changes in your commits.

Now the project should compile without further configuration.

### Compiling the default widgetset and themes

* Compile the default widgetset by running <code>install</code> maven goal in `vaadin-client-compiled` module root.
In Eclipse this is done by right clicking on `vaadin-client-compiled` project in Project Explorer and choosing *Run As* -> *Maven Build...*.
You don't need to do this separately if you have already run <code>install</code> for the root project after your latest changes.
* Compile the default themes by running <code>install</code> maven goal in `vaadin-themes` module root.
In Eclipse this is done by right clicking on `vaadin-themes` project in Project Explorer and choosing *Run As* -> *Maven Build...*.
You don't need to do this separately if you have already run <code>install</code> for the root project after your latest changes.

### Running a UI test

#### Using DevelopmentServerLauncher (recommended)
1. In a Project Explorer navigate to *vaadin-uitest/src/main/java/com/vaadin/launcher*
1. Right-click file DevelopmentServerLauncher.java
1. Open *Run As* -> *Java Application*
1. Open URL [http://localhost:8888/run/&lt;testUI&gt;](http://localhost:8888/run/<testUI>)

#### Using Jetty
1. In a Project Explorer right-click *vaadin-uitest*
1. Open *Run As* -> *Maven build...*
1. Type in <code>jetty:run-exploded</code> into *Goals* and click *Run*
1. Open URL [http://localhost:8888/run/&lt;testUI&gt;](http://localhost:8888/run/<testUI>)

For full instructions please visit [README-TESTS.md](README-TESTS.md).

## IntelliJ IDEA quick setup

For Eclipse users, see [Eclipse Quick Setup](#eclipse-quick-setup).

1. Install and run IDEA. Ultimate Edition is better but Community Edition should also work.
1. Ensure if Git and Maven plugins are installed, properly configured and enabled.
1. Clone the repository, using menu VCS -> Checkout from Version Control -> Git -> Git Repository URL -> https://github.com/vaadin/framework.git.
When the repository is cloned, do **NOT** open it as a project.
* If you are using Windows, you may wish to keep the workspace path reasonably short (e.g. `C:\dev\<workspaceName>`) to avoid problems with too long file paths.
1. Open cloned repository as a maven object. Use File -> Open and choose root _pom.xml_ file
1. Have a coffee break while IDEA is loading dependencies and indexing the project
1. Run Maven targets <code>clean</code> and <code>install</code> using *Maven Projects* tool window to compile the whole project

Unfortunately there is no easy way to replicate [Eclipse workspace preferences](#workspace-preferences) in their entirety in IDEA, but you can use [Eclipse Code Formatter plugin](https://plugins.jetbrains.com/plugin/6546-eclipse-code-formatter) to import /eclipse/VaadinCleanup.xml and /eclipse/VaadinJavaConventions.xml as a starting point.

### Running a specific UI test

1. Open *Maven Projects*
1. Open *vaadin-uitest* -> *Plugins* -> *jetty* -> *jetty:run-exploded*
1. Open URL [http://localhost:8888/run/&lt;testUI&gt;](http://localhost:8888/run/<testUI>)

For full instructions please visit [README-TESTS.md](README-TESTS.md).

### Running a development server

1. Open *Run* menu and click *Edit Configurations*
1. Click green ***+*** sign at top left corner, select *Maven* from popup
1. In the run configuration page, set any name for the configuration, select *vaadin-uitest* project folder as *Working directory*
1. Type <code>exec:exec@run-development-server</code> into *Command line* and save the configuration
1. Run the configuration and open URL [http://localhost:8888/run/&lt;testUI&gt;](http://localhost:8888/run/<testUI>)

### Running a development server in a debug mode

1. Type <code>exec:exec@debug-development-server</code> into *Command line* and save the configuration
1. In the same dialog, create new "Remote" debug configuration, using *localhost* and *Port 5005*
1. Start both configurations and open URL [http://localhost:8888/run/&lt;testUI&gt;](http://localhost:8888/run/<testUI>)

+ 12
- 84
README.md View File



# Vaadin Framework # Vaadin Framework


*[Vaadin Framework](https://vaadin.com/framework) allows you to build modern web apps efficiently in plain Java, without touching low level web technologies.*
*[Vaadin](https://vaadin.com) allows you to build modern web apps efficiently in plain Java, without touching low level web technologies.*


For instructions about _using_ Vaadin to develop applications, please refer to [Vaadin tutorial](https://vaadin.com/docs/-/part/framework/tutorial.html) and other [documentation](https://vaadin.com/docs/).
This repository contains source code and issue tracking for Vaadin 8 and Vaadin 7, both of which use GWT as the base of client-side implementations. You can find source code and issue tracking for newer, web component based Vaadin versions in [vaadin/platform](https://github.com/vaadin/platform).


To contribute, first refer to [Contributing Code](https://github.com/vaadin/framework/blob/master/CONTRIBUTING.md)
for general instructions and requirements for contributing code to the Vaadin framework.

Instructions on how to set up a working environment for developing the Vaadin Framework follow below.

## Building a package

The distribution files can be built by running the standard Maven goal `mvn install` in the project root.

## Eclipse Quick Setup

1. Run
<code>git clone https://github.com/vaadin/framework.git</code>
command or clone the repository your favorite Git tool.
If using Windows, you might want to add these Git settings: `core.autocrlf=false`, `core.fileMode=false` and `core.longpaths=true`.
1. Run <code>mvn install</code> in the project root.
Note that the first compilation takes a while to finish as maven downloads dependencies used in the projects.
1. Start Eclipse with the workspace you would like to use. It is usually a good idea to use the parent folder of the Git repository as the workspace folder.
1. Import the project into Eclipse as a maven project. Use *File* -> *Import* -> *Maven* -> *Existing Maven Projects*.
1. Select the *framework* folder (where you cloned the project)
1. Click “Finish” to complete the import of Vaadin Framework

Now the project should compile without further configuration.

### Compiling the Default Widget Set and Themes

* Compile the default widgetset by running <code>install</code> maven goal in `vaadin-client-compiled` module root.
In Eclipse this is done by right clicking on vaadin-client-compiled project it and choosing *Run As* -> *Maven Build...*.
* Compile the default themes by running <code>install</code> maven goal in `vaadin-themes` module root.
In Eclipse this is done by right clicking on vaadin-themes project it and choosing *Run As* -> *Maven Build...*.

### Set up extra workspace preferences
Vaadin 8 includes Vaadin 7 compatibility classes and is supported until February 21, 2022 (extended support will be available for ten years after that).


The following preferences need to be set to keep the project consistent. You need to do this especially to be able to contribute changes to the project.
Vaadin 7 support has already ended, [extended support](https://vaadin.com/support/vaadin-7-extended-maintenance) is available until February 2029.


1. Open *Window* -> *Preferences* (Windows) or *Eclipse* -> *Preferences* (Mac)
1. Go to *General* -> *Workspace*
1. Set *Text file encoding* to *UTF-8*
1. Set *New text file line delimiter* to *Unix*
1. Go to XML -> XML Files -> Editor
1. Ensure the settings are follows:
## Using Vaadin 8 to develop applications


* Line width: 72
* Format comments: true
* Join lines: true
* Insert whitespace before closing empty end-tags: true
* Indent-using spaces: true
* Indentation size: 4
Please refer to [Vaadin tutorial](https://vaadin.com/docs/v8/framework/tutorial.html) and other [documentation](https://vaadin.com/docs/v8/index.html).


For known issues within Vaadin framework, see [Issue Tracker](https://github.com/vaadin/framework/issues). Comment or react to an existing issue to mark your interest in resolving it. If you don't find an existing report of an issue you are experiencing, [submit a new issue](https://github.com/vaadin/framework/issues/new/choose).


### Running a UI test
## Contributing


1. In a Project Explorer right-click *vaadin-uitest*
1. Open *Run As* -> *Maven build...*
1. Type in <code>jetty:run-exploded</code> into *Goals* and click *Run*
1. Open URL [http://localhost:8888/run/&lt;testUI&gt;](http://localhost:8888/run/<testUI>)

For full instructions please visit [README-TESTS.md](README-TESTS.md).

## Setting up IntelliJ IDEA to Develop Vaadin Framework 8

1. Install and run IDEA. Ultimate Edition is better but Community Edition should also work.
1. Ensure if Git and Maven plugins are installed, properly configured and enabled.
1. Clone the repository, using menu VCS -> Checkout from Version Control -> Git -> Git Repository URL -> https://github.com/vaadin/framework.git.
When the repository is cloned, do **NOT** open it as a project.
1. Open cloned repository as a maven object. Use File -> Open and choose root _pom.xml_ file
1. Have a coffee break while IDEA is loading dependencies and indexing the project
1. Run Maven targets <code>clean</code> and <code>install</code> using *Maven Projects* tool window to compile the whole project

### Running a specific UI test

1. Open *Maven Projects*
1. Open *vaadin-uitest* -> *Plugins* -> *jetty* -> *jetty:run-exploded*
1. Open URL [http://localhost:8888/run/&lt;testUI&gt;](http://localhost:8888/run/<testUI>)

For full instructions please visit [README-TESTS.md](README-TESTS.md).

### Running a Development Server

1. Open *Run* menu and click *Edit Configurations*
1. Click green ***+*** sign at top left corner, select *Maven* from popup
1. In the run configuration page, set any name for the configuration, select *vaadin-uitest* project folder as *Working directory*
1. Type <code>exec:exec@run-development-server</code> into *Command line* and save the configuration
1. Run the configuration and open URL [http://localhost:8888/run/&lt;testUI&gt;](http://localhost:8888/run/<testUI>)
To contribute, first refer to [Contributing Code](CONTRIBUTING.md)
for general instructions and requirements for contributing code to the Vaadin framework.


### Running a Development Server in a debug mode
For instructions on how to set up a working environment for developing the Vaadin framework, please visit [Development Instructions](README-DEV.md). Pay special attention to workspace preferences.


1. Type <code>exec:exec@debug-development-server</code> into *Command line* and save the configuration
1. In the same dialog, create new "Remote" debug configuration, using *localhost* and *Port 5005*
1. Start both configurations and open URL [http://localhost:8888/run/&lt;testUI&gt;](http://localhost:8888/run/<testUI>)
We appreciate all contributors and want to make submitting changes as easy as possible. If you find any mistakes, unclear parts or out-of-date instructions, please let us know by submitting an issue or a pull request.

+ 1
- 1
all/pom.xml View File

<parent> <parent>
<groupId>com.vaadin</groupId> <groupId>com.vaadin</groupId>
<artifactId>vaadin-root</artifactId> <artifactId>vaadin-root</artifactId>
<version>8.10-SNAPSHOT</version>
<version>8.12-SNAPSHOT</version>
</parent> </parent>
<artifactId>vaadin-all</artifactId> <artifactId>vaadin-all</artifactId>
<name>vaadin-all</name> <name>vaadin-all</name>

+ 6
- 8
all/src/main/templates/release-notes.html View File

enhancements. Below is a list of the most notable changes:</p> enhancements. Below is a list of the most notable changes:</p>


<ul> <ul>
<li><tt></tt></li>
<li><tt></tt></li>
<li></li>
<li></li>
</ul> </ul>


</p> </p>


<p> <p>
For enhancements introduced in Vaadin Framework 8.9, see the <a
href="http://vaadin.com/download/release/8.9/8.9.0/release-notes.html">Release
Notes for Vaadin Framework 8.9.0</a>.
For enhancements introduced in Vaadin Framework 8.11, see the <a
href="http://vaadin.com/download/release/8.11/8.11.0/release-notes.html">Release
Notes for Vaadin Framework 8.11.0</a>.
For migrating from previous framework versions, see <a href="#incompatible">the list of incompatible changes</a> and <a href="#migrating">how to migrate For migrating from previous framework versions, see <a href="#incompatible">the list of incompatible changes</a> and <a href="#migrating">how to migrate
to Vaadin Framework 8</a>. to Vaadin Framework 8</a>.
</p> </p>


<h2 id="incompatible">No Incompatible or Behavior-altering Changes in @version-minor@</h2>
<h2 id="incompatible">Incompatible or Behavior-altering Changes in @version-minor@</h2>


<ul> <ul>
<li></li> <li></li>
</ul> </ul>


<h2>For incompatible or behavior-altering changes in 8.9, please see <a href="https://vaadin.com/download/release/8.9/8.9.0/release-notes.html#incompatible">8.9 release notes</a></h2>
<h2>For incompatible or behavior-altering changes in 8.11, please see <a href="https://vaadin.com/download/release/8.11/8.11.0/release-notes.html#incompatible">8.11 release notes</a></h2>


<h3 id="knownissues">Known Issues and Limitations</h3> <h3 id="knownissues">Known Issues and Limitations</h3>
<ul> <ul>

+ 1
- 1
bom/pom.xml View File

<groupId>com.vaadin</groupId> <groupId>com.vaadin</groupId>
<artifactId>vaadin-bom</artifactId> <artifactId>vaadin-bom</artifactId>
<packaging>pom</packaging> <packaging>pom</packaging>
<version>8.10-SNAPSHOT</version>
<version>8.12-SNAPSHOT</version>
<name>Vaadin Framework (Bill of Materials)</name> <name>Vaadin Framework (Bill of Materials)</name>
<description>Vaadin Framework (Bill of Materials)</description> <description>Vaadin Framework (Bill of Materials)</description>
<url>http://vaadin.com</url> <url>http://vaadin.com</url>

+ 1
- 1
client-compiled/pom.xml View File

<parent> <parent>
<groupId>com.vaadin</groupId> <groupId>com.vaadin</groupId>
<artifactId>vaadin-root</artifactId> <artifactId>vaadin-root</artifactId>
<version>8.10-SNAPSHOT</version>
<version>8.12-SNAPSHOT</version>
</parent> </parent>
<!-- Needed by a plugin in release build --> <!-- Needed by a plugin in release build -->
<groupId>com.vaadin</groupId> <groupId>com.vaadin</groupId>

+ 1
- 1
client-compiler/pom.xml View File

<parent> <parent>
<groupId>com.vaadin</groupId> <groupId>com.vaadin</groupId>
<artifactId>vaadin-root</artifactId> <artifactId>vaadin-root</artifactId>
<version>8.10-SNAPSHOT</version>
<version>8.12-SNAPSHOT</version>
</parent> </parent>
<artifactId>vaadin-client-compiler</artifactId> <artifactId>vaadin-client-compiler</artifactId>
<name>vaadin-client-compiler</name> <name>vaadin-client-compiler</name>

+ 1
- 1
client/pom.xml View File

<parent> <parent>
<groupId>com.vaadin</groupId> <groupId>com.vaadin</groupId>
<artifactId>vaadin-root</artifactId> <artifactId>vaadin-root</artifactId>
<version>8.10-SNAPSHOT</version>
<version>8.12-SNAPSHOT</version>
</parent> </parent>
<!-- Needed by a plugin in release build --> <!-- Needed by a plugin in release build -->
<groupId>com.vaadin</groupId> <groupId>com.vaadin</groupId>

+ 24
- 7
client/src/main/java/com/vaadin/client/Util.java View File

} }


if (connector != null) { if (connector != null) {
// check that inside the rootElement
while (browseElement != null && browseElement != rootElement) {
browseElement = browseElement.getParentElement();
}
if (browseElement != rootElement) {
return null;
} else {
if (isConnectedToParent(browseElement, rootElement)) {
return connector; return connector;
} }
return null;
} }


browseElement = browseElement.getParentElement(); browseElement = browseElement.getParentElement();
} }
} }


private static boolean isConnectedToParent(Element element,
Element rootElement) {
Element browseElement = element;
// check if inside the rootElement
while (browseElement != null && browseElement != rootElement) {
browseElement = browseElement.getParentElement();
}
if (browseElement == rootElement) {
return true;
}
// Not inside the root, possibly inside a VOverlay such as
// VWindow instead.
@SuppressWarnings("deprecation")
VOverlay overlay = WidgetUtil.findWidget(element, VOverlay.class,
false);
if (overlay != null && overlay.getOwner() != null) {
browseElement = overlay.getOwner().getElement();
return isConnectedToParent(browseElement, rootElement);
}
return false;
}

/** /**
* Will (attempt) to focus the given DOM Element. * Will (attempt) to focus the given DOM Element.
* *

+ 22
- 0
client/src/main/java/com/vaadin/client/WidgetUtil.java View File

import com.google.gwt.user.client.EventListener; import com.google.gwt.user.client.EventListener;
import com.google.gwt.user.client.Window; import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.RootPanel; import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.Widget; import com.google.gwt.user.client.ui.Widget;
import com.vaadin.shared.ui.ErrorLevel; import com.vaadin.shared.ui.ErrorLevel;
import com.vaadin.shared.util.SharedUtil; import com.vaadin.shared.util.SharedUtil;
return indicator; return indicator;
} }
} }

public static void disableBrowserAutocomplete(TextBox textBox) {
/*-
* Stop the browser from showing its own suggestion popup.
*
* Using an invalid value instead of "off" as suggested by
* https://developer.mozilla.org/en-US/docs/Web/Security/Securing_your_site/Turning_off_form_autocompletion
*
* Leaving the non-standard Safari options autocapitalize and
* autocorrect untouched since those do not interfere in the same
* way, and they might be useful in a combo box where new items are
* allowed.
*/
if (BrowserInfo.get().isChrome()) {
// Chrome supports "off" and random number does not work with
// Chrome
textBox.getElement().setAttribute("autocomplete", "off");
} else {
textBox.getElement().setAttribute("autocomplete", Math.random() + "");
}
}
} }

+ 1
- 1
client/src/main/java/com/vaadin/client/communication/DefaultConnectionStateHandler.java View File

int statusCode = response.getStatusCode(); int statusCode = response.getStatusCode();
getLogger().warning("Heartbeat request returned " + statusCode); getLogger().warning("Heartbeat request returned " + statusCode);


if (response.getStatusCode() == Response.SC_GONE) {
if (response.getStatusCode() == Response.SC_FORBIDDEN) {
// Session expired // Session expired
getConnection().showSessionExpiredError(null); getConnection().showSessionExpiredError(null);
stopApplication(); stopApplication();

+ 56
- 7
client/src/main/java/com/vaadin/client/connectors/grid/GridConnector.java View File

import com.vaadin.client.communication.StateChangeEvent; import com.vaadin.client.communication.StateChangeEvent;
import com.vaadin.client.connectors.AbstractListingConnector; import com.vaadin.client.connectors.AbstractListingConnector;
import com.vaadin.client.connectors.grid.ColumnConnector.CustomColumn; import com.vaadin.client.connectors.grid.ColumnConnector.CustomColumn;
import com.vaadin.client.data.AbstractRemoteDataSource;
import com.vaadin.client.data.DataSource; import com.vaadin.client.data.DataSource;
import com.vaadin.client.ui.SimpleManagedLayout; import com.vaadin.client.ui.SimpleManagedLayout;
import com.vaadin.client.widget.escalator.RowContainer; import com.vaadin.client.widget.escalator.RowContainer;
import com.vaadin.client.widget.grid.CellReference; import com.vaadin.client.widget.grid.CellReference;
import com.vaadin.client.widget.grid.DataAvailableEvent;
import com.vaadin.client.widget.grid.DataAvailableHandler;
import com.vaadin.client.widget.grid.EventCellReference; import com.vaadin.client.widget.grid.EventCellReference;
import com.vaadin.client.widget.grid.events.BodyClickHandler; import com.vaadin.client.widget.grid.events.BodyClickHandler;
import com.vaadin.client.widget.grid.events.BodyDoubleClickHandler; import com.vaadin.client.widget.grid.events.BodyDoubleClickHandler;
* The scrolling methods must trigger the scrolling only after any potential * The scrolling methods must trigger the scrolling only after any potential
* resizing or other similar action triggered from the server side within * resizing or other similar action triggered from the server side within
* the same round trip has had a chance to happen, so there needs to be a * the same round trip has had a chance to happen, so there needs to be a
* delay. The delay is done with <code>scheduleFinally</code> rather than
* <code>scheduleDeferred</code> because the latter has been known to cause
* flickering in Grid.
* delay. The delay is done with <code>scheduleDeferred</code> rather than
* <code>scheduleFinally</code> because otherwise the order of the
* operations isn't guaranteed.
* *
*/ */
private class GridConnectorClientRpc implements GridClientRpc { private class GridConnectorClientRpc implements GridClientRpc {
private final Grid<JsonObject> grid; private final Grid<JsonObject> grid;
private HandlerRegistration dataAvailableHandlerRegistration = null;
private boolean recalculateScheduled = false;


private GridConnectorClientRpc(Grid<JsonObject> grid) { private GridConnectorClientRpc(Grid<JsonObject> grid) {
this.grid = grid; this.grid = grid;


@Override @Override
public void scrollToRow(int row, ScrollDestination destination) { public void scrollToRow(int row, ScrollDestination destination) {
Scheduler.get().scheduleFinally(() -> {
Scheduler.get().scheduleDeferred(() -> {
grid.scrollToRow(row, destination); grid.scrollToRow(row, destination);
// Add details refresh listener and handle possible detail // Add details refresh listener and handle possible detail
// for scrolled row. // for scrolled row.


@Override @Override
public void scrollToStart() { public void scrollToStart() {
Scheduler.get().scheduleFinally(() -> grid.scrollToStart());
Scheduler.get().scheduleDeferred(() -> grid.scrollToStart());
} }


@Override @Override
public void scrollToEnd() { public void scrollToEnd() {
Scheduler.get().scheduleFinally(() -> {
Scheduler.get().scheduleDeferred(() -> {
grid.scrollToEnd(); grid.scrollToEnd();
addDetailsRefreshCallback(() -> { addDetailsRefreshCallback(() -> {
if (rowHasDetails(grid.getDataSource().size() - 1)) { if (rowHasDetails(grid.getDataSource().size() - 1)) {


@Override @Override
public void recalculateColumnWidths() { public void recalculateColumnWidths() {
grid.recalculateColumnWidths();
if (recalculateScheduled) {
return;
}

// Must be scheduled so that possible refreshAll has time to clear
// the cache.
recalculateScheduled = true;
Scheduler.get().scheduleFinally(() -> {
// If cache has been cleared, wait for data to become available.
// Don't trigger another attempt if there is already a handler
// waiting, that one will trigger the call when calculations are
// possible and clear out the registration afterwards.
if (((AbstractRemoteDataSource<JsonObject>) getDataSource())
.getCachedRange().length() == 0
&& getDataSource().size() > 0) {
if (dataAvailableHandlerRegistration == null) {
dataAvailableHandlerRegistration = grid
.addDataAvailableHandler(
new DataAvailableHandler() {

@Override
public void onDataAvailable(
DataAvailableEvent event) {
if (event.getAvailableRows()
.length() == 0
&& getDataSource()
.size() > 0) {
// Cache not populated yet,
// wait for next call.
return;
}
grid.recalculateColumnWidths();
if (dataAvailableHandlerRegistration != null) {
dataAvailableHandlerRegistration
.removeHandler();
dataAvailableHandlerRegistration = null;
}
}
});
}
} else if (dataAvailableHandlerRegistration == null) {
grid.recalculateColumnWidths();
}
recalculateScheduled = false;
});
} }
} }



+ 39
- 3
client/src/main/java/com/vaadin/client/ui/ShortcutActionHandler.java View File

target = Util.findPaintable(client, et); target = Util.findPaintable(client, et);
} }
final ComponentConnector finalTarget = target; final ComponentConnector finalTarget = target;

event.preventDefault(); event.preventDefault();

/* /*
* The focused component might have unpublished changes, try to * The focused component might have unpublished changes, try to
* synchronize them before firing shortcut action. * synchronize them before firing shortcut action.
*/ */
client.flushActiveConnector(); client.flushActiveConnector();

/*
* Legacy components don't have built-in logic for flushing, they need a
* workaround with blur and focus to trigger the value change.
*/
ComponentConnector activeConnector = getActiveConnector(client);
if (activeConnector != null) {
Class<?> clz = activeConnector.getClass();
while (clz != null) {
if (clz.getName().equals(
"com.vaadin.v7.client.ui.AbstractLegacyComponentConnector")) {
shakeTarget(et);
Scheduler.get().scheduleDeferred(() -> {
shakeTarget(et);
});
break;
}
clz = clz.getSuperclass();
}
}
Scheduler.get().scheduleDeferred(() -> { Scheduler.get().scheduleDeferred(() -> {
if (finalTarget != null) { if (finalTarget != null) {
client.updateVariable(paintableId, "actiontarget", finalTarget, client.updateVariable(paintableId, "actiontarget", finalTarget,
}); });
} }


/**
* We try to fire value change in the component the key combination was
* typed. E.g. TextField may contain newly typed text that is expected to be
* sent to server before the shortcut action is triggered. This is done by
* removing focus and then returning it immediately back to target element.
* <p>
* This is a hack copied over from V7 in order to keep the compatibility
* classes working. Main V8 classes don't require shaking.
*/
private static void shakeTarget(final Element e) {
blur(e);
focus(e);
}

private static native ComponentConnector getActiveConnector(
ApplicationConnection ac)
/*-{
return ac.@com.vaadin.client.ApplicationConnection::getActiveConnector()();
}-*/;

private static native void blur(Element e) private static native void blur(Element e)
/*-{ /*-{
if (e.blur) { if (e.blur) {

+ 34
- 10
client/src/main/java/com/vaadin/client/ui/VAbstractCalendarPanel.java View File



package com.vaadin.client.ui; package com.vaadin.client.ui;


import static com.vaadin.client.DateTimeService.asTwoDigits;

import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import com.google.gwt.aria.client.SelectedValue; import com.google.gwt.aria.client.SelectedValue;
import com.google.gwt.dom.client.Element; import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NativeEvent; import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.dom.client.Style;
import com.google.gwt.event.dom.client.BlurEvent; import com.google.gwt.event.dom.client.BlurEvent;
import com.google.gwt.event.dom.client.BlurHandler; import com.google.gwt.event.dom.client.BlurHandler;
import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.ui.FlexTable; import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.InlineHTML; import com.google.gwt.user.client.ui.InlineHTML;
import com.google.gwt.user.client.ui.Widget; import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.BrowserInfo;
import com.vaadin.client.DateTimeService; import com.vaadin.client.DateTimeService;
import com.vaadin.client.WidgetUtil; import com.vaadin.client.WidgetUtil;
import com.vaadin.client.ui.aria.AriaHelper; import com.vaadin.client.ui.aria.AriaHelper;
import com.vaadin.shared.util.SharedUtil; import com.vaadin.shared.util.SharedUtil;


import static com.vaadin.client.DateTimeService.asTwoDigits;

/** /**
* Abstract calendar panel to show and select a date using a resolution. The * Abstract calendar panel to show and select a date using a resolution. The
* class is parameterized by the date resolution enumeration type. * class is parameterized by the date resolution enumeration type.
return true; return true;
} }


// If dateStrResolution has more year digits than rangeEnd, we need
// to pad it in order to be lexicographically compatible
String dateStrResolution = dateStrResolution(date, minResolution); String dateStrResolution = dateStrResolution(date, minResolution);
return rangeEnd.substring(0, dateStrResolution.length())
String paddedEnd = rangeEnd.substring(0);
int yearDigits = dateStrResolution.indexOf("-");
if (yearDigits == -1) {
yearDigits = dateStrResolution.length();
}
while (paddedEnd.indexOf("-") < yearDigits) {
paddedEnd = "0" + paddedEnd;
}
return paddedEnd.substring(0, dateStrResolution.length())
.compareTo(dateStrResolution) >= 0; .compareTo(dateStrResolution) >= 0;
} }


* resolution of the calendar is changed and no date has been * resolution of the calendar is changed and no date has been
* selected. * selected.
*/ */
@SuppressWarnings("rawtypes")
public void renderCalendar(boolean updateDate) { public void renderCalendar(boolean updateDate) {
if (parent instanceof VAbstractPopupCalendar
&& !((VAbstractPopupCalendar) parent).popup.isShowing()) {
// a popup that isn't open cannot possibly need a focus change event
updateDate = false;
}
doRenderCalendar(updateDate); doRenderCalendar(updateDate);


initialRenderDone = true; initialRenderDone = true;
getDateField().getStylePrimaryName() + "-calendarpanel"); getDateField().getStylePrimaryName() + "-calendarpanel");


if (focusedDate == null) { if (focusedDate == null) {
Date now = new Date();
Date date = getDate();
if (date == null) {
date = new Date();
}
// focusedDate must have zero hours, mins, secs, millisecs // focusedDate must have zero hours, mins, secs, millisecs
focusedDate = new FocusedDate(now.getYear(), now.getMonth(),
now.getDate());
displayedMonth = new FocusedDate(now.getYear(), now.getMonth(), 1);
focusedDate = new FocusedDate(date.getYear(), date.getMonth(),
date.getDate());
displayedMonth = new FocusedDate(date.getYear(), date.getMonth(),
1);
} }


if (updateDate && !isDay(getResolution()) if (updateDate && !isDay(getResolution())
*/ */
public void setRangeEnd(String newRangeEnd) { public void setRangeEnd(String newRangeEnd) {
if (!SharedUtil.equals(rangeEnd, newRangeEnd)) { if (!SharedUtil.equals(rangeEnd, newRangeEnd)) {
rangeEnd = newRangeEnd;
// Dates with year 10000 or more has + prefix, which is not compatible
// with format returned by dateStrResolution method
if (newRangeEnd.startsWith("+")) {
rangeEnd = newRangeEnd.substring(1);
} else {
rangeEnd = newRangeEnd;
}
if (initialRenderDone) { if (initialRenderDone) {
// Dynamic updates to the range needs to render the calendar to // Dynamic updates to the range needs to render the calendar to
// update the element stylenames // update the element stylenames

+ 3
- 0
client/src/main/java/com/vaadin/client/ui/VAbstractTextualDate.java View File

import com.vaadin.client.Focusable; import com.vaadin.client.Focusable;
import com.vaadin.client.LocaleNotLoadedException; import com.vaadin.client.LocaleNotLoadedException;
import com.vaadin.client.LocaleService; import com.vaadin.client.LocaleService;
import com.vaadin.client.WidgetUtil;
import com.vaadin.client.ui.aria.AriaHelper; import com.vaadin.client.ui.aria.AriaHelper;
import com.vaadin.client.ui.aria.HandlesAriaCaption; import com.vaadin.client.ui.aria.HandlesAriaCaption;
import com.vaadin.client.ui.aria.HandlesAriaInvalid; import com.vaadin.client.ui.aria.HandlesAriaInvalid;
if (BrowserInfo.get().isIE()) { if (BrowserInfo.get().isIE()) {
addDomHandler(this, KeyDownEvent.getType()); addDomHandler(this, KeyDownEvent.getType());
} }
// Stop the browser from showing its own suggestion popup.
WidgetUtil.disableBrowserAutocomplete(text);
add(text); add(text);
publishJSHelpers(getElement()); publishJSHelpers(getElement());
} }

+ 75
- 27
client/src/main/java/com/vaadin/client/ui/VComboBox.java View File

import com.vaadin.client.ui.combobox.ComboBoxConnector; import com.vaadin.client.ui.combobox.ComboBoxConnector;
import com.vaadin.client.ui.menubar.MenuBar; import com.vaadin.client.ui.menubar.MenuBar;
import com.vaadin.client.ui.menubar.MenuItem; import com.vaadin.client.ui.menubar.MenuItem;
import com.vaadin.client.ui.orderedlayout.Slot;
import com.vaadin.shared.AbstractComponentState; import com.vaadin.shared.AbstractComponentState;
import com.vaadin.shared.ui.ComponentStateUtil; import com.vaadin.shared.ui.ComponentStateUtil;
import com.vaadin.shared.util.SharedUtil; import com.vaadin.shared.util.SharedUtil;
} }
} }


if (offsetWidth + menuMarginBorderPaddingWidth
+ left < VComboBox.this.getAbsoluteLeft()
+ VComboBox.this.getOffsetWidth()) {
// Popup doesn't reach all the way to the end of the input
// field, filtering may have changed the popup width.
left = VComboBox.this.getAbsoluteLeft();
int comboBoxLeft = VComboBox.this.getAbsoluteLeft();
int comboBoxWidth = VComboBox.this.getOffsetWidth();
if (hasParentWithUnadjustedHorizontalPositioning()) {
// ComboBox itself may be incorrectly positioned, don't try to
// adjust horizontal popup position yet. Earlier width
// calculations must be performed anyway to avoid flickering.
if (top != topPosition) {
// Variable 'left' still contains the original popupLeft,
// 'top' has been updated, thus vertical position needs
// adjusting.
setPopupPosition(left, top);
}
return;
}
if (left > comboBoxLeft
|| offsetWidth + menuMarginBorderPaddingWidth
+ left < comboBoxLeft + comboBoxWidth) {
// Popup is positioned too far right or doesn't reach all the
// way to the end of the input field, filtering may have changed
// the popup width.
left = comboBoxLeft;
} }
if (offsetWidth + menuMarginBorderPaddingWidth + left > Window if (offsetWidth + menuMarginBorderPaddingWidth + left > Window
.getClientWidth()) { .getClientWidth()) {
// Popup doesn't fit the view, needs to be opened to the left // Popup doesn't fit the view, needs to be opened to the left
// instead. // instead.
left = VComboBox.this.getAbsoluteLeft()
+ VComboBox.this.getOffsetWidth() - offsetWidth
left = comboBoxLeft + comboBoxWidth - offsetWidth
- (int) menuMarginBorderPaddingWidth; - (int) menuMarginBorderPaddingWidth;
} }
if (left < 0) { if (left < 0) {
menu.setWidth(Window.getClientWidth() + "px"); menu.setWidth(Window.getClientWidth() + "px");
} }


setPopupPosition(left, top);
// Only update the position if it has changed.
if (top != topPosition || left != getPopupLeft()) {
setPopupPosition(left, top);
}
menu.scrollSelectionIntoView(); menu.scrollSelectionIntoView();
} }


/**
* Checks whether there are any {@link VHorizontalLayout}s with
* incomplete internal position calculations among this VComboBox's
* parents.
*
* @return {@code true} if unadjusted parents found, {@code false}
* otherwise
*/
private boolean hasParentWithUnadjustedHorizontalPositioning() {
/*
* If there are any VHorizontalLayouts among this VComboBox's
* parents, any spacing or expand ratio may cause incorrect
* intermediate positioning. The status of the layout's internal
* positioning can be checked from the first slot's margin-left
* style, which will be set to 0px if no spacing or expand ratio
* adjustments are needed, and to a negative pixel amount if they
* are. If the style hasn't been set at all, calculations are still
* underway. Popup position shouldn't be adjusted before such
* calculations have been finished.
*
* VVerticalLayout has the same logic but it only affects the
* vertical positioning, which is irrelevant for the calculations
* here.
*/
Widget toCheck = VComboBox.this;
while (toCheck != null && !(toCheck.getParent() instanceof VUI)) {
toCheck = toCheck.getParent();
if (toCheck instanceof VHorizontalLayout) {
VHorizontalLayout hLayout = (VHorizontalLayout) toCheck;
// because hLayout is a parent it must have at least one
// child widget
Widget slot = hLayout.getWidget(0);
if (slot instanceof Slot && slot.getElement().getStyle()
.getMarginLeft().isEmpty()) {
// margin hasn't been set, layout's internal positioning
// is still being adjusted and ComboBox's position may
// not be final
return true;
}
}
}
return false;
}

/** /**
* Adds in-line CSS rules to the DOM according to the * Adds in-line CSS rules to the DOM according to the
* suggestionPopupWidth field * suggestionPopupWidth field
* @since 7.6.4 * @since 7.6.4
*/ */
public FilterSelectTextBox() { public FilterSelectTextBox() {
/*-
* Stop the browser from showing its own suggestion popup.
*
* Using an invalid value instead of "off" as suggested by
* https://developer.mozilla.org/en-US/docs/Web/Security/Securing_your_site/Turning_off_form_autocompletion
*
* Leaving the non-standard Safari options autocapitalize and
* autocorrect untouched since those do not interfere in the same
* way, and they might be useful in a combo box where new items are
* allowed.
*/
if (BrowserInfo.get().isChrome()) {
// Chrome supports "off" and random number does not work with
// Chrome
getElement().setAttribute("autocomplete", "off");
} else {
getElement().setAttribute("autocomplete", Math.random() + "");
}
WidgetUtil.disableBrowserAutocomplete(this);
} }


/** /**


/** For internal use only. May be removed or replaced in the future. */ /** For internal use only. May be removed or replaced in the future. */
public void updateReadOnly() { public void updateReadOnly() {
if (readonly) {
suggestionPopup.hide();
}
debug("VComboBox: updateReadOnly()"); debug("VComboBox: updateReadOnly()");
tb.setReadOnly(readonly || !textInputEnabled); tb.setReadOnly(readonly || !textInputEnabled);
} }

+ 2
- 0
client/src/main/java/com/vaadin/client/ui/VContextMenu.java View File

import com.google.gwt.core.client.Scheduler; import com.google.gwt.core.client.Scheduler;
import com.google.gwt.dom.client.Element; import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NodeList; import com.google.gwt.dom.client.NodeList;
import com.google.gwt.dom.client.Style;
import com.google.gwt.dom.client.TableRowElement; import com.google.gwt.dom.client.TableRowElement;
import com.google.gwt.dom.client.TableSectionElement; import com.google.gwt.dom.client.TableSectionElement;
import com.google.gwt.event.dom.client.BlurEvent; import com.google.gwt.event.dom.client.BlurEvent;
setHeight(Window.getClientHeight() + "px"); setHeight(Window.getClientHeight() + "px");
} }
setPopupPosition(menuLeft, menuTop); setPopupPosition(menuLeft, menuTop);
getElement().getStyle().setPosition(Style.Position.FIXED);


/* /*
* Move keyboard focus to menu, deferring the focus setting so the * Move keyboard focus to menu, deferring the focus setting so the

+ 2
- 2
client/src/main/java/com/vaadin/client/ui/VRichTextArea.java View File

BrowserInfo browser = BrowserInfo.get(); BrowserInfo browser = BrowserInfo.get();
String result = getValue(); String result = getValue();
if (browser.isFirefox()) { if (browser.isFirefox()) {
if ("<br>".equals(result)) {
if ("<br>".equals(result) || "<div><br></div>".equals(result)) {
result = ""; result = "";
} }
} else if (browser.isWebkit() || browser.isEdge()) { } else if (browser.isWebkit() || browser.isEdge()) {
result = ""; result = "";
} }
} else if (browser.isIE()) { } else if (browser.isIE()) {
if ("<P>&nbsp;</P>".equals(result)) {
if ("<P>&nbsp;</P>".equals(result) || "<p><br></p>".equals(result)) {
result = ""; result = "";
} }
} else if (browser.isOpera()) { } else if (browser.isOpera()) {

+ 65
- 8
client/src/main/java/com/vaadin/client/ui/VUpload.java View File

import com.vaadin.client.StyleConstants; import com.vaadin.client.StyleConstants;
import com.vaadin.client.ui.upload.UploadConnector; import com.vaadin.client.ui.upload.UploadConnector;
import com.vaadin.client.ui.upload.UploadIFrameOnloadStrategy; import com.vaadin.client.ui.upload.UploadIFrameOnloadStrategy;
import com.vaadin.shared.EventId;
import com.vaadin.shared.ui.upload.UploadServerRpc; import com.vaadin.shared.ui.upload.UploadServerRpc;


/** /**
public void onBrowserEvent(Event event) { public void onBrowserEvent(Event event) {
super.onBrowserEvent(event); super.onBrowserEvent(event);
if (event.getTypeInt() == Event.ONCHANGE) { if (event.getTypeInt() == Event.ONCHANGE) {
if (isImmediateMode() && fu.getFilename() != null
&& !fu.getFilename().isEmpty()) {
if (isImmediateMode() && hasFilename()) {
submit(); submit();
} }
} else if (BrowserInfo.get().isIE() } else if (BrowserInfo.get().isIE()


private boolean immediateMode; private boolean immediateMode;


/**
* Just-in-case option to override the default assumption that if no file
* has been selected, no upload attempt should happen. Not part of public
* API so can be removed without a warning -- if you have an actual need for
* this feature (which is currently only accessible through violator
* pattern), let us know.
*/
private boolean allowUploadWithoutFilename = false;

private String acceptMimeTypes; private String acceptMimeTypes;


private Hidden maxfilesize = new Hidden(); private Hidden maxfilesize = new Hidden();
setWidget(panel); setWidget(panel);
panel.add(maxfilesize); panel.add(maxfilesize);
panel.add(fu); panel.add(fu);
fu.addChangeHandler(event -> {
if (!isImmediateMode()) {
updateEnabledForSubmitButton();
}
if (client != null) {
UploadConnector connector = ((UploadConnector) ConnectorMap
.get(client).getConnector(VUpload.this));
if (connector.hasEventListener(EventId.CHANGE)) {
connector.getRpcProxy(UploadServerRpc.class)
.change(fu.getFilename());
}
}
});
submitButton = new VButton(); submitButton = new VButton();
submitButton.addClickHandler(event -> { submitButton.addClickHandler(event -> {
if (isImmediateMode()) { if (isImmediateMode()) {
fu.unsinkEvents(Event.ONCHANGE); fu.unsinkEvents(Event.ONCHANGE);
fu.unsinkEvents(Event.ONFOCUS); fu.unsinkEvents(Event.ONFOCUS);
} }
updateEnabledForSubmitButton();
} }
setStyleName(getElement(), CLASSNAME + "-immediate", immediateMode); setStyleName(getElement(), CLASSNAME + "-immediate", immediateMode);
} }


/** For internal use only. May be removed or replaced in the future. */ /** For internal use only. May be removed or replaced in the future. */
public void disableUpload() { public void disableUpload() {
setEnabledForSubmitButton(false);
if (!submitted) { if (!submitted) {
// Cannot disable the fileupload while submitting or the file won't // Cannot disable the fileupload while submitting or the file won't
// be submitted at all // be submitted at all
fu.getElement().setPropertyBoolean("disabled", true); fu.getElement().setPropertyBoolean("disabled", true);
} }
enabled = false; enabled = false;
updateEnabledForSubmitButton();
} }


/** For internal use only. May be removed or replaced in the future. */ /** For internal use only. May be removed or replaced in the future. */
public void enableUpload() { public void enableUpload() {
setEnabledForSubmitButton(true);
fu.getElement().setPropertyBoolean("disabled", false); fu.getElement().setPropertyBoolean("disabled", false);
enabled = true; enabled = true;
updateEnabledForSubmitButton();
if (submitted) { if (submitted) {
/* /*
* An old request is still in progress (most likely cancelled), * An old request is still in progress (most likely cancelled),
} }
} }


private void setEnabledForSubmitButton(boolean enabled) {
submitButton.setEnabled(enabled);
submitButton.setStyleName(StyleConstants.DISABLED, !enabled);
/**
* Updates the enabled status for submit button. If the widget itself is
* disabled, so is the submit button. It must also follow overall enabled
* status in immediate mode, otherwise you cannot select a file at all. In
* non-immediate mode there is another button for selecting the file, so the
* submit button should be disabled until a file has been selected, unless
* upload without selection has been specifically allowed.
*/
private void updateEnabledForSubmitButton() {
if (enabled && (isImmediateMode() || hasFilename()
|| allowUploadWithoutFilename)) {
submitButton.setEnabled(true);
submitButton.setStyleName(StyleConstants.DISABLED, false);
} else {
submitButton.setEnabled(false);
submitButton.setStyleName(StyleConstants.DISABLED, true);
}
}

private boolean hasFilename() {
return fu.getFilename() != null && !fu.getFilename().isEmpty();
} }


/** /**
if (isImmediateMode()) { if (isImmediateMode()) {
fu.sinkEvents(Event.ONCHANGE); fu.sinkEvents(Event.ONCHANGE);
} }
fu.addChangeHandler(event -> {
if (!isImmediateMode()) {
updateEnabledForSubmitButton();
}
if (client != null) {
UploadConnector connector = ((UploadConnector) ConnectorMap
.get(client).getConnector(VUpload.this));
if (connector.hasEventListener(EventId.CHANGE)) {
connector.getRpcProxy(UploadServerRpc.class)
.change(fu.getFilename());
}
}
});
} }


/** /**
.info("Submit cancelled (disabled or already submitted)"); .info("Submit cancelled (disabled or already submitted)");
return; return;
} }
if (fu.getFilename().isEmpty()) {
if (!hasFilename()) {
if (!allowUploadWithoutFilename) {
return;
}
getLogger().info("Submitting empty selection (no file)"); getLogger().info("Submitting empty selection (no file)");
} }
// flush possibly pending variable changes, so they will be handled // flush possibly pending variable changes, so they will be handled

+ 10
- 4
client/src/main/java/com/vaadin/client/ui/VWindow.java View File

// if clicked or key ENTER or SPACE is pressed // if clicked or key ENTER or SPACE is pressed
} else if (isClosable() && target == closeBox) { } else if (isClosable() && target == closeBox) {
if (type == Event.ONCLICK || (type == Event.ONKEYUP if (type == Event.ONCLICK || (type == Event.ONKEYUP
&& isKeyEnterOrSpace(event.getKeyCode()))) {
onCloseClick();
&& (isKeyEnterOrSpace(event.getKeyCode()))
|| event.getKeyCode() == KeyCodes.KEY_ESCAPE)) {
closeWindow();
} }
bubble = false; bubble = false;
} else if (target == maximizeRestoreBox) { } else if (target == maximizeRestoreBox) {
// if ESC is pressed, close the window
if (type == Event.ONKEYUP
&& event.getKeyCode() == KeyCodes.KEY_ESCAPE) {
closeWindow();
}
// handled in connector // handled in connector
// if clicked or key ENTER or SPACE is pressed // if clicked or key ENTER or SPACE is pressed
if (type != Event.ONCLICK && !(type == Event.ONKEYUP
else if (type != Event.ONCLICK && !(type == Event.ONKEYUP
&& isKeyEnterOrSpace(event.getKeyCode()))) { && isKeyEnterOrSpace(event.getKeyCode()))) {
bubble = false; bubble = false;
} }
} }
} }


private void onCloseClick() {
private void closeWindow() {
// Send the close event to the server // Send the close event to the server
client.updateVariable(id, "close", true, true); client.updateVariable(id, "close", true, true);
} }

+ 6
- 1
client/src/main/java/com/vaadin/client/ui/optiongroup/RadioButtonGroupConnector.java View File



@OnStateChange("readOnly") @OnStateChange("readOnly")
void updateWidgetReadOnly() { void updateWidgetReadOnly() {
getWidget().setEnabled(isEnabled() && !isReadOnly());
getWidget().setReadonly(isReadOnly());
}

@OnStateChange("enabled")
void updateWidgetEnabled() {
getWidget().setEnabled(isEnabled());
} }


@OnStateChange("selectedItemKey") @OnStateChange("selectedItemKey")

+ 10
- 0
client/src/main/java/com/vaadin/client/ui/orderedlayout/AbstractOrderedLayoutConnector.java View File

} }


// Add all necessary listeners // Add all necessary listeners
boolean listenersAdded = false;
if (needsFixedHeight()) { if (needsFixedHeight()) {
slot.setWidgetResizeListener(childComponentResizeListener); slot.setWidgetResizeListener(childComponentResizeListener);
if (slot.hasCaption()) { if (slot.hasCaption()) {
slot.setCaptionResizeListener(slotCaptionResizeListener); slot.setCaptionResizeListener(slotCaptionResizeListener);
} }
listenersAdded = true;
} else if ((hasChildrenWithRelativeHeight } else if ((hasChildrenWithRelativeHeight
|| hasChildrenWithRelativeWidth) && slot.hasCaption()) { || hasChildrenWithRelativeWidth) && slot.hasCaption()) {
/* /*
* as the relative size? * as the relative size?
*/ */
slot.setCaptionResizeListener(slotCaptionResizeListener); slot.setCaptionResizeListener(slotCaptionResizeListener);
listenersAdded = true;
} }


if (needsExpand()) { if (needsExpand()) {
if (slot.hasSpacing()) { if (slot.hasSpacing()) {
slot.setSpacingResizeListener(spacingResizeListener); slot.setSpacingResizeListener(spacingResizeListener);
} }
listenersAdded = true;
}

if (listenersAdded) {
// removing these listeners makes widget unmeasurable and resets the
// measured height, measure again if listeners got added back
getLayoutManager().setNeedsMeasure(child);
} }
} }



+ 25
- 1
client/src/main/java/com/vaadin/client/ui/richtextarea/VRichTextToolbar.java View File

final String url = Window.prompt("Enter a link URL:", final String url = Window.prompt("Enter a link URL:",
"http://"); "http://");
if (url != null) { if (url != null) {
extended.createLink(url);
createLinkViaJSNI(extended, url);
} }
} else if (sender == removeLink) { } else if (sender == removeLink) {
extended.removeLink(); extended.removeLink();
updateStatus(); updateStatus();
} }
} }

private native void createLinkViaJSNI(
RichTextArea.ExtendedFormatter formatter, String url)
/*-{
var elem = formatter.@com.google.gwt.user.client.ui.impl.RichTextAreaImpl::elem;
var wnd = elem.contentWindow;
var selectedText = "";
if (wnd.getSelection) {
selectedText = wnd.getSelection().toString();
}
wnd.focus();
if (selectedText) {
// Add url as the href property of the highlighted text
wnd.document.execCommand("createLink", false, url);
} else {
// Insert url both as a new text and its href-property value
var range = wnd.document.getSelection().getRangeAt(0)
var node = wnd.document.createElement("a");
node.innerHTML = url;
node.setAttribute("href", url);
range.insertNode(node);
}
}-*/;
} }


private static final RichTextArea.FontSize[] FONT_SIZES_CONSTANTS = { private static final RichTextArea.FontSize[] FONT_SIZES_CONSTANTS = {

+ 24
- 25
client/src/main/java/com/vaadin/client/ui/upload/UploadConnector.java View File

import com.vaadin.client.ApplicationConnection; import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.Paintable; import com.vaadin.client.Paintable;
import com.vaadin.client.UIDL; import com.vaadin.client.UIDL;
import com.vaadin.client.annotations.OnStateChange;
import com.vaadin.client.communication.StateChangeEvent; import com.vaadin.client.communication.StateChangeEvent;
import com.vaadin.client.ui.AbstractComponentConnector; import com.vaadin.client.ui.AbstractComponentConnector;
import com.vaadin.client.ui.VUpload; import com.vaadin.client.ui.VUpload;
import com.vaadin.shared.EventId;
import com.vaadin.shared.ui.Connect; import com.vaadin.shared.ui.Connect;
import com.vaadin.shared.ui.upload.UploadClientRpc; import com.vaadin.shared.ui.upload.UploadClientRpc;
import com.vaadin.shared.ui.upload.UploadServerRpc;
import com.vaadin.shared.ui.upload.UploadState; import com.vaadin.shared.ui.upload.UploadState;
import com.vaadin.ui.Upload; import com.vaadin.ui.Upload;


registerRpc(UploadClientRpc.class, () -> getWidget().submit()); registerRpc(UploadClientRpc.class, () -> getWidget().submit());
} }


@Override
protected void init() {
super.init();

getWidget().fu.addChangeHandler(event -> {
if (hasEventListener(EventId.CHANGE)) {
getRpcProxy(UploadServerRpc.class)
.change(getWidget().fu.getFilename());
}
});
}

@Override @Override
public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
if (!isRealUpdate(uidl)) { if (!isRealUpdate(uidl)) {
final String action = client final String action = client
.translateVaadinUri(uidl.getStringVariable("action")); .translateVaadinUri(uidl.getStringVariable("action"));
upload.element.setAction(action); upload.element.setAction(action);
if (uidl.hasAttribute("buttoncaption")) {
upload.submitButton
.setText(uidl.getStringAttribute("buttoncaption"));
if (uidl.hasAttribute("buttonstylename")) {
upload.submitButton.setStyleName(
uidl.getStringAttribute("buttonstylename"));
}
upload.submitButton.setVisible(true);
} else {
upload.submitButton.setVisible(false);
}
upload.fu.setName(upload.paintableId + "_file"); upload.fu.setName(upload.paintableId + "_file");


if (!isEnabled()) { if (!isEnabled()) {
getWidget().disableTitle(hasTooltip()); getWidget().disableTitle(hasTooltip());
} }


/**
* Updates the caption, style name, display mode, and visibility of the
* submit button.
* <p>
* For internal use only. May be removed or replaced in the future.
*/
@OnStateChange({ "buttonCaption", "buttonStyleName",
"buttonCaptionAsHtml" })
private void updateSubmitButton() {
VUpload upload = getWidget();
if (getState().buttonCaption != null) {
if (getState().buttonCaptionAsHtml) {
upload.submitButton.setHtml(getState().buttonCaption);
} else {
upload.submitButton.setText(getState().buttonCaption);
}
upload.submitButton.setStyleName(getState().buttonStyleName);
upload.submitButton.setVisible(true);
} else {
upload.submitButton.setVisible(false);
}
}

@Override @Override
public VUpload getWidget() { public VUpload getWidget() {
return (VUpload) super.getWidget(); return (VUpload) super.getWidget();

+ 8
- 2
client/src/main/java/com/vaadin/client/ui/window/WindowConnector.java View File

// This had to be here because we might not know the content size before // This had to be here because we might not know the content size before
// everything is painted into the window // everything is painted into the window


// centered is this is unset on move/resize
// centered if this is unset on move/resize
window.centered = state.centered; window.centered = state.centered;
// Ensure centering before setting visible (#16486) // Ensure centering before setting visible (#16486)
if (window.centered && getState().windowMode != WindowMode.MAXIMIZED) { if (window.centered && getState().windowMode != WindowMode.MAXIMIZED) {
Scheduler.get().scheduleFinally(() -> getWidget().center());
Scheduler.get().scheduleFinally(() -> {
// the window may have got removed again before this is
// triggered, and centering would re-display it
if (getWidget().isShowing()) {
getWidget().center();
}
});
} }
window.setVisible(true); window.setVisible(true);
} }

+ 70
- 11
client/src/main/java/com/vaadin/client/widgets/Escalator.java View File

import com.google.gwt.animation.client.AnimationScheduler.AnimationCallback; import com.google.gwt.animation.client.AnimationScheduler.AnimationCallback;
import com.google.gwt.animation.client.AnimationScheduler.AnimationHandle; import com.google.gwt.animation.client.AnimationScheduler.AnimationHandle;
import com.google.gwt.core.client.Duration; import com.google.gwt.core.client.Duration;
import com.google.gwt.core.client.JavaScriptException;
import com.google.gwt.core.client.JavaScriptObject; import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsArray; import com.google.gwt.core.client.JsArray;
import com.google.gwt.core.client.Scheduler; import com.google.gwt.core.client.Scheduler;


TableCellElement cellClone = TableCellElement TableCellElement cellClone = TableCellElement
.as((Element) cell.cloneNode(withContent)); .as((Element) cell.cloneNode(withContent));
if (!withContent || columnConfiguration
.getColumnWidth(cell.getCellIndex()) < 0) {
clearRelativeWidthContents(cellClone);
}
cellClone.getStyle().clearHeight(); cellClone.getStyle().clearHeight();
cellClone.getStyle().clearWidth(); cellClone.getStyle().clearWidth();


return requiredWidth; return requiredWidth;
} }


/**
* Contents of an element that is configured to have relative width
* shouldn't be taken into consideration when measuring minimum widths.
* Thus any such contents within the element hierarchy need to be
* cleared out for accurate results. The element itself should remain,
* however, in case it has styles that affect the end results.
*
* @param elem
* an element that might have unnecessary content that
* interferes with minimum width calculations
*/
private void clearRelativeWidthContents(Element elem) {
try {
String width = elem.getStyle().getWidth();
if (width != null && width.endsWith("%")) {
if (elem.hasChildNodes()) {
elem.removeAllChildren();
// add a fake child so that :empty behavior doesn't
// change
elem.setInnerHTML("<a/>");
} else {
elem.setInnerHTML(null);
}
}
} catch (JavaScriptException e) {
// no width set, move on
}
for (int i = 0; i < elem.getChildCount(); ++i) {
Node node = elem.getChild(i);
if (node instanceof Element) {
clearRelativeWidthContents((Element) node);
}
}
}

/** /**
* Gets the minimum width needed to display the cell properly. * Gets the minimum width needed to display the cell properly.
* *
// for a gap if a details row is later closed (e.g. by user) // for a gap if a details row is later closed (e.g. by user)
final int addToBottom = Math.min(rowDiff, final int addToBottom = Math.min(rowDiff,
getRowCount() - logicalTargetIndex); getRowCount() - logicalTargetIndex);
final int addToTop = rowDiff - addToBottom;
final int addToTop = Math.max(rowDiff - addToBottom, 0);


if (addToTop > 0) { if (addToTop > 0) {
fillAndPopulateEscalatorRowsIfNeeded(0, fillAndPopulateEscalatorRowsIfNeeded(0,
updateTopRowLogicalIndex(-addToTop); updateTopRowLogicalIndex(-addToTop);
} }
if (addToBottom > 0) { if (addToBottom > 0) {
fillAndPopulateEscalatorRowsIfNeeded(visualTargetIndex,
logicalTargetIndex, addToBottom);
// take into account that rows may have got added to top as
// well, affects visual but not logical indexing
fillAndPopulateEscalatorRowsIfNeeded(
visualTargetIndex + addToTop, logicalTargetIndex,
addToBottom);

// adding new rows due to resizing may have created a gap in
// the middle, check whether the existing rows need moving
double rowTop = getRowTop(oldTopRowLogicalIndex);
if (rowTop > getRowTop(visualRowOrder.get(addToTop))) {
for (int i = addToTop; i < visualTargetIndex; i++) {

final TableRowElement tr = visualRowOrder.get(i);

setRowPosition(tr, 0, rowTop);
rowTop += getDefaultRowHeight();
SpacerContainer.SpacerImpl spacer = spacerContainer
.getSpacer(oldTopRowLogicalIndex + i);
if (spacer != null) {
spacer.setPosition(0, rowTop);
rowTop += spacer.getHeight();
}
}
}
} }
} else if (rowDiff < 0) { } else if (rowDiff < 0) {
// rows need to be removed // rows need to be removed
public void scrollToRowAndSpacer(final int rowIndex, public void scrollToRowAndSpacer(final int rowIndex,
final ScrollDestination destination, final int padding) final ScrollDestination destination, final int padding)
throws IllegalArgumentException { throws IllegalArgumentException {
// wait for the layout phase to finish
Scheduler.get().scheduleFinally(() -> {
if (rowIndex != -1) {
verifyValidRowIndex(rowIndex);
}
body.scrollToRowSpacerOrBoth(rowIndex, destination, padding,
ScrollType.ROW_AND_SPACER);
});
if (rowIndex != -1) {
verifyValidRowIndex(rowIndex);
}
body.scrollToRowSpacerOrBoth(rowIndex, destination, padding,
ScrollType.ROW_AND_SPACER);
} }


private static void validateScrollDestination( private static void validateScrollDestination(

+ 180
- 84
client/src/main/java/com/vaadin/client/widgets/Grid.java View File

} }
state = State.ACTIVATING; state = State.ACTIVATING;


grid.scrollToRow(rowIndex,
isBuffered() ? ScrollDestination.MIDDLE
: ScrollDestination.ANY,
grid.scrollToRow(rowIndex, ScrollDestination.ANY,
() -> show(rowIndex, columnIndexDOM)); () -> show(rowIndex, columnIndexDOM));
} }


Scheduler.get().scheduleDeferred(this); Scheduler.get().scheduleDeferred(this);
} }
} else if (currentDataAvailable.isEmpty() } else if (currentDataAvailable.isEmpty()
&& dataSource.isWaitingForData()) {
&& (dataSource.isWaitingForData()
|| escalator.getBody().getRowCount() > 0)) {
Scheduler.get().scheduleDeferred(this); Scheduler.get().scheduleDeferred(this);
} else { } else {
calculate(); calculate();


/** /**
* Calculates and applies column widths, taking into account fixed * Calculates and applies column widths, taking into account fixed
* widths and column expand rules
* widths and column expand rules.
* *
* @param immediately
* <code>true</code> if the widths should be executed
* immediately (ignoring lazy loading completely), or
* <code>false</code> if the command should be run after a
* while (duplicate non-immediately invocations are ignored).
* @see Column#setWidth(double) * @see Column#setWidth(double)
* @see Column#setExpandRatio(int) * @see Column#setExpandRatio(int)
* @see Column#setMinimumWidth(double) * @see Column#setMinimumWidth(double)
* @see Column#setMaximumWidth(double) * @see Column#setMaximumWidth(double)
*/ */
public void schedule() { public void schedule() {
if (!isScheduled && isAttached()) {
if (!isScheduled && isAttached() && !(currentDataAvailable.isEmpty()
&& escalator.getBody().getRowCount() > 0)) {
isScheduled = true; isScheduled = true;
Scheduler.get().scheduleFinally(calculateCommand); Scheduler.get().scheduleFinally(calculateCommand);
} }


// Make SelectAllCheckbox visible // Make SelectAllCheckbox visible
getSelectionColumn().ifPresent(col -> { getSelectionColumn().ifPresent(col -> {
if (getDefaultHeaderRow() == null)
if (getDefaultHeaderRow() == null) {
return; return;
}
HeaderCell headerCell = getDefaultHeaderRow().getCell(col); HeaderCell headerCell = getDefaultHeaderRow().getCell(col);
if (headerCell.getType().equals(GridStaticCellType.WIDGET)) { if (headerCell.getType().equals(GridStaticCellType.WIDGET)) {
// SelectAllCheckbox is present already // SelectAllCheckbox is present already
setParent(detailsWidget, null); setParent(detailsWidget, null);
spacerElement.removeAllChildren(); spacerElement.removeAllChildren();
if (getHeightMode() == HeightMode.UNDEFINED) { if (getHeightMode() == HeightMode.UNDEFINED) {
// update spacer height
escalator.getBody().setSpacer(spacer.getRow(), 0);
setHeightByRows(getEscalator().getBody().getRowCount()); setHeightByRows(getEscalator().getBody().getRowCount());
} }
} }


private void setHeightToHeaderCellHeight() { private void setHeightToHeaderCellHeight() {
RowContainer header = grid.escalator.getHeader(); RowContainer header = grid.escalator.getHeader();
if (header.getRowCount() == 0
if (!WidgetUtil.isDisplayed(header.getElement())
|| header.getRowCount() == 0
|| !header.getRowElement(0).hasChildNodes()) { || !header.getRowElement(0).hasChildNodes()) {
getLogger().info( getLogger().info(
"No header cell available when calculating sidebar button height"); "No header cell available when calculating sidebar button height");
openCloseButton.setHeight(header.getDefaultRowHeight() + "px");
// If the Grid is hidden with styles when this is called the
// border height will be off, but it's usually only a matter of
// a pixel or so. Removing a style name cannot trigger a full
// refresh of the layout, it's developer's responsibility to do
// that where needed.
double height = header.getDefaultRowHeight()
- WidgetUtil.measureVerticalBorder(getElement()) / 2;
openCloseButton.setHeight(height + "px");


return; return;
} }


// Use actual height instead of expected height in case the height
// is modified by styles.
Element firstHeaderCell = header.getRowElement(0) Element firstHeaderCell = header.getRowElement(0)
.getFirstChildElement(); .getFirstChildElement();
double height = WidgetUtil double height = WidgetUtil
close(); close();
grid.getElement().appendChild(getElement()); grid.getElement().appendChild(getElement());
Grid.setParent(this, grid); Grid.setParent(this, grid);
// border calculation won't work until attached
setHeightToHeaderCellHeight();
} }
} }


@Override @Override
protected void onAttach() { protected void onAttach() {
super.onAttach(); super.onAttach();
// make sure the button will get correct height if the button should
// be visible when the grid is rendered the first time.
Scheduler.get()
.scheduleDeferred(() -> setHeightToHeaderCellHeight());
// Make sure the button will get correct height whenever the Sidebar
// is added to the Grid.
setHeightToHeaderCellHeight();
} }


@Override @Override
*/ */
private boolean hidingColumn; private boolean hidingColumn;


/**
* When several columns are set hidable, don't reset the Sidebar for
* every column separately.
*/
private boolean toggleUpdateTriggered;

private void updateColumnHidable(final Column<?, T> column) { private void updateColumnHidable(final Column<?, T> column) {
if (column.isHidable()) { if (column.isHidable()) {
MenuItem toggle = columnToHidingToggleMap.get(column); MenuItem toggle = columnToHidingToggleMap.get(column);
} }


private void updateTogglesOrder() { private void updateTogglesOrder() {
if (!hidingColumn) {
int lastIndex = 0;
for (Column<?, T> column : getColumns()) {
if (column.isHidable()) {
final MenuItem menuItem = columnToHidingToggleMap
.get(column);
sidebar.menuBar.removeItem(menuItem);
sidebar.menuBar.insertItem(menuItem, lastIndex++);
if (!hidingColumn && !toggleUpdateTriggered) {
// This method is called whenever a column is set hidable. If
// there are multiple hidable columns, it will get called
// separately for all of them. There is no need to update the
// order more than once and no other layouting is dependent on
// the Sidebar layouting getting finished first, so wait until
// all calls have arrived before proceeding further.
toggleUpdateTriggered = true;
Scheduler.get().scheduleFinally(() -> {
int lastIndex = 0;
for (Column<?, T> column : getColumns()) {
if (column.isHidable()) {
final MenuItem menuItem = columnToHidingToggleMap
.get(column);
sidebar.menuBar.removeItem(menuItem);
sidebar.menuBar.insertItem(menuItem, lastIndex++);
}
} }
}
toggleUpdateTriggered = false;
});
} }
} }




private boolean refreshBodyRequested = false; private boolean refreshBodyRequested = false;


private boolean resizeRequested = false;
private boolean resizeRefreshScheduled = false;

private DragAndDropHandler.DragAndDropCallback headerCellDndCallback = new DragAndDropCallback() { private DragAndDropHandler.DragAndDropCallback headerCellDndCallback = new DragAndDropCallback() {


private final AutoScrollerCallback autoScrollerCallback = new AutoScrollerCallback() { private final AutoScrollerCallback autoScrollerCallback = new AutoScrollerCallback() {
} else { } else {
this.hidden = hidden; this.hidden = hidden;


final int columnIndex = grid.getVisibleColumns()
int columnIndex = grid.getVisibleColumns()
.indexOf(this); .indexOf(this);
grid.escalator.getColumnConfiguration() grid.escalator.getColumnConfiguration()
.insertColumns(columnIndex, 1); .insertColumns(columnIndex, 1);
// escalator doesn't handle situation where the added column // escalator doesn't handle situation where the added column
// would be the last frozen column // would be the last frozen column
int gridFrozenColumns = grid.getFrozenColumnCount(); int gridFrozenColumns = grid.getFrozenColumnCount();
// Correct column index for multiselect mode
if (grid.getSelectionColumn().isPresent()) {
gridFrozenColumns++;
}
int escalatorFrozenColumns = grid.escalator int escalatorFrozenColumns = grid.escalator
.getColumnConfiguration().getFrozenColumnCount(); .getColumnConfiguration().getFrozenColumnCount();
if (gridFrozenColumns > escalatorFrozenColumns if (gridFrozenColumns > escalatorFrozenColumns
&& escalatorFrozenColumns == columnIndex) {
&& escalatorFrozenColumns == columnIndex
&& grid.getColumns()
.indexOf(this) < gridFrozenColumns) {
grid.escalator.getColumnConfiguration() grid.escalator.getColumnConfiguration()
.setFrozenColumnCount(++escalatorFrozenColumns); .setFrozenColumnCount(++escalatorFrozenColumns);
} }
this.dataSource = dataSource; this.dataSource = dataSource;
changeHandler = dataSource changeHandler = dataSource
.addDataChangeHandler(new DataChangeHandler() { .addDataChangeHandler(new DataChangeHandler() {
private boolean recalculateColumnWidthsNeeded = false;

@Override @Override
public void dataUpdated(int firstIndex, int numberOfItems) { public void dataUpdated(int firstIndex, int numberOfItems) {
escalator.getBody().refreshRows(firstIndex, escalator.getBody().refreshRows(firstIndex,
int numberOfItems) { int numberOfItems) {
currentDataAvailable = Range.withLength(firstIndex, currentDataAvailable = Range.withLength(firstIndex,
numberOfItems); numberOfItems);
if (recalculateColumnWidthsNeeded) {
// Ensure that cache has actually been populated or
// all rows removed, otherwise wait for next call.
if (numberOfItems > 0
|| getDataSource().size() == 0) {
recalculateColumnWidths();
recalculateColumnWidthsNeeded = false;
}
}
fireEvent(new DataAvailableEvent(currentDataAvailable)); fireEvent(new DataAvailableEvent(currentDataAvailable));
} }


@Override @Override
public void resetDataAndSize(int newSize) { public void resetDataAndSize(int newSize) {
// It might take a while for new data to arrive,
// clear the record of cached rows.
currentDataAvailable = Range.emptyRange();
RowContainer body = escalator.getBody(); RowContainer body = escalator.getBody();
int oldSize = body.getRowCount(); int oldSize = body.getRowCount();


// Need to recalculate column widths when the // Need to recalculate column widths when the
// first row is added to a non-header grid, // first row is added to a non-header grid,
// otherwise the checkbox will be aligned in a // otherwise the checkbox will be aligned in a
// wrong place.
recalculateColumnWidths();
// wrong place. Wait until the cache has been
// populated before making the call.
recalculateColumnWidthsNeeded = true;
} }
body.insertRows(oldSize, newSize - oldSize); body.insertRows(oldSize, newSize - oldSize);
cellFocusHandler.rowsAddedToBody(Range cellFocusHandler.rowsAddedToBody(Range
visibleRowRange.length()); visibleRowRange.length());
} else { } else {
// We won't expect any data more data updates, so // We won't expect any data more data updates, so
// just make
// the bookkeeping happy
// just make the bookkeeping happy.
dataAvailable(0, 0); dataAvailable(0, 0);
} }


} }


private void updateFrozenColumns() { private void updateFrozenColumns() {
escalator.getColumnConfiguration()
.setFrozenColumnCount(getVisibleFrozenColumnCount());
int visibleFrozenColumnCount = getVisibleFrozenColumnCount();
ColumnConfiguration columnConfiguration = escalator
.getColumnConfiguration();
if (columnConfiguration.getColumnCount() < visibleFrozenColumnCount) {
// new columns may not have got added yet, delay and check the
// correct count again
Scheduler.get().scheduleFinally(() -> columnConfiguration
.setFrozenColumnCount(getVisibleFrozenColumnCount()));
} else {
columnConfiguration.setFrozenColumnCount(visibleFrozenColumnCount);
}
} }


private int getVisibleFrozenColumnCount() { private int getVisibleFrozenColumnCount() {


// for the escalator the hidden columns are not in the frozen column // for the escalator the hidden columns are not in the frozen column
// count, but for grid they are. thus need to convert the index // count, but for grid they are. thus need to convert the index
for (int i = 0; i < frozenColumnCount; i++) {
int limit = getFrozenColumnCount();
if (getSelectionColumn().isPresent()) {
// If the grid is in MultiSelect mode, getColumn(0) in the following
// for loop returns the selection column. Accordingly, verifying
// which frozen columns are visible if the selection column is
// present should take this fact into account.
limit++;
}
for (int i = 0; i < limit; i++) {
if (i >= getColumnCount() || getColumn(i).isHidden()) { if (i >= getColumnCount() || getColumn(i).isHidden()) {
numberOfColumns--; numberOfColumns--;
} }
*/ */
public void scrollToRow(int rowIndex, ScrollDestination destination, public void scrollToRow(int rowIndex, ScrollDestination destination,
Runnable callback) { Runnable callback) {
waitUntilVisible(rowIndex, destination, () -> {
Reference<HandlerRegistration> registration = new Reference<>();
registration.set(addDataAvailableHandler(event -> {
if (event.getAvailableRows().contains(rowIndex)) {
registration.get().removeHandler();
callback.run();
}
}));
});
waitUntilVisible(rowIndex, destination, callback);
} }


/** /**
*/ */
private void waitUntilVisible(int rowIndex, ScrollDestination destination, private void waitUntilVisible(int rowIndex, ScrollDestination destination,
Runnable whenVisible) { Runnable whenVisible) {
final Escalator escalator = getEscalator();
if (escalator.getVisibleRowRange().contains(rowIndex)) {
TableRowElement rowElement = escalator.getBody()
.getRowElement(rowIndex);
long bottomBorder = Math.round(WidgetUtil.getBorderBottomThickness(
rowElement.getFirstChildElement()) + 0.5d);
if (rowElement.getAbsoluteTop() + bottomBorder >= escalator
.getHeader().getElement().getAbsoluteBottom()
&& rowElement.getAbsoluteBottom() <= escalator.getFooter()
.getElement().getAbsoluteTop() + bottomBorder) {
whenVisible.run();
return;
}
boolean waitForCache = false;
if (getDataSource().getRow(rowIndex) == null) {
// not yet in cache, wait for this to change
waitForCache = true;

Reference<Registration> registration = new Reference<>();
registration.set(getDataSource()
.addDataChangeHandler(new DataChangeHandler() {
@Override
public void resetDataAndSize(int estimatedNewDataSize) {
// data set changed, cancel the operation
registration.get().remove();
}

@Override
public void dataUpdated(int firstRowIndex,
int numberOfRows) {
// NOP
}

@Override
public void dataRemoved(int firstRowIndex,
int numberOfRows) {
// data set changed, cancel the operation
registration.get().remove();
}

@Override
public void dataAvailable(int firstRowIndex,
int numberOfRows) {
// if new available range contains the row,
// try again
if (Range.withLength(firstRowIndex, numberOfRows)
.contains(rowIndex)) {
registration.get().remove();
waitUntilVisible(rowIndex, destination,
whenVisible);
}
}

@Override
public void dataAdded(int firstRowIndex,
int numberOfRows) {
// data set changed, cancel the operation
registration.get().remove();
}
}));
} }


Reference<HandlerRegistration> registration = new Reference<>();
registration.set(addScrollHandler(event -> {
if (escalator.getVisibleRowRange().contains(rowIndex)) {
registration.get().removeHandler();
whenVisible.run();
}
}));
scrollToRow(rowIndex, destination); scrollToRow(rowIndex, destination);

if (!waitForCache) {
// all necessary adjustments done, time to perform
whenVisible.run();
}
} }


/** /**
/* /*
* Delay calculation to be deferred so Escalator can do it's magic. * Delay calculation to be deferred so Escalator can do it's magic.
*/ */
Scheduler.get().scheduleFinally(() -> {
if (escalator
.getInnerWidth() != autoColumnWidthsRecalculator.lastCalculatedInnerWidth) {
recalculateColumnWidths();
}
resizeRequested = true;
if (!resizeRefreshScheduled) {
resizeRefreshScheduled = true;
Scheduler.get().scheduleFixedDelay(() -> {
if (!resizeRequested) {
doRefreshOnResize();
resizeRefreshScheduled = false;
return false;
} else {
resizeRequested = false;
return true;
}
}, 50);
}
}


// Vertical resizing could make editor positioning invalid so it
// needs to be recalculated on resize
if (isEditorActive()) {
editor.updateVerticalScrollPosition();
}
private void doRefreshOnResize() {
if (escalator
.getInnerWidth() != autoColumnWidthsRecalculator.lastCalculatedInnerWidth) {
recalculateColumnWidths();
}


// if there is a resize, we need to refresh the body to avoid an
// off-by-one error which occurs when the user scrolls all the
// way to the bottom.
refreshBody();
});
// Vertical resizing could make editor positioning invalid so it
// needs to be recalculated on resize
if (isEditorActive()) {
editor.updateVerticalScrollPosition();
}

// if there is a resize, we need to refresh the body to avoid an
// off-by-one error which occurs when the user scrolls all the
// way to the bottom.
refreshBody();
} }


private double getEscalatorInnerHeight() { private double getEscalatorInnerHeight() {

+ 1
- 1
compatibility-client-compiled/pom.xml View File

<parent> <parent>
<groupId>com.vaadin</groupId> <groupId>com.vaadin</groupId>
<artifactId>vaadin-root</artifactId> <artifactId>vaadin-root</artifactId>
<version>8.10-SNAPSHOT</version>
<version>8.12-SNAPSHOT</version>
</parent> </parent>
<artifactId>vaadin-compatibility-client-compiled</artifactId> <artifactId>vaadin-compatibility-client-compiled</artifactId>
<name>vaadin-compatibility-client-compiled</name> <name>vaadin-compatibility-client-compiled</name>

+ 1
- 1
compatibility-client/pom.xml View File

<parent> <parent>
<groupId>com.vaadin</groupId> <groupId>com.vaadin</groupId>
<artifactId>vaadin-root</artifactId> <artifactId>vaadin-root</artifactId>
<version>8.10-SNAPSHOT</version>
<version>8.12-SNAPSHOT</version>
</parent> </parent>
<artifactId>vaadin-compatibility-client</artifactId> <artifactId>vaadin-compatibility-client</artifactId>
<name>vaadin-compatibility-client</name> <name>vaadin-compatibility-client</name>

+ 2
- 17
compatibility-client/src/main/java/com/vaadin/v7/client/ui/VFilterSelect.java View File

* @since 7.6.4 * @since 7.6.4
*/ */
public FilterSelectTextBox() { public FilterSelectTextBox() {
/*-
* Stop the browser from showing its own suggestion popup.
*
* Using an invalid value instead of "off" as suggested by
* https://developer.mozilla.org/en-US/docs/Web/Security/Securing_your_site/Turning_off_form_autocompletion
*
* Leaving the non-standard Safari options autocapitalize and
* autocorrect untouched since those do not interfere in the same
* way, and they might be useful in a combo box where new items are
* allowed.
*/
if (BrowserInfo.get().isChrome()) {
// Chrome supports "off" and random number does not work with Chrome
getElement().setAttribute("autocomplete", "off");
} else {
getElement().setAttribute("autocomplete", Math.random() + "");
}
// Stop the browser from showing its own suggestion popup.
WidgetUtil.disableBrowserAutocomplete(this);
} }


/** /**

+ 14
- 5
compatibility-client/src/main/java/com/vaadin/v7/client/ui/VScrollTable.java View File



private SelectMode selectMode = SelectMode.NONE; private SelectMode selectMode = SelectMode.NONE;


private boolean multiSelectTouchDetectionEnabled = true;

public final HashSet<String> selectedRowKeys = new HashSet<String>(); public final HashSet<String> selectedRowKeys = new HashSet<String>();


/* /*
} else { } else {
selectMode = SelectMode.NONE; selectMode = SelectMode.NONE;
} }
if (uidl.hasAttribute("touchdetection")) {
multiSelectTouchDetectionEnabled = uidl
.getBooleanAttribute("touchdetection");
}
} }
} }


} }


private void setMultiSelectMode(int multiselectmode) { private void setMultiSelectMode(int multiselectmode) {
if (BrowserInfo.get().isTouchDevice()) {
if (BrowserInfo.get().isTouchDevice()
&& multiSelectTouchDetectionEnabled) {
// Always use the simple mode for touch devices that do not have // Always use the simple mode for touch devices that do not have
// shift/ctrl keys
// shift/ctrl keys (unless this feature is explicitly disabled)
this.multiselectmode = MULTISELECT_MODE_SIMPLE; this.multiselectmode = MULTISELECT_MODE_SIMPLE;
} else { } else {
this.multiselectmode = multiselectmode; this.multiselectmode = multiselectmode;
public void onBrowserEvent(Event event) { public void onBrowserEvent(Event event) {
if (enabled) { if (enabled) {
if (event.getEventTarget().cast() == columnSelector) { if (event.getEventTarget().cast() == columnSelector) {
final int left = DOM.getAbsoluteLeft(columnSelector);
final int top = DOM.getAbsoluteTop(columnSelector)
WidgetUtil.TextRectangle clientRect = WidgetUtil
.getBoundingClientRect(columnSelector);
final int left = (int) clientRect.getLeft();
final int top = (int) (clientRect.getTop()
+ DOM.getElementPropertyInt(columnSelector, + DOM.getElementPropertyInt(columnSelector,
"offsetHeight");
"offsetHeight"));
client.getContextMenu().showAt(this, left, top); client.getContextMenu().showAt(this, left, top);
} }
} }

+ 3
- 0
compatibility-client/src/main/java/com/vaadin/v7/client/ui/VTextualDate.java View File

import com.vaadin.client.Focusable; import com.vaadin.client.Focusable;
import com.vaadin.client.LocaleNotLoadedException; import com.vaadin.client.LocaleNotLoadedException;
import com.vaadin.client.LocaleService; import com.vaadin.client.LocaleService;
import com.vaadin.client.WidgetUtil;
import com.vaadin.client.ui.SubPartAware; import com.vaadin.client.ui.SubPartAware;
import com.vaadin.client.ui.aria.AriaHelper; import com.vaadin.client.ui.aria.AriaHelper;
import com.vaadin.client.ui.aria.HandlesAriaCaption; import com.vaadin.client.ui.aria.HandlesAriaCaption;
if (BrowserInfo.get().isIE()) { if (BrowserInfo.get().isIE()) {
addDomHandler(this, KeyDownEvent.getType()); addDomHandler(this, KeyDownEvent.getType());
} }
// Stop the browser from showing its own suggestion popup.
WidgetUtil.disableBrowserAutocomplete(text);
add(text); add(text);
} }



+ 1
- 0
compatibility-client/src/main/java/com/vaadin/v7/client/widgets/Escalator.java View File

visualRowOrder.getLast()) + 1; visualRowOrder.getLast()) + 1;
moveAndUpdateEscalatorRows(Range.withOnly(0), moveAndUpdateEscalatorRows(Range.withOnly(0),
visualRowOrder.size(), newLogicalIndex); visualRowOrder.size(), newLogicalIndex);
updateTopRowLogicalIndex(1);
} }
} }
} }

+ 46
- 24
compatibility-client/src/main/java/com/vaadin/v7/client/widgets/Grid.java View File



// sometimes focus handling twists the editor row out of alignment // sometimes focus handling twists the editor row out of alignment
// with the grid itself and the position needs to be compensated for // with the grid itself and the position needs to be compensated for
TableRowElement rowElement = grid.getEscalator().getBody()
.getRowElement(grid.getEditor().getRow());
int rowLeft = rowElement.getAbsoluteLeft();
int editorLeft = cellWrapper.getAbsoluteLeft();
if (editorLeft != rowLeft + frozenWidth) {
cellWrapper.getStyle().setLeft(newLeft + rowLeft - editorLeft,
Unit.PX);
try {
TableRowElement rowElement = grid.getEscalator().getBody()
.getRowElement(grid.getEditor().getRow());
int rowLeft = rowElement.getAbsoluteLeft();
int editorLeft = cellWrapper.getAbsoluteLeft();
if (editorLeft != rowLeft + frozenWidth) {
cellWrapper.getStyle().setLeft(newLeft + rowLeft - editorLeft,
Unit.PX);
}
} catch (IllegalStateException e) {
// IllegalStateException may occur if user has scrolled Grid so
// that Escalator has updated, and row under Editor is no longer
// there
} }
} }


private CheckBox selectAllCheckBox; private CheckBox selectAllCheckBox;
private boolean userSelectionAllowed = true; private boolean userSelectionAllowed = true;
private boolean enabled = true; private boolean enabled = true;
private HandlerRegistration headerClickHandler;


SelectionColumn(final Renderer<Boolean> selectColumnRenderer) { SelectionColumn(final Renderer<Boolean> selectColumnRenderer) {
super(selectColumnRenderer); super(selectColumnRenderer);
}); });
selectAllCheckBox.setValue(selected); selectAllCheckBox.setValue(selected);


addHeaderClickHandler(new HeaderClickHandler() {
@Override
public void onClick(GridClickEvent event) {
if (!userSelectionAllowed) {
return;
}

CellReference<?> targetCell = event.getTargetCell();
int defaultRowIndex = getHeader().getRows()
.indexOf(getDefaultHeaderRow());
headerClickHandler = addHeaderClickHandler(
new HeaderClickHandler() {
@Override
public void onClick(GridClickEvent event) {
if (!userSelectionAllowed) {
return;
}


if (targetCell.getColumnIndex() == 0 && targetCell
.getRowIndex() == defaultRowIndex) {
selectAllCheckBox.setValue(
!selectAllCheckBox.getValue(), true);
}
}
});
CellReference<?> targetCell = event
.getTargetCell();
int defaultRowIndex = getHeader().getRows()
.indexOf(getDefaultHeaderRow());

if (targetCell.getColumnIndex() == 0
&& targetCell
.getRowIndex() == defaultRowIndex) {
selectAllCheckBox.setValue(
!selectAllCheckBox.getValue(),
true);
}
}
});


// Select all with space when "select all" cell is active // Select all with space when "select all" cell is active
addHeaderKeyUpHandler(new HeaderKeyUpHandler() { addHeaderKeyUpHandler(new HeaderKeyUpHandler() {
getEscalator().getBody().refreshRows(0, getEscalator().getBody().refreshRows(0,
getEscalator().getBody().getRowCount()); getEscalator().getBody().getRowCount());
} }

private void cleanup() {
if (headerClickHandler != null) {
headerClickHandler.removeHandler();
headerClickHandler = null;
}
}
} }


/** /**
return; return;
} }


if (this.selectionColumn != null) {
selectionColumn.cleanup();
}

if (this.selectColumnRenderer != null) { if (this.selectColumnRenderer != null) {
if (this.selectColumnRenderer instanceof ComplexRenderer) { if (this.selectColumnRenderer instanceof ComplexRenderer) {
// End of Life for the old selection column renderer. // End of Life for the old selection column renderer.

+ 1
- 1
compatibility-server-gae/pom.xml View File

<parent> <parent>
<groupId>com.vaadin</groupId> <groupId>com.vaadin</groupId>
<artifactId>vaadin-root</artifactId> <artifactId>vaadin-root</artifactId>
<version>8.10-SNAPSHOT</version>
<version>8.12-SNAPSHOT</version>
</parent> </parent>
<artifactId>vaadin-compatibility-server-gae</artifactId> <artifactId>vaadin-compatibility-server-gae</artifactId>
<name>vaadin-compatibility-server-gae</name> <name>vaadin-compatibility-server-gae</name>

+ 1
- 1
compatibility-server/pom.xml View File

<parent> <parent>
<groupId>com.vaadin</groupId> <groupId>com.vaadin</groupId>
<artifactId>vaadin-root</artifactId> <artifactId>vaadin-root</artifactId>
<version>8.10-SNAPSHOT</version>
<version>8.12-SNAPSHOT</version>
</parent> </parent>
<artifactId>vaadin-compatibility-server</artifactId> <artifactId>vaadin-compatibility-server</artifactId>
<name>vaadin-compatibility-server</name> <name>vaadin-compatibility-server</name>

+ 3
- 1
compatibility-server/src/main/java/com/vaadin/v7/ui/ComboBox.java View File

// page length usable for non-null items // page length usable for non-null items
int effectivePageLength = pageLength int effectivePageLength = pageLength
- (needNullSelectOption && (currentPage == 0) ? 1 : 0); - (needNullSelectOption && (currentPage == 0) ? 1 : 0);
return Math.min(size - 1, first + effectivePageLength - 1);
// zero pageLength implies infinite page size
return pageLength == 0 ? size - 1
: Math.min(size - 1, first + effectivePageLength - 1);
} }


/** /**

+ 2
- 0
compatibility-server/src/main/java/com/vaadin/v7/ui/Grid.java View File

import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;


import org.jsoup.Jsoup;
import org.jsoup.nodes.Attributes; import org.jsoup.nodes.Attributes;
import org.jsoup.nodes.Element; import org.jsoup.nodes.Element;
import org.jsoup.select.Elements; import org.jsoup.select.Elements;
if (caption == null) { if (caption == null) {
caption = ""; // Render null as empty caption = ""; // Render null as empty
} }
caption = Jsoup.parse(caption).text();
state.headerCaption = caption; state.headerCaption = caption;


HeaderRow row = grid.getHeader().getDefaultRow(); HeaderRow row = grid.getHeader().getDefaultRow();

+ 35
- 1
compatibility-server/src/main/java/com/vaadin/v7/ui/Table.java View File



private MultiSelectMode multiSelectMode = MultiSelectMode.DEFAULT; private MultiSelectMode multiSelectMode = MultiSelectMode.DEFAULT;


private boolean multiSelectTouchDetectionEnabled = true;

private boolean rowCacheInvalidated; private boolean rowCacheInvalidated;


private RowGenerator rowGenerator = null; private RowGenerator rowGenerator = null;
if (isSelectable()) { if (isSelectable()) {
target.addAttribute("selectmode", target.addAttribute("selectmode",
(isMultiSelect() ? "multi" : "single")); (isMultiSelect() ? "multi" : "single"));
if (isMultiSelect()) {
target.addAttribute("touchdetection",
isMultiSelectTouchDetectionEnabled());
}
} else { } else {
target.addAttribute("selectmode", "none"); target.addAttribute("selectmode", "none");
} }
* <p> * <p>
* Note, that on some clients the mode may not be respected. E.g. on touch * Note, that on some clients the mode may not be respected. E.g. on touch
* based devices CTRL/SHIFT base selection method is invalid, so touch based * based devices CTRL/SHIFT base selection method is invalid, so touch based
* browsers always use the {@link MultiSelectMode#SIMPLE}.
* browsers always use the {@link MultiSelectMode#SIMPLE} unless touch multi
* select is explicitly disabled.
*
* @see #setMultiSelectTouchDetectionEnabled(boolean)
* *
* @param mode * @param mode
* The select mode of the table * The select mode of the table
return multiSelectMode; return multiSelectMode;
} }


/**
* Default behavior on touch-reporting devices is to switch from CTRL/SHIFT
* based multi-selection to simple mode, but you can use this method to
* explicitly disable the touch device detection. Thus you can keep using
* keyboard-based multi selection on hybrid devices that have both a touch
* screen and a keyboard.
*
* @param multiSelectTouchDetectionEnabled
* Whether to enable or disable touch screen detection
*/
public void setMultiSelectTouchDetectionEnabled(
boolean multiSelectTouchDetectionEnabled) {
this.multiSelectTouchDetectionEnabled = multiSelectTouchDetectionEnabled;
markAsDirty();
}

/**
* Returns if touch screen detection is used to toggle multi select mode.
*
* @return If touch screen detection for multi select is enabled
*/
public boolean isMultiSelectTouchDetectionEnabled() {
return multiSelectTouchDetectionEnabled;
}

/** /**
* Lazy loading accept criterion for Table. Accepted target rows are loaded * Lazy loading accept criterion for Table. Accepted target rows are loaded
* from server once per drag and drop operation. Developer must override one * from server once per drag and drop operation. Developer must override one

+ 1
- 1
compatibility-shared/pom.xml View File

<parent> <parent>
<groupId>com.vaadin</groupId> <groupId>com.vaadin</groupId>
<artifactId>vaadin-root</artifactId> <artifactId>vaadin-root</artifactId>
<version>8.10-SNAPSHOT</version>
<version>8.12-SNAPSHOT</version>
</parent> </parent>
<artifactId>vaadin-compatibility-shared</artifactId> <artifactId>vaadin-compatibility-shared</artifactId>
<name>vaadin-compatibility-shared</name> <name>vaadin-compatibility-shared</name>

+ 1
- 1
compatibility-themes/pom.xml View File

<parent> <parent>
<groupId>com.vaadin</groupId> <groupId>com.vaadin</groupId>
<artifactId>vaadin-root</artifactId> <artifactId>vaadin-root</artifactId>
<version>8.10-SNAPSHOT</version>
<version>8.12-SNAPSHOT</version>
</parent> </parent>
<artifactId>vaadin-compatibility-themes</artifactId> <artifactId>vaadin-compatibility-themes</artifactId>
<name>vaadin-compatibility-themes</name> <name>vaadin-compatibility-themes</name>

+ 1
- 0
compatibility-themes/src/main/themes/VAADIN/themes/base/table/table.scss View File

} }
.#{$primaryStyleName}-body { .#{$primaryStyleName}-body {
border: 1px solid #aaa; border: 1px solid #aaa;
overflow-anchor: none; /* In Chrome 56+ */
} }
.#{$primaryStyleName}-row-spacer { .#{$primaryStyleName}-row-spacer {
height: 10px; height: 10px;

+ 1
- 0
documentation/articles/FindingTheCurrentUIAndPageAndVaadinSession.asciidoc View File



[[finding-the-current-ui-and-page-and-vaadin-session]] [[finding-the-current-ui-and-page-and-vaadin-session]]
= Finding the current UI and page and vaadin session = Finding the current UI and page and vaadin session

There are many cases where you need a reference to the active `UI`, `Page` There are many cases where you need a reference to the active `UI`, `Page`
or `VaadinServiceSession`, for instance for showing notifications in a or `VaadinServiceSession`, for instance for showing notifications in a
click listener. It is possible to get a reference to the component from click listener. It is possible to get a reference to the component from

+ 9
- 2
documentation/articles/LettingTheUserDownloadAFile.asciidoc View File

return super.handleConnectorRequest(request, response, path); return super.handleConnectorRequest(request, response, path);
} }


private StreamResource getResource () {
return (StreamResource) this.getResource("dl");
private StreamResource getResource() {
StreamResource result = null;
this.getSession().lock();
try {
result = (StreamResource) this.getResource("dl");
} finally {
this.getSession().unlock();
}
return result;
} }
} }
.... ....

+ 7
- 0
documentation/components/components-grid.asciidoc View File

You can set a component in a header or footer cell with You can set a component in a header or footer cell with
[methodname]#setComponent()#. Often, this feature is used to allow filtering. [methodname]#setComponent()#. Often, this feature is used to allow filtering.


[NOTE]
====
Note, when you use [methodname]#setComponent(TextField)#, the [classname]#TextField# will be rendered in compact mode
without caption and icon. If you need to override this behavior, you need to wrap the [classname]#TextField# e.g.
into [classname]#HorizontalLayout#.
====

//// ////
// commented out until filtering is sorted for 8 // commented out until filtering is sorted for 8
[[components.grid.filtering]] [[components.grid.filtering]]

+ 13
- 0
documentation/datamodel/datamodel-forms.asciidoc View File



binder.forField(titleField) binder.forField(titleField)
// Shorthand for requiring the field to be non-empty // Shorthand for requiring the field to be non-empty
// This is conditional on Binding::setAsRequiredEnabled(boolean)
.asRequired("Every employee must have a title") .asRequired("Every employee must have a title")
.bind(Person::getTitle, Person::setTitle); .bind(Person::getTitle, Person::setTitle);
---- ----
---- ----
-- --


[NOTE]
Note, if you need to write the values passing the validation regardless of having one or more failing validators, you can
use binder.writeBeanAsDraft(person).


Binder keeps track of which bindings have been updated by the user and which bindings are in an invalid state. Binder keeps track of which bindings have been updated by the user and which bindings are in an invalid state.
It also fires an event when this status changes. It also fires an event when this status changes.
We can use that event to make the save and reset buttons of our forms become enabled or disabled depending on the current status of the form. We can use that event to make the save and reset buttons of our forms become enabled or disabled depending on the current status of the form.


binder.bindInstanceFields(this); binder.bindInstanceFields(this);
---- ----

[NOTE]
If you need to bind nested proprerties with bindInstanceFields method, you need to instantiate the Binder using:
[source,java]
----
Binder<Person> binder = new Binder<>(Person.class,true);
----

+ 1
- 1
liferay-integration/pom.xml View File

<parent> <parent>
<groupId>com.vaadin</groupId> <groupId>com.vaadin</groupId>
<artifactId>vaadin-root</artifactId> <artifactId>vaadin-root</artifactId>
<version>8.10-SNAPSHOT</version>
<version>8.12-SNAPSHOT</version>
</parent> </parent>


<properties> <properties>

+ 1
- 1
liferay/pom.xml View File

<parent> <parent>
<groupId>com.vaadin</groupId> <groupId>com.vaadin</groupId>
<artifactId>vaadin-root</artifactId> <artifactId>vaadin-root</artifactId>
<version>8.10-SNAPSHOT</version>
<version>8.12-SNAPSHOT</version>
</parent> </parent>
<artifactId>vaadin-liferay</artifactId> <artifactId>vaadin-liferay</artifactId>
<name>vaadin-liferay</name> <name>vaadin-liferay</name>

+ 1
- 1
osgi-integration/pom.xml View File

<parent> <parent>
<groupId>com.vaadin</groupId> <groupId>com.vaadin</groupId>
<artifactId>vaadin-root</artifactId> <artifactId>vaadin-root</artifactId>
<version>8.10-SNAPSHOT</version>
<version>8.12-SNAPSHOT</version>
</parent> </parent>


<properties> <properties>

+ 16
- 2
pom.xml View File

<artifactId>vaadin-root</artifactId> <artifactId>vaadin-root</artifactId>
<name>vaadin-root</name> <name>vaadin-root</name>
<packaging>pom</packaging> <packaging>pom</packaging>
<version>8.10-SNAPSHOT</version>
<version>8.12-SNAPSHOT</version>


<prerequisites> <prerequisites>
<maven>3.1.0</maven> <maven>3.1.0</maven>
</properties> </properties>


<pluginRepositories> <pluginRepositories>
<pluginRepository>
<id>central</id>
<url>https://repo.maven.apache.org/maven2/</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
<pluginRepository> <pluginRepository>
<id>vaadin-snapshots</id> <id>vaadin-snapshots</id>
<url>https://oss.sonatype.org/content/repositories/vaadin-snapshots/</url> <url>https://oss.sonatype.org/content/repositories/vaadin-snapshots/</url>
</pluginRepositories> </pluginRepositories>


<repositories> <repositories>
<repository>
<id>central</id>
<url>https://repo.maven.apache.org/maven2/</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository> <repository>
<id>vaadin-snapshots</id> <id>vaadin-snapshots</id>
<url>https://oss.sonatype.org/content/repositories/vaadin-snapshots/</url> <url>https://oss.sonatype.org/content/repositories/vaadin-snapshots/</url>
<dependency> <dependency>
<groupId>org.hibernate</groupId> <groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId> <artifactId>hibernate-validator</artifactId>
<version>4.2.0.Final</version>
<version>4.3.2.Final</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.jsoup</groupId> <groupId>org.jsoup</groupId>

+ 1
- 1
push/pom.xml View File

<parent> <parent>
<groupId>com.vaadin</groupId> <groupId>com.vaadin</groupId>
<artifactId>vaadin-root</artifactId> <artifactId>vaadin-root</artifactId>
<version>8.10-SNAPSHOT</version>
<version>8.12-SNAPSHOT</version>
</parent> </parent>
<artifactId>vaadin-push</artifactId> <artifactId>vaadin-push</artifactId>
<name>vaadin-push</name> <name>vaadin-push</name>

+ 1
- 1
server/bnd.bnd View File

Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Bundle-License: http://www.apache.org/licenses/LICENSE-2.0 Bundle-License: http://www.apache.org/licenses/LICENSE-2.0
Import-Package: com.vaadin.sass.*;resolution:=optional,\ Import-Package: com.vaadin.sass.*;resolution:=optional,\
com.liferay.portal.kernel.util;resolution:=optional;version='[7.0.0,10.0.0)',\
com.liferay.portal.kernel.util;resolution:=optional;version='[7.0.0,11.0.0)',\
javax.portlet*;resolution:=optional,\ javax.portlet*;resolution:=optional,\
javax.validation*;resolution:=optional;version='${javax.validation.version}',\ javax.validation*;resolution:=optional;version='${javax.validation.version}',\
org.atmosphere*;resolution:=optional;version='${atmosphere.runtime.version}',\ org.atmosphere*;resolution:=optional;version='${atmosphere.runtime.version}',\

+ 1
- 1
server/pom.xml View File

<parent> <parent>
<groupId>com.vaadin</groupId> <groupId>com.vaadin</groupId>
<artifactId>vaadin-root</artifactId> <artifactId>vaadin-root</artifactId>
<version>8.10-SNAPSHOT</version>
<version>8.12-SNAPSHOT</version>
</parent> </parent>
<artifactId>vaadin-server</artifactId> <artifactId>vaadin-server</artifactId>
<name>vaadin-server</name> <name>vaadin-server</name>

+ 5
- 2
server/src/main/java/com/vaadin/data/BeanValidationBinder.java View File

* the bean type to use, not <code>null</code> * the bean type to use, not <code>null</code>
*/ */
public BeanValidationBinder(Class<BEAN> beanType) { public BeanValidationBinder(Class<BEAN> beanType) {
this(beanType,false);
this(beanType, false);
} }


/** /**
* the bean type to use, not {@code null} * the bean type to use, not {@code null}
* @param scanNestedDefinitions * @param scanNestedDefinitions
* if {@code true}, scan for nested property definitions as well * if {@code true}, scan for nested property definitions as well
*
* @since 8.10
*/ */
public BeanValidationBinder(Class<BEAN> beanType, boolean scanNestedDefinitions) {
public BeanValidationBinder(Class<BEAN> beanType,
boolean scanNestedDefinitions) {
super(beanType, scanNestedDefinitions); super(beanType, scanNestedDefinitions);
if (!BeanUtil.checkBeanValidationAvailable()) { if (!BeanUtil.checkBeanValidationAvailable()) {
throw new IllegalStateException(BeanValidationBinder.class throw new IllegalStateException(BeanValidationBinder.class

+ 135
- 19
server/src/main/java/com/vaadin/data/Binder.java View File

public Setter<BEAN, TARGET> getSetter(); public Setter<BEAN, TARGET> getSetter();


/** /**
* Enable or disable asRequired validator.
* The validator is enabled by default.
* Enable or disable asRequired validator. The validator is enabled by
* default.
* *
* @see #asRequired(String) * @see #asRequired(String)
* @see #asRequired(ErrorMessageProvider) * @see #asRequired(ErrorMessageProvider)
* *
* @param asRequiredEnabled * @param asRequiredEnabled
* {@code false} if asRequired validator should
* be disabled, {@code true} otherwise (default)
* {@code false} if asRequired validator should be disabled,
* {@code true} otherwise (default)
*
* @since 8.10
*/ */
public void setAsRequiredEnabled(boolean asRequiredEnabled); public void setAsRequiredEnabled(boolean asRequiredEnabled);


/** /**
* Returns whether asRequired validator is currently enabled or not
* Returns whether asRequired validator is currently enabled or not.
* *
* @see #asRequired(String) * @see #asRequired(String)
* @see #asRequired(ErrorMessageProvider) * @see #asRequired(ErrorMessageProvider)
* *
* @return {@code false} if asRequired validator is disabled * @return {@code false} if asRequired validator is disabled
* {@code true} otherwise (default) * {@code true} otherwise (default)
*
* @since 8.10
*/ */
public boolean isAsRequiredEnabled(); public boolean isAsRequiredEnabled();

/**
* Define whether validators are disabled or enabled for this
* specific binding.
*
* @param validatorsDisabled A boolean value
*
* @since 8.11
*/
public void setValidatorsDisabled(boolean validatorsDisabled);

/**
* Returns if validators are currently disabled or not
*
* @return A boolean value
*
* @since 8.11
*/
public boolean isValidatorsDisabled();
} }


/** /**
private final HasValue<FIELDVALUE> field; private final HasValue<FIELDVALUE> field;
private BindingValidationStatusHandler statusHandler; private BindingValidationStatusHandler statusHandler;
private boolean isStatusHandlerChanged; private boolean isStatusHandlerChanged;
private Binding<BEAN, TARGET> binding;


private boolean bound; private boolean bound;




bound = true; bound = true;
getBinder().incompleteBindings.remove(getField()); getBinder().incompleteBindings.remove(getField());
this.binding = binding;


return binding; return binding;
} }
Binding binding = ((BindingBuilder) finalBinding).bind(getter, Binding binding = ((BindingBuilder) finalBinding).bind(getter,
setter); setter);
getBinder().boundProperties.put(propertyName, binding); getBinder().boundProperties.put(propertyName, binding);
this.binding = binding;
return binding; return binding;
} finally { } finally {
getBinder().incompleteMemberFieldBindings.remove(getField()); getBinder().incompleteMemberFieldBindings.remove(getField());
checkUnbound(); checkUnbound();
Objects.requireNonNull(validator, "validator cannot be null"); Objects.requireNonNull(validator, "validator cannot be null");


Validator<? super TARGET> wrappedValidator = ((value, context) -> {
if (getBinder().isValidatorsDisabled() ||
(binding != null && binding.isValidatorsDisabled())) {
return ValidationResult.ok();
} else {
return validator.apply(value, context);
}
});

converterValidatorChain = ((Converter<FIELDVALUE, TARGET>) converterValidatorChain) converterValidatorChain = ((Converter<FIELDVALUE, TARGET>) converterValidatorChain)
.chain(new ValidatorAsConverter<>(validator));
.chain(new ValidatorAsConverter<>(wrappedValidator));
return this; return this;
} }


this.asRequiredSet = true; this.asRequiredSet = true;
field.setRequiredIndicatorVisible(true); field.setRequiredIndicatorVisible(true);
return withValidator((value, context) -> { return withValidator((value, context) -> {
if (!field.isRequiredIndicatorVisible())
if (!field.isRequiredIndicatorVisible()) {
return ValidationResult.ok(); return ValidationResult.ok();
else
} else {
return customRequiredValidator.apply(value, context); return customRequiredValidator.apply(value, context);
}
}); });
} }




private boolean asRequiredSet; private boolean asRequiredSet;


private boolean validatorsDisabled = false;

public BindingImpl(BindingBuilderImpl<BEAN, FIELDVALUE, TARGET> builder, public BindingImpl(BindingBuilderImpl<BEAN, FIELDVALUE, TARGET> builder,
ValueProvider<BEAN, TARGET> getter, ValueProvider<BEAN, TARGET> getter,
Setter<BEAN, TARGET> setter) { Setter<BEAN, TARGET> setter) {
* field doesn't accept null rather than throwing for some other * field doesn't accept null rather than throwing for some other
* reason. * reason.
*/ */
if (convertedValue == null && getField().getEmptyValue() != null) {
if (convertedValue == null
&& getField().getEmptyValue() != null) {
throw new IllegalStateException(String.format( throw new IllegalStateException(String.format(
"A field of type %s didn't accept a null value." "A field of type %s didn't accept a null value."
+ " If null values are expected, then configure a null representation for the binding.", + " If null values are expected, then configure a null representation for the binding.",
public void setAsRequiredEnabled(boolean asRequiredEnabled) { public void setAsRequiredEnabled(boolean asRequiredEnabled) {
if (!asRequiredSet) { if (!asRequiredSet) {
throw new IllegalStateException( throw new IllegalStateException(
"Unable to toggle asRequired validation since "
+ "asRequired has not been set.");
"Unable to toggle asRequired validation since "
+ "asRequired has not been set.");
} }
if (asRequiredEnabled != isAsRequiredEnabled()) { if (asRequiredEnabled != isAsRequiredEnabled()) {
field.setRequiredIndicatorVisible(asRequiredEnabled); field.setRequiredIndicatorVisible(asRequiredEnabled);
validate();
validate();
} }
} }


public boolean isAsRequiredEnabled() { public boolean isAsRequiredEnabled() {
return field.isRequiredIndicatorVisible(); return field.isRequiredIndicatorVisible();
} }

@Override
public void setValidatorsDisabled(boolean validatorsDisabled) {
this.validatorsDisabled = validatorsDisabled;
}

@Override
public boolean isValidatorsDisabled() {
return validatorsDisabled;
}
} }


/** /**


private Set<Binding<BEAN, ?>> changedBindings = new LinkedHashSet<>(); private Set<Binding<BEAN, ?>> changedBindings = new LinkedHashSet<>();


private boolean validatorsDisabled = false;

/** /**
* Creates a binder using a custom {@link PropertySet} implementation for * Creates a binder using a custom {@link PropertySet} implementation for
* finding and resolving property names for * finding and resolving property names for
* <p> * <p>
* After updating each field, the value is read back from the field and the * After updating each field, the value is read back from the field and the
* bean's property value is updated if it has been changed from the original * bean's property value is updated if it has been changed from the original
* value by the field or a converter.
* value by the field or a converter.
* *
* @see #readBean(Object) * @see #readBean(Object)
* @see #writeBean(Object) * @see #writeBean(Object)
// avoid NPE inside initFieldValue. It happens e.g. when // avoid NPE inside initFieldValue. It happens e.g. when
// we unbind a binding in valueChangeListener of another // we unbind a binding in valueChangeListener of another
// field. // field.
if (binding.getField() != null)
if (binding.getField() != null) {
binding.initFieldValue(bean, false); binding.initFieldValue(bean, false);
}
}); });
getValidationStatusHandler().statusChange( getValidationStatusHandler().statusChange(
BinderValidationStatus.createUnresolvedStatus(this)); BinderValidationStatus.createUnresolvedStatus(this));
* @param bean * @param bean
* the object to which to write the field values, not * the object to which to write the field values, not
* {@code null} * {@code null}
*
* @since 8.10
*/ */
public void writeBeanAsDraft(BEAN bean) { public void writeBeanAsDraft(BEAN bean) {
doWriteDraft(bean, new ArrayList<>(bindings));
doWriteDraft(bean, new ArrayList<>(bindings),false);
}

/**
* Writes successfully converted changes from the bound fields bypassing
* all the Validation, or all fields passing conversion if forced = true.
* If the conversion fails, the value written to the bean will be null.
*
* @see #writeBean(Object)
* @see #writeBeanIfValid(Object)
* @see #readBean(Object)
* @see #setBean(Object)
*
* @param bean
* the object to which to write the field values, not
* {@code null}
* @param forced
* disable all Validators during write
* @since 8.11
*/
public void writeBeanAsDraft(BEAN bean, boolean forced) {
doWriteDraft(bean, new ArrayList<>(bindings),forced);
} }


/** /**
* the bean to write field values into * the bean to write field values into
* @param bindings * @param bindings
* the set of bindings to write to the bean * the set of bindings to write to the bean
* @param forced
* disable validators during write if true
*/ */
@SuppressWarnings({ "unchecked" })
private void doWriteDraft(BEAN bean, Collection<Binding<BEAN, ?>> bindings) {
private void doWriteDraft(BEAN bean,
Collection<Binding<BEAN, ?>> bindings, boolean forced) {
Objects.requireNonNull(bean, "bean cannot be null"); Objects.requireNonNull(bean, "bean cannot be null");


bindings.forEach(binding -> ((BindingImpl<BEAN, ?, ?>) binding)
if (!forced) {
bindings.forEach(binding -> ((BindingImpl<BEAN, ?, ?>) binding)
.writeFieldValue(bean));
} else {
boolean isDisabled = isValidatorsDisabled();
setValidatorsDisabled(true);
bindings.forEach(binding -> ((BindingImpl<BEAN, ?, ?>) binding)
.writeFieldValue(bean)); .writeFieldValue(bean));
setValidatorsDisabled(isDisabled);
}
} }


/** /**
*/ */
public Binder<BEAN> withValidator(Validator<? super BEAN> validator) { public Binder<BEAN> withValidator(Validator<? super BEAN> validator) {
Objects.requireNonNull(validator, "validator cannot be null"); Objects.requireNonNull(validator, "validator cannot be null");
validators.add(validator);
Validator<? super BEAN> wrappedValidator = ((value, context) -> {
if (isValidatorsDisabled()) {
return ValidationResult.ok();
} else {
return validator.apply(value, context);
}
});
validators.add(wrappedValidator);
return this; return this;
} }


.ifPresent(Binding::unbind); .ifPresent(Binding::unbind);
} }


/**
* Control whether validators including bean level validators are
* disabled or enabled globally for this Binder.
*
* @param validatorsDisabled Boolean value
*
* @since 8.11
*/
public void setValidatorsDisabled(boolean validatorsDisabled) {
this.validatorsDisabled = validatorsDisabled;
}

/**
* Returns if the validators including bean level validators
* are disabled or enabled for this Binder.
*
* @return Boolean value
*
* @since 8.11
*/
public boolean isValidatorsDisabled() {
return validatorsDisabled;
}

private static final Logger getLogger() { private static final Logger getLogger() {
return Logger.getLogger(Binder.class.getName()); return Logger.getLogger(Binder.class.getName());
} }

+ 2
- 1
server/src/main/java/com/vaadin/data/RequiredFieldConfigurator.java View File

*/ */
public RequiredFieldConfigurator NOT_EMPTY = annotation -> annotation public RequiredFieldConfigurator NOT_EMPTY = annotation -> annotation
.annotationType().getName() .annotationType().getName()
.equals("org.hibernate.validator.constraints.NotEmpty");
.equals("org.hibernate.validator.constraints.NotEmpty") || annotation
.annotationType().getName().equals("javax.validation.constraints.NotEmpty");


/** /**
* Configurator which is aware of {@literal Size} annotation with * Configurator which is aware of {@literal Size} annotation with

+ 0
- 5
server/src/main/java/com/vaadin/data/ValueContext.java View File



import java.io.Serializable; import java.io.Serializable;
import java.util.Locale; import java.util.Locale;
import java.util.Objects;
import java.util.Optional; import java.util.Optional;


import com.vaadin.ui.Component; import com.vaadin.ui.Component;
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public ValueContext(Component component) { public ValueContext(Component component) {
Objects.requireNonNull(component,
"Component can't be null in ValueContext construction");
this.component = component; this.component = component;
if (component instanceof HasValue) { if (component instanceof HasValue) {
hasValue = (HasValue<?>) component; hasValue = (HasValue<?>) component;
* @since 8.1 * @since 8.1
*/ */
public ValueContext(Component component, HasValue<?> hasValue) { public ValueContext(Component component, HasValue<?> hasValue) {
Objects.requireNonNull(component,
"Component can't be null in ValueContext construction");
this.component = component; this.component = component;
this.hasValue = hasValue; this.hasValue = hasValue;
locale = findLocale(); locale = findLocale();

+ 2
- 2
server/src/main/java/com/vaadin/data/provider/HierarchicalDataCommunicator.java View File

} }


/** /**
* Returns parent index for the row or {@code null}.
* Returns parent index for the row or a negative value.
* *
* @param item * @param item
* the item to find the parent of * the item to find the parent of
* @return the parent index or {@code null} for top-level items
* @return the parent index or a negative value for top-level items
*/ */
public Integer getParentIndex(T item) { public Integer getParentIndex(T item) {
return mapper.getParentIndex(item); return mapper.getParentIndex(item);

+ 13
- 2
server/src/main/java/com/vaadin/data/provider/TreeDataProvider.java View File



private Stream<T> getFilteredStream(Stream<T> stream, private Stream<T> getFilteredStream(Stream<T> stream,
Optional<SerializablePredicate<T>> queryFilter) { Optional<SerializablePredicate<T>> queryFilter) {
final Optional<SerializablePredicate<T>> combinedFilter;
if (filter != null) { if (filter != null) {
stream = stream.filter(filter);
combinedFilter = Optional
.of(queryFilter.map(filter::and).orElse(filter));
} else {
combinedFilter = queryFilter;
} }
return queryFilter.map(stream::filter).orElse(stream);
return combinedFilter.map(
f -> stream.filter(element -> flatten(element).anyMatch(f)))
.orElse(stream);
}

private Stream<T> flatten(T element) {
return Stream.concat(Stream.of(element), getTreeData()
.getChildren(element).stream().flatMap(this::flatten));
} }
} }

+ 5
- 0
server/src/main/java/com/vaadin/navigator/Navigator.java View File

/** /**
* Returns the current navigation state reported by this Navigator's * Returns the current navigation state reported by this Navigator's
* {@link NavigationStateManager}. * {@link NavigationStateManager}.
* <p>
* When the navigation is triggered by the browser (for example by pressing
* the back or forward button in the browser), the navigation state may
* already have been updated to reflect the new address, before the
* {@link #navigateTo(String)} is notified.
* *
* @return The navigation state. * @return The navigation state.
*/ */

+ 2
- 4
server/src/main/java/com/vaadin/server/BootstrapHandler.java View File



Document document = response.getDocument(); Document document = response.getDocument();


DocumentType doctype = new DocumentType("html", "", "",
document.baseUri());
DocumentType doctype = new DocumentType("html", "", "");
document.child(0).before(doctype); document.child(0).before(doctype);


Element head = document.head(); Element head = document.head();
appendMainScriptTagContents(context, builder); appendMainScriptTagContents(context, builder);


builder.append("//]]>"); builder.append("//]]>");
mainScriptTag.appendChild(
new DataNode(builder.toString(), mainScriptTag.baseUri()));
mainScriptTag.appendChild(new DataNode(builder.toString()));
fragmentNodes.add(mainScriptTag); fragmentNodes.add(mainScriptTag);


} }

+ 14
- 0
server/src/main/java/com/vaadin/server/Sizeable.java View File

*/ */
public void setSizeFull(); public void setSizeFull();


/**
* Sets the width to 100%.
*
* @since 8.11
*/
public void setWidthFull();

/**
* Sets the height to 100%.
*
* @since 8.11
*/
public void setHeightFull();

/** /**
* Clears any size settings. * Clears any size settings.
*/ */

+ 18
- 6
server/src/main/java/com/vaadin/server/VaadinService.java View File

* @since 8.2 * @since 8.2
*/ */
protected VaadinService() { protected VaadinService() {
this.deploymentConfiguration = null;
deploymentConfiguration = null;
} }


/** /**
} }


/** /**
* Called at the end of a request, after sending the response. Closes
* inactive UIs in the given session, removes closed UIs from the session,
* and closes the session if it is itself inactive.
* Closes inactive UIs in the given session, removes closed UIs from the
* session, and closes the session if it is itself inactive. This operation
* should not be performed without first acquiring the session lock. By
* default called at the end of each request, after sending the response.
* *
* @param session * @param session
* the session to clean up
*
* @since 8.10
*/ */
void cleanupSession(VaadinSession session) {
public void cleanupSession(VaadinSession session) {
if (isSessionActive(session)) { if (isSessionActive(session)) {
closeInactiveUIs(session); closeInactiveUIs(session);
removeClosedUIs(session); removeClosedUIs(session);
* endless loop. This can at least happen if refreshing a * endless loop. This can at least happen if refreshing a
* resource when the session has expired. * resource when the session has expired.
*/ */
response.sendError(HttpServletResponse.SC_GONE,

// Ensure that the browser does not cache expired responses.
// iOS 6 Safari requires this (#3226)
response.setHeader("Cache-Control", "no-cache");
// If Content-Type is not set, browsers assume text/html and may
// complain about the empty response body (#4167)
response.setHeader("Content-Type", "text/plain");

response.sendError(HttpServletResponse.SC_FORBIDDEN,
"Session expired"); "Session expired");
} }
} catch (IOException e) { } catch (IOException e) {

+ 6
- 7
server/src/main/java/com/vaadin/server/communication/HeartbeatHandler.java View File

if (ui != null) { if (ui != null) {
ui.setLastHeartbeatTimestamp(System.currentTimeMillis()); ui.setLastHeartbeatTimestamp(System.currentTimeMillis());
// Ensure that the browser does not cache heartbeat responses. // Ensure that the browser does not cache heartbeat responses.
// iOS 6 Safari requires this (#10370)
// iOS 6 Safari requires this (#3226)
response.setHeader("Cache-Control", "no-cache"); response.setHeader("Cache-Control", "no-cache");
// If Content-Type is not set, browsers assume text/html and may // If Content-Type is not set, browsers assume text/html and may
// complain about the empty response body (#12182)
// complain about the empty response body (#4167)
response.setHeader("Content-Type", "text/plain"); response.setHeader("Content-Type", "text/plain");
} else { } else {
response.sendError(HttpServletResponse.SC_NOT_FOUND, response.sendError(HttpServletResponse.SC_NOT_FOUND,
if (!ServletPortletHelper.isHeartbeatRequest(request)) { if (!ServletPortletHelper.isHeartbeatRequest(request)) {
return false; return false;
} }

// Ensure that the browser does not cache expired response.
// iOS 6 Safari requires this (#10370)
// Ensure that the browser does not cache expired heartbeat responses.
// iOS 6 Safari requires this (#3226)
response.setHeader("Cache-Control", "no-cache"); response.setHeader("Cache-Control", "no-cache");
// If Content-Type is not set, browsers assume text/html and may // If Content-Type is not set, browsers assume text/html and may
// complain about the empty response body (#12182)
// complain about the empty response body (#4167)
response.setHeader("Content-Type", "text/plain"); response.setHeader("Content-Type", "text/plain");


response.sendError(HttpServletResponse.SC_NOT_FOUND, "Session expired");
response.sendError(HttpServletResponse.SC_FORBIDDEN, "Session expired");
return true; return true;
} }
} }

+ 6
- 9
server/src/main/java/com/vaadin/server/communication/PushHandler.java View File

* the atmosphere resource for the current request * the atmosphere resource for the current request
* @param callback * @param callback
* the push callback to call when a UI is found and locked * the push callback to call when a UI is found and locked
* @param websocket
* true if this is a websocket message (as opposed to a HTTP
* request)
*/ */
private void callWithUi(final AtmosphereResource resource, private void callWithUi(final AtmosphereResource resource,
final PushEventCallback callback, boolean websocket) {
final PushEventCallback callback) {
AtmosphereRequest req = resource.getRequest(); AtmosphereRequest req = resource.getRequest();
VaadinServletRequest vaadinRequest = new VaadinServletRequest(req, VaadinServletRequest vaadinRequest = new VaadinServletRequest(req,
service); service);
VaadinSession session = null; VaadinSession session = null;


if (websocket) {
boolean isWebsocket = resource.transport() == TRANSPORT.WEBSOCKET;
if (isWebsocket) {
// For any HTTP request we have already started the request in the // For any HTTP request we have already started the request in the
// servlet // servlet
service.requestStart(vaadinRequest, null); service.requestStart(vaadinRequest, null);
} }
} finally { } finally {
try { try {
if (websocket) {
if (isWebsocket) {
service.requestEnd(vaadinRequest, null, session); service.requestEnd(vaadinRequest, null, session);
} }
} catch (Exception e) { } catch (Exception e) {
* The related atmosphere resources * The related atmosphere resources
*/ */
void onConnect(AtmosphereResource resource) { void onConnect(AtmosphereResource resource) {
callWithUi(resource, establishCallback, false);
callWithUi(resource, establishCallback);
} }


/** /**
* The related atmosphere resources * The related atmosphere resources
*/ */
void onMessage(AtmosphereResource resource) { void onMessage(AtmosphereResource resource) {
callWithUi(resource, receiveCallback,
resource.transport() == TRANSPORT.WEBSOCKET);
callWithUi(resource, receiveCallback);
} }


/** /**

+ 20
- 2
server/src/main/java/com/vaadin/ui/AbstractComponent.java View File

import java.util.Set; import java.util.Set;
import java.util.StringTokenizer; import java.util.StringTokenizer;


import com.vaadin.annotations.Theme;
import com.vaadin.ui.themes.ValoTheme;
import org.jsoup.nodes.Attribute; import org.jsoup.nodes.Attribute;
import org.jsoup.nodes.Attributes; import org.jsoup.nodes.Attributes;
import org.jsoup.nodes.Element; import org.jsoup.nodes.Element;
setHeight(100, Unit.PERCENTAGE); setHeight(100, Unit.PERCENTAGE);
} }


/*
* (non-Javadoc)
*
* @see com.vaadin.server.Sizeable#setWidthFull()
*/
@Override
public void setWidthFull() {
setWidth(100, Unit.PERCENTAGE);
}

/*
* (non-Javadoc)
*
* @see com.vaadin.server.Sizeable#setHeightFull()
*/
@Override
public void setHeightFull() {
setHeight(100, Unit.PERCENTAGE);
}

/* /*
* (non-Javadoc) * (non-Javadoc)
* *

+ 119
- 21
server/src/main/java/com/vaadin/ui/AbstractDateField.java View File

} }
}; };


/**
* The default start year (inclusive) from which to calculate the
* daylight-saving time zone transition dates.
*/
private static final int DEFAULT_START_YEAR = 1980;

/**
* The default value of the number of future years from the current date for
* which the daylight-saving time zone transition dates are calculated.
*/
private static final int DEFAULT_YEARS_FROM_NOW = 20;

/**
* The optional user-supplied start year (inclusive) from which to calculate
* the daylight-saving time zone transition dates.
*/
private Integer startYear;

/**
* The optional user-supplied end year (inclusive) until which to calculate
* the daylight-saving time zone transition dates.
*/
private Integer endYear;

/** /**
* Value of the field. * Value of the field.
*/ */
* date (taking the resolution into account), the component will not * date (taking the resolution into account), the component will not
* validate. If {@code startDate} is set to {@code null}, any value before * validate. If {@code startDate} is set to {@code null}, any value before
* {@code endDate} will be accepted by the range * {@code endDate} will be accepted by the range
* <p>
* Note: Negative, i.e. BC dates are not supported.
* <p>
* Note: It's usually recommended to use only one of the following at the same
* time: Range validator with Binder or DateField's setRangeStart check.
* *
* @param startDate * @param startDate
* - the allowed range's start date * - the allowed range's start date
* date (taking the resolution into account), the component will not * date (taking the resolution into account), the component will not
* validate. If {@code endDate} is set to {@code null}, any value after * validate. If {@code endDate} is set to {@code null}, any value after
* {@code startDate} will be accepted by the range. * {@code startDate} will be accepted by the range.
* <p>
* Note: It's usually recommended to use only one of the following at the same
* time: Range validator with Binder or DateField's setRangeEnd check.
* *
* @param endDate * @param endDate
* the allowed range's end date (inclusive, based on the current * the allowed range's end date (inclusive, based on the current


/** /**
* Sets the {@link ZoneId}, which is used when {@code z} is included inside * Sets the {@link ZoneId}, which is used when {@code z} is included inside
* the {@link #setDateFormat(String)}.
* the {@link #setDateFormat(String)} .
* *
* @param zoneId * @param zoneId
* the zone id * the zone id
public void setZoneId(ZoneId zoneId) { public void setZoneId(ZoneId zoneId) {
if (zoneId != this.zoneId if (zoneId != this.zoneId
|| (zoneId != null && !zoneId.equals(this.zoneId))) { || (zoneId != null && !zoneId.equals(this.zoneId))) {
updateTimeZoneJSON(zoneId, getLocale());
updateTimeZoneJSON(zoneId, getLocale(), getStartYear(),
getEndYear());
} }
this.zoneId = zoneId; this.zoneId = zoneId;
} }


private void updateTimeZoneJSON(ZoneId zoneId, Locale locale) {
private void updateTimeZoneJSON(ZoneId zoneId, Locale locale, int startYear,
int endYear) {
String timeZoneJSON; String timeZoneJSON;
if (zoneId != null && locale != null) { if (zoneId != null && locale != null) {
timeZoneJSON = TimeZoneUtil.toJSON(zoneId, locale);
timeZoneJSON = TimeZoneUtil.toJSON(zoneId, locale, startYear,
endYear);
} else { } else {
timeZoneJSON = null; timeZoneJSON = null;
} }
getState().timeZoneJSON = timeZoneJSON; getState().timeZoneJSON = timeZoneJSON;
} }


/**
* Sets {@link startYear} and {@link endYear}: the start and end years (both
* inclusive) between which to calculate the daylight-saving time zone
* transition dates. Both parameters are used when '{@code z}' is included
* inside the {@link #setDateFormat(String)}, they would have no effect
* otherwise. Specifically, these parameters determine the range of years in
* which zone names are are adjusted to show the daylight saving names.
*
* If no values are provided, by default {@link startYear} is set to
* {@value #DEFAULT_START_YEAR}, and {@link endYear} is set to
* {@value #DEFAULT_YEARS_FROM_NOW} years into the future from the current
* date.
*
* @param startYear
* the start year of DST transitions
* @param endYear
* the end year of DST transitions
* @since 8.11
*/
public void setDaylightSavingTimeRange(int startYear, int endYear) {
if (startYear > endYear) {
throw new IllegalArgumentException(
"The start year from which to begin calculating the "
+ "daylight-saving time zone transition dates must"
+ " be less than or equal to the end year.\n"
+ startYear + " is greater than " + endYear);
}
if (this.startYear == null || this.endYear == null
|| startYear != this.startYear || endYear != this.endYear) {
updateTimeZoneJSON(getZoneId(), getLocale(), startYear, endYear);
}
this.startYear = startYear;
this.endYear = endYear;
}

private int getStartYear() {
if (startYear == null) {
return DEFAULT_START_YEAR;
} else {
return startYear;
}
}

private int getEndYear() {
if (endYear == null) {
return LocalDate.now().getYear() + DEFAULT_YEARS_FROM_NOW;
} else {
return endYear;
}
}

@Override @Override
public void setLocale(Locale locale) { public void setLocale(Locale locale) {
Locale oldLocale = getLocale(); Locale oldLocale = getLocale();
if (locale != oldLocale if (locale != oldLocale
|| (locale != null && !locale.equals(oldLocale))) { || (locale != null && !locale.equals(oldLocale))) {
updateTimeZoneJSON(getZoneId(), locale);
updateTimeZoneJSON(getZoneId(), locale, getStartYear(),
getEndYear());
} }
super.setLocale(locale); super.setLocale(locale);
} }
* *
* @param value * @param value
* the new value, may be {@code null} * the new value, may be {@code null}
* @throws IllegalArgumentException
* if the value is not within range bounds
*/ */
@Override @Override
public void setValue(T value) { public void setValue(T value) {
currentErrorMessage = null;
/*
* First handle special case when the client side component have a date
* string but value is null (e.g. unparsable date string typed in by the
* user). No value changes should happen, but we need to do some
* internal housekeeping.
*/
if (value == null && !getState(false).parsable) {
RangeValidator<T> validator = getRangeValidator();
ValidationResult result = validator.apply(value,
new ValueContext(this, this));

if (result.isError()) {
throw new IllegalArgumentException(
"value is not within acceptable range");
} else {
currentErrorMessage = null;
/* /*
* Side-effects of doSetValue clears possible previous strings and
* flags about invalid input.
* First handle special case when the client side component has a date
* string but value is null (e.g. unparsable date string typed in by the
* user). No value changes should happen, but we need to do some
* internal housekeeping.
*/ */
doSetValue(null);

markAsDirty();
return;
if (value == null && !getState(false).parsable) {
/*
* Side-effects of doSetValue clears possible previous strings and
* flags about invalid input.
*/
doSetValue(null);

markAsDirty();
return;
}
super.setValue(value);
} }
super.setValue(value);
} }


/** /**
@Override @Override
protected void doSetValue(T value) { protected void doSetValue(T value) {


this.value = value;
// Also set the internal dateString // Also set the internal dateString
this.value = value;
if (value == null) { if (value == null) {
value = getEmptyValue(); value = getEmptyValue();
} }

+ 6
- 4
server/src/main/java/com/vaadin/ui/ConnectorTracker.java View File

} }
Map<String, StreamVariable> nameToStreamVar = pidToNameToStreamVariable Map<String, StreamVariable> nameToStreamVar = pidToNameToStreamVariable
.get(connectorId); .get(connectorId);
StreamVariable streamVar = nameToStreamVar.remove(variableName);
streamVariableToSeckey.remove(streamVar);
if (nameToStreamVar.isEmpty()) {
pidToNameToStreamVariable.remove(connectorId);
if (nameToStreamVar != null) {
StreamVariable streamVar = nameToStreamVar.remove(variableName);
streamVariableToSeckey.remove(streamVar);
if (nameToStreamVar.isEmpty()) {
pidToNameToStreamVariable.remove(connectorId);
}
} }
} }



+ 9
- 3
server/src/main/java/com/vaadin/ui/Grid.java View File

* @param editable * @param editable
* {@code true} if column is editable; {@code false} if not * {@code true} if column is editable; {@code false} if not
* @return this column * @return this column
* @throws IllegalStateException
* if editable is true and column has no editor binding or
* component defined
* *
* @see #setEditorComponent(HasValue, Setter) * @see #setEditorComponent(HasValue, Setter)
* @see #setEditorBinding(Binding) * @see #setEditorBinding(Binding)
*/ */
public Column<T, V> setEditable(boolean editable) {
Objects.requireNonNull(editorBinding,
"Column has no editor binding or component defined");
public Column<T, V> setEditable(boolean editable)
throws IllegalStateException {
if (editable && editorBinding == null) {
throw new IllegalStateException(
"Column has no editor binding or component defined");
}
getState().editable = editable; getState().editable = editable;
return this; return this;
} }

+ 12
- 0
server/src/main/java/com/vaadin/ui/Tree.java View File

public Registration addSelectionListener(SelectionListener<T> listener) { public Registration addSelectionListener(SelectionListener<T> listener) {
return treeGrid.addSelectionListener(listener); return treeGrid.addSelectionListener(listener);
} }
/**
* Use this tree as a multi select in {@link Binder}. Throws
* {@link IllegalStateException} if the tree is not using
* {@link SelectionMode#MULTI}.
*
* @return the multi select wrapper that can be used in binder
* @since 8.11
*/
public MultiSelect<T> asMultiSelect() {
return treeGrid.asMultiSelect();
}


/** /**
* Use this tree as a single select in {@link Binder}. Throws * Use this tree as a single select in {@link Binder}. Throws

+ 5
- 4
server/src/main/java/com/vaadin/ui/UI.java View File

* accessSynchronously. Furthermore, there wasn't an * accessSynchronously. Furthermore, there wasn't an
* ErrorHandlingRunnable that handled the exception. * ErrorHandlingRunnable that handled the exception.
*/ */
getLogger().log(Level.WARNING,
"access() task ignored because UI got detached after the task was enqueued."
+ " To suppress this message, change the task to implement {} and make it handle {}."
+ " Affected task: {}",
getLogger().log(Level.WARNING, "access() task ignored "
+ "because UI got detached after the task was "
+ "enqueued. To suppress this message, change "
+ "the task to implement {0} and make it handle "
+ "{1}. Affected task: {2}",
new Object[] { new Object[] {
ErrorHandlingRunnable.class.getName(), ErrorHandlingRunnable.class.getName(),
UIDetachedException.class.getName(), UIDetachedException.class.getName(),

+ 60
- 35
server/src/main/java/com/vaadin/ui/Upload.java View File



private int totalBytes; private int totalBytes;


private String buttonCaption = "Upload";

private String buttonStyleName;

/** /**
* ProgressListeners to which information about progress is sent during * ProgressListeners to which information about progress is sent during
* upload * upload


target.addAttribute("state", isUploading); target.addAttribute("state", isUploading);


if (buttonCaption != null) {
target.addAttribute("buttoncaption", buttonCaption);
if (buttonStyleName != null) {
target.addAttribute("buttonstylename", buttonStyleName);
}
}

target.addAttribute("nextid", nextid); target.addAttribute("nextid", nextid);


// Post file to this stream variable // Post file to this stream variable
* @return String to be rendered into button that fires uploading * @return String to be rendered into button that fires uploading
*/ */
public String getButtonCaption() { public String getButtonCaption() {
return buttonCaption;
return getState(false).buttonCaption;
} }


/** /**
* Returns the stylename rendered into button that fires uploading.
* Returns the style name rendered into button that fires uploading.
* *
* @return Stylename to be rendered into button that fires uploading
* @return Style name to be rendered into button that fires uploading
* @since 8.2 * @since 8.2
*/ */
public String getButtonStyleName() { public String getButtonStyleName() {
return buttonStyleName;
return getState(false).buttonStyleName;
}

/**
* Checks whether the caption of the button that fires uploading is rendered
* as HTML
* <p>
* The default is {@code false}, i.e. to render that caption as plain text.
*
* @return {@code true} if the caption is rendered as HTML, {@code false} if
* rendered as plain text
* @since 8.11
*/
public boolean isButtonCaptionAsHtml() {
return getState(false).buttonCaptionAsHtml;
} }


/** /**
* {@link #submitUpload()}. * {@link #submitUpload()}.
* <p> * <p>
* In case the Upload is used in immediate mode using * In case the Upload is used in immediate mode using
* {@link #setImmediateMode(boolean)}, the file choose (html input with type
* "file") is hidden and only the button with this text is shown.
* {@link #setImmediateMode(boolean)}, the file chooser (HTML input with
* type "file") is hidden and only the button with this text is shown.
* <p> * <p>
* *
* <p> * <p>
* text for upload components button. * text for upload components button.
*/ */
public void setButtonCaption(String buttonCaption) { public void setButtonCaption(String buttonCaption) {
this.buttonCaption = buttonCaption;
markAsDirty();
getState().buttonCaption = buttonCaption;
} }


/** /**
* In addition to the actual file chooser, upload components have button * In addition to the actual file chooser, upload components have button
* that starts actual upload progress. This method is used to set a
* stylename to that button.
* that starts actual upload progress. This method is used to set a style
* name to that button.
* <p>
* Note: Unlike {@code Button.setStyleName()} this method overrides all the
* styles from the button. If you wish to preserve the default styles, enter
* the style name as {@code "v-button yourStyleName"}.
* *
* @param buttonStyleName * @param buttonStyleName
* styleName for upload components button.
* style name for upload components button.
* @see #setButtonCaption(String) about when the button is shown / hidden. * @see #setButtonCaption(String) about when the button is shown / hidden.
* @since 8.2 * @since 8.2
*/ */
public void setButtonStyleName(String buttonStyleName) { public void setButtonStyleName(String buttonStyleName) {
this.buttonStyleName = buttonStyleName;
markAsDirty();
getState().buttonStyleName = buttonStyleName;
}

/**
* In addition to the actual file chooser, upload components have button
* that starts actual upload progress. This method is used to set whether
* the caption on that button is rendered as HTML.
* <p>
* If set to {@code true}, the caption is rendered in the browser as HTML
* and the developer is responsible for ensuring no harmful HTML is used. If
* set to {@code false}, the caption is rendered in the browser as plain
* text.
* <p>
* The default is {@code false}, i.e. to render the caption as plain text.
*
* @param buttonCaptionAsHtml
* {@code true} if the caption is rendered as HTML, {@code false}
* if rendered as plain text
* @since 8.11
*/
public void setButtonCaptionAsHtml(boolean buttonCaptionAsHtml) {
getState().buttonCaptionAsHtml = buttonCaptionAsHtml;
} }


/** /**
* Forces the upload the send selected file to the server.
* Instructs the upload component to send selected file to the server.
* <p> * <p>
* In case developer wants to use this feature, he/she will most probably * In case developer wants to use this feature, he/she will most probably
* want to hide the uploads internal submit button by setting its caption to
* null with {@link #setButtonCaption(String)} method.
* want to hide the upload component's internal submit button by setting its
* caption to null with {@link #setButtonCaption(String)} method.
* <p> * <p>
* Note, that the upload runs asynchronous. Developer should use normal
* upload listeners to trac the process of upload. If the field is empty
* uploaded the file name will be empty string and file length 0 in the
* upload finished event.
* Note that the upload runs asynchronously. Developer should use normal
* upload listeners to track the process of upload. If the file name field
* is empty, no upload will be triggered.
* <p> * <p>
* Also note, that the developer should not remove or modify the upload in
* the same user transaction where the upload submit is requested. The
* upload may safely be hidden or removed once the upload started event is
* fired.
* Also note that the developer should not remove or modify the upload
* component in the same user transaction where the upload submit is
* requested. The upload component can be safely hidden or removed once the
* upload started event has been fired.
*/ */
public void submitUpload() { public void submitUpload() {
markAsDirty(); markAsDirty();

+ 5
- 1
server/src/main/java/com/vaadin/ui/components/grid/GridRowDragger.java View File

} }


sourceItems.removeAll(droppedItems); sourceItems.removeAll(droppedItems);
listDataProvider.refreshAll();

// if reordering the same grid, DataProvider's refresh will be done later
if (getGridDragSource().getGrid() != getGridDropTarget().getGrid()) {
listDataProvider.refreshAll();
}
} }


private void handleTargetGridDrop(GridDropEvent<T> event, final int index, private void handleTargetGridDrop(GridDropEvent<T> event, final int index,

+ 2
- 2
server/src/main/java/com/vaadin/ui/declarative/Design.java View File

throw new DesignException("Unknown tag: " + tagName); throw new DesignException("Unknown tag: " + tagName);
} }
String[] classNameParts = parts[1].split("-"); String[] classNameParts = parts[1].split("-");
String className = "";
StringBuilder className = new StringBuilder();
for (String classNamePart : classNameParts) { for (String classNamePart : classNameParts) {
// Split will ignore trailing and multiple dashes but that // Split will ignore trailing and multiple dashes but that
// should be // should be
// ok // ok
// <vaadin-button--> will be resolved to <vaadin-button> // <vaadin-button--> will be resolved to <vaadin-button>
// <vaadin--button> will be resolved to <vaadin-button> // <vaadin--button> will be resolved to <vaadin-button>
className += SharedUtil.capitalize(classNamePart);
className.append(SharedUtil.capitalize(classNamePart));
} }
String qualifiedClassName = packageName + "." + className; String qualifiedClassName = packageName + "." + className;



+ 5
- 0
server/src/main/java/com/vaadin/ui/themes/ValoTheme.java View File

/** /**
* Move the default caption icon inside the text field. Can be combined with * Move the default caption icon inside the text field. Can be combined with
* any other TextField style. * any other TextField style.
* <p>
* Note: Does not apply in FormLayout
* <p>
* Note: TextFields with inlined icons in Grid header need to be wrapped into
* e.g. HorizontalLayout
*/ */
public static final String TEXTFIELD_INLINE_ICON = "inline-icon"; public static final String TEXTFIELD_INLINE_ICON = "inline-icon";



+ 37
- 5
server/src/main/java/com/vaadin/util/TimeZoneUtil.java View File

public final class TimeZoneUtil implements Serializable { public final class TimeZoneUtil implements Serializable {


/** /**
* The start year used to send the time zone transition dates.
* The default start year (inclusive) from which to calculate the
* daylight-saving time zone transition dates.
*/ */
private static final int STARTING_YEAR = 1980; private static final int STARTING_YEAR = 1980;


/** /**
* Till how many years from now, should we send the time zone transition
* dates.
* The default value of the number of future years from the current date for
* which the daylight-saving time zone transition dates are calculated.
*/ */
private static final int YEARS_FROM_NOW = 20; private static final int YEARS_FROM_NOW = 20;


* which is used in * which is used in
* {@link com.google.gwt.i18n.client.TimeZone#createTimeZone(String)}. * {@link com.google.gwt.i18n.client.TimeZone#createTimeZone(String)}.
* *
* This method calculates the JSON string from the year
* {@value #STARTING_YEAR} until {@value #YEARS_FROM_NOW} years into the
* future from the current date.
*
* @see #toJSON(ZoneId, Locale, int, int)
*
* @param zoneId * @param zoneId
* the {@link ZoneId} to get the daylight transitions from * the {@link ZoneId} to get the daylight transitions from
* @param locale * @param locale
* @return the encoded string * @return the encoded string
*/ */
public static String toJSON(ZoneId zoneId, Locale locale) { public static String toJSON(ZoneId zoneId, Locale locale) {
int endYear = LocalDate.now().getYear() + YEARS_FROM_NOW;
return toJSON(zoneId, locale, STARTING_YEAR, endYear);
}

/**
* Returns a JSON string of the specified {@code zoneId} and {@link Locale},
* which is used in
* {@link com.google.gwt.i18n.client.TimeZone#createTimeZone(String)}.
*
* This method calculates the JSON string from {@code startYear} until
* {@code startYear}, both inclusive.
*
* @param zoneId
* the {@link ZoneId} to get the daylight transitions from
* @param locale
* the locale used to determine the short name of the time zone
* @param startYear
* the start year of DST transitions
* @param endYear
* the end year of DST transitions
*
* @return the encoded string
* @since 8.11
*/
public static String toJSON(ZoneId zoneId, Locale locale, int startYear,
int endYear) {
if (zoneId == null || locale == null) { if (zoneId == null || locale == null) {
return null; return null;
} }


TimeZoneInfo info = new TimeZoneInfo(); TimeZoneInfo info = new TimeZoneInfo();


int endYear = LocalDate.now().getYear() + YEARS_FROM_NOW;
if (timeZone.useDaylightTime()) { if (timeZone.useDaylightTime()) {
for (int year = STARTING_YEAR; year <= endYear; year++) {
for (int year = startYear; year <= endYear; year++) {
ZonedDateTime i = LocalDateTime.of(year, 1, 1, 0, 0) ZonedDateTime i = LocalDateTime.of(year, 1, 1, 0, 0)
.atZone(zoneId); .atZone(zoneId);
while (true) { while (true) {

+ 77
- 2
server/src/test/java/com/vaadin/data/BinderTest.java View File

Binder<Person> binder = new Binder<>(); Binder<Person> binder = new Binder<>();
binder.forField(nameField) binder.forField(nameField)
.withValidator((value,context) -> { .withValidator((value,context) -> {
if (value.equals("Mike")) return ValidationResult.ok();
else return ValidationResult.error("value must be Mike");
if (value.equals("Mike")) {
return ValidationResult.ok();
} else {
return ValidationResult.error("value must be Mike");
}
}) })
.bind(Person::getFirstName, Person::setFirstName); .bind(Person::getFirstName, Person::setFirstName);
binder.forField(ageField) binder.forField(ageField)
// age is written to draft even if firstname validation // age is written to draft even if firstname validation
// fails // fails
assertEquals(age, person.getAge()); assertEquals(age, person.getAge());

binder.writeBeanAsDraft(person,true);
// name is now written despite validation as write was forced
assertEquals(fieldValue, person.getFirstName());
}

@Test
public void save_bound_bean_disable_validation_binding() throws ValidationException {
Binder<Person> binder = new Binder<>();
Binding<Person, String> nameBinding = binder.forField(nameField)
.withValidator((value,context) -> {
if (value.equals("Mike")) {
return ValidationResult.ok();
} else {
return ValidationResult.error("value must be Mike");
}
})
.bind(Person::getFirstName, Person::setFirstName);
binder.forField(ageField)
.withConverter(new StringToIntegerConverter(""))
.bind(Person::getAge, Person::setAge);

Person person = new Person();

String fieldValue = "John";
nameField.setValue(fieldValue);

int age = 10;
ageField.setValue("10");

person.setFirstName("Mark");

nameBinding.setValidatorsDisabled(true);
binder.writeBean(person);

// name is now written as validation was disabled
assertEquals(fieldValue, person.getFirstName());
assertEquals(age, person.getAge());
}

@Test
public void save_bound_bean_disable_validation_binder() throws ValidationException {
Binder<Person> binder = new Binder<>();
binder.forField(nameField)
.withValidator((value,context) -> {
if (value.equals("Mike")) {
return ValidationResult.ok();
} else {
return ValidationResult.error("value must be Mike");
}
})
.bind(Person::getFirstName, Person::setFirstName);
binder.forField(ageField)
.withConverter(new StringToIntegerConverter(""))
.bind(Person::getAge, Person::setAge);

Person person = new Person();

String fieldValue = "John";
nameField.setValue(fieldValue);

int age = 10;
ageField.setValue("10");

person.setFirstName("Mark");

binder.setValidatorsDisabled(true);
binder.writeBean(person);

// name is now written as validation was disabled
assertEquals(fieldValue, person.getFirstName());
assertEquals(age, person.getAge());
} }


@Test @Test

+ 49
- 4
server/src/test/java/com/vaadin/data/provider/TreeDataProviderTest.java View File

assertEquals(stringData.getChildren("a/b"), Arrays.asList()); assertEquals(stringData.getChildren("a/b"), Arrays.asList());
} }


@Test
public void filter_is_applied_to_children_provider_filter() {
final SerializablePredicate<String> dataProviderFilter = item -> item
.contains("Sub");
final HierarchicalQuery<String, SerializablePredicate<String>> query = new HierarchicalQuery<>(
null, null);
filter_is_applied_to_children(dataProviderFilter, query);
}

@Test
public void filter_is_applied_to_children_query_filter() {
final SerializablePredicate<String> dataProviderFilter = null;
final HierarchicalQuery<String, SerializablePredicate<String>> query = new HierarchicalQuery<>(
item -> item.contains("Sub"), null);
filter_is_applied_to_children(dataProviderFilter, query);
}

@Test
public void filter_is_applied_to_children_both_filters() {
final SerializablePredicate<String> dataProviderFilter = item -> item
.contains("Sub");
final HierarchicalQuery<String, SerializablePredicate<String>> query = new HierarchicalQuery<>(
dataProviderFilter, null);
filter_is_applied_to_children(dataProviderFilter, query);
}

private void filter_is_applied_to_children(
final SerializablePredicate<String> dataProviderFilter,
final HierarchicalQuery<String, SerializablePredicate<String>> query) {
final TreeData<String> stringData = new TreeData<>();
final String root1 = "Main";
final List<String> children1 = Arrays.asList("Sub1", "Sub2");
final String root2 = "Other";
final List<String> children2 = Arrays.asList("Foo1", "Foo2");
stringData.addRootItems(root1, root2);
stringData.addItems(root1, children1);
stringData.addItems(root2, children2);
final TreeDataProvider<String> provider = new TreeDataProvider<>(
stringData);
provider.setFilter(dataProviderFilter);
assertEquals("Unexpected amount of root items after filtering.", 1,
provider.getChildCount(query));
assertTrue(provider.fetchChildren(query).allMatch(root1::equals));
}

@Test @Test
public void setFilter() { public void setFilter() {
getDataProvider().setFilter(item -> item.getValue().equals("Xyz") getDataProvider().setFilter(item -> item.getValue().equals("Xyz")
&& !item.getValue().equals("Xyz")); && !item.getValue().equals("Xyz"));


assertEquals( assertEquals(
"Previous filter should be replaced when setting a new one", 6,
"Previous filter should be replaced when setting a new one", 14,
sizeWithUnfilteredQuery()); sizeWithUnfilteredQuery());


getDataProvider().setFilter(null); getDataProvider().setFilter(null);
public void addFilter() { public void addFilter() {
getDataProvider().addFilter(item -> item.getId() <= 10); getDataProvider().addFilter(item -> item.getId() <= 10);
getDataProvider().addFilter(item -> item.getId() >= 5); getDataProvider().addFilter(item -> item.getId() >= 5);
assertEquals(5, sizeWithUnfilteredQuery());
assertEquals(8, sizeWithUnfilteredQuery());
} }


@Override @Override
.size(new HierarchicalQuery<>("Xyz", null))); .size(new HierarchicalQuery<>("Xyz", null)));
assertEquals("No item should match 'Zyx'", 0, strFilterDataProvider assertEquals("No item should match 'Zyx'", 0, strFilterDataProvider
.size(new HierarchicalQuery<>("Zyx", null))); .size(new HierarchicalQuery<>("Zyx", null)));
assertEquals("Unexpected number of matches for 'Foo'", 3,
assertEquals("Unexpected number of matches for 'Foo'", 4,
strFilterDataProvider strFilterDataProvider
.size(new HierarchicalQuery<>("Foo", null))); .size(new HierarchicalQuery<>("Foo", null)));
assertEquals("No items should've been filtered out", rootData.size(), assertEquals("No items should've been filtered out", rootData.size(),
assertEquals("No item should match 'Zyx'", 0, assertEquals("No item should match 'Zyx'", 0,
dataProvider.size(new HierarchicalQuery<>( dataProvider.size(new HierarchicalQuery<>(
strBean -> strBean.getValue().contains("Zyx"), null))); strBean -> strBean.getValue().contains("Zyx"), null)));
assertEquals("Unexpected number of matches for 'Foo'", 3,
assertEquals("Unexpected number of matches for 'Foo'", 4,
getDataProvider() getDataProvider()
.size(new HierarchicalQuery<>(fooFilter, null))); .size(new HierarchicalQuery<>(fooFilter, null)));
} }

+ 7
- 5
server/src/test/java/com/vaadin/data/provider/hierarchical/HierarchyMapperWithDataTest.java View File

import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream; import java.util.stream.Stream;


import org.junit.Before; import org.junit.Before;
expand(expandedNode); expand(expandedNode);


SerializablePredicate<Node> filter = n -> n.getNumber() % 2 == 0; SerializablePredicate<Node> filter = n -> n.getNumber() % 2 == 0;
List<Node> expectedResult = testData.stream().filter(filter)
.filter(n -> roots.contains(n)
|| n.getParent().equals(testData.get(0))
|| n.getParent().equals(expandedNode))
.collect(Collectors.toList());

// Root nodes plus children of expanded nodes 0 and 4 that match the
// filter
List<Node> expectedResult = IntStream
.of(0, 1, 4, 6, 7, 10, 13, 26, 39, 52).mapToObj(testData::get)
.collect(Collectors.toList());


mapper.setFilter(filter); mapper.setFilter(filter);



+ 20
- 0
server/src/test/java/com/vaadin/tests/server/component/grid/GridRowDraggerOneGridTest.java View File

verifyDataProvider("1", "2", "0"); verifyDataProvider("1", "2", "0");
} }


@Test
public void listDataProvider_calledOnlyOnce() {

final int[] times = new int[1];

source.setItems("0", "1", "2");

source.getDataProvider().addDataProviderListener(ev -> times[0]++);

dragger.setDropIndexCalculator(event -> {
return Integer.MAX_VALUE;
});

drop("1", DropLocation.ABOVE, "0");

verifyDataProvider("1", "2", "0");

Assert.assertArrayEquals("DataProvider should be invoked only once", new int[] { 1 }, times);
}

@Test @Test
public void noopSourceUpdater() { public void noopSourceUpdater() {
source.setItems("0", "1", "2"); source.setItems("0", "1", "2");

+ 11
- 2
server/src/test/java/com/vaadin/ui/DateFieldTestCase.java View File



import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.Rule;
import org.junit.rules.ExpectedException;


public class DateFieldTestCase { public class DateFieldTestCase {


private AbstractLocalDateField dateField; private AbstractLocalDateField dateField;
private LocalDate date; private LocalDate date;


@Rule
public transient ExpectedException exceptionRule = ExpectedException.none();

@Before @Before
public void setup() { public void setup() {
dateField = new AbstractLocalDateField() { dateField = new AbstractLocalDateField() {


@Test @Test
public void belowRangeStartIsNotAcceptedAsValue() { public void belowRangeStartIsNotAcceptedAsValue() {
LocalDate currentDate = dateField.getValue();
dateField.setRangeStart(date); dateField.setRangeStart(date);
exceptionRule.expect(IllegalArgumentException.class);
dateField.setValue(date.minusDays(1)); dateField.setValue(date.minusDays(1));
assertNotNull(dateField.getComponentError());
assertThat(dateField.getValue(), is(currentDate));
} }


@Test @Test


@Test @Test
public void aboveRangeEndIsNotAcceptedAsValue() { public void aboveRangeEndIsNotAcceptedAsValue() {
LocalDate currentDate = dateField.getValue();
dateField.setRangeEnd(date); dateField.setRangeEnd(date);
exceptionRule.expect(IllegalArgumentException.class);
dateField.setValue(date.plusDays(1)); dateField.setValue(date.plusDays(1));
assertNotNull(dateField.getComponentError());
assertThat(dateField.getValue(), is(currentDate));
} }
} }

+ 1
- 1
shared/pom.xml View File

<parent> <parent>
<groupId>com.vaadin</groupId> <groupId>com.vaadin</groupId>
<artifactId>vaadin-root</artifactId> <artifactId>vaadin-root</artifactId>
<version>8.10-SNAPSHOT</version>
<version>8.12-SNAPSHOT</version>
</parent> </parent>
<artifactId>vaadin-shared</artifactId> <artifactId>vaadin-shared</artifactId>
<name>vaadin-shared</name> <name>vaadin-shared</name>

+ 44
- 4
shared/src/main/java/com/vaadin/shared/ui/dnd/criteria/Criterion.java View File

*/ */
private Criterion(String key, ComparisonOperator operator, String value, private Criterion(String key, ComparisonOperator operator, String value,
Payload.ValueType valueType) { Payload.ValueType valueType) {
this.key = key;
this.value = value;
this.valueType = valueType;
this.operator = operator;
setKey(key);
setValue(value);
setValueType(valueType);
setOperator(operator);
} }


/** /**
return key; return key;
} }


/**
* Sets the key of the payload to be compared.
*
* @param key
* key of the payload to be compared
*/
public void setKey(String key) {
this.key = key;
}

/** /**
* Gets the value of the payload to be compared. * Gets the value of the payload to be compared.
* *
return value; return value;
} }


/**
* Sets the value of the payload to be compared.
*
* @param value
* value of the payload to be compared
*/
public void setValue(String value) {
this.value = value;
}

/** /**
* Gets the type of the payload value to be compared. * Gets the type of the payload value to be compared.
* *
return valueType; return valueType;
} }


/**
* Sets the type of the payload value to be compared.
*
* @param valueType
* type of the payload to be compared
*/
public void setValueType(Payload.ValueType valueType) {
this.valueType = valueType;
}

/** /**
* Gets the comparison operator. * Gets the comparison operator.
* *
return operator; return operator;
} }


/**
* Sets the comparison operator.
*
* @param operator
* comparison operator
*/
public void setOperator(ComparisonOperator operator) {
this.operator = operator;
}

/** /**
* Compares this criterion's value to the given payload's value and returns * Compares this criterion's value to the given payload's value and returns
* whether the result matches the criterion's operator. The comparison is * whether the result matches the criterion's operator. The comparison is

+ 4
- 1
shared/src/main/java/com/vaadin/shared/ui/upload/UploadClientRpc.java View File



import com.vaadin.shared.communication.ClientRpc; import com.vaadin.shared.communication.ClientRpc;


/**
* Server-to-client RPC interface for Upload.
*/
public interface UploadClientRpc extends ClientRpc { public interface UploadClientRpc extends ClientRpc {


/** /**
* Forces the upload the send selected file to the server.
* Instructs the upload component to send the selected file to the server.
*/ */
void submitUpload(); void submitUpload();
} }

+ 3
- 0
shared/src/main/java/com/vaadin/shared/ui/upload/UploadServerRpc.java View File



import com.vaadin.shared.communication.ServerRpc; import com.vaadin.shared.communication.ServerRpc;


/**
* Client-to-server RPC interface for Upload.
*/
public interface UploadServerRpc extends ServerRpc { public interface UploadServerRpc extends ServerRpc {


/** /**

+ 13
- 0
shared/src/main/java/com/vaadin/shared/ui/upload/UploadState.java View File

primaryStyleName = "v-upload"; primaryStyleName = "v-upload";
} }


/** Upload component's list of accepted content-types. */
@DelegateToWidget @DelegateToWidget
@NoLayout @NoLayout
public String acceptMimeTypes; public String acceptMimeTypes;

/** Caption of the button that fires uploading. */
public String buttonCaption = "Upload";

/** Style name of the button that fires uploading. */
public String buttonStyleName = "v-button";

/**
* Should the caption of the button that fires uploading be rendered as
* HTML.
*/
public boolean buttonCaptionAsHtml;
} }

+ 1
- 1
test/addon-using-init-param-widget-set/pom.xml View File

<parent> <parent>
<groupId>com.vaadin</groupId> <groupId>com.vaadin</groupId>
<artifactId>vaadin-test</artifactId> <artifactId>vaadin-test</artifactId>
<version>8.10-SNAPSHOT</version>
<version>8.12-SNAPSHOT</version>
</parent> </parent>
<artifactId>vaadin-test-addon-using-init-param-widget-set</artifactId> <artifactId>vaadin-test-addon-using-init-param-widget-set</artifactId>
<packaging>war</packaging> <packaging>war</packaging>

+ 1
- 1
test/addon-using-no-defined-widget-set/pom.xml View File

<parent> <parent>
<groupId>com.vaadin</groupId> <groupId>com.vaadin</groupId>
<artifactId>vaadin-test</artifactId> <artifactId>vaadin-test</artifactId>
<version>8.10-SNAPSHOT</version>
<version>8.12-SNAPSHOT</version>
</parent> </parent>
<artifactId>vaadin-test-addon-using-no-defined-widget-set</artifactId> <artifactId>vaadin-test-addon-using-no-defined-widget-set</artifactId>
<packaging>war</packaging> <packaging>war</packaging>

+ 1
- 1
test/addon-using-own-widget-set/pom.xml View File

<parent> <parent>
<groupId>com.vaadin</groupId> <groupId>com.vaadin</groupId>
<artifactId>vaadin-test</artifactId> <artifactId>vaadin-test</artifactId>
<version>8.10-SNAPSHOT</version>
<version>8.12-SNAPSHOT</version>
</parent> </parent>
<artifactId>vaadin-test-addon-using-own-widget-set</artifactId> <artifactId>vaadin-test-addon-using-own-widget-set</artifactId>
<packaging>war</packaging> <packaging>war</packaging>

+ 1
- 1
test/bean-api-validation/pom.xml View File

<parent> <parent>
<groupId>com.vaadin</groupId> <groupId>com.vaadin</groupId>
<artifactId>vaadin-test</artifactId> <artifactId>vaadin-test</artifactId>
<version>8.10-SNAPSHOT</version>
<version>8.12-SNAPSHOT</version>
</parent> </parent>
<artifactId>vaadin-test-bean-api-validation</artifactId> <artifactId>vaadin-test-bean-api-validation</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>

+ 1
- 1
test/bean-impl-validation/pom.xml View File

<parent> <parent>
<groupId>com.vaadin</groupId> <groupId>com.vaadin</groupId>
<artifactId>vaadin-test</artifactId> <artifactId>vaadin-test</artifactId>
<version>8.10-SNAPSHOT</version>
<version>8.12-SNAPSHOT</version>
</parent> </parent>
<artifactId>vaadin-test-bean-impl-validation</artifactId> <artifactId>vaadin-test-bean-impl-validation</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>

+ 1
- 1
test/cdi/pom.xml View File

<parent> <parent>
<groupId>com.vaadin</groupId> <groupId>com.vaadin</groupId>
<artifactId>vaadin-test</artifactId> <artifactId>vaadin-test</artifactId>
<version>8.10-SNAPSHOT</version>
<version>8.12-SNAPSHOT</version>
</parent> </parent>
<artifactId>vaadin-test-cdi</artifactId> <artifactId>vaadin-test-cdi</artifactId>
<packaging>war</packaging> <packaging>war</packaging>

+ 1
- 1
test/default-widget-set/pom.xml View File

<parent> <parent>
<groupId>com.vaadin</groupId> <groupId>com.vaadin</groupId>
<artifactId>vaadin-test</artifactId> <artifactId>vaadin-test</artifactId>
<version>8.10-SNAPSHOT</version>
<version>8.12-SNAPSHOT</version>
</parent> </parent>
<artifactId>vaadin-test-default-widget-set</artifactId> <artifactId>vaadin-test-default-widget-set</artifactId>
<packaging>war</packaging> <packaging>war</packaging>

+ 1
- 1
test/dependency-rewrite-addon/pom.xml View File

<parent> <parent>
<groupId>com.vaadin</groupId> <groupId>com.vaadin</groupId>
<artifactId>vaadin-test</artifactId> <artifactId>vaadin-test</artifactId>
<version>8.10-SNAPSHOT</version>
<version>8.12-SNAPSHOT</version>
</parent> </parent>
<artifactId>vaadin-test-dependency-rewrite-addon</artifactId> <artifactId>vaadin-test-dependency-rewrite-addon</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>

+ 1
- 1
test/dependency-rewrite/pom.xml View File

<parent> <parent>
<groupId>com.vaadin</groupId> <groupId>com.vaadin</groupId>
<artifactId>vaadin-test</artifactId> <artifactId>vaadin-test</artifactId>
<version>8.10-SNAPSHOT</version>
<version>8.12-SNAPSHOT</version>
</parent> </parent>
<artifactId>vaadin-test-dependency-rewrite</artifactId> <artifactId>vaadin-test-dependency-rewrite</artifactId>
<packaging>war</packaging> <packaging>war</packaging>

+ 1
- 1
test/own-widget-set/pom.xml View File

<parent> <parent>
<groupId>com.vaadin</groupId> <groupId>com.vaadin</groupId>
<artifactId>vaadin-test</artifactId> <artifactId>vaadin-test</artifactId>
<version>8.10-SNAPSHOT</version>
<version>8.12-SNAPSHOT</version>
</parent> </parent>
<artifactId>vaadin-test-own-widget-set</artifactId> <artifactId>vaadin-test-own-widget-set</artifactId>
<packaging>war</packaging> <packaging>war</packaging>

+ 1
- 1
test/pom.xml View File

<groupId>com.vaadin</groupId> <groupId>com.vaadin</groupId>
<artifactId>vaadin-test</artifactId> <artifactId>vaadin-test</artifactId>
<name>vaadin-test</name> <name>vaadin-test</name>
<version>8.10-SNAPSHOT</version>
<version>8.12-SNAPSHOT</version>


<packaging>pom</packaging> <packaging>pom</packaging>
<properties> <properties>

+ 1
- 1
test/servlet-containers/generic-tests/pom.xml View File

<parent> <parent>
<groupId>com.vaadin</groupId> <groupId>com.vaadin</groupId>
<artifactId>vaadin-servlet-containers-test</artifactId> <artifactId>vaadin-servlet-containers-test</artifactId>
<version>8.10-SNAPSHOT</version>
<version>8.12-SNAPSHOT</version>
</parent> </parent>
<artifactId>vaadin-test-server-tests</artifactId> <artifactId>vaadin-test-server-tests</artifactId>
<name>vaadin-test-server-tests</name> <name>vaadin-test-server-tests</name>

+ 1
- 1
test/servlet-containers/generic-ui/pom.xml View File

<parent> <parent>
<groupId>com.vaadin</groupId> <groupId>com.vaadin</groupId>
<artifactId>vaadin-servlet-containers-test</artifactId> <artifactId>vaadin-servlet-containers-test</artifactId>
<version>8.10-SNAPSHOT</version>
<version>8.12-SNAPSHOT</version>
</parent> </parent>
<artifactId>vaadin-test-server-ui</artifactId> <artifactId>vaadin-test-server-ui</artifactId>
<name>vaadin-test-server-ui</name> <name>vaadin-test-server-ui</name>

+ 0
- 0
test/servlet-containers/generic/pom.xml View File


Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save