summaryrefslogtreecommitdiffstats
path: root/src/com/vaadin
diff options
context:
space:
mode:
authorLeif Åstrand <leif@vaadin.com>2011-11-22 12:14:22 +0200
committerLeif Åstrand <leif@vaadin.com>2011-11-22 12:14:22 +0200
commite578248b13275b94c9985aaadc1bf16b4c8b20e5 (patch)
tree0a5aba877d47d538869fc061ee941f264a84af8d /src/com/vaadin
parent4b1d80a7b8906bd8ac72e762ea19e3cb55827f6b (diff)
parentb23d8dfaa5c27cd4bed0df1a654e59b0464707b5 (diff)
downloadvaadin-framework-e578248b13275b94c9985aaadc1bf16b4c8b20e5.tar.gz
vaadin-framework-e578248b13275b94c9985aaadc1bf16b4c8b20e5.zip
Merge remote branch 'origin/6.8'
Conflicts: tests/integration_tests.xml
Diffstat (limited to 'src/com/vaadin')
-rw-r--r--src/com/vaadin/terminal/gwt/DefaultWidgetSet.gwt.xml16
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/VDragAndDropWrapper.java179
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/VDragAndDropWrapperIE.java65
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/VFilterSelect.java277
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/VView.java70
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent.java20
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/dd/VTransferable.java8
-rw-r--r--src/com/vaadin/ui/DragAndDropWrapper.java38
-rw-r--r--src/com/vaadin/ui/Select.java2
-rw-r--r--src/com/vaadin/ui/TreeTable.java20
10 files changed, 524 insertions, 171 deletions
diff --git a/src/com/vaadin/terminal/gwt/DefaultWidgetSet.gwt.xml b/src/com/vaadin/terminal/gwt/DefaultWidgetSet.gwt.xml
index 5884cdaceb..bdbcc75222 100644
--- a/src/com/vaadin/terminal/gwt/DefaultWidgetSet.gwt.xml
+++ b/src/com/vaadin/terminal/gwt/DefaultWidgetSet.gwt.xml
@@ -1,6 +1,6 @@
<module>
- <!-- This GWT module defines the Vaadin DefaultWidgetSet. This is the module
- you want to extend when creating an extended widget set, or when creating
+ <!-- This GWT module defines the Vaadin DefaultWidgetSet. This is the module
+ you want to extend when creating an extended widget set, or when creating
a specialized widget set with a subset of the components. -->
<!-- Hint for WidgetSetBuilder not to automatically update the file -->
@@ -12,7 +12,7 @@
<source path="client" />
- <!-- Use own Scheduler implementation to be able to track if commands are
+ <!-- Use own Scheduler implementation to be able to track if commands are
running -->
<replace-with class="com.vaadin.terminal.gwt.client.VSchedulerImpl">
<when-type-is class="com.google.gwt.core.client.impl.SchedulerImpl" />
@@ -52,6 +52,16 @@
</any>
</replace-with>
+ <replace-with
+ class="com.vaadin.terminal.gwt.client.ui.VDragAndDropWrapperIE">
+ <when-type-is
+ class="com.vaadin.terminal.gwt.client.ui.VDragAndDropWrapper" />
+ <any>
+ <when-property-is name="user.agent" value="ie6" />
+ <when-property-is name="user.agent" value="ie8" />
+ </any>
+ </replace-with>
+
<!-- Workaround for #6682. Remove when fixed in GWT. -->
<replace-with class="com.google.gwt.dom.client.VaadinDOMImplSafari">
<when-type-is class="com.google.gwt.dom.client.DOMImpl" />
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VDragAndDropWrapper.java b/src/com/vaadin/terminal/gwt/client/ui/VDragAndDropWrapper.java
index 7fef345586..05d69d7dec 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/VDragAndDropWrapper.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/VDragAndDropWrapper.java
@@ -25,14 +25,15 @@ import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.xhr.client.ReadyStateChangeHandler;
import com.google.gwt.xhr.client.XMLHttpRequest;
import com.vaadin.terminal.gwt.client.ApplicationConnection;
-import com.vaadin.terminal.gwt.client.BrowserInfo;
import com.vaadin.terminal.gwt.client.MouseEventDetails;
import com.vaadin.terminal.gwt.client.Paintable;
import com.vaadin.terminal.gwt.client.RenderInformation;
import com.vaadin.terminal.gwt.client.RenderInformation.Size;
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.ValueMap;
import com.vaadin.terminal.gwt.client.ui.dd.DDUtil;
import com.vaadin.terminal.gwt.client.ui.dd.HorizontalDropLocation;
import com.vaadin.terminal.gwt.client.ui.dd.VAbstractDropHandler;
@@ -55,8 +56,11 @@ import com.vaadin.terminal.gwt.client.ui.dd.VerticalDropLocation;
*/
public class VDragAndDropWrapper extends VCustomComponent implements
VHasDropHandler {
+ public static final String DRAG_START_MODE = "dragStartMode";
+ public static final String HTML5_DATA_FLAVORS = "html5-data-flavors";
private static final String CLASSNAME = "v-ddwrapper";
+ protected static final String DRAGGABLE = "draggable";
public VDragAndDropWrapper() {
super();
@@ -82,6 +86,7 @@ public class VDragAndDropWrapper extends VCustomComponent implements
}
}
}, TouchStartEvent.getType());
+
sinkEvents(Event.TOUCHEVENTS);
}
@@ -102,7 +107,7 @@ public class VDragAndDropWrapper extends VCustomComponent implements
* @return true if the event was handled as a drag start event
*/
private boolean startDrag(NativeEvent event) {
- if (dragStarMode > 0) {
+ if (dragStartMode == WRAPPER || dragStartMode == COMPONENT) {
VTransferable transferable = new VTransferable();
transferable.setDragSource(VDragAndDropWrapper.this);
@@ -121,7 +126,7 @@ public class VDragAndDropWrapper extends VCustomComponent implements
transferable.setData("mouseDown",
new MouseEventDetails(event).serialize());
- if (dragStarMode == WRAPPER) {
+ if (dragStartMode == WRAPPER) {
dragEvent.createDragImage(getElement(), true);
} else {
dragEvent.createDragImage(((Widget) paintable).getElement(),
@@ -132,16 +137,21 @@ public class VDragAndDropWrapper extends VCustomComponent implements
return false;
}
+ protected final static int NONE = 0;
+ protected final static int COMPONENT = 1;
+ protected final static int WRAPPER = 2;
+ protected final static int HTML5 = 3;
+
+ protected int dragStartMode;
+
private ApplicationConnection client;
private VAbstractDropHandler dropHandler;
private VDragEvent vaadinDragEvent;
- private final static int NONE = 0;
- private final static int COMPONENT = 1;
- private final static int WRAPPER = 2;
- private int dragStarMode;
private int filecounter = 0;
private Map<String, String> fileIdToReceiver;
+ private ValueMap html5DataFlavors;
+ private Element dragStartElement;
@Override
public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
@@ -180,10 +190,35 @@ public class VDragAndDropWrapper extends VCustomComponent implements
}
startNextUpload();
- dragStarMode = uidl.getIntAttribute("dragStartMode");
+ dragStartMode = uidl.getIntAttribute(DRAG_START_MODE);
+ initDragStartMode();
+ html5DataFlavors = uidl.getMapAttribute(HTML5_DATA_FLAVORS);
+ }
+ }
+
+ protected void initDragStartMode() {
+ Element div = getElement();
+ if (dragStartMode == HTML5) {
+ if (dragStartElement == null) {
+ dragStartElement = getDragStartElement();
+ dragStartElement.setPropertyBoolean(DRAGGABLE, true);
+ VConsole.log("draggable = "
+ + dragStartElement.getPropertyBoolean(DRAGGABLE));
+ hookHtml5DragStart(dragStartElement);
+ VConsole.log("drag start listeners hooked.");
+ }
+ } else {
+ dragStartElement = null;
+ if (div.hasAttribute(DRAGGABLE)) {
+ div.removeAttribute(DRAGGABLE);
+ }
}
}
+ protected Element getDragStartElement() {
+ return getElement();
+ }
+
private boolean uploading;
private ReadyStateChangeHandler readyStateChangeHandler = new ReadyStateChangeHandler() {
@@ -227,6 +262,23 @@ public class VDragAndDropWrapper extends VCustomComponent implements
}
+ public boolean html5DragStart(VHtml5DragEvent event) {
+ if (dragStartMode == HTML5) {
+ /*
+ * Populate html5 payload with dataflavors from the serverside
+ */
+ JsArrayString flavors = html5DataFlavors.getKeyArray();
+ for (int i = 0; i < flavors.length(); i++) {
+ String flavor = flavors.get(i);
+ event.setHtml5DataFlavor(flavor,
+ html5DataFlavors.getString(flavor));
+ }
+ event.setEffectAllowed("copy");
+ return true;
+ }
+ return false;
+ }
+
public boolean html5DragEnter(VHtml5DragEvent event) {
if (dropHandler == null) {
return true;
@@ -246,8 +298,12 @@ public class VDragAndDropWrapper extends VCustomComponent implements
VDragAndDropManager.get().setCurrentDropHandler(
getDropHandler());
}
- event.preventDefault();
- event.stopPropagation();
+ try {
+ event.preventDefault();
+ event.stopPropagation();
+ } catch (Exception e) {
+ // VConsole.log("IE9 fails");
+ }
return false;
} catch (Exception e) {
GWT.getUncaughtExceptionHandler().onUncaughtException(e);
@@ -277,8 +333,12 @@ public class VDragAndDropWrapper extends VCustomComponent implements
}
};
dragleavetimer.schedule(350);
- event.preventDefault();
- event.stopPropagation();
+ try {
+ event.preventDefault();
+ event.stopPropagation();
+ } catch (Exception e) {
+ // VConsole.log("IE9 fails");
+ }
return false;
} catch (Exception e) {
GWT.getUncaughtExceptionHandler().onUncaughtException(e);
@@ -299,17 +359,20 @@ public class VDragAndDropWrapper extends VCustomComponent implements
vaadinDragEvent.setCurrentGwtEvent(event);
getDropHandler().dragOver(vaadinDragEvent);
- // needed to be set for Safari, otherwise drop will not happen
- if (BrowserInfo.get().isWebkit()) {
- String s = event.getEffectAllowed();
- if ("all".equals(s) || s.contains("opy")) {
- event.setDragEffect("copy");
- } else {
- event.setDragEffect(s);
- }
+
+ String s = event.getEffectAllowed();
+ if ("all".equals(s) || s.contains("opy")) {
+ event.setDropEffect("copy");
+ } else {
+ event.setDropEffect(s);
+ }
+
+ try {
+ event.preventDefault();
+ event.stopPropagation();
+ } catch (Exception e) {
+ // VConsole.log("IE9 fails");
}
- event.preventDefault();
- event.stopPropagation();
return false;
}
@@ -349,9 +412,12 @@ public class VDragAndDropWrapper extends VCustomComponent implements
VDragAndDropManager.get().endDrag();
vaadinDragEvent = null;
- event.preventDefault();
- event.stopPropagation();
-
+ try {
+ event.preventDefault();
+ event.stopPropagation();
+ } catch (Exception e) {
+ // VConsole.log("IE9 fails");
+ }
return false;
} catch (Exception e) {
GWT.getUncaughtExceptionHandler().onUncaughtException(e);
@@ -491,51 +557,38 @@ public class VDragAndDropWrapper extends VCustomComponent implements
}
+ protected native void hookHtml5DragStart(Element el)
+ /*-{
+ var me = this;
+ el.addEventListener("dragstart", function(ev) {
+ return me.@com.vaadin.terminal.gwt.client.ui.VDragAndDropWrapper::html5DragStart(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev);
+ }, false);
+ }-*/;
+
/**
* Prototype code, memory leak risk.
*
* @param el
*/
- private native void hookHtml5Events(Element el)
+ protected native void hookHtml5Events(Element el)
/*-{
-
var me = this;
-
- if(el.addEventListener) {
- el.addEventListener("dragenter", function(ev) {
- return me.@com.vaadin.terminal.gwt.client.ui.VDragAndDropWrapper::html5DragEnter(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev);
- }, false);
-
- el.addEventListener("dragleave", function(ev) {
- return me.@com.vaadin.terminal.gwt.client.ui.VDragAndDropWrapper::html5DragLeave(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev);
- }, false);
-
- el.addEventListener("dragover", function(ev) {
- return me.@com.vaadin.terminal.gwt.client.ui.VDragAndDropWrapper::html5DragOver(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev);
- }, false);
-
- el.addEventListener("drop", function(ev) {
- return me.@com.vaadin.terminal.gwt.client.ui.VDragAndDropWrapper::html5DragDrop(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev);
- }, false);
-
- } else {
- el.attachEvent("ondragenter", function(ev) {
- return me.@com.vaadin.terminal.gwt.client.ui.VDragAndDropWrapper::html5DragEnter(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev);
- });
-
- el.attachEvent("ondragleave", function(ev) {
- return me.@com.vaadin.terminal.gwt.client.ui.VDragAndDropWrapper::html5DragLeave(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev);
- });
-
- el.attachEvent("ondragover", function(ev) {
- return me.@com.vaadin.terminal.gwt.client.ui.VDragAndDropWrapper::html5DragOver(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev);
- });
-
- el.attachEvent("ondrop", function(ev) {
- return me.@com.vaadin.terminal.gwt.client.ui.VDragAndDropWrapper::html5DragDrop(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev);
- });
- }
-
+
+ el.addEventListener("dragenter", function(ev) {
+ return me.@com.vaadin.terminal.gwt.client.ui.VDragAndDropWrapper::html5DragEnter(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev);
+ }, false);
+
+ el.addEventListener("dragleave", function(ev) {
+ return me.@com.vaadin.terminal.gwt.client.ui.VDragAndDropWrapper::html5DragLeave(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev);
+ }, false);
+
+ el.addEventListener("dragover", function(ev) {
+ return me.@com.vaadin.terminal.gwt.client.ui.VDragAndDropWrapper::html5DragOver(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev);
+ }, false);
+
+ el.addEventListener("drop", function(ev) {
+ return me.@com.vaadin.terminal.gwt.client.ui.VDragAndDropWrapper::html5DragDrop(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev);
+ }, false);
}-*/;
public boolean updateDropDetails(VDragEvent drag) {
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VDragAndDropWrapperIE.java b/src/com/vaadin/terminal/gwt/client/ui/VDragAndDropWrapperIE.java
new file mode 100644
index 0000000000..89f063eaf9
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/VDragAndDropWrapperIE.java
@@ -0,0 +1,65 @@
+package com.vaadin.terminal.gwt.client.ui;
+
+import com.google.gwt.dom.client.AnchorElement;
+import com.google.gwt.dom.client.Document;
+import com.google.gwt.user.client.Element;
+import com.vaadin.terminal.gwt.client.VConsole;
+
+public class VDragAndDropWrapperIE extends VDragAndDropWrapper {
+ private AnchorElement anchor = null;
+
+ @Override
+ protected Element getDragStartElement() {
+ VConsole.log("IE get drag start element...");
+ Element div = getElement();
+ if (dragStartMode == HTML5) {
+ if (anchor == null) {
+ anchor = Document.get().createAnchorElement();
+ anchor.setHref("#");
+ anchor.setClassName("drag-start");
+ div.appendChild(anchor);
+ }
+ VConsole.log("IE get drag start element...");
+ return (Element) anchor.cast();
+ } else {
+ if (anchor != null) {
+ div.removeChild(anchor);
+ anchor = null;
+ }
+ return div;
+ }
+ }
+
+ @Override
+ protected native void hookHtml5DragStart(Element el)
+ /*-{
+ var me = this;
+
+ el.attachEvent("ondragstart", function(ev) {
+ return me.@com.vaadin.terminal.gwt.client.ui.VDragAndDropWrapper::html5DragStart(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev);
+ });
+ }-*/;
+
+ @Override
+ protected native void hookHtml5Events(Element el)
+ /*-{
+ var me = this;
+
+ el.attachEvent("ondragenter", function(ev) {
+ return me.@com.vaadin.terminal.gwt.client.ui.VDragAndDropWrapper::html5DragEnter(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev);
+ });
+
+ el.attachEvent("ondragleave", function(ev) {
+ return me.@com.vaadin.terminal.gwt.client.ui.VDragAndDropWrapper::html5DragLeave(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev);
+ });
+
+ el.attachEvent("ondragover", function(ev) {
+ return me.@com.vaadin.terminal.gwt.client.ui.VDragAndDropWrapper::html5DragOver(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev);
+ });
+
+ el.attachEvent("ondrop", function(ev) {
+ return me.@com.vaadin.terminal.gwt.client.ui.VDragAndDropWrapper::html5DragDrop(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev);
+ });
+ }-*/;
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VFilterSelect.java b/src/com/vaadin/terminal/gwt/client/ui/VFilterSelect.java
index 6f90a140e8..2af04cb97e 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/VFilterSelect.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/VFilterSelect.java
@@ -7,8 +7,10 @@ package com.vaadin.terminal.gwt.client.ui;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
+import java.util.Set;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
@@ -48,6 +50,7 @@ import com.vaadin.terminal.gwt.client.Focusable;
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;
/**
@@ -55,6 +58,7 @@ import com.vaadin.terminal.gwt.client.VTooltip;
*
* TODO needs major refactoring (to be extensible etc)
*/
+@SuppressWarnings("deprecation")
public class VFilterSelect extends Composite implements Paintable, Field,
KeyDownHandler, KeyUpHandler, ClickHandler, FocusHandler, BlurHandler,
Focusable {
@@ -95,7 +99,15 @@ public class VFilterSelect extends Composite implements Paintable, Field,
sb.append(Util.escapeAttribute(iconUri));
sb.append("\" alt=\"\" class=\"v-icon\" />");
}
- sb.append("<span>" + Util.escapeHTML(caption) + "</span>");
+ String content;
+ if ("".equals(caption)) {
+ // Ensure that empty options use the same height as other
+ // options and are not collapsed (#7506)
+ content = "&nbsp;";
+ } else {
+ content = Util.escapeHTML(caption);
+ }
+ sb.append("<span>" + content + "</span>");
return sb.toString();
}
@@ -285,7 +297,7 @@ public class VFilterSelect extends Composite implements Paintable, Field,
.getText().length() - lastFilter.length());
} else if (hasNextPage()) {
- lastIndex = index - 1; // save for paging
+ selectPopupItemWhenResponseIsReceived = Select.FIRST;
filterOptions(currentPage + 1, lastFilter);
}
}
@@ -304,7 +316,7 @@ public class VFilterSelect extends Composite implements Paintable, Field,
.getText().length() - lastFilter.length());
} else if (index == -1) {
if (currentPage > 0) {
- lastIndex = index + 1; // save for paging
+ selectPopupItemWhenResponseIsReceived = Select.LAST;
filterOptions(currentPage - 1, lastFilter);
}
} else {
@@ -329,7 +341,20 @@ public class VFilterSelect extends Composite implements Paintable, Field,
@Override
public void run() {
if (pagesToScroll != 0) {
- filterOptions(currentPage + pagesToScroll, lastFilter);
+ if (!waitingForFilteringResponse) {
+ /*
+ * Avoid scrolling while we are waiting for a response
+ * because otherwise the waiting flag will be reset in
+ * the first response and the second response will be
+ * ignored, causing an empty popup...
+ *
+ * As long as the scrolling delay is suitable
+ * double/triple clicks will work by scrolling two or
+ * three pages at a time and this should not be a
+ * problem.
+ */
+ filterOptions(currentPage + pagesToScroll, lastFilter);
+ }
pagesToScroll = 0;
}
}
@@ -338,7 +363,7 @@ public class VFilterSelect extends Composite implements Paintable, Field,
if (currentPage + pagesToScroll > 0) {
pagesToScroll--;
cancel();
- schedule(100);
+ schedule(200);
}
}
@@ -347,7 +372,7 @@ public class VFilterSelect extends Composite implements Paintable, Field,
* pageLength) {
pagesToScroll++;
cancel();
- schedule(100);
+ schedule(200);
}
}
}
@@ -537,6 +562,13 @@ public class VFilterSelect extends Composite implements Paintable, Field,
public class SuggestionMenu extends MenuBar implements SubPartAware,
LoadHandler {
+ /**
+ * Tracks the item that is currently selected using the keyboard. This
+ * is need only because mouseover changes the selection and we do not
+ * want to use that selection when pressing enter to select the item.
+ */
+ private MenuItem keyboardSelectedItem;
+
private VLazyExecutor delayedImageLoadExecutioner = new VLazyExecutor(
100, new ScheduledCommand() {
@@ -583,6 +615,10 @@ public class VFilterSelect extends Composite implements Paintable, Field,
*/
public void setSuggestions(
Collection<FilterSelectSuggestion> suggestions) {
+ // Reset keyboard selection when contents is updated to avoid
+ // reusing old, invalid data
+ setKeyboardSelectedItem(null);
+
clearItems();
final Iterator<FilterSelectSuggestion> it = suggestions.iterator();
while (it.hasNext()) {
@@ -622,8 +658,8 @@ public class VFilterSelect extends Composite implements Paintable, Field,
return;
}
- selecting = filtering;
- if (!filtering) {
+ updateSelectionWhenReponseIsReceived = waitingForFilteringResponse;
+ if (!waitingForFilteringResponse) {
doPostFilterSelectedItemAction();
}
}
@@ -635,7 +671,7 @@ public class VFilterSelect extends Composite implements Paintable, Field,
final MenuItem item = getSelectedItem();
final String enteredItemValue = tb.getText();
- selecting = false;
+ updateSelectionWhenReponseIsReceived = false;
// check for exact match in menu
int p = getItems().size();
@@ -730,6 +766,25 @@ public class VFilterSelect extends Composite implements Paintable, Field,
delayedImageLoadExecutioner.trigger();
}
+
+ public void selectFirstItem() {
+ MenuItem firstItem = getItems().get(0);
+ selectItem(firstItem);
+ }
+
+ private MenuItem getKeyboardSelectedItem() {
+ return keyboardSelectedItem;
+ }
+
+ private void setKeyboardSelectedItem(MenuItem firstItem) {
+ keyboardSelectedItem = firstItem;
+ }
+
+ public void selectLastItem() {
+ List<MenuItem> items = getItems();
+ MenuItem lastItem = items.get(items.size() - 1);
+ selectItem(lastItem);
+ }
}
public static final int FILTERINGMODE_OFF = 0;
@@ -741,6 +796,8 @@ public class VFilterSelect extends Composite implements Paintable, Field,
protected int pageLength = 10;
+ private boolean enableDebug = false;
+
private final FlowPanel panel = new FlowPanel();
/**
@@ -814,19 +871,24 @@ public class VFilterSelect extends Composite implements Paintable, Field,
* A collection of available suggestions (options) as received from the
* server.
*/
- private final Collection<FilterSelectSuggestion> currentSuggestions = new ArrayList<FilterSelectSuggestion>();
+ private final List<FilterSelectSuggestion> currentSuggestions = new ArrayList<FilterSelectSuggestion>();
private boolean immediate;
private String selectedOptionKey;
- private boolean filtering = false;
- private boolean selecting = false;
- private boolean tabPressed = false;
+ private boolean waitingForFilteringResponse = false;
+ private boolean updateSelectionWhenReponseIsReceived = false;
+ private boolean tabPressedWhenPopupOpen = false;
private boolean initDone = false;
private String lastFilter = "";
- private int lastIndex = -1; // last selected index when using arrows
+
+ private enum Select {
+ NONE, FIRST, LAST
+ };
+
+ private Select selectPopupItemWhenResponseIsReceived = Select.NONE;
/**
* The current suggestion selected from the dropdown. This is one of the
@@ -961,7 +1023,7 @@ public class VFilterSelect extends Composite implements Paintable, Field,
}
}
- filtering = true;
+ waitingForFilteringResponse = true;
client.updateVariable(paintableId, "filter", filter, false);
client.updateVariable(paintableId, "page", page, true);
lastFilter = filter;
@@ -1025,14 +1087,13 @@ public class VFilterSelect extends Composite implements Paintable, Field,
inputPrompt = "";
}
- suggestionPopup.setPagingEnabled(true);
suggestionPopup.updateStyleNames(uidl);
allowNewItem = uidl.hasAttribute("allownewitem");
lastNewItemString = null;
currentSuggestions.clear();
- if (!filtering) {
+ if (!waitingForFilteringResponse) {
/*
* Clear the current suggestions as the server response always
* includes the new ones. Exception is when filtering, then we need
@@ -1052,6 +1113,8 @@ public class VFilterSelect extends Composite implements Paintable, Field,
final UIDL options = uidl.getChildUIDL(0);
if (uidl.hasAttribute("totalMatches")) {
totalMatches = uidl.getIntAttribute("totalMatches");
+ } else {
+ totalMatches = 0;
}
// used only to calculate minimum popup width
@@ -1063,7 +1126,7 @@ public class VFilterSelect extends Composite implements Paintable, Field,
optionUidl);
currentSuggestions.add(suggestion);
if (optionUidl.hasAttribute("selected")) {
- if (!filtering || popupOpenerClicked) {
+ if (!waitingForFilteringResponse || popupOpenerClicked) {
String newSelectedOptionKey = Integer.toString(suggestion
.getOptionKey());
if (!newSelectedOptionKey.equals(selectedOptionKey)
@@ -1087,10 +1150,11 @@ public class VFilterSelect extends Composite implements Paintable, Field,
captions += Util.escapeHTML(suggestion.getReplacementString());
}
- if ((!filtering || popupOpenerClicked) && uidl.hasVariable("selected")
+ if ((!waitingForFilteringResponse || popupOpenerClicked)
+ && uidl.hasVariable("selected")
&& uidl.getStringArrayVariable("selected").length == 0) {
// select nulled
- if (!filtering || !popupOpenerClicked) {
+ if (!waitingForFilteringResponse || !popupOpenerClicked) {
if (!focused) {
/*
* client.updateComponent overwrites all styles so we must
@@ -1108,41 +1172,35 @@ public class VFilterSelect extends Composite implements Paintable, Field,
selectedOptionKey = null;
}
- if (filtering
+ if (waitingForFilteringResponse
&& lastFilter.toLowerCase().equals(
uidl.getStringVariable("filter"))) {
suggestionPopup.showSuggestions(currentSuggestions, currentPage,
totalMatches);
- filtering = false;
- if (!popupOpenerClicked && lastIndex != -1) {
+ waitingForFilteringResponse = false;
+ if (!popupOpenerClicked
+ && selectPopupItemWhenResponseIsReceived != Select.NONE) {
// we're paging w/ arrows
- MenuItem activeMenuItem;
- if (lastIndex == 0) {
- // going up, select last item
- int lastItem = pageLength - 1;
- List<MenuItem> items = suggestionPopup.menu.getItems();
- /*
- * The first page can contain less than 10 items if the null
- * selection item is filtered away
- */
- if (lastItem >= items.size()) {
- lastItem = items.size() - 1;
- }
- activeMenuItem = items.get(lastItem);
- suggestionPopup.menu.selectItem(activeMenuItem);
+ if (selectPopupItemWhenResponseIsReceived == Select.LAST) {
+ suggestionPopup.menu.selectLastItem();
} else {
- // going down, select first item
- activeMenuItem = suggestionPopup.menu.getItems().get(0);
- suggestionPopup.menu.selectItem(activeMenuItem);
+ suggestionPopup.menu.selectFirstItem();
}
+ // This is used for paging so we update the keyboard selection
+ // variable as well.
+ MenuItem activeMenuItem = suggestionPopup.menu
+ .getSelectedItem();
+ suggestionPopup.menu.setKeyboardSelectedItem(activeMenuItem);
+
+ // Update text field to contain the correct text
setTextboxText(activeMenuItem.getText());
tb.setSelectionRange(lastFilter.length(), activeMenuItem
.getText().length() - lastFilter.length());
- lastIndex = -1; // reset
+ selectPopupItemWhenResponseIsReceived = Select.NONE; // reset
}
- if (selecting) {
+ if (updateSelectionWhenReponseIsReceived) {
suggestionPopup.menu.doPostFilterSelectedItemAction();
}
}
@@ -1254,7 +1312,7 @@ public class VFilterSelect extends Composite implements Paintable, Field,
* The suggestion that just got selected.
*/
public void onSuggestionSelected(FilterSelectSuggestion suggestion) {
- selecting = false;
+ updateSelectionWhenReponseIsReceived = false;
currentSuggestion = suggestion;
String newKey;
@@ -1316,6 +1374,15 @@ public class VFilterSelect extends Composite implements Paintable, Field,
marginTop + "px");
}
+ private static Set<Integer> navigationKeyCodes = new HashSet<Integer>();
+ static {
+ navigationKeyCodes.add(KeyCodes.KEY_DOWN);
+ navigationKeyCodes.add(KeyCodes.KEY_UP);
+ navigationKeyCodes.add(KeyCodes.KEY_PAGEDOWN);
+ navigationKeyCodes.add(KeyCodes.KEY_PAGEUP);
+ navigationKeyCodes.add(KeyCodes.KEY_ENTER);
+ }
+
/*
* (non-Javadoc)
*
@@ -1325,34 +1392,39 @@ public class VFilterSelect extends Composite implements Paintable, Field,
*/
public void onKeyDown(KeyDownEvent event) {
if (enabled && !readonly) {
- if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) {
- // Same reaction to enter no matter on whether the popup is open
- if (suggestionPopup.isAttached()) {
- filterOptions(currentPage);
- } else if (currentSuggestion != null
- && tb.getText().equals(
- currentSuggestion.getReplacementString())) {
- // Retain behavior from #6686 by returning without stopping
- // propagation if there's nothing to do
- return;
- }
- if (currentSuggestions.size() == 1 && !allowNewItem) {
- // If there is only one suggestion, select that
- suggestionPopup.menu.selectItem(suggestionPopup.menu
- .getItems().get(0));
- }
- suggestionPopup.menu.doSelectedItemAction();
+ int keyCode = event.getNativeKeyCode();
+ debug("key down: " + keyCode);
+ if (waitingForFilteringResponse
+ && navigationKeyCodes.contains(keyCode)) {
+ /*
+ * Keyboard navigation events should not be handled while we are
+ * waiting for a response. This avoids flickering, disappearing
+ * items, wrongly interpreted responses and more.
+ */
+ debug("Ignoring " + keyCode
+ + " because we are waiting for a filtering response");
+ DOM.eventPreventDefault(DOM.eventGetCurrentEvent());
event.stopPropagation();
return;
- } else if (suggestionPopup.isAttached()) {
+ }
+
+ if (suggestionPopup.isAttached()) {
+ debug("Keycode " + keyCode + " target is popup");
popupKeyDown(event);
} else {
+ debug("Keycode " + keyCode + " target is text field");
inputFieldKeyDown(event);
}
}
}
+ private void debug(String string) {
+ if (enableDebug) {
+ VConsole.error(string);
+ }
+ }
+
/**
* Triggered when a key is pressed in the text box
*
@@ -1365,17 +1437,31 @@ public class VFilterSelect extends Composite implements Paintable, Field,
case KeyCodes.KEY_UP:
case KeyCodes.KEY_PAGEDOWN:
case KeyCodes.KEY_PAGEUP:
- if (!suggestionPopup.isAttached()) {
- // open popup as from gadget
- filterOptions(-1, "");
- lastFilter = "";
- tb.selectAll();
- }
+ // open popup as from gadget
+ filterOptions(-1, "");
+ lastFilter = "";
+ tb.selectAll();
break;
- case KeyCodes.KEY_TAB:
- if (suggestionPopup.isAttached()) {
- filterOptions(currentPage, tb.getText());
+ case KeyCodes.KEY_ENTER:
+ /*
+ * This only handles the case when new items is allowed, a text is
+ * entered, the popup opener button is clicked to close the popup
+ * and enter is then pressed (see #7560).
+ */
+ if (!allowNewItem) {
+ return;
}
+
+ if (currentSuggestion != null
+ && tb.getText().equals(
+ currentSuggestion.getReplacementString())) {
+ // Retain behavior from #6686 by returning without stopping
+ // propagation if there's nothing to do
+ return;
+ }
+ suggestionPopup.menu.doSelectedItemAction();
+
+ event.stopPropagation();
break;
}
@@ -1393,11 +1479,15 @@ public class VFilterSelect extends Composite implements Paintable, Field,
switch (event.getNativeKeyCode()) {
case KeyCodes.KEY_DOWN:
suggestionPopup.selectNextItem();
+ suggestionPopup.menu.setKeyboardSelectedItem(suggestionPopup.menu
+ .getSelectedItem());
DOM.eventPreventDefault(DOM.eventGetCurrentEvent());
event.stopPropagation();
break;
case KeyCodes.KEY_UP:
suggestionPopup.selectPrevItem();
+ suggestionPopup.menu.setKeyboardSelectedItem(suggestionPopup.menu
+ .getSelectedItem());
DOM.eventPreventDefault(DOM.eventGetCurrentEvent());
event.stopPropagation();
break;
@@ -1414,12 +1504,43 @@ public class VFilterSelect extends Composite implements Paintable, Field,
event.stopPropagation();
break;
case KeyCodes.KEY_TAB:
- if (suggestionPopup.isAttached()) {
- tabPressed = true;
- filterOptions(currentPage);
- }
+ tabPressedWhenPopupOpen = true;
+ filterOptions(currentPage);
// onBlur() takes care of the rest
break;
+ case KeyCodes.KEY_ESCAPE:
+ reset();
+ event.stopPropagation();
+ break;
+ case KeyCodes.KEY_ENTER:
+ if (suggestionPopup.menu.getKeyboardSelectedItem() == null) {
+ /*
+ * Nothing selected using up/down. Happens e.g. when entering a
+ * text (causes popup to open) and then pressing enter.
+ */
+ if (!allowNewItem) {
+ /*
+ * New items are not allowed: If there is only one
+ * suggestion, select that. Otherwise do nothing.
+ */
+ if (currentSuggestions.size() == 1) {
+ onSuggestionSelected(currentSuggestions.get(0));
+ }
+ } else {
+ // Handle addition of new items.
+ suggestionPopup.menu.doSelectedItemAction();
+ }
+ } else {
+ /*
+ * Get the suggestion that was navigated to using up/down.
+ */
+ currentSuggestion = ((FilterSelectSuggestion) suggestionPopup.menu
+ .getKeyboardSelectedItem().getCommand());
+ onSuggestionSelected(currentSuggestion);
+ }
+
+ event.stopPropagation();
+ break;
}
}
@@ -1442,10 +1563,8 @@ public class VFilterSelect extends Composite implements Paintable, Field,
case KeyCodes.KEY_UP:
case KeyCodes.KEY_PAGEDOWN:
case KeyCodes.KEY_PAGEUP:
- ; // NOP
- break;
case KeyCodes.KEY_ESCAPE:
- reset();
+ ; // NOP
break;
default:
if (textInputEnabled) {
@@ -1604,8 +1723,8 @@ public class VFilterSelect extends Composite implements Paintable, Field,
focused = false;
if (!readonly) {
// much of the TAB handling takes place here
- if (tabPressed) {
- tabPressed = false;
+ if (tabPressedWhenPopupOpen) {
+ tabPressedWhenPopupOpen = false;
suggestionPopup.menu.doSelectedItemAction();
suggestionPopup.hide();
} else if (!suggestionPopup.isAttached()
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VView.java b/src/com/vaadin/terminal/gwt/client/ui/VView.java
index af87610944..cd9f0c903c 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/VView.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/VView.java
@@ -14,6 +14,7 @@ import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.dom.client.DivElement;
import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Style;
import com.google.gwt.dom.client.Style.Display;
import com.google.gwt.event.dom.client.DomEvent.Type;
@@ -23,7 +24,6 @@ import com.google.gwt.event.shared.EventHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.Window;
@@ -109,7 +109,8 @@ public class VView extends SimplePanel implements Container, ResizeHandler,
private VLazyExecutor delayedResizeExecutor = new VLazyExecutor(200,
new ScheduledCommand() {
public void execute() {
- windowSizeMaybeChanged(getOffsetWidth(), getOffsetHeight());
+ windowSizeMaybeChanged(Window.getClientWidth(),
+ Window.getClientHeight());
}
});
@@ -576,25 +577,58 @@ public class VView extends SimplePanel implements Container, ResizeHandler,
return getElement().getOffsetWidth() - getExcessWidth();
}
- // If not running standalone, we might be inside elements that don't
- // shrink with the browser window if our own components have
+ // If not running standalone, there might be multiple Vaadin apps
+ // that won't shrink with the browser window as the components have
// calculated widths (#3125)
- Element layoutElement = ((Widget) layout).getElement();
- Style layoutStyle = layoutElement.getStyle();
- // Set display:none to the entire application to get a width not
- // influenced by the contents
- String originalDisplay = layoutStyle.getDisplay();
- layoutStyle.setDisplay(Display.NONE);
+ // Find all Vaadin applications on the page
+ ArrayList<String> vaadinApps = new ArrayList<String>();
+ loadAppIdListFromDOM(vaadinApps);
+
+ // Store original styles here so they can be restored
+ ArrayList<String> originalDisplays = new ArrayList<String>(
+ vaadinApps.size());
+
+ String ownAppId = connection.getConfiguration().getRootPanelId();
+
+ // Set display: none for all Vaadin apps
+ for (int i = 0; i < vaadinApps.size(); i++) {
+ String appId = vaadinApps.get(i);
+ Element targetElement;
+ if (appId.equals(ownAppId)) {
+ // Only hide the contents of current application
+ targetElement = ((Widget) layout).getElement();
+ } else {
+ // Hide everything for other applications
+ targetElement = Document.get().getElementById(appId);
+ }
+ Style layoutStyle = targetElement.getStyle();
+
+ originalDisplays.add(i, layoutStyle.getDisplay());
+ layoutStyle.setDisplay(Display.NONE);
+ }
int w = getElement().getOffsetWidth() - getExcessWidth();
// Then restore the old display style before returning
- if (originalDisplay.length() == 0) {
- layoutStyle.clearDisplay();
- } else {
- layoutStyle.setDisplay(Display.valueOf(originalDisplay));
+ for (int i = 0; i < vaadinApps.size(); i++) {
+ String appId = vaadinApps.get(i);
+ Element targetElement;
+ if (appId.equals(ownAppId)) {
+ targetElement = ((Widget) layout).getElement();
+ } else {
+ targetElement = Document.get().getElementById(appId);
+ }
+ Style layoutStyle = targetElement.getStyle();
+ String originalDisplay = originalDisplays.get(i);
+
+ if (originalDisplay.length() == 0) {
+ layoutStyle.clearDisplay();
+ } else {
+ layoutStyle.setProperty("display", originalDisplay);
+ }
}
+
return w;
}
@@ -611,6 +645,14 @@ public class VView extends SimplePanel implements Container, ResizeHandler,
}
};
+ private native static void loadAppIdListFromDOM(ArrayList<String> list)
+ /*-{
+ var j;
+ for(j in $wnd.vaadin.vaadinConfigurations) {
+ list.@java.util.Collection::add(Ljava/lang/Object;)(j);
+ }
+ }-*/;
+
public RenderSpace getAllocatedSpace(Widget child) {
return myRenderSpace;
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent.java b/src/com/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent.java
index 3f3cf54e87..dd5852247a 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent.java
@@ -41,7 +41,15 @@ public class VHtml5DragEvent extends NativeEvent {
return null;
}-*/;
- public final native void setDragEffect(String effect)
+ /**
+ * @deprecated As of Vaadin 6.8, replaced by {@link #setDropEffect(String)}.
+ */
+ @Deprecated
+ public final void setDragEffect(String effect) {
+ setDropEffect(effect);
+ }
+
+ public final native void setDropEffect(String effect)
/*-{
try {
this.dataTransfer.dropEffect = effect;
@@ -53,6 +61,11 @@ public class VHtml5DragEvent extends NativeEvent {
return this.dataTransfer.effectAllowed;
}-*/;
+ public final native void setEffectAllowed(String effect)
+ /*-{
+ this.dataTransfer.effectAllowed = effect;
+ }-*/;
+
public final native int getFileCount()
/*-{
return this.dataTransfer.files ? this.dataTransfer.files.length : 0;
@@ -63,4 +76,9 @@ public class VHtml5DragEvent extends NativeEvent {
return this.dataTransfer.files[fileIndex];
}-*/;
+ public final native void setHtml5DataFlavor(String flavor, String data)
+ /*-{
+ this.dataTransfer.setData(flavor, data);
+ }-*/;
+
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/dd/VTransferable.java b/src/com/vaadin/terminal/gwt/client/ui/dd/VTransferable.java
index 57b4d90e07..bb726586e2 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/dd/VTransferable.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/dd/VTransferable.java
@@ -45,12 +45,12 @@ public class VTransferable {
this.component = component;
}
- public Object getData(String dataFlawor) {
- return variables.get(dataFlawor);
+ public Object getData(String dataFlavor) {
+ return variables.get(dataFlavor);
}
- public void setData(String dataFlawor, Object value) {
- variables.put(dataFlawor, value);
+ public void setData(String dataFlavor, Object value) {
+ variables.put(dataFlavor, value);
}
public Collection<String> getDataFlavors() {
diff --git a/src/com/vaadin/ui/DragAndDropWrapper.java b/src/com/vaadin/ui/DragAndDropWrapper.java
index 83aa6314c8..9e06382eac 100644
--- a/src/com/vaadin/ui/DragAndDropWrapper.java
+++ b/src/com/vaadin/ui/DragAndDropWrapper.java
@@ -6,6 +6,7 @@ package com.vaadin.ui;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Iterator;
+import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
@@ -54,7 +55,7 @@ public class DragAndDropWrapper extends CustomComponent implements DropTarget,
/**
* The component in wrapper that is being dragged or null if the
- * transferrable is not a component (most likely an html5 drag).
+ * transferable is not a component (most likely an html5 drag).
*
* @return
*/
@@ -135,7 +136,8 @@ public class DragAndDropWrapper extends CustomComponent implements DropTarget,
}
/**
- * @return a detail about the drags horizontal position over the wrapper.
+ * @return a detail about the drags horizontal position over the
+ * wrapper.
*/
public HorizontalDropLocation getHorizontalDropLocation() {
return HorizontalDropLocation
@@ -172,9 +174,18 @@ public class DragAndDropWrapper extends CustomComponent implements DropTarget,
/**
* The whole wrapper is used as a drag image when dragging.
*/
- WRAPPER
+ WRAPPER,
+ /**
+ * The whole wrapper is used to start an HTML5 drag.
+ *
+ * NOTE: In Internet Explorer 6 to 8, this prevents user interactions
+ * with the wrapper's contents. For example, clicking a button inside
+ * the wrapper will no longer work.
+ */
+ HTML5,
}
+ private final Map<String, Object> html5DataFlavors = new LinkedHashMap<String, Object>();
private DragStartMode dragStartMode = DragStartMode.NONE;
/**
@@ -187,10 +198,27 @@ public class DragAndDropWrapper extends CustomComponent implements DropTarget,
super(root);
}
+ /**
+ * Sets data flavors available in the DragAndDropWrapper is used to start an
+ * HTML5 style drags. Most commonly the "Text" flavor should be set.
+ * Multiple data types can be set.
+ *
+ * @param type
+ * the string identifier of the drag "payload". E.g. "Text" or
+ * "text/html"
+ * @param value
+ * the value
+ */
+ public void setHTML5DataFlavor(String type, Object value) {
+ html5DataFlavors.put(type, value);
+ requestRepaint();
+ }
+
@Override
public void paintContent(PaintTarget target) throws PaintException {
super.paintContent(target);
- target.addAttribute("dragStartMode", dragStartMode.ordinal());
+ target.addAttribute(VDragAndDropWrapper.DRAG_START_MODE,
+ dragStartMode.ordinal());
if (getDropHandler() != null) {
getDropHandler().getAcceptCriterion().paint(target);
}
@@ -213,6 +241,8 @@ public class DragAndDropWrapper extends CustomComponent implements DropTarget,
}
}
}
+ target.addAttribute(VDragAndDropWrapper.HTML5_DATA_FLAVORS,
+ html5DataFlavors);
}
private DropHandler dropHandler;
diff --git a/src/com/vaadin/ui/Select.java b/src/com/vaadin/ui/Select.java
index 7b6fc07e5b..439efdb864 100644
--- a/src/com/vaadin/ui/Select.java
+++ b/src/com/vaadin/ui/Select.java
@@ -148,7 +148,7 @@ public class Select extends AbstractSelect implements AbstractSelect.Filtering,
}
// Adds the required attribute
- if (isRequired()) {
+ if (!isReadOnly() && isRequired()) {
target.addAttribute("required", true);
}
diff --git a/src/com/vaadin/ui/TreeTable.java b/src/com/vaadin/ui/TreeTable.java
index 9cba7362ac..fb12ffe323 100644
--- a/src/com/vaadin/ui/TreeTable.java
+++ b/src/com/vaadin/ui/TreeTable.java
@@ -333,6 +333,12 @@ public class TreeTable extends Table implements Hierarchical {
private boolean animationsEnabled;
private boolean clearFocusedRowPending;
+ /**
+ * If the container does not send item set change events, always do a full
+ * repaint instead of a partial update when expanding/collapsing nodes.
+ */
+ private boolean containerSupportsPartialUpdates;
+
private ContainerStrategy getContainerStrategy() {
if (cStrategy == null) {
if (getContainerDataSource() instanceof Collapsible) {
@@ -464,7 +470,7 @@ public class TreeTable extends Table implements Hierarchical {
@Override
protected boolean isPartialRowUpdate() {
- return toggledItemId != null;
+ return toggledItemId != null && containerSupportsPartialUpdates;
}
@Override
@@ -515,13 +521,20 @@ public class TreeTable extends Table implements Hierarchical {
// ensure that page still has first item in page, DON'T clear the
// caches.
setCurrentPageFirstItemIndex(getCurrentPageFirstItemIndex(), false);
- requestRepaint();
if (isCollapsed(itemId)) {
fireCollapseEvent(itemId);
} else {
fireExpandEvent(itemId);
}
+
+ if (containerSupportsPartialUpdates) {
+ requestRepaint();
+ } else {
+ // For containers that do not send item set change events, always do
+ // full repaint instead of partial row update.
+ refreshRowCache();
+ }
}
@Override
@@ -537,6 +550,9 @@ public class TreeTable extends Table implements Hierarchical {
@Override
public void setContainerDataSource(Container newDataSource) {
cStrategy = null;
+
+ containerSupportsPartialUpdates = (newDataSource instanceof ItemSetChangeNotifier);
+
if (!(newDataSource instanceof Hierarchical)) {
newDataSource = new ContainerHierarchicalWrapper(newDataSource);
}