import com.vaadin.shared.EventId;
import com.vaadin.shared.ui.ComponentStateUtil;
import com.vaadin.shared.ui.combobox.FilteringMode;
+import com.vaadin.shared.util.SharedUtil;
/**
* Client side implementation of the Select component.
private final String key;
private final String caption;
- private String iconUri;
+ private String untranslatedIconUri;
/**
* Constructor
key = uidl.getStringAttribute("key");
caption = uidl.getStringAttribute("caption");
if (uidl.hasAttribute("icon")) {
- iconUri = client.translateVaadinUri(uidl
- .getStringAttribute("icon"));
+ untranslatedIconUri = uidl.getStringAttribute("icon");
}
}
@Override
public String getDisplayString() {
final StringBuffer sb = new StringBuffer();
- final Icon icon = client.getIcon(iconUri);
+ final Icon icon = client.getIcon(client
+ .translateVaadinUri(untranslatedIconUri));
if (icon != null) {
sb.append(icon.getElement().getString());
}
* @return
*/
public String getIconUri() {
- return iconUri;
+ return client.translateVaadinUri(untranslatedIconUri);
}
/**
|| (caption != null && !caption.equals(other.caption))) {
return false;
}
- if ((iconUri == null && other.iconUri != null)
- || (iconUri != null && !iconUri.equals(other.iconUri))) {
+ if (!SharedUtil.equals(untranslatedIconUri,
+ other.untranslatedIconUri)) {
return false;
}
return true;
actionMap.put(key + "_c", caption);
if (action.hasAttribute("icon")) {
// TODO need some uri handling ??
- actionMap.put(key + "_i", client.translateVaadinUri(action
- .getStringAttribute("icon")));
+ actionMap.put(key + "_i", action.getStringAttribute("icon"));
} else {
actionMap.remove(key + "_i");
}
}
public String getActionIcon(String actionKey) {
- return actionMap.get(actionKey + "_i");
+ return client.translateVaadinUri(actionMap.get(actionKey + "_i"));
}
private void updateHeader(String[] strings) {
activeTheme);
}
+ String oldThemeBase = getConnection().translateVaadinUri("theme://");
+
activeTheme = newTheme;
if (newTheme != null) {
activeTheme);
updateVaadinFavicon(newTheme);
+
}
forceStateChangeRecursively(UIConnector.this);
+ // UIDL has no stored URL which we can repaint so we do some find and
+ // replace magic...
+ String newThemeBase = getConnection().translateVaadinUri("theme://");
+ replaceThemeAttribute(oldThemeBase, newThemeBase);
+
getLayoutManager().forceLayout();
}
+ /**
+ * Finds all attributes where theme:// urls have possibly been used and
+ * replaces any old theme url with a new one
+ *
+ * @param oldPrefix
+ * The start of the old theme URL
+ * @param newPrefix
+ * The start of the new theme URL
+ */
+ private void replaceThemeAttribute(String oldPrefix, String newPrefix) {
+ // Images
+ replaceThemeAttribute("src", oldPrefix, newPrefix);
+ // Embedded flash
+ replaceThemeAttribute("value", oldPrefix, newPrefix);
+ replaceThemeAttribute("movie", oldPrefix, newPrefix);
+ }
+
+ /**
+ * Finds any attribute of the given type where theme:// urls have possibly
+ * been used and replaces any old theme url with a new one
+ *
+ * @param attributeName
+ * The name of the attribute, e.g. "src"
+ * @param oldPrefix
+ * The start of the old theme URL
+ * @param newPrefix
+ * The start of the new theme URL
+ */
+ private void replaceThemeAttribute(String attributeName, String oldPrefix,
+ String newPrefix) {
+ // Find all "attributeName=" which start with "oldPrefix" using e.g.
+ // [^src='http://oldpath']
+ NodeList<Element> elements = querySelectorAll("[" + attributeName
+ + "^='" + oldPrefix + "']");
+ for (int i = 0; i < elements.getLength(); i++) {
+ Element element = elements.getItem(i);
+ element.setAttribute(
+ attributeName,
+ element.getAttribute(attributeName).replace(oldPrefix,
+ newPrefix));
+ }
+ }
+
/**
* Force a full recursive recheck of every connector's state variables.
*
--- /dev/null
+/*
+ * 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.themes;
+
+import com.vaadin.annotations.Theme;
+import com.vaadin.event.Action;
+import com.vaadin.event.Action.Handler;
+import com.vaadin.server.ThemeResource;
+import com.vaadin.server.VaadinRequest;
+import com.vaadin.tests.components.AbstractTestUIWithLog;
+import com.vaadin.tests.util.PersonContainer;
+import com.vaadin.ui.Button;
+import com.vaadin.ui.Button.ClickEvent;
+import com.vaadin.ui.Button.ClickListener;
+import com.vaadin.ui.ComboBox;
+import com.vaadin.ui.Embedded;
+import com.vaadin.ui.HorizontalLayout;
+import com.vaadin.ui.MenuBar;
+import com.vaadin.ui.Table;
+import com.vaadin.ui.VerticalLayout;
+
+@Theme("reindeer")
+public class LegacyComponentThemeChange extends AbstractTestUIWithLog {
+
+ @Override
+ protected void setup(VaadinRequest request) {
+ VerticalLayout vl = new VerticalLayout();
+ vl.setCaption("Change theme by clicking a button");
+ HorizontalLayout hl = new HorizontalLayout();
+ for (final String theme : new String[] { "reindeer", "runo" }) {
+ Button b = new Button(theme);
+ b.setId(theme + "");
+ b.addClickListener(new ClickListener() {
+
+ @Override
+ public void buttonClick(ClickEvent event) {
+ getUI().setTheme(theme);
+ }
+ });
+ hl.addComponent(b);
+ }
+ vl.addComponent(hl);
+
+ // Always wants to use icon from Runo, even if we change theme
+ ThemeResource alwaysTheSameIconImage = new ThemeResource(
+ "../runo/icons/16/ok.png");
+ ThemeResource varyingIcon = new ThemeResource("menubar-theme-icon.png");
+ MenuBar bar = new MenuBar();
+ bar.addItem("runo", alwaysTheSameIconImage, null);
+ bar.addItem("seletedtheme", varyingIcon, null);
+
+ vl.addComponent(bar);
+
+ ComboBox cb = new ComboBox("ComboBox");
+ cb.addItem("No icon");
+ cb.addItem("Icon");
+ cb.setItemIcon("Icon", new ThemeResource("comboboxicon.png"));
+ cb.setValue("Icon");
+
+ vl.addComponent(cb);
+
+ Embedded e = new Embedded("embedded");
+ e.setMimeType("application/x-shockwave-flash");
+ e.setType(Embedded.TYPE_OBJECT);
+ e.setSource(new ThemeResource("embedded.src"));
+ vl.addComponent(e);
+
+ Table t = new Table();
+ t.addActionHandler(new Handler() {
+ @Override
+ public void handleAction(Action action, Object sender, Object target) {
+ }
+
+ @Override
+ public Action[] getActions(Object target, Object sender) {
+ return new Action[] { new Action("Theme icon",
+ new ThemeResource("action-icon.png")) };
+ }
+ });
+ PersonContainer pc = PersonContainer.createWithTestData();
+ pc.addNestedContainerBean("address");
+ t.setContainerDataSource(pc);
+ vl.addComponent(t);
+
+ addComponent(vl);
+ }
+
+}
--- /dev/null
+/*
+ * 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.themes;
+
+import java.util.List;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.remote.DesiredCapabilities;
+import org.openqa.selenium.support.ui.ExpectedCondition;
+
+import com.vaadin.testbench.elements.ButtonElement;
+import com.vaadin.testbench.elements.ComboBoxElement;
+import com.vaadin.testbench.elements.EmbeddedElement;
+import com.vaadin.testbench.elements.MenuBarElement;
+import com.vaadin.testbench.elements.TableElement;
+import com.vaadin.testbench.parallel.Browser;
+import com.vaadin.testbench.parallel.BrowserUtil;
+import com.vaadin.tests.tb3.MultiBrowserTest;
+
+public class LegacyComponentThemeChangeTest extends MultiBrowserTest {
+
+ @Override
+ public List<DesiredCapabilities> getBrowsersToTest() {
+ // Seems like stylesheet onload is not fired on PhantomJS
+ // https://github.com/ariya/phantomjs/issues/12332
+ List<DesiredCapabilities> l = getBrowsersExcludingPhantomJS();
+
+ // For some reason, IE times out when trying to open the combobox,
+ // #18341
+ l.remove(Browser.IE11.getDesiredCapabilities());
+ return l;
+ }
+
+ @Test
+ public void legacyComponentThemeResourceChange() {
+ openTestURL();
+ String theme = "reindeer";
+ assertMenubarTheme(theme);
+ assertCombobBoxTheme(theme);
+ assertTableTheme(theme);
+ assertEmbeddedTheme(theme);
+
+ theme = "runo";
+ changeTheme(theme);
+ assertMenubarTheme(theme);
+ assertCombobBoxTheme(theme);
+ assertTableTheme(theme);
+ assertEmbeddedTheme(theme);
+
+ theme = "reindeer";
+ changeTheme(theme);
+ assertMenubarTheme(theme);
+ assertCombobBoxTheme(theme);
+ assertTableTheme(theme);
+ assertEmbeddedTheme(theme);
+
+ }
+
+ private void assertEmbeddedTheme(String theme) {
+ if (BrowserUtil.isIE8(getDesiredCapabilities())) {
+ // IE8 won't initialize the dummy flash properly
+ return;
+ }
+ EmbeddedElement e = $(EmbeddedElement.class).first();
+ WebElement movieParam = e.findElement(By
+ .xpath(".//param[@name='movie']"));
+ WebElement embed = e.findElement(By.xpath(".//embed"));
+ assertAttributePrefix(movieParam, "value", theme);
+ assertAttributePrefix(embed, "src", theme);
+ assertAttributePrefix(embed, "movie", theme);
+ }
+
+ private void assertTableTheme(String theme) {
+ TableElement t = $(TableElement.class).first();
+ t.getRow(0).contextClick();
+ WebElement popup = findElement(By.className("v-contextmenu"));
+
+ WebElement actionImage = popup.findElement(By.xpath(".//img"));
+ assertAttributePrefix(actionImage, "src", theme);
+ }
+
+ private void assertCombobBoxTheme(String theme) {
+ ComboBoxElement cb = $(ComboBoxElement.class).first();
+ WebElement selectedImage = cb.findElement(By.xpath("./img"));
+ assertAttributePrefix(selectedImage, "src", theme);
+
+ cb.openPopup();
+ WebElement popup = findElement(By
+ .className("v-filterselect-suggestpopup"));
+ WebElement itemImage = popup.findElement(By.xpath(".//img"));
+ assertAttributePrefix(itemImage, "src", theme);
+ }
+
+ private void assertMenubarTheme(String theme) {
+ // The runoImage must always come from Runo
+ WebElement runoImage = $(MenuBarElement.class).first().findElement(
+ By.xpath(".//span[text()='runo']/img"));
+ String runoImageSrc = runoImage.getAttribute("src");
+
+ // Something in Selenium normalizes the image so it becomes
+ // "/themes/runo/icons/16/ok.png" here although it is
+ // "/themes/<currenttheme>/../runo/icons/16/ok.png" in the browser
+ Assert.assertEquals(getThemeURL("runo") + "icons/16/ok.png",
+ runoImageSrc);
+
+ // The other image should change with the theme
+ WebElement themeImage = $(MenuBarElement.class).first().findElement(
+ By.xpath(".//span[text()='seletedtheme']/img"));
+ assertAttributePrefix(themeImage, "src", theme);
+ }
+
+ private void assertAttributePrefix(WebElement element, String attribute,
+ String theme) {
+ String value = element.getAttribute(attribute);
+ String expectedPrefix = getThemeURL(theme);
+ Assert.assertTrue("Attribute " + attribute + "='" + value
+ + "' does not start with " + expectedPrefix,
+ value.startsWith(expectedPrefix));
+
+ }
+
+ private String getThemeURL(String theme) {
+ return getBaseURL() + "/VAADIN/themes/" + theme + "/";
+ }
+
+ private void changeTheme(String theme) {
+ $(ButtonElement.class).id(theme).click();
+ waitForThemeToChange(theme);
+ }
+
+ private void waitForThemeToChange(final String theme) {
+
+ final WebElement rootDiv = findElement(By
+ .xpath("//div[contains(@class,'v-app')]"));
+ waitUntil(new ExpectedCondition<Boolean>() {
+
+ @Override
+ public Boolean apply(WebDriver input) {
+ String rootClass = rootDiv.getAttribute("class").trim();
+
+ return rootClass.contains(theme);
+ }
+ }, 30);
+ }
+
+}