diff options
author | Matti Tahvonen <matti.tahvonen@itmill.com> | 2010-03-05 13:43:43 +0000 |
---|---|---|
committer | Matti Tahvonen <matti.tahvonen@itmill.com> | 2010-03-05 13:43:43 +0000 |
commit | 7cefe21c703e6e7822ba7456d45fa49a14dd067d (patch) | |
tree | 8891e28e97bce4f9db44dd7f50cbb6a844de8aef /src/com | |
parent | 9b84a02d3f46d1f4dca4cc3b17c388d51ca5194a (diff) | |
parent | 2b1deeeced6d098a22d6e52b70e94dbb9e96df57 (diff) | |
download | vaadin-framework-7cefe21c703e6e7822ba7456d45fa49a14dd067d.tar.gz vaadin-framework-7cefe21c703e6e7822ba7456d45fa49a14dd067d.zip |
merged changes from 6.3 + some container related changes
svn changeset:11664/svn branch:6.3_dd
Diffstat (limited to 'src/com')
41 files changed, 1875 insertions, 509 deletions
diff --git a/src/com/vaadin/Application.java b/src/com/vaadin/Application.java index 67bc5e9da6..9a0c180a1d 100644 --- a/src/com/vaadin/Application.java +++ b/src/com/vaadin/Application.java @@ -1303,6 +1303,13 @@ public abstract class Application implements URIHandler, * <li><b>outOfSyncMessage</b> = "Something has caused us to be out of sync * with the server.<br/> * Take note of any unsaved data, and <u>click here</u> to re-sync."</li> + * <li><b>cookiesDisabledURL</b> = null</li> + * <li><b>cookiesDisabledNotificationEnabled</b> = true</li> + * <li><b>cookiesDisabledCaption</b> = "Cookies disabled"</li> + * <li><b>cookiesDisabledMessage</b> = "This application requires cookies to + * function.<br/> + * Please enable cookies in your browser and <u>click here</u> to try again. + * </li> * </ul> * </p> * @@ -1328,6 +1335,11 @@ public abstract class Application implements URIHandler, protected String outOfSyncCaption = "Out of sync"; protected String outOfSyncMessage = "Something has caused us to be out of sync with the server.<br/>Take note of any unsaved data, and <u>click here</u> to re-sync."; + protected String cookiesDisabledURL = null; + protected boolean cookiesDisabledNotificationEnabled = true; + protected String cookiesDisabledCaption = "Cookies disabled"; + protected String cookiesDisabledMessage = "This application requires cookies to function.<br/>Please enable cookies in your browser and <u>click here</u> to try again."; + /** * Use {@link CustomizedSystemMessages} to customize */ @@ -1462,6 +1474,52 @@ public abstract class Application implements URIHandler, return (outOfSyncNotificationEnabled ? outOfSyncMessage : null); } + /** + * Returns the URL the user should be redirected to after dismissing the + * "you have to enable your cookies" message. Typically null. + * + * @return A URL the user should be redirected to after dismissing the + * message or null to reload the current URL. + */ + public String getCookiesDisabledURL() { + return cookiesDisabledURL; + } + + /** + * Determines if "cookies disabled" messages should be shown to the end + * user or not. If the notification is disabled the user will be + * immediately redirected to the URL returned by + * {@link #getCookiesDisabledURL()}. + * + * @return true to show "cookies disabled" messages to the end user, + * false to redirect to the given URL directly + */ + public boolean isCookiesDisabledNotificationEnabled() { + return cookiesDisabledNotificationEnabled; + } + + /** + * Returns the caption of the message shown to the user when cookies are + * disabled in the browser. + * + * @return The caption of the "cookies disabled" message + */ + public String getCookiesDisabledCaption() { + return (cookiesDisabledNotificationEnabled ? cookiesDisabledCaption + : null); + } + + /** + * Returns the message shown to the user when cookies are disabled in + * the browser. + * + * @return The "cookies disabled" message + */ + public String getCookiesDisabledMessage() { + return (cookiesDisabledNotificationEnabled ? cookiesDisabledMessage + : null); + } + } /** @@ -1688,6 +1746,54 @@ public abstract class Application implements URIHandler, this.outOfSyncMessage = outOfSyncMessage; } + /** + * Sets the URL to redirect to when the browser has cookies disabled. + * + * @param cookiesDisabledURL + * the URL to redirect to, or null to reload the current URL + */ + public void setCookiesDisabledURL(String cookiesDisabledURL) { + this.cookiesDisabledURL = cookiesDisabledURL; + } + + /** + * Enables or disables the notification for "cookies disabled" messages. + * If disabled, the URL returned by {@link #getCookiesDisabledURL()} is + * loaded directly. + * + * @param cookiesDisabledNotificationEnabled + * true to enable "cookies disabled" messages, false + * otherwise + */ + public void setCookiesDisabledNotificationEnabled( + boolean cookiesDisabledNotificationEnabled) { + this.cookiesDisabledNotificationEnabled = cookiesDisabledNotificationEnabled; + } + + /** + * Sets the caption of the "cookies disabled" notification. Set to null + * for no caption. If both caption and message is null, the notification + * is disabled. + * + * @param cookiesDisabledCaption + * the caption for the "cookies disabled" notification + */ + public void setCookiesDisabledCaption(String cookiesDisabledCaption) { + this.cookiesDisabledCaption = cookiesDisabledCaption; + } + + /** + * Sets the message of the "cookies disabled" notification. Set to null + * for no message. If both caption and message is null, the notification + * is disabled. + * + * @param cookiesDisabledMessage + * the message for the "cookies disabled" notification + */ + public void setCookiesDisabledMessage(String cookiesDisabledMessage) { + this.cookiesDisabledMessage = cookiesDisabledMessage; + } + } /** diff --git a/src/com/vaadin/data/Container.java b/src/com/vaadin/data/Container.java index 5777bf2b05..6dbcfe779f 100644 --- a/src/com/vaadin/data/Container.java +++ b/src/com/vaadin/data/Container.java @@ -562,26 +562,34 @@ public interface Container extends Serializable { } /** - * Interface is implemented by containers that allow reducing their visible - * contents with set of filters. - * + * Interface that is implemented by containers which allow reducing their + * visible contents based on a set of filters. + * <p> * When a set of filters are set, only items that match the filters are * included in the visible contents of the container. Still new items that * do not match filters can be added to the container. Multiple filters can * be added and the container remembers the state of the filters. When * multiple filters are added, all filters must match for an item to be * visible in the container. - * + * </p> + * <p> * When an {@link com.vaadin.data.Ordered} or * {@link com.vaadin.data.Indexed} container is filtered, all operations of * these interfaces should only use the filtered contents and the filtered * indices to the container. - * + * </p> + * <p> + * How filtering is performed when a {@link Hierarchical} container + * implements {@link Filterable} is implementation specific and should be + * documented in the implementing class. + * </p> + * <p> * Adding items (if supported) to a filtered {@link com.vaadin.data.Ordered} * or {@link com.vaadin.data.Indexed} container should insert them * immediately after the indicated visible item. The unfiltered position of * items added at index 0, at index {@link com.vaadin.data.Container#size()} * or at an undefined position is up to the implementation. + * </p> * * @since 5.0 */ diff --git a/src/com/vaadin/data/util/BeanItemContainer.java b/src/com/vaadin/data/util/BeanItemContainer.java index 7848de5eed..6510229d5a 100644 --- a/src/com/vaadin/data/util/BeanItemContainer.java +++ b/src/com/vaadin/data/util/BeanItemContainer.java @@ -38,9 +38,23 @@ import com.vaadin.data.Property.ValueChangeNotifier; @SuppressWarnings("serial") public class BeanItemContainer<BT> implements Indexed, Sortable, Filterable, ItemSetChangeNotifier, ValueChangeListener { - // filtered and unfiltered item IDs - private ArrayList<BT> list = new ArrayList<BT>(); - private ArrayList<BT> allItems = new ArrayList<BT>(); + /** + * The filteredItems variable contains the items that are visible outside + * the container. If filters are enabled this contains a subset of allItems, + * if no filters are set this contains the same items as allItems. + */ + private ListSet<BT> filteredItems = new ListSet<BT>(); + + /** + * The allItems variable always contains all the items in the container. + * Some or all of these are also in the filteredItems list. + */ + private ListSet<BT> allItems = new ListSet<BT>(); + + /** + * Maps all pojos (item ids) in the container (including filtered) to their + * corresponding BeanItem. + */ private final Map<BT, BeanItem<BT>> beanToItem = new HashMap<BT, BeanItem<BT>>(); // internal data model to obtain property IDs etc. @@ -89,6 +103,7 @@ public class BeanItemContainer<BT> implements Indexed, Sortable, Filterable, * @throws IllegalArgumentException * If the collection is null or empty. */ + @SuppressWarnings("unchecked") public BeanItemContainer(Collection<BT> collection) throws IllegalArgumentException { if (collection == null || collection.isEmpty()) { @@ -98,10 +113,22 @@ public class BeanItemContainer<BT> implements Indexed, Sortable, Filterable, type = (Class<? extends BT>) collection.iterator().next().getClass(); model = BeanItem.getPropertyDescriptors(type); - int i = 0; - for (BT bt : collection) { - addItemAt(i++, bt); + addAll(collection); + } + + private void addAll(Collection<BT> collection) { + // Pre-allocate space for the collection + allItems.ensureCapacity(allItems.size() + collection.size()); + + int idx = size(); + for (BT bean : collection) { + if (internalAddAt(idx, bean) != null) { + idx++; + } } + + // Filter the contents when all items have been added + filterAll(); } /** @@ -147,36 +174,65 @@ public class BeanItemContainer<BT> implements Indexed, Sortable, Filterable, * @return Returns new item or null if the operation fails. */ private BeanItem<BT> addItemAtInternalIndex(int index, Object newItemId) { - // Make sure that the Item has not been created yet - if (allItems.contains(newItemId)) { + BeanItem<BT> beanItem = internalAddAt(index, (BT) newItemId); + if (beanItem != null) { + filterAll(); + } + + return beanItem; + } + + /** + * Adds the bean to all internal data structures at the given position. + * Fails if the bean is already in the container or is not assignable to the + * correct type. Returns the new BeanItem if the bean was added + * successfully. + * + * <p> + * Caller should call {@link #filterAll()} after calling this method to + * ensure the filtered list is updated. + * </p> + * + * @param position + * The position at which the bean should be inserted + * @param bean + * The bean to insert + * + * @return true if the bean was added successfully, false otherwise + */ + private BeanItem<BT> internalAddAt(int position, BT bean) { + // Make sure that the item has not been added previously + if (allItems.contains(bean)) { return null; } - if (type.isAssignableFrom(newItemId.getClass())) { - BT pojo = (BT) newItemId; - // "list" will be updated in filterAll() - allItems.add(index, pojo); - BeanItem<BT> beanItem = new BeanItem<BT>(pojo, model); - beanToItem.put(pojo, beanItem); - // add listeners to be able to update filtering on property changes - for (Filter filter : filters) { - // addValueChangeListener avoids adding duplicates - addValueChangeListener(beanItem, filter.propertyId); - } - // it is somewhat suboptimal to filter all items - filterAll(); - return beanItem; - } else { + if (!type.isAssignableFrom(bean.getClass())) { return null; } + + // "filteredList" will be updated in filterAll() which should be invoked + // by the caller after calling this method. + allItems.add(position, bean); + BeanItem<BT> beanItem = new BeanItem<BT>(bean, model); + beanToItem.put(bean, beanItem); + + // add listeners to be able to update filtering on property + // changes + for (Filter filter : filters) { + // addValueChangeListener avoids adding duplicates + addValueChangeListener(beanItem, filter.propertyId); + } + + return beanItem; } + @SuppressWarnings("unchecked") public BT getIdByIndex(int index) { - return list.get(index); + return filteredItems.get(index); } public int indexOfId(Object itemId) { - return list.indexOf(itemId); + return filteredItems.indexOf(itemId); } /** @@ -296,7 +352,7 @@ public class BeanItemContainer<BT> implements Indexed, Sortable, Filterable, public boolean containsId(Object itemId) { // only look at visible items after filtering - return list.contains(itemId); + return filteredItems.contains(itemId); } public Property getContainerProperty(Object itemId, Object propertyId) { @@ -311,8 +367,9 @@ public class BeanItemContainer<BT> implements Indexed, Sortable, Filterable, return beanToItem.get(itemId); } + @SuppressWarnings("unchecked") public Collection<BT> getItemIds() { - return (Collection<BT>) list.clone(); + return (Collection<BT>) filteredItems.clone(); } public Class<?> getType(Object propertyId) { @@ -321,7 +378,7 @@ public class BeanItemContainer<BT> implements Indexed, Sortable, Filterable, public boolean removeAllItems() throws UnsupportedOperationException { allItems.clear(); - list.clear(); + filteredItems.clear(); // detach listeners from all BeanItems for (BeanItem<BT> item : beanToItem.values()) { removeAllValueChangeListeners(item); @@ -345,7 +402,7 @@ public class BeanItemContainer<BT> implements Indexed, Sortable, Filterable, removeAllValueChangeListeners(getItem(itemId)); // remove item beanToItem.remove(itemId); - list.remove(itemId); + filteredItems.remove(itemId); fireItemSetChange(); return true; } @@ -375,7 +432,7 @@ public class BeanItemContainer<BT> implements Indexed, Sortable, Filterable, } public int size() { - return list.size(); + return filteredItems.size(); } public Collection<Object> getSortableContainerPropertyIds() { @@ -445,7 +502,7 @@ public class BeanItemContainer<BT> implements Indexed, Sortable, Filterable, public void addContainerFilter(Object propertyId, String filterString, boolean ignoreCase, boolean onlyMatchPrefix) { if (filters.isEmpty()) { - list = (ArrayList<BT>) allItems.clone(); + filteredItems = (ListSet<BT>) allItems.clone(); } // listen to change events to be able to update filtering for (BeanItem<BT> item : beanToItem.values()) { @@ -465,22 +522,22 @@ public class BeanItemContainer<BT> implements Indexed, Sortable, Filterable, */ protected void filterAll() { // avoid notification if the filtering had no effect - List<BT> originalItems = list; + List<BT> originalItems = filteredItems; // it is somewhat inefficient to do a (shallow) clone() every time - list = (ArrayList<BT>) allItems.clone(); + filteredItems = (ListSet<BT>) allItems.clone(); for (Filter f : filters) { filter(f); } // check if exactly the same items are there after filtering to avoid // unnecessary notifications // this may be slow in some cases as it uses BT.equals() - if (!originalItems.equals(list)) { + if (!originalItems.equals(filteredItems)) { fireItemSetChange(); } } protected void filter(Filter f) { - Iterator<BT> iterator = list.iterator(); + Iterator<BT> iterator = filteredItems.iterator(); while (iterator.hasNext()) { BT bean = iterator.next(); if (!f.passesFilter(getItem(bean))) { diff --git a/src/com/vaadin/data/util/ContainerHierarchicalWrapper.java b/src/com/vaadin/data/util/ContainerHierarchicalWrapper.java index 80065f9254..8b6fedb8e4 100644 --- a/src/com/vaadin/data/util/ContainerHierarchicalWrapper.java +++ b/src/com/vaadin/data/util/ContainerHierarchicalWrapper.java @@ -220,7 +220,12 @@ public class ContainerHierarchicalWrapper implements Container.Hierarchical, return ((Container.Hierarchical) container) .areChildrenAllowed(itemId); } - return !noChildrenAllowed.contains(itemId); + + if (noChildrenAllowed.contains(itemId)) { + return false; + } + + return containsId(itemId); } /* @@ -284,7 +289,11 @@ public class ContainerHierarchicalWrapper implements Container.Hierarchical, return ((Container.Hierarchical) container).isRoot(itemId); } - return parent.get(itemId) == null; + if (parent.containsKey(itemId)) { + return false; + } + + return containsId(itemId); } /* diff --git a/src/com/vaadin/data/util/HierarchicalContainer.java b/src/com/vaadin/data/util/HierarchicalContainer.java index 2b5519e651..a404d83eb1 100644 --- a/src/com/vaadin/data/util/HierarchicalContainer.java +++ b/src/com/vaadin/data/util/HierarchicalContainer.java @@ -42,16 +42,29 @@ public class HierarchicalContainer extends IndexedContainer implements private final HashMap<Object, LinkedList<Object>> children = new HashMap<Object, LinkedList<Object>>(); /** + * Mapping from Item ID to a list of child IDs when filtered + */ + private HashMap<Object, LinkedList<Object>> filteredChildren = null; + + /** * List that contains all root elements of the container. */ private final LinkedList<Object> roots = new LinkedList<Object>(); + /** + * List that contains all filtered root elements of the container. + */ + private LinkedList<Object> filteredRoots = null; + /* * Can the specified Item have any children? Don't add a JavaDoc comment * here, we use the default documentation from implemented interface. */ public boolean areChildrenAllowed(Object itemId) { - return !noChildrenAllowed.contains(itemId); + if (noChildrenAllowed.contains(itemId)) { + return false; + } + return containsId(itemId); } /* @@ -60,7 +73,14 @@ public class HierarchicalContainer extends IndexedContainer implements * interface. */ public Collection getChildren(Object itemId) { - final Collection c = children.get(itemId); + LinkedList<Object> c; + + if (filteredChildren != null) { + c = filteredChildren.get(itemId); + } else { + c = children.get(itemId); + } + if (c == null) { return null; } @@ -82,7 +102,11 @@ public class HierarchicalContainer extends IndexedContainer implements * interface. */ public boolean hasChildren(Object itemId) { - return children.get(itemId) != null; + if (filteredChildren != null) { + return filteredChildren.containsKey(itemId); + } else { + return children.containsKey(itemId); + } } /* @@ -91,7 +115,15 @@ public class HierarchicalContainer extends IndexedContainer implements * interface. */ public boolean isRoot(Object itemId) { - return parent.get(itemId) == null; + if (filteredRoots != null && !filteredRoots.contains(itemId)) { + return false; + } + + if (parent.containsKey(itemId)) { + return false; + } + + return containsId(itemId); } /* @@ -100,7 +132,11 @@ public class HierarchicalContainer extends IndexedContainer implements * interface. */ public Collection rootItemIds() { - return Collections.unmodifiableCollection(roots); + if (filteredRoots != null) { + return Collections.unmodifiableCollection(filteredRoots); + } else { + return Collections.unmodifiableCollection(roots); + } } /** @@ -139,25 +175,6 @@ public class HierarchicalContainer extends IndexedContainer implements return true; } - @Override - public Item addItemAt(int index, Object newItemId) { - Item retval = super.addItemAt(index, newItemId); - if (getParent(newItemId) == null) { - int refIndex = roots.size() - 1; - int indexOfId = indexOfId(roots.get(refIndex)); - while (indexOfId > index) { - refIndex--; - if (refIndex < 0) { - // inserts as first - break; - } - indexOfId = indexOfId(roots.get(refIndex)); - } - roots.add(refIndex + 1, newItemId); - } - return retval; - } - /** * <p> * Sets the parent of an Item. The new parent item must exist and be able to @@ -191,27 +208,58 @@ public class HierarchicalContainer extends IndexedContainer implements return true; } - // Making root + // Making root? if (newParentId == null) { + // The itemId should become a root so we need to + // - Remove it from the old parent's children list (also filtered + // list) + // - Add it as a root + // - Remove it from the item -> parent list (parent is null for + // roots) // Removes from old parents children list - final LinkedList l = children.get(itemId); + final LinkedList<Object> l = children.get(itemId); if (l != null) { l.remove(itemId); if (l.isEmpty()) { children.remove(itemId); } + + if (filteredChildren != null) { + LinkedList<Object> f = filteredChildren.get(itemId); + if (f != null) { + f.remove(itemId); + if (f.isEmpty()) { + filteredChildren.remove(f); + } + } + } } // Add to be a root roots.add(itemId); + if (filteredRoots != null) { + if (passesFilters(itemId)) { + filteredRoots.add(itemId); + } + } // Updates parent parent.remove(itemId); + fireContentsChange(-1); + return true; } + // We get here when the item should not become a root and we need to + // - Verify the new parent exists and can have children + // - Check that the new parent is not a child of the selected itemId + // - Updated the item -> parent mapping to point to the new parent + // - Remove the item from the roots list if it was a root + // - Remove the item from the old parent's children list if it was not a + // root + // Checks that the new parent exists in container and can have // children if (!containsId(newParentId) || noChildrenAllowed.contains(newParentId)) { @@ -229,29 +277,92 @@ public class HierarchicalContainer extends IndexedContainer implements // Updates parent parent.put(itemId, newParentId); - LinkedList pcl = children.get(newParentId); + LinkedList<Object> pcl = children.get(newParentId); if (pcl == null) { - pcl = new LinkedList(); + // Create an empty list for holding children if one were not + // previously created + pcl = new LinkedList<Object>(); children.put(newParentId, pcl); } pcl.add(itemId); + // Add children list for filtered case also + if (filteredChildren != null) { + LinkedList<Object> f = filteredChildren.get(newParentId); + if (f == null) { + // Create an empty list for holding children if one were not + // previously created + f = new LinkedList<Object>(); + filteredChildren.put(newParentId, f); + } + } + // Removes from old parent or root if (oldParentId == null) { roots.remove(itemId); } else { - final LinkedList l = children.get(oldParentId); + final LinkedList<Object> l = children.get(oldParentId); if (l != null) { l.remove(itemId); if (l.isEmpty()) { children.remove(oldParentId); } } + if (filteredChildren != null) { + LinkedList<Object> f = filteredChildren.get(oldParentId); + if (f != null) { + f.remove(itemId); + if (f.isEmpty()) { + filteredChildren.remove(oldParentId); + } + } + } } + fireContentsChange(-1); + return true; } + /** + * TODO javadoc + * + * @param itemId + * @param siblingId + */ + public void moveAfterSibling(Object itemId, Object siblingId) { + Object parent2 = getParent(itemId); + LinkedList<Object> childrenList; + if (parent2 == null) { + childrenList = roots; + } else { + childrenList = children.get(parent2); + } + if (siblingId == null) { + childrenList.remove(itemId); + childrenList.addFirst(itemId); + + } else { + int oldIndex = childrenList.indexOf(itemId); + int indexOfSibling = childrenList.indexOf(siblingId); + if (indexOfSibling != -1 && oldIndex != -1) { + int newIndex; + if (oldIndex > indexOfSibling) { + newIndex = indexOfSibling + 1; + } else { + newIndex = indexOfSibling; + } + childrenList.remove(oldIndex); + childrenList.add(newIndex, itemId); + } else { + throw new IllegalArgumentException( + "Given identifiers no not have the same parent."); + } + } + fireContentsChange(-1); + + } + /* * (non-Javadoc) * @@ -259,12 +370,21 @@ public class HierarchicalContainer extends IndexedContainer implements */ @Override public Object addItem() { - final Object id = super.addItem(); - if (id != null && !roots.contains(id)) { - roots.add(id); + final Object itemId = super.addItem(); + if (itemId == null) { + return null; } - return id; + if (!roots.contains(itemId)) { + roots.add(itemId); + if (filteredRoots != null) { + if (passesFilters(itemId)) { + filteredRoots.add(itemId); + } + } + } + + return itemId; } /* @@ -275,9 +395,18 @@ public class HierarchicalContainer extends IndexedContainer implements @Override public Item addItem(Object itemId) { final Item item = super.addItem(itemId); - if (item != null) { - roots.add(itemId); + if (item == null) { + return null; + } + + roots.add(itemId); + + if (filteredRoots != null) { + if (passesFilters(itemId)) { + filteredRoots.add(itemId); + } } + return item; } @@ -295,6 +424,12 @@ public class HierarchicalContainer extends IndexedContainer implements parent.clear(); children.clear(); noChildrenAllowed.clear(); + if (filteredRoots != null) { + filteredRoots = null; + } + if (filteredChildren != null) { + filteredChildren = null; + } } return success; } @@ -309,20 +444,44 @@ public class HierarchicalContainer extends IndexedContainer implements final boolean success = super.removeItem(itemId); if (success) { - if (isRoot(itemId)) { - roots.remove(itemId); + // Remove from roots if this was a root + if (roots.remove(itemId)) { + + // If filtering is enabled we might need to remove it from the + // filtered list also + if (filteredRoots != null) { + filteredRoots.remove(itemId); + } } - LinkedList<Object> remove = children.remove(itemId); - if (remove != null) { - for (Object object : remove) { - removeItem(object); + + // Clear the children list. Old children will now become root nodes + LinkedList<Object> childNodeIds = children.remove(itemId); + if (childNodeIds != null) { + if (filteredChildren != null) { + filteredChildren.remove(itemId); + } + for (Object childId : childNodeIds) { + setParent(childId, null); } } - final Object p = parent.get(itemId); - if (p != null) { - final LinkedList c = children.get(p); + + // Parent of the item that we are removing will contain the item id + // in its children list + final Object parentItemId = parent.get(itemId); + if (parentItemId != null) { + final LinkedList<Object> c = children.get(parentItemId); if (c != null) { c.remove(itemId); + + // Found in the children list so might also be in the + // filteredChildren list + if (filteredChildren != null) { + LinkedList<Object> f = filteredChildren + .get(parentItemId); + if (f != null) { + f.remove(parentItemId); + } + } } } parent.remove(itemId); @@ -332,6 +491,35 @@ public class HierarchicalContainer extends IndexedContainer implements return success; } + /** + * Removes the Item identified by ItemId from the Container and all its + * children. + * + * @see #removeItem(Object) + * @param itemId + * the identifier of the Item to remove + * @return true if the operation succeeded + */ + public boolean removeItemRecursively(Object itemId) { + boolean success = true; + Collection<Object> children2 = getChildren(itemId); + if (children2 != null) { + Object[] array = children2.toArray(); + for (int i = 0; i < array.length; i++) { + boolean removeItemRecursively = removeItemRecursively(array[i]); + if (!removeItemRecursively) { + success = false; + } + } + } + boolean removeItem = removeItem(itemId); + if (!removeItem) { + success = false; + } + return success; + + } + /* * (non-Javadoc) * @@ -347,4 +535,40 @@ public class HierarchicalContainer extends IndexedContainer implements } } + /* + * Overridden to provide filtering for root & children items. + * + * (non-Javadoc) + * + * @see com.vaadin.data.util.IndexedContainer#updateContainerFiltering() + */ + @Override + protected void updateContainerFiltering() { + super.updateContainerFiltering(); + + filteredRoots = new LinkedList<Object>(); + filteredChildren = new HashMap<Object, LinkedList<Object>>(); + + // Filter root item ids + for (Object rootId : roots) { + if (passesFilters(rootId)) { + filteredRoots.add(rootId); + } + } + + // Filter children + for (Object parent : children.keySet()) { + if (passesFilters(parent)) { + LinkedList<Object> filtered = new LinkedList<Object>(); + filteredChildren.put(parent, filtered); + for (Object child : children.get(parent)) { + if (passesFilters(child)) { + filtered.add(child); + } + } + } + } + + } + } diff --git a/src/com/vaadin/data/util/IndexedContainer.java b/src/com/vaadin/data/util/IndexedContainer.java index d6d6d9e77c..3bac19cd7a 100644 --- a/src/com/vaadin/data/util/IndexedContainer.java +++ b/src/com/vaadin/data/util/IndexedContainer.java @@ -469,7 +469,13 @@ public class IndexedContainer implements Container.Indexed, return null; } try { - return itemIds.get(itemIds.indexOf(itemId) + 1); + int idx = itemIds.indexOf(itemId); + if (idx == -1) { + // If the given Item is not found in the Container, + // null is returned. + return null; + } + return itemIds.get(idx + 1); } catch (final IndexOutOfBoundsException e) { return null; } @@ -548,6 +554,11 @@ public class IndexedContainer implements Container.Indexed, * java.lang.Object) */ public Item addItemAfter(Object previousItemId, Object newItemId) { + // Adding an item after null item adds the item as first item of the + // ordered container. + if (previousItemId == null) { + return addItemAt(0, newItemId); + } // Get the index of the addition int index = -1; @@ -574,7 +585,11 @@ public class IndexedContainer implements Container.Indexed, // Creates a new id final Object id = generateId(); - return addItemAfter(previousItemId, id); + if (addItemAfter(previousItemId, id) != null) { + return id; + } else { + return null; + } } /* @@ -953,7 +968,7 @@ public class IndexedContainer implements Container.Indexed, * @param addedItemIndex * index of new item if change event was an item addition */ - private void fireContentsChange(int addedItemIndex) { + protected void fireContentsChange(int addedItemIndex) { if (itemSetChangeListeners != null) { final Object[] l = itemSetChangeListeners.toArray(); final Container.ItemSetChangeEvent event = new IndexedContainer.ItemSetChangeEvent( @@ -1549,7 +1564,7 @@ public class IndexedContainer implements Container.Indexed, } } - private void updateContainerFiltering() { + protected void updateContainerFiltering() { // Clearing filters? if (filters == null || filters.isEmpty()) { @@ -1571,7 +1586,7 @@ public class IndexedContainer implements Container.Indexed, // Filter for (final Iterator i = itemIds.iterator(); i.hasNext();) { final Object id = i.next(); - if (passesFilters(new IndexedContainerItem(id))) { + if (passesFilters(id)) { filteredItemIds.add(id); } } @@ -1579,6 +1594,10 @@ public class IndexedContainer implements Container.Indexed, fireContentsChange(-1); } + protected final boolean passesFilters(Object itemId) { + return passesFilters(new IndexedContainerItem(itemId)); + } + private boolean passesFilters(Item item) { if (filters == null) { return true; diff --git a/src/com/vaadin/data/util/ListSet.java b/src/com/vaadin/data/util/ListSet.java new file mode 100644 index 0000000000..8dc8c6bbfd --- /dev/null +++ b/src/com/vaadin/data/util/ListSet.java @@ -0,0 +1,198 @@ +package com.vaadin.data.util;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+
+/**
+ * ListSet is an internal Vaadin class which implements a combination of a List
+ * and a Set. The main purpose of this class is to provide a fast
+ * {@link #contains(Object)} method. Each inserted object must by unique (as
+ * specified by {@link #equals(Object)}).
+ *
+ * This class is subject to change and should not be used outside Vaadin core.
+ */
+public class ListSet<E> extends ArrayList<E> {
+ private HashSet<E> itemSet = null;
+
+ public ListSet() {
+ super();
+ itemSet = new HashSet<E>();
+ }
+
+ public ListSet(Collection<? extends E> c) {
+ super(c);
+ itemSet = new HashSet<E>(c.size());
+ itemSet.addAll(c);
+ }
+
+ public ListSet(int initialCapacity) {
+ super(initialCapacity);
+ itemSet = new HashSet<E>(initialCapacity);
+ }
+
+ // Delegate contains operations to the set
+ @Override
+ public boolean contains(Object o) {
+ return itemSet.contains(o);
+ }
+
+ @Override
+ public boolean containsAll(Collection<?> c) {
+ return itemSet.containsAll(c);
+ }
+
+ // Methods for updating the set when the list is updated.
+ @Override
+ public boolean add(E e) {
+ if (contains(e)) {
+ // Duplicates are not allowed
+ return false;
+ }
+
+ if (super.add(e)) {
+ itemSet.add(e);
+ return true;
+ } else {
+ return false;
+ }
+ };
+
+ /**
+ * Works as java.util.ArrayList#add(int, java.lang.Object) but returns
+ * immediately if the element is already in the ListSet.
+ */
+ @Override
+ public void add(int index, E element) {
+ if (contains(element)) {
+ // Duplicates are not allowed
+ return;
+ }
+
+ super.add(index, element);
+ itemSet.add(element);
+ }
+
+ @Override
+ public boolean addAll(Collection<? extends E> c) {
+ boolean modified = false;
+ Iterator<? extends E> i = c.iterator();
+ while (i.hasNext()) {
+ E e = i.next();
+ if (contains(e)) {
+ continue;
+ }
+
+ if (add(e)) {
+ itemSet.add(e);
+ modified = true;
+ }
+ }
+ return modified;
+ }
+
+ @Override
+ public boolean addAll(int index, Collection<? extends E> c) {
+ ensureCapacity(size() + c.size());
+
+ boolean modified = false;
+ Iterator<? extends E> i = c.iterator();
+ while (i.hasNext()) {
+ E e = i.next();
+ if (contains(e)) {
+ continue;
+ }
+
+ add(index++, e);
+ itemSet.add(e);
+ modified = true;
+ }
+
+ return modified;
+ }
+
+ @Override
+ public void clear() {
+ super.clear();
+ itemSet.clear();
+ }
+
+ @Override
+ public int indexOf(Object o) {
+ if (!contains(o)) {
+ return -1;
+ }
+
+ return super.indexOf(o);
+ }
+
+ @Override
+ public int lastIndexOf(Object o) {
+ if (!contains(o)) {
+ return -1;
+ }
+
+ return super.lastIndexOf(o);
+ }
+
+ @Override
+ public E remove(int index) {
+ E e = super.remove(index);
+
+ if (e != null) {
+ itemSet.remove(e);
+ }
+
+ return e;
+ }
+
+ @Override
+ public boolean remove(Object o) {
+ if (super.remove(o)) {
+ itemSet.remove(o);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ protected void removeRange(int fromIndex, int toIndex) {
+ HashSet<E> toRemove = new HashSet<E>();
+ for (int idx = fromIndex; idx < toIndex; idx++) {
+ toRemove.add(get(idx));
+ }
+ super.removeRange(fromIndex, toIndex);
+ itemSet.removeAll(toRemove);
+ }
+
+ @Override
+ public E set(int index, E element) {
+ if (contains(element)) {
+ // Element already exist in the list
+ if (get(index) == element) {
+ // At the same position, nothing to be done
+ return element;
+ } else {
+ // At another position, cannot set
+ return null;
+ }
+ }
+
+ E old = super.set(index, element);
+ itemSet.remove(old);
+ itemSet.add(element);
+
+ return old;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Object clone() {
+ ListSet<E> v = (ListSet<E>) super.clone();
+ v.itemSet = new HashSet<E>(itemSet);
+ return v;
+ }
+
+}
diff --git a/src/com/vaadin/terminal/FileResource.java b/src/com/vaadin/terminal/FileResource.java index 46f1a6c028..571622b0e4 100644 --- a/src/com/vaadin/terminal/FileResource.java +++ b/src/com/vaadin/terminal/FileResource.java @@ -10,6 +10,7 @@ import java.io.FileNotFoundException; import com.vaadin.Application; import com.vaadin.service.FileTypeResolver; +import com.vaadin.terminal.Terminal.ErrorEvent; /** * <code>FileResources</code> are files or directories on local filesystem. The @@ -67,7 +68,15 @@ public class FileResource implements ApplicationResource { ds.setCacheTime(cacheTime); return ds; } catch (final FileNotFoundException e) { - // No logging for non-existing files at this level. + // Log the exception using the application error handler + getApplication().getErrorHandler().terminalError(new ErrorEvent() { + + public Throwable getThrowable() { + return e; + } + + }); + return null; } } diff --git a/src/com/vaadin/terminal/gwt/client/ApplicationConfiguration.java b/src/com/vaadin/terminal/gwt/client/ApplicationConfiguration.java index 39f4e845f7..5bcf613f6d 100644 --- a/src/com/vaadin/terminal/gwt/client/ApplicationConfiguration.java +++ b/src/com/vaadin/terminal/gwt/client/ApplicationConfiguration.java @@ -148,20 +148,15 @@ public class ApplicationConfiguration { int lastdot = module.lastIndexOf("."); String base = module.substring(0, lastdot); String simpleName = module.substring(lastdot + 1); - // if (!wsname.startsWith(base) || !wsname.endsWith(simpleName)) { - // // WidgetSet module name does not match implementation name; - // // probably inherited WidgetSet with entry-point. Skip. - // GWT.log("Ignored init for " + wsname + " when starting " + module, - // null); - // return; - // } if (initedWidgetSet != null) { - // Something went wrong: multiple widgetsets inited - String msg = "Tried to init " + widgetset.getClass().getName() - + ", but " + initedWidgetSet.getClass().getName() - + " was already inited."; - ApplicationConnection.getConsole().log(msg); + // Multiple widgetsets inited; can happen with custom WS + entry + // point + String msg = "Ignoring " + widgetset.getClass().getName() + + ", because " + initedWidgetSet.getClass().getName() + + " was already inited (if this is wrong, your entry point" + + " is probably not first your .gwt.xml)."; + throw new IllegalStateException(msg); } initedWidgetSet = widgetset; ArrayList<String> appIds = new ArrayList<String>(); diff --git a/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java b/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java index 34e3580608..8574bc0476 100755 --- a/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java +++ b/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java @@ -1116,7 +1116,7 @@ public class ApplicationConnection { * @param newValue * the new value to be sent * @param immediate - * true if the update is to be sent as suun as possible + * true if the update is to be sent as soon as possible */ public void updateVariable(String paintableId, String variableName, Paintable newValue, boolean immediate) { @@ -1139,7 +1139,7 @@ public class ApplicationConnection { * @param newValue * the new value to be sent * @param immediate - * true if the update is to be sent as suun as possible + * true if the update is to be sent as soon as possible */ public void updateVariable(String paintableId, String variableName, @@ -1162,7 +1162,7 @@ public class ApplicationConnection { * @param newValue * the new value to be sent * @param immediate - * true if the update is to be sent as suun as possible + * true if the update is to be sent as soon as possible */ public void updateVariable(String paintableId, String variableName, @@ -1186,7 +1186,7 @@ public class ApplicationConnection { * @param newValue * the new value to be sent * @param immediate - * true if the update is to be sent as suun as possible + * true if the update is to be sent as soon as possible */ public void updateVariable(String paintableId, String variableName, @@ -1210,7 +1210,7 @@ public class ApplicationConnection { * @param newValue * the new value to be sent * @param immediate - * true if the update is to be sent as suun as possible + * true if the update is to be sent as soon as possible */ public void updateVariable(String paintableId, String variableName, @@ -1234,7 +1234,7 @@ public class ApplicationConnection { * @param newValue * the new value to be sent * @param immediate - * true if the update is to be sent as suun as possible + * true if the update is to be sent as soon as possible */ public void updateVariable(String paintableId, String variableName, @@ -1258,7 +1258,7 @@ public class ApplicationConnection { * @param newValue * the new value to be sent * @param immediate - * true if the update is to be sent as suun as possible + * true if the update is to be sent as soon as possible */ public void updateVariable(String paintableId, String variableName, @@ -1282,7 +1282,7 @@ public class ApplicationConnection { * @param newValue * the new value to be sent * @param immediate - * true if the update is to be sent as suun as possible + * true if the update is to be sent as soon as possible */ public void updateVariable(String paintableId, String variableName, Map<String, Object> map, boolean immediate) { @@ -1347,7 +1347,7 @@ public class ApplicationConnection { * @param newValue * the new value to be sent * @param immediate - * true if the update is to be sent as suun as possible + * true if the update is to be sent as soon as possible */ public void updateVariable(String paintableId, String variableName, String[] values, boolean immediate) { @@ -1382,7 +1382,7 @@ public class ApplicationConnection { * @param newValue * the new value to be sent * @param immediate - * true if the update is to be sent as suun as possible + * true if the update is to be sent as soon as possible */ public void updateVariable(String paintableId, String variableName, Object[] values, boolean immediate) { diff --git a/src/com/vaadin/terminal/gwt/client/ClientExceptionHandler.java b/src/com/vaadin/terminal/gwt/client/ClientExceptionHandler.java index 724bd24a69..f1b01509d4 100644 --- a/src/com/vaadin/terminal/gwt/client/ClientExceptionHandler.java +++ b/src/com/vaadin/terminal/gwt/client/ClientExceptionHandler.java @@ -3,7 +3,7 @@ package com.vaadin.terminal.gwt.client; public class ClientExceptionHandler {
public static void displayError(Throwable e) {
- displayError(e.getMessage());
+ displayError(e.getClass().getName() + ": " + e.getMessage());
e.printStackTrace();
}
diff --git a/src/com/vaadin/terminal/gwt/client/DefaultWidgetSet.java b/src/com/vaadin/terminal/gwt/client/DefaultWidgetSet.java index 8ee6b0bb14..c34e5d8bb3 100644 --- a/src/com/vaadin/terminal/gwt/client/DefaultWidgetSet.java +++ b/src/com/vaadin/terminal/gwt/client/DefaultWidgetSet.java @@ -38,7 +38,14 @@ public class DefaultWidgetSet implements WidgetSet { * This is the entry point method. It will start the first */ public void onModuleLoad() { - ApplicationConfiguration.initConfigurations(this); + try { + ApplicationConfiguration.initConfigurations(this); + } catch (Exception e) { + // Log & don't continue; + // custom WidgetSets w/ entry points will cause this + ApplicationConnection.getConsole().log(e.getMessage()); + return; + } ApplicationConfiguration.startNextApplication(); // start first app map = GWT.create(WidgetMap.class); } diff --git a/src/com/vaadin/terminal/gwt/client/VDebugConsole.java b/src/com/vaadin/terminal/gwt/client/VDebugConsole.java index 45b8c9353b..172e4df627 100755 --- a/src/com/vaadin/terminal/gwt/client/VDebugConsole.java +++ b/src/com/vaadin/terminal/gwt/client/VDebugConsole.java @@ -8,6 +8,7 @@ import java.util.List; import java.util.Set; import com.google.gwt.core.client.JsArray; +import com.google.gwt.dom.client.Style.FontWeight; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.user.client.DOM; @@ -304,6 +305,10 @@ public final class VDebugConsole extends VOverlay implements Console { * @see com.vaadin.terminal.gwt.client.Console#log(java.lang.String) */ public void log(String msg) { + if (msg == null) { + msg = "null"; + } + panel.add(new HTML(msg)); System.out.println(msg); consoleLog(msg); @@ -315,7 +320,14 @@ public final class VDebugConsole extends VOverlay implements Console { * @see com.vaadin.terminal.gwt.client.Console#error(java.lang.String) */ public void error(String msg) { - panel.add((new HTML(msg))); + if (msg == null) { + msg = "null"; + } + + HTML html = new HTML(msg); + html.getElement().getStyle().setColor("#f00"); + html.getElement().getStyle().setFontWeight(FontWeight.BOLD); + panel.add(html); System.err.println(msg); consoleErr(msg); } @@ -327,7 +339,13 @@ public final class VDebugConsole extends VOverlay implements Console { * Object) */ public void printObject(Object msg) { - panel.add((new Label(msg.toString()))); + String str; + if (msg == null) { + str = "null"; + } else { + str = msg.toString(); + } + panel.add((new Label(str))); consoleLog(msg.toString()); } diff --git a/src/com/vaadin/terminal/gwt/client/ui/VCssLayout.java b/src/com/vaadin/terminal/gwt/client/ui/VCssLayout.java index bf13530d7f..90cf102cb9 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/VCssLayout.java +++ b/src/com/vaadin/terminal/gwt/client/ui/VCssLayout.java @@ -184,10 +184,11 @@ public class VCssLayout extends SimplePanel implements Paintable, Container { // loop oldWidgetWrappers that where not re-attached and unregister // them - for (final Iterator<Widget> it = oldWidgets.iterator(); it - .hasNext();) { - final Paintable w = (Paintable) it.next(); - client.unregisterPaintable(w); + for (Widget w : oldWidgets) { + if (w instanceof Paintable) { + final Paintable p = (Paintable) w; + client.unregisterPaintable(p); + } widgetToCaption.remove(w); } } diff --git a/src/com/vaadin/terminal/gwt/client/ui/VEmbedded.java b/src/com/vaadin/terminal/gwt/client/ui/VEmbedded.java index 7d69e5682a..ffd09dab2c 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/VEmbedded.java +++ b/src/com/vaadin/terminal/gwt/client/ui/VEmbedded.java @@ -140,6 +140,7 @@ public class VEmbedded extends HTML implements Paintable { parameters.put("movie", getSrc(uidl, client)); } + // Add the parameters to the Object for (String name : parameters.keySet()) { html += "<param name=\"" + escapeAttribute(name) + "\" value=\"" @@ -147,9 +148,15 @@ public class VEmbedded extends HTML implements Paintable { } html += "<embed src=\"" + getSrc(uidl, client) + "\" width=\"" - + width + "\" height=\"" + height + "\"></embed>"; + + width + "\" height=\"" + height + "\" "; - html += "</object>"; + // Add the parameters to the Embed + for (String name : parameters.keySet()) { + html += escapeAttribute(name) + "=\"" + + escapeAttribute(parameters.get(name)) + "\" "; + } + + html += "></embed></object>"; setHTML(html); } else if (mime.equals("image/svg+xml")) { String data; diff --git a/src/com/vaadin/terminal/gwt/client/ui/VFilterSelect.java b/src/com/vaadin/terminal/gwt/client/ui/VFilterSelect.java index 2dcf7562ae..669b5b90ea 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/VFilterSelect.java +++ b/src/com/vaadin/terminal/gwt/client/ui/VFilterSelect.java @@ -631,6 +631,23 @@ public class VFilterSelect extends Composite implements Paintable, Field, public void onLoad(LoadEvent event) { updateRootWidth(); updateSelectedIconPosition(); + + /* + * We need to re-calculate the widths in IE at load time to + * ensure that the text is not placed behind the icon. #3991 + */ + if (BrowserInfo.get().isIE()) { + int tbWidth = Util.getRequiredWidth(tb); + int openerWidth = Util.getRequiredWidth(popupOpener); + int iconWidth = selectedItemIcon.isAttached() ? Util + .measureMarginLeft(tb.getElement()) + - Util.measureMarginLeft(selectedItemIcon + .getElement()) : 0; + + int w = tbWidth + openerWidth + iconWidth; + tb.setWidth((tbWidth - getTextboxPadding()) + "px"); + setTextboxWidth(w); + } } }); @@ -883,8 +900,8 @@ public class VFilterSelect extends Composite implements Paintable, Field, panel.remove(selectedItemIcon); updateRootWidth(); } else { - selectedItemIcon.setUrl(iconUri); panel.insert(selectedItemIcon, 0); + selectedItemIcon.setUrl(iconUri); updateRootWidth(); updateSelectedIconPosition(); } diff --git a/src/com/vaadin/terminal/gwt/client/ui/VMenuBar.java b/src/com/vaadin/terminal/gwt/client/ui/VMenuBar.java index 0a28fd6b41..75ecbd3249 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/VMenuBar.java +++ b/src/com/vaadin/terminal/gwt/client/ui/VMenuBar.java @@ -8,11 +8,14 @@ import java.util.Stack; import com.google.gwt.dom.client.NodeList; import com.google.gwt.event.logical.shared.CloseEvent; import com.google.gwt.event.logical.shared.CloseHandler; +import com.google.gwt.event.logical.shared.ValueChangeEvent; +import com.google.gwt.event.logical.shared.ValueChangeHandler; import com.google.gwt.user.client.Command; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.DeferredCommand; import com.google.gwt.user.client.Element; import com.google.gwt.user.client.Event; +import com.google.gwt.user.client.History; import com.google.gwt.user.client.Timer; import com.google.gwt.user.client.ui.HasHTML; import com.google.gwt.user.client.ui.PopupPanel; @@ -27,7 +30,8 @@ import com.vaadin.terminal.gwt.client.UIDL; import com.vaadin.terminal.gwt.client.Util; public class VMenuBar extends Widget implements Paintable, - CloseHandler<PopupPanel>, ContainerResizedListener { +CloseHandler<PopupPanel>, ContainerResizedListener, +ValueChangeHandler<String> { /** Set the CSS class name to allow styling. */ public static final String CLASSNAME = "v-menubar"; @@ -76,6 +80,8 @@ public class VMenuBar extends Widget implements Paintable, if (!subMenu) { setStylePrimaryName(CLASSNAME); + // Monitor back&forward buttons + History.addValueChangeHandler(this); } else { setStylePrimaryName(CLASSNAME + "-submenu"); } @@ -866,4 +872,12 @@ public class VMenuBar extends Widget implements Paintable, return w; } + public void onValueChange(ValueChangeEvent<String> arg0) { + // Close menu if user uses back & forward buttons #4109 + if (!subMenu) { + setSelected(null); + hideChildren(); + menuVisible = false; + } + } } diff --git a/src/com/vaadin/terminal/gwt/client/ui/VNativeButton.java b/src/com/vaadin/terminal/gwt/client/ui/VNativeButton.java index e544e1f9ce..ef7e183698 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/VNativeButton.java +++ b/src/com/vaadin/terminal/gwt/client/ui/VNativeButton.java @@ -19,7 +19,7 @@ import com.vaadin.terminal.gwt.client.UIDL; import com.vaadin.terminal.gwt.client.Util; import com.vaadin.terminal.gwt.client.VTooltip; -public class VNativeButton extends Button implements Paintable { +public class VNativeButton extends Button implements Paintable, ClickHandler { public static final String CLASSNAME = "v-nativebutton"; @@ -48,18 +48,8 @@ public class VNativeButton extends Button implements Paintable { getElement().appendChild(captionElement); captionElement.setClassName(getStyleName() + "-caption"); - addClickHandler(new ClickHandler() { - public void onClick(ClickEvent event) { - if (id == null || client == null) { - return; - } - if (BrowserInfo.get().isSafari()) { - VNativeButton.this.setFocus(true); - } - client.updateVariable(id, "state", true, true); - clickPending = false; - } - }); + addClickHandler(this); + sinkEvents(VTooltip.TOOLTIP_EVENTS); sinkEvents(Event.ONMOUSEDOWN); sinkEvents(Event.ONMOUSEUP); @@ -179,4 +169,24 @@ public class VNativeButton extends Button implements Paintable { } } + /* + * (non-Javadoc) + * + * @see + * com.google.gwt.event.dom.client.ClickHandler#onClick(com.google.gwt.event + * .dom.client.ClickEvent) + */ + public void onClick(ClickEvent event) { + if (id == null || client == null) { + return; + } + + if (BrowserInfo.get().isSafari()) { + VNativeButton.this.setFocus(true); + } + + client.updateVariable(id, "state", true, true); + clickPending = false; + } + } diff --git a/src/com/vaadin/terminal/gwt/client/ui/VPanel.java b/src/com/vaadin/terminal/gwt/client/ui/VPanel.java index f9da8f67d9..d41a739655 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/VPanel.java +++ b/src/com/vaadin/terminal/gwt/client/ui/VPanel.java @@ -189,7 +189,6 @@ public class VPanel extends SimplePanel implements Container { } layout.updateFromUIDL(layoutUidl, client); - runHacks(false); // We may have actions attached to this panel if (uidl.getChildCount() > 1) { final int cnt = uidl.getChildCount(); @@ -224,6 +223,10 @@ public class VPanel extends SimplePanel implements Container { scrollLeft = contentNode.getScrollLeft(); } + // Must be run after scrollTop is set as Webkit overflow fix re-sets the + // scrollTop + runHacks(false); + rendering = false; } @@ -393,7 +396,8 @@ public class VPanel extends SimplePanel implements Container { super.setHeight(height); if (height != null && height != "") { final int targetHeight = getOffsetHeight(); - int containerHeight = targetHeight - captionNode.getOffsetHeight() + int containerHeight = targetHeight + - captionNode.getParentElement().getOffsetHeight() - bottomDecoration.getOffsetHeight() - getContainerBorderHeight(); if (containerHeight < 0) { diff --git a/src/com/vaadin/terminal/gwt/client/ui/VPopupCalendar.java b/src/com/vaadin/terminal/gwt/client/ui/VPopupCalendar.java index 8d83e8b2b5..9b504bc5f3 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/VPopupCalendar.java +++ b/src/com/vaadin/terminal/gwt/client/ui/VPopupCalendar.java @@ -52,7 +52,6 @@ public class VPopupCalendar extends VTextualDate implements Paintable, Field, public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
boolean lastReadOnlyState = readonly;
super.updateFromUIDL(uidl, client);
- addStyleName(CLASSNAME + "-popupcalendar");
popup.setStyleName(VDateField.CLASSNAME + "-popup "
+ VDateField.CLASSNAME + "-"
+ resolutionToString(currentResolution));
@@ -67,6 +66,12 @@ public class VPopupCalendar extends VTextualDate implements Paintable, Field, }
+ @Override
+ public void setStyleName(String style) {
+ // make sure the style is there before size calculation
+ super.setStyleName(style + " " + CLASSNAME + "-popupcalendar");
+ }
+
public void onClick(ClickEvent event) {
if (event.getSource() == calendarToggle && !open && !readonly) {
open = true;
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VScrollTable.java b/src/com/vaadin/terminal/gwt/client/ui/VScrollTable.java index 59c5d252fe..53f0033bcd 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/VScrollTable.java +++ b/src/com/vaadin/terminal/gwt/client/ui/VScrollTable.java @@ -19,11 +19,14 @@ import com.google.gwt.dom.client.TableRowElement; import com.google.gwt.dom.client.TableSectionElement; import com.google.gwt.event.dom.client.ScrollEvent; import com.google.gwt.event.dom.client.ScrollHandler; +import com.google.gwt.event.logical.shared.ValueChangeEvent; +import com.google.gwt.event.logical.shared.ValueChangeHandler; import com.google.gwt.user.client.Command; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.DeferredCommand; import com.google.gwt.user.client.Element; import com.google.gwt.user.client.Event; +import com.google.gwt.user.client.History; import com.google.gwt.user.client.Timer; import com.google.gwt.user.client.Window; import com.google.gwt.user.client.ui.FlowPanel; @@ -72,7 +75,7 @@ import com.vaadin.terminal.gwt.client.ui.dd.VerticalDropLocation; * TODO implement unregistering for child components in Cells */ public class VScrollTable extends FlowPanel implements Table, ScrollHandler, - VHasDropHandler { + VHasDropHandler, ValueChangeHandler<String> { public static final String CLASSNAME = "v-table"; public static final String ITEM_CLICK_EVENT_ID = "itemClick"; @@ -168,6 +171,8 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler, rowRequestHandler = new RowRequestHandler(); + // Handle back & forward browser buttons + History.addValueChangeHandler(this); } @SuppressWarnings("unchecked") @@ -3264,4 +3269,8 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler, } + public void onValueChange(ValueChangeEvent<String> arg0) { + client.getContextMenu().hide(); + } + } diff --git a/src/com/vaadin/terminal/gwt/client/ui/VSplitPanel.java b/src/com/vaadin/terminal/gwt/client/ui/VSplitPanel.java index d7a11427fb..101c1e9848 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/VSplitPanel.java +++ b/src/com/vaadin/terminal/gwt/client/ui/VSplitPanel.java @@ -122,6 +122,9 @@ public class VSplitPanel extends ComplexPanel implements Container, private boolean rendering = false; + /* The current position of the split handle in either percentages or pixels */ + private String position; + public VSplitPanel() { this(ORIENTATION_HORIZONTAL); } @@ -208,7 +211,8 @@ public class VSplitPanel extends ComplexPanel implements Container, setStylenames(); - setSplitPosition(uidl.getStringAttribute("position")); + position = uidl.getStringAttribute("position"); + setSplitPosition(position); final Paintable newFirstChild = client.getPaintable(uidl .getChildUIDL(0)); @@ -257,14 +261,26 @@ public class VSplitPanel extends ComplexPanel implements Container, } private void setSplitPosition(String pos) { + if (pos == null) { + return; + } + + // Convert percentage values to pixels + if (pos.indexOf("%") > 0) { + pos = Float.parseFloat(pos.substring(0, pos.length() - 1)) + / 100 + * (orientation == ORIENTATION_HORIZONTAL ? getOffsetWidth() + : getOffsetHeight()) + "px"; + } + if (orientation == ORIENTATION_HORIZONTAL) { DOM.setStyleAttribute(splitter, "left", pos); } else { DOM.setStyleAttribute(splitter, "top", pos); } + iLayout(); client.runDescendentsLayout(this); - } /* @@ -441,9 +457,6 @@ public class VSplitPanel extends ComplexPanel implements Container, onVerticalMouseMove(y); break; } - iLayout(); - // TODO Check if this is needed - client.runDescendentsLayout(this); } @@ -455,7 +468,18 @@ public class VSplitPanel extends ComplexPanel implements Container, if (newX + getSplitterSize() > getOffsetWidth()) { newX = getOffsetWidth() - getSplitterSize(); } - DOM.setStyleAttribute(splitter, "left", newX + "px"); + + if (position.indexOf("%") > 0) { + float pos = newX; + // 100% needs special handling + if (newX + getSplitterSize() >= getOffsetWidth()) { + pos = getOffsetWidth(); + } + position = pos / getOffsetWidth() * 100 + "%"; + } + + setSplitPosition(newX + "px"); + if (origX != newX) { resized = true; } @@ -470,7 +494,18 @@ public class VSplitPanel extends ComplexPanel implements Container, if (newY + getSplitterSize() > getOffsetHeight()) { newY = getOffsetHeight() - getSplitterSize(); } - DOM.setStyleAttribute(splitter, "top", newY + "px"); + + if (position.indexOf("%") > 0) { + float pos = newY; + // 100% needs special handling + if (newY + getSplitterSize() >= getOffsetHeight()) { + pos = getOffsetHeight(); + } + position = pos / getOffsetHeight() * 100 + "%"; + } + + setSplitPosition(newY + "px"); + if (origY != newY) { resized = true; } @@ -554,9 +589,9 @@ public class VSplitPanel extends ComplexPanel implements Container, this.height = height; super.setHeight(height); + if (!rendering && client != null) { - iLayout(); - client.runDescendentsLayout(this); + setSplitPosition(position); } } @@ -568,9 +603,9 @@ public class VSplitPanel extends ComplexPanel implements Container, this.width = width; super.setWidth(width); + if (!rendering && client != null) { - iLayout(); - client.runDescendentsLayout(this); + setSplitPosition(position); } } @@ -627,12 +662,14 @@ public class VSplitPanel extends ComplexPanel implements Container, * Updates the new split position back to server. */ private void updateSplitPositionToServer() { - // We always send pixel values to server - final String position = orientation == ORIENTATION_HORIZONTAL ? splitter - .getStyle().getProperty("left") - : splitter.getStyle().getProperty("top"); - final int pos = Integer.parseInt(position.substring(0, position - .length() - 2)); + int pos = 0; + if (position.indexOf("%") > 0) { + pos = Float.valueOf(position.substring(0, position.length() - 1)) + .intValue(); + } else { + pos = Integer + .parseInt(position.substring(0, position.length() - 2)); + } client.updateVariable(id, "position", pos, immediate); } diff --git a/src/com/vaadin/terminal/gwt/client/ui/VTextField.java b/src/com/vaadin/terminal/gwt/client/ui/VTextField.java index fe2c03496c..f4bc96f66a 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/VTextField.java +++ b/src/com/vaadin/terminal/gwt/client/ui/VTextField.java @@ -218,6 +218,7 @@ public class VTextField extends TextBoxBase implements Paintable, Field, if (prompting) { setText(""); removeStyleDependentName(CLASSNAME_PROMPT); + setPrompting(false); if (BrowserInfo.get().isIE6()) { // IE6 does not show the cursor when tabbing into the field setCursorPos(0); diff --git a/src/com/vaadin/terminal/gwt/client/ui/VView.java b/src/com/vaadin/terminal/gwt/client/ui/VView.java index 838b60a6d8..033d35dab1 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/VView.java +++ b/src/com/vaadin/terminal/gwt/client/ui/VView.java @@ -372,10 +372,6 @@ public class VView extends SimplePanel implements Container, ResizeHandler, onResize(Window.getClientWidth(), Window.getClientHeight()); - if (BrowserInfo.get().isSafari()) { - Util.runWebkitOverflowAutoFix(getElement()); - } - // finally set scroll position from UIDL if (uidl.hasVariable("scrollTop")) { scrollable = true; @@ -387,6 +383,12 @@ public class VView extends SimplePanel implements Container, ResizeHandler, scrollable = false; } + // Safari workaround must be run after scrollTop is updated as it sets + // scrollTop using a deferred command. + if (BrowserInfo.get().isSafari()) { + Util.runWebkitOverflowAutoFix(getElement()); + } + rendering = false; } diff --git a/src/com/vaadin/terminal/gwt/server/AbstractApplicationPortlet.java b/src/com/vaadin/terminal/gwt/server/AbstractApplicationPortlet.java index 428a9b38b9..148474bccc 100644 --- a/src/com/vaadin/terminal/gwt/server/AbstractApplicationPortlet.java +++ b/src/com/vaadin/terminal/gwt/server/AbstractApplicationPortlet.java @@ -14,6 +14,7 @@ import java.security.GeneralSecurityException; import java.util.Date; import java.util.Enumeration; import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.Locale; import java.util.Map; import java.util.Properties; @@ -28,12 +29,10 @@ import javax.portlet.PortalContext; import javax.portlet.PortletConfig; import javax.portlet.PortletContext; import javax.portlet.PortletException; -import javax.portlet.PortletMode; import javax.portlet.PortletRequest; import javax.portlet.PortletResponse; import javax.portlet.PortletSession; import javax.portlet.PortletURL; -import javax.portlet.RenderMode; import javax.portlet.RenderRequest; import javax.portlet.RenderResponse; import javax.portlet.ResourceRequest; @@ -47,9 +46,14 @@ import com.vaadin.Application.SystemMessages; import com.vaadin.external.org.apache.commons.fileupload.portlet.PortletFileUpload; import com.vaadin.terminal.DownloadStream; import com.vaadin.terminal.Terminal; +import com.vaadin.terminal.gwt.client.ApplicationConfiguration; +import com.vaadin.terminal.gwt.client.ApplicationConnection; import com.vaadin.ui.Window; /** + * Portlet 2.0 base class. This replaces the servlet in servlet/portlet 1.0 + * deployments and handles various portlet requests from the browser. + * * TODO Document me! * * @author peholmst @@ -193,7 +197,7 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet * the Default to be used. * @return String value or default if not found */ - private String getApplicationOrSystemProperty(String parameterName, + protected String getApplicationOrSystemProperty(String parameterName, String defaultValue) { String val = null; @@ -240,7 +244,7 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet } } - enum RequestType { + protected enum RequestType { FILE_UPLOAD, UIDL, RENDER, STATIC_FILE, APPLICATION_RESOURCE, DUMMY, EVENT, ACTION, UNKNOWN; } @@ -304,7 +308,7 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet RequestType requestType = getRequestType(request); if (requestType == RequestType.UNKNOWN) { - System.err.println("Unknown request type"); + handleUnknownRequest(request, response); } else if (requestType == RequestType.DUMMY) { /* * This dummy page is used by action responses to redirect to, in @@ -317,8 +321,7 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet final PrintWriter outWriter = new PrintWriter(new BufferedWriter( new OutputStreamWriter(out, "UTF-8"))); outWriter.print("<html><body>dummy page</body></html>"); - outWriter.flush(); - out.close(); + outWriter.close(); } else if (requestType == RequestType.STATIC_FILE) { serveStaticResources((ResourceRequest) request, (ResourceResponse) response); @@ -386,20 +389,43 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet /* Notify listeners */ + // Finds the window within the application + Window window = null; + synchronized (application) { + if (application.isRunning()) { + switch (requestType) { + case FILE_UPLOAD: + // no window + break; + case APPLICATION_RESOURCE: + // use main window - should not need any window + window = application.getMainWindow(); + break; + default: + window = applicationManager.getApplicationWindow( + request, this, application, null); + } + // if window not found, not a problem - use null + } + } + // TODO Should this happen before or after the transaction // starts? if (request instanceof RenderRequest) { applicationContext.firePortletRenderRequest(application, - (RenderRequest) request, (RenderResponse) response); + window, (RenderRequest) request, + (RenderResponse) response); } else if (request instanceof ActionRequest) { applicationContext.firePortletActionRequest(application, - (ActionRequest) request, (ActionResponse) response); + window, (ActionRequest) request, + (ActionResponse) response); } else if (request instanceof EventRequest) { applicationContext.firePortletEventRequest(application, - (EventRequest) request, (EventResponse) response); + window, (EventRequest) request, + (EventResponse) response); } else if (request instanceof ResourceRequest) { applicationContext.firePortletResourceRequest(application, - (ResourceRequest) request, + window, (ResourceRequest) request, (ResourceResponse) response); } @@ -412,7 +438,7 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet // Handles AJAX UIDL requests applicationManager.handleUidlRequest( (ResourceRequest) request, - (ResourceResponse) response, this); + (ResourceResponse) response, this, window); return; } else { /* @@ -423,45 +449,9 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet return; } - /* - * Always use the main window when running inside a portlet. - */ - Window window = getPortletWindow(request, application); - if (window == null) { - throw new PortletException(ERROR_NO_WINDOW_FOUND); - } - - /* - * Sets terminal type for the window, if not already set - */ - if (window.getTerminal() == null) { - window.setTerminal(applicationContext.getBrowser()); - } - - /* - * Handle parameters - */ - final Map<String, String[]> parameters = request - .getParameterMap(); - if (window != null && parameters != null) { - window.handleParameters(parameters); - } - - if (requestType == RequestType.APPLICATION_RESOURCE) { - handleURI(applicationManager, window, - (ResourceRequest) request, - (ResourceResponse) response); - } else if (requestType == RequestType.RENDER) { - writeAjaxPage((RenderRequest) request, - (RenderResponse) response, window, application); - } else if (requestType == RequestType.EVENT) { - // nothing to do, listeners do all the work - } else if (requestType == RequestType.ACTION) { - // nothing to do, listeners do all the work - } else { - throw new IllegalStateException( - "handleRequest() without anything to do - should never happen!"); - } + handleOtherRequest(request, response, requestType, + application, window, applicationContext, + applicationManager); } } catch (final SessionExpiredException e) { // TODO Figure out a better way to deal with @@ -494,26 +484,67 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet } } + private void handleUnknownRequest(PortletRequest request, + PortletResponse response) { + System.err.println("Unknown request type"); + } + /** - * Returns a window for a portlet mode. To define custom content for a - * portlet mode, add (in the application) a window whose name matches the - * portlet mode name. By default, the main window is returned. + * Handle a portlet request that is not for static files, UIDL or upload. + * Also render requests are handled here. * - * Alternatively, a PortletListener can change the main window content. + * This method is called after starting the application and calling portlet + * and transaction listeners. * * @param request + * @param response + * @param requestType * @param application - * @return Window to show in the portlet for the given portlet mode + * @param applicationContext + * @param applicationManager + * @throws PortletException + * @throws IOException + * @throws MalformedURLException */ - protected Window getPortletWindow(PortletRequest request, - Application application) { - PortletMode mode = request.getPortletMode(); - Window window = application.getWindow(mode.toString()); - if (window != null) { - return window; + private void handleOtherRequest(PortletRequest request, + PortletResponse response, RequestType requestType, + Application application, Window window, + PortletApplicationContext2 applicationContext, + PortletCommunicationManager applicationManager) + throws PortletException, IOException, MalformedURLException { + if (window == null) { + throw new PortletException(ERROR_NO_WINDOW_FOUND); + } + + /* + * Sets terminal type for the window, if not already set + */ + if (window.getTerminal() == null) { + window.setTerminal(applicationContext.getBrowser()); + } + + /* + * Handle parameters + */ + final Map<String, String[]> parameters = request.getParameterMap(); + if (window != null && parameters != null) { + window.handleParameters(parameters); + } + + if (requestType == RequestType.APPLICATION_RESOURCE) { + handleURI(applicationManager, window, (ResourceRequest) request, + (ResourceResponse) response); + } else if (requestType == RequestType.RENDER) { + writeAjaxPage((RenderRequest) request, (RenderResponse) response, + window, application); + } else if (requestType == RequestType.EVENT) { + // nothing to do, listeners do all the work + } else if (requestType == RequestType.ACTION) { + // nothing to do, listeners do all the work + } else { + throw new IllegalStateException( + "handleRequest() without anything to do - should never happen!"); } - // no specific window found - return application.getMainWindow(); } private void updateBrowserProperties(WebBrowser browser, @@ -648,53 +679,26 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet handleRequest(request, response); } - /** - * Handles a request for the "view" (default) portlet mode. In Vaadin, the - * basic portlet modes ("view", "edit" and "help") are handled identically, - * and their behavior can be changed by registering windows in the - * application with window names identical to the portlet mode names. - * Alternatively, a PortletListener can change the application main window - * contents. - * - * To implement custom portlet modes, subclass the portlet class and - * implement a method annotated with {@link RenderMode} for the custom mode, - * calling {@link #handleRequest(PortletRequest, PortletResponse)} directly - * from it. - * - * Note that the portlet class in the portlet configuration needs to be - * changed when overriding methods of this class. - * - * @param request - * @param response - * @throws PortletException - * @throws IOException - */ @Override - protected void doView(RenderRequest request, RenderResponse response) + protected void doDispatch(RenderRequest request, RenderResponse response) throws PortletException, IOException { - handleRequest(request, response); - } + try { + // try to let super handle - it'll call methods annotated for + // handling, the default doXYZ(), or throw if a handler for the mode + // is not found + super.doDispatch(request, response); - /** - * Handle a request for the "edit" portlet mode. - * - * @see #doView(RenderRequest, RenderResponse) - */ - @Override - protected void doEdit(RenderRequest request, RenderResponse response) - throws PortletException, IOException { - handleRequest(request, response); - } + } catch (PortletException e) { + if (e.getCause() == null) { + // No cause interpreted as 'unknown mode' - pass that trough + // so that the application can handle + handleRequest(request, response); - /** - * Handle a request for the "help" portlet mode. - * - * @see #doView(RenderRequest, RenderResponse) - */ - @Override - protected void doHelp(RenderRequest request, RenderResponse response) - throws PortletException, IOException { - handleRequest(request, response); + } else { + // Something else failed, pass on + throw e; + } + } } @Override @@ -833,17 +837,63 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet return null; } + /** + * Returns the URL from which the widgetset is served on the portal. + * + * @param widgetset + * @param request + * @return + */ protected String getWidgetsetURL(String widgetset, PortletRequest request) { return getStaticFilesLocation(request) + "/" + WIDGETSET_DIRECTORY_PATH + widgetset + "/" + widgetset + ".nocache.js?" + new Date().getTime(); } + /** + * Returns the theme URI for the named theme on the portal. + * + * Note that this is not the only location referring to the theme URI - also + * e.g. PortletCommunicationManager uses its own way to access the portlet + * 2.0 theme resources. + * + * @param themeName + * @param request + * @return + */ protected String getThemeURI(String themeName, PortletRequest request) { return getStaticFilesLocation(request) + "/" + THEME_DIRECTORY_PATH + themeName; } + /** + * Writes the html host page (aka kickstart page) that starts the actual + * Vaadin application. + * + * If one needs to override parts of the portlet HTML contents creation, it + * is suggested that one overrides one of several submethods including: + * <ul> + * <li> + * {@link #writeAjaxPageHtmlMainDiv(RenderRequest, RenderResponse, BufferedWriter, String)} + * <li> + * {@link #getVaadinConfigurationMap(RenderRequest, RenderResponse, Application, String)} + * <li> + * {@link #writeAjaxPageHtmlVaadinScripts(RenderRequest, RenderResponse, BufferedWriter, Application, String)} + * </ul> + * + * @param request + * the portlet request. + * @param response + * the portlet response to write to. + * @param window + * @param application + * @throws IOException + * if the writing failed due to input/output error. + * @throws MalformedURLException + * if the application is denied access the persistent data store + * represented by the given URL. + * @throws PortletException + */ protected void writeAjaxPage(RenderRequest request, RenderResponse response, Window window, Application application) throws IOException, MalformedURLException, PortletException { @@ -852,27 +902,62 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet final BufferedWriter page = new BufferedWriter(new OutputStreamWriter( response.getPortletOutputStream(), "UTF-8")); - // TODO check - String requestWidgetset = getApplicationOrSystemProperty( - PARAMETER_WIDGETSET, null); - String sharedWidgetset = getPortalProperty( - PORTAL_PARAMETER_VAADIN_WIDGETSET, request.getPortalContext()); - - String widgetset; - if (requestWidgetset != null) { - widgetset = requestWidgetset; - } else if (sharedWidgetset != null) { - widgetset = sharedWidgetset; - } else { - widgetset = DEFAULT_WIDGETSET; - } - // TODO Currently, we can only load widgetsets and themes from the // portal String themeName = getThemeForWindow(request, window); - String widgetsetURL = getWidgetsetURL(widgetset, request); + writeAjaxPageHtmlVaadinScripts(request, response, page, application, + themeName); + + /*- Add classnames; + * .v-app + * .v-app-loading + * .v-app-<simpleName for app class> + * .v-theme-<themeName, remove non-alphanum> + */ + String appClass = "v-app-"; + try { + appClass += getApplicationClass().getSimpleName(); + } catch (ClassNotFoundException e) { + appClass += "unknown"; + e.printStackTrace(); + } + String themeClass = "v-theme-" + + themeName.replaceAll("[^a-zA-Z0-9]", ""); + + String classNames = "v-app v-app-loading " + themeClass + " " + + appClass; + + String style = getApplicationProperty(PORTLET_PARAMETER_STYLE); + String divStyle = ""; + if (style != null) { + divStyle = "style=\"" + style + "\""; + } + + writeAjaxPageHtmlMainDiv(request, response, page, + request.getWindowID(), classNames, divStyle); + + page.close(); + } + + /** + * This method writes the scripts to load the widgetset and the themes as + * well as define Vaadin configuration parameters on the HTML fragment that + * starts the actual Vaadin application. + * + * @param request + * @param response + * @param writer + * @param application + * @param themeName + * @throws IOException + * @throws PortletException + */ + protected void writeAjaxPageHtmlVaadinScripts(RenderRequest request, + RenderResponse response, final BufferedWriter writer, + Application application, String themeName) throws IOException, + PortletException { String themeURI = getThemeURI(themeName, request); // fixed base theme to use - all portal pages with Vaadin @@ -880,52 +965,113 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet String portalTheme = getPortalProperty(PORTAL_PARAMETER_VAADIN_THEME, request.getPortalContext()); - // Get system messages - Application.SystemMessages systemMessages = null; - try { - systemMessages = getSystemMessages(); - } catch (SystemMessageException e) { - // failing to get the system messages is always a problem - throw new PortletException("Failed to obtain system messages!", e); - } - - page.write("<script type=\"text/javascript\">\n"); - page.write("if(!vaadin || !vaadin.vaadinConfigurations) {\n " + writer.write("<script type=\"text/javascript\">\n"); + writer.write("if(!vaadin || !vaadin.vaadinConfigurations) {\n " + "if(!vaadin) { var vaadin = {}} \n" + "vaadin.vaadinConfigurations = {};\n" + "if (!vaadin.themesLoaded) { vaadin.themesLoaded = {}; }\n"); if (!isProductionMode()) { - page.write("vaadin.debug = true;\n"); + writer.write("vaadin.debug = true;\n"); } - page + + writeAjaxPageScriptWidgetset(request, response, writer); + + Map<String, String> config = getVaadinConfigurationMap(request, + response, application, themeURI); + writeAjaxPageScriptConfigurations(request, response, writer, config); + + writer.write("</script>\n"); + + writeAjaxPageHtmlTheme(request, writer, themeName, themeURI, + portalTheme); + + // TODO Warn if widgetset has not been loaded after 15 seconds + } + + /** + * Writes the script to load the widgetset on the HTML fragment created by + * the portlet. + * + * @param request + * @param response + * @param writer + * @throws IOException + */ + protected void writeAjaxPageScriptWidgetset(RenderRequest request, + RenderResponse response, final BufferedWriter writer) + throws IOException { + String requestWidgetset = getApplicationOrSystemProperty( + PARAMETER_WIDGETSET, null); + String sharedWidgetset = getPortalProperty( + PORTAL_PARAMETER_VAADIN_WIDGETSET, request.getPortalContext()); + + String widgetset; + if (requestWidgetset != null) { + widgetset = requestWidgetset; + } else if (sharedWidgetset != null) { + widgetset = sharedWidgetset; + } else { + widgetset = DEFAULT_WIDGETSET; + } + String widgetsetURL = getWidgetsetURL(widgetset, request); + writer .write("document.write('<iframe tabIndex=\"-1\" id=\"__gwt_historyFrame\" " + "style=\"width:0;height:0;border:0;overflow:" + "hidden\" src=\"javascript:false\"></iframe>');\n"); - page.write("document.write(\"<script language='javascript' src='" + writer.write("document.write(\"<script language='javascript' src='" + widgetsetURL + "'><\\/script>\");\n}\n"); + } - page.write("vaadin.vaadinConfigurations[\"" + request.getWindowID() - + "\"] = {"); + /** + * Returns the configuration parameters to pass to the client. + * + * To add configuration parameters for the client, override, call the super + * method and then modify the map. Overriding this method may also require + * client side changes in {@link ApplicationConnection} and + * {@link ApplicationConfiguration}. + * + * Note that this method must escape and quote the values when appropriate. + * + * The map returned is typically a {@link LinkedHashMap} to preserve + * insertion order, but it is not guaranteed to be one. + * + * @param request + * @param response + * @param application + * @param themeURI + * @return modifiable Map from parameter name to its full value + * @throws PortletException + */ + protected Map<String, String> getVaadinConfigurationMap( + RenderRequest request, RenderResponse response, + Application application, String themeURI) throws PortletException { + Map<String, String> config = new LinkedHashMap<String, String>(); /* * We need this in order to get uploads to work. */ PortletURL appUri = response.createActionURL(); - - page.write("appUri: '" + appUri.toString() + "', "); - page.write("usePortletURLs: true, "); - + config.put("appUri", "'" + appUri.toString() + "'"); + config.put("usePortletURLs", "true"); ResourceURL uidlUrlBase = response.createResourceURL(); uidlUrlBase.setResourceID("UIDL"); + config.put("portletUidlURLBase", "'" + uidlUrlBase.toString() + "'"); + config.put("pathInfo", "''"); + config.put("themeUri", "'" + themeURI + "'"); + + String versionInfo = "{vaadinVersion:\"" + + AbstractApplicationServlet.VERSION + + "\",applicationVersion:\"" + application.getVersion() + "\"}"; + config.put("versionInfo", versionInfo); - page.write("portletUidlURLBase: '" + uidlUrlBase.toString() + "', "); - page.write("pathInfo: '', "); - page.write("themeUri: '" + themeURI + "', "); - page.write("versionInfo : {vaadinVersion:\""); - page.write(AbstractApplicationServlet.VERSION); - page.write("\",applicationVersion:\""); - page.write(application.getVersion()); - page.write("\"},"); + // Get system messages + Application.SystemMessages systemMessages = null; + try { + systemMessages = getSystemMessages(); + } catch (SystemMessageException e) { + // failing to get the system messages is always a problem + throw new PortletException("Failed to obtain system messages!", e); + } if (systemMessages != null) { // Write the CommunicationError -message to client String caption = systemMessages.getCommunicationErrorCaption(); @@ -941,73 +1087,117 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet url = "\"" + url + "\""; } - page.write("\"comErrMsg\": {" + "\"caption\":" + caption + "," + config.put("\"comErrMsg\"", "{" + "\"caption\":" + caption + "," + "\"message\" : " + message + "," + "\"url\" : " + url + "}"); } - page.write("};\n</script>\n"); - page.write("<script type=\"text/javascript\">\n"); + return config; + } + + /** + * Constructs the Vaadin configuration section for + * {@link ApplicationConnection} and {@link ApplicationConfiguration}. + * + * Typically this method should not be overridden. Instead, modify + * {@link #getVaadinConfigurationMap(RenderRequest, RenderResponse, Application, String)} + * . + * + * @param request + * @param response + * @param writer + * @param config + * @throws IOException + * @throws PortletException + */ + protected void writeAjaxPageScriptConfigurations(RenderRequest request, + RenderResponse response, final BufferedWriter writer, + Map<String, String> config) throws IOException, PortletException { + + writer.write("vaadin.vaadinConfigurations[\"" + request.getWindowID() + + "\"] = {"); + + Iterator<String> keyIt = config.keySet().iterator(); + while (keyIt.hasNext()) { + String key = keyIt.next(); + writer.write(key + ": " + config.get(key)); + if (keyIt.hasNext()) { + writer.write(", "); + } + } + + writer.write("};\n"); + } + + /** + * Writes the Vaadin theme loading section of the portlet HTML. Loads both + * the portal theme and the portlet theme in this order, skipping loading of + * themes that are already loaded (matched by name). + * + * @param request + * @param writer + * @param themeName + * @param themeURI + * @param portalTheme + * @throws IOException + */ + protected void writeAjaxPageHtmlTheme(RenderRequest request, + final BufferedWriter writer, String themeName, String themeURI, + String portalTheme) throws IOException { + writer.write("<script type=\"text/javascript\">\n"); if (portalTheme == null) { portalTheme = DEFAULT_THEME_NAME; } - page.write("if(!vaadin.themesLoaded['" + portalTheme + "']) {\n"); - page.write("var defaultStylesheet = document.createElement('link');\n"); - page.write("defaultStylesheet.setAttribute('rel', 'stylesheet');\n"); - page.write("defaultStylesheet.setAttribute('type', 'text/css');\n"); - page.write("defaultStylesheet.setAttribute('href', '" + writer.write("if(!vaadin.themesLoaded['" + portalTheme + "']) {\n"); + writer + .write("var defaultStylesheet = document.createElement('link');\n"); + writer.write("defaultStylesheet.setAttribute('rel', 'stylesheet');\n"); + writer.write("defaultStylesheet.setAttribute('type', 'text/css');\n"); + writer.write("defaultStylesheet.setAttribute('href', '" + getThemeURI(portalTheme, request) + "/styles.css');\n"); - page + writer .write("document.getElementsByTagName('head')[0].appendChild(defaultStylesheet);\n"); - page.write("vaadin.themesLoaded['" + portalTheme + "'] = true;\n}\n"); + writer.write("vaadin.themesLoaded['" + portalTheme + "'] = true;\n}\n"); if (!portalTheme.equals(themeName)) { - page.write("if(!vaadin.themesLoaded['" + themeName + "']) {\n"); - page.write("var stylesheet = document.createElement('link');\n"); - page.write("stylesheet.setAttribute('rel', 'stylesheet');\n"); - page.write("stylesheet.setAttribute('type', 'text/css');\n"); - page.write("stylesheet.setAttribute('href', '" + themeURI + writer.write("if(!vaadin.themesLoaded['" + themeName + "']) {\n"); + writer.write("var stylesheet = document.createElement('link');\n"); + writer.write("stylesheet.setAttribute('rel', 'stylesheet');\n"); + writer.write("stylesheet.setAttribute('type', 'text/css');\n"); + writer.write("stylesheet.setAttribute('href', '" + themeURI + "/styles.css');\n"); - page + writer .write("document.getElementsByTagName('head')[0].appendChild(stylesheet);\n"); - page.write("vaadin.themesLoaded['" + themeName + "'] = true;\n}\n"); - } - - page.write("</script>\n"); - - // TODO Warn if widgetset has not been loaded after 15 seconds - - /*- Add classnames; - * .v-app - * .v-app-loading - * .v-app-<simpleName for app class> - * .v-theme-<themeName, remove non-alphanum> - */ - - String appClass = "v-app-"; - try { - appClass += getApplicationClass().getSimpleName(); - } catch (ClassNotFoundException e) { - appClass += "unknown"; - e.printStackTrace(); + writer.write("vaadin.themesLoaded['" + themeName + + "'] = true;\n}\n"); } - String themeClass = "v-theme-" - + themeName.replaceAll("[^a-zA-Z0-9]", ""); - String classNames = "v-app v-app-loading " + themeClass + " " - + appClass; + writer.write("</script>\n"); + } - String style = getApplicationProperty(PORTLET_PARAMETER_STYLE); - String divStyle = ""; - if (style != null) { - divStyle = "style=\"" + style + "\""; - } - page.write("<div id=\"" + request.getWindowID() + "\" class=\"" + /** + * Method to write the div element into which that actual Vaadin application + * is rendered. + * <p> + * Override this method if you want to add some custom html around around + * the div element into which the actual Vaadin application will be + * rendered. + * + * @param request + * @param response + * @param writer + * @param id + * @param classNames + * @param divStyle + * @throws IOException + */ + protected void writeAjaxPageHtmlMainDiv(RenderRequest request, + RenderResponse response, final BufferedWriter writer, String id, + String classNames, String divStyle) throws IOException { + writer.write("<div id=\"" + id + "\" class=\"" + classNames + "\" " + divStyle + "></div>\n"); - - page.close(); } /** @@ -1017,7 +1207,7 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet * @param window * @return */ - private String getThemeForWindow(PortletRequest request, Window window) { + protected String getThemeForWindow(PortletRequest request, Window window) { // Finds theme name String themeName; @@ -1189,12 +1379,21 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet + "\"appError\": {" + "\"caption\":" + caption + "," + "\"message\" : " + message + "," + "\"url\" : " + url + "}}, \"resources\": {}, \"locales\":[]}]"); - outWriter.flush(); outWriter.close(); - out.flush(); } - private static String getPortalProperty(String name, PortalContext context) { + /** + * Returns a portal configuration property. + * + * Liferay is handled separately as + * {@link PortalContext#getProperty(String)} does not return portal + * properties from e.g. portal-ext.properties . + * + * @param name + * @param context + * @return + */ + protected static String getPortalProperty(String name, PortalContext context) { boolean isLifeRay = context.getPortalInfo().toLowerCase().contains( "liferay"); diff --git a/src/com/vaadin/terminal/gwt/server/AbstractApplicationServlet.java b/src/com/vaadin/terminal/gwt/server/AbstractApplicationServlet.java index 7a3978f323..8c901ba7e7 100644 --- a/src/com/vaadin/terminal/gwt/server/AbstractApplicationServlet.java +++ b/src/com/vaadin/terminal/gwt/server/AbstractApplicationServlet.java @@ -362,6 +362,9 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements HttpServletResponse response) throws ServletException, IOException { RequestType requestType = getRequestType(request); + if (!ensureCookiesEnabled(requestType, request, response)) { + return; + } if (requestType == RequestType.STATIC_FILE) { serveStaticResources(request, response); @@ -435,7 +438,10 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements return; } else if (requestType == RequestType.UIDL) { // Handles AJAX UIDL requests - applicationManager.handleUidlRequest(request, response, this); + Window window = applicationManager.getApplicationWindow( + request, this, application, null); + applicationManager.handleUidlRequest(request, response, this, + window); return; } @@ -502,6 +508,39 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements } } + /** + * Check that cookie support is enabled in the browser. Only checks UIDL + * requests. + * + * @param requestType + * Type of the request as returned by + * {@link #getRequestType(HttpServletRequest)} + * @param request + * The request from the browser + * @param response + * The response to which an error can be written + * @return false if cookies are disabled, true otherwise + * @throws IOException + */ + private boolean ensureCookiesEnabled(RequestType requestType, + HttpServletRequest request, HttpServletResponse response) + throws IOException { + if (requestType == RequestType.UIDL && !isRepaintAll(request)) { + // In all other but the first UIDL request a cookie should be + // returned by the browser. + // This can be removed if cookieless mode (#3228) is supported + if (request.getRequestedSessionId() == null) { + // User has cookies disabled + criticalNotification(request, response, getSystemMessages() + .getCookiesDisabledCaption(), getSystemMessages() + .getCookiesDisabledMessage(), null, getSystemMessages() + .getCookiesDisabledURL()); + return false; + } + } + return true; + } + private void updateBrowserProperties(WebBrowser browser, HttpServletRequest request) { browser.updateBrowserProperties(request.getLocale(), request @@ -535,38 +574,40 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements } /** - * Send notification to client's application. Used to notify client of - * critical errors and session expiration due to long inactivity. Server has - * no knowledge of what application client refers to. + * Send a notification to client's application. Used to notify client of + * critical errors, session expiration and more. Server has no knowledge of + * what application client refers to. * * @param request * the HTTP request instance. * @param response * the HTTP response to write to. * @param caption - * for the notification + * the notification caption * @param message - * for the notification + * to notification body * @param details - * a detail message to show in addition to the passed message. - * Currently shown directly but could be hidden behind a details - * drop down. + * a detail message to show in addition to the message. Currently + * shown directly below the message but could be hidden behind a + * details drop down in the future. Mainly used to give + * additional information not necessarily useful to the end user. * @param url - * url to load after message, null for current page + * url to load when the message is dismissed. Null will reload + * the current page. * @throws IOException * if the writing failed due to input/output error. */ - void criticalNotification(HttpServletRequest request, + protected final void criticalNotification(HttpServletRequest request, HttpServletResponse response, String caption, String message, String details, String url) throws IOException { - // clients JS app is still running, but server application either - // no longer exists or it might fail to perform reasonably. - // send a notification to client's application and link how - // to "restart" application. + if (!isUIDLRequest(request)) { + throw new RuntimeException( + "criticalNotification can only be used in UIDL requests"); + } if (caption != null) { - caption = "\"" + caption + "\""; + caption = "\"" + JsonPaintTarget.escapeJSON(caption) + "\""; } if (details != null) { if (message == null) { @@ -575,11 +616,12 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements message += "<br/><br/>" + details; } } + if (message != null) { - message = "\"" + message + "\""; + message = "\"" + JsonPaintTarget.escapeJSON(message) + "\""; } if (url != null) { - url = "\"" + url + "\""; + url = "\"" + JsonPaintTarget.escapeJSON(url) + "\""; } // Set the response type @@ -1489,7 +1531,7 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements * is rendered. * <p> * Override this method if you want to add some custom html around around - * the div element into which the actual vaadin application will be + * the div element into which the actual Vaadin application will be * rendered. * * @param page @@ -1507,8 +1549,7 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements } /** - * - * * Method to write the script part of the page which loads needed vaadin + * Method to write the script part of the page which loads needed Vaadin * scripts and themes. * <p> * Override this method if you want to add some custom html around scripts. diff --git a/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java b/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java index 67b806f0f0..481e42c0e9 100644 --- a/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java +++ b/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java @@ -498,11 +498,14 @@ public abstract class AbstractCommunicationManager implements * @param request * @param response * @param callback + * @param window + * target window for the UIDL request, can be null if target not + * found * @throws IOException * @throws InvalidUIDLSecurityKeyException */ protected void doHandleUidlRequest(Request request, Response response, - Callback callback) throws IOException, + Callback callback, Window window) throws IOException, InvalidUIDLSecurityKeyException { // repaint requested or session has timed out and new one is created @@ -528,10 +531,7 @@ public abstract class AbstractCommunicationManager implements synchronized (application) { // Finds the window within the application - Window window = null; if (application.isRunning()) { - window = doGetApplicationWindow(request, callback, application, - null); // Returns if no window found if (window == null) { // This should not happen, no windows exists but @@ -588,8 +588,7 @@ public abstract class AbstractCommunicationManager implements } } - // out.flush(); - this line will cause errors when deployed on GateIn. - out.close(); + outWriter.close(); } /** @@ -947,7 +946,6 @@ public abstract class AbstractCommunicationManager implements outWriter.print("}]"); } - outWriter.flush(); outWriter.close(); } @@ -1871,7 +1869,7 @@ public abstract class AbstractCommunicationManager implements } return stream; } else { - // Resolve the prefix end inded + // Resolve the prefix end index final int index = uri.indexOf('/'); if (index > 0) { String prefix = uri.substring(0, index); diff --git a/src/com/vaadin/terminal/gwt/server/CommunicationManager.java b/src/com/vaadin/terminal/gwt/server/CommunicationManager.java index 8799e37899..4e6bfbd158 100644 --- a/src/com/vaadin/terminal/gwt/server/CommunicationManager.java +++ b/src/com/vaadin/terminal/gwt/server/CommunicationManager.java @@ -238,16 +238,22 @@ public class CommunicationManager extends AbstractCommunicationManager { * * @param request * @param response + * @param applicationServlet + * @param window + * target window of the UIDL request, can be null if window not + * found * @throws IOException * @throws ServletException */ public void handleUidlRequest(HttpServletRequest request, HttpServletResponse response, - AbstractApplicationServlet applicationServlet) throws IOException, - ServletException, InvalidUIDLSecurityKeyException { + AbstractApplicationServlet applicationServlet, Window window) + throws IOException, ServletException, + InvalidUIDLSecurityKeyException { doHandleUidlRequest(new HttpServletRequestWrapper(request), new HttpServletResponseWrapper(response), - new AbstractApplicationServletWrapper(applicationServlet)); + new AbstractApplicationServletWrapper(applicationServlet), + window); } /** diff --git a/src/com/vaadin/terminal/gwt/server/PortletApplicationContext2.java b/src/com/vaadin/terminal/gwt/server/PortletApplicationContext2.java index ab1b1da81f..a6f8d7f204 100644 --- a/src/com/vaadin/terminal/gwt/server/PortletApplicationContext2.java +++ b/src/com/vaadin/terminal/gwt/server/PortletApplicationContext2.java @@ -139,18 +139,18 @@ public class PortletApplicationContext2 extends AbstractWebApplicationContext { } } - public void firePortletRenderRequest(Application app, + public void firePortletRenderRequest(Application app, Window window, RenderRequest request, RenderResponse response) { Set<PortletListener> listeners = portletListeners.get(app); if (listeners != null) { for (PortletListener l : listeners) { l.handleRenderRequest(request, new RestrictedRenderResponse( - response)); + response), window); } } } - public void firePortletActionRequest(Application app, + public void firePortletActionRequest(Application app, Window window, ActionRequest request, ActionResponse response) { String key = request.getParameter(ActionRequest.ACTION_NAME); if (eventActionDestinationMap.containsKey(key)) { @@ -172,28 +172,28 @@ public class PortletApplicationContext2 extends AbstractWebApplicationContext { Set<PortletListener> listeners = portletListeners.get(app); if (listeners != null) { for (PortletListener l : listeners) { - l.handleActionRequest(request, response); + l.handleActionRequest(request, response, window); } } } } - public void firePortletEventRequest(Application app, EventRequest request, - EventResponse response) { + public void firePortletEventRequest(Application app, Window window, + EventRequest request, EventResponse response) { Set<PortletListener> listeners = portletListeners.get(app); if (listeners != null) { for (PortletListener l : listeners) { - l.handleEventRequest(request, response); + l.handleEventRequest(request, response, window); } } } - public void firePortletResourceRequest(Application app, + public void firePortletResourceRequest(Application app, Window window, ResourceRequest request, ResourceResponse response) { Set<PortletListener> listeners = portletListeners.get(app); if (listeners != null) { for (PortletListener l : listeners) { - l.handleResourceRequest(request, response); + l.handleResourceRequest(request, response, window); } } } @@ -201,16 +201,16 @@ public class PortletApplicationContext2 extends AbstractWebApplicationContext { public interface PortletListener extends Serializable { public void handleRenderRequest(RenderRequest request, - RenderResponse response); + RenderResponse response, Window window); public void handleActionRequest(ActionRequest request, - ActionResponse response); + ActionResponse response, Window window); public void handleEventRequest(EventRequest request, - EventResponse response); + EventResponse response, Window window); public void handleResourceRequest(ResourceRequest request, - ResourceResponse response); + ResourceResponse response, Window window); } /** diff --git a/src/com/vaadin/terminal/gwt/server/PortletCommunicationManager.java b/src/com/vaadin/terminal/gwt/server/PortletCommunicationManager.java index 3a5cd90f7c..7fe324b1db 100644 --- a/src/com/vaadin/terminal/gwt/server/PortletCommunicationManager.java +++ b/src/com/vaadin/terminal/gwt/server/PortletCommunicationManager.java @@ -3,6 +3,7 @@ package com.vaadin.terminal.gwt.server; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.lang.reflect.Method; import javax.portlet.ActionRequest; import javax.portlet.ActionResponse; @@ -13,6 +14,8 @@ import javax.portlet.PortletResponse; import javax.portlet.PortletSession; import javax.portlet.ResourceRequest; import javax.portlet.ResourceResponse; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequestWrapper; import com.vaadin.Application; import com.vaadin.external.org.apache.commons.fileupload.FileItemIterator; @@ -54,7 +57,20 @@ public class PortletCommunicationManager extends AbstractCommunicationManager { } public String getParameter(String name) { - return request.getParameter(name); + String value = request.getParameter(name); + if (value == null) { + // for GateIn portlet container simple-portal + try { + Method getRealReq = request.getClass().getMethod( + "getRealRequest"); + HttpServletRequestWrapper origRequest = (HttpServletRequestWrapper) getRealReq + .invoke(request); + value = origRequest.getParameter(name); + } catch (Exception e) { + // do nothing - not on GateIn simple-portal + } + } + return value; } public String getRequestID() { @@ -165,19 +181,6 @@ public class PortletCommunicationManager extends AbstractCommunicationManager { + themeName + "/" + resource); } - /** - * Find the application window to use based on the portlet mode. For - * internal use only, not in the {@link Callback} interface. - * - * @param request - * @param application - * @return - */ - public Window getPortletWindow(PortletRequest request, - Application application) { - return portlet.getPortletWindow(request, application); - } - } public PortletCommunicationManager(Application application) { @@ -222,11 +225,12 @@ public class PortletCommunicationManager extends AbstractCommunicationManager { public void handleUidlRequest(ResourceRequest request, ResourceResponse response, - AbstractApplicationPortlet applicationPortlet) + AbstractApplicationPortlet applicationPortlet, Window window) throws InvalidUIDLSecurityKeyException, IOException { doHandleUidlRequest(new PortletRequestWrapper(request), new PortletResponseWrapper(response), - new AbstractApplicationPortletWrapper(applicationPortlet)); + new AbstractApplicationPortletWrapper(applicationPortlet), + window); } DownloadStream handleURI(Window window, ResourceRequest request, @@ -237,18 +241,29 @@ public class PortletCommunicationManager extends AbstractCommunicationManager { new AbstractApplicationPortletWrapper(applicationPortlet)); } - @Override - protected Window doGetApplicationWindow(Request request, Callback callback, + /** + * Gets the existing application or creates a new one. Get a window within + * an application based on the requested URI. + * + * @param request + * the portlet Request. + * @param applicationPortlet + * @param application + * the Application to query for window. + * @param assumedWindow + * if the window has been already resolved once, this parameter + * must contain the window. + * @return Window matching the given URI or null if not found. + * @throws ServletException + * if an exception has occurred that interferes with the + * servlet's normal operation. + */ + Window getApplicationWindow(PortletRequest request, + AbstractApplicationPortlet applicationPortlet, Application application, Window assumedWindow) { - // find window based on portlet mode - if (assumedWindow == null - && callback instanceof AbstractApplicationPortletWrapper - && request.getWrappedRequest() instanceof PortletRequest) { - assumedWindow = ((AbstractApplicationPortletWrapper) callback) - .getPortletWindow((PortletRequest) request - .getWrappedRequest(), application); - } - return super.doGetApplicationWindow(request, callback, application, - assumedWindow); + return doGetApplicationWindow(new PortletRequestWrapper(request), + new AbstractApplicationPortletWrapper(applicationPortlet), + application, assumedWindow); } + } diff --git a/src/com/vaadin/terminal/gwt/widgetsetutils/ClassPathExplorer.java b/src/com/vaadin/terminal/gwt/widgetsetutils/ClassPathExplorer.java index b3c670ba81..ca20b8b895 100644 --- a/src/com/vaadin/terminal/gwt/widgetsetutils/ClassPathExplorer.java +++ b/src/com/vaadin/terminal/gwt/widgetsetutils/ClassPathExplorer.java @@ -19,7 +19,6 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.TreeSet; import java.util.jar.Attributes; import java.util.jar.JarEntry; import java.util.jar.JarFile; @@ -175,7 +174,9 @@ public class ClassPathExplorer { for (int i = 0; i < widgetsetNames.length; i++) { String widgetsetname = widgetsetNames[i].trim() .intern(); - widgetsets.put(widgetsetname, location); + if (!widgetsetname.equals("")) { + widgetsets.put(widgetsetname, location); + } } } } @@ -439,8 +440,15 @@ public class ClassPathExplorer { * @return URL */ public static URL getDefaultSourceDirectory() { - logger.fine("classpathLocations keys: " - + new TreeSet<URL>(classpathLocations.keySet())); + if (logger.isLoggable(Level.FINE)) { + logger.fine("classpathLocations keys:"); + ArrayList<URL> locations = new ArrayList<URL>(classpathLocations + .keySet()); + for (URL location : locations) { + logger.fine(location.toString()); + } + } + Iterator<String> it = rawClasspathEntries.iterator(); while (it.hasNext()) { String entry = it.next(); diff --git a/src/com/vaadin/ui/Button.java b/src/com/vaadin/ui/Button.java index 3ed50b2d42..65a34a18bb 100644 --- a/src/com/vaadin/ui/Button.java +++ b/src/com/vaadin/ui/Button.java @@ -13,6 +13,7 @@ import com.vaadin.data.Property; import com.vaadin.terminal.PaintException; import com.vaadin.terminal.PaintTarget; import com.vaadin.terminal.gwt.client.ui.VButton; +import com.vaadin.ui.themes.BaseTheme; /** * A generic button component. @@ -239,7 +240,12 @@ public class Button extends AbstractField { private static final Method BUTTON_CLICK_METHOD; - /* Button style with no decorations. Looks like a link, acts like a button */ + /** + * Button style with no decorations. Looks like a link, acts like a button + * + * @deprecated use {@link BaseTheme#BUTTON_LINK} instead. + */ + @Deprecated public static final String STYLE_LINK = "link"; static { diff --git a/src/com/vaadin/ui/Form.java b/src/com/vaadin/ui/Form.java index c6c9a5a738..678c323dc3 100644 --- a/src/com/vaadin/ui/Form.java +++ b/src/com/vaadin/ui/Form.java @@ -479,54 +479,99 @@ public class Form extends AbstractField implements Item.Editor, Buffered, Item, return false; } + // Register and attach the created field addField(id, field); return true; } /** - * Adds the field to form. + * Registers the field with the form and adds the field to the form layout. * * <p> * The property id must not be already used in the form. * </p> * * <p> - * This field is added to the form layout in the default position (the - * position used by {@link Layout#addComponent(Component)} method. In the - * special case that the underlying layout is a custom layout, string - * representation of the property id is used instead of the default - * location. + * This field is added to the layout using the + * {@link #attachField(Object, Field)} method. * </p> * * @param propertyId * the Property id the the field. * @param field - * the New field added to the form. + * the field which should be added to the form. */ public void addField(Object propertyId, Field field) { + registerField(propertyId, field); + attachField(propertyId, field); + requestRepaint(); + } - if (propertyId != null && field != null) { - fields.put(propertyId, field); - field.addListener(fieldValueChangeListener); - if (!propertyIds.contains(propertyId)) { - // adding a field directly - propertyIds.addLast(propertyId); - } - field.setReadThrough(readThrough); - field.setWriteThrough(writeThrough); - if (isImmediate() && field instanceof AbstractComponent) { - ((AbstractComponent) field).setImmediate(true); - } - if (layout instanceof CustomLayout) { - ((CustomLayout) layout).addComponent(field, propertyId - .toString()); - } else { - layout.addComponent(field); - } + /** + * Register the field with the form. All registered fields are validated + * when the form is validated and also committed when the form is committed. + * + * <p> + * The property id must not be already used in the form. + * </p> + * + * + * @param propertyId + * the Property id of the field. + * @param field + * the Field that should be registered + */ + private void registerField(Object propertyId, Field field) { + if (propertyId == null || field == null) { + return; + } - requestRepaint(); + fields.put(propertyId, field); + field.addListener(fieldValueChangeListener); + if (!propertyIds.contains(propertyId)) { + // adding a field directly + propertyIds.addLast(propertyId); } + + // Update the read and write through status and immediate to match the + // form. + // Should this also include invalidCommitted (#3993)? + field.setReadThrough(readThrough); + field.setWriteThrough(writeThrough); + if (isImmediate() && field instanceof AbstractComponent) { + ((AbstractComponent) field).setImmediate(true); + } + } + + /** + * Adds the field to the form layout. + * <p> + * The field is added to the form layout in the default position (the + * position used by {@link Layout#addComponent(Component)}. If the + * underlying layout is a {@link CustomLayout} the field is added to the + * CustomLayout location given by the string representation of the property + * id using {@link CustomLayout#addComponent(Component, String)}. + * </p> + * + * <p> + * Override this method to control how the fields are added to the layout. + * </p> + * + * @param propertyId + * @param field + */ + protected void attachField(Object propertyId, Field field) { + if (propertyId == null || field == null) { + return; + } + + if (layout instanceof CustomLayout) { + ((CustomLayout) layout).addComponent(field, propertyId.toString()); + } else { + layout.addComponent(field); + } + } /** diff --git a/src/com/vaadin/ui/Panel.java b/src/com/vaadin/ui/Panel.java index df08c4e62b..f4ce352687 100644 --- a/src/com/vaadin/ui/Panel.java +++ b/src/com/vaadin/ui/Panel.java @@ -19,6 +19,8 @@ import com.vaadin.terminal.PaintTarget; import com.vaadin.terminal.Scrollable; import com.vaadin.terminal.gwt.client.MouseEventDetails; import com.vaadin.terminal.gwt.client.ui.VPanel; +import com.vaadin.ui.themes.Reindeer; +import com.vaadin.ui.themes.Runo; /** * Panel - a simple single component container. @@ -36,6 +38,16 @@ public class Panel extends AbstractComponentContainer implements Scrollable, private static final String CLICK_EVENT = VPanel.CLICK_EVENT_IDENTIFIER; + /** + * Removes extra decorations from the Panel. + * + * @deprecated this style is no longer part of the core framework and this + * component, even though most built-in themes implement this + * style. Use the constant specified in the theme class file + * that you're using, if it provides one, e.g. + * {@link Reindeer#PANEL_LIGHT} or {@link Runo#PANEL_LIGHT} . + */ + @Deprecated public static final String STYLE_LIGHT = "light"; /** diff --git a/src/com/vaadin/ui/SplitPanel.java b/src/com/vaadin/ui/SplitPanel.java index 5e67b93369..1a149d7394 100644 --- a/src/com/vaadin/ui/SplitPanel.java +++ b/src/com/vaadin/ui/SplitPanel.java @@ -281,10 +281,11 @@ public class SplitPanel extends AbstractLayout { * Moves the position of the splitter. * * @param pos - * the new size of the first region in percentage + * the new size of the first region in the unit that was last + * used (default is percentage) */ public void setSplitPosition(int pos) { - setSplitPosition(pos, UNITS_PERCENTAGE, true); + setSplitPosition(pos, posUnit, true); } /** @@ -303,7 +304,7 @@ public class SplitPanel extends AbstractLayout { * Moves the position of the splitter. * * @param pos - * the new size of the first region in percentage + * the new size of the first region * @param unit * the unit (from {@link Sizeable}) in which the size is given. * @param repaintNotNeeded @@ -312,6 +313,10 @@ public class SplitPanel extends AbstractLayout { * knows the position. */ private void setSplitPosition(int pos, int unit, boolean repaintNeeded) { + if (unit != UNITS_PERCENTAGE && unit != UNITS_PIXELS) { + throw new IllegalArgumentException( + "Only percentage and pixel units are allowed"); + } this.pos = pos; posUnit = unit; if (repaintNeeded) { @@ -353,8 +358,7 @@ public class SplitPanel extends AbstractLayout { if (variables.containsKey("position") && !isLocked()) { Integer newPos = (Integer) variables.get("position"); - // Client always sends pixel values. Repaint is not needed. - setSplitPosition(newPos, UNITS_PIXELS, false); + setSplitPosition(newPos, posUnit, false); } if (variables.containsKey(SPLITTER_CLICK_EVENT)) { diff --git a/src/com/vaadin/ui/Table.java b/src/com/vaadin/ui/Table.java index 9246067541..753a52a3de 100644 --- a/src/com/vaadin/ui/Table.java +++ b/src/com/vaadin/ui/Table.java @@ -40,14 +40,20 @@ import com.vaadin.terminal.gwt.client.ui.VScrollTable; /** * <p> - * <code>TableComponent</code> is used for representing data or components in - * pageable and selectable table. + * <code>Table</code> is used for representing data or components in a pageable + * and selectable table. * </p> * * <p> - * Note! Since version 5, components in Table will not have their caption nor - * icon rendered. In order to workaround this limitation, wrap your component in - * a Layout. + * Scalability of the Table is largely dictated by the container. A table does + * not have a limit for the number of items and is just as fast with hundreds of + * thousands of items as with just a few. The current GWT implementation with + * scrolling however limits the number of rows to around 500000, depending on + * the browser and the pixel height of rows. + * </p> + * + * <p> + * Components in a Table will not have their caption nor icon rendered. * </p> * * @author IT Mill Ltd. @@ -518,6 +524,8 @@ public class Table extends AbstractSelect implements Action.Container, } // Assures the visual refresh + // FIXME: Is this really needed? Header captions should not affect + // content so requestRepaint() should be sufficient. resetPageBuffer(); refreshRenderedCells(); } @@ -987,6 +995,8 @@ public class Table extends AbstractSelect implements Action.Container, columnHeaders.put(propertyId, header); // Assures the visual refresh + // FIXME: Is this really needed? Header captions should not affect + // content so requestRepaint() should be sufficient. refreshRenderedCells(); } diff --git a/src/com/vaadin/ui/Tree.java b/src/com/vaadin/ui/Tree.java index 665e98cfee..d28d02216c 100644 --- a/src/com/vaadin/ui/Tree.java +++ b/src/com/vaadin/ui/Tree.java @@ -45,6 +45,7 @@ import com.vaadin.terminal.gwt.client.ui.dd.VLazyInitItemIdentifiers; import com.vaadin.terminal.gwt.client.ui.dd.VOverTreeNode; import com.vaadin.terminal.gwt.client.ui.dd.VTargetNodeIsChildOf; import com.vaadin.terminal.gwt.client.ui.dd.VerticalDropLocation; +import com.vaadin.tools.ReflectTools; /** * Tree component. A Tree can be used to select an item (or multiple items) from @@ -60,29 +61,12 @@ import com.vaadin.terminal.gwt.client.ui.dd.VerticalDropLocation; public class Tree extends AbstractSelect implements Container.Hierarchical, Action.Container, ItemClickSource, DragSource, DropTarget { - private static final Method EXPAND_METHOD; - - private static final Method COLLAPSE_METHOD; - - static { - try { - EXPAND_METHOD = ExpandListener.class.getDeclaredMethod( - "nodeExpand", new Class[] { ExpandEvent.class }); - COLLAPSE_METHOD = CollapseListener.class.getDeclaredMethod( - "nodeCollapse", new Class[] { CollapseEvent.class }); - } catch (final java.lang.NoSuchMethodException e) { - // This should never happen - throw new java.lang.RuntimeException( - "Internal error finding methods in Tree"); - } - } - /* Private members */ /** * Set of expanded nodes. */ - private final HashSet expanded = new HashSet(); + private final HashSet<Object> expanded = new HashSet<Object>(); /** * List of action handlers. @@ -775,6 +759,9 @@ public class Tree extends AbstractSelect implements Container.Hierarchical, */ public interface ExpandListener extends Serializable { + public static final Method EXPAND_METHOD = ReflectTools.findMethod( + ExpandListener.class, "nodeExpand", ExpandEvent.class); + /** * A node has been expanded. * @@ -791,7 +778,7 @@ public class Tree extends AbstractSelect implements Container.Hierarchical, * the Listener to be added. */ public void addListener(ExpandListener listener) { - addListener(ExpandEvent.class, listener, EXPAND_METHOD); + addListener(ExpandEvent.class, listener, ExpandListener.EXPAND_METHOD); } /** @@ -801,7 +788,8 @@ public class Tree extends AbstractSelect implements Container.Hierarchical, * the Listener to be removed. */ public void removeListener(ExpandListener listener) { - removeListener(ExpandEvent.class, listener, EXPAND_METHOD); + removeListener(ExpandEvent.class, listener, + ExpandListener.EXPAND_METHOD); } /** @@ -860,6 +848,9 @@ public class Tree extends AbstractSelect implements Container.Hierarchical, */ public interface CollapseListener extends Serializable { + public static final Method COLLAPSE_METHOD = ReflectTools.findMethod( + CollapseListener.class, "nodeCollapse", CollapseEvent.class); + /** * A node has been collapsed. * @@ -876,7 +867,8 @@ public class Tree extends AbstractSelect implements Container.Hierarchical, * the Listener to be added. */ public void addListener(CollapseListener listener) { - addListener(CollapseEvent.class, listener, COLLAPSE_METHOD); + addListener(CollapseEvent.class, listener, + CollapseListener.COLLAPSE_METHOD); } /** @@ -886,7 +878,8 @@ public class Tree extends AbstractSelect implements Container.Hierarchical, * the Listener to be removed. */ public void removeListener(CollapseListener listener) { - removeListener(CollapseEvent.class, listener, COLLAPSE_METHOD); + removeListener(CollapseEvent.class, listener, + CollapseListener.COLLAPSE_METHOD); } /** @@ -1155,7 +1148,7 @@ public class Tree extends AbstractSelect implements Container.Hierarchical, Object itemIdOver = getItemIdOver(); if (areChildrenAllowed(itemIdOver) - && getDropLocation() != VerticalDropLocation.TOP) { + && getDropLocation() == VerticalDropLocation.MIDDLE) { return itemIdOver; } return getParent(itemIdOver); diff --git a/src/com/vaadin/ui/Window.java b/src/com/vaadin/ui/Window.java index 63daabdf90..f4657d81f6 100644 --- a/src/com/vaadin/ui/Window.java +++ b/src/com/vaadin/ui/Window.java @@ -1053,28 +1053,63 @@ public class Window extends Panel implements URIHandler, ParameterHandler { } } + /** + * An interface used for listening to Window close events. Add the + * CloseListener to a browser level window or a sub window and + * {@link CloseListener#windowClose(CloseEvent)} will be called whenever the + * user closes the window. + * + * <p> + * Note that removing windows using {@link #removeWindow(Window)} will not + * fire the CloseListener. + * </p> + */ public interface CloseListener extends Serializable { + /** + * Called when the user closes a window. Use + * {@link CloseEvent#getWindow()} to get a reference to the + * {@link Window} that was closed. + * + * @param e + * Event containing + */ public void windowClose(CloseEvent e); } /** - * Adds the listener. + * Adds a CloseListener to the window. + * + * For a sub window the CloseListener is fired when the user closes it + * (clicks on the close button). + * + * For a browser level window the CloseListener is fired when the browser + * level window is closed. Note that closing a browser level window does not + * mean it will be destroyed. + * + * <p> + * Note that removing windows using {@link #removeWindow(Window)} will not + * fire the CloseListener. + * </p> * * @param listener - * the listener to add. + * the CloseListener to add. */ public void addListener(CloseListener listener) { addListener(CloseEvent.class, listener, WINDOW_CLOSE_METHOD); } /** - * Removes the listener. + * Removes the CloseListener from the window. + * + * <p> + * For more information on CloseListeners see {@link CloseListener}. + * </p> * * @param listener - * the listener to remove. + * the CloseListener to remove. */ public void removeListener(CloseListener listener) { - addListener(CloseEvent.class, listener, WINDOW_CLOSE_METHOD); + removeListener(CloseEvent.class, listener, WINDOW_CLOSE_METHOD); } protected void fireClose() { diff --git a/src/com/vaadin/ui/themes/BaseTheme.java b/src/com/vaadin/ui/themes/BaseTheme.java new file mode 100644 index 0000000000..48083f5240 --- /dev/null +++ b/src/com/vaadin/ui/themes/BaseTheme.java @@ -0,0 +1,44 @@ +package com.vaadin.ui.themes; + +/** + * <p> + * The Base theme is the foundation for all Vaadin themes. Although it is not + * necessary to use it as the starting point for all other themes, it is heavily + * encouraged, since it abstracts and hides away many necessary style properties + * that the Vaadin terminal expects and needs. + * </p> + * <p> + * When creating your own theme, either extend this class and specify the styles + * implemented in your theme here, or extend some other theme that has a class + * file specified (e.g. Reindeer or Runo). + * </p> + * <p> + * All theme class files should follow the convention of specifying the theme + * name as a string constant <code>THEME_NAME</code>. + * + * @since 6.3.0 + * + */ +public class BaseTheme { + + public static final String THEME_NAME = "Base"; + + /** + * Creates a button that looks like a regular hypertext link but still acts + * like a normal button. + */ + public static final String BUTTON_LINK = "link"; + + /** + * Removes extra decorations from the panel. + * + * @deprecated Base theme does not implement this style, but it is defined + * here since it has been a part of the framework before + * multiple themes were available. Use the constant provided by + * the theme you're using instead, e.g. + * {@link Reindeer#PANEL_LIGHT} or {@link Runo#PANEL_LIGHT}. + */ + @Deprecated + public static final String PANEL_LIGHT = "light"; + +}
\ No newline at end of file diff --git a/src/com/vaadin/ui/themes/Reindeer.java b/src/com/vaadin/ui/themes/Reindeer.java new file mode 100644 index 0000000000..acbf003151 --- /dev/null +++ b/src/com/vaadin/ui/themes/Reindeer.java @@ -0,0 +1,163 @@ +package com.vaadin.ui.themes; + +import com.vaadin.ui.CssLayout; +import com.vaadin.ui.FormLayout; +import com.vaadin.ui.GridLayout; +import com.vaadin.ui.HorizontalLayout; +import com.vaadin.ui.VerticalLayout; + +public class Reindeer extends BaseTheme { + + public static final String THEME_NAME = "Reindeer"; + + /*************************************************************************** + * + * Label styles + * + **************************************************************************/ + + /** + * Large font for main application headings + */ + public static final String LABEL_H1 = "h1"; + + /** + * Large font for different sections in the application + */ + public static final String LABEL_H2 = "h2"; + + /** + * Small and a little lighter font + */ + public static final String LABEL_SMALL = "light"; + + /*************************************************************************** + * + * Button styles + * + **************************************************************************/ + + /** + * Default action style for buttons (the button that gets activated when + * user presses 'enter' in a form). Use sparingly, only one default button + * per screen should be visible. + */ + public static final String BUTTON_DEFAULT = "primary"; + + /** + * Small sized button, use for context specific actions for example + */ + public static final String BUTTON_SMALL = "small"; + + /*************************************************************************** + * + * TextField styles + * + **************************************************************************/ + + /** + * Small sized text field with small font + */ + public static final String TEXTFIELD_SMALL = "small"; + + /*************************************************************************** + * + * Panel styles + * + **************************************************************************/ + + /** + * Removes borders and background color from the panel + */ + public static final String PANEL_LIGHT = "light"; + + /*************************************************************************** + * + * SplitPanel styles + * + **************************************************************************/ + + /** + * Reduces the split handle to a minimal size (1 pixel) + */ + public static final String SPLITPANEL_SMALL = "small"; + + /*************************************************************************** + * + * TabSheet styles + * + **************************************************************************/ + + /** + * Removes borders and background color from the tab sheet, and shows the + * tabs as a small bar. + */ + public static final String TABSHEET_BAR = "bar"; + + /** + * Removes borders and background color from the tab sheet. The tabs are + * presented with minimal lines indicating the selected tab. + */ + public static final String TABSHEET_MINIMAL = "minimal"; + + /*************************************************************************** + * + * Table styles + * + **************************************************************************/ + + /** + * Removes borders from the table + */ + public static final String TABLE_BORDERLESS = "borderless"; + + /** + * Makes the table headers dark and more prominent. + */ + public static final String TABLE_STRONG = "strong"; + + /*************************************************************************** + * + * Layout styles + * + **************************************************************************/ + + /** + * Changes the background of a layout to a shade of blue. Applies to + * {@link VerticalLayout}, {@link HorizontalLayout}, {@link GridLayout}, + * {@link FormLayout} and {@link CssLayout}. + */ + public static final String LAYOUT_BLUE = "blue"; + + /** + * <p> + * Changes the background of a layout to almost black, and at the same time + * transforms contained components to their black style correspondents when + * available. At least texts, buttons, text fields, selects, date fields, + * tables and a few other component styles should change. + * </p> + * <p> + * Applies to {@link VerticalLayout}, {@link HorizontalLayout}, + * {@link GridLayout}, {@link FormLayout} and {@link CssLayout}. + * </p> + * + */ + public static final String LAYOUT_BLACK = "black"; + + /*************************************************************************** + * + * Window styles + * + **************************************************************************/ + + /** + * Makes the whole window white and increases the font size of the title. + */ + public static final String WINDOW_LIGHT = "light"; + + /** + * Makes the whole window black, and changes contained components in the + * same way as {@link #LAYOUT_BLACK} does. + */ + public static final String WINDOW_BLACK = "black"; +} diff --git a/src/com/vaadin/ui/themes/Runo.java b/src/com/vaadin/ui/themes/Runo.java new file mode 100644 index 0000000000..4df6046756 --- /dev/null +++ b/src/com/vaadin/ui/themes/Runo.java @@ -0,0 +1,30 @@ +package com.vaadin.ui.themes; + + +public class Runo extends BaseTheme { + + public static final String THEME_NAME = "Runo"; + + /*************************************************************************** + * + * Button styles + * + **************************************************************************/ + + /** + * Small sized button, use for context specific actions for example + */ + public static final String BUTTON_SMALL = "small"; + + /*************************************************************************** + * + * Panel styles + * + **************************************************************************/ + + /** + * Removes borders and background color from the panel + */ + public static final String PANEL_LIGHT = "light"; + +} |