From dc08a1c857d75e964a47842f4a2a9538878457b9 Mon Sep 17 00:00:00 2001 From: Teemu Pöntelin Date: Fri, 13 Jun 2014 18:07:48 +0300 Subject: ComboBox no longer displays input prompt if disabled or read-only (#10573) Change-Id: I3e0ad83bc5ec4a98a0c8e7658dfb606c6c5dc191 --- client/src/com/vaadin/client/ui/VFilterSelect.java | 2 +- client/src/com/vaadin/client/ui/combobox/ComboBoxConnector.java | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'client/src') diff --git a/client/src/com/vaadin/client/ui/VFilterSelect.java b/client/src/com/vaadin/client/ui/VFilterSelect.java index a1de2c2b6d..d4e76e07a6 100644 --- a/client/src/com/vaadin/client/ui/VFilterSelect.java +++ b/client/src/com/vaadin/client/ui/VFilterSelect.java @@ -1716,7 +1716,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, setPromptingOff(text); selectedOptionKey = currentSuggestion.key; } else { - if (focused) { + if (focused || readonly || !enabled) { setPromptingOff(""); } else { setPromptingOn(); diff --git a/client/src/com/vaadin/client/ui/combobox/ComboBoxConnector.java b/client/src/com/vaadin/client/ui/combobox/ComboBoxConnector.java index c3cdb43703..2e64fcba46 100644 --- a/client/src/com/vaadin/client/ui/combobox/ComboBoxConnector.java +++ b/client/src/com/vaadin/client/ui/combobox/ComboBoxConnector.java @@ -303,8 +303,10 @@ public class ComboBoxConnector extends AbstractFieldConnector implements * ALWAYS set the prompting style at this point, even though we * think it has been set already... */ - getWidget().prompting = false; - getWidget().setPromptingOn(); + getWidget().setPromptingOff(""); + if (getWidget().enabled && !getWidget().readonly) { + getWidget().setPromptingOn(); + } } else { // we have focus in field, prompting can't be set on, instead // just clear the input if the value has changed from something -- cgit v1.2.3 From f455b391b76b1a2ad9169c5308645ad9764505ec Mon Sep 17 00:00:00 2001 From: Juuso Valli Date: Thu, 12 Jun 2014 15:43:37 +0300 Subject: Delay tooltips when moving between adjacent elements (#13998) Change-Id: Ia0845c9439e22ecece0825aaad521e900153fc81 --- client/src/com/vaadin/client/VTooltip.java | 46 ++++++------ .../tooltip/AdjacentElementsWithTooltips.java | 82 ++++++++++++++++++++++ .../tooltip/AdjacentElementsWithTooltipsTest.java | 75 ++++++++++++++++++++ 3 files changed, 181 insertions(+), 22 deletions(-) create mode 100644 uitest/src/com/vaadin/tests/tooltip/AdjacentElementsWithTooltips.java create mode 100644 uitest/src/com/vaadin/tests/tooltip/AdjacentElementsWithTooltipsTest.java (limited to 'client/src') diff --git a/client/src/com/vaadin/client/VTooltip.java b/client/src/com/vaadin/client/VTooltip.java index 8e653a0476..1385a52469 100644 --- a/client/src/com/vaadin/client/VTooltip.java +++ b/client/src/com/vaadin/client/VTooltip.java @@ -229,18 +229,23 @@ public class VTooltip extends VWindowOverlay { /** * For assistive tooltips to work correctly we must have the tooltip visible - * and attached to the DOM well in advance. + * and attached to the DOM well in advance. For this reason both isShowing + * and isVisible return false positives. We can't override either of them as + * external code may depend on this behavior. * - * @return + * @return boolean */ - public boolean isActuallyVisible() { - return super.isShowing() && getPopupLeft() > 0 && getPopupTop() > 0; + public boolean isTooltipOpen() { + return super.isShowing() && super.isVisible() && getPopupLeft() > 0 + && getPopupTop() > 0; } private void closeNow() { hide(); setWidth(""); closing = false; + justClosedTimer.schedule(getQuickOpenTimeout()); + justClosed = true; } private Timer showTimer = new Timer() { @@ -255,8 +260,6 @@ public class VTooltip extends VWindowOverlay { @Override public void run() { closeNow(); - justClosedTimer.schedule(getQuickOpenTimeout()); - justClosed = true; } }; @@ -279,7 +282,7 @@ public class VTooltip extends VWindowOverlay { // already about to close return; } - if (isActuallyVisible()) { + if (isTooltipOpen()) { closeTimer.schedule(getCloseTimeout()); closing = true; } @@ -331,6 +334,8 @@ public class VTooltip extends VWindowOverlay { if (closing) { closeTimer.cancel(); closeNow(); + justClosedTimer.cancel(); + justClosed = false; } showTooltip(); @@ -449,7 +454,7 @@ public class VTooltip extends VWindowOverlay { // hasn't changed, we ignore the event. // TooltipInfo contains a reference to the parent component that is // checked in it's equals-method. - if (currentElement != null && isActuallyVisible()) { + if (currentElement != null && isTooltipOpen()) { TooltipInfo currentTooltip = getTooltipFor(currentElement); TooltipInfo newTooltip = getTooltipFor(element); if (currentTooltip != null && currentTooltip.equals(newTooltip)) { @@ -465,24 +470,21 @@ public class VTooltip extends VWindowOverlay { closeTimer.cancel(); closing = false; } + + if (isTooltipOpen()) { + closeNow(); + } + setTooltipText(info); updatePosition(event, isFocused); - if (isActuallyVisible() && !isFocused) { + // Schedule timer for showing the tooltip according to if it + // was recently closed or not. + int timeout = justClosed ? getQuickOpenDelay() : getOpenDelay(); + if (timeout == 0) { showTooltip(); } else { - if (isActuallyVisible()) { - closeNow(); - } - // Schedule timer for showing the tooltip according to if it - // was recently closed or not. - int timeout = justClosed ? getQuickOpenDelay() - : getOpenDelay(); - if (timeout == 0) { - showTooltip(); - } else { - showTimer.schedule(timeout); - opening = true; - } + showTimer.schedule(timeout); + opening = true; } } diff --git a/uitest/src/com/vaadin/tests/tooltip/AdjacentElementsWithTooltips.java b/uitest/src/com/vaadin/tests/tooltip/AdjacentElementsWithTooltips.java new file mode 100644 index 0000000000..60167e43b4 --- /dev/null +++ b/uitest/src/com/vaadin/tests/tooltip/AdjacentElementsWithTooltips.java @@ -0,0 +1,82 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.tooltip; + +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.Button; +import com.vaadin.ui.Component; +import com.vaadin.ui.HorizontalLayout; +import com.vaadin.ui.TooltipConfiguration; + +/** + * When moving between adjacent elements, the tooltip replace should obey + * quickOpenDelay + * + * @author Vaadin Ltd + */ +public class AdjacentElementsWithTooltips extends AbstractTestUI { + + private int buttonCount = 0; + + /* + * (non-Javadoc) + * + * @see com.vaadin.tests.components.AbstractTestUI#setup(com.vaadin.server. + * VaadinRequest) + */ + @Override + protected void setup(VaadinRequest request) { + TooltipConfiguration ttc = super.getTooltipConfiguration(); + ttc.setMaxWidth(350); + ttc.setOpenDelay(200); + ttc.setCloseTimeout(200); + ttc.setQuickOpenDelay(1000); + ttc.setQuickOpenTimeout(1000); + HorizontalLayout layout = new HorizontalLayout(); + layout.addComponent(makeButton("first")); + layout.addComponent(makeButton("second")); + addComponent(layout); + + } + + private Component makeButton(String tooltip) { + Button button = new Button("Button " + buttonCount++); + button.setDescription(tooltip); + return button; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.tests.components.AbstractTestUI#getTestDescription() + */ + @Override + protected String getTestDescription() { + return "Moving between adjacent elements with tooltips should open quickOpenDelay"; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.tests.components.AbstractTestUI#getTicketNumber() + */ + @Override + protected Integer getTicketNumber() { + return 13998; + } + +} diff --git a/uitest/src/com/vaadin/tests/tooltip/AdjacentElementsWithTooltipsTest.java b/uitest/src/com/vaadin/tests/tooltip/AdjacentElementsWithTooltipsTest.java new file mode 100644 index 0000000000..b9fc788008 --- /dev/null +++ b/uitest/src/com/vaadin/tests/tooltip/AdjacentElementsWithTooltipsTest.java @@ -0,0 +1,75 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.tooltip; + +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.lessThan; +import static org.junit.Assert.assertThat; + +import java.util.List; + +import org.junit.Test; +import org.openqa.selenium.interactions.HasInputDevices; +import org.openqa.selenium.interactions.Mouse; +import org.openqa.selenium.interactions.internal.Coordinates; +import org.openqa.selenium.internal.Locatable; +import org.openqa.selenium.remote.DesiredCapabilities; + +import com.vaadin.testbench.elements.ButtonElement; +import com.vaadin.tests.tb3.MultiBrowserTest; + +/** + * Test to see if tooltips obey quickOpenDelay when moving between directly + * adjacent elements. + * + * @author Vaadin Ltd + */ +public class AdjacentElementsWithTooltipsTest extends MultiBrowserTest { + + @Override + public List getBrowsersToTest() { + return getBrowsersExcludingIE(); + } + + @Test + public void tooltipsHaveQuickOpenDelay() throws InterruptedException { + openTestURL(); + Coordinates button0Coordinates = getButtonCoordinates("Button 0"); + Coordinates button1Coordinates = getButtonCoordinates("Button 1"); + + Mouse mouse = getMouse(); + mouse.mouseMove(button0Coordinates, 10, 10); + sleep(1000); + assertThat(getTooltipElement().getLocation().x, is(greaterThan(0))); + + mouse.mouseMove(button1Coordinates, 10, 10); + assertThat(getTooltipElement().getLocation().x, is(lessThan(-1000))); + + sleep(1000); + assertThat(getTooltipElement().getLocation().x, + is(greaterThan(button1Coordinates.onPage().x))); + } + + private Coordinates getButtonCoordinates(String caption) { + return getCoordinates(getButton(caption)); + } + + private ButtonElement getButton(String caption) { + return $(ButtonElement.class) + .caption(caption).first(); + } +} -- cgit v1.2.3 From d076dc7317b4f211e95ef560442793cb1200cfc9 Mon Sep 17 00:00:00 2001 From: Markus Koivisto Date: Thu, 12 Jun 2014 17:38:44 +0300 Subject: Prevent table from scrolling on selectionChange when in Multiselect mode (#13341) Change-Id: Ie3df61fab6d76dce3e2027cd732d0e6e7dd299e9 --- client/src/com/vaadin/client/ui/VScrollTable.java | 14 +++- .../table/SelectAllConstantViewport.java | 98 ++++++++++++++++++++++ .../table/SelectAllConstantViewportTest.java | 61 ++++++++++++++ 3 files changed, 170 insertions(+), 3 deletions(-) create mode 100644 uitest/src/com/vaadin/tests/components/table/SelectAllConstantViewport.java create mode 100644 uitest/src/com/vaadin/tests/components/table/SelectAllConstantViewportTest.java (limited to 'client/src') diff --git a/client/src/com/vaadin/client/ui/VScrollTable.java b/client/src/com/vaadin/client/ui/VScrollTable.java index d3317abd4d..f8b1ff8d83 100644 --- a/client/src/com/vaadin/client/ui/VScrollTable.java +++ b/client/src/com/vaadin/client/ui/VScrollTable.java @@ -1082,12 +1082,20 @@ public class VScrollTable extends FlowPanel implements HasWidgets, selected = true; keyboardSelectionOverRowFetchInProgress = true; } - if (selected) { + if (isSingleSelectMode() && selected) { + if (focusedRow == null || !selectedRowKeys.contains(focusedRow .getKey())) { - // The focus is no longer on a selected row, - // move focus to first selected row + /* + * The focus is no longer on a selected row. If we + * are in single select mode, move focus to the + * selected row. (#10522) + * + * Don't modify the focused row when in multiselect + * mode. (#13341) + */ + setRowFocus(row); } } diff --git a/uitest/src/com/vaadin/tests/components/table/SelectAllConstantViewport.java b/uitest/src/com/vaadin/tests/components/table/SelectAllConstantViewport.java new file mode 100644 index 0000000000..5a406eac48 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/table/SelectAllConstantViewport.java @@ -0,0 +1,98 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.components.table; + +import com.vaadin.data.Property.ValueChangeListener; +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUIWithLog; +import com.vaadin.ui.CheckBox; +import com.vaadin.ui.CssLayout; +import com.vaadin.ui.Table; + +/** + * + * @since + * @author Vaadin Ltd + */ +public class SelectAllConstantViewport extends AbstractTestUIWithLog { + + /* + * (non-Javadoc) + * + * @see com.vaadin.tests.components.AbstractTestUI#setup(com.vaadin.server. + * VaadinRequest) + */ + @Override + protected void setup(VaadinRequest request) { + + final Table table = new Table(); + table.addContainerProperty("", Integer.class, null); + table.setSizeFull(); + table.setMultiSelect(true); + table.setNullSelectionAllowed(true); + table.setSelectable(true); + + CheckBox selectAllCheckbox = new CheckBox("Select All"); + selectAllCheckbox.addValueChangeListener(new ValueChangeListener() { + @Override + public void valueChange( + com.vaadin.data.Property.ValueChangeEvent event) { + Object checked = event.getProperty().getValue(); + if (checked instanceof Boolean) { + if (((Boolean) checked).booleanValue()) { + table.setValue(table.getItemIds()); + } else { + table.setValue(null); + } + } + } + }); + + for (int i = 0; i < 200; i++) { + table.addItem(new Object[] { new Integer(i) }, new Integer(i)); + } + + table.setCurrentPageFirstItemIndex(185); + + final CssLayout layout = new CssLayout(); + layout.addComponent(selectAllCheckbox); + layout.addComponent(table); + layout.setSizeFull(); + addComponent(layout); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.tests.components.AbstractTestUI#getTestDescription() + */ + @Override + protected String getTestDescription() { + + return "The scroll position of a table with many items should remain constant if all items are selected."; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.tests.components.AbstractTestUI#getTicketNumber() + */ + @Override + protected Integer getTicketNumber() { + return 13341; + } + +} diff --git a/uitest/src/com/vaadin/tests/components/table/SelectAllConstantViewportTest.java b/uitest/src/com/vaadin/tests/components/table/SelectAllConstantViewportTest.java new file mode 100644 index 0000000000..0e7a7c08a4 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/table/SelectAllConstantViewportTest.java @@ -0,0 +1,61 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.components.table; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +import java.io.IOException; + +import org.junit.Test; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.ui.ExpectedCondition; + +import com.vaadin.testbench.By; +import com.vaadin.testbench.elements.CheckBoxElement; +import com.vaadin.testbench.elements.TableElement; +import com.vaadin.tests.tb3.MultiBrowserTest; + +public class SelectAllConstantViewportTest extends MultiBrowserTest { + + @Test + public void testViewportUnchanged() throws IOException { + openTestURL(); + + CheckBoxElement checkbox = $(CheckBoxElement.class).first(); + + WebElement row = $(TableElement.class).first().getCell(190, 0); + final WebElement scrollPositionDisplay = getDriver().findElement( + By.className("v-table-scrollposition")); + waitUntilNot(new ExpectedCondition() { + + @Override + public Boolean apply(WebDriver input) { + return scrollPositionDisplay.isDisplayed(); + } + }, 10); + + int rowLocation = row.getLocation().getY(); + + // use click x,y with non-zero offset to actually toggle the checkbox. (#13763) + checkbox.click(5, 5); + int newRowLocation = row.getLocation().getY(); + + assertThat(newRowLocation, is(rowLocation)); + + } +} -- cgit v1.2.3 From 89eea17f571bce2ab99625dfeda90d59c101a337 Mon Sep 17 00:00:00 2001 From: Teemu Pöntelin Date: Fri, 13 Jun 2014 15:57:37 +0300 Subject: Added initial style name for VColorPickerArea (#14021) Change-Id: I0f7be75f51ec771a68f6e388b412346f2c366957 --- client/src/com/vaadin/client/ui/VColorPickerArea.java | 2 ++ 1 file changed, 2 insertions(+) (limited to 'client/src') diff --git a/client/src/com/vaadin/client/ui/VColorPickerArea.java b/client/src/com/vaadin/client/ui/VColorPickerArea.java index e581cf3448..23a9379c80 100644 --- a/client/src/com/vaadin/client/ui/VColorPickerArea.java +++ b/client/src/com/vaadin/client/ui/VColorPickerArea.java @@ -34,6 +34,7 @@ import com.google.gwt.user.client.ui.Widget; public class VColorPickerArea extends Widget implements ClickHandler, HasHTML, HasClickHandlers { + public static final String CLASSNAME = "v-colorpicker"; private String color = null; private boolean isOpen; @@ -47,6 +48,7 @@ public class VColorPickerArea extends Widget implements ClickHandler, HasHTML, public VColorPickerArea() { super(); setElement(DOM.createDiv()); + setStyleName(CLASSNAME); caption = new HTML(); caption.addStyleName("v-caption"); -- cgit v1.2.3 From 8b0846967af04070bf692d444f7d684499410d24 Mon Sep 17 00:00:00 2001 From: Teemu Pöntelin Date: Thu, 12 Jun 2014 19:34:49 +0300 Subject: Fix for unnecessary RPC request when clicking on CheckBox label (#8259) Change-Id: I7efb8b10a0d1b19ffdc9eb1d29db1f00b45f17aa --- .../client/ui/checkbox/CheckBoxConnector.java | 22 ++++---- .../components/checkbox/CheckBoxRpcCount.java | 62 ++++++++++++++++++++++ .../components/checkbox/CheckBoxRpcCountTest.java | 50 +++++++++++++++++ 3 files changed, 125 insertions(+), 9 deletions(-) create mode 100644 uitest/src/com/vaadin/tests/components/checkbox/CheckBoxRpcCount.java create mode 100644 uitest/src/com/vaadin/tests/components/checkbox/CheckBoxRpcCountTest.java (limited to 'client/src') diff --git a/client/src/com/vaadin/client/ui/checkbox/CheckBoxConnector.java b/client/src/com/vaadin/client/ui/checkbox/CheckBoxConnector.java index a72049aa90..28f6beefa6 100644 --- a/client/src/com/vaadin/client/ui/checkbox/CheckBoxConnector.java +++ b/client/src/com/vaadin/client/ui/checkbox/CheckBoxConnector.java @@ -144,14 +144,18 @@ public class CheckBoxConnector extends AbstractFieldConnector implements return; } - getState().checked = getWidget().getValue(); - - // Add mouse details - MouseEventDetails details = MouseEventDetailsBuilder - .buildMouseEventDetails(event.getNativeEvent(), getWidget() - .getElement()); - getRpcProxy(CheckBoxServerRpc.class).setChecked(getState().checked, - details); - + // We get click events also from the label text, which do not alter the + // actual value. The server-side is only interested in real changes to + // the state. + if (getState().checked != getWidget().getValue()) { + getState().checked = getWidget().getValue(); + + // Add mouse details + MouseEventDetails details = MouseEventDetailsBuilder + .buildMouseEventDetails(event.getNativeEvent(), getWidget() + .getElement()); + getRpcProxy(CheckBoxServerRpc.class).setChecked(getState().checked, + details); + } } } diff --git a/uitest/src/com/vaadin/tests/components/checkbox/CheckBoxRpcCount.java b/uitest/src/com/vaadin/tests/components/checkbox/CheckBoxRpcCount.java new file mode 100644 index 0000000000..bd44e8a074 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/checkbox/CheckBoxRpcCount.java @@ -0,0 +1,62 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.components.checkbox; + +import com.vaadin.server.VaadinRequest; +import com.vaadin.shared.MouseEventDetails; +import com.vaadin.shared.ui.checkbox.CheckBoxServerRpc; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.CheckBox; +import com.vaadin.ui.Label; + +public class CheckBoxRpcCount extends AbstractTestUI { + + @Override + protected void setup(VaadinRequest request) { + final Label countLabel = new Label("No RPC calls made yet."); + countLabel.setId("count-label"); + addComponent(countLabel); + + CheckBox cb = new CheckBox("Click me to start counting...") { + { + // Register a new RPC that counts the number of invocations. + registerRpc(new CheckBoxServerRpc() { + private int rpcCount = 0; + + @Override + public void setChecked(boolean checked, + MouseEventDetails mouseEventDetails) { + rpcCount++; + countLabel.setValue(rpcCount + " RPC call(s) made."); + } + + }); + } + }; + addComponent(cb); + } + + @Override + protected String getTestDescription() { + return "Test for verifying that no extra RPC calls are made when clicking on CheckBox label."; + } + + @Override + protected Integer getTicketNumber() { + return 8259; + } + +} diff --git a/uitest/src/com/vaadin/tests/components/checkbox/CheckBoxRpcCountTest.java b/uitest/src/com/vaadin/tests/components/checkbox/CheckBoxRpcCountTest.java new file mode 100644 index 0000000000..c32051b593 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/checkbox/CheckBoxRpcCountTest.java @@ -0,0 +1,50 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.components.checkbox; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; +import org.openqa.selenium.By; +import org.openqa.selenium.WebElement; + +import com.vaadin.tests.tb3.MultiBrowserTest; + +public class CheckBoxRpcCountTest extends MultiBrowserTest { + + @Test + public void testNumberOfRpcCalls() { + openTestURL(); + + WebElement labelElem = driver.findElement(By + .cssSelector(".v-checkbox label")); + WebElement inputElem = driver.findElement(By + .cssSelector(".v-checkbox input")); + WebElement countElem = driver.findElement(By.id("count-label")); + + // Click on the actual checkbox. + inputElem.click(); + assertEquals("1 RPC call(s) made.", countElem.getText()); + + // Click on the checkbox label. + labelElem.click(); + assertEquals("2 RPC call(s) made.", countElem.getText()); + + // Again on the label. + labelElem.click(); + assertEquals("3 RPC call(s) made.", countElem.getText()); + } +} -- cgit v1.2.3 From 3200aecd5d7c55aadc3142025221b4cc87bd2e22 Mon Sep 17 00:00:00 2001 From: Juuso Valli Date: Fri, 13 Jun 2014 11:08:13 +0300 Subject: Prevent empty tooltips from appearing (#14015) Change-Id: Iee9d5be9208ff54cd0e4a58c19daaa3a917f9b9d --- client/src/com/vaadin/client/VTooltip.java | 13 +++---------- server/src/com/vaadin/server/AbstractErrorMessage.java | 2 +- 2 files changed, 4 insertions(+), 11 deletions(-) (limited to 'client/src') diff --git a/client/src/com/vaadin/client/VTooltip.java b/client/src/com/vaadin/client/VTooltip.java index 1385a52469..4db4477caa 100644 --- a/client/src/com/vaadin/client/VTooltip.java +++ b/client/src/com/vaadin/client/VTooltip.java @@ -109,13 +109,13 @@ public class VTooltip extends VWindowOverlay { } private void setTooltipText(TooltipInfo info) { - if (info.getErrorMessage() != null) { + if (info.getErrorMessage() != null && !info.getErrorMessage().isEmpty()) { em.setVisible(true); em.updateMessage(info.getErrorMessage()); } else { em.setVisible(false); } - if (info.getTitle() != null && !"".equals(info.getTitle())) { + if (info.getTitle() != null && !info.getTitle().isEmpty()) { description.setInnerHTML(info.getTitle()); description.getStyle().clearDisplay(); } else { @@ -130,13 +130,7 @@ public class VTooltip extends VWindowOverlay { * */ private void showTooltip() { - boolean hasContent = false; - if (currentTooltipInfo.getErrorMessage() != null - || (currentTooltipInfo.getTitle() != null && !"" - .equals(currentTooltipInfo.getTitle()))) { - hasContent = true; - } - if (hasContent) { + if (currentTooltipInfo.hasMessage()) { // Issue #8454: With IE7 the tooltips size is calculated based on // the last tooltip's position, causing problems if the last one was // in the right or bottom edge. For this reason the tooltip is moved @@ -491,7 +485,6 @@ public class VTooltip extends VWindowOverlay { handledByFocus = isFocused; currentElement = element; } - } private final TooltipEventHandler tooltipEventHandler = new TooltipEventHandler(); diff --git a/server/src/com/vaadin/server/AbstractErrorMessage.java b/server/src/com/vaadin/server/AbstractErrorMessage.java index c733cc493e..b56521993a 100644 --- a/server/src/com/vaadin/server/AbstractErrorMessage.java +++ b/server/src/com/vaadin/server/AbstractErrorMessage.java @@ -126,7 +126,7 @@ public abstract class AbstractErrorMessage implements ErrorMessage { StringBuilder sb = new StringBuilder(); for (ErrorMessage cause : getCauses()) { String childMessage = cause.getFormattedHtmlMessage(); - if (null != childMessage) { + if (null != childMessage && !childMessage.isEmpty()) { sb.append("
"); sb.append(childMessage); sb.append("
\n"); -- cgit v1.2.3 From 26f74f5b45dc5d7c2646b792d1ac12851eed1285 Mon Sep 17 00:00:00 2001 From: Teemu Pöntelin Date: Thu, 22 May 2014 21:22:09 +0300 Subject: Fixed an NPE while changing a DateField from 24 to 12h clock (#13722) A client-side NullPointerException was fixed by not reusing the existing VTime panel and always recreating it instead. Otherwise the ListBox for switching between AM/PM might have been null. Change-Id: I8d54d91627043a12b52ac5d5e54d6f7a729af1ac --- .../src/com/vaadin/client/ui/VCalendarPanel.java | 4 +- .../tests/components/datefield/LocaleChange.java | 82 ++++++++++++++++++++++ .../components/datefield/LocaleChangeTest.java | 62 ++++++++++++++++ 3 files changed, 145 insertions(+), 3 deletions(-) create mode 100644 uitest/src/com/vaadin/tests/components/datefield/LocaleChange.java create mode 100644 uitest/src/com/vaadin/tests/components/datefield/LocaleChangeTest.java (limited to 'client/src') diff --git a/client/src/com/vaadin/client/ui/VCalendarPanel.java b/client/src/com/vaadin/client/ui/VCalendarPanel.java index d8c96917d8..eaa2292c69 100644 --- a/client/src/com/vaadin/client/ui/VCalendarPanel.java +++ b/client/src/com/vaadin/client/ui/VCalendarPanel.java @@ -813,14 +813,12 @@ public class VCalendarPanel extends FocusableFlexTable implements buildCalendarBody(); } - if (isTimeSelectorNeeded() && time == null) { + if (isTimeSelectorNeeded()) { time = new VTime(); setWidget(2, 0, time); getFlexCellFormatter().setColSpan(2, 0, 5); getFlexCellFormatter().setStyleName(2, 0, parent.getStylePrimaryName() + "-calendarpanel-time"); - } else if (isTimeSelectorNeeded()) { - time.updateTimes(); } else if (time != null) { remove(time); } diff --git a/uitest/src/com/vaadin/tests/components/datefield/LocaleChange.java b/uitest/src/com/vaadin/tests/components/datefield/LocaleChange.java new file mode 100644 index 0000000000..f69c93419b --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/datefield/LocaleChange.java @@ -0,0 +1,82 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.components.datefield; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; + +import com.vaadin.server.VaadinRequest; +import com.vaadin.shared.ui.datefield.Resolution; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.Button; +import com.vaadin.ui.Button.ClickEvent; +import com.vaadin.ui.Button.ClickListener; +import com.vaadin.ui.DateField; + +public class LocaleChange extends AbstractTestUI { + + private final Locale locale12hClock = Locale.US; + private final Locale locale24hClock = Locale.FRANCE; + + private final String caption = "Switch to %s hour clock"; + private static final Date dateValue; + static { + try { + dateValue = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss") + .parse("2014-05-22 20:00:00"); + } catch (ParseException e) { + throw new ExceptionInInitializerError("Should never happen."); + } + } + + @Override + protected void setup(VaadinRequest request) { + final DateField df = new DateField(); + df.setLocale(locale24hClock); + df.setResolution(Resolution.SECOND); + df.setValue(dateValue); + + Button button = new Button(String.format(caption, "12")); + button.addClickListener(new ClickListener() { + + @Override + public void buttonClick(ClickEvent event) { + if (locale12hClock.equals(df.getLocale())) { + df.setLocale(locale24hClock); + event.getButton().setCaption(String.format(caption, "12")); + } else { + df.setLocale(locale12hClock); + event.getButton().setCaption(String.format(caption, "24")); + } + } + }); + + addComponent(df); + addComponent(button); + } + + @Override + protected String getTestDescription() { + return "Testing locale change from one with 24h clock to a 12h clock locale."; + } + + @Override + protected Integer getTicketNumber() { + return 13722; + } +} \ No newline at end of file diff --git a/uitest/src/com/vaadin/tests/components/datefield/LocaleChangeTest.java b/uitest/src/com/vaadin/tests/components/datefield/LocaleChangeTest.java new file mode 100644 index 0000000000..cf756034a1 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/datefield/LocaleChangeTest.java @@ -0,0 +1,62 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.components.datefield; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; +import org.openqa.selenium.By; + +import com.vaadin.tests.tb3.MultiBrowserTest; + +public class LocaleChangeTest extends MultiBrowserTest { + + @Test + public void testLocaleChange() { + openTestURL(); + + // Check the initial value and that popup can be opened. + assertEquals("22/05/14 20:00:00", getDateValue()); + toggleDatePopup(); + assertPopupOpen(true); + + // Close the popup and change the locale. + toggleDatePopup(); + assertPopupOpen(false); + driver.findElement(By.className("v-button")).click(); // Locale change. + + // Check that the value has changed and the popup can still be opened + // without problems. + assertEquals("5/22/14 08:00:00 PM", getDateValue()); + toggleDatePopup(); + assertPopupOpen(true); + } + + private void assertPopupOpen(boolean open) { + assertEquals("Date popup was not " + (open ? "open" : "closed") + ".", + (open ? 1 : 0), + driver.findElements(By.className("v-datefield-popup")).size()); + } + + private void toggleDatePopup() { + driver.findElement(By.className("v-datefield-button")).click(); + } + + private String getDateValue() { + return driver.findElement(By.className("v-datefield-textfield")) + .getAttribute("value"); + } +} -- cgit v1.2.3 From dd8cf12dde433198070f77d97d9220dd66a7e7d5 Mon Sep 17 00:00:00 2001 From: Dmitrii Rogozin Date: Tue, 17 Jun 2014 14:43:34 +0300 Subject: Bug fix menu navigation with space (#14041) Change-Id: I1466381b12a147fea90240420d3f6c05cd156a1b --- client/src/com/vaadin/client/Util.java | 19 ++++- client/src/com/vaadin/client/ui/VMenuBar.java | 84 +++++++++++++--------- .../menubar/SpaceMenuBarNavigationTest.java | 17 +++-- 3 files changed, 77 insertions(+), 43 deletions(-) (limited to 'client/src') diff --git a/client/src/com/vaadin/client/Util.java b/client/src/com/vaadin/client/Util.java index e031b37422..f12a02c64f 100644 --- a/client/src/com/vaadin/client/Util.java +++ b/client/src/com/vaadin/client/Util.java @@ -35,6 +35,7 @@ import com.google.gwt.dom.client.Style; import com.google.gwt.dom.client.Style.Display; import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.dom.client.Touch; +import com.google.gwt.event.dom.client.KeyEvent; import com.google.gwt.user.client.Command; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Event; @@ -66,7 +67,23 @@ public class Util { }-*/; /** - * + * Helper method for a bug fix #14041. For mozilla getKeyCode return 0 for + * space bar (because space is considered as char). If return 0 use + * getCharCode. + * + * @param event + * @return return key code + */ + public static int getKeyCode(KeyEvent event) { + int keyCode = event.getNativeEvent().getKeyCode(); + if (keyCode == 0) { + keyCode = event.getNativeEvent().getCharCode(); + } + return keyCode; + } + + /** + * * Returns the topmost element of from given coordinates. * * TODO fix crossplat issues clientX vs pageX. See quircksmode. Not critical diff --git a/client/src/com/vaadin/client/ui/VMenuBar.java b/client/src/com/vaadin/client/ui/VMenuBar.java index 11fda6222c..909b25f7fb 100644 --- a/client/src/com/vaadin/client/ui/VMenuBar.java +++ b/client/src/com/vaadin/client/ui/VMenuBar.java @@ -1,12 +1,12 @@ /* * Copyright 2000-2014 Vaadin Ltd. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the @@ -243,7 +243,7 @@ public class VMenuBar extends SimpleFocusablePanel implements /** * This is called by the items in the menu and it communicates the * information to the server - * + * * @param clickedItemId * id of the item that was clicked */ @@ -280,7 +280,7 @@ public class VMenuBar extends SimpleFocusablePanel implements /** * Returns the containing element of the menu - * + * * @return */ @Override @@ -290,7 +290,7 @@ public class VMenuBar extends SimpleFocusablePanel implements /** * Add a new item to this menu - * + * * @param html * items text * @param cmd @@ -307,7 +307,7 @@ public class VMenuBar extends SimpleFocusablePanel implements /** * Add a new item to this menu - * + * * @param item */ public void addItem(CustomMenuItem item) { @@ -332,7 +332,7 @@ public class VMenuBar extends SimpleFocusablePanel implements /** * Remove the given item from this menu - * + * * @param item */ public void removeItem(CustomMenuItem item) { @@ -429,7 +429,7 @@ public class VMenuBar extends SimpleFocusablePanel implements /** * When an item is clicked - * + * * @param item */ public void itemClick(CustomMenuItem item) { @@ -460,7 +460,7 @@ public class VMenuBar extends SimpleFocusablePanel implements /** * When the user hovers the mouse over the item - * + * * @param item */ public void itemOver(CustomMenuItem item) { @@ -485,7 +485,7 @@ public class VMenuBar extends SimpleFocusablePanel implements /** * When the mouse is moved away from an item - * + * * @param item */ public void itemOut(CustomMenuItem item) { @@ -543,7 +543,7 @@ public class VMenuBar extends SimpleFocusablePanel implements /** * Shows the child menu of an item. The caller must ensure that the item has * a submenu. - * + * * @param item */ public void showChildMenu(CustomMenuItem item) { @@ -665,7 +665,7 @@ public class VMenuBar extends SimpleFocusablePanel implements /** * Hides the submenu of an item - * + * * @param item */ public void hideChildMenu(CustomMenuItem item) { @@ -729,7 +729,7 @@ public class VMenuBar extends SimpleFocusablePanel implements /** * Returns the parent menu of this menu, or null if this is the top-level * menu - * + * * @return */ public VMenuBar getParentMenu() { @@ -738,7 +738,7 @@ public class VMenuBar extends SimpleFocusablePanel implements /** * Set the parent menu of this menu - * + * * @param parent */ public void setParentMenu(VMenuBar parent) { @@ -748,7 +748,7 @@ public class VMenuBar extends SimpleFocusablePanel implements /** * Returns the currently selected item of this menu, or null if nothing is * selected - * + * * @return */ public CustomMenuItem getSelected() { @@ -757,7 +757,7 @@ public class VMenuBar extends SimpleFocusablePanel implements /** * Set the currently selected item of this menu - * + * * @param item */ public void setSelected(CustomMenuItem item) { @@ -774,9 +774,9 @@ public class VMenuBar extends SimpleFocusablePanel implements } /** - * + * * A class to hold information on menu items - * + * */ public static class CustomMenuItem extends Widget implements HasHTML { @@ -795,7 +795,7 @@ public class VMenuBar extends SimpleFocusablePanel implements /** * Default menu item {@link Widget} constructor for GWT.create(). - * + * * Use {@link #setHTML(String)} and {@link #setCommand(Command)} after * constructing a menu item. */ @@ -805,7 +805,7 @@ public class VMenuBar extends SimpleFocusablePanel implements /** * Creates a menu item {@link Widget}. - * + * * @param html * @param cmd * @deprecated use the default constructor and {@link #setHTML(String)} @@ -1039,7 +1039,7 @@ public class VMenuBar extends SimpleFocusablePanel implements /** * Checks if the item can be selected. - * + * * @return true if it is possible to select this item, false otherwise */ public boolean isSelectable() { @@ -1157,7 +1157,14 @@ public class VMenuBar extends SimpleFocusablePanel implements */ @Override public void onKeyPress(KeyPressEvent event) { - if (handleNavigation(event.getNativeEvent().getKeyCode(), + // A bug fix for #14041 + // getKeyCode and getCharCode return different values for different + // browsers + int keyCode = event.getNativeEvent().getKeyCode(); + if (keyCode == 0) { + keyCode = event.getNativeEvent().getCharCode(); + } + if (handleNavigation(keyCode, event.isControlKeyDown() || event.isMetaKeyDown(), event.isShiftKeyDown())) { event.preventDefault(); @@ -1173,7 +1180,14 @@ public class VMenuBar extends SimpleFocusablePanel implements */ @Override public void onKeyDown(KeyDownEvent event) { - if (handleNavigation(event.getNativeEvent().getKeyCode(), + // A bug fix for #14041 + // getKeyCode and getCharCode return different values for different + // browsers + int keyCode = event.getNativeEvent().getKeyCode(); + if (keyCode == 0) { + keyCode = event.getNativeEvent().getCharCode(); + } + if (handleNavigation(keyCode, event.isControlKeyDown() || event.isMetaKeyDown(), event.isShiftKeyDown())) { event.preventDefault(); @@ -1184,7 +1198,7 @@ public class VMenuBar extends SimpleFocusablePanel implements * Get the key that moves the selection upwards. By default it is the up * arrow key but by overriding this you can change the key to whatever you * want. - * + * * @return The keycode of the key */ protected int getNavigationUpKey() { @@ -1195,7 +1209,7 @@ public class VMenuBar extends SimpleFocusablePanel implements * Get the key that moves the selection downwards. By default it is the down * arrow key but by overriding this you can change the key to whatever you * want. - * + * * @return The keycode of the key */ protected int getNavigationDownKey() { @@ -1206,7 +1220,7 @@ public class VMenuBar extends SimpleFocusablePanel implements * Get the key that moves the selection left. By default it is the left * arrow key but by overriding this you can change the key to whatever you * want. - * + * * @return The keycode of the key */ protected int getNavigationLeftKey() { @@ -1217,7 +1231,7 @@ public class VMenuBar extends SimpleFocusablePanel implements * Get the key that moves the selection right. By default it is the right * arrow key but by overriding this you can change the key to whatever you * want. - * + * * @return The keycode of the key */ protected int getNavigationRightKey() { @@ -1227,7 +1241,7 @@ public class VMenuBar extends SimpleFocusablePanel implements /** * Get the key that selects a menu item. By default it is the Enter key but * by overriding this you can change the key to whatever you want. - * + * * @deprecated use {@link #isNavigationSelectKey(int)} instead * @return */ @@ -1240,7 +1254,7 @@ public class VMenuBar extends SimpleFocusablePanel implements * Checks whether key code selects a menu item. By default it is the Enter * and Space keys but by overriding this you can change the keys to whatever * you want. - * + * * @since 7.2 * @param keycode * @return true if key selects menu item @@ -1253,7 +1267,7 @@ public class VMenuBar extends SimpleFocusablePanel implements /** * Get the key that closes the menu. By default it is the escape key but by * overriding this yoy can change the key to whatever you want. - * + * * @return */ protected int getCloseMenuKey() { @@ -1262,7 +1276,7 @@ public class VMenuBar extends SimpleFocusablePanel implements /** * Handles the keyboard events handled by the MenuBar - * + * * @param event * The keyboard event received * @return true iff the navigation event was handled @@ -1549,7 +1563,7 @@ public class VMenuBar extends SimpleFocusablePanel implements /** * Get menu item with given DOM element - * + * * @param element * Element used in search * @return Menu item or null if not found @@ -1578,11 +1592,11 @@ public class VMenuBar extends SimpleFocusablePanel implements /** * Get menu item with given DOM element - * + * * @param element * Element used in search * @return Menu item or null if not found - * + * * @since 7.2 */ public CustomMenuItem getMenuItemWithElement(Element element) { diff --git a/uitest/src/com/vaadin/tests/components/menubar/SpaceMenuBarNavigationTest.java b/uitest/src/com/vaadin/tests/components/menubar/SpaceMenuBarNavigationTest.java index 3e0053d0d1..a32a611d9a 100644 --- a/uitest/src/com/vaadin/tests/components/menubar/SpaceMenuBarNavigationTest.java +++ b/uitest/src/com/vaadin/tests/components/menubar/SpaceMenuBarNavigationTest.java @@ -1,12 +1,12 @@ /* * Copyright 2000-2014 Vaadin Ltd. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the @@ -23,10 +23,11 @@ import org.openqa.selenium.Keys; import org.openqa.selenium.WebElement; import com.vaadin.testbench.By; +import com.vaadin.testbench.elements.MenuBarElement; import com.vaadin.tests.tb3.MultiBrowserTest; /** - * + * * @since 7.2 * @author Vaadin Ltd */ @@ -36,19 +37,21 @@ public class SpaceMenuBarNavigationTest extends MultiBrowserTest { public void testEnableParentLayout() { openTestURL(); - WebElement menu = driver.findElement(By.className("menu-bar")); + MenuBarElement menu = $(MenuBarElement.class).get(0); + menu.focus(); menu.sendKeys(Keys.ARROW_RIGHT); - menu.sendKeys(Keys.SPACE); + menu.sendKeys(Keys.ENTER); List captions = driver.findElements(By .className("v-menubar-menuitem-caption")); boolean found = false; + for (WebElement caption : captions) { if ("subitem".equals(caption.getText())) { found = true; } } - Assert.assertTrue("Sub menu is not opened on SPACE key", found); + Assert.assertTrue("Sub menu is not opened on ENTER key", found); menu.sendKeys(Keys.SPACE); -- cgit v1.2.3 From 6e109e4d73b7e3c09da3000a813904b52ea89991 Mon Sep 17 00:00:00 2001 From: Tomi Virkki Date: Wed, 9 Apr 2014 07:24:20 +0300 Subject: Update RichTextArea editor height on resize (#11320) Change-Id: I4d4d054c2e4f068aacd9b324350be4ee696cf3d3 --- client/src/com/vaadin/client/ui/VRichTextArea.java | 91 +--------------------- .../ui/richtextarea/RichTextAreaConnector.java | 27 ++++++- .../RichTextAreaRelativeHeightResize.java | 58 ++++++++++++++ .../RichTextAreaRelativeHeightResizeTest.java | 55 +++++++++++++ 4 files changed, 143 insertions(+), 88 deletions(-) create mode 100644 uitest/src/com/vaadin/tests/components/richtextarea/RichTextAreaRelativeHeightResize.java create mode 100644 uitest/src/com/vaadin/tests/components/richtextarea/RichTextAreaRelativeHeightResizeTest.java (limited to 'client/src') diff --git a/client/src/com/vaadin/client/ui/VRichTextArea.java b/client/src/com/vaadin/client/ui/VRichTextArea.java index 52e3782f32..3f63f38067 100644 --- a/client/src/com/vaadin/client/ui/VRichTextArea.java +++ b/client/src/com/vaadin/client/ui/VRichTextArea.java @@ -21,10 +21,6 @@ import java.util.Map; import java.util.Map.Entry; import com.google.gwt.core.client.Scheduler; -import com.google.gwt.dom.client.Element; -import com.google.gwt.dom.client.Style.Position; -import com.google.gwt.dom.client.Style.Unit; -import com.google.gwt.dom.client.Style.Visibility; import com.google.gwt.event.dom.client.BlurHandler; import com.google.gwt.event.dom.client.KeyDownEvent; import com.google.gwt.event.dom.client.KeyDownHandler; @@ -32,7 +28,6 @@ import com.google.gwt.event.dom.client.KeyPressEvent; import com.google.gwt.event.dom.client.KeyPressHandler; import com.google.gwt.event.shared.HandlerRegistration; import com.google.gwt.user.client.Command; -import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Timer; import com.google.gwt.user.client.ui.Composite; import com.google.gwt.user.client.ui.FlowPanel; @@ -43,7 +38,6 @@ import com.google.gwt.user.client.ui.Widget; import com.vaadin.client.ApplicationConnection; import com.vaadin.client.BrowserInfo; import com.vaadin.client.ConnectorMap; -import com.vaadin.client.Util; import com.vaadin.client.ui.ShortcutActionHandler.ShortcutActionHandlerOwner; import com.vaadin.client.ui.richtextarea.VRichTextToolbar; @@ -73,7 +67,8 @@ public class VRichTextArea extends Composite implements Field, KeyPressHandler, /** For internal use only. May be removed or replaced in the future. */ public RichTextArea rta; - private VRichTextToolbar formatter; + /** For internal use only. May be removed or replaced in the future. */ + public VRichTextToolbar formatter; /** For internal use only. May be removed or replaced in the future. */ public HTML html = new HTML(); @@ -82,9 +77,6 @@ public class VRichTextArea extends Composite implements Field, KeyPressHandler, private boolean enabled = true; - private int extraHorizontalPixels = -1; - private int extraVerticalPixels = -1; - /** For internal use only. May be removed or replaced in the future. */ public int maxLength = -1; @@ -193,92 +185,17 @@ public class VRichTextArea extends Composite implements Field, KeyPressHandler, return readOnly; } - /** - * @return space used by components paddings and borders - */ - private int getExtraHorizontalPixels() { - if (extraHorizontalPixels < 0) { - detectExtraSizes(); - } - return extraHorizontalPixels; - } - - /** - * @return space used by components paddings and borders - */ - private int getExtraVerticalPixels() { - if (extraVerticalPixels < 0) { - detectExtraSizes(); - } - return extraVerticalPixels; - } - - /** - * Detects space used by components paddings and borders. - */ - private void detectExtraSizes() { - Element clone = Util.cloneNode(getElement(), false); - DOM.setElementAttribute(clone, "id", ""); - clone.getStyle().setVisibility(Visibility.HIDDEN); - clone.getStyle().setPosition(Position.ABSOLUTE); - // due FF3 bug set size to 10px and later subtract it from extra pixels - clone.getStyle().setWidth(10, Unit.PX); - clone.getStyle().setHeight(10, Unit.PX); - DOM.appendChild(DOM.getParent(getElement()), clone); - extraHorizontalPixels = DOM.getElementPropertyInt(clone, "offsetWidth") - 10; - extraVerticalPixels = DOM.getElementPropertyInt(clone, "offsetHeight") - 10; - - DOM.removeChild(DOM.getParent(getElement()), clone); - } - @Override public void setHeight(String height) { - if (height.endsWith("px")) { - float h = Float - .parseFloat(height.substring(0, height.length() - 2)); - h -= getExtraVerticalPixels(); - if (h < 0) { - h = 0; - } - - super.setHeight(h + "px"); - } else { - super.setHeight(height); - } - + super.setHeight(height); if (height == null || height.equals("")) { rta.setHeight(""); - } else { - /* - * The formatter height will be initially calculated wrong so we - * delay the height setting so the DOM has had time to stabilize. - */ - Scheduler.get().scheduleDeferred(new Command() { - @Override - public void execute() { - int editorHeight = getOffsetHeight() - - getExtraVerticalPixels() - - formatter.getOffsetHeight(); - if (editorHeight < 0) { - editorHeight = 0; - } - rta.setHeight(editorHeight + "px"); - } - }); } } @Override public void setWidth(String width) { - if (width.endsWith("px")) { - float w = Float.parseFloat(width.substring(0, width.length() - 2)); - w -= getExtraHorizontalPixels(); - if (w < 0) { - w = 0; - } - - super.setWidth(w + "px"); - } else if (width.equals("")) { + if (width.equals("")) { /* * IE cannot calculate the width of the 100% iframe correctly if * there is no width specified for the parent. In this case we would diff --git a/client/src/com/vaadin/client/ui/richtextarea/RichTextAreaConnector.java b/client/src/com/vaadin/client/ui/richtextarea/RichTextAreaConnector.java index 6b3bf84578..5fe637447e 100644 --- a/client/src/com/vaadin/client/ui/richtextarea/RichTextAreaConnector.java +++ b/client/src/com/vaadin/client/ui/richtextarea/RichTextAreaConnector.java @@ -23,6 +23,7 @@ import com.vaadin.client.Paintable; import com.vaadin.client.UIDL; import com.vaadin.client.ui.AbstractFieldConnector; import com.vaadin.client.ui.ShortcutActionHandler.BeforeShortcutActionListener; +import com.vaadin.client.ui.SimpleManagedLayout; import com.vaadin.client.ui.VRichTextArea; import com.vaadin.shared.ui.Connect; import com.vaadin.shared.ui.Connect.LoadStyle; @@ -31,7 +32,7 @@ import com.vaadin.ui.RichTextArea; @Connect(value = RichTextArea.class, loadStyle = LoadStyle.LAZY) public class RichTextAreaConnector extends AbstractFieldConnector implements - Paintable, BeforeShortcutActionListener { + Paintable, BeforeShortcutActionListener, SimpleManagedLayout { /* * Last value received from the server @@ -47,6 +48,15 @@ public class RichTextAreaConnector extends AbstractFieldConnector implements flush(); } }); + getLayoutManager().registerDependency(this, + getWidget().formatter.getElement()); + } + + @Override + public void onUnregister() { + super.onUnregister(); + getLayoutManager().unregisterDependency(this, + getWidget().formatter.getElement()); } @Override @@ -110,4 +120,19 @@ public class RichTextAreaConnector extends AbstractFieldConnector implements } } } + + @Override + public void layout() { + if (!isUndefinedHeight()) { + int rootElementInnerHeight = getLayoutManager().getInnerHeight( + getWidget().getElement()); + int formatterHeight = getLayoutManager().getOuterHeight( + getWidget().formatter.getElement()); + int editorHeight = rootElementInnerHeight - formatterHeight; + if (editorHeight < 0) { + editorHeight = 0; + } + getWidget().rta.setHeight(editorHeight + "px"); + } + } } diff --git a/uitest/src/com/vaadin/tests/components/richtextarea/RichTextAreaRelativeHeightResize.java b/uitest/src/com/vaadin/tests/components/richtextarea/RichTextAreaRelativeHeightResize.java new file mode 100644 index 0000000000..870c6cb8cb --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/richtextarea/RichTextAreaRelativeHeightResize.java @@ -0,0 +1,58 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.components.richtextarea; + +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.Button; +import com.vaadin.ui.Button.ClickEvent; +import com.vaadin.ui.RichTextArea; +import com.vaadin.ui.VerticalLayout; + +public class RichTextAreaRelativeHeightResize extends AbstractTestUI { + + @Override + protected void setup(VaadinRequest request) { + final VerticalLayout layout = new VerticalLayout(); + layout.setSizeFull(); + layout.setHeight("300px"); + + RichTextArea richTextArea = new RichTextArea(); + richTextArea.setSizeFull(); + layout.addComponent(richTextArea); + + addComponent(layout); + addComponent(new Button("Increase height", new Button.ClickListener() { + @Override + public void buttonClick(ClickEvent event) { + layout.setHeight("400px"); + } + })); + + } + + @Override + protected String getTestDescription() { + return "Tests that a RichTextArea with dynamic height " + + "updates its editor elements height on resize"; + } + + @Override + protected Integer getTicketNumber() { + return 11320; + } + +} diff --git a/uitest/src/com/vaadin/tests/components/richtextarea/RichTextAreaRelativeHeightResizeTest.java b/uitest/src/com/vaadin/tests/components/richtextarea/RichTextAreaRelativeHeightResizeTest.java new file mode 100644 index 0000000000..5c31ce4dc7 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/richtextarea/RichTextAreaRelativeHeightResizeTest.java @@ -0,0 +1,55 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.components.richtextarea; + +import org.junit.Assert; +import org.junit.Test; + +import com.vaadin.testbench.By; +import com.vaadin.tests.tb3.MultiBrowserTest; + +public class RichTextAreaRelativeHeightResizeTest extends MultiBrowserTest { + + @Test + public void testCenteredClosingAndPostLayout() { + openTestURL(); + + int originalHeight = driver + .findElement(By.cssSelector(".v-richtextarea")).getSize() + .getHeight(); + int originalEditorHeight = driver + .findElement(By.cssSelector(".v-richtextarea iframe")) + .getSize().getHeight(); + + // Increase the component height + driver.findElement(By.cssSelector(".v-button")).click(); + + int newHeight = driver.findElement(By.cssSelector(".v-richtextarea")) + .getSize().getHeight(); + int newEditorHeight = driver + .findElement(By.cssSelector(".v-richtextarea iframe")) + .getSize().getHeight(); + + // Check that the component height changed and that the editor height + // changed equally as much + Assert.assertTrue("RichTextArea height didn't change", + newHeight != originalHeight); + Assert.assertEquals( + "Editor height change didn't match the Component height change", + newHeight - originalHeight, newEditorHeight + - originalEditorHeight); + } +} -- cgit v1.2.3 From 7f769ec77d52aecbbf9d4787ab2e482d3b31a2af Mon Sep 17 00:00:00 2001 From: Juuso Valli Date: Wed, 18 Jun 2014 15:10:28 +0300 Subject: Fix regression with DnD tooltips (#7766) Change-Id: I74e6c35ef0aa30dbb24301bfb5858cedd0008e71 --- client/src/com/vaadin/client/TooltipInfo.java | 6 +- client/src/com/vaadin/client/VTooltip.java | 14 +-- .../DragAndDropWrapperTooltips.html | 53 -------- .../DragAndDropWrapperTooltips.java | 134 --------------------- .../tests/tooltip/DragAndDropWrapperTooltips.java | 134 +++++++++++++++++++++ .../tooltip/DragAndDropWrapperTooltipsTest.java | 44 +++++++ 6 files changed, 189 insertions(+), 196 deletions(-) delete mode 100644 uitest/src/com/vaadin/tests/components/draganddropwrapper/DragAndDropWrapperTooltips.html delete mode 100644 uitest/src/com/vaadin/tests/components/draganddropwrapper/DragAndDropWrapperTooltips.java create mode 100644 uitest/src/com/vaadin/tests/tooltip/DragAndDropWrapperTooltips.java create mode 100644 uitest/src/com/vaadin/tests/tooltip/DragAndDropWrapperTooltipsTest.java (limited to 'client/src') diff --git a/client/src/com/vaadin/client/TooltipInfo.java b/client/src/com/vaadin/client/TooltipInfo.java index 06940536c8..c1dd5037eb 100644 --- a/client/src/com/vaadin/client/TooltipInfo.java +++ b/client/src/com/vaadin/client/TooltipInfo.java @@ -15,6 +15,8 @@ */ package com.vaadin.client; +import com.vaadin.shared.util.SharedUtil; + public class TooltipInfo { private String title; @@ -79,7 +81,7 @@ public class TooltipInfo { } public boolean equals(TooltipInfo other) { - return (other != null && other.title == title - && other.errorMessageHtml == errorMessageHtml && other.identifier == identifier); + return (other != null && SharedUtil.equals(other.title, title) + && SharedUtil.equals(other.errorMessageHtml, errorMessageHtml) && other.identifier == identifier); } } diff --git a/client/src/com/vaadin/client/VTooltip.java b/client/src/com/vaadin/client/VTooltip.java index 4db4477caa..d1a2c395f7 100644 --- a/client/src/com/vaadin/client/VTooltip.java +++ b/client/src/com/vaadin/client/VTooltip.java @@ -22,13 +22,13 @@ import com.google.gwt.dom.client.Element; import com.google.gwt.dom.client.Style.Display; import com.google.gwt.event.dom.client.BlurEvent; import com.google.gwt.event.dom.client.BlurHandler; -import com.google.gwt.event.dom.client.ClickEvent; -import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.dom.client.DomEvent; import com.google.gwt.event.dom.client.FocusEvent; import com.google.gwt.event.dom.client.FocusHandler; import com.google.gwt.event.dom.client.KeyDownEvent; import com.google.gwt.event.dom.client.KeyDownHandler; +import com.google.gwt.event.dom.client.MouseDownEvent; +import com.google.gwt.event.dom.client.MouseDownHandler; import com.google.gwt.event.dom.client.MouseMoveEvent; import com.google.gwt.event.dom.client.MouseMoveHandler; import com.google.gwt.user.client.DOM; @@ -337,7 +337,7 @@ public class VTooltip extends VWindowOverlay { } private class TooltipEventHandler implements MouseMoveHandler, - ClickHandler, KeyDownHandler, FocusHandler, BlurHandler { + KeyDownHandler, FocusHandler, BlurHandler, MouseDownHandler { /** * Current element hovered @@ -403,7 +403,7 @@ public class VTooltip extends VWindowOverlay { } @Override - public void onClick(ClickEvent event) { + public void onMouseDown(MouseDownEvent event) { handleHideEvent(); } @@ -449,9 +449,9 @@ public class VTooltip extends VWindowOverlay { // TooltipInfo contains a reference to the parent component that is // checked in it's equals-method. if (currentElement != null && isTooltipOpen()) { - TooltipInfo currentTooltip = getTooltipFor(currentElement); TooltipInfo newTooltip = getTooltipFor(element); - if (currentTooltip != null && currentTooltip.equals(newTooltip)) { + if (currentTooltipInfo != null + && currentTooltipInfo.equals(newTooltip)) { return; } } @@ -498,7 +498,7 @@ public class VTooltip extends VWindowOverlay { public void connectHandlersToWidget(Widget widget) { Profiler.enter("VTooltip.connectHandlersToWidget"); widget.addDomHandler(tooltipEventHandler, MouseMoveEvent.getType()); - widget.addDomHandler(tooltipEventHandler, ClickEvent.getType()); + widget.addDomHandler(tooltipEventHandler, MouseDownEvent.getType()); widget.addDomHandler(tooltipEventHandler, KeyDownEvent.getType()); widget.addDomHandler(tooltipEventHandler, FocusEvent.getType()); widget.addDomHandler(tooltipEventHandler, BlurEvent.getType()); diff --git a/uitest/src/com/vaadin/tests/components/draganddropwrapper/DragAndDropWrapperTooltips.html b/uitest/src/com/vaadin/tests/components/draganddropwrapper/DragAndDropWrapperTooltips.html deleted file mode 100644 index 3c91c8b24f..0000000000 --- a/uitest/src/com/vaadin/tests/components/draganddropwrapper/DragAndDropWrapperTooltips.html +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - -New Test - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
New Test
open/run/com.vaadin.tests.components.draganddropwrapper.DragAndDropWrapperTooltips?restartApplication
showTooltipvaadin=runcomvaadintestscomponentsdraganddropwrapperDragAndDropWrapperTooltips::PID_Swrapper3/VLabel[0]0,0
screenCapturetooltip-initial
dragvaadin=runcomvaadintestscomponentsdraganddropwrapperDragAndDropWrapperTooltips::PID_Swrapper4/VLabel[0]30,41
dropvaadin=runcomvaadintestscomponentsdraganddropwrapperDragAndDropWrapperTooltips::PID_Swrapper24,42
showTooltipvaadin=runcomvaadintestscomponentsdraganddropwrapperDragAndDropWrapperTooltips::PID_Swrapper1/VLabel[0]0,0
screenCapturetooltip-after-drag
- - diff --git a/uitest/src/com/vaadin/tests/components/draganddropwrapper/DragAndDropWrapperTooltips.java b/uitest/src/com/vaadin/tests/components/draganddropwrapper/DragAndDropWrapperTooltips.java deleted file mode 100644 index f0010acce8..0000000000 --- a/uitest/src/com/vaadin/tests/components/draganddropwrapper/DragAndDropWrapperTooltips.java +++ /dev/null @@ -1,134 +0,0 @@ -package com.vaadin.tests.components.draganddropwrapper; - -import java.util.Iterator; - -import com.vaadin.event.Transferable; -import com.vaadin.event.TransferableImpl; -import com.vaadin.event.dd.DragAndDropEvent; -import com.vaadin.event.dd.DropHandler; -import com.vaadin.event.dd.DropTarget; -import com.vaadin.event.dd.TargetDetails; -import com.vaadin.event.dd.acceptcriteria.AcceptAll; -import com.vaadin.event.dd.acceptcriteria.AcceptCriterion; -import com.vaadin.tests.components.TestBase; -import com.vaadin.tests.util.TestUtils; -import com.vaadin.ui.Component; -import com.vaadin.ui.CssLayout; -import com.vaadin.ui.DragAndDropWrapper; -import com.vaadin.ui.Label; -import com.vaadin.ui.VerticalLayout; - -public class DragAndDropWrapperTooltips extends TestBase { - - private final String BASE = ".v-widget.greenblock {vertical-align: middle; float:left; width:60px;height:60px;background: green !important; padding:0; margin:2px;-webkit-transition: width 0.3s ease-in-out;color: white;}"; - private final String B2 = ".v-widget.b2 {background-color: red !important;}"; - private final String B3 = ".v-widget.b3 {background-color: yellow !important;color: black;}"; - private final String B4 = ".v-widget.b4 {background-color: blue !important;}"; - private final String HIDEDRAGSOURCE = ".v-active-drag-source { overflow:hidden; width:0px !important;}"; - private DragAndDropWrapper dragAndDropWrapper; - - @Override - protected void setup() { - TestUtils.injectCSS(getMainWindow(), BASE + B4 + B2 + B3 - + HIDEDRAGSOURCE); - - VerticalLayout l = new VerticalLayout(); - l.setWidth("400px"); - l.setHeight("100px"); - dragAndDropWrapper = new DragAndDropWrapper(cssLayout); - dragAndDropWrapper - .setDescription("Tooltip for the wrapper wrapping all the draggable layouts"); - dragAndDropWrapper.setSizeFull(); - l.addComponent(dragAndDropWrapper); - - addComponent(l); - - for (int i = 1; i <= 4; i++) { - WrappedLabel wl = new WrappedLabel("Block"); - wl.setId("wrapper" + i); - wl.addStyleName("b" + i); - cssLayout.addComponent(wl); - } - - } - - int count; - - private CssLayout cssLayout = new CssLayout() { - }; - - class WrappedLabel extends DragAndDropWrapper { - - private static final long serialVersionUID = 1L; - - public WrappedLabel(String content) { - super(new Label(content + " " + ++count)); - getCompositionRoot().setSizeUndefined(); - setHeight("60px"); // FIXME custom component seems to be broken: - // can't set height with css only - setWidth("60px"); - setDragStartMode(DragStartMode.WRAPPER); - addStyleName("greenblock"); - } - - @Override - public DropHandler getDropHandler() { - return dh; - } - - } - - private DropHandler dh = new DropHandler() { - - @Override - public AcceptCriterion getAcceptCriterion() { - return AcceptAll.get(); - } - - @Override - public void drop(DragAndDropEvent dropEvent) { - Transferable transferable = dropEvent.getTransferable(); - if (transferable instanceof TransferableImpl) { - TransferableImpl ct = (TransferableImpl) transferable; - Component sourceComponent = ct.getSourceComponent(); - if (sourceComponent instanceof WrappedLabel) { - int index = 1; - Iterator componentIterator = cssLayout - .getComponentIterator(); - Component next = componentIterator.next(); - TargetDetails dropTargetData = dropEvent.getTargetDetails(); - DropTarget target = dropTargetData.getTarget(); - while (next != target) { - if (next != sourceComponent) { - index++; - } - next = componentIterator.next(); - } - if (dropTargetData.getData("horizontalLocation").equals( - "LEFT")) { - index--; - if (index < 0) { - index = 0; - } - } - - cssLayout.removeComponent(sourceComponent); - cssLayout.addComponent(sourceComponent, index); - - dragAndDropWrapper - .setDescription("Drag was performed and tooltip was changed"); - } - } - } - }; - - @Override - protected String getDescription() { - return "A tooltip should be shown when hovering the DragAndDropWrapper containing all the draggable layouts"; - } - - @Override - protected Integer getTicketNumber() { - return 7708; - } -} diff --git a/uitest/src/com/vaadin/tests/tooltip/DragAndDropWrapperTooltips.java b/uitest/src/com/vaadin/tests/tooltip/DragAndDropWrapperTooltips.java new file mode 100644 index 0000000000..606a773141 --- /dev/null +++ b/uitest/src/com/vaadin/tests/tooltip/DragAndDropWrapperTooltips.java @@ -0,0 +1,134 @@ +package com.vaadin.tests.tooltip; + +import java.util.Iterator; + +import com.vaadin.event.Transferable; +import com.vaadin.event.TransferableImpl; +import com.vaadin.event.dd.DragAndDropEvent; +import com.vaadin.event.dd.DropHandler; +import com.vaadin.event.dd.DropTarget; +import com.vaadin.event.dd.TargetDetails; +import com.vaadin.event.dd.acceptcriteria.AcceptAll; +import com.vaadin.event.dd.acceptcriteria.AcceptCriterion; +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.tests.util.TestUtils; +import com.vaadin.ui.Component; +import com.vaadin.ui.CssLayout; +import com.vaadin.ui.DragAndDropWrapper; +import com.vaadin.ui.Label; +import com.vaadin.ui.VerticalLayout; + +public class DragAndDropWrapperTooltips extends AbstractTestUI { + + private final String BASE = ".v-widget.greenblock {vertical-align: middle; float:left; width:60px;height:60px;background: green !important; padding:0; margin:2px;-webkit-transition: width 0.3s ease-in-out;color: white;}"; + private final String B2 = ".v-widget.b2 {background-color: red !important;}"; + private final String B3 = ".v-widget.b3 {background-color: yellow !important;color: black;}"; + private final String B4 = ".v-widget.b4 {background-color: blue !important;}"; + private final String HIDEDRAGSOURCE = ".v-active-drag-source { overflow:hidden; width:0px !important;}"; + private DragAndDropWrapper dragAndDropWrapper; + + @Override + protected void setup(VaadinRequest request) { + TestUtils.injectCSS(this, BASE + B4 + B2 + B3 + HIDEDRAGSOURCE); + + VerticalLayout l = new VerticalLayout(); + l.setWidth("400px"); + l.setHeight("100px"); + dragAndDropWrapper = new DragAndDropWrapper(cssLayout); + dragAndDropWrapper + .setDescription("Tooltip for the wrapper wrapping all the draggable layouts"); + dragAndDropWrapper.setSizeFull(); + l.addComponent(dragAndDropWrapper); + + addComponent(l); + + for (int i = 1; i <= 4; i++) { + WrappedLabel wl = new WrappedLabel("Block"); + wl.setId("wrapper" + i); + wl.addStyleName("b" + i); + cssLayout.addComponent(wl); + } + getTooltipConfiguration().setOpenDelay(300); + } + + int count; + + private CssLayout cssLayout = new CssLayout() { + }; + + class WrappedLabel extends DragAndDropWrapper { + + private static final long serialVersionUID = 1L; + + public WrappedLabel(String content) { + super(new Label(content + " " + ++count)); + getCompositionRoot().setSizeUndefined(); + setHeight("60px"); // FIXME custom component seems to be broken: + // can't set height with css only + setWidth("60px"); + setDragStartMode(DragStartMode.WRAPPER); + addStyleName("greenblock"); + } + + @Override + public DropHandler getDropHandler() { + return dh; + } + + } + + private DropHandler dh = new DropHandler() { + + @Override + public AcceptCriterion getAcceptCriterion() { + return AcceptAll.get(); + } + + @Override + public void drop(DragAndDropEvent dropEvent) { + Transferable transferable = dropEvent.getTransferable(); + if (transferable instanceof TransferableImpl) { + TransferableImpl ct = (TransferableImpl) transferable; + Component sourceComponent = ct.getSourceComponent(); + if (sourceComponent instanceof WrappedLabel) { + int index = 1; + Iterator componentIterator = cssLayout + .getComponentIterator(); + Component next = componentIterator.next(); + TargetDetails dropTargetData = dropEvent.getTargetDetails(); + DropTarget target = dropTargetData.getTarget(); + while (next != target) { + if (next != sourceComponent) { + index++; + } + next = componentIterator.next(); + } + if (dropTargetData.getData("horizontalLocation").equals( + "LEFT")) { + index--; + if (index < 0) { + index = 0; + } + } + + cssLayout.removeComponent(sourceComponent); + cssLayout.addComponent(sourceComponent, index); + + dragAndDropWrapper + .setDescription("Drag was performed and tooltip was changed"); + } + } + } + }; + + @Override + protected String getTestDescription() { + return "A tooltip should be shown when hovering the DragAndDropWrapper containing all the draggable layouts"; + } + + @Override + protected Integer getTicketNumber() { + return 7708; + } +} diff --git a/uitest/src/com/vaadin/tests/tooltip/DragAndDropWrapperTooltipsTest.java b/uitest/src/com/vaadin/tests/tooltip/DragAndDropWrapperTooltipsTest.java new file mode 100644 index 0000000000..d913c8cc12 --- /dev/null +++ b/uitest/src/com/vaadin/tests/tooltip/DragAndDropWrapperTooltipsTest.java @@ -0,0 +1,44 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.tooltip; + +import org.junit.Test; +import org.openqa.selenium.interactions.Actions; + +import com.vaadin.testbench.elements.LabelElement; +import com.vaadin.tests.tb3.TooltipTest; + +/** + * + * + * @author Vaadin Ltd + */ +public class DragAndDropWrapperTooltipsTest extends TooltipTest { + @Test + public void testDragAndDropTooltips() throws Exception { + openTestURL(); + LabelElement element = $(LabelElement.class).get(4); + LabelElement targetElement = $(LabelElement.class).get(1); + checkTooltip(element, + "Tooltip for the wrapper wrapping all the draggable layouts"); + new Actions(getDriver()).clickAndHold(element) + .moveToElement(targetElement).perform(); + sleep(500); + checkTooltipNotPresent(); + new Actions(getDriver()).release().perform(); + checkTooltip(element, "Drag was performed and tooltip was changed"); + } +} -- cgit v1.2.3 From b5bc598c0938fd254ca666dd4b0ebdc3dc36d8da Mon Sep 17 00:00:00 2001 From: Leif Åstrand Date: Mon, 23 Jun 2014 15:03:29 +0300 Subject: Improve error message for missing sync id (#14065) Change-Id: Iab2ddc31261f198c486b9028249fab9cc2cc06a1 --- client/src/com/vaadin/client/ApplicationConnection.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'client/src') diff --git a/client/src/com/vaadin/client/ApplicationConnection.java b/client/src/com/vaadin/client/ApplicationConnection.java index 694fc71060..5d696f1ddb 100644 --- a/client/src/com/vaadin/client/ApplicationConnection.java +++ b/client/src/com/vaadin/client/ApplicationConnection.java @@ -66,7 +66,6 @@ import com.google.gwt.user.client.Window.ClosingHandler; import com.google.gwt.user.client.ui.HasWidgets; import com.google.gwt.user.client.ui.Widget; import com.vaadin.client.ApplicationConfiguration.ErrorMessage; -import com.vaadin.client.ApplicationConnection.ApplicationStoppedEvent; import com.vaadin.client.ResourceLoader.ResourceLoadEvent; import com.vaadin.client.ResourceLoader.ResourceLoadListener; import com.vaadin.client.communication.HasJavaScriptConnectorHelper; @@ -1437,7 +1436,8 @@ public class ApplicationConnection implements HasHandlers { lastSeenServerSyncId = syncId; } else { - VConsole.error("Server response didn't contain an id."); + VConsole.error("Server response didn't contain a sync id. " + + "Please verify that the server is up-to-date and that the response data has not been modified in transmission."); } // Handle redirect -- cgit v1.2.3 From 413aace227c47bfbb5df6f33953f5cf854f3f3dd Mon Sep 17 00:00:00 2001 From: Markus Koivisto Date: Wed, 18 Jun 2014 11:06:53 +0300 Subject: Selecting in a mulsel table should focus, not scroll. (#13341, #10522) Previous patch caused a regression that was caught by tests for #10522. Change-Id: I3ebc6a3ffe08c24d463cc70d95c4417d465fc490 --- client/src/com/vaadin/client/ui/VScrollTable.java | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) (limited to 'client/src') diff --git a/client/src/com/vaadin/client/ui/VScrollTable.java b/client/src/com/vaadin/client/ui/VScrollTable.java index f8b1ff8d83..7ec6845a11 100644 --- a/client/src/com/vaadin/client/ui/VScrollTable.java +++ b/client/src/com/vaadin/client/ui/VScrollTable.java @@ -1082,18 +1082,14 @@ public class VScrollTable extends FlowPanel implements HasWidgets, selected = true; keyboardSelectionOverRowFetchInProgress = true; } - if (isSingleSelectMode() && selected) { + if (selected) { if (focusedRow == null || !selectedRowKeys.contains(focusedRow .getKey())) { /* - * The focus is no longer on a selected row. If we - * are in single select mode, move focus to the - * selected row. (#10522) - * - * Don't modify the focused row when in multiselect - * mode. (#13341) + * The focus is no longer on a selected row. Move + * focus to the selected row. (#10522) */ setRowFocus(row); @@ -7266,7 +7262,14 @@ public class VScrollTable extends FlowPanel implements HasWidgets, // Set new focused row focusedRow = row; - ensureRowIsVisible(row); + /* + * Don't scroll to the focused row when in multiselect mode. + * (#13341) + */ + + if (isSingleSelectMode()) { + ensureRowIsVisible(row); + } return true; } -- cgit v1.2.3 From 0f38151da3e89a65e4403d8f53402906fe8dcc37 Mon Sep 17 00:00:00 2001 From: Artur Signell Date: Wed, 23 Apr 2014 23:44:19 +0300 Subject: Fix moving a single component inside a split panel (#11920) Change-Id: Ib9b3625e4104763143906eb1b7986ef7b3b80737 --- .../com/vaadin/client/ui/VAbstractSplitPanel.java | 8 +++ .../ui/splitpanel/AbstractSplitPanelConnector.java | 33 +++++++++++ .../splitpanel/SplitPanelMoveComponent.java | 64 ++++++++++++++++++++++ .../splitpanel/SplitPanelMoveComponentTest.java | 55 +++++++++++++++++++ 4 files changed, 160 insertions(+) create mode 100644 uitest/src/com/vaadin/tests/components/splitpanel/SplitPanelMoveComponent.java create mode 100644 uitest/src/com/vaadin/tests/components/splitpanel/SplitPanelMoveComponentTest.java (limited to 'client/src') diff --git a/client/src/com/vaadin/client/ui/VAbstractSplitPanel.java b/client/src/com/vaadin/client/ui/VAbstractSplitPanel.java index 269db23366..6ee88d51dd 100644 --- a/client/src/com/vaadin/client/ui/VAbstractSplitPanel.java +++ b/client/src/com/vaadin/client/ui/VAbstractSplitPanel.java @@ -511,6 +511,10 @@ public class VAbstractSplitPanel extends ComplexPanel { firstChild = w; } + public Widget getFirstWidget() { + return firstChild; + } + /** For internal use only. May be removed or replaced in the future. */ public void setSecondWidget(Widget w) { if (secondChild == w) { @@ -525,6 +529,10 @@ public class VAbstractSplitPanel extends ComplexPanel { secondChild = w; } + public Widget getSecondWidget() { + return secondChild; + } + @Override public void onBrowserEvent(Event event) { switch (DOM.eventGetType(event)) { diff --git a/client/src/com/vaadin/client/ui/splitpanel/AbstractSplitPanelConnector.java b/client/src/com/vaadin/client/ui/splitpanel/AbstractSplitPanelConnector.java index ce8b3c8fea..6bf03ad880 100644 --- a/client/src/com/vaadin/client/ui/splitpanel/AbstractSplitPanelConnector.java +++ b/client/src/com/vaadin/client/ui/splitpanel/AbstractSplitPanelConnector.java @@ -161,6 +161,35 @@ public abstract class AbstractSplitPanelConnector extends getLayoutManager().setNeedsLayout(this); getWidget().makeScrollable(); + + handleSingleComponentMove(); + } + + /** + * Handles the case when there is only one child component and that + * component is moved between first <-> second. This does not trigger a + * hierarchy change event as the list of children contains the same + * component in both cases. + */ + private void handleSingleComponentMove() { + if (getChildComponents().size() == 1) { + Widget stateFirstChild = null; + Widget stateSecondChild = null; + if (getState().firstChild != null) { + stateFirstChild = ((ComponentConnector) getState().firstChild) + .getWidget(); + } + if (getState().secondChild != null) { + stateSecondChild = ((ComponentConnector) getState().secondChild) + .getWidget(); + } + + if (stateFirstChild == getWidget().getSecondWidget() + || stateSecondChild == getWidget().getFirstWidget()) { + handleHierarchyChange(); + } + } + } @Override @@ -212,6 +241,10 @@ public abstract class AbstractSplitPanelConnector extends @Override public void onConnectorHierarchyChange(ConnectorHierarchyChangeEvent event) { + handleHierarchyChange(); + } + + private void handleHierarchyChange() { /* * When the connector gets detached, the state isn't updated but there's * still a hierarchy change -> verify that the child from the state is diff --git a/uitest/src/com/vaadin/tests/components/splitpanel/SplitPanelMoveComponent.java b/uitest/src/com/vaadin/tests/components/splitpanel/SplitPanelMoveComponent.java new file mode 100644 index 0000000000..b788b568c2 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/splitpanel/SplitPanelMoveComponent.java @@ -0,0 +1,64 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.components.splitpanel; + +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.Button; +import com.vaadin.ui.Button.ClickEvent; +import com.vaadin.ui.Button.ClickListener; +import com.vaadin.ui.HorizontalSplitPanel; + +public class SplitPanelMoveComponent extends AbstractTestUI { + + @Override + protected void setup(VaadinRequest request) { + final HorizontalSplitPanel split = new HorizontalSplitPanel(); + split.setHeight("200px"); + final Button targetComponent = new Button( + "Button in splitpanel. Click to move to the other side"); + split.setFirstComponent(targetComponent); + + targetComponent.addClickListener(new ClickListener() { + + @Override + public void buttonClick(ClickEvent event) { + if (split.getFirstComponent() != null) { + split.setFirstComponent(null); + split.setSecondComponent(targetComponent); + } else { + split.setSecondComponent(null); + split.setFirstComponent(targetComponent); + + } + } + + }); + + addComponent(split); + } + + @Override + protected String getTestDescription() { + return "Fail to swap components in HorizontalSplitPanel"; + } + + @Override + protected Integer getTicketNumber() { + return 11920; + } + +} diff --git a/uitest/src/com/vaadin/tests/components/splitpanel/SplitPanelMoveComponentTest.java b/uitest/src/com/vaadin/tests/components/splitpanel/SplitPanelMoveComponentTest.java new file mode 100644 index 0000000000..1bf5212185 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/splitpanel/SplitPanelMoveComponentTest.java @@ -0,0 +1,55 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.components.splitpanel; + +import org.junit.Assert; +import org.junit.Test; +import org.openqa.selenium.By; +import org.openqa.selenium.WebElement; + +import com.vaadin.tests.tb3.MultiBrowserTest; + +public class SplitPanelMoveComponentTest extends MultiBrowserTest { + + private static final String BUTTON_TEXT = "Button in splitpanel. Click to move to the other side"; + + @Test + public void moveComponent() { + openTestURL(); + Assert.assertEquals(BUTTON_TEXT, getFirstChild().getText()); + getFirstChild().click(); + Assert.assertEquals(BUTTON_TEXT, getSecondChild().getText()); + getSecondChild().click(); + Assert.assertEquals(BUTTON_TEXT, getFirstChild().getText()); + } + + private WebElement getFirstChild() { + WebElement container = getDriver() + .findElement( + By.xpath("//div[contains(@class,'v-splitpanel-first-container')]")); + return container.findElement(By + .xpath("//div[contains(@class, 'v-button')]")); + } + + private WebElement getSecondChild() { + WebElement container = getDriver() + .findElement( + By.xpath("//div[contains(@class,'v-splitpanel-second-container')]")); + return container.findElement(By + .xpath("//div[contains(@class, 'v-button')]")); + } + +} -- cgit v1.2.3 From 297c97a7a927a1f4e6450f81518290bf845929e1 Mon Sep 17 00:00:00 2001 From: Markus Koivisto Date: Wed, 4 Jun 2014 12:49:43 +0300 Subject: Fixed regression with changing widths when stylenames have changed (#13444, #8801): Changing the name of a style should not cause the width of the textbox to change. If there is a width set for the wrapper, the width for the wrapper should not change if the styles changes. To reconcile these two demands, we have removed the width for the wrapper and only manipulate the width of the textbox itself. This solves both issues neatly. Change-Id: Idb2dbc5d2ca6406cafb44df93110e079a1dff473 --- client/src/com/vaadin/client/ui/VFilterSelect.java | 109 +++++++++++---------- .../client/ui/combobox/ComboBoxConnector.java | 27 +---- 2 files changed, 58 insertions(+), 78 deletions(-) (limited to 'client/src') diff --git a/client/src/com/vaadin/client/ui/VFilterSelect.java b/client/src/com/vaadin/client/ui/VFilterSelect.java index d4e76e07a6..7157ef99b3 100644 --- a/client/src/com/vaadin/client/ui/VFilterSelect.java +++ b/client/src/com/vaadin/client/ui/VFilterSelect.java @@ -81,7 +81,7 @@ import com.vaadin.shared.ui.combobox.FilteringMode; /** * Client side implementation of the Select component. - * + * * TODO needs major refactoring (to be extensible etc) */ @SuppressWarnings("deprecation") @@ -101,7 +101,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, /** * Constructor - * + * * @param uidl * The UIDL recieved from the server */ @@ -150,7 +150,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, /** * Get the option key which represents the item on the server side. - * + * * @return The key of the item */ public String getOptionKey() { @@ -159,7 +159,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, /** * Get the URI of the icon. Used when constructing the displayed option. - * + * * @return */ public String getIconUri() { @@ -256,7 +256,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, /** * Shows the popup where the user can see the filtered options - * + * * @param currentSuggestions * The filtered suggestions * @param currentPage @@ -342,7 +342,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, /** * Should the next page button be visible to the user? - * + * * @param active */ private void setNextButtonActive(boolean active) { @@ -362,7 +362,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, /** * Should the previous page button be visible to the user - * + * * @param active */ private void setPrevButtonActive(boolean active) { @@ -449,7 +449,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, * because otherwise the waiting flag will be reset in * the first response and the second response will be * ignored, causing an empty popup... - * + * * As long as the scrolling delay is suitable * double/triple clicks will work by scrolling two or * three pages at a time and this should not be a @@ -484,7 +484,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, /* * (non-Javadoc) - * + * * @see * com.google.gwt.user.client.ui.Widget#onBrowserEvent(com.google.gwt * .user.client.Event) @@ -521,7 +521,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, * amount of items are visible at a time and a scrollbar or buttons are * visible to change page. If paging is turned of then all options are * rendered into the popup menu. - * + * * @param paging * Should the paging be turned on? */ @@ -544,7 +544,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, /* * (non-Javadoc) - * + * * @see * com.google.gwt.user.client.ui.PopupPanel$PositionCallback#setPosition * (int, int) @@ -637,7 +637,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, /** * Was the popup just closed? - * + * * @return true if popup was just closed */ public boolean isJustClosed() { @@ -648,7 +648,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, /* * (non-Javadoc) - * + * * @see * com.google.gwt.event.logical.shared.CloseHandler#onClose(com.google * .gwt.event.logical.shared.CloseEvent) @@ -666,7 +666,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, /** * Updates style names in suggestion popup to help theme building. - * + * * @param uidl * UIDL for the whole combo box * @param componentState @@ -746,7 +746,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, /** * Sets the suggestions rendered in the menu - * + * * @param suggestions * The suggestions to be rendered in the menu */ @@ -941,7 +941,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, /** * TextBox variant used as input element for filter selects, which prevents * selecting text when disabled. - * + * * @since 7.1.5 */ public class FilterSelectTextBox extends TextBox { @@ -993,7 +993,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, /* * (non-Javadoc) - * + * * @see * com.google.gwt.user.client.ui.Widget#onBrowserEvent(com.google.gwt * .user.client.Event) @@ -1166,7 +1166,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, /* * (non-Javadoc) - * + * * @see * com.google.gwt.user.client.ui.Composite#onBrowserEvent(com.google.gwt * .user.client.Event) @@ -1193,7 +1193,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, * It is invoked during the Constructor and should only be overridden if a * custom TextBox shall be used. The overriding method cannot use any * instance variables. - * + * * @since 7.1.5 * @return TextBox instance used by this VFilterSelect */ @@ -1206,7 +1206,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, * instance. It is invoked during the Constructor and should only be * overridden if a custom SuggestionPopup shall be used. The overriding * method cannot use any instance variables. - * + * * @since 7.1.5 * @return SuggestionPopup instance used by this VFilterSelect */ @@ -1234,7 +1234,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, /** * Does the Select have more pages? - * + * * @return true if a next page exists, else false if the current page is the * last page */ @@ -1249,7 +1249,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, /** * Filters the options at a certain page. Uses the text box input as a * filter - * + * * @param page * The page which items are to be filtered */ @@ -1259,7 +1259,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, /** * Filters the options at certain page using the given filter - * + * * @param page * The page to filter * @param filter @@ -1271,7 +1271,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, /** * Filters the options at certain page using the given filter - * + * * @param page * The page to filter * @param filter @@ -1336,7 +1336,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, /** * Sets the text in the text box. - * + * * @param text * the text to set in the text box */ @@ -1365,7 +1365,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, * shown in the text box if nothing has been entered. *

* For internal use only. May be removed or replaced in the future. - * + * * @param text * The text the text box should contain. */ @@ -1380,7 +1380,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, /** * Triggered when a suggestion is selected - * + * * @param suggestion * The suggestion that just got selected. */ @@ -1420,7 +1420,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, /** * Sets the icon URI of the selected item. The icon is shown on the left * side of the item caption text. Set the URI to null to remove the icon. - * + * * @param iconUri * The URI of the icon */ @@ -1493,7 +1493,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, /* * (non-Javadoc) - * + * * @see * com.google.gwt.event.dom.client.KeyDownHandler#onKeyDown(com.google.gwt * .event.dom.client.KeyDownEvent) @@ -1546,7 +1546,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, /** * Triggered when a key is pressed in the text box - * + * * @param event * The KeyDownEvent */ @@ -1591,7 +1591,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, /** * Triggered when a key was pressed in the suggestion popup. - * + * * @param event * The KeyDownEvent of the key */ @@ -1673,7 +1673,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, /** * Triggered when a key was depressed - * + * * @param event * The KeyUpEvent of the key depressed */ @@ -1816,7 +1816,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, /* * (non-Javadoc) - * + * * @see * com.google.gwt.event.dom.client.FocusHandler#onFocus(com.google.gwt.event * .dom.client.FocusEvent) @@ -1860,7 +1860,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, /* * (non-Javadoc) - * + * * @see * com.google.gwt.event.dom.client.BlurHandler#onBlur(com.google.gwt.event * .dom.client.BlurEvent) @@ -1925,7 +1925,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, /* * (non-Javadoc) - * + * * @see com.vaadin.client.Focusable#focus() */ @@ -1946,22 +1946,9 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, * For internal use only. May be removed or replaced in the future. */ public void updateRootWidth() { - updateRootWidth(false); - } - - /** - * Calculates the width of the select if the select has undefined width. - * Should be called when the width changes or when the icon changes. - *

- * For internal use only. May be removed or replaced in the future. - * - * @param forceUpdate - * a flag that forces a recalculation even if one would not - * normally be done - */ - public void updateRootWidth(boolean forceUpdate) { ComponentConnector paintable = ConnectorMap.get(client).getConnector( this); + if (paintable.isUndefinedWidth()) { /* @@ -1973,7 +1960,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, */ int w = Util.getRequiredWidth(this); - if (forceUpdate || (!initDone || currentPage + 1 < 0) + if ((!initDone || currentPage + 1 < 0) && suggestionPopupMinWidth > w) { /* * We want to compensate for the paddings just to preserve the @@ -1987,11 +1974,25 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, String originalBorder = style.getBorderWidth(); style.setPaddingLeft(0, Unit.PX); style.setBorderWidth(0, Unit.PX); - int offset = w - Util.getRequiredWidth(this); style.setProperty("padding", originalPadding); style.setProperty("borderWidth", originalBorder); - setWidth(suggestionPopupMinWidth + offset + "px"); + // Use util.getRequiredWidth instead of getOffsetWidth here + + int iconWidth = selectedItemIcon == null ? 0 : Util + .getRequiredWidth(selectedItemIcon); + int buttonWidth = popupOpener == null ? 0 : Util + .getRequiredWidth(popupOpener); + + /* + * Instead of setting the width of the wrapper, set the width of + * the combobox. Subtract the width of the icon and the + * popupopener + */ + + tb.setWidth((suggestionPopupMinWidth - iconWidth - buttonWidth) + + "px"); + } /* @@ -2009,7 +2010,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, /** * Get the width of the select in pixels where the text area and icon has * been included. - * + * * @return The width in pixels */ private int getMainWidth() { @@ -2026,7 +2027,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, /** * Handles special behavior of the mouse down event - * + * * @param event */ private void handleMouseDownEvent(Event event) { diff --git a/client/src/com/vaadin/client/ui/combobox/ComboBoxConnector.java b/client/src/com/vaadin/client/ui/combobox/ComboBoxConnector.java index 2e64fcba46..78505d034c 100644 --- a/client/src/com/vaadin/client/ui/combobox/ComboBoxConnector.java +++ b/client/src/com/vaadin/client/ui/combobox/ComboBoxConnector.java @@ -24,7 +24,6 @@ import com.google.gwt.core.client.Scheduler.ScheduledCommand; import com.vaadin.client.ApplicationConnection; import com.vaadin.client.Paintable; import com.vaadin.client.UIDL; -import com.vaadin.client.communication.StateChangeEvent; import com.vaadin.client.ui.AbstractFieldConnector; import com.vaadin.client.ui.SimpleManagedLayout; import com.vaadin.client.ui.VFilterSelect; @@ -44,10 +43,6 @@ public class ComboBoxConnector extends AbstractFieldConnector implements // update textbox text by a changed item caption. private boolean oldSuggestionTextMatchTheOldSelection; - // Need to recompute the width of the combobox when styles change, see - // #13444 - private boolean stylesChanged; - /* * (non-Javadoc) * @@ -201,16 +196,11 @@ public class ComboBoxConnector extends AbstractFieldConnector implements getWidget().popupOpenerClicked = false; /* - * if styles have changed or this is our first time we need to - * recalculate the root width. + * if this is our first time we need to recalculate the root width. */ if (!getWidget().initDone) { - // no need to force update since we have no existing width - getWidget().updateRootWidth(false); - } else if (stylesChanged) { - // we have previously calculated a width, we must force an update - // due to changed styles - getWidget().updateRootWidth(true); + + getWidget().updateRootWidth(); } // Focus dependent style names are lost during the update, so we add @@ -219,9 +209,6 @@ public class ComboBoxConnector extends AbstractFieldConnector implements getWidget().addStyleDependentName("focus"); } - // width has been recalculated above, clear style change flag - stylesChanged = false; - getWidget().initDone = true; } @@ -347,12 +334,4 @@ public class ComboBoxConnector extends AbstractFieldConnector implements getWidget().tb.setEnabled(widgetEnabled); } - @Override - public void onStateChanged(StateChangeEvent event) { - super.onStateChanged(event); - if (event.hasPropertyChanged("styles")) { - stylesChanged = true; - } - } - } -- cgit v1.2.3 From f846f8d699ad6f599d91f7f86aa419be9b22b5a8 Mon Sep 17 00:00:00 2001 From: Anthony Guerreiro Date: Mon, 23 Jun 2014 14:13:37 +0300 Subject: Fix wrong width on event resize in Vaadin Calendar (#13961) Change-Id: I68775af42c1c6086d347bea81e54bbe3cf7e5a22 --- .../ui/calendar/schedule/DateCellDayEvent.java | 1 + .../calendar/CalendarResizeOverlappingEvents.java | 103 ++++++++++++++ .../CalendarResizeOverlappingEventsTest.java | 153 +++++++++++++++++++++ 3 files changed, 257 insertions(+) create mode 100644 uitest/src/com/vaadin/tests/components/calendar/CalendarResizeOverlappingEvents.java create mode 100644 uitest/src/com/vaadin/tests/components/calendar/CalendarResizeOverlappingEventsTest.java (limited to 'client/src') diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/DateCellDayEvent.java b/client/src/com/vaadin/client/ui/calendar/schedule/DateCellDayEvent.java index bbbd355a32..3b168b636c 100644 --- a/client/src/com/vaadin/client/ui/calendar/schedule/DateCellDayEvent.java +++ b/client/src/com/vaadin/client/ui/calendar/schedule/DateCellDayEvent.java @@ -298,6 +298,7 @@ public class DateCellDayEvent extends FocusableHTML implements weekGrid.getCalendar().getEventResizeListener() .eventResized(calendarEvent); } + dateCell.recalculateEventWidths(); } } diff --git a/uitest/src/com/vaadin/tests/components/calendar/CalendarResizeOverlappingEvents.java b/uitest/src/com/vaadin/tests/components/calendar/CalendarResizeOverlappingEvents.java new file mode 100644 index 0000000000..2025ce2ea7 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/calendar/CalendarResizeOverlappingEvents.java @@ -0,0 +1,103 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.components.calendar; + +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.Calendar; +import com.vaadin.ui.components.calendar.event.BasicEvent; +import com.vaadin.ui.components.calendar.event.CalendarEvent; +import com.vaadin.ui.components.calendar.event.CalendarEventProvider; + +/** + * + * @author Vaadin Ltd + */ +public class CalendarResizeOverlappingEvents extends AbstractTestUI { + + /* + * (non-Javadoc) + * + * @see com.vaadin.tests.components.AbstractTestUI#setup(com.vaadin.server. + * VaadinRequest) + */ + @Override + protected void setup(VaadinRequest request) { + Calendar calendar = new Calendar(new CalendarEventProvider() { + + @Override + public List getEvents(Date startDate, Date endDate) { + DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + DateFormat dayFormat = new SimpleDateFormat("yyyy-MM-dd"); + List events = new ArrayList(); + try { + java.util.Calendar today = java.util.Calendar.getInstance(); + + String todayString = dayFormat.format(today.getTime()); + + Date date1 = format.parse(todayString + " 09:00:00"); + Date date2 = format.parse(todayString + " 11:00:00"); + Date date3 = format.parse(todayString + " 12:00:00"); + Date date4 = format.parse(todayString + " 14:00:00"); + Date date5 = format.parse(todayString + " 15:00:00"); + Date date6 = format.parse(todayString + " 17:00:00"); + + CalendarEvent event1 = new BasicEvent("First", "", date1, + date2); + CalendarEvent event2 = new BasicEvent("Second", "", date3, + date4); + CalendarEvent event3 = new BasicEvent("Third", "", date5, + date6); + + events.add(event1); + events.add(event2); + events.add(event3); + } catch (ParseException e) { + } + return events; + } + }); + calendar.setSizeFull(); + setContent(calendar); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.tests.components.AbstractTestUI#getTestDescription() + */ + @Override + protected String getTestDescription() { + return "Verify the widths of the events are correctly recalculated when these are resized and overlapped"; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.tests.components.AbstractTestUI#getTicketNumber() + */ + @Override + protected Integer getTicketNumber() { + return 13961; + } +} diff --git a/uitest/src/com/vaadin/tests/components/calendar/CalendarResizeOverlappingEventsTest.java b/uitest/src/com/vaadin/tests/components/calendar/CalendarResizeOverlappingEventsTest.java new file mode 100644 index 0000000000..f664149cce --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/calendar/CalendarResizeOverlappingEventsTest.java @@ -0,0 +1,153 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.components.calendar; + +import java.io.IOException; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import org.junit.Assert; +import org.junit.Test; +import org.openqa.selenium.By; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.interactions.Actions; + +import com.vaadin.tests.tb3.MultiBrowserTest; + +/** + * + * @author Vaadin Ltd + */ +public class CalendarResizeOverlappingEventsTest extends MultiBrowserTest { + + private int noOverlapWidth; + private int oneOverlapWidth; + private int twoOverlapsWidth; + + private WebElement firstEvent; + private WebElement secondEvent; + private WebElement thirdEvent; + + private WebElement firstEventBottomResize; + private WebElement secondEventBottomResize; + private WebElement thirdEventBottomResize; + + @Test + public void testCalendarResizeOverlappingEvents() + throws InterruptedException, IOException { + + openTestURL(); + initParams(); + doTest(); + } + + private void doTest() { + assertWidths(noOverlapWidth, noOverlapWidth, noOverlapWidth); + + dragAndDrop(firstEventBottomResize, 240); + assertWidths(oneOverlapWidth, oneOverlapWidth, oneOverlapWidth); + + dragAndDrop(secondEventBottomResize, 240); + assertWidths(twoOverlapsWidth, twoOverlapsWidth, twoOverlapsWidth); + + dragAndDrop(secondEventBottomResize, -240); + dragAndDrop(firstEventBottomResize, -240); + assertWidths(noOverlapWidth, noOverlapWidth, noOverlapWidth); + + } + + private void assertWidths(int firstEventExpectedWidth, + int secondEventExpectedWidth, int thirdEventExpectedWidth) { + int widthTolerance = 5; + String errorMessage = "Wrong event width after resizing, expected [%d] (+/-%d), obtained [%d]"; + + int actualWidth = firstEvent.getSize().getWidth(); + int expectedWidth = firstEventExpectedWidth; + Assert.assertTrue(String.format(errorMessage, expectedWidth, + widthTolerance, actualWidth), + isAproximateWidth(actualWidth, expectedWidth, widthTolerance)); + + actualWidth = secondEvent.getSize().getWidth(); + expectedWidth = secondEventExpectedWidth; + Assert.assertTrue(String.format(errorMessage, expectedWidth, + widthTolerance, actualWidth), + isAproximateWidth(actualWidth, expectedWidth, widthTolerance)); + + actualWidth = thirdEvent.getSize().getWidth(); + expectedWidth = thirdEventExpectedWidth; + Assert.assertTrue(String.format(errorMessage, expectedWidth, + widthTolerance, actualWidth), + isAproximateWidth(actualWidth, expectedWidth, widthTolerance)); + } + + private boolean isAproximateWidth(int actualWidth, int expectedWidth, + int tolerance) { + return Math.abs(expectedWidth - actualWidth) <= tolerance; + } + + private void dragAndDrop(WebElement element, int yOffset) { + /* + * Selenium doesn't properly drag and drop items in IE8. It tries to + * start dragging an element from a position above the element itself. + */ + if (BrowserUtil.isIE8(getDesiredCapabilities())) { + Actions action = new Actions(getDriver()); + action.moveToElement(element); + action.moveByOffset(0, 1); + action.clickAndHold(); + action.moveByOffset(0, yOffset); + action.release(); + action.build().perform(); + } else { + Actions action = new Actions(getDriver()); + action.dragAndDropBy(element, 0, yOffset); + action.build().perform(); + } + } + + private void initParams() { + WebElement dateSlot = getDriver().findElement( + By.className("v-datecellslot")); + int dateSlotWidth = dateSlot.getSize().getWidth(); + noOverlapWidth = dateSlotWidth; + oneOverlapWidth = dateSlotWidth / 2; + twoOverlapsWidth = dateSlotWidth / 3; + + Comparator startTimeComparator = new Comparator() { + @Override + public int compare(WebElement e1, WebElement e2) { + int e1Top = e1.getLocation().getY(); + int e2Top = e2.getLocation().getY(); + return e1Top - e2Top; + } + }; + + List eventElements = getDriver().findElements( + By.className("v-calendar-event-content")); + Collections.sort(eventElements, startTimeComparator); + firstEvent = eventElements.get(0); + secondEvent = eventElements.get(1); + thirdEvent = eventElements.get(2); + + List resizeBottomElements = getDriver().findElements( + By.className("v-calendar-event-resizebottom")); + Collections.sort(resizeBottomElements, startTimeComparator); + firstEventBottomResize = resizeBottomElements.get(0); + secondEventBottomResize = resizeBottomElements.get(1); + thirdEventBottomResize = resizeBottomElements.get(2); + } +} -- cgit v1.2.3 From 087bf4215b76107d0b4e4c70e678ad7201270b12 Mon Sep 17 00:00:00 2001 From: Leif Åstrand Date: Wed, 25 Jun 2014 13:50:14 +0300 Subject: Don't complain about missing sync id for critical notifications (#14081) Change-Id: I351d256230a5cd674a5e1b8066d3e2aef07ff3bf --- client/src/com/vaadin/client/ApplicationConnection.java | 13 ++++++++++--- server/src/com/vaadin/server/VaadinService.java | 2 ++ 2 files changed, 12 insertions(+), 3 deletions(-) (limited to 'client/src') diff --git a/client/src/com/vaadin/client/ApplicationConnection.java b/client/src/com/vaadin/client/ApplicationConnection.java index 5d696f1ddb..8bc43dda9a 100644 --- a/client/src/com/vaadin/client/ApplicationConnection.java +++ b/client/src/com/vaadin/client/ApplicationConnection.java @@ -66,6 +66,7 @@ import com.google.gwt.user.client.Window.ClosingHandler; import com.google.gwt.user.client.ui.HasWidgets; import com.google.gwt.user.client.ui.Widget; import com.vaadin.client.ApplicationConfiguration.ErrorMessage; +import com.vaadin.client.ApplicationConnection.ApplicationStoppedEvent; import com.vaadin.client.ResourceLoader.ResourceLoadEvent; import com.vaadin.client.ResourceLoader.ResourceLoadListener; import com.vaadin.client.communication.HasJavaScriptConnectorHelper; @@ -1431,10 +1432,16 @@ public class ApplicationConnection implements HasHandlers { if (json.containsKey(ApplicationConstants.SERVER_SYNC_ID)) { int syncId = json.getInt(ApplicationConstants.SERVER_SYNC_ID); - assert (lastSeenServerSyncId == UNDEFINED_SYNC_ID || syncId == lastSeenServerSyncId + 1) : "Newly retrieved server sync id was not exactly one larger than the previous one (new: " - + syncId + ", last seen: " + lastSeenServerSyncId + ")"; + /* + * Use sync id unless explicitly set as undefined, as is done by + * e.g. critical server-side notifications + */ + if (syncId != -1) { + assert (lastSeenServerSyncId == UNDEFINED_SYNC_ID || syncId == lastSeenServerSyncId + 1) : "Newly retrieved server sync id was not exactly one larger than the previous one (new: " + + syncId + ", last seen: " + lastSeenServerSyncId + ")"; - lastSeenServerSyncId = syncId; + lastSeenServerSyncId = syncId; + } } else { VConsole.error("Server response didn't contain a sync id. " + "Please verify that the server is up-to-date and that the response data has not been modified in transmission."); diff --git a/server/src/com/vaadin/server/VaadinService.java b/server/src/com/vaadin/server/VaadinService.java index 08bc6f5c79..e8cdcd7055 100644 --- a/server/src/com/vaadin/server/VaadinService.java +++ b/server/src/com/vaadin/server/VaadinService.java @@ -59,6 +59,7 @@ import com.vaadin.server.communication.HeartbeatHandler; import com.vaadin.server.communication.PublishedFileHandler; import com.vaadin.server.communication.SessionRequestHandler; import com.vaadin.server.communication.UidlRequestHandler; +import com.vaadin.shared.ApplicationConstants; import com.vaadin.shared.JsonConstants; import com.vaadin.shared.ui.ui.UIConstants; import com.vaadin.ui.UI; @@ -1590,6 +1591,7 @@ public abstract class VaadinService implements Serializable { json.put("resources", new JSONObject()); json.put("locales", new JSONObject()); json.put("meta", meta); + json.put(ApplicationConstants.SERVER_SYNC_ID, -1); returnString = json.toString(); } catch (JSONException e) { getLogger().log(Level.WARNING, -- cgit v1.2.3 From 9a84fb14f2b7fc3c405f1446a007d5b9cd796c97 Mon Sep 17 00:00:00 2001 From: Manolo Carrasco Date: Wed, 30 Apr 2014 13:26:32 +0200 Subject: License Checker for vaadin cval products (#13696 #13474) - This patch includes four elements: 1.- A class able to validate a licensed product against Vaadin license server. It can be used in any vaadin product (thought for non addons like TB) just adding vaadin dependency, or copying the class. 2.- A class able to inspect all addons in the classpath and figure out, based on the MANIFEST.MF info, whether we have to check developer license. 3.- A modification to Vaadin connector generator to use the classes above and to stop compilation in case. 4.- A modification to ConnectorBundleLoader, so as when a new connector is instatiated, we check whether it is using an evaluation license and show a notice. We only show the notice once. - In addition to validating developer licenses, the checker caches the server response for using it when there are connection problems. - This stuff is in Vaadin core, so as we dont maintain license code in each addon. For checking an addon license we just add the license type to the manifest when packaging the artefact. - It checks expiration time, product name and major version. Fixes: #13696 In some-way related with: #13474 Change-Id: Ib61b1c2e9c3cacd463a1ce5db02c2cfbc06851c2 --- .classpath | 1 + .../ConnectorBundleLoaderFactory.java | 44 +- .../src/com/vaadin/tools/CvalAddonsChecker.java | 191 ++++++++ .../src/com/vaadin/tools/CvalChecker.java | 488 +++++++++++++++++++++ .../src/com/vaadin/tools/CvalChecker.properties | 13 + .../com/vaadin/tools/CvalAddonsCheckerTest.java | 184 ++++++++ .../tools/CvalAddonstCheckerUseCasesTest.java | 217 +++++++++ .../src/com/vaadin/tools/CvalCheckerTest.java | 342 +++++++++++++++ .../client/metadata/ConnectorBundleLoader.java | 51 ++- client/src/com/vaadin/client/metadata/Type.java | 17 +- 10 files changed, 1534 insertions(+), 14 deletions(-) create mode 100644 client-compiler/src/com/vaadin/tools/CvalAddonsChecker.java create mode 100644 client-compiler/src/com/vaadin/tools/CvalChecker.java create mode 100644 client-compiler/src/com/vaadin/tools/CvalChecker.properties create mode 100644 client-compiler/tests/src/com/vaadin/tools/CvalAddonsCheckerTest.java create mode 100644 client-compiler/tests/src/com/vaadin/tools/CvalAddonstCheckerUseCasesTest.java create mode 100644 client-compiler/tests/src/com/vaadin/tools/CvalCheckerTest.java (limited to 'client/src') diff --git a/.classpath b/.classpath index ccbd35cffb..62c06df6c8 100644 --- a/.classpath +++ b/.classpath @@ -6,6 +6,7 @@ + diff --git a/client-compiler/src/com/vaadin/server/widgetsetutils/ConnectorBundleLoaderFactory.java b/client-compiler/src/com/vaadin/server/widgetsetutils/ConnectorBundleLoaderFactory.java index cc1841ec05..5519dd1aae 100644 --- a/client-compiler/src/com/vaadin/server/widgetsetutils/ConnectorBundleLoaderFactory.java +++ b/client-compiler/src/com/vaadin/server/widgetsetutils/ConnectorBundleLoaderFactory.java @@ -1,12 +1,12 @@ /* * Copyright 2000-2014 Vaadin Ltd. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the @@ -48,6 +48,7 @@ import com.vaadin.client.JsArrayObject; import com.vaadin.client.ServerConnector; import com.vaadin.client.annotations.OnStateChange; import com.vaadin.client.metadata.ConnectorBundleLoader; +import com.vaadin.client.metadata.ConnectorBundleLoader.CValUiInfo; import com.vaadin.client.metadata.InvokationHandler; import com.vaadin.client.metadata.OnStateChangeMethod; import com.vaadin.client.metadata.ProxyHandler; @@ -70,6 +71,9 @@ import com.vaadin.shared.communication.ClientRpc; import com.vaadin.shared.communication.ServerRpc; import com.vaadin.shared.ui.Connect; import com.vaadin.shared.ui.Connect.LoadStyle; +import com.vaadin.tools.CvalAddonsChecker; +import com.vaadin.tools.CvalChecker; +import com.vaadin.tools.CvalChecker.InvalidCvalException; public class ConnectorBundleLoaderFactory extends Generator { /** @@ -211,6 +215,8 @@ public class ConnectorBundleLoaderFactory extends Generator { } + private CvalAddonsChecker cvalChecker = new CvalAddonsChecker(); + @Override public String generate(TreeLogger logger, GeneratorContext context, String typeName) throws UnableToCompleteException { @@ -231,7 +237,6 @@ public class ConnectorBundleLoaderFactory extends Generator { logger.log(Type.ERROR, getClass() + " failed", e); throw new UnableToCompleteException(); } - } private void generateClass(TreeLogger logger, GeneratorContext context, @@ -243,6 +248,23 @@ public class ConnectorBundleLoaderFactory extends Generator { return; } + List cvalInfos = null; + try { + if (cvalChecker != null) { + cvalInfos = cvalChecker.run(); + // Don't run twice + cvalChecker = null; + } + } catch (InvalidCvalException e) { + System.err.println("\n\n\n\n" + CvalChecker.LINE); + for (String line : e.getMessage().split("\n")) { + System.err.println(line); + } + System.err.println(CvalChecker.LINE + "\n\n\n\n"); + System.exit(1); + throw new UnableToCompleteException(); + } + List bundles = buildBundles(logger, context.getTypeOracle()); @@ -364,6 +386,18 @@ public class ConnectorBundleLoaderFactory extends Generator { w.println("});"); } + if (cvalInfos != null && !cvalInfos.isEmpty()) { + w.println("{"); + for (CValUiInfo c : cvalInfos) { + if ("evaluation".equals(c.type)) { + w.println("cvals.add(new CValUiInfo(\"" + c.product + + "\", \"" + c.version + "\", \"" + c.widgetset + + "\", null));"); + } + } + w.println("}"); + } + w.outdent(); w.println("}"); @@ -1101,7 +1135,7 @@ public class ConnectorBundleLoaderFactory extends Generator { * {@link ServerConnector} that have a @{@link Connect} annotation. It also * checks that multiple connectors aren't connected to the same server-side * class. - * + * * @param logger * the logger to which information can be logged * @param typeOracle diff --git a/client-compiler/src/com/vaadin/tools/CvalAddonsChecker.java b/client-compiler/src/com/vaadin/tools/CvalAddonsChecker.java new file mode 100644 index 0000000000..6780b1bc75 --- /dev/null +++ b/client-compiler/src/com/vaadin/tools/CvalAddonsChecker.java @@ -0,0 +1,191 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.tools; + +import static com.vaadin.tools.CvalChecker.LINE; +import static com.vaadin.tools.CvalChecker.computeMajorVersion; +import static com.vaadin.tools.CvalChecker.getErrorMessage; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; +import java.util.jar.Attributes; +import java.util.jar.JarFile; +import java.util.jar.Manifest; + +import com.vaadin.client.metadata.ConnectorBundleLoader.CValUiInfo; +import com.vaadin.tools.CvalChecker.CvalInfo; +import com.vaadin.tools.CvalChecker.CvalServer; +import com.vaadin.tools.CvalChecker.InvalidCvalException; +import com.vaadin.tools.CvalChecker.UnreachableCvalServerException; + +/** + * This class is able to visit all MANIFEST.MF files present in the classpath, + * filter by name, and check if the user has a valid license. + * + * Manifest files should have a few attributes indicating the license type of + * the addon: + *

    + *
  • Implementation-Version: 4.x.x + *
  • AdVaaName: addon_name + *
  • AdVaaLicen: cval, agpl, empty + *
  • AdVaaPkg: package of the widgets in this addon + *
+ * + * The class also have a method to check just one product. + */ +public final class CvalAddonsChecker { + + // Manifest attributes + public static final String VAADIN_ADDON_LICENSE = "AdVaaLicen"; + public static final String VAADIN_ADDON_NAME = "AdVaaName"; + public static final String VAADIN_ADDON_WIDGETSET = "Vaadin-Widgetsets"; + public static final String VAADIN_ADDON_VERSION = "Implementation-Version"; + public static final String VAADIN_ADDON_TITLE = "Implementation-Title"; + + // License types + public static final String VAADIN_AGPL = "agpl"; + public static final String VAADIN_CVAL = "cval"; + + private CvalChecker cvalChecker = new CvalChecker(); + private String filterPattern; + + /** + * The constructor. + */ + public CvalAddonsChecker() { + setLicenseProvider(new CvalServer()); + setFilter(".*vaadin.*"); + } + + /** + * Visit all MANIFEST.MF files in the classpath validating licenses. + * + * Return a list of Cval licensed products in order to have enough info to + * generate nag messages in the UI. + */ + public List run() throws InvalidCvalException { + List ret = new ArrayList(); + try { + // Visit all MANIFEST in our classpath + Enumeration manifests = Thread.currentThread() + .getContextClassLoader() + .getResources(JarFile.MANIFEST_NAME); + while (manifests.hasMoreElements()) { + try { + URL url = manifests.nextElement(); + // Discard manifests whose name does not match the filter + // pattern + if (!url.getPath().matches(filterPattern)) { + continue; + } + InputStream is = url.openStream(); + // Should never happen, but we don't want a NPE here + if (is == null) { + continue; + } + // Read manifest attributes + Manifest manifest = new Manifest(is); + Attributes attribs = manifest.getMainAttributes(); + String license = attribs.getValue(VAADIN_ADDON_LICENSE); + String name = attribs.getValue(VAADIN_ADDON_NAME); + String vers = attribs.getValue(VAADIN_ADDON_VERSION) == null ? "" + : attribs.getValue(VAADIN_ADDON_VERSION); + String title = attribs.getValue(VAADIN_ADDON_TITLE) == null ? name + : attribs.getValue(VAADIN_ADDON_TITLE); + + String widgetsets = attribs + .getValue(VAADIN_ADDON_WIDGETSET) == null ? name + : attribs.getValue(VAADIN_ADDON_WIDGETSET); + + if (name == null || license == null) { + continue; + } + if (VAADIN_AGPL.equals(license)) { + // For agpl version we print an info message + printAgplLicense(title, vers); + } else if (VAADIN_CVAL.equals(license)) { + // We only check cval licensed products + CvalInfo info; + try { + info = cvalChecker.validateProduct(name, vers, + title); + printValidLicense(info, title, vers); + } catch (UnreachableCvalServerException e) { + info = CvalChecker.parseJson("{'product':{'name':'" + + name + "'}}"); + printServerUnreachable(title, vers); + } + for (String w : widgetsets.split("[, ]+")) { + ret.add(new CValUiInfo(title, String + .valueOf(computeMajorVersion(vers)), w, + info.getType())); + } + } + } catch (IOException ignored) { + } + } + } catch (IOException ignored) { + } + return ret; + } + + /** + * Set the filter regexp of .jar names which we have to consider. + * + * default is '.*touchkit.*' + */ + public CvalAddonsChecker setFilter(String regexp) { + filterPattern = regexp; + return this; + } + + /* + * Change the license provider, only used in tests. + */ + protected CvalAddonsChecker setLicenseProvider(CvalServer p) { + cvalChecker.setLicenseProvider(p); + return this; + } + + private void printAgplLicense(String name, String version) { + System.out.println(LINE + "\n" + + getErrorMessage("agpl", name, computeMajorVersion(version)) + + "\n" + LINE); + } + + private void printServerUnreachable(String name, String version) { + System.out.println(LINE + + "\n" + + getErrorMessage("unreachable", name, + computeMajorVersion(version)) + "\n" + LINE); + } + + private void printValidLicense(CvalInfo info, String title, String version) { + String msg = info.getMessage(); + if (msg == null) { + String key = "evaluation".equals(info.getType()) ? "evaluation" + : "valid"; + msg = getErrorMessage(key, title, computeMajorVersion(version), + info.getLicensee()); + } + System.out.println("\n" + LINE + "\n" + msg + "\n" + LINE + "\n"); + } +} diff --git a/client-compiler/src/com/vaadin/tools/CvalChecker.java b/client-compiler/src/com/vaadin/tools/CvalChecker.java new file mode 100644 index 0000000000..5ad44a70f8 --- /dev/null +++ b/client-compiler/src/com/vaadin/tools/CvalChecker.java @@ -0,0 +1,488 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.tools; + +import static java.lang.Integer.parseInt; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; +import java.text.MessageFormat; +import java.util.Arrays; +import java.util.Date; +import java.util.Locale; +import java.util.ResourceBundle; +import java.util.prefs.Preferences; + +import org.apache.commons.io.IOUtils; +import org.json.JSONException; +import org.json.JSONObject; + +/** + * This class is able to validate the vaadin CVAL license. + * + * It reads the developer license file and asks the server to validate the + * licenseKey. If the license is invalid it throws an exception with the + * information about the problem and the server response. + */ +public final class CvalChecker { + + /* + * Class used for binding the JSON gotten from server. + * + * It is not in a separate f le, so as it is easier to copy into any product + * which does not depend on vaadin core. + * + * We are using org.json in order not to use additional dependency like + * auto-beans, gson, etc. + */ + public static class CvalInfo { + + public static class Product { + private JSONObject o; + + public Product(JSONObject o) { + this.o = o; + } + + public String getName() { + return get(o, "name", String.class); + } + + public Integer getVersion() { + return get(o, "version", Integer.class); + } + } + + @SuppressWarnings("unchecked") + private static T get(JSONObject o, String k, Class clz) { + Object ret = null; + try { + if (clz == String.class) { + ret = o.getString(k); + } else if (clz == JSONObject.class) { + ret = o.getJSONObject(k); + } else if (clz == Integer.class) { + ret = o.getInt(k); + } else if (clz == Date.class) { + ret = new Date(o.getLong(k)); + } else if (clz == Boolean.class) { + ret = o.getBoolean(k); + } + } catch (JSONException e) { + } + return (T) ret; + } + + private static T put(JSONObject o, String k, Object v) { + try { + o.put(k, v); + } catch (JSONException e) { + } + return null; + } + + private JSONObject o; + + private Product product; + + public CvalInfo(JSONObject o) { + this.o = o; + product = new Product(get(o, "product", JSONObject.class)); + } + + public Boolean getExpired() { + return get(o, "expired", Boolean.class); + } + + public Date getExpiredEpoch() { + return get(o, "expiredEpoch", Date.class); + } + + public String getLicensee() { + return get(o, "licensee", String.class); + } + + public String getLicenseKey() { + return get(o, "licenseKey", String.class); + } + + public String getMessage() { + return get(o, "message", String.class); + } + + public Product getProduct() { + return product; + } + + public String getType() { + return get(o, "type", String.class); + } + + public void setExpiredEpoch(Date expiredEpoch) { + put(o, "expiredEpoch", expiredEpoch.getTime()); + } + + public void setMessage(String msg) { + put(o, "message", msg); + } + + @Override + public String toString() { + return o.toString(); + } + + public boolean isLicenseExpired() { + return (getExpired() != null && getExpired()) + || (getExpiredEpoch() != null && getExpiredEpoch().before( + new Date())); + } + + public boolean isValidVersion(int majorVersion) { + return getProduct().getVersion() == null + || getProduct().getVersion() >= majorVersion; + + } + + private boolean isValidInfo(String name, String key) { + return getProduct() != null && getProduct().getName() != null + && getLicenseKey() != null + && getProduct().getName().equals(name) + && getLicenseKey().equals(key); + } + } + + /* + * The class with the method for getting json from server side. It is here + * and protected just for replacing it in tests. + */ + public static class CvalServer { + protected String licenseUrl = LICENSE_URL_PROD; + + String askServer(String productName, String productKey, int timeoutMs) + throws IOException { + String url = licenseUrl + productKey; + URLConnection con; + try { + // Send some additional info in the User-Agent string. + String ua = "Cval " + productName + " " + productKey + " " + + getFirstLaunch(); + for (String prop : Arrays.asList("java.vendor.url", + "java.version", "os.name", "os.version", "os.arch")) { + ua += " " + System.getProperty(prop, "-").replace(" ", "_"); + } + con = new URL(url).openConnection(); + con.setRequestProperty("User-Agent", ua); + con.setConnectTimeout(timeoutMs); + con.setReadTimeout(timeoutMs); + String r = IOUtils.toString(con.getInputStream()); + return r; + } catch (MalformedURLException e) { + e.printStackTrace(); + return null; + } + } + + /* + * Get the GWT firstLaunch timestamp. + */ + String getFirstLaunch() { + try { + Class clz = Class + .forName("com.google.gwt.dev.shell.CheckForUpdates"); + return Preferences.userNodeForPackage(clz).get("firstLaunch", + "-"); + } catch (ClassNotFoundException e) { + return "-"; + } + } + } + + /** + * Exception thrown when the user does not have a valid cval license. + */ + public static class InvalidCvalException extends Exception { + private static final long serialVersionUID = 1L; + public final CvalInfo info; + public final String name; + public final String key; + public final String version; + public final String title; + + public InvalidCvalException(String name, String version, String title, + String key, CvalInfo info) { + super(composeMessage(title, version, key, info)); + this.info = info; + this.name = name; + this.key = key; + this.version = version; + this.title = title; + } + + static String composeMessage(String title, String version, String key, + CvalInfo info) { + String msg = ""; + int majorVers = computeMajorVersion(version); + + if (info != null && info.getMessage() != null) { + msg = info.getMessage().replace("\\n", "\n"); + } else if (info != null && info.isLicenseExpired()) { + String type = "evaluation".equals(info.getType()) ? "Evaluation license" + : "License"; + msg = getErrorMessage("expired", title, majorVers, type); + } else if (key == null) { + msg = getErrorMessage("none", title, majorVers); + } else { + msg = getErrorMessage("invalid", title, majorVers); + } + return msg; + } + } + + /** + * Exception thrown when the license server is unreachable + */ + public static class UnreachableCvalServerException extends Exception { + private static final long serialVersionUID = 1L; + public final String name; + + public UnreachableCvalServerException(String name, Exception e) { + super(e); + this.name = name; + } + } + + public static final String LINE = "----------------------------------------------------------------------------------------------------------------------"; + + static final int GRACE_DAYS_MSECS = 2 * 24 * 60 * 60 * 1000; + + private static final String LICENSE_URL_PROD = "https://tools.vaadin.com/vaadin-license-server/licenses/"; + + /* + * used in tests + */ + static void cacheLicenseInfo(CvalInfo info) { + if (info != null) { + Preferences p = Preferences.userNodeForPackage(CvalInfo.class); + if (info.toString().length() > Preferences.MAX_VALUE_LENGTH) { + // This should never happen since MAX_VALUE_LENGTH is big + // enough. + // But server could eventually send a very big message, so we + // discard it in cache and would use hard-coded messages. + info.setMessage(null); + } + p.put(info.getProduct().getName(), info.toString()); + } + } + + /* + * used in tests + */ + static void deleteCache(String productName) { + Preferences p = Preferences.userNodeForPackage(CvalInfo.class); + p.remove(productName); + } + + /** + * Given a product name returns the name of the file with the license key. + * + * Traditionally we have delivered license keys with a name like + * 'vaadin.touchkit.developer.license' but our database product name is + * 'vaadin-touchkit' so we have to replace '-' by '.' to maintain + * compatibility. + */ + static final String computeLicenseName(String productName) { + return productName.replace("-", ".") + ".developer.license"; + } + + static final int computeMajorVersion(String productVersion) { + return productVersion == null || productVersion.isEmpty() ? 0 + : parseInt(productVersion.replaceFirst("[^\\d]+.*$", "")); + } + + /* + * used in tests + */ + static CvalInfo parseJson(String json) { + if (json == null) { + return null; + } + try { + JSONObject o = new JSONObject(json); + return new CvalInfo(o); + } catch (JSONException e) { + return null; + } + } + + private CvalServer provider; + + /** + * The constructor. + */ + public CvalChecker() { + setLicenseProvider(new CvalServer()); + } + + /** + * Validate whether there is a valid license key for a product. + * + * @param productName + * for example vaadin-touchkit + * @param productVersion + * for instance 4.0.1 + * @return CvalInfo Server response or cache response if server is offline + * @throws InvalidCvalException + * when there is no a valid license for the product + * @throws UnreachableCvalServerException + * when we have license key but server is unreachable + */ + public CvalInfo validateProduct(String productName, String productVersion, + String productTitle) throws InvalidCvalException, + UnreachableCvalServerException { + String key = getDeveloperLicenseKey(productName, productVersion, + productTitle); + + CvalInfo info = null; + if (key != null && !key.isEmpty()) { + info = getCachedLicenseInfo(productName); + if (info != null && !info.isValidInfo(productName, key)) { + deleteCache(productName); + info = null; + } + info = askLicenseServer(productName, key, productVersion, info); + if (info != null && info.isValidInfo(productName, key) + && info.isValidVersion(computeMajorVersion(productVersion)) + && !info.isLicenseExpired()) { + return info; + } + } + + throw new InvalidCvalException(productName, productVersion, + productTitle, key, info); + } + + /* + * Change the license provider, only used in tests. + */ + final CvalChecker setLicenseProvider(CvalServer p) { + provider = p; + return this; + } + + private CvalInfo askLicenseServer(String productName, String productKey, + String productVersion, CvalInfo info) + throws UnreachableCvalServerException { + + int majorVersion = computeMajorVersion(productVersion); + + // If we have a valid license info here, it means that we got it from + // cache. + // We add a grace time when so as if the server is unreachable + // we allow the user to use the product. + if (info != null && info.getExpiredEpoch() != null + && !"evaluation".equals(info.getType())) { + long ts = info.getExpiredEpoch().getTime() + GRACE_DAYS_MSECS; + info.setExpiredEpoch(new Date(ts)); + } + + boolean validCache = info != null + && info.isValidInfo(productName, productKey) + && info.isValidVersion(majorVersion) + && !info.isLicenseExpired(); + + // if we have a validCache we set the timeout smaller + int timeout = validCache ? 2000 : 10000; + + try { + CvalInfo srvinfo = parseJson(provider.askServer(productName + "-" + + productVersion, productKey, timeout)); + if (srvinfo != null && srvinfo.isValidInfo(productName, productKey) + && srvinfo.isValidVersion(majorVersion)) { + // We always cache the info if it is valid although it is + // expired + cacheLicenseInfo(srvinfo); + info = srvinfo; + } + } catch (FileNotFoundException e) { + // 404 + return null; + } catch (Exception e) { + if (info == null) { + throw new UnreachableCvalServerException(productName, e); + } + } + return info; + } + + private CvalInfo getCachedLicenseInfo(String productName) { + Preferences p = Preferences.userNodeForPackage(CvalInfo.class); + String json = p.get(productName, ""); + if (!json.isEmpty()) { + CvalInfo info = parseJson(json); + if (info != null) { + return info; + } + } + return null; + } + + private String getDeveloperLicenseKey(String productName, + String productVersion, String productTitle) + throws InvalidCvalException { + String licenseName = computeLicenseName(productName); + + String key = System.getProperty(licenseName); + if (key != null && !key.isEmpty()) { + return key; + } + + try { + String dotLicenseName = "." + licenseName; + String userHome = "file://" + System.getProperty("user.home") + "/"; + for (URL url : new URL[] { new URL(userHome + dotLicenseName), + new URL(userHome + licenseName), + URL.class.getResource("/" + dotLicenseName), + URL.class.getResource("/" + licenseName) }) { + + if (url != null) { + try { + key = IOUtils.toString(url.openStream()); + if (key != null && !(key = key.trim()).isEmpty()) { + return key; + } + } catch (IOException ignored) { + } + } + } + } catch (Exception e) { + e.printStackTrace(); + } + throw new InvalidCvalException(productName, productVersion, + productTitle, null, null); + } + + static String getErrorMessage(String key, Object... pars) { + Locale loc = Locale.getDefault(); + ResourceBundle res = ResourceBundle.getBundle( + CvalChecker.class.getName(), loc); + String msg = res.getString(key); + return new MessageFormat(msg, loc).format(pars); + } +} diff --git a/client-compiler/src/com/vaadin/tools/CvalChecker.properties b/client-compiler/src/com/vaadin/tools/CvalChecker.properties new file mode 100644 index 0000000000..3f4fd52cb7 --- /dev/null +++ b/client-compiler/src/com/vaadin/tools/CvalChecker.properties @@ -0,0 +1,13 @@ +expired={2} for {0} {1} has expired. Get a valid license at vaadin.com/pro + +none=License for {0} {1} not found. Go to vaadin.com/pro for more details. + +invalid=License for {0} {1} is not valid. Get a valid license from vaadin.com/pro + +unreachable=License for {0} {1} has not been validated. Check your network connection. + +evaluation= > Using an evaluation license for {0} {1}. + +valid= > Using a valid license for {0} {1}. + +agpl=Using AGPL version of {0} {1}. Commercial licensing options available at vaadin.com/pro \ No newline at end of file diff --git a/client-compiler/tests/src/com/vaadin/tools/CvalAddonsCheckerTest.java b/client-compiler/tests/src/com/vaadin/tools/CvalAddonsCheckerTest.java new file mode 100644 index 0000000000..1fb9413ee4 --- /dev/null +++ b/client-compiler/tests/src/com/vaadin/tools/CvalAddonsCheckerTest.java @@ -0,0 +1,184 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.tools; + +import static com.vaadin.tools.CvalAddonsChecker.VAADIN_AGPL; +import static com.vaadin.tools.CvalAddonsChecker.VAADIN_CVAL; +import static com.vaadin.tools.CvalChecker.GRACE_DAYS_MSECS; +import static com.vaadin.tools.CvalChecker.computeLicenseName; +import static com.vaadin.tools.CvalChecker.deleteCache; +import static com.vaadin.tools.CvalCheckerTest.VALID_KEY; +import static com.vaadin.tools.CvalCheckerTest.addLicensedJarToClasspath; +import static com.vaadin.tools.CvalCheckerTest.cacheExists; +import static com.vaadin.tools.CvalCheckerTest.captureSystemOut; +import static com.vaadin.tools.CvalCheckerTest.productNameAgpl; +import static com.vaadin.tools.CvalCheckerTest.productNameApache; +import static com.vaadin.tools.CvalCheckerTest.productNameCval; +import static com.vaadin.tools.CvalCheckerTest.readSystemOut; +import static com.vaadin.tools.CvalCheckerTest.saveCache; +import static com.vaadin.tools.CvalCheckerTest.unreachableLicenseProvider; +import static com.vaadin.tools.CvalCheckerTest.validLicenseProvider; + +import java.util.List; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import com.vaadin.client.metadata.ConnectorBundleLoader.CValUiInfo; +import com.vaadin.tools.CvalChecker.InvalidCvalException; + +/** + * The CvalAddonsChecker test. + */ +public class CvalAddonsCheckerTest { + + CvalAddonsChecker addonChecker; + private String licenseName; + + @Before + public void setup() { + addonChecker = new CvalAddonsChecker().setLicenseProvider( + validLicenseProvider).setFilter(".*test.*"); + licenseName = computeLicenseName(productNameCval); + + deleteCache(productNameCval); + System.getProperties().remove(licenseName); + } + + @Test + public void testRunChecker() throws Exception { + // Create a product .jar with a cval license non required and add to our + // classpath + addLicensedJarToClasspath(productNameCval, VAADIN_CVAL); + // Remove other products in case other tests added them previously + addLicensedJarToClasspath(productNameAgpl, null); + addLicensedJarToClasspath(productNameApache, null); + + // No license + // -> Break compilation + System.getProperties().remove(licenseName); + addonChecker.setLicenseProvider(validLicenseProvider); + try { + addonChecker.run(); + Assert.fail(); + } catch (InvalidCvalException expected) { + } + Assert.assertFalse(cacheExists(productNameCval)); + + // We have a license that has never been validated from the server and + // we are offline + // -> Show a message on compile time (“Your license for TouchKit 4 has + // not been validated.”) + System.setProperty(licenseName, VALID_KEY); + addonChecker.setLicenseProvider(unreachableLicenseProvider); + captureSystemOut(); + addonChecker.run(); + Assert.assertTrue(readSystemOut().contains("has not been validated")); + Assert.assertFalse(cacheExists(productNameCval)); + + // Valid license has previously been validated from the server and we + // are offline + // -> Use the cached server response + System.setProperty(licenseName, VALID_KEY); + addonChecker.setLicenseProvider(validLicenseProvider); + captureSystemOut(); + addonChecker.run(); + Assert.assertTrue(cacheExists(productNameCval)); + addonChecker.setLicenseProvider(unreachableLicenseProvider); + addonChecker.run(); + + // Expired license and we are offline + // -> If it has expired less than 14 days ago, just work with no nag + // messages + System.setProperty(licenseName, VALID_KEY); + addonChecker.setLicenseProvider(unreachableLicenseProvider); + setCacheFileTs(System.currentTimeMillis() - (GRACE_DAYS_MSECS / 2), + "normal"); + captureSystemOut(); + addonChecker.run(); + + // Expired license and we are offline + // -> After 14 days, interpret it as expired license + setCacheFileTs(System.currentTimeMillis() - (GRACE_DAYS_MSECS * 2), + "normal"); + try { + addonChecker.run(); + Assert.fail(); + } catch (InvalidCvalException expected) { + } + + // Invalid evaluation license + // -> Fail compilation with a message + // "Your evaluation license for TouchKit 4 is not valid" + System.setProperty(licenseName, VALID_KEY); + addonChecker.setLicenseProvider(unreachableLicenseProvider); + setCacheFileTs(System.currentTimeMillis() - (GRACE_DAYS_MSECS / 2), + "evaluation"); + try { + addonChecker.run(); + Assert.fail(); + } catch (InvalidCvalException expected) { + Assert.assertTrue(expected.getMessage().contains("expired")); + } + + // Valid evaluation license + // -> The choice on whether to show the message is generated in + // widgetset + // compilation phase. No license checks are done in application runtime. + System.setProperty(licenseName, VALID_KEY); + addonChecker.setLicenseProvider(unreachableLicenseProvider); + setCacheFileTs(System.currentTimeMillis() + GRACE_DAYS_MSECS, + "evaluation"); + List uiInfo = addonChecker.run(); + Assert.assertEquals(1, uiInfo.size()); + Assert.assertEquals("Test " + productNameCval, uiInfo.get(0).product); + Assert.assertEquals("evaluation", uiInfo.get(0).type); + + // Valid real license + // -> Work as expected + // -> Show info message “Using TouchKit 4 license + // 312-312321-321312-3-12-312-312 + // licensed to (1 developer license)” + System.setProperty(licenseName, VALID_KEY); + addonChecker.setLicenseProvider(validLicenseProvider); + captureSystemOut(); + addonChecker.run(); + Assert.assertTrue(readSystemOut().contains("valid")); + } + + @Test + public void validateMultipleLicenses() throws Exception { + addLicensedJarToClasspath(productNameCval, VAADIN_CVAL); + addLicensedJarToClasspath(productNameAgpl, VAADIN_AGPL); + addLicensedJarToClasspath(productNameApache, "apache"); + + // We have a valid license for all products + System.setProperty(licenseName, VALID_KEY); + captureSystemOut(); + addonChecker.run(); + String out = readSystemOut(); + Assert.assertTrue(out.contains("valid")); + Assert.assertTrue(out.contains("AGPL")); + Assert.assertTrue(cacheExists(productNameCval)); + } + + private void setCacheFileTs(long expireTs, String type) { + saveCache(productNameCval, null, false, expireTs, type); + } + +} diff --git a/client-compiler/tests/src/com/vaadin/tools/CvalAddonstCheckerUseCasesTest.java b/client-compiler/tests/src/com/vaadin/tools/CvalAddonstCheckerUseCasesTest.java new file mode 100644 index 0000000000..89c8fc1f81 --- /dev/null +++ b/client-compiler/tests/src/com/vaadin/tools/CvalAddonstCheckerUseCasesTest.java @@ -0,0 +1,217 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.tools; + +import static com.vaadin.tools.CvalAddonsChecker.VAADIN_AGPL; +import static com.vaadin.tools.CvalAddonsChecker.VAADIN_CVAL; +import static com.vaadin.tools.CvalChecker.computeLicenseName; +import static com.vaadin.tools.CvalChecker.deleteCache; +import static com.vaadin.tools.CvalCheckerTest.INVALID_KEY; +import static com.vaadin.tools.CvalCheckerTest.VALID_KEY; +import static com.vaadin.tools.CvalCheckerTest.addLicensedJarToClasspath; +import static com.vaadin.tools.CvalCheckerTest.cachedPreferences; +import static com.vaadin.tools.CvalCheckerTest.captureSystemOut; +import static com.vaadin.tools.CvalCheckerTest.expiredLicenseProvider; +import static com.vaadin.tools.CvalCheckerTest.productNameAgpl; +import static com.vaadin.tools.CvalCheckerTest.productNameCval; +import static com.vaadin.tools.CvalCheckerTest.readSystemOut; +import static com.vaadin.tools.CvalCheckerTest.restoreSystemOut; +import static com.vaadin.tools.CvalCheckerTest.saveCache; +import static com.vaadin.tools.CvalCheckerTest.unreachableLicenseProvider; +import static com.vaadin.tools.CvalCheckerTest.validEvaluationLicenseProvider; +import static com.vaadin.tools.CvalCheckerTest.validLicenseProvider; + +import org.junit.Assert; +import org.junit.Test; + +import com.vaadin.tools.CvalChecker.CvalServer; + +/** + * Tests for Use Cases + */ +public class CvalAddonstCheckerUseCasesTest { + + enum License { + NONE, EVAL, INVALID, REAL, EVAL_EXPIRED, REAL_EXPIRED + }; + + enum Version { + AGPL, CVAL + }; + + enum Validated { + YES, NO, OLD_KEY + }; + + enum Network { + ON, OFF + }; + + enum Compile { + YES, NO + }; + + enum Cached { + YES, NO + }; + + enum Message { + AGPL("AGPL"), VALID(">.* valid"), INVALID("not valid"), NO_LICENSE( + "not found"), NO_VALIDATED("has not been validated"), EXPIRED( + "has expired"), EVALUATION("evaluation"); + + String msg; + + Message(String s) { + msg = s; + } + } + + @Test + public void testUseCases() throws Exception { + useCase(1, License.NONE, Version.AGPL, Validated.NO, Network.OFF, + Compile.YES, Cached.NO, Message.AGPL); + + useCase(2, License.NONE, Version.CVAL, Validated.NO, Network.ON, + Compile.NO, Cached.NO, Message.NO_LICENSE); + + useCase(3, License.NONE, Version.CVAL, Validated.NO, Network.OFF, + Compile.NO, Cached.NO, Message.NO_LICENSE); + + useCase(4, License.EVAL, Version.CVAL, Validated.NO, Network.ON, + Compile.YES, Cached.YES, Message.EVALUATION); + + useCase(5, License.INVALID, Version.CVAL, Validated.NO, Network.OFF, + Compile.YES, Cached.NO, Message.NO_VALIDATED); + + useCase(6, License.INVALID, Version.CVAL, Validated.NO, Network.ON, + Compile.NO, Cached.NO, Message.INVALID); + + useCase(7, License.REAL, Version.CVAL, Validated.NO, Network.ON, + Compile.YES, Cached.YES, Message.VALID); + + useCase(8, License.REAL, Version.CVAL, Validated.NO, Network.OFF, + Compile.YES, Cached.NO, Message.NO_VALIDATED); + + useCase(9, License.REAL, Version.CVAL, Validated.YES, Network.OFF, + Compile.YES, Cached.YES, Message.VALID); + + useCase(10, License.EVAL_EXPIRED, Version.CVAL, Validated.NO, + Network.ON, Compile.NO, Cached.YES, Message.EXPIRED); + + useCase(11, License.EVAL_EXPIRED, Version.CVAL, Validated.YES, + Network.OFF, Compile.NO, Cached.YES, Message.EXPIRED); + + useCase(12, License.REAL_EXPIRED, Version.CVAL, Validated.YES, + Network.OFF, Compile.NO, Cached.YES, Message.EXPIRED); + + useCase(13, License.REAL_EXPIRED, Version.CVAL, Validated.NO, + Network.ON, Compile.NO, Cached.YES, Message.EXPIRED); + + useCase(14, License.INVALID, Version.CVAL, Validated.OLD_KEY, + Network.OFF, Compile.YES, Cached.NO, Message.NO_VALIDATED); + } + + @Test + public void testMultipleLicenseUseCases() throws Exception { + addLicensedJarToClasspath("test.foo", VAADIN_CVAL); + System.setProperty(computeLicenseName("test.foo"), VALID_KEY); + + useCase(15, License.REAL, Version.CVAL, Validated.YES, Network.OFF, + Compile.YES, Cached.YES, Message.NO_VALIDATED); + + useCase(16, License.REAL, Version.CVAL, Validated.YES, Network.ON, + Compile.NO, Cached.YES, Message.INVALID); + } + + private void useCase(int number, License lic, Version ver, Validated val, + Network net, Compile res, Cached cached, Message msg) + throws Exception { + + if (ver == Version.AGPL) { + addLicensedJarToClasspath(productNameAgpl, VAADIN_AGPL); + addLicensedJarToClasspath(productNameCval, null); + } else { + addLicensedJarToClasspath(productNameAgpl, null); + addLicensedJarToClasspath(productNameCval, VAADIN_CVAL); + } + + String licenseName = computeLicenseName(productNameCval); + + if (lic == License.NONE) { + System.getProperties().remove(licenseName); + } else if (lic == License.INVALID) { + System.setProperty(licenseName, INVALID_KEY); + } else { + System.setProperty(licenseName, VALID_KEY); + } + + if (val == Validated.NO) { + deleteCache(productNameCval); + } else { + String type = lic == License.EVAL || lic == License.EVAL_EXPIRED ? "evaluation" + : null; + Boolean expired = lic == License.EVAL_EXPIRED + || lic == License.REAL_EXPIRED ? true : null; + String key = val == Validated.OLD_KEY ? "oldkey" : null; + saveCache(productNameCval, key, expired, null, type); + } + + CvalServer licenseProvider = validLicenseProvider; + if (net == Network.OFF) { + licenseProvider = unreachableLicenseProvider; + } else if (lic == License.EVAL_EXPIRED || lic == License.REAL_EXPIRED) { + licenseProvider = expiredLicenseProvider; + } else if (lic == License.EVAL) { + licenseProvider = validEvaluationLicenseProvider; + } + + CvalAddonsChecker addonChecker = new CvalAddonsChecker(); + addonChecker.setLicenseProvider(licenseProvider).setFilter(".*test.*"); + + captureSystemOut(); + + String testNumber = "Test #" + number + " "; + String message; + try { + addonChecker.run(); + message = readSystemOut(); + if (res == Compile.NO) { + Assert.fail(testNumber + "Exception not thrown:" + message); + } + } catch (Exception e) { + restoreSystemOut(); + message = e.getMessage(); + if (res == Compile.YES) { + Assert.fail(testNumber + "Unexpected Exception: " + + e.getMessage()); + } + } + + // System.err.println("\n> " + testNumber + " " + lic + " " + ver + " " + // + val + " " + net + " " + res + " " + cached + "\n" + message); + + Assert.assertTrue(testNumber + "Fail:\n" + message + + "\nDoes not match:" + msg.msg, + message.matches("(?s).*" + msg.msg + ".*")); + + String c = cachedPreferences(productNameCval); + Assert.assertTrue(testNumber + "Fail: cacheExists != " + + (cached == Cached.YES) + "\n " + c, + (c != null) == (cached == Cached.YES)); + } +} diff --git a/client-compiler/tests/src/com/vaadin/tools/CvalCheckerTest.java b/client-compiler/tests/src/com/vaadin/tools/CvalCheckerTest.java new file mode 100644 index 0000000000..51b12f4c7e --- /dev/null +++ b/client-compiler/tests/src/com/vaadin/tools/CvalCheckerTest.java @@ -0,0 +1,342 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.tools; + +import static com.vaadin.tools.CvalAddonsChecker.VAADIN_ADDON_LICENSE; +import static com.vaadin.tools.CvalAddonsChecker.VAADIN_ADDON_NAME; +import static com.vaadin.tools.CvalAddonsChecker.VAADIN_ADDON_TITLE; +import static com.vaadin.tools.CvalAddonsChecker.VAADIN_ADDON_VERSION; +import static com.vaadin.tools.CvalChecker.GRACE_DAYS_MSECS; +import static com.vaadin.tools.CvalChecker.cacheLicenseInfo; +import static com.vaadin.tools.CvalChecker.deleteCache; +import static com.vaadin.tools.CvalChecker.parseJson; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; +import java.util.prefs.Preferences; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import com.vaadin.tools.CvalChecker.CvalInfo; +import com.vaadin.tools.CvalChecker.CvalServer; +import com.vaadin.tools.CvalChecker.InvalidCvalException; +import com.vaadin.tools.CvalChecker.UnreachableCvalServerException; + +/** + * The CvalChecker test. + */ +public class CvalCheckerTest { + + static final String productNameCval = "test.cval"; + static final String productTitleCval = "Vaadin Test"; + static final String productNameAgpl = "test.agpl"; + static final String productTitleAgpl = "Vaadin Test"; + static final String productNameApache = "test.apache"; + static final String VALID_KEY = "valid"; + static final String INVALID_KEY = "invalid"; + + static final String responseJson = "{'licenseKey':'" + VALID_KEY + "'," + + "'licensee':'Test User','type':'normal'," + + "'expiredEpoch':'1893511225000'," + "'product':{'name':'" + + productNameCval + "', 'version': 2}}"; + + private static ByteArrayOutputStream outContent; + + // A provider returning a valid license if productKey is valid or null if + // invalid + static final CvalServer validLicenseProvider = new CvalServer() { + @Override + String askServer(String productName, String productKey, int timeout) { + return VALID_KEY.equals(productKey) ? responseJson : null; + } + }; + // A provider returning a valid evaluation license + static final CvalServer validEvaluationLicenseProvider = new CvalServer() { + @Override + String askServer(String productName, String productKey, int timeout) { + return responseJson.replace("normal", "evaluation"); + } + }; + // A provider returning an expired license with a server message + static final CvalServer expiredLicenseProviderWithMessage = new CvalServer() { + @Override + String askServer(String productName, String productKey, int timeout) { + return responseJson + .replace("'expired", + "'message':'Custom\\\\nServer\\\\nMessage','expired':true,'expired"); + } + }; + // A provider returning an expired license with a server message + static final CvalServer expiredLicenseProvider = new CvalServer() { + @Override + String askServer(String productName, String productKey, int timeout) { + return responseJson.replace("'expired", "'expired':true,'expired"); + } + }; + // A provider returning an expired epoch license + static final CvalServer expiredEpochLicenseProvider = new CvalServer() { + @Override + String askServer(String productName, String productKey, int timeout) { + long ts = System.currentTimeMillis() - GRACE_DAYS_MSECS - 1000; + return responseJson.replace("1893511225000", "" + ts); + } + }; + // A provider returning an unlimited license + static final CvalServer unlimitedLicenseProvider = new CvalServer() { + @Override + String askServer(String productName, String productKey, int timeout) { + return responseJson.replaceFirst("1893511225000", ""); + } + }; + // An unreachable provider + static final CvalServer unreachableLicenseProvider = new CvalServer() { + @Override + String askServer(String productName, String productKey, int timeout) + throws IOException { + // Normally there is no route for this ip in public routers, so we + // should get a timeout. + licenseUrl = "http://localhost:9999/"; + return super.askServer(productName, productKey, 1000); + } + }; + + private CvalChecker licenseChecker; + private String licenseName; + + @Before + public void setup() { + licenseChecker = new CvalChecker() + .setLicenseProvider(validLicenseProvider); + licenseName = CvalChecker.computeLicenseName(productNameCval); + System.getProperties().remove(licenseName); + deleteCache(productNameCval); + } + + @Test + public void testValidateProduct() throws Exception { + deleteCache(productNameCval); + + // If the license key in our environment is null, throw an exception + try { + licenseChecker.validateProduct(productNameCval, "2.1", + productTitleCval); + Assert.fail(); + } catch (InvalidCvalException expected) { + Assert.assertEquals(productNameCval, expected.name); + } + Assert.assertFalse(cacheExists(productNameCval)); + + // If the license key is empty, throw an exception + System.setProperty(licenseName, ""); + try { + licenseChecker.validateProduct(productNameCval, "2.1", + productTitleCval); + Assert.fail(); + } catch (InvalidCvalException expected) { + Assert.assertEquals(productNameCval, expected.name); + } + Assert.assertFalse(cacheExists(productNameCval)); + + // If license key is invalid, throw an exception + System.setProperty(licenseName, "invalid"); + try { + licenseChecker.validateProduct(productNameCval, "2.1", + productTitleCval); + Assert.fail(); + } catch (InvalidCvalException expected) { + Assert.assertEquals(productNameCval, expected.name); + } + Assert.assertFalse(cacheExists(productNameCval)); + + // Fail if version is bigger + System.setProperty(licenseName, VALID_KEY); + try { + licenseChecker.validateProduct(productNameCval, "3.0", + productTitleCval); + Assert.fail(); + } catch (InvalidCvalException expected) { + Assert.assertEquals(productNameCval, expected.name); + } + Assert.assertFalse(cacheExists(productNameCval)); + + // Success if license key and version are valid + System.setProperty(licenseName, VALID_KEY); + licenseChecker + .validateProduct(productNameCval, "2.1", productTitleCval); + Assert.assertTrue(cacheExists(productNameCval)); + + // Success if license and cache file are valid, although the license + // server is offline + licenseChecker.setLicenseProvider(unreachableLicenseProvider); + licenseChecker + .validateProduct(productNameCval, "2.1", productTitleCval); + Assert.assertTrue(cacheExists(productNameCval)); + + // Fail if license key changes although cache file were validated + // previously and it is ok, we are offline + try { + System.setProperty(licenseName, INVALID_KEY); + licenseChecker.validateProduct(productNameCval, "2.1", + productTitleCval); + Assert.fail(); + } catch (InvalidCvalException expected) { + Assert.fail(); + } catch (UnreachableCvalServerException expected) { + Assert.assertEquals(productNameCval, expected.name); + } + Assert.assertFalse(cacheExists(productNameCval)); + + // Fail with unreachable exception if license has never verified and + // server is offline + try { + System.setProperty(licenseName, VALID_KEY); + licenseChecker.validateProduct(productNameCval, "2.1", + productTitleCval); + Assert.fail(); + } catch (InvalidCvalException expected) { + Assert.fail(); + } catch (UnreachableCvalServerException expected) { + Assert.assertEquals(productNameCval, expected.name); + } + Assert.assertFalse(cacheExists(productNameCval)); + + // Fail when expired flag comes in the server response, although the + // expired is valid. + deleteCache(productNameCval); + licenseChecker.setLicenseProvider(expiredLicenseProviderWithMessage); + try { + licenseChecker.validateProduct(productNameCval, "2.1", + productTitleCval); + Assert.fail(); + } catch (InvalidCvalException expected) { + Assert.assertEquals(productNameCval, expected.name); + // Check that we use server customized message if it comes + Assert.assertTrue(expected.getMessage().contains("Custom")); + } + Assert.assertTrue(cacheExists(productNameCval)); + + // Check an unlimited license + licenseChecker.setLicenseProvider(unlimitedLicenseProvider); + licenseChecker + .validateProduct(productNameCval, "2.1", productTitleCval); + Assert.assertTrue(cacheExists(productNameCval)); + + // Fail if expired flag does not come, but expired epoch is in the past + System.setProperty(licenseName, VALID_KEY); + deleteCache(productNameCval); + licenseChecker.setLicenseProvider(expiredEpochLicenseProvider); + try { + licenseChecker.validateProduct(productNameCval, "2.1", + productTitleCval); + Assert.fail(); + } catch (InvalidCvalException expected) { + Assert.assertEquals(productNameCval, expected.name); + } + Assert.assertTrue(cacheExists(productNameCval)); + } + + /* + * Creates a new .jar file with a MANIFEST.MF with all vaadin license info + * attributes set, and add the .jar to the classpath + */ + static void addLicensedJarToClasspath(String productName, String licenseType) + throws Exception { + // Create a manifest with Vaadin CVAL license + Manifest testManifest = new Manifest(); + testManifest.getMainAttributes().putValue("Manifest-Version", "1.0"); + testManifest.getMainAttributes().putValue(VAADIN_ADDON_LICENSE, + licenseType); + testManifest.getMainAttributes().putValue(VAADIN_ADDON_NAME, + productName); + testManifest.getMainAttributes().putValue(VAADIN_ADDON_TITLE, + "Test " + productName); + testManifest.getMainAttributes().putValue(VAADIN_ADDON_VERSION, "2"); + + // Create a temporary Jar + String tmpDir = System.getProperty("java.io.tmpdir"); + File testJarFile = new File(tmpDir + "vaadin." + productName + ".jar"); + testJarFile.deleteOnExit(); + JarOutputStream target = new JarOutputStream(new FileOutputStream( + testJarFile), testManifest); + target.close(); + + // Add the new jar to our classpath (use reflection) + URL url = new URL("file://" + testJarFile.getAbsolutePath()); + final Method addURL = URLClassLoader.class.getDeclaredMethod("addURL", + new Class[] { URL.class }); + addURL.setAccessible(true); + final URLClassLoader urlClassLoader = (URLClassLoader) Thread + .currentThread().getContextClassLoader(); + addURL.invoke(urlClassLoader, new Object[] { url }); + } + + static boolean cacheExists(String productName) { + return cachedPreferences(productName) != null; + } + + static String cachedPreferences(String productName) { + // ~/Library/Preferences/com.apple.java.util.prefs.plist + // .java/.userPrefs/com/google/gwt/dev/shell/prefs.xml + // HKEY_CURRENT_USER\SOFTWARE\JavaSoft\Prefs + Preferences p = Preferences.userNodeForPackage(CvalInfo.class); + return p.get(productName, null); + } + + static void saveCache(String productName, String key, Boolean expired, + Long expireTs, String type) { + String json = responseJson.replace(productNameCval, productName); + if (expired != null && expired) { + expireTs = System.currentTimeMillis() - GRACE_DAYS_MSECS - 1000; + } + if (expireTs != null) { + json = json.replace("1893511225000", "" + expireTs); + } + if (key != null) { + json = json.replace(VALID_KEY, key); + } + if (type != null) { + json = json.replace("normal", type); + + } + cacheLicenseInfo(parseJson(json)); + } + + static void captureSystemOut() { + outContent = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outContent)); + } + + static String readSystemOut() { + restoreSystemOut(); + return outContent.toString(); + } + + static void restoreSystemOut() { + System.setOut(new PrintStream(new FileOutputStream(FileDescriptor.out))); + } +} diff --git a/client/src/com/vaadin/client/metadata/ConnectorBundleLoader.java b/client/src/com/vaadin/client/metadata/ConnectorBundleLoader.java index 7d2078061e..846bfd4671 100644 --- a/client/src/com/vaadin/client/metadata/ConnectorBundleLoader.java +++ b/client/src/com/vaadin/client/metadata/ConnectorBundleLoader.java @@ -1,12 +1,12 @@ /* * Copyright 2000-2014 Vaadin Ltd. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the @@ -15,13 +15,35 @@ */ package com.vaadin.client.metadata; +import java.util.ArrayList; import java.util.List; import com.google.gwt.core.shared.GWT; +import com.google.gwt.user.client.ui.HTML; +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.client.ApplicationConfiguration; import com.vaadin.client.FastStringMap; import com.vaadin.client.metadata.AsyncBundleLoader.State; +import com.vaadin.client.ui.VNotification; +import com.vaadin.shared.Position; public abstract class ConnectorBundleLoader { + + public static class CValUiInfo { + public final String widgetset; + public final String product; + public final String version; + public final String type; + + public CValUiInfo(String product, String version, String widgetset, + String type) { + this.product = product; + this.version = version; + this.widgetset = widgetset; + this.type = type; + } + } + public static final String EAGER_BUNDLE_NAME = "__eager"; public static final String DEFERRED_BUNDLE_NAME = "__deferred"; @@ -113,4 +135,27 @@ public abstract class ConnectorBundleLoader { public abstract void init(); + protected List cvals = new ArrayList(); + + public void cval(String typeName) { + if (!cvals.isEmpty()) { + String msg = ""; + for (CValUiInfo c : cvals) { + String ns = c.widgetset.replaceFirst("\\.[^\\.]+$", ""); + if (typeName.startsWith(ns)) { + cvals.remove(c); + msg += c.product + " " + c.version + "
"; + } + } + if (!msg.isEmpty()) { + // We need a widget for using VNotification, using the + // context-menu parent. Is there an easy way? + Widget w = ApplicationConfiguration.getRunningApplications() + .get(0).getContextMenu().getParent(); + VNotification n = VNotification.createNotification(0, w); + n.setWidget(new HTML("Using Evaluation License of:
" + msg)); + n.show(Position.BOTTOM_RIGHT); + } + } + } } diff --git a/client/src/com/vaadin/client/metadata/Type.java b/client/src/com/vaadin/client/metadata/Type.java index cc185dff96..15617ffe3c 100644 --- a/client/src/com/vaadin/client/metadata/Type.java +++ b/client/src/com/vaadin/client/metadata/Type.java @@ -1,12 +1,12 @@ /* * Copyright 2000-2014 Vaadin Ltd. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the @@ -19,6 +19,7 @@ import java.util.Collection; import com.google.gwt.core.client.JsArrayString; import com.vaadin.client.JsArrayObject; +import com.vaadin.client.ServerConnector; import com.vaadin.client.communication.JSONSerializer; public class Type { @@ -47,7 +48,11 @@ public class Type { public Object createInstance() throws NoDataException { Invoker invoker = TypeDataStore.getConstructor(this); - return invoker.invoke(null); + Object ret = invoker.invoke(null); + if (ret instanceof ServerConnector) { + ConnectorBundleLoader.get().cval(name); + } + return ret; } public Method getMethod(String name) { @@ -57,7 +62,7 @@ public class Type { /** * @return * @throws NoDataException - * + * * @deprecated As of 7.0.1, use {@link #getPropertiesAsArray()} instead for * improved performance */ @@ -96,7 +101,7 @@ public class Type { * returned string may change without notice and should not be used for any * other purpose than identification. The signature is currently based on * the fully qualified name of the type. - * + * * @return the unique signature of this type */ public String getSignature() { -- cgit v1.2.3