summaryrefslogtreecommitdiffstats
path: root/client
diff options
context:
space:
mode:
Diffstat (limited to 'client')
-rwxr-xr-xclient/src/com/vaadin/DefaultWidgetSet.gwt.xml4
-rw-r--r--client/src/com/vaadin/Vaadin.gwt.xml36
-rw-r--r--client/src/com/vaadin/client/ApplicationConfiguration.java55
-rw-r--r--client/src/com/vaadin/client/ApplicationConnection.java284
-rw-r--r--client/src/com/vaadin/client/ComponentLocator.java698
-rw-r--r--client/src/com/vaadin/client/ConnectorMap.java2
-rw-r--r--client/src/com/vaadin/client/LayoutManager.java169
-rw-r--r--client/src/com/vaadin/client/Profiler.java4
-rw-r--r--client/src/com/vaadin/client/RenderInformation.java21
-rw-r--r--client/src/com/vaadin/client/SimpleTree.java33
-rw-r--r--client/src/com/vaadin/client/Util.java24
-rw-r--r--client/src/com/vaadin/client/VCaption.java70
-rw-r--r--client/src/com/vaadin/client/VErrorMessage.java20
-rw-r--r--client/src/com/vaadin/client/VLoadingIndicator.java6
-rw-r--r--client/src/com/vaadin/client/VTooltip.java77
-rw-r--r--client/src/com/vaadin/client/annotations/OnStateChange.java50
-rw-r--r--client/src/com/vaadin/client/communication/AtmospherePushConnection.java22
-rw-r--r--client/src/com/vaadin/client/communication/Date_Serializer.java44
-rw-r--r--client/src/com/vaadin/client/communication/Heartbeat.java171
-rw-r--r--client/src/com/vaadin/client/communication/JSONSerializer.java7
-rw-r--r--client/src/com/vaadin/client/communication/PushConnection.java7
-rw-r--r--client/src/com/vaadin/client/componentlocator/ComponentLocator.java250
-rw-r--r--client/src/com/vaadin/client/componentlocator/LegacyLocatorStrategy.java718
-rw-r--r--client/src/com/vaadin/client/componentlocator/LocatorStrategy.java124
-rw-r--r--client/src/com/vaadin/client/componentlocator/LocatorUtil.java76
-rw-r--r--client/src/com/vaadin/client/componentlocator/SelectorPredicate.java228
-rw-r--r--client/src/com/vaadin/client/componentlocator/VaadinFinderLocatorStrategy.java748
-rw-r--r--client/src/com/vaadin/client/debug/internal/AnalyzeLayoutsPanel.java267
-rw-r--r--client/src/com/vaadin/client/debug/internal/ConnectorInfoPanel.java107
-rw-r--r--client/src/com/vaadin/client/debug/internal/HierarchyPanel.java178
-rw-r--r--client/src/com/vaadin/client/debug/internal/HierarchySection.java478
-rw-r--r--client/src/com/vaadin/client/debug/internal/Highlight.java60
-rw-r--r--client/src/com/vaadin/client/debug/internal/Icon.java6
-rw-r--r--client/src/com/vaadin/client/debug/internal/OptimizedWidgetsetPanel.java137
-rw-r--r--client/src/com/vaadin/client/debug/internal/SelectConnectorListener.java38
-rw-r--r--client/src/com/vaadin/client/debug/internal/SelectorPath.java268
-rw-r--r--client/src/com/vaadin/client/debug/internal/TestBenchSection.java281
-rw-r--r--client/src/com/vaadin/client/event/PointerCancelEvent.java62
-rw-r--r--client/src/com/vaadin/client/event/PointerCancelHandler.java34
-rw-r--r--client/src/com/vaadin/client/event/PointerDownEvent.java61
-rw-r--r--client/src/com/vaadin/client/event/PointerDownHandler.java34
-rw-r--r--client/src/com/vaadin/client/event/PointerEvent.java173
-rw-r--r--client/src/com/vaadin/client/event/PointerEventSupport.java55
-rw-r--r--client/src/com/vaadin/client/event/PointerEventSupportImpl.java51
-rw-r--r--client/src/com/vaadin/client/event/PointerEventSupportImplIE10.java34
-rw-r--r--client/src/com/vaadin/client/event/PointerEventSupportImplModernIE.java72
-rw-r--r--client/src/com/vaadin/client/event/PointerMoveEvent.java61
-rw-r--r--client/src/com/vaadin/client/event/PointerMoveHandler.java34
-rw-r--r--client/src/com/vaadin/client/event/PointerUpEvent.java61
-rw-r--r--client/src/com/vaadin/client/event/PointerUpHandler.java34
-rw-r--r--client/src/com/vaadin/client/extensions/ResponsiveConnector.java406
-rw-r--r--client/src/com/vaadin/client/metadata/ConnectorBundleLoader.java8
-rw-r--r--client/src/com/vaadin/client/metadata/JsniInvoker.java53
-rw-r--r--client/src/com/vaadin/client/metadata/Method.java18
-rw-r--r--client/src/com/vaadin/client/metadata/OnStateChangeMethod.java111
-rw-r--r--client/src/com/vaadin/client/metadata/Property.java26
-rw-r--r--client/src/com/vaadin/client/metadata/TypeDataStore.java276
-rw-r--r--client/src/com/vaadin/client/ui/AbstractClickEventHandler.java4
-rw-r--r--client/src/com/vaadin/client/ui/AbstractComponentConnector.java20
-rw-r--r--client/src/com/vaadin/client/ui/AbstractConnector.java35
-rw-r--r--client/src/com/vaadin/client/ui/Action.java11
-rw-r--r--client/src/com/vaadin/client/ui/FocusElementPanel.java13
-rw-r--r--client/src/com/vaadin/client/ui/FocusableScrollPanel.java13
-rw-r--r--client/src/com/vaadin/client/ui/FontIcon.java149
-rw-r--r--client/src/com/vaadin/client/ui/Icon.java62
-rw-r--r--client/src/com/vaadin/client/ui/ImageIcon.java76
-rw-r--r--client/src/com/vaadin/client/ui/LayoutClickEventHandler.java7
-rw-r--r--client/src/com/vaadin/client/ui/ShortcutActionHandler.java2
-rw-r--r--client/src/com/vaadin/client/ui/SubPartAware.java9
-rw-r--r--client/src/com/vaadin/client/ui/VAbsoluteLayout.java2
-rw-r--r--client/src/com/vaadin/client/ui/VAbstractSplitPanel.java2
-rw-r--r--client/src/com/vaadin/client/ui/VAccordion.java170
-rw-r--r--client/src/com/vaadin/client/ui/VButton.java23
-rw-r--r--client/src/com/vaadin/client/ui/VCalendar.java2
-rw-r--r--client/src/com/vaadin/client/ui/VCalendarPanel.java20
-rw-r--r--client/src/com/vaadin/client/ui/VCheckBox.java4
-rw-r--r--client/src/com/vaadin/client/ui/VContextMenu.java6
-rw-r--r--client/src/com/vaadin/client/ui/VCustomLayout.java12
-rw-r--r--client/src/com/vaadin/client/ui/VDateField.java5
-rw-r--r--client/src/com/vaadin/client/ui/VDragAndDropWrapper.java39
-rw-r--r--client/src/com/vaadin/client/ui/VDragAndDropWrapperIE.java26
-rw-r--r--client/src/com/vaadin/client/ui/VEmbedded.java2
-rw-r--r--client/src/com/vaadin/client/ui/VFilterSelect.java98
-rw-r--r--client/src/com/vaadin/client/ui/VForm.java8
-rw-r--r--client/src/com/vaadin/client/ui/VFormLayout.java22
-rw-r--r--client/src/com/vaadin/client/ui/VGridLayout.java28
-rw-r--r--client/src/com/vaadin/client/ui/VLabel.java8
-rw-r--r--client/src/com/vaadin/client/ui/VLink.java6
-rw-r--r--client/src/com/vaadin/client/ui/VMediaBase.java4
-rw-r--r--client/src/com/vaadin/client/ui/VMenuBar.java55
-rw-r--r--client/src/com/vaadin/client/ui/VNativeButton.java8
-rw-r--r--client/src/com/vaadin/client/ui/VNotification.java99
-rw-r--r--client/src/com/vaadin/client/ui/VOptionGroup.java12
-rw-r--r--client/src/com/vaadin/client/ui/VOverlay.java16
-rw-r--r--client/src/com/vaadin/client/ui/VPanel.java24
-rw-r--r--client/src/com/vaadin/client/ui/VPopupCalendar.java9
-rw-r--r--client/src/com/vaadin/client/ui/VPopupView.java7
-rw-r--r--client/src/com/vaadin/client/ui/VProgressBar.java2
-rw-r--r--client/src/com/vaadin/client/ui/VRichTextArea.java2
-rw-r--r--client/src/com/vaadin/client/ui/VScrollTable.java179
-rw-r--r--client/src/com/vaadin/client/ui/VSlider.java10
-rw-r--r--client/src/com/vaadin/client/ui/VTabsheet.java556
-rw-r--r--client/src/com/vaadin/client/ui/VTabsheetBase.java100
-rw-r--r--client/src/com/vaadin/client/ui/VTabsheetPanel.java5
-rw-r--r--client/src/com/vaadin/client/ui/VTextField.java2
-rw-r--r--client/src/com/vaadin/client/ui/VTextualDate.java8
-rw-r--r--client/src/com/vaadin/client/ui/VTree.java54
-rw-r--r--client/src/com/vaadin/client/ui/VTreeTable.java9
-rw-r--r--client/src/com/vaadin/client/ui/VTwinColSelect.java13
-rw-r--r--client/src/com/vaadin/client/ui/VUI.java36
-rw-r--r--client/src/com/vaadin/client/ui/VUnknownComponent.java2
-rw-r--r--client/src/com/vaadin/client/ui/VUpload.java2
-rw-r--r--client/src/com/vaadin/client/ui/VVideo.java2
-rw-r--r--client/src/com/vaadin/client/ui/VWindow.java382
-rw-r--r--client/src/com/vaadin/client/ui/VWindowOverlay.java78
-rw-r--r--client/src/com/vaadin/client/ui/absolutelayout/AbsoluteLayoutConnector.java32
-rw-r--r--client/src/com/vaadin/client/ui/accordion/AccordionConnector.java59
-rw-r--r--client/src/com/vaadin/client/ui/aria/AriaHelper.java5
-rw-r--r--client/src/com/vaadin/client/ui/aria/HandlesAriaCaption.java4
-rw-r--r--client/src/com/vaadin/client/ui/button/ButtonConnector.java99
-rw-r--r--client/src/com/vaadin/client/ui/calendar/CalendarConnector.java4
-rw-r--r--client/src/com/vaadin/client/ui/calendar/schedule/DateCell.java32
-rw-r--r--client/src/com/vaadin/client/ui/calendar/schedule/DateCellDayEvent.java12
-rw-r--r--client/src/com/vaadin/client/ui/calendar/schedule/DayToolbar.java2
-rw-r--r--client/src/com/vaadin/client/ui/calendar/schedule/MonthGrid.java1
-rw-r--r--client/src/com/vaadin/client/ui/calendar/schedule/SimpleDayCell.java1
-rw-r--r--client/src/com/vaadin/client/ui/calendar/schedule/dd/CalendarMonthDropHandler.java9
-rw-r--r--client/src/com/vaadin/client/ui/calendar/schedule/dd/CalendarWeekDropHandler.java23
-rw-r--r--client/src/com/vaadin/client/ui/checkbox/CheckBoxConnector.java23
-rw-r--r--client/src/com/vaadin/client/ui/colorpicker/VColorPickerGradient.java5
-rw-r--r--client/src/com/vaadin/client/ui/colorpicker/VColorPickerGrid.java2
-rw-r--r--client/src/com/vaadin/client/ui/csslayout/CssLayoutConnector.java6
-rw-r--r--client/src/com/vaadin/client/ui/dd/DDUtil.java5
-rw-r--r--client/src/com/vaadin/client/ui/dd/VDragAndDropManager.java18
-rw-r--r--client/src/com/vaadin/client/ui/dd/VDragEvent.java97
-rw-r--r--client/src/com/vaadin/client/ui/dd/VHtml5File.java10
-rw-r--r--client/src/com/vaadin/client/ui/dd/VIsOverId.java3
-rw-r--r--client/src/com/vaadin/client/ui/dd/VItemIdIs.java2
-rw-r--r--client/src/com/vaadin/client/ui/embedded/EmbeddedConnector.java2
-rw-r--r--client/src/com/vaadin/client/ui/form/FormConnector.java20
-rw-r--r--client/src/com/vaadin/client/ui/formlayout/FormLayoutConnector.java8
-rw-r--r--client/src/com/vaadin/client/ui/gridlayout/GridLayoutConnector.java6
-rw-r--r--client/src/com/vaadin/client/ui/label/LabelConnector.java6
-rw-r--r--client/src/com/vaadin/client/ui/layout/VLayoutSlot.java8
-rw-r--r--client/src/com/vaadin/client/ui/link/LinkConnector.java73
-rw-r--r--client/src/com/vaadin/client/ui/menubar/MenuBar.java2
-rw-r--r--client/src/com/vaadin/client/ui/menubar/MenuBarConnector.java7
-rw-r--r--client/src/com/vaadin/client/ui/nativebutton/NativeButtonConnector.java24
-rw-r--r--client/src/com/vaadin/client/ui/orderedlayout/AbstractOrderedLayoutConnector.java19
-rw-r--r--client/src/com/vaadin/client/ui/orderedlayout/Slot.java124
-rw-r--r--client/src/com/vaadin/client/ui/orderedlayout/VAbstractOrderedLayout.java82
-rw-r--r--client/src/com/vaadin/client/ui/panel/PanelConnector.java4
-rw-r--r--client/src/com/vaadin/client/ui/popupview/PopupViewConnector.java5
-rw-r--r--client/src/com/vaadin/client/ui/richtextarea/RichTextAreaConnector.java2
-rw-r--r--client/src/com/vaadin/client/ui/slider/SliderConnector.java20
-rw-r--r--client/src/com/vaadin/client/ui/splitpanel/AbstractSplitPanelConnector.java11
-rw-r--r--client/src/com/vaadin/client/ui/table/TableConnector.java6
-rw-r--r--client/src/com/vaadin/client/ui/tabsheet/TabsheetBaseConnector.java69
-rw-r--r--client/src/com/vaadin/client/ui/tabsheet/TabsheetConnector.java82
-rw-r--r--client/src/com/vaadin/client/ui/tree/TreeConnector.java9
-rw-r--r--client/src/com/vaadin/client/ui/treetable/TreeTableConnector.java4
-rw-r--r--client/src/com/vaadin/client/ui/ui/UIConnector.java27
-rw-r--r--client/src/com/vaadin/client/ui/upload/UploadConnector.java19
-rw-r--r--client/src/com/vaadin/client/ui/window/WindowConnector.java18
164 files changed, 8539 insertions, 2564 deletions
diff --git a/client/src/com/vaadin/DefaultWidgetSet.gwt.xml b/client/src/com/vaadin/DefaultWidgetSet.gwt.xml
index 3aba1f6fee..2719493853 100755
--- a/client/src/com/vaadin/DefaultWidgetSet.gwt.xml
+++ b/client/src/com/vaadin/DefaultWidgetSet.gwt.xml
@@ -10,4 +10,8 @@
<entry-point class="com.vaadin.client.ApplicationConfiguration" />
+ <!-- Since 7.2. Compile all permutations (browser support) into one Javascript
+ file. Speeds up compilation and does not make the Javascript significantly
+ larger. -->
+ <collapse-all-properties />
</module>
diff --git a/client/src/com/vaadin/Vaadin.gwt.xml b/client/src/com/vaadin/Vaadin.gwt.xml
index a1dca07a1c..711729f64f 100644
--- a/client/src/com/vaadin/Vaadin.gwt.xml
+++ b/client/src/com/vaadin/Vaadin.gwt.xml
@@ -1,4 +1,3 @@
-
<module>
<!-- This GWT module inherits all Vaadin client side functionality modules.
This is the module you want to inherit in your client side project to be
@@ -59,8 +58,43 @@
<!-- Use the new cross site linker to get a nocache.js without document.write -->
<add-linker name="xsiframe" />
+ <extend-property name="user.agent" values="opera" />
<!-- Remove IE6/IE7 permutation as they are not supported -->
<set-property name="user.agent" value="ie8,ie9,ie10,gecko1_8,safari,opera" />
+ <!-- Pointer event support -->
+ <define-property name="modernie" values="none,yes" />
+ <property-provider name="modernie"><![CDATA[
+ {
+ var ua = $wnd.navigator.userAgent;
+ if (ua.indexOf('IE') == -1 && ua.indexOf('Trident') != -1) { return 'yes'; }
+ return 'none';
+ }
+ ]]></property-provider>
+
+ <set-property name="modernie" value="none">
+ <none>
+ <when-property-is name="user.agent" value="gecko1_8" />
+ </none>
+ </set-property>
+
+ <!-- Fall through to this rule when the browser doesn't support pointer
+ event -->
+ <replace-with class="com.vaadin.client.event.PointerEventSupportImpl">
+ <when-type-is class="com.vaadin.client.event.PointerEventSupportImpl" />
+ </replace-with>
+
+ <replace-with
+ class="com.vaadin.client.event.PointerEventSupportImplModernIE">
+ <when-type-is class="com.vaadin.client.event.PointerEventSupportImpl" />
+ <none>
+ <when-property-is value="none" name="modernie" />
+ </none>
+ </replace-with>
+
+ <replace-with class="com.vaadin.client.event.PointerEventSupportImplIE10">
+ <when-type-is class="com.vaadin.client.event.PointerEventSupportImpl" />
+ <when-property-is value="ie10" name="user.agent" />
+ </replace-with>
</module>
diff --git a/client/src/com/vaadin/client/ApplicationConfiguration.java b/client/src/com/vaadin/client/ApplicationConfiguration.java
index 7a70080c7e..47edd5efdc 100644
--- a/client/src/com/vaadin/client/ApplicationConfiguration.java
+++ b/client/src/com/vaadin/client/ApplicationConfiguration.java
@@ -41,7 +41,9 @@ import com.vaadin.client.debug.internal.LogSection;
import com.vaadin.client.debug.internal.NetworkSection;
import com.vaadin.client.debug.internal.ProfilerSection;
import com.vaadin.client.debug.internal.Section;
+import com.vaadin.client.debug.internal.TestBenchSection;
import com.vaadin.client.debug.internal.VDebugWindow;
+import com.vaadin.client.event.PointerEventSupport;
import com.vaadin.client.metadata.BundleLoadCallback;
import com.vaadin.client.metadata.ConnectorBundleLoader;
import com.vaadin.client.metadata.NoDataException;
@@ -511,6 +513,30 @@ public class ApplicationConfiguration implements EntryPoint {
}
}
+ /**
+ * Returns all tags for given class. Tags are used in
+ * {@link ApplicationConfiguration} to keep track of different classes and
+ * their hierarchy
+ *
+ * @since 7.2
+ * @param classname
+ * name of class which tags we want
+ * @return Integer array of tags pointing to this classname
+ */
+ public Integer[] getTagsForServerSideClassName(String classname) {
+ List<Integer> tags = new ArrayList<Integer>();
+
+ for (Map.Entry<Integer, String> entry : tagToServerSideClassName
+ .entrySet()) {
+ if (classname.equals(entry.getValue())) {
+ tags.add(entry.getKey());
+ }
+ }
+
+ Integer[] out = new Integer[tags.size()];
+ return tags.toArray(out);
+ }
+
public Integer getParentTag(int tag) {
return componentInheritanceMap.get(tag);
}
@@ -580,6 +606,14 @@ public class ApplicationConfiguration implements EntryPoint {
enableIOS6castFix();
}
+ // Enable IE prompt fix (#13367)
+ if (browserInfo.isIE() && browserInfo.getBrowserMajorVersion() >= 10) {
+ enableIEPromptFix();
+ }
+
+ // Register pointer events (must be done before any events are used)
+ PointerEventSupport.init();
+
// Prepare the debugging window
if (isDebugMode()) {
/*
@@ -595,6 +629,7 @@ public class ApplicationConfiguration implements EntryPoint {
window.addSection((Section) GWT.create(InfoSection.class));
window.addSection((Section) GWT.create(HierarchySection.class));
window.addSection((Section) GWT.create(NetworkSection.class));
+ window.addSection((Section) GWT.create(TestBenchSection.class));
if (Profiler.isEnabled()) {
window.addSection((Section) GWT.create(ProfilerSection.class));
}
@@ -656,6 +691,25 @@ public class ApplicationConfiguration implements EntryPoint {
}-*/;
/**
+ * Make Metro versions of IE suggest switching to the desktop when
+ * window.prompt is called.
+ */
+ private static native void enableIEPromptFix()
+ /*-{
+ var prompt = $wnd.prompt;
+ $wnd.prompt = function () {
+ var result = prompt.apply($wnd, Array.prototype.slice.call(arguments));
+ if (result === undefined) {
+ // force the browser to suggest desktop mode
+ showModalDialog();
+ return null;
+ } else {
+ return result;
+ }
+ };
+ }-*/;
+
+ /**
* Registers that callback that the bootstrap javascript uses to start
* applications once the widgetset is loaded and all required information is
* available
@@ -760,5 +814,4 @@ public class ApplicationConfiguration implements EntryPoint {
private static final Logger getLogger() {
return Logger.getLogger(ApplicationConfiguration.class.getName());
}
-
}
diff --git a/client/src/com/vaadin/client/ApplicationConnection.java b/client/src/com/vaadin/client/ApplicationConnection.java
index a87fa3e342..5d614439bb 100644
--- a/client/src/com/vaadin/client/ApplicationConnection.java
+++ b/client/src/com/vaadin/client/ApplicationConnection.java
@@ -1,4 +1,4 @@
-/*
+/*
* Copyright 2000-2013 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
@@ -38,6 +38,7 @@ import com.google.gwt.core.client.JsArray;
import com.google.gwt.core.client.JsArrayString;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
+import com.google.gwt.dom.client.Element;
import com.google.gwt.event.shared.EventBus;
import com.google.gwt.event.shared.EventHandler;
import com.google.gwt.event.shared.GwtEvent;
@@ -50,13 +51,13 @@ import com.google.gwt.http.client.RequestException;
import com.google.gwt.http.client.Response;
import com.google.gwt.http.client.URL;
import com.google.gwt.json.client.JSONArray;
+import com.google.gwt.json.client.JSONNumber;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONString;
import com.google.gwt.regexp.shared.MatchResult;
import com.google.gwt.regexp.shared.RegExp;
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.Timer;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.Window.ClosingEvent;
@@ -64,15 +65,18 @@ import com.google.gwt.user.client.Window.ClosingHandler;
import com.google.gwt.user.client.ui.HasWidgets;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.ApplicationConfiguration.ErrorMessage;
+import com.vaadin.client.ApplicationConnection.ApplicationStoppedEvent;
import com.vaadin.client.ResourceLoader.ResourceLoadEvent;
import com.vaadin.client.ResourceLoader.ResourceLoadListener;
import com.vaadin.client.communication.HasJavaScriptConnectorHelper;
+import com.vaadin.client.communication.Heartbeat;
import com.vaadin.client.communication.JavaScriptMethodInvocation;
import com.vaadin.client.communication.JsonDecoder;
import com.vaadin.client.communication.JsonEncoder;
import com.vaadin.client.communication.PushConnection;
import com.vaadin.client.communication.RpcManager;
import com.vaadin.client.communication.StateChangeEvent;
+import com.vaadin.client.componentlocator.ComponentLocator;
import com.vaadin.client.extensions.AbstractExtensionConnector;
import com.vaadin.client.metadata.ConnectorBundleLoader;
import com.vaadin.client.metadata.Method;
@@ -82,6 +86,9 @@ import com.vaadin.client.metadata.Type;
import com.vaadin.client.metadata.TypeData;
import com.vaadin.client.ui.AbstractComponentConnector;
import com.vaadin.client.ui.AbstractConnector;
+import com.vaadin.client.ui.FontIcon;
+import com.vaadin.client.ui.Icon;
+import com.vaadin.client.ui.ImageIcon;
import com.vaadin.client.ui.VContextMenu;
import com.vaadin.client.ui.VNotification;
import com.vaadin.client.ui.VNotification.HideEvent;
@@ -91,6 +98,7 @@ import com.vaadin.client.ui.ui.UIConnector;
import com.vaadin.client.ui.window.WindowConnector;
import com.vaadin.shared.AbstractComponentState;
import com.vaadin.shared.ApplicationConstants;
+import com.vaadin.shared.JsonConstants;
import com.vaadin.shared.Version;
import com.vaadin.shared.communication.LegacyChangeVariablesInvocation;
import com.vaadin.shared.communication.MethodInvocation;
@@ -137,10 +145,6 @@ public class ApplicationConnection {
public static final String ERROR_CLASSNAME_EXT = "-error";
- public static final char VAR_BURST_SEPARATOR = '\u001d';
-
- public static final char VAR_ESCAPE_CHARACTER = '\u001b';
-
/**
* A string that, if found in a non-JSON response to a UIDL request, will
* cause the browser to refresh the page. If followed by a colon, optional
@@ -271,8 +275,6 @@ public class ApplicationConnection {
/** Event bus for communication events */
private EventBus eventBus = GWT.create(SimpleEventBus.class);
- private int lastResponseId = -1;
-
/**
* The communication handler methods are called at certain points during
* communication with the server. This allows for making add-ons that keep
@@ -366,7 +368,7 @@ public class ApplicationConnection {
*
* To listen for the event add a {@link ApplicationStoppedHandler} by
* invoking
- * {@link ApplicationConnection#addHandler(ApplicationStoppedEvent.Type, ApplicationStoppedHandler)}
+ * {@link ApplicationConnection#addHandler(ApplicationConnection.ApplicationStoppedEvent.Type, ApplicationStoppedHandler)}
* to the {@link ApplicationConnection}
*
* @since 7.1.8
@@ -431,6 +433,8 @@ public class ApplicationConnection {
private VLoadingIndicator loadingIndicator;
+ private Heartbeat heartbeat = GWT.create(Heartbeat.class);
+
public static class MultiStepDuration extends Duration {
private int previousStep = elapsedMillis();
@@ -493,7 +497,7 @@ public class ApplicationConnection {
getLoadingIndicator().show();
- scheduleHeartbeat();
+ heartbeat.init(this);
Window.addWindowClosingHandler(new ClosingHandler() {
@Override
@@ -540,44 +544,57 @@ public class ApplicationConnection {
// initial UIDL provided in DOM, continue as if returned by request
handleJSONText(jsonText, -1);
+
+ // Tooltip can't be created earlier because the necessary fields are
+ // not setup to add it in the correct place in the DOM
+ getVTooltip().showAssistive(new TooltipInfo(" "));
}
}
private native void initializeTestbenchHooks(
ComponentLocator componentLocator, String TTAppId)
/*-{
- var ap = this;
- var client = {};
- client.isActive = $entry(function() {
- return ap.@com.vaadin.client.ApplicationConnection::hasActiveRequest()()
- || ap.@com.vaadin.client.ApplicationConnection::isExecutingDeferredCommands()();
- });
- var vi = ap.@com.vaadin.client.ApplicationConnection::getVersionInfo()();
- if (vi) {
- client.getVersionInfo = function() {
- return vi;
- }
- }
+ var ap = this;
+ var client = {};
+ client.isActive = $entry(function() {
+ return ap.@com.vaadin.client.ApplicationConnection::hasActiveRequest()()
+ || ap.@com.vaadin.client.ApplicationConnection::isExecutingDeferredCommands()();
+ });
+ var vi = ap.@com.vaadin.client.ApplicationConnection::getVersionInfo()();
+ if (vi) {
+ client.getVersionInfo = function() {
+ return vi;
+ }
+ }
- client.getProfilingData = $entry(function() {
- var pd = [
- ap.@com.vaadin.client.ApplicationConnection::lastProcessingTime,
+ client.getProfilingData = $entry(function() {
+ var pd = [
+ ap.@com.vaadin.client.ApplicationConnection::lastProcessingTime,
ap.@com.vaadin.client.ApplicationConnection::totalProcessingTime
- ];
- pd = pd.concat(ap.@com.vaadin.client.ApplicationConnection::serverTimingInfo);
- pd[pd.length] = ap.@com.vaadin.client.ApplicationConnection::bootstrapTime;
- return pd;
- });
+ ];
+ pd = pd.concat(ap.@com.vaadin.client.ApplicationConnection::serverTimingInfo);
+ pd[pd.length] = ap.@com.vaadin.client.ApplicationConnection::bootstrapTime;
+ return pd;
+ });
- client.getElementByPath = $entry(function(id) {
- return componentLocator.@com.vaadin.client.ComponentLocator::getElementByPath(Ljava/lang/String;)(id);
- });
- client.getPathForElement = $entry(function(element) {
- return componentLocator.@com.vaadin.client.ComponentLocator::getPathForElement(Lcom/google/gwt/user/client/Element;)(element);
- });
- client.initializing = false;
+ client.getElementByPath = $entry(function(id) {
+ return componentLocator.@com.vaadin.client.componentlocator.ComponentLocator::getElementByPath(Ljava/lang/String;)(id);
+ });
+ client.getElementByPathStartingAt = $entry(function(id, element) {
+ return componentLocator.@com.vaadin.client.componentlocator.ComponentLocator::getElementByPathStartingAt(Ljava/lang/String;Lcom/google/gwt/dom/client/Element;)(id, element);
+ });
+ client.getElementsByPath = $entry(function(id) {
+ return componentLocator.@com.vaadin.client.componentlocator.ComponentLocator::getElementsByPath(Ljava/lang/String;)(id);
+ });
+ client.getElementsByPathStartingAt = $entry(function(id, element) {
+ return componentLocator.@com.vaadin.client.componentlocator.ComponentLocator::getElementsByPathStartingAt(Ljava/lang/String;Lcom/google/gwt/dom/client/Element;)(id, element);
+ });
+ client.getPathForElement = $entry(function(element) {
+ return componentLocator.@com.vaadin.client.componentlocator.ComponentLocator::getPathForElement(Lcom/google/gwt/dom/client/Element;)(element);
+ });
+ client.initializing = false;
- $wnd.vaadin.clients[TTAppId] = client;
+ $wnd.vaadin.clients[TTAppId] = client;
}-*/;
private static native final int calculateBootstrapTime()
@@ -721,7 +738,7 @@ public class ApplicationConnection {
}-*/;
protected void repaintAll() {
- makeUidlRequest("", getRepaintAllParameters());
+ makeUidlRequest(new JSONArray(), getRepaintAllParameters());
}
/**
@@ -752,20 +769,25 @@ public class ApplicationConnection {
/**
* Makes an UIDL request to the server.
*
- * @param requestData
- * Data that is passed to the server.
+ * @param reqInvocations
+ * Data containing RPC invocations and all related information.
* @param extraParams
* Parameters that are added as GET parameters to the url.
* Contains key=value pairs joined by & characters or is empty if
* no parameters should be added. Should not start with any
* special character.
*/
- protected void makeUidlRequest(final String requestData,
+ protected void makeUidlRequest(final JSONArray reqInvocations,
final String extraParams) {
startRequest();
- // Security: double cookie submission pattern
- final String payload = getCsrfToken() + VAR_BURST_SEPARATOR
- + requestData;
+
+ JSONObject payload = new JSONObject();
+ payload.put(ApplicationConstants.CSRF_TOKEN, new JSONString(
+ getCsrfToken()));
+ payload.put(ApplicationConstants.RPC_INVOCATIONS, reqInvocations);
+ payload.put(ApplicationConstants.SERVER_SYNC_ID, new JSONNumber(
+ lastSeenServerSyncId));
+
VConsole.log("Making UIDL Request with params: " + payload);
String uri = translateVaadinUri(ApplicationConstants.APP_PROTOCOL_PREFIX
+ ApplicationConstants.UIDL_PATH + '/');
@@ -789,7 +811,7 @@ public class ApplicationConnection {
* @param payload
* The contents of the request to send
*/
- protected void doUidlRequest(final String uri, final String payload) {
+ protected void doUidlRequest(final String uri, final JSONObject payload) {
RequestCallback requestCallback = new RequestCallback() {
@Override
public void onError(Request request, Throwable exception) {
@@ -956,14 +978,14 @@ public class ApplicationConnection {
* @throws RequestException
* if the request could not be sent
*/
- protected void doAjaxRequest(String uri, String payload,
+ protected void doAjaxRequest(String uri, JSONObject payload,
RequestCallback requestCallback) throws RequestException {
RequestBuilder rb = new RequestBuilder(RequestBuilder.POST, uri);
// TODO enable timeout
// rb.setTimeoutMillis(timeoutMillis);
// TODO this should be configurable
- rb.setHeader("Content-Type", "text/plain;charset=utf-8");
- rb.setRequestData(payload);
+ rb.setHeader("Content-Type", JsonConstants.JSON_CONTENT_TYPE);
+ rb.setRequestData(payload.toString());
rb.setCallback(requestCallback);
final Request request = rb.send();
@@ -1028,6 +1050,29 @@ public class ApplicationConnection {
*/
private ValueMap serverTimingInfo;
+ /**
+ * Holds the last seen response id given by the server.
+ * <p>
+ * The server generates a strictly increasing id for each response to each
+ * request from the client. This ID is then replayed back to the server on
+ * each request. This helps the server in knowing in what state the client
+ * is, and compare it to its own state. In short, it helps with concurrent
+ * changes between the client and server.
+ * <p>
+ * Initial value, i.e. no responses received from the server, is
+ * {@link #UNDEFINED_SYNC_ID} ({@value #UNDEFINED_SYNC_ID}). This happens
+ * between the bootstrap HTML being loaded and the first UI being rendered;
+ */
+ private int lastSeenServerSyncId = UNDEFINED_SYNC_ID;
+
+ /**
+ * The value of an undefined sync id.
+ * <p>
+ * This must be <code>-1</code>, because of the contract in
+ * {@link #getLastResponseId()}
+ */
+ private static final int UNDEFINED_SYNC_ID = -1;
+
static final int MAX_CSS_WAITS = 100;
protected void handleWhenCSSLoaded(final String jsonText,
@@ -1308,7 +1353,13 @@ public class ApplicationConnection {
* @return and id identifying the response
*/
public int getLastResponseId() {
- return lastResponseId;
+ /*
+ * The discrepancy between field name and getter name is simply historic
+ * - API can't be changed, but the field was repurposed in a more
+ * general, yet compatible, use. "Response id" was deemed unsuitable a
+ * name, so it was called "server sync id" instead.
+ */
+ return lastSeenServerSyncId;
}
protected void handleUIDLMessage(final Date start, final String jsonText,
@@ -1335,6 +1386,17 @@ public class ApplicationConnection {
VConsole.log("Handling message from server");
eventBus.fireEvent(new ResponseHandlingStartedEvent(this));
+ if (json.containsKey(ApplicationConstants.SERVER_SYNC_ID)) {
+ int syncId = json.getInt(ApplicationConstants.SERVER_SYNC_ID);
+
+ assert (lastSeenServerSyncId == UNDEFINED_SYNC_ID || syncId == lastSeenServerSyncId + 1) : "Newly retrieved server sync id was not exactly one larger than the previous one (new: "
+ + syncId + ", last seen: " + lastSeenServerSyncId + ")";
+
+ lastSeenServerSyncId = syncId;
+ } else {
+ VConsole.error("Server response didn't contain an id.");
+ }
+
// Handle redirect
if (json.containsKey("redirect")) {
String url = json.getValueMap("redirect").getString("url");
@@ -1343,8 +1405,6 @@ public class ApplicationConnection {
return;
}
- lastResponseId++;
-
final MultiStepDuration handleUIDLDuration = new MultiStepDuration();
// Get security key
@@ -2532,15 +2592,13 @@ public class ApplicationConnection {
*/
private void buildAndSendVariableBurst(
LinkedHashMap<String, MethodInvocation> pendingInvocations) {
- final StringBuffer req = new StringBuffer();
- while (!pendingInvocations.isEmpty()) {
+ JSONArray reqJson = new JSONArray();
+ if (!pendingInvocations.isEmpty()) {
if (ApplicationConfiguration.isDebugMode()) {
Util.logVariableBurst(this, pendingInvocations.values());
}
- JSONArray reqJson = new JSONArray();
-
for (MethodInvocation invocation : pendingInvocations.values()) {
JSONArray invocationJson = new JSONArray();
invocationJson.set(0,
@@ -2579,9 +2637,6 @@ public class ApplicationConnection {
reqJson.set(reqJson.size(), invocationJson);
}
- // escape burst separators (if any)
- req.append(escapeBurstContents(reqJson.toString()));
-
pendingInvocations.clear();
// Keep tag string short
lastInvocationTag = 0;
@@ -2605,7 +2660,7 @@ public class ApplicationConnection {
getConfiguration().setWidgetsetVersionSent();
}
- makeUidlRequest(req.toString(), extraParams);
+ makeUidlRequest(reqJson, extraParams);
}
private boolean isJavascriptRpc(MethodInvocation invocation) {
@@ -2849,35 +2904,6 @@ public class ApplicationConnection {
}
/**
- * Encode burst separator characters in a String for transport over the
- * network. This protects from separator injection attacks.
- *
- * @param value
- * to encode
- * @return encoded value
- */
- protected String escapeBurstContents(String value) {
- final StringBuilder result = new StringBuilder();
- for (int i = 0; i < value.length(); ++i) {
- char character = value.charAt(i);
- switch (character) {
- case VAR_ESCAPE_CHARACTER:
- // fall-through - escape character is duplicated
- case VAR_BURST_SEPARATOR:
- result.append(VAR_ESCAPE_CHARACTER);
- // encode as letters for easier reading
- result.append(((char) (character + 0x30)));
- break;
- default:
- // the char is not a special one - add it to the result as is
- result.append(character);
- break;
- }
- }
- return result.toString();
- }
-
- /**
* Does absolutely nothing. Replaced by {@link LayoutManager}.
*
* @param container
@@ -3009,6 +3035,26 @@ public class ApplicationConnection {
}
/**
+ * Gets an {@link Icon} instance corresponding to a URI.
+ *
+ * @since 7.2
+ * @param uri
+ * @return Icon object
+ */
+ public Icon getIcon(String uri) {
+ Icon icon;
+ if (uri == null) {
+ return null;
+ } else if (FontIcon.isFontIconUri(uri)) {
+ icon = GWT.create(FontIcon.class);
+ } else {
+ icon = GWT.create(ImageIcon.class);
+ }
+ icon.setUri(translateVaadinUri(uri));
+ return icon;
+ }
+
+ /**
* Translates custom protocols in UIDL URI's to be recognizable by browser.
* All uri's from UIDL should be routed via this method before giving them
* to browser due URI's in UIDL may contain custom protocols like theme://.
@@ -3304,20 +3350,11 @@ public class ApplicationConnection {
* interval elapses if the interval is a positive number. Otherwise, does
* nothing.
*
- * @see #sendHeartbeat()
- * @see ApplicationConfiguration#getHeartbeatInterval()
+ * @deprecated as of 7.2, use {@link Heartbeat#schedule()} instead
*/
+ @Deprecated
protected void scheduleHeartbeat() {
- final int interval = getConfiguration().getHeartbeatInterval();
- if (interval > 0) {
- VConsole.log("Scheduling heartbeat in " + interval + " seconds");
- new Timer() {
- @Override
- public void run() {
- sendHeartbeat();
- }
- }.schedule(interval * 1000);
- }
+ heartbeat.schedule();
}
/**
@@ -3326,51 +3363,12 @@ public class ApplicationConnection {
* Heartbeat requests are used to inform the server that the client-side is
* still alive. If the client page is closed or the connection lost, the
* server will eventually close the inactive UI.
- * <p>
- * <b>TODO</b>: Improved error handling, like in doUidlRequest().
*
- * @see #scheduleHeartbeat()
+ * @deprecated as of 7.2, use {@link Heartbeat#send()} instead
*/
+ @Deprecated
protected void sendHeartbeat() {
- final String uri = addGetParameters(
- translateVaadinUri(ApplicationConstants.APP_PROTOCOL_PREFIX
- + ApplicationConstants.HEARTBEAT_PATH + '/'),
- UIConstants.UI_ID_PARAMETER + "="
- + getConfiguration().getUIId());
-
- final RequestBuilder rb = new RequestBuilder(RequestBuilder.POST, uri);
-
- final RequestCallback callback = new RequestCallback() {
-
- @Override
- public void onResponseReceived(Request request, Response response) {
- int status = response.getStatusCode();
- if (status == Response.SC_OK) {
- // TODO Permit retry in some error situations
- VConsole.log("Heartbeat response OK");
- scheduleHeartbeat();
- } else if (status == Response.SC_GONE) {
- showSessionExpiredError(null);
- } else {
- VConsole.error("Failed sending heartbeat to server. Error code: "
- + status);
- }
- }
-
- @Override
- public void onError(Request request, Throwable exception) {
- VConsole.error("Exception sending heartbeat: " + exception);
- }
- };
-
- rb.setCallback(callback);
-
- try {
- VConsole.log("Sending heartbeat request...");
- rb.send();
- } catch (RequestException re) {
- callback.onError(null, re);
- }
+ heartbeat.send();
}
/**
diff --git a/client/src/com/vaadin/client/ComponentLocator.java b/client/src/com/vaadin/client/ComponentLocator.java
index af934470c2..f30528c0c0 100644
--- a/client/src/com/vaadin/client/ComponentLocator.java
+++ b/client/src/com/vaadin/client/ComponentLocator.java
@@ -15,706 +15,22 @@
*/
package com.vaadin.client;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.Element;
-import com.google.gwt.user.client.ui.HasWidgets;
-import com.google.gwt.user.client.ui.RootPanel;
-import com.google.gwt.user.client.ui.Widget;
-import com.vaadin.client.ui.SubPartAware;
-import com.vaadin.client.ui.VCssLayout;
-import com.vaadin.client.ui.VGridLayout;
-import com.vaadin.client.ui.VOverlay;
-import com.vaadin.client.ui.VTabsheetPanel;
-import com.vaadin.client.ui.VUI;
-import com.vaadin.client.ui.VWindow;
-import com.vaadin.client.ui.orderedlayout.Slot;
-import com.vaadin.client.ui.orderedlayout.VAbstractOrderedLayout;
-import com.vaadin.client.ui.window.WindowConnector;
-import com.vaadin.shared.AbstractComponentState;
-import com.vaadin.shared.Connector;
-import com.vaadin.shared.communication.SharedState;
-
/**
* ComponentLocator provides methods for generating a String locator for a given
* DOM element and for locating a DOM element using a String locator.
+ *
+ * @since 5.4
+ * @deprecated Moved to com.vaadin.client.componentlocator.ComponentLocator
*/
-public class ComponentLocator {
-
- /**
- * Separator used in the String locator between a parent and a child widget.
- */
- private static final String PARENTCHILD_SEPARATOR = "/";
-
- /**
- * Separator used in the String locator between the part identifying the
- * containing widget and the part identifying the target element within the
- * widget.
- */
- private static final String SUBPART_SEPARATOR = "#";
-
- /**
- * String that identifies the root panel when appearing first in the String
- * locator.
- */
- private static final String ROOT_ID = "Root";
-
- /**
- * Reference to ApplicationConnection instance.
- */
- private ApplicationConnection client;
-
+public class ComponentLocator extends
+ com.vaadin.client.componentlocator.ComponentLocator {
/**
* Construct a ComponentLocator for the given ApplicationConnection.
- *
+ *
* @param client
* ApplicationConnection instance for the application.
*/
public ComponentLocator(ApplicationConnection client) {
- this.client = client;
- }
-
- /**
- * Generates a String locator which uniquely identifies the target element.
- * The {@link #getElementByPath(String)} method can be used for the inverse
- * operation, i.e. locating an element based on the return value from this
- * method.
- * <p>
- * Note that getElementByPath(getPathForElement(element)) == element is not
- * always true as {@link #getPathForElement(Element)} can return a path to
- * another element if the widget determines an action on the other element
- * will give the same result as the action on the target element.
- * </p>
- *
- * @since 5.4
- * @param targetElement
- * The element to generate a path for.
- * @return A String locator that identifies the target element or null if a
- * String locator could not be created.
- */
- public String getPathForElement(Element targetElement) {
- String pid = null;
-
- targetElement = getElement(targetElement);
-
- Element e = targetElement;
-
- while (true) {
- pid = ConnectorMap.get(client).getConnectorId(e);
- if (pid != null) {
- break;
- }
-
- e = DOM.getParent(e);
- if (e == null) {
- break;
- }
- }
-
- Widget w = null;
- if (pid != null) {
- // If we found a Paintable then we use that as reference. We should
- // find the Paintable for all but very special cases (like
- // overlays).
- w = ((ComponentConnector) ConnectorMap.get(client)
- .getConnector(pid)).getWidget();
-
- /*
- * Still if the Paintable contains a widget that implements
- * SubPartAware, we want to use that as a reference
- */
- Widget targetParent = findParentWidget(targetElement, w);
- while (targetParent != w && targetParent != null) {
- if (targetParent instanceof SubPartAware) {
- /*
- * The targetParent widget is a child of the Paintable and
- * the first parent (of the targetElement) that implements
- * SubPartAware
- */
- w = targetParent;
- break;
- }
- targetParent = targetParent.getParent();
- }
- }
- if (w == null) {
- // Check if the element is part of a widget that is attached
- // directly to the root panel
- RootPanel rootPanel = RootPanel.get();
- int rootWidgetCount = rootPanel.getWidgetCount();
- for (int i = 0; i < rootWidgetCount; i++) {
- Widget rootWidget = rootPanel.getWidget(i);
- if (rootWidget.getElement().isOrHasChild(targetElement)) {
- // The target element is contained by this root widget
- w = findParentWidget(targetElement, rootWidget);
- break;
- }
- }
- if (w != null) {
- // We found a widget but we should still see if we find a
- // SubPartAware implementor (we cannot find the Paintable as
- // there is no link from VOverlay to its paintable/owner).
- Widget subPartAwareWidget = findSubPartAwareParentWidget(w);
- if (subPartAwareWidget != null) {
- w = subPartAwareWidget;
- }
- }
- }
-
- if (w == null) {
- // Containing widget not found
- return null;
- }
-
- // Determine the path for the target widget
- String path = getPathForWidget(w);
- if (path == null) {
- /*
- * No path could be determined for the target widget. Cannot create
- * a locator string.
- */
- return null;
- }
-
- // The parent check is a work around for Firefox 15 which fails to
- // compare elements properly (#9534)
- if (w.getElement() == targetElement) {
- /*
- * We are done if the target element is the root of the target
- * widget.
- */
- return path;
- } else if (w instanceof SubPartAware) {
- /*
- * If the widget can provide an identifier for the targetElement we
- * let it do that
- */
- String elementLocator = ((SubPartAware) w)
- .getSubPartName(targetElement);
- if (elementLocator != null) {
- return path + SUBPART_SEPARATOR + elementLocator;
- }
- }
- /*
- * If everything else fails we use the DOM path to identify the target
- * element
- */
- String domPath = getDOMPathForElement(targetElement, w.getElement());
- if (domPath == null) {
- return path;
- } else {
- return path + domPath;
- }
- }
-
- /**
- * Returns the element passed to the method. Or in case of Firefox 15,
- * returns the real element that is in the DOM instead of the element passed
- * to the method (which is the same element but not ==).
- *
- * @param targetElement
- * the element to return
- * @return the element passed to the method
- */
- private Element getElement(Element targetElement) {
- if (targetElement == null) {
- return null;
- }
-
- if (!BrowserInfo.get().isFirefox()) {
- return targetElement;
- }
-
- if (BrowserInfo.get().getBrowserMajorVersion() != 15) {
- return targetElement;
- }
-
- // Firefox 15, you make me sad
- if (targetElement.getNextSibling() != null) {
- return (Element) targetElement.getNextSibling()
- .getPreviousSibling();
- }
- if (targetElement.getPreviousSibling() != null) {
- return (Element) targetElement.getPreviousSibling()
- .getNextSibling();
- }
- // No siblings so this is the only child
- return (Element) targetElement.getParentNode().getChild(0);
- }
-
- /**
- * Finds the first widget in the hierarchy (moving upwards) that implements
- * SubPartAware. Returns the SubPartAware implementor or null if none is
- * found.
- *
- * @param w
- * The widget to start from. This is returned if it implements
- * SubPartAware.
- * @return The first widget (upwards in hierarchy) that implements
- * SubPartAware or null
- */
- private Widget findSubPartAwareParentWidget(Widget w) {
-
- while (w != null) {
- if (w instanceof SubPartAware) {
- return w;
- }
- w = w.getParent();
- }
- return null;
- }
-
- /**
- * Returns the first widget found when going from {@code targetElement}
- * upwards in the DOM hierarchy, assuming that {@code ancestorWidget} is a
- * parent of {@code targetElement}.
- *
- * @param targetElement
- * @param ancestorWidget
- * @return The widget whose root element is a parent of
- * {@code targetElement}.
- */
- private Widget findParentWidget(Element targetElement, Widget ancestorWidget) {
- /*
- * As we cannot resolve Widgets from the element we start from the
- * widget and move downwards to the correct child widget, as long as we
- * find one.
- */
- if (ancestorWidget instanceof HasWidgets) {
- for (Widget w : ((HasWidgets) ancestorWidget)) {
- if (w.getElement().isOrHasChild(targetElement)) {
- return findParentWidget(targetElement, w);
- }
- }
- }
-
- // No children found, this is it
- return ancestorWidget;
- }
-
- /**
- * Locates an element based on a DOM path and a base element.
- *
- * @param baseElement
- * The base element which the path is relative to
- * @param path
- * String locator (consisting of domChild[x] parts) that
- * identifies the element
- * @return The element identified by path, relative to baseElement or null
- * if the element could not be found.
- */
- private Element getElementByDOMPath(Element baseElement, String path) {
- String parts[] = path.split(PARENTCHILD_SEPARATOR);
- Element element = baseElement;
-
- for (String part : parts) {
- if (part.startsWith("domChild[")) {
- String childIndexString = part.substring("domChild[".length(),
- part.length() - 1);
-
- if (Util.findWidget(baseElement, null) instanceof VAbstractOrderedLayout) {
- if (element.hasChildNodes()) {
- Element e = element.getFirstChildElement().cast();
- String cn = e.getClassName();
- if (cn != null
- && (cn.equals("v-expand") || cn
- .contains("v-has-caption"))) {
- element = e;
- }
- }
- }
-
- try {
- int childIndex = Integer.parseInt(childIndexString);
- element = DOM.getChild(element, childIndex);
- } catch (Exception e) {
- return null;
- }
-
- if (element == null) {
- return null;
- }
-
- }
- }
-
- return element;
+ super(client);
}
-
- /**
- * Generates a String locator using domChild[x] parts for the element
- * relative to the baseElement.
- *
- * @param element
- * The target element
- * @param baseElement
- * The starting point for the locator. The generated path is
- * relative to this element.
- * @return A String locator that can be used to locate the target element
- * using {@link #getElementByDOMPath(Element, String)} or null if
- * the locator String cannot be created.
- */
- private String getDOMPathForElement(Element element, Element baseElement) {
- Element e = element;
- String path = "";
- while (true) {
- int childIndex = -1;
- Element siblingIterator = e;
- while (siblingIterator != null) {
- childIndex++;
- siblingIterator = siblingIterator.getPreviousSiblingElement()
- .cast();
- }
-
- path = PARENTCHILD_SEPARATOR + "domChild[" + childIndex + "]"
- + path;
-
- JavaScriptObject parent = e.getParentElement();
- if (parent == null) {
- return null;
- }
- // The parent check is a work around for Firefox 15 which fails to
- // compare elements properly (#9534)
- if (parent == baseElement) {
- break;
- }
-
- e = parent.cast();
- }
-
- return path;
- }
-
- /**
- * Locates an element using a String locator (path) which identifies a DOM
- * element. The {@link #getPathForElement(Element)} method can be used for
- * the inverse operation, i.e. generating a string expression for a DOM
- * element.
- *
- * @since 5.4
- * @param path
- * The String locater which identifies the target element.
- * @return The DOM element identified by {@code path} or null if the element
- * could not be located.
- */
- public Element getElementByPath(String path) {
- /*
- * Path is of type "targetWidgetPath#componentPart" or
- * "targetWidgetPath".
- */
- String parts[] = path.split(SUBPART_SEPARATOR, 2);
- String widgetPath = parts[0];
- Widget w = getWidgetFromPath(widgetPath);
- if (w == null || !Util.isAttachedAndDisplayed(w)) {
- return null;
- }
-
- if (parts.length == 1) {
- int pos = widgetPath.indexOf("domChild");
- if (pos == -1) {
- return w.getElement();
- }
-
- // Contains dom reference to a sub element of the widget
- String subPath = widgetPath.substring(pos);
- return getElementByDOMPath(w.getElement(), subPath);
- } else if (parts.length == 2) {
- if (w instanceof SubPartAware) {
- return ((SubPartAware) w).getSubPartElement(parts[1]);
- }
- }
-
- return null;
- }
-
- /**
- * Creates a locator String for the given widget. The path can be used to
- * locate the widget using {@link #getWidgetFromPath(String)}.
- *
- * Returns null if no path can be determined for the widget or if the widget
- * is null.
- *
- * @param w
- * The target widget
- * @return A String locator for the widget
- */
- private String getPathForWidget(Widget w) {
- if (w == null) {
- return null;
- }
- String elementId = w.getElement().getId();
- if (elementId != null && !elementId.isEmpty()
- && !elementId.startsWith("gwt-uid-")) {
- // Use PID_S+id if the user has set an id but do not use it for auto
- // generated id:s as these might not be consistent
- return "PID_S" + elementId;
- } else if (w instanceof VUI) {
- return "";
- } else if (w instanceof VWindow) {
- Connector windowConnector = ConnectorMap.get(client)
- .getConnector(w);
- List<WindowConnector> subWindowList = client.getUIConnector()
- .getSubWindows();
- int indexOfSubWindow = subWindowList.indexOf(windowConnector);
- return PARENTCHILD_SEPARATOR + "VWindow[" + indexOfSubWindow + "]";
- } else if (w instanceof RootPanel) {
- return ROOT_ID;
- }
-
- Widget parent = w.getParent();
-
- String basePath = getPathForWidget(parent);
- if (basePath == null) {
- return null;
- }
- String simpleName = Util.getSimpleName(w);
-
- /*
- * Check if the parent implements Iterable. At least VPopupView does not
- * implement HasWdgets so we cannot check for that.
- */
- if (!(parent instanceof Iterable<?>)) {
- // Parent does not implement Iterable so we cannot find out which
- // child this is
- return null;
- }
-
- Iterator<?> i = ((Iterable<?>) parent).iterator();
- int pos = 0;
- while (i.hasNext()) {
- Object child = i.next();
- if (child == w) {
- return basePath + PARENTCHILD_SEPARATOR + simpleName + "["
- + pos + "]";
- }
- String simpleName2 = Util.getSimpleName(child);
- if (simpleName.equals(simpleName2)) {
- pos++;
- }
- }
-
- return null;
- }
-
- /**
- * Locates the widget based on a String locator.
- *
- * @param path
- * The String locator that identifies the widget.
- * @return The Widget identified by the String locator or null if the widget
- * could not be identified.
- */
- private Widget getWidgetFromPath(String path) {
- Widget w = null;
- String parts[] = path.split(PARENTCHILD_SEPARATOR);
-
- for (int i = 0; i < parts.length; i++) {
- String part = parts[i];
-
- if (part.equals(ROOT_ID)) {
- w = RootPanel.get();
- } else if (part.equals("")) {
- w = client.getUIConnector().getWidget();
- } else if (w == null) {
- String id = part;
- // Must be old static pid (PID_S*)
- ServerConnector connector = ConnectorMap.get(client)
- .getConnector(id);
- if (connector == null) {
- // Lookup by component id
- // TODO Optimize this
- connector = findConnectorById(client.getUIConnector(),
- id.substring(5));
- }
-
- if (connector instanceof ComponentConnector) {
- w = ((ComponentConnector) connector).getWidget();
- } else {
- // Not found
- return null;
- }
- } else if (part.startsWith("domChild[")) {
- // The target widget has been found and the rest identifies the
- // element
- break;
- } else if (w instanceof Iterable) {
- // W identifies a widget that contains other widgets, as it
- // should. Try to locate the child
- Iterable<?> parent = (Iterable<?>) w;
-
- // Part is of type "VVerticalLayout[0]", split this into
- // VVerticalLayout and 0
- String[] split = part.split("\\[", 2);
- String widgetClassName = split[0];
- String indexString = split[1].substring(0,
- split[1].length() - 1);
- int widgetPosition = Integer.parseInt(indexString);
-
- // AbsolutePanel in GridLayout has been removed -> skip it
- if (w instanceof VGridLayout
- && "AbsolutePanel".equals(widgetClassName)) {
- continue;
- }
-
- // FlowPane in CSSLayout has been removed -> skip it
- if (w instanceof VCssLayout
- && "VCssLayout$FlowPane".equals(widgetClassName)) {
- continue;
- }
-
- // ChildComponentContainer and VOrderedLayout$Slot have been
- // replaced with Slot
- if (w instanceof VAbstractOrderedLayout
- && ("ChildComponentContainer".equals(widgetClassName) || "VOrderedLayout$Slot"
- .equals(widgetClassName))) {
- widgetClassName = "Slot";
- }
-
- if (w instanceof VTabsheetPanel && widgetPosition != 0) {
- // TabSheetPanel now only contains 1 connector => the index
- // is always 0 which indicates the widget in the active tab
- widgetPosition = 0;
- }
- if (w instanceof VOverlay
- && "VCalendarPanel".equals(widgetClassName)) {
- // Vaadin 7.1 adds a wrapper for datefield popups
- parent = (Iterable<?>) ((Iterable) parent).iterator()
- .next();
- }
- /*
- * The new grid and ordered layotus do not contain
- * ChildComponentContainer widgets. This is instead simulated by
- * constructing a path step that would find the desired widget
- * from the layout and injecting it as the next search step
- * (which would originally have found the widget inside the
- * ChildComponentContainer)
- */
- if ((w instanceof VGridLayout)
- && "ChildComponentContainer".equals(widgetClassName)
- && i + 1 < parts.length) {
-
- HasWidgets layout = (HasWidgets) w;
-
- String nextPart = parts[i + 1];
- String[] nextSplit = nextPart.split("\\[", 2);
- String nextWidgetClassName = nextSplit[0];
-
- // Find the n:th child and count the number of children with
- // the same type before it
- int nextIndex = 0;
- for (Widget child : layout) {
- boolean matchingType = nextWidgetClassName.equals(Util
- .getSimpleName(child));
- if (matchingType && widgetPosition == 0) {
- // This is the n:th child that we looked for
- break;
- } else if (widgetPosition < 0) {
- // Error if we're past the desired position without
- // a match
- return null;
- } else if (matchingType) {
- // If this was another child of the expected type,
- // increase the count for the next step
- nextIndex++;
- }
-
- // Don't count captions
- if (!(child instanceof VCaption)) {
- widgetPosition--;
- }
- }
-
- // Advance to the next step, this time checking for the
- // actual child widget
- parts[i + 1] = nextWidgetClassName + '[' + nextIndex + ']';
- continue;
- }
-
- // Locate the child
- Iterator<? extends Widget> iterator;
-
- /*
- * VWindow and VContextMenu workarounds for backwards
- * compatibility
- */
- if (widgetClassName.equals("VWindow")) {
- List<WindowConnector> windows = client.getUIConnector()
- .getSubWindows();
- List<VWindow> windowWidgets = new ArrayList<VWindow>(
- windows.size());
- for (WindowConnector wc : windows) {
- windowWidgets.add(wc.getWidget());
- }
- iterator = windowWidgets.iterator();
- } else if (widgetClassName.equals("VContextMenu")) {
- return client.getContextMenu();
- } else {
- iterator = (Iterator<? extends Widget>) parent.iterator();
- }
-
- boolean ok = false;
-
- // Find the widgetPosition:th child of type "widgetClassName"
- while (iterator.hasNext()) {
-
- Widget child = iterator.next();
- String simpleName2 = Util.getSimpleName(child);
-
- if (!widgetClassName.equals(simpleName2)
- && child instanceof Slot) {
- /*
- * Support legacy tests without any selector for the
- * Slot widget (i.e. /VVerticalLayout[0]/VButton[0]) by
- * directly checking the stuff inside the slot
- */
- child = ((Slot) child).getWidget();
- simpleName2 = Util.getSimpleName(child);
- }
-
- if (widgetClassName.equals(simpleName2)) {
- if (widgetPosition == 0) {
- w = child;
- ok = true;
- break;
- }
- widgetPosition--;
-
- }
- }
-
- if (!ok) {
- // Did not find the child
- return null;
- }
- } else {
- // W identifies something that is not a "HasWidgets". This
- // should not happen as all widget containers should implement
- // HasWidgets.
- return null;
- }
- }
-
- return w;
- }
-
- private ServerConnector findConnectorById(ServerConnector root, String id) {
- SharedState state = root.getState();
- if (state instanceof AbstractComponentState
- && id.equals(((AbstractComponentState) state).id)) {
- return root;
- }
- for (ServerConnector child : root.getChildren()) {
- ServerConnector found = findConnectorById(child, id);
- if (found != null) {
- return found;
- }
- }
-
- return null;
- }
-
}
diff --git a/client/src/com/vaadin/client/ConnectorMap.java b/client/src/com/vaadin/client/ConnectorMap.java
index 810f12824a..c2f1eda21d 100644
--- a/client/src/com/vaadin/client/ConnectorMap.java
+++ b/client/src/com/vaadin/client/ConnectorMap.java
@@ -116,7 +116,7 @@ public class ConnectorMap {
* no connector was found
*/
public ComponentConnector getConnector(Widget widget) {
- return getConnector(widget.getElement());
+ return widget == null ? null : getConnector(widget.getElement());
}
public void registerConnector(String id, ServerConnector connector) {
diff --git a/client/src/com/vaadin/client/LayoutManager.java b/client/src/com/vaadin/client/LayoutManager.java
index 5b27031a29..69f3f08144 100644
--- a/client/src/com/vaadin/client/LayoutManager.java
+++ b/client/src/com/vaadin/client/LayoutManager.java
@@ -20,6 +20,7 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
+import java.util.logging.Logger;
import com.google.gwt.core.client.Duration;
import com.google.gwt.core.client.JsArrayString;
@@ -827,6 +828,12 @@ public class LayoutManager {
/**
* Marks that a ManagedLayout should be layouted in the next layout phase
* even if none of the elements managed by the layout have been resized.
+ * <p>
+ * This method should not be invoked during a layout phase since it only
+ * controls what will happen in the beginning of the next phase. If you want
+ * to explicitly cause some layout to be considered in an ongoing layout
+ * phase, you should use {@link #setNeedsMeasure(ComponentConnector)}
+ * instead.
*
* @param layout
* the managed layout that should be layouted
@@ -840,14 +847,25 @@ public class LayoutManager {
* Marks that a ManagedLayout should be layouted horizontally in the next
* layout phase even if none of the elements managed by the layout have been
* resized horizontally.
- *
+ * <p>
* For SimpleManagedLayout which is always layouted in both directions, this
* has the same effect as {@link #setNeedsLayout(ManagedLayout)}.
+ * <p>
+ * This method should not be invoked during a layout phase since it only
+ * controls what will happen in the beginning of the next phase. If you want
+ * to explicitly cause some layout to be considered in an ongoing layout
+ * phase, you should use {@link #setNeedsMeasure(ComponentConnector)}
+ * instead.
*
* @param layout
* the managed layout that should be layouted
*/
public final void setNeedsHorizontalLayout(ManagedLayout layout) {
+ if (isLayoutRunning()) {
+ getLogger()
+ .warning(
+ "setNeedsHorizontalLayout should not be run while a layout phase is in progress.");
+ }
needsHorizontalLayout.add(layout.getConnectorId());
}
@@ -855,14 +873,25 @@ public class LayoutManager {
* Marks that a ManagedLayout should be layouted vertically in the next
* layout phase even if none of the elements managed by the layout have been
* resized vertically.
- *
+ * <p>
* For SimpleManagedLayout which is always layouted in both directions, this
* has the same effect as {@link #setNeedsLayout(ManagedLayout)}.
+ * <p>
+ * This method should not be invoked during a layout phase since it only
+ * controls what will happen in the beginning of the next phase. If you want
+ * to explicitly cause some layout to be considered in an ongoing layout
+ * phase, you should use {@link #setNeedsMeasure(ComponentConnector)}
+ * instead.
*
* @param layout
* the managed layout that should be layouted
*/
public final void setNeedsVerticalLayout(ManagedLayout layout) {
+ if (isLayoutRunning()) {
+ getLogger()
+ .warning(
+ "setNeedsVerticalLayout should not be run while a layout phase is in progress.");
+ }
needsVerticalLayout.add(layout.getConnectorId());
}
@@ -1038,6 +1067,98 @@ public class LayoutManager {
}
/**
+ * Gets the top border of the given element, provided that it has been
+ * measured. These elements are guaranteed to be measured:
+ * <ul>
+ * <li>ManagedLayotus and their child Connectors
+ * <li>Elements for which there is at least one ElementResizeListener
+ * <li>Elements for which at least one ManagedLayout has registered a
+ * dependency
+ * </ul>
+ *
+ * A negative number is returned if the element has not been measured. If 0
+ * is returned, it might indicate that the element is not attached to the
+ * DOM.
+ *
+ * @param element
+ * the element to get the measured size for
+ * @return the measured top border of the element in pixels.
+ */
+ public int getBorderTop(Element element) {
+ assert needsMeasure(element) : "Getting measurement for element that is not measured";
+ return getMeasuredSize(element, nullSize).getBorderTop();
+ }
+
+ /**
+ * Gets the left border of the given element, provided that it has been
+ * measured. These elements are guaranteed to be measured:
+ * <ul>
+ * <li>ManagedLayotus and their child Connectors
+ * <li>Elements for which there is at least one ElementResizeListener
+ * <li>Elements for which at least one ManagedLayout has registered a
+ * dependency
+ * </ul>
+ *
+ * A negative number is returned if the element has not been measured. If 0
+ * is returned, it might indicate that the element is not attached to the
+ * DOM.
+ *
+ * @param element
+ * the element to get the measured size for
+ * @return the measured left border of the element in pixels.
+ */
+ public int getBorderLeft(Element element) {
+ assert needsMeasure(element) : "Getting measurement for element that is not measured";
+ return getMeasuredSize(element, nullSize).getBorderLeft();
+ }
+
+ /**
+ * Gets the bottom border of the given element, provided that it has been
+ * measured. These elements are guaranteed to be measured:
+ * <ul>
+ * <li>ManagedLayotus and their child Connectors
+ * <li>Elements for which there is at least one ElementResizeListener
+ * <li>Elements for which at least one ManagedLayout has registered a
+ * dependency
+ * </ul>
+ *
+ * A negative number is returned if the element has not been measured. If 0
+ * is returned, it might indicate that the element is not attached to the
+ * DOM.
+ *
+ * @param element
+ * the element to get the measured size for
+ * @return the measured bottom border of the element in pixels.
+ */
+ public int getBorderBottom(Element element) {
+ assert needsMeasure(element) : "Getting measurement for element that is not measured";
+ return getMeasuredSize(element, nullSize).getBorderBottom();
+ }
+
+ /**
+ * Gets the right border of the given element, provided that it has been
+ * measured. These elements are guaranteed to be measured:
+ * <ul>
+ * <li>ManagedLayotus and their child Connectors
+ * <li>Elements for which there is at least one ElementResizeListener
+ * <li>Elements for which at least one ManagedLayout has registered a
+ * dependency
+ * </ul>
+ *
+ * A negative number is returned if the element has not been measured. If 0
+ * is returned, it might indicate that the element is not attached to the
+ * DOM.
+ *
+ * @param element
+ * the element to get the measured size for
+ * @return the measured right border of the element in pixels.
+ */
+ public int getBorderRight(Element element) {
+ assert needsMeasure(element) : "Getting measurement for element that is not measured";
+ return getMeasuredSize(element, nullSize).getBorderRight();
+ }
+
+ /**
* Gets the padding width (left padding + right padding) of the given
* element, provided that it has been measured. These elements are
* guaranteed to be measured:
@@ -1458,10 +1579,15 @@ public class LayoutManager {
/**
* Informs this LayoutManager that the size of a component might have
- * changed. If there is no upcoming layout phase, a new layout phase is
- * scheduled. This method should be used whenever a size might have changed
- * from outside of Vaadin's normal update phase, e.g. when an icon has been
- * loaded or when the user resizes some part of the UI using the mouse.
+ * changed. This method should be used whenever the size of an individual
+ * component might have changed from outside of Vaadin's normal update
+ * phase, e.g. when an icon has been loaded or when the user resizes some
+ * part of the UI using the mouse.
+ * <p>
+ * To set an entire component hierarchy to be measured, use
+ * {@link #setNeedsMeasureRecursively(ComponentConnector)} instead.
+ * <p>
+ * If there is no upcoming layout phase, a new layout phase is scheduled.
*
* @param component
* the component whose size might have changed.
@@ -1475,6 +1601,33 @@ public class LayoutManager {
}
}
+ /**
+ * Informs this LayoutManager that some sizes in a component hierarchy might
+ * have changed. This method should be used whenever the size of any child
+ * component might have changed from outside of Vaadin's normal update
+ * phase, e.g. when a CSS class name related to sizing has been changed.
+ * <p>
+ * To set a single component to be measured, use
+ * {@link #setNeedsMeasure(ComponentConnector)} instead.
+ * <p>
+ * If there is no upcoming layout phase, a new layout phase is scheduled.
+ *
+ * @since 7.2
+ * @param component
+ * the component at the root of the component hierarchy to
+ * measure
+ */
+ public void setNeedsMeasureRecursively(ComponentConnector component) {
+ setNeedsMeasure(component);
+
+ if (component instanceof HasComponentsConnector) {
+ HasComponentsConnector hasComponents = (HasComponentsConnector) component;
+ for (ComponentConnector child : hasComponents.getChildComponents()) {
+ setNeedsMeasureRecursively(child);
+ }
+ }
+ }
+
public void setEverythingNeedsMeasure() {
everythingNeedsMeasure = true;
}
@@ -1485,4 +1638,8 @@ public class LayoutManager {
protected void cleanMeasuredSizes() {
}
+ private static Logger getLogger() {
+ return Logger.getLogger(LayoutManager.class.getName());
+ }
+
}
diff --git a/client/src/com/vaadin/client/Profiler.java b/client/src/com/vaadin/client/Profiler.java
index 083f2559b1..cfce59b08b 100644
--- a/client/src/com/vaadin/client/Profiler.java
+++ b/client/src/com/vaadin/client/Profiler.java
@@ -297,10 +297,6 @@ public class Profiler {
if (isEnabled()) {
double now = Duration.currentTimeMillis();
- StringBuilder stringBuilder = new StringBuilder(
- "Time since window.performance.timing events");
- SimpleTree tree = new SimpleTree(stringBuilder.toString());
-
String[] keys = new String[] { "navigationStart",
"unloadEventStart", "unloadEventEnd", "redirectStart",
"redirectEnd", "fetchStart", "domainLookupStart",
diff --git a/client/src/com/vaadin/client/RenderInformation.java b/client/src/com/vaadin/client/RenderInformation.java
index e1ad9a8999..4d856e90ee 100644
--- a/client/src/com/vaadin/client/RenderInformation.java
+++ b/client/src/com/vaadin/client/RenderInformation.java
@@ -15,7 +15,8 @@
*/
package com.vaadin.client;
-import com.google.gwt.user.client.Element;
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.user.client.DOM;
/**
* Contains size information about a rendered container and its content area.
@@ -51,8 +52,11 @@ public class RenderInformation {
* @param widget
*
* @return true if the size has changed since last update
+ * @deprecated As of 7.2, call and override {@link #updateSize(Element)}
+ * instead
*/
- public boolean updateSize(Element element) {
+ @Deprecated
+ public boolean updateSize(com.google.gwt.user.client.Element element) {
Size newSize = new Size(element.getOffsetWidth(),
element.getOffsetHeight());
if (newSize.equals(renderedSize)) {
@@ -63,6 +67,19 @@ public class RenderInformation {
}
}
+ /**
+ * Update the size of the widget.
+ *
+ * @param widget
+ *
+ * @return true if the size has changed since last update
+ *
+ * @since 7.2
+ */
+ public boolean updateSize(Element element) {
+ return updateSize(DOM.asOld(element));
+ }
+
@Override
public String toString() {
return "RenderInformation [contentArea=" + contentArea
diff --git a/client/src/com/vaadin/client/SimpleTree.java b/client/src/com/vaadin/client/SimpleTree.java
index 7370496cb8..39e76a6d75 100644
--- a/client/src/com/vaadin/client/SimpleTree.java
+++ b/client/src/com/vaadin/client/SimpleTree.java
@@ -17,6 +17,7 @@
package com.vaadin.client;
import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.SpanElement;
import com.google.gwt.dom.client.Style;
import com.google.gwt.dom.client.Style.Cursor;
@@ -30,7 +31,7 @@ import com.google.gwt.event.dom.client.DoubleClickHandler;
import com.google.gwt.event.dom.client.HasDoubleClickHandlers;
import com.google.gwt.event.shared.HandlerManager;
import com.google.gwt.event.shared.HandlerRegistration;
-import com.google.gwt.user.client.Element;
+import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.ComplexPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Widget;
@@ -43,7 +44,7 @@ import com.google.gwt.user.client.ui.Widget;
*/
@Deprecated
public class SimpleTree extends ComplexPanel implements HasDoubleClickHandlers {
- private Element children = Document.get().createDivElement().cast();
+ private Element children = Document.get().createDivElement();
private SpanElement handle = Document.get().createSpanElement();
private SpanElement text = Document.get().createSpanElement();
@@ -116,6 +117,14 @@ public class SimpleTree extends ComplexPanel implements HasDoubleClickHandlers {
}
}
+ public boolean isOpen() {
+ return "-".equals(handle.getInnerHTML());
+ }
+
+ public String getCaption() {
+ return text.getInnerText();
+ }
+
public SimpleTree(String caption) {
this();
setText(caption);
@@ -135,14 +144,32 @@ public class SimpleTree extends ComplexPanel implements HasDoubleClickHandlers {
add(child, children);
}
+ /**
+ * {@inheritDoc}
+ *
+ * @deprecated As of 7.2, call and override {@link #add(Widget, Element)}
+ * instead.
+ */
@Override
- protected void add(Widget child, Element container) {
+ @Deprecated
+ protected void add(Widget child,
+ com.google.gwt.user.client.Element container) {
super.add(child, container);
handle.getStyle().setDisplay(Display.INLINE_BLOCK);
getElement().getStyle().setPaddingLeft(3, Unit.PX);
}
/**
+ * {@inheritDoc}
+ *
+ * @since 7.2
+ */
+ @Override
+ protected void add(Widget child, Element container) {
+ add(child, DOM.asOld(container));
+ }
+
+ /**
* {@inheritDoc} Events are not fired when double clicking child widgets.
*/
@Override
diff --git a/client/src/com/vaadin/client/Util.java b/client/src/com/vaadin/client/Util.java
index aae3dd5458..730f844985 100644
--- a/client/src/com/vaadin/client/Util.java
+++ b/client/src/com/vaadin/client/Util.java
@@ -27,6 +27,7 @@ import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.dom.client.AnchorElement;
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.NativeEvent;
import com.google.gwt.dom.client.Node;
import com.google.gwt.dom.client.NodeList;
@@ -35,7 +36,6 @@ import com.google.gwt.dom.client.Style.Display;
import com.google.gwt.dom.client.Touch;
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.EventListener;
import com.google.gwt.user.client.Window;
@@ -75,7 +75,8 @@ public class Util {
* @param y
* @return the element at given coordinates
*/
- public static native Element getElementFromPoint(int clientX, int clientY)
+ public static native com.google.gwt.user.client.Element getElementFromPoint(
+ int clientX, int clientY)
/*-{
var el = $wnd.document.elementFromPoint(clientX, clientY);
// Call elementFromPoint two times to make sure IE8 also returns something sensible if the application is running in an iframe
@@ -196,7 +197,8 @@ public class Util {
* clone child tree also
* @return
*/
- public static native Element cloneNode(Element element, boolean deep)
+ public static native com.google.gwt.user.client.Element cloneNode(
+ Element element, boolean deep)
/*-{
return element.cloneNode(deep);
}-*/;
@@ -789,7 +791,7 @@ public class Util {
if (connector != null) {
// check that inside the rootElement
while (browseElement != null && browseElement != rootElement) {
- browseElement = (Element) browseElement.getParentElement();
+ browseElement = browseElement.getParentElement();
}
if (browseElement != rootElement) {
return null;
@@ -798,7 +800,7 @@ public class Util {
}
}
- browseElement = (Element) browseElement.getParentElement();
+ browseElement = browseElement.getParentElement();
}
// No connector found, element is possibly inside a VOverlay
@@ -855,6 +857,7 @@ public class Util {
* @param class1
* the Widget type to seek for
*/
+ @SuppressWarnings("unchecked")
public static <T> T findWidget(Element element,
Class<? extends Widget> class1) {
if (element != null) {
@@ -863,7 +866,7 @@ public class Util {
while (eventListener == null && element != null) {
eventListener = Event.getEventListener(element);
if (eventListener == null) {
- element = (Element) element.getParentElement();
+ element = element.getParentElement();
}
}
if (eventListener instanceof Widget) {
@@ -946,7 +949,7 @@ public class Util {
NodeList<com.google.gwt.dom.client.Element> imgElements = element
.getElementsByTagName("img");
for (int i = 0; i < imgElements.getLength(); i++) {
- DOM.sinkEvents((Element) imgElements.getItem(i), Event.ONLOAD);
+ DOM.sinkEvents(imgElements.getItem(i), Event.ONLOAD);
}
}
@@ -1073,7 +1076,8 @@ public class Util {
* the mouse event to get coordinates from
* @return the element at the coordinates of the event
*/
- public static Element getElementUnderMouse(NativeEvent event) {
+ public static com.google.gwt.user.client.Element getElementUnderMouse(
+ NativeEvent event) {
int pageX = getTouchOrMouseClientX(event);
int pageY = getTouchOrMouseClientY(event);
@@ -1180,7 +1184,7 @@ public class Util {
*
* @return The active element or null if no active element could be found.
*/
- public native static Element getFocusedElement()
+ public native static com.google.gwt.user.client.Element getFocusedElement()
/*-{
if ($wnd.document.activeElement) {
return $wnd.document.activeElement;
@@ -1196,7 +1200,7 @@ public class Util {
* @deprecated Use #getFocusedElement instead
*/
@Deprecated
- public static Element getIEFocusedElement() {
+ public static com.google.gwt.user.client.Element getIEFocusedElement() {
return getFocusedElement();
}
diff --git a/client/src/com/vaadin/client/VCaption.java b/client/src/com/vaadin/client/VCaption.java
index d0338de4a1..1b98a0fa78 100644
--- a/client/src/com/vaadin/client/VCaption.java
+++ b/client/src/com/vaadin/client/VCaption.java
@@ -17,13 +17,14 @@
package com.vaadin.client;
import com.google.gwt.aria.client.Roles;
+import com.google.gwt.dom.client.Element;
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.ui.HTML;
import com.vaadin.client.communication.StateChangeEvent;
import com.vaadin.client.ui.AbstractFieldConnector;
import com.vaadin.client.ui.Icon;
+import com.vaadin.client.ui.ImageIcon;
import com.vaadin.client.ui.aria.AriaHelper;
import com.vaadin.shared.AbstractComponentState;
import com.vaadin.shared.AbstractFieldState;
@@ -42,6 +43,8 @@ public class VCaption extends HTML {
private Icon icon;
+ private String iconAltText = "";
+
private Element captionText;
private final ApplicationConnection client;
@@ -104,6 +107,7 @@ public class VCaption extends HTML {
if (null != owner) {
AriaHelper.bindCaption(owner.getWidget(), getElement());
}
+
}
@Override
@@ -112,6 +116,8 @@ public class VCaption extends HTML {
if (null != owner) {
AriaHelper.bindCaption(owner.getWidget(), null);
+ AriaHelper.handleInputInvalid(owner.getWidget(), false);
+ AriaHelper.handleInputRequired(owner.getWidget(), false);
}
}
@@ -155,25 +161,27 @@ public class VCaption extends HTML {
showRequired = ((AbstractFieldConnector) owner).isRequired();
}
+ if (icon != null) {
+ getElement().removeChild(icon.getElement());
+ icon = null;
+ }
if (hasIcon) {
- if (icon == null) {
- icon = new Icon(client);
+ String uri = owner.getState().resources.get(
+ ComponentConstants.ICON_RESOURCE).getURL();
+
+ icon = client.getIcon(uri);
+
+ if (icon instanceof ImageIcon) {
+ // onload will set appropriate size later
icon.setWidth("0");
icon.setHeight("0");
-
- DOM.insertChild(getElement(), icon.getElement(),
- getInsertPosition(InsertPosition.ICON));
}
- // Icon forces the caption to be above the component
- placedAfterComponent = false;
- icon.setUri(owner.getState().resources.get(
- ComponentConstants.ICON_RESOURCE).getURL());
+ DOM.insertChild(getElement(), icon.getElement(),
+ getInsertPosition(InsertPosition.ICON));
- } else if (icon != null) {
- // Remove existing
- DOM.removeChild(getElement(), icon.getElement());
- icon = null;
+ // Icon forces the caption to be above the component
+ placedAfterComponent = false;
}
if (owner.getState().caption != null) {
@@ -300,6 +308,14 @@ public class VCaption extends HTML {
@Deprecated
public boolean updateCaptionWithoutOwner(String caption, boolean disabled,
boolean hasDescription, boolean hasError, String iconURL) {
+ return updateCaptionWithoutOwner(caption, disabled, hasDescription,
+ hasError, iconURL, "");
+ }
+
+ @Deprecated
+ public boolean updateCaptionWithoutOwner(String caption, boolean disabled,
+ boolean hasDescription, boolean hasError, String iconURL,
+ String iconAltText) {
boolean wasPlacedAfterComponent = placedAfterComponent;
// Caption is placed after component unless there is some part which
@@ -320,24 +336,24 @@ public class VCaption extends HTML {
}
boolean hasIcon = iconURL != null;
+ if (icon != null) {
+ getElement().removeChild(icon.getElement());
+ icon = null;
+ }
if (hasIcon) {
- if (icon == null) {
- icon = new Icon(client);
+ icon = client.getIcon(iconURL);
+ if (icon instanceof ImageIcon) {
+ // onload sets appropriate size later
icon.setWidth("0");
icon.setHeight("0");
-
- DOM.insertChild(getElement(), icon.getElement(),
- getInsertPosition(InsertPosition.ICON));
}
+ icon.setAlternateText(iconAltText);
+ DOM.insertChild(getElement(), icon.getElement(),
+ getInsertPosition(InsertPosition.ICON));
+
// Icon forces the caption to be above the component
placedAfterComponent = false;
- icon.setUri(iconURL);
-
- } else if (icon != null) {
- // Remove existing
- DOM.removeChild(getElement(), icon.getElement());
- icon = null;
}
if (caption != null) {
@@ -653,8 +669,8 @@ public class VCaption extends HTML {
return tooltipInfo;
}
- protected Element getTextElement() {
- return captionText;
+ protected com.google.gwt.user.client.Element getTextElement() {
+ return DOM.asOld(captionText);
}
public static String getCaptionOwnerPid(Element e) {
diff --git a/client/src/com/vaadin/client/VErrorMessage.java b/client/src/com/vaadin/client/VErrorMessage.java
index 2e42b98a05..77b3970aba 100644
--- a/client/src/com/vaadin/client/VErrorMessage.java
+++ b/client/src/com/vaadin/client/VErrorMessage.java
@@ -16,8 +16,8 @@
package com.vaadin.client;
+import com.google.gwt.dom.client.Element;
import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.Widget;
@@ -31,9 +31,6 @@ public class VErrorMessage extends FlowPanel {
public VErrorMessage() {
super();
setStyleName(CLASSNAME);
-
- // Needed for binding with WAI-ARIA attributes
- getElement().setId(DOM.createUniqueId());
}
/**
@@ -64,8 +61,10 @@ public class VErrorMessage extends FlowPanel {
* Shows this error message next to given element.
*
* @param indicatorElement
+ * @deprecated As of 7.2, call and override {@link #showAt(Element)} instead
*/
- public void showAt(Element indicatorElement) {
+ @Deprecated
+ public void showAt(com.google.gwt.user.client.Element indicatorElement) {
VOverlay errorContainer = (VOverlay) getParent();
if (errorContainer == null) {
errorContainer = new VOverlay();
@@ -85,6 +84,17 @@ public class VErrorMessage extends FlowPanel {
}
+ /**
+ * Shows this error message next to given element.
+ *
+ * @param indicatorElement
+ *
+ * @since 7.2
+ */
+ public void showAt(Element indicatorElement) {
+ showAt(DOM.asOld(indicatorElement));
+ }
+
public void hide() {
final VOverlay errorContainer = (VOverlay) getParent();
if (errorContainer != null) {
diff --git a/client/src/com/vaadin/client/VLoadingIndicator.java b/client/src/com/vaadin/client/VLoadingIndicator.java
index 3b6cf2252c..3a6f8e08bb 100644
--- a/client/src/com/vaadin/client/VLoadingIndicator.java
+++ b/client/src/com/vaadin/client/VLoadingIndicator.java
@@ -16,10 +16,10 @@
package com.vaadin.client;
+import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Style.Display;
import com.google.gwt.dom.client.Style.Position;
import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Timer;
/**
@@ -228,14 +228,14 @@ public class VLoadingIndicator {
*
* @return The loading indicator DOM element
*/
- public Element getElement() {
+ public com.google.gwt.user.client.Element getElement() {
if (element == null) {
element = DOM.createDiv();
element.getStyle().setPosition(Position.ABSOLUTE);
getConnection().getUIConnector().getWidget().getElement()
.appendChild(element);
}
- return element;
+ return DOM.asOld(element);
}
}
diff --git a/client/src/com/vaadin/client/VTooltip.java b/client/src/com/vaadin/client/VTooltip.java
index 6191821988..9badd0ca1c 100644
--- a/client/src/com/vaadin/client/VTooltip.java
+++ b/client/src/com/vaadin/client/VTooltip.java
@@ -15,8 +15,10 @@
*/
package com.vaadin.client;
-import com.google.gwt.aria.client.Id;
+import com.google.gwt.aria.client.LiveValue;
+import com.google.gwt.aria.client.RelevantValue;
import com.google.gwt.aria.client.Roles;
+import com.google.gwt.dom.client.Element;
import com.google.gwt.event.dom.client.BlurEvent;
import com.google.gwt.event.dom.client.BlurHandler;
import com.google.gwt.event.dom.client.ClickEvent;
@@ -29,19 +31,18 @@ import com.google.gwt.event.dom.client.KeyDownHandler;
import com.google.gwt.event.dom.client.MouseMoveEvent;
import com.google.gwt.event.dom.client.MouseMoveHandler;
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;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;
-import com.vaadin.client.ui.VOverlay;
+import com.vaadin.client.ui.VWindowOverlay;
/**
* TODO open for extension
*/
-public class VTooltip extends VOverlay {
+public class VTooltip extends VWindowOverlay {
private static final String CLASSNAME = "v-tooltip";
private static final int MARGIN = 4;
public static final int TOOLTIP_EVENTS = Event.ONKEYDOWN
@@ -57,7 +58,6 @@ public class VTooltip extends VOverlay {
private boolean justClosed = false;
private String uniqueId = DOM.createUniqueId();
- private Element layoutElement;
private int maxWidth;
// Delays for the tooltip, configurable on the server side
@@ -80,17 +80,28 @@ public class VTooltip extends VOverlay {
setWidget(layout);
layout.add(em);
DOM.setElementProperty(description, "className", CLASSNAME + "-text");
-
- layoutElement = layout.getElement();
- DOM.appendChild(layoutElement, description);
+ DOM.appendChild(layout.getElement(), description);
setSinkShadowEvents(true);
- // Used to bind the tooltip to the owner for assistive devices
- layoutElement.setId(uniqueId);
+ // When a tooltip is shown, the content of the tooltip changes. With a
+ // tooltip being a live-area, this change is notified to a assistive
+ // device.
+ Roles.getTooltipRole().set(getElement());
+ Roles.getTooltipRole().setAriaLiveProperty(getElement(),
+ LiveValue.ASSERTIVE);
+ Roles.getTooltipRole().setAriaRelevantProperty(getElement(),
+ RelevantValue.ADDITIONS);
+ }
- description.setId(DOM.createUniqueId());
- Roles.getTooltipRole().set(layoutElement);
- Roles.getTooltipRole().setAriaHiddenState(layoutElement, true);
+ /**
+ * Show the tooltip with the provided info for assistive devices.
+ *
+ * @param info
+ * with the content of the tooltip
+ */
+ public void showAssistive(TooltipInfo info) {
+ updatePosition(null, true);
+ show(info);
}
/**
@@ -229,10 +240,11 @@ public class VTooltip extends VOverlay {
@Override
public void hide() {
- super.hide();
- Roles.getTooltipRole().setAriaHiddenState(layoutElement, true);
- Roles.getTooltipRole().removeAriaDescribedbyProperty(
- tooltipEventHandler.currentElement);
+ em.updateMessage("");
+ description.setInnerHTML("");
+
+ updatePosition(null, true);
+ setPopupPosition(tooltipEventMouseX, tooltipEventMouseY);
}
private int tooltipEventMouseX;
@@ -287,9 +299,9 @@ public class VTooltip extends VOverlay {
private com.google.gwt.dom.client.Element currentElement = null;
/**
- * Current element focused
+ * Marker for handling of tooltip through focus
*/
- private boolean currentIsFocused;
+ private boolean handledByFocus;
/**
* Current tooltip active
@@ -392,44 +404,37 @@ public class VTooltip extends VOverlay {
*/
@Override
public void onBlur(BlurEvent be) {
+ handledByFocus = false;
handleHideEvent();
}
private void handleShowHide(DomEvent domEvent, boolean isFocused) {
Event event = Event.as(domEvent.getNativeEvent());
- com.google.gwt.dom.client.Element element = Element.as(event
- .getEventTarget());
+ Element element = Element.as(event.getEventTarget());
// We can ignore move event if it's handled by move or over already
- if (currentElement == element && currentIsFocused == isFocused) {
+ if (currentElement == element && handledByFocus == true) {
return;
}
- boolean connectorAndTooltipFound = resolveConnector((com.google.gwt.user.client.Element) element);
+ boolean connectorAndTooltipFound = resolveConnector(element);
if (!connectorAndTooltipFound) {
if (isShowing()) {
handleHideEvent();
- Roles.getButtonRole()
- .removeAriaDescribedbyProperty(element);
} else {
currentTooltipInfo = null;
}
} else {
updatePosition(event, isFocused);
- if (isShowing()) {
+ if (isShowing() && !isFocused) {
replaceCurrentTooltip();
- Roles.getTooltipRole().removeAriaDescribedbyProperty(
- currentElement);
} else {
showTooltip();
}
-
- Roles.getTooltipRole().setAriaDescribedbyProperty(element,
- Id.of(uniqueId));
}
- currentIsFocused = isFocused;
+ handledByFocus = isFocused;
currentElement = element;
}
}
@@ -464,9 +469,11 @@ public class VTooltip extends VOverlay {
@Override
public void setPopupPositionAndShow(PositionCallback callback) {
- super.setPopupPositionAndShow(callback);
-
- Roles.getTooltipRole().setAriaHiddenState(layoutElement, false);
+ if (isAttached()) {
+ callback.setPosition(getOffsetWidth(), getOffsetHeight());
+ } else {
+ super.setPopupPositionAndShow(callback);
+ }
}
/**
diff --git a/client/src/com/vaadin/client/annotations/OnStateChange.java b/client/src/com/vaadin/client/annotations/OnStateChange.java
new file mode 100644
index 0000000000..8223507b7f
--- /dev/null
+++ b/client/src/com/vaadin/client/annotations/OnStateChange.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import com.vaadin.client.communication.StateChangeEvent;
+
+/**
+ * Marks a method in Connector classes that should be used to handle changes to
+ * specific properties in the connector's shared state.
+ * <p>
+ * The annotated method will be called whenever at least one of the named state
+ * properties have changed. If multiple listened properties are changed by the
+ * same {@link StateChangeEvent}, the method will only be called once.
+ * <p>
+ * If there is no state variable with the provided name, the widgetset
+ * compilation will fail.
+ *
+ * @since 7.2
+ * @author Vaadin Ltd
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+@Documented
+public @interface OnStateChange {
+ /**
+ * Defines a list of property names to listen for.
+ *
+ * @return an array of property names, should contain at least one item
+ */
+ public String[] value();
+}
diff --git a/client/src/com/vaadin/client/communication/AtmospherePushConnection.java b/client/src/com/vaadin/client/communication/AtmospherePushConnection.java
index f9bff8199e..f0e3eb5b48 100644
--- a/client/src/com/vaadin/client/communication/AtmospherePushConnection.java
+++ b/client/src/com/vaadin/client/communication/AtmospherePushConnection.java
@@ -20,6 +20,7 @@ import java.util.ArrayList;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.Scheduler;
+import com.google.gwt.json.client.JSONObject;
import com.google.gwt.user.client.Command;
import com.vaadin.client.ApplicationConfiguration;
import com.vaadin.client.ApplicationConnection;
@@ -110,7 +111,7 @@ public class AtmospherePushConnection implements PushConnection {
private JavaScriptObject socket;
- private ArrayList<String> messageQueue = new ArrayList<String>();
+ private ArrayList<JSONObject> messageQueue = new ArrayList<JSONObject>();
private State state = State.CONNECT_PENDING;
@@ -191,14 +192,8 @@ public class AtmospherePushConnection implements PushConnection {
}
}
- /*
- * (non-Javadoc)
- *
- * @see
- * com.vaadin.client.communication.PushConenction#push(java.lang.String)
- */
@Override
- public void push(String message) {
+ public void push(JSONObject message) {
switch (state) {
case CONNECT_PENDING:
assert isActive();
@@ -210,12 +205,13 @@ public class AtmospherePushConnection implements PushConnection {
VConsole.log("Sending push message: " + message);
if (transport.equals("websocket")) {
- FragmentedMessage fragmented = new FragmentedMessage(message);
+ FragmentedMessage fragmented = new FragmentedMessage(
+ message.toString());
while (fragmented.hasNextFragment()) {
doPush(socket, fragmented.getNextFragment());
}
} else {
- doPush(socket, message);
+ doPush(socket, message.toString());
}
break;
case DISCONNECT_PENDING:
@@ -254,7 +250,7 @@ public class AtmospherePushConnection implements PushConnection {
switch (state) {
case CONNECT_PENDING:
state = State.CONNECTED;
- for (String message : messageQueue) {
+ for (JSONObject message : messageQueue) {
push(message);
}
messageQueue.clear();
@@ -448,13 +444,13 @@ public class AtmospherePushConnection implements PushConnection {
return {
transport: 'websocket',
maxStreamingLength: 1000000,
- fallbackTransport: 'streaming',
+ fallbackTransport: 'long-polling',
contentType: 'application/json; charset=UTF-8',
reconnectInterval: 5000,
timeout: -1,
maxReconnectOnClose: 10000000,
trackMessageLength: true,
- enableProtocol: false,
+ enableProtocol: true,
messageDelimiter: String.fromCharCode(@com.vaadin.shared.communication.PushConstants::MESSAGE_DELIMITER)
};
}-*/;
diff --git a/client/src/com/vaadin/client/communication/Date_Serializer.java b/client/src/com/vaadin/client/communication/Date_Serializer.java
new file mode 100644
index 0000000000..c6eb7af188
--- /dev/null
+++ b/client/src/com/vaadin/client/communication/Date_Serializer.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.communication;
+
+import java.util.Date;
+
+import com.google.gwt.json.client.JSONNumber;
+import com.google.gwt.json.client.JSONValue;
+import com.vaadin.client.ApplicationConnection;
+import com.vaadin.client.metadata.Type;
+
+/**
+ * Client side serializer/deserializer for java.util.Date
+ *
+ * @since 7.2
+ * @author Vaadin Ltd
+ */
+public class Date_Serializer implements JSONSerializer<Date> {
+
+ @Override
+ public Date deserialize(Type type, JSONValue jsonValue,
+ ApplicationConnection connection) {
+ return new Date((long) ((JSONNumber) jsonValue).doubleValue());
+ }
+
+ @Override
+ public JSONValue serialize(Date value, ApplicationConnection connection) {
+ return new JSONNumber(value.getTime());
+ }
+
+}
diff --git a/client/src/com/vaadin/client/communication/Heartbeat.java b/client/src/com/vaadin/client/communication/Heartbeat.java
new file mode 100644
index 0000000000..4b80827127
--- /dev/null
+++ b/client/src/com/vaadin/client/communication/Heartbeat.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.communication;
+
+import java.util.logging.Logger;
+
+import com.google.gwt.http.client.Request;
+import com.google.gwt.http.client.RequestBuilder;
+import com.google.gwt.http.client.RequestCallback;
+import com.google.gwt.http.client.RequestException;
+import com.google.gwt.http.client.Response;
+import com.google.gwt.user.client.Timer;
+import com.vaadin.client.ApplicationConnection;
+import com.vaadin.client.ApplicationConnection.ApplicationStoppedEvent;
+import com.vaadin.shared.ApplicationConstants;
+import com.vaadin.shared.ui.ui.UIConstants;
+
+/**
+ * Handles sending of heartbeats to the server and reacting to the response
+ *
+ * @since 7.2
+ * @author Vaadin Ltd
+ */
+public class Heartbeat {
+
+ private int interval = -1;
+ private Timer timer = new Timer() {
+ @Override
+ public void run() {
+ send();
+ }
+ };
+
+ private ApplicationConnection connection;
+
+ private static Logger getLogger() {
+ return Logger.getLogger(Heartbeat.class.getName());
+ }
+
+ /**
+ * Initializes the heartbeat for the given application connection
+ *
+ * @param connection
+ * the connection
+ */
+ public void init(ApplicationConnection connection) {
+ this.connection = connection;
+ interval = connection.getConfiguration().getHeartbeatInterval();
+ setInterval(interval);
+ schedule();
+
+ connection.addHandler(
+ ApplicationConnection.ApplicationStoppedEvent.TYPE,
+ new ApplicationConnection.ApplicationStoppedHandler() {
+
+ @Override
+ public void onApplicationStopped(
+ ApplicationStoppedEvent event) {
+ setInterval(-1);
+ schedule();
+ }
+ });
+
+ }
+
+ /**
+ * Sends a heartbeat to the server
+ */
+ public void send() {
+ final String uri = ApplicationConnection.addGetParameters(
+ getConnection().translateVaadinUri(
+ ApplicationConstants.APP_PROTOCOL_PREFIX
+ + ApplicationConstants.HEARTBEAT_PATH + '/'),
+ UIConstants.UI_ID_PARAMETER + "="
+ + getConnection().getConfiguration().getUIId());
+
+ final RequestBuilder rb = new RequestBuilder(RequestBuilder.POST, uri);
+
+ final RequestCallback callback = new RequestCallback() {
+
+ @Override
+ public void onResponseReceived(Request request, Response response) {
+ int status = response.getStatusCode();
+ if (status == Response.SC_OK) {
+ // TODO Permit retry in some error situations
+ getLogger().fine("Heartbeat response OK");
+ schedule();
+ } else if (status == Response.SC_GONE) {
+ // FIXME This should really do something else like send an
+ // event
+ getConnection().showSessionExpiredError(null);
+ } else {
+ getLogger().warning(
+ "Failed sending heartbeat to server. Error code: "
+ + status);
+ }
+ }
+
+ @Override
+ public void onError(Request request, Throwable exception) {
+ getLogger().severe("Exception sending heartbeat: " + exception);
+ }
+ };
+
+ rb.setCallback(callback);
+
+ try {
+ getLogger().fine("Sending heartbeat request...");
+ rb.send();
+ } catch (RequestException re) {
+ callback.onError(null, re);
+ }
+
+ }
+
+ /**
+ * @return the interval at which heartbeat requests are sent
+ */
+ public int getInterval() {
+ return interval;
+ }
+
+ /**
+ * sets the interval at which heartbeat requests are sent
+ *
+ * @param interval
+ * the new interval
+ */
+ public void setInterval(int interval) {
+ this.interval = interval;
+ }
+
+ /**
+ * Updates the schedule of the heartbeat to match the set interval. A
+ * negative interval disables the heartbeat.
+ */
+ public void schedule() {
+ if (getInterval() > 0) {
+ getLogger()
+ .fine("Scheduling heartbeat in " + interval + " seconds");
+ timer.schedule(interval * 1000);
+ } else {
+ if (timer != null) {
+ getLogger().fine("Disabling heartbeat");
+ timer.cancel();
+ }
+ }
+
+ }
+
+ /**
+ * @return the application connection
+ */
+ protected ApplicationConnection getConnection() {
+ return connection;
+ }
+
+}
diff --git a/client/src/com/vaadin/client/communication/JSONSerializer.java b/client/src/com/vaadin/client/communication/JSONSerializer.java
index e5829ece24..a4e78e503c 100644
--- a/client/src/com/vaadin/client/communication/JSONSerializer.java
+++ b/client/src/com/vaadin/client/communication/JSONSerializer.java
@@ -23,14 +23,17 @@ import com.vaadin.client.metadata.Type;
/**
* Implementors of this interface knows how to serialize an Object of a given
* type to JSON and how to deserialize the JSON back into an object.
- *
+ * <p>
* The {@link #serialize(Object, ApplicationConnection)} and
* {@link #deserialize(Type, JSONValue, ApplicationConnection)} methods must be
* symmetric so they can be chained and produce the original result (or an equal
* result).
- *
+ * <p>
* Each {@link JSONSerializer} implementation can handle an object of a single
* type - see {@link Type#findSerializer()}.
+ * <p>
+ * This is the client side interface, see
+ * com.vaadin.server.communication.JSONSerializer for the server side interface.
*
* @since 7.0
*/
diff --git a/client/src/com/vaadin/client/communication/PushConnection.java b/client/src/com/vaadin/client/communication/PushConnection.java
index a7eba224be..ba79af9d2c 100644
--- a/client/src/com/vaadin/client/communication/PushConnection.java
+++ b/client/src/com/vaadin/client/communication/PushConnection.java
@@ -16,6 +16,7 @@
package com.vaadin.client.communication;
+import com.google.gwt.json.client.JSONObject;
import com.google.gwt.user.client.Command;
import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.ApplicationConnection.CommunicationErrorHandler;
@@ -53,14 +54,14 @@ public interface PushConnection {
* replay those messages in the original order when the connection has been
* established.
*
- * @param message
- * the message to push
+ * @param payload
+ * the payload to push
* @throws IllegalStateException
* if this connection is not active
*
* @see #isActive()
*/
- public void push(String message);
+ public void push(JSONObject payload);
/**
* Checks whether this push connection is in a state where it can push
diff --git a/client/src/com/vaadin/client/componentlocator/ComponentLocator.java b/client/src/com/vaadin/client/componentlocator/ComponentLocator.java
new file mode 100644
index 0000000000..f0b76766a7
--- /dev/null
+++ b/client/src/com/vaadin/client/componentlocator/ComponentLocator.java
@@ -0,0 +1,250 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.componentlocator;
+
+import java.util.Arrays;
+import java.util.List;
+
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.core.client.JsArray;
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.user.client.DOM;
+import com.vaadin.client.ApplicationConnection;
+
+/**
+ * ComponentLocator provides methods for generating a String locator for a given
+ * DOM element and for locating a DOM element using a String locator.
+ * <p>
+ * The main use for this class is locating components for automated testing
+ * purposes.
+ *
+ * @since 7.2, moved from {@link com.vaadin.client.ComponentLocator}
+ */
+public class ComponentLocator {
+
+ private final List<LocatorStrategy> locatorStrategies;
+
+ /**
+ * Reference to ApplicationConnection instance.
+ */
+
+ private final ApplicationConnection client;
+
+ /**
+ * Construct a ComponentLocator for the given ApplicationConnection.
+ *
+ * @param client
+ * ApplicationConnection instance for the application.
+ */
+ public ComponentLocator(ApplicationConnection client) {
+ this.client = client;
+ locatorStrategies = Arrays.asList(new VaadinFinderLocatorStrategy(
+ client), new LegacyLocatorStrategy(client));
+ }
+
+ /**
+ * Generates a String locator which uniquely identifies the target element.
+ * The {@link #getElementByPath(String)} method can be used for the inverse
+ * operation, i.e. locating an element based on the return value from this
+ * method.
+ * <p>
+ * Note that getElementByPath(getPathForElement(element)) == element is not
+ * always true as #getPathForElement(Element) can return a path to another
+ * element if the widget determines an action on the other element will give
+ * the same result as the action on the target element.
+ * </p>
+ *
+ * @since 5.4
+ * @param targetElement
+ * The element to generate a path for.
+ * @return A String locator that identifies the target element or null if a
+ * String locator could not be created.
+ * @deprecated As of 7.2, call and override
+ * {@link #getPathForElement(Element)} instead
+ */
+ @Deprecated
+ public String getPathForElement(
+ com.google.gwt.user.client.Element targetElement) {
+ for (LocatorStrategy strategy : locatorStrategies) {
+ String path = strategy.getPathForElement(targetElement);
+ if (null != path) {
+ return path;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Generates a String locator which uniquely identifies the target element.
+ * The {@link #getElementByPath(String)} method can be used for the inverse
+ * operation, i.e. locating an element based on the return value from this
+ * method.
+ * <p>
+ * Note that getElementByPath(getPathForElement(element)) == element is not
+ * always true as #getPathForElement(Element) can return a path to another
+ * element if the widget determines an action on the other element will give
+ * the same result as the action on the target element.
+ * </p>
+ *
+ * @since 7.2
+ * @param targetElement
+ * The element to generate a path for.
+ * @return A String locator that identifies the target element or null if a
+ * String locator could not be created.
+ */
+ public String getPathForElement(Element targetElement) {
+ return getPathForElement(DOM.asOld(targetElement));
+ }
+
+ /**
+ * Locates an element using a String locator (path) which identifies a DOM
+ * element. The {@link #getPathForElement(Element)} method can be used for
+ * the inverse operation, i.e. generating a string expression for a DOM
+ * element.
+ *
+ * @since 5.4
+ * @param path
+ * The String locator which identifies the target element.
+ * @return The DOM element identified by {@code path} or null if the element
+ * could not be located.
+ */
+ public com.google.gwt.user.client.Element getElementByPath(String path) {
+ for (LocatorStrategy strategy : locatorStrategies) {
+ if (strategy.validatePath(path)) {
+ Element element = strategy.getElementByPath(path);
+ if (null != element) {
+ return DOM.asOld(element);
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Locates elements using a String locator (path) which identifies DOM
+ * elements.
+ *
+ * @since 7.2
+ * @param path
+ * The String locator which identifies target elements.
+ * @return The JavaScriptArray of DOM elements identified by {@code path} or
+ * empty array if elements could not be located.
+ */
+ public JsArray<Element> getElementsByPath(String path) {
+ JsArray<Element> jsElements = JavaScriptObject.createArray().cast();
+ for (LocatorStrategy strategy : locatorStrategies) {
+ if (strategy.validatePath(path)) {
+ List<Element> elements = strategy.getElementsByPath(path);
+ if (elements.size() > 0) {
+ for (Element e : elements) {
+ jsElements.push(e);
+ }
+ return jsElements;
+ }
+ }
+ }
+ return jsElements;
+ }
+
+ /**
+ * Locates elements using a String locator (path) which identifies DOM
+ * elements. The path starts from the specified root element.
+ *
+ * @see #getElementByPath(String)
+ *
+ * @since 7.2
+ * @param path
+ * The path of elements to be found
+ * @param root
+ * The root element where the path is anchored
+ * @return The JavaScriptArray of DOM elements identified by {@code path} or
+ * empty array if elements could not be located.
+ */
+ public JsArray<Element> getElementsByPathStartingAt(String path,
+ Element root) {
+ JsArray<Element> jsElements = JavaScriptObject.createArray().cast();
+ for (LocatorStrategy strategy : locatorStrategies) {
+ if (strategy.validatePath(path)) {
+ List<Element> elements = strategy.getElementsByPathStartingAt(
+ path, root);
+ if (elements.size() > 0) {
+ for (Element e : elements) {
+ jsElements.push(e);
+ }
+ return jsElements;
+ }
+ }
+ }
+ return jsElements;
+ }
+
+ /**
+ * Locates an element using a String locator (path) which identifies a DOM
+ * element. The path starts from the specified root element.
+ *
+ * @see #getElementByPath(String)
+ *
+ * @since 7.2
+ *
+ * @param path
+ * The path of the element to be found
+ * @param root
+ * The root element where the path is anchored
+ * @return The DOM element identified by {@code path} or null if the element
+ * could not be located.
+ */
+ public com.google.gwt.user.client.Element getElementByPathStartingAt(
+ String path, Element root) {
+ for (LocatorStrategy strategy : locatorStrategies) {
+ if (strategy.validatePath(path)) {
+ Element element = strategy.getElementByPathStartingAt(path,
+ root);
+ if (null != element) {
+ return DOM.asOld(element);
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the {@link ApplicationConnection} used by this locator.
+ * <p>
+ * This method is primarily for internal use by the framework.
+ *
+ * @return the application connection
+ */
+ public ApplicationConnection getClient() {
+ return client;
+ }
+
+ /**
+ * Check if a given selector is valid for LegacyLocatorStrategy.
+ *
+ * @param path
+ * Vaadin selector path
+ * @return true if passes path validation with LegacyLocatorStrategy
+ */
+ public boolean isValidForLegacyLocator(String path) {
+ for (LocatorStrategy ls : locatorStrategies) {
+ if (ls instanceof LegacyLocatorStrategy) {
+ return ls.validatePath(path);
+ }
+ }
+ return false;
+ }
+
+}
diff --git a/client/src/com/vaadin/client/componentlocator/LegacyLocatorStrategy.java b/client/src/com/vaadin/client/componentlocator/LegacyLocatorStrategy.java
new file mode 100644
index 0000000000..232433273f
--- /dev/null
+++ b/client/src/com/vaadin/client/componentlocator/LegacyLocatorStrategy.java
@@ -0,0 +1,718 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.componentlocator;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.regexp.shared.RegExp;
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.ui.HasWidgets;
+import com.google.gwt.user.client.ui.RootPanel;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.client.ApplicationConnection;
+import com.vaadin.client.ComponentConnector;
+import com.vaadin.client.ConnectorMap;
+import com.vaadin.client.ServerConnector;
+import com.vaadin.client.Util;
+import com.vaadin.client.VCaption;
+import com.vaadin.client.ui.SubPartAware;
+import com.vaadin.client.ui.VCssLayout;
+import com.vaadin.client.ui.VGridLayout;
+import com.vaadin.client.ui.VOverlay;
+import com.vaadin.client.ui.VTabsheetPanel;
+import com.vaadin.client.ui.VUI;
+import com.vaadin.client.ui.VWindow;
+import com.vaadin.client.ui.orderedlayout.Slot;
+import com.vaadin.client.ui.orderedlayout.VAbstractOrderedLayout;
+import com.vaadin.client.ui.window.WindowConnector;
+import com.vaadin.shared.AbstractComponentState;
+import com.vaadin.shared.Connector;
+import com.vaadin.shared.communication.SharedState;
+
+/**
+ * The LegacyLocatorStrategy class handles the legacy locator syntax that was
+ * introduced in version 5.4 of the framework. The legacy locator strategy is
+ * always used if no other strategy claims responsibility for a locator string.
+ *
+ * @since 7.2
+ * @author Vaadin Ltd
+ */
+public class LegacyLocatorStrategy implements LocatorStrategy {
+
+ /**
+ * Separator used in the String locator between a parent and a child widget.
+ */
+ static final String PARENTCHILD_SEPARATOR = "/";
+ /**
+ * Separator used in the String locator between the part identifying the
+ * containing widget and the part identifying the target element within the
+ * widget.
+ */
+ static final String SUBPART_SEPARATOR = "#";
+ /**
+ * String that identifies the root panel when appearing first in the String
+ * locator.
+ */
+ static final String ROOT_ID = "Root";
+
+ private final ApplicationConnection client;
+
+ private static final RegExp validSyntax = RegExp
+ .compile("^((\\w+::)?((PID_S)?\\w[-$_a-zA-Z0-9.' ]*)?)?(/[-$_a-zA-Z0-9]+\\[\\d+\\])*/?(#.*)?$");
+
+ public LegacyLocatorStrategy(ApplicationConnection clientConnection) {
+ client = clientConnection;
+ }
+
+ @Override
+ public boolean validatePath(String path) {
+ return validSyntax.test(path);
+ }
+
+ @Override
+ public String getPathForElement(Element targetElement) {
+ ComponentConnector connector = Util
+ .findPaintable(client, targetElement);
+
+ Widget w = null;
+ if (connector != null) {
+ // If we found a Paintable then we use that as reference. We should
+ // find the Paintable for all but very special cases (like
+ // overlays).
+ w = connector.getWidget();
+
+ /*
+ * Still if the Paintable contains a widget that implements
+ * SubPartAware, we want to use that as a reference
+ */
+ Widget targetParent = findParentWidget(targetElement, w);
+ while (targetParent != w && targetParent != null) {
+ if (targetParent instanceof SubPartAware) {
+ /*
+ * The targetParent widget is a child of the Paintable and
+ * the first parent (of the targetElement) that implements
+ * SubPartAware
+ */
+ w = targetParent;
+ break;
+ }
+ targetParent = targetParent.getParent();
+ }
+ }
+ if (w == null) {
+ // Check if the element is part of a widget that is attached
+ // directly to the root panel
+ RootPanel rootPanel = RootPanel.get();
+ int rootWidgetCount = rootPanel.getWidgetCount();
+ for (int i = 0; i < rootWidgetCount; i++) {
+ Widget rootWidget = rootPanel.getWidget(i);
+ if (rootWidget.getElement().isOrHasChild(targetElement)) {
+ // The target element is contained by this root widget
+ w = findParentWidget(targetElement, rootWidget);
+ break;
+ }
+ }
+ if (w != null) {
+ // We found a widget but we should still see if we find a
+ // SubPartAware implementor (we cannot find the Paintable as
+ // there is no link from VOverlay to its paintable/owner).
+ Widget subPartAwareWidget = findSubPartAwareParentWidget(w);
+ if (subPartAwareWidget != null) {
+ w = subPartAwareWidget;
+ }
+ }
+ }
+
+ if (w == null) {
+ // Containing widget not found
+ return null;
+ }
+
+ // Determine the path for the target widget
+ String path = getPathForWidget(w);
+ if (path == null) {
+ /*
+ * No path could be determined for the target widget. Cannot create
+ * a locator string.
+ */
+ return null;
+ }
+
+ // The parent check is a work around for Firefox 15 which fails to
+ // compare elements properly (#9534)
+ if (w.getElement() == targetElement) {
+ /*
+ * We are done if the target element is the root of the target
+ * widget.
+ */
+ return path;
+ } else if (w instanceof SubPartAware) {
+ /*
+ * If the widget can provide an identifier for the targetElement we
+ * let it do that
+ */
+ String elementLocator = ((SubPartAware) w).getSubPartName(DOM
+ .asOld(targetElement));
+ if (elementLocator != null) {
+ return path + LegacyLocatorStrategy.SUBPART_SEPARATOR
+ + elementLocator;
+ }
+ }
+ /*
+ * If everything else fails we use the DOM path to identify the target
+ * element
+ */
+ String domPath = getDOMPathForElement(targetElement, w.getElement());
+ if (domPath == null) {
+ return path;
+ } else {
+ return path + domPath;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Element getElementByPath(String path) {
+ return getElementByPathStartingAt(path, null);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Element getElementByPathStartingAt(String path, Element baseElement) {
+ /*
+ * Path is of type "targetWidgetPath#componentPart" or
+ * "targetWidgetPath".
+ */
+ String parts[] = path.split(LegacyLocatorStrategy.SUBPART_SEPARATOR, 2);
+ String widgetPath = parts[0];
+
+ // Note that this only works if baseElement can be mapped to a
+ // widget to which the path is relative. Otherwise, the current
+ // implementation simply interprets the path as if baseElement was
+ // null.
+ Widget baseWidget = Util.findWidget(baseElement, null);
+
+ Widget w = getWidgetFromPath(widgetPath, baseWidget);
+ if (w == null || !Util.isAttachedAndDisplayed(w)) {
+ return null;
+ }
+ if (parts.length == 1) {
+ int pos = widgetPath.indexOf("domChild");
+ if (pos == -1) {
+ return w.getElement();
+ }
+
+ // Contains dom reference to a sub element of the widget
+ String subPath = widgetPath.substring(pos);
+ return getElementByDOMPath(w.getElement(), subPath);
+ } else if (parts.length == 2) {
+ if (w instanceof SubPartAware) {
+ return ((SubPartAware) w).getSubPartElement(parts[1]);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public List<Element> getElementsByPath(String path) {
+ // This type of search is not supported in LegacyLocator
+ List<Element> array = new ArrayList<Element>();
+ Element e = getElementByPath(path);
+ if (e != null) {
+ array.add(e);
+ }
+ return array;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public List<Element> getElementsByPathStartingAt(String path, Element root) {
+ // This type of search is not supported in LegacyLocator
+ List<Element> array = new ArrayList<Element>();
+ Element e = getElementByPathStartingAt(path, root);
+ if (e != null) {
+ array.add(e);
+ }
+ return array;
+ }
+
+ /**
+ * Finds the first widget in the hierarchy (moving upwards) that implements
+ * SubPartAware. Returns the SubPartAware implementor or null if none is
+ * found.
+ *
+ * @param w
+ * The widget to start from. This is returned if it implements
+ * SubPartAware.
+ * @return The first widget (upwards in hierarchy) that implements
+ * SubPartAware or null
+ */
+ Widget findSubPartAwareParentWidget(Widget w) {
+
+ while (w != null) {
+ if (w instanceof SubPartAware) {
+ return w;
+ }
+ w = w.getParent();
+ }
+ return null;
+ }
+
+ /**
+ * Returns the first widget found when going from {@code targetElement}
+ * upwards in the DOM hierarchy, assuming that {@code ancestorWidget} is a
+ * parent of {@code targetElement}.
+ *
+ * @param targetElement
+ * @param ancestorWidget
+ * @return The widget whose root element is a parent of
+ * {@code targetElement}.
+ */
+ private Widget findParentWidget(Element targetElement, Widget ancestorWidget) {
+ /*
+ * As we cannot resolve Widgets from the element we start from the
+ * widget and move downwards to the correct child widget, as long as we
+ * find one.
+ */
+ if (ancestorWidget instanceof HasWidgets) {
+ for (Widget w : ((HasWidgets) ancestorWidget)) {
+ if (w.getElement().isOrHasChild(targetElement)) {
+ return findParentWidget(targetElement, w);
+ }
+ }
+ }
+
+ // No children found, this is it
+ return ancestorWidget;
+ }
+
+ /**
+ * Locates an element based on a DOM path and a base element.
+ *
+ * @param baseElement
+ * The base element which the path is relative to
+ * @param path
+ * String locator (consisting of domChild[x] parts) that
+ * identifies the element
+ * @return The element identified by path, relative to baseElement or null
+ * if the element could not be found.
+ */
+ private Element getElementByDOMPath(Element baseElement, String path) {
+ String parts[] = path.split(PARENTCHILD_SEPARATOR);
+ Element element = baseElement;
+
+ for (int i = 0, l = parts.length; i < l; ++i) {
+ String part = parts[i];
+ if (part.startsWith("domChild[")) {
+ String childIndexString = part.substring("domChild[".length(),
+ part.length() - 1);
+
+ if (Util.findWidget(baseElement, null) instanceof VAbstractOrderedLayout) {
+ if (element.hasChildNodes()) {
+ Element e = element.getFirstChildElement().cast();
+ String cn = e.getClassName();
+ if (cn != null
+ && (cn.equals("v-expand") || cn
+ .contains("v-has-caption"))) {
+ element = e;
+ }
+ }
+ }
+
+ try {
+ int childIndex = Integer.parseInt(childIndexString);
+ element = DOM.getChild(element, childIndex);
+ } catch (Exception e) {
+ return null;
+ }
+
+ if (element == null) {
+ return null;
+ }
+
+ } else {
+
+ path = parts[i];
+ for (int j = i + 1; j < l; ++j) {
+ path += PARENTCHILD_SEPARATOR + parts[j];
+ }
+
+ return getElementByPathStartingAt(path, element);
+ }
+ }
+
+ return element;
+ }
+
+ /**
+ * Generates a String locator using domChild[x] parts for the element
+ * relative to the baseElement.
+ *
+ * @param element
+ * The target element
+ * @param baseElement
+ * The starting point for the locator. The generated path is
+ * relative to this element.
+ * @return A String locator that can be used to locate the target element
+ * using {@link #getElementByDOMPath(Element, String)} or null if
+ * the locator String cannot be created.
+ */
+ private String getDOMPathForElement(Element element, Element baseElement) {
+ Element e = element;
+ String path = "";
+ while (true) {
+ int childIndex = -1;
+ Element siblingIterator = e;
+ while (siblingIterator != null) {
+ childIndex++;
+ siblingIterator = siblingIterator.getPreviousSiblingElement()
+ .cast();
+ }
+
+ path = PARENTCHILD_SEPARATOR + "domChild[" + childIndex + "]"
+ + path;
+
+ JavaScriptObject parent = e.getParentElement();
+ if (parent == null) {
+ return null;
+ }
+ // The parent check is a work around for Firefox 15 which fails to
+ // compare elements properly (#9534)
+ if (parent == baseElement) {
+ break;
+ }
+
+ e = parent.cast();
+ }
+
+ return path;
+ }
+
+ /**
+ * Creates a locator String for the given widget. The path can be used to
+ * locate the widget using {@link #getWidgetFromPath(String, Widget)}.
+ * <p/>
+ * Returns null if no path can be determined for the widget or if the widget
+ * is null.
+ *
+ * @param w
+ * The target widget
+ * @return A String locator for the widget
+ */
+ private String getPathForWidget(Widget w) {
+ if (w == null) {
+ return null;
+ }
+ String elementId = w.getElement().getId();
+ if (elementId != null && !elementId.isEmpty()
+ && !elementId.startsWith("gwt-uid-")) {
+ // Use PID_S+id if the user has set an id but do not use it for auto
+ // generated id:s as these might not be consistent
+ return "PID_S" + elementId;
+ } else if (w instanceof VUI) {
+ return "";
+ } else if (w instanceof VWindow) {
+ Connector windowConnector = ConnectorMap.get(client)
+ .getConnector(w);
+ List<WindowConnector> subWindowList = client.getUIConnector()
+ .getSubWindows();
+ int indexOfSubWindow = subWindowList.indexOf(windowConnector);
+ return PARENTCHILD_SEPARATOR + "VWindow[" + indexOfSubWindow + "]";
+ } else if (w instanceof RootPanel) {
+ return ROOT_ID;
+ }
+
+ Widget parent = w.getParent();
+
+ String basePath = getPathForWidget(parent);
+ if (basePath == null) {
+ return null;
+ }
+ String simpleName = Util.getSimpleName(w);
+
+ /*
+ * Check if the parent implements Iterable. At least VPopupView does not
+ * implement HasWdgets so we cannot check for that.
+ */
+ if (!(parent instanceof Iterable<?>)) {
+ // Parent does not implement Iterable so we cannot find out which
+ // child this is
+ return null;
+ }
+
+ Iterator<?> i = ((Iterable<?>) parent).iterator();
+ int pos = 0;
+ while (i.hasNext()) {
+ Object child = i.next();
+ if (child == w) {
+ return basePath + PARENTCHILD_SEPARATOR + simpleName + "["
+ + pos + "]";
+ }
+ String simpleName2 = Util.getSimpleName(child);
+ if (simpleName.equals(simpleName2)) {
+ pos++;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Locates the widget based on a String locator.
+ *
+ * @param path
+ * The String locator that identifies the widget.
+ * @param baseWidget
+ * the widget to which the path is relative, null if relative to
+ * root
+ * @return The Widget identified by the String locator or null if the widget
+ * could not be identified.
+ */
+ @SuppressWarnings("unchecked")
+ private Widget getWidgetFromPath(String path, Widget baseWidget) {
+ Widget w = baseWidget;
+ String parts[] = path.split(PARENTCHILD_SEPARATOR);
+
+ for (int i = 0; i < parts.length; i++) {
+ String part = parts[i];
+
+ if (part.equals(ROOT_ID)) {
+ w = RootPanel.get();
+ } else if (part.equals("")) {
+ if (w == null) {
+ w = client.getUIConnector().getWidget();
+ }
+ } else if (w == null) {
+ String id = part;
+ // Must be old static pid (PID_S*)
+ ServerConnector connector = ConnectorMap.get(client)
+ .getConnector(id);
+ if (connector == null) {
+ // Lookup by component id
+ // TODO Optimize this
+ connector = findConnectorById(client.getUIConnector(),
+ id.substring(5));
+ }
+
+ if (connector instanceof ComponentConnector) {
+ w = ((ComponentConnector) connector).getWidget();
+ } else {
+ // Not found
+ return null;
+ }
+ } else if (part.startsWith("domChild[")) {
+ // The target widget has been found and the rest identifies the
+ // element
+ break;
+ } else if (w instanceof Iterable) {
+ // W identifies a widget that contains other widgets, as it
+ // should. Try to locate the child
+ Iterable<?> parent = (Iterable<?>) w;
+
+ // Part is of type "VVerticalLayout[0]", split this into
+ // VVerticalLayout and 0
+ String[] split = part.split("\\[", 2);
+ String widgetClassName = split[0];
+ String indexString = split[1].substring(0,
+ split[1].length() - 1);
+
+ int widgetPosition;
+ try {
+ widgetPosition = Integer.parseInt(indexString);
+ } catch (NumberFormatException e) {
+ // We've probably been fed a new-style Vaadin locator with a
+ // string-form predicate, that doesn't match anything in the
+ // search space.
+ return null;
+ }
+
+ // AbsolutePanel in GridLayout has been removed -> skip it
+ if (w instanceof VGridLayout
+ && "AbsolutePanel".equals(widgetClassName)) {
+ continue;
+ }
+
+ // FlowPane in CSSLayout has been removed -> skip it
+ if (w instanceof VCssLayout
+ && "VCssLayout$FlowPane".equals(widgetClassName)) {
+ continue;
+ }
+
+ // ChildComponentContainer and VOrderedLayout$Slot have been
+ // replaced with Slot
+ if (w instanceof VAbstractOrderedLayout
+ && ("ChildComponentContainer".equals(widgetClassName) || "VOrderedLayout$Slot"
+ .equals(widgetClassName))) {
+ widgetClassName = "Slot";
+ }
+
+ if (w instanceof VTabsheetPanel && widgetPosition != 0) {
+ // TabSheetPanel now only contains 1 connector => the index
+ // is always 0 which indicates the widget in the active tab
+ widgetPosition = 0;
+ }
+ if (w instanceof VOverlay
+ && "VCalendarPanel".equals(widgetClassName)) {
+ // Vaadin 7.1 adds a wrapper for datefield popups
+ parent = (Iterable<?>) ((Iterable<?>) parent).iterator()
+ .next();
+ }
+ /*
+ * The new grid and ordered layouts do not contain
+ * ChildComponentContainer widgets. This is instead simulated by
+ * constructing a path step that would find the desired widget
+ * from the layout and injecting it as the next search step
+ * (which would originally have found the widget inside the
+ * ChildComponentContainer)
+ */
+ if ((w instanceof VGridLayout)
+ && "ChildComponentContainer".equals(widgetClassName)
+ && i + 1 < parts.length) {
+
+ HasWidgets layout = (HasWidgets) w;
+
+ String nextPart = parts[i + 1];
+ String[] nextSplit = nextPart.split("\\[", 2);
+ String nextWidgetClassName = nextSplit[0];
+
+ // Find the n:th child and count the number of children with
+ // the same type before it
+ int nextIndex = 0;
+ for (Widget child : layout) {
+ boolean matchingType = nextWidgetClassName.equals(Util
+ .getSimpleName(child));
+ if (matchingType && widgetPosition == 0) {
+ // This is the n:th child that we looked for
+ break;
+ } else if (widgetPosition < 0) {
+ // Error if we're past the desired position without
+ // a match
+ return null;
+ } else if (matchingType) {
+ // If this was another child of the expected type,
+ // increase the count for the next step
+ nextIndex++;
+ }
+
+ // Don't count captions
+ if (!(child instanceof VCaption)) {
+ widgetPosition--;
+ }
+ }
+
+ // Advance to the next step, this time checking for the
+ // actual child widget
+ parts[i + 1] = nextWidgetClassName + '[' + nextIndex + ']';
+ continue;
+ }
+
+ // Locate the child
+ Iterator<? extends Widget> iterator;
+
+ /*
+ * VWindow and VContextMenu workarounds for backwards
+ * compatibility
+ */
+ if (widgetClassName.equals("VWindow")) {
+ List<WindowConnector> windows = client.getUIConnector()
+ .getSubWindows();
+ List<VWindow> windowWidgets = new ArrayList<VWindow>(
+ windows.size());
+ for (WindowConnector wc : windows) {
+ windowWidgets.add(wc.getWidget());
+ }
+ iterator = windowWidgets.iterator();
+ } else if (widgetClassName.equals("VContextMenu")) {
+ return client.getContextMenu();
+ } else {
+ iterator = (Iterator<? extends Widget>) parent.iterator();
+ }
+
+ boolean ok = false;
+
+ // Find the widgetPosition:th child of type "widgetClassName"
+ while (iterator.hasNext()) {
+
+ Widget child = iterator.next();
+ String simpleName2 = Util.getSimpleName(child);
+
+ if (!widgetClassName.equals(simpleName2)
+ && child instanceof Slot) {
+ /*
+ * Support legacy tests without any selector for the
+ * Slot widget (i.e. /VVerticalLayout[0]/VButton[0]) by
+ * directly checking the stuff inside the slot
+ */
+ child = ((Slot) child).getWidget();
+ simpleName2 = Util.getSimpleName(child);
+ }
+
+ if (widgetClassName.equals(simpleName2)) {
+ if (widgetPosition == 0) {
+ w = child;
+ ok = true;
+ break;
+ }
+ widgetPosition--;
+
+ }
+ }
+
+ if (!ok) {
+ // Did not find the child
+ return null;
+ }
+ } else {
+ // W identifies something that is not a "HasWidgets". This
+ // should not happen as all widget containers should implement
+ // HasWidgets.
+ return null;
+ }
+ }
+
+ return w;
+ }
+
+ private ServerConnector findConnectorById(ServerConnector root, String id) {
+ SharedState state = root.getState();
+ if (state instanceof AbstractComponentState
+ && id.equals(((AbstractComponentState) state).id)) {
+ return root;
+ }
+ for (ServerConnector child : root.getChildren()) {
+ ServerConnector found = findConnectorById(child, id);
+ if (found != null) {
+ return found;
+ }
+ }
+
+ return null;
+ }
+
+}
diff --git a/client/src/com/vaadin/client/componentlocator/LocatorStrategy.java b/client/src/com/vaadin/client/componentlocator/LocatorStrategy.java
new file mode 100644
index 0000000000..6b3103c677
--- /dev/null
+++ b/client/src/com/vaadin/client/componentlocator/LocatorStrategy.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.componentlocator;
+
+import java.util.List;
+
+import com.google.gwt.dom.client.Element;
+
+/**
+ * This interface should be implemented by all locator strategies. A locator
+ * strategy is responsible for generating and decoding a string that identifies
+ * an element in the DOM. A strategy can implement its own syntax for the
+ * locator string, which may be completely different from any other strategy's
+ * syntax.
+ *
+ * @since 7.2
+ * @author Vaadin Ltd
+ */
+public interface LocatorStrategy {
+
+ /**
+ * Test the given input path for formatting errors. If a given path can not
+ * be validated, the locator strategy will not be attempted.
+ *
+ * @param path
+ * a locator path expression
+ * @return true, if the implementing class can process the given path,
+ * otherwise false
+ */
+ boolean validatePath(String path);
+
+ /**
+ * Generates a String locator which uniquely identifies the target element.
+ * The {@link #getElementByPath(String)} method can be used for the inverse
+ * operation, i.e. locating an element based on the return value from this
+ * method.
+ * <p>
+ * Note that getElementByPath(getPathForElement(element)) == element is not
+ * always true as #getPathForElement(Element) can return a path to another
+ * element if the widget determines an action on the other element will give
+ * the same result as the action on the target element.
+ * </p>
+ *
+ * @param targetElement
+ * The element to generate a path for.
+ * @return A String locator that identifies the target element or null if a
+ * String locator could not be created.
+ */
+ String getPathForElement(Element targetElement);
+
+ /**
+ * Locates an element using a String locator (path) which identifies a DOM
+ * element. The {@link #getPathForElement(Element)} method can be used for
+ * the inverse operation, i.e. generating a string expression for a DOM
+ * element.
+ *
+ * @param path
+ * The String locator which identifies the target element.
+ * @return The DOM element identified by {@code path} or null if the element
+ * could not be located.
+ */
+ Element getElementByPath(String path);
+
+ /**
+ * Locates an element using a String locator (path) which identifies a DOM
+ * element. The path starts from the specified root element.
+ *
+ * @see #getElementByPath(String)
+ *
+ * @param path
+ * The String locator which identifies the target element.
+ * @param root
+ * The element that is at the root of the path.
+ * @return The DOM element identified by {@code path} or null if the element
+ * could not be located.
+ */
+ Element getElementByPathStartingAt(String path,
+ Element root);
+
+ /**
+ * Locates all elements that match a String locator (path) which identifies
+ * DOM elements.
+ *
+ * This functionality is limited in {@link LegacyLocatorStrategy}.
+ *
+ * @param path
+ * The String locator which identifies target elements.
+ * @return List that contains all matched elements. Empty list if none
+ * found.
+ */
+ List<Element> getElementsByPath(String path);
+
+ /**
+ * Locates all elements that match a String locator (path) which identifies
+ * DOM elements. The path starts from the specified root element.
+ *
+ * This functionality is limited in {@link LegacyLocatorStrategy}.
+ *
+ * @see #getElementsByPath(String)
+ *
+ * @param path
+ * The String locator which identifies target elements.
+ * @param root
+ * The element that is at the root of the path.
+ * @return List that contains all matched elements. Empty list if none
+ * found.
+ */
+
+ List<Element> getElementsByPathStartingAt(
+ String path, Element root);
+}
diff --git a/client/src/com/vaadin/client/componentlocator/LocatorUtil.java b/client/src/com/vaadin/client/componentlocator/LocatorUtil.java
new file mode 100644
index 0000000000..04624920a9
--- /dev/null
+++ b/client/src/com/vaadin/client/componentlocator/LocatorUtil.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.componentlocator;
+
+/**
+ * Common String manipulator utilities used in VaadinFinderLocatorStrategy and
+ * SelectorPredicates.
+ *
+ * @since 7.2
+ * @author Vaadin Ltd
+ */
+public class LocatorUtil {
+
+ /**
+ * Find first occurrence of character that's not inside quotes starting from
+ * specified index.
+ *
+ * @param str
+ * Full string for searching
+ * @param find
+ * Character we want to find
+ * @param startingAt
+ * Index where we start
+ * @return Index of character. -1 if character not found
+ */
+ protected static int indexOfIgnoringQuoted(String str, char find,
+ int startingAt) {
+ boolean quote = false;
+ String quoteChars = "'\"";
+ char currentQuote = '"';
+ for (int i = startingAt; i < str.length(); ++i) {
+ char cur = str.charAt(i);
+ if (quote) {
+ if (cur == currentQuote) {
+ quote = !quote;
+ }
+ continue;
+ } else if (cur == find) {
+ return i;
+ } else {
+ if (quoteChars.indexOf(cur) >= 0) {
+ currentQuote = cur;
+ quote = !quote;
+ }
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Find first occurrence of character that's not inside quotes starting from
+ * the beginning of string.
+ *
+ * @param str
+ * Full string for searching
+ * @param find
+ * Character we want to find
+ * @return Index of character. -1 if character not found
+ */
+ protected static int indexOfIgnoringQuoted(String str, char find) {
+ return indexOfIgnoringQuoted(str, find, 0);
+ }
+}
diff --git a/client/src/com/vaadin/client/componentlocator/SelectorPredicate.java b/client/src/com/vaadin/client/componentlocator/SelectorPredicate.java
new file mode 100644
index 0000000000..32b33005ed
--- /dev/null
+++ b/client/src/com/vaadin/client/componentlocator/SelectorPredicate.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.componentlocator;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * SelectorPredicates are statements about the state of different components
+ * that VaadinFinderLocatorStrategy is finding. SelectorPredicates also provide
+ * useful information of said components to debug window by giving means to
+ * provide better variable naming.
+ *
+ * @since 7.2
+ * @author Vaadin Ltd
+ */
+public class SelectorPredicate {
+ private String name = "";
+ private String value = "";
+ private boolean wildcard = false;
+ private int index = -1;
+
+ public static List<SelectorPredicate> extractPostFilterPredicates(
+ String path) {
+ if (path.startsWith("(")) {
+ return extractPredicates(path.substring(path.lastIndexOf(')')));
+ }
+ return new ArrayList<SelectorPredicate>();
+ }
+
+ /**
+ * Generate a list of predicates from a single predicate string
+ *
+ * @param str
+ * a comma separated string of predicates
+ * @return a List of Predicate objects
+ */
+ public static List<SelectorPredicate> extractPredicates(String path) {
+ List<SelectorPredicate> predicates = new ArrayList<SelectorPredicate>();
+
+ String predicateStr = extractPredicateString(path);
+ if (null == predicateStr || predicateStr.length() == 0) {
+ return predicates;
+ }
+
+ // Extract input strings
+ List<String> input = readPredicatesFromString(predicateStr);
+
+ // Process each predicate into proper predicate descriptor
+ for (String s : input) {
+ SelectorPredicate p = new SelectorPredicate();
+ s = s.trim();
+
+ try {
+ // If we can parse out the predicate as a pure index argument,
+ // stop processing here.
+ p.index = Integer.parseInt(s);
+ predicates.add(p);
+
+ continue;
+ } catch (Exception e) {
+ p.index = -1;
+ }
+
+ int idx = LocatorUtil.indexOfIgnoringQuoted(s, '=');
+ if (idx < 0) {
+ continue;
+ }
+ p.name = s.substring(0, idx);
+ p.value = s.substring(idx + 1);
+
+ if (p.value.equals("?")) {
+ p.wildcard = true;
+ p.value = null;
+ } else {
+ // Only unquote predicate value once we're sure it's a proper
+ // value...
+
+ p.value = unquote(p.value);
+ }
+
+ predicates.add(p);
+ }
+ // Move any (and all) index predicates to last place in the list.
+ for (int i = 0, l = predicates.size(); i < l - 1; ++i) {
+ if (predicates.get(i).index > -1) {
+ predicates.add(predicates.remove(i));
+ --i;
+ --l;
+ }
+ }
+
+ return predicates;
+ }
+
+ /**
+ * Splits the predicate string to list of predicate strings.
+ *
+ * @param predicateStr
+ * Comma separated predicate strings
+ * @return List of predicate strings
+ */
+ private static List<String> readPredicatesFromString(String predicateStr) {
+ List<String> predicates = new ArrayList<String>();
+ int prevIdx = 0;
+ int idx = LocatorUtil.indexOfIgnoringQuoted(predicateStr, ',', prevIdx);
+
+ while (idx > -1) {
+ predicates.add(predicateStr.substring(prevIdx, idx));
+ prevIdx = idx + 1;
+ idx = LocatorUtil.indexOfIgnoringQuoted(predicateStr, ',', prevIdx);
+ }
+ predicates.add(predicateStr.substring(prevIdx));
+
+ return predicates;
+ }
+
+ /**
+ * Returns the predicate string, i.e. the string between the brackets in a
+ * path fragment. Examples: <code>
+ * VTextField[0] => 0
+ * VTextField[caption='foo'] => caption='foo'
+ * </code>
+ *
+ * @param pathFragment
+ * The path fragment from which to extract the predicate string.
+ * @return The predicate string for the path fragment or empty string if not
+ * found.
+ */
+ private static String extractPredicateString(String pathFragment) {
+ int ixOpenBracket = LocatorUtil
+ .indexOfIgnoringQuoted(pathFragment, '[');
+ if (ixOpenBracket >= 0) {
+ int ixCloseBracket = LocatorUtil.indexOfIgnoringQuoted(
+ pathFragment, ']', ixOpenBracket);
+ return pathFragment.substring(ixOpenBracket + 1, ixCloseBracket);
+ }
+ return "";
+ }
+
+ /**
+ * Removes the surrounding quotes from a string if it is quoted.
+ *
+ * @param str
+ * the possibly quoted string
+ * @return an unquoted version of str
+ */
+ private static String unquote(String str) {
+ if ((str.startsWith("\"") && str.endsWith("\""))
+ || (str.startsWith("'") && str.endsWith("'"))) {
+ return str.substring(1, str.length() - 1);
+ }
+ return str;
+ }
+
+ /**
+ * @return the name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * @param name
+ * the name to set
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * @return the value
+ */
+ public String getValue() {
+ return value;
+ }
+
+ /**
+ * @param value
+ * the value to set
+ */
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ /**
+ * @return the index
+ */
+ public int getIndex() {
+ return index;
+ }
+
+ /**
+ * @param index
+ * the index to set
+ */
+ public void setIndex(int index) {
+ this.index = index;
+ }
+
+ /**
+ * @return the wildcard
+ */
+ public boolean isWildcard() {
+ return wildcard;
+ }
+
+ /**
+ * @param wildcard
+ * the wildcard to set
+ */
+ public void setWildcard(boolean wildcard) {
+ this.wildcard = wildcard;
+ }
+}
diff --git a/client/src/com/vaadin/client/componentlocator/VaadinFinderLocatorStrategy.java b/client/src/com/vaadin/client/componentlocator/VaadinFinderLocatorStrategy.java
new file mode 100644
index 0000000000..e7e752ef34
--- /dev/null
+++ b/client/src/com/vaadin/client/componentlocator/VaadinFinderLocatorStrategy.java
@@ -0,0 +1,748 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.componentlocator;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.user.client.ui.RootPanel;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.client.ApplicationConnection;
+import com.vaadin.client.ComponentConnector;
+import com.vaadin.client.HasComponentsConnector;
+import com.vaadin.client.Util;
+import com.vaadin.client.metadata.Property;
+import com.vaadin.client.metadata.TypeDataStore;
+import com.vaadin.client.ui.AbstractConnector;
+import com.vaadin.client.ui.SubPartAware;
+import com.vaadin.client.ui.VNotification;
+
+/**
+ * The VaadinFinder locator strategy implements an XPath-like syntax for
+ * locating elements in Vaadin applications. This is used in the new
+ * VaadinFinder API in TestBench 4.
+ *
+ * Examples of the supported syntax:
+ * <ul>
+ * <li>Find the third text field in the DOM: {@code //VTextField[2]}</li>
+ * <li>Find the second button inside the first vertical layout:
+ * {@code //VVerticalLayout/VButton[1]}</li>
+ * <li>Find the first column on the third row of the "Accounts" table:
+ * {@code //VScrollTable[caption="Accounts"]#row[2]/col[0]}</li>
+ * </ul>
+ *
+ * @since 7.2
+ * @author Vaadin Ltd
+ */
+public class VaadinFinderLocatorStrategy implements LocatorStrategy {
+
+ public static final String SUBPART_SEPARATOR = "#";
+
+ private final ApplicationConnection client;
+
+ /**
+ * Internal descriptor for connector/element/widget name combinations
+ */
+ private static final class ConnectorPath {
+ private String name;
+ private ComponentConnector connector;
+ }
+
+ public VaadinFinderLocatorStrategy(ApplicationConnection clientConnection) {
+ client = clientConnection;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getPathForElement(Element targetElement) {
+ if (targetElement == null) {
+ return "";
+ }
+
+ List<ConnectorPath> hierarchy = getConnectorHierarchyForElement(targetElement);
+ List<String> path = new ArrayList<String>();
+
+ // Assemble longname path components back-to-forth with useful
+ // predicates - first try ID, then caption.
+ for (int i = 0; i < hierarchy.size(); ++i) {
+ ConnectorPath cp = hierarchy.get(i);
+ String pathFragment = cp.name;
+ String identifier = getPropertyValue(cp.connector, "id");
+
+ if (identifier != null) {
+ pathFragment += "[id=\"" + identifier + "\"]";
+ } else {
+ identifier = getPropertyValue(cp.connector, "caption");
+ if (identifier != null) {
+ pathFragment += "[caption=\"" + identifier + "\"]";
+ }
+ }
+ path.add(pathFragment);
+ }
+
+ if (path.size() == 0) {
+ // If we didn't find a single element, return null..
+ return null;
+ }
+
+ return getBestSelector(generateQueries(path), targetElement);
+ }
+
+ /**
+ * Search different queries for the best one. Use the fact that the lowest
+ * possible index is with the last selector. Last selector is the full
+ * search path containing the complete Component hierarchy.
+ *
+ * @param selectors
+ * List of selectors
+ * @param target
+ * Target element
+ * @return Best selector string formatted with a post filter
+ */
+ private String getBestSelector(List<String> selectors, Element target) {
+ // The last selector gives us smallest list index for target element.
+ String bestSelector = selectors.get(selectors.size() - 1);
+ int min = getElementsByPath(bestSelector).indexOf(target);
+ if (selectors.size() > 1
+ && min == getElementsByPath(selectors.get(0)).indexOf(target)) {
+ // The first selector has same index as last. It's much shorter.
+ bestSelector = selectors.get(0);
+ } else if (selectors.size() > 2) {
+ // See if we get minimum from second last. If not then we already
+ // have the best one.. Second last one contains almost full
+ // component hierarchy.
+ if (getElementsByPath(selectors.get(selectors.size() - 2)).indexOf(
+ target) == min) {
+ for (int i = 1; i < selectors.size() - 2; ++i) {
+ // Loop through the remaining selectors and look for one
+ // with the same index
+ if (getElementsByPath(selectors.get(i)).indexOf(target) == min) {
+ bestSelector = selectors.get(i);
+ break;
+ }
+ }
+
+ }
+ }
+ return "(" + bestSelector + ")[" + min + "]";
+
+ }
+
+ /**
+ * Function to generate all possible search paths for given component list.
+ * Function strips out all the com.vaadin.ui. prefixes from elements as this
+ * functionality makes generating a query later on easier.
+ *
+ * @param components
+ * List of components
+ * @return List of Vaadin selectors
+ */
+ private List<String> generateQueries(List<String> components) {
+ // Prepare to loop through all the elements.
+ List<String> paths = new ArrayList<String>();
+ int compIdx = 0;
+ String basePath = components.get(compIdx).replace("com.vaadin.ui.", "");
+ // Add a basic search for the first element (eg. //Button)
+ paths.add((components.size() == 1 ? "/" : "//") + basePath);
+ while (++compIdx < components.size()) {
+ // Loop through the remaining components
+ for (int i = components.size() - 1; i >= compIdx; --i) {
+ boolean recursive = false;
+ if (i > compIdx) {
+ recursive = true;
+ }
+ paths.add((i == components.size() - 1 ? "/" : "//")
+ + components.get(i).replace("com.vaadin.ui.", "")
+ + (recursive ? "//" : "/") + basePath);
+ }
+ // Add the element at index compIdx to the basePath so it is
+ // included in all the following searches.
+ basePath = components.get(compIdx).replace("com.vaadin.ui.", "")
+ + "/" + basePath;
+ }
+
+ return paths;
+ }
+
+ /**
+ * Helper method to get the string-form value of a named property of a
+ * component connector
+ *
+ * @since 7.2
+ * @param c
+ * any ComponentConnector instance
+ * @param propertyName
+ * property name to test for
+ * @return a string, if the property is found, or null, if the property does
+ * not exist on the object (or some other error is encountered).
+ */
+ private String getPropertyValue(ComponentConnector c, String propertyName) {
+ Property prop = AbstractConnector.getStateType(c).getProperty(
+ propertyName);
+ try {
+ return prop.getValue(c.getState()).toString();
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ /**
+ * Generate a list representing the top-to-bottom connector hierarchy for
+ * any given element. ConnectorPath element provides long- and short names,
+ * as well as connector and widget root element references.
+ *
+ * @since 7.2
+ * @param elem
+ * any Element that is part of a widget hierarchy
+ * @return a list of ConnectorPath objects, in descending order towards the
+ * common root container.
+ */
+ private List<ConnectorPath> getConnectorHierarchyForElement(Element elem) {
+ Element e = elem;
+ ComponentConnector c = Util.findPaintable(client, e);
+ List<ConnectorPath> connectorHierarchy = new ArrayList<ConnectorPath>();
+
+ while (c != null) {
+
+ for (String id : getIDsForConnector(c)) {
+ ConnectorPath cp = new ConnectorPath();
+ cp.name = getFullClassName(id);
+ cp.connector = c;
+
+ // We want to make an exception for the UI object, since it's
+ // our default search context (and can't be found inside itself)
+ if (!cp.name.equals("com.vaadin.ui.UI")) {
+ connectorHierarchy.add(cp);
+ }
+ }
+
+ e = e.getParentElement();
+ if (e != null) {
+ c = Util.findPaintable(client, e);
+ e = c != null ? c.getWidget().getElement() : null;
+ }
+
+ }
+
+ return connectorHierarchy;
+ }
+
+ private boolean isNotificationExpression(String path) {
+ String[] starts = { "//", "/" };
+
+ String[] frags = { "com.vaadin.ui.Notification.class",
+ "com.vaadin.ui.Notification", "VNotification.class",
+ "VNotification", "Notification.class", "Notification" };
+
+ String[] ends = { "/", "[" };
+
+ for (String s : starts) {
+ for (String f : frags) {
+ if (path.equals(s + f)) {
+ return true;
+ }
+
+ for (String e : ends) {
+ if (path.startsWith(s + f + e)) {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public List<Element> getElementsByPath(String path) {
+ List<SelectorPredicate> postFilters = SelectorPredicate
+ .extractPostFilterPredicates(path);
+ if (postFilters.size() > 0) {
+ path = path.substring(1, path.lastIndexOf(')'));
+ }
+
+ List<Element> elements = new ArrayList<Element>();
+ if (isNotificationExpression(path)) {
+
+ for (VNotification n : findNotificationsByPath(path)) {
+ elements.add(n.getElement());
+ }
+
+ } else {
+
+ elements.addAll(eliminateDuplicates(getElementsByPathStartingAtConnector(
+ path, client.getUIConnector())));
+ }
+
+ for (SelectorPredicate p : postFilters) {
+ // Post filtering supports only indexes and follows instruction
+ // blindly. Index that is outside of our list results into an empty
+ // list and multiple indexes are likely to ruin a search completely
+ if (p.getIndex() >= 0) {
+ if (p.getIndex() >= elements.size()) {
+ elements.clear();
+ } else {
+ Element e = elements.get(p.getIndex());
+ elements.clear();
+ elements.add(e);
+ }
+ }
+ }
+
+ return elements;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Element getElementByPath(String path) {
+ List<Element> elements = getElementsByPath(path);
+ if (elements.isEmpty()) {
+ return null;
+ }
+ return elements.get(0);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Element getElementByPathStartingAt(String path, Element root) {
+ List<Element> elements = getElementsByPathStartingAt(path, root);
+ if (elements.isEmpty()) {
+ return null;
+ }
+ return elements.get(0);
+
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public List<Element> getElementsByPathStartingAt(String path, Element root) {
+ List<SelectorPredicate> postFilters = SelectorPredicate
+ .extractPostFilterPredicates(path);
+ if (postFilters.size() > 0) {
+ path = path.substring(1, path.lastIndexOf(')'));
+ }
+
+ List<Element> elements = getElementsByPathStartingAtConnector(path,
+ Util.findPaintable(client, root));
+
+ for (SelectorPredicate p : postFilters) {
+ // Post filtering supports only indexes and follows instruction
+ // blindly. Index that is outside of our list results into an empty
+ // list and multiple indexes are likely to ruin a search completely
+ if (p.getIndex() >= 0) {
+ if (p.getIndex() >= elements.size()) {
+ elements.clear();
+ } else {
+ Element e = elements.get(p.getIndex());
+ elements.clear();
+ elements.add(e);
+ }
+ }
+ }
+
+ return elements;
+ }
+
+ /**
+ * Special case for finding notifications as they have no connectors and are
+ * directly attached to {@link RootPanel}.
+ *
+ * @param path
+ * The path of the notification, should be
+ * {@code "//VNotification"} optionally followed by an index in
+ * brackets.
+ * @return the notification element or null if not found.
+ */
+ private List<VNotification> findNotificationsByPath(String path) {
+
+ List<VNotification> notifications = new ArrayList<VNotification>();
+ for (Widget w : RootPanel.get()) {
+ if (w instanceof VNotification) {
+ notifications.add((VNotification) w);
+ }
+ }
+
+ List<SelectorPredicate> predicates = SelectorPredicate
+ .extractPredicates(path);
+ for (SelectorPredicate p : predicates) {
+
+ if (p.getIndex() > -1) {
+ VNotification n = notifications.get(p.getIndex());
+ notifications.clear();
+ if (n != null) {
+ notifications.add(n);
+ }
+ }
+
+ }
+
+ return eliminateDuplicates(notifications);
+ }
+
+ /**
+ * Finds a list of elements by the specified path, starting traversal of the
+ * connector hierarchy from the specified root.
+ *
+ * @param path
+ * the locator path
+ * @param root
+ * the root connector
+ * @return the list of elements identified by path or empty list if not
+ * found.
+ */
+ private List<Element> getElementsByPathStartingAtConnector(String path,
+ ComponentConnector root) {
+ String[] pathComponents = path.split(SUBPART_SEPARATOR);
+ List<ComponentConnector> connectors;
+ if (pathComponents[0].length() > 0) {
+ connectors = findConnectorsByPath(pathComponents[0],
+ Arrays.asList(root));
+ } else {
+ connectors = Arrays.asList(root);
+ }
+
+ List<Element> output = new ArrayList<Element>();
+ if (null != connectors && !connectors.isEmpty()) {
+ if (pathComponents.length > 1) {
+ // We have subparts
+ for (ComponentConnector connector : connectors) {
+ if (connector.getWidget() instanceof SubPartAware) {
+ output.add(((SubPartAware) connector.getWidget())
+ .getSubPartElement(pathComponents[1]));
+ }
+ }
+ } else {
+ for (ComponentConnector connector : connectors) {
+ output.add(connector.getWidget().getElement());
+ }
+ }
+ }
+ return eliminateDuplicates(output);
+ }
+
+ /**
+ * Recursively finds connectors for the elements identified by the provided
+ * path by traversing the connector hierarchy starting from {@code parents}
+ * connectors.
+ *
+ * @param path
+ * The path identifying elements.
+ * @param parents
+ * The list of connectors to start traversing from.
+ * @return The list of connectors identified by {@code path} or empty list
+ * if no such connectors could be found.
+ */
+ private List<ComponentConnector> findConnectorsByPath(String path,
+ List<ComponentConnector> parents) {
+ boolean findRecursively = path.startsWith("//");
+ // Strip away the one or two slashes from the beginning of the path
+ path = path.substring(findRecursively ? 2 : 1);
+
+ String[] fragments = splitFirstFragmentFromTheRest(path);
+
+ List<ComponentConnector> connectors = new ArrayList<ComponentConnector>();
+ for (ComponentConnector parent : parents) {
+ connectors.addAll(filterMatches(
+ collectPotentialMatches(parent, fragments[0],
+ findRecursively), SelectorPredicate
+ .extractPredicates(fragments[0])));
+ }
+
+ if (!connectors.isEmpty() && fragments.length > 1) {
+ return (findConnectorsByPath(fragments[1], connectors));
+ }
+ return eliminateDuplicates(connectors);
+ }
+
+ /**
+ * Go through a list of potentially matching components, modifying that list
+ * until all elements that remain in that list match the complete list of
+ * predicates.
+ *
+ * @param potentialMatches
+ * a list of component connectors. Will be changed.
+ * @param predicates
+ * an immutable list of predicates
+ * @return filtered list of component connectors.
+ */
+ private List<ComponentConnector> filterMatches(
+ List<ComponentConnector> potentialMatches,
+ List<SelectorPredicate> predicates) {
+
+ for (SelectorPredicate p : predicates) {
+
+ if (p.getIndex() > -1) {
+ try {
+ ComponentConnector v = potentialMatches.get(p.getIndex());
+ potentialMatches.clear();
+ potentialMatches.add(v);
+ } catch (IndexOutOfBoundsException e) {
+ potentialMatches.clear();
+ }
+
+ continue;
+ }
+
+ for (int i = 0, l = potentialMatches.size(); i < l; ++i) {
+
+ String propData = getPropertyValue(potentialMatches.get(i),
+ p.getName());
+
+ if ((p.isWildcard() && propData == null)
+ || (!p.isWildcard() && !p.getValue().equals(propData))) {
+ potentialMatches.remove(i);
+ --l;
+ --i;
+ }
+ }
+
+ }
+
+ return eliminateDuplicates(potentialMatches);
+ }
+
+ /**
+ * Collects all connectors that match the widget class name of the path
+ * fragment. If the {@code collectRecursively} parameter is true, a
+ * depth-first search of the connector hierarchy is performed.
+ *
+ * Searching depth-first ensure that we can return the matches in correct
+ * order for selecting based on index predicates.
+ *
+ * @param parent
+ * The {@link ComponentConnector} to start the search from.
+ * @param pathFragment
+ * The path fragment identifying which type of widget to search
+ * for.
+ * @param collectRecursively
+ * If true, all matches from all levels below {@code parent} will
+ * be collected. If false only direct children will be collected.
+ * @return A list of {@link ComponentConnector}s matching the widget type
+ * specified in the {@code pathFragment}.
+ */
+ private List<ComponentConnector> collectPotentialMatches(
+ ComponentConnector parent, String pathFragment,
+ boolean collectRecursively) {
+ ArrayList<ComponentConnector> potentialMatches = new ArrayList<ComponentConnector>();
+ if (parent instanceof HasComponentsConnector) {
+ List<ComponentConnector> children = ((HasComponentsConnector) parent)
+ .getChildComponents();
+ for (ComponentConnector child : children) {
+ String widgetName = getWidgetName(pathFragment);
+ if (connectorMatchesPathFragment(child, widgetName)) {
+ potentialMatches.add(child);
+ }
+ if (collectRecursively) {
+ potentialMatches.addAll(collectPotentialMatches(child,
+ pathFragment, collectRecursively));
+ }
+ }
+ }
+ return eliminateDuplicates(potentialMatches);
+ }
+
+ private List<String> getIDsForConnector(ComponentConnector connector) {
+ Class<?> connectorClass = connector.getClass();
+ List<String> ids = new ArrayList<String>();
+
+ TypeDataStore.get().findIdentifiersFor(connectorClass).addAllTo(ids);
+
+ return ids;
+ }
+
+ /**
+ * Determines whether a connector matches a path fragment. This is done by
+ * comparing the path fragment to the name of the widget type of the
+ * connector.
+ *
+ * @param connector
+ * The connector to compare.
+ * @param widgetName
+ * The name of the widget class.
+ * @return true if the widget type of the connector equals the widget type
+ * identified by the path fragment.
+ */
+ private boolean connectorMatchesPathFragment(ComponentConnector connector,
+ String widgetName) {
+
+ List<String> ids = getIDsForConnector(connector);
+
+ Integer[] widgetTags = client.getConfiguration()
+ .getTagsForServerSideClassName(getFullClassName(widgetName));
+ if (widgetTags.length == 0) {
+ widgetTags = client.getConfiguration()
+ .getTagsForServerSideClassName(
+ getFullClassName("com.vaadin.ui." + widgetName));
+ }
+
+ for (int i = 0, l = ids.size(); i < l; ++i) {
+
+ // Fuzz the connector name, so that the client can provide (for
+ // example: /Button, /Button.class, /com.vaadin.ui.Button,
+ // /com.vaadin.ui.Button.class, etc)
+
+ String name = ids.get(i);
+ final String simpleName = getSimpleClassName(name);
+ final String fullName = getFullClassName(name);
+
+ if (widgetTags.length > 0) {
+ Integer[] foundTags = client.getConfiguration()
+ .getTagsForServerSideClassName(fullName);
+ for (int tag : foundTags) {
+ if (tagsMatch(widgetTags, tag)) {
+ return true;
+ }
+ }
+ }
+
+ // Fallback if something failed before.
+ if (widgetName.equals(fullName + ".class")
+ || widgetName.equals(fullName)
+ || widgetName.equals(simpleName + ".class")
+ || widgetName.equals(simpleName) || widgetName.equals(name)) {
+ return true;
+ }
+ }
+
+ // If the server-side class name didn't match, fall back to testing for
+ // the explicit widget name
+ String widget = Util.getSimpleName(connector.getWidget());
+ return widgetName.equals(widget)
+ || widgetName.equals(widget + ".class");
+
+ }
+
+ /**
+ * Extracts the name of the widget class from a path fragment
+ *
+ * @param pathFragment
+ * the path fragment
+ * @return the name of the widget class.
+ */
+ private String getWidgetName(String pathFragment) {
+ String widgetName = pathFragment;
+ int ixBracket = pathFragment.indexOf('[');
+ if (ixBracket >= 0) {
+ widgetName = pathFragment.substring(0, ixBracket);
+ }
+ return widgetName;
+ }
+
+ /**
+ * Splits off the first path fragment from a path and returns an array of
+ * two elements, where the first element is the first path fragment and the
+ * second element is the rest of the path (all remaining path fragments
+ * untouched).
+ *
+ * @param path
+ * The path to split.
+ * @return An array of two elements: The first path fragment and the rest of
+ * the path.
+ */
+ private String[] splitFirstFragmentFromTheRest(String path) {
+ int ixOfSlash = LocatorUtil.indexOfIgnoringQuoted(path, '/');
+ if (ixOfSlash > 0) {
+ return new String[] { path.substring(0, ixOfSlash),
+ path.substring(ixOfSlash) };
+ }
+ return new String[] { path };
+ }
+
+ private String getSimpleClassName(String s) {
+ String[] parts = s.split("\\.");
+ if (s.endsWith(".class")) {
+ return parts[parts.length - 2];
+ }
+ return parts.length > 0 ? parts[parts.length - 1] : s;
+ }
+
+ private String getFullClassName(String s) {
+ if (s.endsWith(".class")) {
+ return s.substring(0, s.lastIndexOf(".class"));
+ }
+ return s;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.vaadin.client.componentlocator.LocatorStrategy#validatePath(java.
+ * lang.String)
+ */
+ @Override
+ public boolean validatePath(String path) {
+ // This syntax is so difficult to regexp properly, that we'll just try
+ // to find something with it regardless of the correctness of the
+ // syntax...
+ return true;
+ }
+
+ /**
+ * Go through a list, removing all duplicate elements from it. This method
+ * is used to avoid accumulation of duplicate entries in result lists
+ * resulting from low-context recursion.
+ *
+ * Preserves first entry in list, removes others. Preserves list order.
+ *
+ * @return list passed as parameter, after modification
+ */
+ private final <T> List<T> eliminateDuplicates(List<T> list) {
+
+ int l = list.size();
+ for (int j = 0; j < l; ++j) {
+ T ref = list.get(j);
+
+ for (int i = j + 1; i < l; ++i) {
+ if (list.get(i) == ref) {
+ list.remove(i);
+ --i;
+ --l;
+ }
+ }
+ }
+
+ return list;
+ }
+
+ private boolean tagsMatch(Integer[] targets, Integer tag) {
+ for (int i = 0; i < targets.length; ++i) {
+ if (targets[i].equals(tag)) {
+ return true;
+ }
+ }
+
+ try {
+ return tagsMatch(targets,
+ client.getConfiguration().getParentTag(tag));
+ } catch (Exception e) {
+ return false;
+ }
+ }
+}
diff --git a/client/src/com/vaadin/client/debug/internal/AnalyzeLayoutsPanel.java b/client/src/com/vaadin/client/debug/internal/AnalyzeLayoutsPanel.java
new file mode 100644
index 0000000000..7561bc2c03
--- /dev/null
+++ b/client/src/com/vaadin/client/debug/internal/AnalyzeLayoutsPanel.java
@@ -0,0 +1,267 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.debug.internal;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import com.google.gwt.core.client.JsArray;
+import com.google.gwt.dom.client.Style.TextDecoration;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.event.dom.client.MouseOutEvent;
+import com.google.gwt.event.dom.client.MouseOutHandler;
+import com.google.gwt.event.dom.client.MouseOverEvent;
+import com.google.gwt.event.dom.client.MouseOverHandler;
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.HTML;
+import com.google.gwt.user.client.ui.Label;
+import com.google.gwt.user.client.ui.VerticalPanel;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.client.ApplicationConfiguration;
+import com.vaadin.client.ApplicationConnection;
+import com.vaadin.client.ComponentConnector;
+import com.vaadin.client.ComputedStyle;
+import com.vaadin.client.ConnectorMap;
+import com.vaadin.client.ServerConnector;
+import com.vaadin.client.SimpleTree;
+import com.vaadin.client.Util;
+import com.vaadin.client.ValueMap;
+
+/**
+ * Analyze layouts view panel of the debug window.
+ *
+ * @since 7.1.4
+ */
+public class AnalyzeLayoutsPanel extends FlowPanel {
+
+ private List<SelectConnectorListener> listeners = new ArrayList<SelectConnectorListener>();
+
+ public void update() {
+ clear();
+ add(new Label("Analyzing layouts..."));
+ List<ApplicationConnection> runningApplications = ApplicationConfiguration
+ .getRunningApplications();
+ for (ApplicationConnection applicationConnection : runningApplications) {
+ applicationConnection.analyzeLayouts();
+ }
+ }
+
+ public void meta(ApplicationConnection ac, ValueMap meta) {
+ clear();
+ JsArray<ValueMap> valueMapArray = meta
+ .getJSValueMapArray("invalidLayouts");
+ int size = valueMapArray.length();
+
+ if (size > 0) {
+ SimpleTree root = new SimpleTree("Layouts analyzed, " + size
+ + " top level problems");
+ for (int i = 0; i < size; i++) {
+ printLayoutError(ac, valueMapArray.get(i), root);
+ }
+ root.open(false);
+ add(root);
+ } else {
+ add(new Label("Layouts analyzed, no top level problems"));
+ }
+
+ Set<ComponentConnector> zeroHeightComponents = new HashSet<ComponentConnector>();
+ Set<ComponentConnector> zeroWidthComponents = new HashSet<ComponentConnector>();
+ findZeroSizeComponents(zeroHeightComponents, zeroWidthComponents,
+ ac.getUIConnector());
+ if (zeroHeightComponents.size() > 0 || zeroWidthComponents.size() > 0) {
+ add(new HTML("<h4> Client side notifications</h4>"
+ + " <em>The following relative sized components were "
+ + "rendered to a zero size container on the client side."
+ + " Note that these are not necessarily invalid "
+ + "states, but reported here as they might be.</em>"));
+ if (zeroHeightComponents.size() > 0) {
+ add(new HTML("<p><strong>Vertically zero size:</strong></p>"));
+ printClientSideDetectedIssues(zeroHeightComponents, ac);
+ }
+ if (zeroWidthComponents.size() > 0) {
+ add(new HTML("<p><strong>Horizontally zero size:</strong></p>"));
+ printClientSideDetectedIssues(zeroWidthComponents, ac);
+ }
+ }
+
+ }
+
+ private void printClientSideDetectedIssues(
+ Set<ComponentConnector> zeroSized, ApplicationConnection ac) {
+
+ // keep track of already highlighted parents
+ HashSet<String> parents = new HashSet<String>();
+
+ for (final ComponentConnector connector : zeroSized) {
+ final ServerConnector parent = connector.getParent();
+ final String parentId = parent.getConnectorId();
+
+ final Label errorDetails = new Label(Util.getSimpleName(connector)
+ + "[" + connector.getConnectorId() + "]" + " inside "
+ + Util.getSimpleName(parent));
+
+ if (parent instanceof ComponentConnector) {
+ final ComponentConnector parentConnector = (ComponentConnector) parent;
+ if (!parents.contains(parentId)) {
+ parents.add(parentId);
+ Highlight.show(parentConnector, "yellow");
+ }
+
+ errorDetails.addMouseOverHandler(new MouseOverHandler() {
+ @Override
+ public void onMouseOver(MouseOverEvent event) {
+ Highlight.hideAll();
+ Highlight.show(parentConnector, "yellow");
+ Highlight.show(connector);
+ errorDetails.getElement().getStyle()
+ .setTextDecoration(TextDecoration.UNDERLINE);
+ }
+ });
+ errorDetails.addMouseOutHandler(new MouseOutHandler() {
+ @Override
+ public void onMouseOut(MouseOutEvent event) {
+ Highlight.hideAll();
+ errorDetails.getElement().getStyle()
+ .setTextDecoration(TextDecoration.NONE);
+ }
+ });
+ errorDetails.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(ClickEvent event) {
+ fireSelectEvent(connector);
+ }
+ });
+
+ }
+
+ Highlight.show(connector);
+ add(errorDetails);
+
+ }
+ }
+
+ private void printLayoutError(ApplicationConnection ac, ValueMap valueMap,
+ SimpleTree root) {
+ final String pid = valueMap.getString("id");
+
+ // find connector
+ final ComponentConnector connector = (ComponentConnector) ConnectorMap
+ .get(ac).getConnector(pid);
+
+ if (connector == null) {
+ root.add(new SimpleTree("[" + pid + "] NOT FOUND"));
+ return;
+ }
+
+ Highlight.show(connector);
+
+ final SimpleTree errorNode = new SimpleTree(
+ Util.getSimpleName(connector) + " id: " + pid);
+ errorNode.addDomHandler(new MouseOverHandler() {
+ @Override
+ public void onMouseOver(MouseOverEvent event) {
+ Highlight.showOnly(connector);
+ ((Widget) event.getSource()).getElement().getStyle()
+ .setTextDecoration(TextDecoration.UNDERLINE);
+ }
+ }, MouseOverEvent.getType());
+ errorNode.addDomHandler(new MouseOutHandler() {
+ @Override
+ public void onMouseOut(MouseOutEvent event) {
+ Highlight.hideAll();
+ ((Widget) event.getSource()).getElement().getStyle()
+ .setTextDecoration(TextDecoration.NONE);
+ }
+ }, MouseOutEvent.getType());
+
+ errorNode.addDomHandler(new ClickHandler() {
+ @Override
+ public void onClick(ClickEvent event) {
+ if (event.getNativeEvent().getEventTarget().cast() == errorNode
+ .getElement().getChild(1).cast()) {
+ fireSelectEvent(connector);
+ }
+ }
+ }, ClickEvent.getType());
+
+ VerticalPanel errorDetails = new VerticalPanel();
+
+ if (valueMap.containsKey("heightMsg")) {
+ errorDetails.add(new Label("Height problem: "
+ + valueMap.getString("heightMsg")));
+ }
+ if (valueMap.containsKey("widthMsg")) {
+ errorDetails.add(new Label("Width problem: "
+ + valueMap.getString("widthMsg")));
+ }
+ if (errorDetails.getWidgetCount() > 0) {
+ errorNode.add(errorDetails);
+ }
+ if (valueMap.containsKey("subErrors")) {
+ HTML l = new HTML(
+ "<em>Expand this node to show problems that may be dependent on this problem.</em>");
+ errorDetails.add(l);
+ JsArray<ValueMap> suberrors = valueMap
+ .getJSValueMapArray("subErrors");
+ for (int i = 0; i < suberrors.length(); i++) {
+ ValueMap value = suberrors.get(i);
+ printLayoutError(ac, value, errorNode);
+ }
+
+ }
+ root.add(errorNode);
+ }
+
+ private void findZeroSizeComponents(
+ Set<ComponentConnector> zeroHeightComponents,
+ Set<ComponentConnector> zeroWidthComponents,
+ ComponentConnector connector) {
+ Widget widget = connector.getWidget();
+ ComputedStyle computedStyle = new ComputedStyle(widget.getElement());
+ if (computedStyle.getIntProperty("height") == 0) {
+ zeroHeightComponents.add(connector);
+ }
+ if (computedStyle.getIntProperty("width") == 0) {
+ zeroWidthComponents.add(connector);
+ }
+ List<ServerConnector> children = connector.getChildren();
+ for (ServerConnector serverConnector : children) {
+ if (serverConnector instanceof ComponentConnector) {
+ findZeroSizeComponents(zeroHeightComponents,
+ zeroWidthComponents,
+ (ComponentConnector) serverConnector);
+ }
+ }
+ }
+
+ public void addListener(SelectConnectorListener listener) {
+ listeners.add(listener);
+ }
+
+ public void removeListener(SelectConnectorListener listener) {
+ listeners.remove(listener);
+ }
+
+ private void fireSelectEvent(ServerConnector connector) {
+ for (SelectConnectorListener listener : listeners) {
+ listener.select(connector, null);
+ }
+ }
+
+}
diff --git a/client/src/com/vaadin/client/debug/internal/ConnectorInfoPanel.java b/client/src/com/vaadin/client/debug/internal/ConnectorInfoPanel.java
new file mode 100644
index 0000000000..fc7b55497e
--- /dev/null
+++ b/client/src/com/vaadin/client/debug/internal/ConnectorInfoPanel.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.debug.internal;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.HTML;
+import com.vaadin.client.ComponentConnector;
+import com.vaadin.client.JsArrayObject;
+import com.vaadin.client.ServerConnector;
+import com.vaadin.client.Util;
+import com.vaadin.client.VConsole;
+import com.vaadin.client.metadata.NoDataException;
+import com.vaadin.client.metadata.Property;
+import com.vaadin.client.ui.AbstractConnector;
+import com.vaadin.shared.AbstractComponentState;
+import com.vaadin.shared.communication.SharedState;
+
+/**
+ * Connector information view panel of the debug window.
+ *
+ * @since 7.1.4
+ */
+public class ConnectorInfoPanel extends FlowPanel {
+
+ /**
+ * Update the panel to show information about a connector.
+ *
+ * @param connector
+ */
+ public void update(ServerConnector connector) {
+ SharedState state = connector.getState();
+
+ Set<String> ignoreProperties = new HashSet<String>();
+ ignoreProperties.add("id");
+
+ String html = getRowHTML("Id", connector.getConnectorId());
+ html += getRowHTML("Connector", Util.getSimpleName(connector));
+
+ if (connector instanceof ComponentConnector) {
+ ComponentConnector component = (ComponentConnector) connector;
+
+ ignoreProperties.addAll(Arrays.asList("caption", "description",
+ "width", "height"));
+
+ AbstractComponentState componentState = component.getState();
+
+ html += getRowHTML("Widget",
+ Util.getSimpleName(component.getWidget()));
+ html += getRowHTML("Caption", componentState.caption);
+ html += getRowHTML("Description", componentState.description);
+ html += getRowHTML("Width", componentState.width + " (actual: "
+ + component.getWidget().getOffsetWidth() + "px)");
+ html += getRowHTML("Height", componentState.height + " (actual: "
+ + component.getWidget().getOffsetHeight() + "px)");
+ }
+
+ try {
+ JsArrayObject<Property> properties = AbstractConnector
+ .getStateType(connector).getPropertiesAsArray();
+ for (int i = 0; i < properties.size(); i++) {
+ Property property = properties.get(i);
+ String name = property.getName();
+ if (!ignoreProperties.contains(name)) {
+ html += getRowHTML(property.getDisplayName(),
+ property.getValue(state));
+ }
+ }
+ } catch (NoDataException e) {
+ html += "<div>Could not read state, error has been logged to the console</div>";
+ VConsole.error(e);
+ }
+
+ clear();
+ add(new HTML(html));
+ }
+
+ private String getRowHTML(String caption, Object value) {
+ return "<div class=\"" + VDebugWindow.STYLENAME
+ + "-row\"><span class=\"caption\">" + caption
+ + "</span><span class=\"value\">"
+ + Util.escapeHTML(String.valueOf(value)) + "</span></div>";
+ }
+
+ /**
+ * Clear the contents of the panel.
+ */
+ public void clearContents() {
+ clear();
+ }
+}
diff --git a/client/src/com/vaadin/client/debug/internal/HierarchyPanel.java b/client/src/com/vaadin/client/debug/internal/HierarchyPanel.java
new file mode 100644
index 0000000000..755f076b7a
--- /dev/null
+++ b/client/src/com/vaadin/client/debug/internal/HierarchyPanel.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.debug.internal;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.event.dom.client.DoubleClickEvent;
+import com.google.gwt.event.dom.client.DoubleClickHandler;
+import com.google.gwt.event.dom.client.HasDoubleClickHandlers;
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.HasWidgets;
+import com.google.gwt.user.client.ui.Label;
+import com.google.gwt.user.client.ui.SimplePanel;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.client.ApplicationConfiguration;
+import com.vaadin.client.ApplicationConnection;
+import com.vaadin.client.FastStringSet;
+import com.vaadin.client.ServerConnector;
+import com.vaadin.client.SimpleTree;
+import com.vaadin.client.Util;
+
+/**
+ * Hierarchy view panel of the debug window. This class can be used in various
+ * debug window sections to show the current connector hierarchy.
+ *
+ * @since 7.1.4
+ */
+public class HierarchyPanel extends FlowPanel {
+
+ // TODO separate click listeners for simple selection and doubleclick
+ private List<SelectConnectorListener> listeners = new ArrayList<SelectConnectorListener>();
+
+ public void update() {
+ // Try to keep track of currently open nodes and reopen them
+ FastStringSet openNodes = FastStringSet.create();
+ Iterator<Widget> it = iterator();
+ while (it.hasNext()) {
+ collectOpenNodes(it.next(), openNodes);
+ }
+
+ clear();
+
+ SimplePanel trees = new SimplePanel();
+
+ for (ApplicationConnection application : ApplicationConfiguration
+ .getRunningApplications()) {
+ ServerConnector uiConnector = application.getUIConnector();
+ Widget connectorTree = buildConnectorTree(uiConnector, openNodes);
+
+ trees.add(connectorTree);
+ }
+
+ add(trees);
+ }
+
+ /**
+ * Adds the captions of all open (non-leaf) nodes in the hierarchy tree
+ * recursively.
+ *
+ * @param widget
+ * the widget in which to search for open nodes (if SimpleTree)
+ * @param openNodes
+ * the set in which open nodes should be added
+ */
+ private void collectOpenNodes(Widget widget, FastStringSet openNodes) {
+ if (widget instanceof SimpleTree) {
+ SimpleTree tree = (SimpleTree) widget;
+ if (tree.isOpen()) {
+ openNodes.add(tree.getCaption());
+ } else {
+ // no need to look inside closed nodes
+ return;
+ }
+ }
+ if (widget instanceof HasWidgets) {
+ Iterator<Widget> it = ((HasWidgets) widget).iterator();
+ while (it.hasNext()) {
+ collectOpenNodes(it.next(), openNodes);
+ }
+ }
+ }
+
+ private Widget buildConnectorTree(final ServerConnector connector,
+ FastStringSet openNodes) {
+ String connectorString = Util.getConnectorString(connector);
+
+ List<ServerConnector> children = connector.getChildren();
+
+ Widget widget;
+ if (children == null || children.isEmpty()) {
+ // Leaf node, just add a label
+ Label label = new Label(connectorString);
+ label.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(ClickEvent event) {
+ Highlight.showOnly(connector);
+ showServerDebugInfo(connector);
+ }
+ });
+ widget = label;
+ } else {
+ SimpleTree tree = new SimpleTree(connectorString) {
+ @Override
+ protected void select(ClickEvent event) {
+ super.select(event);
+ Highlight.showOnly(connector);
+ showServerDebugInfo(connector);
+ }
+ };
+ for (ServerConnector child : children) {
+ tree.add(buildConnectorTree(child, openNodes));
+ }
+ if (openNodes.contains(connectorString)) {
+ tree.open(false);
+ }
+ widget = tree;
+ }
+
+ if (widget instanceof HasDoubleClickHandlers) {
+ HasDoubleClickHandlers has = (HasDoubleClickHandlers) widget;
+ has.addDoubleClickHandler(new DoubleClickHandler() {
+ @Override
+ public void onDoubleClick(DoubleClickEvent event) {
+ fireSelectEvent(connector);
+ }
+ });
+ }
+
+ return widget;
+ }
+
+ public void addListener(SelectConnectorListener listener) {
+ listeners.add(listener);
+ }
+
+ public void removeListener(SelectConnectorListener listener) {
+ listeners.remove(listener);
+ }
+
+ private void fireSelectEvent(ServerConnector connector) {
+ for (SelectConnectorListener listener : listeners) {
+ listener.select(connector, null);
+ }
+ }
+
+ /**
+ * Outputs debug information on the server - usually in the console of an
+ * IDE, with a clickable reference to the relevant code location.
+ *
+ * @since 7.1
+ * @param connector
+ * show debug info for this connector
+ */
+ static void showServerDebugInfo(ServerConnector connector) {
+ if (connector != null) {
+ connector.getConnection().getUIConnector()
+ .showServerDebugInfo(connector);
+ }
+ }
+
+}
diff --git a/client/src/com/vaadin/client/debug/internal/HierarchySection.java b/client/src/com/vaadin/client/debug/internal/HierarchySection.java
index 90c9086d7d..1647a61256 100644
--- a/client/src/com/vaadin/client/debug/internal/HierarchySection.java
+++ b/client/src/com/vaadin/client/debug/internal/HierarchySection.java
@@ -15,53 +15,26 @@
*/
package com.vaadin.client.debug.internal;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.dom.client.Style.TextDecoration;
+import com.google.gwt.dom.client.Element;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.event.dom.client.DoubleClickEvent;
-import com.google.gwt.event.dom.client.DoubleClickHandler;
-import com.google.gwt.event.dom.client.HasDoubleClickHandlers;
import com.google.gwt.event.dom.client.KeyCodes;
-import com.google.gwt.event.dom.client.MouseOutEvent;
-import com.google.gwt.event.dom.client.MouseOutHandler;
-import com.google.gwt.event.dom.client.MouseOverEvent;
-import com.google.gwt.event.dom.client.MouseOverHandler;
import com.google.gwt.event.shared.HandlerRegistration;
-import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Event.NativePreviewEvent;
import com.google.gwt.user.client.Event.NativePreviewHandler;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HTML;
-import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.SimplePanel;
-import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.ApplicationConfiguration;
import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.ComponentConnector;
-import com.vaadin.client.ComputedStyle;
-import com.vaadin.client.ConnectorMap;
-import com.vaadin.client.JsArrayObject;
import com.vaadin.client.ServerConnector;
-import com.vaadin.client.SimpleTree;
import com.vaadin.client.Util;
-import com.vaadin.client.VConsole;
import com.vaadin.client.ValueMap;
-import com.vaadin.client.metadata.NoDataException;
-import com.vaadin.client.metadata.Property;
-import com.vaadin.client.ui.AbstractConnector;
-import com.vaadin.client.ui.UnknownComponentConnector;
-import com.vaadin.shared.AbstractComponentState;
-import com.vaadin.shared.communication.SharedState;
/**
* Provides functionality for examining the UI component hierarchy.
@@ -73,7 +46,15 @@ public class HierarchySection implements Section {
private final DebugButton tabButton = new DebugButton(Icon.HIERARCHY,
"Examine component hierarchy");
- private final FlowPanel content = new FlowPanel();
+ private final SimplePanel content = new SimplePanel();
+
+ // TODO highlighting logic is split between these, should be refactored
+ private final FlowPanel helpPanel = new FlowPanel();
+ private final ConnectorInfoPanel infoPanel = new ConnectorInfoPanel();
+ private final HierarchyPanel hierarchyPanel = new HierarchyPanel();
+ private final OptimizedWidgetsetPanel widgetsetPanel = new OptimizedWidgetsetPanel();
+ private final AnalyzeLayoutsPanel analyzeLayoutsPanel = new AnalyzeLayoutsPanel();
+
private final FlowPanel controls = new FlowPanel();
private final Button find = new DebugButton(Icon.HIGHLIGHT,
@@ -125,79 +106,42 @@ public class HierarchySection implements Section {
}
});
+ hierarchyPanel.addListener(new SelectConnectorListener() {
+ @Override
+ public void select(ServerConnector connector,
+ Element element) {
+ printState(connector, true);
+ }
+ });
+
+ analyzeLayoutsPanel.addListener(new SelectConnectorListener() {
+ @Override
+ public void select(ServerConnector connector,
+ Element element) {
+ printState(connector, true);
+ }
+ });
+
content.setStylePrimaryName(VDebugWindow.STYLENAME + "-hierarchy");
+ initializeHelpPanel();
+ content.setWidget(helpPanel);
+ }
+
+ private void initializeHelpPanel() {
HTML info = new HTML(showHierarchy.getHTML() + " "
+ showHierarchy.getTitle() + "<br/>" + find.getHTML() + " "
+ find.getTitle() + "<br/>" + analyze.getHTML() + " "
+ analyze.getTitle() + "<br/>" + generateWS.getHTML() + " "
+ generateWS.getTitle() + "<br/>");
info.setStyleName(VDebugWindow.STYLENAME + "-info");
- content.add(info);
+ helpPanel.add(info);
}
private void showHierarchy() {
Highlight.hideAll();
- content.clear();
-
- // TODO Clearing and rebuilding the contents is not optimal for UX as
- // any previous expansions are lost.
- SimplePanel trees = new SimplePanel();
-
- for (ApplicationConnection application : ApplicationConfiguration
- .getRunningApplications()) {
- ServerConnector uiConnector = application.getUIConnector();
- Widget connectorTree = buildConnectorTree(uiConnector);
-
- trees.add(connectorTree);
- }
-
- content.add(trees);
- }
-
- private Widget buildConnectorTree(final ServerConnector connector) {
- String connectorString = Util.getConnectorString(connector);
-
- List<ServerConnector> children = connector.getChildren();
-
- Widget widget;
- if (children == null || children.isEmpty()) {
- // Leaf node, just add a label
- Label label = new Label(connectorString);
- label.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- Highlight.showOnly(connector);
- Highlight.showServerDebugInfo(connector);
- }
- });
- widget = label;
- } else {
- SimpleTree tree = new SimpleTree(connectorString) {
- @Override
- protected void select(ClickEvent event) {
- super.select(event);
- Highlight.showOnly(connector);
- Highlight.showServerDebugInfo(connector);
- }
- };
- for (ServerConnector child : children) {
- tree.add(buildConnectorTree(child));
- }
- widget = tree;
- }
-
- if (widget instanceof HasDoubleClickHandlers) {
- HasDoubleClickHandlers has = (HasDoubleClickHandlers) widget;
- has.addDoubleClickHandler(new DoubleClickHandler() {
- @Override
- public void onDoubleClick(DoubleClickEvent event) {
- printState(connector, true);
- }
- });
- }
-
- return widget;
+ hierarchyPanel.update();
+ content.setWidget(hierarchyPanel);
}
@Override
@@ -226,302 +170,19 @@ public class HierarchySection implements Section {
}
private void generateWidgetset() {
-
- content.clear();
- HTML h = new HTML("Getting used connectors");
- content.add(h);
-
- String s = "";
- for (ApplicationConnection ac : ApplicationConfiguration
- .getRunningApplications()) {
- ApplicationConfiguration conf = ac.getConfiguration();
- s += "<h1>Used connectors for " + conf.getServiceUrl() + "</h1>";
-
- for (String connectorName : getUsedConnectorNames(conf)) {
- s += connectorName + "<br/>";
- }
-
- s += "<h2>To make an optimized widgetset based on these connectors, do:</h2>";
- s += "<h3>1. Add to your widgetset.gwt.xml file:</h2>";
- s += "<textarea rows=\"3\" style=\"width:90%\">";
- s += "<generate-with class=\"OptimizedConnectorBundleLoaderFactory\">\n";
- s += " <when-type-assignable class=\"com.vaadin.client.metadata.ConnectorBundleLoader\" />\n";
- s += "</generate-with>";
- s += "</textarea>";
-
- s += "<h3>2. Add the following java file to your project:</h2>";
- s += "<textarea rows=\"5\" style=\"width:90%\">";
- s += generateOptimizedWidgetSet(getUsedConnectorNames(conf));
- s += "</textarea>";
- s += "<h3>3. Recompile widgetset</h2>";
-
- }
-
- h.setHTML(s);
- }
-
- private Set<String> getUsedConnectorNames(
- ApplicationConfiguration configuration) {
- int tag = 0;
- Set<String> usedConnectors = new HashSet<String>();
- while (true) {
- String serverSideClass = configuration
- .getServerSideClassNameForTag(tag);
- if (serverSideClass == null) {
- break;
- }
- Class<? extends ServerConnector> connectorClass = configuration
- .getConnectorClassByEncodedTag(tag);
- if (connectorClass == null) {
- break;
- }
-
- if (connectorClass != UnknownComponentConnector.class) {
- usedConnectors.add(connectorClass.getName());
- }
- tag++;
- if (tag > 10000) {
- // Sanity check
- VConsole.error("Search for used connector classes was forcefully terminated");
- break;
- }
- }
- return usedConnectors;
- }
-
- public String generateOptimizedWidgetSet(Set<String> usedConnectors) {
- String s = "import java.util.HashSet;\n";
- s += "import java.util.Set;\n";
-
- s += "import com.google.gwt.core.ext.typeinfo.JClassType;\n";
- s += "import com.vaadin.client.ui.ui.UIConnector;\n";
- s += "import com.vaadin.server.widgetsetutils.ConnectorBundleLoaderFactory;\n";
- s += "import com.vaadin.shared.ui.Connect.LoadStyle;\n\n";
-
- s += "public class OptimizedConnectorBundleLoaderFactory extends\n";
- s += " ConnectorBundleLoaderFactory {\n";
- s += " private Set<String> eagerConnectors = new HashSet<String>();\n";
- s += " {\n";
- for (String c : usedConnectors) {
- s += " eagerConnectors.add(" + c
- + ".class.getName());\n";
- }
- s += " }\n";
- s += "\n";
- s += " @Override\n";
- s += " protected LoadStyle getLoadStyle(JClassType connectorType) {\n";
- s += " if (eagerConnectors.contains(connectorType.getQualifiedBinaryName())) {\n";
- s += " return LoadStyle.EAGER;\n";
- s += " } else {\n";
- s += " // Loads all other connectors immediately after the initial view has\n";
- s += " // been rendered\n";
- s += " return LoadStyle.DEFERRED;\n";
- s += " }\n";
- s += " }\n";
- s += "}\n";
-
- return s;
+ widgetsetPanel.update();
+ content.setWidget(widgetsetPanel);
}
private void analyzeLayouts() {
- content.clear();
- content.add(new Label("Analyzing layouts..."));
- List<ApplicationConnection> runningApplications = ApplicationConfiguration
- .getRunningApplications();
- for (ApplicationConnection applicationConnection : runningApplications) {
- applicationConnection.analyzeLayouts();
- }
+ analyzeLayoutsPanel.update();
+ content.setWidget(analyzeLayoutsPanel);
}
@Override
public void meta(ApplicationConnection ac, ValueMap meta) {
- content.clear();
- JsArray<ValueMap> valueMapArray = meta
- .getJSValueMapArray("invalidLayouts");
- int size = valueMapArray.length();
-
- if (size > 0) {
- SimpleTree root = new SimpleTree("Layouts analyzed, " + size
- + " top level problems");
- for (int i = 0; i < size; i++) {
- printLayoutError(ac, valueMapArray.get(i), root);
- }
- root.open(false);
- content.add(root);
- } else {
- content.add(new Label("Layouts analyzed, no top level problems"));
- }
-
- Set<ComponentConnector> zeroHeightComponents = new HashSet<ComponentConnector>();
- Set<ComponentConnector> zeroWidthComponents = new HashSet<ComponentConnector>();
- findZeroSizeComponents(zeroHeightComponents, zeroWidthComponents,
- ac.getUIConnector());
- if (zeroHeightComponents.size() > 0 || zeroWidthComponents.size() > 0) {
- content.add(new HTML("<h4> Client side notifications</h4>"
- + " <em>The following relative sized components were "
- + "rendered to a zero size container on the client side."
- + " Note that these are not necessarily invalid "
- + "states, but reported here as they might be.</em>"));
- if (zeroHeightComponents.size() > 0) {
- content.add(new HTML(
- "<p><strong>Vertically zero size:</strong></p>"));
- printClientSideDetectedIssues(zeroHeightComponents, ac);
- }
- if (zeroWidthComponents.size() > 0) {
- content.add(new HTML(
- "<p><strong>Horizontally zero size:</strong></p>"));
- printClientSideDetectedIssues(zeroWidthComponents, ac);
- }
- }
-
- }
-
- private void printClientSideDetectedIssues(
- Set<ComponentConnector> zeroSized, ApplicationConnection ac) {
-
- // keep track of already highlighted parents
- HashSet<String> parents = new HashSet<String>();
-
- for (final ComponentConnector connector : zeroSized) {
- final ServerConnector parent = connector.getParent();
- final String parentId = parent.getConnectorId();
-
- final Label errorDetails = new Label(Util.getSimpleName(connector)
- + "[" + connector.getConnectorId() + "]" + " inside "
- + Util.getSimpleName(parent));
-
- if (parent instanceof ComponentConnector) {
- final ComponentConnector parentConnector = (ComponentConnector) parent;
- if (!parents.contains(parentId)) {
- parents.add(parentId);
- Highlight.show(parentConnector, "yellow");
- }
-
- errorDetails.addMouseOverHandler(new MouseOverHandler() {
- @Override
- public void onMouseOver(MouseOverEvent event) {
- Highlight.hideAll();
- Highlight.show(parentConnector, "yellow");
- Highlight.show(connector);
- errorDetails.getElement().getStyle()
- .setTextDecoration(TextDecoration.UNDERLINE);
- }
- });
- errorDetails.addMouseOutHandler(new MouseOutHandler() {
- @Override
- public void onMouseOut(MouseOutEvent event) {
- Highlight.hideAll();
- errorDetails.getElement().getStyle()
- .setTextDecoration(TextDecoration.NONE);
- }
- });
- errorDetails.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- printState(connector, true);
- }
- });
-
- }
-
- Highlight.show(connector);
- content.add(errorDetails);
-
- }
- }
-
- private void printLayoutError(ApplicationConnection ac, ValueMap valueMap,
- SimpleTree root) {
- final String pid = valueMap.getString("id");
-
- // find connector
- final ComponentConnector connector = (ComponentConnector) ConnectorMap
- .get(ac).getConnector(pid);
-
- if (connector == null) {
- root.add(new SimpleTree("[" + pid + "] NOT FOUND"));
- return;
- }
-
- Highlight.show(connector);
-
- final SimpleTree errorNode = new SimpleTree(
- Util.getSimpleName(connector) + " id: " + pid);
- errorNode.addDomHandler(new MouseOverHandler() {
- @Override
- public void onMouseOver(MouseOverEvent event) {
- Highlight.showOnly(connector);
- ((Widget) event.getSource()).getElement().getStyle()
- .setTextDecoration(TextDecoration.UNDERLINE);
- }
- }, MouseOverEvent.getType());
- errorNode.addDomHandler(new MouseOutHandler() {
- @Override
- public void onMouseOut(MouseOutEvent event) {
- Highlight.hideAll();
- ((Widget) event.getSource()).getElement().getStyle()
- .setTextDecoration(TextDecoration.NONE);
- }
- }, MouseOutEvent.getType());
-
- errorNode.addDomHandler(new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- if (event.getNativeEvent().getEventTarget().cast() == errorNode
- .getElement().getChild(1).cast()) {
- printState(connector, true);
- }
- }
- }, ClickEvent.getType());
-
- VerticalPanel errorDetails = new VerticalPanel();
-
- if (valueMap.containsKey("heightMsg")) {
- errorDetails.add(new Label("Height problem: "
- + valueMap.getString("heightMsg")));
- }
- if (valueMap.containsKey("widthMsg")) {
- errorDetails.add(new Label("Width problem: "
- + valueMap.getString("widthMsg")));
- }
- if (errorDetails.getWidgetCount() > 0) {
- errorNode.add(errorDetails);
- }
- if (valueMap.containsKey("subErrors")) {
- HTML l = new HTML(
- "<em>Expand this node to show problems that may be dependent on this problem.</em>");
- errorDetails.add(l);
- JsArray<ValueMap> suberrors = valueMap
- .getJSValueMapArray("subErrors");
- for (int i = 0; i < suberrors.length(); i++) {
- ValueMap value = suberrors.get(i);
- printLayoutError(ac, value, errorNode);
- }
-
- }
- root.add(errorNode);
- }
-
- private void findZeroSizeComponents(
- Set<ComponentConnector> zeroHeightComponents,
- Set<ComponentConnector> zeroWidthComponents,
- ComponentConnector connector) {
- Widget widget = connector.getWidget();
- ComputedStyle computedStyle = new ComputedStyle(widget.getElement());
- if (computedStyle.getIntProperty("height") == 0) {
- zeroHeightComponents.add(connector);
- }
- if (computedStyle.getIntProperty("width") == 0) {
- zeroWidthComponents.add(connector);
- }
- List<ServerConnector> children = connector.getChildren();
- for (ServerConnector serverConnector : children) {
- if (serverConnector instanceof ComponentConnector) {
- findZeroSizeComponents(zeroHeightComponents,
- zeroWidthComponents,
- (ComponentConnector) serverConnector);
- }
- }
+ // show the results of analyzeLayouts
+ analyzeLayoutsPanel.meta(ac, meta);
}
@Override
@@ -561,60 +222,11 @@ public class HierarchySection implements Section {
private void printState(ServerConnector connector, boolean serverDebug) {
Highlight.showOnly(connector);
if (serverDebug) {
- Highlight.showServerDebugInfo(connector);
+ HierarchyPanel.showServerDebugInfo(connector);
}
- SharedState state = connector.getState();
-
- Set<String> ignoreProperties = new HashSet<String>();
- ignoreProperties.add("id");
-
- String html = getRowHTML("Id", connector.getConnectorId());
- html += getRowHTML("Connector", Util.getSimpleName(connector));
-
- if (connector instanceof ComponentConnector) {
- ComponentConnector component = (ComponentConnector) connector;
-
- ignoreProperties.addAll(Arrays.asList("caption", "description",
- "width", "height"));
-
- AbstractComponentState componentState = component.getState();
-
- html += getRowHTML("Widget",
- Util.getSimpleName(component.getWidget()));
- html += getRowHTML("Caption", componentState.caption);
- html += getRowHTML("Description", componentState.description);
- html += getRowHTML("Width", componentState.width + " (actual: "
- + component.getWidget().getOffsetWidth() + "px)");
- html += getRowHTML("Height", componentState.height + " (actual: "
- + component.getWidget().getOffsetHeight() + "px)");
- }
-
- try {
- JsArrayObject<Property> properties = AbstractConnector
- .getStateType(connector).getPropertiesAsArray();
- for (int i = 0; i < properties.size(); i++) {
- Property property = properties.get(i);
- String name = property.getName();
- if (!ignoreProperties.contains(name)) {
- html += getRowHTML(property.getDisplayName(),
- property.getValue(state));
- }
- }
- } catch (NoDataException e) {
- html += "<div>Could not read state, error has been logged to the console</div>";
- VConsole.error(e);
- }
-
- content.clear();
- content.add(new HTML(html));
- }
-
- private String getRowHTML(String caption, Object value) {
- return "<div class=\"" + VDebugWindow.STYLENAME
- + "-row\"><span class=\"caption\">" + caption
- + "</span><span class=\"value\">"
- + Util.escapeHTML(String.valueOf(value)) + "</span></div>";
+ infoPanel.update(connector);
+ content.setWidget(infoPanel);
}
private final NativePreviewHandler highlightModeHandler = new NativePreviewHandler() {
@@ -634,7 +246,7 @@ public class HierarchySection implements Section {
.getNativeEvent().getClientX(), event.getNativeEvent()
.getClientY());
if (VDebugWindow.get().getElement().isOrHasChild(eventTarget)) {
- content.clear();
+ infoPanel.clear();
return;
}
@@ -654,7 +266,7 @@ public class HierarchySection implements Section {
return;
}
}
- content.clear();
+ infoPanel.clear();
}
if (event.getTypeInt() == Event.ONCLICK) {
Highlight.hideAll();
diff --git a/client/src/com/vaadin/client/debug/internal/Highlight.java b/client/src/com/vaadin/client/debug/internal/Highlight.java
index 3c1af445a9..262313b9b3 100644
--- a/client/src/com/vaadin/client/debug/internal/Highlight.java
+++ b/client/src/com/vaadin/client/debug/internal/Highlight.java
@@ -17,11 +17,11 @@ package com.vaadin.client.debug.internal;
import java.util.HashSet;
+import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Style;
import com.google.gwt.dom.client.Style.Position;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.BrowserInfo;
@@ -144,20 +144,55 @@ public class Highlight {
*/
static Element show(Widget widget, String color) {
if (widget != null) {
+ show(widget.getElement(), color);
+ }
+ return null;
+ }
+
+ /**
+ * Highlights the given {@link Element}.
+ * <p>
+ * Pass the returned {@link Element} to {@link #hide(Element)} to remove
+ * this particular highlight.
+ * </p>
+ *
+ * @param element
+ * Element to highlight
+ * @return Highlight element
+ */
+ static Element show(Element element) {
+ return show(element, DEFAULT_COLOR);
+ }
+
+ /**
+ * Highlight the given {@link Element} using the given color.
+ * <p>
+ * Pass the returned highlight {@link Element} to {@link #hide(Element)} to
+ * remove this particular highlight.
+ * </p>
+ *
+ * @param element
+ * Element to highlight
+ * @param color
+ * Color of highlight
+ * @return Highlight element
+ */
+ static Element show(Element element, String color) {
+ if (element != null) {
if (highlights == null) {
highlights = new HashSet<Element>();
}
Element highlight = DOM.createDiv();
Style style = highlight.getStyle();
- style.setTop(widget.getAbsoluteTop(), Unit.PX);
- style.setLeft(widget.getAbsoluteLeft(), Unit.PX);
- int width = widget.getOffsetWidth();
+ style.setTop(element.getAbsoluteTop(), Unit.PX);
+ style.setLeft(element.getAbsoluteLeft(), Unit.PX);
+ int width = element.getOffsetWidth();
if (width < MIN_WIDTH) {
width = MIN_WIDTH;
}
style.setWidth(width, Unit.PX);
- int height = widget.getOffsetHeight();
+ int height = element.getOffsetHeight();
if (height < MIN_HEIGHT) {
height = MIN_HEIGHT;
}
@@ -207,19 +242,4 @@ public class Highlight {
}
}
- /**
- * Outputs debug information on the server - usually in the console of an
- * IDE, with a clickable reference to the relevant code location.
- *
- * @since 7.1
- * @param connector
- * show debug info for this connector
- */
- static void showServerDebugInfo(ServerConnector connector) {
- if (connector != null) {
- connector.getConnection().getUIConnector()
- .showServerDebugInfo(connector);
- }
- }
-
}
diff --git a/client/src/com/vaadin/client/debug/internal/Icon.java b/client/src/com/vaadin/client/debug/internal/Icon.java
index cc2ef3b348..70bac11175 100644
--- a/client/src/com/vaadin/client/debug/internal/Icon.java
+++ b/client/src/com/vaadin/client/debug/internal/Icon.java
@@ -32,6 +32,8 @@ public enum Icon {
LOG("&#xf0c9;"), //
OPTIMIZE("&#xf0d0;"), //
HIERARCHY("&#xf0e8;"), //
+ // TODO create more appropriate icon
+ SELECTOR("&#x2263;"), //
MENU("&#xf013;"), //
NETWORK("&#xf0ec;"), //
ANALYZE("&#xf0f0;"), //
@@ -42,7 +44,9 @@ public enum Icon {
// BAN_CIRCLE("&#xf05e;"), //
MAXIMIZE("&#xf065;"), //
RESET("&#xf021;"), //
- PERSIST("&#xf02e"); //
+ PERSIST("&#xf02e"), //
+ TESTBENCH("&#xe600"), //
+ ;
private String id;
diff --git a/client/src/com/vaadin/client/debug/internal/OptimizedWidgetsetPanel.java b/client/src/com/vaadin/client/debug/internal/OptimizedWidgetsetPanel.java
new file mode 100644
index 0000000000..a8d8aad888
--- /dev/null
+++ b/client/src/com/vaadin/client/debug/internal/OptimizedWidgetsetPanel.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.debug.internal;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.HTML;
+import com.vaadin.client.ApplicationConfiguration;
+import com.vaadin.client.ApplicationConnection;
+import com.vaadin.client.ServerConnector;
+import com.vaadin.client.VConsole;
+import com.vaadin.client.ui.UnknownComponentConnector;
+
+/**
+ * Optimized widgetset view panel of the debug window.
+ *
+ * @since 7.1.4
+ */
+public class OptimizedWidgetsetPanel extends FlowPanel {
+
+ /**
+ * Update the panel contents based on the connectors that have been used so
+ * far on this execution of the application.
+ */
+ public void update() {
+ clear();
+ HTML h = new HTML("Getting used connectors");
+ add(h);
+
+ String s = "";
+ for (ApplicationConnection ac : ApplicationConfiguration
+ .getRunningApplications()) {
+ ApplicationConfiguration conf = ac.getConfiguration();
+ s += "<h1>Used connectors for " + conf.getServiceUrl() + "</h1>";
+
+ for (String connectorName : getUsedConnectorNames(conf)) {
+ s += connectorName + "<br/>";
+ }
+
+ s += "<h2>To make an optimized widgetset based on these connectors, do:</h2>";
+ s += "<h3>1. Add to your widgetset.gwt.xml file:</h2>";
+ s += "<textarea rows=\"3\" style=\"width:90%\">";
+ s += "<generate-with class=\"OptimizedConnectorBundleLoaderFactory\">\n";
+ s += " <when-type-assignable class=\"com.vaadin.client.metadata.ConnectorBundleLoader\" />\n";
+ s += "</generate-with>";
+ s += "</textarea>";
+
+ s += "<h3>2. Add the following java file to your project:</h2>";
+ s += "<textarea rows=\"5\" style=\"width:90%\">";
+ s += generateOptimizedWidgetSet(getUsedConnectorNames(conf));
+ s += "</textarea>";
+ s += "<h3>3. Recompile widgetset</h2>";
+
+ }
+
+ h.setHTML(s);
+ }
+
+ private Set<String> getUsedConnectorNames(
+ ApplicationConfiguration configuration) {
+ int tag = 0;
+ Set<String> usedConnectors = new HashSet<String>();
+ while (true) {
+ String serverSideClass = configuration
+ .getServerSideClassNameForTag(tag);
+ if (serverSideClass == null) {
+ break;
+ }
+ Class<? extends ServerConnector> connectorClass = configuration
+ .getConnectorClassByEncodedTag(tag);
+ if (connectorClass == null) {
+ break;
+ }
+
+ if (connectorClass != UnknownComponentConnector.class) {
+ usedConnectors.add(connectorClass.getName());
+ }
+ tag++;
+ if (tag > 10000) {
+ // Sanity check
+ VConsole.error("Search for used connector classes was forcefully terminated");
+ break;
+ }
+ }
+ return usedConnectors;
+ }
+
+ public String generateOptimizedWidgetSet(Set<String> usedConnectors) {
+ String s = "import java.util.HashSet;\n";
+ s += "import java.util.Set;\n";
+
+ s += "import com.google.gwt.core.ext.typeinfo.JClassType;\n";
+ s += "import com.vaadin.client.ui.ui.UIConnector;\n";
+ s += "import com.vaadin.server.widgetsetutils.ConnectorBundleLoaderFactory;\n";
+ s += "import com.vaadin.shared.ui.Connect.LoadStyle;\n\n";
+
+ s += "public class OptimizedConnectorBundleLoaderFactory extends\n";
+ s += " ConnectorBundleLoaderFactory {\n";
+ s += " private Set<String> eagerConnectors = new HashSet<String>();\n";
+ s += " {\n";
+ for (String c : usedConnectors) {
+ s += " eagerConnectors.add(" + c
+ + ".class.getName());\n";
+ }
+ s += " }\n";
+ s += "\n";
+ s += " @Override\n";
+ s += " protected LoadStyle getLoadStyle(JClassType connectorType) {\n";
+ s += " if (eagerConnectors.contains(connectorType.getQualifiedBinaryName())) {\n";
+ s += " return LoadStyle.EAGER;\n";
+ s += " } else {\n";
+ s += " // Loads all other connectors immediately after the initial view has\n";
+ s += " // been rendered\n";
+ s += " return LoadStyle.DEFERRED;\n";
+ s += " }\n";
+ s += " }\n";
+ s += "}\n";
+
+ return s;
+ }
+
+}
diff --git a/client/src/com/vaadin/client/debug/internal/SelectConnectorListener.java b/client/src/com/vaadin/client/debug/internal/SelectConnectorListener.java
new file mode 100644
index 0000000000..ec3a36c7c4
--- /dev/null
+++ b/client/src/com/vaadin/client/debug/internal/SelectConnectorListener.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.debug.internal;
+
+import com.google.gwt.dom.client.Element;
+import com.vaadin.client.ServerConnector;
+
+/**
+ * Listener for the selection of a connector in the debug window.
+ *
+ * @since 7.1.4
+ */
+public interface SelectConnectorListener {
+ /**
+ * Listener method called when a connector has been selected. If a specific
+ * element of the connector was selected, it is also given.
+ *
+ * @param connector
+ * selected connector
+ * @param element
+ * selected element of the connector or null if unknown
+ */
+ public void select(ServerConnector connector,
+ Element element);
+} \ No newline at end of file
diff --git a/client/src/com/vaadin/client/debug/internal/SelectorPath.java b/client/src/com/vaadin/client/debug/internal/SelectorPath.java
new file mode 100644
index 0000000000..56b48b2447
--- /dev/null
+++ b/client/src/com/vaadin/client/debug/internal/SelectorPath.java
@@ -0,0 +1,268 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.vaadin.client.debug.internal;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.google.gwt.dom.client.Element;
+import com.vaadin.client.ServerConnector;
+import com.vaadin.client.componentlocator.ComponentLocator;
+import com.vaadin.client.componentlocator.SelectorPredicate;
+
+/**
+ * A single segment of a selector path pointing to an Element.
+ * <p>
+ * This class should be considered internal to the framework and may change at
+ * any time.
+ * <p>
+ *
+ * @since 7.1.x
+ */
+public class SelectorPath {
+ private final String path;
+ private final Element element;
+ private final ComponentLocator locator;
+ private static Map<String, Integer> counter = new HashMap<String, Integer>();
+ private static Map<String, String> legacyNames = new HashMap<String, String>();
+
+ static {
+ legacyNames.put("FilterSelect", "ComboBox");
+ legacyNames.put("ScrollTable", "Table");
+ }
+
+ protected SelectorPath(ServerConnector c, Element e) {
+ element = e;
+ locator = new ComponentLocator(c.getConnection());
+ path = locator.getPathForElement(e);
+ }
+
+ public String getPath() {
+ return path;
+ }
+
+ public Element getElement() {
+ return element;
+ }
+
+ public ComponentLocator getLocator() {
+ return locator;
+ }
+
+ /**
+ * Generate ElementQuery code for Java. Fallback to By.vaadin(path) if
+ * dealing with LegacyLocator
+ *
+ * @return String containing Java code for finding the element described by
+ * path
+ */
+ public String getElementQuery() {
+ if (path.isEmpty() || locator.isValidForLegacyLocator(path)) {
+ return getLegacyLocatorQuery();
+ }
+
+ String[] fragments;
+ String tmpPath = path;
+ List<SelectorPredicate> postFilters = SelectorPredicate
+ .extractPostFilterPredicates(path);
+ if (postFilters.size() > 0) {
+ tmpPath = tmpPath.substring(1, tmpPath.lastIndexOf(')'));
+ }
+
+ // Generate an ElementQuery
+ fragments = tmpPath.split("/");
+ String elementQueryString = "";
+ int index = 0;
+ for (SelectorPredicate p : postFilters) {
+ if (p.getIndex() > 0) {
+ index = p.getIndex();
+ }
+ }
+
+ for (int i = 1; i < fragments.length; ++i) {
+ if (fragments[i].isEmpty()) {
+ // Recursive searches cause empty fragments
+ continue;
+ }
+
+ // if i == 1 or previous fragment was empty, search is recursive
+ boolean recursive = (i > 1 ? fragments[i - 1].isEmpty() : false);
+
+ // if elementQueryString is not empty, join the next query with .
+ String queryFragment = (!elementQueryString.isEmpty() ? "." : "");
+ // if search is not recursive, add another $ in front of fragment
+ queryFragment += (!recursive ? "$" : "")
+ + generateFragment(fragments[i]);
+
+ elementQueryString += queryFragment;
+ }
+
+ if (!hasId(fragments[fragments.length - 1])) {
+ if (index == 0) {
+ elementQueryString += ".first()";
+ } else {
+ elementQueryString += ".get(" + index + ")";
+ }
+ }
+
+ // Return full Java variable assignment and eQuery
+ return generateJavaVariable(fragments[fragments.length - 1])
+ + elementQueryString + ";";
+ }
+
+ /**
+ * Finds out if the given query fragment has a defined id
+ *
+ * @param fragment
+ * Query fragment
+ * @return true if has id
+ */
+ private boolean hasId(String fragment) {
+ for (SelectorPredicate p : SelectorPredicate
+ .extractPredicates(fragment)) {
+ if (p.getName().equals("id")) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Generates a recursive ElementQuery for given path fragment
+ *
+ * @param fragment
+ * Query fragment
+ * @return ElementQuery java code as a String
+ */
+ private String generateFragment(String fragment) {
+ // Get Element.class -name
+ String elementClass = getComponentName(fragment) + "Element.class";
+
+ String queryFragment = "$(" + elementClass + ")";
+
+ for (SelectorPredicate p : SelectorPredicate
+ .extractPredicates(fragment)) {
+ // Add in predicates like .caption and .id
+ queryFragment += "." + p.getName() + "(\"" + p.getValue() + "\")";
+ }
+ return queryFragment;
+ }
+
+ /**
+ * Returns the name of the component described by given query fragment
+ *
+ * @param fragment
+ * Query fragment
+ * @return Class part of fragment
+ */
+ protected String getComponentName(String fragment) {
+ return fragment.split("\\[")[0];
+ }
+
+ /**
+ * Generates a legacy locator for SelectorPath.
+ *
+ * @return String containing Java code for element search and assignment
+ */
+ private String getLegacyLocatorQuery() {
+ String name;
+ if (!path.isEmpty()) {
+ String[] frags = path.split("/");
+ name = getComponentName(frags[frags.length - 1]).substring(1);
+ } else {
+ name = "root";
+ }
+
+ if (legacyNames.containsKey(name)) {
+ name = legacyNames.get(name);
+ }
+
+ name = getNameWithCount(name);
+
+ // Use direct path and elementX naming style.
+ return "WebElement " + name.substring(0, 1).toLowerCase()
+ + name.substring(1) + " = getDriver().findElement(By.vaadin(\""
+ + path + "\"));";
+ }
+
+ /**
+ * Get variable name with counter for given component name.
+ *
+ * @param name
+ * Component name
+ * @return name followed by count
+ */
+ protected String getNameWithCount(String name) {
+ if (!counter.containsKey(name)) {
+ counter.put(name, 0);
+ }
+ counter.put(name, counter.get(name) + 1);
+ name += counter.get(name);
+ return name;
+ }
+
+ /**
+ * Generate Java variable assignment from given selector fragment
+ *
+ * @param pathFragment
+ * Selector fragment
+ * @return piece of java code
+ */
+ private String generateJavaVariable(String pathFragment) {
+ // Get element type and predicates from fragment
+ List<SelectorPredicate> predicates = SelectorPredicate
+ .extractPredicates(pathFragment);
+ String elementType = pathFragment.split("\\[")[0];
+ String name = getNameFromPredicates(predicates, elementType);
+
+ if (name.equals(elementType)) {
+ name = getNameWithCount(name);
+ }
+
+ // Replace unusable characters
+ name = name.replaceAll("\\W", "");
+
+ // Lowercase the first character of name
+ return elementType + "Element " + name.substring(0, 1).toLowerCase()
+ + name.substring(1) + " = ";
+ }
+
+ /**
+ * Get variable name based on predicates. Fallback to elementType
+ *
+ * @param predicates
+ * Predicates related to element
+ * @param elementType
+ * Element type
+ * @return name for Variable
+ */
+ private String getNameFromPredicates(List<SelectorPredicate> predicates,
+ String elementType) {
+ String name = elementType;
+ for (SelectorPredicate p : predicates) {
+ if ("caption".equals(p.getName())) {
+ // Caption + elementType is a suitable name
+ name = p.getValue() + elementType;
+ } else if ("id".equals(p.getName())) {
+ // Just id. This is unique, use it.
+ return p.getValue();
+ }
+ }
+ return name;
+ }
+} \ No newline at end of file
diff --git a/client/src/com/vaadin/client/debug/internal/TestBenchSection.java b/client/src/com/vaadin/client/debug/internal/TestBenchSection.java
new file mode 100644
index 0000000000..d35c575568
--- /dev/null
+++ b/client/src/com/vaadin/client/debug/internal/TestBenchSection.java
@@ -0,0 +1,281 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.debug.internal;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.event.dom.client.KeyCodes;
+import com.google.gwt.event.dom.client.MouseOutEvent;
+import com.google.gwt.event.dom.client.MouseOutHandler;
+import com.google.gwt.event.dom.client.MouseOverEvent;
+import com.google.gwt.event.dom.client.MouseOverHandler;
+import com.google.gwt.event.shared.HandlerRegistration;
+import com.google.gwt.user.client.Event;
+import com.google.gwt.user.client.Event.NativePreviewEvent;
+import com.google.gwt.user.client.Event.NativePreviewHandler;
+import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.HTML;
+import com.google.gwt.user.client.ui.RootPanel;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.client.ApplicationConfiguration;
+import com.vaadin.client.ApplicationConnection;
+import com.vaadin.client.ComponentConnector;
+import com.vaadin.client.ServerConnector;
+import com.vaadin.client.Util;
+import com.vaadin.client.ValueMap;
+
+/**
+ * Provides functionality for picking selectors for Vaadin TestBench.
+ *
+ * @since 7.1.x
+ * @author Vaadin Ltd
+ */
+public class TestBenchSection implements Section {
+
+ /**
+ * Selector widget showing a selector in a program-usable form.
+ */
+ private static class SelectorWidget extends HTML implements
+ MouseOverHandler, MouseOutHandler {
+ private final SelectorPath path;
+
+ public SelectorWidget(final SelectorPath path) {
+ this.path = path;
+
+ String html = "<div class=\"" + VDebugWindow.STYLENAME
+ + "-selector\"><span class=\"tb-selector\">"
+ + Util.escapeHTML(path.getElementQuery()) + "</span></div>";
+ setHTML(html);
+
+ addMouseOverHandler(this);
+ addMouseOutHandler(this);
+ }
+
+ @Override
+ public void onMouseOver(MouseOverEvent event) {
+ Highlight.hideAll();
+
+ Element element = path.getElement();
+ if (null != element) {
+ Highlight.show(element);
+ }
+ }
+
+ @Override
+ public void onMouseOut(MouseOutEvent event) {
+ Highlight.hideAll();
+ }
+ }
+
+ private final DebugButton tabButton = new DebugButton(Icon.TESTBENCH,
+ "Pick Vaadin TestBench selectors");
+
+ private final FlowPanel content = new FlowPanel();
+
+ private final FlowPanel selectorPanel = new FlowPanel();
+ // map from full path to SelectorWidget to enable reuse of old selectors
+ private Map<SelectorPath, SelectorWidget> selectorWidgets = new HashMap<SelectorPath, SelectorWidget>();
+
+ private final FlowPanel controls = new FlowPanel();
+
+ private final Button find = new DebugButton(Icon.HIGHLIGHT,
+ "Pick an element and generate a query for it");
+
+ private final Button clear = new DebugButton(Icon.CLEAR,
+ "Clear current elements");
+
+ private HandlerRegistration highlightModeRegistration = null;
+
+ public TestBenchSection() {
+
+ controls.add(find);
+ find.setStylePrimaryName(VDebugWindow.STYLENAME_BUTTON);
+ find.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(ClickEvent event) {
+ toggleFind();
+ }
+ });
+
+ controls.add(clear);
+ clear.setStylePrimaryName(VDebugWindow.STYLENAME_BUTTON);
+ clear.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(ClickEvent event) {
+ clearResults();
+ }
+ });
+
+ content.setStylePrimaryName(VDebugWindow.STYLENAME + "-testbench");
+ content.add(selectorPanel);
+ }
+
+ @Override
+ public DebugButton getTabButton() {
+ return tabButton;
+ }
+
+ @Override
+ public Widget getControls() {
+ return controls;
+ }
+
+ @Override
+ public Widget getContent() {
+ return content;
+ }
+
+ @Override
+ public void show() {
+
+ }
+
+ @Override
+ public void hide() {
+ stopFind();
+ }
+
+ @Override
+ public void meta(ApplicationConnection ac, ValueMap meta) {
+ // NOP
+ }
+
+ @Override
+ public void uidl(ApplicationConnection ac, ValueMap uidl) {
+ // NOP
+ }
+
+ private boolean isFindMode() {
+ return (highlightModeRegistration != null);
+ }
+
+ private void toggleFind() {
+ if (isFindMode()) {
+ stopFind();
+ } else {
+ startFind();
+ }
+ }
+
+ private void startFind() {
+ Highlight.hideAll();
+ if (!isFindMode()) {
+ highlightModeRegistration = Event
+ .addNativePreviewHandler(highlightModeHandler);
+ find.addStyleDependentName(VDebugWindow.STYLENAME_ACTIVE);
+ }
+ }
+
+ private void stopFind() {
+ if (isFindMode()) {
+ highlightModeRegistration.removeHandler();
+ highlightModeRegistration = null;
+ find.removeStyleDependentName(VDebugWindow.STYLENAME_ACTIVE);
+ }
+ Highlight.hideAll();
+ }
+
+ private void pickSelector(ServerConnector connector, Element element) {
+
+ SelectorPath p = new SelectorPath(connector, Util
+ .findPaintable(connector.getConnection(), element).getWidget()
+ .getElement());
+ SelectorWidget w = new SelectorWidget(p);
+
+ content.add(w);
+ }
+
+ private final NativePreviewHandler highlightModeHandler = new NativePreviewHandler() {
+
+ @Override
+ public void onPreviewNativeEvent(NativePreviewEvent event) {
+
+ if (event.getTypeInt() == Event.ONKEYDOWN
+ && event.getNativeEvent().getKeyCode() == KeyCodes.KEY_ESCAPE) {
+ stopFind();
+ Highlight.hideAll();
+ return;
+ }
+ if (event.getTypeInt() == Event.ONMOUSEMOVE
+ || event.getTypeInt() == Event.ONCLICK) {
+ Element eventTarget = Util.getElementFromPoint(event
+ .getNativeEvent().getClientX(), event.getNativeEvent()
+ .getClientY());
+ if (VDebugWindow.get().getElement().isOrHasChild(eventTarget)) {
+ if (isFindMode() && event.getTypeInt() == Event.ONCLICK) {
+ stopFind();
+ event.cancel();
+ }
+ return;
+ }
+
+ // make sure that not finding the highlight element only
+ Highlight.hideAll();
+
+ eventTarget = Util.getElementFromPoint(event.getNativeEvent()
+ .getClientX(), event.getNativeEvent().getClientY());
+ ComponentConnector connector = findConnector(eventTarget);
+
+ if (event.getTypeInt() == Event.ONMOUSEMOVE) {
+ if (connector != null) {
+ Highlight.showOnly(connector);
+ event.cancel();
+ event.consume();
+ event.getNativeEvent().stopPropagation();
+ return;
+ }
+ } else if (event.getTypeInt() == Event.ONCLICK) {
+ event.cancel();
+ event.consume();
+ event.getNativeEvent().stopPropagation();
+ if (connector != null) {
+ Highlight.showOnly(connector);
+ pickSelector(connector, eventTarget);
+ return;
+ }
+ }
+ }
+ event.cancel();
+ }
+
+ };
+
+ private ComponentConnector findConnector(Element element) {
+ for (ApplicationConnection a : ApplicationConfiguration
+ .getRunningApplications()) {
+ ComponentConnector connector = Util.getConnectorForElement(a, a
+ .getUIConnector().getWidget(), element);
+ if (connector == null) {
+ connector = Util.getConnectorForElement(a, RootPanel.get(),
+ element);
+ }
+ if (connector != null) {
+ return connector;
+ }
+ }
+ return null;
+ }
+
+ private void clearResults() {
+ content.clear();
+ }
+
+}
diff --git a/client/src/com/vaadin/client/event/PointerCancelEvent.java b/client/src/com/vaadin/client/event/PointerCancelEvent.java
new file mode 100644
index 0000000000..bd29ca7dfd
--- /dev/null
+++ b/client/src/com/vaadin/client/event/PointerCancelEvent.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.event;
+
+import com.google.gwt.event.dom.client.DomEvent;
+
+/**
+ * Represents a native PointerCancelEvent.
+ *
+ * @since 7.2
+ */
+public class PointerCancelEvent extends PointerEvent<PointerCancelHandler> {
+
+ /**
+ * Event type for PointerCancelEvent. Represents the meta-data associated
+ * with this event.
+ */
+ private static final Type<PointerCancelHandler> TYPE = new Type<PointerCancelHandler>(
+ EventType.PointerCancel.getNativeEventName(),
+ new PointerCancelEvent());
+
+ /**
+ * Gets the event type associated with pointer cancel events.
+ *
+ * @return the handler type
+ */
+ public static Type<PointerCancelHandler> getType() {
+ return TYPE;
+ }
+
+ /**
+ * Protected constructor, use
+ * {@link DomEvent#fireNativeEvent(com.google.gwt.dom.client.NativeEvent, com.google.gwt.event.shared.HasHandlers)}
+ * to fire pointer up events.
+ */
+ protected PointerCancelEvent() {
+ }
+
+ @Override
+ public final Type<PointerCancelHandler> getAssociatedType() {
+ return TYPE;
+ }
+
+ @Override
+ protected void dispatch(PointerCancelHandler handler) {
+ handler.onPointerCancel(this);
+ }
+
+}
diff --git a/client/src/com/vaadin/client/event/PointerCancelHandler.java b/client/src/com/vaadin/client/event/PointerCancelHandler.java
new file mode 100644
index 0000000000..58da738c2a
--- /dev/null
+++ b/client/src/com/vaadin/client/event/PointerCancelHandler.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.event;
+
+import com.google.gwt.event.shared.EventHandler;
+
+/**
+ * Handler interface for {@link PointerCancelEvent} events.
+ *
+ * @since 7.2
+ */
+public interface PointerCancelHandler extends EventHandler {
+
+ /**
+ * Called when PointerCancelEvent is fired.
+ *
+ * @param event
+ * the {@link PointerCancelEvent} that was fired
+ */
+ void onPointerCancel(PointerCancelEvent event);
+}
diff --git a/client/src/com/vaadin/client/event/PointerDownEvent.java b/client/src/com/vaadin/client/event/PointerDownEvent.java
new file mode 100644
index 0000000000..b9df1bd852
--- /dev/null
+++ b/client/src/com/vaadin/client/event/PointerDownEvent.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.event;
+
+import com.google.gwt.event.dom.client.DomEvent;
+
+/**
+ * Represents a native PointerDownEvent.
+ *
+ * @since 7.2
+ */
+public class PointerDownEvent extends PointerEvent<PointerDownHandler> {
+
+ /**
+ * Event type for PointerDownEvent. Represents the meta-data associated with
+ * this event.
+ */
+ private static final Type<PointerDownHandler> TYPE = new Type<PointerDownHandler>(
+ EventType.PointerDown.getNativeEventName(), new PointerDownEvent());
+
+ /**
+ * Gets the event type associated with PointerDownEvent events.
+ *
+ * @return the handler type
+ */
+ public static Type<PointerDownHandler> getType() {
+ return TYPE;
+ }
+
+ /**
+ * Protected constructor, use
+ * {@link DomEvent#fireNativeEvent(com.google.gwt.dom.client.NativeEvent, com.google.gwt.event.shared.HasHandlers)}
+ * to fire pointer down events.
+ */
+ protected PointerDownEvent() {
+ }
+
+ @Override
+ public final Type<PointerDownHandler> getAssociatedType() {
+ return TYPE;
+ }
+
+ @Override
+ protected void dispatch(PointerDownHandler handler) {
+ handler.onPointerDown(this);
+ }
+
+}
diff --git a/client/src/com/vaadin/client/event/PointerDownHandler.java b/client/src/com/vaadin/client/event/PointerDownHandler.java
new file mode 100644
index 0000000000..631fe3c716
--- /dev/null
+++ b/client/src/com/vaadin/client/event/PointerDownHandler.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.event;
+
+import com.google.gwt.event.shared.EventHandler;
+
+/**
+ * Handler interface for {@link PointerDownEvent} events.
+ *
+ * @since 7.2
+ */
+public interface PointerDownHandler extends EventHandler {
+
+ /**
+ * Called when PointerDownEvent is fired.
+ *
+ * @param event
+ * the {@link PointerDownEvent} that was fired
+ */
+ void onPointerDown(PointerDownEvent event);
+}
diff --git a/client/src/com/vaadin/client/event/PointerEvent.java b/client/src/com/vaadin/client/event/PointerEvent.java
new file mode 100644
index 0000000000..7aac68abf4
--- /dev/null
+++ b/client/src/com/vaadin/client/event/PointerEvent.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.event;
+
+import com.google.gwt.dom.client.NativeEvent;
+import com.google.gwt.event.dom.client.MouseEvent;
+import com.google.gwt.event.shared.EventHandler;
+
+/**
+ * Abstract class representing Pointer events.
+ *
+ * @param <H>
+ * handler type
+ *
+ * @since 7.2
+ */
+public abstract class PointerEvent<H extends EventHandler> extends
+ MouseEvent<H> {
+
+ enum EventType {
+ PointerDown, PointerMove, PointerOut, PointerOver, PointerUp, PointerCancel;
+
+ String getNativeEventName() {
+ return PointerEventSupport.getNativeEventName(this);
+ }
+ }
+
+ public static final String TYPE_UNKNOWN = "";
+ public static final String TYPE_TOUCH = "touch";
+ public static final String TYPE_PEN = "pen";
+ public static final String TYPE_MOUSE = "mouse";
+
+ /**
+ * Gets a unique identifier for the pointer that caused this event. The
+ * identifiers of previously active but retired pointers may be recycled.
+ *
+ * @return unique pointer id
+ */
+ public final int getPointerId() {
+ return getPointerId(getNativeEvent());
+ }
+
+ /**
+ * Gets the width of the contact geometry of the pointer in CSS pixels.
+ *
+ * @return width in CSS pixels
+ */
+ public final int getWidth() {
+ return getWidth(getNativeEvent());
+ }
+
+ /**
+ * Gets the height of the contact geometry of the pointer in CSS pixels.
+ *
+ * @return height in CSS pixels.
+ */
+ public final int getHeight() {
+ return getHeight(getNativeEvent());
+ }
+
+ /**
+ * Gets the pressure of the pointer input as a value in the range of [0, 1]
+ * where 0 and 1 represent the minimum and maximum, respectively.
+ *
+ * @return input pressure as a value between 0 and 1
+ */
+ public final double getPressure() {
+ return getPressure(getNativeEvent());
+ }
+
+ /**
+ * Gets the angle between the Y-Z plane and the plane containing both the
+ * transducer and the Y axis. A positive tilt is to the right.
+ *
+ * @return the tilt along the X axis as degrees in the range of [-90, 90],
+ * or 0 if the device does not support tilt
+ */
+ public final double getTiltX() {
+ return getTiltX(getNativeEvent());
+ }
+
+ /**
+ * Gets the angle between the X-Z plane and the plane containing both the
+ * transducer and the X axis. A positive tilt is towards the user.
+ *
+ * @return the tilt along the Y axis as degrees in the range of [-90, 90],
+ * or 0 if the device does not support tilt
+ */
+ public final double getTiltY() {
+ return getTiltY(getNativeEvent());
+ }
+
+ /**
+ * Gets the type of the pointer device that caused this event.
+ *
+ * @see PointerEvent#TYPE_UNKNOWN
+ * @see PointerEvent#TYPE_TOUCH
+ * @see PointerEvent#TYPE_PEN
+ * @see PointerEvent#TYPE_MOUSE
+ *
+ * @return a String indicating the type of the pointer device
+ */
+ public final String getPointerType() {
+ return getPointerType(getNativeEvent());
+ }
+
+ /**
+ * Indicates whether the pointer is the primary pointer of this type.
+ *
+ * @return true if the pointer is the primary pointer, otherwise false
+ */
+ public final boolean isPrimary() {
+ return isPrimary(getNativeEvent());
+ }
+
+ private static native final int getPointerId(NativeEvent e)
+ /*-{
+ return e.pointerId;
+ }-*/;
+
+ private static native final int getWidth(NativeEvent e)
+ /*-{
+ return e.width;
+ }-*/;
+
+ private static native final int getHeight(NativeEvent e)
+ /*-{
+ return e.height;
+ }-*/;
+
+ private static native final double getPressure(NativeEvent e)
+ /*-{
+ return e.pressure;
+ }-*/;
+
+ private static native final double getTiltX(NativeEvent e)
+ /*-{
+ return e.tiltX;
+ }-*/;
+
+ private static native final double getTiltY(NativeEvent e)
+ /*-{
+ return e.tiltY;
+ }-*/;
+
+ private static native final String getPointerType(NativeEvent e)
+ /*-{
+ var pointerType = e.pointerType;
+ if (typeof pointerType === "number") {
+ pointerType = [ , , "touch", "pen", "mouse" ][pointerType];
+ }
+ return pointerType || "";
+ }-*/;
+
+ private static native final boolean isPrimary(NativeEvent e)
+ /*-{
+ return e.isPrimary;
+ }-*/;
+
+}
diff --git a/client/src/com/vaadin/client/event/PointerEventSupport.java b/client/src/com/vaadin/client/event/PointerEventSupport.java
new file mode 100644
index 0000000000..af8a444247
--- /dev/null
+++ b/client/src/com/vaadin/client/event/PointerEventSupport.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.event;
+
+import com.google.gwt.core.client.GWT;
+import com.vaadin.client.event.PointerEvent.EventType;
+
+/**
+ * Main class for pointer event support. Contains functionality for determining
+ * if pointer events are available or not.
+ *
+ * @since 7.2
+ * @author Vaadin Ltd
+ */
+public class PointerEventSupport {
+
+ private static final PointerEventSupportImpl impl = GWT
+ .create(PointerEventSupportImpl.class);
+
+ private PointerEventSupport() {
+ }
+
+ public static void init() {
+ impl.init();
+ }
+
+ /**
+ * @return true if pointer events are supported by the browser, false
+ * otherwise
+ */
+ public static boolean isSupported() {
+ return impl.isSupported();
+ }
+
+ /**
+ * @param eventType
+ * @return the native event name of the given event
+ */
+ public static String getNativeEventName(EventType eventType) {
+ return impl.getNativeEventName(eventType);
+ }
+} \ No newline at end of file
diff --git a/client/src/com/vaadin/client/event/PointerEventSupportImpl.java b/client/src/com/vaadin/client/event/PointerEventSupportImpl.java
new file mode 100644
index 0000000000..75cbfce690
--- /dev/null
+++ b/client/src/com/vaadin/client/event/PointerEventSupportImpl.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.event;
+
+import com.vaadin.client.event.PointerEvent.EventType;
+
+/**
+ * Main pointer event support implementation class. Made for browser without
+ * pointer event support.
+ *
+ * @since 7.2
+ * @author Vaadin Ltd
+ */
+public class PointerEventSupportImpl {
+
+ /**
+ * @return true if the pointer events are supported, false otherwise
+ */
+ protected boolean isSupported() {
+ return false;
+ }
+
+ /**
+ * @param events
+ * @return the native event name of the given event
+ */
+ public String getNativeEventName(EventType eventName) {
+ return eventName.toString().toLowerCase();
+ }
+
+ /**
+ * Initializes event support
+ */
+ protected void init() {
+
+ }
+
+}
diff --git a/client/src/com/vaadin/client/event/PointerEventSupportImplIE10.java b/client/src/com/vaadin/client/event/PointerEventSupportImplIE10.java
new file mode 100644
index 0000000000..8f60f0bcf3
--- /dev/null
+++ b/client/src/com/vaadin/client/event/PointerEventSupportImplIE10.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.event;
+
+import com.vaadin.client.event.PointerEvent.EventType;
+
+/**
+ * Pointer event support class for IE 10 ("ms" prefixed pointer events)
+ *
+ * @since 7.2
+ * @author Vaadin Ltd
+ */
+public class PointerEventSupportImplIE10 extends
+ PointerEventSupportImplModernIE {
+
+ @Override
+ public String getNativeEventName(EventType eventName) {
+ return "MS" + eventName;
+ }
+
+}
diff --git a/client/src/com/vaadin/client/event/PointerEventSupportImplModernIE.java b/client/src/com/vaadin/client/event/PointerEventSupportImplModernIE.java
new file mode 100644
index 0000000000..9c06062866
--- /dev/null
+++ b/client/src/com/vaadin/client/event/PointerEventSupportImplModernIE.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.event;
+
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.user.client.impl.DOMImplStandard;
+import com.vaadin.client.event.PointerEvent.EventType;
+
+/**
+ * Pointer event support class for IE 11+ (unprefixed pointer events)
+ *
+ * @since 7.2
+ * @author Vaadin Ltd
+ */
+
+public class PointerEventSupportImplModernIE extends PointerEventSupportImpl {
+
+ protected static boolean inited = false;
+
+ @Override
+ protected boolean isSupported() {
+ return true;
+ }
+
+ @Override
+ protected void init() {
+ if (!inited) {
+ JavaScriptObject eventDispatcherMapExtensions = JavaScriptObject
+ .createObject();
+ JavaScriptObject captureEventDispatcherMapExtensions = JavaScriptObject
+ .createObject();
+ for (EventType e : EventType.values()) {
+ addEventDispatcher(e.getNativeEventName(),
+ eventDispatcherMapExtensions);
+ getPointerEventCaptureDispatchers(e.getNativeEventName(),
+ captureEventDispatcherMapExtensions);
+ }
+ DOMImplStandard
+ .addBitlessEventDispatchers(eventDispatcherMapExtensions);
+ DOMImplStandard
+ .addCaptureEventDispatchers(captureEventDispatcherMapExtensions);
+
+ inited = true;
+ }
+ }
+
+ private static native void addEventDispatcher(String eventName,
+ JavaScriptObject jso)
+ /*-{
+ jso[eventName] = @com.google.gwt.user.client.impl.DOMImplStandard::dispatchEvent(*);
+ }-*/;
+
+ private static native void getPointerEventCaptureDispatchers(
+ String eventName, JavaScriptObject jso)
+ /*-{
+ jso[eventName] = @com.google.gwt.user.client.impl.DOMImplStandard::dispatchCapturedMouseEvent(*);
+ }-*/;
+
+}
diff --git a/client/src/com/vaadin/client/event/PointerMoveEvent.java b/client/src/com/vaadin/client/event/PointerMoveEvent.java
new file mode 100644
index 0000000000..dd34e979e2
--- /dev/null
+++ b/client/src/com/vaadin/client/event/PointerMoveEvent.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.event;
+
+import com.google.gwt.event.dom.client.DomEvent;
+
+/**
+ * Represents a native PointerMoveEvent event.
+ *
+ * @since 7.2
+ */
+public class PointerMoveEvent extends PointerEvent<PointerMoveHandler> {
+
+ /**
+ * Event type for PointerMoveEvent. Represents the meta-data associated with
+ * this event.
+ */
+ private static final Type<PointerMoveHandler> TYPE = new Type<PointerMoveHandler>(
+ EventType.PointerMove.getNativeEventName(), new PointerMoveEvent());
+
+ /**
+ * Gets the event type associated with PointerMoveEvent.
+ *
+ * @return the handler type
+ */
+ public static Type<PointerMoveHandler> getType() {
+ return TYPE;
+ }
+
+ /**
+ * Protected constructor, use
+ * {@link DomEvent#fireNativeEvent(com.google.gwt.dom.client.NativeEvent, com.google.gwt.event.shared.HasHandlers)}
+ * to fire pointer down events.
+ */
+ protected PointerMoveEvent() {
+ }
+
+ @Override
+ public final Type<PointerMoveHandler> getAssociatedType() {
+ return TYPE;
+ }
+
+ @Override
+ protected void dispatch(PointerMoveHandler handler) {
+ handler.onPointerMove(this);
+ }
+
+}
diff --git a/client/src/com/vaadin/client/event/PointerMoveHandler.java b/client/src/com/vaadin/client/event/PointerMoveHandler.java
new file mode 100644
index 0000000000..99f693ecf9
--- /dev/null
+++ b/client/src/com/vaadin/client/event/PointerMoveHandler.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.event;
+
+import com.google.gwt.event.shared.EventHandler;
+
+/**
+ * Handler interface for {@link PointerMoveEvent} events.
+ *
+ * @since 7.2
+ */
+public interface PointerMoveHandler extends EventHandler {
+
+ /**
+ * Called when PointerMoveEvent is fired.
+ *
+ * @param event
+ * the {@link PointerMoveEvent} that was fired
+ */
+ void onPointerMove(PointerMoveEvent event);
+}
diff --git a/client/src/com/vaadin/client/event/PointerUpEvent.java b/client/src/com/vaadin/client/event/PointerUpEvent.java
new file mode 100644
index 0000000000..565bf5b644
--- /dev/null
+++ b/client/src/com/vaadin/client/event/PointerUpEvent.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.event;
+
+import com.google.gwt.event.dom.client.DomEvent;
+
+/**
+ * Represents a native PointerUpEvent.
+ *
+ * @since 7.2
+ */
+public class PointerUpEvent extends PointerEvent<PointerUpHandler> {
+
+ /**
+ * Event type for PointerUpEvent. Represents the meta-data associated with
+ * this event.
+ */
+ private static final Type<PointerUpHandler> TYPE = new Type<PointerUpHandler>(
+ EventType.PointerUp.getNativeEventName(), new PointerUpEvent());
+
+ /**
+ * Gets the event type associated with PointerUpEvent.
+ *
+ * @return the handler type
+ */
+ public static Type<PointerUpHandler> getType() {
+ return TYPE;
+ }
+
+ /**
+ * Protected constructor, use
+ * {@link DomEvent#fireNativeEvent(com.google.gwt.dom.client.NativeEvent, com.google.gwt.event.shared.HasHandlers)}
+ * to fire pointer down events.
+ */
+ protected PointerUpEvent() {
+ }
+
+ @Override
+ public final Type<PointerUpHandler> getAssociatedType() {
+ return TYPE;
+ }
+
+ @Override
+ protected void dispatch(PointerUpHandler handler) {
+ handler.onPointerUp(this);
+ }
+
+}
diff --git a/client/src/com/vaadin/client/event/PointerUpHandler.java b/client/src/com/vaadin/client/event/PointerUpHandler.java
new file mode 100644
index 0000000000..1ffa8abf93
--- /dev/null
+++ b/client/src/com/vaadin/client/event/PointerUpHandler.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.event;
+
+import com.google.gwt.event.shared.EventHandler;
+
+/**
+ * Handler interface for {@link PointerUpEvent} events.
+ *
+ * @since 7.2
+ */
+public interface PointerUpHandler extends EventHandler {
+
+ /**
+ * Called when PointerUpEvent is fired.
+ *
+ * @param event
+ * the {@link PointerUpEvent} that was fired
+ */
+ void onPointerUp(PointerUpEvent event);
+}
diff --git a/client/src/com/vaadin/client/extensions/ResponsiveConnector.java b/client/src/com/vaadin/client/extensions/ResponsiveConnector.java
new file mode 100644
index 0000000000..500e4a0916
--- /dev/null
+++ b/client/src/com/vaadin/client/extensions/ResponsiveConnector.java
@@ -0,0 +1,406 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.vaadin.client.extensions;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.dom.client.Element;
+import com.vaadin.client.BrowserInfo;
+import com.vaadin.client.LayoutManager;
+import com.vaadin.client.ServerConnector;
+import com.vaadin.client.ui.AbstractComponentConnector;
+import com.vaadin.client.ui.layout.ElementResizeEvent;
+import com.vaadin.client.ui.layout.ElementResizeListener;
+import com.vaadin.server.Responsive;
+import com.vaadin.shared.ui.Connect;
+
+/**
+ * The client side connector for the Responsive extension.
+ *
+ * @author Vaadin Ltd
+ * @since 7.2
+ */
+@SuppressWarnings("GwtInconsistentSerializableClass")
+@Connect(Responsive.class)
+public class ResponsiveConnector extends AbstractExtensionConnector implements
+ ElementResizeListener {
+
+ /**
+ * The target component which we will monitor for width changes
+ */
+ protected AbstractComponentConnector target;
+
+ /**
+ * All the width breakpoints found for this particular instance
+ */
+ protected JavaScriptObject widthBreakpoints;
+
+ /**
+ * All the height breakpoints found for this particular instance
+ */
+ protected JavaScriptObject heightBreakpoints;
+
+ /**
+ * All width-range breakpoints found from the style sheets on the page.
+ * Common for all instances.
+ */
+ protected static JavaScriptObject widthRangeCache;
+
+ /**
+ * All height-range breakpoints found from the style sheets on the page.
+ * Common for all instances.
+ */
+ protected static JavaScriptObject heightRangeCache;
+
+ private static Logger getLogger() {
+ return Logger.getLogger(ResponsiveConnector.class.getName());
+ }
+
+ private static void error(String message) {
+ getLogger().log(Level.SEVERE, message);
+ }
+
+ @Override
+ protected void extend(ServerConnector target) {
+ // Initialize cache if not already done
+ if (widthRangeCache == null) {
+ searchForBreakPoints();
+ }
+
+ this.target = (AbstractComponentConnector) target;
+
+ // Get any breakpoints from the styles defined for this widget
+ getBreakPointsFor(constructSelectorsForTarget());
+
+ // Start listening for size changes
+ LayoutManager.get(getConnection()).addElementResizeListener(
+ this.target.getWidget().getElement(), this);
+ }
+
+ /**
+ * Construct the list of selectors that should be matched against in the
+ * range selectors
+ *
+ * @return The selectors in a comma delimited string.
+ */
+ protected String constructSelectorsForTarget() {
+ String primaryStyle = this.target.getState().primaryStyleName;
+ StringBuilder selectors = new StringBuilder();
+ selectors.append(".").append(primaryStyle);
+
+ if (this.target.getState().styles != null
+ && this.target.getState().styles.size() > 0) {
+ for (String style : this.target.getState().styles) {
+ selectors.append(",.").append(style);
+ selectors.append(",.").append(primaryStyle).append(".")
+ .append(style);
+ selectors.append(",.").append(style).append(".")
+ .append(primaryStyle);
+ selectors.append(",.").append(primaryStyle).append("-")
+ .append(style);
+ }
+ }
+
+ // Allow the ID to be used as the selector as well for ranges
+ if (this.target.getState().id != null) {
+ selectors.append(",#").append(this.target.getState().id);
+ }
+ return selectors.toString();
+ }
+
+ @Override
+ public void onUnregister() {
+ super.onUnregister();
+ LayoutManager.get(getConnection()).removeElementResizeListener(
+ this.target.getWidget().getElement(), this);
+ }
+
+ /**
+ * Build a cache of all 'width-range' and 'height-range' attribute selectors
+ * found in the stylesheets.
+ */
+ private static native void searchForBreakPoints()
+ /*-{
+
+ // Initialize variables
+ @com.vaadin.client.extensions.ResponsiveConnector::widthRangeCache = [];
+ @com.vaadin.client.extensions.ResponsiveConnector::heightRangeCache = [];
+
+ var widthRanges = @com.vaadin.client.extensions.ResponsiveConnector::widthRangeCache;
+ var heightRanges = @com.vaadin.client.extensions.ResponsiveConnector::heightRangeCache;
+
+ // Can't do squat if we can't parse stylesheets
+ if(!$doc.styleSheets)
+ return;
+
+ var sheets = $doc.styleSheets;
+
+ // Loop all stylesheets on the page and process them individually
+ for(var i = 0, len = sheets.length; i < len; i++) {
+ var sheet = sheets[i];
+ @com.vaadin.client.extensions.ResponsiveConnector::searchStylesheetForBreakPoints(Lcom/google/gwt/core/client/JavaScriptObject;)(sheet);
+ }
+
+ }-*/;
+
+ /**
+ * Process an individual stylesheet object. Any @import statements are
+ * handled recursively. Regular rule declarations are searched for
+ * 'width-range' and 'height-range' attribute selectors.
+ *
+ * @param sheet
+ */
+ private static native void searchStylesheetForBreakPoints(
+ final JavaScriptObject sheet)
+ /*-{
+
+ // Inline variables for easier reading
+ var widthRanges = @com.vaadin.client.extensions.ResponsiveConnector::widthRangeCache;
+ var heightRanges = @com.vaadin.client.extensions.ResponsiveConnector::heightRangeCache;
+
+ // Get all the rulesets from the stylesheet
+ var theRules = new Array();
+ var IE = @com.vaadin.client.BrowserInfo::get()().@com.vaadin.client.BrowserInfo::isIE()();
+ var IE8 = @com.vaadin.client.BrowserInfo::get()().@com.vaadin.client.BrowserInfo::isIE8()();
+
+ if (sheet.cssRules) {
+ theRules = sheet.cssRules
+ } else if (sheet.rules) {
+ theRules = sheet.rules
+ }
+
+ // Special import handling for IE8
+ if (IE8) {
+ try {
+ for(var i = 0, len = sheet.imports.length; i < len; i++) {
+ @com.vaadin.client.extensions.ResponsiveConnector::searchStylesheetForBreakPoints(Lcom/google/gwt/core/client/JavaScriptObject;)(sheet.imports[i]);
+ }
+ } catch(e) {
+ // This is added due to IE8 failing to handle imports of some sheets for unknown reason (throws a permission denied exception)
+ @com.vaadin.client.extensions.ResponsiveConnector::error(Ljava/lang/String;)("Failed to handle imports of CSS style sheet: " + sheet.href);
+ }
+ }
+
+ // Loop through the rulesets
+ for(var i = 0, len = theRules.length; i < len; i++) {
+ var rule = theRules[i];
+
+ if(rule.type == 3) {
+ // @import rule, traverse recursively
+ @com.vaadin.client.extensions.ResponsiveConnector::searchStylesheetForBreakPoints(Lcom/google/gwt/core/client/JavaScriptObject;)(rule.styleSheet);
+
+ } else if(rule.type == 1 || !rule.type) {
+ // Regular selector rule
+
+ // IE parses CSS like .class[attr="val"] into [attr="val"].class so we need to check for both
+
+ // Pattern for matching [width-range] selectors
+ var widths = IE? /\[width-range~?=["|'](.*)-(.*)["|']\]([\.|#]\S+)/i : /([\.|#]\S+)\[width-range~?=["|'](.*)-(.*)["|']\]/i;
+
+ // Patter for matching [height-range] selectors
+ var heights = IE? /\[height-range~?=["|'](.*)-(.*)["|']\]([\.|#]\S+)/i : /([\.|#]\S+)\[height-range~?=["|'](.*)-(.*)["|']\]/i;
+
+ // Array of all of the separate selectors in this ruleset
+ var haystack = rule.selectorText.split(",");
+
+ // Loop all the selectors in this ruleset
+ for(var k = 0, len2 = haystack.length; k < len2; k++) {
+ var result;
+
+ // Check for width-range matches
+ if(result = haystack[k].match(widths)) {
+ var selector = IE? result[3] : result[1]
+ var min = IE? result[1] : result[2];
+ var max = IE? result[2] : result[3];
+
+ // Avoid adding duplicates
+ var duplicate = false;
+ for(var l = 0, len3 = widthRanges.length; l < len3; l++) {
+ var bp = widthRanges[l];
+ if(selector == bp[0] && min == bp[1] && max == bp[2]) {
+ duplicate = true;
+ break;
+ }
+ }
+ if(!duplicate) {
+ widthRanges.push([selector, min, max]);
+ }
+ }
+
+ // Check for height-range matches
+ if(result = haystack[k].match(heights)) {
+ var selector = IE? result[3] : result[1]
+ var min = IE? result[1] : result[2];
+ var max = IE? result[2] : result[3];
+
+ // Avoid adding duplicates
+ var duplicate = false;
+ for(var l = 0, len3 = heightRanges.length; l < len3; l++) {
+ var bp = heightRanges[l];
+ if(selector == bp[0] && min == bp[1] && max == bp[2]) {
+ duplicate = true;
+ break;
+ }
+ }
+ if(!duplicate) {
+ heightRanges.push([selector, min, max]);
+ }
+ }
+ }
+ }
+ }
+
+ }-*/;
+
+ /**
+ * Get all matching ranges from the cache for this particular instance.
+ *
+ * @param selectors
+ */
+ private native void getBreakPointsFor(final String selectors)
+ /*-{
+
+ var selectors = selectors.split(",");
+
+ var widthBreakpoints = this.@com.vaadin.client.extensions.ResponsiveConnector::widthBreakpoints = [];
+ var heightBreakpoints = this.@com.vaadin.client.extensions.ResponsiveConnector::heightBreakpoints = [];
+
+ var widthRanges = @com.vaadin.client.extensions.ResponsiveConnector::widthRangeCache;
+ var heightRanges = @com.vaadin.client.extensions.ResponsiveConnector::heightRangeCache;
+
+ for(var i = 0, len = widthRanges.length; i < len; i++) {
+ var bp = widthRanges[i];
+ for(var j = 0, len2 = selectors.length; j < len2; j++) {
+ if(bp[0] == selectors[j])
+ widthBreakpoints.push(bp);
+ }
+ }
+
+ for(var i = 0, len = heightRanges.length; i < len; i++) {
+ var bp = heightRanges[i];
+ for(var j = 0, len2 = selectors.length; j < len2; j++) {
+ if(bp[0] == selectors[j])
+ heightBreakpoints.push(bp);
+ }
+ }
+
+ // Only for debugging
+ // console.log("Breakpoints for", selectors.join(","), widthBreakpoints, heightBreakpoints);
+
+ }-*/;
+
+ private String currentWidthRanges;
+ private String currentHeightRanges;
+
+ @Override
+ public void onElementResize(ElementResizeEvent event) {
+ int width = event.getLayoutManager().getOuterWidth(event.getElement());
+ int height = event.getLayoutManager()
+ .getOuterHeight(event.getElement());
+
+ com.google.gwt.user.client.Element element = this.target.getWidget()
+ .getElement();
+ boolean forceRedraw = false;
+
+ // Loop through breakpoints and see which one applies to this width
+ currentWidthRanges = resolveBreakpoint("width", width,
+ event.getElement());
+
+ if (!"".equals(currentWidthRanges)) {
+ this.target.getWidget().getElement()
+ .setAttribute("width-range", currentWidthRanges);
+ forceRedraw = true;
+ } else {
+ element.removeAttribute("width-range");
+ }
+
+ // Loop through breakpoints and see which one applies to this height
+ currentHeightRanges = resolveBreakpoint("height", height,
+ event.getElement());
+
+ if (!"".equals(currentHeightRanges)) {
+ this.target.getWidget().getElement()
+ .setAttribute("height-range", currentHeightRanges);
+ forceRedraw = true;
+ } else {
+ element.removeAttribute("height-range");
+ }
+
+ if (forceRedraw) {
+ forceRedrawIfIE8(element);
+ }
+ }
+
+ /**
+ * Forces IE8 to reinterpret CSS rules.
+ * {@link com.vaadin.client.Util#forceIE8Redraw(com.google.gwt.dom.client.Element)}
+ * doesn't work in this case.
+ *
+ * @param element
+ * the element to redraw
+ */
+ private void forceRedrawIfIE8(Element element) {
+ if (BrowserInfo.get().isIE8()) {
+ element.addClassName("foo");
+ element.removeClassName("foo");
+ }
+ }
+
+ private native String resolveBreakpoint(String which, int size,
+ Element element)
+ /*-{
+
+ // Default to "width" breakpoints
+ var breakpoints = this.@com.vaadin.client.extensions.ResponsiveConnector::widthBreakpoints;
+
+ // Use height breakpoints if we're measuring the height
+ if(which == "height")
+ breakpoints = this.@com.vaadin.client.extensions.ResponsiveConnector::heightBreakpoints;
+
+ // Output string that goes into either the "width-range" or "height-range" attribute in the element
+ var ranges = "";
+
+ // Loop the breakpoints
+ for(var i = 0, len = breakpoints.length; i < len; i++) {
+ var bp = breakpoints[i];
+
+ var min = parseInt(bp[1]);
+ var max = parseInt(bp[2]);
+
+ if(min && max) {
+ if(min <= size && size <= max) {
+ ranges += " " + bp[1] + "-" + bp[2];
+ }
+ } else if (min) {
+ if(min <= size) {
+ ranges += " " + bp[1] + "-";
+ }
+ } else if (max) {
+ if (size <= max) {
+ ranges += " -" + bp[2];
+ }
+ }
+ }
+
+ // Trim the output and return it
+ return ranges.replace(/^\s+/, "");
+
+ }-*/;
+
+}
diff --git a/client/src/com/vaadin/client/metadata/ConnectorBundleLoader.java b/client/src/com/vaadin/client/metadata/ConnectorBundleLoader.java
index f1a9fa1ee7..8148010b54 100644
--- a/client/src/com/vaadin/client/metadata/ConnectorBundleLoader.java
+++ b/client/src/com/vaadin/client/metadata/ConnectorBundleLoader.java
@@ -15,11 +15,10 @@
*/
package com.vaadin.client.metadata;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
import com.google.gwt.core.shared.GWT;
+import com.vaadin.client.FastStringMap;
import com.vaadin.client.metadata.AsyncBundleLoader.State;
public abstract class ConnectorBundleLoader {
@@ -28,8 +27,9 @@ public abstract class ConnectorBundleLoader {
private static ConnectorBundleLoader impl;
- private Map<String, AsyncBundleLoader> asyncBlockLoaders = new HashMap<String, AsyncBundleLoader>();
- private Map<String, String> identifierToBundle = new HashMap<String, String>();
+ private FastStringMap<AsyncBundleLoader> asyncBlockLoaders = FastStringMap
+ .create();
+ private FastStringMap<String> identifierToBundle = FastStringMap.create();
private final TypeDataStore datStore = new TypeDataStore();
diff --git a/client/src/com/vaadin/client/metadata/JsniInvoker.java b/client/src/com/vaadin/client/metadata/JsniInvoker.java
new file mode 100644
index 0000000000..4692a18cfe
--- /dev/null
+++ b/client/src/com/vaadin/client/metadata/JsniInvoker.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.metadata;
+
+import com.google.gwt.core.client.JavaScriptObject;
+import com.vaadin.client.JsArrayObject;
+
+/**
+ * Special {@link Invoker} that uses JSNI to invoke methods with limited
+ * visibility.
+ *
+ * @since 7.2
+ * @author Vaadin Ltd
+ */
+public abstract class JsniInvoker implements Invoker {
+
+ @Override
+ public Object invoke(Object target, Object... params) {
+ JsArrayObject<Object> jsParams = JavaScriptObject.createArray().cast();
+ for (Object object : params) {
+ jsParams.add(object);
+ }
+ return jsniInvoke(target, jsParams);
+ }
+
+ /**
+ * Abstract method that will be generated to contain JSNI for invoking the
+ * actual method.
+ *
+ * @param target
+ * the object upon which to invoke the method
+ * @param params
+ * a js array with arguments to pass to the method
+ * @return the value returned by the invoked method, or <code>null</code> if
+ * the target method return type is <code>void</code>.
+ */
+ protected abstract Object jsniInvoke(Object target,
+ JsArrayObject<Object> params);
+
+}
diff --git a/client/src/com/vaadin/client/metadata/Method.java b/client/src/com/vaadin/client/metadata/Method.java
index aea2fd7f85..390574cdf8 100644
--- a/client/src/com/vaadin/client/metadata/Method.java
+++ b/client/src/com/vaadin/client/metadata/Method.java
@@ -19,13 +19,10 @@ public class Method {
private final Type type;
private final String name;
- private String signature;
public Method(Type type, String name) {
this.type = type;
this.name = name;
- // Cache derived signature value
- signature = type.getSignature() + "." + name;
}
public Type getType() {
@@ -53,7 +50,20 @@ public class Method {
* @return the unique signature of this method
*/
public String getSignature() {
- return signature;
+ return type.getSignature() + "." + name;
+ }
+
+ /**
+ * Gets the string that is internally used when looking up generated support
+ * code for this method. This is the same as {@link #getSignature()}, but
+ * without any type parameters.
+ *
+ * @return the string to use for looking up generated support code
+ *
+ * @since 7.2
+ */
+ public String getLookupKey() {
+ return type.getBaseTypeName() + "." + name;
}
@Override
diff --git a/client/src/com/vaadin/client/metadata/OnStateChangeMethod.java b/client/src/com/vaadin/client/metadata/OnStateChangeMethod.java
new file mode 100644
index 0000000000..2ba06fd4eb
--- /dev/null
+++ b/client/src/com/vaadin/client/metadata/OnStateChangeMethod.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.metadata;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import com.vaadin.client.ServerConnector;
+import com.vaadin.client.annotations.OnStateChange;
+import com.vaadin.client.communication.StateChangeEvent;
+
+/**
+ * Encapsulates the data that the widgetset compiler generates for supporting a
+ * connector method annotated with {@link OnStateChange}
+ *
+ * @since 7.2
+ * @author Vaadin Ltd
+ */
+public class OnStateChangeMethod {
+
+ private final String methodName;
+ private final List<String> properties;
+ private final Class<?> declaringClass;
+
+ /**
+ * Creates a new instance based on a method name, a list of parameters names
+ * and a list of properties to listen for.
+ *
+ * @param methodName
+ * the name of the method to call
+ * @param properties
+ * an array of state property names to listen to
+ */
+ public OnStateChangeMethod(String methodName, String[] properties) {
+ this(null, methodName, properties);
+ }
+
+ /**
+ * Creates a new instance based on declaring class, a method name, a list of
+ * parameters names and a list of properties to listen for.
+ * <p>
+ * If the declaring class is <code>null</code>, the method is found based on
+ * the type of the connector that fired the state change event.
+ *
+ * @param declaringClass
+ * the class in which the target method is declared, or
+ * <code>null</code> to use the class of the connector firing the
+ * event
+ * @param methodName
+ * the name of the method to call
+ * @param properties
+ * an array of state property names to listen to
+ */
+ public OnStateChangeMethod(Class<?> declaringClass, String methodName,
+ String[] properties) {
+
+ this.methodName = methodName;
+
+ this.properties = Collections.unmodifiableList(Arrays
+ .asList(properties));
+
+ this.declaringClass = declaringClass;
+ }
+
+ /**
+ * Invokes the listener method for a state change.
+ *
+ * @param stateChangeEvent
+ * the state change event
+ */
+ public void invoke(StateChangeEvent stateChangeEvent) {
+ ServerConnector connector = (ServerConnector) stateChangeEvent
+ .getSource();
+
+ Class<?> declaringClass = this.declaringClass;
+ if (declaringClass == null) {
+ declaringClass = connector.getClass();
+ }
+ Type declaringType = TypeDataStore.getType(declaringClass);
+
+ try {
+ declaringType.getMethod(methodName).invoke(connector);
+ } catch (NoDataException e) {
+ throw new RuntimeException("Couldn't invoke @OnStateChange method "
+ + declaringType.getSignature() + "." + methodName, e);
+ }
+ }
+
+ /**
+ * Gets the list of state property names to listen for.
+ *
+ * @return the list of state property names to listen for
+ */
+ public List<String> getProperties() {
+ return properties;
+ }
+}
diff --git a/client/src/com/vaadin/client/metadata/Property.java b/client/src/com/vaadin/client/metadata/Property.java
index 2e0ea49c88..72ed7fec41 100644
--- a/client/src/com/vaadin/client/metadata/Property.java
+++ b/client/src/com/vaadin/client/metadata/Property.java
@@ -20,21 +20,18 @@ import com.vaadin.shared.annotations.DelegateToWidget;
public class Property {
private final Type bean;
private final String name;
- private final String signature;
public Property(Type bean, String name) {
this.bean = bean;
this.name = name;
- // Cache derived signature value
- signature = bean.getSignature() + "." + name;
}
public Object getValue(Object bean) throws NoDataException {
- return TypeDataStore.getGetter(this).invoke(bean);
+ return TypeDataStore.getValue(this, bean);
}
public void setValue(Object bean, Object value) throws NoDataException {
- TypeDataStore.getSetter(this).invoke(bean, value);
+ TypeDataStore.setValue(this, bean, value);
}
public String getDelegateToWidgetMethodName() {
@@ -50,6 +47,10 @@ public class Property {
return TypeDataStore.getType(this);
}
+ public Type getBeanType() {
+ return bean;
+ }
+
/**
* The unique signature used to identify this property. The structure of the
* returned string may change without notice and should not be used for any
@@ -59,7 +60,20 @@ public class Property {
* @return the unique signature of this property
*/
public String getSignature() {
- return signature;
+ return bean.getSignature() + "." + name;
+ }
+
+ /**
+ * Gets the string that is internally used when looking up generated support
+ * code for this method. This is the same as {@link #getSignature()}, but
+ * without any type parameters.
+ *
+ * @return the string to use for looking up generated support code
+ *
+ * @since 7.2
+ */
+ public String getLookupKey() {
+ return bean.getBaseTypeName() + "." + name;
}
@Override
diff --git a/client/src/com/vaadin/client/metadata/TypeDataStore.java b/client/src/com/vaadin/client/metadata/TypeDataStore.java
index aa37d75dc8..bc6610a6ff 100644
--- a/client/src/com/vaadin/client/metadata/TypeDataStore.java
+++ b/client/src/com/vaadin/client/metadata/TypeDataStore.java
@@ -23,6 +23,7 @@ import com.google.gwt.core.client.JsArrayString;
import com.vaadin.client.FastStringMap;
import com.vaadin.client.FastStringSet;
import com.vaadin.client.JsArrayObject;
+import com.vaadin.client.annotations.OnStateChange;
import com.vaadin.client.communication.JSONSerializer;
public class TypeDataStore {
@@ -34,11 +35,15 @@ public class TypeDataStore {
.create();
private final FastStringMap<ProxyHandler> proxyHandlers = FastStringMap
.create();
- private final FastStringMap<JsArrayObject<Property>> properties = FastStringMap
- .create();
private final FastStringMap<JsArrayString> delegateToWidgetProperties = FastStringMap
.create();
+ /**
+ * Maps connector class -> state property name -> hander method data
+ */
+ private final FastStringMap<FastStringMap<JsArrayObject<OnStateChangeMethod>>> onStateChangeMethods = FastStringMap
+ .create();
+
private final FastStringSet delayedMethods = FastStringSet.create();
private final FastStringSet lastOnlyMethods = FastStringSet.create();
@@ -46,12 +51,11 @@ public class TypeDataStore {
private final FastStringMap<Invoker> invokers = FastStringMap.create();
private final FastStringMap<Type[]> paramTypes = FastStringMap.create();
- private final FastStringMap<Type> propertyTypes = FastStringMap.create();
- private final FastStringMap<Invoker> setters = FastStringMap.create();
- private final FastStringMap<Invoker> getters = FastStringMap.create();
private final FastStringMap<String> delegateToWidget = FastStringMap
.create();
+ private final JavaScriptObject jsTypeData = JavaScriptObject.createObject();
+
public static TypeDataStore get() {
return ConnectorBundleLoader.get().getTypeDataStore();
}
@@ -69,12 +73,28 @@ public class TypeDataStore {
return class1;
}
+ // this is a very inefficient implementation for getting all the identifiers
+ // for a class
+ public FastStringSet findIdentifiersFor(Class<?> type) {
+ FastStringSet result = FastStringSet.create();
+
+ JsArrayString keys = identifiers.getKeys();
+ for (int i = 0; i < keys.length(); i++) {
+ String key = keys.get(i);
+ if (identifiers.get(key) == type) {
+ result.add(key);
+ }
+ }
+
+ return result;
+ }
+
public static Type getType(Class<?> clazz) {
return new Type(clazz);
}
public static Type getReturnType(Method method) throws NoDataException {
- Type type = get().returnTypes.get(method.getSignature());
+ Type type = get().returnTypes.get(method.getLookupKey());
if (type == null) {
throw new NoDataException("There is no return type for "
+ method.getSignature());
@@ -83,7 +103,7 @@ public class TypeDataStore {
}
public static Invoker getInvoker(Method method) throws NoDataException {
- Invoker invoker = get().invokers.get(method.getSignature());
+ Invoker invoker = get().invokers.get(method.getLookupKey());
if (invoker == null) {
throw new NoDataException("There is no invoker for "
+ method.getSignature());
@@ -93,7 +113,7 @@ public class TypeDataStore {
public static Invoker getConstructor(Type type) throws NoDataException {
Invoker invoker = get().invokers.get(new Method(type, CONSTRUCTOR_NAME)
- .getSignature());
+ .getLookupKey());
if (invoker == null) {
throw new NoDataException("There is no constructor for "
+ type.getSignature());
@@ -101,45 +121,37 @@ public class TypeDataStore {
return invoker;
}
- public static Invoker getGetter(Property property) throws NoDataException {
- Invoker getter = get().getters.get(property.getSignature());
- if (getter == null) {
- throw new NoDataException("There is no getter for "
- + property.getSignature());
- }
-
- return getter;
- }
-
- public void setGetter(Class<?> clazz, String propertyName, Invoker invoker) {
- getters.put(new Property(getType(clazz), propertyName).getSignature(),
- invoker);
+ public static Object getValue(Property property, Object target)
+ throws NoDataException {
+ return getJsPropertyValue(get().jsTypeData, property.getBeanType()
+ .getBaseTypeName(), property.getName(), target);
}
public static String getDelegateToWidget(Property property) {
- return get().delegateToWidget.get(property.getSignature());
+ return get().delegateToWidget.get(property.getLookupKey());
}
public static JsArrayString getDelegateToWidgetProperites(Type type) {
- return get().delegateToWidgetProperties.get(type.getSignature());
+ return get().delegateToWidgetProperties.get(type.getBaseTypeName());
}
public void setDelegateToWidget(Class<?> clazz, String propertyName,
String delegateValue) {
Type type = getType(clazz);
- delegateToWidget.put(new Property(type, propertyName).getSignature(),
+ delegateToWidget.put(new Property(type, propertyName).getLookupKey(),
delegateValue);
JsArrayString typeProperties = delegateToWidgetProperties.get(type
- .getSignature());
+ .getBaseTypeName());
if (typeProperties == null) {
typeProperties = JavaScriptObject.createArray().cast();
- delegateToWidgetProperties.put(type.getSignature(), typeProperties);
+ delegateToWidgetProperties.put(type.getBaseTypeName(),
+ typeProperties);
}
typeProperties.push(propertyName);
}
public void setReturnType(Class<?> type, String methodName, Type returnType) {
- returnTypes.put(new Method(getType(type), methodName).getSignature(),
+ returnTypes.put(new Method(getType(type), methodName).getLookupKey(),
returnType);
}
@@ -148,12 +160,12 @@ public class TypeDataStore {
}
public void setInvoker(Class<?> type, String methodName, Invoker invoker) {
- invokers.put(new Method(getType(type), methodName).getSignature(),
+ invokers.put(new Method(getType(type), methodName).getLookupKey(),
invoker);
}
public static Type[] getParamTypes(Method method) throws NoDataException {
- Type[] types = get().paramTypes.get(method.getSignature());
+ Type[] types = get().paramTypes.get(method.getLookupKey());
if (types == null) {
throw new NoDataException("There are no parameter type data for "
+ method.getSignature());
@@ -164,7 +176,7 @@ public class TypeDataStore {
public void setParamTypes(Class<?> type, String methodName,
Type[] paramTypes) {
this.paramTypes.put(
- new Method(getType(type), methodName).getSignature(),
+ new Method(getType(type), methodName).getLookupKey(),
paramTypes);
}
@@ -174,8 +186,8 @@ public class TypeDataStore {
public static ProxyHandler getProxyHandler(Type type)
throws NoDataException {
- ProxyHandler proxyHandler = get().proxyHandlers
- .get(type.getSignature());
+ ProxyHandler proxyHandler = get().proxyHandlers.get(type
+ .getBaseTypeName());
if (proxyHandler == null) {
throw new NoDataException("No proxy handler for "
+ type.getSignature());
@@ -184,24 +196,24 @@ public class TypeDataStore {
}
public void setProxyHandler(Class<?> type, ProxyHandler proxyHandler) {
- proxyHandlers.put(getType(type).getSignature(), proxyHandler);
+ proxyHandlers.put(getType(type).getBaseTypeName(), proxyHandler);
}
public static boolean isDelayed(Method method) {
- return get().delayedMethods.contains(method.getSignature());
+ return get().delayedMethods.contains(method.getLookupKey());
}
public void setDelayed(Class<?> type, String methodName) {
- delayedMethods.add(getType(type).getMethod(methodName).getSignature());
+ delayedMethods.add(getType(type).getMethod(methodName).getLookupKey());
}
public static boolean isLastOnly(Method method) {
- return get().lastOnlyMethods.contains(method.getSignature());
+ return get().lastOnlyMethods.contains(method.getLookupKey());
}
public void setLastOnly(Class<?> clazz, String methodName) {
lastOnlyMethods
- .add(getType(clazz).getMethod(methodName).getSignature());
+ .add(getType(clazz).getMethod(methodName).getLookupKey());
}
/**
@@ -227,60 +239,40 @@ public class TypeDataStore {
public static JsArrayObject<Property> getPropertiesAsArray(Type type)
throws NoDataException {
- JsArrayObject<Property> properties = get().properties.get(type
- .getSignature());
- if (properties == null) {
- throw new NoDataException("No property list for "
- + type.getSignature());
- }
- return properties;
- }
+ JsArrayString names = getJsPropertyNames(get().jsTypeData,
+ type.getBaseTypeName());
- public void setProperties(Class<?> clazz, String[] propertyNames) {
+ // Create Property instances for each property name
JsArrayObject<Property> properties = JavaScriptObject.createArray()
.cast();
- Type type = getType(clazz);
- for (String name : propertyNames) {
- properties.add(new Property(type, name));
+ for (int i = 0; i < names.length(); i++) {
+ properties.add(new Property(type, names.get(i)));
}
- this.properties.put(type.getSignature(), properties);
- }
- public static Type getType(Property property) throws NoDataException {
- Type type = get().propertyTypes.get(property.getSignature());
- if (type == null) {
- throw new NoDataException("No return type for "
- + property.getSignature());
- }
- return type;
+ return properties;
}
- public void setPropertyType(Class<?> clazz, String propertName, Type type) {
- propertyTypes.put(
- new Property(getType(clazz), propertName).getSignature(), type);
+ public static Type getType(Property property) throws NoDataException {
+ return getJsPropertyType(get().jsTypeData, property.getBeanType()
+ .getBaseTypeName(), property.getName());
}
- public static Invoker getSetter(Property property) throws NoDataException {
- Invoker setter = get().setters.get(property.getSignature());
- if (setter == null) {
- throw new NoDataException("No setter for "
- + property.getSignature());
- }
- return setter;
+ public void setPropertyType(Class<?> clazz, String propertyName, Type type) {
+ setJsPropertyType(jsTypeData, clazz.getName(), propertyName, type);
}
- public void setSetter(Class<?> clazz, String propertyName, Invoker setter) {
- setters.put(new Property(getType(clazz), propertyName).getSignature(),
- setter);
+ public static void setValue(Property property, Object target, Object value) {
+ setJsPropertyValue(get().jsTypeData, property.getBeanType()
+ .getBaseTypeName(), property.getName(), target, value);
}
public void setSerializerFactory(Class<?> clazz, Invoker factory) {
- serializerFactories.put(getType(clazz).getSignature(), factory);
+ serializerFactories.put(getType(clazz).getBaseTypeName(), factory);
}
public static JSONSerializer<?> findSerializer(Type type) {
Invoker factoryCreator = get().serializerFactories.get(type
- .getSignature());
+ .getBaseTypeName());
if (factoryCreator == null) {
return null;
}
@@ -288,6 +280,142 @@ public class TypeDataStore {
}
public static boolean hasProperties(Type type) {
- return get().properties.containsKey(type.getSignature());
+ return hasJsProperties(get().jsTypeData, type.getBaseTypeName());
+ }
+
+ public void setSuperClass(Class<?> baseClass, Class<?> superClass) {
+ String superClassName = superClass == null ? null : superClass
+ .getName();
+ setSuperClass(jsTypeData, baseClass.getName(), superClassName);
+ }
+
+ public void setPropertyData(Class<?> type, String propertyName,
+ JavaScriptObject propertyData) {
+ setPropertyData(jsTypeData, type.getName(), propertyName, propertyData);
+ }
+
+ private static native void setPropertyData(JavaScriptObject typeData,
+ String className, String propertyName, JavaScriptObject propertyData)
+ /*-{
+ typeData[className][propertyName] = propertyData;
+ }-*/;
+
+ /*
+ * This method sets up prototypes chain for <code>baseClassName</code>.
+ * Precondition is : <code>superClassName</code> had to be handled before
+ * its child <code>baseClassName</code>.
+ *
+ * It makes all properties defined in the <code>superClassName</code>
+ * available for <code>baseClassName</code> as well.
+ */
+ private static native void setSuperClass(JavaScriptObject typeData,
+ String baseClassName, String superClassName)
+ /*-{
+ var parentType = typeData[superClassName];
+ if (parentType !== undefined ){
+ var ctor = function () {};
+ ctor.prototype = parentType;
+ typeData[baseClassName] = new ctor;
+ }
+ else {
+ typeData[baseClassName] = {};
+ }
+ }-*/;
+
+ private static native boolean hasGetter(JavaScriptObject typeData,
+ String beanName, String propertyName)
+ /*-{
+ return typeData[beanName][propertyName].getter !== undefined;
+ }-*/;
+
+ private static native boolean hasSetter(JavaScriptObject typeData,
+ String beanName, String propertyName)
+ /*-{
+ return typeData[beanName][propertyName].setter !== undefined;
+ }-*/;
+
+ private static native Object getJsPropertyValue(JavaScriptObject typeData,
+ String beanName, String propertyName, Object beanInstance)
+ /*-{
+ return typeData[beanName][propertyName].getter(beanInstance);
+ }-*/;
+
+ private static native void setJsPropertyValue(JavaScriptObject typeData,
+ String beanName, String propertyName, Object beanInstance,
+ Object value)
+ /*-{
+ typeData[beanName][propertyName].setter(beanInstance, value);
+ }-*/;
+
+ private static native Type getJsPropertyType(JavaScriptObject typeData,
+ String beanName, String propertyName)
+ /*-{
+ return typeData[beanName][propertyName].type;
+ }-*/;
+
+ private static native void setJsPropertyType(JavaScriptObject typeData,
+ String beanName, String propertyName, Type type)
+ /*-{
+ typeData[beanName][propertyName].type = type;
+ }-*/;
+
+ private static native JsArrayString getJsPropertyNames(
+ JavaScriptObject typeData, String beanName)
+ /*-{
+ var names = [];
+ for(var name in typeData[beanName]) {
+ names.push(name);
+ }
+ return names;
+ }-*/;
+
+ private static native boolean hasJsProperties(JavaScriptObject typeData,
+ String beanName)
+ /*-{
+ return typeData[beanName] !== undefined ;
+ }-*/;
+
+ /**
+ * Gets data for all methods annotated with {@link OnStateChange} in the
+ * given connector type.
+ *
+ * @since 7.2
+ * @param type
+ * the connector type
+ * @return a map of state property names to handler method data
+ */
+ public static FastStringMap<JsArrayObject<OnStateChangeMethod>> getOnStateChangeMethods(
+ Class<?> type) {
+ return get().onStateChangeMethods.get(getType(type).getSignature());
+ }
+
+ /**
+ * Adds data about a method annotated with {@link OnStateChange} for the
+ * given connector type.
+ *
+ * @since 7.2
+ * @param clazz
+ * the connector type
+ * @param method
+ * the state change method data
+ */
+ public void addOnStateChangeMethod(Class<?> clazz,
+ OnStateChangeMethod method) {
+ FastStringMap<JsArrayObject<OnStateChangeMethod>> handlers = getOnStateChangeMethods(clazz);
+ if (handlers == null) {
+ handlers = FastStringMap.create();
+ onStateChangeMethods.put(getType(clazz).getSignature(), handlers);
+ }
+
+ for (String property : method.getProperties()) {
+ JsArrayObject<OnStateChangeMethod> propertyHandlers = handlers
+ .get(property);
+ if (propertyHandlers == null) {
+ propertyHandlers = JsArrayObject.createArray().cast();
+ handlers.put(property, propertyHandlers);
+ }
+
+ propertyHandlers.add(method);
+ }
}
}
diff --git a/client/src/com/vaadin/client/ui/AbstractClickEventHandler.java b/client/src/com/vaadin/client/ui/AbstractClickEventHandler.java
index e91abe9663..32c10fac49 100644
--- a/client/src/com/vaadin/client/ui/AbstractClickEventHandler.java
+++ b/client/src/com/vaadin/client/ui/AbstractClickEventHandler.java
@@ -15,6 +15,7 @@
*/
package com.vaadin.client.ui;
+import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.event.dom.client.ContextMenuEvent;
import com.google.gwt.event.dom.client.ContextMenuHandler;
@@ -27,7 +28,6 @@ import com.google.gwt.event.dom.client.MouseUpEvent;
import com.google.gwt.event.dom.client.MouseUpHandler;
import com.google.gwt.event.shared.EventHandler;
import com.google.gwt.event.shared.HandlerRegistration;
-import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Event.NativePreviewEvent;
import com.google.gwt.user.client.Event.NativePreviewHandler;
@@ -238,7 +238,7 @@ public abstract class AbstractClickEventHandler implements MouseDownHandler,
* @return The Element used for calculating relative coordinates for a click
* or null if no relative coordinates can be calculated.
*/
- protected Element getRelativeToElement() {
+ protected com.google.gwt.user.client.Element getRelativeToElement() {
return connector.getWidget().getElement();
}
diff --git a/client/src/com/vaadin/client/ui/AbstractComponentConnector.java b/client/src/com/vaadin/client/ui/AbstractComponentConnector.java
index 8fdaf0a5be..f6c26cda05 100644
--- a/client/src/com/vaadin/client/ui/AbstractComponentConnector.java
+++ b/client/src/com/vaadin/client/ui/AbstractComponentConnector.java
@@ -91,9 +91,8 @@ public abstract class AbstractComponentConnector extends AbstractConnector
"Default implementation of createWidget() does not work for "
+ Util.getSimpleName(this)
+ ". This might be caused by explicitely using "
- + "super.createWidget(), using a widget type with "
- + "generics or some unspecified problem with the "
- + "widgetset compilation.", e);
+ + "super.createWidget() or some unspecified "
+ + "problem with the widgetset compilation.", e);
}
}
@@ -462,15 +461,24 @@ public abstract class AbstractComponentConnector extends AbstractConnector
}
/**
- * Gets the icon set for this component.
+ * Gets the URI of the icon set for this component.
*
- * @return the URL of the icon, or <code>null</code> if no icon has been
+ * @return the URI of the icon, or <code>null</code> if no icon has been
* defined.
*/
- protected String getIcon() {
+ protected String getIconUri() {
return getResourceUrl(ComponentConstants.ICON_RESOURCE);
}
+ /**
+ * Gets the icon set for this component.
+ *
+ * @return the icon, or <code>null</code> if no icon has been defined.
+ */
+ protected Icon getIcon() {
+ return getConnection().getIcon(getIconUri());
+ }
+
/*
* (non-Javadoc)
*
diff --git a/client/src/com/vaadin/client/ui/AbstractConnector.java b/client/src/com/vaadin/client/ui/AbstractConnector.java
index 6855c5cd2d..bd499ac4bc 100644
--- a/client/src/com/vaadin/client/ui/AbstractConnector.java
+++ b/client/src/com/vaadin/client/ui/AbstractConnector.java
@@ -18,6 +18,7 @@ package com.vaadin.client.ui;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -27,6 +28,7 @@ import com.google.gwt.event.shared.HandlerManager;
import com.google.web.bindery.event.shared.HandlerRegistration;
import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.FastStringMap;
+import com.vaadin.client.JsArrayObject;
import com.vaadin.client.Profiler;
import com.vaadin.client.ServerConnector;
import com.vaadin.client.Util;
@@ -35,8 +37,10 @@ import com.vaadin.client.communication.RpcProxy;
import com.vaadin.client.communication.StateChangeEvent;
import com.vaadin.client.communication.StateChangeEvent.StateChangeHandler;
import com.vaadin.client.metadata.NoDataException;
+import com.vaadin.client.metadata.OnStateChangeMethod;
import com.vaadin.client.metadata.Type;
import com.vaadin.client.metadata.TypeData;
+import com.vaadin.client.metadata.TypeDataStore;
import com.vaadin.shared.communication.ClientRpc;
import com.vaadin.shared.communication.ServerRpc;
import com.vaadin.shared.communication.SharedState;
@@ -290,6 +294,37 @@ public abstract class AbstractConnector implements ServerConnector,
}
updateEnabledState(isEnabled());
+
+ FastStringMap<JsArrayObject<OnStateChangeMethod>> handlers = TypeDataStore
+ .getOnStateChangeMethods(getClass());
+ if (handlers != null) {
+ Profiler.enter("AbstractConnector.onStateChanged @OnStateChange");
+
+ HashSet<OnStateChangeMethod> invokedMethods = new HashSet<OnStateChangeMethod>();
+
+ JsArrayString propertyNames = handlers.getKeys();
+ for (int i = 0; i < propertyNames.length(); i++) {
+ String propertyName = propertyNames.get(i);
+
+ if (stateChangeEvent.hasPropertyChanged(propertyName)) {
+ JsArrayObject<OnStateChangeMethod> propertyMethods = handlers
+ .get(propertyName);
+
+ for (int j = 0; j < propertyMethods.size(); j++) {
+ OnStateChangeMethod method = propertyMethods.get(j);
+
+ if (invokedMethods.add(method)) {
+
+ method.invoke(stateChangeEvent);
+
+ }
+ }
+ }
+ }
+
+ Profiler.leave("AbstractConnector.onStateChanged @OnStateChange");
+ }
+
Profiler.leave("AbstractConnector.onStateChanged");
}
diff --git a/client/src/com/vaadin/client/ui/Action.java b/client/src/com/vaadin/client/ui/Action.java
index e1b85dcb82..84399f7611 100644
--- a/client/src/com/vaadin/client/ui/Action.java
+++ b/client/src/com/vaadin/client/ui/Action.java
@@ -17,7 +17,6 @@
package com.vaadin.client.ui;
import com.google.gwt.user.client.Command;
-import com.vaadin.client.Util;
/**
*
@@ -43,9 +42,12 @@ public abstract class Action implements Command {
public String getHTML() {
final StringBuffer sb = new StringBuffer();
sb.append("<div>");
- if (getIconUrl() != null) {
- sb.append("<img src=\"" + Util.escapeAttribute(getIconUrl())
- + "\" alt=\"icon\" />");
+ // Could store the icon in a field instead, but it doesn't really matter
+ // right now because Actions are recreated every time they are needed
+ Icon icon = owner.getClient().getIcon(getIconUrl());
+ if (icon != null) {
+ icon.setAlternateText("icon");
+ sb.append(icon.getElement().getString());
}
sb.append(getCaption());
sb.append("</div>");
@@ -78,5 +80,4 @@ public abstract class Action implements Command {
return "Action [owner=" + owner + ", iconUrl=" + iconUrl + ", caption="
+ caption + "]";
}
-
}
diff --git a/client/src/com/vaadin/client/ui/FocusElementPanel.java b/client/src/com/vaadin/client/ui/FocusElementPanel.java
index d439a4e56a..dd5544f016 100644
--- a/client/src/com/vaadin/client/ui/FocusElementPanel.java
+++ b/client/src/com/vaadin/client/ui/FocusElementPanel.java
@@ -21,7 +21,6 @@ import com.google.gwt.dom.client.Style;
import com.google.gwt.dom.client.Style.Position;
import com.google.gwt.dom.client.Style.Unit;
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.ui.Widget;
import com.google.gwt.user.client.ui.impl.FocusImpl;
@@ -47,9 +46,7 @@ public class FocusElementPanel extends SimpleFocusablePanel {
style.setLeft(0, Unit.PX);
getElement().appendChild(focusElement);
/* Sink from focusElement too as focus and blur don't bubble */
- DOM.sinkEvents(
- (com.google.gwt.user.client.Element) focusElement.cast(),
- Event.FOCUSEVENTS);
+ DOM.sinkEvents(focusElement, Event.FOCUSEVENTS);
// revert to original, not focusable
getElement().setPropertyObject("tabIndex", null);
} else {
@@ -67,11 +64,9 @@ public class FocusElementPanel extends SimpleFocusablePanel {
@Override
public void setFocus(boolean focus) {
if (focus) {
- FocusImpl.getFocusImplForPanel().focus(
- (Element) focusElement.cast());
+ FocusImpl.getFocusImplForPanel().focus(focusElement);
} else {
- FocusImpl.getFocusImplForPanel()
- .blur((Element) focusElement.cast());
+ FocusImpl.getFocusImplForPanel().blur(focusElement);
}
}
@@ -86,7 +81,7 @@ public class FocusElementPanel extends SimpleFocusablePanel {
/**
* @return the focus element
*/
- public Element getFocusElement() {
+ public com.google.gwt.user.client.Element getFocusElement() {
return focusElement.cast();
}
}
diff --git a/client/src/com/vaadin/client/ui/FocusableScrollPanel.java b/client/src/com/vaadin/client/ui/FocusableScrollPanel.java
index 01d39392af..d01ffc00ff 100644
--- a/client/src/com/vaadin/client/ui/FocusableScrollPanel.java
+++ b/client/src/com/vaadin/client/ui/FocusableScrollPanel.java
@@ -29,7 +29,6 @@ import com.google.gwt.event.dom.client.ScrollEvent;
import com.google.gwt.event.dom.client.ScrollHandler;
import com.google.gwt.event.shared.HandlerRegistration;
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.ui.ScrollPanel;
import com.google.gwt.user.client.ui.Widget;
@@ -75,9 +74,7 @@ public class FocusableScrollPanel extends SimpleFocusablePanel implements
style.setLeft(0, Unit.PX);
getElement().appendChild(focusElement);
/* Sink from focusElemet too as focusa and blur don't bubble */
- DOM.sinkEvents(
- (com.google.gwt.user.client.Element) focusElement
- .cast(), Event.FOCUSEVENTS);
+ DOM.sinkEvents(focusElement, Event.FOCUSEVENTS);
// revert to original, not focusable
getElement().setPropertyObject("tabIndex", null);
@@ -98,11 +95,9 @@ public class FocusableScrollPanel extends SimpleFocusablePanel implements
public void setFocus(boolean focus) {
if (useFakeFocusElement()) {
if (focus) {
- FocusImpl.getFocusImplForPanel().focus(
- (Element) focusElement.cast());
+ FocusImpl.getFocusImplForPanel().focus(focusElement);
} else {
- FocusImpl.getFocusImplForPanel().blur(
- (Element) focusElement.cast());
+ FocusImpl.getFocusImplForPanel().blur(focusElement);
}
} else {
super.setFocus(focus);
@@ -192,7 +187,7 @@ public class FocusableScrollPanel extends SimpleFocusablePanel implements
});
}
- public Element getFocusElement() {
+ public com.google.gwt.user.client.Element getFocusElement() {
if (useFakeFocusElement()) {
return focusElement.cast();
} else {
diff --git a/client/src/com/vaadin/client/ui/FontIcon.java b/client/src/com/vaadin/client/ui/FontIcon.java
new file mode 100644
index 0000000000..04640fab06
--- /dev/null
+++ b/client/src/com/vaadin/client/ui/FontIcon.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.ui;
+
+import com.google.gwt.http.client.URL;
+import com.google.gwt.user.client.DOM;
+import com.vaadin.shared.ApplicationConstants;
+
+/**
+ * A font-based icon implementation.
+ * <p>
+ * The icon represents a specific character (identified by codepoint,
+ * {@link #getCodepoint()}, {@link #setCodepoint(int)}) within a specific font
+ * (identified by font-family, {@link #getFontFamily()},
+ * {@link #setFontFamily(String)}).
+ * </p>
+ *
+ * @since 7.2
+ * @author Vaadin Ltd
+ */
+public class FontIcon extends Icon {
+
+ private int codepoint;
+ private String fontFamily;
+
+ public FontIcon() {
+ setElement(DOM.createSpan());
+ setStyleName(CLASSNAME);
+ }
+
+ @Override
+ public void setUri(String uri) {
+ String[] parts = uri.substring(
+ ApplicationConstants.FONTICON_PROTOCOL_PREFIX.length()).split(
+ "/");
+ setFontFamily(URL.decode(parts[0]));
+ setCodepoint(Integer.parseInt(parts[1], 16));
+ }
+
+ /**
+ * Not implemeted for {@link FontIcon} yet.
+ *
+ * @see com.vaadin.client.ui.Icon#setAlternateText(java.lang.String)
+ */
+ @Override
+ public void setAlternateText(String alternateText) {
+ // TODO this is mostly for WAI-ARIA and should be implemented in an
+ // appropriate way.
+
+ }
+
+ /**
+ * Sets the font-family from which this icon comes. Use
+ * {@link #setCodepoint(int)} to specify a particular icon (character)
+ * within the font.
+ *
+ * @param fontFamily
+ * font-family name
+ */
+ protected void setFontFamily(String fontFamily) {
+ if (this.fontFamily != null) {
+ removeStyleName(getFontStylename());
+ }
+ this.fontFamily = fontFamily;
+ if (fontFamily != null) {
+ addStyleName(getFontStylename());
+ }
+ }
+
+ /**
+ * Gets the font-family from which this icon comes. Use
+ * {@link #getCodepoint()} to find out which particular icon (character)
+ * within the font this is.
+ *
+ * @return font-family name
+ */
+ public String getFontFamily() {
+ return fontFamily;
+ }
+
+ /**
+ * Sets the codepoint indicating which particular icon (character) within
+ * the font-family this is.
+ *
+ * @param codepoint
+ */
+ protected void setCodepoint(int codepoint) {
+ this.codepoint = codepoint;
+ getElement().setInnerText(new String(Character.toChars(codepoint)));
+ }
+
+ /**
+ * Gets the codepoint indicating which particular icon (character) within
+ * the font-family this is.
+ *
+ * @return
+ */
+ public int getCodepoint() {
+ return codepoint;
+ }
+
+ /**
+ * Get the font-family based stylename used to apply the font-family.
+ *
+ * @since 7.2
+ * @return stylename used to apply font-family
+ */
+ protected String getFontStylename() {
+ if (fontFamily == null) {
+ return null;
+ }
+ return fontFamily.replace(' ', '-');
+ }
+
+ /**
+ * Checks whether or not the given uri is a font icon uri. Does not check
+ * whether or not the font icon is available and can be rendered.
+ *
+ * @since 7.2
+ * @param uri
+ * @return true if it's a fonticon uri
+ */
+ public static boolean isFontIconUri(String uri) {
+ return uri != null
+ && uri.startsWith(ApplicationConstants.FONTICON_PROTOCOL_PREFIX);
+ }
+
+ @Override
+ public String getUri() {
+ if (fontFamily == null) {
+ return null;
+ }
+ return ApplicationConstants.FONTICON_PROTOCOL_PREFIX + fontFamily + "/"
+ + codepoint;
+ }
+}
diff --git a/client/src/com/vaadin/client/ui/Icon.java b/client/src/com/vaadin/client/ui/Icon.java
index 9d5829c079..219692161e 100644
--- a/client/src/com/vaadin/client/ui/Icon.java
+++ b/client/src/com/vaadin/client/ui/Icon.java
@@ -13,45 +13,40 @@
* License for the specific language governing permissions and limitations under
* the License.
*/
-
package com.vaadin.client.ui;
-import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.UIObject;
import com.vaadin.client.ApplicationConnection;
-public class Icon extends UIObject {
- public static final String CLASSNAME = "v-icon";
- private final ApplicationConnection client;
- private String myUri;
-
- public Icon(ApplicationConnection client) {
- setElement(DOM.createImg());
- DOM.setElementProperty(getElement(), "alt", "");
- setStyleName(CLASSNAME);
- this.client = client;
- }
+/**
+ * An abstract representation of an icon.
+ *
+ * @since 7.2
+ * @author Vaadin Ltd
+ */
+public abstract class Icon extends UIObject {
- public Icon(ApplicationConnection client, String uidlUri) {
- this(client);
- setUri(uidlUri);
- }
+ public static final String CLASSNAME = "v-icon";
- public void setUri(String uidlUri) {
- if (!uidlUri.equals(myUri)) {
- /*
- * Start sinking onload events, widgets responsibility to react. We
- * must do this BEFORE we set src as IE fires the event immediately
- * if the image is found in cache (#2592).
- */
- sinkEvents(Event.ONLOAD);
+ /**
+ * Sets the URI for the icon. The URI should be run trough
+ * {@link ApplicationConnection#translateVaadinUri(String)} before setting.
+ * <p>
+ * This might be a URL referencing a image (e.g {@link ImageIcon}) or a
+ * custom URI (e.g {@link FontIcon}).
+ * </p>
+ *
+ * @param uri
+ * the URI for this icon
+ */
+ public abstract void setUri(String uri);
- String uri = client.translateVaadinUri(uidlUri);
- DOM.setElementProperty(getElement(), "src", uri);
- myUri = uidlUri;
- }
- }
+ /**
+ * Gets the current URI for this icon.
+ *
+ * @return URI in use
+ */
+ public abstract String getUri();
/**
* Sets the alternate text for the icon.
@@ -59,9 +54,6 @@ public class Icon extends UIObject {
* @param alternateText
* with the alternate text.
*/
- public void setAlternateText(String alternateText) {
- getElement().setAttribute("alt",
- alternateText == null ? "" : alternateText);
- }
+ public abstract void setAlternateText(String alternateText);
}
diff --git a/client/src/com/vaadin/client/ui/ImageIcon.java b/client/src/com/vaadin/client/ui/ImageIcon.java
new file mode 100644
index 0000000000..787b0175aa
--- /dev/null
+++ b/client/src/com/vaadin/client/ui/ImageIcon.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.vaadin.client.ui;
+
+import com.google.gwt.core.client.Scheduler;
+import com.google.gwt.core.client.Scheduler.ScheduledCommand;
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.Event;
+import com.vaadin.client.BrowserInfo;
+
+/**
+ * A image based implementation of {@link Icon}.
+ * <p>
+ * The image is loaded from the given URL ( {@link #setUri(String)}) and
+ * displayed in full.
+ * </p>
+ *
+ * @author Vaadin Ltd
+ */
+public class ImageIcon extends Icon {
+ public static final String CLASSNAME = "v-icon";
+
+ public ImageIcon() {
+ setElement(DOM.createImg());
+ setStyleName(CLASSNAME);
+ }
+
+ @Override
+ public void setUri(final String imageUrl) {
+ /*
+ * Start sinking onload events, widgets responsibility to react. We must
+ * do this BEFORE we set src as IE fires the event immediately if the
+ * image is found in cache (#2592).
+ */
+ sinkEvents(Event.ONLOAD);
+
+ if (BrowserInfo.get().isIE()) {
+ // apply src later for IE, to ensure onload is fired
+ Scheduler.get().scheduleDeferred(new ScheduledCommand() {
+ @Override
+ public void execute() {
+ DOM.setElementProperty(getElement(), "src", imageUrl);
+ }
+ });
+ }
+
+ DOM.setElementProperty(getElement(), "src", imageUrl);
+
+ }
+
+ @Override
+ public void setAlternateText(String alternateText) {
+ getElement().setAttribute("alt",
+ alternateText == null ? "" : alternateText);
+ }
+
+ @Override
+ public String getUri() {
+ return DOM.getElementProperty(getElement(), "src");
+ }
+
+}
diff --git a/client/src/com/vaadin/client/ui/LayoutClickEventHandler.java b/client/src/com/vaadin/client/ui/LayoutClickEventHandler.java
index adff5233fd..af398a1e07 100644
--- a/client/src/com/vaadin/client/ui/LayoutClickEventHandler.java
+++ b/client/src/com/vaadin/client/ui/LayoutClickEventHandler.java
@@ -16,7 +16,6 @@
package com.vaadin.client.ui;
import com.google.gwt.dom.client.NativeEvent;
-import com.google.gwt.user.client.Element;
import com.vaadin.client.ComponentConnector;
import com.vaadin.client.MouseEventDetailsBuilder;
import com.vaadin.shared.EventId;
@@ -34,10 +33,12 @@ public abstract class LayoutClickEventHandler extends AbstractClickEventHandler
super(connector, clickEventIdentifier);
}
- protected abstract ComponentConnector getChildComponent(Element element);
+ protected abstract ComponentConnector getChildComponent(
+ com.google.gwt.user.client.Element element);
protected ComponentConnector getChildComponent(NativeEvent event) {
- return getChildComponent((Element) event.getEventTarget().cast());
+ return getChildComponent((com.google.gwt.user.client.Element) event
+ .getEventTarget().cast());
}
@Override
diff --git a/client/src/com/vaadin/client/ui/ShortcutActionHandler.java b/client/src/com/vaadin/client/ui/ShortcutActionHandler.java
index 525900732f..1ed044ffda 100644
--- a/client/src/com/vaadin/client/ui/ShortcutActionHandler.java
+++ b/client/src/com/vaadin/client/ui/ShortcutActionHandler.java
@@ -20,9 +20,9 @@ import java.util.ArrayList;
import java.util.Iterator;
import com.google.gwt.core.client.Scheduler;
+import com.google.gwt.dom.client.Element;
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.ui.HasWidgets;
import com.google.gwt.user.client.ui.KeyboardListener;
diff --git a/client/src/com/vaadin/client/ui/SubPartAware.java b/client/src/com/vaadin/client/ui/SubPartAware.java
index a7d72fab01..fea8c2dc4a 100644
--- a/client/src/com/vaadin/client/ui/SubPartAware.java
+++ b/client/src/com/vaadin/client/ui/SubPartAware.java
@@ -15,9 +15,8 @@
*/
package com.vaadin.client.ui;
-import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.ui.Widget;
-import com.vaadin.client.ComponentLocator;
+import com.vaadin.client.componentlocator.ComponentLocator;
/**
* Interface implemented by {@link Widget}s which can provide identifiers for at
@@ -36,7 +35,7 @@ public interface SubPartAware {
* @return The element identified by subPart or null if the element could
* not be found.
*/
- Element getSubPartElement(String subPart);
+ com.google.gwt.user.client.Element getSubPartElement(String subPart);
/**
* Provides an identifier that identifies the element within the component.
@@ -57,6 +56,6 @@ public interface SubPartAware {
* @return An identifier that uniquely identifies {@code subElement} or null
* if no identifier could be provided.
*/
- String getSubPartName(Element subElement);
+ String getSubPartName(com.google.gwt.user.client.Element subElement);
-} \ No newline at end of file
+}
diff --git a/client/src/com/vaadin/client/ui/VAbsoluteLayout.java b/client/src/com/vaadin/client/ui/VAbsoluteLayout.java
index dc080bcf7d..bbec8b9e6c 100644
--- a/client/src/com/vaadin/client/ui/VAbsoluteLayout.java
+++ b/client/src/com/vaadin/client/ui/VAbsoluteLayout.java
@@ -17,9 +17,9 @@ package com.vaadin.client.ui;
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.user.client.DOM;
-import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.ui.ComplexPanel;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.Widget;
diff --git a/client/src/com/vaadin/client/ui/VAbstractSplitPanel.java b/client/src/com/vaadin/client/ui/VAbstractSplitPanel.java
index 1cc25c741e..a2de144ad2 100644
--- a/client/src/com/vaadin/client/ui/VAbstractSplitPanel.java
+++ b/client/src/com/vaadin/client/ui/VAbstractSplitPanel.java
@@ -19,6 +19,7 @@ package com.vaadin.client.ui;
import java.util.Collections;
import java.util.List;
+import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Node;
import com.google.gwt.dom.client.Style;
import com.google.gwt.event.dom.client.TouchCancelEvent;
@@ -32,7 +33,6 @@ import com.google.gwt.event.dom.client.TouchStartHandler;
import com.google.gwt.event.shared.EventHandler;
import com.google.gwt.event.shared.GwtEvent;
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.ui.ComplexPanel;
import com.google.gwt.user.client.ui.Widget;
diff --git a/client/src/com/vaadin/client/ui/VAccordion.java b/client/src/com/vaadin/client/ui/VAccordion.java
index f87186fe37..d348e6863b 100644
--- a/client/src/com/vaadin/client/ui/VAccordion.java
+++ b/client/src/com/vaadin/client/ui/VAccordion.java
@@ -15,56 +15,53 @@
*/
package com.vaadin.client.ui;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
+import com.google.gwt.dom.client.Element;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
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.ui.ComplexPanel;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.ComponentConnector;
-import com.vaadin.client.ConnectorMap;
-import com.vaadin.client.UIDL;
import com.vaadin.client.Util;
import com.vaadin.client.VCaption;
import com.vaadin.client.ui.TouchScrollDelegate.TouchScrollHandler;
-import com.vaadin.shared.ui.tabsheet.TabsheetBaseConstants;
-import com.vaadin.shared.ui.tabsheet.TabsheetConstants;
+import com.vaadin.shared.ComponentConstants;
+import com.vaadin.shared.ui.accordion.AccordionState;
+import com.vaadin.shared.ui.tabsheet.TabState;
+import com.vaadin.shared.ui.tabsheet.TabsheetServerRpc;
public class VAccordion extends VTabsheetBase {
- public static final String CLASSNAME = "v-accordion";
+ public static final String CLASSNAME = AccordionState.PRIMARY_STYLE_NAME;
private Set<Widget> widgets = new HashSet<Widget>();
- /** For internal use only. May be removed or replaced in the future. */
- public HashMap<StackItem, UIDL> lazyUpdateMap = new HashMap<StackItem, UIDL>();
-
- private StackItem openTab = null;
+ private StackItem openTab;
/** For internal use only. May be removed or replaced in the future. */
- public int selectedUIDLItemIndex = -1;
+ public int selectedItemIndex = -1;
private final TouchScrollHandler touchScrollHandler;
public VAccordion() {
super(CLASSNAME);
+
touchScrollHandler = TouchScrollDelegate.enableTouchScrolling(this);
}
@Override
- public void renderTab(UIDL tabUidl, int index, boolean selected,
- boolean hidden) {
+ public void renderTab(TabState tabState, int index) {
StackItem item;
int itemIndex;
+
if (getWidgetCount() <= index) {
// Create stackItem and render caption
- item = new StackItem(tabUidl);
+ item = new StackItem();
if (getWidgetCount() == 0) {
item.addStyleDependentName("first");
}
@@ -72,23 +69,19 @@ public class VAccordion extends VTabsheetBase {
add(item, getElement());
} else {
item = getStackItem(index);
- item = moveStackItemIfNeeded(item, index, tabUidl);
+
itemIndex = index;
}
- item.updateCaption(tabUidl);
+ item.updateCaption(tabState);
- item.updateTabStyleName(tabUidl
- .getStringAttribute(TabsheetConstants.TAB_STYLE_NAME));
+ item.updateTabStyleName(tabState.styleName);
- item.setVisible(!hidden);
-
- if (selected) {
- selectedUIDLItemIndex = itemIndex;
- }
+ item.setVisible(tabState.visible);
+ }
- if (tabUidl.getChildCount() > 0) {
- lazyUpdateMap.put(item, tabUidl.getChildUIDL(0));
- }
+ @Override
+ public void selectTab(int index) {
+ selectedItemIndex = index;
}
@Override
@@ -112,69 +105,6 @@ public class VAccordion extends VTabsheetBase {
}
}
- /**
- * This method tries to find out if a tab has been rendered with a different
- * index previously. If this is the case it re-orders the children so the
- * same StackItem is used for rendering this time. E.g. if the first tab has
- * been removed all tabs which contain cached content must be moved 1 step
- * up to preserve the cached content.
- *
- * @param item
- * @param newIndex
- * @param tabUidl
- * @return
- */
- private StackItem moveStackItemIfNeeded(StackItem item, int newIndex,
- UIDL tabUidl) {
- UIDL tabContentUIDL = null;
- ComponentConnector tabContent = null;
- if (tabUidl.getChildCount() > 0) {
- tabContentUIDL = tabUidl.getChildUIDL(0);
- tabContent = client.getPaintable(tabContentUIDL);
- }
-
- Widget itemWidget = item.getComponent();
- if (tabContent != null) {
- if (tabContent.getWidget() != itemWidget) {
- /*
- * This is not the same widget as before, find out if it has
- * been moved
- */
- int oldIndex = -1;
- StackItem oldItem = null;
- for (int i = 0; i < getWidgetCount(); i++) {
- Widget w = getWidget(i);
- oldItem = (StackItem) w;
- if (tabContent == oldItem.getComponent()) {
- oldIndex = i;
- break;
- }
- }
-
- if (oldIndex != -1 && oldIndex > newIndex) {
- /*
- * The tab has previously been rendered in another position
- * so we must move the cached content to correct position.
- * We move only items with oldIndex > newIndex to prevent
- * moving items already rendered in this update. If for
- * instance tabs 1,2,3 are removed and added as 3,2,1 we
- * cannot re-use "1" when we get to the third tab.
- */
- insert(oldItem, getElement(), newIndex, true);
- return oldItem;
- }
- }
- } else {
- // Tab which has never been loaded. Must assure we use an empty
- // StackItem
- Widget oldWidget = item.getComponent();
- if (oldWidget != null) {
- oldWidget.removeFromParent();
- }
- }
- return item;
- }
-
/** For internal use only. May be removed or replaced in the future. */
public void open(int itemIndex) {
StackItem item = (StackItem) getWidget(itemIndex);
@@ -210,10 +140,14 @@ public class VAccordion extends VTabsheetBase {
public void onSelectTab(StackItem item) {
final int index = getWidgetIndex(item);
+
if (index != activeTabIndex && !disabled && !readonly
&& !disabledTabKeys.contains(tabKeys.get(index))) {
+
addStyleDependentName("loading");
- client.updateVariable(id, "selected", "" + tabKeys.get(index), true);
+
+ connector.getRpcProxy(TabsheetServerRpc.class).setSelected(
+ tabKeys.get(index).toString());
}
}
@@ -296,7 +230,7 @@ public class VAccordion extends VTabsheetBase {
private Element captionNode = DOM.createDiv();
private String styleName;
- public StackItem(UIDL tabUidl) {
+ public StackItem() {
setElement(DOM.createDiv());
caption = new VCaption(client);
caption.addClickHandler(this);
@@ -328,8 +262,8 @@ public class VAccordion extends VTabsheetBase {
onSelectTab(this);
}
- public Element getContainerElement() {
- return content;
+ public com.google.gwt.user.client.Element getContainerElement() {
+ return DOM.asOld(content);
}
public Widget getChildWidget() {
@@ -343,6 +277,7 @@ public class VAccordion extends VTabsheetBase {
public void replaceWidget(Widget newWidget) {
if (getWidgetCount() > 1) {
Widget oldWidget = getWidget(1);
+ remove(oldWidget);
widgets.remove(oldWidget);
}
add(newWidget, content);
@@ -375,10 +310,17 @@ public class VAccordion extends VTabsheetBase {
return open;
}
- public void setContent(UIDL contentUidl) {
- final ComponentConnector newPntbl = client
- .getPaintable(contentUidl);
- Widget newWidget = newPntbl.getWidget();
+ /**
+ * Updates the content of the open tab of the accordion.
+ *
+ * This method is mostly for internal use and may change in future
+ * versions.
+ *
+ * @since 7.2
+ * @param newWidget
+ * new content
+ */
+ public void setContent(Widget newWidget) {
if (getChildWidget() == null) {
add(newWidget, content);
widgets.add(newWidget);
@@ -395,14 +337,19 @@ public class VAccordion extends VTabsheetBase {
onSelectTab(this);
}
- public void updateCaption(UIDL uidl) {
+ public void updateCaption(TabState tabState) {
// TODO need to call this because the caption does not have an owner
caption.updateCaptionWithoutOwner(
- uidl.getStringAttribute(TabsheetBaseConstants.ATTRIBUTE_TAB_CAPTION),
- uidl.hasAttribute(TabsheetBaseConstants.ATTRIBUTE_TAB_DISABLED),
- uidl.hasAttribute(TabsheetBaseConstants.ATTRIBUTE_TAB_DESCRIPTION),
- uidl.hasAttribute(TabsheetBaseConstants.ATTRIBUTE_TAB_ERROR_MESSAGE),
- uidl.getStringAttribute(TabsheetBaseConstants.ATTRIBUTE_TAB_ICON));
+ tabState.caption,
+ !tabState.enabled,
+ hasAttribute(tabState.description),
+ hasAttribute(tabState.componentError),
+ connector.getResourceUrl(ComponentConstants.ICON_RESOURCE
+ + tabState.key));
+ }
+
+ private boolean hasAttribute(String string) {
+ return string != null && !string.trim().isEmpty();
}
/**
@@ -450,20 +397,7 @@ public class VAccordion extends VTabsheetBase {
clear();
}
- boolean isDynamicWidth() {
- ComponentConnector paintable = ConnectorMap.get(client).getConnector(
- this);
- return paintable.isUndefinedWidth();
- }
-
- boolean isDynamicHeight() {
- ComponentConnector paintable = ConnectorMap.get(client).getConnector(
- this);
- return paintable.isUndefinedHeight();
- }
-
@Override
- @SuppressWarnings("unchecked")
public Iterator<Widget> getWidgetIterator() {
return widgets.iterator();
}
@@ -489,7 +423,7 @@ public class VAccordion extends VTabsheetBase {
}
Widget w = stackItem.getChildWidget();
if (w != null) {
- return ConnectorMap.get(client).getConnector(w);
+ return getConnectorForWidget(w);
}
}
diff --git a/client/src/com/vaadin/client/ui/VButton.java b/client/src/com/vaadin/client/ui/VButton.java
index 840ed39114..98df258f57 100644
--- a/client/src/com/vaadin/client/ui/VButton.java
+++ b/client/src/com/vaadin/client/ui/VButton.java
@@ -225,10 +225,16 @@ public class VButton extends FocusWidget implements ClickHandler {
DOM.eventPreventDefault(event);
}
break;
+ case Event.ONMOUSEOVER:
+ if (isCapturing && isTargetInsideButton(event)) {
+ // This means a mousedown happened on the button and a mouseup
+ // has not happened yet
+ setHovering(true);
+ addStyleName(CLASSNAME_PRESSED);
+ }
+ break;
case Event.ONMOUSEOUT:
- Element to = event.getRelatedTarget();
- if (getElement().isOrHasChild(DOM.eventGetTarget(event))
- && (to == null || !getElement().isOrHasChild(to))) {
+ if (isTargetInsideButton(event)) {
if (clickPending
&& Math.abs(mousedownX - event.getClientX()) < MOVE_THRESHOLD
&& Math.abs(mousedownY - event.getClientY()) < MOVE_THRESHOLD) {
@@ -236,8 +242,6 @@ public class VButton extends FocusWidget implements ClickHandler {
break;
}
clickPending = false;
- if (isCapturing) {
- }
setHovering(false);
removeStyleName(CLASSNAME_PRESSED);
}
@@ -290,6 +294,15 @@ public class VButton extends FocusWidget implements ClickHandler {
}
}
+ /**
+ * Check if the event occurred over an element which is part of this button
+ */
+ private boolean isTargetInsideButton(Event event) {
+ Element to = event.getRelatedTarget();
+ return getElement().isOrHasChild(DOM.eventGetTarget(event))
+ && (to == null || !getElement().isOrHasChild(to));
+ }
+
final void setHovering(boolean hovering) {
if (hovering != isHovering()) {
isHovering = hovering;
diff --git a/client/src/com/vaadin/client/ui/VCalendar.java b/client/src/com/vaadin/client/ui/VCalendar.java
index 38bcc0b14f..965d2a148e 100644
--- a/client/src/com/vaadin/client/ui/VCalendar.java
+++ b/client/src/com/vaadin/client/ui/VCalendar.java
@@ -22,10 +22,10 @@ import java.util.Comparator;
import java.util.Date;
import java.util.List;
+import com.google.gwt.dom.client.Element;
import com.google.gwt.event.dom.client.ContextMenuEvent;
import com.google.gwt.event.dom.client.ContextMenuHandler;
import com.google.gwt.i18n.client.DateTimeFormat;
-import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.DockPanel;
import com.google.gwt.user.client.ui.ScrollPanel;
diff --git a/client/src/com/vaadin/client/ui/VCalendarPanel.java b/client/src/com/vaadin/client/ui/VCalendarPanel.java
index c6832226a4..74462e501d 100644
--- a/client/src/com/vaadin/client/ui/VCalendarPanel.java
+++ b/client/src/com/vaadin/client/ui/VCalendarPanel.java
@@ -21,6 +21,7 @@ import java.util.Iterator;
import com.google.gwt.aria.client.Roles;
import com.google.gwt.aria.client.SelectedValue;
+import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Node;
import com.google.gwt.event.dom.client.BlurEvent;
import com.google.gwt.event.dom.client.BlurHandler;
@@ -43,7 +44,6 @@ import com.google.gwt.event.dom.client.MouseOutHandler;
import com.google.gwt.event.dom.client.MouseUpEvent;
import com.google.gwt.event.dom.client.MouseUpHandler;
import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.FlexTable;
@@ -170,8 +170,6 @@ public class VCalendarPanel extends FocusableFlexTable implements
private Resolution resolution = Resolution.YEAR;
- private int focusedRow;
-
private Timer mouseTimer;
private Date value;
@@ -256,7 +254,6 @@ public class VCalendarPanel extends FocusableFlexTable implements
if (curday.getDate().equals(date)) {
curday.addStyleDependentName(CN_FOCUSED);
focusedDay = curday;
- focusedRow = i;
return;
}
}
@@ -741,7 +738,6 @@ public class VCalendarPanel extends FocusableFlexTable implements
}
if (curr.equals(focusedDate)) {
focusedDay = day;
- focusedRow = weekOfMonth;
if (hasFocus) {
day.addStyleDependentName(CN_FOCUSED);
}
@@ -1800,10 +1796,8 @@ public class VCalendarPanel extends FocusableFlexTable implements
* Updates the valus to correspond to the values in value
*/
public void updateTimes() {
- boolean selected = true;
if (value == null) {
value = new Date();
- selected = false;
}
if (getDateTimeService().isTwelveHourClock()) {
int h = value.getHours();
@@ -1838,10 +1832,6 @@ public class VCalendarPanel extends FocusableFlexTable implements
}
- private int getMilliseconds() {
- return DateTimeService.getMilliseconds(value);
- }
-
private DateTimeService getDateTimeService() {
if (dateTimeService == null) {
dateTimeService = new DateTimeService();
@@ -2039,7 +2029,6 @@ public class VCalendarPanel extends FocusableFlexTable implements
private static final String SUBPART_HOUR_SELECT = "h";
private static final String SUBPART_MINUTE_SELECT = "m";
private static final String SUBPART_SECS_SELECT = "s";
- private static final String SUBPART_MSECS_SELECT = "ms";
private static final String SUBPART_AMPM_SELECT = "ampm";
private static final String SUBPART_DAY = "day";
private static final String SUBPART_MONTH_YEAR_HEADER = "header";
@@ -2049,7 +2038,7 @@ public class VCalendarPanel extends FocusableFlexTable implements
private Date rangeEnd;
@Override
- public String getSubPartName(Element subElement) {
+ public String getSubPartName(com.google.gwt.user.client.Element subElement) {
if (contains(nextMonth, subElement)) {
return SUBPART_NEXT_MONTH;
} else if (contains(prevMonth, subElement)) {
@@ -2108,7 +2097,7 @@ public class VCalendarPanel extends FocusableFlexTable implements
}
@Override
- public Element getSubPartElement(String subPart) {
+ public com.google.gwt.user.client.Element getSubPartElement(String subPart) {
if (SUBPART_NEXT_MONTH.equals(subPart)) {
return nextMonth.getElement();
}
@@ -2153,7 +2142,8 @@ public class VCalendarPanel extends FocusableFlexTable implements
}
if (SUBPART_MONTH_YEAR_HEADER.equals(subPart)) {
- return (Element) getCellFormatter().getElement(0, 2).getChild(0);
+ return DOM.asOld((Element) getCellFormatter().getElement(0, 2)
+ .getChild(0));
}
return null;
}
diff --git a/client/src/com/vaadin/client/ui/VCheckBox.java b/client/src/com/vaadin/client/ui/VCheckBox.java
index 94b37f418e..59058caf81 100644
--- a/client/src/com/vaadin/client/ui/VCheckBox.java
+++ b/client/src/com/vaadin/client/ui/VCheckBox.java
@@ -16,10 +16,10 @@
package com.vaadin.client.ui;
+import com.google.gwt.dom.client.Element;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.BrowserInfo;
@@ -93,7 +93,7 @@ public class VCheckBox extends com.google.gwt.user.client.ui.CheckBox implements
*/
private Element getCheckBoxElement() {
// FIXME: Would love to use a better way to access the checkbox element
- return (Element) getElement().getFirstChildElement();
+ return getElement().getFirstChildElement();
}
@Override
diff --git a/client/src/com/vaadin/client/ui/VContextMenu.java b/client/src/com/vaadin/client/ui/VContextMenu.java
index 02ee5b07c5..7aa4035f10 100644
--- a/client/src/com/vaadin/client/ui/VContextMenu.java
+++ b/client/src/com/vaadin/client/ui/VContextMenu.java
@@ -18,6 +18,7 @@ package com.vaadin.client.ui;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
+import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NodeList;
import com.google.gwt.dom.client.TableRowElement;
import com.google.gwt.dom.client.TableSectionElement;
@@ -43,7 +44,6 @@ import com.google.gwt.event.logical.shared.CloseHandler;
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.Window;
import com.google.gwt.user.client.ui.MenuBar;
import com.google.gwt.user.client.ui.MenuItem;
@@ -279,7 +279,7 @@ public class VContextMenu extends VOverlay implements SubPartAware {
}
@Override
- public Element getSubPartElement(String subPart) {
+ public com.google.gwt.user.client.Element getSubPartElement(String subPart) {
int index = Integer.parseInt(subPart.substring(6));
// ApplicationConnection.getConsole().log(
// "Searching element for selection index " + index);
@@ -290,7 +290,7 @@ public class VContextMenu extends VOverlay implements SubPartAware {
}
@Override
- public String getSubPartName(Element subElement) {
+ public String getSubPartName(com.google.gwt.user.client.Element subElement) {
if (getElement().isOrHasChild(subElement)) {
com.google.gwt.dom.client.Element e = subElement;
{
diff --git a/client/src/com/vaadin/client/ui/VCustomLayout.java b/client/src/com/vaadin/client/ui/VCustomLayout.java
index 7a33f17314..7f5e4d4028 100644
--- a/client/src/com/vaadin/client/ui/VCustomLayout.java
+++ b/client/src/com/vaadin/client/ui/VCustomLayout.java
@@ -19,6 +19,7 @@ package com.vaadin.client.ui;
import java.util.HashMap;
import java.util.Iterator;
+import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.ImageElement;
import com.google.gwt.dom.client.NodeList;
import com.google.gwt.dom.client.Style;
@@ -26,7 +27,6 @@ import com.google.gwt.dom.client.Style.BorderStyle;
import com.google.gwt.dom.client.Style.Position;
import com.google.gwt.dom.client.Style.Unit;
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.ui.ComplexPanel;
import com.google.gwt.user.client.ui.Widget;
@@ -242,12 +242,10 @@ public class VCustomLayout extends ComplexPanel {
* parent about possible size change.
*/
private void initImgElements() {
- NodeList<com.google.gwt.dom.client.Element> nodeList = getElement()
- .getElementsByTagName("IMG");
+ NodeList<Element> nodeList = getElement().getElementsByTagName("IMG");
for (int i = 0; i < nodeList.getLength(); i++) {
- com.google.gwt.dom.client.ImageElement img = (ImageElement) nodeList
- .getItem(i);
- DOM.sinkEvents((Element) img.cast(), Event.ONLOAD);
+ ImageElement img = ImageElement.as(nodeList.getItem(i));
+ DOM.sinkEvents(img, Event.ONLOAD);
}
}
@@ -419,7 +417,7 @@ public class VCustomLayout extends ComplexPanel {
* @return true if layout function exists and was run successfully, else
* false.
*/
- public native boolean iLayoutJS(Element el)
+ public native boolean iLayoutJS(com.google.gwt.user.client.Element el)
/*-{
if(el && el.iLayoutJS) {
try {
diff --git a/client/src/com/vaadin/client/ui/VDateField.java b/client/src/com/vaadin/client/ui/VDateField.java
index 5b49711df1..cd9052dc73 100644
--- a/client/src/com/vaadin/client/ui/VDateField.java
+++ b/client/src/com/vaadin/client/ui/VDateField.java
@@ -19,11 +19,12 @@ package com.vaadin.client.ui;
import java.util.Date;
import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.HasEnabled;
import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.DateTimeService;
import com.vaadin.shared.ui.datefield.Resolution;
-public class VDateField extends FlowPanel implements Field {
+public class VDateField extends FlowPanel implements Field, HasEnabled {
public static final String CLASSNAME = "v-datefield";
@@ -161,10 +162,12 @@ public class VDateField extends FlowPanel implements Field {
this.readonly = readonly;
}
+ @Override
public boolean isEnabled() {
return enabled;
}
+ @Override
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
diff --git a/client/src/com/vaadin/client/ui/VDragAndDropWrapper.java b/client/src/com/vaadin/client/ui/VDragAndDropWrapper.java
index ccd7e2758e..237d2b55c2 100644
--- a/client/src/com/vaadin/client/ui/VDragAndDropWrapper.java
+++ b/client/src/com/vaadin/client/ui/VDragAndDropWrapper.java
@@ -22,13 +22,14 @@ import java.util.Map;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JsArrayString;
import com.google.gwt.core.client.Scheduler;
+import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.event.dom.client.MouseDownEvent;
import com.google.gwt.event.dom.client.MouseDownHandler;
import com.google.gwt.event.dom.client.TouchStartEvent;
import com.google.gwt.event.dom.client.TouchStartHandler;
import com.google.gwt.user.client.Command;
-import com.google.gwt.user.client.Element;
+import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.Widget;
@@ -81,6 +82,7 @@ public class VDragAndDropWrapper extends VCustomComponent implements
@Override
public void onMouseDown(MouseDownEvent event) {
if (getConnector().isEnabled()
+ && event.getNativeEvent().getButton() == Event.BUTTON_LEFT
&& startDrag(event.getNativeEvent())) {
event.preventDefault(); // prevent text selection
}
@@ -117,7 +119,7 @@ public class VDragAndDropWrapper extends VCustomComponent implements
transferable.setDragSource(getConnector());
ComponentConnector paintable = Util.findPaintable(client,
- (Element) event.getEventTarget().cast());
+ Element.as(event.getEventTarget()));
Widget widget = paintable.getWidget();
transferable.setData("component", paintable);
VDragEvent dragEvent = VDragAndDropManager.get().startDrag(
@@ -182,7 +184,7 @@ public class VDragAndDropWrapper extends VCustomComponent implements
}
}
- protected Element getDragStartElement() {
+ protected com.google.gwt.user.client.Element getDragStartElement() {
return getElement();
}
@@ -548,7 +550,13 @@ public class VDragAndDropWrapper extends VCustomComponent implements
return ConnectorMap.get(client).getConnector(this);
}
- protected native void hookHtml5DragStart(Element el)
+ /**
+ * @deprecated As of 7.2, call or override
+ * {@link #hookHtml5DragStart(Element)} instead
+ */
+ @Deprecated
+ protected native void hookHtml5DragStart(
+ com.google.gwt.user.client.Element el)
/*-{
var me = this;
el.addEventListener("dragstart", $entry(function(ev) {
@@ -557,11 +565,21 @@ public class VDragAndDropWrapper extends VCustomComponent implements
}-*/;
/**
+ * @since 7.2
+ */
+ protected void hookHtml5DragStart(Element el) {
+ hookHtml5DragStart(DOM.asOld(el));
+ }
+
+ /**
* Prototype code, memory leak risk.
*
* @param el
+ * @deprecated As of 7.2, call or override {@link #hookHtml5Events(Element)}
+ * instead
*/
- protected native void hookHtml5Events(Element el)
+ @Deprecated
+ protected native void hookHtml5Events(com.google.gwt.user.client.Element el)
/*-{
var me = this;
@@ -582,6 +600,17 @@ public class VDragAndDropWrapper extends VCustomComponent implements
}), false);
}-*/;
+ /**
+ * Prototype code, memory leak risk.
+ *
+ * @param el
+ *
+ * @since 7.2
+ */
+ protected void hookHtml5Events(Element el) {
+ hookHtml5Events(DOM.asOld(el));
+ }
+
public boolean updateDropDetails(VDragEvent drag) {
VerticalDropLocation oldVL = verticalDropLocation;
verticalDropLocation = DDUtil.getVerticalDropLocation(getElement(),
diff --git a/client/src/com/vaadin/client/ui/VDragAndDropWrapperIE.java b/client/src/com/vaadin/client/ui/VDragAndDropWrapperIE.java
index d66856e857..d434a4adb1 100644
--- a/client/src/com/vaadin/client/ui/VDragAndDropWrapperIE.java
+++ b/client/src/com/vaadin/client/ui/VDragAndDropWrapperIE.java
@@ -18,14 +18,15 @@ package com.vaadin.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.google.gwt.dom.client.Element;
+import com.google.gwt.user.client.DOM;
import com.vaadin.client.VConsole;
public class VDragAndDropWrapperIE extends VDragAndDropWrapper {
private AnchorElement anchor = null;
@Override
- protected Element getDragStartElement() {
+ protected com.google.gwt.user.client.Element getDragStartElement() {
VConsole.log("IE get drag start element...");
Element div = getElement();
if (dragStartMode == HTML5) {
@@ -36,18 +37,20 @@ public class VDragAndDropWrapperIE extends VDragAndDropWrapper {
div.appendChild(anchor);
}
VConsole.log("IE get drag start element...");
- return (Element) anchor.cast();
+ return anchor.cast();
} else {
if (anchor != null) {
div.removeChild(anchor);
anchor = null;
}
- return div;
+ return DOM.asOld(div);
}
}
+ @Deprecated
@Override
- protected native void hookHtml5DragStart(Element el)
+ protected native void hookHtml5DragStart(
+ com.google.gwt.user.client.Element el)
/*-{
var me = this;
@@ -57,7 +60,13 @@ public class VDragAndDropWrapperIE extends VDragAndDropWrapper {
}-*/;
@Override
- protected native void hookHtml5Events(Element el)
+ protected void hookHtml5DragStart(Element el) {
+ hookHtml5DragStart(DOM.asOld(el));
+ }
+
+ @Deprecated
+ @Override
+ protected native void hookHtml5Events(com.google.gwt.user.client.Element el)
/*-{
var me = this;
@@ -78,4 +87,9 @@ public class VDragAndDropWrapperIE extends VDragAndDropWrapper {
}));
}-*/;
+ @Override
+ protected void hookHtml5Events(Element el) {
+ hookHtml5Events(DOM.asOld(el));
+ }
+
}
diff --git a/client/src/com/vaadin/client/ui/VEmbedded.java b/client/src/com/vaadin/client/ui/VEmbedded.java
index 0dd81efe82..d38d74f394 100644
--- a/client/src/com/vaadin/client/ui/VEmbedded.java
+++ b/client/src/com/vaadin/client/ui/VEmbedded.java
@@ -20,8 +20,8 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
+import com.google.gwt.dom.client.Element;
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.ui.HTML;
import com.vaadin.client.ApplicationConnection;
diff --git a/client/src/com/vaadin/client/ui/VFilterSelect.java b/client/src/com/vaadin/client/ui/VFilterSelect.java
index 0bc4e0d75b..e0ced98394 100644
--- a/client/src/com/vaadin/client/ui/VFilterSelect.java
+++ b/client/src/com/vaadin/client/ui/VFilterSelect.java
@@ -27,6 +27,7 @@ import java.util.Set;
import com.google.gwt.aria.client.Roles;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
+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.dom.client.Style.Unit;
@@ -47,18 +48,17 @@ import com.google.gwt.event.logical.shared.CloseEvent;
import com.google.gwt.event.logical.shared.CloseHandler;
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;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HTML;
-import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.PopupPanel.PositionCallback;
import com.google.gwt.user.client.ui.SuggestOracle.Suggestion;
import com.google.gwt.user.client.ui.TextBox;
+import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.BrowserInfo;
import com.vaadin.client.ComponentConnector;
@@ -122,10 +122,9 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
@Override
public String getDisplayString() {
final StringBuffer sb = new StringBuffer();
- if (iconUri != null) {
- sb.append("<img src=\"");
- sb.append(Util.escapeAttribute(iconUri));
- sb.append("\" alt=\"\" class=\"v-icon\" />");
+ final Icon icon = client.getIcon(iconUri);
+ if (icon != null) {
+ sb.append(icon.getElement().getString());
}
String content;
if ("".equals(caption)) {
@@ -556,8 +555,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
offsetHeight = getOffsetHeight();
final int desiredWidth = getMainWidth();
- Element menuFirstChild = menu.getElement().getFirstChildElement()
- .cast();
+ Element menuFirstChild = menu.getElement().getFirstChildElement();
int naturalMenuWidth = menuFirstChild.getOffsetWidth();
if (popupOuterPadding == -1) {
@@ -853,7 +851,8 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
private static final String SUBPART_PREFIX = "item";
@Override
- public Element getSubPartElement(String subPart) {
+ public com.google.gwt.user.client.Element getSubPartElement(
+ String subPart) {
int index = Integer.parseInt(subPart.substring(SUBPART_PREFIX
.length()));
@@ -863,7 +862,8 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
}
@Override
- public String getSubPartName(Element subElement) {
+ public String getSubPartName(
+ com.google.gwt.user.client.Element subElement) {
if (!getElement().isOrHasChild(subElement)) {
return null;
}
@@ -989,7 +989,13 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
}
};
- private final Image selectedItemIcon = new Image();
+ private class IconWidget extends Widget {
+ IconWidget(Icon icon) {
+ setElement(icon.getElement());
+ }
+ }
+
+ private IconWidget selectedItemIcon;
/** For internal use only. May be removed or replaced in the future. */
public ApplicationConnection client;
@@ -1031,7 +1037,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
/** For internal use only. May be removed or replaced in the future. */
public enum Select {
NONE, FIRST, LAST
- };
+ }
/** For internal use only. May be removed or replaced in the future. */
public Select selectPopupItemWhenResponseIsReceived = Select.NONE;
@@ -1112,21 +1118,6 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
tb = createTextBox();
suggestionPopup = createSuggestionPopup();
- selectedItemIcon.setStyleName("v-icon");
- selectedItemIcon.addLoadHandler(new LoadHandler() {
-
- @Override
- public void onLoad(LoadEvent event) {
- if (BrowserInfo.get().isIE8()) {
- // IE8 needs some help to discover it should reposition the
- // text field
- forceReflow();
- }
- updateRootWidth();
- updateSelectedIconPosition();
- }
- });
-
popupOpener.sinkEvents(Event.ONMOUSEDOWN);
Roles.getButtonRole()
.setAriaHiddenState(popupOpener.getElement(), true);
@@ -1412,20 +1403,36 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
* The URI of the icon
*/
public void setSelectedItemIcon(String iconUri) {
+
if (iconUri == null || iconUri.length() == 0) {
- if (selectedItemIcon.isAttached()) {
+ if (selectedItemIcon != null) {
panel.remove(selectedItemIcon);
- if (BrowserInfo.get().isIE8()) {
- // IE8 needs some help to discover it should reposition the
- // text field
- forceReflow();
- }
- updateRootWidth();
+ selectedItemIcon = null;
+ afterSelectedItemIconChange();
}
} else {
+ if (selectedItemIcon != null) {
+ panel.remove(selectedItemIcon);
+ }
+ selectedItemIcon = new IconWidget(client.getIcon(iconUri));
+ selectedItemIcon.addDomHandler(new LoadHandler() {
+ @Override
+ public void onLoad(LoadEvent event) {
+ afterSelectedItemIconChange();
+ }
+ }, LoadEvent.getType());
panel.insert(selectedItemIcon, 0);
- selectedItemIcon.setUrl(iconUri);
- updateRootWidth();
+ afterSelectedItemIconChange();
+ }
+ }
+
+ private void afterSelectedItemIconChange() {
+ if (BrowserInfo.get().isWebkit() || BrowserInfo.get().isIE8()) {
+ // Some browsers need a nudge to reposition the text field
+ forceReflow();
+ }
+ updateRootWidth();
+ if (selectedItemIcon != null) {
updateSelectedIconPosition();
}
}
@@ -1662,7 +1669,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
case KeyCodes.KEY_PAGEDOWN:
case KeyCodes.KEY_PAGEUP:
case KeyCodes.KEY_ESCAPE:
- ; // NOP
+ // NOP
break;
default:
if (textInputEnabled) {
@@ -1812,6 +1819,11 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
if (client.hasEventListeners(this, EventId.FOCUS)) {
client.updateVariable(paintableId, EventId.FOCUS, "", true);
}
+
+ ComponentConnector connector = ConnectorMap.get(client).getConnector(
+ this);
+ client.getVTooltip().showAssistive(
+ connector.getTooltipInfo(getElement()));
}
/**
@@ -1945,8 +1957,9 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
* locked
*/
if (!tb.getElement().getStyle().getWidth().endsWith("px")) {
- tb.setWidth((tb.getOffsetWidth() - selectedItemIcon
- .getOffsetWidth()) + "px");
+ int iconWidth = selectedItemIcon == null ? 0 : selectedItemIcon
+ .getOffsetWidth();
+ tb.setWidth((tb.getOffsetWidth() - iconWidth) + "px");
}
}
}
@@ -2004,7 +2017,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
}
@Override
- public Element getSubPartElement(String subPart) {
+ public com.google.gwt.user.client.Element getSubPartElement(String subPart) {
if ("textbox".equals(subPart)) {
return tb.getElement();
} else if ("button".equals(subPart)) {
@@ -2016,7 +2029,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
}
@Override
- public String getSubPartName(Element subElement) {
+ public String getSubPartName(com.google.gwt.user.client.Element subElement) {
if (tb.getElement().isOrHasChild(subElement)) {
return "textbox";
} else if (popupOpener.getElement().isOrHasChild(subElement)) {
@@ -2038,7 +2051,8 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
}
@Override
- public void bindAriaCaption(Element captionElement) {
+ public void bindAriaCaption(
+ com.google.gwt.user.client.Element captionElement) {
AriaHelper.bindCaption(tb, captionElement);
}
}
diff --git a/client/src/com/vaadin/client/ui/VForm.java b/client/src/com/vaadin/client/ui/VForm.java
index f88bc8d1c0..94379a5611 100644
--- a/client/src/com/vaadin/client/ui/VForm.java
+++ b/client/src/com/vaadin/client/ui/VForm.java
@@ -16,11 +16,11 @@
package com.vaadin.client.ui;
+import com.google.gwt.dom.client.Element;
import com.google.gwt.event.dom.client.KeyDownEvent;
import com.google.gwt.event.dom.client.KeyDownHandler;
import com.google.gwt.event.shared.HandlerRegistration;
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.ui.ComplexPanel;
import com.google.gwt.user.client.ui.Widget;
@@ -136,10 +136,4 @@ public class VForm extends ComplexPanel implements KeyDownHandler {
}
lo = newLayoutWidget;
}
-
- /** For internal use only. May be removed or replaced in the future. */
- @Override
- public void add(Widget child, Element container) {
- super.add(child, container);
- }
}
diff --git a/client/src/com/vaadin/client/ui/VFormLayout.java b/client/src/com/vaadin/client/ui/VFormLayout.java
index 6c2661d1d8..9ce6f4b992 100644
--- a/client/src/com/vaadin/client/ui/VFormLayout.java
+++ b/client/src/com/vaadin/client/ui/VFormLayout.java
@@ -21,10 +21,10 @@ import java.util.HashMap;
import java.util.List;
import com.google.gwt.aria.client.Roles;
+import com.google.gwt.dom.client.Element;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.SimplePanel;
@@ -260,21 +260,17 @@ public class VFormLayout extends SimplePanel {
boolean isEmpty = true;
+ if (icon != null) {
+ getElement().removeChild(icon.getElement());
+ icon = null;
+ }
if (state.resources.containsKey(ComponentConstants.ICON_RESOURCE)) {
- if (icon == null) {
- icon = new Icon(owner.getConnection());
+ icon = owner.getConnection().getIcon(
+ state.resources.get(ComponentConstants.ICON_RESOURCE)
+ .getURL());
+ DOM.insertChild(getElement(), icon.getElement(), 0);
- DOM.insertChild(getElement(), icon.getElement(), 0);
- }
- icon.setUri(state.resources.get(
- ComponentConstants.ICON_RESOURCE).getURL());
isEmpty = false;
- } else {
- if (icon != null) {
- DOM.removeChild(getElement(), icon.getElement());
- icon = null;
- }
-
}
if (state.caption != null) {
diff --git a/client/src/com/vaadin/client/ui/VGridLayout.java b/client/src/com/vaadin/client/ui/VGridLayout.java
index 46051655b8..07dba1f9b3 100644
--- a/client/src/com/vaadin/client/ui/VGridLayout.java
+++ b/client/src/com/vaadin/client/ui/VGridLayout.java
@@ -22,10 +22,11 @@ import java.util.List;
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.Position;
import com.google.gwt.dom.client.Style.Unit;
-import com.google.gwt.user.client.Element;
+import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.ComplexPanel;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.ApplicationConnection;
@@ -665,9 +666,32 @@ public class VGridLayout extends ComplexPanel {
* this layout
* @return The Paintable which the element is a part of. Null if the element
* belongs to the layout and not to a child.
+ * @deprecated As of 7.2, call or override {@link #getComponent(Element)}
+ * instead
*/
- public ComponentConnector getComponent(Element element) {
+ @Deprecated
+ public ComponentConnector getComponent(
+ com.google.gwt.user.client.Element element) {
return Util.getConnectorForElement(client, this, element);
+
+ }
+
+ /**
+ * Returns the deepest nested child component which contains "element". The
+ * child component is also returned if "element" is part of its caption.
+ * <p>
+ * For internal use only. May be removed or replaced in the future.
+ *
+ * @param element
+ * An element that is a nested sub element of the root element in
+ * this layout
+ * @return The Paintable which the element is a part of. Null if the element
+ * belongs to the layout and not to a child.
+ *
+ * @since 7.2
+ */
+ public ComponentConnector getComponent(Element element) {
+ return getComponent(DOM.asOld(element));
}
/** For internal use only. May be removed or replaced in the future. */
diff --git a/client/src/com/vaadin/client/ui/VLabel.java b/client/src/com/vaadin/client/ui/VLabel.java
index 8acd653778..35f47d540a 100644
--- a/client/src/com/vaadin/client/ui/VLabel.java
+++ b/client/src/com/vaadin/client/ui/VLabel.java
@@ -18,7 +18,6 @@ package com.vaadin.client.ui;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.HTML;
-import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.BrowserInfo;
import com.vaadin.client.Util;
import com.vaadin.client.VTooltip;
@@ -28,8 +27,6 @@ public class VLabel extends HTML {
public static final String CLASSNAME = "v-label";
private static final String CLASSNAME_UNDEFINED_WIDTH = "v-label-undef-w";
- private ApplicationConnection connection;
-
public VLabel() {
super();
setStyleName(CLASSNAME);
@@ -71,9 +68,4 @@ public class VLabel extends HTML {
super.setText(text);
}
}
-
- /** For internal use only. May be removed or replaced in the future. */
- public void setConnection(ApplicationConnection client) {
- connection = client;
- }
}
diff --git a/client/src/com/vaadin/client/ui/VLink.java b/client/src/com/vaadin/client/ui/VLink.java
index fa4ee36bcf..b528e770d4 100644
--- a/client/src/com/vaadin/client/ui/VLink.java
+++ b/client/src/com/vaadin/client/ui/VLink.java
@@ -16,14 +16,13 @@
package com.vaadin.client.ui;
+import com.google.gwt.dom.client.Element;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
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.Window;
import com.google.gwt.user.client.ui.HTML;
-import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.Util;
import com.vaadin.shared.ui.BorderStyle;
@@ -70,9 +69,6 @@ public class VLink extends HTML implements ClickHandler {
/** For internal use only. May be removed or replaced in the future. */
public Icon icon;
- /** For internal use only. May be removed or replaced in the future. */
- public ApplicationConnection client;
-
public VLink() {
super();
getElement().appendChild(anchor);
diff --git a/client/src/com/vaadin/client/ui/VMediaBase.java b/client/src/com/vaadin/client/ui/VMediaBase.java
index b77e8bb161..eadc8258c6 100644
--- a/client/src/com/vaadin/client/ui/VMediaBase.java
+++ b/client/src/com/vaadin/client/ui/VMediaBase.java
@@ -17,11 +17,11 @@
package com.vaadin.client.ui;
import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.MediaElement;
import com.google.gwt.dom.client.NodeList;
import com.google.gwt.dom.client.SourceElement;
import com.google.gwt.dom.client.Text;
-import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.ui.Widget;
public abstract class VMediaBase extends Widget {
@@ -82,7 +82,7 @@ public abstract class VMediaBase extends Widget {
}
public void addSource(String sourceUrl, String sourceType) {
- Element src = Document.get().createElement(SourceElement.TAG).cast();
+ Element src = Document.get().createElement(SourceElement.TAG);
src.setAttribute("src", sourceUrl);
src.setAttribute("type", sourceType);
media.appendChild(src);
diff --git a/client/src/com/vaadin/client/ui/VMenuBar.java b/client/src/com/vaadin/client/ui/VMenuBar.java
index 0aa26e4999..a2715fd786 100644
--- a/client/src/com/vaadin/client/ui/VMenuBar.java
+++ b/client/src/com/vaadin/client/ui/VMenuBar.java
@@ -21,6 +21,7 @@ import java.util.List;
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.Element;
import com.google.gwt.dom.client.Style;
import com.google.gwt.dom.client.Style.Overflow;
import com.google.gwt.dom.client.Style.Unit;
@@ -35,7 +36,6 @@ import com.google.gwt.event.logical.shared.CloseEvent;
import com.google.gwt.event.logical.shared.CloseHandler;
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.ui.HasHTML;
@@ -226,11 +226,9 @@ public class VMenuBar extends SimpleFocusablePanel implements
itemHTML.append("<span class=\"" + getStylePrimaryName()
+ "-menuitem-caption\">");
- if (item.hasAttribute("icon")) {
- itemHTML.append("<img src=\""
- + Util.escapeAttribute(client.translateVaadinUri(item
- .getStringAttribute("icon"))) + "\" class=\""
- + Icon.CLASSNAME + "\" alt=\"\" />");
+ Icon icon = client.getIcon(item.getStringAttribute("icon"));
+ if (icon != null) {
+ itemHTML.append(icon.getElement().getString());
}
String itemText = item.getStringAttribute("text");
if (!htmlContentAllowed) {
@@ -286,8 +284,8 @@ public class VMenuBar extends SimpleFocusablePanel implements
* @return
*/
@Override
- public Element getContainerElement() {
- return containerElement;
+ public com.google.gwt.user.client.Element getContainerElement() {
+ return DOM.asOld(containerElement);
}
/**
@@ -1230,13 +1228,29 @@ public class VMenuBar extends SimpleFocusablePanel implements
* Get the key that selects a menu item. By default it is the Enter key but
* by overriding this you can change the key to whatever you want.
*
+ * @deprecated use {@link #isNavigationSelectKey(int)} instead
* @return
*/
+ @Deprecated
protected int getNavigationSelectKey() {
return KeyCodes.KEY_ENTER;
}
/**
+ * Checks whether key code selects a menu item. By default it is the Enter
+ * and Space keys but by overriding this you can change the keys to whatever
+ * you want.
+ *
+ * @since 7.2
+ * @param keycode
+ * @return true if key selects menu item
+ */
+ protected boolean isNavigationSelectKey(int keycode) {
+ return keycode == getNavigationSelectKey()
+ || keycode == KeyCodes.KEY_SPACE;
+ }
+
+ /**
* Get the key that closes the menu. By default it is the escape key but by
* overriding this yoy can change the key to whatever you want.
*
@@ -1434,7 +1448,7 @@ public class VMenuBar extends SimpleFocusablePanel implements
hideChildren();
menuVisible = false;
- } else if (keycode == getNavigationSelectKey()) {
+ } else if (isNavigationSelectKey(keycode)) {
if (getSelected() == null) {
// If nothing is selected then select the first item
selectFirstItem();
@@ -1502,7 +1516,7 @@ public class VMenuBar extends SimpleFocusablePanel implements
private final String SUBPART_PREFIX = "item";
@Override
- public Element getSubPartElement(String subPart) {
+ public com.google.gwt.user.client.Element getSubPartElement(String subPart) {
int index = Integer
.parseInt(subPart.substring(SUBPART_PREFIX.length()));
CustomMenuItem item = getItems().get(index);
@@ -1511,7 +1525,7 @@ public class VMenuBar extends SimpleFocusablePanel implements
}
@Override
- public String getSubPartName(Element subElement) {
+ public String getSubPartName(com.google.gwt.user.client.Element subElement) {
if (!getElement().isOrHasChild(subElement)) {
return null;
}
@@ -1539,8 +1553,12 @@ public class VMenuBar extends SimpleFocusablePanel implements
* @param element
* Element used in search
* @return Menu item or null if not found
+ * @deprecated As of 7.2, call or override
+ * {@link #getMenuItemWithElement(Element)} instead
*/
- public CustomMenuItem getMenuItemWithElement(Element element) {
+ @Deprecated
+ public CustomMenuItem getMenuItemWithElement(
+ com.google.gwt.user.client.Element element) {
for (int i = 0; i < items.size(); i++) {
CustomMenuItem item = items.get(i);
if (DOM.isOrHasChild(item.getElement(), element)) {
@@ -1557,4 +1575,17 @@ public class VMenuBar extends SimpleFocusablePanel implements
return null;
}
+
+ /**
+ * Get menu item with given DOM element
+ *
+ * @param element
+ * Element used in search
+ * @return Menu item or null if not found
+ *
+ * @since 7.2
+ */
+ public CustomMenuItem getMenuItemWithElement(Element element) {
+ return getMenuItemWithElement(DOM.asOld(element));
+ }
}
diff --git a/client/src/com/vaadin/client/ui/VNativeButton.java b/client/src/com/vaadin/client/ui/VNativeButton.java
index 67fa6f2ee3..d38e4c3374 100644
--- a/client/src/com/vaadin/client/ui/VNativeButton.java
+++ b/client/src/com/vaadin/client/ui/VNativeButton.java
@@ -33,8 +33,6 @@ public class VNativeButton extends Button implements ClickHandler {
public static final String CLASSNAME = "v-nativebutton";
- protected String width = null;
-
/** For internal use only. May be removed or replaced in the future. */
public String paintableId;
@@ -123,12 +121,6 @@ public class VNativeButton extends Button implements ClickHandler {
}
}
- @Override
- public void setWidth(String width) {
- this.width = width;
- super.setWidth(width);
- }
-
/*
* (non-Javadoc)
*
diff --git a/client/src/com/vaadin/client/ui/VNotification.java b/client/src/com/vaadin/client/ui/VNotification.java
index 7019394e3b..a43f508f6e 100644
--- a/client/src/com/vaadin/client/ui/VNotification.java
+++ b/client/src/com/vaadin/client/ui/VNotification.java
@@ -21,20 +21,26 @@ import java.util.Date;
import java.util.EventObject;
import java.util.Iterator;
+import com.google.gwt.aria.client.Roles;
import com.google.gwt.core.client.GWT;
+import com.google.gwt.dom.client.Element;
import com.google.gwt.event.dom.client.KeyCodes;
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.ui.FlowPanel;
import com.google.gwt.user.client.ui.HTML;
+import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.BrowserInfo;
import com.vaadin.client.UIDL;
import com.vaadin.client.Util;
+import com.vaadin.client.ui.aria.AriaHelper;
import com.vaadin.shared.Position;
import com.vaadin.shared.ui.ui.UIConstants;
+import com.vaadin.shared.ui.ui.UIState.NotificationTypeConfiguration;
+import com.vaadin.shared.ui.ui.NotificationRole;
public class VNotification extends VOverlay {
@@ -46,6 +52,12 @@ public class VNotification extends VOverlay {
public static final Position BOTTOM_LEFT = Position.BOTTOM_LEFT;
public static final Position BOTTOM_RIGHT = Position.BOTTOM_RIGHT;
+ /**
+ * Position that is only accessible for assistive devices, invisible for
+ * visual users.
+ */
+ public static final Position ASSISTIVE = Position.ASSISTIVE;
+
public static final int DELAY_FOREVER = -1;
public static final int DELAY_NONE = 0;
@@ -149,15 +161,74 @@ public class VNotification extends VOverlay {
}
public void show(Widget widget, Position position, String style) {
- setWidget(widget);
+ NotificationTypeConfiguration styleSetup = getUiState(style);
+ setWaiAriaRole(styleSetup);
+
+ FlowPanel panel = new FlowPanel();
+ if (hasPrefix(styleSetup)) {
+ panel.add(new Label(styleSetup.prefix));
+ AriaHelper.setVisibleForAssistiveDevicesOnly(panel.getElement(),
+ true);
+ }
+
+ panel.add(widget);
+
+ if (hasPostfix(styleSetup)) {
+ panel.add(new Label(styleSetup.postfix));
+ AriaHelper.setVisibleForAssistiveDevicesOnly(panel.getElement(),
+ true);
+ }
+ setWidget(panel);
show(position, style);
}
+ private boolean hasPostfix(NotificationTypeConfiguration styleSetup) {
+ return styleSetup != null && styleSetup.postfix != null && !styleSetup.postfix.isEmpty();
+ }
+
+ private boolean hasPrefix(NotificationTypeConfiguration styleSetup) {
+ return styleSetup != null && styleSetup.prefix != null && !styleSetup.prefix.isEmpty();
+ }
+
public void show(String html, Position position, String style) {
- setWidget(new HTML(html));
+ NotificationTypeConfiguration styleSetup = getUiState(style);
+ String assistiveDeviceOnlyStyle = AriaHelper.ASSISTIVE_DEVICE_ONLY_STYLE;
+
+ setWaiAriaRole(styleSetup);
+
+ String type = "";
+ String usage = "";
+
+ if (hasPrefix(styleSetup)) {
+ type = "<span class='" + assistiveDeviceOnlyStyle + "'>"
+ + styleSetup.prefix + "</span>";
+ }
+
+ if (hasPostfix(styleSetup)) {
+ usage = "<span class='" + assistiveDeviceOnlyStyle + "'>"
+ + styleSetup.postfix + "</span>";
+ }
+
+ setWidget(new HTML(type + html + usage));
show(position, style);
}
+ private NotificationTypeConfiguration getUiState(String style) {
+ return getApplicationConnection()
+ .getUIConnector().getState().notificationConfigurations
+ .get(style);
+ }
+
+ private void setWaiAriaRole(NotificationTypeConfiguration styleSetup) {
+ Roles.getAlertRole().set(getElement());
+
+ if (styleSetup != null && styleSetup.notificationRole != null) {
+ if (NotificationRole.STATUS == styleSetup.notificationRole) {
+ Roles.getStatusRole().set(getElement());
+ }
+ }
+ }
+
public void show(Position position, String style) {
setOpacity(getElement(), startOpacity);
if (style != null) {
@@ -239,6 +310,15 @@ public class VNotification extends VOverlay {
DOM.setStyleAttribute(el, "top", "0px");
DOM.setStyleAttribute(el, "right", "0px");
break;
+ case MIDDLE_LEFT:
+ center();
+ DOM.setStyleAttribute(el, "left", "0px");
+ break;
+ case MIDDLE_RIGHT:
+ center();
+ DOM.setStyleAttribute(el, "left", "");
+ DOM.setStyleAttribute(el, "right", "0px");
+ break;
case BOTTOM_RIGHT:
DOM.setStyleAttribute(el, "position", "absolute");
DOM.setStyleAttribute(el, "bottom", "0px");
@@ -257,6 +337,10 @@ public class VNotification extends VOverlay {
DOM.setStyleAttribute(el, "top", "");
DOM.setStyleAttribute(el, "bottom", "0px");
break;
+ case ASSISTIVE:
+ DOM.setStyleAttribute(el, "top", "-2000px");
+ DOM.setStyleAttribute(el, "left", "-2000px");
+ break;
default:
case MIDDLE_CENTER:
center();
@@ -374,10 +458,9 @@ public class VNotification extends VOverlay {
.hasAttribute(UIConstants.NOTIFICATION_HTML_CONTENT_NOT_ALLOWED);
String html = "";
if (notification.hasAttribute(UIConstants.ATTRIBUTE_NOTIFICATION_ICON)) {
- final String parsedUri = client
- .translateVaadinUri(notification
- .getStringAttribute(UIConstants.ATTRIBUTE_NOTIFICATION_ICON));
- html += "<img src=\"" + Util.escapeAttribute(parsedUri) + "\" />";
+ String iconUri = notification
+ .getStringAttribute(UIConstants.ATTRIBUTE_NOTIFICATION_ICON);
+ html += client.getIcon(iconUri).getElement().getString();
}
if (notification
.hasAttribute(UIConstants.ATTRIBUTE_NOTIFICATION_CAPTION)) {
@@ -417,6 +500,8 @@ public class VNotification extends VOverlay {
public static VNotification createNotification(int delayMsec, Widget owner) {
final VNotification notification = GWT.create(VNotification.class);
+ notification.setWaiAriaRole(null);
+
notification.delayMsec = delayMsec;
if (BrowserInfo.get().isTouchDevice()) {
new Timer() {
diff --git a/client/src/com/vaadin/client/ui/VOptionGroup.java b/client/src/com/vaadin/client/ui/VOptionGroup.java
index fe4ef214cb..3e4b357be3 100644
--- a/client/src/com/vaadin/client/ui/VOptionGroup.java
+++ b/client/src/com/vaadin/client/ui/VOptionGroup.java
@@ -139,12 +139,10 @@ public class VOptionGroup extends VOptionGroupBase implements FocusHandler,
itemHtml = Util.escapeHTML(itemHtml);
}
- String icon = opUidl.getStringAttribute("icon");
- if (icon != null && icon.length() != 0) {
- String iconUrl = client.translateVaadinUri(icon);
- itemHtml = "<img src=\"" + Util.escapeAttribute(iconUrl)
- + "\" class=\"" + Icon.CLASSNAME + "\" alt=\"\" />"
- + itemHtml;
+ String iconUrl = opUidl.getStringAttribute("icon");
+ if (iconUrl != null && iconUrl.length() != 0) {
+ Icon icon = client.getIcon(iconUrl);
+ itemHtml = icon.getElement().getString() + itemHtml;
}
String key = opUidl.getStringAttribute("key");
@@ -161,7 +159,7 @@ public class VOptionGroup extends VOptionGroupBase implements FocusHandler,
op = new RadioButton(paintableId);
op.setStyleName("v-radiobutton");
}
- if (icon != null && icon.length() != 0) {
+ if (iconUrl != null && iconUrl.length() != 0) {
Util.sinkOnloadForImages(op.getElement());
op.addHandler(iconLoadHandler, LoadEvent.getType());
}
diff --git a/client/src/com/vaadin/client/ui/VOverlay.java b/client/src/com/vaadin/client/ui/VOverlay.java
index e2c9001fed..7661fbfcf7 100644
--- a/client/src/com/vaadin/client/ui/VOverlay.java
+++ b/client/src/com/vaadin/client/ui/VOverlay.java
@@ -19,6 +19,7 @@ package com.vaadin.client.ui;
import com.google.gwt.animation.client.Animation;
import com.google.gwt.aria.client.Roles;
import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.IFrameElement;
import com.google.gwt.dom.client.Style;
import com.google.gwt.dom.client.Style.BorderStyle;
@@ -27,7 +28,6 @@ import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.logical.shared.CloseEvent;
import com.google.gwt.event.logical.shared.CloseHandler;
import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.RootPanel;
@@ -172,7 +172,7 @@ public class VOverlay extends PopupPanel implements CloseHandler<PopupPanel> {
*
* See default theme 'shadow.css' for implementation example.
*/
- private static final String SHADOW_HTML = "<div class=\"top-left\"></div><div class=\"top\"></div><div class=\"top-right\"></div><div class=\"left\"></div><div class=\"center\"></div><div class=\"right\"></div><div class=\"bottom-left\"></div><div class=\"bottom\"></div><div class=\"bottom-right\"></div>";
+ private static final String SHADOW_HTML = "<div aria-hidden=\"true\" class=\"top-left\"></div><div class=\"top\"></div><div class=\"top-right\"></div><div class=\"left\"></div><div class=\"center\"></div><div class=\"right\"></div><div class=\"bottom-left\"></div><div class=\"bottom\"></div><div class=\"bottom-right\"></div>";
/**
* Matches {@link PopupPanel}.ANIMATION_DURATION
@@ -482,7 +482,7 @@ public class VOverlay extends PopupPanel implements CloseHandler<PopupPanel> {
// Animate the size
positionAndSize.setAnimationFromCenterProgress(progress);
- Element container = getElement().getParentElement().cast();
+ Element container = getElement().getParentElement();
if (isShadowEnabled()) {
updateShadowPosition(progress, zIndex, positionAndSize);
@@ -535,8 +535,7 @@ public class VOverlay extends PopupPanel implements CloseHandler<PopupPanel> {
}
private void updateShimPosition(PositionAndSize positionAndSize) {
- updatePositionAndSize((Element) Element.as(getShimElement()),
- positionAndSize);
+ updatePositionAndSize(getShimElement(), positionAndSize);
}
/**
@@ -666,7 +665,7 @@ public class VOverlay extends PopupPanel implements CloseHandler<PopupPanel> {
* {@link ApplicationConnection} or another element if the current
* {@link ApplicationConnection} cannot be determined.
*/
- public Element getOverlayContainer() {
+ public com.google.gwt.user.client.Element getOverlayContainer() {
ApplicationConnection ac = getApplicationConnection();
if (ac == null) {
// could not figure out which one we belong to, styling will
@@ -688,7 +687,8 @@ public class VOverlay extends PopupPanel implements CloseHandler<PopupPanel> {
* A reference to {@link ApplicationConnection}
* @return The overlay container
*/
- public static Element getOverlayContainer(ApplicationConnection ac) {
+ public static com.google.gwt.user.client.Element getOverlayContainer(
+ ApplicationConnection ac) {
String id = ac.getConfiguration().getRootPanelId();
id = id += "-overlays";
Element container = DOM.getElementById(id);
@@ -701,7 +701,7 @@ public class VOverlay extends PopupPanel implements CloseHandler<PopupPanel> {
container.addClassName(CLASSNAME_CONTAINER);
RootPanel.get().getElement().appendChild(container);
}
- return container;
+ return DOM.asOld(container);
}
/**
diff --git a/client/src/com/vaadin/client/ui/VPanel.java b/client/src/com/vaadin/client/ui/VPanel.java
index 6b02f079d1..3b263d6dbb 100644
--- a/client/src/com/vaadin/client/ui/VPanel.java
+++ b/client/src/com/vaadin/client/ui/VPanel.java
@@ -18,8 +18,8 @@ package com.vaadin.client.ui;
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.user.client.DOM;
-import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.SimplePanel;
import com.vaadin.client.ApplicationConnection;
@@ -124,8 +124,8 @@ public class VPanel extends SimplePanel implements ShortcutActionHandlerOwner,
}
@Override
- protected Element getContainerElement() {
- return contentNode;
+ protected com.google.gwt.user.client.Element getContainerElement() {
+ return DOM.asOld(contentNode);
}
/** For internal use only. May be removed or replaced in the future. */
@@ -152,17 +152,12 @@ public class VPanel extends SimplePanel implements ShortcutActionHandlerOwner,
/** For internal use only. May be removed or replaced in the future. */
public void setIconUri(String iconUri, ApplicationConnection client) {
- if (iconUri == null) {
- if (icon != null) {
- DOM.removeChild(captionNode, icon.getElement());
- icon = null;
- }
- } else {
- if (icon == null) {
- icon = new Icon(client);
- DOM.insertChild(captionNode, icon.getElement(), 0);
- }
- icon.setUri(iconUri);
+ if (icon != null) {
+ captionNode.removeChild(icon.getElement());
+ }
+ icon = client.getIcon(iconUri);
+ if (icon != null) {
+ DOM.insertChild(captionNode, icon.getElement(), 0);
}
}
@@ -170,7 +165,6 @@ public class VPanel extends SimplePanel implements ShortcutActionHandlerOwner,
public void onBrowserEvent(Event event) {
super.onBrowserEvent(event);
- final Element target = DOM.eventGetTarget(event);
final int type = DOM.eventGetType(event);
if (type == Event.ONKEYDOWN && shortcutHandler != null) {
shortcutHandler.handleKeyboardEvent(event);
diff --git a/client/src/com/vaadin/client/ui/VPopupCalendar.java b/client/src/com/vaadin/client/ui/VPopupCalendar.java
index e2ad40c929..e180239fc1 100644
--- a/client/src/com/vaadin/client/ui/VPopupCalendar.java
+++ b/client/src/com/vaadin/client/ui/VPopupCalendar.java
@@ -22,6 +22,7 @@ import com.google.gwt.aria.client.Id;
import com.google.gwt.aria.client.LiveValue;
import com.google.gwt.aria.client.Roles;
import com.google.gwt.core.client.GWT;
+import com.google.gwt.dom.client.Element;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.DomEvent;
@@ -30,7 +31,6 @@ import com.google.gwt.event.logical.shared.CloseEvent;
import com.google.gwt.event.logical.shared.CloseHandler;
import com.google.gwt.i18n.client.DateTimeFormat;
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;
@@ -255,7 +255,8 @@ public class VPopupCalendar extends VTextualDate implements Field,
}
@Override
- public void bindAriaCaption(Element captionElement) {
+ public void bindAriaCaption(
+ com.google.gwt.user.client.Element captionElement) {
if (captionElement == null) {
captionId = null;
} else {
@@ -547,7 +548,7 @@ public class VPopupCalendar extends VTextualDate implements Field,
private final String CALENDAR_TOGGLE_ID = "popupButton";
@Override
- public Element getSubPartElement(String subPart) {
+ public com.google.gwt.user.client.Element getSubPartElement(String subPart) {
if (subPart.equals(CALENDAR_TOGGLE_ID)) {
return calendarToggle.getElement();
}
@@ -556,7 +557,7 @@ public class VPopupCalendar extends VTextualDate implements Field,
}
@Override
- public String getSubPartName(Element subElement) {
+ public String getSubPartName(com.google.gwt.user.client.Element subElement) {
if (calendarToggle.getElement().isOrHasChild(subElement)) {
return CALENDAR_TOGGLE_ID;
}
diff --git a/client/src/com/vaadin/client/ui/VPopupView.java b/client/src/com/vaadin/client/ui/VPopupView.java
index dba4c8b092..384cb006ce 100644
--- a/client/src/com/vaadin/client/ui/VPopupView.java
+++ b/client/src/com/vaadin/client/ui/VPopupView.java
@@ -20,6 +20,7 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
+import com.google.gwt.dom.client.Element;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.KeyCodes;
@@ -29,7 +30,6 @@ import com.google.gwt.event.logical.shared.CloseEvent;
import com.google.gwt.event.logical.shared.CloseHandler;
import com.google.gwt.event.shared.HandlerRegistration;
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.ui.Focusable;
import com.google.gwt.user.client.ui.HTML;
@@ -102,7 +102,8 @@ public class VPopupView extends HTML implements Iterable<Widget> {
}
});
- popup.setAnimationEnabled(true);
+ // TODO: Enable animations once GWT fix has been merged
+ popup.setAnimationEnabled(false);
popup.setAutoHideOnHistoryEventsEnabled(false);
}
@@ -356,7 +357,7 @@ public class VPopupView extends HTML implements Iterable<Widget> {
}
@Override
- public Element getContainerElement() {
+ public com.google.gwt.user.client.Element getContainerElement() {
return super.getContainerElement();
}
diff --git a/client/src/com/vaadin/client/ui/VProgressBar.java b/client/src/com/vaadin/client/ui/VProgressBar.java
index 8cfc28005c..3efbbbd8a6 100644
--- a/client/src/com/vaadin/client/ui/VProgressBar.java
+++ b/client/src/com/vaadin/client/ui/VProgressBar.java
@@ -16,9 +16,9 @@
package com.vaadin.client.ui;
+import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.ui.HasEnabled;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.ApplicationConnection;
diff --git a/client/src/com/vaadin/client/ui/VRichTextArea.java b/client/src/com/vaadin/client/ui/VRichTextArea.java
index 0b2c1e574c..cb3cba3f1d 100644
--- a/client/src/com/vaadin/client/ui/VRichTextArea.java
+++ b/client/src/com/vaadin/client/ui/VRichTextArea.java
@@ -21,6 +21,7 @@ import java.util.Map;
import java.util.Map.Entry;
import com.google.gwt.core.client.Scheduler;
+import com.google.gwt.dom.client.Element;
import com.google.gwt.event.dom.client.BlurHandler;
import com.google.gwt.event.dom.client.KeyDownEvent;
import com.google.gwt.event.dom.client.KeyDownHandler;
@@ -29,7 +30,6 @@ import com.google.gwt.event.dom.client.KeyPressHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlowPanel;
diff --git a/client/src/com/vaadin/client/ui/VScrollTable.java b/client/src/com/vaadin/client/ui/VScrollTable.java
index 261c50b73b..438a201984 100644
--- a/client/src/com/vaadin/client/ui/VScrollTable.java
+++ b/client/src/com/vaadin/client/ui/VScrollTable.java
@@ -30,6 +30,7 @@ import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.dom.client.Node;
import com.google.gwt.dom.client.NodeList;
@@ -61,9 +62,10 @@ import com.google.gwt.event.dom.client.ScrollHandler;
import com.google.gwt.event.logical.shared.CloseEvent;
import com.google.gwt.event.logical.shared.CloseHandler;
import com.google.gwt.event.shared.HandlerRegistration;
+import com.google.gwt.regexp.shared.MatchResult;
+import com.google.gwt.regexp.shared.RegExp;
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;
@@ -71,7 +73,6 @@ import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HasWidgets;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.PopupPanel;
-import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.UIObject;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.ApplicationConnection;
@@ -80,6 +81,7 @@ import com.vaadin.client.ComponentConnector;
import com.vaadin.client.ConnectorMap;
import com.vaadin.client.Focusable;
import com.vaadin.client.MouseEventDetailsBuilder;
+import com.vaadin.client.StyleConstants;
import com.vaadin.client.TooltipInfo;
import com.vaadin.client.UIDL;
import com.vaadin.client.Util;
@@ -123,7 +125,7 @@ import com.vaadin.shared.ui.table.TableConstants;
*/
public class VScrollTable extends FlowPanel implements HasWidgets,
ScrollHandler, VHasDropHandler, FocusHandler, BlurHandler, Focusable,
- ActionOwner {
+ ActionOwner, SubPartAware {
public static final String STYLENAME = "v-table";
@@ -346,7 +348,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
return startRow.getIndex() + length - 1;
}
- };
+ }
private final HashSet<SelectionRange> selectedRowRanges = new HashSet<SelectionRange>();
@@ -2318,7 +2320,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
private int reqFirstRow = 0;
private int reqRows = 0;
- private boolean isRunning = false;
+ private boolean isRequestHandlerRunning = false;
public void triggerRowFetch(int first, int rows) {
setReqFirstRow(first);
@@ -2336,12 +2338,12 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
deferRowFetch(250);
}
- public boolean isRunning() {
- return isRunning;
+ public boolean isRequestHandlerRunning() {
+ return isRequestHandlerRunning;
}
public void deferRowFetch(int msec) {
- isRunning = true;
+ isRequestHandlerRunning = true;
if (reqRows > 0 && reqFirstRow < totalRows) {
schedule(msec);
@@ -2483,7 +2485,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
unSyncedselectionsBeforeRowFetch = new HashSet<Object>(
selectedRowKeys);
}
- isRunning = false;
+ isRequestHandlerRunning = false;
}
}
@@ -2491,7 +2493,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
* Sends request to refresh content at this position.
*/
public void refreshContent() {
- isRunning = true;
+ isRequestHandlerRunning = true;
int first = (int) (firstRowInViewPort - pageLength * cache_rate);
int reqRows = (int) (2 * pageLength * cache_rate + pageLength);
if (first < 0) {
@@ -2815,13 +2817,27 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
DOM.setInnerHTML(floatingCopyOfHeaderCell, DOM.getInnerHTML(td));
floatingCopyOfHeaderCell = DOM
.getChild(floatingCopyOfHeaderCell, 2);
- DOM.setElementProperty(floatingCopyOfHeaderCell, "className",
- VScrollTable.this.getStylePrimaryName() + "-header-drag");
+ // #12714 the shown "ghost element" should be inside
+ // v-overlay-container, and it should contain the same styles as the
+ // table to enable theming (except v-table & v-widget).
+ String stylePrimaryName = VScrollTable.this.getStylePrimaryName();
+ StringBuilder sb = new StringBuilder();
+ for (String s : VScrollTable.this.getStyleName().split(" ")) {
+ if (!s.equals(StyleConstants.UI_WIDGET)) {
+ sb.append(s);
+ if (s.equals(stylePrimaryName)) {
+ sb.append("-header-drag ");
+ } else {
+ sb.append(" ");
+ }
+ }
+ }
+ floatingCopyOfHeaderCell.setClassName(sb.toString().trim());
// otherwise might wrap or be cut if narrow column
DOM.setStyleAttribute(floatingCopyOfHeaderCell, "width", "auto");
updateFloatingCopysPosition(DOM.getAbsoluteLeft(td),
DOM.getAbsoluteTop(td));
- DOM.appendChild(RootPanel.get().getElement(),
+ DOM.appendChild(VOverlay.getOverlayContainer(client),
floatingCopyOfHeaderCell);
}
@@ -2836,8 +2852,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
}
private void hideFloatingCopy() {
- DOM.removeChild(RootPanel.get().getElement(),
- floatingCopyOfHeaderCell);
+ floatingCopyOfHeaderCell.removeFromParent();
floatingCopyOfHeaderCell = null;
}
@@ -5083,6 +5098,20 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
}
}
+ public int indexOf(Widget row) {
+ int relIx = -1;
+ for (int ix = 0; ix < renderedRows.size(); ix++) {
+ if (renderedRows.get(ix) == row) {
+ relIx = ix;
+ break;
+ }
+ }
+ if (relIx >= 0) {
+ return firstRendered + relIx;
+ }
+ return -1;
+ }
+
public class VScrollTableRow extends Panel implements ActionOwner {
private static final int TOUCHSCROLL_TIMEOUT = 100;
@@ -5529,7 +5558,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
com.google.gwt.dom.client.Element target) {
TooltipInfo info = null;
- final Element targetTdOrTr = getTdOrTr((Element) target.cast());
+ final Element targetTdOrTr = getTdOrTr(target);
if (targetTdOrTr != null
&& "td".equals(targetTdOrTr.getTagName().toLowerCase())) {
TableCellElement td = (TableCellElement) targetTdOrTr
@@ -5554,8 +5583,8 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
// Iterate upwards until we find the TR element
Element element = target;
while (element != null
- && element.getParentElement().cast() != thisTrElement) {
- element = element.getParentElement().cast();
+ && element.getParentElement() != thisTrElement) {
+ element = element.getParentElement();
}
return element;
}
@@ -6025,8 +6054,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
&& rowKeyIsSelected(rowKey)) {
// Create a drag image of ALL rows
- ev.createDragImage(
- (Element) scrollBody.tBodyElement.cast(), true);
+ ev.createDragImage(scrollBody.tBodyElement, true);
// Hide rows which are not selected
Element dragImage = ev.getDragImage();
@@ -6064,7 +6092,6 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
private Element getEventTargetTdOrTr(Event event) {
final Element eventTarget = event.getEventTarget().cast();
Widget widget = Util.findWidget(eventTarget, null);
- final Element thisTrElement = getElement();
if (widget != this) {
/*
@@ -6896,10 +6923,9 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
String s = uidl.hasAttribute("caption") ? uidl
.getStringAttribute("caption") : "";
if (uidl.hasAttribute("icon")) {
- s = "<img src=\""
- + Util.escapeAttribute(client.translateVaadinUri(uidl
- .getStringAttribute("icon")))
- + "\" alt=\"icon\" class=\"v-icon\">" + s;
+ Icon icon = client.getIcon(uidl.getStringAttribute("icon"));
+ icon.setAlternateText("icon");
+ s = icon.getElement().getString() + s;
}
return s;
}
@@ -7080,7 +7106,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
Element tr = row.getElement();
Element element = elementOver;
while (element != null && element.getParentElement() != tr) {
- element = (Element) element.getParentElement();
+ element = element.getParentElement();
}
int childIndex = DOM.getChildIndex(tr, element);
dropDetails.colkey = tHead.getHeaderCell(childIndex)
@@ -7140,7 +7166,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
private void deEmphasis() {
UIObject.setStyleName(getElement(),
- VScrollTable.this.getStylePrimaryName() + "-drag", false);
+ getStylePrimaryName() + "-drag", false);
if (lastEmphasized == null) {
return;
}
@@ -7166,7 +7192,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
private void emphasis(TableDDDetails details) {
deEmphasis();
UIObject.setStyleName(getElement(),
- VScrollTable.this.getStylePrimaryName() + "-drag", true);
+ getStylePrimaryName() + "-drag", true);
// iterate old and new emphasized row
for (Widget w : scrollBody.renderedRows) {
VScrollTableRow row = (VScrollTableRow) w;
@@ -7798,4 +7824,101 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
return this;
}
+ private static final String SUBPART_HEADER = "header";
+ private static final String SUBPART_FOOTER = "footer";
+ private static final String SUBPART_ROW = "row";
+ private static final String SUBPART_COL = "col";
+ /**
+ * Matches header[ix] - used for extracting the index of the targeted header
+ * cell
+ */
+ private static final RegExp SUBPART_HEADER_REGEXP = RegExp
+ .compile(SUBPART_HEADER + "\\[(\\d+)\\]");
+ /**
+ * Matches footer[ix] - used for extracting the index of the targeted footer
+ * cell
+ */
+ private static final RegExp SUBPART_FOOTER_REGEXP = RegExp
+ .compile(SUBPART_FOOTER + "\\[(\\d+)\\]");
+ /** Matches row[ix] - used for extracting the index of the targeted row */
+ private static final RegExp SUBPART_ROW_REGEXP = RegExp.compile(SUBPART_ROW
+ + "\\[(\\d+)]");
+ /** Matches col[ix] - used for extracting the index of the targeted column */
+ private static final RegExp SUBPART_ROW_COL_REGEXP = RegExp
+ .compile(SUBPART_ROW + "\\[(\\d+)\\]/" + SUBPART_COL
+ + "\\[(\\d+)\\]");
+
+ @Override
+ public com.google.gwt.user.client.Element getSubPartElement(String subPart) {
+ if (SUBPART_ROW_COL_REGEXP.test(subPart)) {
+ MatchResult result = SUBPART_ROW_COL_REGEXP.exec(subPart);
+ int rowIx = Integer.valueOf(result.getGroup(1));
+ int colIx = Integer.valueOf(result.getGroup(2));
+ VScrollTableRow row = scrollBody.getRowByRowIndex(rowIx);
+ if (row != null) {
+ Element rowElement = row.getElement();
+ if (colIx < rowElement.getChildCount()) {
+ return rowElement.getChild(colIx).getFirstChild().cast();
+ }
+ }
+
+ } else if (SUBPART_ROW_REGEXP.test(subPart)) {
+ MatchResult result = SUBPART_ROW_REGEXP.exec(subPart);
+ int rowIx = Integer.valueOf(result.getGroup(1));
+ VScrollTableRow row = scrollBody.getRowByRowIndex(rowIx);
+ if (row != null) {
+ return row.getElement();
+ }
+
+ } else if (SUBPART_HEADER_REGEXP.test(subPart)) {
+ MatchResult result = SUBPART_HEADER_REGEXP.exec(subPart);
+ int headerIx = Integer.valueOf(result.getGroup(1));
+ HeaderCell headerCell = tHead.getHeaderCell(headerIx);
+ if (headerCell != null) {
+ return headerCell.getElement();
+ }
+
+ } else if (SUBPART_FOOTER_REGEXP.test(subPart)) {
+ MatchResult result = SUBPART_FOOTER_REGEXP.exec(subPart);
+ int footerIx = Integer.valueOf(result.getGroup(1));
+ FooterCell footerCell = tFoot.getFooterCell(footerIx);
+ if (footerCell != null) {
+ return footerCell.getElement();
+ }
+ }
+ // Nothing found.
+ return null;
+ }
+
+ @Override
+ public String getSubPartName(com.google.gwt.user.client.Element subElement) {
+ Widget widget = Util.findWidget(subElement, null);
+ if (widget instanceof HeaderCell) {
+ return SUBPART_HEADER + "[" + tHead.visibleCells.indexOf(widget)
+ + "]";
+ } else if (widget instanceof FooterCell) {
+ return SUBPART_FOOTER + "[" + tFoot.visibleCells.indexOf(widget)
+ + "]";
+ } else if (widget instanceof VScrollTableRow) {
+ // a cell in a row
+ VScrollTableRow row = (VScrollTableRow) widget;
+ int rowIx = scrollBody.indexOf(row);
+ if (rowIx >= 0) {
+ int colIx = -1;
+ for (int ix = 0; ix < row.getElement().getChildCount(); ix++) {
+ if (row.getElement().getChild(ix).isOrHasChild(subElement)) {
+ colIx = ix;
+ break;
+ }
+ }
+ if (colIx >= 0) {
+ return SUBPART_ROW + "[" + rowIx + "]/" + SUBPART_COL + "["
+ + colIx + "]";
+ }
+ return SUBPART_ROW + "[" + rowIx + "]";
+ }
+ }
+ // Nothing found.
+ return null;
+ }
}
diff --git a/client/src/com/vaadin/client/ui/VSlider.java b/client/src/com/vaadin/client/ui/VSlider.java
index 9d993964d3..7ac31fd85a 100644
--- a/client/src/com/vaadin/client/ui/VSlider.java
+++ b/client/src/com/vaadin/client/ui/VSlider.java
@@ -18,6 +18,7 @@ package com.vaadin.client.ui;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
+import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Style.Display;
import com.google.gwt.dom.client.Style.Overflow;
import com.google.gwt.dom.client.Style.Unit;
@@ -28,20 +29,18 @@ import com.google.gwt.event.logical.shared.ValueChangeHandler;
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.Window;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HasValue;
import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.BrowserInfo;
-import com.vaadin.client.ContainerResizedListener;
import com.vaadin.client.Util;
import com.vaadin.client.VConsole;
import com.vaadin.shared.ui.slider.SliderOrientation;
public class VSlider extends SimpleFocusablePanel implements Field,
- ContainerResizedListener, HasValue<Double> {
+ HasValue<Double> {
public static final String CLASSNAME = "v-slider";
@@ -199,7 +198,7 @@ public class VSlider extends SimpleFocusablePanel implements Field,
return;
}
- final Element p = getElement().getParentElement().cast();
+ final Element p = getElement().getParentElement();
if (p.getPropertyInt(domProperty) > 50) {
if (isVertical()) {
setHeight();
@@ -214,7 +213,7 @@ public class VSlider extends SimpleFocusablePanel implements Field,
@Override
public void execute() {
- final Element p = getElement().getParentElement().cast();
+ final Element p = getElement().getParentElement();
if (p.getPropertyInt(domProperty) > (MIN_SIZE + 5)) {
if (isVertical()) {
setHeight();
@@ -432,7 +431,6 @@ public class VSlider extends SimpleFocusablePanel implements Field,
}
}
- @Override
public void iLayout() {
if (isVertical()) {
setHeight();
diff --git a/client/src/com/vaadin/client/ui/VTabsheet.java b/client/src/com/vaadin/client/ui/VTabsheet.java
index 172a25e3f3..a523287101 100644
--- a/client/src/com/vaadin/client/ui/VTabsheet.java
+++ b/client/src/com/vaadin/client/ui/VTabsheet.java
@@ -19,8 +19,13 @@ package com.vaadin.client.ui;
import java.util.Iterator;
import java.util.List;
+import com.google.gwt.aria.client.Id;
+import com.google.gwt.aria.client.LiveValue;
+import com.google.gwt.aria.client.Roles;
+import com.google.gwt.aria.client.SelectedValue;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.dom.client.DivElement;
+import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Style;
import com.google.gwt.dom.client.Style.Visibility;
import com.google.gwt.dom.client.TableCellElement;
@@ -38,9 +43,10 @@ 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.event.shared.HandlerRegistration;
+import com.google.gwt.regexp.shared.MatchResult;
+import com.google.gwt.regexp.shared.RegExp;
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.ui.ComplexPanel;
import com.google.gwt.user.client.ui.SimplePanel;
@@ -49,20 +55,23 @@ import com.google.gwt.user.client.ui.impl.FocusImpl;
import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.BrowserInfo;
import com.vaadin.client.ComponentConnector;
-import com.vaadin.client.ConnectorMap;
import com.vaadin.client.Focusable;
import com.vaadin.client.TooltipInfo;
-import com.vaadin.client.UIDL;
import com.vaadin.client.Util;
import com.vaadin.client.VCaption;
+import com.vaadin.client.VTooltip;
+import com.vaadin.client.ui.aria.AriaHelper;
import com.vaadin.shared.AbstractComponentState;
+import com.vaadin.shared.ComponentConstants;
import com.vaadin.shared.EventId;
+import com.vaadin.shared.communication.FieldRpc.FocusAndBlurServerRpc;
import com.vaadin.shared.ui.ComponentStateUtil;
-import com.vaadin.shared.ui.tabsheet.TabsheetBaseConstants;
-import com.vaadin.shared.ui.tabsheet.TabsheetConstants;
+import com.vaadin.shared.ui.tabsheet.TabState;
+import com.vaadin.shared.ui.tabsheet.TabsheetServerRpc;
+import com.vaadin.shared.ui.tabsheet.TabsheetState;
public class VTabsheet extends VTabsheetBase implements Focusable,
- FocusHandler, BlurHandler, KeyDownHandler {
+ FocusHandler, BlurHandler, KeyDownHandler, SubPartAware {
private static class VCloseEvent {
private Tab tab;
@@ -83,7 +92,7 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
/**
* Representation of a single "tab" shown in the TabBar
- *
+ *
*/
public static class Tab extends SimplePanel implements HasFocusHandlers,
HasBlurHandlers, HasKeyDownHandlers {
@@ -94,12 +103,18 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
+ "-selected";
private static final String TD_SELECTED_FIRST_CLASSNAME = TD_SELECTED_CLASSNAME
+ "-first";
+ private static final String TD_FOCUS_CLASSNAME = TD_CLASSNAME
+ + "-focus";
+ private static final String TD_FOCUS_FIRST_CLASSNAME = TD_FOCUS_CLASSNAME
+ + "-first";
private static final String TD_DISABLED_CLASSNAME = TD_CLASSNAME
+ "-disabled";
private static final String DIV_CLASSNAME = CLASSNAME + "-tabitem";
private static final String DIV_SELECTED_CLASSNAME = DIV_CLASSNAME
+ "-selected";
+ private static final String DIV_FOCUS_CLASSNAME = DIV_CLASSNAME
+ + "-focus";
private TabCaption tabCaption;
Element td = getElement();
@@ -112,21 +127,29 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
private String styleName;
+ private String id;
+
private Tab(TabBar tabBar) {
super(DOM.createTD());
this.tabBar = tabBar;
setStyleName(td, TD_CLASSNAME);
+ Roles.getTabRole().set(getElement());
+ Roles.getTabRole().setAriaSelectedState(getElement(),
+ SelectedValue.FALSE);
+
div = DOM.createDiv();
setTabulatorIndex(-1);
setStyleName(div, DIV_CLASSNAME);
DOM.appendChild(td, div);
- tabCaption = new TabCaption(this, getTabsheet()
- .getApplicationConnection());
+ tabCaption = new TabCaption(this);
add(tabCaption);
+ Roles.getTabRole().setAriaLabelledbyProperty(getElement(),
+ Id.of(tabCaption.getElement()));
+
addFocusHandler(getTabsheet());
addBlurHandler(getTabsheet());
addKeyDownHandler(getTabsheet());
@@ -138,12 +161,13 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
public void setHiddenOnServer(boolean hiddenOnServer) {
this.hiddenOnServer = hiddenOnServer;
+ Roles.getTabRole().setAriaHiddenState(getElement(), hiddenOnServer);
}
@Override
- protected Element getContainerElement() {
+ protected com.google.gwt.user.client.Element getContainerElement() {
// Attach caption element to div, not td
- return div;
+ return DOM.asOld(div);
}
public boolean isEnabledOnServer() {
@@ -152,6 +176,8 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
public void setEnabledOnServer(boolean enabled) {
enabledOnServer = enabled;
+ Roles.getTabRole().setAriaDisabledState(getElement(), !enabled);
+
setStyleName(td, TD_DISABLED_CLASSNAME, !enabled);
if (!enabled) {
focusImpl.setTabIndex(td, -1);
@@ -168,17 +194,25 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
/**
* Toggles the style names for the Tab
- *
+ *
* @param selected
* true if the Tab is selected
* @param first
* true if the Tab is the first visible Tab
*/
public void setStyleNames(boolean selected, boolean first) {
+ setStyleNames(selected, first, false);
+ }
+
+ public void setStyleNames(boolean selected, boolean first,
+ boolean keyboardFocus) {
setStyleName(td, TD_FIRST_CLASSNAME, first);
setStyleName(td, TD_SELECTED_CLASSNAME, selected);
setStyleName(td, TD_SELECTED_FIRST_CLASSNAME, selected && first);
setStyleName(div, DIV_SELECTED_CLASSNAME, selected);
+ setStyleName(td, TD_FOCUS_CLASSNAME, keyboardFocus);
+ setStyleName(td, TD_FOCUS_FIRST_CLASSNAME, keyboardFocus && first);
+ setStyleName(div, DIV_FOCUS_CLASSNAME, keyboardFocus);
}
public void setTabulatorIndex(int tabIndex) {
@@ -197,17 +231,15 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
return tabBar.getTabsheet();
}
- public void updateFromUIDL(UIDL tabUidl) {
- tabCaption.updateCaption(tabUidl);
-
+ private void updateFromState(TabState tabState) {
+ tabCaption.update(tabState);
// Apply the styleName set for the tab
- String newStyleName = tabUidl
- .getStringAttribute(TabsheetConstants.TAB_STYLE_NAME);
+ String newStyleName = tabState.styleName;
// Find the nth td element
- if (newStyleName != null && newStyleName.length() != 0) {
+ if (newStyleName != null && !newStyleName.isEmpty()) {
if (!newStyleName.equals(styleName)) {
// If we have a new style name
- if (styleName != null && styleName.length() != 0) {
+ if (styleName != null && !styleName.isEmpty()) {
// Remove old style name if present
td.removeClassName(TD_CLASSNAME + "-" + styleName);
}
@@ -221,6 +253,15 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
td.removeClassName(TD_CLASSNAME + "-" + styleName);
styleName = null;
}
+
+ String newId = tabState.id;
+ if (newId != null && !newId.isEmpty()) {
+ td.setId(newId);
+ id = newId;
+ } else if (id != null) {
+ td.removeAttribute("id");
+ id = null;
+ }
}
public void recalculateCaptionWidth() {
@@ -251,15 +292,21 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
focusImpl.blur(td);
}
- public boolean isSelectable() {
- VTabsheet ts = getTabsheet();
- if (ts.client == null || ts.disabled || ts.waitingForResponse) {
- return false;
- }
- if (!isEnabledOnServer() || isHiddenOnServer()) {
- return false;
- }
- return true;
+ public boolean hasTooltip() {
+ return tabCaption.getTooltipInfo() != null;
+ }
+
+ public TooltipInfo getTooltipInfo() {
+ return tabCaption.getTooltipInfo();
+ }
+
+ public void setAssistiveDescription(String descriptionId) {
+ Roles.getTablistRole().setAriaDescribedbyProperty(getElement(),
+ Id.of(descriptionId));
+ }
+
+ public void removeAssistiveDescription() {
+ Roles.getTablistRole().removeAriaDescribedbyProperty(getElement());
}
}
@@ -268,38 +315,46 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
private boolean closable = false;
private Element closeButton;
private Tab tab;
- private ApplicationConnection client;
- TabCaption(Tab tab, ApplicationConnection client) {
- super(client);
- this.client = client;
+ TabCaption(Tab tab) {
+ super(tab.getTabsheet().connector.getConnection());
this.tab = tab;
+
+ AriaHelper.ensureHasId(getElement());
}
- public boolean updateCaption(UIDL uidl) {
- if (uidl.hasAttribute(TabsheetBaseConstants.ATTRIBUTE_TAB_DESCRIPTION)
- || uidl.hasAttribute(TabsheetBaseConstants.ATTRIBUTE_TAB_ERROR_MESSAGE)) {
- setTooltipInfo(new TooltipInfo(
- uidl.getStringAttribute(TabsheetBaseConstants.ATTRIBUTE_TAB_DESCRIPTION),
- uidl.getStringAttribute(TabsheetBaseConstants.ATTRIBUTE_TAB_ERROR_MESSAGE)));
+ private boolean update(TabState tabState) {
+ if (tabState.description != null
+ || tabState.componentError != null) {
+ setTooltipInfo(new TooltipInfo(tabState.description,
+ tabState.componentError));
} else {
setTooltipInfo(null);
}
// TODO need to call this instead of super because the caption does
// not have an owner
- boolean ret = updateCaptionWithoutOwner(
- uidl.getStringAttribute(TabsheetBaseConstants.ATTRIBUTE_TAB_CAPTION),
- uidl.hasAttribute(TabsheetBaseConstants.ATTRIBUTE_TAB_DISABLED),
- uidl.hasAttribute(TabsheetBaseConstants.ATTRIBUTE_TAB_DESCRIPTION),
- uidl.hasAttribute(TabsheetBaseConstants.ATTRIBUTE_TAB_ERROR_MESSAGE),
- uidl.getStringAttribute(TabsheetBaseConstants.ATTRIBUTE_TAB_ICON));
-
- setClosable(uidl.hasAttribute("closable"));
+ String captionString = tabState.caption.isEmpty() ? null
+ : tabState.caption;
+ boolean ret = updateCaptionWithoutOwner(captionString,
+ !tabState.enabled,
+ hasAttribute(tabState.description),
+ hasAttribute(tabState.componentError),
+ tab.getTabsheet().connector
+ .getResourceUrl(ComponentConstants.ICON_RESOURCE
+ + tabState.key),
+ tabState.iconAltText
+ );
+
+ setClosable(tabState.closable);
return ret;
}
+ private boolean hasAttribute(String string) {
+ return string != null && !string.trim().isEmpty();
+ }
+
private VTabsheet getTabsheet() {
return tab.getTabsheet();
}
@@ -331,6 +386,10 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
closeButton.setInnerHTML("&times;");
closeButton
.setClassName(VTabsheet.CLASSNAME + "-caption-close");
+
+ Roles.getTabRole().setAriaHiddenState(closeButton, true);
+ Roles.getTabRole().setAriaDisabledState(closeButton, true);
+
getElement().appendChild(closeButton);
} else if (!closable && closeButton != null) {
getElement().removeChild(closeButton);
@@ -356,8 +415,8 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
return width;
}
- public Element getCloseButton() {
- return closeButton;
+ public com.google.gwt.user.client.Element getCloseButton() {
+ return DOM.asOld(closeButton);
}
}
@@ -377,6 +436,8 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
this.tabsheet = tabsheet;
Element el = DOM.createTable();
+ Roles.getPresentationRole().set(el);
+
Element tbody = DOM.createTBody();
DOM.appendChild(el, tbody);
DOM.appendChild(tbody, tr);
@@ -396,8 +457,8 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
getTabsheet().sendTabClosedEvent(tabIndex);
}
- protected Element getContainerElement() {
- return tr;
+ protected com.google.gwt.user.client.Element getContainerElement() {
+ return DOM.asOld(tr);
}
public int getTabCount() {
@@ -433,7 +494,12 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
}
int index = getWidgetIndex(caption.getParent());
- getTabsheet().onTabSelected(index);
+
+ navigateTab(getTabsheet().focusedTabIndex, index);
+ getTabsheet().focusedTabIndex = index;
+ getTabsheet().focusedTab = getTab(index);
+ getTabsheet().focus();
+ getTabsheet().loadTabSheet(index);
}
public VTabsheet getTabsheet() {
@@ -451,13 +517,18 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
final Tab newSelected = getTab(index);
final Tab oldSelected = selected;
- newSelected.setStyleNames(true, isFirstVisibleTab(index));
+ newSelected.setStyleNames(true, isFirstVisibleTab(index), true);
newSelected.setTabulatorIndex(getTabsheet().tabulatorIndex);
+ Roles.getTabRole().setAriaSelectedState(newSelected.getElement(),
+ SelectedValue.TRUE);
if (oldSelected != null && oldSelected != newSelected) {
oldSelected.setStyleNames(false,
isFirstVisibleTab(getWidgetIndex(oldSelected)));
oldSelected.setTabulatorIndex(-1);
+
+ Roles.getTabRole().setAriaSelectedState(
+ oldSelected.getElement(), SelectedValue.FALSE);
}
// Update the field holding the currently selected tab
@@ -468,6 +539,23 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
getTab(tabsheet.activeTabIndex).recalculateCaptionWidth();
}
+ public void navigateTab(int fromIndex, int toIndex) {
+ Tab newNavigated = getTab(toIndex);
+ if (newNavigated == null) {
+ throw new IllegalArgumentException(
+ "Tab at provided index toIndex was not found");
+ }
+
+ Tab oldNavigated = getTab(fromIndex);
+ newNavigated.setStyleNames(newNavigated.equals(selected),
+ isFirstVisibleTab(toIndex), true);
+
+ if (oldNavigated != null && fromIndex != toIndex) {
+ oldNavigated.setStyleNames(oldNavigated.equals(selected),
+ isFirstVisibleTab(fromIndex), false);
+ }
+ }
+
public void removeTab(int i) {
Tab tab = getTab(i);
if (tab == null) {
@@ -493,7 +581,7 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
/**
* Returns the index of the first visible tab
- *
+ *
* @return
*/
private int getFirstVisibleTab() {
@@ -502,7 +590,7 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
/**
* Find the next visible tab. Returns -1 if none is found.
- *
+ *
* @param i
* @return
*/
@@ -521,7 +609,7 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
/**
* Find the previous visible tab. Returns -1 if none is found.
- *
+ *
* @param i
* @return
*/
@@ -557,15 +645,25 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
currentFirst.recalculateCaptionWidth();
return nextVisible;
}
+
+ private void recalculateCaptionWidths() {
+ for (int i = 0; i < getTabCount(); ++i) {
+ getTab(i).recalculateCaptionWidth();
+ }
+ }
}
- public static final String CLASSNAME = "v-tabsheet";
+ // TODO using the CLASSNAME directly makes primaryStyleName for TabSheet of
+ // very limited use - all use of style names should be refactored in the
+ // future
+ public static final String CLASSNAME = TabsheetState.PRIMARY_STYLE_NAME;
- public static final String TABS_CLASSNAME = "v-tabsheet-tabcontainer";
- public static final String SCROLLER_CLASSNAME = "v-tabsheet-scroller";
+ public static final String TABS_CLASSNAME = CLASSNAME + "-tabcontainer";
+ public static final String SCROLLER_CLASSNAME = CLASSNAME + "-scroller";
/** For internal use only. May be removed or replaced in the future. */
- public final Element tabs; // tabbar and 'scroller' container
+ // tabbar and 'scroller' container
+ public final Element tabs;
Tab focusedTab;
/**
* The tabindex property (position in the browser's focus cycle.) Named like
@@ -575,9 +673,12 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
private static final FocusImpl focusImpl = FocusImpl.getFocusImplForPanel();
- private final Element scroller; // tab-scroller element
- private final Element scrollerNext; // tab-scroller next button element
- private final Element scrollerPrev; // tab-scroller prev button element
+ // tab-scroller element
+ private final Element scroller;
+ // tab-scroller next button element
+ private final Element scrollerNext;
+ // tab-scroller prev button element
+ private final Element scrollerPrev;
/**
* The index of the first visible tab (when scrolled)
@@ -586,7 +687,7 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
final TabBar tb = new TabBar(this);
/** For internal use only. May be removed or replaced in the future. */
- public final VTabsheetPanel tp = new VTabsheetPanel();
+ protected final VTabsheetPanel tabPanel = new VTabsheetPanel();
/** For internal use only. May be removed or replaced in the future. */
public final Element contentNode;
@@ -597,35 +698,92 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
private String currentStyle;
- private void onTabSelected(final int tabIndex) {
- if (activeTabIndex != tabIndex) {
+ /** For internal use only. May be removed or replaced in the future. */
+ private int focusedTabIndex = 0;
+
+ /**
+ * @return Whether the tab could be selected or not.
+ */
+ private boolean canSelectTab(final int tabIndex) {
+ Tab tab = tb.getTab(tabIndex);
+ if (getApplicationConnection() == null || disabled
+ || waitingForResponse) {
+ return false;
+ }
+ if (!tab.isEnabledOnServer() || tab.isHiddenOnServer()) {
+ return false;
+ }
+
+ // Note that we return true when tabIndex == activeTabIndex; the active
+ // tab could be selected, it's just a no-op.
+ return true;
+ }
+
+ /**
+ * Load the content of a tab of the provided index.
+ *
+ * @param index
+ * of the tab to load
+ */
+ public void loadTabSheet(int tabIndex) {
+ if (activeTabIndex != tabIndex && canSelectTab(tabIndex)) {
tb.selectTab(tabIndex);
- // If this TabSheet already has focus, set the new selected tab
- // as focused.
- if (focusedTab != null) {
- focusedTab = tb.getTab(tabIndex);
- }
+ activeTabIndex = tabIndex;
addStyleDependentName("loading");
// Hide the current contents so a loading indicator can be shown
// instead
- Widget currentlyDisplayedWidget = tp.getWidget(tp
- .getVisibleWidget());
- currentlyDisplayedWidget.getElement().getParentElement().getStyle()
- .setVisibility(Visibility.HIDDEN);
- client.updateVariable(id, "selected", tabKeys.get(tabIndex)
- .toString(), true);
+ getCurrentlyDisplayedWidget().getElement().getParentElement()
+ .getStyle().setVisibility(Visibility.HIDDEN);
+
+ getRpcProxy().setSelected(tabKeys.get(tabIndex).toString());
+
waitingForResponse = true;
tb.getTab(tabIndex).focus(); // move keyboard focus to active tab
}
}
+ /**
+ * Returns the currently displayed widget in the tab panel.
+ *
+ * @since 7.2
+ * @return currently displayed content widget
+ */
+ public Widget getCurrentlyDisplayedWidget() {
+ return tabPanel.getWidget(tabPanel.getVisibleWidget());
+ }
+
+ /**
+ * Returns the client to server RPC proxy for the tabsheet.
+ *
+ * @since 7.2
+ * @return RPC proxy
+ */
+ protected TabsheetServerRpc getRpcProxy() {
+ return connector.getRpcProxy(TabsheetServerRpc.class);
+ }
+
+ /**
+ * For internal use only.
+ *
+ * Avoid using this method directly and use appropriate superclass methods
+ * where applicable.
+ *
+ * @deprecated since 7.2 - use more specific methods instead (getRpcProxy(),
+ * getConnectorForWidget(Widget) etc.)
+ * @return ApplicationConnection
+ */
+ @Deprecated
public ApplicationConnection getApplicationConnection() {
return client;
}
+ private VTooltip getVTooltip() {
+ return getApplicationConnection().getVTooltip();
+ }
+
public void tabSizeMightHaveChanged(Tab tab) {
// icon onloads may change total width of tabsheet
if (isDynamicWidth()) {
@@ -636,19 +794,7 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
}
void sendTabClosedEvent(int tabIndex) {
- client.updateVariable(id, "close", tabKeys.get(tabIndex), true);
- }
-
- boolean isDynamicWidth() {
- ComponentConnector paintable = ConnectorMap.get(client).getConnector(
- this);
- return paintable.isUndefinedWidth();
- }
-
- boolean isDynamicHeight() {
- ComponentConnector paintable = ConnectorMap.get(client).getConnector(
- this);
- return paintable.isUndefinedHeight();
+ getRpcProxy().closeTab(tabKeys.get(tabIndex));
}
public VTabsheet() {
@@ -661,22 +807,30 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
DOM.setStyleAttribute(getElement(), "overflow", "hidden");
tabs = DOM.createDiv();
DOM.setElementProperty(tabs, "className", TABS_CLASSNAME);
+ Roles.getTablistRole().set(tabs);
+ Roles.getTablistRole().setAriaLiveProperty(tabs, LiveValue.OFF);
scroller = DOM.createDiv();
+ Roles.getTablistRole().setAriaHiddenState(scroller, true);
DOM.setElementProperty(scroller, "className", SCROLLER_CLASSNAME);
scrollerPrev = DOM.createButton();
+ scrollerPrev.setTabIndex(-1);
DOM.setElementProperty(scrollerPrev, "className", SCROLLER_CLASSNAME
+ "Prev");
+ Roles.getTablistRole().setAriaHiddenState(scrollerPrev, true);
DOM.sinkEvents(scrollerPrev, Event.ONCLICK);
scrollerNext = DOM.createButton();
+ scrollerNext.setTabIndex(-1);
DOM.setElementProperty(scrollerNext, "className", SCROLLER_CLASSNAME
+ "Next");
+ Roles.getTablistRole().setAriaHiddenState(scrollerNext, true);
DOM.sinkEvents(scrollerNext, Event.ONCLICK);
DOM.appendChild(getElement(), tabs);
// Tabs
- tp.setStyleName(CLASSNAME + "-tabsheetpanel");
+ tabPanel.setStyleName(CLASSNAME + "-tabsheetpanel");
contentNode = DOM.createDiv();
+ Roles.getTabpanelRole().set(contentNode);
deco = DOM.createDiv();
@@ -690,7 +844,7 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
DOM.appendChild(scroller, scrollerNext);
DOM.appendChild(getElement(), contentNode);
- add(tp, contentNode);
+ add(tabPanel, contentNode);
DOM.appendChild(getElement(), deco);
DOM.appendChild(tabs, scroller);
@@ -731,7 +885,7 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
/**
* Checks if the tab with the selected index has been scrolled out of the
* view (on the left side).
- *
+ *
* @param index
* @return
*/
@@ -740,7 +894,7 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
}
/** For internal use only. May be removed or replaced in the future. */
- public void handleStyleNames(UIDL uidl, AbstractComponentState state) {
+ public void handleStyleNames(AbstractComponentState state) {
// Add proper stylenames for all elements (easier to prevent unwanted
// style inheritance)
if (ComponentStateUtil.hasStyles(state)) {
@@ -771,14 +925,6 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
+ "-content");
DOM.setElementProperty(deco, "className", CLASSNAME + "-deco");
}
-
- if (uidl.hasAttribute("hidetabs")) {
- tb.setVisible(false);
- addStyleName(CLASSNAME + "-hidetabs");
- } else {
- tb.setVisible(true);
- removeStyleName(CLASSNAME + "-hidetabs");
- }
}
/** For internal use only. May be removed or replaced in the future. */
@@ -795,16 +941,16 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
int tabsWidth = tb.getOffsetWidth() - spacerWidth + spacerMinWidth;
// Find content width
- Style style = tp.getElement().getStyle();
+ Style style = tabPanel.getElement().getStyle();
String overflow = style.getProperty("overflow");
style.setProperty("overflow", "hidden");
style.setPropertyPx("width", tabsWidth);
- boolean hasTabs = tp.getWidgetCount() > 0;
+ boolean hasTabs = tabPanel.getWidgetCount() > 0;
Style wrapperstyle = null;
if (hasTabs) {
- wrapperstyle = tp.getWidget(tp.getVisibleWidget()).getElement()
+ wrapperstyle = getCurrentlyDisplayedWidget().getElement()
.getParentElement().getStyle();
wrapperstyle.setPropertyPx("width", tabsWidth);
}
@@ -812,7 +958,7 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
int contentWidth = 0;
if (hasTabs) {
- contentWidth = tp.getWidget(tp.getVisibleWidget()).getOffsetWidth();
+ contentWidth = getCurrentlyDisplayedWidget().getOffsetWidth();
}
style.setProperty("overflow", overflow);
@@ -835,26 +981,22 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
}
@Override
- public void renderTab(final UIDL tabUidl, int index, boolean selected,
- boolean hidden) {
+ public void renderTab(final TabState tabState, int index) {
Tab tab = tb.getTab(index);
if (tab == null) {
tab = tb.addTab();
}
- if (selected) {
- tb.selectTab(index);
- renderContent(tabUidl.getChildUIDL(0));
- }
- tab.updateFromUIDL(tabUidl);
+
+ tab.updateFromState(tabState);
tab.setEnabledOnServer((!disabledTabKeys.contains(tabKeys.get(index))));
- tab.setHiddenOnServer(hidden);
+ tab.setHiddenOnServer(!tabState.visible);
if (scrolledOutOfView(index)) {
// Should not set tabs visible if they are scrolled out of view
- hidden = true;
+ tabState.visible = false;
}
// Set the current visibility of the tab (in the browser)
- tab.setVisible(!hidden);
+ tab.setVisible(tabState.visible);
/*
* Force the width of the caption container so the content will not wrap
@@ -874,29 +1016,45 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
}
}
- private void renderContent(final UIDL contentUIDL) {
- final ComponentConnector content = client.getPaintable(contentUIDL);
- Widget newWidget = content.getWidget();
+ /**
+ * Renders the widget content for a tab sheet.
+ *
+ * @param newWidget
+ */
+ public void renderContent(Widget newWidget) {
+ assert tabPanel.getWidgetCount() <= 1;
- assert tp.getWidgetCount() <= 1;
+ if (null == newWidget) {
+ newWidget = new SimplePanel();
+ }
- if (tp.getWidgetCount() == 0) {
- tp.add(newWidget);
- } else if (tp.getWidget(0) != newWidget) {
- tp.remove(0);
- tp.add(newWidget);
+ if (tabPanel.getWidgetCount() == 0) {
+ tabPanel.add(newWidget);
+ } else if (tabPanel.getWidget(0) != newWidget) {
+ tabPanel.remove(0);
+ tabPanel.add(newWidget);
}
- assert tp.getWidgetCount() <= 1;
+ assert tabPanel.getWidgetCount() <= 1;
// There's never any other index than 0, but maintaining API for now
- tp.showWidget(0);
+ tabPanel.showWidget(0);
VTabsheet.this.iLayout();
updateOpenTabSize();
VTabsheet.this.removeStyleDependentName("loading");
}
+ /**
+ * Recalculates the sizes of tab captions, causing the tabs to be
+ * rendered the correct size.
+ */
+ private void updateTabCaptionSizes() {
+ for (int tabIx = 0; tabIx < tb.getTabCount(); tabIx++) {
+ tb.getTab(tabIx).recalculateCaptionWidth();
+ }
+ }
+
/** For internal use only. May be removed or replaced in the future. */
public void updateContentNodeHeight() {
if (!isDynamicHeight()) {
@@ -914,8 +1072,12 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
}
}
+ /**
+ * Run internal layouting.
+ */
public void iLayout() {
updateTabScroller();
+ updateTabCaptionSizes();
}
/**
@@ -947,7 +1109,7 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
*/
minWidth = tb.getOffsetWidth() - getContentAreaBorderWidth();
}
- tp.fixVisibleTabSize(width, height, minWidth);
+ tabPanel.fixVisibleTabSize(width, height, minWidth);
}
@@ -1038,13 +1200,13 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
while (i > 0) {
tb.removeTab(--i);
}
- tp.clear();
+ tabPanel.clear();
}
@Override
public Iterator<Widget> getWidgetIterator() {
- return tp.iterator();
+ return tabPanel.iterator();
}
private int borderW = -1;
@@ -1064,9 +1226,9 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
@Override
public ComponentConnector getTab(int index) {
- if (tp.getWidgetCount() > index) {
- Widget widget = tp.getWidget(index);
- return ConnectorMap.get(client).getConnector(widget);
+ if (tabPanel.getWidgetCount() > index) {
+ Widget widget = tabPanel.getWidget(index);
+ return getConnectorForWidget(widget);
}
return null;
}
@@ -1079,11 +1241,19 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
}
@Override
+ public void selectTab(int index) {
+ tb.selectTab(index);
+ }
+
+ @Override
public void onBlur(BlurEvent event) {
+ getVTooltip().hideTooltip();
+
if (focusedTab != null && focusedTab == event.getSource()) {
+ focusedTab.removeAssistiveDescription();
focusedTab = null;
- if (client.hasEventListeners(this, EventId.BLUR)) {
- client.updateVariable(id, EventId.BLUR, "", true);
+ if (connector.hasEventListener(EventId.BLUR)) {
+ connector.getRpcProxy(FocusAndBlurServerRpc.class).blur();
}
}
}
@@ -1092,8 +1262,13 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
public void onFocus(FocusEvent event) {
if (focusedTab == null && event.getSource() instanceof Tab) {
focusedTab = (Tab) event.getSource();
- if (client.hasEventListeners(this, EventId.FOCUS)) {
- client.updateVariable(id, EventId.FOCUS, "", true);
+ if (connector.hasEventListener(EventId.FOCUS)) {
+ connector.getRpcProxy(FocusAndBlurServerRpc.class).focus();
+ }
+
+ if (focusedTab.hasTooltip()) {
+ focusedTab.setAssistiveDescription(getVTooltip().getUniqueId());
+ getVTooltip().showAssistive(focusedTab.getTooltipInfo());
}
}
}
@@ -1115,13 +1290,17 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
if (!event.isAnyModifierKeyDown()) {
if (keycode == getPreviousTabKey()) {
selectPreviousTab();
+ event.stopPropagation();
} else if (keycode == getNextTabKey()) {
selectNextTab();
+ event.stopPropagation();
} else if (keycode == getCloseTabKey()) {
Tab tab = tb.getTab(activeTabIndex);
if (tab.isClosable()) {
tab.onClose();
}
+ } else if (keycode == getSelectTabKey()) {
+ loadTabSheet(focusedTabIndex);
}
}
}
@@ -1135,6 +1314,10 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
return KeyCodes.KEY_LEFT;
}
+ protected int getSelectTabKey() {
+ return 32; // Space key
+ }
+
/**
* @return The key code of the keyboard shortcut that selects the next tab
* in a focused tabsheet.
@@ -1152,32 +1335,43 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
}
private void selectPreviousTab() {
- int newTabIndex = activeTabIndex;
- Tab newTab;
+ int newTabIndex = focusedTabIndex;
// Find the previous visible and enabled tab if any.
do {
newTabIndex--;
- newTab = tb.getTab(newTabIndex);
- } while (newTabIndex >= 0 && !newTab.isSelectable());
+ } while (newTabIndex >= 0 && !canSelectTab(newTabIndex));
if (newTabIndex >= 0) {
- onTabSelected(newTabIndex);
- activeTabIndex = newTabIndex;
+ tb.navigateTab(focusedTabIndex, newTabIndex);
+ focusedTabIndex = newTabIndex;
+
+ // If this TabSheet already has focus, set the new selected tab
+ // as focused.
+ if (focusedTab != null) {
+ focusedTab = tb.getTab(focusedTabIndex);
+ focusedTab.focus();
+ }
}
}
private void selectNextTab() {
- int newTabIndex = activeTabIndex;
- Tab newTab;
+ int newTabIndex = focusedTabIndex;
// Find the next visible and enabled tab if any.
do {
newTabIndex++;
- newTab = tb.getTab(newTabIndex);
- } while (newTabIndex < getTabCount() && !newTab.isSelectable());
+ } while (newTabIndex < getTabCount() && !canSelectTab(newTabIndex));
if (newTabIndex < getTabCount()) {
- onTabSelected(newTabIndex);
- activeTabIndex = newTabIndex;
+
+ tb.navigateTab(focusedTabIndex, newTabIndex);
+ focusedTabIndex = newTabIndex;
+
+ // If this TabSheet already has focus, set the new selected tab
+ // as focused.
+ if (focusedTab != null) {
+ focusedTab = tb.getTab(focusedTabIndex);
+ focusedTab.focus();
+ }
}
}
@@ -1196,4 +1390,70 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
}
}
}
+
+ /**
+ * Makes tab bar visible.
+ *
+ * @since 7.2
+ */
+ public void showTabs() {
+ tb.setVisible(true);
+ removeStyleName(CLASSNAME + "-hidetabs");
+ tb.recalculateCaptionWidths();
+ }
+
+ /**
+ * Makes tab bar invisible.
+ *
+ * @since 7.2
+ */
+ public void hideTabs() {
+ tb.setVisible(false);
+ addStyleName(CLASSNAME + "-hidetabs");
+ }
+
+ /** Matches tab[ix] - used for extracting the index of the targeted tab */
+ private static final RegExp SUBPART_TAB_REGEXP = RegExp
+ .compile("tab\\[(\\d+)](.*)");
+
+ @Override
+ public com.google.gwt.user.client.Element getSubPartElement(String subPart) {
+ if ("tabpanel".equals(subPart)) {
+ return DOM.asOld(tabPanel.getElement().getFirstChildElement());
+ } else if (SUBPART_TAB_REGEXP.test(subPart)) {
+ MatchResult result = SUBPART_TAB_REGEXP.exec(subPart);
+ int tabIx = Integer.valueOf(result.getGroup(1));
+ Tab tab = tb.getTab(tabIx);
+ if (tab != null) {
+ if ("/close".equals(result.getGroup(2))) {
+ if (tab.isClosable()) {
+ return tab.tabCaption.getCloseButton();
+ }
+ } else {
+ return tab.tabCaption.getElement();
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public String getSubPartName(com.google.gwt.user.client.Element subElement) {
+ if (tabPanel.getElement().equals(subElement.getParentElement())
+ || tabPanel.getElement().equals(subElement)) {
+ return "tabpanel";
+ } else {
+ for (int i = 0; i < tb.getTabCount(); ++i) {
+ Tab tab = tb.getTab(i);
+ if (tab.isClosable()
+ && tab.tabCaption.getCloseButton().isOrHasChild(
+ subElement)) {
+ return "tab[" + i + "]/close";
+ } else if (tab.getElement().isOrHasChild(subElement)) {
+ return "tab[" + i + "]";
+ }
+ }
+ }
+ return null;
+ }
}
diff --git a/client/src/com/vaadin/client/ui/VTabsheetBase.java b/client/src/com/vaadin/client/ui/VTabsheetBase.java
index 0923248115..6d9f78e87f 100644
--- a/client/src/com/vaadin/client/ui/VTabsheetBase.java
+++ b/client/src/com/vaadin/client/ui/VTabsheetBase.java
@@ -25,26 +25,28 @@ import com.google.gwt.user.client.ui.ComplexPanel;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.ComponentConnector;
-import com.vaadin.client.UIDL;
+import com.vaadin.client.ConnectorMap;
+import com.vaadin.shared.ui.tabsheet.TabState;
public abstract class VTabsheetBase extends ComplexPanel {
/** For internal use only. May be removed or replaced in the future. */
- public String id;
- /** For internal use only. May be removed or replaced in the future. */
- public ApplicationConnection client;
+ protected ApplicationConnection client;
/** For internal use only. May be removed or replaced in the future. */
- public final ArrayList<String> tabKeys = new ArrayList<String>();
+ protected final ArrayList<String> tabKeys = new ArrayList<String>();
/** For internal use only. May be removed or replaced in the future. */
- public Set<String> disabledTabKeys = new HashSet<String>();
+ protected Set<String> disabledTabKeys = new HashSet<String>();
/** For internal use only. May be removed or replaced in the future. */
- public int activeTabIndex = 0;
+ protected int activeTabIndex = 0;
+ /** For internal use only. May be removed or replaced in the future. */
+ protected boolean disabled;
/** For internal use only. May be removed or replaced in the future. */
- public boolean disabled;
+ protected boolean readonly;
+
/** For internal use only. May be removed or replaced in the future. */
- public boolean readonly;
+ protected AbstractComponentConnector connector;
public VTabsheetBase(String classname) {
setElement(DOM.createDiv());
@@ -59,14 +61,13 @@ public abstract class VTabsheetBase extends ComplexPanel {
/**
* Clears current tabs and contents
*/
- abstract protected void clearPaintables();
+ protected abstract void clearPaintables();
/**
* Implement in extending classes. This method should render needed elements
* and set the visibility of the tab according to the 'selected' parameter.
*/
- public abstract void renderTab(final UIDL tabUidl, int index,
- boolean selected, boolean hidden);
+ public abstract void renderTab(TabState tabState, int index);
/**
* Implement in extending classes. This method should return the number of
@@ -85,4 +86,79 @@ public abstract class VTabsheetBase extends ComplexPanel {
* tab with the specified index.
*/
public abstract void removeTab(int index);
+
+ /**
+ * Returns true if the width of the widget is undefined, false otherwise.
+ *
+ * @since 7.2
+ * @return true if width of the widget is determined by its content
+ */
+ protected boolean isDynamicWidth() {
+ return getConnectorForWidget(this).isUndefinedWidth();
+ }
+
+ /**
+ * Returns true if the height of the widget is undefined, false otherwise.
+ *
+ * @since 7.2
+ * @return true if width of the height is determined by its content
+ */
+ protected boolean isDynamicHeight() {
+ return getConnectorForWidget(this).isUndefinedHeight();
+ }
+
+ /**
+ * Sets the connector that should be notified of events etc.
+ *
+ * For internal use only. This method may be removed or replaced in the
+ * future.
+ *
+ * @since 7.2
+ * @param connector
+ */
+ public void setConnector(AbstractComponentConnector connector) {
+ this.connector = connector;
+ }
+
+ /** For internal use only. May be removed or replaced in the future. */
+ public void clearTabKeys() {
+ tabKeys.clear();
+ disabledTabKeys.clear();
+ }
+
+ /** For internal use only. May be removed or replaced in the future. */
+ public void addTabKey(String key, boolean disabled) {
+ tabKeys.add(key);
+ if (disabled) {
+ disabledTabKeys.add(key);
+ }
+ }
+
+ /** For internal use only. May be removed or replaced in the future. */
+ public void setClient(ApplicationConnection client) {
+ this.client = client;
+ }
+
+ /** For internal use only. May be removed or replaced in the future. */
+ public void setActiveTabIndex(int activeTabIndex) {
+ this.activeTabIndex = activeTabIndex;
+ }
+
+ /** For internal use only. May be removed or replaced in the future. */
+ public void setEnabled(boolean enabled) {
+ disabled = !enabled;
+ }
+
+ /** For internal use only. May be removed or replaced in the future. */
+ public void setReadonly(boolean readonly) {
+ this.readonly = readonly;
+ }
+
+ /** For internal use only. May be removed or replaced in the future. */
+ protected ComponentConnector getConnectorForWidget(Widget widget) {
+ return ConnectorMap.get(client).getConnector(widget);
+ }
+
+ /** For internal use only. May be removed or replaced in the future. */
+ public abstract void selectTab(int index);
}
diff --git a/client/src/com/vaadin/client/ui/VTabsheetPanel.java b/client/src/com/vaadin/client/ui/VTabsheetPanel.java
index 10ef0aeb65..6bd63cdbd3 100644
--- a/client/src/com/vaadin/client/ui/VTabsheetPanel.java
+++ b/client/src/com/vaadin/client/ui/VTabsheetPanel.java
@@ -16,8 +16,8 @@
package com.vaadin.client.ui;
+import com.google.gwt.dom.client.Element;
import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.ui.ComplexPanel;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.ui.TouchScrollDelegate.TouchScrollHandler;
@@ -165,8 +165,7 @@ public class VTabsheetPanel extends ComplexPanel {
width = minWidth;
}
- Element wrapperDiv = (Element) visibleWidget.getElement()
- .getParentElement();
+ Element wrapperDiv = visibleWidget.getElement().getParentElement();
// width first
getElement().getStyle().setPropertyPx("width", width);
diff --git a/client/src/com/vaadin/client/ui/VTextField.java b/client/src/com/vaadin/client/ui/VTextField.java
index 9360a6e172..98c8699405 100644
--- a/client/src/com/vaadin/client/ui/VTextField.java
+++ b/client/src/com/vaadin/client/ui/VTextField.java
@@ -16,6 +16,7 @@
package com.vaadin.client.ui;
+import com.google.gwt.dom.client.Element;
import com.google.gwt.event.dom.client.BlurEvent;
import com.google.gwt.event.dom.client.BlurHandler;
import com.google.gwt.event.dom.client.ChangeEvent;
@@ -26,7 +27,6 @@ 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.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.ui.TextBoxBase;
diff --git a/client/src/com/vaadin/client/ui/VTextualDate.java b/client/src/com/vaadin/client/ui/VTextualDate.java
index 9d7e31faab..44a3321f6f 100644
--- a/client/src/com/vaadin/client/ui/VTextualDate.java
+++ b/client/src/com/vaadin/client/ui/VTextualDate.java
@@ -25,7 +25,6 @@ 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.user.client.Element;
import com.google.gwt.user.client.ui.TextBox;
import com.vaadin.client.Focusable;
import com.vaadin.client.LocaleNotLoadedException;
@@ -164,7 +163,8 @@ public class VTextualDate extends VDateField implements Field, ChangeHandler,
}
@Override
- public void bindAriaCaption(Element captionElement) {
+ public void bindAriaCaption(
+ com.google.gwt.user.client.Element captionElement) {
AriaHelper.bindCaption(text, captionElement);
}
@@ -364,7 +364,7 @@ public class VTextualDate extends VDateField implements Field, ChangeHandler,
private final String TEXTFIELD_ID = "field";
@Override
- public Element getSubPartElement(String subPart) {
+ public com.google.gwt.user.client.Element getSubPartElement(String subPart) {
if (subPart.equals(TEXTFIELD_ID)) {
return text.getElement();
}
@@ -373,7 +373,7 @@ public class VTextualDate extends VDateField implements Field, ChangeHandler,
}
@Override
- public String getSubPartName(Element subElement) {
+ public String getSubPartName(com.google.gwt.user.client.Element subElement) {
if (text.getElement().isOrHasChild(subElement)) {
return TEXTFIELD_ID;
}
diff --git a/client/src/com/vaadin/client/ui/VTree.java b/client/src/com/vaadin/client/ui/VTree.java
index 51c00ca310..4979de6a47 100644
--- a/client/src/com/vaadin/client/ui/VTree.java
+++ b/client/src/com/vaadin/client/ui/VTree.java
@@ -31,6 +31,7 @@ import com.google.gwt.aria.client.SelectedValue;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
+import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.dom.client.Node;
import com.google.gwt.event.dom.client.BlurEvent;
@@ -46,7 +47,6 @@ import com.google.gwt.event.dom.client.KeyPressEvent;
import com.google.gwt.event.dom.client.KeyPressHandler;
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.Window;
import com.google.gwt.user.client.ui.FlowPanel;
@@ -70,6 +70,7 @@ import com.vaadin.client.ui.dd.VDragEvent;
import com.vaadin.client.ui.dd.VDropHandler;
import com.vaadin.client.ui.dd.VHasDropHandler;
import com.vaadin.client.ui.dd.VTransferable;
+import com.vaadin.client.ui.tree.TreeConnector;
import com.vaadin.shared.MouseEventDetails;
import com.vaadin.shared.MouseEventDetails.MouseButton;
import com.vaadin.shared.ui.MultiSelectMode;
@@ -162,6 +163,9 @@ public class VTree extends FocusElementPanel implements VHasDropHandler,
/** For internal use only. May be removed or replaced in the future. */
public String[] bodyActionKeys;
+ /** For internal use only. May be removed or replaced in the future. */
+ public TreeConnector connector;
+
public VLazyExecutor iconLoaded = new VLazyExecutor(50,
new ScheduledCommand() {
@@ -1120,23 +1124,15 @@ public class VTree extends FocusElementPanel implements VHasDropHandler,
}
public void setIcon(String iconUrl, String altText) {
- if (iconUrl != null) {
- // Add icon if not present
- if (icon == null) {
- icon = new Icon(client);
- Roles.getImgRole().set(icon.getElement());
- DOM.insertBefore(DOM.getFirstChild(nodeCaptionDiv),
- icon.getElement(), nodeCaptionSpan);
- }
- icon.setUri(iconUrl);
- icon.getElement().setAttribute("alt", altText);
- } else {
- // Remove icon if present
- if (icon != null) {
- DOM.removeChild(DOM.getFirstChild(nodeCaptionDiv),
- icon.getElement());
- icon = null;
- }
+ if (icon != null) {
+ DOM.getFirstChild(nodeCaptionDiv)
+ .removeChild(icon.getElement());
+ }
+ icon = client.getIcon(iconUrl);
+ if (icon != null) {
+ DOM.insertBefore(DOM.getFirstChild(nodeCaptionDiv),
+ icon.getElement(), nodeCaptionSpan);
+ icon.setAlternateText(altText);
}
}
@@ -1729,6 +1725,7 @@ public class VTree extends FocusElementPanel implements VHasDropHandler,
}
}
}
+ showTooltipForKeyboardNavigation(node);
return true;
}
@@ -1754,6 +1751,7 @@ public class VTree extends FocusElementPanel implements VHasDropHandler,
}
}
}
+ showTooltipForKeyboardNavigation(node);
return true;
}
@@ -1774,6 +1772,7 @@ public class VTree extends FocusElementPanel implements VHasDropHandler,
focusAndSelectNode(focusedNode.getParentNode());
}
}
+ showTooltipForKeyboardNavigation(focusedNode);
return true;
}
@@ -1792,6 +1791,7 @@ public class VTree extends FocusElementPanel implements VHasDropHandler,
focusAndSelectNode(focusedNode.getChildren().get(0));
}
}
+ showTooltipForKeyboardNavigation(focusedNode);
return true;
}
@@ -1820,6 +1820,7 @@ public class VTree extends FocusElementPanel implements VHasDropHandler,
selectNode(node, true);
}
sendSelectionToServer();
+ showTooltipForKeyboardNavigation(node);
return true;
}
@@ -1836,12 +1837,20 @@ public class VTree extends FocusElementPanel implements VHasDropHandler,
selectNode(node, true);
}
sendSelectionToServer();
+ showTooltipForKeyboardNavigation(node);
return true;
}
return false;
}
+ private void showTooltipForKeyboardNavigation(TreeNode node) {
+ if (connector != null) {
+ getClient().getVTooltip().showAssistive(
+ connector.getTooltipInfo(node.nodeCaptionSpan));
+ }
+ }
+
private void focusAndSelectNode(TreeNode node) {
/*
* Keyboard navigation doesn't work reliably if the tree is in
@@ -2051,7 +2060,7 @@ public class VTree extends FocusElementPanel implements VHasDropHandler,
* .lang.String)
*/
@Override
- public Element getSubPartElement(String subPart) {
+ public com.google.gwt.user.client.Element getSubPartElement(String subPart) {
if ("fe".equals(subPart)) {
if (BrowserInfo.get().isOpera() && focusedNode != null) {
return focusedNode.getElement();
@@ -2087,7 +2096,7 @@ public class VTree extends FocusElementPanel implements VHasDropHandler,
if (expandCollapse) {
return treeNode.getElement();
} else {
- return treeNode.nodeCaptionSpan;
+ return DOM.asOld(treeNode.nodeCaptionSpan);
}
} catch (Exception e) {
// Invalid locator string or node could not be found
@@ -2104,7 +2113,7 @@ public class VTree extends FocusElementPanel implements VHasDropHandler,
* .gwt.user.client.Element)
*/
@Override
- public String getSubPartName(Element subElement) {
+ public String getSubPartName(com.google.gwt.user.client.Element subElement) {
// Supported identifiers:
//
// n[index]/n[index]/n[index]{/expand}
@@ -2213,7 +2222,8 @@ public class VTree extends FocusElementPanel implements VHasDropHandler,
}
@Override
- public void bindAriaCaption(Element captionElement) {
+ public void bindAriaCaption(
+ com.google.gwt.user.client.Element captionElement) {
AriaHelper.bindCaption(body, captionElement);
}
}
diff --git a/client/src/com/vaadin/client/ui/VTreeTable.java b/client/src/com/vaadin/client/ui/VTreeTable.java
index 54c9c2d30c..591aa6b0de 100644
--- a/client/src/com/vaadin/client/ui/VTreeTable.java
+++ b/client/src/com/vaadin/client/ui/VTreeTable.java
@@ -25,6 +25,7 @@ import com.google.gwt.animation.client.Animation;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.ImageElement;
import com.google.gwt.dom.client.SpanElement;
import com.google.gwt.dom.client.Style.Display;
@@ -33,7 +34,6 @@ import com.google.gwt.dom.client.Style.Visibility;
import com.google.gwt.dom.client.TableCellElement;
import com.google.gwt.event.dom.client.KeyCodes;
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.ui.Widget;
import com.vaadin.client.ComputedStyle;
@@ -663,8 +663,8 @@ public class VTreeTable extends VScrollTable {
}
private void insertAnimatingDiv() {
- Element tableBody = getElement().cast();
- Element tableBodyParent = tableBody.getParentElement().cast();
+ Element tableBody = getElement();
+ Element tableBodyParent = tableBody.getParentElement();
tableBodyParent.insertAfter(cloneDiv, tableBody);
}
@@ -709,8 +709,7 @@ public class VTreeTable extends VScrollTable {
resetCellWrapperDivsDisplayProperty(row);
row.removeStyleName("v-table-row-animating");
}
- Element tableBodyParent = (Element) getElement()
- .getParentElement();
+ Element tableBodyParent = getElement().getParentElement();
tableBodyParent.removeChild(cloneDiv);
}
diff --git a/client/src/com/vaadin/client/ui/VTwinColSelect.java b/client/src/com/vaadin/client/ui/VTwinColSelect.java
index a53ed835d2..33f1afea31 100644
--- a/client/src/com/vaadin/client/ui/VTwinColSelect.java
+++ b/client/src/com/vaadin/client/ui/VTwinColSelect.java
@@ -33,7 +33,6 @@ import com.google.gwt.event.dom.client.MouseDownEvent;
import com.google.gwt.event.dom.client.MouseDownHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.ListBox;
@@ -569,20 +568,20 @@ public class VTwinColSelect extends VOptionGroupBase implements KeyDownHandler,
private static final String SUBPART_REMOVE_BUTTON = "remove";
@Override
- public Element getSubPartElement(String subPart) {
+ public com.google.gwt.user.client.Element getSubPartElement(String subPart) {
if (SUBPART_OPTION_SELECT.equals(subPart)) {
return options.getElement();
} else if (subPart.startsWith(SUBPART_OPTION_SELECT_ITEM)) {
String idx = subPart.substring(SUBPART_OPTION_SELECT_ITEM.length());
- return (Element) options.getElement().getChild(
- Integer.parseInt(idx));
+ return (com.google.gwt.user.client.Element) options.getElement()
+ .getChild(Integer.parseInt(idx));
} else if (SUBPART_SELECTION_SELECT.equals(subPart)) {
return selections.getElement();
} else if (subPart.startsWith(SUBPART_SELECTION_SELECT_ITEM)) {
String idx = subPart.substring(SUBPART_SELECTION_SELECT_ITEM
.length());
- return (Element) selections.getElement().getChild(
- Integer.parseInt(idx));
+ return (com.google.gwt.user.client.Element) selections.getElement()
+ .getChild(Integer.parseInt(idx));
} else if (optionsCaption != null
&& SUBPART_LEFT_CAPTION.equals(subPart)) {
return optionsCaption.getElement();
@@ -599,7 +598,7 @@ public class VTwinColSelect extends VOptionGroupBase implements KeyDownHandler,
}
@Override
- public String getSubPartName(Element subElement) {
+ public String getSubPartName(com.google.gwt.user.client.Element subElement) {
if (optionsCaption != null
&& optionsCaption.getElement().isOrHasChild(subElement)) {
return SUBPART_LEFT_CAPTION;
diff --git a/client/src/com/vaadin/client/ui/VUI.java b/client/src/com/vaadin/client/ui/VUI.java
index ba3495743d..590263a5ed 100644
--- a/client/src/com/vaadin/client/ui/VUI.java
+++ b/client/src/com/vaadin/client/ui/VUI.java
@@ -18,6 +18,7 @@ package com.vaadin.client.ui;
import java.util.ArrayList;
+import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.dom.client.Element;
import com.google.gwt.event.dom.client.HasScrollHandlers;
@@ -43,6 +44,7 @@ import com.vaadin.client.ConnectorMap;
import com.vaadin.client.Focusable;
import com.vaadin.client.LayoutManager;
import com.vaadin.client.Profiler;
+import com.vaadin.client.Util;
import com.vaadin.client.VConsole;
import com.vaadin.client.ui.ShortcutActionHandler.ShortcutActionHandlerOwner;
import com.vaadin.client.ui.TouchScrollDelegate.TouchScrollHandler;
@@ -162,6 +164,8 @@ public class VUI extends SimplePanel implements ResizeHandler,
});
+ private Element storedFocus;
+
public VUI() {
super();
// Allow focusing the view by using the focus() method, the view
@@ -494,4 +498,36 @@ public class VUI extends SimplePanel implements ResizeHandler,
FocusUtil.setTabIndex(this, index);
}
+ /**
+ * Allows to store the currently focused Element.
+ *
+ * Current use case is to store the focus when a Window is opened. Does
+ * currently handle only a single value. Needs to be extended for #12158
+ *
+ * @param focusedElement
+ */
+ public void storeFocus() {
+ storedFocus = Util.getFocusedElement();
+ }
+
+ /**
+ * Restores the previously stored focus Element.
+ *
+ * Current use case is to restore the focus when a Window is closed. Does
+ * currently handle only a single value. Needs to be extended for #12158
+ *
+ * @return the lastFocusElementBeforeDialogOpened
+ */
+ public void focusStoredElement() {
+ if (storedFocus != null) {
+ storedFocus.focus();
+
+ Scheduler.get().scheduleDeferred(new ScheduledCommand() {
+ @Override
+ public void execute() {
+ storedFocus.focus();
+ }
+ });
+ }
+ }
}
diff --git a/client/src/com/vaadin/client/ui/VUnknownComponent.java b/client/src/com/vaadin/client/ui/VUnknownComponent.java
index e77a4f76dd..ea97110aaa 100644
--- a/client/src/com/vaadin/client/ui/VUnknownComponent.java
+++ b/client/src/com/vaadin/client/ui/VUnknownComponent.java
@@ -22,7 +22,7 @@ import com.vaadin.client.SimpleTree;
public class VUnknownComponent extends Composite {
- com.google.gwt.user.client.ui.Label caption = new com.google.gwt.user.client.ui.Label();;
+ com.google.gwt.user.client.ui.Label caption = new com.google.gwt.user.client.ui.Label();
SimpleTree uidlTree;
protected VerticalPanel panel;
diff --git a/client/src/com/vaadin/client/ui/VUpload.java b/client/src/com/vaadin/client/ui/VUpload.java
index 8e55387d39..bcb4265d50 100644
--- a/client/src/com/vaadin/client/ui/VUpload.java
+++ b/client/src/com/vaadin/client/ui/VUpload.java
@@ -21,11 +21,11 @@ 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.FormElement;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.Command;
-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.ui.FileUpload;
diff --git a/client/src/com/vaadin/client/ui/VVideo.java b/client/src/com/vaadin/client/ui/VVideo.java
index 9d6a531a74..376c832bed 100644
--- a/client/src/com/vaadin/client/ui/VVideo.java
+++ b/client/src/com/vaadin/client/ui/VVideo.java
@@ -17,9 +17,9 @@
package com.vaadin.client.ui;
import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.dom.client.VideoElement;
-import com.google.gwt.user.client.Element;
import com.vaadin.client.Util;
public class VVideo extends VMediaBase {
diff --git a/client/src/com/vaadin/client/ui/VWindow.java b/client/src/com/vaadin/client/ui/VWindow.java
index 6afb6c2d8a..5b6595ea43 100644
--- a/client/src/com/vaadin/client/ui/VWindow.java
+++ b/client/src/com/vaadin/client/ui/VWindow.java
@@ -18,11 +18,18 @@ package com.vaadin.client.ui;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.Comparator;
+import java.util.List;
+import com.google.gwt.aria.client.Id;
+import com.google.gwt.aria.client.RelevantValue;
+import com.google.gwt.aria.client.Roles;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.dom.client.Style;
import com.google.gwt.dom.client.Style.Position;
import com.google.gwt.dom.client.Style.Unit;
@@ -33,35 +40,43 @@ 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.event.dom.client.KeyUpEvent;
+import com.google.gwt.event.dom.client.KeyUpHandler;
import com.google.gwt.event.dom.client.ScrollEvent;
import com.google.gwt.event.dom.client.ScrollHandler;
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.Event.NativePreviewEvent;
+import com.google.gwt.user.client.Event.NativePreviewHandler;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.BrowserInfo;
+import com.vaadin.client.ComponentConnector;
import com.vaadin.client.ConnectorMap;
import com.vaadin.client.Focusable;
import com.vaadin.client.LayoutManager;
import com.vaadin.client.Util;
import com.vaadin.client.debug.internal.VDebugWindow;
import com.vaadin.client.ui.ShortcutActionHandler.ShortcutActionHandlerOwner;
+import com.vaadin.client.ui.aria.AriaHelper;
import com.vaadin.client.ui.window.WindowMoveEvent;
import com.vaadin.client.ui.window.WindowMoveHandler;
+import com.vaadin.shared.Connector;
import com.vaadin.shared.EventId;
import com.vaadin.shared.ui.window.WindowMode;
+import com.vaadin.shared.ui.window.WindowRole;
/**
* "Sub window" component.
*
* @author Vaadin Ltd
*/
-public class VWindow extends VOverlay implements ShortcutActionHandlerOwner,
- ScrollHandler, KeyDownHandler, FocusHandler, BlurHandler, Focusable {
+public class VWindow extends VWindowOverlay implements
+ ShortcutActionHandlerOwner, ScrollHandler, KeyDownHandler,
+ KeyUpHandler, FocusHandler, BlurHandler, Focusable {
private static ArrayList<VWindow> windowOrder = new ArrayList<VWindow>();
@@ -145,6 +160,22 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner,
private boolean closable = true;
+ private Connector[] assistiveConnectors = new Connector[0];
+ private String assistivePrefix;
+ private String assistivePostfix;
+
+ private Element topTabStop;
+ private Element bottomTabStop;
+
+ private NativePreviewHandler topEventBlocker;
+ private NativePreviewHandler bottomEventBlocker;
+
+ private HandlerRegistration topBlockerRegistration;
+ private HandlerRegistration bottomBlockerRegistration;
+
+ // Prevents leaving the window with the Tab key when true
+ private boolean doTabStop;
+
private boolean hasFocus;
/**
@@ -181,13 +212,74 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner,
// Different style of shadow for windows
setShadowStyle("window");
+ Roles.getDialogRole().set(getElement());
+ Roles.getDialogRole().setAriaRelevantProperty(getElement(),
+ RelevantValue.ADDITIONS);
+
constructDOM();
contentPanel.addScrollHandler(this);
contentPanel.addKeyDownHandler(this);
+ contentPanel.addKeyUpHandler(this);
contentPanel.addFocusHandler(this);
contentPanel.addBlurHandler(this);
}
+ @Override
+ protected void onAttach() {
+ super.onAttach();
+
+ /*
+ * Stores the element that has focus in the application UI when the
+ * window is opened, so it can be restored when the window closes.
+ *
+ * This is currently implemented for the case when one non-modal window
+ * can be open at the same time, and the focus is not changed while the
+ * window is open.
+ */
+ getApplicationConnection().getUIConnector().getWidget().storeFocus();
+
+ /*
+ * When this window gets reattached, set the tabstop to the previous
+ * state.
+ */
+ setTabStopEnabled(doTabStop);
+ }
+
+ @Override
+ protected void onDetach() {
+ super.onDetach();
+
+ /*
+ * Restores the previously stored focused element.
+ *
+ * When the focus was changed outside the window while the window was
+ * open, the originally stored element is restored.
+ */
+ getApplicationConnection().getUIConnector().getWidget()
+ .focusStoredElement();
+
+ removeTabBlockHandlers();
+ }
+
+ private void addTabBlockHandlers() {
+ if (topBlockerRegistration == null) {
+ topBlockerRegistration = Event
+ .addNativePreviewHandler(topEventBlocker);
+ bottomBlockerRegistration = Event
+ .addNativePreviewHandler(bottomEventBlocker);
+ }
+ }
+
+ private void removeTabBlockHandlers() {
+ if (topBlockerRegistration != null) {
+ topBlockerRegistration.removeHandler();
+ topBlockerRegistration = null;
+
+ bottomBlockerRegistration.removeHandler();
+ bottomBlockerRegistration = null;
+ }
+ }
+
public void bringToFront() {
int curIndex = windowOrder.indexOf(this);
if (curIndex + 1 < windowOrder.size()) {
@@ -240,17 +332,20 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner,
}
}
- protected Element getModalityCurtain() {
+ protected com.google.gwt.user.client.Element getModalityCurtain() {
if (modalityCurtain == null) {
modalityCurtain = DOM.createDiv();
modalityCurtain.setClassName(CLASSNAME + "-modalitycurtain");
}
- return modalityCurtain;
+ return DOM.asOld(modalityCurtain);
}
protected void constructDOM() {
setStyleName(CLASSNAME);
+ topTabStop = DOM.createDiv();
+ DOM.setElementAttribute(topTabStop, "tabindex", "0");
+
header = DOM.createDiv();
DOM.setElementProperty(header, "className", CLASSNAME + "-outerheader");
headerText = DOM.createDiv();
@@ -265,18 +360,25 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner,
maximizeRestoreBox = DOM.createDiv();
DOM.setElementProperty(maximizeRestoreBox, "className", CLASSNAME
+ "-maximizebox");
+ DOM.setElementAttribute(maximizeRestoreBox, "tabindex", "0");
DOM.setElementProperty(closeBox, "className", CLASSNAME + "-closebox");
+ DOM.setElementAttribute(closeBox, "tabindex", "0");
DOM.appendChild(footer, resizeBox);
+ bottomTabStop = DOM.createDiv();
+ DOM.setElementAttribute(bottomTabStop, "tabindex", "0");
+
wrapper = DOM.createDiv();
DOM.setElementProperty(wrapper, "className", CLASSNAME + "-wrap");
+ DOM.appendChild(wrapper, topTabStop);
DOM.appendChild(wrapper, header);
DOM.appendChild(wrapper, maximizeRestoreBox);
DOM.appendChild(wrapper, closeBox);
DOM.appendChild(header, headerText);
DOM.appendChild(wrapper, contents);
DOM.appendChild(wrapper, footer);
+ DOM.appendChild(wrapper, bottomTabStop);
DOM.appendChild(super.getContainerElement(), wrapper);
sinkEvents(Event.ONDBLCLICK | Event.MOUSEEVENTS | Event.TOUCHEVENTS
@@ -284,6 +386,96 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner,
setWidget(contentPanel);
+ // Make the closebox accessible for assistive devices
+ Roles.getButtonRole().set(closeBox);
+ Roles.getButtonRole().setAriaLabelProperty(closeBox, "close button");
+
+ // Make the maximizebox accessible for assistive devices
+ Roles.getButtonRole().set(maximizeRestoreBox);
+ Roles.getButtonRole().setAriaLabelProperty(maximizeRestoreBox,
+ "maximize button");
+
+ // Provide the title to assistive devices
+ AriaHelper.ensureHasId(headerText);
+ Roles.getDialogRole().setAriaLabelledbyProperty(getElement(),
+ Id.of(headerText));
+
+ // Handlers to Prevent tab to leave the window
+ topEventBlocker = new NativePreviewHandler() {
+ @Override
+ public void onPreviewNativeEvent(NativePreviewEvent event) {
+ NativeEvent nativeEvent = event.getNativeEvent();
+ if (nativeEvent.getEventTarget().cast() == topTabStop
+ && nativeEvent.getKeyCode() == KeyCodes.KEY_TAB
+ && nativeEvent.getShiftKey()) {
+ nativeEvent.preventDefault();
+ }
+ }
+ };
+
+ bottomEventBlocker = new NativePreviewHandler() {
+ @Override
+ public void onPreviewNativeEvent(NativePreviewEvent event) {
+ NativeEvent nativeEvent = event.getNativeEvent();
+ if (nativeEvent.getEventTarget().cast() == bottomTabStop
+ && nativeEvent.getKeyCode() == KeyCodes.KEY_TAB
+ && !nativeEvent.getShiftKey()) {
+ nativeEvent.preventDefault();
+ }
+ }
+ };
+ }
+
+ /**
+ * Sets the message that is provided to users of assistive devices when the
+ * user reaches the top of the window when leaving a window with the tab key
+ * is prevented.
+ * <p>
+ * This message is not visible on the screen.
+ *
+ * @param topMessage
+ * String provided when the user navigates with Shift-Tab keys to
+ * the top of the window
+ */
+ public void setTabStopTopAssistiveText(String topMessage) {
+ Roles.getNoteRole().setAriaLabelProperty(topTabStop, topMessage);
+ }
+
+ /**
+ * Sets the message that is provided to users of assistive devices when the
+ * user reaches the bottom of the window when leaving a window with the tab
+ * key is prevented.
+ * <p>
+ * This message is not visible on the screen.
+ *
+ * @param bottomMessage
+ * String provided when the user navigates with the Tab key to
+ * the bottom of the window
+ */
+ public void setTabStopBottomAssistiveText(String bottomMessage) {
+ Roles.getNoteRole().setAriaLabelProperty(bottomTabStop, bottomMessage);
+ }
+
+ /**
+ * Gets the message that is provided to users of assistive devices when the
+ * user reaches the top of the window when leaving a window with the tab key
+ * is prevented.
+ *
+ * @return the top message
+ */
+ public String getTabStopTopAssistiveText() {
+ return Roles.getNoteRole().getAriaLabelProperty(topTabStop);
+ }
+
+ /**
+ * Gets the message that is provided to users of assistive devices when the
+ * user reaches the bottom of the window when leaving a window with the tab
+ * key is prevented.
+ *
+ * @return the bottom message
+ */
+ public String getTabStopBottomAssistiveText() {
+ return Roles.getNoteRole().getAriaLabelProperty(bottomTabStop);
}
/**
@@ -517,6 +709,7 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner,
if (isAttached()) {
showModalityCurtain();
}
+ addTabBlockHandlers();
deferOrdering();
} else {
if (modalityCurtain != null) {
@@ -525,6 +718,9 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner,
}
modalityCurtain = null;
}
+ if (!doTabStop) {
+ removeTabBlockHandlers();
+ }
}
}
@@ -682,18 +878,71 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner,
if (icon != null) {
icon = client.translateVaadinUri(icon);
html = "<img src=\"" + Util.escapeAttribute(icon)
- + "\" class=\"v-icon\" />" + html;
+ + "\" class=\"v-icon\" alt=\"\" />" + html;
}
+
+ // Provide information to assistive device users that a sub window was
+ // opened
+ String prefix = "<span class='"
+ + AriaHelper.ASSISTIVE_DEVICE_ONLY_STYLE + "'>"
+ + assistivePrefix + "</span>";
+ String postfix = "<span class='"
+ + AriaHelper.ASSISTIVE_DEVICE_ONLY_STYLE + "'>"
+ + assistivePostfix + "</span>";
+
+ html = prefix + html + postfix;
DOM.setInnerHTML(headerText, html);
}
+ /**
+ * Setter for the text for assistive devices the window caption is prefixed
+ * with.
+ *
+ * @param assistivePrefix
+ * the assistivePrefix to set
+ */
+ public void setAssistivePrefix(String assistivePrefix) {
+ this.assistivePrefix = assistivePrefix;
+ }
+
+ /**
+ * Getter for the text for assistive devices the window caption is prefixed
+ * with.
+ *
+ * @return the assistivePrefix
+ */
+ public String getAssistivePrefix() {
+ return assistivePrefix;
+ }
+
+ /**
+ * Setter for the text for assistive devices the window caption is postfixed
+ * with.
+ *
+ * @param assistivePostfix
+ * the assistivePostfix to set
+ */
+ public void setAssistivePostfix(String assistivePostfix) {
+ this.assistivePostfix = assistivePostfix;
+ }
+
+ /**
+ * Getter for the text for assistive devices the window caption is postfixed
+ * with.
+ *
+ * @return the assistivePostfix
+ */
+ public String getAssistivePostfix() {
+ return assistivePostfix;
+ }
+
@Override
- protected Element getContainerElement() {
+ protected com.google.gwt.user.client.Element getContainerElement() {
// in GWT 1.5 this method is used in PopupPanel constructor
if (contents == null) {
return super.getContainerElement();
}
- return contents;
+ return DOM.asOld(contents);
}
private Event headerDragPending;
@@ -742,14 +991,14 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner,
}
bubble = false;
}
+ if (type == Event.ONCLICK) {
+ activateOnClick();
+ }
} else if (dragging || !contents.isOrHasChild(target)) {
onDragEvent(event);
bubble = false;
} else if (type == Event.ONCLICK) {
- // clicked inside window, ensure to be on top
- if (!isActive()) {
- bringToFront();
- }
+ activateOnClick();
}
/*
@@ -771,6 +1020,13 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner,
}
}
+ private void activateOnClick() {
+ // clicked inside window or inside header, ensure to be on top
+ if (!isActive()) {
+ bringToFront();
+ }
+ }
+
private void onCloseClick() {
client.updateVariable(id, "close", true, true);
}
@@ -1068,6 +1324,13 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner,
}
@Override
+ public void onKeyUp(KeyUpEvent event) {
+ if (isClosable() && event.getNativeKeyCode() == KeyCodes.KEY_ESCAPE) {
+ onCloseClick();
+ }
+ }
+
+ @Override
public void onBlur(BlurEvent event) {
hasFocus = false;
@@ -1108,9 +1371,104 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner,
}
/**
+ * Allows to specify which connectors contain the description for the
+ * window. Text contained in the widgets of the connectors will be read by
+ * assistive devices when it is opened.
+ * <p>
+ * When the provided array is empty, an existing description is removed.
+ *
+ * @param connectors
+ * with the connectors of the widgets to use as description
+ */
+ public void setAssistiveDescription(Connector[] connectors) {
+ if (connectors != null) {
+ assistiveConnectors = connectors;
+
+ if (connectors.length == 0) {
+ Roles.getDialogRole().removeAriaDescribedbyProperty(
+ getElement());
+ } else {
+ Id[] ids = new Id[connectors.length];
+ for (int index = 0; index < connectors.length; index++) {
+ if (connectors[index] == null) {
+ throw new IllegalArgumentException(
+ "All values in parameter description need to be non-null");
+ }
+
+ Element element = ((ComponentConnector) connectors[index])
+ .getWidget().getElement();
+ AriaHelper.ensureHasId(element);
+ ids[index] = Id.of(element);
+ }
+
+ Roles.getDialogRole().setAriaDescribedbyProperty(getElement(),
+ ids);
+ }
+ } else {
+ throw new IllegalArgumentException(
+ "Parameter description must be non-null");
+ }
+ }
+
+ /**
+ * Gets the connectors that are used as assistive description. Text
+ * contained in these connectors will be read by assistive devices when the
+ * window is opened.
+ *
+ * @return list of previously set connectors
+ */
+ public List<Connector> getAssistiveDescription() {
+ return Collections.unmodifiableList(Arrays.asList(assistiveConnectors));
+ }
+
+ /**
+ * Sets the WAI-ARIA role the window.
+ *
+ * This role defines how an assistive device handles a window. Available
+ * roles are alertdialog and dialog (@see <a
+ * href="http://www.w3.org/TR/2011/CR-wai-aria-20110118/roles">Roles
+ * Model</a>).
+ *
+ * The default role is dialog.
+ *
+ * @param role
+ * WAI-ARIA role to set for the window
+ */
+ public void setWaiAriaRole(WindowRole role) {
+ if ("alertdialog".equals(role)) {
+ Roles.getAlertdialogRole().set(getElement());
+ } else {
+ Roles.getDialogRole().set(getElement());
+ }
+ }
+
+ /**
+ * Registers the handlers that prevent to leave the window using the
+ * Tab-key.
+ * <p>
+ * The value of the parameter doTabStop is stored and used for non-modal
+ * windows. For modal windows, the handlers are always registered, while
+ * preserving the stored value.
+ *
+ * @param doTabStop
+ * true to prevent leaving the window, false to allow leaving the
+ * window for non modal windows
+ */
+ public void setTabStopEnabled(boolean doTabStop) {
+ this.doTabStop = doTabStop;
+
+ if (doTabStop || vaadinModality) {
+ addTabBlockHandlers();
+ } else {
+ removeTabBlockHandlers();
+ }
+ }
+
+ /**
* Adds a Handler for when user moves the window.
*
* @since 7.1.9
+ *
* @return {@link HandlerRegistration} used to remove the handler
*/
public HandlerRegistration addMoveHandler(WindowMoveHandler handler) {
diff --git a/client/src/com/vaadin/client/ui/VWindowOverlay.java b/client/src/com/vaadin/client/ui/VWindowOverlay.java
new file mode 100644
index 0000000000..6558ab14fa
--- /dev/null
+++ b/client/src/com/vaadin/client/ui/VWindowOverlay.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.vaadin.client.ui;
+
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.ui.RootPanel;
+import com.vaadin.client.ApplicationConnection;
+
+public class VWindowOverlay extends VOverlay {
+ public VWindowOverlay() {
+ }
+
+ public VWindowOverlay(boolean autoHide, boolean modal, boolean showShadow) {
+ super(autoHide, modal, showShadow);
+ }
+
+ /**
+ * Gets the 'overlay container' element. Tries to find the current
+ * {@link ApplicationConnection} using {@link #getApplicationConnection()}.
+ *
+ * @return the overlay container element for the current
+ * {@link ApplicationConnection} or another element if the current
+ * {@link ApplicationConnection} cannot be determined.
+ */
+ @Override
+ public com.google.gwt.user.client.Element getOverlayContainer() {
+ ApplicationConnection ac = getApplicationConnection();
+ if (ac == null) {
+ return super.getOverlayContainer();
+ } else {
+ Element overlayContainer = getOverlayContainer(ac);
+ return DOM.asOld(overlayContainer);
+ }
+ }
+
+ /**
+ * Gets the 'overlay container' element pertaining to the given
+ * {@link ApplicationConnection}. Each overlay should be created in a
+ * overlay container element, so that the correct theme and styles can be
+ * applied.
+ *
+ * @param ac
+ * A reference to {@link ApplicationConnection}
+ * @return The overlay container
+ */
+ public static com.google.gwt.user.client.Element getOverlayContainer(
+ ApplicationConnection ac) {
+ String id = ac.getConfiguration().getRootPanelId();
+ id = id += "-window-overlays";
+ Element container = DOM.getElementById(id);
+ if (container == null) {
+ container = DOM.createDiv();
+ container.setId(id);
+ String styles = ac.getUIConnector().getWidget().getParent()
+ .getStyleName();
+ container.addClassName(styles);
+ container.addClassName(CLASSNAME_CONTAINER);
+ RootPanel.get().getElement().appendChild(container);
+ }
+
+ return DOM.asOld(container);
+ }
+}
diff --git a/client/src/com/vaadin/client/ui/absolutelayout/AbsoluteLayoutConnector.java b/client/src/com/vaadin/client/ui/absolutelayout/AbsoluteLayoutConnector.java
index da79639dcd..6a6a1429f8 100644
--- a/client/src/com/vaadin/client/ui/absolutelayout/AbsoluteLayoutConnector.java
+++ b/client/src/com/vaadin/client/ui/absolutelayout/AbsoluteLayoutConnector.java
@@ -17,7 +17,8 @@ package com.vaadin.client.ui.absolutelayout;
import java.util.List;
-import com.google.gwt.user.client.Element;
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.user.client.DOM;
import com.vaadin.client.ComponentConnector;
import com.vaadin.client.ConnectorHierarchyChangeEvent;
import com.vaadin.client.DirectionalManagedLayout;
@@ -46,14 +47,15 @@ public class AbsoluteLayoutConnector extends
this) {
@Override
- protected ComponentConnector getChildComponent(Element element) {
+ protected ComponentConnector getChildComponent(
+ com.google.gwt.user.client.Element element) {
return getConnectorForElement(element);
}
@Override
protected LayoutClickRpc getLayoutClickRPC() {
return getRpcProxy(AbsoluteLayoutServerRpc.class);
- };
+ }
};
private StateChangeHandler childStateChangeHandler = new StateChangeHandler() {
@@ -91,12 +93,32 @@ public class AbsoluteLayoutConnector extends
* this layout
* @return The Paintable which the element is a part of. Null if the element
* belongs to the layout and not to a child.
+ * @deprecated As of 7.2, call or override
+ * {@link #getConnectorForElement(Element)} instead
*/
- protected ComponentConnector getConnectorForElement(Element element) {
+ @Deprecated
+ protected ComponentConnector getConnectorForElement(
+ com.google.gwt.user.client.Element element) {
return Util.getConnectorForElement(getConnection(), getWidget(),
element);
}
+ /**
+ * Returns the deepest nested child component which contains "element". The
+ * child component is also returned if "element" is part of its caption.
+ *
+ * @param element
+ * An element that is a nested sub element of the root element in
+ * this layout
+ * @return The Paintable which the element is a part of. Null if the element
+ * belongs to the layout and not to a child.
+ *
+ * @since 7.2
+ */
+ protected ComponentConnector getConnectorForElement(Element element) {
+ return getConnectorForElement(DOM.asOld(element));
+ }
+
/*
* (non-Javadoc)
*
@@ -163,7 +185,7 @@ public class AbsoluteLayoutConnector extends
private void setChildWidgetPosition(ComponentConnector child) {
getWidget().setWidgetPosition(child.getWidget(),
getState().connectorToCssPosition.get(child.getConnectorId()));
- };
+ }
/*
* (non-Javadoc)
diff --git a/client/src/com/vaadin/client/ui/accordion/AccordionConnector.java b/client/src/com/vaadin/client/ui/accordion/AccordionConnector.java
index 99fbd07f9b..ce843dc22f 100644
--- a/client/src/com/vaadin/client/ui/accordion/AccordionConnector.java
+++ b/client/src/com/vaadin/client/ui/accordion/AccordionConnector.java
@@ -15,19 +15,17 @@
*/
package com.vaadin.client.ui.accordion;
-import java.util.Iterator;
-
-import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.ComponentConnector;
import com.vaadin.client.ConnectorHierarchyChangeEvent;
-import com.vaadin.client.UIDL;
import com.vaadin.client.Util;
+import com.vaadin.client.communication.StateChangeEvent;
import com.vaadin.client.ui.SimpleManagedLayout;
import com.vaadin.client.ui.VAccordion;
import com.vaadin.client.ui.VAccordion.StackItem;
import com.vaadin.client.ui.layout.MayScrollChildren;
import com.vaadin.client.ui.tabsheet.TabsheetBaseConnector;
import com.vaadin.shared.ui.Connect;
+import com.vaadin.shared.ui.accordion.AccordionState;
import com.vaadin.ui.Accordion;
@Connect(Accordion.class)
@@ -35,35 +33,32 @@ public class AccordionConnector extends TabsheetBaseConnector implements
SimpleManagedLayout, MayScrollChildren {
@Override
- public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
- getWidget().selectedUIDLItemIndex = -1;
- super.updateFromUIDL(uidl, client);
+ protected void init() {
+ super.init();
+ getWidget().setConnector(this);
+ }
+
+ @Override
+ public void onStateChanged(StateChangeEvent stateChangeEvent) {
+ super.onStateChanged(stateChangeEvent);
+
/*
* Render content after all tabs have been created and we know how large
* the content area is
*/
- if (getWidget().selectedUIDLItemIndex >= 0) {
+ if (getWidget().selectedItemIndex >= 0) {
StackItem selectedItem = getWidget().getStackItem(
- getWidget().selectedUIDLItemIndex);
- UIDL selectedTabUIDL = getWidget().lazyUpdateMap
- .remove(selectedItem);
- getWidget().open(getWidget().selectedUIDLItemIndex);
+ getWidget().selectedItemIndex);
- selectedItem.setContent(selectedTabUIDL);
- } else if (isRealUpdate(uidl) && getWidget().getOpenStackItem() != null) {
- getWidget().close(getWidget().getOpenStackItem());
- }
+ getWidget().open(getWidget().selectedItemIndex);
- // finally render possible hidden tabs
- if (getWidget().lazyUpdateMap.size() > 0) {
- for (Iterator iterator = getWidget().lazyUpdateMap.keySet()
- .iterator(); iterator.hasNext();) {
- StackItem item = (StackItem) iterator.next();
- item.setContent(getWidget().lazyUpdateMap.get(item));
+ ComponentConnector contentConnector = getChildComponents().get(0);
+ if (contentConnector != null) {
+ selectedItem.setContent(contentConnector.getWidget());
}
- getWidget().lazyUpdateMap.clear();
+ } else if (getWidget().getOpenStackItem() != null) {
+ getWidget().close(getWidget().getOpenStackItem());
}
-
}
@Override
@@ -123,14 +118,24 @@ public class AccordionConnector extends TabsheetBaseConnector implements
openTab.setHeight(spaceForOpenItem);
} else {
openTab.setHeightFromWidget();
-
}
-
}
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.client.ConnectorHierarchyChangeEvent.
+ * ConnectorHierarchyChangeHandler
+ * #onConnectorHierarchyChange(com.vaadin.client
+ * .ConnectorHierarchyChangeEvent)
+ */
@Override
public void onConnectorHierarchyChange(
ConnectorHierarchyChangeEvent connectorHierarchyChangeEvent) {
- // TODO Move code from updateFromUIDL to this method
+ }
+
+ @Override
+ public AccordionState getState() {
+ return (AccordionState) super.getState();
}
}
diff --git a/client/src/com/vaadin/client/ui/aria/AriaHelper.java b/client/src/com/vaadin/client/ui/aria/AriaHelper.java
index 0ff58cf510..b1f51b85e9 100644
--- a/client/src/com/vaadin/client/ui/aria/AriaHelper.java
+++ b/client/src/com/vaadin/client/ui/aria/AriaHelper.java
@@ -19,8 +19,8 @@ package com.vaadin.client.ui.aria;
import com.google.gwt.aria.client.Id;
import com.google.gwt.aria.client.InvalidValue;
import com.google.gwt.aria.client.Roles;
+import com.google.gwt.dom.client.Element;
import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.ui.Widget;
/**
@@ -47,7 +47,8 @@ public class AriaHelper {
((HandlesAriaCaption) widget).bindAriaCaption(null);
} else {
ensureHasId(captionElement);
- ((HandlesAriaCaption) widget).bindAriaCaption(captionElement);
+ ((HandlesAriaCaption) widget).bindAriaCaption(DOM
+ .asOld(captionElement));
}
} else if (captionElement != null) {
// Handle the default case
diff --git a/client/src/com/vaadin/client/ui/aria/HandlesAriaCaption.java b/client/src/com/vaadin/client/ui/aria/HandlesAriaCaption.java
index 50f83fdede..e754f2d095 100644
--- a/client/src/com/vaadin/client/ui/aria/HandlesAriaCaption.java
+++ b/client/src/com/vaadin/client/ui/aria/HandlesAriaCaption.java
@@ -16,8 +16,6 @@
package com.vaadin.client.ui.aria;
-import com.google.gwt.user.client.Element;
-
/**
* Some Widgets need to handle the caption handling for WAI-ARIA themselfs, as
* for example the required ids need to be set in a specific way. In such a
@@ -35,5 +33,5 @@ public interface HandlesAriaCaption {
* @param captionElement
* Element of the caption
*/
- void bindAriaCaption(Element captionElement);
+ void bindAriaCaption(com.google.gwt.user.client.Element captionElement);
}
diff --git a/client/src/com/vaadin/client/ui/button/ButtonConnector.java b/client/src/com/vaadin/client/ui/button/ButtonConnector.java
index a6630f28b9..32a457c1f1 100644
--- a/client/src/com/vaadin/client/ui/button/ButtonConnector.java
+++ b/client/src/com/vaadin/client/ui/button/ButtonConnector.java
@@ -26,8 +26,8 @@ import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.DOM;
import com.vaadin.client.EventHelper;
import com.vaadin.client.MouseEventDetailsBuilder;
+import com.vaadin.client.annotations.OnStateChange;
import com.vaadin.client.communication.StateChangeEvent;
-import com.vaadin.client.communication.StateChangeEvent.StateChangeHandler;
import com.vaadin.client.ui.AbstractComponentConnector;
import com.vaadin.client.ui.Icon;
import com.vaadin.client.ui.VButton;
@@ -56,48 +56,38 @@ public class ButtonConnector extends AbstractComponentConnector implements
super.init();
getWidget().addClickHandler(this);
getWidget().client = getConnection();
- addStateChangeHandler("errorMessage", new StateChangeHandler() {
- @Override
- public void onStateChanged(StateChangeEvent stateChangeEvent) {
- if (null != getState().errorMessage) {
- if (getWidget().errorIndicatorElement == null) {
- getWidget().errorIndicatorElement = DOM.createSpan();
- getWidget().errorIndicatorElement
- .setClassName("v-errorindicator");
- }
- getWidget().wrapper.insertBefore(
- getWidget().errorIndicatorElement,
- getWidget().captionElement);
-
- } else if (getWidget().errorIndicatorElement != null) {
- getWidget().wrapper
- .removeChild(getWidget().errorIndicatorElement);
- getWidget().errorIndicatorElement = null;
- }
- }
- });
-
- addStateChangeHandler("resources", new StateChangeHandler() {
- @Override
- public void onStateChanged(StateChangeEvent stateChangeEvent) {
- if (getIcon() != null) {
- if (getWidget().icon == null) {
- getWidget().icon = new Icon(getConnection());
- getWidget().wrapper.insertBefore(
- getWidget().icon.getElement(),
- getWidget().captionElement);
- }
- getWidget().icon.setUri(getIcon());
- getWidget().icon.setAlternateText(getState().iconAltText);
- } else {
- if (getWidget().icon != null) {
- getWidget().wrapper.removeChild(getWidget().icon
- .getElement());
- getWidget().icon = null;
- }
- }
+ }
+
+ @OnStateChange("errorMessage")
+ void setErrorMessage() {
+ if (null != getState().errorMessage) {
+ if (getWidget().errorIndicatorElement == null) {
+ getWidget().errorIndicatorElement = DOM.createSpan();
+ getWidget().errorIndicatorElement
+ .setClassName("v-errorindicator");
}
- });
+ getWidget().wrapper.insertBefore(getWidget().errorIndicatorElement,
+ getWidget().captionElement);
+
+ } else if (getWidget().errorIndicatorElement != null) {
+ getWidget().wrapper.removeChild(getWidget().errorIndicatorElement);
+ getWidget().errorIndicatorElement = null;
+ }
+ }
+
+ @OnStateChange("resources")
+ void onResourceChange() {
+ if (getWidget().icon != null) {
+ getWidget().wrapper.removeChild(getWidget().icon.getElement());
+ getWidget().icon = null;
+ }
+ Icon icon = getIcon();
+ if (icon != null) {
+ getWidget().icon = icon;
+ icon.setAlternateText(getState().iconAltText);
+ getWidget().wrapper.insertBefore(icon.getElement(),
+ getWidget().captionElement);
+ }
}
@Override
@@ -107,22 +97,27 @@ public class ButtonConnector extends AbstractComponentConnector implements
focusHandlerRegistration);
blurHandlerRegistration = EventHelper.updateBlurHandler(this,
blurHandlerRegistration);
+ }
- if (stateChangeEvent.hasPropertyChanged("caption")
- || stateChangeEvent.hasPropertyChanged("htmlContentAllowed")) {
- // Set text
- if (getState().htmlContentAllowed) {
- getWidget().setHtml(getState().caption);
- } else {
- getWidget().setText(getState().caption);
- }
+ @OnStateChange({ "caption", "htmlContentAllowed" })
+ void setCaption() {
+ String caption = getState().caption;
+ if (getState().htmlContentAllowed) {
+ getWidget().setHtml(caption);
+ } else {
+ getWidget().setText(caption);
}
+ }
- if (getWidget().icon != null
- && stateChangeEvent.hasPropertyChanged("iconAltText")) {
+ @OnStateChange("iconAltText")
+ void setIconAltText() {
+ if (getWidget().icon != null) {
getWidget().icon.setAlternateText(getState().iconAltText);
}
+ }
+ @OnStateChange("clickShortcutKeyCode")
+ void setClickShortcut() {
getWidget().clickShortcut = getState().clickShortcutKeyCode;
}
diff --git a/client/src/com/vaadin/client/ui/calendar/CalendarConnector.java b/client/src/com/vaadin/client/ui/calendar/CalendarConnector.java
index 49cd2386ac..89f923d483 100644
--- a/client/src/com/vaadin/client/ui/calendar/CalendarConnector.java
+++ b/client/src/com/vaadin/client/ui/calendar/CalendarConnector.java
@@ -24,11 +24,11 @@ import java.util.Iterator;
import java.util.List;
import com.google.gwt.core.shared.GWT;
+import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.event.dom.client.ContextMenuEvent;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.ApplicationConnection;
@@ -420,7 +420,7 @@ public class CalendarConnector extends AbstractComponentConnector implements
@Override
public TooltipInfo getTooltipInfo(com.google.gwt.dom.client.Element element) {
TooltipInfo tooltipInfo = null;
- Widget w = Util.findWidget((Element) element, null);
+ Widget w = Util.findWidget(element, null);
if (w instanceof HasTooltipKey) {
tooltipInfo = GWT.create(TooltipInfo.class);
String title = tooltips.get(((HasTooltipKey) w).getTooltipKey());
diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/DateCell.java b/client/src/com/vaadin/client/ui/calendar/schedule/DateCell.java
index 516447153e..c3fd2b54cf 100644
--- a/client/src/com/vaadin/client/ui/calendar/schedule/DateCell.java
+++ b/client/src/com/vaadin/client/ui/calendar/schedule/DateCell.java
@@ -342,8 +342,7 @@ public class DateCell extends FocusableComplexPanel implements
}
public int getSlotBorder() {
- return Util
- .measureVerticalBorder((com.google.gwt.user.client.Element) slotElements[0]);
+ return Util.measureVerticalBorder(slotElements[0]);
}
private void drawDayEvents(List<DateCellGroup> groups) {
@@ -504,7 +503,7 @@ public class DateCell extends FocusableComplexPanel implements
updatePositionFor(dayEvent, targetDay, calendarEvent);
}
- add(dayEvent, (com.google.gwt.user.client.Element) main);
+ add(dayEvent, main);
}
// date methods are not deprecated in GWT
@@ -561,8 +560,7 @@ public class DateCell extends FocusableComplexPanel implements
}
index++;
}
- this.insert(dayEvent, (com.google.gwt.user.client.Element) main, index,
- true);
+ this.insert(dayEvent, main, index, true);
}
public void removeEvent(DateCellDayEvent dayEvent) {
@@ -784,11 +782,28 @@ public class DateCell extends FocusableComplexPanel implements
return today != null;
}
+ /**
+ * @deprecated As of 7.2, call or override
+ * {@link #addEmphasisStyle(Element)} instead
+ */
+ @Deprecated
public void addEmphasisStyle(com.google.gwt.user.client.Element elementOver) {
String originalStylename = getStyleName(elementOver);
setStyleName(elementOver, originalStylename + DRAGEMPHASISSTYLE);
}
+ /**
+ * @since 7.2
+ */
+ public void addEmphasisStyle(Element elementOver) {
+ addEmphasisStyle(DOM.asOld(elementOver));
+ }
+
+ /**
+ * @deprecated As of 7.2, call or override
+ * {@link #removeEmphasisStyle(Element)} instead
+ */
+ @Deprecated
public void removeEmphasisStyle(
com.google.gwt.user.client.Element elementOver) {
String originalStylename = getStyleName(elementOver);
@@ -798,6 +813,13 @@ public class DateCell extends FocusableComplexPanel implements
- DRAGEMPHASISSTYLE.length()));
}
+ /**
+ * @since 7.2
+ */
+ public void removeEmphasisStyle(Element elementOver) {
+ removeEmphasisStyle(DOM.asOld(elementOver));
+ }
+
@Override
public void onContextMenu(ContextMenuEvent event) {
if (weekgrid.getCalendar().getMouseEventListener() != null) {
diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/DateCellDayEvent.java b/client/src/com/vaadin/client/ui/calendar/schedule/DateCellDayEvent.java
index 39de122694..ae86833952 100644
--- a/client/src/com/vaadin/client/ui/calendar/schedule/DateCellDayEvent.java
+++ b/client/src/com/vaadin/client/ui/calendar/schedule/DateCellDayEvent.java
@@ -42,7 +42,6 @@ import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.vaadin.client.Util;
-import com.vaadin.client.ui.VCalendar;
import com.vaadin.shared.ui.calendar.DateConstants;
/**
@@ -71,8 +70,8 @@ public class DateCellDayEvent extends FocusableHTML implements
private int startXrelative;
private boolean disabled;
private final WeekGrid weekGrid;
- private com.google.gwt.user.client.Element topResizeBar;
- private com.google.gwt.user.client.Element bottomResizeBar;
+ private Element topResizeBar;
+ private Element bottomResizeBar;
private Element clickTarget;
private final Integer eventIndex;
private int slotHeight;
@@ -105,7 +104,6 @@ public class DateCellDayEvent extends FocusableHTML implements
eventContent.addClassName("v-calendar-event-content");
getElement().appendChild(eventContent);
- VCalendar calendar = weekGrid.getCalendar();
if (weekGrid.getCalendar().isEventResizeAllowed()) {
topResizeBar = DOM.createDiv();
bottomResizeBar = DOM.createDiv();
@@ -189,9 +187,11 @@ public class DateCellDayEvent extends FocusableHTML implements
String escapedCaption = Util.escapeHTML(calendarEvent.getCaption());
String timeAsText = calendarEvent.getTimeAsText();
if (bigMode) {
- innerHtml = "<span>" + timeAsText + "</span><br />" + escapedCaption;
+ innerHtml = "<span>" + timeAsText + "</span><br />"
+ + escapedCaption;
} else {
- innerHtml = "<span>" + timeAsText + "<span>:</span></span> " + escapedCaption;
+ innerHtml = "<span>" + timeAsText + "<span>:</span></span> "
+ + escapedCaption;
}
caption.setInnerHTML(innerHtml);
eventContent.setInnerHTML("");
diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/DayToolbar.java b/client/src/com/vaadin/client/ui/calendar/schedule/DayToolbar.java
index 6233e8111e..58b5fafa7f 100644
--- a/client/src/com/vaadin/client/ui/calendar/schedule/DayToolbar.java
+++ b/client/src/com/vaadin/client/ui/calendar/schedule/DayToolbar.java
@@ -72,8 +72,6 @@ public class DayToolbar extends HorizontalPanel implements ClickHandler {
setCellWidth(nextLabel, MARGINRIGHT + "px");
setCellHorizontalAlignment(nextLabel, ALIGN_RIGHT);
int cellw = width / (count - 2);
- int remain = width % (count - 2);
- int cellw2 = cellw + 1;
if (cellw > 0) {
int[] cellWidths = VCalendar
.distributeSize(width, count - 2, 0);
diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/MonthGrid.java b/client/src/com/vaadin/client/ui/calendar/schedule/MonthGrid.java
index df9bc42d2a..3b1c774793 100644
--- a/client/src/com/vaadin/client/ui/calendar/schedule/MonthGrid.java
+++ b/client/src/com/vaadin/client/ui/calendar/schedule/MonthGrid.java
@@ -35,7 +35,6 @@ public class MonthGrid extends FocusableGrid implements KeyDownHandler {
private SimpleDayCell selectionEnd;
private final VCalendar calendar;
private boolean rangeSelectDisabled;
- private boolean disabled;
private boolean enabled = true;
private final HandlerRegistration keyDownHandler;
diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/SimpleDayCell.java b/client/src/com/vaadin/client/ui/calendar/schedule/SimpleDayCell.java
index cf8006ef66..00fc1ef3ea 100644
--- a/client/src/com/vaadin/client/ui/calendar/schedule/SimpleDayCell.java
+++ b/client/src/com/vaadin/client/ui/calendar/schedule/SimpleDayCell.java
@@ -83,7 +83,6 @@ public class SimpleDayCell extends FocusableFlowPanel implements
private Widget clickedWidget;
private HandlerRegistration bottomSpacerMouseDownHandler;
private boolean scrollable = false;
- private boolean eventCanceled;
private MonthGrid monthGrid;
private HandlerRegistration keyDownHandler;
diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/dd/CalendarMonthDropHandler.java b/client/src/com/vaadin/client/ui/calendar/schedule/dd/CalendarMonthDropHandler.java
index fd0be4881e..7c0c541ee3 100644
--- a/client/src/com/vaadin/client/ui/calendar/schedule/dd/CalendarMonthDropHandler.java
+++ b/client/src/com/vaadin/client/ui/calendar/schedule/dd/CalendarMonthDropHandler.java
@@ -15,8 +15,8 @@
*/
package com.vaadin.client.ui.calendar.schedule.dd;
+import com.google.gwt.dom.client.Element;
import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.Element;
import com.vaadin.client.Util;
import com.vaadin.client.ui.calendar.CalendarConnector;
import com.vaadin.client.ui.calendar.schedule.SimpleDayCell;
@@ -101,10 +101,9 @@ public class CalendarMonthDropHandler extends CalendarDropHandler {
* The element to check
* @return
*/
- private boolean isLocationValid(
- com.google.gwt.user.client.Element elementOver) {
- com.google.gwt.user.client.Element monthGridElement = calendarConnector
- .getWidget().getMonthGrid().getElement();
+ private boolean isLocationValid(Element elementOver) {
+ Element monthGridElement = calendarConnector.getWidget().getMonthGrid()
+ .getElement();
// drops are not allowed in:
// - weekday header
diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/dd/CalendarWeekDropHandler.java b/client/src/com/vaadin/client/ui/calendar/schedule/dd/CalendarWeekDropHandler.java
index cede1827a2..d19dcfedc4 100644
--- a/client/src/com/vaadin/client/ui/calendar/schedule/dd/CalendarWeekDropHandler.java
+++ b/client/src/com/vaadin/client/ui/calendar/schedule/dd/CalendarWeekDropHandler.java
@@ -15,8 +15,8 @@
*/
package com.vaadin.client.ui.calendar.schedule.dd;
+import com.google.gwt.dom.client.Element;
import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.Element;
import com.vaadin.client.Util;
import com.vaadin.client.ui.calendar.CalendarConnector;
import com.vaadin.client.ui.calendar.schedule.DateCell;
@@ -34,7 +34,7 @@ import com.vaadin.client.ui.dd.VDragEvent;
*/
public class CalendarWeekDropHandler extends CalendarDropHandler {
- private com.google.gwt.user.client.Element currentTargetElement;
+ private Element currentTargetElement;
private DateCell currentTargetDay;
public CalendarWeekDropHandler(CalendarConnector connector) {
@@ -100,17 +100,16 @@ public class CalendarWeekDropHandler extends CalendarDropHandler {
* The element to check
* @return
*/
- private boolean isLocationValid(
- com.google.gwt.user.client.Element elementOver) {
- com.google.gwt.user.client.Element weekGridElement = calendarConnector
- .getWidget().getWeekGrid().getElement();
- com.google.gwt.user.client.Element timeBarElement = calendarConnector
- .getWidget().getWeekGrid().getTimeBar().getElement();
-
- com.google.gwt.user.client.Element todayBarElement = null;
+ private boolean isLocationValid(Element elementOver) {
+ Element weekGridElement = calendarConnector.getWidget().getWeekGrid()
+ .getElement();
+ Element timeBarElement = calendarConnector.getWidget().getWeekGrid()
+ .getTimeBar().getElement();
+
+ Element todayBarElement = null;
if (calendarConnector.getWidget().getWeekGrid().hasToday()) {
- todayBarElement = (Element) calendarConnector.getWidget()
- .getWeekGrid().getDateCellOfToday().getTodaybarElement();
+ todayBarElement = calendarConnector.getWidget().getWeekGrid()
+ .getDateCellOfToday().getTodaybarElement();
}
// drops are not allowed in:
diff --git a/client/src/com/vaadin/client/ui/checkbox/CheckBoxConnector.java b/client/src/com/vaadin/client/ui/checkbox/CheckBoxConnector.java
index 85e4e5ee8b..b40e96ff95 100644
--- a/client/src/com/vaadin/client/ui/checkbox/CheckBoxConnector.java
+++ b/client/src/com/vaadin/client/ui/checkbox/CheckBoxConnector.java
@@ -30,6 +30,7 @@ import com.vaadin.client.VTooltip;
import com.vaadin.client.communication.StateChangeEvent;
import com.vaadin.client.ui.AbstractFieldConnector;
import com.vaadin.client.ui.Icon;
+import com.vaadin.client.ui.ImageIcon;
import com.vaadin.client.ui.VCheckBox;
import com.vaadin.shared.MouseEventDetails;
import com.vaadin.shared.communication.FieldRpc.FocusAndBlurServerRpc;
@@ -96,21 +97,17 @@ public class CheckBoxConnector extends AbstractFieldConnector implements
getWidget().setEnabled(false);
}
- if (getIcon() != null) {
- if (getWidget().icon == null) {
- getWidget().icon = new Icon(getConnection());
- DOM.insertChild(getWidget().getElement(),
- getWidget().icon.getElement(), 1);
- getWidget().icon.sinkEvents(VTooltip.TOOLTIP_EVENTS);
- getWidget().icon.sinkEvents(Event.ONCLICK);
- }
- getWidget().icon.setUri(getIcon());
- } else if (getWidget().icon != null) {
- // detach icon
- DOM.removeChild(getWidget().getElement(),
- getWidget().icon.getElement());
+ if (getWidget().icon != null) {
+ getWidget().getElement().removeChild(getWidget().icon.getElement());
getWidget().icon = null;
}
+ Icon icon = getIcon();
+ if (icon != null) {
+ getWidget().icon = icon;
+ DOM.insertChild(getWidget().getElement(), icon.getElement(), 1);
+ icon.sinkEvents(VTooltip.TOOLTIP_EVENTS);
+ icon.sinkEvents(Event.ONCLICK);
+ }
// Set text
getWidget().setText(getState().caption);
diff --git a/client/src/com/vaadin/client/ui/colorpicker/VColorPickerGradient.java b/client/src/com/vaadin/client/ui/colorpicker/VColorPickerGradient.java
index dbfbf43eb6..3a135e2381 100644
--- a/client/src/com/vaadin/client/ui/colorpicker/VColorPickerGradient.java
+++ b/client/src/com/vaadin/client/ui/colorpicker/VColorPickerGradient.java
@@ -22,7 +22,6 @@ import com.google.gwt.event.dom.client.MouseMoveEvent;
import com.google.gwt.event.dom.client.MouseMoveHandler;
import com.google.gwt.event.dom.client.MouseUpEvent;
import com.google.gwt.event.dom.client.MouseUpHandler;
-import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.FocusPanel;
import com.google.gwt.user.client.ui.HTML;
@@ -191,7 +190,7 @@ public class VColorPickerGradient extends FocusPanel implements
}
@Override
- public Element getSubPartElement(String subPart) {
+ public com.google.gwt.user.client.Element getSubPartElement(String subPart) {
if (subPart.equals(CLICKLAYER_ID)) {
return clicklayer.getElement();
}
@@ -200,7 +199,7 @@ public class VColorPickerGradient extends FocusPanel implements
}
@Override
- public String getSubPartName(Element subElement) {
+ public String getSubPartName(com.google.gwt.user.client.Element subElement) {
if (clicklayer.getElement().isOrHasChild(subElement)) {
return CLICKLAYER_ID;
}
diff --git a/client/src/com/vaadin/client/ui/colorpicker/VColorPickerGrid.java b/client/src/com/vaadin/client/ui/colorpicker/VColorPickerGrid.java
index 9f4b0e0d76..8c9c34d668 100644
--- a/client/src/com/vaadin/client/ui/colorpicker/VColorPickerGrid.java
+++ b/client/src/com/vaadin/client/ui/colorpicker/VColorPickerGrid.java
@@ -15,11 +15,11 @@
*/
package com.vaadin.client.ui.colorpicker;
+import com.google.gwt.dom.client.Element;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.HasClickHandlers;
import com.google.gwt.event.shared.HandlerRegistration;
-import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HTMLTable.Cell;
diff --git a/client/src/com/vaadin/client/ui/csslayout/CssLayoutConnector.java b/client/src/com/vaadin/client/ui/csslayout/CssLayoutConnector.java
index 45e52c890e..034fd79364 100644
--- a/client/src/com/vaadin/client/ui/csslayout/CssLayoutConnector.java
+++ b/client/src/com/vaadin/client/ui/csslayout/CssLayoutConnector.java
@@ -16,7 +16,6 @@
package com.vaadin.client.ui.csslayout;
import com.google.gwt.dom.client.Style;
-import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.BrowserInfo;
import com.vaadin.client.ComponentConnector;
@@ -46,7 +45,8 @@ public class CssLayoutConnector extends AbstractLayoutConnector {
this) {
@Override
- protected ComponentConnector getChildComponent(Element element) {
+ protected ComponentConnector getChildComponent(
+ com.google.gwt.user.client.Element element) {
return Util.getConnectorForElement(getConnection(), getWidget(),
element);
}
@@ -54,7 +54,7 @@ public class CssLayoutConnector extends AbstractLayoutConnector {
@Override
protected LayoutClickRpc getLayoutClickRPC() {
return getRpcProxy(CssLayoutServerRpc.class);
- };
+ }
};
private final FastStringMap<VCaption> childIdToCaption = FastStringMap
diff --git a/client/src/com/vaadin/client/ui/dd/DDUtil.java b/client/src/com/vaadin/client/ui/dd/DDUtil.java
index c8a0621d74..96f681b54a 100644
--- a/client/src/com/vaadin/client/ui/dd/DDUtil.java
+++ b/client/src/com/vaadin/client/ui/dd/DDUtil.java
@@ -15,8 +15,8 @@
*/
package com.vaadin.client.ui.dd;
+import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NativeEvent;
-import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Window;
import com.vaadin.client.Util;
import com.vaadin.shared.ui.dd.HorizontalDropLocation;
@@ -58,7 +58,8 @@ public class DDUtil {
}
public static HorizontalDropLocation getHorizontalDropLocation(
- Element element, NativeEvent event, double leftRightRatio) {
+ Element element, NativeEvent event,
+ double leftRightRatio) {
int clientX = Util.getTouchOrMouseClientX(event);
// Event coordinates are relative to the viewport, element absolute
diff --git a/client/src/com/vaadin/client/ui/dd/VDragAndDropManager.java b/client/src/com/vaadin/client/ui/dd/VDragAndDropManager.java
index b911c28a07..f44fceb398 100644
--- a/client/src/com/vaadin/client/ui/dd/VDragAndDropManager.java
+++ b/client/src/com/vaadin/client/ui/dd/VDragAndDropManager.java
@@ -144,13 +144,11 @@ public class VDragAndDropManager {
}
if (currentDropHandler != null) {
- currentDrag
- .setElementOver((com.google.gwt.user.client.Element) targetElement);
+ currentDrag.setElementOver(targetElement);
currentDropHandler.dragEnter(currentDrag);
}
} else if (findDragTarget != null) {
- currentDrag
- .setElementOver((com.google.gwt.user.client.Element) targetElement);
+ currentDrag.setElementOver(targetElement);
currentDropHandler.dragOver(currentDrag);
}
// prevent text selection on IE
@@ -162,8 +160,7 @@ public class VDragAndDropManager {
// ApplicationConnection.getConsole().log(
// "Target just modified on "
// + event.getType());
- currentDrag
- .setElementOver((com.google.gwt.user.client.Element) targetElement);
+ currentDrag.setElementOver(targetElement);
break;
}
@@ -191,8 +188,7 @@ public class VDragAndDropManager {
// ApplicationConnection.getConsole().log(
// "DropHandler now"
// + currentDropHandler.getPaintable());
- currentDrag
- .setElementOver((com.google.gwt.user.client.Element) targetElement);
+ currentDrag.setElementOver(targetElement);
target.dragEnter(currentDrag);
} else if (target == null && currentDropHandler != null) {
// ApplicationConnection.getConsole().log("Invalid state!?");
@@ -223,8 +219,7 @@ public class VDragAndDropManager {
case Event.ONMOUSEMOVE:
case Event.ONTOUCHMOVE:
if (currentDropHandler != null) {
- currentDrag
- .setElementOver((com.google.gwt.user.client.Element) targetElement);
+ currentDrag.setElementOver(targetElement);
currentDropHandler.dragOver(currentDrag);
}
nativeEvent.preventDefault();
@@ -473,8 +468,7 @@ public class VDragAndDropManager {
*/
private VDropHandler findDragTarget(Element element) {
try {
- Widget w = Util.findWidget(
- (com.google.gwt.user.client.Element) element, null);
+ Widget w = Util.findWidget(element, null);
if (w == null) {
return null;
}
diff --git a/client/src/com/vaadin/client/ui/dd/VDragEvent.java b/client/src/com/vaadin/client/ui/dd/VDragEvent.java
index a4667e57f3..9b592cfcbd 100644
--- a/client/src/com/vaadin/client/ui/dd/VDragEvent.java
+++ b/client/src/com/vaadin/client/ui/dd/VDragEvent.java
@@ -19,12 +19,13 @@ import java.util.HashMap;
import java.util.Map;
import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.dom.client.TableElement;
import com.google.gwt.dom.client.TableSectionElement;
import com.google.gwt.event.dom.client.MouseOverEvent;
-import com.google.gwt.user.client.Element;
+import com.google.gwt.user.client.DOM;
import com.vaadin.client.BrowserInfo;
import com.vaadin.client.Util;
@@ -94,20 +95,32 @@ public class VDragEvent {
*
* @return the element in {@link VDropHandler} on which mouse cursor is on
*/
- public Element getElementOver() {
+ public com.google.gwt.user.client.Element getElementOver() {
if (elementOver != null) {
- return elementOver;
+ return DOM.asOld(elementOver);
} else if (currentGwtEvent != null) {
return currentGwtEvent.getEventTarget().cast();
}
return null;
}
- public void setElementOver(Element targetElement) {
+ /**
+ * @deprecated As of 7.2, call or override {@link #setElementOver(Element)}
+ * instead
+ */
+ @Deprecated
+ public void setElementOver(com.google.gwt.user.client.Element targetElement) {
elementOver = targetElement;
}
/**
+ * @since 7.2
+ */
+ public void setElementOver(Element targetElement) {
+ setElementOver(DOM.asOld(targetElement));
+ }
+
+ /**
* Sets the drag image used for current drag and drop operation. Drag image
* is displayed next to mouse cursor during drag and drop.
* <p>
@@ -121,12 +134,36 @@ public class VDragEvent {
* to HTML5 DataTransfer
*
* @param node
+ * @deprecated As of 7.2, call or override {@link #setDragImage(Element)}
+ * instead
*/
- public void setDragImage(Element node) {
+ @Deprecated
+ public void setDragImage(com.google.gwt.user.client.Element node) {
setDragImage(node, DEFAULT_OFFSET, DEFAULT_OFFSET);
}
/**
+ * Sets the drag image used for current drag and drop operation. Drag image
+ * is displayed next to mouse cursor during drag and drop.
+ * <p>
+ * The element to be used as drag image will automatically get CSS style
+ * name "v-drag-element".
+ *
+ * TODO decide if this method should be here or in {@link VTransferable} (in
+ * HTML5 it is in DataTransfer) or {@link VDragAndDropManager}
+ *
+ * TODO should be possible to override behavior. Like to proxy the element
+ * to HTML5 DataTransfer
+ *
+ * @param node
+ *
+ * @since 7.2
+ */
+ public void setDragImage(Element node) {
+ setDragImage(DOM.asOld(node));
+ }
+
+ /**
* TODO consider using similar smaller (than map) api as in Transferable
*
* TODO clean up when drop handler changes
@@ -150,19 +187,44 @@ public class VDragEvent {
* the horizontal offset of drag image from mouse cursor
* @param offsetY
* the vertical offset of drag image from mouse cursor
+ * @deprecated As of 7.2, call or override
+ * {@link #setDragImage(Element,int,int)} instead
*/
- public void setDragImage(Element element, int offsetX, int offsetY) {
+ @Deprecated
+ public void setDragImage(com.google.gwt.user.client.Element element,
+ int offsetX, int offsetY) {
element.getStyle().setMarginLeft(offsetX, Unit.PX);
element.getStyle().setMarginTop(offsetY, Unit.PX);
VDragAndDropManager.get().setDragElement(element);
+
+ }
+
+ /**
+ * Sets the drag image used for current drag and drop operation. Drag image
+ * is displayed next to mouse cursor during drag and drop.
+ * <p>
+ * The element to be used as drag image will automatically get CSS style
+ * name "v-drag-element".
+ *
+ * @param element
+ * the dom element to be positioned next to mouse cursor
+ * @param offsetX
+ * the horizontal offset of drag image from mouse cursor
+ * @param offsetY
+ * the vertical offset of drag image from mouse cursor
+ *
+ * @since 7.2
+ */
+ public void setDragImage(Element element, int offsetX, int offsetY) {
+ setDragImage(DOM.asOld(element), offsetX, offsetY);
}
/**
* @return the current Element used as a drag image (aka drag proxy) or null
* if drag image is not currently set for this drag operation.
*/
- public Element getDragImage() {
- return (Element) VDragAndDropManager.get().getDragElement();
+ public com.google.gwt.user.client.Element getDragImage() {
+ return DOM.asOld(VDragAndDropManager.get().getDragElement());
}
/**
@@ -172,8 +234,12 @@ public class VDragEvent {
* @param alignImageToEvent
* if true, proxy image is aligned to start event, else next to
* mouse cursor
+ * @deprecated As of 7.2, call or override
+ * {@link #createDragImage(Element,boolean)} instead
*/
- public void createDragImage(Element element, boolean alignImageToEvent) {
+ @Deprecated
+ public void createDragImage(com.google.gwt.user.client.Element element,
+ boolean alignImageToEvent) {
Element cloneNode = (Element) element.cloneNode(true);
if (BrowserInfo.get().isIE()) {
if (cloneNode.getTagName().toLowerCase().equals("tr")) {
@@ -198,4 +264,17 @@ public class VDragEvent {
}
+ /**
+ * Automatically tries to create a proxy image from given element.
+ *
+ * @param element
+ * @param alignImageToEvent
+ * if true, proxy image is aligned to start event, else next to
+ * mouse cursor
+ * @since 7.2
+ */
+ public void createDragImage(Element element, boolean alignImageToEvent) {
+ createDragImage(DOM.asOld(element), alignImageToEvent);
+ }
+
}
diff --git a/client/src/com/vaadin/client/ui/dd/VHtml5File.java b/client/src/com/vaadin/client/ui/dd/VHtml5File.java
index 4b36e7fd1b..ef74439063 100644
--- a/client/src/com/vaadin/client/ui/dd/VHtml5File.java
+++ b/client/src/com/vaadin/client/ui/dd/VHtml5File.java
@@ -23,7 +23,7 @@ import com.google.gwt.core.client.JavaScriptObject;
public class VHtml5File extends JavaScriptObject {
protected VHtml5File() {
- };
+ }
public native final String getName()
/*-{
@@ -35,7 +35,13 @@ public class VHtml5File extends JavaScriptObject {
return this.type;
}-*/;
- public native final int getSize()
+ /*
+ * Browser implementations support files >2GB dropped and report the value
+ * as long. Due to JSNI limitations this value needs to be sent as double
+ * and then cast back to a long value.
+ * www.gwtproject.org/doc/latest/DevGuideCodingBasicsJSNI.html#important
+ */
+ public native final double getSize()
/*-{
return this.size ? this.size : 0;
}-*/;
diff --git a/client/src/com/vaadin/client/ui/dd/VIsOverId.java b/client/src/com/vaadin/client/ui/dd/VIsOverId.java
index f8083f8b60..7e2f596a20 100644
--- a/client/src/com/vaadin/client/ui/dd/VIsOverId.java
+++ b/client/src/com/vaadin/client/ui/dd/VIsOverId.java
@@ -19,7 +19,6 @@
package com.vaadin.client.ui.dd;
import com.vaadin.client.ComponentConnector;
-import com.vaadin.client.ConnectorMap;
import com.vaadin.client.UIDL;
import com.vaadin.shared.ui.dd.AcceptCriterion;
import com.vaadin.ui.AbstractSelect;
@@ -36,8 +35,6 @@ final public class VIsOverId extends VAcceptCriterion {
.getCurrentDropHandler();
ComponentConnector dropHandlerConnector = currentDropHandler
.getConnector();
- ConnectorMap paintableMap = ConnectorMap.get(currentDropHandler
- .getApplicationConnection());
String pid2 = dropHandlerConnector.getConnectorId();
if (pid2.equals(pid)) {
diff --git a/client/src/com/vaadin/client/ui/dd/VItemIdIs.java b/client/src/com/vaadin/client/ui/dd/VItemIdIs.java
index 7d60eda4f9..b022f434f4 100644
--- a/client/src/com/vaadin/client/ui/dd/VItemIdIs.java
+++ b/client/src/com/vaadin/client/ui/dd/VItemIdIs.java
@@ -32,8 +32,6 @@ final public class VItemIdIs extends VAcceptCriterion {
String pid = configuration.getStringAttribute("s");
ComponentConnector dragSource = drag.getTransferable()
.getDragSource();
- VDropHandler currentDropHandler = VDragAndDropManager.get()
- .getCurrentDropHandler();
String pid2 = dragSource.getConnectorId();
if (pid2.equals(pid)) {
Object searchedId = drag.getTransferable().getData("itemId");
diff --git a/client/src/com/vaadin/client/ui/embedded/EmbeddedConnector.java b/client/src/com/vaadin/client/ui/embedded/EmbeddedConnector.java
index c6e4883fa9..c6e9e774ee 100644
--- a/client/src/com/vaadin/client/ui/embedded/EmbeddedConnector.java
+++ b/client/src/com/vaadin/client/ui/embedded/EmbeddedConnector.java
@@ -19,13 +19,13 @@ package com.vaadin.client.ui.embedded;
import java.util.Map;
import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.dom.client.Node;
import com.google.gwt.dom.client.NodeList;
import com.google.gwt.dom.client.ObjectElement;
import com.google.gwt.dom.client.Style;
import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.Paintable;
diff --git a/client/src/com/vaadin/client/ui/form/FormConnector.java b/client/src/com/vaadin/client/ui/form/FormConnector.java
index acd0e917fc..f5256adbb2 100644
--- a/client/src/com/vaadin/client/ui/form/FormConnector.java
+++ b/client/src/com/vaadin/client/ui/form/FormConnector.java
@@ -27,7 +27,6 @@ import com.vaadin.client.Paintable;
import com.vaadin.client.TooltipInfo;
import com.vaadin.client.UIDL;
import com.vaadin.client.ui.AbstractComponentContainerConnector;
-import com.vaadin.client.ui.Icon;
import com.vaadin.client.ui.ShortcutActionHandler;
import com.vaadin.client.ui.VForm;
import com.vaadin.client.ui.layout.ElementResizeEvent;
@@ -74,7 +73,7 @@ public class FormConnector extends AbstractComponentContainerConnector
protected void init() {
getLayoutManager().addElementResizeListener(
getWidget().errorMessage.getElement(), footerResizeListener);
- };
+ }
@Override
public void onUnregister() {
@@ -108,17 +107,14 @@ public class FormConnector extends AbstractComponentContainerConnector
} else {
getWidget().caption.setInnerText("");
}
- if (getIcon() != null) {
- if (getWidget().icon == null) {
- getWidget().icon = new Icon(client);
- getWidget().legend.insertFirst(getWidget().icon.getElement());
- }
- getWidget().icon.setUri(getIcon());
+ if (getWidget().icon != null) {
+ getWidget().legend.removeChild(getWidget().icon.getElement());
+ }
+ if (getIconUri() != null) {
+ getWidget().icon = client.getIcon(getIconUri());
+ getWidget().legend.insertFirst(getWidget().icon.getElement());
+
legendEmpty = false;
- } else {
- if (getWidget().icon != null) {
- getWidget().legend.removeChild(getWidget().icon.getElement());
- }
}
if (legendEmpty) {
getWidget().addStyleDependentName("nocaption");
diff --git a/client/src/com/vaadin/client/ui/formlayout/FormLayoutConnector.java b/client/src/com/vaadin/client/ui/formlayout/FormLayoutConnector.java
index c65f689f7a..8328e1f0a7 100644
--- a/client/src/com/vaadin/client/ui/formlayout/FormLayoutConnector.java
+++ b/client/src/com/vaadin/client/ui/formlayout/FormLayoutConnector.java
@@ -114,18 +114,14 @@ public class FormLayoutConnector extends AbstractLayoutConnector {
TooltipInfo info = null;
if (element != getWidget().getElement()) {
- Object node = Util.findWidget(
- (com.google.gwt.user.client.Element) element,
- VFormLayout.Caption.class);
+ Object node = Util.findWidget(element, VFormLayout.Caption.class);
if (node != null) {
VFormLayout.Caption caption = (VFormLayout.Caption) node;
info = caption.getOwner().getTooltipInfo(element);
} else {
- node = Util.findWidget(
- (com.google.gwt.user.client.Element) element,
- VFormLayout.ErrorFlag.class);
+ node = Util.findWidget(element, VFormLayout.ErrorFlag.class);
if (node != null) {
VFormLayout.ErrorFlag flag = (VFormLayout.ErrorFlag) node;
diff --git a/client/src/com/vaadin/client/ui/gridlayout/GridLayoutConnector.java b/client/src/com/vaadin/client/ui/gridlayout/GridLayoutConnector.java
index 29d41e00b7..cc052fa6d5 100644
--- a/client/src/com/vaadin/client/ui/gridlayout/GridLayoutConnector.java
+++ b/client/src/com/vaadin/client/ui/gridlayout/GridLayoutConnector.java
@@ -17,7 +17,6 @@ package com.vaadin.client.ui.gridlayout;
import java.util.Map.Entry;
-import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.ComponentConnector;
@@ -49,14 +48,15 @@ public class GridLayoutConnector extends AbstractComponentContainerConnector
this) {
@Override
- protected ComponentConnector getChildComponent(Element element) {
+ protected ComponentConnector getChildComponent(
+ com.google.gwt.user.client.Element element) {
return getWidget().getComponent(element);
}
@Override
protected LayoutClickRpc getLayoutClickRPC() {
return getRpcProxy(GridLayoutServerRpc.class);
- };
+ }
};
diff --git a/client/src/com/vaadin/client/ui/label/LabelConnector.java b/client/src/com/vaadin/client/ui/label/LabelConnector.java
index 9639987e8d..6a04c91562 100644
--- a/client/src/com/vaadin/client/ui/label/LabelConnector.java
+++ b/client/src/com/vaadin/client/ui/label/LabelConnector.java
@@ -36,12 +36,6 @@ public class LabelConnector extends AbstractComponentConnector {
}
@Override
- protected void init() {
- super.init();
- getWidget().setConnection(getConnection());
- }
-
- @Override
public void onStateChanged(StateChangeEvent stateChangeEvent) {
super.onStateChanged(stateChangeEvent);
boolean sinkOnloads = false;
diff --git a/client/src/com/vaadin/client/ui/layout/VLayoutSlot.java b/client/src/com/vaadin/client/ui/layout/VLayoutSlot.java
index 125f135aee..5c8a627dea 100644
--- a/client/src/com/vaadin/client/ui/layout/VLayoutSlot.java
+++ b/client/src/com/vaadin/client/ui/layout/VLayoutSlot.java
@@ -16,18 +16,18 @@
package com.vaadin.client.ui.layout;
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.Position;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.VCaption;
import com.vaadin.shared.ui.AlignmentInfo;
public abstract class VLayoutSlot {
- private final Element wrapper = Document.get().createDivElement().cast();
+ private final Element wrapper = Document.get().createDivElement();
private AlignmentInfo alignment;
private VCaption caption;
@@ -288,8 +288,8 @@ public abstract class VLayoutSlot {
return isVertical ? isRelativeHeight() : isRelativeWidth();
}
- public Element getWrapperElement() {
- return wrapper;
+ public com.google.gwt.user.client.Element getWrapperElement() {
+ return DOM.asOld(wrapper);
}
public void setExpandRatio(double expandRatio) {
diff --git a/client/src/com/vaadin/client/ui/link/LinkConnector.java b/client/src/com/vaadin/client/ui/link/LinkConnector.java
index 228897278e..c8bbc426e9 100644
--- a/client/src/com/vaadin/client/ui/link/LinkConnector.java
+++ b/client/src/com/vaadin/client/ui/link/LinkConnector.java
@@ -17,38 +17,21 @@
package com.vaadin.client.ui.link;
import com.google.gwt.user.client.DOM;
-import com.vaadin.client.ApplicationConnection;
-import com.vaadin.client.Paintable;
-import com.vaadin.client.UIDL;
import com.vaadin.client.communication.StateChangeEvent;
-import com.vaadin.client.communication.StateChangeEvent.StateChangeHandler;
import com.vaadin.client.ui.AbstractComponentConnector;
import com.vaadin.client.ui.Icon;
import com.vaadin.client.ui.VLink;
-import com.vaadin.shared.ui.BorderStyle;
import com.vaadin.shared.ui.Connect;
import com.vaadin.shared.ui.link.LinkConstants;
import com.vaadin.shared.ui.link.LinkState;
import com.vaadin.ui.Link;
@Connect(Link.class)
-public class LinkConnector extends AbstractComponentConnector implements
- Paintable {
+public class LinkConnector extends AbstractComponentConnector {
@Override
protected void init() {
super.init();
- addStateChangeHandler("resources", new StateChangeHandler() {
- @Override
- public void onStateChanged(StateChangeEvent stateChangeEvent) {
- getWidget().src = getResourceUrl(LinkConstants.HREF_RESOURCE);
- if (getWidget().src == null) {
- getWidget().anchor.removeAttribute("href");
- } else {
- getWidget().anchor.setAttribute("href", getWidget().src);
- }
- }
- });
}
@Override
@@ -62,35 +45,30 @@ public class LinkConnector extends AbstractComponentConnector implements
}
@Override
- public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
-
- if (!isRealUpdate(uidl)) {
- return;
- }
-
- getWidget().client = client;
+ public void onStateChanged(StateChangeEvent stateChangeEvent) {
+ super.onStateChanged(stateChangeEvent);
getWidget().enabled = isEnabled();
- if (uidl.hasAttribute("name")) {
- getWidget().target = uidl.getStringAttribute("name");
- getWidget().anchor.setAttribute("target", getWidget().target);
- }
-
- if (uidl.hasAttribute("border")) {
- if ("none".equals(uidl.getStringAttribute("border"))) {
- getWidget().borderStyle = BorderStyle.NONE;
+ if (stateChangeEvent.hasPropertyChanged("resources")) {
+ getWidget().src = getResourceUrl(LinkConstants.HREF_RESOURCE);
+ if (getWidget().src == null) {
+ getWidget().anchor.removeAttribute("href");
} else {
- getWidget().borderStyle = BorderStyle.MINIMAL;
+ getWidget().anchor.setAttribute("href", getWidget().src);
}
+ }
+
+ getWidget().target = getState().target;
+ if (getWidget().target == null) {
+ getWidget().anchor.removeAttribute("target");
} else {
- getWidget().borderStyle = BorderStyle.DEFAULT;
+ getWidget().anchor.setAttribute("target", getWidget().target);
}
- getWidget().targetHeight = uidl.hasAttribute("targetHeight") ? uidl
- .getIntAttribute("targetHeight") : -1;
- getWidget().targetWidth = uidl.hasAttribute("targetWidth") ? uidl
- .getIntAttribute("targetWidth") : -1;
+ getWidget().borderStyle = getState().targetBorder;
+ getWidget().targetWidth = getState().targetWidth;
+ getWidget().targetHeight = getState().targetHeight;
// Set link caption
getWidget().captionElement.setInnerText(getState().caption);
@@ -109,15 +87,16 @@ public class LinkConnector extends AbstractComponentConnector implements
"none");
}
- if (getIcon() != null) {
- if (getWidget().icon == null) {
- getWidget().icon = new Icon(client);
- getWidget().anchor.insertBefore(getWidget().icon.getElement(),
- getWidget().captionElement);
- }
- getWidget().icon.setUri(getIcon());
+ if (getWidget().icon != null) {
+ getWidget().anchor.removeChild(getWidget().icon.getElement());
+ getWidget().icon = null;
+ }
+ Icon icon = getIcon();
+ if (icon != null) {
+ getWidget().icon = icon;
+ getWidget().anchor.insertBefore(icon.getElement(),
+ getWidget().captionElement);
}
-
}
@Override
diff --git a/client/src/com/vaadin/client/ui/menubar/MenuBar.java b/client/src/com/vaadin/client/ui/menubar/MenuBar.java
index 4441faf7ab..6f0546601a 100644
--- a/client/src/com/vaadin/client/ui/menubar/MenuBar.java
+++ b/client/src/com/vaadin/client/ui/menubar/MenuBar.java
@@ -37,9 +37,9 @@ import java.util.ArrayList;
import java.util.List;
import com.google.gwt.core.client.Scheduler;
+import com.google.gwt.dom.client.Element;
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.ui.PopupListener;
import com.google.gwt.user.client.ui.PopupPanel;
diff --git a/client/src/com/vaadin/client/ui/menubar/MenuBarConnector.java b/client/src/com/vaadin/client/ui/menubar/MenuBarConnector.java
index 3e22ebb05b..4ead614275 100644
--- a/client/src/com/vaadin/client/ui/menubar/MenuBarConnector.java
+++ b/client/src/com/vaadin/client/ui/menubar/MenuBarConnector.java
@@ -27,7 +27,7 @@ import com.vaadin.client.TooltipInfo;
import com.vaadin.client.UIDL;
import com.vaadin.client.Util;
import com.vaadin.client.ui.AbstractComponentConnector;
-import com.vaadin.client.ui.Icon;
+import com.vaadin.client.ui.ImageIcon;
import com.vaadin.client.ui.SimpleManagedLayout;
import com.vaadin.client.ui.VMenuBar;
import com.vaadin.shared.ui.ComponentStateUtil;
@@ -81,7 +81,8 @@ public class MenuBarConnector extends AbstractComponentConnector implements
+ Util.escapeAttribute(client
.translateVaadinUri(moreItemUIDL
.getStringAttribute("icon")))
- + "\" class=\"" + Icon.CLASSNAME + "\" alt=\"\" />");
+ + "\" class=\"" + ImageIcon.CLASSNAME
+ + "\" alt=\"\" />");
}
String moreItemText = moreItemUIDL.getStringAttribute("text");
@@ -196,7 +197,7 @@ public class MenuBarConnector extends AbstractComponentConnector implements
if (element != getWidget().getElement()) {
VMenuBar.CustomMenuItem item = getWidget().getMenuItemWithElement(
- (com.google.gwt.user.client.Element) element);
+ element);
if (item != null) {
info = item.getTooltip();
}
diff --git a/client/src/com/vaadin/client/ui/nativebutton/NativeButtonConnector.java b/client/src/com/vaadin/client/ui/nativebutton/NativeButtonConnector.java
index 11a76b483b..e4e88899eb 100644
--- a/client/src/com/vaadin/client/ui/nativebutton/NativeButtonConnector.java
+++ b/client/src/com/vaadin/client/ui/nativebutton/NativeButtonConnector.java
@@ -87,20 +87,16 @@ public class NativeButtonConnector extends AbstractComponentConnector implements
getWidget().errorIndicatorElement = null;
}
- if (getIcon() != null) {
- if (getWidget().icon == null) {
- getWidget().icon = new Icon(getConnection());
- getWidget().getElement().insertBefore(
- getWidget().icon.getElement(),
- getWidget().captionElement);
- }
- getWidget().icon.setUri(getIcon());
- } else {
- if (getWidget().icon != null) {
- getWidget().getElement().removeChild(
- getWidget().icon.getElement());
- getWidget().icon = null;
- }
+ if (getWidget().icon != null) {
+ getWidget().getElement().removeChild(getWidget().icon.getElement());
+ getWidget().icon = null;
+ }
+ Icon icon = getIcon();
+ if (icon != null) {
+ getWidget().icon = icon;
+ getWidget().getElement().insertBefore(icon.getElement(),
+ getWidget().captionElement);
+ icon.setAlternateText(getState().iconAltText);
}
}
diff --git a/client/src/com/vaadin/client/ui/orderedlayout/AbstractOrderedLayoutConnector.java b/client/src/com/vaadin/client/ui/orderedlayout/AbstractOrderedLayoutConnector.java
index d9299f2621..fbf94f0da3 100644
--- a/client/src/com/vaadin/client/ui/orderedlayout/AbstractOrderedLayoutConnector.java
+++ b/client/src/com/vaadin/client/ui/orderedlayout/AbstractOrderedLayoutConnector.java
@@ -17,8 +17,8 @@ package com.vaadin.client.ui.orderedlayout;
import java.util.List;
+import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Style.Unit;
-import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.ComponentConnector;
@@ -32,6 +32,7 @@ import com.vaadin.client.communication.StateChangeEvent;
import com.vaadin.client.communication.StateChangeEvent.StateChangeHandler;
import com.vaadin.client.ui.AbstractFieldConnector;
import com.vaadin.client.ui.AbstractLayoutConnector;
+import com.vaadin.client.ui.Icon;
import com.vaadin.client.ui.LayoutClickEventHandler;
import com.vaadin.client.ui.aria.AriaHelper;
import com.vaadin.client.ui.layout.ElementResizeEvent;
@@ -59,7 +60,8 @@ public abstract class AbstractOrderedLayoutConnector extends
this) {
@Override
- protected ComponentConnector getChildComponent(Element element) {
+ protected ComponentConnector getChildComponent(
+ com.google.gwt.user.client.Element element) {
return Util.getConnectorForElement(getConnection(), getWidget(),
element);
}
@@ -97,12 +99,12 @@ public abstract class AbstractOrderedLayoutConnector extends
public void onElementResize(ElementResizeEvent e) {
// Get all needed element references
- Element captionElement = (Element) e.getElement().cast();
+ Element captionElement = e.getElement();
// Caption position determines if the widget element is the first or
// last child inside the caption wrap
CaptionPosition pos = getWidget().getCaptionPositionFromElement(
- (Element) captionElement.getParentElement().cast());
+ captionElement.getParentElement());
// The default is the last child
Element widgetElement = captionElement.getParentElement()
@@ -244,6 +246,8 @@ public abstract class AbstractOrderedLayoutConnector extends
URLReference iconUrl = child.getState().resources
.get(ComponentConstants.ICON_RESOURCE);
String iconUrlString = iconUrl != null ? iconUrl.getURL() : null;
+ Icon icon = child.getConnection().getIcon(iconUrlString);
+
List<String> styles = child.getState().styles;
String error = child.getState().errorMessage;
boolean showError = error != null;
@@ -262,8 +266,8 @@ public abstract class AbstractOrderedLayoutConnector extends
slot.setCaptionResizeListener(null);
}
- slot.setCaption(caption, iconUrlString, styles, error, showError,
- required, enabled);
+ slot.setCaption(caption, icon, styles, error, showError, required,
+ enabled);
AriaHelper.handleInputRequired(child.getWidget(), required);
AriaHelper.handleInputInvalid(child.getWidget(), showError);
@@ -374,8 +378,7 @@ public abstract class AbstractOrderedLayoutConnector extends
@Override
public TooltipInfo getTooltipInfo(com.google.gwt.dom.client.Element element) {
if (element != getWidget().getElement()) {
- Slot slot = Util.findWidget(
- (com.google.gwt.user.client.Element) element, Slot.class);
+ Slot slot = Util.findWidget(element, Slot.class);
if (slot != null && slot.getCaptionElement() != null
&& slot.getParent() == getWidget()
&& slot.getCaptionElement().isOrHasChild(element)) {
diff --git a/client/src/com/vaadin/client/ui/orderedlayout/Slot.java b/client/src/com/vaadin/client/ui/orderedlayout/Slot.java
index d9037cda3c..03038eefd3 100644
--- a/client/src/com/vaadin/client/ui/orderedlayout/Slot.java
+++ b/client/src/com/vaadin/client/ui/orderedlayout/Slot.java
@@ -19,18 +19,21 @@ package com.vaadin.client.ui.orderedlayout;
import java.util.List;
import com.google.gwt.aria.client.Roles;
+import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.Element;
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.ui.SimplePanel;
-import com.google.gwt.user.client.ui.UIObject;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.BrowserInfo;
import com.vaadin.client.LayoutManager;
import com.vaadin.client.StyleConstants;
import com.vaadin.client.Util;
+import com.vaadin.client.ui.FontIcon;
+import com.vaadin.client.ui.Icon;
+import com.vaadin.client.ui.ImageIcon;
import com.vaadin.client.ui.layout.ElementResizeEvent;
import com.vaadin.client.ui.layout.ElementResizeListener;
import com.vaadin.shared.ui.AlignmentInfo;
@@ -39,44 +42,6 @@ import com.vaadin.shared.ui.AlignmentInfo;
* Represents a slot which contains the actual widget in the layout.
*/
public final class Slot extends SimplePanel {
- /**
- * The icon for each widget. Located in the caption of the slot.
- */
- private static class Icon extends UIObject {
-
- public static final String CLASSNAME = "v-icon";
-
- private String myUrl;
-
- /**
- * Constructor
- */
- public Icon() {
- setElement(DOM.createImg());
- DOM.setElementProperty(getElement(), "alt", "");
- setStyleName(CLASSNAME);
- }
-
- /**
- * Set the URL where the icon is located
- *
- * @param url
- * A fully qualified URL
- */
- public void setUri(String url) {
- if (!url.equals(myUrl)) {
- /*
- * Start sinking onload events, widgets responsibility to react.
- * We must do this BEFORE we set src as IE fires the event
- * immediately if the image is found in cache (#2592).
- */
- sinkEvents(Event.ONLOAD);
-
- DOM.setElementProperty(getElement(), "src", url);
- myUrl = url;
- }
- }
- }
private static final String ALIGN_CLASS_PREFIX = "v-align-";
@@ -123,15 +88,12 @@ public final class Slot extends SimplePanel {
private double expandRatio = -1;
/**
- * Constructor
+ * Constructs a slot.
*
+ * @param layout
+ * The layout to which this slot belongs
* @param widget
* The widget to put in the slot
- * @param layout
- * TODO
- *
- * @param layoutManager
- * The layout manager used by the layout
*/
public Slot(VAbstractOrderedLayout layout, Widget widget) {
this.layout = layout;
@@ -370,8 +332,8 @@ public final class Slot extends SimplePanel {
*
* @return
*/
- public Element getSpacingElement() {
- return spacer;
+ public com.google.gwt.user.client.Element getSpacingElement() {
+ return DOM.asOld(spacer);
}
/**
@@ -445,7 +407,7 @@ public final class Slot extends SimplePanel {
* @param captionText
* The text of the caption
* @param iconUrl
- * The icon URL
+ * The icon URL, must already be run trough translateVaadinUri()
* @param styles
* The style names
* @param error
@@ -456,10 +418,47 @@ public final class Slot extends SimplePanel {
* Is the (field) required
* @param enabled
* Is the component enabled
+ *
+ * @deprecated Use
+ * {@link #setCaption(String, Icon, List, String, boolean, boolean, boolean)}
+ * instead
*/
+ @Deprecated
public void setCaption(String captionText, String iconUrl,
List<String> styles, String error, boolean showError,
boolean required, boolean enabled) {
+ Icon icon;
+ if (FontIcon.isFontIconUri(iconUrl)) {
+ icon = GWT.create(FontIcon.class);
+ } else {
+ icon = GWT.create(ImageIcon.class);
+ }
+ icon.setUri(iconUrl);
+
+ setCaption(captionText, icon, styles, error, showError, required,
+ enabled);
+ }
+
+ /**
+ * Set the caption of the slot
+ *
+ * @param captionText
+ * The text of the caption
+ * @param icon
+ * The icon
+ * @param styles
+ * The style names
+ * @param error
+ * The error message
+ * @param showError
+ * Should the error message be shown
+ * @param required
+ * Is the (field) required
+ * @param enabled
+ * Is the component enabled
+ */
+ public void setCaption(String captionText, Icon icon, List<String> styles,
+ String error, boolean showError, boolean required, boolean enabled) {
// TODO place for optimization: check if any of these have changed
// since last time, and only run those changes
@@ -469,7 +468,7 @@ public final class Slot extends SimplePanel {
final Element focusedElement = Util.getFocusedElement();
// By default focus will not be lost
boolean focusLost = false;
- if (captionText != null || iconUrl != null || error != null || required) {
+ if (captionText != null || icon != null || error != null || required) {
if (caption == null) {
caption = DOM.createDiv();
captionWrap = DOM.createDiv();
@@ -516,16 +515,13 @@ public final class Slot extends SimplePanel {
}
// Icon
- if (iconUrl != null) {
- if (icon == null) {
- icon = new Icon();
- caption.insertFirst(icon.getElement());
- }
- icon.setUri(iconUrl);
- } else if (icon != null) {
- icon.getElement().removeFromParent();
- icon = null;
+ if (this.icon != null) {
+ this.icon.getElement().removeFromParent();
+ }
+ if (icon != null) {
+ caption.insertFirst(icon.getElement());
}
+ this.icon = icon;
// Required
if (required) {
@@ -576,7 +572,7 @@ public final class Slot extends SimplePanel {
}
// Caption position
- if (captionText != null || iconUrl != null) {
+ if (captionText != null || icon != null) {
setCaptionPosition(CaptionPosition.TOP);
} else {
setCaptionPosition(CaptionPosition.RIGHT);
@@ -633,8 +629,8 @@ public final class Slot extends SimplePanel {
/**
* Get the slots caption element
*/
- public Element getCaptionElement() {
- return caption;
+ public com.google.gwt.user.client.Element getCaptionElement() {
+ return DOM.asOld(caption);
}
private boolean relativeWidth = false;
@@ -729,11 +725,11 @@ public final class Slot extends SimplePanel {
* @see com.google.gwt.user.client.ui.SimplePanel#getContainerElement()
*/
@Override
- protected Element getContainerElement() {
+ protected com.google.gwt.user.client.Element getContainerElement() {
if (captionWrap == null) {
return getElement();
} else {
- return captionWrap;
+ return DOM.asOld(captionWrap);
}
}
diff --git a/client/src/com/vaadin/client/ui/orderedlayout/VAbstractOrderedLayout.java b/client/src/com/vaadin/client/ui/orderedlayout/VAbstractOrderedLayout.java
index 54c9eb6c68..4a1dae9b95 100644
--- a/client/src/com/vaadin/client/ui/orderedlayout/VAbstractOrderedLayout.java
+++ b/client/src/com/vaadin/client/ui/orderedlayout/VAbstractOrderedLayout.java
@@ -18,6 +18,7 @@ package com.vaadin.client.ui.orderedlayout;
import java.util.HashMap;
import java.util.Map;
+import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Node;
import com.google.gwt.dom.client.Style;
import com.google.gwt.dom.client.Style.Unit;
@@ -25,7 +26,6 @@ import com.google.gwt.dom.client.Style.Visibility;
import com.google.gwt.regexp.shared.MatchResult;
import com.google.gwt.regexp.shared.RegExp;
import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.RequiresResize;
import com.google.gwt.user.client.ui.Widget;
@@ -141,9 +141,14 @@ public class VAbstractOrderedLayout extends FlowPanel {
/**
* {@inheritDoc}
+ *
+ * @deprecated As of 7.2, use or override
+ * {@link #insert(Widget, Element, int, boolean)} instead.
*/
@Override
- protected void insert(Widget child, Element container, int beforeIndex,
+ @Deprecated
+ protected void insert(Widget child,
+ com.google.gwt.user.client.Element container, int beforeIndex,
boolean domInsert) {
// Validate index; adjust if the widget is already a child of this
// panel.
@@ -156,7 +161,8 @@ public class VAbstractOrderedLayout extends FlowPanel {
getChildren().insert(child, beforeIndex);
// Physical attach.
- container = expandWrapper != null ? expandWrapper : getElement();
+ container = expandWrapper != null ? DOM.asOld(expandWrapper)
+ : getElement();
if (domInsert) {
if (spacing) {
if (beforeIndex != 0) {
@@ -184,6 +190,17 @@ public class VAbstractOrderedLayout extends FlowPanel {
}
/**
+ * {@inheritDoc}
+ *
+ * @since 7.2
+ */
+ @Override
+ protected void insert(Widget child, Element container, int beforeIndex,
+ boolean domInsert) {
+ insert(child, DOM.asOld(container), beforeIndex, domInsert);
+ }
+
+ /**
* Remove a slot from the layout
*
* @param widget
@@ -220,8 +237,10 @@ public class VAbstractOrderedLayout extends FlowPanel {
* @param widgetElement
* The element of the widget ( Same as getWidget().getElement() )
* @return
+ * @deprecated As of 7.2, call or override {@link #getSlot(Element)} instead
*/
- public Slot getSlot(Element widgetElement) {
+ @Deprecated
+ public Slot getSlot(com.google.gwt.user.client.Element widgetElement) {
for (Map.Entry<Widget, Slot> entry : widgetToSlot.entrySet()) {
if (entry.getKey().getElement() == widgetElement) {
return entry.getValue();
@@ -231,6 +250,20 @@ public class VAbstractOrderedLayout extends FlowPanel {
}
/**
+ * Gets a slot based on the widget element. If no slot is found then null is
+ * returned.
+ *
+ * @param widgetElement
+ * The element of the widget ( Same as getWidget().getElement() )
+ * @return
+ *
+ * @since 7.2
+ */
+ public Slot getSlot(Element widgetElement) {
+ return getSlot(DOM.asOld(widgetElement));
+ }
+
+ /**
* Set the layout manager for the layout
*
* @param manager
@@ -257,8 +290,12 @@ public class VAbstractOrderedLayout extends FlowPanel {
* The wrapping element
*
* @return The caption position
+ * @deprecated As of 7.2, call or override
+ * {@link #getCaptionPositionFromElement(Element)} instead
*/
- public CaptionPosition getCaptionPositionFromElement(Element captionWrap) {
+ @Deprecated
+ public CaptionPosition getCaptionPositionFromElement(
+ com.google.gwt.user.client.Element captionWrap) {
RegExp captionPositionRegexp = RegExp.compile("v-caption-on-(\\S+)");
// Get caption position from the classname
@@ -274,16 +311,34 @@ public class VAbstractOrderedLayout extends FlowPanel {
}
/**
+ * Deducts the caption position by examining the wrapping element.
+ * <p>
+ * For internal use only. May be removed or replaced in the future.
+ *
+ * @param captionWrap
+ * The wrapping element
+ *
+ * @return The caption position
+ * @since 7.2
+ */
+ public CaptionPosition getCaptionPositionFromElement(Element captionWrap) {
+ return getCaptionPositionFromElement(DOM.asOld(captionWrap));
+ }
+
+ /**
* Update the offset off the caption relative to the slot
* <p>
* For internal use only. May be removed or replaced in the future.
*
* @param caption
* The caption element
+ * @deprecated As of 7.2, call or override
+ * {@link #updateCaptionOffset(Element)} instead
*/
- public void updateCaptionOffset(Element caption) {
+ @Deprecated
+ public void updateCaptionOffset(com.google.gwt.user.client.Element caption) {
- Element captionWrap = caption.getParentElement().cast();
+ Element captionWrap = caption.getParentElement();
Style captionWrapStyle = captionWrap.getStyle();
captionWrapStyle.clearPaddingTop();
@@ -341,6 +396,19 @@ public class VAbstractOrderedLayout extends FlowPanel {
}
/**
+ * Update the offset off the caption relative to the slot
+ * <p>
+ * For internal use only. May be removed or replaced in the future.
+ *
+ * @param caption
+ * The caption element
+ * @since 7.2
+ */
+ public void updateCaptionOffset(Element caption) {
+ updateCaptionOffset(DOM.asOld(caption));
+ }
+
+ /**
* Set the margin of the layout
*
* @param marginInfo
diff --git a/client/src/com/vaadin/client/ui/panel/PanelConnector.java b/client/src/com/vaadin/client/ui/panel/PanelConnector.java
index 4011f86c76..f2e73bae80 100644
--- a/client/src/com/vaadin/client/ui/panel/PanelConnector.java
+++ b/client/src/com/vaadin/client/ui/panel/PanelConnector.java
@@ -137,8 +137,8 @@ public class PanelConnector extends AbstractSingleComponentContainerConnector
getWidget().client = client;
getWidget().id = uidl.getId();
- if (getIcon() != null) {
- getWidget().setIconUri(getIcon(), client);
+ if (getIconUri() != null) {
+ getWidget().setIconUri(getIconUri(), client);
} else {
getWidget().setIconUri(null, client);
}
diff --git a/client/src/com/vaadin/client/ui/popupview/PopupViewConnector.java b/client/src/com/vaadin/client/ui/popupview/PopupViewConnector.java
index f49c4ba3ee..2f53280c99 100644
--- a/client/src/com/vaadin/client/ui/popupview/PopupViewConnector.java
+++ b/client/src/com/vaadin/client/ui/popupview/PopupViewConnector.java
@@ -49,11 +49,6 @@ public class PopupViewConnector extends AbstractHasComponentsConnector
}
@Override
- public boolean delegateCaptionHandling() {
- return false;
- }
-
- @Override
public void onStateChanged(StateChangeEvent stateChangeEvent) {
super.onStateChanged(stateChangeEvent);
diff --git a/client/src/com/vaadin/client/ui/richtextarea/RichTextAreaConnector.java b/client/src/com/vaadin/client/ui/richtextarea/RichTextAreaConnector.java
index d2576eb133..a19e931713 100644
--- a/client/src/com/vaadin/client/ui/richtextarea/RichTextAreaConnector.java
+++ b/client/src/com/vaadin/client/ui/richtextarea/RichTextAreaConnector.java
@@ -109,5 +109,5 @@ public class RichTextAreaConnector extends AbstractFieldConnector implements
getState().immediate);
}
}
- };
+ }
}
diff --git a/client/src/com/vaadin/client/ui/slider/SliderConnector.java b/client/src/com/vaadin/client/ui/slider/SliderConnector.java
index 71462d69f0..b4eb9f14f7 100644
--- a/client/src/com/vaadin/client/ui/slider/SliderConnector.java
+++ b/client/src/com/vaadin/client/ui/slider/SliderConnector.java
@@ -21,6 +21,8 @@ import com.vaadin.client.communication.RpcProxy;
import com.vaadin.client.communication.StateChangeEvent;
import com.vaadin.client.ui.AbstractFieldConnector;
import com.vaadin.client.ui.VSlider;
+import com.vaadin.client.ui.layout.ElementResizeEvent;
+import com.vaadin.client.ui.layout.ElementResizeListener;
import com.vaadin.shared.ui.Connect;
import com.vaadin.shared.ui.slider.SliderServerRpc;
import com.vaadin.shared.ui.slider.SliderState;
@@ -33,11 +35,29 @@ public class SliderConnector extends AbstractFieldConnector implements
protected SliderServerRpc rpc = RpcProxy
.create(SliderServerRpc.class, this);
+ private final ElementResizeListener resizeListener = new ElementResizeListener() {
+
+ @Override
+ public void onElementResize(ElementResizeEvent e) {
+ getWidget().iLayout();
+ }
+ };
+
@Override
public void init() {
super.init();
getWidget().setConnection(getConnection());
getWidget().addValueChangeHandler(this);
+
+ getLayoutManager().addElementResizeListener(getWidget().getElement(),
+ resizeListener);
+ }
+
+ @Override
+ public void onUnregister() {
+ super.onUnregister();
+ getLayoutManager().removeElementResizeListener(
+ getWidget().getElement(), resizeListener);
}
@Override
diff --git a/client/src/com/vaadin/client/ui/splitpanel/AbstractSplitPanelConnector.java b/client/src/com/vaadin/client/ui/splitpanel/AbstractSplitPanelConnector.java
index 464dd5057c..45535de3de 100644
--- a/client/src/com/vaadin/client/ui/splitpanel/AbstractSplitPanelConnector.java
+++ b/client/src/com/vaadin/client/ui/splitpanel/AbstractSplitPanelConnector.java
@@ -18,12 +18,13 @@ package com.vaadin.client.ui.splitpanel;
import java.util.LinkedList;
import java.util.List;
+import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.event.dom.client.DomEvent;
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.Element;
+import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.ComponentConnector;
@@ -103,12 +104,12 @@ public abstract class AbstractSplitPanelConnector extends
}
return super.shouldFireEvent(event);
- };
+ }
@Override
- protected Element getRelativeToElement() {
- return getWidget().splitter;
- };
+ protected com.google.gwt.user.client.Element getRelativeToElement() {
+ return DOM.asOld(getWidget().splitter);
+ }
@Override
protected void fireClick(NativeEvent event,
diff --git a/client/src/com/vaadin/client/ui/table/TableConnector.java b/client/src/com/vaadin/client/ui/table/TableConnector.java
index 4d7d3c6277..d2bd06a753 100644
--- a/client/src/com/vaadin/client/ui/table/TableConnector.java
+++ b/client/src/com/vaadin/client/ui/table/TableConnector.java
@@ -264,7 +264,7 @@ public class TableConnector extends AbstractHasComponentsConnector implements
if (getWidget().focusedRow != null) {
if (!getWidget().focusedRow.isAttached()
- && !getWidget().rowRequestHandler.isRunning()) {
+ && !getWidget().rowRequestHandler.isRequestHandlerRunning()) {
// focused row has been orphaned, can't focus
if (getWidget().selectedRowKeys.contains(getWidget().focusedRow
.getKey())) {
@@ -402,9 +402,7 @@ public class TableConnector extends AbstractHasComponentsConnector implements
TooltipInfo info = null;
if (element != getWidget().getElement()) {
- Object node = Util.findWidget(
- (com.google.gwt.user.client.Element) element,
- VScrollTableRow.class);
+ Object node = Util.findWidget(element, VScrollTableRow.class);
if (node != null) {
VScrollTableRow row = (VScrollTableRow) node;
diff --git a/client/src/com/vaadin/client/ui/tabsheet/TabsheetBaseConnector.java b/client/src/com/vaadin/client/ui/tabsheet/TabsheetBaseConnector.java
index 283bc1b63b..30c9e47c6e 100644
--- a/client/src/com/vaadin/client/ui/tabsheet/TabsheetBaseConnector.java
+++ b/client/src/com/vaadin/client/ui/tabsheet/TabsheetBaseConnector.java
@@ -19,31 +19,41 @@ import java.util.ArrayList;
import java.util.Iterator;
import com.google.gwt.user.client.ui.Widget;
-import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.ComponentConnector;
-import com.vaadin.client.Paintable;
-import com.vaadin.client.UIDL;
+import com.vaadin.client.communication.StateChangeEvent;
import com.vaadin.client.ui.AbstractComponentContainerConnector;
import com.vaadin.client.ui.VTabsheetBase;
-import com.vaadin.shared.ui.tabsheet.TabsheetBaseConstants;
+import com.vaadin.shared.ui.tabsheet.TabState;
+import com.vaadin.shared.ui.tabsheet.TabsheetState;
public abstract class TabsheetBaseConnector extends
- AbstractComponentContainerConnector implements Paintable {
+ AbstractComponentContainerConnector {
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.client.ui.AbstractConnector#init()
+ */
@Override
- public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
- getWidget().client = client;
+ protected void init() {
+ super.init();
- if (!isRealUpdate(uidl)) {
- return;
- }
+ getWidget().setClient(getConnection());
+ }
- // Update member references
- getWidget().id = uidl.getId();
- getWidget().disabled = !isEnabled();
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.vaadin.client.ui.AbstractComponentConnector#onStateChanged(com.vaadin
+ * .client.communication.StateChangeEvent)
+ */
+ @Override
+ public void onStateChanged(StateChangeEvent stateChangeEvent) {
+ super.onStateChanged(stateChangeEvent);
- // Render content
- final UIDL tabs = uidl.getChildUIDL(0);
+ // Update member references
+ getWidget().setEnabled(isEnabled());
// Widgets in the TabSheet before update
ArrayList<Widget> oldWidgets = new ArrayList<Widget>();
@@ -53,26 +63,22 @@ public abstract class TabsheetBaseConnector extends
}
// Clear previous values
- getWidget().tabKeys.clear();
- getWidget().disabledTabKeys.clear();
+ getWidget().clearTabKeys();
int index = 0;
- for (final Iterator<Object> it = tabs.getChildIterator(); it.hasNext();) {
- final UIDL tab = (UIDL) it.next();
- final String key = tab.getStringAttribute("key");
- final boolean selected = tab.getBooleanAttribute("selected");
- final boolean hidden = tab.getBooleanAttribute("hidden");
-
- if (tab.getBooleanAttribute(TabsheetBaseConstants.ATTRIBUTE_TAB_DISABLED)) {
- getWidget().disabledTabKeys.add(key);
- }
+ for (TabState tab : getState().tabs) {
+ final String key = tab.key;
+ final boolean selected = key.equals(getState().selected);
- getWidget().tabKeys.add(key);
+ getWidget().addTabKey(key, !tab.enabled && tab.visible);
if (selected) {
- getWidget().activeTabIndex = index;
+ getWidget().setActiveTabIndex(index);
+ }
+ getWidget().renderTab(tab, index);
+ if (selected) {
+ getWidget().selectTab(index);
}
- getWidget().renderTab(tab, index, selected, hidden);
index++;
}
@@ -104,4 +110,9 @@ public abstract class TabsheetBaseConnector extends
return (VTabsheetBase) super.getWidget();
}
+ @Override
+ public TabsheetState getState() {
+ return (TabsheetState) super.getState();
+ }
+
}
diff --git a/client/src/com/vaadin/client/ui/tabsheet/TabsheetConnector.java b/client/src/com/vaadin/client/ui/tabsheet/TabsheetConnector.java
index 04a514738d..564e5847d9 100644
--- a/client/src/com/vaadin/client/ui/tabsheet/TabsheetConnector.java
+++ b/client/src/com/vaadin/client/ui/tabsheet/TabsheetConnector.java
@@ -17,36 +17,62 @@ package com.vaadin.client.ui.tabsheet;
import com.google.gwt.dom.client.Element;
import com.google.gwt.user.client.DOM;
-import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.ComponentConnector;
import com.vaadin.client.ConnectorHierarchyChangeEvent;
import com.vaadin.client.TooltipInfo;
-import com.vaadin.client.UIDL;
import com.vaadin.client.Util;
+import com.vaadin.client.communication.StateChangeEvent;
import com.vaadin.client.ui.SimpleManagedLayout;
import com.vaadin.client.ui.VTabsheet;
import com.vaadin.client.ui.layout.MayScrollChildren;
import com.vaadin.shared.ui.Connect;
-import com.vaadin.shared.ui.tabsheet.TabsheetState;
+import com.vaadin.shared.ui.tabsheet.TabsheetClientRpc;
import com.vaadin.ui.TabSheet;
@Connect(TabSheet.class)
public class TabsheetConnector extends TabsheetBaseConnector implements
SimpleManagedLayout, MayScrollChildren {
- // Can't use "style" as it's already in use
+ public TabsheetConnector() {
+ registerRpc(TabsheetClientRpc.class, new TabsheetClientRpc() {
+ @Override
+ public void revertToSharedStateSelection() {
+ for (int i = 0; i < getState().tabs.size(); ++i) {
+ final String key = getState().tabs.get(i).key;
+ final boolean selected = key.equals(getState().selected);
+ if (selected) {
+ getWidget().selectTab(i);
+ break;
+ }
+ }
+ renderContent();
+ }
+ });
+ }
+
@Override
- public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
+ protected void init() {
+ super.init();
+ getWidget().setConnector(this);
+ }
- if (isRealUpdate(uidl)) {
- // Handle stylename changes before generics (might affect size
- // calculations)
- getWidget().handleStyleNames(uidl, getState());
- }
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.vaadin.client.ui.AbstractComponentConnector#onStateChanged(com.vaadin
+ * .client.communication.StateChangeEvent)
+ */
+ @Override
+ public void onStateChanged(StateChangeEvent stateChangeEvent) {
+ super.onStateChanged(stateChangeEvent);
+
+ getWidget().handleStyleNames(getState());
- super.updateFromUIDL(uidl, client);
- if (!isRealUpdate(uidl)) {
- return;
+ if (getState().tabsVisible) {
+ getWidget().showTabs();
+ } else {
+ getWidget().hideTabs();
}
// tabs; push or not
@@ -76,11 +102,6 @@ public class TabsheetConnector extends TabsheetBaseConnector implements
}
@Override
- public TabsheetState getState() {
- return (TabsheetState) super.getState();
- }
-
- @Override
public void updateCaption(ComponentConnector component) {
/* Tabsheet does not render its children's captions */
}
@@ -119,9 +140,7 @@ public class TabsheetConnector extends TabsheetBaseConnector implements
// Find a tooltip for the tab, if the element is a tab
if (element != getWidget().getElement()) {
- Object node = Util.findWidget(
- (com.google.gwt.user.client.Element) element,
- VTabsheet.TabCaption.class);
+ Object node = Util.findWidget(element, VTabsheet.TabCaption.class);
if (node != null) {
VTabsheet.TabCaption caption = (VTabsheet.TabCaption) node;
@@ -149,7 +168,24 @@ public class TabsheetConnector extends TabsheetBaseConnector implements
@Override
public void onConnectorHierarchyChange(
- ConnectorHierarchyChangeEvent connectorHierarchyChangeEvent) {
- // TODO Move code from updateFromUIDL to this method
+ ConnectorHierarchyChangeEvent connector) {
+ renderContent();
}
+
+ /**
+ * (Re-)render the content of the active tab.
+ */
+ protected void renderContent() {
+ ComponentConnector contentConnector = null;
+ if (!getChildComponents().isEmpty()) {
+ contentConnector = getChildComponents().get(0);
+ }
+
+ if (null != contentConnector) {
+ getWidget().renderContent(contentConnector.getWidget());
+ } else {
+ getWidget().renderContent(null);
+ }
+ }
+
}
diff --git a/client/src/com/vaadin/client/ui/tree/TreeConnector.java b/client/src/com/vaadin/client/ui/tree/TreeConnector.java
index 7560a0f56b..8b09c10ad3 100644
--- a/client/src/com/vaadin/client/ui/tree/TreeConnector.java
+++ b/client/src/com/vaadin/client/ui/tree/TreeConnector.java
@@ -46,6 +46,11 @@ public class TreeConnector extends AbstractComponentConnector implements
protected final Map<TreeNode, TooltipInfo> tooltipMap = new HashMap<TreeNode, TooltipInfo>();
@Override
+ protected void init() {
+ getWidget().connector = this;
+ }
+
+ @Override
public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
if (!isRealUpdate(uidl)) {
return;
@@ -327,9 +332,7 @@ public class TreeConnector extends AbstractComponentConnector implements
// Try to find a tooltip for a node
if (element != getWidget().getElement()) {
- Object node = Util.findWidget(
- (com.google.gwt.user.client.Element) element,
- TreeNode.class);
+ Object node = Util.findWidget(element, TreeNode.class);
if (node != null) {
TreeNode tnode = (TreeNode) node;
diff --git a/client/src/com/vaadin/client/ui/treetable/TreeTableConnector.java b/client/src/com/vaadin/client/ui/treetable/TreeTableConnector.java
index a8c6a8823a..4f62f0df89 100644
--- a/client/src/com/vaadin/client/ui/treetable/TreeTableConnector.java
+++ b/client/src/com/vaadin/client/ui/treetable/TreeTableConnector.java
@@ -123,9 +123,7 @@ public class TreeTableConnector extends TableConnector {
TooltipInfo info = null;
if (element != getWidget().getElement()) {
- Object node = Util.findWidget(
- (com.google.gwt.user.client.Element) element,
- VTreeTableRow.class);
+ Object node = Util.findWidget(element, VTreeTableRow.class);
if (node != null) {
VTreeTableRow row = (VTreeTableRow) node;
diff --git a/client/src/com/vaadin/client/ui/ui/UIConnector.java b/client/src/com/vaadin/client/ui/ui/UIConnector.java
index 149d99de17..576f50d018 100644
--- a/client/src/com/vaadin/client/ui/ui/UIConnector.java
+++ b/client/src/com/vaadin/client/ui/ui/UIConnector.java
@@ -22,6 +22,7 @@ import java.util.List;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.HeadElement;
import com.google.gwt.dom.client.LinkElement;
import com.google.gwt.dom.client.NativeEvent;
@@ -35,7 +36,6 @@ import com.google.gwt.event.logical.shared.ResizeHandler;
import com.google.gwt.http.client.URL;
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.History;
import com.google.gwt.user.client.Timer;
@@ -49,7 +49,6 @@ import com.vaadin.client.ApplicationConnection.ApplicationStoppedEvent;
import com.vaadin.client.BrowserInfo;
import com.vaadin.client.ComponentConnector;
import com.vaadin.client.ConnectorHierarchyChangeEvent;
-import com.vaadin.client.ConnectorMap;
import com.vaadin.client.Focusable;
import com.vaadin.client.Paintable;
import com.vaadin.client.ServerConnector;
@@ -102,10 +101,6 @@ public class UIConnector extends AbstractSingleComponentContainerConnector
protected void init() {
super.init();
registerRpc(PageClientRpc.class, new PageClientRpc() {
- @Override
- public void setTitle(String title) {
- com.google.gwt.user.client.Window.setTitle(title);
- }
@Override
public void reload() {
@@ -196,7 +191,6 @@ public class UIConnector extends AbstractSingleComponentContainerConnector
@Override
public void updateFromUIDL(final UIDL uidl, ApplicationConnection client) {
- ConnectorMap paintableMap = ConnectorMap.get(getConnection());
getWidget().id = getConnectorId();
boolean firstPaint = getWidget().connection == null;
getWidget().connection = client;
@@ -510,6 +504,18 @@ public class UIConnector extends AbstractSingleComponentContainerConnector
return (VUI) super.getWidget();
}
+ @Override
+ protected ComponentConnector getContent() {
+ ComponentConnector connector = super.getContent();
+ // VWindow (WindowConnector is its connector)is also a child component
+ // but it's never a content widget
+ if (connector instanceof WindowConnector) {
+ return null;
+ } else {
+ return connector;
+ }
+ }
+
protected void onChildSizeChange() {
ComponentConnector child = getContent();
if (child == null) {
@@ -685,6 +691,13 @@ public class UIConnector extends AbstractSingleComponentContainerConnector
configurePolling();
}
+ if (stateChangeEvent.hasPropertyChanged("pageState.title")) {
+ String title = getState().pageState.title;
+ if (title != null) {
+ com.google.gwt.user.client.Window.setTitle(title);
+ }
+ }
+
if (stateChangeEvent.hasPropertyChanged("pushConfiguration")) {
getConnection().setPushEnabled(
getState().pushConfiguration.mode.isEnabled());
diff --git a/client/src/com/vaadin/client/ui/upload/UploadConnector.java b/client/src/com/vaadin/client/ui/upload/UploadConnector.java
index 989a913adc..03f1a2802c 100644
--- a/client/src/com/vaadin/client/ui/upload/UploadConnector.java
+++ b/client/src/com/vaadin/client/ui/upload/UploadConnector.java
@@ -16,13 +16,17 @@
package com.vaadin.client.ui.upload;
+import com.google.gwt.event.dom.client.ChangeEvent;
+import com.google.gwt.event.dom.client.ChangeHandler;
import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.Paintable;
import com.vaadin.client.UIDL;
import com.vaadin.client.ui.AbstractComponentConnector;
import com.vaadin.client.ui.VUpload;
+import com.vaadin.shared.EventId;
import com.vaadin.shared.ui.Connect;
import com.vaadin.shared.ui.upload.UploadClientRpc;
+import com.vaadin.shared.ui.upload.UploadServerRpc;
import com.vaadin.ui.Upload;
@Connect(Upload.class)
@@ -39,6 +43,21 @@ public class UploadConnector extends AbstractComponentConnector implements
}
@Override
+ protected void init() {
+ super.init();
+
+ getWidget().fu.addChangeHandler(new ChangeHandler() {
+ @Override
+ public void onChange(ChangeEvent event) {
+ if (hasEventListener(EventId.CHANGE)) {
+ getRpcProxy(UploadServerRpc.class).change(
+ getWidget().fu.getFilename());
+ }
+ }
+ });
+ }
+
+ @Override
public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
if (!isRealUpdate(uidl)) {
return;
diff --git a/client/src/com/vaadin/client/ui/window/WindowConnector.java b/client/src/com/vaadin/client/ui/window/WindowConnector.java
index 54ea384f5a..07015e7fbe 100644
--- a/client/src/com/vaadin/client/ui/window/WindowConnector.java
+++ b/client/src/com/vaadin/client/ui/window/WindowConnector.java
@@ -94,7 +94,7 @@ public class WindowConnector extends AbstractSingleComponentContainerConnector
@Override
public boolean delegateCaptionHandling() {
return false;
- };
+ }
@Override
protected void init() {
@@ -229,7 +229,7 @@ public class WindowConnector extends AbstractSingleComponentContainerConnector
Style childStyle = layoutElement.getStyle();
// IE8 needs some hackery to measure its content correctly
- Util.forceIE8Redraw((com.google.gwt.user.client.Element) layoutElement);
+ Util.forceIE8Redraw(layoutElement);
if (content.isRelativeHeight() && !BrowserInfo.get().isIE9()) {
childStyle.setPosition(Position.ABSOLUTE);
@@ -295,11 +295,21 @@ public class WindowConnector extends AbstractSingleComponentContainerConnector
// Caption must be set before required header size is measured. If
// the caption attribute is missing the caption should be cleared.
String iconURL = null;
- if (getIcon() != null) {
- iconURL = getIcon();
+ if (getIconUri() != null) {
+ iconURL = getIconUri();
}
+
+ window.setAssistivePrefix(state.assistivePrefix);
+ window.setAssistivePostfix(state.assistivePostfix);
window.setCaption(state.caption, iconURL);
+ window.setWaiAriaRole(getState().role);
+ window.setAssistiveDescription(state.contentDescription);
+
+ window.setTabStopEnabled(getState().assistiveTabStop);
+ window.setTabStopTopAssistiveText(getState().assistiveTabStopTopText);
+ window.setTabStopBottomAssistiveText(getState().assistiveTabStopBottomText);
+
clickEventHandler.handleEventHandlerRegistration();
window.immediate = state.immediate;