diff options
author | Henri Sara <henri.sara@itmill.com> | 2011-06-16 07:19:43 +0000 |
---|---|---|
committer | Henri Sara <henri.sara@itmill.com> | 2011-06-16 07:19:43 +0000 |
commit | 79c52ac3d4756a6f8cbb94b9e7dc264ba61c5430 (patch) | |
tree | d2ede4345c21d3caffe5dd2873f10ccb4aabbfff /src/com | |
parent | 3239587ad38fb20df82a235d732c10d88aebe5b8 (diff) | |
parent | 3547a37b82a25f2f8a7ecf4ca012094d62159ab3 (diff) | |
download | vaadin-framework-79c52ac3d4756a6f8cbb94b9e7dc264ba61c5430.tar.gz vaadin-framework-79c52ac3d4756a6f8cbb94b9e7dc264ba61c5430.zip |
Merge from 6.6
svn changeset:19412/svn branch:6.7
Diffstat (limited to 'src/com')
-rw-r--r-- | src/com/vaadin/data/util/HierarchicalContainer.java | 18 | ||||
-rwxr-xr-x | src/com/vaadin/terminal/gwt/client/ApplicationConnection.java | 3 | ||||
-rw-r--r-- | src/com/vaadin/terminal/gwt/client/ui/VMenuBar.java | 223 | ||||
-rw-r--r-- | src/com/vaadin/terminal/gwt/client/ui/VOverlay.java | 113 | ||||
-rw-r--r-- | src/com/vaadin/terminal/gwt/client/ui/VScrollTable.java | 4 | ||||
-rw-r--r-- | src/com/vaadin/terminal/gwt/client/ui/VTextField.java | 18 | ||||
-rw-r--r-- | src/com/vaadin/terminal/gwt/client/ui/VWindow.java | 48 | ||||
-rw-r--r-- | src/com/vaadin/terminal/gwt/server/AbstractApplicationPortlet.java | 22 | ||||
-rw-r--r-- | src/com/vaadin/terminal/gwt/server/AbstractApplicationServlet.java | 16 | ||||
-rw-r--r-- | src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java | 4 | ||||
-rw-r--r-- | src/com/vaadin/terminal/gwt/server/WebBrowser.java | 200 | ||||
-rw-r--r-- | src/com/vaadin/ui/AbstractOrderedLayout.java | 2 | ||||
-rw-r--r-- | src/com/vaadin/ui/Button.java | 100 | ||||
-rw-r--r-- | src/com/vaadin/ui/MenuBar.java | 122 | ||||
-rw-r--r-- | src/com/vaadin/ui/themes/BaseTheme.java | 8 |
15 files changed, 640 insertions, 261 deletions
diff --git a/src/com/vaadin/data/util/HierarchicalContainer.java b/src/com/vaadin/data/util/HierarchicalContainer.java index d671dbc833..99de7a1352 100644 --- a/src/com/vaadin/data/util/HierarchicalContainer.java +++ b/src/com/vaadin/data/util/HierarchicalContainer.java @@ -471,6 +471,9 @@ public class HierarchicalContainer extends IndexedContainer implements if (filteredChildren != null) { filteredChildren = null; } + if (filteredParent != null) { + filteredParent = null; + } } enableAndFireContentsChangeEvents(); return success; @@ -516,18 +519,30 @@ public class HierarchicalContainer extends IndexedContainer implements if (c != null) { c.remove(itemId); + if (c.isEmpty()) { + children.remove(parentItemId); + } + // 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); + f.remove(itemId); + if (f.isEmpty()) { + filteredChildren.remove(parentItemId); + } } } } } parent.remove(itemId); + if (filteredParent != null) { + // Item id no longer has a parent as the item id is not in the + // container. + filteredParent.remove(itemId); + } noChildrenAllowed.remove(itemId); } @@ -642,6 +657,7 @@ public class HierarchicalContainer extends IndexedContainer implements // All filters removed filteredRoots = null; filteredChildren = null; + filteredParent = null; return super.doFilterContainer(hasFilters); } diff --git a/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java b/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java index 5f808ea8cd..056428881f 100755 --- a/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java +++ b/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java @@ -367,7 +367,8 @@ public class ApplicationConnection { + "&vw=" + offsetWidth + "&vh=" + offsetHeight + "&fr=" + token + "&tzo=" + tzOffset + "&rtzo=" + rtzOffset + "&dstd=" + dstDiff + "&dston=" + dstInEffect + "&curdate=" + curDate - + "&wsver=" + widgetsetVersion; + + "&wsver=" + widgetsetVersion + + (BrowserInfo.get().isTouchDevice() ? "&td=1" : ""); return parameters; } diff --git a/src/com/vaadin/terminal/gwt/client/ui/VMenuBar.java b/src/com/vaadin/terminal/gwt/client/ui/VMenuBar.java index 996b719e33..4d43b7c346 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/VMenuBar.java +++ b/src/com/vaadin/terminal/gwt/client/ui/VMenuBar.java @@ -8,6 +8,7 @@ import java.util.Iterator; import java.util.List; import java.util.Stack; +import com.google.gwt.core.client.GWT; import com.google.gwt.core.client.Scheduler; import com.google.gwt.core.client.Scheduler.ScheduledCommand; import com.google.gwt.dom.client.NodeList; @@ -66,6 +67,8 @@ public class VMenuBar extends SimpleFocusablePanel implements Paintable, public static final String OPEN_ROOT_MENU_ON_HOWER = "ormoh"; + public static final String ATTRIBUTE_CHECKED = "checked"; + /** Widget fields **/ protected boolean subMenu; protected ArrayList<CustomMenuItem> items; @@ -154,8 +157,8 @@ public class VMenuBar extends SimpleFocusablePanel implements Paintable, // special case that uses another method. Really should fix the // Util.setWidthExcludingPaddingAndBorder method but that will // probably break additional cases - int requestedPixelWidth = Integer.parseInt(width.substring(0, width - .length() - 2)); + int requestedPixelWidth = Integer.parseInt(width.substring(0, + width.length() - 2)); int paddingBorder = Util.measureHorizontalPaddingAndBorder( getElement(), 0); int w = requestedPixelWidth - paddingBorder; @@ -228,9 +231,12 @@ public class VMenuBar extends SimpleFocusablePanel implements Paintable, } itemHTML.append(moreItemText); - moreItem = new CustomMenuItem(itemHTML.toString(), emptyCommand); - collapsedRootItems = new VMenuBar(true, (VMenuBar) client - .getPaintable(uidlId)); + moreItem = GWT.create(CustomMenuItem.class); + moreItem.setHTML(itemHTML.toString()); + moreItem.setCommand(emptyCommand); + + collapsedRootItems = new VMenuBar(true, + (VMenuBar) client.getPaintable(uidlId)); moreItem.setSubMenu(collapsedRootItems); moreItem.addStyleName(CLASSNAME + "-more-menuitem"); } @@ -245,45 +251,16 @@ public class VMenuBar extends SimpleFocusablePanel implements Paintable, UIDL item = (UIDL) itr.next(); CustomMenuItem currentItem = null; - String itemText = item.getStringAttribute("text"); final int itemId = item.getIntAttribute("id"); boolean itemHasCommand = item.hasAttribute("command"); + boolean itemIsCheckable = item.hasAttribute(ATTRIBUTE_CHECKED); - // Construct html from the text and the optional icon - StringBuffer itemHTML = new StringBuffer(); - Command cmd = null; - - if (item.hasAttribute("separator")) { - itemHTML.append("<span>---</span>"); - } else { - // Add submenu indicator - if (item.getChildCount() > 0) { - // FIXME For compatibility reasons: remove in version 7 - String bgStyle = ""; - if (submenuIcon != null) { - bgStyle = " style=\"background-image: url(" - + submenuIcon - + "); text-indent: -999px; width: 1em;\""; - } - itemHTML.append("<span class=\"" + CLASSNAME - + "-submenu-indicator\"" + bgStyle - + ">►</span>"); - } - - itemHTML.append("<span class=\"" + CLASSNAME - + "-menuitem-caption\">"); - if (item.hasAttribute("icon")) { - itemHTML - .append("<img src=\"" - + client.translateVaadinUri(item - .getStringAttribute("icon")) - + "\" class=\"" + Icon.CLASSNAME - + "\" alt=\"\" />"); - } - itemHTML.append(Util.escapeHTML(itemText) + "</span>"); + String itemHTML = buildItemHTML(item); - if (itemHasCommand) { + Command cmd = null; + if (!item.hasAttribute("separator")) { + if (itemHasCommand || itemIsCheckable) { // Construct a command that fires onMenuClick(int) with the // item's id-number cmd = new Command() { @@ -312,6 +289,17 @@ public class VMenuBar extends SimpleFocusablePanel implements Paintable, } while (!itr.hasNext() && !iteratorStack.empty()) { + boolean hasCheckableItem = false; + for (CustomMenuItem menuItem : currentMenu.getItems()) { + hasCheckableItem = hasCheckableItem + || menuItem.isCheckable(); + } + if (hasCheckableItem) { + currentMenu.addStyleDependentName("check-column"); + } else { + currentMenu.removeStyleDependentName("check-column"); + } + itr = iteratorStack.pop(); currentMenu = menuStack.pop(); } @@ -322,6 +310,45 @@ public class VMenuBar extends SimpleFocusablePanel implements Paintable, }// updateFromUIDL /** + * Build the HTML content for a menu item. + * + * @param item + * @return + */ + protected String buildItemHTML(UIDL item) { + // Construct html from the text and the optional icon + StringBuffer itemHTML = new StringBuffer(); + if (item.hasAttribute("separator")) { + itemHTML.append("<span>---</span>"); + } else { + // Add submenu indicator + if (item.getChildCount() > 0) { + // FIXME For compatibility reasons: remove in version 7 + String bgStyle = ""; + if (submenuIcon != null) { + bgStyle = " style=\"background-image: url(" + submenuIcon + + "); text-indent: -999px; width: 1em;\""; + } + itemHTML.append("<span class=\"" + CLASSNAME + + "-submenu-indicator\"" + bgStyle + ">►</span>"); + } + + itemHTML.append("<span class=\"" + CLASSNAME + + "-menuitem-caption\">"); + if (item.hasAttribute("icon")) { + itemHTML.append("<img src=\"" + + client.translateVaadinUri(item + .getStringAttribute("icon")) + "\" class=\"" + + Icon.CLASSNAME + "\" alt=\"\" />"); + } + String itemText = item.getStringAttribute("text"); + itemHTML.append(Util.escapeHTML(itemText)); + itemHTML.append("</span>"); + } + return itemHTML.toString(); + } + + /** * This is called by the items in the menu and it communicates the * information to the server * @@ -379,7 +406,10 @@ public class VMenuBar extends SimpleFocusablePanel implements Paintable, * @return the item created */ public CustomMenuItem addItem(String html, Command cmd) { - CustomMenuItem item = new CustomMenuItem(html, cmd); + CustomMenuItem item = GWT.create(CustomMenuItem.class); + item.setHTML(html); + item.setCommand(cmd); + addItem(item); return item; } @@ -418,8 +448,8 @@ public class VMenuBar extends SimpleFocusablePanel implements Paintable, if (items.contains(item)) { int index = items.indexOf(item); - DOM.removeChild(getContainerElement(), DOM.getChild( - getContainerElement(), index)); + DOM.removeChild(getContainerElement(), + DOM.getChild(getContainerElement(), index)); items.remove(index); } } @@ -672,8 +702,7 @@ public class VMenuBar extends SimpleFocusablePanel implements Paintable, popup.show(); if (left + popup.getOffsetWidth() >= RootPanel.getBodyElement() - .getOffsetWidth() - - shadowSpace) { + .getOffsetWidth() - shadowSpace) { if (subMenu) { left = item.getParentMenu().getAbsoluteLeft() - popup.getOffsetWidth() - shadowSpace; @@ -701,8 +730,8 @@ public class VMenuBar extends SimpleFocusablePanel implements Paintable, } if (popup.getElement().getStyle().getProperty("width") == null - || popup.getElement().getStyle().getProperty( - "width") == "") { + || popup.getElement().getStyle() + .getProperty("width") == "") { popup.setWidth(popup.getOffsetWidth() + "px"); } popup.getElement().getStyle().setProperty("zoom", "1"); @@ -835,7 +864,28 @@ public class VMenuBar extends SimpleFocusablePanel implements Paintable, protected VMenuBar parentMenu = null; protected boolean enabled = true; protected boolean isSeparator = false; + protected boolean checkable = false; + protected boolean checked = false; + + /** + * Default menu item {@link Widget} constructor for GWT.create(). + * + * Use {@link #setHTML(String)} and {@link #setCommand(Command)} after + * constructing a menu item. + */ + public CustomMenuItem() { + this("", null); + } + /** + * Creates a menu item {@link Widget}. + * + * @param html + * @param cmd + * @deprecated use the default constructor and {@link #setHTML(String)} + * and {@link #setCommand(Command)} instead + */ + @Deprecated public CustomMenuItem(String html, Command cmd) { // We need spans to allow inline-block in IE setElement(DOM.createSpan()); @@ -846,20 +896,64 @@ public class VMenuBar extends SimpleFocusablePanel implements Paintable, setStyleName(CLASSNAME + "-menuitem"); sinkEvents(VTooltip.TOOLTIP_EVENTS); - - // Sink the onload event for any icons. The onload - // events are handled by the parent VMenuBar. - Util.sinkOnloadForImages(getElement()); } public void setSelected(boolean selected) { if (selected && !isSeparator) { addStyleDependentName("selected"); + // needed for IE6 to have a single style name to match for an + // element + if (checkable) { + if (checked) { + removeStyleDependentName("selected-unchecked"); + addStyleDependentName("selected-checked"); + } else { + removeStyleDependentName("selected-checked"); + addStyleDependentName("selected-unchecked"); + } + } } else { removeStyleDependentName("selected"); + // needed for IE6 to have a single style name to match for an + // element + removeStyleDependentName("selected-checked"); + removeStyleDependentName("selected-unchecked"); } } + public void setChecked(boolean checked) { + if (checkable && !isSeparator) { + this.checked = checked; + + if (checked) { + addStyleDependentName("checked"); + removeStyleDependentName("unchecked"); + } else { + addStyleDependentName("unchecked"); + removeStyleDependentName("checked"); + } + } else { + this.checked = false; + } + } + + public boolean isChecked() { + return checked; + } + + public void setCheckable(boolean checkable) { + if (checkable && !isSeparator) { + this.checkable = true; + } else { + setChecked(false); + this.checkable = false; + } + } + + public boolean isCheckable() { + return checkable; + } + /* * setters and getters for the fields */ @@ -895,6 +989,10 @@ public class VMenuBar extends SimpleFocusablePanel implements Paintable, public void setHTML(String html) { this.html = html; DOM.setInnerHTML(getElement(), html); + + // Sink the onload event for any icons. The onload + // events are handled by the parent VMenuBar. + Util.sinkOnloadForImages(getElement()); } public String getText() { @@ -937,6 +1035,15 @@ public class VMenuBar extends SimpleFocusablePanel implements Paintable, setSeparator(uidl.hasAttribute("separator")); setEnabled(!uidl.hasAttribute("disabled")); + if (!isSeparator() && uidl.hasAttribute(ATTRIBUTE_CHECKED)) { + // if the selected attribute is present (either true or false), + // the item is selectable + setCheckable(true); + setChecked(uidl.getBooleanAttribute(ATTRIBUTE_CHECKED)); + } else { + setCheckable(false); + } + if (uidl.hasAttribute("style")) { String itemStyle = uidl.getStringAttribute("style"); addStyleDependentName(itemStyle); @@ -1111,9 +1218,9 @@ public class VMenuBar extends SimpleFocusablePanel implements Paintable, * .gwt.event.dom.client.KeyPressEvent) */ public void onKeyPress(KeyPressEvent event) { - if (handleNavigation(event.getNativeEvent().getKeyCode(), event - .isControlKeyDown() - || event.isMetaKeyDown(), event.isShiftKeyDown())) { + if (handleNavigation(event.getNativeEvent().getKeyCode(), + event.isControlKeyDown() || event.isMetaKeyDown(), + event.isShiftKeyDown())) { event.preventDefault(); } } @@ -1126,9 +1233,9 @@ public class VMenuBar extends SimpleFocusablePanel implements Paintable, * .event.dom.client.KeyDownEvent) */ public void onKeyDown(KeyDownEvent event) { - if (handleNavigation(event.getNativeEvent().getKeyCode(), event - .isControlKeyDown() - || event.isMetaKeyDown(), event.isShiftKeyDown())) { + if (handleNavigation(event.getNativeEvent().getKeyCode(), + event.isControlKeyDown() || event.isMetaKeyDown(), + event.isShiftKeyDown())) { event.preventDefault(); } } @@ -1435,9 +1542,7 @@ public class VMenuBar extends SimpleFocusablePanel implements Paintable, * .dom.client.FocusEvent) */ public void onFocus(FocusEvent event) { - // Focus the first element directly, so the keyboard focus becomes - // visible - setSelected(items.get(0)); + } private final String SUBPART_PREFIX = "item"; diff --git a/src/com/vaadin/terminal/gwt/client/ui/VOverlay.java b/src/com/vaadin/terminal/gwt/client/ui/VOverlay.java index aba7ccf217..a277b4c42d 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/VOverlay.java +++ b/src/com/vaadin/terminal/gwt/client/ui/VOverlay.java @@ -22,7 +22,7 @@ import com.vaadin.terminal.gwt.client.Util; * temporary float over other components like context menus etc. This is to deal * stacking order correctly with VWindow objects. */ -public class VOverlay extends PopupPanel { +public class VOverlay extends PopupPanel implements CloseHandler<PopupPanel> { /* * The z-index value from where all overlays live. This can be overridden in @@ -84,23 +84,43 @@ public class VOverlay extends PopupPanel { public VOverlay(boolean autoHide, boolean modal, boolean showShadow) { super(autoHide, modal); - if (showShadow) { - shadow = DOM.createDiv(); - shadow.setClassName(CLASSNAME_SHADOW); - shadow.setInnerHTML(SHADOW_HTML); - DOM.setStyleAttribute(shadow, "position", "absolute"); - - addCloseHandler(new CloseHandler<PopupPanel>() { - public void onClose(CloseEvent<PopupPanel> event) { - if (shadow.getParentElement() != null) { - shadow.getParentElement().removeChild(shadow); - } - } - }); - } + setShadowEnabled(showShadow); adjustZIndex(); } + /** + * Method to controle whether DOM elements for shadow are added. With this + * method subclasses can control displaying of shadow also after the + * constructor. + * + * @param enabled + * true if shadow should be displayed + */ + protected void setShadowEnabled(boolean enabled) { + if (enabled != isShadowEnabled()) { + if (enabled) { + shadow = DOM.createDiv(); + shadow.setClassName(CLASSNAME_SHADOW); + shadow.setInnerHTML(SHADOW_HTML); + DOM.setStyleAttribute(shadow, "position", "absolute"); + addCloseHandler(this); + } else { + removeShadowIfPresent(); + shadow = null; + } + } + } + + protected boolean isShadowEnabled() { + return shadow != null; + } + + private void removeShadowIfPresent() { + if (isShadowEnabled() && shadow.getParentElement() != null) { + shadow.getParentElement().removeChild(shadow); + } + } + private void adjustZIndex() { setZIndex(Z_INDEX); } @@ -113,7 +133,7 @@ public class VOverlay extends PopupPanel { */ protected void setZIndex(int zIndex) { DOM.setStyleAttribute(getElement(), "zIndex", "" + zIndex); - if (shadow != null) { + if (isShadowEnabled()) { DOM.setStyleAttribute(shadow, "zIndex", "" + zIndex); } } @@ -128,25 +148,37 @@ public class VOverlay extends PopupPanel { style.setMarginLeft(-adjustByRelativeLeftBodyMargin(), Unit.PX); style.setMarginTop(-adjustByRelativeTopBodyMargin(), Unit.PX); super.setPopupPosition(left, top); - if (shadow != null) { - updateShadowSizeAndPosition(isAnimationEnabled() ? 0 : 1); - } + updateShadowSizeAndPosition(isAnimationEnabled() ? 0 : 1); } private static int adjustByRelativeTopBodyMargin() { if (topFix == -1) { - topFix = detectRelativeBodyFixes("top"); + boolean ie6OrIe7 = BrowserInfo.get().isIE() + && BrowserInfo.get().getIEVersion() <= 7; + topFix = detectRelativeBodyFixes("top", ie6OrIe7); } return topFix; } - private native static int detectRelativeBodyFixes(String axis) + private native static int detectRelativeBodyFixes(String axis, + boolean removeClientLeftOrTop) /*-{ try { var b = $wnd.document.body; var cstyle = b.currentStyle ? b.currentStyle : getComputedStyle(b); if(cstyle && cstyle.position == 'relative') { - return b.getBoundingClientRect()[axis]; + var offset = b.getBoundingClientRect()[axis]; + if (removeClientLeftOrTop) { + // IE6 and IE7 include the top left border of the client area into the boundingClientRect + var clientTopOrLeft = 0; + if (axis == "top") + clientTopOrLeft = $wnd.document.documentElement.clientTop; + else + clientTopOrLeft = $wnd.document.documentElement.clientLeft; + + offset -= clientTopOrLeft; + } + return offset; } } catch(e){} return 0; @@ -154,7 +186,10 @@ public class VOverlay extends PopupPanel { private static int adjustByRelativeLeftBodyMargin() { if (leftFix == -1) { - leftFix = detectRelativeBodyFixes("left"); + boolean ie6OrIe7 = BrowserInfo.get().isIE() + && BrowserInfo.get().getIEVersion() <= 7; + leftFix = detectRelativeBodyFixes("left", ie6OrIe7); + } return leftFix; } @@ -162,7 +197,7 @@ public class VOverlay extends PopupPanel { @Override public void show() { super.show(); - if (shadow != null) { + if (isShadowEnabled()) { if (isAnimationEnabled()) { ShadowAnimation sa = new ShadowAnimation(); sa.run(200); @@ -180,9 +215,17 @@ public class VOverlay extends PopupPanel { } @Override + protected void onDetach() { + super.onDetach(); + + // Always ensure shadow is removed when the overlay is removed. + removeShadowIfPresent(); + } + + @Override public void setVisible(boolean visible) { super.setVisible(visible); - if (shadow != null) { + if (isShadowEnabled()) { shadow.getStyle().setProperty("visibility", visible ? "visible" : "hidden"); } @@ -191,17 +234,13 @@ public class VOverlay extends PopupPanel { @Override public void setWidth(String width) { super.setWidth(width); - if (shadow != null) { - updateShadowSizeAndPosition(1.0); - } + updateShadowSizeAndPosition(1.0); } @Override public void setHeight(String height) { super.setHeight(height); - if (shadow != null) { - updateShadowSizeAndPosition(1.0); - } + updateShadowSizeAndPosition(1.0); } /** @@ -215,7 +254,7 @@ public class VOverlay extends PopupPanel { * name=='v-shadow-foobar'. */ protected void setShadowStyle(String style) { - if (shadow != null) { + if (isShadowEnabled()) { shadow.setClassName(CLASSNAME_SHADOW + "-" + style); } } @@ -241,7 +280,7 @@ public class VOverlay extends PopupPanel { */ private void updateShadowSizeAndPosition(final double progress) { // Don't do anything if overlay element is not attached - if (!isAttached()) { + if (!isAttached() || shadow == null) { return; } // Calculate proper z-index @@ -336,9 +375,11 @@ public class VOverlay extends PopupPanel { protected class ShadowAnimation extends Animation { @Override protected void onUpdate(double progress) { - if (shadow != null) { - updateShadowSizeAndPosition(progress); - } + updateShadowSizeAndPosition(progress); } } + + public void onClose(CloseEvent<PopupPanel> event) { + removeShadowIfPresent(); + } } diff --git a/src/com/vaadin/terminal/gwt/client/ui/VScrollTable.java b/src/com/vaadin/terminal/gwt/client/ui/VScrollTable.java index c1d674a367..d36c2137d8 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/VScrollTable.java +++ b/src/com/vaadin/terminal/gwt/client/ui/VScrollTable.java @@ -843,6 +843,10 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler, if (uidl.hasAttribute("alb")) { bodyActionKeys = uidl.getStringArrayAttribute("alb"); + } else { + // Need to clear the actions if the action handlers have been + // removed + bodyActionKeys = null; } setCacheRate(uidl.hasAttribute("cr") ? uidl.getDoubleAttribute("cr") diff --git a/src/com/vaadin/terminal/gwt/client/ui/VTextField.java b/src/com/vaadin/terminal/gwt/client/ui/VTextField.java index 3df2e356d1..a05cef3984 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/VTextField.java +++ b/src/com/vaadin/terminal/gwt/client/ui/VTextField.java @@ -12,6 +12,9 @@ import com.google.gwt.event.dom.client.ChangeEvent; import com.google.gwt.event.dom.client.ChangeHandler; import com.google.gwt.event.dom.client.FocusEvent; import com.google.gwt.event.dom.client.FocusHandler; +import com.google.gwt.event.dom.client.KeyCodes; +import com.google.gwt.event.dom.client.KeyDownEvent; +import com.google.gwt.event.dom.client.KeyDownHandler; import com.google.gwt.user.client.Command; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Element; @@ -24,7 +27,6 @@ import com.vaadin.terminal.gwt.client.EventId; import com.vaadin.terminal.gwt.client.Paintable; import com.vaadin.terminal.gwt.client.UIDL; import com.vaadin.terminal.gwt.client.Util; -import com.vaadin.terminal.gwt.client.VConsole; import com.vaadin.terminal.gwt.client.VTooltip; import com.vaadin.terminal.gwt.client.ui.ShortcutActionHandler.BeforeShortcutActionListener; @@ -35,7 +37,8 @@ import com.vaadin.terminal.gwt.client.ui.ShortcutActionHandler.BeforeShortcutAct * */ public class VTextField extends TextBoxBase implements Paintable, Field, - ChangeHandler, FocusHandler, BlurHandler, BeforeShortcutActionListener { + ChangeHandler, FocusHandler, BlurHandler, BeforeShortcutActionListener, + KeyDownHandler { public static final String VAR_CUR_TEXT = "curText"; /** @@ -85,6 +88,11 @@ public class VTextField extends TextBoxBase implements Paintable, Field, } setStyleName(CLASSNAME); addChangeHandler(this); + if (BrowserInfo.get().isIE()) { + // IE does not send change events when pressing enter in a text + // input so we handle it using a key listener instead + addKeyDownHandler(this); + } addFocusHandler(this); addBlurHandler(this); sinkEvents(VTooltip.TOOLTIP_EVENTS); @@ -590,4 +598,10 @@ public class VTextField extends TextBoxBase implements Paintable, Field, } wordwrap = enabled; } + + public void onKeyDown(KeyDownEvent event) { + if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) { + valueChange(false); + } + } } diff --git a/src/com/vaadin/terminal/gwt/client/ui/VWindow.java b/src/com/vaadin/terminal/gwt/client/ui/VWindow.java index 98ebf73b77..e2fe47cc2c 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/VWindow.java +++ b/src/com/vaadin/terminal/gwt/client/ui/VWindow.java @@ -14,13 +14,13 @@ import com.google.gwt.core.client.Scheduler; import com.google.gwt.core.client.Scheduler.ScheduledCommand; import com.google.gwt.event.dom.client.BlurEvent; import com.google.gwt.event.dom.client.BlurHandler; +import com.google.gwt.event.dom.client.DomEvent.Type; import com.google.gwt.event.dom.client.FocusEvent; import com.google.gwt.event.dom.client.FocusHandler; import com.google.gwt.event.dom.client.KeyDownEvent; import com.google.gwt.event.dom.client.KeyDownHandler; import com.google.gwt.event.dom.client.ScrollEvent; import com.google.gwt.event.dom.client.ScrollHandler; -import com.google.gwt.event.dom.client.DomEvent.Type; import com.google.gwt.event.shared.EventHandler; import com.google.gwt.event.shared.HandlerRegistration; import com.google.gwt.user.client.Command; @@ -258,9 +258,7 @@ public class VWindow extends VOverlay implements Container, footer = DOM.createDiv(); DOM.setElementProperty(footer, "className", CLASSNAME + "-footer"); resizeBox = DOM.createDiv(); - DOM - .setElementProperty(resizeBox, "className", CLASSNAME - + "-resizebox"); + DOM.setElementProperty(resizeBox, "className", CLASSNAME + "-resizebox"); closeBox = DOM.createDiv(); DOM.setElementProperty(closeBox, "className", CLASSNAME + "-closebox"); DOM.appendChild(footer, resizeBox); @@ -313,11 +311,11 @@ public class VWindow extends VOverlay implements Container, resizeLazy = uidl.hasAttribute(VView.RESIZE_LAZY); setDraggable(!uidl.hasAttribute("fixedposition")); - } - if (uidl.hasAttribute("caption")) { - setCaption(uidl.getStringAttribute("caption"), uidl - .getStringAttribute("icon")); + // Caption must be set before required header size is measured. If + // the caption attribute is missing the caption should be cleared. + setCaption(uidl.getStringAttribute("caption"), + uidl.getStringAttribute("icon")); } visibilityChangesDisabled = true; @@ -458,8 +456,7 @@ public class VWindow extends VOverlay implements Container, } final String style = notification.hasAttribute("style") ? notification - .getStringAttribute("style") - : null; + .getStringAttribute("style") : null; final int position = notification .getIntAttribute("position"); final int delay = notification.getIntAttribute("delay"); @@ -679,7 +676,8 @@ public class VWindow extends VOverlay implements Container, * Sets the closable state of the window. Additionally hides/shows the close * button according to the new state. * - * @param closable true if the window can be closed by the user + * @param closable + * true if the window can be closed by the user */ protected void setClosable(boolean closable) { if (this.closable == closable) { @@ -777,14 +775,15 @@ public class VWindow extends VOverlay implements Container, private void showModalityCurtain() { if (BrowserInfo.get().isFF2()) { - DOM.setStyleAttribute(getModalityCurtain(), "height", DOM - .getElementPropertyInt(RootPanel.getBodyElement(), - "offsetHeight") - + "px"); + DOM.setStyleAttribute( + getModalityCurtain(), + "height", + DOM.getElementPropertyInt(RootPanel.getBodyElement(), + "offsetHeight") + "px"); DOM.setStyleAttribute(getModalityCurtain(), "position", "absolute"); } - DOM.setStyleAttribute(getModalityCurtain(), "zIndex", "" - + (windowOrder.indexOf(this) + Z_INDEX)); + DOM.setStyleAttribute(getModalityCurtain(), "zIndex", + "" + (windowOrder.indexOf(this) + Z_INDEX)); if (isShowing()) { RootPanel.getBodyElement().insertBefore(getModalityCurtain(), getElement()); @@ -987,7 +986,8 @@ public class VWindow extends VOverlay implements Container, * Checks if the cursor was inside the browser content area when the event * happened. * - * @param event The event to be checked + * @param event + * The event to be checked * @return true, if the cursor is inside the browser content area * * false, otherwise @@ -1075,8 +1075,8 @@ public class VWindow extends VOverlay implements Container, rootPixelWidth = getElement().getOffsetWidth(); width = rootPixelWidth + "px"; } else { - rootPixelWidth = Integer.parseInt(width.substring(0, width - .indexOf("px"))); + rootPixelWidth = Integer.parseInt(width.substring(0, + width.indexOf("px"))); } // "width" now contains the new width in pixels @@ -1305,15 +1305,15 @@ public class VWindow extends VOverlay implements Container, public void onScroll(ScrollEvent event) { client.updateVariable(id, "scrollTop", contentPanel.getScrollPosition(), false); - client.updateVariable(id, "scrollLeft", contentPanel - .getHorizontalScrollPosition(), false); + client.updateVariable(id, "scrollLeft", + contentPanel.getHorizontalScrollPosition(), false); } public void onKeyDown(KeyDownEvent event) { if (shortcutHandler != null) { - shortcutHandler.handleKeyboardEvent(Event - .as(event.getNativeEvent())); + shortcutHandler + .handleKeyboardEvent(Event.as(event.getNativeEvent())); return; } } diff --git a/src/com/vaadin/terminal/gwt/server/AbstractApplicationPortlet.java b/src/com/vaadin/terminal/gwt/server/AbstractApplicationPortlet.java index cb4ba16f65..b4f194c7f7 100644 --- a/src/com/vaadin/terminal/gwt/server/AbstractApplicationPortlet.java +++ b/src/com/vaadin/terminal/gwt/server/AbstractApplicationPortlet.java @@ -561,15 +561,19 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet private void updateBrowserProperties(WebBrowser browser, PortletRequest request) { String userAgent = getHTTPHeader(request, "user-agent"); - browser.updateBrowserProperties(request.getLocale(), null, - request.isSecure(), userAgent, - getHTTPRequestParameter(request, "sw"), - getHTTPRequestParameter(request, "sh"), - getHTTPRequestParameter(request, "tzo"), - getHTTPRequestParameter(request, "rtzo"), - getHTTPRequestParameter(request, "dstd"), - getHTTPRequestParameter(request, "dstActive"), - getHTTPRequestParameter(request, "curdate")); + browser.updateRequestDetails(request.getLocale(), null, + request.isSecure(), userAgent); + if (getHTTPRequestParameter(request, "repaintAll") != null) { + browser.updateClientSideDetails( + getHTTPRequestParameter(request, "sw"), + getHTTPRequestParameter(request, "sh"), + getHTTPRequestParameter(request, "tzo"), + getHTTPRequestParameter(request, "rtzo"), + getHTTPRequestParameter(request, "dstd"), + getHTTPRequestParameter(request, "dstActive"), + getHTTPRequestParameter(request, "curdate"), + getHTTPRequestParameter(request, "td") != null); + } } @Override diff --git a/src/com/vaadin/terminal/gwt/server/AbstractApplicationServlet.java b/src/com/vaadin/terminal/gwt/server/AbstractApplicationServlet.java index f274a474dd..c394e8ae6a 100644 --- a/src/com/vaadin/terminal/gwt/server/AbstractApplicationServlet.java +++ b/src/com/vaadin/terminal/gwt/server/AbstractApplicationServlet.java @@ -589,12 +589,18 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements private void updateBrowserProperties(WebBrowser browser, HttpServletRequest request) { - browser.updateBrowserProperties(request.getLocale(), + // request based details updated always + browser.updateRequestDetails(request.getLocale(), request.getRemoteAddr(), request.isSecure(), - request.getHeader("user-agent"), request.getParameter("sw"), - request.getParameter("sh"), request.getParameter("tzo"), - request.getParameter("rtzo"), request.getParameter("dstd"), - request.getParameter("dston"), request.getParameter("curdate")); + request.getHeader("user-agent")); + if (request.getParameter("repaintAll") != null) { + browser.updateClientSideDetails(request.getParameter("sw"), + request.getParameter("sh"), request.getParameter("tzo"), + request.getParameter("rtzo"), request.getParameter("dstd"), + request.getParameter("dston"), + request.getParameter("curdate"), + request.getParameter("td") != null); + } } protected ClassLoader getClassLoader() throws ServletException { diff --git a/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java b/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java index 152fa81bb5..74b7a27a96 100644 --- a/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java +++ b/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java @@ -392,6 +392,7 @@ public abstract class AbstractCommunicationManager implements int contentLength = request.getContentLength(); boolean atStart = false; + boolean firstFileFieldFound = false; String rawfilename = "unknown"; String rawMimeType = "application/octet-stream"; @@ -410,7 +411,8 @@ public abstract class AbstractCommunicationManager implements rawfilename = rawfilename.substring(1); rawfilename = rawfilename.substring(0, rawfilename.indexOf(parenthesis)); - } else if (readLine.equals("")) { + firstFileFieldFound = true; + } else if (firstFileFieldFound && readLine.equals("")) { atStart = true; } else if (readLine.startsWith("Content-Type")) { rawMimeType = readLine.split(": ")[1]; diff --git a/src/com/vaadin/terminal/gwt/server/WebBrowser.java b/src/com/vaadin/terminal/gwt/server/WebBrowser.java index 1b0d7fe986..4835b3c55b 100644 --- a/src/com/vaadin/terminal/gwt/server/WebBrowser.java +++ b/src/com/vaadin/terminal/gwt/server/WebBrowser.java @@ -30,9 +30,10 @@ public class WebBrowser implements Terminal { private int rawTimezoneOffset = 0; private int dstSavings; private boolean dstInEffect; - private Date currentDate; + private boolean touchDevice; private VBrowserDetails browserDetails; + private long clientServerTimeDelta; /** * There is no default-theme for this terminal type. @@ -71,91 +72,6 @@ public class WebBrowser implements Terminal { } /** - * For internal use by AbstractApplicationServlet/AbstractApplicationPortlet - * only. Updates all properties in the class according to the given - * information. - * - * @param locale - * The browser primary locale - * @param address - * The browser ip address - * @param secureConnection - * true if using an https connection - * @param agent - * Raw userAgent string from the browser - * @param sw - * Screen width - * @param sh - * Screen height - * @param tzo - * TimeZone offset in minutes from GMT - * @param rtzo - * raw TimeZone offset in minutes from GMT (w/o DST adjustment) - * @param dstSavings - * the difference between the raw TimeZone and DST in minutes - * @param dstInEffect - * is DST currently active in the region or not? - * @param curDate - * the current date in milliseconds since the epoch - */ - void updateBrowserProperties(Locale locale, String address, - boolean secureConnection, String agent, String sw, String sh, - String tzo, String rtzo, String dstSavings, String dstInEffect, - String curDate) { - this.locale = locale; - this.address = address; - this.secureConnection = secureConnection; - if (agent != null) { - browserApplication = agent; - browserDetails = new VBrowserDetails(agent); - } - - if (sw != null) { - try { - screenHeight = Integer.parseInt(sh); - screenWidth = Integer.parseInt(sw); - } catch (final NumberFormatException e) { - screenHeight = screenWidth = 0; - } - } - if (tzo != null) { - try { - // browser->java conversion: min->ms, reverse sign - timezoneOffset = -Integer.parseInt(tzo) * 60 * 1000; - } catch (final NumberFormatException e) { - timezoneOffset = 0; // default gmt+0 - } - } - if (rtzo != null) { - try { - // browser->java conversion: min->ms, reverse sign - rawTimezoneOffset = -Integer.parseInt(rtzo) * 60 * 1000; - } catch (final NumberFormatException e) { - rawTimezoneOffset = 0; // default gmt+0 - } - } - if (dstSavings != null) { - try { - // browser->java conversion: min->ms - this.dstSavings = Integer.parseInt(dstSavings) * 60 * 1000; - } catch (final NumberFormatException e) { - this.dstSavings = 0; // default no savings - } - } - if (dstInEffect != null) { - this.dstInEffect = Boolean.parseBoolean(dstInEffect); - } - if (curDate != null) { - try { - long curTime = Long.parseLong(curDate); - currentDate = new Date(curTime); - } catch (final NumberFormatException e) { - currentDate = new Date(); - } - } - } - - /** * Gets the IP-address of the web browser. If the application is running * inside a portlet, this method will return null. * @@ -368,12 +284,120 @@ public class WebBrowser implements Terminal { /** * Returns the current date and time of the browser. This will not be * entirely accurate due to varying network latencies, but should provide a - * close-enough value for most cases. + * close-enough value for most cases. Also note that the returned Date + * object uses servers default time zone, not the clients. * * @return the current date and time of the browser. + * @see #isDSTInEffect() + * @see #getDSTSavings() + * @see #getTimezoneOffset() */ public Date getCurrentDate() { - return currentDate; + return new Date(new Date().getTime() + clientServerTimeDelta); + } + + /** + * @return true if the browser is detected to support touch events + */ + public boolean isTouchDevice() { + return touchDevice; + } + + /** + * For internal use by AbstractApplicationServlet/AbstractApplicationPortlet + * only. Updates all properties in the class according to the given + * information. + * + * @param sw + * Screen width + * @param sh + * Screen height + * @param tzo + * TimeZone offset in minutes from GMT + * @param rtzo + * raw TimeZone offset in minutes from GMT (w/o DST adjustment) + * @param dstSavings + * the difference between the raw TimeZone and DST in minutes + * @param dstInEffect + * is DST currently active in the region or not? + * @param curDate + * the current date in milliseconds since the epoch + * @param touchDevice + */ + void updateClientSideDetails(String sw, String sh, String tzo, String rtzo, + String dstSavings, String dstInEffect, String curDate, + boolean touchDevice) { + if (sw != null) { + try { + screenHeight = Integer.parseInt(sh); + screenWidth = Integer.parseInt(sw); + } catch (final NumberFormatException e) { + screenHeight = screenWidth = 0; + } + } + if (tzo != null) { + try { + // browser->java conversion: min->ms, reverse sign + timezoneOffset = -Integer.parseInt(tzo) * 60 * 1000; + } catch (final NumberFormatException e) { + timezoneOffset = 0; // default gmt+0 + } + } + if (rtzo != null) { + try { + // browser->java conversion: min->ms, reverse sign + rawTimezoneOffset = -Integer.parseInt(rtzo) * 60 * 1000; + } catch (final NumberFormatException e) { + rawTimezoneOffset = 0; // default gmt+0 + } + } + if (dstSavings != null) { + try { + // browser->java conversion: min->ms + this.dstSavings = Integer.parseInt(dstSavings) * 60 * 1000; + } catch (final NumberFormatException e) { + this.dstSavings = 0; // default no savings + } + } + if (dstInEffect != null) { + this.dstInEffect = Boolean.parseBoolean(dstInEffect); + } + if (curDate != null) { + try { + long curTime = Long.parseLong(curDate); + clientServerTimeDelta = curTime - new Date().getTime(); + } catch (final NumberFormatException e) { + clientServerTimeDelta = 0; + } + } + this.touchDevice = touchDevice; + + } + + /** + * For internal use by AbstractApplicationServlet/AbstractApplicationPortlet + * only. Updates all properties in the class according to the given + * information. + * + * @param locale + * The browser primary locale + * @param address + * The browser ip address + * @param secureConnection + * true if using an https connection + * @param agent + * Raw userAgent string from the browser + */ + void updateRequestDetails(Locale locale, String address, + boolean secureConnection, String agent) { + this.locale = locale; + this.address = address; + this.secureConnection = secureConnection; + if (agent != null) { + browserApplication = agent; + browserDetails = new VBrowserDetails(agent); + } + } } diff --git a/src/com/vaadin/ui/AbstractOrderedLayout.java b/src/com/vaadin/ui/AbstractOrderedLayout.java index 8dfbe07b18..0b0fff2a34 100644 --- a/src/com/vaadin/ui/AbstractOrderedLayout.java +++ b/src/com/vaadin/ui/AbstractOrderedLayout.java @@ -23,7 +23,7 @@ public abstract class AbstractOrderedLayout extends AbstractLayout implements private static final String CLICK_EVENT = EventId.LAYOUT_CLICK; - private static final Alignment ALIGNMENT_DEFAULT = Alignment.TOP_LEFT; + public static final Alignment ALIGNMENT_DEFAULT = Alignment.TOP_LEFT; /** * Custom layout slots containing the components. diff --git a/src/com/vaadin/ui/Button.java b/src/com/vaadin/ui/Button.java index c64cd386ce..122d155e0f 100644 --- a/src/com/vaadin/ui/Button.java +++ b/src/com/vaadin/ui/Button.java @@ -176,15 +176,29 @@ public class Button extends AbstractField implements FieldEvents.BlurNotifier, if (newValue != null && !newValue.equals(oldValue) && !isReadOnly()) { setValue(newValue); - fireClick(MouseEventDetails.deSerialize((String) variables - .get("mousedetails"))); + if (variables.containsKey("mousedetails")) { + fireClick(MouseEventDetails + .deSerialize((String) variables + .get("mousedetails"))); + } else { + // for compatibility with custom implementations which + // don't send mouse details + fireClick(); + } } } else { // Only send click event if the button is pushed if (newValue.booleanValue()) { - fireClick(MouseEventDetails.deSerialize((String) variables - .get("mousedetails"))); + if (variables.containsKey("mousedetails")) { + fireClick(MouseEventDetails + .deSerialize((String) variables + .get("mousedetails"))); + } else { + // for compatibility with custom implementations which + // don't send mouse details + fireClick(); + } } // If the button is true for some reason, release it @@ -298,7 +312,7 @@ public class Button extends AbstractField implements FieldEvents.BlurNotifier, */ public class ClickEvent extends Component.Event { - private MouseEventDetails details; + private final MouseEventDetails details; /** * New instance of text change event. @@ -308,6 +322,7 @@ public class Button extends AbstractField implements FieldEvents.BlurNotifier, */ public ClickEvent(Component source) { super(source); + details = null; } /** @@ -336,20 +351,28 @@ public class Button extends AbstractField implements FieldEvents.BlurNotifier, * Returns the mouse position (x coordinate) when the click took place. * The position is relative to the browser client area. * - * @return The mouse cursor x position + * @return The mouse cursor x position or -1 if unknown */ public int getClientX() { - return details.getClientX(); + if (null != details) { + return details.getClientX(); + } else { + return -1; + } } /** * Returns the mouse position (y coordinate) when the click took place. * The position is relative to the browser client area. * - * @return The mouse cursor y position + * @return The mouse cursor y position or -1 if unknown */ public int getClientY() { - return details.getClientY(); + if (null != details) { + return details.getClientY(); + } else { + return -1; + } } /** @@ -360,7 +383,11 @@ public class Button extends AbstractField implements FieldEvents.BlurNotifier, * component or -1 if no x coordinate available */ public int getRelativeX() { - return details.getRelativeX(); + if (null != details) { + return details.getRelativeX(); + } else { + return -1; + } } /** @@ -371,46 +398,67 @@ public class Button extends AbstractField implements FieldEvents.BlurNotifier, * component or -1 if no y coordinate available */ public int getRelativeY() { - return details.getRelativeY(); + if (null != details) { + return details.getRelativeY(); + } else { + return -1; + } } /** * Checks if the Alt key was down when the mouse event took place. * * @return true if Alt was down when the event occured, false otherwise + * or if unknown */ public boolean isAltKey() { - return details.isAltKey(); + if (null != details) { + return details.isAltKey(); + } else { + return false; + } } /** * Checks if the Ctrl key was down when the mouse event took place. * * @return true if Ctrl was pressed when the event occured, false - * otherwise + * otherwise or if unknown */ public boolean isCtrlKey() { - return details.isCtrlKey(); + if (null != details) { + return details.isCtrlKey(); + } else { + return false; + } } /** * Checks if the Meta key was down when the mouse event took place. * * @return true if Meta was pressed when the event occured, false - * otherwise + * otherwise or if unknown */ public boolean isMetaKey() { - return details.isMetaKey(); + if (null != details) { + return details.isMetaKey(); + } else { + return false; + } } /** * Checks if the Shift key was down when the mouse event took place. * * @return true if Shift was pressed when the event occured, false - * otherwise + * otherwise or if unknown */ public boolean isShiftKey() { - return details.isShiftKey(); + if (null != details) { + return details.isShiftKey(); + } else { + return false; + } } } @@ -457,13 +505,25 @@ public class Button extends AbstractField implements FieldEvents.BlurNotifier, } /** - * Emits the options change event. + * Fires a click event to all listeners without any event details. + * + * In subclasses, override {@link #fireClick(MouseEventDetails)} instead of + * this method. */ protected void fireClick() { fireEvent(new Button.ClickEvent(this)); } - private void fireClick(MouseEventDetails details) { + /** + * Fires a click event to all listeners. + * + * @param details + * MouseEventDetails from which keyboard modifiers and other + * information about the mouse click can be obtained. If the + * button was clicked by a keyboard event, some of the fields may + * be empty/undefined. + */ + protected void fireClick(MouseEventDetails details) { fireEvent(new Button.ClickEvent(this, details)); } diff --git a/src/com/vaadin/ui/MenuBar.java b/src/com/vaadin/ui/MenuBar.java index d79e070fca..f8fbf91712 100644 --- a/src/com/vaadin/ui/MenuBar.java +++ b/src/com/vaadin/ui/MenuBar.java @@ -123,7 +123,12 @@ public class MenuBar extends AbstractComponent { if (description != null && description.length() > 0) { target.addAttribute("description", description); } - + if (item.isCheckable()) { + // if the "checked" attribute is present (either true or false), + // the item is checkable + target.addAttribute(VMenuBar.ATTRIBUTE_CHECKED, + item.isChecked()); + } if (item.hasChildren()) { for (MenuItem child : item.getChildren()) { paintItem(target, child); @@ -167,7 +172,12 @@ public class MenuBar extends AbstractComponent { // If we got the clicked item, launch the command. if (found && tmpItem.isEnabled()) { - tmpItem.getCommand().menuSelected(tmpItem); + if (tmpItem.isCheckable()) { + tmpItem.setChecked(!tmpItem.isChecked()); + } + if (null != tmpItem.getCommand()) { + tmpItem.getCommand().menuSelected(tmpItem); + } } }// if }// changeVariables @@ -431,6 +441,8 @@ public class MenuBar extends AbstractComponent { private boolean isSeparator = false; private String styleName; private String description; + private boolean checkable = false; + private boolean checked = false; /** * Constructs a new menu item that can optionally have an icon and a @@ -505,13 +517,19 @@ public class MenuBar extends AbstractComponent { * the icon for the menu item * @param command * the command for the menu item + * @throws IllegalStateException + * If the item is checkable and thus cannot have children. */ public MenuBar.MenuItem addItem(String caption, Resource icon, - MenuBar.Command command) { + MenuBar.Command command) throws IllegalStateException { if (isSeparator()) { throw new UnsupportedOperationException( "Cannot add items to a separator"); } + if (isCheckable()) { + throw new IllegalStateException( + "A checkable item cannot have children"); + } if (caption == null) { throw new IllegalArgumentException("Caption cannot be null"); } @@ -544,11 +562,16 @@ public class MenuBar extends AbstractComponent { * the command for the menu item * @param itemToAddBefore * the item that will be after the new item - * + * @throws IllegalStateException + * If the item is checkable and thus cannot have children. */ public MenuBar.MenuItem addItemBefore(String caption, Resource icon, - MenuBar.Command command, MenuBar.MenuItem itemToAddBefore) { - + MenuBar.Command command, MenuBar.MenuItem itemToAddBefore) + throws IllegalStateException { + if (isCheckable()) { + throw new IllegalStateException( + "A checkable item cannot have children"); + } MenuItem newItem = null; if (hasChildren() && itsChildren.contains(itemToAddBefore)) { @@ -814,6 +837,93 @@ public class MenuBar extends AbstractComponent { return description; } + /** + * Gets the checkable state of the item - whether the item has checked + * and unchecked states. If an item is checkable its checked state (as + * returned by {@link #isChecked()}) is indicated in the UI. + * + * <p> + * An item is not checkable by default. + * </p> + * + * @return true if the item is checkable, false otherwise + * @since 6.6.2 + */ + public boolean isCheckable() { + return checkable; + } + + /** + * Sets the checkable state of the item. If an item is checkable its + * checked state (as returned by {@link #isChecked()}) is indicated in + * the UI. + * + * <p> + * An item is not checkable by default. + * </p> + * + * <p> + * Items with sub items cannot be checkable. + * </p> + * + * @param checkable + * true if the item should be checkable, false otherwise + * @throws IllegalStateException + * If the item has children + * @since 6.6.2 + */ + public void setCheckable(boolean checkable) + throws IllegalStateException { + if (hasChildren()) { + throw new IllegalStateException( + "A menu item with children cannot be checkable"); + } + this.checkable = checkable; + requestRepaint(); + } + + /** + * Gets the checked state of the item (checked or unchecked). Only used + * if the item is checkable (as indicated by {@link #isCheckable()}). + * The checked state is indicated in the UI with the item, if the item + * is checkable. + * + * <p> + * An item is not checked by default. + * </p> + * + * <p> + * The CSS style corresponding to the checked state is "-checked". + * </p> + * + * @return true if the item is checked, false otherwise + * @since 6.6.2 + */ + public boolean isChecked() { + return checked; + } + + /** + * Sets the checked state of the item. Only used if the item is + * checkable (indicated by {@link #isCheckable()}). The checked state is + * indicated in the UI with the item, if the item is checkable. + * + * <p> + * An item is not checked by default. + * </p> + * + * <p> + * The CSS style corresponding to the checked state is "-checked". + * </p> + * + * @return true if the item is checked, false otherwise + * @since 6.6.2 + */ + public void setChecked(boolean checked) { + this.checked = checked; + requestRepaint(); + } + }// class MenuItem }// class MenuBar diff --git a/src/com/vaadin/ui/themes/BaseTheme.java b/src/com/vaadin/ui/themes/BaseTheme.java index babdb1f1d5..c8ba08d30d 100644 --- a/src/com/vaadin/ui/themes/BaseTheme.java +++ b/src/com/vaadin/ui/themes/BaseTheme.java @@ -44,12 +44,4 @@ public class BaseTheme { @Deprecated public static final String PANEL_LIGHT = "light"; - /** - * Adds connector lines between the tree nodes to better visualize the - * hierarchy. - * - * @since 6.6.1 - */ - public static final String TREE_CONNECTORS = "connectors"; - }
\ No newline at end of file |